diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2018-11-09 10:55:05 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2018-11-09 10:55:05 +0100 |
commit | 227426f16163d3721fee82650980aae0eef01375 (patch) | |
tree | a64101906c15b814ec942468e88875ce8160e45d /drivers | |
parent | c024f90f5dd5604d3f82dbbc055fd88a69d59187 (diff) | |
parent | ac7324a0ed12a67fc836b383388f2562d32e55ed (diff) | |
download | barebox-227426f16163d3721fee82650980aae0eef01375.tar.gz barebox-227426f16163d3721fee82650980aae0eef01375.tar.xz |
Merge branch 'for-next/net-switch-mv88e6xxx'
Diffstat (limited to 'drivers')
50 files changed, 2772 insertions, 122 deletions
diff --git a/drivers/aiodev/core.c b/drivers/aiodev/core.c index 79f935d710..b5d06da932 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) { @@ -115,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/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])); 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; 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 3b39c28494..4acc4cfa1e 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -183,6 +183,11 @@ int register_device(struct device_d *new_device) } } + if (new_device->id != DEVICE_ID_SINGLE) + new_device->unique_name = basprintf(FORMAT_DRIVER_NAME_ID, + new_device->name, + new_device->id); + debug ("register_device: %s\n", dev_name(new_device)); list_add_tail(&new_device->list, &device_list); @@ -443,17 +448,39 @@ int dummy_probe(struct device_d *dev) } EXPORT_SYMBOL(dummy_probe); -const char *dev_id(const struct device_d *dev) +/** + * dev_set_name - set a device name + * @dev: device + * @fmt: format string for the device's name + * + * NOTE: This function expects dev->name to be free()-able, so extra + * precautions needs to be taken when mixing its usage with manual + * assignement of device_d.name. + */ +int dev_set_name(struct device_d *dev, const char *fmt, ...) { - static char buf[MAX_DRIVER_NAME + 16]; - - 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; + 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 = 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; } +EXPORT_SYMBOL_GPL(dev_set_name); static void devices_shutdown(void) { 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/clocksource/Kconfig b/drivers/clocksource/Kconfig index 3d63f7ff16..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 @@ -70,3 +73,10 @@ config CLOCKSOURCE_ARMV8_TIMER bool default y depends on ARM && CPU_64v8 + +config CLOCKSOURCE_ARM_GLOBAL_TIMER + bool + depends on ARM && CPU_V7 +config CLOCKSOURCE_IMX_GPT + def_bool y + depends on ARCH_HAS_IMX_GPT diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index ea33fff502..ce4d74137a 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -12,3 +12,5 @@ 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 +obj-$(CONFIG_CLOCKSOURCE_IMX_GPT) += timer-imx-gpt.o diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c new file mode 100644 index 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 <andrew.smirnov@gmail.com> + * + * 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 <stuart.menefy@st.com> + * Author: Srinivas Kandagatla <srinivas.kandagatla@st.com> + */ +#include <common.h> +#include <init.h> +#include <clock.h> +#include <linux/clk.h> +#include <io.h> +#include <asm/system.h> + +#define GT_COUNTER0 0x00 +#define GT_COUNTER1 0x04 + +#define GT_CONTROL 0x08 +#define GT_CONTROL_TIMER_ENABLE BIT(0) /* this bit is NOT banked */ + +static void __iomem *gt_base; + +/* + * To get the value from the Global Timer Counter register proceed as follows: + * 1. Read the upper 32-bit timer counter register + * 2. Read the lower 32-bit timer counter register + * 3. Read the upper 32-bit timer counter register again. If the value is + * different to the 32-bit upper value read previously, go back to step 2. + * Otherwise the 64-bit timer counter value is correct. + */ +static uint64_t arm_global_clocksource_read(void) +{ + uint64_t counter; + uint32_t lower; + uint32_t upper, old_upper; + + upper = readl(gt_base + GT_COUNTER1); + do { + old_upper = upper; + lower = readl(gt_base + GT_COUNTER0); + upper = readl(gt_base + GT_COUNTER1); + } while (upper != old_upper); + + counter = upper; + counter <<= 32; + counter |= lower; + return counter; +} + +static struct clocksource cs = { + .read = arm_global_clocksource_read, + .mask = CLOCKSOURCE_MASK(64), + .shift = 0, +}; + +static int arm_global_timer_probe(struct device_d *dev) +{ + struct resource *iores; + struct clk *clk; + int ret; + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + + clk = clk_get(dev, NULL); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + dev_err(dev, "clock not found: %d\n", ret); + return ret; + } + + ret = clk_enable(clk); + if (ret) { + dev_err(dev, "clock failed to enable: %d\n", ret); + return ret; + } + + gt_base = IOMEM(iores->start); + + cs.mult = clocksource_hz2mult(clk_get_rate(clk), cs.shift); + + writel(0, gt_base + GT_CONTROL); + writel(0, gt_base + GT_COUNTER0); + writel(0, gt_base + GT_COUNTER1); + /* enables timer on all the cores */ + writel(GT_CONTROL_TIMER_ENABLE, gt_base + GT_CONTROL); + + return init_clock(&cs); +} + +static struct of_device_id arm_global_timer_dt_ids[] = { + { .compatible = "arm,cortex-a9-global-timer", }, + { } +}; + +static struct driver_d arm_global_timer_driver = { + .name = "arm-global-timer", + .probe = arm_global_timer_probe, + .of_compatible = DRV_OF_COMPAT(arm_global_timer_dt_ids), +}; +postcore_platform_driver(arm_global_timer_driver); + diff --git a/drivers/clocksource/timer-imx-gpt.c b/drivers/clocksource/timer-imx-gpt.c new file mode 100644 index 0000000000..4d6c6c2b0b --- /dev/null +++ b/drivers/clocksource/timer-imx-gpt.c @@ -0,0 +1,192 @@ +/* + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Alex Zuepke <azu@sysgo.de> + * + * (C) Copyright 2002 + * Gary Jennejohn, DENX Software Engineering, <gj@denx.de> + * + * 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 <common.h> +#include <init.h> +#include <clock.h> +#include <errno.h> +#include <of.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <notifier.h> +#include <io.h> + +/* Part 1: Registers */ +# define GPT_TCTL 0x00 +# define GPT_TPRER 0x04 + +/* Part 2: Bitfields */ +#define TCTL_SWR (1 << 15) /* Software reset */ +#define IMX1_TCTL_FRR (1 << 8) /* Freerun / restart */ +#define IMX31_TCTL_FRR (1 << 9) /* Freerun / restart */ +#define IMX1_TCTL_CLKSOURCE_PER (1 << 1) /* Clock source bit position */ +#define IMX31_TCTL_CLKSOURCE_IPG (1 << 6) /* Clock source bit position */ +#define IMX31_TCTL_CLKSOURCE_PER (2 << 6) /* Clock source bit position */ +#define TCTL_TEN (1 << 0) /* Timer enable */ + +static struct clk *clk_gpt; + +struct imx_gpt_regs { + unsigned int tcn; + uint32_t tctl_val; +}; + +static struct imx_gpt_regs regs_imx1 = { + .tcn = 0x10, + .tctl_val = IMX1_TCTL_FRR | IMX1_TCTL_CLKSOURCE_PER | TCTL_TEN, +}; + +static struct imx_gpt_regs regs_imx31 = { + .tcn = 0x24, + .tctl_val = IMX31_TCTL_FRR | IMX31_TCTL_CLKSOURCE_PER | TCTL_TEN, +}; + +static struct imx_gpt_regs *regs; +static void __iomem *timer_base; + +static uint64_t imx_clocksource_read(void) +{ + return readl(timer_base + regs->tcn); +} + +static struct clocksource cs = { + .read = imx_clocksource_read, + .mask = CLOCKSOURCE_MASK(32), + .shift = 10, +}; + +static int imx_clocksource_clock_change(struct notifier_block *nb, unsigned long event, void *data) +{ + cs.mult = clocksource_hz2mult(clk_get_rate(clk_gpt), cs.shift); + return 0; +} + +static struct notifier_block imx_clock_notifier = { + .notifier_call = imx_clocksource_clock_change, +}; + +static int imx_gpt_probe(struct device_d *dev) +{ + struct resource *iores; + int i; + int ret; + unsigned long rate; + + /* one timer is enough */ + if (timer_base) + return 0; + + ret = dev_get_drvdata(dev, (const void **)®s); + if (ret) + return ret; + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + timer_base = IOMEM(iores->start); + + /* setup GP Timer 1 */ + writel(TCTL_SWR, timer_base + GPT_TCTL); + + for (i = 0; i < 100; i++) + writel(0, timer_base + GPT_TCTL); /* We have no udelay by now */ + + clk_gpt = clk_get(dev, "per"); + if (IS_ERR(clk_gpt)) { + rate = 20000000; + dev_err(dev, "failed to get clock, assume %lu Hz\n", rate); + } else { + rate = clk_get_rate(clk_gpt); + if (!rate) { + dev_err(dev, "clock reports rate == 0\n"); + return -EIO; + } + } + + writel(0, timer_base + GPT_TPRER); + writel(regs->tctl_val, timer_base + GPT_TCTL); + + cs.mult = clocksource_hz2mult(rate, cs.shift); + + init_clock(&cs); + + clock_register_client(&imx_clock_notifier); + + return 0; +} + +static __maybe_unused struct of_device_id imx_gpt_dt_ids[] = { + { + .compatible = "fsl,imx1-gpt", + .data = ®s_imx1, + }, { + .compatible = "fsl,imx21-gpt", + .data = ®s_imx1, + }, { + .compatible = "fsl,imx31-gpt", + .data = ®s_imx31, + }, { + .compatible = "fsl,imx6q-gpt", + .data = ®s_imx31, + }, { + .compatible = "fsl,imx6dl-gpt", + .data = ®s_imx31, + }, { + .compatible = "fsl,imx6ul-gpt", + .data = ®s_imx31, + }, { + .compatible = "fsl,imx7d-gpt", + .data = ®s_imx31, + }, { + /* sentinel */ + } +}; + +static struct platform_device_id imx_gpt_ids[] = { + { + .name = "imx1-gpt", + .driver_data = (unsigned long)®s_imx1, + }, { + .name = "imx31-gpt", + .driver_data = (unsigned long)®s_imx31, + }, { + /* sentinel */ + }, +}; + +static struct driver_d imx_gpt_driver = { + .name = "imx-gpt", + .probe = imx_gpt_probe, + .of_compatible = DRV_OF_COMPAT(imx_gpt_dt_ids), + .id_table = imx_gpt_ids, +}; + +static int imx_gpt_init(void) +{ + return platform_driver_register(&imx_gpt_driver); +} +postcore_initcall(imx_gpt_init); diff --git a/drivers/efi/efi-device.c b/drivers/efi/efi-device.c index 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/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/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 7d53bcc3da..cda05afae7 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -26,6 +26,7 @@ #include <errno.h> #include <linux/phy.h> #include <linux/err.h> +#include <of_device.h> #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) { @@ -222,7 +234,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; @@ -335,16 +347,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) 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..ac08b5ef54 --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/chip.c @@ -0,0 +1,913 @@ +#include <common.h> +#include <init.h> +#include <linux/mii.h> +#include <linux/ethtool.h> +#include <linux/phy.h> +#include <linux/bitfield.h> +#include <linux/nvmem-provider.h> + +#include <gpio.h> +#include <of_device.h> +#include <of_gpio.h> + +#include "chip.h" +#include "global2.h" +#include "port.h" + + +/* List of supported models */ +enum mv88e6xxx_model { + MV88E6085, + MV88E6095, + MV88E6097, + MV88E6123, + MV88E6131, + MV88E6141, + MV88E6161, + MV88E6165, + MV88E6171, + MV88E6172, + MV88E6175, + MV88E6176, + MV88E6185, + MV88E6190, + MV88E6190X, + MV88E6191, + MV88E6240, + MV88E6290, + MV88E6320, + MV88E6321, + MV88E6341, + MV88E6350, + MV88E6351, + MV88E6352, + MV88E6390, + MV88E6390X, +}; + +static const struct mv88e6xxx_ops mv88e6085_ops = { + /* MV88E6XXX_FAMILY_6097 */ + /* FIXME: Was not ported due to lack of HW */ + .phy_read = NULL, + .phy_write = NULL, +}; + +static const struct mv88e6xxx_ops mv88e6095_ops = { + /* MV88E6XXX_FAMILY_6095 */ + /* FIXME: Was not ported due to lack of HW */ + .phy_read = NULL, + .phy_write = NULL, +}; + +static const struct mv88e6xxx_ops mv88e6097_ops = { + /* MV88E6XXX_FAMILY_6097 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6123_ops = { + /* MV88E6XXX_FAMILY_6165 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6131_ops = { + /* MV88E6XXX_FAMILY_6185 */ + /* FIXME: Was not ported due to lack of HW */ + .phy_read = NULL, + .phy_write = NULL, +}; + +static const struct mv88e6xxx_ops mv88e6141_ops = { + /* MV88E6XXX_FAMILY_6341 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom8, + .set_eeprom = mv88e6xxx_g2_set_eeprom8, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6161_ops = { + /* MV88E6XXX_FAMILY_6165 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6165_ops = { + /* MV88E6XXX_FAMILY_6165 */ + /* FIXME: Was not ported due to lack of HW */ + .phy_read = NULL, + .phy_write = NULL, +}; + +static const struct mv88e6xxx_ops mv88e6171_ops = { + /* MV88E6XXX_FAMILY_6351 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6185_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6172_ops = { + /* MV88E6XXX_FAMILY_6352 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom16, + .set_eeprom = mv88e6xxx_g2_set_eeprom16, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6352_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6175_ops = { + /* MV88E6XXX_FAMILY_6351 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6185_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6176_ops = { + /* MV88E6XXX_FAMILY_6352 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom16, + .set_eeprom = mv88e6xxx_g2_set_eeprom16, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6352_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6185_ops = { + /* MV88E6XXX_FAMILY_6185 */ + /* FIXME: Was not ported due to lack of HW */ + .phy_read = NULL, + .phy_write = NULL, +}; + +static const struct mv88e6xxx_ops mv88e6190_ops = { + /* MV88E6XXX_FAMILY_6390 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom8, + .set_eeprom = mv88e6xxx_g2_set_eeprom8, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6190x_ops = { + /* MV88E6XXX_FAMILY_6390 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom8, + .set_eeprom = mv88e6xxx_g2_set_eeprom8, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390x_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6191_ops = { + /* MV88E6XXX_FAMILY_6390 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom8, + .set_eeprom = mv88e6xxx_g2_set_eeprom8, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6240_ops = { + /* MV88E6XXX_FAMILY_6352 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom16, + .set_eeprom = mv88e6xxx_g2_set_eeprom16, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6352_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6290_ops = { + /* MV88E6XXX_FAMILY_6390 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom8, + .set_eeprom = mv88e6xxx_g2_set_eeprom8, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390_port_set_speed, + .port_set_cmode = mv88e6390x_port_set_cmode, +}; + +static const struct mv88e6xxx_ops mv88e6320_ops = { + /* MV88E6XXX_FAMILY_6320 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom16, + .set_eeprom = mv88e6xxx_g2_set_eeprom16, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6321_ops = { + /* MV88E6XXX_FAMILY_6320 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom16, + .set_eeprom = mv88e6xxx_g2_set_eeprom16, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_speed = mv88e6185_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6341_ops = { + /* MV88E6XXX_FAMILY_6341 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom8, + .set_eeprom = mv88e6xxx_g2_set_eeprom8, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6350_ops = { + /* MV88E6XXX_FAMILY_6351 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6185_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6351_ops = { + /* MV88E6XXX_FAMILY_6351 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6185_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6352_ops = { + /* MV88E6XXX_FAMILY_6352 */ + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .get_eeprom = mv88e6xxx_g2_get_eeprom16, + .set_eeprom = mv88e6xxx_g2_set_eeprom16, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6352_port_set_speed, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6390_ops = { + /* MV88E6XXX_FAMILY_6390 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom8, + .set_eeprom = mv88e6xxx_g2_set_eeprom8, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390_port_set_speed, + .port_set_cmode = mv88e6390x_port_set_cmode, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_ops mv88e6390x_ops = { + /* MV88E6XXX_FAMILY_6390 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom8, + .set_eeprom = mv88e6xxx_g2_set_eeprom8, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390x_port_set_speed, + .port_set_cmode = mv88e6390x_port_set_cmode, + .port_link_state = mv88e6352_port_link_state, +}; + +static const struct mv88e6xxx_info mv88e6xxx_table[] = { + [MV88E6085] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6085, + .family = MV88E6XXX_FAMILY_6097, + .name = "Marvell 88E6085", + .num_ports = 10, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6085_ops, + }, + + [MV88E6095] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6095, + .family = MV88E6XXX_FAMILY_6095, + .name = "Marvell 88E6095/88E6095F", + .num_ports = 11, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6095_ops, + }, + + [MV88E6097] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6097, + .family = MV88E6XXX_FAMILY_6097, + .name = "Marvell 88E6097/88E6097F", + .num_ports = 11, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6097_ops, + }, + + [MV88E6123] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6123, + .family = MV88E6XXX_FAMILY_6165, + .name = "Marvell 88E6123", + .num_ports = 3, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6123_ops, + }, + + [MV88E6131] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6131, + .family = MV88E6XXX_FAMILY_6185, + .name = "Marvell 88E6131", + .num_ports = 8, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6131_ops, + }, + + [MV88E6141] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6141, + .family = MV88E6XXX_FAMILY_6341, + .name = "Marvell 88E6341", + .num_ports = 6, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6141_ops, + }, + + [MV88E6161] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6161, + .family = MV88E6XXX_FAMILY_6165, + .name = "Marvell 88E6161", + .num_ports = 6, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6161_ops, + }, + + [MV88E6165] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6165, + .family = MV88E6XXX_FAMILY_6165, + .name = "Marvell 88E6165", + .num_ports = 6, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6165_ops, + }, + + [MV88E6171] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6171, + .family = MV88E6XXX_FAMILY_6351, + .name = "Marvell 88E6171", + .num_ports = 7, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6171_ops, + }, + + [MV88E6172] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6172, + .family = MV88E6XXX_FAMILY_6352, + .name = "Marvell 88E6172", + .num_ports = 7, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6172_ops, + }, + + [MV88E6175] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6175, + .family = MV88E6XXX_FAMILY_6351, + .name = "Marvell 88E6175", + .num_ports = 7, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6175_ops, + }, + + [MV88E6176] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6176, + .family = MV88E6XXX_FAMILY_6352, + .name = "Marvell 88E6176", + .num_ports = 7, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6176_ops, + }, + + [MV88E6185] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6185, + .family = MV88E6XXX_FAMILY_6185, + .name = "Marvell 88E6185", + .num_ports = 10, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6185_ops, + }, + + [MV88E6190] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6190, + .family = MV88E6XXX_FAMILY_6390, + .name = "Marvell 88E6190", + .num_ports = 11, /* 10 + Z80 */ + .port_base_addr = 0x0, + .global2_addr = 0x1c, + .ops = &mv88e6190_ops, + }, + + [MV88E6190X] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6190X, + .family = MV88E6XXX_FAMILY_6390, + .name = "Marvell 88E6190X", + .num_ports = 11, /* 10 + Z80 */ + .port_base_addr = 0x0, + .global2_addr = 0x1c, + .ops = &mv88e6190x_ops, + }, + + [MV88E6191] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6191, + .family = MV88E6XXX_FAMILY_6390, + .name = "Marvell 88E6191", + .num_ports = 11, /* 10 + Z80 */ + .port_base_addr = 0x0, + .global2_addr = 0x1c, + .ops = &mv88e6191_ops, + }, + + [MV88E6240] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6240, + .family = MV88E6XXX_FAMILY_6352, + .name = "Marvell 88E6240", + .num_ports = 7, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6240_ops, + }, + + [MV88E6290] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6290, + .family = MV88E6XXX_FAMILY_6390, + .name = "Marvell 88E6290", + .num_ports = 11, /* 10 + Z80 */ + .port_base_addr = 0x0, + .global2_addr = 0x1c, + .ops = &mv88e6290_ops, + }, + + [MV88E6320] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6320, + .family = MV88E6XXX_FAMILY_6320, + .name = "Marvell 88E6320", + .num_ports = 7, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6320_ops, + }, + + [MV88E6321] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6321, + .family = MV88E6XXX_FAMILY_6320, + .name = "Marvell 88E6321", + .num_ports = 7, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6321_ops, + }, + + [MV88E6341] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6341, + .family = MV88E6XXX_FAMILY_6341, + .name = "Marvell 88E6341", + .num_ports = 6, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6341_ops, + }, + + [MV88E6350] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6350, + .family = MV88E6XXX_FAMILY_6351, + .name = "Marvell 88E6350", + .num_ports = 7, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6350_ops, + }, + + [MV88E6351] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6351, + .family = MV88E6XXX_FAMILY_6351, + .name = "Marvell 88E6351", + .num_ports = 7, + .port_base_addr = 0x10, + .global2_addr = 0x1c, + .ops = &mv88e6351_ops, + }, + + [MV88E6352] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6352, + .family = MV88E6XXX_FAMILY_6352, + .name = "Marvell 88E6352", + .num_ports = 7, + .port_base_addr = 0x10, + .ops = &mv88e6352_ops, + .global2_addr = 0x1c, + .ops = &mv88e6352_ops, + }, + + [MV88E6390] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6390, + .family = MV88E6XXX_FAMILY_6390, + .name = "Marvell 88E6390", + .num_ports = 11, /* 10 + Z80 */ + .port_base_addr = 0x0, + .global2_addr = 0x1c, + .ops = &mv88e6390_ops, + }, + + [MV88E6390X] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6390X, + .family = MV88E6XXX_FAMILY_6390, + .name = "Marvell 88E6390X", + .num_ports = 11, /* 10 + Z80 */ + .port_base_addr = 0x0, + .global2_addr = 0x1c, + .ops = &mv88e6390x_ops, + }, +}; + +int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val) +{ + int ret; + ret = mdiobus_write(chip->parent_miibus, addr, reg, val); + if (ret < 0) + return ret; + + dev_dbg(chip->dev, "<- addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n", + addr, reg, val); + + return 0; +} + +int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val) +{ + int ret; + + ret = mdiobus_read(chip->parent_miibus, addr, reg); + if (ret < 0) + return ret; + + *val = ret & 0xffff; + + dev_dbg(chip->dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n", + addr, reg, *val); + + return 0; +} + +int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask) +{ + int i; + + for (i = 0; i < 16; i++) { + u16 val; + int err; + + err = mv88e6xxx_read(chip, addr, reg, &val); + if (err) + return err; + + if (!(val & mask)) + return 0; + + udelay(2000); + } + + dev_err(chip->dev, "Timeout while waiting for switch\n"); + return -ETIMEDOUT; +} + +static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg) +{ + struct mv88e6xxx_chip *chip = bus->priv; + u16 val; + int err; + + if (!chip->info->ops->phy_read) + return -EOPNOTSUPP; + + err = chip->info->ops->phy_read(chip, bus, phy, reg, &val); + + if (reg == MII_PHYSID2) { + /* Some internal PHYS don't have a model number. Use + * the mv88e6390 family model number instead. + */ + if (!(val & 0x3f0)) + val |= MV88E6XXX_PORT_SWITCH_ID_PROD_6390 >> 4; + } + + return err ?: val; +} + +static int mv88e6xxx_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val) +{ + struct mv88e6xxx_chip *chip = bus->priv; + int err; + + if (!chip->info->ops->phy_write) + return -EOPNOTSUPP; + + err = chip->info->ops->phy_write(chip, bus, phy, reg, val); + + return err; +} + +static const struct mv88e6xxx_info * +mv88e6xxx_lookup_info(unsigned int prod_num) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mv88e6xxx_table); ++i) + if (mv88e6xxx_table[i].prod_num == prod_num) + return &mv88e6xxx_table[i]; + + return NULL; +} + +static int mv88e6xxx_detect(struct mv88e6xxx_chip *chip) +{ + const struct mv88e6xxx_info *info; + unsigned int prod_num, rev; + u16 id; + int err; + + err = mv88e6xxx_port_read(chip, 0, MV88E6XXX_PORT_SWITCH_ID, &id); + if (err) + return err; + + prod_num = id & MV88E6XXX_PORT_SWITCH_ID_PROD_MASK; + rev = id & MV88E6XXX_PORT_SWITCH_ID_REV_MASK; + + info = mv88e6xxx_lookup_info(prod_num); + if (!info) + return -ENODEV; + + /* Update the compatible info with the probed one */ + chip->info = info; + + dev_info(chip->dev, "switch 0x%x detected: %s, revision %u\n", + chip->info->prod_num, chip->info->name, rev); + + return 0; +} + +/* + * Linux driver has this delay at 20ms, but it doesn't seem to be + * enough in Barebox and trying to access switch registers immediately + * after this function will return all F's on some platforms + * tested. Increasing this to 50ms seem to resolve the issue. + */ +static void mv88e6xxx_hardware_reset_delay(void) +{ + udelay(50000); +} + +static void mv88e6xxx_hardware_reset(struct mv88e6xxx_chip *chip) +{ + /* If there is a GPIO connected to the reset pin, toggle it */ + if (gpio_is_valid(chip->reset)) { + gpio_set_active(chip->reset, 1); + mv88e6xxx_hardware_reset_delay(); + gpio_set_active(chip->reset, 0); + mv88e6xxx_hardware_reset_delay(); + } +} + +static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip) +{ + mv88e6xxx_hardware_reset(chip); + return 0; +} + +static int mv88e6xxx_eeprom_read(struct device_d *dev, const int offset, + void *val, int bytes) +{ + struct mv88e6xxx_chip *chip = dev->parent->priv; + struct ethtool_eeprom eeprom = { + .offset = offset, + .len = bytes, + }; + + if (!chip->info->ops->get_eeprom) + return -ENOTSUPP; + + return chip->info->ops->get_eeprom(chip, &eeprom, val); +} + +static int mv88e6xxx_eeprom_write(struct device_d *dev, const int offset, + const void *val, int bytes) +{ + struct mv88e6xxx_chip *chip = dev->parent->priv; + struct ethtool_eeprom eeprom = { + .offset = offset, + .len = bytes, + }; + + if (!chip->info->ops->set_eeprom) + return -ENOTSUPP; + + return chip->info->ops->set_eeprom(chip, &eeprom, (void *)val); +} + +static const struct nvmem_bus mv88e6xxx_eeprom_nvmem_bus = { + .write = mv88e6xxx_eeprom_write, + .read = mv88e6xxx_eeprom_read, +}; + +static int mv88e6xxx_probe(struct device_d *dev) +{ + struct device_node *np = dev->device_node; + struct device_node *mdio_node; + struct mv88e6xxx_chip *chip; + enum of_gpio_flags of_flags; + u32 eeprom_len = 0; + int err; + u32 reg; + + err = of_property_read_u32(np, "reg", ®); + if (err) { + dev_err(dev, "Couldn't determine switch MIDO address\n"); + return err; + } + + if (reg) { + dev_err(dev, "Only single-chip address mode is supported\n"); + return -ENOTSUPP; + } + + chip = xzalloc(sizeof(struct mv88e6xxx_chip)); + chip->dev = dev; + dev->priv = chip; + chip->info = of_device_get_match_data(dev); + + of_property_read_u32(np, "eeprom-length", &eeprom_len); + + chip->parent_miibus = of_mdio_find_bus(np->parent); + if (!chip->parent_miibus) + return -EPROBE_DEFER; + + chip->reset = of_get_named_gpio_flags(np, "reset-gpios", 0, &of_flags); + if (gpio_is_valid(chip->reset)) { + unsigned long flags = GPIOF_OUT_INIT_INACTIVE; + char *name; + + if (of_flags & OF_GPIO_ACTIVE_LOW) + flags |= GPIOF_ACTIVE_LOW; + + name = basprintf("%s reset", dev_name(dev)); + err = gpio_request_one(chip->reset, flags, name); + if (err < 0) + return err; + /* + * We assume that reset line was previously held low + * and give the switch time to initialize before + * trying to read its registers + */ + mv88e6xxx_hardware_reset_delay(); + } + + err = mv88e6xxx_detect(chip); + if (err) + return err; + + err = mv88e6xxx_switch_reset(chip); + if (err) + return err; + + if (eeprom_len) { + struct nvmem_config config = { + .name = basprintf("%s-eeprom", dev_name(dev)), + .dev = dev, + .word_size = 1, + .stride = 1, + .size = eeprom_len, + .read_only = false, + .bus = &mv88e6xxx_eeprom_nvmem_bus, + }; + + if (IS_ERR(nvmem_register(&config))) + dev_err(dev, "Failed to register EEPROM\n"); + } + + /* + * In single-chip address mode addresses 0x10 - + * port_base_address are reserved to access various switch + * registers and do not correspond to any PHYs, so we mask + * them to pervent from being exposed. + */ + chip->parent_miibus->phy_mask |= GENMASK(0x1f, + chip->info->port_base_addr); + /* + * Mask all of the devices on child MDIO bus. Call to + * mv88e6xxx_port_probe() will unmask port that can be probed + * using standard methods + */ + chip->miibus.phy_mask |= GENMASK(0x1f, 0x00); + + chip->miibus.read = mv88e6xxx_mdio_read; + chip->miibus.write = mv88e6xxx_mdio_write; + + chip->miibus.priv = chip; + chip->miibus.parent = dev; + + mdio_node = of_get_child_by_name(np, "mdio"); + if (mdio_node) + chip->miibus.dev.device_node = mdio_node; + + err = mv88e6xxx_port_probe(chip); + if (err) + return err; + + return mdiobus_register(&chip->miibus); +} + +static const struct of_device_id mv88e6xxx_of_match[] = { + { + .compatible = "marvell,mv88e6085", + .data = &mv88e6xxx_table[MV88E6085], + }, + { + .compatible = "marvell,mv88e6190", + .data = &mv88e6xxx_table[MV88E6190], + }, + {}, +}; + +static struct driver_d mv88e6xxx_driver = { + .name = "mv88e6085", + .probe = mv88e6xxx_probe, + .of_compatible = mv88e6xxx_of_match, +}; +device_platform_driver(mv88e6xxx_driver); diff --git a/drivers/net/phy/mv88e6xxx/chip.h b/drivers/net/phy/mv88e6xxx/chip.h new file mode 100644 index 0000000000..7548358de0 --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/chip.h @@ -0,0 +1,142 @@ +#ifndef _MV88E6XXX_CHIP_H +#define _MV88E6XXX_CHIP_H + +#include <common.h> +#include <init.h> +#include <linux/mii.h> +#include <linux/phy.h> + +/* sub-devices MDIO addresses */ +#define MV88E6XXX_SWITCH_GLOBAL_REGS_1 0x1b +#define MV88E6XXX_SWITCH_GLOBAL_REGS_2 0x1c + +enum mv88e6xxx_family { + MV88E6XXX_FAMILY_NONE, + MV88E6XXX_FAMILY_6065, /* 6031 6035 6061 6065 */ + MV88E6XXX_FAMILY_6095, /* 6092 6095 */ + MV88E6XXX_FAMILY_6097, /* 6046 6085 6096 6097 */ + MV88E6XXX_FAMILY_6165, /* 6123 6161 6165 */ + MV88E6XXX_FAMILY_6185, /* 6108 6121 6122 6131 6152 6155 6182 6185 */ + MV88E6XXX_FAMILY_6320, /* 6320 6321 */ + MV88E6XXX_FAMILY_6341, /* 6141 6341 */ + MV88E6XXX_FAMILY_6351, /* 6171 6175 6350 6351 */ + MV88E6XXX_FAMILY_6352, /* 6172 6176 6240 6352 */ + MV88E6XXX_FAMILY_6390, /* 6190 6190X 6191 6290 6390 6390X */ +}; + +struct mv88e6xxx_ops; + +#define DSA_MAX_PORTS 12 + +struct mv88e6xxx_info { + enum mv88e6xxx_family family; + u16 prod_num; + const char *name; + unsigned int num_ports; + unsigned int port_base_addr; + unsigned int global2_addr; + + const struct mv88e6xxx_ops *ops; +}; + +struct mv88e6xxx_port { + u8 cmode; +}; + +struct mv88e6xxx_chip { + const struct mv88e6xxx_info *info; + struct mii_bus *parent_miibus; + struct mii_bus miibus; + struct device_d *dev; + int reset; + + /* Array of port structures. */ + struct mv88e6xxx_port ports[DSA_MAX_PORTS]; +}; + +struct ethtool_eeprom { + __u32 offset; + __u32 len; +}; + +struct phylink_link_state { + phy_interface_t interface; + int speed; + int duplex; + int pause; + unsigned int link:1; + unsigned int an_enabled:1; + unsigned int an_complete:1; +}; + +struct mv88e6xxx_ops { + int (*phy_read)(struct mv88e6xxx_chip *chip, + struct mii_bus *bus, + int addr, int reg, u16 *val); + int (*phy_write)(struct mv88e6xxx_chip *chip, + struct mii_bus *bus, + int addr, int reg, u16 val); + + int (*get_eeprom)(struct mv88e6xxx_chip *chip, + struct ethtool_eeprom *eeprom, u8 *data); + int (*set_eeprom)(struct mv88e6xxx_chip *chip, + struct ethtool_eeprom *eeprom, u8 *data); + + /* RGMII Receive/Transmit Timing Control + * Add delay on PHY_INTERFACE_MODE_RGMII_*ID, no delay otherwise. + */ + int (*port_set_rgmii_delay)(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode); + +#define LINK_FORCED_DOWN 0 +#define LINK_FORCED_UP 1 +#define LINK_UNFORCED -2 + + /* Port's MAC link state + * Use LINK_FORCED_UP or LINK_FORCED_DOWN to force link up or down, + * or LINK_UNFORCED for normal link detection. + */ + int (*port_set_link)(struct mv88e6xxx_chip *chip, int port, int link); + +#define DUPLEX_UNFORCED -2 + + /* Port's MAC duplex mode + * + * Use DUPLEX_HALF or DUPLEX_FULL to force half or full duplex, + * or DUPLEX_UNFORCED for normal duplex detection. + */ + int (*port_set_duplex)(struct mv88e6xxx_chip *chip, int port, int dup); + +#define PAUSE_ON 1 +#define PAUSE_OFF 0 + + /* Enable/disable sending Pause */ + int (*port_set_pause)(struct mv88e6xxx_chip *chip, int port, + int pause); + +#define SPEED_MAX INT_MAX +#define SPEED_UNFORCED -2 + + /* Port's MAC speed (in Mbps) + * + * Depending on the chip, 10, 100, 200, 1000, 2500, 10000 are valid. + * Use SPEED_UNFORCED for normal detection, SPEED_MAX for max value. + */ + int (*port_set_speed)(struct mv88e6xxx_chip *chip, int port, int speed); + + /* CMODE control what PHY mode the MAC will use, eg. SGMII, RGMII, etc. + * Some chips allow this to be configured on specific ports. + */ + int (*port_set_cmode)(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode); + + /* Return the port link state, as required by phylink */ + int (*port_link_state)(struct mv88e6xxx_chip *chip, int port, + struct phylink_link_state *state); +}; + +int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val); +int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val); +int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask); + +#endif /* _MV88E6XXX_CHIP_H */ diff --git a/drivers/net/phy/mv88e6xxx/global2.c b/drivers/net/phy/mv88e6xxx/global2.c new file mode 100644 index 0000000000..970a7291e7 --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/global2.c @@ -0,0 +1,389 @@ +#include <linux/ethtool.h> +#include <linux/bitfield.h> + +#include "global2.h" + +int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val) +{ + return mv88e6xxx_read(chip, chip->info->global2_addr, reg, val); +} + +int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val) +{ + return mv88e6xxx_write(chip, chip->info->global2_addr, reg, val); +} + +int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask) +{ + return mv88e6xxx_wait(chip, chip->info->global2_addr, reg, mask); +} + +/* Offset 0x14: EEPROM Command + * Offset 0x15: EEPROM Data (for 16-bit data access) + * Offset 0x15: EEPROM Addr (for 8-bit data access) + */ + +static int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip) +{ + return mv88e6xxx_g2_wait(chip, MV88E6XXX_G2_EEPROM_CMD, + MV88E6XXX_G2_EEPROM_CMD_BUSY | + MV88E6XXX_G2_EEPROM_CMD_RUNNING); +} + +static int mv88e6xxx_g2_eeprom_cmd(struct mv88e6xxx_chip *chip, u16 cmd) +{ + int err; + + err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_EEPROM_CMD, + MV88E6XXX_G2_EEPROM_CMD_BUSY | cmd); + if (err) + return err; + + return mv88e6xxx_g2_eeprom_wait(chip); +} + +static int mv88e6xxx_g2_eeprom_read8(struct mv88e6xxx_chip *chip, + u16 addr, u8 *data) +{ + u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_READ; + int err; + + err = mv88e6xxx_g2_eeprom_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g2_write(chip, MV88E6390_G2_EEPROM_ADDR, addr); + if (err) + return err; + + err = mv88e6xxx_g2_eeprom_cmd(chip, cmd); + if (err) + return err; + + err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_EEPROM_CMD, &cmd); + if (err) + return err; + + *data = cmd & 0xff; + + return 0; +} + +static int mv88e6xxx_g2_eeprom_write8(struct mv88e6xxx_chip *chip, + u16 addr, u8 data) +{ + u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_WRITE | + MV88E6XXX_G2_EEPROM_CMD_WRITE_EN; + int err; + + err = mv88e6xxx_g2_eeprom_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g2_write(chip, MV88E6390_G2_EEPROM_ADDR, addr); + if (err) + return err; + + return mv88e6xxx_g2_eeprom_cmd(chip, cmd | data); +} + +static int mv88e6xxx_g2_eeprom_read16(struct mv88e6xxx_chip *chip, + u8 addr, u16 *data) +{ + u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_READ | addr; + int err; + + err = mv88e6xxx_g2_eeprom_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g2_eeprom_cmd(chip, cmd); + if (err) + return err; + + return mv88e6xxx_g2_read(chip, MV88E6352_G2_EEPROM_DATA, data); +} + +static int mv88e6xxx_g2_eeprom_write16(struct mv88e6xxx_chip *chip, + u8 addr, u16 data) +{ + u16 cmd = MV88E6XXX_G2_EEPROM_CMD_OP_WRITE | addr; + int err; + + err = mv88e6xxx_g2_eeprom_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g2_write(chip, MV88E6352_G2_EEPROM_DATA, data); + if (err) + return err; + + return mv88e6xxx_g2_eeprom_cmd(chip, cmd); +} + +int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip, + struct ethtool_eeprom *eeprom, u8 *data) +{ + unsigned int offset = eeprom->offset; + unsigned int len = eeprom->len; + int err; + + eeprom->len = 0; + + while (len) { + err = mv88e6xxx_g2_eeprom_read8(chip, offset, data); + if (err) + return err; + + eeprom->len++; + offset++; + data++; + len--; + } + + return 0; +} + +int mv88e6xxx_g2_set_eeprom8(struct mv88e6xxx_chip *chip, + struct ethtool_eeprom *eeprom, u8 *data) +{ + unsigned int offset = eeprom->offset; + unsigned int len = eeprom->len; + int err; + + eeprom->len = 0; + + while (len) { + err = mv88e6xxx_g2_eeprom_write8(chip, offset, *data); + if (err) + return err; + + eeprom->len++; + offset++; + data++; + len--; + } + + return 0; +} + +int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip, + struct ethtool_eeprom *eeprom, u8 *data) +{ + unsigned int offset = eeprom->offset; + unsigned int len = eeprom->len; + u16 val; + int err; + + eeprom->len = 0; + + if (offset & 1) { + err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); + if (err) + return err; + + *data++ = (val >> 8) & 0xff; + + offset++; + len--; + eeprom->len++; + } + + while (len >= 2) { + err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); + if (err) + return err; + + *data++ = val & 0xff; + *data++ = (val >> 8) & 0xff; + + offset += 2; + len -= 2; + eeprom->len += 2; + } + + if (len) { + err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); + if (err) + return err; + + *data++ = val & 0xff; + + offset++; + len--; + eeprom->len++; + } + + return 0; +} + +int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip, + struct ethtool_eeprom *eeprom, u8 *data) +{ + unsigned int offset = eeprom->offset; + unsigned int len = eeprom->len; + u16 val; + int err; + + /* Ensure the RO WriteEn bit is set */ + err = mv88e6xxx_g2_read(chip, MV88E6XXX_G2_EEPROM_CMD, &val); + if (err) + return err; + + if (!(val & MV88E6XXX_G2_EEPROM_CMD_WRITE_EN)) + return -EROFS; + + eeprom->len = 0; + + if (offset & 1) { + err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); + if (err) + return err; + + val = (*data++ << 8) | (val & 0xff); + + err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val); + if (err) + return err; + + offset++; + len--; + eeprom->len++; + } + + while (len >= 2) { + val = *data++; + val |= *data++ << 8; + + err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val); + if (err) + return err; + + offset += 2; + len -= 2; + eeprom->len += 2; + } + + if (len) { + err = mv88e6xxx_g2_eeprom_read16(chip, offset >> 1, &val); + if (err) + return err; + + val = (val & 0xff00) | *data++; + + err = mv88e6xxx_g2_eeprom_write16(chip, offset >> 1, val); + if (err) + return err; + + offset++; + len--; + eeprom->len++; + } + + return 0; +} + +static int mv88e6xxx_g2_smi_phy_wait(struct mv88e6xxx_chip *chip) +{ + return mv88e6xxx_g2_wait(chip, MV88E6XXX_G2_SMI_PHY_CMD, + MV88E6XXX_G2_SMI_PHY_CMD_BUSY); +} + +static int mv88e6xxx_g2_smi_phy_cmd(struct mv88e6xxx_chip *chip, u16 cmd) +{ + int err; + + err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_CMD, + MV88E6XXX_G2_SMI_PHY_CMD_BUSY | cmd); + if (err) + return err; + + return mv88e6xxx_g2_smi_phy_wait(chip); +} + +static int mv88e6xxx_g2_smi_phy_access(struct mv88e6xxx_chip *chip, + bool external, bool c45, u16 op, int dev, + int reg) +{ + u16 cmd = op; + + if (external) + cmd |= MV88E6390_G2_SMI_PHY_CMD_FUNC_EXTERNAL; + else + cmd |= MV88E6390_G2_SMI_PHY_CMD_FUNC_INTERNAL; /* empty mask */ + + if (c45) + cmd |= MV88E6XXX_G2_SMI_PHY_CMD_MODE_45; /* empty mask */ + else + cmd |= MV88E6XXX_G2_SMI_PHY_CMD_MODE_22; + + dev <<= __bf_shf(MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK); + cmd |= dev & MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK; + cmd |= reg & MV88E6XXX_G2_SMI_PHY_CMD_REG_ADDR_MASK; + + return mv88e6xxx_g2_smi_phy_cmd(chip, cmd); +} + +static int mv88e6xxx_g2_smi_phy_access_c22(struct mv88e6xxx_chip *chip, + bool external, u16 op, int dev, + int reg) +{ + return mv88e6xxx_g2_smi_phy_access(chip, external, false, op, dev, reg); +} + +/* IEEE 802.3 Clause 22 Read Data Register */ +static int mv88e6xxx_g2_smi_phy_read_data_c22(struct mv88e6xxx_chip *chip, + bool external, int dev, int reg, + u16 *data) +{ + u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_22_READ_DATA; + int err; + + err = mv88e6xxx_g2_smi_phy_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g2_smi_phy_access_c22(chip, external, op, dev, reg); + if (err) + return err; + + return mv88e6xxx_g2_read(chip, MV88E6XXX_G2_SMI_PHY_DATA, data); +} + +/* IEEE 802.3 Clause 22 Write Data Register */ +static int mv88e6xxx_g2_smi_phy_write_data_c22(struct mv88e6xxx_chip *chip, + bool external, int dev, int reg, + u16 data) +{ + u16 op = MV88E6XXX_G2_SMI_PHY_CMD_OP_22_WRITE_DATA; + int err; + + err = mv88e6xxx_g2_smi_phy_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g2_write(chip, MV88E6XXX_G2_SMI_PHY_DATA, data); + if (err) + return err; + + return mv88e6xxx_g2_smi_phy_access_c22(chip, external, op, dev, reg); +} + + +int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, struct mii_bus *bus, + int addr, int reg, u16 *val) +{ + bool external = false; + + return mv88e6xxx_g2_smi_phy_read_data_c22(chip, external, addr, reg, + val); +} + +int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, struct mii_bus *bus, + int addr, int reg, u16 val) +{ + bool external = false; + + return mv88e6xxx_g2_smi_phy_write_data_c22(chip, external, addr, reg, + val); +} diff --git a/drivers/net/phy/mv88e6xxx/global2.h b/drivers/net/phy/mv88e6xxx/global2.h new file mode 100644 index 0000000000..4e23b04232 --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/global2.h @@ -0,0 +1,70 @@ +#ifndef _MV88E6XXX_GLOBAL2_H +#define _MV88E6XXX_GLOBAL2_H + +#include "chip.h" + +/* Offset 0x18: SMI PHY Command Register */ +#define MV88E6XXX_G2_SMI_PHY_CMD 0x18 +#define MV88E6XXX_G2_SMI_PHY_CMD_BUSY 0x8000 +#define MV88E6390_G2_SMI_PHY_CMD_FUNC_MASK 0x6000 +#define MV88E6390_G2_SMI_PHY_CMD_FUNC_INTERNAL 0x0000 +#define MV88E6390_G2_SMI_PHY_CMD_FUNC_EXTERNAL 0x2000 +#define MV88E6390_G2_SMI_PHY_CMD_FUNC_SETUP 0x4000 +#define MV88E6XXX_G2_SMI_PHY_CMD_MODE_MASK 0x1000 +#define MV88E6XXX_G2_SMI_PHY_CMD_MODE_45 0x0000 +#define MV88E6XXX_G2_SMI_PHY_CMD_MODE_22 0x1000 +#define MV88E6XXX_G2_SMI_PHY_CMD_OP_MASK 0x0c00 +#define MV88E6XXX_G2_SMI_PHY_CMD_OP_22_WRITE_DATA 0x0400 +#define MV88E6XXX_G2_SMI_PHY_CMD_OP_22_READ_DATA 0x0800 +#define MV88E6XXX_G2_SMI_PHY_CMD_OP_45_WRITE_ADDR 0x0000 +#define MV88E6XXX_G2_SMI_PHY_CMD_OP_45_WRITE_DATA 0x0400 +#define MV88E6XXX_G2_SMI_PHY_CMD_OP_45_READ_DATA_INC 0x0800 +#define MV88E6XXX_G2_SMI_PHY_CMD_OP_45_READ_DATA 0x0c00 +#define MV88E6XXX_G2_SMI_PHY_CMD_DEV_ADDR_MASK 0x03e0 +#define MV88E6XXX_G2_SMI_PHY_CMD_REG_ADDR_MASK 0x001f +#define MV88E6XXX_G2_SMI_PHY_CMD_SETUP_PTR_MASK 0x03ff + +/* Offset 0x19: SMI PHY Data Register */ +#define MV88E6XXX_G2_SMI_PHY_DATA 0x19 + +/* Offset 0x14: EEPROM Command */ +#define MV88E6XXX_G2_EEPROM_CMD 0x14 +#define MV88E6XXX_G2_EEPROM_CMD_BUSY 0x8000 +#define MV88E6XXX_G2_EEPROM_CMD_OP_MASK 0x7000 +#define MV88E6XXX_G2_EEPROM_CMD_OP_WRITE 0x3000 +#define MV88E6XXX_G2_EEPROM_CMD_OP_READ 0x4000 +#define MV88E6XXX_G2_EEPROM_CMD_OP_LOAD 0x6000 +#define MV88E6XXX_G2_EEPROM_CMD_RUNNING 0x0800 +#define MV88E6XXX_G2_EEPROM_CMD_WRITE_EN 0x0400 +#define MV88E6352_G2_EEPROM_CMD_ADDR_MASK 0x00ff +#define MV88E6390_G2_EEPROM_CMD_DATA_MASK 0x00ff + +/* Offset 0x15: EEPROM Data */ +#define MV88E6352_G2_EEPROM_DATA 0x15 +#define MV88E6352_G2_EEPROM_DATA_MASK 0xffff + +/* Offset 0x15: EEPROM Addr */ +#define MV88E6390_G2_EEPROM_ADDR 0x15 +#define MV88E6390_G2_EEPROM_ADDR_MASK 0xffff + +int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val); +int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val); +int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask); + +int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, + struct mii_bus *bus, + int addr, int reg, u16 *val); +int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, + struct mii_bus *bus, + int addr, int reg, u16 val); + +int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip, + struct ethtool_eeprom *eeprom, u8 *data); +int mv88e6xxx_g2_set_eeprom8(struct mv88e6xxx_chip *chip, + struct ethtool_eeprom *eeprom, u8 *data); +int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip, + struct ethtool_eeprom *eeprom, u8 *data); +int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip, + struct ethtool_eeprom *eeprom, u8 *data); + +#endif /* _MV88E6XXX_GLOBAL2_H */ diff --git a/drivers/net/phy/mv88e6xxx/port.c b/drivers/net/phy/mv88e6xxx/port.c new file mode 100644 index 0000000000..52f95d622c --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/port.c @@ -0,0 +1,663 @@ +#include <common.h> +#include <init.h> + +#include <linux/bitfield.h> +#include <linux/marvell_phy.h> + +#include "port.h" + +int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, + u16 *val) +{ + int addr = chip->info->port_base_addr + port; + + return mv88e6xxx_read(chip, addr, reg, val); +} + +int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, + u16 val) +{ + int addr = chip->info->port_base_addr + port; + + return mv88e6xxx_write(chip, addr, reg, val); +} + +/* Offset 0x00: MAC (or PCS or Physical) Status Register + * + * For most devices, this is read only. However the 6185 has the MyPause + * bit read/write. + */ +int mv88e6185_port_set_pause(struct mv88e6xxx_chip *chip, int port, + int pause) +{ + u16 reg; + int err; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); + if (err) + return err; + + if (pause) + reg |= MV88E6XXX_PORT_STS_MY_PAUSE; + else + reg &= ~MV88E6XXX_PORT_STS_MY_PAUSE; + + return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_STS, reg); +} + +/* Offset 0x01: MAC (or PCS or Physical) Control Register + * + * Link, Duplex and Flow Control have one force bit, one value bit. + * + * For port's MAC speed, ForceSpd (or SpdValue) bits 1:0 program the value. + * Alternative values require the 200BASE (or AltSpeed) bit 12 set. + * Newer chips need a ForcedSpd bit 13 set to consider the value. + */ + +static int mv88e6xxx_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode) +{ + u16 reg; + int err; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®); + if (err) + return err; + + reg &= ~(MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK | + MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK); + + switch (mode) { + case PHY_INTERFACE_MODE_RGMII_RXID: + reg |= MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + reg |= MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK; + break; + case PHY_INTERFACE_MODE_RGMII_ID: + reg |= MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK | + MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK; + break; + case PHY_INTERFACE_MODE_RGMII: + break; + default: + return 0; + } + + err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg); + if (err) + return err; + + dev_dbg(chip->dev, "p%d: delay RXCLK %s, TXCLK %s\n", port, + reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK ? "yes" : "no", + reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK ? "yes" : "no"); + + return 0; +} + +int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode) +{ + if (port < 5) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_rgmii_delay(chip, port, mode); +} + +int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode) +{ + if (port != 0) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_rgmii_delay(chip, port, mode); +} + +int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link) +{ + u16 reg; + int err; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®); + if (err) + return err; + + reg &= ~(MV88E6XXX_PORT_MAC_CTL_FORCE_LINK | + MV88E6XXX_PORT_MAC_CTL_LINK_UP); + + switch (link) { + case LINK_FORCED_DOWN: + reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_LINK; + break; + case LINK_FORCED_UP: + reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_LINK | + MV88E6XXX_PORT_MAC_CTL_LINK_UP; + break; + case LINK_UNFORCED: + /* normal link detection */ + break; + default: + return -EINVAL; + } + + err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg); + if (err) + return err; + + dev_dbg(chip->dev, "p%d: %s link %s\n", port, + reg & MV88E6XXX_PORT_MAC_CTL_FORCE_LINK ? "Force" : "Unforce", + reg & MV88E6XXX_PORT_MAC_CTL_LINK_UP ? "up" : "down"); + + return 0; +} + +int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup) +{ + u16 reg; + int err; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®); + if (err) + return err; + + reg &= ~(MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX | + MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL); + + switch (dup) { + case DUPLEX_HALF: + reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX; + break; + case DUPLEX_FULL: + reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX | + MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL; + break; + case DUPLEX_UNFORCED: + /* normal duplex detection */ + break; + default: + return -EINVAL; + } + + err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg); + if (err) + return err; + + dev_dbg(chip->dev, "p%d: %s %s duplex\n", port, + reg & MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX ? "Force" : "Unforce", + reg & MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL ? "full" : "half"); + + return 0; +} + +static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port, + int speed, bool alt_bit, bool force_bit) +{ + u16 reg, ctrl; + int err; + + switch (speed) { + case 10: + ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_10; + break; + case 100: + ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100; + break; + case 200: + if (alt_bit) + ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100 | + MV88E6390_PORT_MAC_CTL_ALTSPEED; + else + ctrl = MV88E6065_PORT_MAC_CTL_SPEED_200; + break; + case 1000: + ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_1000; + break; + case 2500: + ctrl = MV88E6390_PORT_MAC_CTL_SPEED_10000 | + MV88E6390_PORT_MAC_CTL_ALTSPEED; + break; + case 10000: + /* all bits set, fall through... */ + case SPEED_UNFORCED: + ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_UNFORCED; + break; + default: + return -EOPNOTSUPP; + } + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®); + if (err) + return err; + + reg &= ~MV88E6XXX_PORT_MAC_CTL_SPEED_MASK; + if (alt_bit) + reg &= ~MV88E6390_PORT_MAC_CTL_ALTSPEED; + if (force_bit) { + reg &= ~MV88E6390_PORT_MAC_CTL_FORCE_SPEED; + if (speed != SPEED_UNFORCED) + ctrl |= MV88E6390_PORT_MAC_CTL_FORCE_SPEED; + } + reg |= ctrl; + + err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg); + if (err) + return err; + + if (speed) + dev_dbg(chip->dev, "p%d: Speed set to %d Mbps\n", port, speed); + else + dev_dbg(chip->dev, "p%d: Speed unforced\n", port); + + return 0; +} + +/* Support 10, 100, 200 Mbps (e.g. 88E6065 family) */ +int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ + if (speed == SPEED_MAX) + speed = 200; + + if (speed > 200) + return -EOPNOTSUPP; + + /* Setting 200 Mbps on port 0 to 3 selects 100 Mbps */ + return mv88e6xxx_port_set_speed(chip, port, speed, false, false); +} + +/* Support 10, 100, 1000 Mbps (e.g. 88E6185 family) */ +int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ + if (speed == SPEED_MAX) + speed = 1000; + + if (speed == 200 || speed > 1000) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_speed(chip, port, speed, false, false); +} + +/* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */ +int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ + if (speed == SPEED_MAX) + speed = 1000; + + if (speed > 1000) + return -EOPNOTSUPP; + + if (speed == 200 && port < 5) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_speed(chip, port, speed, true, false); +} + +/* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6390) */ +int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ + if (speed == SPEED_MAX) + speed = port < 9 ? 1000 : 2500; + + if (speed > 2500) + return -EOPNOTSUPP; + + if (speed == 200 && port != 0) + return -EOPNOTSUPP; + + if (speed == 2500 && port < 9) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_speed(chip, port, speed, true, true); +} + +/* Support 10, 100, 200, 1000, 2500, 10000 Mbps (e.g. 88E6190X) */ +int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ + if (speed == SPEED_MAX) + speed = port < 9 ? 1000 : 10000; + + if (speed == 200 && port != 0) + return -EOPNOTSUPP; + + if (speed >= 2500 && port < 9) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_speed(chip, port, speed, true, true); +} + +int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode) +{ + u16 cmode; + + if (mode == PHY_INTERFACE_MODE_NA) + return 0; + + if (port != 9 && port != 10) + return -EOPNOTSUPP; + + switch (mode) { + case PHY_INTERFACE_MODE_1000BASEX: + cmode = MV88E6XXX_PORT_STS_CMODE_1000BASE_X; + break; + case PHY_INTERFACE_MODE_SGMII: + cmode = MV88E6XXX_PORT_STS_CMODE_SGMII; + break; + case PHY_INTERFACE_MODE_2500BASEX: + cmode = MV88E6XXX_PORT_STS_CMODE_2500BASEX; + break; + case PHY_INTERFACE_MODE_XGMII: + case PHY_INTERFACE_MODE_XAUI: + cmode = MV88E6XXX_PORT_STS_CMODE_XAUI; + break; + case PHY_INTERFACE_MODE_RXAUI: + cmode = MV88E6XXX_PORT_STS_CMODE_RXAUI; + break; + default: + cmode = 0; + } + + chip->ports[port].cmode = cmode; + + return 0; +} + +int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port, + struct phylink_link_state *state) +{ + int err; + u16 reg; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); + if (err) + return err; + + switch (reg & MV88E6XXX_PORT_STS_SPEED_MASK) { + case MV88E6XXX_PORT_STS_SPEED_10: + state->speed = SPEED_10; + break; + case MV88E6XXX_PORT_STS_SPEED_100: + state->speed = SPEED_100; + break; + case MV88E6XXX_PORT_STS_SPEED_1000: + state->speed = SPEED_1000; + break; + case MV88E6XXX_PORT_STS_SPEED_10000: + if ((reg & MV88E6XXX_PORT_STS_CMODE_MASK) == + MV88E6XXX_PORT_STS_CMODE_2500BASEX) + state->speed = SPEED_2500; + else + state->speed = SPEED_10000; + break; + } + + state->duplex = reg & MV88E6XXX_PORT_STS_DUPLEX ? + DUPLEX_FULL : DUPLEX_HALF; + state->link = !!(reg & MV88E6XXX_PORT_STS_LINK); + state->an_enabled = 1; + state->an_complete = state->link; + + return 0; +} + +int mv88e6185_port_link_state(struct mv88e6xxx_chip *chip, int port, + struct phylink_link_state *state) +{ + if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { + u8 cmode = chip->ports[port].cmode; + + /* When a port is in "Cross-chip serdes" mode, it uses + * 1000Base-X full duplex mode, but there is no automatic + * link detection. Use the sync OK status for link (as it + * would do for 1000Base-X mode.) + */ + if (cmode == MV88E6185_PORT_STS_CMODE_SERDES) { + u16 mac; + int err; + + err = mv88e6xxx_port_read(chip, port, + MV88E6XXX_PORT_MAC_CTL, &mac); + if (err) + return err; + + state->link = !!(mac & MV88E6185_PORT_MAC_CTL_SYNC_OK); + state->an_enabled = 1; + state->an_complete = + !!(mac & MV88E6185_PORT_MAC_CTL_AN_DONE); + state->duplex = + state->link ? DUPLEX_FULL : DUPLEX_UNKNOWN; + state->speed = + state->link ? SPEED_1000 : SPEED_UNKNOWN; + + return 0; + } + } + + return mv88e6352_port_link_state(chip, port, state); +} + + +static int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, + int link, int speed, int duplex, int pause, + phy_interface_t mode) +{ + int err; + + if (!chip->info->ops->port_set_link) + return 0; + + /* Port's MAC control must not be changed unless the link is down */ + err = chip->info->ops->port_set_link(chip, port, 0); + if (err) + return err; + + if (chip->info->ops->port_set_speed) { + err = chip->info->ops->port_set_speed(chip, port, speed); + if (err && err != -EOPNOTSUPP) + goto restore_link; + } + + if (chip->info->ops->port_set_pause) { + err = chip->info->ops->port_set_pause(chip, port, pause); + if (err) + goto restore_link; + + } + + if (chip->info->ops->port_set_duplex) { + err = chip->info->ops->port_set_duplex(chip, port, duplex); + if (err && err != -EOPNOTSUPP) + goto restore_link; + } + + if (chip->info->ops->port_set_rgmii_delay) { + err = chip->info->ops->port_set_rgmii_delay(chip, port, mode); + if (err && err != -EOPNOTSUPP) + goto restore_link; + } + + if (chip->info->ops->port_set_cmode) { + err = chip->info->ops->port_set_cmode(chip, port, mode); + if (err && err != -EOPNOTSUPP) + goto restore_link; + } + + err = 0; +restore_link: + if (chip->info->ops->port_set_link(chip, port, link)) + dev_err(chip->dev, "p%d: failed to restore MAC's link\n", port); + + return err; +} + +static int mv88e6xxx_port_config_init(struct phy_device *phydev) +{ + struct mv88e6xxx_chip *chip = phydev->dev.priv; + int port = phydev->addr - chip->info->port_base_addr; + int err; + + err = mv88e6xxx_port_setup_mac(chip, port, phydev->link, phydev->speed, + phydev->duplex, phydev->pause, + phydev->interface); + + if (err && err != -EOPNOTSUPP) + dev_err(&phydev->dev, "p%d: failed to configure MAC\n", port); + + return err; +} + +static int mv88e6xxx_port_config_aneg(struct phy_device *phydev) +{ + return 0; +} + +static int mv88e6xxx_port_read_status(struct phy_device *phydev) +{ + struct mv88e6xxx_chip *chip = phydev->dev.priv; + int port = phydev->addr - chip->info->port_base_addr; + struct phylink_link_state state; + int err; + + err = mv88e6352_port_link_state(chip, port, &state); + if (err) + return err; + + phydev->link = state.link; + phydev->duplex = state.duplex; + phydev->speed = state.speed; + + phydev->pause = phydev->asym_pause = 0; + + return 0; +} + +/* + * Fake switch PHY_ID used to match this driver against devices + * create in mv88e6xxx_port_probe. + */ +#define MV88E6XXX_SWITCH_PORT_PHY_ID (0x01410000 | \ + MV88E6XXX_PORT_SWITCH_ID_PROD_6085) + +static struct phy_driver mv88e6xxx_port_driver = { + .phy_id = MV88E6XXX_SWITCH_PORT_PHY_ID, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .drv.name = "Marvel 88E6xxx Port", + .features = PHY_GBIT_FEATURES & ~SUPPORTED_Autoneg, + .config_init = mv88e6xxx_port_config_init, + .config_aneg = mv88e6xxx_port_config_aneg, + .read_status = mv88e6xxx_port_read_status, +}; + +static int __init mv88e6xxx_port_driver_register(void) +{ + return phy_driver_register(&mv88e6xxx_port_driver); +} +fs_initcall(mv88e6xxx_port_driver_register); + +int mv88e6xxx_port_probe(struct mv88e6xxx_chip *chip) +{ + struct device_d *dev = chip->dev; + struct device_node *np = dev->device_node; + struct device_node *port_node, *switch_node; + struct device_node *port_nodes[DSA_MAX_PORTS] = { NULL }; + int err, i; + + switch_node = of_find_node_by_name(np, "ports"); + if (!switch_node) + return -EINVAL; + + for_each_available_child_of_node(switch_node, port_node) { + u32 nr; + + err = of_property_read_u32(port_node, "reg", &nr); + if (err) { + dev_err(dev, + "Error: Failed to find reg for child %s\n", + port_node->full_name); + continue; + } + + port_nodes[nr] = port_node; + } + + /* + * Walk through all of the ports and unmask those that are + * connected to a PHY via child MDIO bus, so the can be picked + * up via regular PHY discover. + * + * While at it, also create PHY objects for ports that are + * not, so they can be correctly configured + */ + for (i = 0; i < chip->info->num_ports; i++) { + struct phy_device *phydev; + u16 status; + bool force_mac = false; + int addr = i; + + err = mv88e6xxx_port_read(chip, i, MV88E6XXX_PORT_STS, + &status); + if (err) + return err; + + /* + * FIXME: This most likely affects more than 6161, so + * this will have to be expanded to more chips + */ + if (chip->info->prod_num == + MV88E6XXX_PORT_SWITCH_ID_PROD_6161) { + const uint8_t cmode = + FIELD_GET(MV88E6185_PORT_STS_CMODE_MASK, + status); + switch (i) { + case 4: + case 5: /* FALLTHROUGH */ + if (cmode == MV88E6165_PORT_STS_CMODE_SGMII) { + /* + * Port is configured to SGMII + * bypassing SERDES/PHY + */ + force_mac = true; + break; + } + + if (cmode != MV88E6165_PORT_STS_CMODE_PHY) { + /* + * If port is configured to + * SERDES we need to adjust + * its MDIO address + */ + addr += MV88E6165_PORT_SERDES_OFFSET; + } + + break; + } + } + + if (status & MV88E6XXX_PORT_STS_PHY_DETECT && !force_mac) { + /* + * True PHYs will be automaticall handled by + * generic PHY driver, so we ignore those. + */ + chip->miibus.phy_mask &= ~BIT(addr); + continue; + } + + /* + * In order to expose MAC-only ports on the switch, so + * they can be properly configured to match other + * end's settings, we create pseudo PHY devices that + * will match against our special PHY driver + */ + phydev = phy_device_create(chip->parent_miibus, + chip->info->port_base_addr + i, + MV88E6XXX_SWITCH_PORT_PHY_ID); + phydev->dev.device_node = port_nodes[i]; + phydev->dev.priv = chip; + phydev->duplex = DUPLEX_UNFORCED; + + err = phy_register_device(phydev); + if (err) + dev_err(dev, "Error: Failed to register a PHY\n"); + } + + return 0; +}
\ No newline at end of file diff --git a/drivers/net/phy/mv88e6xxx/port.h b/drivers/net/phy/mv88e6xxx/port.h new file mode 100644 index 0000000000..07d937ecbd --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/port.h @@ -0,0 +1,137 @@ +#ifndef _MV88E6XXX_PORT_H +#define _MV88E6XXX_PORT_H + +#include "chip.h" + +/* Offset 0x00: Port Status Register */ +#define MV88E6XXX_PORT_STS 0x00 +#define MV88E6XXX_PORT_STS_PAUSE_EN 0x8000 +#define MV88E6XXX_PORT_STS_MY_PAUSE 0x4000 +#define MV88E6XXX_PORT_STS_HD_FLOW 0x2000 +#define MV88E6XXX_PORT_STS_PHY_DETECT 0x1000 +#define MV88E6XXX_PORT_STS_LINK 0x0800 +#define MV88E6XXX_PORT_STS_DUPLEX 0x0400 +#define MV88E6XXX_PORT_STS_SPEED_MASK 0x0300 +#define MV88E6XXX_PORT_STS_SPEED_10 0x0000 +#define MV88E6XXX_PORT_STS_SPEED_100 0x0100 +#define MV88E6XXX_PORT_STS_SPEED_1000 0x0200 +#define MV88E6XXX_PORT_STS_SPEED_10000 0x0300 +#define MV88E6352_PORT_STS_EEE 0x0040 +#define MV88E6165_PORT_STS_AM_DIS 0x0040 +#define MV88E6185_PORT_STS_MGMII 0x0040 +#define MV88E6XXX_PORT_STS_TX_PAUSED 0x0020 +#define MV88E6XXX_PORT_STS_FLOW_CTL 0x0010 +#define MV88E6XXX_PORT_STS_CMODE_MASK 0x000f +#define MV88E6XXX_PORT_STS_CMODE_100BASE_X 0x0008 +#define MV88E6XXX_PORT_STS_CMODE_1000BASE_X 0x0009 +#define MV88E6XXX_PORT_STS_CMODE_SGMII 0x000a +#define MV88E6XXX_PORT_STS_CMODE_2500BASEX 0x000b +#define MV88E6XXX_PORT_STS_CMODE_XAUI 0x000c +#define MV88E6XXX_PORT_STS_CMODE_RXAUI 0x000d +#define MV88E6185_PORT_STS_CDUPLEX 0x0008 +#define MV88E6185_PORT_STS_CMODE_MASK 0x0007 +#define MV88E6185_PORT_STS_CMODE_GMII_FD 0x0000 +#define MV88E6185_PORT_STS_CMODE_MII_100_FD_PS 0x0001 +#define MV88E6185_PORT_STS_CMODE_MII_100 0x0002 +#define MV88E6185_PORT_STS_CMODE_MII_10 0x0003 +#define MV88E6185_PORT_STS_CMODE_SERDES 0x0004 +#define MV88E6165_PORT_STS_CMODE_PHY 0x0004 +#define MV88E6185_PORT_STS_CMODE_1000BASE_X 0x0005 +#define MV88E6185_PORT_STS_CMODE_PHY 0x0006 +#define MV88E6165_PORT_STS_CMODE_SGMII 0x0006 +#define MV88E6185_PORT_STS_CMODE_DISABLED 0x0007 + + + +/* Offset 0x01: MAC (or PCS or Physical) Control Register */ +#define MV88E6XXX_PORT_MAC_CTL 0x01 +#define MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK 0x8000 +#define MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK 0x4000 +#define MV88E6185_PORT_MAC_CTL_SYNC_OK 0x4000 +#define MV88E6390_PORT_MAC_CTL_FORCE_SPEED 0x2000 +#define MV88E6390_PORT_MAC_CTL_ALTSPEED 0x1000 +#define MV88E6352_PORT_MAC_CTL_200BASE 0x1000 +#define MV88E6185_PORT_MAC_CTL_AN_EN 0x0400 +#define MV88E6185_PORT_MAC_CTL_AN_RESTART 0x0200 +#define MV88E6185_PORT_MAC_CTL_AN_DONE 0x0100 +#define MV88E6XXX_PORT_MAC_CTL_FC 0x0080 +#define MV88E6XXX_PORT_MAC_CTL_FORCE_FC 0x0040 +#define MV88E6XXX_PORT_MAC_CTL_LINK_UP 0x0020 +#define MV88E6XXX_PORT_MAC_CTL_FORCE_LINK 0x0010 +#define MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL 0x0008 +#define MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX 0x0004 +#define MV88E6XXX_PORT_MAC_CTL_SPEED_MASK 0x0003 +#define MV88E6XXX_PORT_MAC_CTL_SPEED_10 0x0000 +#define MV88E6XXX_PORT_MAC_CTL_SPEED_100 0x0001 +#define MV88E6065_PORT_MAC_CTL_SPEED_200 0x0002 +#define MV88E6XXX_PORT_MAC_CTL_SPEED_1000 0x0002 +#define MV88E6390_PORT_MAC_CTL_SPEED_10000 0x0003 +#define MV88E6XXX_PORT_MAC_CTL_SPEED_UNFORCED 0x0003 + +/* Offset 0x03: Switch Identifier Register */ +#define MV88E6XXX_PORT_SWITCH_ID 0x03 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_MASK 0xfff0 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6085 0x04a0 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6095 0x0950 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6097 0x0990 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6190X 0x0a00 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6390X 0x0a10 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6131 0x1060 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6320 0x1150 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6123 0x1210 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6161 0x1610 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6165 0x1650 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6171 0x1710 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6172 0x1720 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6175 0x1750 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6176 0x1760 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6190 0x1900 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6191 0x1910 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6185 0x1a70 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6240 0x2400 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6290 0x2900 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6321 0x3100 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6141 0x3400 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6341 0x3410 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6352 0x3520 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6350 0x3710 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6351 0x3750 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6390 0x3900 +#define MV88E6XXX_PORT_SWITCH_ID_REV_MASK 0x000f + + +/* + * SERDES connected to port 0x04 is accessible at address 0xC + */ +#define MV88E6165_PORT_SERDES_OFFSET (0x0C - 0x04) + +int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, + u16 *val); +int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, + u16 val); + +int mv88e6185_port_set_pause(struct mv88e6xxx_chip *chip, int port, + int pause); +int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode); +int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode); +int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link); +int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup); +int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, + phy_interface_t mode); +int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port, + struct phylink_link_state *state); +int mv88e6185_port_link_state(struct mv88e6xxx_chip *chip, int port, + struct phylink_link_state *state); + +/* Barebox specific */ +int mv88e6xxx_port_probe(struct mv88e6xxx_chip *chip); + + +#endif /* _MV88E6XXX_PORT_H */ diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 63f249fcff..7c4d14bf4c 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; } @@ -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); @@ -444,9 +448,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)) 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 ba35bef99a..e689559ee3 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/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); diff --git a/drivers/of/platform.c b/drivers/of/platform.c index c9157cdd74..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 - snprintf(dev->name, MAX_DRIVER_NAME, "d%x.%s", *reg, name); -#else /* CONFIG_PPC_DCR_NATIVE */ - u64 addr = of_translate_dcr_address(np, *reg, NULL); - if (addr != OF_BAD_ADDR) { - snprintf(dev->name, MAX_DRIVER_NAME, "D%llx.%s", - (unsigned long long)addr, name); - free(name); - return; - } -#endif /* !CONFIG_PPC_DCR_NATIVE */ - } -#endif /* CONFIG_PPC_DCR */ - - /* - * For MMIO, get the physical address - */ - reg = of_get_property(np, "reg", NULL); - if (reg) { - if (of_can_translate_address(np)) { - addr = of_translate_address(np, reg); - } else { - addrp = of_get_address(np, 0, NULL, NULL); - if (addrp) - addr = of_read_number(addrp, 1); - else - addr = OF_BAD_ADDR; - } - if (addr != OF_BAD_ADDR) { - snprintf(dev->name, MAX_DRIVER_NAME, "%llx.%s", - (unsigned long long)addr, name); - free(name); + + /* 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 - */ - snprintf(dev->name, MAX_DRIVER_NAME, "%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; + } } /** 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); } |