diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index 5cf828e..2f13e2b 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h @@ -61,7 +61,7 @@ #define CR_TE (1 << 30) /* Thumb exception enable */ #ifndef __ASSEMBLY__ -#if __LINUX_ARM_ARCH__ >= 7 +#if __LINUX_ARM_ARCH__ > 7 static inline unsigned int current_el(void) { unsigned int el; diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index b2b250f..edfc851 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -84,58 +84,68 @@ config ARCH_IMX1 bool select CPU_ARM920T + select ARCH_HAS_IMX_GPT select PINCTRL_IMX_IOMUX_V1 config ARCH_IMX21 bool select CPU_ARM926T + select ARCH_HAS_IMX_GPT select PINCTRL_IMX_IOMUX_V1 config ARCH_IMX25 bool select CPU_ARM926T select ARCH_HAS_FEC_IMX + select ARCH_HAS_IMX_GPT select PINCTRL_IMX_IOMUX_V3 config ARCH_IMX27 bool select CPU_ARM926T select ARCH_HAS_FEC_IMX + select ARCH_HAS_IMX_GPT select PINCTRL_IMX_IOMUX_V1 config ARCH_IMX31 select CPU_V6 bool + select ARCH_HAS_IMX_GPT select PINCTRL_IMX_IOMUX_V2 config ARCH_IMX35 bool select CPU_V6 select ARCH_HAS_FEC_IMX + select ARCH_HAS_IMX_GPT select PINCTRL_IMX_IOMUX_V3 config ARCH_IMX50 bool select CPU_V7 select ARCH_HAS_FEC_IMX + select ARCH_HAS_IMX_GPT select PINCTRL_IMX_IOMUX_V3 config ARCH_IMX51 bool select CPU_V7 select ARCH_HAS_FEC_IMX + select ARCH_HAS_IMX_GPT select PINCTRL_IMX_IOMUX_V3 config ARCH_IMX53 bool select CPU_V7 select ARCH_HAS_FEC_IMX + select ARCH_HAS_IMX_GPT select PINCTRL_IMX_IOMUX_V3 config ARCH_IMX6 bool select ARCH_HAS_L2X0 select ARCH_HAS_FEC_IMX + select ARCH_HAS_IMX_GPT select CPU_V7 select PINCTRL_IMX_IOMUX_V3 select OFTREE @@ -163,6 +173,7 @@ select OFTREE select COMMON_CLK_OF_PROVIDER select ARCH_HAS_FEC_IMX + select ARCH_HAS_IMX_GPT config ARCH_IMX8MQ bool @@ -184,6 +195,7 @@ select COMMON_CLK_OF_PROVIDER select NVMEM select IMX_OCOTP # Needed for clock adjustement + select CLOCKSOURCE_ARM_GLOBAL_TIMER config IMX_MULTI_BOARDS bool "Allow multiple boards to be selected" diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index 5a01dd5..97c5440 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile @@ -1,4 +1,3 @@ -obj-y += clocksource.o obj-$(CONFIG_ARCH_IMX1) += imx1.o obj-$(CONFIG_ARCH_IMX25) += imx25.o obj-$(CONFIG_ARCH_IMX21) += imx21.o diff --git a/arch/arm/mach-imx/clocksource.c b/arch/arm/mach-imx/clocksource.c deleted file mode 100644 index 4d6c6c2..0000000 --- a/arch/arm/mach-imx/clocksource.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - * (C) Copyright 2002 - * Sysgo Real-Time Solutions, GmbH - * Marius Groeger - * - * (C) Copyright 2002 - * Sysgo Real-Time Solutions, GmbH - * Alex Zuepke - * - * (C) Copyright 2002 - * Gary Jennejohn, DENX Software Engineering, - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Part 1: Registers */ -# define GPT_TCTL 0x00 -# define GPT_TPRER 0x04 - -/* Part 2: Bitfields */ -#define TCTL_SWR (1 << 15) /* Software reset */ -#define IMX1_TCTL_FRR (1 << 8) /* Freerun / restart */ -#define IMX31_TCTL_FRR (1 << 9) /* Freerun / restart */ -#define IMX1_TCTL_CLKSOURCE_PER (1 << 1) /* Clock source bit position */ -#define IMX31_TCTL_CLKSOURCE_IPG (1 << 6) /* Clock source bit position */ -#define IMX31_TCTL_CLKSOURCE_PER (2 << 6) /* Clock source bit position */ -#define TCTL_TEN (1 << 0) /* Timer enable */ - -static struct clk *clk_gpt; - -struct imx_gpt_regs { - unsigned int tcn; - uint32_t tctl_val; -}; - -static struct imx_gpt_regs regs_imx1 = { - .tcn = 0x10, - .tctl_val = IMX1_TCTL_FRR | IMX1_TCTL_CLKSOURCE_PER | TCTL_TEN, -}; - -static struct imx_gpt_regs regs_imx31 = { - .tcn = 0x24, - .tctl_val = IMX31_TCTL_FRR | IMX31_TCTL_CLKSOURCE_PER | TCTL_TEN, -}; - -static struct imx_gpt_regs *regs; -static void __iomem *timer_base; - -static uint64_t imx_clocksource_read(void) -{ - return readl(timer_base + regs->tcn); -} - -static struct clocksource cs = { - .read = imx_clocksource_read, - .mask = CLOCKSOURCE_MASK(32), - .shift = 10, -}; - -static int imx_clocksource_clock_change(struct notifier_block *nb, unsigned long event, void *data) -{ - cs.mult = clocksource_hz2mult(clk_get_rate(clk_gpt), cs.shift); - return 0; -} - -static struct notifier_block imx_clock_notifier = { - .notifier_call = imx_clocksource_clock_change, -}; - -static int imx_gpt_probe(struct device_d *dev) -{ - struct resource *iores; - int i; - int ret; - unsigned long rate; - - /* one timer is enough */ - if (timer_base) - return 0; - - ret = dev_get_drvdata(dev, (const void **)®s); - if (ret) - return ret; - - iores = dev_request_mem_resource(dev, 0); - if (IS_ERR(iores)) - return PTR_ERR(iores); - timer_base = IOMEM(iores->start); - - /* setup GP Timer 1 */ - writel(TCTL_SWR, timer_base + GPT_TCTL); - - for (i = 0; i < 100; i++) - writel(0, timer_base + GPT_TCTL); /* We have no udelay by now */ - - clk_gpt = clk_get(dev, "per"); - if (IS_ERR(clk_gpt)) { - rate = 20000000; - dev_err(dev, "failed to get clock, assume %lu Hz\n", rate); - } else { - rate = clk_get_rate(clk_gpt); - if (!rate) { - dev_err(dev, "clock reports rate == 0\n"); - return -EIO; - } - } - - writel(0, timer_base + GPT_TPRER); - writel(regs->tctl_val, timer_base + GPT_TCTL); - - cs.mult = clocksource_hz2mult(rate, cs.shift); - - init_clock(&cs); - - clock_register_client(&imx_clock_notifier); - - return 0; -} - -static __maybe_unused struct of_device_id imx_gpt_dt_ids[] = { - { - .compatible = "fsl,imx1-gpt", - .data = ®s_imx1, - }, { - .compatible = "fsl,imx21-gpt", - .data = ®s_imx1, - }, { - .compatible = "fsl,imx31-gpt", - .data = ®s_imx31, - }, { - .compatible = "fsl,imx6q-gpt", - .data = ®s_imx31, - }, { - .compatible = "fsl,imx6dl-gpt", - .data = ®s_imx31, - }, { - .compatible = "fsl,imx6ul-gpt", - .data = ®s_imx31, - }, { - .compatible = "fsl,imx7d-gpt", - .data = ®s_imx31, - }, { - /* sentinel */ - } -}; - -static struct platform_device_id imx_gpt_ids[] = { - { - .name = "imx1-gpt", - .driver_data = (unsigned long)®s_imx1, - }, { - .name = "imx31-gpt", - .driver_data = (unsigned long)®s_imx31, - }, { - /* sentinel */ - }, -}; - -static struct driver_d imx_gpt_driver = { - .name = "imx-gpt", - .probe = imx_gpt_probe, - .of_compatible = DRV_OF_COMPAT(imx_gpt_dt_ids), - .id_table = imx_gpt_ids, -}; - -static int imx_gpt_init(void) -{ - return platform_driver_register(&imx_gpt_driver); -} -postcore_initcall(imx_gpt_init); diff --git a/arch/arm/mach-imx/iim.c b/arch/arm/mach-imx/iim.c index d4794cb..207e187 100644 --- a/arch/arm/mach-imx/iim.c +++ b/arch/arm/mach-imx/iim.c @@ -474,7 +474,7 @@ imx_iim = iim; - strcpy(iim->dev.name, "iim"); + dev_set_name(&iim->dev, "iim"); iim->dev.parent = dev; iim->dev.id = DEVICE_ID_SINGLE; ret = register_device(&iim->dev); diff --git a/arch/arm/mach-imx/include/mach/devices-imx51.h b/arch/arm/mach-imx/include/mach/devices-imx51.h index cccd8f4..66fe643 100644 --- a/arch/arm/mach-imx/include/mach/devices-imx51.h +++ b/arch/arm/mach-imx/include/mach/devices-imx51.h @@ -81,7 +81,7 @@ dev->resource = xzalloc(sizeof(struct resource) * ARRAY_SIZE(res)); memcpy(dev->resource, res, sizeof(struct resource) * ARRAY_SIZE(res)); dev->num_resources = ARRAY_SIZE(res); - strcpy(dev->name, "imx_nand"); + dev_set_name(dev, "imx_nand"); dev->id = DEVICE_ID_DYNAMIC; dev->platform_data = pdata; diff --git a/arch/arm/mach-imx/include/mach/devices-imx53.h b/arch/arm/mach-imx/include/mach/devices-imx53.h index 10caae8..27200a2 100644 --- a/arch/arm/mach-imx/include/mach/devices-imx53.h +++ b/arch/arm/mach-imx/include/mach/devices-imx53.h @@ -95,7 +95,7 @@ dev->resource = xzalloc(sizeof(struct resource) * ARRAY_SIZE(res)); memcpy(dev->resource, res, sizeof(struct resource) * ARRAY_SIZE(res)); dev->num_resources = ARRAY_SIZE(res); - strcpy(dev->name, "imx_nand"); + dev_set_name(dev, "imx_nand"); dev->id = DEVICE_ID_DYNAMIC; dev->platform_data = pdata; diff --git a/arch/arm/mach-mxs/include/mach/devices.h b/arch/arm/mach-mxs/include/mach/devices.h index 5680d61..b212aa7 100644 --- a/arch/arm/mach-mxs/include/mach/devices.h +++ b/arch/arm/mach-mxs/include/mach/devices.h @@ -26,7 +26,7 @@ dev->resource = xzalloc(sizeof(struct resource) * ARRAY_SIZE(res)); memcpy(dev->resource, res, sizeof(struct resource) * ARRAY_SIZE(res)); dev->num_resources = ARRAY_SIZE(res); - strcpy(dev->name, "mxs_nand"); + dev_set_name(dev, "mxs_nand"); dev->id = DEVICE_ID_DYNAMIC; platform_device_register(dev); diff --git a/arch/arm/mach-mxs/ocotp.c b/arch/arm/mach-mxs/ocotp.c index b41fde9..01db731 100644 --- a/arch/arm/mach-mxs/ocotp.c +++ b/arch/arm/mach-mxs/ocotp.c @@ -197,7 +197,7 @@ priv->cdev.size = cpu_is_mx23() ? 128 : 160; priv->cdev.name = DRIVERNAME; - strcpy(priv->dev.name, "ocotp"); + dev_set_name(&priv->dev, "ocotp"); priv->dev.parent = dev; err = register_device(&priv->dev); if (err) diff --git a/arch/sandbox/board/console.c b/arch/sandbox/board/console.c index cf1781d..006bbd1 100644 --- a/arch/sandbox/board/console.c +++ b/arch/sandbox/board/console.c @@ -32,7 +32,7 @@ data = (struct linux_console_data *)(dev + 1); dev->platform_data = data; - strcpy(dev->name, "console"); + dev_set_name(dev, "console"); dev->id = DEVICE_ID_DYNAMIC; data->stdoutfd = stdoutfd; diff --git a/common/console.c b/common/console.c index 40c26b6..47ccf2e 100644 --- a/common/console.c +++ b/common/console.c @@ -314,10 +314,10 @@ if (newcdev->devname) { dev->id = newcdev->devid; - strcpy(dev->name, newcdev->devname); + dev_set_name(dev, newcdev->devname); } else { dev->id = DEVICE_ID_DYNAMIC; - strcpy(dev->name, "cs"); + dev_set_name(dev, "cs"); } if (newcdev->dev) diff --git a/common/state/state.c b/common/state/state.c index 089d8f5..1597197 100644 --- a/common/state/state.c +++ b/common/state/state.c @@ -179,7 +179,7 @@ int ret; state = xzalloc(sizeof(*state)); - safe_strncpy(state->dev.name, name, MAX_DRIVER_NAME); + dev_set_name(&state->dev, name); state->name = state->dev.name; state->dev.id = DEVICE_ID_SINGLE; INIT_LIST_HEAD(&state->variables); diff --git a/drivers/aiodev/core.c b/drivers/aiodev/core.c index 79f935d..b5d06da 100644 --- a/drivers/aiodev/core.c +++ b/drivers/aiodev/core.c @@ -106,8 +106,6 @@ aiodev->dev.id = DEVICE_ID_SINGLE; aiodev->name = of_alias_get(aiodev->hwdev->device_node); - if (!aiodev->name) - aiodev->name = aiodev->hwdev->device_node->name; } if (!aiodev->name) { @@ -115,7 +113,7 @@ aiodev->dev.id = DEVICE_ID_DYNAMIC; } - strcpy(aiodev->dev.name, aiodev->name); + dev_set_name(&aiodev->dev, aiodev->name); aiodev->dev.parent = aiodev->hwdev; diff --git a/drivers/aiodev/imx_thermal.c b/drivers/aiodev/imx_thermal.c index c020a10..9e50708 100644 --- a/drivers/aiodev/imx_thermal.c +++ b/drivers/aiodev/imx_thermal.c @@ -177,6 +177,7 @@ imx_thermal->aiodev.num_channels = 1; imx_thermal->aiodev.hwdev = dev; + imx_thermal->aiodev.name = "thermal-sensor"; imx_thermal->aiodev.channels = xmalloc(imx_thermal->aiodev.num_channels * sizeof(imx_thermal->aiodev.channels[0])); diff --git a/drivers/aiodev/qoriq_thermal.c b/drivers/aiodev/qoriq_thermal.c index d29da02..7556fef 100644 --- a/drivers/aiodev/qoriq_thermal.c +++ b/drivers/aiodev/qoriq_thermal.c @@ -232,6 +232,7 @@ data->aiodev.num_channels = 1; data->aiodev.hwdev = dev; + data->aiodev.name = "thermal-sensor"; data->aiodev.channels = xmalloc(data->aiodev.num_channels * sizeof(data->aiodev.channels[0])); data->aiodev.channels[0] = &data->aiochan; diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index ae5df13..a8cd168 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -200,7 +200,7 @@ dev = xzalloc(sizeof(*dev)); - strcpy(dev->dev.name, name); + dev_set_name(&dev->dev, name); dev->dev.id = id; dev->res.start = base; dev->res.end = base + size - 1; diff --git a/drivers/ata/disk_ata_drive.c b/drivers/ata/disk_ata_drive.c index 5ebddbd..11f7151 100644 --- a/drivers/ata/disk_ata_drive.c +++ b/drivers/ata/disk_ata_drive.c @@ -325,10 +325,10 @@ int ret; if (port->devname) { - strcpy(port->class_dev.name, port->devname); + dev_set_name(&port->class_dev, port->devname); port->class_dev.id = DEVICE_ID_SINGLE; } else { - strcpy(port->class_dev.name, "ata"); + dev_set_name(&port->class_dev, "ata"); port->class_dev.id = DEVICE_ID_DYNAMIC; } diff --git a/drivers/base/bus.c b/drivers/base/bus.c index b889a48..1038d20 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -32,7 +32,7 @@ return -EEXIST; bus->dev = xzalloc(sizeof(*bus->dev)); - strcpy(bus->dev->name, bus->name); + dev_set_name(bus->dev, bus->name); bus->dev->id = DEVICE_ID_SINGLE; ret = register_device(bus->dev); diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 3b39c28..4acc4cf 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -183,6 +183,11 @@ } } + if (new_device->id != DEVICE_ID_SINGLE) + new_device->unique_name = basprintf(FORMAT_DRIVER_NAME_ID, + new_device->name, + new_device->id); + debug ("register_device: %s\n", dev_name(new_device)); list_add_tail(&new_device->list, &device_list); @@ -443,17 +448,39 @@ } EXPORT_SYMBOL(dummy_probe); -const char *dev_id(const struct device_d *dev) +/** + * dev_set_name - set a device name + * @dev: device + * @fmt: format string for the device's name + * + * NOTE: This function expects dev->name to be free()-able, so extra + * precautions needs to be taken when mixing its usage with manual + * assignement of device_d.name. + */ +int dev_set_name(struct device_d *dev, const char *fmt, ...) { - static char buf[MAX_DRIVER_NAME + 16]; + va_list vargs; + int err; + /* + * Save old pointer in case we are overriding already set name + */ + char *oldname = dev->name; - if (dev->id != DEVICE_ID_SINGLE) - snprintf(buf, sizeof(buf), FORMAT_DRIVER_NAME_ID, dev->name, dev->id); - else - snprintf(buf, sizeof(buf), "%s", dev->name); + va_start(vargs, fmt); + err = vasprintf(&dev->name, fmt, vargs); + va_end(vargs); - return buf; + /* + * Free old pointer, we do this after vasprintf call in case + * old device name was in one of vargs + */ + free(oldname); + + WARN_ON(err < 0); + + return err; } +EXPORT_SYMBOL_GPL(dev_set_name); static void devices_shutdown(void) { diff --git a/drivers/base/resource.c b/drivers/base/resource.c index 6c2d7fe..cb7105b 100644 --- a/drivers/base/resource.c +++ b/drivers/base/resource.c @@ -27,7 +27,7 @@ struct device_d *dev; dev = xzalloc(sizeof(*dev)); - strcpy(dev->name, devname); + dev_set_name(dev, devname); dev->id = id; return dev; diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 3d63f7f..3caf725 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -1,3 +1,6 @@ +config ARCH_HAS_IMX_GPT + bool + config AMBA_SP804 bool depends on ARM_AMBA @@ -70,3 +73,10 @@ bool default y depends on ARM && CPU_64v8 + +config CLOCKSOURCE_ARM_GLOBAL_TIMER + bool + depends on ARM && CPU_V7 +config CLOCKSOURCE_IMX_GPT + def_bool y + depends on ARCH_HAS_IMX_GPT diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index ea33fff..ce4d741 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -12,3 +12,5 @@ obj-$(CONFIG_CLOCKSOURCE_ROCKCHIP)+= rk_timer.o obj-$(CONFIG_CLOCKSOURCE_ATMEL_PIT) += timer-atmel-pit.o obj-$(CONFIG_CLOCKSOURCE_ARMV8_TIMER) += armv8-timer.o +obj-$(CONFIG_CLOCKSOURCE_ARM_GLOBAL_TIMER) += arm_global_timer.o +obj-$(CONFIG_CLOCKSOURCE_IMX_GPT) += timer-imx-gpt.o diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c new file mode 100644 index 0000000..44e3a3c --- /dev/null +++ b/drivers/clocksource/arm_global_timer.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Clocksource driver for generic Cortex A9 timer block + * + * Copyright (C) 2018 Zodiac Inflight Innovations + * Author: Andrey Smirnov + * + * based on corresponding driver from Linux kernel with the following + * copyright: + * + * drivers/clocksource/arm_global_timer.c + * + * Copyright (C) 2013 STMicroelectronics (R&D) Limited. + * Author: Stuart Menefy + * Author: Srinivas Kandagatla + */ +#include +#include +#include +#include +#include +#include + +#define GT_COUNTER0 0x00 +#define GT_COUNTER1 0x04 + +#define GT_CONTROL 0x08 +#define GT_CONTROL_TIMER_ENABLE BIT(0) /* this bit is NOT banked */ + +static void __iomem *gt_base; + +/* + * To get the value from the Global Timer Counter register proceed as follows: + * 1. Read the upper 32-bit timer counter register + * 2. Read the lower 32-bit timer counter register + * 3. Read the upper 32-bit timer counter register again. If the value is + * different to the 32-bit upper value read previously, go back to step 2. + * Otherwise the 64-bit timer counter value is correct. + */ +static uint64_t arm_global_clocksource_read(void) +{ + uint64_t counter; + uint32_t lower; + uint32_t upper, old_upper; + + upper = readl(gt_base + GT_COUNTER1); + do { + old_upper = upper; + lower = readl(gt_base + GT_COUNTER0); + upper = readl(gt_base + GT_COUNTER1); + } while (upper != old_upper); + + counter = upper; + counter <<= 32; + counter |= lower; + return counter; +} + +static struct clocksource cs = { + .read = arm_global_clocksource_read, + .mask = CLOCKSOURCE_MASK(64), + .shift = 0, +}; + +static int arm_global_timer_probe(struct device_d *dev) +{ + struct resource *iores; + struct clk *clk; + int ret; + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + + clk = clk_get(dev, NULL); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + dev_err(dev, "clock not found: %d\n", ret); + return ret; + } + + ret = clk_enable(clk); + if (ret) { + dev_err(dev, "clock failed to enable: %d\n", ret); + return ret; + } + + gt_base = IOMEM(iores->start); + + cs.mult = clocksource_hz2mult(clk_get_rate(clk), cs.shift); + + writel(0, gt_base + GT_CONTROL); + writel(0, gt_base + GT_COUNTER0); + writel(0, gt_base + GT_COUNTER1); + /* enables timer on all the cores */ + writel(GT_CONTROL_TIMER_ENABLE, gt_base + GT_CONTROL); + + return init_clock(&cs); +} + +static struct of_device_id arm_global_timer_dt_ids[] = { + { .compatible = "arm,cortex-a9-global-timer", }, + { } +}; + +static struct driver_d arm_global_timer_driver = { + .name = "arm-global-timer", + .probe = arm_global_timer_probe, + .of_compatible = DRV_OF_COMPAT(arm_global_timer_dt_ids), +}; +postcore_platform_driver(arm_global_timer_driver); + diff --git a/drivers/clocksource/timer-imx-gpt.c b/drivers/clocksource/timer-imx-gpt.c new file mode 100644 index 0000000..4d6c6c2 --- /dev/null +++ b/drivers/clocksource/timer-imx-gpt.c @@ -0,0 +1,192 @@ +/* + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH + * Marius Groeger + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH + * Alex Zuepke + * + * (C) Copyright 2002 + * Gary Jennejohn, DENX Software Engineering, + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Part 1: Registers */ +# define GPT_TCTL 0x00 +# define GPT_TPRER 0x04 + +/* Part 2: Bitfields */ +#define TCTL_SWR (1 << 15) /* Software reset */ +#define IMX1_TCTL_FRR (1 << 8) /* Freerun / restart */ +#define IMX31_TCTL_FRR (1 << 9) /* Freerun / restart */ +#define IMX1_TCTL_CLKSOURCE_PER (1 << 1) /* Clock source bit position */ +#define IMX31_TCTL_CLKSOURCE_IPG (1 << 6) /* Clock source bit position */ +#define IMX31_TCTL_CLKSOURCE_PER (2 << 6) /* Clock source bit position */ +#define TCTL_TEN (1 << 0) /* Timer enable */ + +static struct clk *clk_gpt; + +struct imx_gpt_regs { + unsigned int tcn; + uint32_t tctl_val; +}; + +static struct imx_gpt_regs regs_imx1 = { + .tcn = 0x10, + .tctl_val = IMX1_TCTL_FRR | IMX1_TCTL_CLKSOURCE_PER | TCTL_TEN, +}; + +static struct imx_gpt_regs regs_imx31 = { + .tcn = 0x24, + .tctl_val = IMX31_TCTL_FRR | IMX31_TCTL_CLKSOURCE_PER | TCTL_TEN, +}; + +static struct imx_gpt_regs *regs; +static void __iomem *timer_base; + +static uint64_t imx_clocksource_read(void) +{ + return readl(timer_base + regs->tcn); +} + +static struct clocksource cs = { + .read = imx_clocksource_read, + .mask = CLOCKSOURCE_MASK(32), + .shift = 10, +}; + +static int imx_clocksource_clock_change(struct notifier_block *nb, unsigned long event, void *data) +{ + cs.mult = clocksource_hz2mult(clk_get_rate(clk_gpt), cs.shift); + return 0; +} + +static struct notifier_block imx_clock_notifier = { + .notifier_call = imx_clocksource_clock_change, +}; + +static int imx_gpt_probe(struct device_d *dev) +{ + struct resource *iores; + int i; + int ret; + unsigned long rate; + + /* one timer is enough */ + if (timer_base) + return 0; + + ret = dev_get_drvdata(dev, (const void **)®s); + if (ret) + return ret; + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + timer_base = IOMEM(iores->start); + + /* setup GP Timer 1 */ + writel(TCTL_SWR, timer_base + GPT_TCTL); + + for (i = 0; i < 100; i++) + writel(0, timer_base + GPT_TCTL); /* We have no udelay by now */ + + clk_gpt = clk_get(dev, "per"); + if (IS_ERR(clk_gpt)) { + rate = 20000000; + dev_err(dev, "failed to get clock, assume %lu Hz\n", rate); + } else { + rate = clk_get_rate(clk_gpt); + if (!rate) { + dev_err(dev, "clock reports rate == 0\n"); + return -EIO; + } + } + + writel(0, timer_base + GPT_TPRER); + writel(regs->tctl_val, timer_base + GPT_TCTL); + + cs.mult = clocksource_hz2mult(rate, cs.shift); + + init_clock(&cs); + + clock_register_client(&imx_clock_notifier); + + return 0; +} + +static __maybe_unused struct of_device_id imx_gpt_dt_ids[] = { + { + .compatible = "fsl,imx1-gpt", + .data = ®s_imx1, + }, { + .compatible = "fsl,imx21-gpt", + .data = ®s_imx1, + }, { + .compatible = "fsl,imx31-gpt", + .data = ®s_imx31, + }, { + .compatible = "fsl,imx6q-gpt", + .data = ®s_imx31, + }, { + .compatible = "fsl,imx6dl-gpt", + .data = ®s_imx31, + }, { + .compatible = "fsl,imx6ul-gpt", + .data = ®s_imx31, + }, { + .compatible = "fsl,imx7d-gpt", + .data = ®s_imx31, + }, { + /* sentinel */ + } +}; + +static struct platform_device_id imx_gpt_ids[] = { + { + .name = "imx1-gpt", + .driver_data = (unsigned long)®s_imx1, + }, { + .name = "imx31-gpt", + .driver_data = (unsigned long)®s_imx31, + }, { + /* sentinel */ + }, +}; + +static struct driver_d imx_gpt_driver = { + .name = "imx-gpt", + .probe = imx_gpt_probe, + .of_compatible = DRV_OF_COMPAT(imx_gpt_dt_ids), + .id_table = imx_gpt_ids, +}; + +static int imx_gpt_init(void) +{ + return platform_driver_register(&imx_gpt_driver); +} +postcore_initcall(imx_gpt_init); diff --git a/drivers/efi/efi-device.c b/drivers/efi/efi-device.c index 3a27323..b7dea7c 100644 --- a/drivers/efi/efi-device.c +++ b/drivers/efi/efi-device.c @@ -185,7 +185,7 @@ efidev->dev.info = efi_devinfo; efidev->devpath = devpath; - sprintf(efidev->dev.name, "handle-%p", handle); + dev_set_name(&efidev->dev, "handle-%p", handle); efidev->parent_handle = efi_find_parent(efidev->handle); diff --git a/drivers/firmware/socfpga.c b/drivers/firmware/socfpga.c index c1eae98..6d11da3 100644 --- a/drivers/firmware/socfpga.c +++ b/drivers/firmware/socfpga.c @@ -438,7 +438,7 @@ dev_dbg(dev, "Registering FPGA firmware programmer\n"); mgr->dev.id = DEVICE_ID_SINGLE; - strcpy(mgr->dev.name, "fpga"); + dev_set_name(&mgr->dev, "fpga"); mgr->dev.parent = dev; ret = register_device(&mgr->dev); if (ret) diff --git a/drivers/i2c/i2c.c b/drivers/i2c/i2c.c index 608f828..25e0fe7 100644 --- a/drivers/i2c/i2c.c +++ b/drivers/i2c/i2c.c @@ -389,7 +389,7 @@ int status; client = xzalloc(sizeof *client); - strcpy(client->dev.name, chip->type); + dev_set_name(&client->dev, chip->type); client->dev.type_data = client; client->dev.platform_data = chip->platform_data; client->dev.id = DEVICE_ID_DYNAMIC; @@ -588,7 +588,7 @@ } adapter->dev.id = adapter->nr; - strcpy(adapter->dev.name, "i2c"); + dev_set_name(&adapter->dev, "i2c"); ret = register_device(&adapter->dev); if (ret) diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c index c6b4e02..c8d1d5e 100644 --- a/drivers/mci/mci-core.c +++ b/drivers/mci/mci-core.c @@ -1802,10 +1802,10 @@ mci->host = host; if (host->devname) { - strcpy(mci->dev.name, host->devname); + dev_set_name(&mci->dev, host->devname); mci->dev.id = DEVICE_ID_SINGLE; } else { - strcpy(mci->dev.name, "mci"); + dev_set_name(&mci->dev, "mci"); mci->dev.id = DEVICE_ID_DYNAMIC; } diff --git a/drivers/mtd/core.c b/drivers/mtd/core.c index 15fe9ce..56e85b3 100644 --- a/drivers/mtd/core.c +++ b/drivers/mtd/core.c @@ -607,7 +607,7 @@ if (!devname) devname = "mtd"; - strcpy(mtd->class_dev.name, devname); + dev_set_name(&mtd->class_dev, devname); mtd->class_dev.id = device_id; if (mtd->parent) mtd->class_dev.parent = mtd->parent; diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c index 6269668..ed5377b 100644 --- a/drivers/mtd/spi-nor/cadence-quadspi.c +++ b/drivers/mtd/spi-nor/cadence-quadspi.c @@ -1051,7 +1051,7 @@ if (np) { nor->dev = xzalloc(sizeof(*nor->dev)); - strcpy(nor->dev->name, np->name); + dev_set_name(nor->dev, np->name); nor->dev->device_node = np; nor->dev->id = DEVICE_ID_SINGLE; diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 536077e..493c778 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -157,7 +157,7 @@ *ref = 0; sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num); - sprintf(ubi->dev.name, "%s.ubi", ubi->mtd->cdev.name); + dev_set_name(&ubi->dev, "%s.ubi", ubi->mtd->cdev.name); ubi->dev.id = DEVICE_ID_SINGLE; ubi->dev.parent = &ubi->mtd->class_dev; diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 6177048..99da791 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -145,7 +145,7 @@ vol->last_eb_bytes = vol->usable_leb_size; } - sprintf(vol->dev.name, "%s.%s", dev_name(&ubi->dev), vol->name); + dev_set_name(&vol->dev, "%s.%s", dev_name(&ubi->dev), vol->name); vol->dev.id = DEVICE_ID_SINGLE; vol->dev.parent = &ubi->dev; err = register_device(&vol->dev); @@ -443,7 +443,7 @@ dbg_gen("add volume"); - sprintf(vol->dev.name, "%s.%s", dev_name(&ubi->dev), vol->name); + dev_set_name(&vol->dev, "%s.%s", dev_name(&ubi->dev), vol->name); vol->dev.id = DEVICE_ID_SINGLE; vol->dev.parent = &ubi->dev; err = register_device(&vol->dev); diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index 3d3939c..c6fc21d 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -916,7 +916,7 @@ if (ret) goto err_out; - sprintf(dev->name, "cpsw-slave"); + dev_set_name(dev, "cpsw-slave"); dev->id = slave->slave_num; dev->parent = priv->dev; ret = register_device(dev); diff --git a/drivers/net/e1000/eeprom.c b/drivers/net/e1000/eeprom.c index dda022e..180b32e 100644 --- a/drivers/net/e1000/eeprom.c +++ b/drivers/net/e1000/eeprom.c @@ -1529,7 +1529,7 @@ if (ret < 0) return ret; - strcpy(hw->invm.dev.name, "invm"); + dev_set_name(&hw->invm.dev, "invm"); hw->invm.dev.id = hw->dev->id; hw->invm.dev.parent = hw->dev; ret = register_device(&hw->invm.dev); diff --git a/drivers/net/orion-gbe.c b/drivers/net/orion-gbe.c index e6bd757..431ec5c 100644 --- a/drivers/net/orion-gbe.c +++ b/drivers/net/orion-gbe.c @@ -462,7 +462,7 @@ reg |= RGMII_ENABLE; writel(reg, port->regs + PORT_SC1); - snprintf(dev->name, MAX_DRIVER_NAME, "%08x.ethernet-port", (u32)gbe->regs); + dev_set_name(dev, "%08x.ethernet-port", (u32)gbe->regs); dev->id = port->portno; dev->parent = parent; dev->device_node = port->np; diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 79fb917..3b1a6ea 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -48,6 +48,12 @@ ---help--- Currently supports the LAN83C185, LAN8187 and LAN8700 PHYs +config NET_DSA_MV88E6XXX + tristate "Marvell 88E6xxx Ethernet switch fabric support" + help + This driver adds support for most of the Marvell 88E6xxx models of + Ethernet switch chips, except 88E6060. + comment "MII bus device drivers" config MDIO_MVEBU diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 4424054..e4d9ec6 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_MICREL_PHY) += micrel.o obj-$(CONFIG_NATIONAL_PHY) += national.o obj-$(CONFIG_SMSC_PHY) += smsc.o +obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx/ obj-$(CONFIG_MDIO_MVEBU) += mdio-mvebu.o obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 7d53bcc..cda05af 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -26,6 +26,7 @@ #include #include #include +#include #define DEFAULT_GPIO_RESET_ASSERT 1000 /* us */ #define DEFAULT_GPIO_RESET_DEASSERT 1000 /* us */ @@ -179,8 +180,19 @@ /* Loop over the child nodes and register a phy_device for each one */ for_each_available_child_of_node(np, child) { - if (!of_mdiobus_child_is_phy(child)) + if (!of_mdiobus_child_is_phy(child)) { + if (of_get_property(child, "compatible", NULL)) { + if (!of_platform_device_create(child, + &mdio->dev)) { + dev_err(&mdio->dev, + "Failed to create device " + "for %s\n", + child->full_name); + } + } + continue; + } ret = of_property_read_u32(child, "reg", &addr); if (ret) { @@ -222,7 +234,7 @@ bus->dev.priv = bus; bus->dev.id = DEVICE_ID_DYNAMIC; - strcpy(bus->dev.name, "miibus"); + dev_set_name(&bus->dev, "miibus"); bus->dev.parent = bus->parent; bus->dev.detect = mdiobus_detect; @@ -335,16 +347,19 @@ * @dev: target PHY device * @drv: given PHY driver * - * Description: Given a PHY device, and a PHY driver, return 1 if - * the driver supports the device. Otherwise, return 0. + * Description: Given a PHY device, and a PHY driver, return 0 if + * the driver supports the device. Otherwise, return 1. */ static int mdio_bus_match(struct device_d *dev, struct driver_d *drv) { struct phy_device *phydev = to_phy_device(dev); struct phy_driver *phydrv = to_phy_driver(drv); - return ((phydrv->phy_id & phydrv->phy_id_mask) != - (phydev->phy_id & phydrv->phy_id_mask)); + if ((phydrv->phy_id & phydrv->phy_id_mask) == + (phydev->phy_id & phydrv->phy_id_mask)) + return 0; + + return 1; } static ssize_t phydev_read(struct cdev *cdev, void *_buf, size_t count, loff_t offset, ulong flags) diff --git a/drivers/net/phy/mv88e6xxx/Makefile b/drivers/net/phy/mv88e6xxx/Makefile new file mode 100644 index 0000000..e09ea0a --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/Makefile @@ -0,0 +1,5 @@ +obj-y += mv88e6xxx.o + +mv88e6xxx-objs := chip.o +mv88e6xxx-objs += global2.o +mv88e6xxx-objs += port.o diff --git a/drivers/net/phy/mv88e6xxx/chip.c b/drivers/net/phy/mv88e6xxx/chip.c new file mode 100644 index 0000000..ac08b5e --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/chip.c @@ -0,0 +1,913 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "chip.h" +#include "global2.h" +#include "port.h" + + +/* List of supported models */ +enum mv88e6xxx_model { + MV88E6085, + MV88E6095, + MV88E6097, + MV88E6123, + MV88E6131, + MV88E6141, + MV88E6161, + MV88E6165, + MV88E6171, + MV88E6172, + MV88E6175, + MV88E6176, + MV88E6185, + MV88E6190, + MV88E6190X, + MV88E6191, + MV88E6240, + MV88E6290, + MV88E6320, + MV88E6321, + MV88E6341, + MV88E6350, + MV88E6351, + MV88E6352, + MV88E6390, + MV88E6390X, +}; + +static const struct mv88e6xxx_ops mv88e6085_ops = { + /* MV88E6XXX_FAMILY_6097 */ + /* FIXME: Was not ported due to lack of HW */ + .phy_read = NULL, + .phy_write = NULL, +}; + +static const struct mv88e6xxx_ops mv88e6095_ops = { + /* MV88E6XXX_FAMILY_6095 */ + /* FIXME: Was not ported due to lack of HW */ + .phy_read = NULL, + .phy_write = NULL, +}; + +static const struct mv88e6xxx_ops mv88e6097_ops = { + /* MV88E6XXX_FAMILY_6097 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6123_ops = { + /* MV88E6XXX_FAMILY_6165 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6131_ops = { + /* MV88E6XXX_FAMILY_6185 */ + /* FIXME: Was not ported due to lack of HW */ + .phy_read = NULL, + .phy_write = NULL, +}; + +static const struct mv88e6xxx_ops mv88e6141_ops = { + /* MV88E6XXX_FAMILY_6341 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom8, + .set_eeprom = mv88e6xxx_g2_set_eeprom8, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6161_ops = { + /* MV88E6XXX_FAMILY_6165 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6165_ops = { + /* MV88E6XXX_FAMILY_6165 */ + /* FIXME: Was not ported due to lack of HW */ + .phy_read = NULL, + .phy_write = NULL, +}; + +static const struct mv88e6xxx_ops mv88e6171_ops = { + /* MV88E6XXX_FAMILY_6351 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6185_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6172_ops = { + /* MV88E6XXX_FAMILY_6352 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom16, + .set_eeprom = mv88e6xxx_g2_set_eeprom16, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6352_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6175_ops = { + /* MV88E6XXX_FAMILY_6351 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6185_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6176_ops = { + /* MV88E6XXX_FAMILY_6352 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom16, + .set_eeprom = mv88e6xxx_g2_set_eeprom16, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6352_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6185_ops = { + /* MV88E6XXX_FAMILY_6185 */ + /* FIXME: Was not ported due to lack of HW */ + .phy_read = NULL, + .phy_write = NULL, +}; + +static const struct mv88e6xxx_ops mv88e6190_ops = { + /* MV88E6XXX_FAMILY_6390 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom8, + .set_eeprom = mv88e6xxx_g2_set_eeprom8, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6190x_ops = { + /* MV88E6XXX_FAMILY_6390 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom8, + .set_eeprom = mv88e6xxx_g2_set_eeprom8, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390x_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6191_ops = { + /* MV88E6XXX_FAMILY_6390 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom8, + .set_eeprom = mv88e6xxx_g2_set_eeprom8, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6240_ops = { + /* MV88E6XXX_FAMILY_6352 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom16, + .set_eeprom = mv88e6xxx_g2_set_eeprom16, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6352_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6290_ops = { + /* MV88E6XXX_FAMILY_6390 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom8, + .set_eeprom = mv88e6xxx_g2_set_eeprom8, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390_port_set_speed, + .port_set_cmode = mv88e6390x_port_set_cmode, +}; + +static const struct mv88e6xxx_ops mv88e6320_ops = { + /* MV88E6XXX_FAMILY_6320 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom16, + .set_eeprom = mv88e6xxx_g2_set_eeprom16, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6321_ops = { + /* MV88E6XXX_FAMILY_6320 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom16, + .set_eeprom = mv88e6xxx_g2_set_eeprom16, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6341_ops = { + /* MV88E6XXX_FAMILY_6341 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom8, + .set_eeprom = mv88e6xxx_g2_set_eeprom8, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6350_ops = { + /* MV88E6XXX_FAMILY_6351 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6185_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6351_ops = { + /* MV88E6XXX_FAMILY_6351 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6185_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6352_ops = { + /* MV88E6XXX_FAMILY_6352 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .get_eeprom = mv88e6xxx_g2_get_eeprom16, + .set_eeprom = mv88e6xxx_g2_set_eeprom16, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6352_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6390_ops = { + /* MV88E6XXX_FAMILY_6390 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom8, + .set_eeprom = mv88e6xxx_g2_set_eeprom8, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390_port_set_speed, + .port_set_cmode = mv88e6390x_port_set_cmode, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6390x_ops = { + /* MV88E6XXX_FAMILY_6390 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom8, + .set_eeprom = mv88e6xxx_g2_set_eeprom8, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390x_port_set_speed, + .port_set_cmode = mv88e6390x_port_set_cmode, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_info mv88e6xxx_table[] = { + [MV88E6085] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6085, + .family = MV88E6XXX_FAMILY_6097, + .name = "Marvell 88E6085", + .num_ports = 10, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6085_ops, + }, + + [MV88E6095] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6095, + .family = MV88E6XXX_FAMILY_6095, + .name = "Marvell 88E6095/88E6095F", + .num_ports = 11, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6095_ops, + }, + + [MV88E6097] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6097, + .family = MV88E6XXX_FAMILY_6097, + .name = "Marvell 88E6097/88E6097F", + .num_ports = 11, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6097_ops, + }, + + [MV88E6123] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6123, + .family = MV88E6XXX_FAMILY_6165, + .name = "Marvell 88E6123", + .num_ports = 3, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6123_ops, + }, + + [MV88E6131] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6131, + .family = MV88E6XXX_FAMILY_6185, + .name = "Marvell 88E6131", + .num_ports = 8, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6131_ops, + }, + + [MV88E6141] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6141, + .family = MV88E6XXX_FAMILY_6341, + .name = "Marvell 88E6341", + .num_ports = 6, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6141_ops, + }, + + [MV88E6161] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6161, + .family = MV88E6XXX_FAMILY_6165, + .name = "Marvell 88E6161", + .num_ports = 6, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6161_ops, + }, + + [MV88E6165] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6165, + .family = MV88E6XXX_FAMILY_6165, + .name = "Marvell 88E6165", + .num_ports = 6, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6165_ops, + }, + + [MV88E6171] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6171, + .family = MV88E6XXX_FAMILY_6351, + .name = "Marvell 88E6171", + .num_ports = 7, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6171_ops, + }, + + [MV88E6172] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6172, + .family = MV88E6XXX_FAMILY_6352, + .name = "Marvell 88E6172", + .num_ports = 7, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6172_ops, + }, + + [MV88E6175] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6175, + .family = MV88E6XXX_FAMILY_6351, + .name = "Marvell 88E6175", + .num_ports = 7, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6175_ops, + }, + + [MV88E6176] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6176, + .family = MV88E6XXX_FAMILY_6352, + .name = "Marvell 88E6176", + .num_ports = 7, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6176_ops, + }, + + [MV88E6185] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6185, + .family = MV88E6XXX_FAMILY_6185, + .name = "Marvell 88E6185", + .num_ports = 10, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6185_ops, + }, + + [MV88E6190] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6190, + .family = MV88E6XXX_FAMILY_6390, + .name = "Marvell 88E6190", + .num_ports = 11, /* 10 + Z80 */ + .port_base_addr = 0x0, + .global2_addr = 0x1c, + .ops = &mv88e6190_ops, + }, + + [MV88E6190X] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6190X, + .family = MV88E6XXX_FAMILY_6390, + .name = "Marvell 88E6190X", + .num_ports = 11, /* 10 + Z80 */ + .port_base_addr = 0x0, + .global2_addr = 0x1c, + .ops = &mv88e6190x_ops, + }, + + [MV88E6191] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6191, + .family = MV88E6XXX_FAMILY_6390, + .name = "Marvell 88E6191", + .num_ports = 11, /* 10 + Z80 */ + .port_base_addr = 0x0, + .global2_addr = 0x1c, + .ops = &mv88e6191_ops, + }, + + [MV88E6240] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6240, + .family = MV88E6XXX_FAMILY_6352, + .name = "Marvell 88E6240", + .num_ports = 7, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6240_ops, + }, + + [MV88E6290] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6290, + .family = MV88E6XXX_FAMILY_6390, + .name = "Marvell 88E6290", + .num_ports = 11, /* 10 + Z80 */ + .port_base_addr = 0x0, + .global2_addr = 0x1c, + .ops = &mv88e6290_ops, + }, + + [MV88E6320] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6320, + .family = MV88E6XXX_FAMILY_6320, + .name = "Marvell 88E6320", + .num_ports = 7, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6320_ops, + }, + + [MV88E6321] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6321, + .family = MV88E6XXX_FAMILY_6320, + .name = "Marvell 88E6321", + .num_ports = 7, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6321_ops, + }, + + [MV88E6341] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6341, + .family = MV88E6XXX_FAMILY_6341, + .name = "Marvell 88E6341", + .num_ports = 6, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6341_ops, + }, + + [MV88E6350] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6350, + .family = MV88E6XXX_FAMILY_6351, + .name = "Marvell 88E6350", + .num_ports = 7, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6350_ops, + }, + + [MV88E6351] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6351, + .family = MV88E6XXX_FAMILY_6351, + .name = "Marvell 88E6351", + .num_ports = 7, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6351_ops, + }, + + [MV88E6352] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6352, + .family = MV88E6XXX_FAMILY_6352, + .name = "Marvell 88E6352", + .num_ports = 7, + .port_base_addr = 0x10, + .ops = &mv88e6352_ops, + .global2_addr = 0x1c, + .ops = &mv88e6352_ops, + }, + + [MV88E6390] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6390, + .family = MV88E6XXX_FAMILY_6390, + .name = "Marvell 88E6390", + .num_ports = 11, /* 10 + Z80 */ + .port_base_addr = 0x0, + .global2_addr = 0x1c, + .ops = &mv88e6390_ops, + }, + + [MV88E6390X] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6390X, + .family = MV88E6XXX_FAMILY_6390, + .name = "Marvell 88E6390X", + .num_ports = 11, /* 10 + Z80 */ + .port_base_addr = 0x0, + .global2_addr = 0x1c, + .ops = &mv88e6390x_ops, + }, +}; + +int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val) +{ + int ret; + ret = mdiobus_write(chip->parent_miibus, addr, reg, val); + if (ret < 0) + return ret; + + dev_dbg(chip->dev, "<- addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n", + addr, reg, val); + + return 0; +} + +int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val) +{ + int ret; + + ret = mdiobus_read(chip->parent_miibus, addr, reg); + if (ret < 0) + return ret; + + *val = ret & 0xffff; + + dev_dbg(chip->dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n", + addr, reg, *val); + + return 0; +} + +int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask) +{ + int i; + + for (i = 0; i < 16; i++) { + u16 val; + int err; + + err = mv88e6xxx_read(chip, addr, reg, &val); + if (err) + return err; + + if (!(val & mask)) + return 0; + + udelay(2000); + } + + dev_err(chip->dev, "Timeout while waiting for switch\n"); + return -ETIMEDOUT; +} + +static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg) +{ + struct mv88e6xxx_chip *chip = bus->priv; + u16 val; + int err; + + if (!chip->info->ops->phy_read) + return -EOPNOTSUPP; + + err = chip->info->ops->phy_read(chip, bus, phy, reg, &val); + + if (reg == MII_PHYSID2) { + /* Some internal PHYS don't have a model number. Use + * the mv88e6390 family model number instead. + */ + if (!(val & 0x3f0)) + val |= MV88E6XXX_PORT_SWITCH_ID_PROD_6390 >> 4; + } + + return err ?: val; +} + +static int mv88e6xxx_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val) +{ + struct mv88e6xxx_chip *chip = bus->priv; + int err; + + if (!chip->info->ops->phy_write) + return -EOPNOTSUPP; + + err = chip->info->ops->phy_write(chip, bus, phy, reg, val); + + return err; +} + +static const struct mv88e6xxx_info * +mv88e6xxx_lookup_info(unsigned int prod_num) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mv88e6xxx_table); ++i) + if (mv88e6xxx_table[i].prod_num == prod_num) + return &mv88e6xxx_table[i]; + + return NULL; +} + +static int mv88e6xxx_detect(struct mv88e6xxx_chip *chip) +{ + const struct mv88e6xxx_info *info; + unsigned int prod_num, rev; + u16 id; + int err; + + err = mv88e6xxx_port_read(chip, 0, MV88E6XXX_PORT_SWITCH_ID, &id); + if (err) + return err; + + prod_num = id & MV88E6XXX_PORT_SWITCH_ID_PROD_MASK; + rev = id & MV88E6XXX_PORT_SWITCH_ID_REV_MASK; + + info = mv88e6xxx_lookup_info(prod_num); + if (!info) + return -ENODEV; + + /* Update the compatible info with the probed one */ + chip->info = info; + + dev_info(chip->dev, "switch 0x%x detected: %s, revision %u\n", + chip->info->prod_num, chip->info->name, rev); + + return 0; +} + +/* + * Linux driver has this delay at 20ms, but it doesn't seem to be + * enough in Barebox and trying to access switch registers immediately + * after this function will return all F's on some platforms + * tested. Increasing this to 50ms seem to resolve the issue. + */ +static void mv88e6xxx_hardware_reset_delay(void) +{ + udelay(50000); +} + +static void mv88e6xxx_hardware_reset(struct mv88e6xxx_chip *chip) +{ + /* If there is a GPIO connected to the reset pin, toggle it */ + if (gpio_is_valid(chip->reset)) { + gpio_set_active(chip->reset, 1); + mv88e6xxx_hardware_reset_delay(); + gpio_set_active(chip->reset, 0); + mv88e6xxx_hardware_reset_delay(); + } +} + +static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip) +{ + mv88e6xxx_hardware_reset(chip); + return 0; +} + +static int mv88e6xxx_eeprom_read(struct device_d *dev, const int offset, + void *val, int bytes) +{ + struct mv88e6xxx_chip *chip = dev->parent->priv; + struct ethtool_eeprom eeprom = { + .offset = offset, + .len = bytes, + }; + + if (!chip->info->ops->get_eeprom) + return -ENOTSUPP; + + return chip->info->ops->get_eeprom(chip, &eeprom, val); +} + +static int mv88e6xxx_eeprom_write(struct device_d *dev, const int offset, + const void *val, int bytes) +{ + struct mv88e6xxx_chip *chip = dev->parent->priv; + struct ethtool_eeprom eeprom = { + .offset = offset, + .len = bytes, + }; + + if (!chip->info->ops->set_eeprom) + return -ENOTSUPP; + + return chip->info->ops->set_eeprom(chip, &eeprom, (void *)val); +} + +static const struct nvmem_bus mv88e6xxx_eeprom_nvmem_bus = { + .write = mv88e6xxx_eeprom_write, + .read = mv88e6xxx_eeprom_read, +}; + +static int mv88e6xxx_probe(struct device_d *dev) +{ + struct device_node *np = dev->device_node; + struct device_node *mdio_node; + struct mv88e6xxx_chip *chip; + enum of_gpio_flags of_flags; + u32 eeprom_len = 0; + int err; + u32 reg; + + err = of_property_read_u32(np, "reg", ®); + if (err) { + dev_err(dev, "Couldn't determine switch MIDO address\n"); + return err; + } + + if (reg) { + dev_err(dev, "Only single-chip address mode is supported\n"); + return -ENOTSUPP; + } + + chip = xzalloc(sizeof(struct mv88e6xxx_chip)); + chip->dev = dev; + dev->priv = chip; + chip->info = of_device_get_match_data(dev); + + of_property_read_u32(np, "eeprom-length", &eeprom_len); + + chip->parent_miibus = of_mdio_find_bus(np->parent); + if (!chip->parent_miibus) + return -EPROBE_DEFER; + + chip->reset = of_get_named_gpio_flags(np, "reset-gpios", 0, &of_flags); + if (gpio_is_valid(chip->reset)) { + unsigned long flags = GPIOF_OUT_INIT_INACTIVE; + char *name; + + if (of_flags & OF_GPIO_ACTIVE_LOW) + flags |= GPIOF_ACTIVE_LOW; + + name = basprintf("%s reset", dev_name(dev)); + err = gpio_request_one(chip->reset, flags, name); + if (err < 0) + return err; + /* + * We assume that reset line was previously held low + * and give the switch time to initialize before + * trying to read its registers + */ + mv88e6xxx_hardware_reset_delay(); + } + + err = mv88e6xxx_detect(chip); + if (err) + return err; + + err = mv88e6xxx_switch_reset(chip); + if (err) + return err; + + if (eeprom_len) { + struct nvmem_config config = { + .name = basprintf("%s-eeprom", dev_name(dev)), + .dev = dev, + .word_size = 1, + .stride = 1, + .size = eeprom_len, + .read_only = false, + .bus = &mv88e6xxx_eeprom_nvmem_bus, + }; + + if (IS_ERR(nvmem_register(&config))) + dev_err(dev, "Failed to register EEPROM\n"); + } + + /* + * In single-chip address mode addresses 0x10 - + * port_base_address are reserved to access various switch + * registers and do not correspond to any PHYs, so we mask + * them to pervent from being exposed. + */ + chip->parent_miibus->phy_mask |= GENMASK(0x1f, + chip->info->port_base_addr); + /* + * Mask all of the devices on child MDIO bus. Call to + * mv88e6xxx_port_probe() will unmask port that can be probed + * using standard methods + */ + chip->miibus.phy_mask |= GENMASK(0x1f, 0x00); + + chip->miibus.read = mv88e6xxx_mdio_read; + chip->miibus.write = mv88e6xxx_mdio_write; + + chip->miibus.priv = chip; + chip->miibus.parent = dev; + + mdio_node = of_get_child_by_name(np, "mdio"); + if (mdio_node) + chip->miibus.dev.device_node = mdio_node; + + err = mv88e6xxx_port_probe(chip); + if (err) + return err; + + return mdiobus_register(&chip->miibus); +} + +static const struct of_device_id mv88e6xxx_of_match[] = { + { + .compatible = "marvell,mv88e6085", + .data = &mv88e6xxx_table[MV88E6085], + }, + { + .compatible = "marvell,mv88e6190", + .data = &mv88e6xxx_table[MV88E6190], + }, + {}, +}; + +static struct driver_d mv88e6xxx_driver = { + .name = "mv88e6085", + .probe = mv88e6xxx_probe, + .of_compatible = mv88e6xxx_of_match, +}; +device_platform_driver(mv88e6xxx_driver); diff --git a/drivers/net/phy/mv88e6xxx/chip.h b/drivers/net/phy/mv88e6xxx/chip.h new file mode 100644 index 0000000..7548358 --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/chip.h @@ -0,0 +1,142 @@ +#ifndef _MV88E6XXX_CHIP_H +#define _MV88E6XXX_CHIP_H + +#include +#include +#include +#include + +/* sub-devices MDIO addresses */ +#define MV88E6XXX_SWITCH_GLOBAL_REGS_1 0x1b +#define MV88E6XXX_SWITCH_GLOBAL_REGS_2 0x1c + +enum mv88e6xxx_family { + MV88E6XXX_FAMILY_NONE, + MV88E6XXX_FAMILY_6065, /* 6031 6035 6061 6065 */ + MV88E6XXX_FAMILY_6095, /* 6092 6095 */ + MV88E6XXX_FAMILY_6097, /* 6046 6085 6096 6097 */ + MV88E6XXX_FAMILY_6165, /* 6123 6161 6165 */ + MV88E6XXX_FAMILY_6185, /* 6108 6121 6122 6131 6152 6155 6182 6185 */ + MV88E6XXX_FAMILY_6320, /* 6320 6321 */ + MV88E6XXX_FAMILY_6341, /* 6141 6341 */ + MV88E6XXX_FAMILY_6351, /* 6171 6175 6350 6351 */ + MV88E6XXX_FAMILY_6352, /* 6172 6176 6240 6352 */ + MV88E6XXX_FAMILY_6390, /* 6190 6190X 6191 6290 6390 6390X */ +}; + +struct mv88e6xxx_ops; + +#define DSA_MAX_PORTS 12 + +struct mv88e6xxx_info { + enum mv88e6xxx_family family; + u16 prod_num; + const char *name; + unsigned int num_ports; + unsigned int port_base_addr; + unsigned int global2_addr; + + const struct mv88e6xxx_ops *ops; +}; + +struct mv88e6xxx_port { + u8 cmode; +}; + +struct mv88e6xxx_chip { + const struct mv88e6xxx_info *info; + struct mii_bus *parent_miibus; + struct mii_bus miibus; + struct device_d *dev; + int reset; + + /* Array of port structures. */ + struct mv88e6xxx_port ports[DSA_MAX_PORTS]; +}; + +struct ethtool_eeprom { + __u32 offset; + __u32 len; +}; + +struct phylink_link_state { + phy_interface_t interface; + int speed; + int duplex; + int pause; + unsigned int link:1; + unsigned int an_enabled:1; + unsigned int an_complete:1; +}; + +struct mv88e6xxx_ops { + int (*phy_read)(struct mv88e6xxx_chip *chip, + struct mii_bus *bus, + int addr, int reg, u16 *val); + int (*phy_write)(struct mv88e6xxx_chip *chip, + struct mii_bus *bus, + int addr, int reg, u16 val); + + int (*get_eeprom)(struct mv88e6xxx_chip *chip, + struct ethtool_eeprom *eeprom, u8 *data); + int (*set_eeprom)(struct mv88e6xxx_chip *chip, + struct ethtool_eeprom *eeprom, u8 *data); + + /* RGMII Receive/Transmit Timing Control + * Add delay on PHY_INTERFACE_MODE_RGMII_*ID, no delay otherwise. + */ + int (*port_set_rgmii_delay)(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode); + +#define LINK_FORCED_DOWN 0 +#define LINK_FORCED_UP 1 +#define LINK_UNFORCED -2 + + /* Port's MAC link state + * Use LINK_FORCED_UP or LINK_FORCED_DOWN to force link up or down, + * or LINK_UNFORCED for normal link detection. + */ + int (*port_set_link)(struct mv88e6xxx_chip *chip, int port, int link); + +#define DUPLEX_UNFORCED -2 + + /* Port's MAC duplex mode + * + * Use DUPLEX_HALF or DUPLEX_FULL to force half or full duplex, + * or DUPLEX_UNFORCED for normal duplex detection. + */ + int (*port_set_duplex)(struct mv88e6xxx_chip *chip, int port, int dup); + +#define PAUSE_ON 1 +#define PAUSE_OFF 0 + + /* Enable/disable sending Pause */ + int (*port_set_pause)(struct mv88e6xxx_chip *chip, int port, + int pause); + +#define SPEED_MAX INT_MAX +#define SPEED_UNFORCED -2 + + /* Port's MAC speed (in Mbps) + * + * Depending on the chip, 10, 100, 200, 1000, 2500, 10000 are valid. + * Use SPEED_UNFORCED for normal detection, SPEED_MAX for max value. + */ + int (*port_set_speed)(struct mv88e6xxx_chip *chip, int port, int speed); + + /* CMODE control what PHY mode the MAC will use, eg. SGMII, RGMII, etc. + * Some chips allow this to be configured on specific ports. + */ + int (*port_set_cmode)(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode); + + /* Return the port link state, as required by phylink */ + int (*port_link_state)(struct mv88e6xxx_chip *chip, int port, + struct phylink_link_state *state); +}; + +int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val); +int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val); +int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask); + +#endif /* _MV88E6XXX_CHIP_H */ diff --git a/drivers/net/phy/mv88e6xxx/global2.c b/drivers/net/phy/mv88e6xxx/global2.c new file mode 100644 index 0000000..970a729 --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/global2.c @@ -0,0 +1,389 @@ +#include +#include + +#include "global2.h" + +int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val) +{ + return mv88e6xxx_read(chip, chip->info->global2_addr, reg, val); +} + +int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val) +{ + return mv88e6xxx_write(chip, chip->info->global2_addr, reg, val); +} + +int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask) +{ + return mv88e6xxx_wait(chip, chip->info->global2_addr, reg, mask); +} + +/* Offset 0x14: EEPROM Command + * Offset 0x15: EEPROM Data (for 16-bit data access) + * Offset 0x15: EEPROM Addr (for 8-bit data access) + */ + +static int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip) +{ + return mv88e6xxx_g2_wait(chip, MV88E6XXX_G2_EEPROM_CMD, + MV88E6XXX_G2_EEPROM_CMD_BUSY | + MV88E6XXX_G2_EEPROM_CMD_RUNNING); +} + +static int mv88e6xxx_g2_eeprom_cmd(struct mv88e6xxx_chip *chip, u16 cmd) +{ + int err; + + err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_EEPROM_CMD, + MV88E6XXX_G2_EEPROM_CMD_BUSY | cmd); + if (err) + return err; + + return mv88e6xxx_g2_eeprom_wait(chip); +} + +static int mv88e6xxx_g2_eeprom_read8(struct mv88e6xxx_chip *chip, + u16 addr, u8 *data) +{ + u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_READ; + int err; + + err = mv88e6xxx_g2_eeprom_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g2_write(chip, MV88E6390_G2_EEPROM_ADDR, addr); + if (err) + return err; + + err = mv88e6xxx_g2_eeprom_cmd(chip, cmd); + if (err) + return err; + + err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_EEPROM_CMD, &cmd); + if (err) + return err; + + *data = cmd & 0xff; + + return 0; +} + +static int mv88e6xxx_g2_eeprom_write8(struct mv88e6xxx_chip *chip, + u16 addr, u8 data) +{ + u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_WRITE | + MV88E6XXX_G2_EEPROM_CMD_WRITE_EN; + int err; + + err = mv88e6xxx_g2_eeprom_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g2_write(chip, MV88E6390_G2_EEPROM_ADDR, addr); + if (err) + return err; + + return mv88e6xxx_g2_eeprom_cmd(chip, cmd | data); +} + +static int mv88e6xxx_g2_eeprom_read16(struct mv88e6xxx_chip *chip, + u8 addr, u16 *data) +{ + u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_READ | addr; + int err; + + err = mv88e6xxx_g2_eeprom_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g2_eeprom_cmd(chip, cmd); + if (err) + return err; + + return mv88e6xxx_g2_read(chip, MV88E6352_G2_EEPROM_DATA, data); +} + +static int mv88e6xxx_g2_eeprom_write16(struct mv88e6xxx_chip *chip, + u8 addr, u16 data) +{ + u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_WRITE | addr; + int err; + + err = mv88e6xxx_g2_eeprom_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g2_write(chip, MV88E6352_G2_EEPROM_DATA, data); + if (err) + return err; + + return mv88e6xxx_g2_eeprom_cmd(chip, cmd); +} + +int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip, + struct ethtool_eeprom *eeprom, u8 *data) +{ + unsigned int offset = eeprom->offset; + unsigned int len = eeprom->len; + int err; + + eeprom->len = 0; + + while (len) { + err = mv88e6xxx_g2_eeprom_read8(chip, offset, data); + if (err) + return err; + + eeprom->len++; + offset++; + data++; + len--; + } + + return 0; +} + +int mv88e6xxx_g2_set_eeprom8(struct mv88e6xxx_chip *chip, + struct ethtool_eeprom *eeprom, u8 *data) +{ + unsigned int offset = eeprom->offset; + unsigned int len = eeprom->len; + int err; + + eeprom->len = 0; + + while (len) { + err = mv88e6xxx_g2_eeprom_write8(chip, offset, *data); + if (err) + return err; + + eeprom->len++; + offset++; + data++; + len--; + } + + return 0; +} + +int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip, + struct ethtool_eeprom *eeprom, u8 *data) +{ + unsigned int offset = eeprom->offset; + unsigned int len = eeprom->len; + u16 val; + int err; + + eeprom->len = 0; + + if (offset & 1) { + err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); + if (err) + return err; + + *data++ = (val >> 8) & 0xff; + + offset++; + len--; + eeprom->len++; + } + + while (len >= 2) { + err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); + if (err) + return err; + + *data++ = val & 0xff; + *data++ = (val >> 8) & 0xff; + + offset += 2; + len -= 2; + eeprom->len += 2; + } + + if (len) { + err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); + if (err) + return err; + + *data++ = val & 0xff; + + offset++; + len--; + eeprom->len++; + } + + return 0; +} + +int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip, + struct ethtool_eeprom *eeprom, u8 *data) +{ + unsigned int offset = eeprom->offset; + unsigned int len = eeprom->len; + u16 val; + int err; + + /* Ensure the RO WriteEn bit is set */ + err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_EEPROM_CMD, &val); + if (err) + return err; + + if (!(val & MV88E6XXX_G2_EEPROM_CMD_WRITE_EN)) + return -EROFS; + + eeprom->len = 0; + + if (offset & 1) { + err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); + if (err) + return err; + + val = (*data++ << 8) | (val & 0xff); + + err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val); + if (err) + return err; + + offset++; + len--; + eeprom->len++; + } + + while (len >= 2) { + val = *data++; + val |= *data++ << 8; + + err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val); + if (err) + return err; + + offset += 2; + len -= 2; + eeprom->len += 2; + } + + if (len) { + err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); + if (err) + return err; + + val = (val & 0xff00) | *data++; + + err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val); + if (err) + return err; + + offset++; + len--; + eeprom->len++; + } + + return 0; +} + +static int mv88e6xxx_g2_smi_phy_wait(struct mv88e6xxx_chip *chip) +{ + return mv88e6xxx_g2_wait(chip, MV88E6XXX_G2_SMI_PHY_CMD, + MV88E6XXX_G2_SMI_PHY_CMD_BUSY); +} + +static int mv88e6xxx_g2_smi_phy_cmd(struct mv88e6xxx_chip *chip, u16 cmd) +{ + int err; + + err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_CMD, + MV88E6XXX_G2_SMI_PHY_CMD_BUSY | cmd); + if (err) + return err; + + return mv88e6xxx_g2_smi_phy_wait(chip); +} + +static int mv88e6xxx_g2_smi_phy_access(struct mv88e6xxx_chip *chip, + bool external, bool c45, u16 op, int dev, + int reg) +{ + u16 cmd = op; + + if (external) + cmd |= MV88E6390_G2_SMI_PHY_CMD_FUNC_EXTERNAL; + else + cmd |= MV88E6390_G2_SMI_PHY_CMD_FUNC_INTERNAL; /* empty mask */ + + if (c45) + cmd |= MV88E6XXX_G2_SMI_PHY_CMD_MODE_45; /* empty mask */ + else + cmd |= MV88E6XXX_G2_SMI_PHY_CMD_MODE_22; + + dev <<= __bf_shf(MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK); + cmd |= dev & MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK; + cmd |= reg & MV88E6XXX_G2_SMI_PHY_CMD_REG_ADDR_MASK; + + return mv88e6xxx_g2_smi_phy_cmd(chip, cmd); +} + +static int mv88e6xxx_g2_smi_phy_access_c22(struct mv88e6xxx_chip *chip, + bool external, u16 op, int dev, + int reg) +{ + return mv88e6xxx_g2_smi_phy_access(chip, external, false, op, dev, reg); +} + +/* IEEE 802.3 Clause 22 Read Data Register */ +static int mv88e6xxx_g2_smi_phy_read_data_c22(struct mv88e6xxx_chip *chip, + bool external, int dev, int reg, + u16 *data) +{ + u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_22_READ_DATA; + int err; + + err = mv88e6xxx_g2_smi_phy_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g2_smi_phy_access_c22(chip, external, op, dev, reg); + if (err) + return err; + + return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SMI_PHY_DATA, data); +} + +/* IEEE 802.3 Clause 22 Write Data Register */ +static int mv88e6xxx_g2_smi_phy_write_data_c22(struct mv88e6xxx_chip *chip, + bool external, int dev, int reg, + u16 data) +{ + u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_22_WRITE_DATA; + int err; + + err = mv88e6xxx_g2_smi_phy_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, data); + if (err) + return err; + + return mv88e6xxx_g2_smi_phy_access_c22(chip, external, op, dev, reg); +} + + +int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus, + int addr, int reg, u16 *val) +{ + bool external = false; + + return mv88e6xxx_g2_smi_phy_read_data_c22(chip, external, addr, reg, + val); +} + +int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus, + int addr, int reg, u16 val) +{ + bool external = false; + + return mv88e6xxx_g2_smi_phy_write_data_c22(chip, external, addr, reg, + val); +} diff --git a/drivers/net/phy/mv88e6xxx/global2.h b/drivers/net/phy/mv88e6xxx/global2.h new file mode 100644 index 0000000..4e23b04 --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/global2.h @@ -0,0 +1,70 @@ +#ifndef _MV88E6XXX_GLOBAL2_H +#define _MV88E6XXX_GLOBAL2_H + +#include "chip.h" + +/* Offset 0x18: SMI PHY Command Register */ +#define MV88E6XXX_G2_SMI_PHY_CMD 0x18 +#define MV88E6XXX_G2_SMI_PHY_CMD_BUSY 0x8000 +#define MV88E6390_G2_SMI_PHY_CMD_FUNC_MASK 0x6000 +#define MV88E6390_G2_SMI_PHY_CMD_FUNC_INTERNAL 0x0000 +#define MV88E6390_G2_SMI_PHY_CMD_FUNC_EXTERNAL 0x2000 +#define MV88E6390_G2_SMI_PHY_CMD_FUNC_SETUP 0x4000 +#define MV88E6XXX_G2_SMI_PHY_CMD_MODE_MASK 0x1000 +#define MV88E6XXX_G2_SMI_PHY_CMD_MODE_45 0x0000 +#define MV88E6XXX_G2_SMI_PHY_CMD_MODE_22 0x1000 +#define MV88E6XXX_G2_SMI_PHY_CMD_OP_MASK 0x0c00 +#define MV88E6XXX_G2_SMI_PHY_CMD_OP_22_WRITE_DATA 0x0400 +#define MV88E6XXX_G2_SMI_PHY_CMD_OP_22_READ_DATA 0x0800 +#define MV88E6XXX_G2_SMI_PHY_CMD_OP_45_WRITE_ADDR 0x0000 +#define MV88E6XXX_G2_SMI_PHY_CMD_OP_45_WRITE_DATA 0x0400 +#define MV88E6XXX_G2_SMI_PHY_CMD_OP_45_READ_DATA_INC 0x0800 +#define MV88E6XXX_G2_SMI_PHY_CMD_OP_45_READ_DATA 0x0c00 +#define MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK 0x03e0 +#define MV88E6XXX_G2_SMI_PHY_CMD_REG_ADDR_MASK 0x001f +#define MV88E6XXX_G2_SMI_PHY_CMD_SETUP_PTR_MASK 0x03ff + +/* Offset 0x19: SMI PHY Data Register */ +#define MV88E6XXX_G2_SMI_PHY_DATA 0x19 + +/* Offset 0x14: EEPROM Command */ +#define MV88E6XXX_G2_EEPROM_CMD 0x14 +#define MV88E6XXX_G2_EEPROM_CMD_BUSY 0x8000 +#define MV88E6XXX_G2_EEPROM_CMD_OP_MASK 0x7000 +#define MV88E6XXX_G2_EEPROM_CMD_OP_WRITE 0x3000 +#define MV88E6XXX_G2_EEPROM_CMD_OP_READ 0x4000 +#define MV88E6XXX_G2_EEPROM_CMD_OP_LOAD 0x6000 +#define MV88E6XXX_G2_EEPROM_CMD_RUNNING 0x0800 +#define MV88E6XXX_G2_EEPROM_CMD_WRITE_EN 0x0400 +#define MV88E6352_G2_EEPROM_CMD_ADDR_MASK 0x00ff +#define MV88E6390_G2_EEPROM_CMD_DATA_MASK 0x00ff + +/* Offset 0x15: EEPROM Data */ +#define MV88E6352_G2_EEPROM_DATA 0x15 +#define MV88E6352_G2_EEPROM_DATA_MASK 0xffff + +/* Offset 0x15: EEPROM Addr */ +#define MV88E6390_G2_EEPROM_ADDR 0x15 +#define MV88E6390_G2_EEPROM_ADDR_MASK 0xffff + +int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val); +int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val); +int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask); + +int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, + struct mii_bus *bus, + int addr, int reg, u16 *val); +int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, + struct mii_bus *bus, + int addr, int reg, u16 val); + +int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip, + struct ethtool_eeprom *eeprom, u8 *data); +int mv88e6xxx_g2_set_eeprom8(struct mv88e6xxx_chip *chip, + struct ethtool_eeprom *eeprom, u8 *data); +int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip, + struct ethtool_eeprom *eeprom, u8 *data); +int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip, + struct ethtool_eeprom *eeprom, u8 *data); + +#endif /* _MV88E6XXX_GLOBAL2_H */ diff --git a/drivers/net/phy/mv88e6xxx/port.c b/drivers/net/phy/mv88e6xxx/port.c new file mode 100644 index 0000000..52f95d6 --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/port.c @@ -0,0 +1,663 @@ +#include +#include + +#include +#include + +#include "port.h" + +int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, + u16 *val) +{ + int addr = chip->info->port_base_addr + port; + + return mv88e6xxx_read(chip, addr, reg, val); +} + +int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, + u16 val) +{ + int addr = chip->info->port_base_addr + port; + + return mv88e6xxx_write(chip, addr, reg, val); +} + +/* Offset 0x00: MAC (or PCS or Physical) Status Register + * + * For most devices, this is read only. However the 6185 has the MyPause + * bit read/write. + */ +int mv88e6185_port_set_pause(struct mv88e6xxx_chip *chip, int port, + int pause) +{ + u16 reg; + int err; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); + if (err) + return err; + + if (pause) + reg |= MV88E6XXX_PORT_STS_MY_PAUSE; + else + reg &= ~MV88E6XXX_PORT_STS_MY_PAUSE; + + return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_STS, reg); +} + +/* Offset 0x01: MAC (or PCS or Physical) Control Register + * + * Link, Duplex and Flow Control have one force bit, one value bit. + * + * For port's MAC speed, ForceSpd (or SpdValue) bits 1:0 program the value. + * Alternative values require the 200BASE (or AltSpeed) bit 12 set. + * Newer chips need a ForcedSpd bit 13 set to consider the value. + */ + +static int mv88e6xxx_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode) +{ + u16 reg; + int err; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®); + if (err) + return err; + + reg &= ~(MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK | + MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK); + + switch (mode) { + case PHY_INTERFACE_MODE_RGMII_RXID: + reg |= MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + reg |= MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK; + break; + case PHY_INTERFACE_MODE_RGMII_ID: + reg |= MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK | + MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK; + break; + case PHY_INTERFACE_MODE_RGMII: + break; + default: + return 0; + } + + err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg); + if (err) + return err; + + dev_dbg(chip->dev, "p%d: delay RXCLK %s, TXCLK %s\n", port, + reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK ? "yes" : "no", + reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK ? "yes" : "no"); + + return 0; +} + +int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode) +{ + if (port < 5) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_rgmii_delay(chip, port, mode); +} + +int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode) +{ + if (port != 0) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_rgmii_delay(chip, port, mode); +} + +int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link) +{ + u16 reg; + int err; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®); + if (err) + return err; + + reg &= ~(MV88E6XXX_PORT_MAC_CTL_FORCE_LINK | + MV88E6XXX_PORT_MAC_CTL_LINK_UP); + + switch (link) { + case LINK_FORCED_DOWN: + reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_LINK; + break; + case LINK_FORCED_UP: + reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_LINK | + MV88E6XXX_PORT_MAC_CTL_LINK_UP; + break; + case LINK_UNFORCED: + /* normal link detection */ + break; + default: + return -EINVAL; + } + + err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg); + if (err) + return err; + + dev_dbg(chip->dev, "p%d: %s link %s\n", port, + reg & MV88E6XXX_PORT_MAC_CTL_FORCE_LINK ? "Force" : "Unforce", + reg & MV88E6XXX_PORT_MAC_CTL_LINK_UP ? "up" : "down"); + + return 0; +} + +int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup) +{ + u16 reg; + int err; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®); + if (err) + return err; + + reg &= ~(MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX | + MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL); + + switch (dup) { + case DUPLEX_HALF: + reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX; + break; + case DUPLEX_FULL: + reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX | + MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL; + break; + case DUPLEX_UNFORCED: + /* normal duplex detection */ + break; + default: + return -EINVAL; + } + + err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg); + if (err) + return err; + + dev_dbg(chip->dev, "p%d: %s %s duplex\n", port, + reg & MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX ? "Force" : "Unforce", + reg & MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL ? "full" : "half"); + + return 0; +} + +static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port, + int speed, bool alt_bit, bool force_bit) +{ + u16 reg, ctrl; + int err; + + switch (speed) { + case 10: + ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_10; + break; + case 100: + ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100; + break; + case 200: + if (alt_bit) + ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100 | + MV88E6390_PORT_MAC_CTL_ALTSPEED; + else + ctrl = MV88E6065_PORT_MAC_CTL_SPEED_200; + break; + case 1000: + ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_1000; + break; + case 2500: + ctrl = MV88E6390_PORT_MAC_CTL_SPEED_10000 | + MV88E6390_PORT_MAC_CTL_ALTSPEED; + break; + case 10000: + /* all bits set, fall through... */ + case SPEED_UNFORCED: + ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_UNFORCED; + break; + default: + return -EOPNOTSUPP; + } + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®); + if (err) + return err; + + reg &= ~MV88E6XXX_PORT_MAC_CTL_SPEED_MASK; + if (alt_bit) + reg &= ~MV88E6390_PORT_MAC_CTL_ALTSPEED; + if (force_bit) { + reg &= ~MV88E6390_PORT_MAC_CTL_FORCE_SPEED; + if (speed != SPEED_UNFORCED) + ctrl |= MV88E6390_PORT_MAC_CTL_FORCE_SPEED; + } + reg |= ctrl; + + err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg); + if (err) + return err; + + if (speed) + dev_dbg(chip->dev, "p%d: Speed set to %d Mbps\n", port, speed); + else + dev_dbg(chip->dev, "p%d: Speed unforced\n", port); + + return 0; +} + +/* Support 10, 100, 200 Mbps (e.g. 88E6065 family) */ +int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ + if (speed == SPEED_MAX) + speed = 200; + + if (speed > 200) + return -EOPNOTSUPP; + + /* Setting 200 Mbps on port 0 to 3 selects 100 Mbps */ + return mv88e6xxx_port_set_speed(chip, port, speed, false, false); +} + +/* Support 10, 100, 1000 Mbps (e.g. 88E6185 family) */ +int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ + if (speed == SPEED_MAX) + speed = 1000; + + if (speed == 200 || speed > 1000) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_speed(chip, port, speed, false, false); +} + +/* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */ +int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ + if (speed == SPEED_MAX) + speed = 1000; + + if (speed > 1000) + return -EOPNOTSUPP; + + if (speed == 200 && port < 5) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_speed(chip, port, speed, true, false); +} + +/* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6390) */ +int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ + if (speed == SPEED_MAX) + speed = port < 9 ? 1000 : 2500; + + if (speed > 2500) + return -EOPNOTSUPP; + + if (speed == 200 && port != 0) + return -EOPNOTSUPP; + + if (speed == 2500 && port < 9) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_speed(chip, port, speed, true, true); +} + +/* Support 10, 100, 200, 1000, 2500, 10000 Mbps (e.g. 88E6190X) */ +int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ + if (speed == SPEED_MAX) + speed = port < 9 ? 1000 : 10000; + + if (speed == 200 && port != 0) + return -EOPNOTSUPP; + + if (speed >= 2500 && port < 9) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_speed(chip, port, speed, true, true); +} + +int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode) +{ + u16 cmode; + + if (mode == PHY_INTERFACE_MODE_NA) + return 0; + + if (port != 9 && port != 10) + return -EOPNOTSUPP; + + switch (mode) { + case PHY_INTERFACE_MODE_1000BASEX: + cmode = MV88E6XXX_PORT_STS_CMODE_1000BASE_X; + break; + case PHY_INTERFACE_MODE_SGMII: + cmode = MV88E6XXX_PORT_STS_CMODE_SGMII; + break; + case PHY_INTERFACE_MODE_2500BASEX: + cmode = MV88E6XXX_PORT_STS_CMODE_2500BASEX; + break; + case PHY_INTERFACE_MODE_XGMII: + case PHY_INTERFACE_MODE_XAUI: + cmode = MV88E6XXX_PORT_STS_CMODE_XAUI; + break; + case PHY_INTERFACE_MODE_RXAUI: + cmode = MV88E6XXX_PORT_STS_CMODE_RXAUI; + break; + default: + cmode = 0; + } + + chip->ports[port].cmode = cmode; + + return 0; +} + +int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port, + struct phylink_link_state *state) +{ + int err; + u16 reg; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); + if (err) + return err; + + switch (reg & MV88E6XXX_PORT_STS_SPEED_MASK) { + case MV88E6XXX_PORT_STS_SPEED_10: + state->speed = SPEED_10; + break; + case MV88E6XXX_PORT_STS_SPEED_100: + state->speed = SPEED_100; + break; + case MV88E6XXX_PORT_STS_SPEED_1000: + state->speed = SPEED_1000; + break; + case MV88E6XXX_PORT_STS_SPEED_10000: + if ((reg & MV88E6XXX_PORT_STS_CMODE_MASK) == + MV88E6XXX_PORT_STS_CMODE_2500BASEX) + state->speed = SPEED_2500; + else + state->speed = SPEED_10000; + break; + } + + state->duplex = reg & MV88E6XXX_PORT_STS_DUPLEX ? + DUPLEX_FULL : DUPLEX_HALF; + state->link = !!(reg & MV88E6XXX_PORT_STS_LINK); + state->an_enabled = 1; + state->an_complete = state->link; + + return 0; +} + +int mv88e6185_port_link_state(struct mv88e6xxx_chip *chip, int port, + struct phylink_link_state *state) +{ + if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { + u8 cmode = chip->ports[port].cmode; + + /* When a port is in "Cross-chip serdes" mode, it uses + * 1000Base-X full duplex mode, but there is no automatic + * link detection. Use the sync OK status for link (as it + * would do for 1000Base-X mode.) + */ + if (cmode == MV88E6185_PORT_STS_CMODE_SERDES) { + u16 mac; + int err; + + err = mv88e6xxx_port_read(chip, port, + MV88E6XXX_PORT_MAC_CTL, &mac); + if (err) + return err; + + state->link = !!(mac & MV88E6185_PORT_MAC_CTL_SYNC_OK); + state->an_enabled = 1; + state->an_complete = + !!(mac & MV88E6185_PORT_MAC_CTL_AN_DONE); + state->duplex = + state->link ? DUPLEX_FULL : DUPLEX_UNKNOWN; + state->speed = + state->link ? SPEED_1000 : SPEED_UNKNOWN; + + return 0; + } + } + + return mv88e6352_port_link_state(chip, port, state); +} + + +static int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, + int link, int speed, int duplex, int pause, + phy_interface_t mode) +{ + int err; + + if (!chip->info->ops->port_set_link) + return 0; + + /* Port's MAC control must not be changed unless the link is down */ + err = chip->info->ops->port_set_link(chip, port, 0); + if (err) + return err; + + if (chip->info->ops->port_set_speed) { + err = chip->info->ops->port_set_speed(chip, port, speed); + if (err && err != -EOPNOTSUPP) + goto restore_link; + } + + if (chip->info->ops->port_set_pause) { + err = chip->info->ops->port_set_pause(chip, port, pause); + if (err) + goto restore_link; + + } + + if (chip->info->ops->port_set_duplex) { + err = chip->info->ops->port_set_duplex(chip, port, duplex); + if (err && err != -EOPNOTSUPP) + goto restore_link; + } + + if (chip->info->ops->port_set_rgmii_delay) { + err = chip->info->ops->port_set_rgmii_delay(chip, port, mode); + if (err && err != -EOPNOTSUPP) + goto restore_link; + } + + if (chip->info->ops->port_set_cmode) { + err = chip->info->ops->port_set_cmode(chip, port, mode); + if (err && err != -EOPNOTSUPP) + goto restore_link; + } + + err = 0; +restore_link: + if (chip->info->ops->port_set_link(chip, port, link)) + dev_err(chip->dev, "p%d: failed to restore MAC's link\n", port); + + return err; +} + +static int mv88e6xxx_port_config_init(struct phy_device *phydev) +{ + struct mv88e6xxx_chip *chip = phydev->dev.priv; + int port = phydev->addr - chip->info->port_base_addr; + int err; + + err = mv88e6xxx_port_setup_mac(chip, port, phydev->link, phydev->speed, + phydev->duplex, phydev->pause, + phydev->interface); + + if (err && err != -EOPNOTSUPP) + dev_err(&phydev->dev, "p%d: failed to configure MAC\n", port); + + return err; +} + +static int mv88e6xxx_port_config_aneg(struct phy_device *phydev) +{ + return 0; +} + +static int mv88e6xxx_port_read_status(struct phy_device *phydev) +{ + struct mv88e6xxx_chip *chip = phydev->dev.priv; + int port = phydev->addr - chip->info->port_base_addr; + struct phylink_link_state state; + int err; + + err = mv88e6352_port_link_state(chip, port, &state); + if (err) + return err; + + phydev->link = state.link; + phydev->duplex = state.duplex; + phydev->speed = state.speed; + + phydev->pause = phydev->asym_pause = 0; + + return 0; +} + +/* + * Fake switch PHY_ID used to match this driver against devices + * create in mv88e6xxx_port_probe. + */ +#define MV88E6XXX_SWITCH_PORT_PHY_ID (0x01410000 | \ + MV88E6XXX_PORT_SWITCH_ID_PROD_6085) + +static struct phy_driver mv88e6xxx_port_driver = { + .phy_id = MV88E6XXX_SWITCH_PORT_PHY_ID, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .drv.name = "Marvel 88E6xxx Port", + .features = PHY_GBIT_FEATURES & ~SUPPORTED_Autoneg, + .config_init = mv88e6xxx_port_config_init, + .config_aneg = mv88e6xxx_port_config_aneg, + .read_status = mv88e6xxx_port_read_status, +}; + +static int __init mv88e6xxx_port_driver_register(void) +{ + return phy_driver_register(&mv88e6xxx_port_driver); +} +fs_initcall(mv88e6xxx_port_driver_register); + +int mv88e6xxx_port_probe(struct mv88e6xxx_chip *chip) +{ + struct device_d *dev = chip->dev; + struct device_node *np = dev->device_node; + struct device_node *port_node, *switch_node; + struct device_node *port_nodes[DSA_MAX_PORTS] = { NULL }; + int err, i; + + switch_node = of_find_node_by_name(np, "ports"); + if (!switch_node) + return -EINVAL; + + for_each_available_child_of_node(switch_node, port_node) { + u32 nr; + + err = of_property_read_u32(port_node, "reg", &nr); + if (err) { + dev_err(dev, + "Error: Failed to find reg for child %s\n", + port_node->full_name); + continue; + } + + port_nodes[nr] = port_node; + } + + /* + * Walk through all of the ports and unmask those that are + * connected to a PHY via child MDIO bus, so the can be picked + * up via regular PHY discover. + * + * While at it, also create PHY objects for ports that are + * not, so they can be correctly configured + */ + for (i = 0; i < chip->info->num_ports; i++) { + struct phy_device *phydev; + u16 status; + bool force_mac = false; + int addr = i; + + err = mv88e6xxx_port_read(chip, i, MV88E6XXX_PORT_STS, + &status); + if (err) + return err; + + /* + * FIXME: This most likely affects more than 6161, so + * this will have to be expanded to more chips + */ + if (chip->info->prod_num == + MV88E6XXX_PORT_SWITCH_ID_PROD_6161) { + const uint8_t cmode = + FIELD_GET(MV88E6185_PORT_STS_CMODE_MASK, + status); + switch (i) { + case 4: + case 5: /* FALLTHROUGH */ + if (cmode == MV88E6165_PORT_STS_CMODE_SGMII) { + /* + * Port is configured to SGMII + * bypassing SERDES/PHY + */ + force_mac = true; + break; + } + + if (cmode != MV88E6165_PORT_STS_CMODE_PHY) { + /* + * If port is configured to + * SERDES we need to adjust + * its MDIO address + */ + addr += MV88E6165_PORT_SERDES_OFFSET; + } + + break; + } + } + + if (status & MV88E6XXX_PORT_STS_PHY_DETECT && !force_mac) { + /* + * True PHYs will be automaticall handled by + * generic PHY driver, so we ignore those. + */ + chip->miibus.phy_mask &= ~BIT(addr); + continue; + } + + /* + * In order to expose MAC-only ports on the switch, so + * they can be properly configured to match other + * end's settings, we create pseudo PHY devices that + * will match against our special PHY driver + */ + phydev = phy_device_create(chip->parent_miibus, + chip->info->port_base_addr + i, + MV88E6XXX_SWITCH_PORT_PHY_ID); + phydev->dev.device_node = port_nodes[i]; + phydev->dev.priv = chip; + phydev->duplex = DUPLEX_UNFORCED; + + err = phy_register_device(phydev); + if (err) + dev_err(dev, "Error: Failed to register a PHY\n"); + } + + return 0; +} \ No newline at end of file diff --git a/drivers/net/phy/mv88e6xxx/port.h b/drivers/net/phy/mv88e6xxx/port.h new file mode 100644 index 0000000..07d937e --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/port.h @@ -0,0 +1,137 @@ +#ifndef _MV88E6XXX_PORT_H +#define _MV88E6XXX_PORT_H + +#include "chip.h" + +/* Offset 0x00: Port Status Register */ +#define MV88E6XXX_PORT_STS 0x00 +#define MV88E6XXX_PORT_STS_PAUSE_EN 0x8000 +#define MV88E6XXX_PORT_STS_MY_PAUSE 0x4000 +#define MV88E6XXX_PORT_STS_HD_FLOW 0x2000 +#define MV88E6XXX_PORT_STS_PHY_DETECT 0x1000 +#define MV88E6XXX_PORT_STS_LINK 0x0800 +#define MV88E6XXX_PORT_STS_DUPLEX 0x0400 +#define MV88E6XXX_PORT_STS_SPEED_MASK 0x0300 +#define MV88E6XXX_PORT_STS_SPEED_10 0x0000 +#define MV88E6XXX_PORT_STS_SPEED_100 0x0100 +#define MV88E6XXX_PORT_STS_SPEED_1000 0x0200 +#define MV88E6XXX_PORT_STS_SPEED_10000 0x0300 +#define MV88E6352_PORT_STS_EEE 0x0040 +#define MV88E6165_PORT_STS_AM_DIS 0x0040 +#define MV88E6185_PORT_STS_MGMII 0x0040 +#define MV88E6XXX_PORT_STS_TX_PAUSED 0x0020 +#define MV88E6XXX_PORT_STS_FLOW_CTL 0x0010 +#define MV88E6XXX_PORT_STS_CMODE_MASK 0x000f +#define MV88E6XXX_PORT_STS_CMODE_100BASE_X 0x0008 +#define MV88E6XXX_PORT_STS_CMODE_1000BASE_X 0x0009 +#define MV88E6XXX_PORT_STS_CMODE_SGMII 0x000a +#define MV88E6XXX_PORT_STS_CMODE_2500BASEX 0x000b +#define MV88E6XXX_PORT_STS_CMODE_XAUI 0x000c +#define MV88E6XXX_PORT_STS_CMODE_RXAUI 0x000d +#define MV88E6185_PORT_STS_CDUPLEX 0x0008 +#define MV88E6185_PORT_STS_CMODE_MASK 0x0007 +#define MV88E6185_PORT_STS_CMODE_GMII_FD 0x0000 +#define MV88E6185_PORT_STS_CMODE_MII_100_FD_PS 0x0001 +#define MV88E6185_PORT_STS_CMODE_MII_100 0x0002 +#define MV88E6185_PORT_STS_CMODE_MII_10 0x0003 +#define MV88E6185_PORT_STS_CMODE_SERDES 0x0004 +#define MV88E6165_PORT_STS_CMODE_PHY 0x0004 +#define MV88E6185_PORT_STS_CMODE_1000BASE_X 0x0005 +#define MV88E6185_PORT_STS_CMODE_PHY 0x0006 +#define MV88E6165_PORT_STS_CMODE_SGMII 0x0006 +#define MV88E6185_PORT_STS_CMODE_DISABLED 0x0007 + + + +/* Offset 0x01: MAC (or PCS or Physical) Control Register */ +#define MV88E6XXX_PORT_MAC_CTL 0x01 +#define MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK 0x8000 +#define MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK 0x4000 +#define MV88E6185_PORT_MAC_CTL_SYNC_OK 0x4000 +#define MV88E6390_PORT_MAC_CTL_FORCE_SPEED 0x2000 +#define MV88E6390_PORT_MAC_CTL_ALTSPEED 0x1000 +#define MV88E6352_PORT_MAC_CTL_200BASE 0x1000 +#define MV88E6185_PORT_MAC_CTL_AN_EN 0x0400 +#define MV88E6185_PORT_MAC_CTL_AN_RESTART 0x0200 +#define MV88E6185_PORT_MAC_CTL_AN_DONE 0x0100 +#define MV88E6XXX_PORT_MAC_CTL_FC 0x0080 +#define MV88E6XXX_PORT_MAC_CTL_FORCE_FC 0x0040 +#define MV88E6XXX_PORT_MAC_CTL_LINK_UP 0x0020 +#define MV88E6XXX_PORT_MAC_CTL_FORCE_LINK 0x0010 +#define MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL 0x0008 +#define MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX 0x0004 +#define MV88E6XXX_PORT_MAC_CTL_SPEED_MASK 0x0003 +#define MV88E6XXX_PORT_MAC_CTL_SPEED_10 0x0000 +#define MV88E6XXX_PORT_MAC_CTL_SPEED_100 0x0001 +#define MV88E6065_PORT_MAC_CTL_SPEED_200 0x0002 +#define MV88E6XXX_PORT_MAC_CTL_SPEED_1000 0x0002 +#define MV88E6390_PORT_MAC_CTL_SPEED_10000 0x0003 +#define MV88E6XXX_PORT_MAC_CTL_SPEED_UNFORCED 0x0003 + +/* Offset 0x03: Switch Identifier Register */ +#define MV88E6XXX_PORT_SWITCH_ID 0x03 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_MASK 0xfff0 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6085 0x04a0 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6095 0x0950 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6097 0x0990 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6190X 0x0a00 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6390X 0x0a10 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6131 0x1060 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6320 0x1150 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6123 0x1210 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6161 0x1610 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6165 0x1650 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6171 0x1710 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6172 0x1720 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6175 0x1750 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6176 0x1760 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6190 0x1900 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6191 0x1910 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6185 0x1a70 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6240 0x2400 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6290 0x2900 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6321 0x3100 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6141 0x3400 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6341 0x3410 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6352 0x3520 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6350 0x3710 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6351 0x3750 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6390 0x3900 +#define MV88E6XXX_PORT_SWITCH_ID_REV_MASK 0x000f + + +/* + * SERDES connected to port 0x04 is accessible at address 0xC + */ +#define MV88E6165_PORT_SERDES_OFFSET (0x0C - 0x04) + +int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, + u16 *val); +int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, + u16 val); + +int mv88e6185_port_set_pause(struct mv88e6xxx_chip *chip, int port, + int pause); +int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode); +int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode); +int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link); +int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup); +int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode); +int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port, + struct phylink_link_state *state); +int mv88e6185_port_link_state(struct mv88e6xxx_chip *chip, int port, + struct phylink_link_state *state); + +/* Barebox specific */ +int mv88e6xxx_port_probe(struct mv88e6xxx_chip *chip); + + +#endif /* _MV88E6XXX_PORT_H */ diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 63f249f..7c4d14b 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -176,12 +176,12 @@ phydev->dev.bus = &mdio_bus_type; if (bus) { - sprintf(phydev->dev.name, "mdio%d-phy%02x", - phydev->bus->dev.id, - phydev->addr); + dev_set_name(&phydev->dev, "mdio%d-phy%02x", + phydev->bus->dev.id, + phydev->addr); phydev->dev.id = DEVICE_ID_SINGLE; } else { - sprintf(phydev->dev.name, "fixed-phy"); + dev_set_name(&phydev->dev, "fixed-phy"); phydev->dev.id = DEVICE_ID_DYNAMIC; } @@ -234,6 +234,10 @@ u32 phy_id = 0; int r; + /* skip masked out PHY addresses */ + if (bus->phy_mask & BIT(addr)) + return ERR_PTR(-ENODEV); + r = get_phy_id(bus, addr, &phy_id); if (r) return ERR_PTR(r); @@ -444,9 +448,6 @@ } for (i = 0; i < PHY_MAX_ADDR && !edev->phydev; i++) { - /* skip masked out PHY addresses */ - if (bus->phy_mask & (1 << i)) - continue; phy = mdiobus_scan(bus, i); if (IS_ERR(phy)) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index c0f61f4..63c0f99 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -199,7 +199,7 @@ nvmem->read_only = of_property_read_bool(np, "read-only") | config->read_only; - safe_strncpy(nvmem->dev.name, config->name, MAX_DRIVER_NAME); + dev_set_name(&nvmem->dev, config->name); nvmem->dev.id = DEVICE_ID_DYNAMIC; dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name); diff --git a/drivers/nvmem/ocotp.c b/drivers/nvmem/ocotp.c index ba35bef..e689559 100644 --- a/drivers/nvmem/ocotp.c +++ b/drivers/nvmem/ocotp.c @@ -531,7 +531,7 @@ if (IS_ERR(priv->clk)) return PTR_ERR(priv->clk); - strcpy(priv->dev.name, "ocotp"); + dev_set_name(&priv->dev, "ocotp"); priv->dev.parent = dev; register_device(&priv->dev); diff --git a/drivers/of/address.c b/drivers/of/address.c index 14db080..4e12522 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -379,8 +379,8 @@ /* Count address cells & copy address locally */ bus->count_cells(dev, &na, &ns); if (!OF_CHECK_COUNTS(na, ns)) { - printk(KERN_ERR "prom_parse: Bad cell count for %s\n", - dev->full_name); + pr_debug("prom_parse: Bad cell count for %s\n", + dev->full_name); return OF_BAD_ADDR; } memcpy(addr, in_addr, na * 4); diff --git a/drivers/of/platform.c b/drivers/of/platform.c index c9157cd..17052f4 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -43,71 +43,35 @@ * of_device_make_bus_id - Use the device node data to assign a unique name * @dev: pointer to device structure that is linked to a device tree node * - * This routine will first try using either the dcr-reg or the reg property - * value to derive a unique name. As a last resort it will use the node - * name followed by a unique number. + * This routine will first try using the translated bus address to + * derive a unique name. If it cannot, then it will prepend names from + * parent nodes until a unique name can be derived. */ static void of_device_make_bus_id(struct device_d *dev) { - static int bus_no_reg_magic; - struct device_node *np = dev->device_node; - const __be32 *reg, *addrp; + struct device_node *node = dev->device_node; + const __be32 *reg; u64 addr; - char *name, *at; - name = xstrdup(np->name); - at = strchr(name, '@'); - if (at) - *at = '\0'; - -#ifdef CONFIG_PPC_DCR - /* - * If it's a DCR based device, use 'd' for native DCRs - * and 'D' for MMIO DCRs. - */ - reg = of_get_property(np, "dcr-reg", NULL); - if (reg) { -#ifdef CONFIG_PPC_DCR_NATIVE - snprintf(dev->name, MAX_DRIVER_NAME, "d%x.%s", *reg, name); -#else /* CONFIG_PPC_DCR_NATIVE */ - u64 addr = of_translate_dcr_address(np, *reg, NULL); - if (addr != OF_BAD_ADDR) { - snprintf(dev->name, MAX_DRIVER_NAME, "D%llx.%s", - (unsigned long long)addr, name); - free(name); + /* Construct the name, using parent nodes if necessary to ensure uniqueness */ + while (node->parent) { + /* + * If the address can be translated, then that is as much + * uniqueness as we need. Make it the first component and return + */ + reg = of_get_property(node, "reg", NULL); + if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) { + dev_set_name(dev, dev->name ? "%llx.%s:%s" : "%llx.%s", + (unsigned long long)addr, node->name, + dev->name); return; } -#endif /* !CONFIG_PPC_DCR_NATIVE */ - } -#endif /* CONFIG_PPC_DCR */ - /* - * For MMIO, get the physical address - */ - reg = of_get_property(np, "reg", NULL); - if (reg) { - if (of_can_translate_address(np)) { - addr = of_translate_address(np, reg); - } else { - addrp = of_get_address(np, 0, NULL, NULL); - if (addrp) - addr = of_read_number(addrp, 1); - else - addr = OF_BAD_ADDR; - } - if (addr != OF_BAD_ADDR) { - snprintf(dev->name, MAX_DRIVER_NAME, "%llx.%s", - (unsigned long long)addr, name); - free(name); - return; - } + /* format arguments only used if dev_name() resolves to NULL */ + dev_set_name(dev, dev->name ? "%s:%s" : "%s", + kbasename(node->full_name), dev->name); + node = node->parent; } - - /* - * No BusID, use the node name and add a globally incremented counter - */ - snprintf(dev->name, MAX_DRIVER_NAME, "%s.%d", name, bus_no_reg_magic++); - free(name); } /** diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 201675b..ac15623 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -87,8 +87,7 @@ struct device_d *dev = &pdev->dev; int ret; - snprintf(dev->name, MAX_DRIVER_NAME, "pci-%04x:%04x.", - pdev->vendor, pdev->device); + dev_set_name(dev, "pci-%04x:%04x.", pdev->vendor, pdev->device); dev->bus = &pci_bus; dev->id = DEVICE_ID_DYNAMIC; diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 1b6a9f7..9d6288f 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -46,7 +46,7 @@ id = phy_ida++; - snprintf(phy->dev.name, MAX_DRIVER_NAME, "phy"); + dev_set_name(&phy->dev, "phy"); phy->dev.id = id; phy->dev.parent = dev; phy->dev.device_node = node ?: dev->device_node; diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 80fade0..c801699 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -89,7 +89,7 @@ pwm->chip = chip; pwm->hwdev = dev; - strcpy(pwm->dev.name, chip->devname); + dev_set_name(&pwm->dev, chip->devname); pwm->dev.id = DEVICE_ID_SINGLE; pwm->dev.parent = dev; diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 8b047a6..5b58271 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -67,7 +67,7 @@ return -EINVAL; dev->id = DEVICE_ID_DYNAMIC; - strcpy(dev->name, "rtc"); + dev_set_name(dev, "rtc"); if (rtcdev->dev) dev->parent = rtcdev->dev; platform_device_register(dev); diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 5650098..25bb988 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -76,7 +76,7 @@ proxy->bits_per_word = chip->bits_per_word ? chip->bits_per_word : 8; proxy->dev.platform_data = chip->platform_data; proxy->dev.bus = &spi_bus; - strcpy(proxy->dev.name, chip->name); + dev_set_name(&proxy->dev, chip->name); /* allocate a free id for this chip */ proxy->dev.id = DEVICE_ID_DYNAMIC; proxy->dev.type_data = proxy; diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 70ded6d..0ee8808 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -431,9 +431,10 @@ dev->serial, sizeof(dev->serial)); if (parent) { - sprintf(dev->dev.name, "%s-%d", parent->dev.name, dev->portnr - 1); + dev_set_name(&dev->dev, "%s-%d", parent->dev.name, + dev->portnr - 1); } else { - sprintf(dev->dev.name, "usb%d", dev->host->busnum); + dev_set_name(&dev->dev, "usb%d", dev->host->busnum); } dev->dev.id = DEVICE_ID_SINGLE; diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index ed99b53..e357456 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -179,7 +179,7 @@ if (!udc) goto err1; - strcpy(gadget->dev.name, "usbgadget"); + dev_set_name(&gadget->dev, "usbgadget"); gadget->dev.id = DEVICE_ID_SINGLE; gadget->dev.parent = parent; @@ -198,7 +198,7 @@ dev_add_param_string(&gadget->dev, "productname", NULL, NULL, &gadget->productname, NULL); - strcpy(udc->dev.name, "udc"); + dev_set_name(&udc->dev, "udc"); udc->dev.id = DEVICE_ID_DYNAMIC; udc->gadget = gadget; diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 431b97e..5fe3bcb 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -333,7 +333,7 @@ { int ret; - strcpy(glue->otg_dev.name, "otg"); + dev_set_name(&glue->otg_dev, "otg"); glue->otg_dev.id = DEVICE_ID_DYNAMIC, glue->otg_dev.parent = glue->dev; diff --git a/drivers/video/backlight.c b/drivers/video/backlight.c index 30d52fc..3913d1c 100644 --- a/drivers/video/backlight.c +++ b/drivers/video/backlight.c @@ -71,7 +71,7 @@ { int ret; - sprintf(bl->dev.name, "backlight"); + dev_set_name(&bl->dev, "backlight"); bl->dev.id = DEVICE_ID_DYNAMIC; ret = register_device(&bl->dev); diff --git a/drivers/video/fb.c b/drivers/video/fb.c index c9d184d..72f33a6 100644 --- a/drivers/video/fb.c +++ b/drivers/video/fb.c @@ -309,7 +309,7 @@ dev->id = id; dev->info = fb_info; - sprintf(dev->name, "fb"); + dev_set_name(dev, "fb"); ret = register_device(&info->dev); if (ret) diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index ff57386..694ffa8 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -423,7 +423,7 @@ char str[18]; int ret; - sprintf(dev->dev.name, "w1-%x-", dev->fid); + dev_set_name(&dev->dev, "w1-%x-", dev->fid); dev->dev.id = DEVICE_ID_DYNAMIC; dev->dev.bus = &w1_bustype; dev->bus = bus; @@ -619,7 +619,7 @@ list_add_tail(&bus->list, &w1_buses); - strcpy(bus->dev.name, "w1_bus"); + dev_set_name(&bus->dev, "w1_bus"); bus->dev.id = DEVICE_ID_DYNAMIC; bus->dev.parent = bus->parent; diff --git a/drivers/watchdog/wd_core.c b/drivers/watchdog/wd_core.c index 56e8626..e6e5dde 100644 --- a/drivers/watchdog/wd_core.c +++ b/drivers/watchdog/wd_core.c @@ -104,7 +104,7 @@ { wd->dev.parent = wd->hwdev; wd->dev.id = id; - strncpy(wd->dev.name, name, MAX_DRIVER_NAME); + dev_set_name(&wd->dev, name); return register_device(&wd->dev); } diff --git a/fs/fs.c b/fs/fs.c index 45750f7..a30afa3 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -2792,7 +2792,7 @@ fsdev = xzalloc(sizeof(struct fs_device_d)); fsdev->backingstore = xstrdup(device); - safe_strncpy(fsdev->dev.name, fsname, MAX_DRIVER_NAME); + dev_set_name(&fsdev->dev, fsname); fsdev->dev.id = get_free_deviceid(fsdev->dev.name); fsdev->dev.bus = &fs_bus; fsdev->options = xstrdup(fsoptions); diff --git a/include/driver.h b/include/driver.h index 63facbf..7da184d 100644 --- a/include/driver.h +++ b/include/driver.h @@ -23,7 +23,6 @@ #include #include -#define MAX_DRIVER_NAME 32 #define FORMAT_DRIVER_NAME_ID "%s%d" #include @@ -38,10 +37,19 @@ /** @brief Describes a particular device present in the system */ struct device_d { - /*! This member (and 'type' described below) is used to match with a - * driver. This is a descriptive name and could be MPC5XXX_ether or - * imx_serial. */ - char name[MAX_DRIVER_NAME]; + /*! This member (and 'type' described below) is used to match + * with a driver. This is a descriptive name and could be + * MPC5XXX_ether or imx_serial. Unless absolutely necessary, + * should not be modified directly and dev_set_name() should + * be used instead. + */ + char *name; + + /*! This member is used to store device's unique name as + * obtained by calling dev_id(). Internal field, do not + * access it directly. + */ + char *unique_name; /*! The id is used to uniquely identify a device in the system. The id * will show up under /dev/ as the device's name. Usually this is * something like eth0 or nor0. */ @@ -170,13 +178,18 @@ char *deviceid_from_spec_str(const char *str, char **endp); -extern const char *dev_id(const struct device_d *dev); +static inline const char *dev_id(const struct device_d *dev) +{ + return (dev->id != DEVICE_ID_SINGLE) ? dev->unique_name : dev->name; +} static inline const char *dev_name(const struct device_d *dev) { return dev_id(dev); } +int dev_set_name(struct device_d *dev, const char *fmt, ...); + /* * get resource 'num' for a device */ diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 4d83fe0..324e40c 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -72,9 +72,12 @@ #define SPEED_2500 2500 #define SPEED_10000 10000 +#define SPEED_UNKNOWN -1 + /* Duplex, half or full. */ #define DUPLEX_HALF 0x00 #define DUPLEX_FULL 0x01 +#define DUPLEX_UNKNOWN 0xff /* Which connector port. */ #define PORT_TP 0x00 diff --git a/include/linux/phy.h b/include/linux/phy.h index ac750f5..5b2c63f 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -54,7 +54,16 @@ PHY_INTERFACE_MODE_RGMII_TXID, PHY_INTERFACE_MODE_RTBI, PHY_INTERFACE_MODE_SMII, + PHY_INTERFACE_MODE_XGMII, PHY_INTERFACE_MODE_QSGMII, + PHY_INTERFACE_MODE_TRGMII, + PHY_INTERFACE_MODE_1000BASEX, + PHY_INTERFACE_MODE_2500BASEX, + PHY_INTERFACE_MODE_RXAUI, + PHY_INTERFACE_MODE_XAUI, + /* 10GBASE-KR, XFI, SFI - single lane 10G Serdes */ + PHY_INTERFACE_MODE_10GKR, + PHY_INTERFACE_MODE_MAX, } phy_interface_t; #define PHY_INIT_TIMEOUT 100000 diff --git a/include/linux/string.h b/include/linux/string.h index 90fe1b6..fd42f50 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -111,6 +111,18 @@ void *memchr_inv(const void *start, int c, size_t bytes); +/** + * kbasename - return the last part of a pathname. + * + * @path: path to extract the filename from. + */ +static inline const char *kbasename(const char *path) +{ + const char *tail = strrchr(path, '/'); + return tail ? tail + 1 : path; +} + + #ifdef __cplusplus } #endif diff --git a/include/of.h b/include/of.h index 7fc4b77..184acb4 100644 --- a/include/of.h +++ b/include/of.h @@ -321,6 +321,12 @@ return -ENOSYS; } +static inline struct device_d *of_platform_device_create(struct device_node *np, + struct device_d *parent) +{ + return NULL; +} + static inline int of_n_addr_cells(struct device_node *np) { return 0; diff --git a/lib/libgen.c b/lib/libgen.c index 08ef352..61a8670 100644 --- a/lib/libgen.c +++ b/lib/libgen.c @@ -17,23 +17,11 @@ #include #include +#include char *basename (char *path) { - char *fname; - - if(!strchr(path, '/')) - return path; - - fname = path + strlen(path) - 1; - while (fname >= path) { - if (*fname == '/') { - fname++; - break; - } - fname--; - } - return fname; + return (char *)kbasename(path); } EXPORT_SYMBOL(basename); diff --git a/net/eth.c b/net/eth.c index badd183..b3e8124 100644 --- a/net/eth.c +++ b/net/eth.c @@ -364,7 +364,7 @@ return -1; } - strcpy(edev->dev.name, "eth"); + dev_set_name(&edev->dev, "eth"); if (edev->parent) edev->dev.parent = edev->parent;