From 50caed4644262e85aa474b32ec24ab3b5924eef2 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 16 Oct 2018 21:50:21 +0200 Subject: ratp: fix compile warnings Signed-off-by: Oleksij Rempel Signed-off-by: Sascha Hauer --- common/ratp/ratp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/ratp/ratp.c b/common/ratp/ratp.c index fae9cec5b9..9aea1786d6 100644 --- a/common/ratp/ratp.c +++ b/common/ratp/ratp.c @@ -72,7 +72,7 @@ int register_ratp_command(struct ratp_command *cmd) } EXPORT_SYMBOL(register_ratp_command); -struct ratp_command *find_ratp_request(uint16_t request_id) +static struct ratp_command *find_ratp_request(uint16_t request_id) { struct ratp_command *cmdtp; -- cgit v1.2.3 From 1e360f6f0e2d03518f6de5b2ab559c97d09bc167 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Oct 2018 12:15:31 -0700 Subject: ARM: Do not expose ARMv8 functions on ARMv7 Assembly implementing current_el(), read_mpidr(), set_cntfrq(), get_cntfrq() and get_cntpct() is ARMv8 specific, so change #if guard protecting it to reflect that fact. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- arch/arm/include/asm/system.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index 5cf828ea36..2f13e2b98d 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; -- cgit v1.2.3 From c3389cfed5794bfc862e79d472f6a1cf66e6d76b Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Oct 2018 12:15:32 -0700 Subject: clocksource: Add ARM global timer support Port corresponding Linux kernel driver. Currently VFxxx SoC is the intended consumer because it doesn't include common i.MX GPT block used as clocksource by other i.MX SoCs. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/clocksource/Kconfig | 4 ++ drivers/clocksource/Makefile | 1 + drivers/clocksource/arm_global_timer.c | 113 +++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 drivers/clocksource/arm_global_timer.c diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 3d63f7ff16..ec1e622b48 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -70,3 +70,7 @@ config CLOCKSOURCE_ARMV8_TIMER bool default y depends on ARM && CPU_64v8 + +config CLOCKSOURCE_ARM_GLOBAL_TIMER + bool + depends on ARM && CPU_V7 diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index ea33fff502..51f6cb2f4a 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_CLOCKSOURCE_UEMD) += uemd.o 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 diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c new file mode 100644 index 0000000000..44e3a3c762 --- /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); + -- cgit v1.2.3 From 6e3aa6bec3d93be5e4fcfe334a43c9b3942208de Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Oct 2018 12:15:33 -0700 Subject: VFxxx: Select CLOCKSOURCE_ARM_GLOBAL_TIMER Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- arch/arm/mach-imx/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index 63a92bd5bd..1320fc4d90 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -184,6 +184,7 @@ config ARCH_VF610 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" -- cgit v1.2.3 From a191dcb38cdc216e0f042b04a7d41f663fffdd2e Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Oct 2018 12:15:34 -0700 Subject: i.MX: Move GPT driver to drivers/clocksource Move GPT driver to drivers/clocksource and rename it to timer-imx-gpt.c to match Linux kernel as well as to keep all clocksource drivers in a common location. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- arch/arm/mach-imx/Makefile | 1 - arch/arm/mach-imx/clocksource.c | 192 ------------------------------------ drivers/clocksource/Kconfig | 3 + drivers/clocksource/Makefile | 1 + drivers/clocksource/timer-imx-gpt.c | 192 ++++++++++++++++++++++++++++++++++++ 5 files changed, 196 insertions(+), 193 deletions(-) delete mode 100644 arch/arm/mach-imx/clocksource.c create mode 100644 drivers/clocksource/timer-imx-gpt.c diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index 5a01dd57e8..97c54406e6 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 4d6c6c2b0b..0000000000 --- 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/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index ec1e622b48..1a33b2f0c9 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -74,3 +74,6 @@ config CLOCKSOURCE_ARMV8_TIMER config CLOCKSOURCE_ARM_GLOBAL_TIMER bool depends on ARM && CPU_V7 +config CLOCKSOURCE_IMX_GPT + def_bool y + depends on ARCH_IMX diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 51f6cb2f4a..ce4d74137a 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -13,3 +13,4 @@ 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/timer-imx-gpt.c b/drivers/clocksource/timer-imx-gpt.c new file mode 100644 index 0000000000..4d6c6c2b0b --- /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); -- cgit v1.2.3 From b513cd87edba1973f0e3997deb394c5fe3d23853 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Oct 2018 12:15:35 -0700 Subject: clocksource: Introduce ARCH_HAS_IMX_GPT Not all SoCs use i.MX GPT block as a clocksource, so introduce ARCH_HAS_IMX_GPT to mark the ones that do. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- arch/arm/mach-imx/Kconfig | 11 +++++++++++ drivers/clocksource/Kconfig | 5 ++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index 1320fc4d90..c0788e7d93 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -84,58 +84,68 @@ comment "Freescale i.MX System-on-Chip" 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 @@ config ARCH_IMX7 select OFTREE select COMMON_CLK_OF_PROVIDER select ARCH_HAS_FEC_IMX + select ARCH_HAS_IMX_GPT config ARCH_IMX8MQ bool diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 1a33b2f0c9..3caf72503a 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 @@ -76,4 +79,4 @@ config CLOCKSOURCE_ARM_GLOBAL_TIMER depends on ARM && CPU_V7 config CLOCKSOURCE_IMX_GPT def_bool y - depends on ARCH_IMX + depends on ARCH_HAS_IMX_GPT -- cgit v1.2.3 From 68b65dcedf8f3f9c75d5c5c37606feabaed6855c Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Oct 2018 12:15:36 -0700 Subject: of: Demote "Bad cell count for" to debug There are valid use-cases where getting OF_BAD_ADDR via that codepath is expected. In addition to that analogous code in Linux kernel uses pr_debug as well. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/of/address.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/of/address.c b/drivers/of/address.c index 14db080417..4e12522a0a 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -379,8 +379,8 @@ static u64 __of_translate_address(struct device_node *dev, /* 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); -- cgit v1.2.3 From f16af961993a7af3cf9361c4fe0cb56eb499e518 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Oct 2018 12:15:37 -0700 Subject: aiodev: Don't try to use DT node name as aiodev->name Don't try to use DT node name as aiodev->name in aiodev_regster() since, for some devices (e. g. tempmon) than name would already be taken by parent platform device. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/aiodev/core.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/aiodev/core.c b/drivers/aiodev/core.c index 79f935d710..90df8a6e7c 100644 --- a/drivers/aiodev/core.c +++ b/drivers/aiodev/core.c @@ -106,8 +106,6 @@ int aiodevice_register(struct aiodevice *aiodev) 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) { -- cgit v1.2.3 From 803163573d89da65840299712b27891e592cc883 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Oct 2018 12:15:38 -0700 Subject: aiodev: imx_thermal: Give aiodev a more descriptive name Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/aiodev/imx_thermal.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/aiodev/imx_thermal.c b/drivers/aiodev/imx_thermal.c index c020a1091d..9e50708355 100644 --- a/drivers/aiodev/imx_thermal.c +++ b/drivers/aiodev/imx_thermal.c @@ -177,6 +177,7 @@ static int imx_thermal_probe(struct device_d *dev) 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])); -- cgit v1.2.3 From d800b11619bb857ed857df1332c5b6e9c41d6414 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Oct 2018 12:15:39 -0700 Subject: aiodev: qoriq_thermal: Give aiodev a more descriptive name Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/aiodev/qoriq_thermal.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/aiodev/qoriq_thermal.c b/drivers/aiodev/qoriq_thermal.c index d29da02a68..7556fef025 100644 --- a/drivers/aiodev/qoriq_thermal.c +++ b/drivers/aiodev/qoriq_thermal.c @@ -232,6 +232,7 @@ static int qoriq_tmu_probe(struct device_d *dev) 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; -- cgit v1.2.3 From 05683764a77ff91419cabe0c26e537589b8a28ff Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Oct 2018 12:15:40 -0700 Subject: drivers: Introduce dev_set_name() Introduce dev_set_name() in order to hide implementation details of setting device's name so it'd be easier to change it. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- arch/arm/mach-imx/iim.c | 2 +- arch/arm/mach-imx/include/mach/devices-imx51.h | 2 +- arch/arm/mach-imx/include/mach/devices-imx53.h | 2 +- arch/arm/mach-mxs/include/mach/devices.h | 2 +- arch/arm/mach-mxs/ocotp.c | 2 +- arch/sandbox/board/console.c | 2 +- common/console.c | 4 ++-- common/state/state.c | 2 +- drivers/aiodev/core.c | 2 +- drivers/amba/bus.c | 2 +- drivers/ata/disk_ata_drive.c | 4 ++-- drivers/base/bus.c | 2 +- drivers/base/driver.c | 21 +++++++++++++++++++++ drivers/base/resource.c | 2 +- drivers/efi/efi-device.c | 2 +- drivers/firmware/socfpga.c | 2 +- drivers/i2c/i2c.c | 4 ++-- drivers/mci/mci-core.c | 4 ++-- drivers/mtd/core.c | 2 +- drivers/mtd/spi-nor/cadence-quadspi.c | 2 +- drivers/mtd/ubi/build.c | 2 +- drivers/mtd/ubi/vmt.c | 4 ++-- drivers/net/cpsw.c | 2 +- drivers/net/e1000/eeprom.c | 2 +- drivers/net/orion-gbe.c | 2 +- drivers/net/phy/mdio_bus.c | 2 +- drivers/net/phy/phy.c | 8 ++++---- drivers/nvmem/core.c | 2 +- drivers/nvmem/ocotp.c | 2 +- drivers/of/platform.c | 12 ++++++------ drivers/pci/bus.c | 3 +-- drivers/phy/phy-core.c | 2 +- drivers/pwm/core.c | 2 +- drivers/rtc/class.c | 2 +- drivers/spi/spi.c | 2 +- drivers/usb/core/usb.c | 5 +++-- drivers/usb/gadget/udc-core.c | 4 ++-- drivers/usb/musb/musb_dsps.c | 2 +- drivers/video/backlight.c | 2 +- drivers/video/fb.c | 2 +- drivers/w1/w1.c | 4 ++-- drivers/watchdog/wd_core.c | 2 +- fs/fs.c | 2 +- include/driver.h | 11 ++++++++--- net/eth.c | 2 +- 45 files changed, 89 insertions(+), 63 deletions(-) diff --git a/arch/arm/mach-imx/iim.c b/arch/arm/mach-imx/iim.c index d4794cbac8..207e1879c3 100644 --- a/arch/arm/mach-imx/iim.c +++ b/arch/arm/mach-imx/iim.c @@ -474,7 +474,7 @@ static int imx_iim_probe(struct device_d *dev) 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 cccd8f4610..66fe643f82 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 @@ static inline struct device_d *imx51_add_nand(struct imx_nand_platform_data *pda 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 10caae8c93..27200a26d0 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 @@ static inline struct device_d *imx53_add_nand(struct imx_nand_platform_data *pda 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 5680d61c9e..b212aa783e 100644 --- a/arch/arm/mach-mxs/include/mach/devices.h +++ b/arch/arm/mach-mxs/include/mach/devices.h @@ -26,7 +26,7 @@ static inline struct device_d *mxs_add_nand(unsigned long gpmi_base, unsigned lo 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 b41fde9919..01db731166 100644 --- a/arch/arm/mach-mxs/ocotp.c +++ b/arch/arm/mach-mxs/ocotp.c @@ -197,7 +197,7 @@ static int mxs_ocotp_probe(struct device_d *dev) 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 cf1781d156..006bbd1a6e 100644 --- a/arch/sandbox/board/console.c +++ b/arch/sandbox/board/console.c @@ -32,7 +32,7 @@ int barebox_register_console(int stdinfd, int stdoutfd) 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 40c26b66d9..47ccf2e54d 100644 --- a/common/console.c +++ b/common/console.c @@ -314,10 +314,10 @@ int console_register(struct console_device *newcdev) 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 25d9502111..55804a5213 100644 --- a/common/state/state.c +++ b/common/state/state.c @@ -179,7 +179,7 @@ static struct state *state_new(const char *name) 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 90df8a6e7c..b5d06da932 100644 --- a/drivers/aiodev/core.c +++ b/drivers/aiodev/core.c @@ -113,7 +113,7 @@ int aiodevice_register(struct aiodevice *aiodev) 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/amba/bus.c b/drivers/amba/bus.c index ae5df13c96..a8cd16835d 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -200,7 +200,7 @@ struct amba_device *amba_device_alloc(const char *name, int id, resource_size_t 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 5ebddbdec8..11f7151e51 100644 --- a/drivers/ata/disk_ata_drive.c +++ b/drivers/ata/disk_ata_drive.c @@ -325,10 +325,10 @@ int ata_port_register(struct ata_port *port) 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 b889a48662..1038d20a12 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -32,7 +32,7 @@ int bus_register(struct bus_type *bus) 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 c43a4bde2a..c9e6e6ddd6 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -478,6 +478,27 @@ const char *dev_id(const struct device_d *dev) return buf; } +/** + * dev_set_name - set a device name + * @dev: device + * @fmt: format string for the device's name + * + */ +int dev_set_name(struct device_d *dev, const char *fmt, ...) +{ + va_list vargs; + int err; + + va_start(vargs, fmt); + err = vsnprintf(dev->name, sizeof(dev->name), fmt, vargs); + va_end(vargs); + + WARN_ON(err < 0); + + return err; +} +EXPORT_SYMBOL_GPL(dev_set_name); + static void devices_shutdown(void) { struct device_d *dev; diff --git a/drivers/base/resource.c b/drivers/base/resource.c index 6c2d7fed46..cb7105bf8d 100644 --- a/drivers/base/resource.c +++ b/drivers/base/resource.c @@ -27,7 +27,7 @@ struct device_d *device_alloc(const char *devname, int id) 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/efi/efi-device.c b/drivers/efi/efi-device.c index 3a27323a00..b7dea7c822 100644 --- a/drivers/efi/efi-device.c +++ b/drivers/efi/efi-device.c @@ -185,7 +185,7 @@ static struct efi_device *efi_add_device(efi_handle_t *handle, efi_guid_t **guid 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 c1eae98acc..6d11da32a5 100644 --- a/drivers/firmware/socfpga.c +++ b/drivers/firmware/socfpga.c @@ -438,7 +438,7 @@ static int fpgamgr_probe(struct device_d *dev) 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 608f8289bf..25e0fe7add 100644 --- a/drivers/i2c/i2c.c +++ b/drivers/i2c/i2c.c @@ -389,7 +389,7 @@ static struct i2c_client *i2c_new_device(struct i2c_adapter *adapter, 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 @@ int i2c_add_numbered_adapter(struct i2c_adapter *adapter) } 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 c6b4e02cbc..c8d1d5e164 100644 --- a/drivers/mci/mci-core.c +++ b/drivers/mci/mci-core.c @@ -1802,10 +1802,10 @@ int mci_register(struct mci_host *host) 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 15fe9ce798..56e85b3d8f 100644 --- a/drivers/mtd/core.c +++ b/drivers/mtd/core.c @@ -607,7 +607,7 @@ int add_mtd_device(struct mtd_info *mtd, const char *devname, int device_id) 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 6269668187..ed5377bd4b 100644 --- a/drivers/mtd/spi-nor/cadence-quadspi.c +++ b/drivers/mtd/spi-nor/cadence-quadspi.c @@ -1051,7 +1051,7 @@ static int cqspi_setup_flash(struct device_d *dev, 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 536077ebf3..493c778c3f 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -157,7 +157,7 @@ static int uif_init(struct ubi_device *ubi, int *ref) *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 6177048214..99da79171b 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -145,7 +145,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) 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 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) 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 3d3939cfae..c6fc21dc54 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -916,7 +916,7 @@ static int cpsw_slave_setup(struct cpsw_slave *slave, int slave_num, 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 dda022e054..180b32ede8 100644 --- a/drivers/net/e1000/eeprom.c +++ b/drivers/net/e1000/eeprom.c @@ -1529,7 +1529,7 @@ int e1000_register_invm(struct e1000_hw *hw) 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 e6bd757689..431ec5c31f 100644 --- a/drivers/net/orion-gbe.c +++ b/drivers/net/orion-gbe.c @@ -462,7 +462,7 @@ static int port_probe(struct device_d *parent, struct port_priv *port) 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/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 7d53bcc3da..177d54863c 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -222,7 +222,7 @@ int mdiobus_register(struct mii_bus *bus) 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; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 42dcad9069..19d458e07e 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -176,12 +176,12 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id) 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; } diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index c0f61f453a..63c0f997b3 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -199,7 +199,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) 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 c2d2982ee1..0d07da280d 100644 --- a/drivers/nvmem/ocotp.c +++ b/drivers/nvmem/ocotp.c @@ -531,7 +531,7 @@ static int imx_ocotp_probe(struct device_d *dev) 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/platform.c b/drivers/of/platform.c index c9157cdd74..4fd3ce2b75 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -68,12 +68,12 @@ static void of_device_make_bus_id(struct device_d *dev) 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); + dev_set_name(dev, "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); + dev_set_name(dev, "D%llx.%s", + (unsigned long long)addr, name); free(name); return; } @@ -96,8 +96,8 @@ static void of_device_make_bus_id(struct device_d *dev) addr = OF_BAD_ADDR; } if (addr != OF_BAD_ADDR) { - snprintf(dev->name, MAX_DRIVER_NAME, "%llx.%s", - (unsigned long long)addr, name); + dev_set_name(dev, "%llx.%s", + (unsigned long long)addr, name); free(name); return; } @@ -106,7 +106,7 @@ static void of_device_make_bus_id(struct device_d *dev) /* * 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++); + dev_set_name(dev, "%s.%d", name, bus_no_reg_magic++); free(name); } diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 201675b486..ac15623307 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -87,8 +87,7 @@ int pci_register_device(struct pci_dev *pdev) 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 1b6a9f7b1d..9d6288fc07 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -46,7 +46,7 @@ struct phy *phy_create(struct device_d *dev, struct device_node *node, 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 80fade0611..c8016999f0 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -89,7 +89,7 @@ int pwmchip_add(struct pwm_chip *chip, struct device_d *dev) 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 8b047a638d..5b58271c07 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -67,7 +67,7 @@ int rtc_register(struct rtc_device *rtcdev) 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 5650098a0a..25bb988794 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -76,7 +76,7 @@ struct spi_device *spi_new_device(struct spi_master *master, 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 70ded6ded1..0ee8808a6b 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -431,9 +431,10 @@ int usb_new_device(struct usb_device *dev) 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 ed99b53df7..e357456098 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -179,7 +179,7 @@ int usb_add_gadget_udc_release(struct device_d *parent, struct usb_gadget *gadge 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 @@ int usb_add_gadget_udc_release(struct device_d *parent, struct usb_gadget *gadge 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 431b97ea9b..5fe3bcb7cd 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -333,7 +333,7 @@ static int dsps_register_otg_device(struct dsps_glue *glue) { 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 30d52fcb68..3913d1c4c9 100644 --- a/drivers/video/backlight.c +++ b/drivers/video/backlight.c @@ -71,7 +71,7 @@ int backlight_register(struct backlight_device *bl) { 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 c9d184d502..72f33a6db6 100644 --- a/drivers/video/fb.c +++ b/drivers/video/fb.c @@ -309,7 +309,7 @@ int register_framebuffer(struct fb_info *info) 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 ff573860ea..694ffa853e 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -423,7 +423,7 @@ static int w1_device_register(struct w1_bus *bus, struct w1_device *dev) 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 @@ int w1_bus_register(struct w1_bus *bus) 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 56e8626cb1..e6e5ddecd2 100644 --- a/drivers/watchdog/wd_core.c +++ b/drivers/watchdog/wd_core.c @@ -104,7 +104,7 @@ static int watchdog_register_dev(struct watchdog *wd, const char *name, int id) { 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 d76d829140..57eaaf5854 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -2757,7 +2757,7 @@ int mount(const char *device, const char *fsname, const char *pathname, 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 1b61f20660..c12ca9c2f7 100644 --- a/include/driver.h +++ b/include/driver.h @@ -38,9 +38,12 @@ struct platform_device_id { /** @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. */ + /*! 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[MAX_DRIVER_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 @@ -177,6 +180,8 @@ 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/net/eth.c b/net/eth.c index 9dc4411952..36260478d8 100644 --- a/net/eth.c +++ b/net/eth.c @@ -363,7 +363,7 @@ int eth_register(struct eth_device *edev) return -1; } - strcpy(edev->dev.name, "eth"); + dev_set_name(&edev->dev, "eth"); if (edev->parent) edev->dev.parent = edev->parent; -- cgit v1.2.3 From e317adeeb7c50a5eeb0a88da39857c7cc6f9b366 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Oct 2018 12:15:41 -0700 Subject: base: Don't use shared buffer for results of dev_id() Using shared memory buffer to return results of dev_id() leads to incorrect results when used as follows: dev_info(..., "... %s ...\n", ..., dev_name(foo), ...); since result returned for dev_name(foo) will be overwritten by dev_name() call that will happen as a part of dev_* logging functions. To prevent that allocate a dedicated field "unique_name" in struct device_d and use it to store unique name returned by dev_id()/dev_name(). Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/base/driver.c | 19 +++++++------------ include/driver.h | 11 ++++++++++- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/drivers/base/driver.c b/drivers/base/driver.c index c9e6e6ddd6..b7720f31ab 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -183,6 +183,13 @@ int register_device(struct device_d *new_device) } } + if (new_device->id != DEVICE_ID_SINGLE) + snprintf(new_device->unique_name, + sizeof(new_device->unique_name), + 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); @@ -466,18 +473,6 @@ int dummy_probe(struct device_d *dev) } EXPORT_SYMBOL(dummy_probe); -const char *dev_id(const struct device_d *dev) -{ - static char buf[MAX_DRIVER_NAME + 16]; - - 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); - - return buf; -} - /** * dev_set_name - set a device name * @dev: device diff --git a/include/driver.h b/include/driver.h index c12ca9c2f7..94fddc735e 100644 --- a/include/driver.h +++ b/include/driver.h @@ -45,6 +45,12 @@ struct device_d { * be used instead. */ char name[MAX_DRIVER_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[MAX_DRIVER_NAME + 16]; /*! 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. */ @@ -173,7 +179,10 @@ int get_free_deviceid(const char *name_template); 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) { -- cgit v1.2.3 From 3e06eceaf441cfbe02dc4c436dc77ac28a0e5cb9 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Oct 2018 12:15:42 -0700 Subject: drivers: base: Convert device_d name to be dynamically allocated Convert device_d name to be dynamically allocated in order to lift MAX_DRIVER_NAME length limit needed for commits that follow. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/base/driver.c | 23 +++++++++++++++++------ include/driver.h | 5 ++--- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/base/driver.c b/drivers/base/driver.c index b7720f31ab..1941a972cc 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -184,11 +184,9 @@ int register_device(struct device_d *new_device) } if (new_device->id != DEVICE_ID_SINGLE) - snprintf(new_device->unique_name, - sizeof(new_device->unique_name), - FORMAT_DRIVER_NAME_ID, - new_device->name, - new_device->id); + new_device->unique_name = basprintf(FORMAT_DRIVER_NAME_ID, + new_device->name, + new_device->id); debug ("register_device: %s\n", dev_name(new_device)); @@ -478,16 +476,29 @@ EXPORT_SYMBOL(dummy_probe); * @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, ...) { va_list vargs; int err; + /* + * Save old pointer in case we are overriding already set name + */ + char *oldname = dev->name; va_start(vargs, fmt); - err = vsnprintf(dev->name, sizeof(dev->name), fmt, vargs); + err = vasprintf(&dev->name, fmt, vargs); va_end(vargs); + /* + * 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; diff --git a/include/driver.h b/include/driver.h index 94fddc735e..db844aed3d 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 @@ -44,13 +43,13 @@ struct device_d { * should not be modified directly and dev_set_name() should * be used instead. */ - char name[MAX_DRIVER_NAME]; + 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[MAX_DRIVER_NAME + 16]; + 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. */ -- cgit v1.2.3 From c41dfceb951b795357a014205faebac2853f7c02 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Oct 2018 12:15:43 -0700 Subject: linux: string: Port kbasename() Port kbasename() from Linux and use it to implement basename() we already have. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- include/linux/string.h | 12 ++++++++++++ lib/libgen.c | 16 ++-------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/include/linux/string.h b/include/linux/string.h index 3418b4fbe4..c9823dab8e 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -111,6 +111,18 @@ extern char *strim(char *); 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/lib/libgen.c b/lib/libgen.c index 08ef3528af..61a8670fb5 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); -- cgit v1.2.3 From 7e497d48acbd1184e3f6bd23bd16dfae84dd1129 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Oct 2018 12:15:44 -0700 Subject: of: Port latest of_device_make_bus_id() implementation Code implementing of_device_make_bus_id() in Barebox uses rather old implementation from Linux kernel and has a very significan limitation in that it will produce identical names for different DT nodes that happen to have the same node name as well as "reg" property. One such example, that tirggered this change, is "switch@0" nodes that can be found in dts/src/arm/vf610-zii-dev-rev-c.dts This commit replaces original code with the current Linux kernel implementation that traverses DT hierarchy from leaf node to its root concatenating node names in the process. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/of/platform.c | 80 ++++++++++++++------------------------------------- 1 file changed, 22 insertions(+), 58 deletions(-) diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 4fd3ce2b75..17052f4199 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -43,71 +43,35 @@ EXPORT_SYMBOL(of_find_device_by_node); * 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 - dev_set_name(dev, "d%x.%s", *reg, name); -#else /* CONFIG_PPC_DCR_NATIVE */ - u64 addr = of_translate_dcr_address(np, *reg, NULL); - if (addr != OF_BAD_ADDR) { - dev_set_name(dev, "D%llx.%s", - (unsigned long long)addr, name); - free(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) { - dev_set_name(dev, "%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; } - } - /* - * No BusID, use the node name and add a globally incremented counter - */ - dev_set_name(dev, "%s.%d", name, bus_no_reg_magic++); - free(name); + /* 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; + } } /** -- cgit v1.2.3 From 11ed45da4c23192f0882210cba0f7525eee9116a Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Oct 2018 12:15:45 -0700 Subject: mdio_bus: Fix documentation for mdio_bus_match() Fix documentation for mdio_bus_match(). While at it, re-arrange the code to be easier to follow. Seeing != used as a result of a matching function is extremely confusing. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/net/phy/mdio_bus.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 177d54863c..d7d6d8940b 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -335,16 +335,19 @@ EXPORT_SYMBOL(of_mdio_find_bus); * @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) -- cgit v1.2.3 From e28b3b1b67ac935645206f916f9ab026f855163f Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Oct 2018 12:15:46 -0700 Subject: include: linux: phy: Add missing PHY_INTERFACE_* constants Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- include/linux/phy.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/linux/phy.h b/include/linux/phy.h index ac750f5c3a..5b2c63ff69 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -54,7 +54,16 @@ typedef enum { 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 -- cgit v1.2.3 From 1412ea2d2a43867ba27fdad38bf05199e03009d7 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Oct 2018 12:15:47 -0700 Subject: include: linux: ethtool: Add missing *_UNKNOWN constants Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- include/linux/ethtool.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 4d83fe019e..324e40cdeb 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 -- cgit v1.2.3 From 01c926daf58ebd18728ddcd989c5e8f20751ecae Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Oct 2018 12:15:48 -0700 Subject: net: phy: Check phy_mask in get_phy_device() Do not try to probe PHY devices if they are masked in phy_mask. This way we won't try adding devices that are unlikely to be proper PHYs by default. With this change it still remains possible to add such a device explicitly either using "miitool" or calling phy_device_create() explicilty. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/net/phy/phy.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 19d458e07e..b985b75673 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -234,6 +234,10 @@ struct phy_device *get_phy_device(struct mii_bus *bus, int addr) 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); @@ -440,9 +444,6 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, } 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)) -- cgit v1.2.3 From 0247554a3629f59debb92e4188d45d3d98d2aded Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Oct 2018 12:15:49 -0700 Subject: mdio_bus: Allow for non PHY-devices on MDIO buses Instead of just creating a simple PHY device for every child of MDIO bus node, add code to check if any of them have "compatible" property set, as well as code to create a proper platform device for such cases. This change is useful for when MDIO bus and some of Ethernet ports are connected to a switch or some other MDIO device that doesn't behave like a generic PHY and can't be probed via its PHY ID. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/net/phy/mdio_bus.c | 14 +++++++++++++- include/of.h | 6 ++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index d7d6d8940b..cda05afae7 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 @@ static int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) /* 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) { diff --git a/include/of.h b/include/of.h index 7fc4b7791f..184acb4741 100644 --- a/include/of.h +++ b/include/of.h @@ -321,6 +321,12 @@ static inline int of_set_root_node(struct device_node *node) 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; -- cgit v1.2.3 From 368d297936f0fefa773006a67167519c62e84aef Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Oct 2018 12:15:50 -0700 Subject: net: phy: Add basic driver for MV88E6XXX switches from Marvell Port a very abridged version of MV88E6XXX DSA driver from Linux kernel. Currently only internal MDIO bus connected to switch PHYs is exposed. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/net/phy/Kconfig | 6 + drivers/net/phy/Makefile | 1 + drivers/net/phy/mv88e6xxx/Makefile | 5 + drivers/net/phy/mv88e6xxx/chip.c | 723 ++++++++++++++++++++++++++++++++++++ drivers/net/phy/mv88e6xxx/chip.h | 61 +++ drivers/net/phy/mv88e6xxx/global2.c | 124 +++++++ drivers/net/phy/mv88e6xxx/global2.h | 41 ++ drivers/net/phy/mv88e6xxx/port.c | 20 + drivers/net/phy/mv88e6xxx/port.h | 89 +++++ 9 files changed, 1070 insertions(+) create mode 100644 drivers/net/phy/mv88e6xxx/Makefile create mode 100644 drivers/net/phy/mv88e6xxx/chip.c create mode 100644 drivers/net/phy/mv88e6xxx/chip.h create mode 100644 drivers/net/phy/mv88e6xxx/global2.c create mode 100644 drivers/net/phy/mv88e6xxx/global2.h create mode 100644 drivers/net/phy/mv88e6xxx/port.c create mode 100644 drivers/net/phy/mv88e6xxx/port.h diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 79fb917ee7..3b1a6ea7e3 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -48,6 +48,12 @@ config SMSC_PHY ---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 4424054d91..e4d9ec65a3 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_MARVELL_PHY) += marvell.o 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/mv88e6xxx/Makefile b/drivers/net/phy/mv88e6xxx/Makefile new file mode 100644 index 0000000000..e09ea0aa47 --- /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 0000000000..cc4bfc02ec --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/chip.c @@ -0,0 +1,723 @@ +#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, +}; + +static const struct mv88e6xxx_ops mv88e6123_ops = { + /* MV88E6XXX_FAMILY_6165 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, +}; + +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 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, +}; + +static const struct mv88e6xxx_ops mv88e6161_ops = { + /* MV88E6XXX_FAMILY_6165 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, +}; + +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, +}; + +static const struct mv88e6xxx_ops mv88e6172_ops = { + /* MV88E6XXX_FAMILY_6352 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, +}; + +static const struct mv88e6xxx_ops mv88e6175_ops = { + /* MV88E6XXX_FAMILY_6351 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, +}; + +static const struct mv88e6xxx_ops mv88e6176_ops = { + /* MV88E6XXX_FAMILY_6352 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, +}; + +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 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, +}; + +static const struct mv88e6xxx_ops mv88e6190x_ops = { + /* MV88E6XXX_FAMILY_6390 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, +}; + +static const struct mv88e6xxx_ops mv88e6191_ops = { + /* MV88E6XXX_FAMILY_6390 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, +}; + +static const struct mv88e6xxx_ops mv88e6240_ops = { + /* MV88E6XXX_FAMILY_6352 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, +}; + +static const struct mv88e6xxx_ops mv88e6290_ops = { + /* MV88E6XXX_FAMILY_6390 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, +}; + +static const struct mv88e6xxx_ops mv88e6320_ops = { + /* MV88E6XXX_FAMILY_6320 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, +}; + +static const struct mv88e6xxx_ops mv88e6321_ops = { + /* MV88E6XXX_FAMILY_6320 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, +}; + +static const struct mv88e6xxx_ops mv88e6341_ops = { + /* MV88E6XXX_FAMILY_6341 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, +}; + +static const struct mv88e6xxx_ops mv88e6350_ops = { + /* MV88E6XXX_FAMILY_6351 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, +}; + +static const struct mv88e6xxx_ops mv88e6351_ops = { + /* MV88E6XXX_FAMILY_6351 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, +}; + +static const struct mv88e6xxx_ops mv88e6352_ops = { + /* MV88E6XXX_FAMILY_6352 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, +}; + +static const struct mv88e6xxx_ops mv88e6390_ops = { + /* MV88E6XXX_FAMILY_6390 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, +}; + +static const struct mv88e6xxx_ops mv88e6390x_ops = { + /* MV88E6XXX_FAMILY_6390 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, +}; + +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_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; + 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; + chip->info = of_device_get_match_data(dev); + + 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; + /* + * In single-chip address mode addresses 0x10 - 0x1f are + * reserved to access various switch registers and do not + * correspond to any PHYs, so we mask them to pervent from + * being exposed as phy devices + */ + chip->parent_miibus->phy_mask |= GENMASK(0x1f, 0x10); + /* + * Address 0x0f on internal bus is dedicated to SERDES + * registers and won't be very useful against standard PHY + * driver + */ + chip->miibus.phy_mask |= GENMASK(0x1f, 0x0f); + + 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; + + 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 0000000000..6c3f0705b4 --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/chip.h @@ -0,0 +1,61 @@ +#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; + +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_chip { + const struct mv88e6xxx_info *info; + struct mii_bus *parent_miibus; + struct mii_bus miibus; + struct device_d *dev; + int reset; +}; + +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 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 0000000000..a3c7bc0e53 --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/global2.c @@ -0,0 +1,124 @@ +#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); +} + +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 0000000000..0d8e9f3594 --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/global2.h @@ -0,0 +1,41 @@ +#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 + +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); + +#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 0000000000..59afdbd975 --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/port.c @@ -0,0 +1,20 @@ +#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); +} diff --git a/drivers/net/phy/mv88e6xxx/port.h b/drivers/net/phy/mv88e6xxx/port.h new file mode 100644 index 0000000000..7bb2e8bb4c --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/port.h @@ -0,0 +1,89 @@ +#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 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 + +/* 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 MV88E6390_PORT_MAC_CTL_FORCE_SPEED 0x2000 +#define MV88E6390_PORT_MAC_CTL_ALTSPEED 0x1000 +#define MV88E6352_PORT_MAC_CTL_200BASE 0x1000 +#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 + +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); + + +#endif /* _MV88E6XXX_PORT_H */ -- cgit v1.2.3 From 367aeaba0f6ea5b2221ab80fc1e69f751da0d9a8 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Oct 2018 12:15:51 -0700 Subject: net: phy: mv88e6xxx: Port EEPROM support code Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/net/phy/mv88e6xxx/chip.c | 84 ++++++++++++ drivers/net/phy/mv88e6xxx/chip.h | 10 ++ drivers/net/phy/mv88e6xxx/global2.c | 265 ++++++++++++++++++++++++++++++++++++ drivers/net/phy/mv88e6xxx/global2.h | 29 ++++ 4 files changed, 388 insertions(+) diff --git a/drivers/net/phy/mv88e6xxx/chip.c b/drivers/net/phy/mv88e6xxx/chip.c index cc4bfc02ec..7eb961fc0d 100644 --- a/drivers/net/phy/mv88e6xxx/chip.c +++ b/drivers/net/phy/mv88e6xxx/chip.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -79,6 +80,8 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { 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, }; @@ -104,6 +107,8 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { 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, }; @@ -116,6 +121,8 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { 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, }; @@ -129,48 +136,64 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { 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, }; 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, }; 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, }; 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, }; 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, }; 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, }; 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, }; 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, }; @@ -191,16 +214,22 @@ 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, }; 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, }; 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, }; @@ -621,12 +650,48 @@ static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *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; @@ -643,8 +708,11 @@ static int mv88e6xxx_probe(struct device_d *dev) 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; @@ -676,6 +744,22 @@ static int mv88e6xxx_probe(struct device_d *dev) 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 - 0x1f are * reserved to access various switch registers and do not diff --git a/drivers/net/phy/mv88e6xxx/chip.h b/drivers/net/phy/mv88e6xxx/chip.h index 6c3f0705b4..3a798a11ea 100644 --- a/drivers/net/phy/mv88e6xxx/chip.h +++ b/drivers/net/phy/mv88e6xxx/chip.h @@ -45,6 +45,11 @@ struct mv88e6xxx_chip { int reset; }; +struct ethtool_eeprom { + __u32 offset; + __u32 len; +}; + struct mv88e6xxx_ops { int (*phy_read)(struct mv88e6xxx_chip *chip, struct mii_bus *bus, @@ -52,6 +57,11 @@ struct mv88e6xxx_ops { 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); }; int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val); diff --git a/drivers/net/phy/mv88e6xxx/global2.c b/drivers/net/phy/mv88e6xxx/global2.c index a3c7bc0e53..970a7291e7 100644 --- a/drivers/net/phy/mv88e6xxx/global2.c +++ b/drivers/net/phy/mv88e6xxx/global2.c @@ -18,6 +18,271 @@ 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, diff --git a/drivers/net/phy/mv88e6xxx/global2.h b/drivers/net/phy/mv88e6xxx/global2.h index 0d8e9f3594..4e23b04232 100644 --- a/drivers/net/phy/mv88e6xxx/global2.h +++ b/drivers/net/phy/mv88e6xxx/global2.h @@ -27,6 +27,26 @@ /* 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); @@ -38,4 +58,13 @@ 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 */ -- cgit v1.2.3 From ac7324a0ed12a67fc836b383388f2562d32e55ed Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 16 Oct 2018 12:15:52 -0700 Subject: net: phy: mv88e6xxx: Add support for MAC ports Add support for MAC-only/no-PHY ports by adding code that will create a pseudo PHY device for such ports and a driver to match against. Original Linux driver exposes all of the ports using DSA subystem, but lacking that in Barebox, creating pseudo PHYs is the simplest option to have code to properly configure various interface parameters. This is needed, for example, on ZII RPU2 board where FEC is connected directly to one of the MAC-only ports. That link requires proper RGMII delay configuration in order to be functional. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/net/phy/mv88e6xxx/chip.c | 124 +++++++- drivers/net/phy/mv88e6xxx/chip.h | 71 +++++ drivers/net/phy/mv88e6xxx/port.c | 643 +++++++++++++++++++++++++++++++++++++++ drivers/net/phy/mv88e6xxx/port.h | 48 +++ 4 files changed, 877 insertions(+), 9 deletions(-) diff --git a/drivers/net/phy/mv88e6xxx/chip.c b/drivers/net/phy/mv88e6xxx/chip.c index 7eb961fc0d..ac08b5ef54 100644 --- a/drivers/net/phy/mv88e6xxx/chip.c +++ b/drivers/net/phy/mv88e6xxx/chip.c @@ -63,12 +63,20 @@ 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 = { @@ -84,12 +92,20 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .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 = { @@ -103,6 +119,11 @@ 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 = { @@ -111,12 +132,22 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .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 = { @@ -125,6 +156,11 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .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 = { @@ -140,6 +176,11 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .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 = { @@ -148,6 +189,11 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .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 = { @@ -156,6 +202,11 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .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 = { @@ -164,6 +215,11 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .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 = { @@ -172,6 +228,11 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .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 = { @@ -180,6 +241,10 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .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 = { @@ -188,6 +253,10 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .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 = { @@ -196,18 +265,33 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .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 = { @@ -216,6 +300,11 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .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 = { @@ -224,6 +313,12 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .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 = { @@ -232,6 +327,12 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .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[] = { @@ -761,18 +862,19 @@ static int mv88e6xxx_probe(struct device_d *dev) } /* - * In single-chip address mode addresses 0x10 - 0x1f are - * reserved to access various switch registers and do not - * correspond to any PHYs, so we mask them to pervent from - * being exposed as phy devices + * 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, 0x10); + chip->parent_miibus->phy_mask |= GENMASK(0x1f, + chip->info->port_base_addr); /* - * Address 0x0f on internal bus is dedicated to SERDES - * registers and won't be very useful against standard PHY - * driver + * 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, 0x0f); + chip->miibus.phy_mask |= GENMASK(0x1f, 0x00); chip->miibus.read = mv88e6xxx_mdio_read; chip->miibus.write = mv88e6xxx_mdio_write; @@ -784,6 +886,10 @@ static int mv88e6xxx_probe(struct device_d *dev) if (mdio_node) chip->miibus.dev.device_node = mdio_node; + err = mv88e6xxx_port_probe(chip); + if (err) + return err; + return mdiobus_register(&chip->miibus); } diff --git a/drivers/net/phy/mv88e6xxx/chip.h b/drivers/net/phy/mv88e6xxx/chip.h index 3a798a11ea..7548358de0 100644 --- a/drivers/net/phy/mv88e6xxx/chip.h +++ b/drivers/net/phy/mv88e6xxx/chip.h @@ -26,6 +26,8 @@ enum mv88e6xxx_family { struct mv88e6xxx_ops; +#define DSA_MAX_PORTS 12 + struct mv88e6xxx_info { enum mv88e6xxx_family family; u16 prod_num; @@ -37,12 +39,19 @@ struct mv88e6xxx_info { 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 { @@ -50,6 +59,16 @@ struct ethtool_eeprom { __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, @@ -62,6 +81,58 @@ struct mv88e6xxx_ops { 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); diff --git a/drivers/net/phy/mv88e6xxx/port.c b/drivers/net/phy/mv88e6xxx/port.c index 59afdbd975..52f95d622c 100644 --- a/drivers/net/phy/mv88e6xxx/port.c +++ b/drivers/net/phy/mv88e6xxx/port.c @@ -1,6 +1,9 @@ #include #include +#include +#include + #include "port.h" int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, @@ -18,3 +21,643 @@ int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, 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 index 7bb2e8bb4c..07d937ecbd 100644 --- a/drivers/net/phy/mv88e6xxx/port.h +++ b/drivers/net/phy/mv88e6xxx/port.h @@ -15,6 +15,7 @@ #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 @@ -27,14 +28,32 @@ #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 @@ -80,10 +99,39 @@ #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 */ -- cgit v1.2.3