diff options
Diffstat (limited to 'drivers')
74 files changed, 4187 insertions, 829 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index 70797c182c..d0b5e3abdb 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -20,5 +20,6 @@ source "drivers/watchdog/Kconfig" source "drivers/pwm/Kconfig" source "drivers/dma/Kconfig" source "drivers/gpio/Kconfig" +source "drivers/of/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 28a5cb8f35..2a1f8b0f23 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -1,4 +1,5 @@ obj-y += base/ +obj-$(CONFIG_ARM_AMBA) += amba/ obj-y += net/ obj-y += serial/ obj-y += mtd/ @@ -19,3 +20,4 @@ obj-y += misc/ obj-y += dma/ obj-y += watchdog/ obj-y += gpio/ +obj-$(CONFIG_OFDEVICE) += of/ diff --git a/drivers/amba/Makefile b/drivers/amba/Makefile new file mode 100644 index 0000000000..a4a511b322 --- /dev/null +++ b/drivers/amba/Makefile @@ -0,0 +1,2 @@ + +obj-y += bus.o diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c new file mode 100644 index 0000000000..383c77ee13 --- /dev/null +++ b/drivers/amba/bus.c @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2003 Deep Blue Solutions Ltd, All Rights Reserved. + * Copyright (c) 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> + * + * Under GPLv2. + */ +#include <common.h> +#include <driver.h> +#include <linux/clk.h> +#include <linux/amba/bus.h> +#include <io.h> + +#define to_amba_driver(d) container_of(d, struct amba_driver, drv) + +static const struct amba_id * +amba_lookup(const struct amba_id *table, struct amba_device *dev) +{ + int ret = 0; + + while (table->mask) { + ret = (dev->periphid & table->mask) == table->id; + if (ret) + break; + table++; + } + + return ret ? table : NULL; +} + +static int amba_match(struct device_d *dev, struct driver_d *drv) +{ + struct amba_device *pcdev = to_amba_device(dev); + + struct amba_driver *pcdrv = to_amba_driver(drv); + + return amba_lookup(pcdrv->id_table, pcdev) == NULL; +} + +static int amba_get_enable_pclk(struct amba_device *pcdev) +{ + struct clk *pclk = clk_get(&pcdev->dev, "apb_pclk"); + int ret; + + pcdev->pclk = pclk; + + if (IS_ERR(pclk)) + return PTR_ERR(pclk); + + ret = clk_enable(pclk); + if (ret) { + clk_put(pclk); + } + + return ret; +} + +static int amba_probe(struct device_d *dev) +{ + struct amba_device *pcdev = to_amba_device(dev); + struct amba_driver *pcdrv = to_amba_driver(dev->driver); + const struct amba_id *id = amba_lookup(pcdrv->id_table, pcdev); + + return pcdrv->probe(pcdev, id); +} + +static void amba_remove(struct device_d *dev) +{ + struct amba_device *pcdev = to_amba_device(dev); + struct amba_driver *drv = to_amba_driver(dev->driver); + + drv->remove(pcdev); +} + +struct bus_type amba_bustype = { + .name = "amba", + .match = amba_match, + .probe = amba_probe, + .remove = amba_remove, +}; + +int amba_driver_register(struct amba_driver *drv) +{ + drv->drv.bus = &amba_bustype; + + if (drv->probe) + drv->drv.probe = amba_probe; + if (drv->remove) + drv->drv.remove = amba_remove; + + return register_driver(&drv->drv); +} + +/** + * amba_device_add - add a previously allocated AMBA device structure + * @dev: AMBA device allocated by amba_device_alloc + * @parent: resource parent for this devices resources + * + * Claim the resource, and read the device cell ID if not already + * initialized. Register the AMBA device with the Linux device + * manager. + */ +int amba_device_add(struct amba_device *dev) +{ + u32 size; + void __iomem *tmp; + int i, ret; + struct resource *res = NULL; + + dev->dev.bus = &amba_bustype; + + /* + * Dynamically calculate the size of the resource + * and use this for iomap + */ + size = resource_size(&dev->res); + res = request_iomem_region("amba", dev->res.start, dev->res.end); + if (!res) + return -ENOMEM; + dev->base = tmp = (void __force __iomem *)res->start; + if (!tmp) { + ret = -ENOMEM; + goto err_release; + } + + /* Hard-coded primecell ID instead of plug-n-play */ + if (dev->periphid != 0) + goto skip_probe; + + ret = amba_get_enable_pclk(dev); + if (ret == 0) { + u32 pid, cid; + + /* + * Read pid and cid based on size of resource + * they are located at end of region + */ + for (pid = 0, i = 0; i < 4; i++) + pid |= (readl(tmp + size - 0x20 + 4 * i) & 255) << + (i * 8); + for (cid = 0, i = 0; i < 4; i++) + cid |= (readl(tmp + size - 0x10 + 4 * i) & 255) << + (i * 8); + + if (cid == AMBA_CID) + dev->periphid = pid; + + if (!dev->periphid) + ret = -ENODEV; + } + + if (ret) + goto err_release; + + skip_probe: + ret = register_device(&dev->dev); + if (ret) + goto err_release; + + return ret; + err_release: + release_region(res); + return ret; +} + +struct amba_device * +amba_aphb_device_add(struct device_d *parent, const char *name, int id, + resource_size_t base, size_t size, + void *pdata, unsigned int periphid) +{ + struct amba_device *dev; + int ret; + + dev = amba_device_alloc(name, id, base, size); + + dev->periphid = periphid; + dev->dev.platform_data = pdata; + dev->dev.parent = parent; + + ret = amba_device_add(dev); + if (ret) + return ERR_PTR(ret); + + return dev; +} + +/** + * amba_device_alloc - allocate an AMBA device + * @name: sysfs name of the AMBA device + * @base: base of AMBA device + * @size: size of AMBA device + * + * Allocate and initialize an AMBA device structure. Returns %NULL + * on failure. + */ +struct amba_device *amba_device_alloc(const char *name, int id, resource_size_t base, + size_t size) +{ + struct amba_device *dev; + + dev = xzalloc(sizeof(*dev)); + + strcpy(dev->dev.name, name); + dev->dev.id = id; + dev->res.start = base; + dev->res.end = base + size - 1; + dev->res.flags = IORESOURCE_MEM; + + return dev; +} + diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 957ca5ac2a..e1f1c7a0ad 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -1,3 +1,4 @@ +obj-y += bus.o obj-y += driver.o obj-y += platform.o obj-y += resource.o diff --git a/drivers/base/bus.c b/drivers/base/bus.c new file mode 100644 index 0000000000..1dd139f7a3 --- /dev/null +++ b/drivers/base/bus.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> + * + * Under GPLv2 + */ + +#include <common.h> +#include <driver.h> +#include <errno.h> + +LIST_HEAD(bus_list); +EXPORT_SYMBOL(bus_list); + +struct bus_type *get_bus_by_name(const char *name) +{ + struct bus_type *bus; + + for_each_bus(bus) { + if(!strcmp(bus->name, name)) + return bus; + } + + return NULL; +} + +int bus_register(struct bus_type *bus) +{ + if (get_bus_by_name(bus->name)) + return -EEXIST; + + INIT_LIST_HEAD(&bus->device_list); + INIT_LIST_HEAD(&bus->driver_list); + + list_add_tail(&bus->list, &bus_list); + + return 0; +} diff --git a/drivers/base/driver.c b/drivers/base/driver.c index dc29021738..1def2f85e7 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -82,8 +82,6 @@ static int match(struct driver_d *drv, struct device_d *dev) dev->driver = drv; - if (dev->bus != drv->bus) - goto err_out; if (dev->bus->match(dev, drv)) goto err_out; if (dev->bus->probe(dev)) @@ -113,18 +111,29 @@ int register_device(struct device_d *new_device) debug ("register_device: %s\n", dev_name(new_device)); - if (!new_device->bus) { -// dev_err(new_device, "no bus type associated. Needs fixup\n"); + if (!new_device->bus) new_device->bus = &platform_bus; + + if (new_device->bus == &platform_bus && new_device->resource) { + struct device_d *dev; + + bus_for_each_device(new_device->bus, dev) { + if (!dev->resource) + continue; + if (dev->resource->start == new_device->resource->start) { + return -EBUSY; + } + } } list_add_tail(&new_device->list, &device_list); + list_add_tail(&new_device->bus_list, &new_device->bus->device_list); INIT_LIST_HEAD(&new_device->children); INIT_LIST_HEAD(&new_device->cdevs); INIT_LIST_HEAD(&new_device->parameters); INIT_LIST_HEAD(&new_device->active); - for_each_driver(drv) { + bus_for_each_driver(new_device->bus, drv) { if (!match(drv, new_device)) break; } @@ -156,6 +165,7 @@ int unregister_device(struct device_d *old_dev) } list_del(&old_dev->list); + list_del(&old_dev->bus_list); list_del(&old_dev->active); /* remove device from parents child list */ @@ -209,13 +219,14 @@ int register_driver(struct driver_d *drv) } list_add_tail(&drv->list, &driver_list); + list_add_tail(&drv->bus_list, &drv->bus->driver_list); if (!drv->info) drv->info = noinfo; if (!drv->shortinfo) drv->shortinfo = noshortinfo; - for_each_device(dev) + bus_for_each_device(drv->bus, dev) match(drv, dev); return 0; @@ -310,6 +321,25 @@ const char *dev_id(const struct device_d *dev) return buf; } +int dev_printf(const struct device_d *dev, const char *format, ...) +{ + va_list args; + int ret = 0; + + if (dev->driver && dev->driver->name) + ret += printf("%s ", dev->driver->name); + + ret += printf("%s: ", dev_name(dev)); + + va_start(args, format); + + ret += vprintf(format, args); + + va_end(args); + + return ret; +} + void devices_shutdown(void) { struct device_d *dev; @@ -354,6 +384,21 @@ static int do_devinfo_subtree(struct device_d *dev, int depth) return 0; } +int dev_get_drvdata(struct device_d *dev, unsigned long *data) +{ + if (dev->of_id_entry) { + *data = dev->of_id_entry->data; + return 0; + } + + if (dev->id_entry) { + *data = dev->id_entry->driver_data; + return 0; + } + + return -ENODEV; +} + static int do_devinfo(int argc, char *argv[]) { struct device_d *dev; @@ -403,6 +448,12 @@ static int do_devinfo(int argc, char *argv[]) list_for_each_entry(param, &dev->parameters, list) printf("%16s = %s\n", param->name, dev_get_param(dev, param->name)); +#ifdef CONFIG_OFDEVICE + if (dev->device_node) { + printf("\ndevice node: %s\n", dev->device_node->full_name); + of_print_nodes(dev->device_node, 0); + } +#endif } return 0; diff --git a/drivers/base/platform.c b/drivers/base/platform.c index f1069bc899..d3021ab833 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -18,10 +18,31 @@ */ #include <common.h> #include <driver.h> +#include <errno.h> +#include <init.h> static int platform_match(struct device_d *dev, struct driver_d *drv) { - return strcmp(dev->name, drv->name) ? -1 : 0; + if (IS_ENABLED(CONFIG_OFDEVICE) && dev->device_node && + drv->of_compatible) + return of_match(dev, drv); + + if (!strcmp(dev->name, drv->name)) + return 0; + + if (drv->id_table) { + struct platform_device_id *id = drv->id_table; + + while (id->name) { + if (!strcmp(id->name, dev->name)) { + dev->id_entry = id; + return 0; + } + id++; + } + } + + return -1; } static int platform_probe(struct device_d *dev) @@ -41,15 +62,8 @@ struct bus_type platform_bus = { .remove = platform_remove, }; -#if 0 -LIST_HEAD(bus_list); -EXPORT_SYMBOL(bus_list); - -int bus_register(struct bus_type *bus) +static int plarform_init(void) { - list_add_tail(&bus->list, &bus_list); - - return 0; + return bus_register(&platform_bus); } -#endif - +pure_initcall(plarform_init); diff --git a/drivers/eeprom/at25.c b/drivers/eeprom/at25.c index 03d191eb90..5578c783ad 100644 --- a/drivers/eeprom/at25.c +++ b/drivers/eeprom/at25.c @@ -312,7 +312,7 @@ static struct driver_d at25_driver = { static int at25_init(void) { - register_driver(&at25_driver); + spi_register_driver(&at25_driver); return 0; } diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index fa3b041534..118135182d 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -145,7 +145,7 @@ static int stmpe_gpio_probe(struct device_d *dev) return ret; } - dev_info(dev, "probed stmpe gpiochip%d with base %d\n", dev->id, stmpegpio->chip.base); + dev_dbg(dev, "probed stmpe gpiochip%d with base %d\n", dev->id, stmpegpio->chip.base); return 0; } diff --git a/drivers/i2c/i2c.c b/drivers/i2c/i2c.c index 3af2b3e66c..27fd256cf7 100644 --- a/drivers/i2c/i2c.c +++ b/drivers/i2c/i2c.c @@ -21,6 +21,7 @@ #include <errno.h> #include <malloc.h> #include <xfuncs.h> +#include <init.h> #include <i2c/i2c.h> @@ -251,6 +252,7 @@ struct i2c_client *i2c_new_device(struct i2c_adapter *adapter, strcpy(client->dev.name, chip->type); client->dev.type_data = client; client->dev.platform_data = chip->platform_data; + client->dev.bus = &i2c_bus; client->adapter = adapter; client->addr = chip->addr; @@ -372,3 +374,31 @@ int i2c_add_numbered_adapter(struct i2c_adapter *adapter) return 0; } EXPORT_SYMBOL(i2c_add_numbered_adapter); + +static int i2c_match(struct device_d *dev, struct driver_d *drv) +{ + return strcmp(dev->name, drv->name) ? -1 : 0; +} + +static int i2c_probe(struct device_d *dev) +{ + return dev->driver->probe(dev); +} + +static void i2c_remove(struct device_d *dev) +{ + dev->driver->remove(dev); +} + +struct bus_type i2c_bus = { + .name = "i2c", + .match = i2c_match, + .probe = i2c_probe, + .remove = i2c_remove, +}; + +static int i2c_bus_init(void) +{ + return bus_register(&i2c_bus); +} +pure_initcall(i2c_bus_init); diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c index d78cc01a05..7c12eba5da 100644 --- a/drivers/mci/imx-esdhc.c +++ b/drivers/mci/imx-esdhc.c @@ -331,7 +331,7 @@ esdhc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data) esdhc_write32(®s->irqstat, -1); /* Wait for the bus to be idle */ - ret = wait_on_timeout(100 * MSECOND, + ret = wait_on_timeout(SECOND, !(esdhc_read32(®s->prsstat) & (PRSSTAT_CICHB | PRSSTAT_CIDHB))); if (ret) { @@ -563,16 +563,28 @@ static int fsl_esdhc_probe(struct device_d *dev) return 0; } +static __maybe_unused struct of_device_id fsl_esdhc_compatible[] = { + { + .compatible = "fsl,imx51-esdhc", + }, { + .compatible = "fsl,imx53-esdhc", + }, { + .compatible = "fsl,imx6q-usdhc", + }, { + /* sentinel */ + } +}; + static struct driver_d fsl_esdhc_driver = { - .name = "imx-esdhc", - .probe = fsl_esdhc_probe, + .name = "imx-esdhc", + .probe = fsl_esdhc_probe, + .of_compatible = DRV_OF_COMPAT(fsl_esdhc_compatible), }; static int fsl_esdhc_init_driver(void) { - register_driver(&fsl_esdhc_driver); - return 0; + register_driver(&fsl_esdhc_driver); + return 0; } device_initcall(fsl_esdhc_init_driver); - diff --git a/drivers/mci/mci_spi.c b/drivers/mci/mci_spi.c index 35b454751f..5894104231 100644 --- a/drivers/mci/mci_spi.c +++ b/drivers/mci/mci_spi.c @@ -178,7 +178,7 @@ static uint mmc_spi_readdata(struct mmc_spi_host *host, void *xbuf, mmc_spi_readbytes(host, bsize, buf); mmc_spi_readbytes(host, 2, &crc); #ifdef CONFIG_MMC_SPI_CRC_ON - if (swab16(cyg_crc16(buf, bsize)) != crc) { + if (be16_to_cpu(cyg_crc16(buf, bsize)) != crc) { dev_dbg(host->dev, "%s: CRC error\n", __func__); r1 = R1_SPI_COM_CRC; break; @@ -209,7 +209,7 @@ static uint mmc_spi_writedata(struct mmc_spi_host *host, const void *xbuf, while (bcnt--) { #ifdef CONFIG_MMC_SPI_CRC_ON - crc = swab16(cyg_crc16((u8 *)buf, bsize)); + crc = be16_to_cpu(cyg_crc16((u8 *)buf, bsize)); #endif mmc_spi_writebytes(host, 2, tok); mmc_spi_writebytes(host, bsize, (void *)buf); @@ -288,7 +288,7 @@ static int mmc_spi_request(struct mci_host *mci, struct mci_cmd *cmd, struct mci } else if (cmd->resp_type == MMC_RSP_R2) { r1 = mmc_spi_readdata(host, cmd->response, 1, 16); for (i = 0; i < 4; i++) - cmd->response[i] = swab32(cmd->response[i]); + cmd->response[i] = be32_to_cpu(cmd->response[i]); dev_dbg(host->dev, "MMC_RSP_R2 -> %x %x %x %x\n", cmd->response[0], cmd->response[1], cmd->response[2], cmd->response[3]); } else if (!data) { @@ -296,7 +296,7 @@ static int mmc_spi_request(struct mci_host *mci, struct mci_cmd *cmd, struct mci case SD_CMD_SEND_IF_COND: case MMC_CMD_SPI_READ_OCR: mmc_spi_readbytes(host, 4, cmd->response); - cmd->response[0] = swab32(cmd->response[0]); + cmd->response[0] = be32_to_cpu(cmd->response[0]); break; } } else { @@ -420,7 +420,7 @@ static struct driver_d spi_mci_driver = { static int spi_mci_init_driver(void) { - register_driver(&spi_mci_driver); + spi_register_driver(&spi_mci_driver); return 0; } diff --git a/drivers/mci/omap_hsmmc.c b/drivers/mci/omap_hsmmc.c index 00bfc79765..eba1a6aedf 100644 --- a/drivers/mci/omap_hsmmc.c +++ b/drivers/mci/omap_hsmmc.c @@ -234,7 +234,7 @@ static int mmc_init_setup(struct mci_host *mci, struct device_d *dev) start = get_time_ns(); while ((readl(&mmc_base->sysstatus) & RESETDONE) == 0) { if (is_timeout(start, SECOND)) { - dev_dbg(hsmmc->dev, "timedout waiting for cc2!\n"); + dev_dbg(hsmmc->dev, "timeout waiting for reset done\n"); return -ETIMEDOUT; } } diff --git a/drivers/mfd/lp3972.c b/drivers/mfd/lp3972.c index 9d12717b56..082ecaa54b 100644 --- a/drivers/mfd/lp3972.c +++ b/drivers/mfd/lp3972.c @@ -99,7 +99,7 @@ static struct driver_d lp_driver = { static int lp_init(void) { - register_driver(&lp_driver); + i2c_register_driver(&lp_driver); return 0; } diff --git a/drivers/mfd/mc13xxx.c b/drivers/mfd/mc13xxx.c index a6a9655886..d6cf73cba8 100644 --- a/drivers/mfd/mc13xxx.c +++ b/drivers/mfd/mc13xxx.c @@ -332,31 +332,51 @@ static int mc_probe(struct device_d *dev, enum mc13xxx_mode mode) return 0; } +static __maybe_unused struct of_device_id mc13892_dt_ids[] = { + { + .compatible = "fsl,mc13892", + }, { + .compatible = "fsl,mc13783", + }, { + /* sentinel */ + } +}; + +#ifdef CONFIG_I2C static int mc_i2c_probe(struct device_d *dev) { return mc_probe(dev, MC13XXX_MODE_I2C); } -static int mc_spi_probe(struct device_d *dev) -{ - return mc_probe(dev, MC13XXX_MODE_SPI); -} - static struct driver_d mc_i2c_driver = { .name = "mc13xxx-i2c", .probe = mc_i2c_probe, + .of_compatible = DRV_OF_COMPAT(mc13892_dt_ids), }; +static int mc_i2c_init(void) +{ + return i2c_register_driver(&mc_i2c_driver); +} +device_initcall(mc_i2c_init); +#endif + +#ifdef CONFIG_SPI +static int mc_spi_probe(struct device_d *dev) +{ + return mc_probe(dev, MC13XXX_MODE_SPI); +} + static struct driver_d mc_spi_driver = { .name = "mc13xxx-spi", .probe = mc_spi_probe, + .of_compatible = DRV_OF_COMPAT(mc13892_dt_ids), }; -static int mc_init(void) +static int mc_spi_init(void) { - register_driver(&mc_i2c_driver); - register_driver(&mc_spi_driver); - return 0; + return spi_register_driver(&mc_spi_driver); } -device_initcall(mc_init); +device_initcall(mc_spi_init); +#endif diff --git a/drivers/mfd/mc34704.c b/drivers/mfd/mc34704.c index 6dc04c7330..276b723b1b 100644 --- a/drivers/mfd/mc34704.c +++ b/drivers/mfd/mc34704.c @@ -130,7 +130,7 @@ static struct driver_d mc34704_driver = { static int mc34704_init(void) { - register_driver(&mc34704_driver); - return 0; + i2c_register_driver(&mc34704_driver); + return 0; } device_initcall(mc34704_init); diff --git a/drivers/mfd/mc34708.c b/drivers/mfd/mc34708.c index a3b3530082..b1166de6b8 100644 --- a/drivers/mfd/mc34708.c +++ b/drivers/mfd/mc34708.c @@ -262,31 +262,40 @@ static int mc_probe(struct device_d *dev, enum mc34708_mode mode) return 0; } +#ifdef CONFIG_I2C static int mc_i2c_probe(struct device_d *dev) { return mc_probe(dev, MC34708_MODE_I2C); } -static int mc_spi_probe(struct device_d *dev) -{ - return mc_probe(dev, MC34708_MODE_SPI); -} - static struct driver_d mc_i2c_driver = { .name = "mc34708-i2c", .probe = mc_i2c_probe, }; +static int mc_i2c_init(void) +{ + return i2c_register_driver(&mc_i2c_driver); +} + +device_initcall(mc_i2c_init); +#endif + +#ifdef CONFIG_SPI +static int mc_spi_probe(struct device_d *dev) +{ + return mc_probe(dev, MC34708_MODE_SPI); +} + static struct driver_d mc_spi_driver = { .name = "mc34708-spi", .probe = mc_spi_probe, }; -static int mc_init(void) +static int mc_spi_init(void) { - register_driver(&mc_i2c_driver); - register_driver(&mc_spi_driver); - return 0; + return spi_register_driver(&mc_spi_driver); } -device_initcall(mc_init); +device_initcall(mc_spi_init); +#endif diff --git a/drivers/mfd/mc9sdz60.c b/drivers/mfd/mc9sdz60.c index 00ba0ffa87..76c62e331e 100644 --- a/drivers/mfd/mc9sdz60.c +++ b/drivers/mfd/mc9sdz60.c @@ -142,7 +142,7 @@ static struct driver_d mc_driver = { static int mc_init(void) { - register_driver(&mc_driver); + i2c_register_driver(&mc_driver); return 0; } diff --git a/drivers/mfd/stmpe-i2c.c b/drivers/mfd/stmpe-i2c.c index 4af8b7b88c..12e95c16b4 100644 --- a/drivers/mfd/stmpe-i2c.c +++ b/drivers/mfd/stmpe-i2c.c @@ -146,7 +146,7 @@ static struct driver_d stmpe_driver = { static int stmpe_init(void) { - register_driver(&stmpe_driver); + i2c_register_driver(&stmpe_driver); return 0; } diff --git a/drivers/mfd/twl4030.c b/drivers/mfd/twl4030.c index 191c91f36a..93097659c6 100644 --- a/drivers/mfd/twl4030.c +++ b/drivers/mfd/twl4030.c @@ -53,7 +53,7 @@ static struct driver_d twl_driver = { static int twl_init(void) { - register_driver(&twl_driver); + i2c_register_driver(&twl_driver); return 0; } diff --git a/drivers/mfd/twl6030.c b/drivers/mfd/twl6030.c index 7ecfed8062..01728fd317 100644 --- a/drivers/mfd/twl6030.c +++ b/drivers/mfd/twl6030.c @@ -49,7 +49,7 @@ static struct driver_d twl_driver = { static int twl_init(void) { - register_driver(&twl_driver); + i2c_register_driver(&twl_driver); return 0; } diff --git a/drivers/mtd/mtdraw.c b/drivers/mtd/mtdraw.c index d28ae0744e..f541802a84 100644 --- a/drivers/mtd/mtdraw.c +++ b/drivers/mtd/mtdraw.c @@ -128,7 +128,8 @@ static ssize_t mtdraw_read(struct cdev *cdev, void *buf, size_t count, skip = offset % (mtd->writesize + mtd->oobsize); while (ret > 0 && count > 0) { - toread = min_t(int, count, mtd->writesize + mtd->oobsize); + toread = min_t(int, count, + mtd->writesize + mtd->oobsize - skip); ret = mtdraw_read_unaligned(mtd, buf, toread, skip, numpage++ * mtd->writesize); buf += ret; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 37e57b32ce..fe9a6e7ab3 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1137,7 +1137,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, { struct nand_flash_dev *type = NULL; int i, dev_id, maf_idx; - int tmp_id, tmp_manf; + int id_data[8]; int ret; /* Select the device */ @@ -1166,13 +1166,13 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, /* Read manufacturer and device IDs */ - tmp_manf = chip->read_byte(mtd); - tmp_id = chip->read_byte(mtd); + id_data[0] = chip->read_byte(mtd); + id_data[1] = chip->read_byte(mtd); - if (tmp_manf != *maf_id || tmp_id != dev_id) { + if (id_data[0] != *maf_id || id_data[1] != dev_id) { printk(KERN_ERR "%s: second ID read did not match " "%02x,%02x against %02x,%02x\n", __func__, - *maf_id, dev_id, tmp_manf, tmp_id); + *maf_id, dev_id, id_data[0], id_data[1]); return ERR_PTR(-ENODEV); } @@ -1196,29 +1196,75 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, } } + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + + /* Read entire ID string */ + + for (i = 0; i < 8; i++) + id_data[i] = chip->read_byte(mtd); + if (!mtd->name) mtd->name = type->name; chip->chipsize = type->chipsize << 20; - /* Newer devices have all the information in additional id bytes */ if (!type->pagesize) { int extid; /* The 3rd id byte holds MLC / multichip data */ - chip->cellinfo = chip->read_byte(mtd); + chip->cellinfo = id_data[2]; /* The 4th id byte is the important one */ - extid = chip->read_byte(mtd); - /* Calc pagesize */ - mtd->writesize = 1024 << (extid & 0x3); - extid >>= 2; - /* Calc oobsize */ - mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); - extid >>= 2; - /* Calc blocksize. Blocksize is multiples of 64KiB */ - mtd->erasesize = (64 * 1024) << (extid & 0x03); - extid >>= 2; - /* Get buswidth information */ - busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + extid = id_data[3]; + + /* + * Field definitions are in the following datasheets: + * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32) + * New style (6 byte ID): Samsung K9GBG08U0M (p.40) + * + * Check for wraparound + Samsung ID + nonzero 6th byte + * to decide what to do. + */ + if (id_data[0] == id_data[6] && id_data[1] == id_data[7] && + id_data[0] == NAND_MFR_SAMSUNG && + (chip->cellinfo & NAND_CI_CELLTYPE_MSK) && + id_data[5] != 0x00) { + /* Calc pagesize */ + mtd->writesize = 2048 << (extid & 0x03); + extid >>= 2; + /* Calc oobsize */ + switch (extid & 0x03) { + case 1: + mtd->oobsize = 128; + break; + case 2: + mtd->oobsize = 218; + break; + case 3: + mtd->oobsize = 400; + break; + default: + mtd->oobsize = 436; + break; + } + extid >>= 2; + /* Calc blocksize */ + mtd->erasesize = (128 * 1024) << + (((extid >> 1) & 0x04) | (extid & 0x03)); + busw = 0; + } else { + /* Calc pagesize */ + mtd->writesize = 1024 << (extid & 0x03); + extid >>= 2; + /* Calc oobsize */ + mtd->oobsize = (8 << (extid & 0x01)) * + (mtd->writesize >> 9); + extid >>= 2; + /* Calc blocksize. Blocksize is multiples of 64KiB */ + mtd->erasesize = (64 * 1024) << (extid & 0x03); + extid >>= 2; + /* Get buswidth information */ + busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + } + } else { /* @@ -1228,6 +1274,19 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, mtd->writesize = type->pagesize; mtd->oobsize = mtd->writesize / 32; busw = type->options & NAND_BUSWIDTH_16; + + /* + * Check for Spansion/AMD ID + repeating 5th, 6th byte since + * some Spansion chips have erasesize that conflicts with size + * listed in nand_ids table + * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39) + */ + if (*maf_id == NAND_MFR_AMD && id_data[4] != 0x00 && + id_data[5] == 0x00 && id_data[6] == 0x00 && + id_data[7] == 0x00 && mtd->writesize == 512) { + mtd->erasesize = 128 * 1024; + mtd->erasesize <<= ((id_data[3] & 0x03) << 1); + } } /* Try to identify manufacturer */ diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index cb53fc61ca..72593d44a8 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -118,6 +118,36 @@ struct nand_flash_dev nand_flash_ids[] = { {__NANDSTR("NAND 2GiB 1,8V 16-bit"), 0xB5, 0, 2048, 0, LP_OPTIONS16}, {__NANDSTR("NAND 2GiB 3,3V 16-bit"), 0xC5, 0, 2048, 0, LP_OPTIONS16}, + /* 32 Gigabit */ + {__NANDSTR("NAND 4GiB 1,8V 8-bit"), 0xA7, 0, 4096, 0, LP_OPTIONS}, + {__NANDSTR("NAND 4GiB 3,3V 8-bit"), 0xD7, 0, 4096, 0, LP_OPTIONS}, + {__NANDSTR("NAND 4GiB 1,8V 16-bit"), 0xB7, 0, 4096, 0, LP_OPTIONS16}, + {__NANDSTR("NAND 4GiB 3,3V 16-bit"), 0xC7, 0, 4096, 0, LP_OPTIONS16}, + + /* 64 Gigabit */ + {__NANDSTR("NAND 8GiB 1,8V 8-bit"), 0xAE, 0, 8192, 0, LP_OPTIONS}, + {__NANDSTR("NAND 8GiB 3,3V 8-bit"), 0xDE, 0, 8192, 0, LP_OPTIONS}, + {__NANDSTR("NAND 8GiB 1,8V 16-bit"), 0xBE, 0, 8192, 0, LP_OPTIONS16}, + {__NANDSTR("NAND 8GiB 3,3V 16-bit"), 0xCE, 0, 8192, 0, LP_OPTIONS16}, + + /* 128 Gigabit */ + {__NANDSTR("NAND 16GiB 1,8V 8-bit"), 0x1A, 0, 16384, 0, LP_OPTIONS}, + {__NANDSTR("NAND 16GiB 3,3V 8-bit"), 0x3A, 0, 16384, 0, LP_OPTIONS}, + {__NANDSTR("NAND 16GiB 1,8V 16-bit"), 0x2A, 0, 16384, 0, LP_OPTIONS16}, + {__NANDSTR("NAND 16GiB 3,3V 16-bit"), 0x4A, 0, 16384, 0, LP_OPTIONS16}, + + /* 256 Gigabit */ + {__NANDSTR("NAND 32GiB 1,8V 8-bit"), 0x1C, 0, 32768, 0, LP_OPTIONS}, + {__NANDSTR("NAND 32GiB 3,3V 8-bit"), 0x3C, 0, 32768, 0, LP_OPTIONS}, + {__NANDSTR("NAND 32GiB 1,8V 16-bit"), 0x2C, 0, 32768, 0, LP_OPTIONS16}, + {__NANDSTR("NAND 32GiB 3,3V 16-bit"), 0x4C, 0, 32768, 0, LP_OPTIONS16}, + + /* 512 Gigabit */ + {__NANDSTR("NAND 64GiB 1,8V 8-bit"), 0x1E, 0, 65536, 0, LP_OPTIONS}, + {__NANDSTR("NAND 64GiB 3,3V 8-bit"), 0x3E, 0, 65536, 0, LP_OPTIONS}, + {__NANDSTR("NAND 64GiB 1,8V 16-bit"), 0x2E, 0, 65536, 0, LP_OPTIONS16}, + {__NANDSTR("NAND 64GiB 3,3V 16-bit"), 0x4E, 0, 65536, 0, LP_OPTIONS16}, + /* * Renesas AND 1 Gigabit. Those chips do not support extended id and * have a strange page/block layout ! The chosen minimum erasesize is diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 3c5f729db3..b3e5a83dc1 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -19,64 +19,71 @@ config HAS_DESIGNWARE_ETH config ARCH_HAS_FEC_IMX bool -config MIIDEV +config PHYLIB bool menu "Network drivers " depends on NET +source "drivers/net/phy/Kconfig" + config DRIVER_NET_CS8900 bool "cs8900 ethernet driver" depends on HAS_CS8900 config DRIVER_NET_SMC911X bool "smc911x ethernet driver" - select MIIDEV + select PHYLIB help This option enables support for the SMSC LAN9[12]1[567] ethernet chip. config DRIVER_NET_SMC91111 bool "smc91111 ethernet driver" - select MIIDEV + select PHYLIB help This option enables support for the SMSC LAN91C111 ethernet chip. +config DRIVER_NET_DAVINCI_EMAC + bool "TI Davinci/OMAP EMAC ethernet driver" + depends on ARCH_DAVINCI || ARCH_OMAP3 + select PHYLIB + config DRIVER_NET_DM9K bool "Davicom dm9k[E|A|B] ethernet driver" depends on HAS_DM9000 - select MIIDEV + select PHYLIB config DRIVER_NET_NETX bool "Hilscher Netx ethernet driver" depends on HAS_NETX_ETHER - select MIIDEV + select PHYLIB config DRIVER_NET_AT91_ETHER bool "at91 ethernet driver" depends on HAS_AT91_ETHER - select MIIDEV + select PHYLIB config DRIVER_NET_MPC5200 bool "MPC5200 Ethernet driver" depends on ARCH_MPC5200 - select MIIDEV + select PHYLIB config DRIVER_NET_FEC_IMX bool "i.MX FEC Ethernet driver" depends on ARCH_HAS_FEC_IMX - select MIIDEV + select PHYLIB config DRIVER_NET_EP93XX bool "EP93xx Ethernet driver" depends on ARCH_EP93XX - select MIIDEV + select PHYLIB config DRIVER_NET_MACB bool "macb Ethernet driver" depends on HAS_MACB - select MIIDEV + select PHYLIB config DRIVER_NET_TAP bool "tap Ethernet driver" @@ -85,7 +92,7 @@ config DRIVER_NET_TAP config DRIVER_NET_TSE depends on NIOS2 bool "Altera TSE ethernet driver" - select MIIDEV + select PHYLIB help This option enables support for the Altera TSE MAC. @@ -100,14 +107,14 @@ config TSE_USE_DEDICATED_DESC_MEM config DRIVER_NET_KS8851_MLL bool "ks8851 mll ethernet driver" - select MIIDEV + select PHYLIB help This option enables support for the Micrel KS8851 MLL ethernet chip. config DRIVER_NET_DESIGNWARE bool "Designware Universal MAC ethernet driver" - select MIIDEV + select PHYLIB depends on HAS_DESIGNWARE_ETH help This option enables support for the Synopsys @@ -121,9 +128,16 @@ config DRIVER_NET_DESIGNWARE_ALTDESCRIPTOR config DRIVER_NET_GIANFAR bool "Gianfar Ethernet" depends on ARCH_MPC85XX - select MIIDEV + select PHYLIB source "drivers/net/usb/Kconfig" +config DRIVER_NET_MICREL + depends on SPI + bool "Micrel KSZ8864RMN Ethernet Switch driver" + help + This option enables support for enabling the Micrel + KSZ8864RMN Ethernet Switch over SPI. + endmenu diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 4d960e856f..4e6b49b191 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_DRIVER_NET_CS8900) += cs8900.o obj-$(CONFIG_DRIVER_NET_SMC911X) += smc911x.o obj-$(CONFIG_DRIVER_NET_SMC91111) += smc91111.o +obj-$(CONFIG_DRIVER_NET_DAVINCI_EMAC) += davinci_emac.o obj-$(CONFIG_DRIVER_NET_DM9K) += dm9k.o obj-$(CONFIG_DRIVER_NET_NETX) += netx_eth.o obj-$(CONFIG_DRIVER_NET_AT91_ETHER) += at91_ether.o @@ -9,9 +10,10 @@ obj-$(CONFIG_DRIVER_NET_FEC_IMX) += fec_imx.o obj-$(CONFIG_DRIVER_NET_EP93XX) += ep93xx.o obj-$(CONFIG_DRIVER_NET_MACB) += macb.o obj-$(CONFIG_DRIVER_NET_TAP) += tap.o -obj-$(CONFIG_MIIDEV) += miidev.o +obj-$(CONFIG_PHYLIB) += phy/ obj-$(CONFIG_NET_USB) += usb/ obj-$(CONFIG_DRIVER_NET_TSE) += altera_tse.o obj-$(CONFIG_DRIVER_NET_KS8851_MLL) += ks8851_mll.o obj-$(CONFIG_DRIVER_NET_DESIGNWARE) += designware.o obj-$(CONFIG_DRIVER_NET_GIANFAR) += gianfar.o +obj-$(CONFIG_DRIVER_NET_MICREL) += ksz8864rmn.o diff --git a/drivers/net/altera_tse.c b/drivers/net/altera_tse.c index 4643f001b4..9323f38019 100644 --- a/drivers/net/altera_tse.c +++ b/drivers/net/altera_tse.c @@ -22,10 +22,10 @@ #include <common.h> #include <net.h> -#include <miidev.h> #include <init.h> #include <clock.h> #include <linux/mii.h> +#include <linux/phy.h> #include <io.h> #include <asm/dma-mapping.h> @@ -243,10 +243,9 @@ static int tse_set_ethaddr(struct eth_device *edev, unsigned char *m) return 0; } -static int tse_phy_read(struct mii_device *mdev, int phy_addr, int reg) +static int tse_phy_read(struct mii_bus *bus, int phy_addr, int reg) { - struct eth_device *edev = mdev->edev; - struct altera_tse_priv *priv = edev->priv; + struct altera_tse_priv *priv = bus->priv; struct alt_tse_mac *mac_dev = priv->tse_regs; uint32_t *mdio_regs; @@ -257,10 +256,9 @@ static int tse_phy_read(struct mii_device *mdev, int phy_addr, int reg) return readl(&mdio_regs[reg]) & 0xFFFF; } -static int tse_phy_write(struct mii_device *mdev, int phy_addr, int reg, int val) +static int tse_phy_write(struct mii_bus *bus, int phy_addr, int reg, u16 val) { - struct eth_device *edev = mdev->edev; - struct altera_tse_priv *priv = edev->priv; + struct altera_tse_priv *priv = bus->priv; struct alt_tse_mac *mac_dev = priv->tse_regs; uint32_t *mdio_regs; @@ -343,9 +341,12 @@ static void tse_reset(struct eth_device *edev) static int tse_eth_open(struct eth_device *edev) { struct altera_tse_priv *priv = edev->priv; + int ret; - miidev_wait_aneg(priv->miidev); - miidev_print_status(priv->miidev); + ret = phy_device_connect(edev, priv->miibus, priv->phy_addr, NULL, 0, + PHY_INTERFACE_MODE_NA); + if (ret) + return ret; return 0; } @@ -484,15 +485,13 @@ static int tse_init_dev(struct eth_device *edev) /* enable MAC */ writel(ALTERA_TSE_CMD_TX_ENA_MSK | ALTERA_TSE_CMD_RX_ENA_MSK, &mac_dev->command_config); - miidev_restart_aneg(priv->miidev); - return 0; } static int tse_probe(struct device_d *dev) { struct altera_tse_priv *priv; - struct mii_device *miidev; + struct mii_bus *miibus; struct eth_device *edev; struct alt_sgdma_descriptor *rx_desc; struct alt_sgdma_descriptor *tx_desc; @@ -501,7 +500,7 @@ static int tse_probe(struct device_d *dev) #endif edev = xzalloc(sizeof(struct eth_device)); priv = xzalloc(sizeof(struct altera_tse_priv)); - miidev = xzalloc(sizeof(struct mii_device)); + miibus = xzalloc(sizeof(struct mii_bus)); edev->priv = priv; @@ -523,7 +522,7 @@ static int tse_probe(struct device_d *dev) if (!tx_desc) { free(edev); - free(miidev); + free(miibus); return 0; } #endif @@ -537,22 +536,19 @@ static int tse_probe(struct device_d *dev) priv->rx_desc = rx_desc; priv->tx_desc = tx_desc; - priv->miidev = miidev; + priv->miibus = miibus; - miidev->read = tse_phy_read; - miidev->write = tse_phy_write; - miidev->flags = 0; - miidev->edev = edev; - miidev->parent = dev; + miibus->read = tse_phy_read; + miibus->write = tse_phy_write; + miibus->priv = priv; + miibus->parent = dev; if (dev->platform_data != NULL) - miidev->address = *((int8_t *)(dev->platform_data)); - else { - printf("No PHY address specified.\n"); - return -ENODEV; - } + priv->phy_addr = *((int8_t *)(dev->platform_data)); + else + priv->phy_addr = -1; - mii_register(miidev); + mdiobus_register(miibus); return eth_register(edev); } diff --git a/drivers/net/altera_tse.h b/drivers/net/altera_tse.h index 866dbce8c3..5a66a7d289 100644 --- a/drivers/net/altera_tse.h +++ b/drivers/net/altera_tse.h @@ -292,7 +292,8 @@ struct altera_tse_priv { void __iomem *sgdma_tx_regs; void __iomem *rx_desc; void __iomem *tx_desc; - struct mii_device *miidev; + int phy_addr; + struct mii_bus *miibus; }; #endif /* _ALTERA_TSE_H_ */ diff --git a/drivers/net/at91_ether.c b/drivers/net/at91_ether.c index cd00dabf4a..dee2039b11 100644 --- a/drivers/net/at91_ether.c +++ b/drivers/net/at91_ether.c @@ -26,7 +26,6 @@ #include <driver.h> #include <xfuncs.h> #include <init.h> -#include <miidev.h> #include <asm/io.h> #include <mach/hardware.h> #include <mach/at91rm9200_emac.h> @@ -36,18 +35,18 @@ #include <linux/mii.h> #include <errno.h> #include <asm/mmu.h> +#include <linux/phy.h> #include "at91_ether.h" -#define SPEED_100 1 -#define DUPLEX_FULL 1 - struct ether_device { struct eth_device netdev; - struct mii_device miidev; + struct mii_bus miibus; struct rbf_t *rbfp; struct rbf_t *rbfdt; unsigned char *rbf_framebuf; + int phy_addr; + phy_interface_t interface; }; #define to_ether(_nd) container_of(_nd, struct ether_device, netdev) @@ -94,7 +93,7 @@ static inline int at91_phy_wait(void) return 0; } -static int at91_ether_mii_read(struct mii_device *dev, int addr, int reg) +static int at91_ether_mii_read(struct mii_bus *dev, int addr, int reg) { int value; @@ -116,7 +115,7 @@ out: return value; } -static int at91_ether_mii_write(struct mii_device *dev, int addr, int reg, int val) +static int at91_ether_mii_write(struct mii_bus *dev, int addr, int reg, u16 val) { int ret; @@ -132,19 +131,19 @@ static int at91_ether_mii_write(struct mii_device *dev, int addr, int reg, int v return ret; } -static void update_linkspeed(struct mii_device *dev, int speed, int duplex) +static void update_linkspeed(struct eth_device *edev) { unsigned int mac_cfg; /* Update the MAC */ mac_cfg = at91_emac_read(AT91_EMAC_CFG) & ~(AT91_EMAC_SPD | AT91_EMAC_FD); - if (speed == SPEED_100) { - if (duplex == DUPLEX_FULL) /* 100 Full Duplex */ + if (edev->phydev->speed == SPEED_100) { + if (edev->phydev->duplex) mac_cfg |= AT91_EMAC_SPD | AT91_EMAC_FD; else /* 100 Half Duplex */ mac_cfg |= AT91_EMAC_SPD; } else { - if (duplex == DUPLEX_FULL) /* 10 Full Duplex */ + if (edev->phydev->duplex) mac_cfg |= AT91_EMAC_FD; else {} /* 10 Half Duplex */ } @@ -157,11 +156,12 @@ static int at91_ether_open(struct eth_device *edev) unsigned long ctl; struct ether_device *etdev = to_ether(edev); unsigned char *rbf_framebuf = etdev->rbf_framebuf; + int ret; - miidev_wait_aneg(&etdev->miidev); - miidev_print_status(&etdev->miidev); - - update_linkspeed(&etdev->miidev, SPEED_100, DUPLEX_FULL); + ret = phy_device_connect(edev, &etdev->miibus, etdev->phy_addr, + update_linkspeed, 0, etdev->interface); + if (ret) + return ret; /* Clear internal statistics */ ctl = at91_emac_read(AT91_EMAC_CTL); @@ -295,7 +295,7 @@ static int at91_ether_probe(struct device_d *dev) unsigned int mac_cfg; struct ether_device *ether_dev; struct eth_device *edev; - struct mii_device *miidev; + struct mii_bus *miibus; unsigned long ether_hz; struct clk *pclk; struct at91_ether_platform_data *pdata; @@ -310,7 +310,7 @@ static int at91_ether_probe(struct device_d *dev) ether_dev = xzalloc(sizeof(struct ether_device)); edev = ðer_dev->netdev; - miidev = ðer_dev->miidev; + miibus = ðer_dev->miibus; edev->priv = ether_dev; edev->init = at91_ether_init; @@ -323,10 +323,9 @@ static int at91_ether_probe(struct device_d *dev) ether_dev->rbf_framebuf = dma_alloc_coherent(MAX_RX_DESCR * MAX_RBUFF_SZ); ether_dev->rbfdt = dma_alloc_coherent(sizeof(struct rbf_t) * MAX_RX_DESCR); - miidev->address = pdata->phy_addr; - miidev->read = at91_ether_mii_read; - miidev->write = at91_ether_mii_write; - miidev->edev = edev; + ether_dev->phy_addr = pdata->phy_addr; + miibus->read = at91_ether_mii_read; + miibus->write = at91_ether_mii_write; /* Sanitize the clocks */ mac_cfg = at91_emac_read(AT91_EMAC_CFG); @@ -343,12 +342,16 @@ static int at91_ether_probe(struct device_d *dev) mac_cfg |= AT91_EMAC_CLK_DIV32 | AT91_EMAC_BIG; - if (pdata->flags & AT91SAM_ETHER_RMII) + if (pdata->flags & AT91SAM_ETHER_RMII) { + ether_dev->interface = PHY_INTERFACE_MODE_RGMII; mac_cfg |= AT91_EMAC_RMII; + } else { + ether_dev->interface = PHY_INTERFACE_MODE_MII; + } at91_emac_write(AT91_EMAC_CFG, mac_cfg); - mii_register(miidev); + mdiobus_register(miibus); eth_register(edev); return 0; diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c new file mode 100644 index 0000000000..7d4dbdcdd9 --- /dev/null +++ b/drivers/net/davinci_emac.c @@ -0,0 +1,618 @@ +/* + * Copyright (C) 2012 Jan Luebbe <j.luebbe@pengutronix.de> + * + * Ethernet driver for TI TMS320DM644x (DaVinci) chips. + * + * Copyright (C) 2007 Sergey Kubushyn <ksi@koi8.net> + * + * Parts shamelessly stolen from TI's dm644x_emac.c. Original copyright + * follows: + * + * ---------------------------------------------------------------------------- + * + * dm644x_emac.c + * + * TI DaVinci (DM644X) EMAC peripheral driver source for DV-EVM + * + * Copyright (C) 2005 Texas Instruments. + * + * ---------------------------------------------------------------------------- + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * ---------------------------------------------------------------------------- + + * Modifications: + * ver. 1.0: Sep 2005, Anant Gole - Created EMAC version for uBoot. + * ver 1.1: Nov 2005, Anant Gole - Extended the RX logic for multiple descriptors + * + */ + +#include <common.h> +#include <io.h> +#include <clock.h> +#include <net.h> +#include <malloc.h> +#include <init.h> +#include <asm/mmu.h> +#include <asm/system.h> +#include <linux/phy.h> +#include <mach/emac_defs.h> +#include <net/davinci_emac.h> +#include "davinci_emac.h" + +struct davinci_emac_priv { + struct device_d *dev; + struct eth_device edev; + struct mii_bus miibus; + + /* EMAC Addresses */ + void __iomem *adap_emac; /* = EMAC_BASE_ADDR */ + void __iomem *adap_ewrap; /* = EMAC_WRAPPER_BASE_ADDR */ + void __iomem *adap_mdio; /* = EMAC_MDIO_BASE_ADDR */ + + /* EMAC descriptors */ + void __iomem *emac_desc_base; /* = EMAC_WRAPPER_RAM_ADDR */ + void __iomem *emac_rx_desc; /* = EMAC_WRAPPER_RAM_ADDR + EMAC_RX_DESC_BASE */ + void __iomem *emac_tx_desc; /* = EMAC_WRAPPER_RAM_ADDR + EMAC_TX_DESC_BASE */ + void __iomem *emac_rx_active_head; /* = 0 */ + void __iomem *emac_rx_active_tail; /* = 0 */ + int emac_rx_queue_active; /* = 0 */ + + /* Receive packet buffers */ + unsigned char *emac_rx_buffers; /* [EMAC_MAX_RX_BUFFERS * (EMAC_MAX_ETHERNET_PKT_SIZE + EMAC_PKT_ALIGN)] */ + + /* PHY-specific information */ + phy_interface_t interface; + uint8_t phy_addr; + uint32_t phy_flags; + + /* mac_addr[0] goes out on the wire first */ + uint8_t mac_addr[6]; +}; + +#ifdef EMAC_HW_RAM_ADDR +static inline uint32_t BD_TO_HW(void __iomem *x) +{ + if (x == 0) + return 0; + + return (uint32_t)(x) - EMAC_WRAPPER_RAM_ADDR + EMAC_HW_RAM_ADDR; +} + +static inline void __iomem *HW_TO_BD(uint32_t x) +{ + if (x == 0) + return 0; + + return (struct emac_desc*)(x - EMAC_HW_RAM_ADDR + EMAC_WRAPPER_RAM_ADDR); +} +#else +#define BD_TO_HW(x) (x) +#define HW_TO_BD(x) (x) +#endif + +static void davinci_eth_mdio_enable(struct davinci_emac_priv *priv) +{ + uint32_t clkdiv; + + clkdiv = (EMAC_MDIO_BUS_FREQ / EMAC_MDIO_CLOCK_FREQ) - 1; + + dev_dbg(priv->dev, "mdio_enable + 0x%08x\n", + readl(priv->adap_mdio + EMAC_MDIO_CONTROL)); + writel((clkdiv & 0xff) | + MDIO_CONTROL_ENABLE | + MDIO_CONTROL_FAULT | + MDIO_CONTROL_FAULT_ENABLE, + priv->adap_mdio + EMAC_MDIO_CONTROL); + dev_dbg(priv->dev, "mdio_enable - 0x%08x\n", + readl(priv->adap_mdio + EMAC_MDIO_CONTROL)); + + while (readl(priv->adap_mdio + EMAC_MDIO_CONTROL) & MDIO_CONTROL_IDLE); +} + +static int davinci_miibus_read(struct mii_bus *bus, int addr, int reg) +{ + struct davinci_emac_priv *priv = bus->priv; + uint16_t value; + int tmp; + + while (readl(priv->adap_mdio + EMAC_MDIO_USERACCESS0) & MDIO_USERACCESS0_GO); + + writel(MDIO_USERACCESS0_GO | + MDIO_USERACCESS0_WRITE_READ | + ((reg & 0x1f) << 21) | + ((addr & 0x1f) << 16), + priv->adap_mdio + EMAC_MDIO_USERACCESS0); + + /* Wait for command to complete */ + while ((tmp = readl(priv->adap_mdio + EMAC_MDIO_USERACCESS0)) & MDIO_USERACCESS0_GO); + + if (tmp & MDIO_USERACCESS0_ACK) { + value = tmp & 0xffff; + dev_dbg(priv->dev, "davinci_miibus_read: addr=0x%02x reg=0x%02x value=0x%04x\n", + addr, reg, value); + return value; + } + + return -1; +} + +static int davinci_miibus_write(struct mii_bus *bus, int addr, int reg, u16 value) +{ + struct davinci_emac_priv *priv = bus->priv; + while (readl(priv->adap_mdio + EMAC_MDIO_USERACCESS0) & MDIO_USERACCESS0_GO); + + dev_dbg(priv->dev, "davinci_miibus_write: addr=0x%02x reg=0x%02x value=0x%04x\n", + addr, reg, value); + writel(MDIO_USERACCESS0_GO | + MDIO_USERACCESS0_WRITE_WRITE | + ((reg & 0x1f) << 21) | + ((addr & 0x1f) << 16) | + (value & 0xffff), + priv->adap_mdio + EMAC_MDIO_USERACCESS0); + + /* Wait for command to complete */ + while (readl(priv->adap_mdio + EMAC_MDIO_USERACCESS0) & MDIO_USERACCESS0_GO); + + return 0; +} + +static int davinci_emac_get_ethaddr(struct eth_device *edev, unsigned char *adr) +{ + return -1; +} + +/* + * This function must be called before emac_open() if you want to override + * the default mac address. + */ +static int davinci_emac_set_ethaddr(struct eth_device *edev, unsigned char *addr) +{ + struct davinci_emac_priv *priv = edev->priv; + int i; + + for (i = 0; i < sizeof(priv->mac_addr); i++) + priv->mac_addr[i] = addr[i]; + return 0; +} + +static int davinci_emac_init(struct eth_device *edev) +{ + dev_dbg(&edev->dev, "* emac_init\n"); + return 0; +} + +static int davinci_emac_open(struct eth_device *edev) +{ + struct davinci_emac_priv *priv = edev->priv; + uint32_t clkdiv, cnt; + void __iomem *rx_desc; + unsigned long mac_hi, mac_lo; + int ret; + + dev_dbg(priv->dev, "+ emac_open\n"); + + dev_dbg(priv->dev, "emac->TXIDVER: 0x%08x\n", + readl(priv->adap_emac + EMAC_TXIDVER)); + dev_dbg(priv->dev, "emac->RXIDVER: 0x%08x\n", + readl(priv->adap_emac + EMAC_RXIDVER)); + + /* Reset EMAC module and disable interrupts in wrapper */ + writel(1, priv->adap_emac + EMAC_SOFTRESET); + while (readl(priv->adap_emac + EMAC_SOFTRESET) != 0); + writel(1, priv->adap_ewrap + EMAC_EWRAP_SOFTRESET); + while (readl(priv->adap_ewrap + EMAC_EWRAP_SOFTRESET) != 0); + + writel(0, priv->adap_ewrap + EMAC_EWRAP_C0RXEN); + writel(0, priv->adap_ewrap + EMAC_EWRAP_C1RXEN); + writel(0, priv->adap_ewrap + EMAC_EWRAP_C2RXEN); + writel(0, priv->adap_ewrap + EMAC_EWRAP_C0TXEN); + writel(0, priv->adap_ewrap + EMAC_EWRAP_C1TXEN); + writel(0, priv->adap_ewrap + EMAC_EWRAP_C2TXEN); + writel(0, priv->adap_ewrap + EMAC_EWRAP_C0MISCEN); + writel(0, priv->adap_ewrap + EMAC_EWRAP_C1MISCEN); + writel(0, priv->adap_ewrap + EMAC_EWRAP_C2MISCEN); + + rx_desc = priv->emac_rx_desc; + + /* + * Set MAC Addresses & Init multicast Hash to 0 (disable any multicast + * receive) + * Use channel 0 only - other channels are disabled + */ + writel(0, priv->adap_emac + EMAC_MACINDEX); + mac_hi = (priv->mac_addr[3] << 24) | + (priv->mac_addr[2] << 16) | + (priv->mac_addr[1] << 8) | + (priv->mac_addr[0]); + mac_lo = (priv->mac_addr[5] << 8) | + (priv->mac_addr[4]); + + writel(mac_hi, priv->adap_emac + EMAC_MACADDRHI); + writel(mac_lo | EMAC_MAC_ADDR_IS_VALID | EMAC_MAC_ADDR_MATCH, + priv->adap_emac + EMAC_MACADDRLO); + + /* Set source MAC address - REQUIRED */ + writel(mac_hi, priv->adap_emac + EMAC_MACSRCADDRHI); + writel(mac_lo, priv->adap_emac + EMAC_MACSRCADDRLO); + + /* Set DMA head and completion pointers to 0 */ + for(cnt = 0; cnt < 8; cnt++) { + writel(0, (void *)priv->adap_emac + EMAC_TX0HDP + 4 * cnt); + writel(0, (void *)priv->adap_emac + EMAC_RX0HDP + 4 * cnt); + writel(0, (void *)priv->adap_emac + EMAC_TX0CP + 4 * cnt); + writel(0, (void *)priv->adap_emac + EMAC_RX0CP + 4 * cnt); + } + + /* Clear Statistics (do this before setting MacControl register) */ + for(cnt = 0; cnt < EMAC_NUM_STATS; cnt++) + writel(0, (void *)priv->adap_emac + EMAC_RXGOODFRAMES + 4 * cnt); + + /* No multicast addressing */ + writel(0, priv->adap_emac + EMAC_MACHASH1); + writel(0, priv->adap_emac + EMAC_MACHASH2); + + writel(0x01, priv->adap_emac + EMAC_TXCONTROL); + writel(0x01, priv->adap_emac + EMAC_RXCONTROL); + + /* Create RX queue and set receive process in place */ + priv->emac_rx_active_head = priv->emac_rx_desc; + for (cnt = 0; cnt < EMAC_MAX_RX_BUFFERS; cnt++) { + writel(BD_TO_HW(rx_desc + EMAC_DESC_SIZE), rx_desc + EMAC_DESC_NEXT); + writel(&priv->emac_rx_buffers[cnt * (EMAC_MAX_ETHERNET_PKT_SIZE + EMAC_PKT_ALIGN)], rx_desc + EMAC_DESC_BUFFER); + writel(EMAC_MAX_ETHERNET_PKT_SIZE, rx_desc + EMAC_DESC_BUFF_OFF_LEN); + writel(EMAC_CPPI_OWNERSHIP_BIT, rx_desc + EMAC_DESC_PKT_FLAG_LEN); + rx_desc += EMAC_DESC_SIZE; + } + + /* Set the last descriptor's "next" parameter to 0 to end the RX desc list */ + rx_desc -= EMAC_DESC_SIZE; + writel(0, rx_desc + EMAC_DESC_NEXT); + priv->emac_rx_active_tail = rx_desc; + priv->emac_rx_queue_active = 1; + + /* Enable TX/RX */ + writel(EMAC_MAX_ETHERNET_PKT_SIZE, priv->adap_emac + EMAC_RXMAXLEN); + writel(0, priv->adap_emac + EMAC_RXBUFFEROFFSET); + + /* No fancy configs - Use this for promiscous for debug - EMAC_RXMBPENABLE_RXCAFEN_ENABLE */ + writel(EMAC_RXMBPENABLE_RXBROADEN, priv->adap_emac + EMAC_RXMBPENABLE); + + /* Enable ch 0 only */ + writel(0x01, priv->adap_emac + EMAC_RXUNICASTSET); + + /* Enable MII interface and full duplex mode (using RMMI) */ + writel((EMAC_MACCONTROL_MIIEN_ENABLE | + EMAC_MACCONTROL_FULLDUPLEX_ENABLE | + EMAC_MACCONTROL_RMIISPEED_100), + priv->adap_emac + EMAC_MACCONTROL); + + /* Init MDIO & get link state */ + clkdiv = (EMAC_MDIO_BUS_FREQ / EMAC_MDIO_CLOCK_FREQ) - 1; + writel((clkdiv & 0xff) | MDIO_CONTROL_ENABLE | MDIO_CONTROL_FAULT, + priv->adap_mdio + EMAC_MDIO_CONTROL); + + /* Start receive process */ + writel(BD_TO_HW(priv->emac_rx_desc), priv->adap_emac + EMAC_RX0HDP); + + ret = phy_device_connect(edev, &priv->miibus, priv->phy_addr, NULL, + priv->phy_flags, priv->interface); + if (ret) + return ret; + + dev_dbg(priv->dev, "- emac_open\n"); + + return 0; +} + +/* EMAC Channel Teardown */ +static void davinci_eth_ch_teardown(struct davinci_emac_priv *priv, int ch) +{ + uint32_t dly = 0xff; + uint32_t cnt; + + dev_dbg(priv->dev, "+ emac_ch_teardown\n"); + + if (ch == EMAC_CH_TX) { + /* Init TX channel teardown */ + writel(0, priv->adap_emac + EMAC_TXTEARDOWN); + for(cnt = 0; cnt != 0xfffffffc; cnt = readl(priv->adap_emac + EMAC_TX0CP)) { + /* Wait here for Tx teardown completion interrupt to occur + * Note: A task delay can be called here to pend rather than + * occupying CPU cycles - anyway it has been found that teardown + * takes very few cpu cycles and does not affect functionality */ + dly--; + udelay(1); + if (dly == 0) + break; + } + writel(cnt, priv->adap_emac + EMAC_TX0CP); + writel(0, priv->adap_emac + EMAC_TX0HDP); + } else { + /* Init RX channel teardown */ + writel(0, priv->adap_emac + EMAC_RXTEARDOWN); + for(cnt = 0; cnt != 0xfffffffc; cnt = readl(priv->adap_emac + EMAC_RX0CP)) { + /* Wait here for Rx teardown completion interrupt to occur + * Note: A task delay can be called here to pend rather than + * occupying CPU cycles - anyway it has been found that teardown + * takes very few cpu cycles and does not affect functionality */ + dly--; + udelay(1); + if (dly == 0) + break; + } + writel(cnt, priv->adap_emac + EMAC_RX0CP); + writel(0, priv->adap_emac + EMAC_RX0HDP); + } + + dev_dbg(priv->dev, "- emac_ch_teardown\n"); +} + +static void davinci_emac_halt(struct eth_device *edev) +{ + struct davinci_emac_priv *priv = edev->priv; + + dev_dbg(priv->dev, "+ emac_halt\n"); + + davinci_eth_ch_teardown(priv, EMAC_CH_TX); /* TX Channel teardown */ + davinci_eth_ch_teardown(priv, EMAC_CH_RX); /* RX Channel teardown */ + + /* Reset EMAC module and disable interrupts in wrapper */ + writel(1, priv->adap_emac + EMAC_SOFTRESET); + writel(1, priv->adap_ewrap + EMAC_EWRAP_SOFTRESET); + + writel(0, priv->adap_ewrap + EMAC_EWRAP_C0RXEN); + writel(0, priv->adap_ewrap + EMAC_EWRAP_C1RXEN); + writel(0, priv->adap_ewrap + EMAC_EWRAP_C2RXEN); + writel(0, priv->adap_ewrap + EMAC_EWRAP_C0TXEN); + writel(0, priv->adap_ewrap + EMAC_EWRAP_C1TXEN); + writel(0, priv->adap_ewrap + EMAC_EWRAP_C2TXEN); + writel(0, priv->adap_ewrap + EMAC_EWRAP_C0MISCEN); + writel(0, priv->adap_ewrap + EMAC_EWRAP_C1MISCEN); + writel(0, priv->adap_ewrap + EMAC_EWRAP_C2MISCEN); + + dev_dbg(priv->dev, "- emac_halt\n"); +} + +/* + * This function sends a single packet on the network and returns + * positive number (number of bytes transmitted) or negative for error + */ +static int davinci_emac_send(struct eth_device *edev, void *packet, int length) +{ + struct davinci_emac_priv *priv = edev->priv; + uint64_t start; + int ret_status; + + dev_dbg(priv->dev, "+ emac_send (length %d)\n", length); + + /* Check packet size and if < EMAC_MIN_ETHERNET_PKT_SIZE, pad it up */ + if (length < EMAC_MIN_ETHERNET_PKT_SIZE) { + length = EMAC_MIN_ETHERNET_PKT_SIZE; + } + + /* Populate the TX descriptor */ + writel(0, priv->emac_tx_desc + EMAC_DESC_NEXT); + writel((uint8_t *) packet, priv->emac_tx_desc + EMAC_DESC_BUFFER); + writel((length & 0xffff), priv->emac_tx_desc + EMAC_DESC_BUFF_OFF_LEN); + writel(((length & 0xffff) | EMAC_CPPI_SOP_BIT | + EMAC_CPPI_OWNERSHIP_BIT | + EMAC_CPPI_EOP_BIT), + priv->emac_tx_desc + EMAC_DESC_PKT_FLAG_LEN); + dma_flush_range((ulong) packet, (ulong)packet + length); + /* Send the packet */ + writel(BD_TO_HW(priv->emac_tx_desc), priv->adap_emac + EMAC_TX0HDP); + + /* Wait for packet to complete or link down */ + start = get_time_ns(); + while (1) { + if (readl(priv->adap_emac + EMAC_TXINTSTATRAW) & 0x01) { + /* Acknowledge the TX descriptor */ + writel(BD_TO_HW(priv->emac_tx_desc), priv->adap_emac + EMAC_TX0CP); + ret_status = 0; + break; + } + if (is_timeout(start, 100 * MSECOND)) { + ret_status = -ETIMEDOUT; + break; + } + } + + dev_dbg(priv->dev, "- emac_send (ret_status %i)\n", ret_status); + return ret_status; +} + +/* + * This function handles receipt of a packet from the network + */ +static int davinci_emac_recv(struct eth_device *edev) +{ + struct davinci_emac_priv *priv = edev->priv; + void __iomem *rx_curr_desc, *curr_desc, *tail_desc; + unsigned char *pkt; + int status, len, ret = -1; + + dev_dbg(priv->dev, "+ emac_recv\n"); + + rx_curr_desc = priv->emac_rx_active_head; + status = readl(rx_curr_desc + EMAC_DESC_PKT_FLAG_LEN); + if (status & EMAC_CPPI_OWNERSHIP_BIT) { + ret = 0; + goto out; + } + + if (status & EMAC_CPPI_RX_ERROR_FRAME) { + /* Error in packet - discard it and requeue desc */ + dev_warn(priv->dev, "WARN: emac_rcv_pkt: Error in packet\n"); + } else { + pkt = (unsigned char *)readl(rx_curr_desc + EMAC_DESC_BUFFER); + len = readl(rx_curr_desc + EMAC_DESC_BUFF_OFF_LEN) & 0xffff; + dev_dbg(priv->dev, "| emac_recv got packet (length %i)\n", len); + dma_inv_range((ulong)pkt, + (ulong)readl(rx_curr_desc + EMAC_DESC_BUFFER) + len); + net_receive(pkt, len); + ret = len; + } + + /* Ack received packet descriptor */ + writel(BD_TO_HW(rx_curr_desc), priv->adap_emac + EMAC_RX0CP); + curr_desc = rx_curr_desc; + priv->emac_rx_active_head = HW_TO_BD(readl(rx_curr_desc + EMAC_DESC_NEXT)); + + if (status & EMAC_CPPI_EOQ_BIT) { + if (priv->emac_rx_active_head) { + writel(BD_TO_HW(priv->emac_rx_active_head), + priv->adap_emac + EMAC_RX0HDP); + } else { + priv->emac_rx_queue_active = 0; + dev_info(priv->dev, "INFO:emac_rcv_packet: RX Queue not active\n"); + } + } + + /* Recycle RX descriptor */ + writel(EMAC_MAX_ETHERNET_PKT_SIZE, rx_curr_desc + EMAC_DESC_BUFF_OFF_LEN); + writel(EMAC_CPPI_OWNERSHIP_BIT, rx_curr_desc + EMAC_DESC_PKT_FLAG_LEN); + writel(0, rx_curr_desc + EMAC_DESC_NEXT); + + if (priv->emac_rx_active_head == 0) { + dev_info(priv->dev, "INFO: emac_rcv_pkt: active queue head = 0\n"); + priv->emac_rx_active_head = curr_desc; + priv->emac_rx_active_tail = curr_desc; + if (priv->emac_rx_queue_active != 0) { + writel(BD_TO_HW(priv->emac_rx_active_head), priv->adap_emac + EMAC_RX0HDP); + dev_info(priv->dev, "INFO: emac_rcv_pkt: active queue head = 0, HDP fired\n"); + priv->emac_rx_queue_active = 1; + } + } else { + tail_desc = priv->emac_rx_active_tail; + priv->emac_rx_active_tail = curr_desc; + writel(BD_TO_HW(curr_desc), tail_desc + EMAC_DESC_NEXT); + status = readl(tail_desc + EMAC_DESC_PKT_FLAG_LEN); + if (status & EMAC_CPPI_EOQ_BIT) { + writel(BD_TO_HW(curr_desc), priv->adap_emac + EMAC_RX0HDP); + status &= ~EMAC_CPPI_EOQ_BIT; + writel(status, tail_desc + EMAC_DESC_PKT_FLAG_LEN); + } + } + +out: + dev_dbg(priv->dev, "- emac_recv\n"); + + return ret; +} + +static int davinci_emac_probe(struct device_d *dev) +{ + struct davinci_emac_platform_data *pdata; + struct davinci_emac_priv *priv; + uint64_t start; + uint32_t phy_mask; + + dev_dbg(dev, "+ emac_probe\n"); + + if (!dev->platform_data) { + dev_err(dev, "no platform_data\n"); + return -ENODEV; + } + pdata = dev->platform_data; + + priv = xzalloc(sizeof(*priv)); + dev->priv = priv; + + priv->dev = dev; + + priv->adap_emac = dev_request_mem_region(dev, 0); + priv->adap_ewrap = dev_request_mem_region(dev, 1); + priv->adap_mdio = dev_request_mem_region(dev, 2); + priv->emac_desc_base = dev_request_mem_region(dev, 3); + + /* EMAC descriptors */ + priv->emac_rx_desc = priv->emac_desc_base + EMAC_RX_DESC_BASE; + priv->emac_tx_desc = priv->emac_desc_base + EMAC_TX_DESC_BASE; + priv->emac_rx_active_head = NULL; + priv->emac_rx_active_tail = NULL; + priv->emac_rx_queue_active = 0; + + /* Receive packet buffers */ + priv->emac_rx_buffers = xmemalign(4096, EMAC_MAX_RX_BUFFERS * (EMAC_MAX_ETHERNET_PKT_SIZE + EMAC_PKT_ALIGN)); + + priv->edev.priv = priv; + priv->edev.init = davinci_emac_init; + priv->edev.open = davinci_emac_open; + priv->edev.halt = davinci_emac_halt; + priv->edev.send = davinci_emac_send; + priv->edev.recv = davinci_emac_recv; + priv->edev.get_ethaddr = davinci_emac_get_ethaddr; + priv->edev.set_ethaddr = davinci_emac_set_ethaddr; + priv->edev.parent = dev; + + davinci_eth_mdio_enable(priv); + + start = get_time_ns(); + while (1) { + phy_mask = readl(priv->adap_mdio + EMAC_MDIO_ALIVE); + if (phy_mask) { + dev_info(dev, "detected phy mask 0x%x\n", phy_mask); + phy_mask = ~phy_mask; + break; + } + if (is_timeout(start, 256 * MSECOND)) { + dev_err(dev, "no live phy, scanning all\n"); + phy_mask = 0; + break; + } + } + + if (pdata->interface_rmii) + priv->interface = PHY_INTERFACE_MODE_RMII; + else + priv->interface = PHY_INTERFACE_MODE_MII; + priv->phy_addr = pdata->phy_addr; + priv->phy_flags = pdata->force_link ? PHYLIB_FORCE_LINK : 0; + + priv->miibus.read = davinci_miibus_read; + priv->miibus.write = davinci_miibus_write; + priv->miibus.priv = priv; + priv->miibus.parent = dev; + priv->miibus.phy_mask = phy_mask; + + mdiobus_register(&priv->miibus); + + eth_register(&priv->edev); + + dev_dbg(dev, "- emac_probe\n"); + return 0; +} + +static void davinci_emac_remove(struct device_d *dev) +{ + struct davinci_emac_priv *priv = dev->priv; + + davinci_emac_halt(&priv->edev); +} + +static struct driver_d davinci_emac_driver = { + .name = "davinci_emac", + .probe = davinci_emac_probe, + .remove = davinci_emac_remove, +}; + +static int davinci_emac_register(void) +{ + register_driver(&davinci_emac_driver); + return 0; +} + +device_initcall(davinci_emac_register); diff --git a/drivers/net/davinci_emac.h b/drivers/net/davinci_emac.h new file mode 100644 index 0000000000..c5fa018fb3 --- /dev/null +++ b/drivers/net/davinci_emac.h @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2007 Sergey Kubushyn <ksi@koi8.net> + * + * Based on: + * + * ---------------------------------------------------------------------------- + * + * dm644x_emac.h + * + * TI DaVinci (DM644X) EMAC peripheral driver header for DV-EVM + * + * Copyright (C) 2005 Texas Instruments. + * + * ---------------------------------------------------------------------------- + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * ---------------------------------------------------------------------------- + */ + +#ifndef _DAVINCI_EMAC_H_ +#define _DAVINCI_EMAC_H_ + +/* PHY mask - set only those phy number bits where phy is/can be connected */ +#define EMAC_MDIO_PHY_NUM 1 +#define EMAC_MDIO_PHY_MASK (1 << EMAC_MDIO_PHY_NUM) + +/* Ethernet Min/Max packet size */ +#define EMAC_MIN_ETHERNET_PKT_SIZE 60 +#define EMAC_MAX_ETHERNET_PKT_SIZE 1518 +#define EMAC_PKT_ALIGN 18 /* 1518 + 18 = 1536 (packet aligned on 32 byte boundry) */ + +/* Number of RX packet buffers + * NOTE: Only 1 buffer supported as of now + */ +#define EMAC_MAX_RX_BUFFERS 10 + +/*********************************************** + ******** Internally used macros *************** + ***********************************************/ + +#define EMAC_CH_TX 1 +#define EMAC_CH_RX 0 + +/* Each descriptor occupies 4 words, lets start RX desc's at 0 and + * reserve space for 64 descriptors max + */ +#define EMAC_RX_DESC_BASE 0x0 +#define EMAC_TX_DESC_BASE 0x1000 + +/* EMAC Teardown value */ +#define EMAC_TEARDOWN_VALUE 0xfffffffc + +/* MII Status Register */ +#define MII_STATUS_REG 1 + +/* Number of statistics registers */ +#define EMAC_NUM_STATS 36 + +/* EMAC Descriptor Offsets */ +#define EMAC_DESC_NEXT 0x0 /* Pointer to next descriptor in chain */ +#define EMAC_DESC_BUFFER 0x4 /* Pointer to data buffer */ +#define EMAC_DESC_BUFF_OFF_LEN 0x8 /* Buffer Offset(MSW) and Length(LSW) */ +#define EMAC_DESC_PKT_FLAG_LEN 0xc /* Packet Flags(MSW) and Length(LSW) */ +#define EMAC_DESC_SIZE 0x10 + +/* CPPI bit positions */ +#define EMAC_CPPI_SOP_BIT (0x80000000) +#define EMAC_CPPI_EOP_BIT (0x40000000) +#define EMAC_CPPI_OWNERSHIP_BIT (0x20000000) +#define EMAC_CPPI_EOQ_BIT (0x10000000) +#define EMAC_CPPI_TEARDOWN_COMPLETE_BIT (0x08000000) +#define EMAC_CPPI_PASS_CRC_BIT (0x04000000) + +#define EMAC_CPPI_RX_ERROR_FRAME (0x03fc0000) + +#define EMAC_MACCONTROL_MIIEN_ENABLE (0x20) +#define EMAC_MACCONTROL_FULLDUPLEX_ENABLE (0x1) +#define EMAC_MACCONTROL_GIGABIT_ENABLE (1 << 7) +#define EMAC_MACCONTROL_GIGFORCE (1 << 17) +#define EMAC_MACCONTROL_RMIISPEED_100 (1 << 15) + +#define EMAC_MAC_ADDR_MATCH (1 << 19) +#define EMAC_MAC_ADDR_IS_VALID (1 << 20) + +#define EMAC_RXMBPENABLE_RXCAFEN_ENABLE (0x200000) +#define EMAC_RXMBPENABLE_RXBROADEN (0x2000) + +#define MDIO_CONTROL_IDLE (0x80000000) +#define MDIO_CONTROL_ENABLE (0x40000000) +#define MDIO_CONTROL_FAULT_ENABLE (0x40000) +#define MDIO_CONTROL_FAULT (0x80000) +#define MDIO_USERACCESS0_GO (0x80000000) +#define MDIO_USERACCESS0_WRITE_READ (0x0) +#define MDIO_USERACCESS0_WRITE_WRITE (0x40000000) +#define MDIO_USERACCESS0_ACK (0x20000000) + +/* Ethernet MAC Registers */ +#define EMAC_TXIDVER 0x000 +#define EMAC_TXCONTROL 0x004 +#define EMAC_TXTEARDOWN 0x008 +#define EMAC_RXIDVER 0x010 +#define EMAC_RXCONTROL 0x014 +#define EMAC_RXTEARDOWN 0x018 +#define EMAC_TXINTSTATRAW 0x080 +#define EMAC_TXINTSTATMASKED 0x084 +#define EMAC_TXINTMASKSET 0x088 +#define EMAC_TXINTMASKCLEAR 0x08c +#define EMAC_MACINVECTOR 0x090 +#define EMAC_MACEOIVECTOR 0x094 +#define EMAC_RXINTSTATRAW 0x0a0 +#define EMAC_RXINTSTATMASKED 0x0a4 +#define EMAC_RXINTMASKSET 0x0a8 +#define EMAC_RXINTMASKCLEAR 0x0ac +#define EMAC_MACINTSTATRAW 0x0b0 +#define EMAC_MACINTSTATMASKED 0x0b4 +#define EMAC_MACINTMASKSET 0x0b8 +#define EMAC_MACINTMASKCLEAR 0x0bc +#define EMAC_RXMBPENABLE 0x100 +#define EMAC_RXUNICASTSET 0x104 +#define EMAC_RXUNICASTCLEAR 0x108 +#define EMAC_RXMAXLEN 0x10c +#define EMAC_RXBUFFEROFFSET 0x110 +#define EMAC_RXFILTERLOWTHRESH 0x114 +#define EMAC_RX0FLOWTHRESH 0x120 +#define EMAC_RX1FLOWTHRESH 0x124 +#define EMAC_RX2FLOWTHRESH 0x128 +#define EMAC_RX3FLOWTHRESH 0x12c +#define EMAC_RX4FLOWTHRESH 0x130 +#define EMAC_RX5FLOWTHRESH 0x134 +#define EMAC_RX6FLOWTHRESH 0x138 +#define EMAC_RX7FLOWTHRESH 0x13c +#define EMAC_RX0FREEBUFFER 0x140 +#define EMAC_RX1FREEBUFFER 0x144 +#define EMAC_RX2FREEBUFFER 0x148 +#define EMAC_RX3FREEBUFFER 0x14c +#define EMAC_RX4FREEBUFFER 0x150 +#define EMAC_RX5FREEBUFFER 0x154 +#define EMAC_RX6FREEBUFFER 0x158 +#define EMAC_RX7FREEBUFFER 0x15c +#define EMAC_MACCONTROL 0x160 +#define EMAC_MACSTATUS 0x164 +#define EMAC_EMCONTROL 0x168 +#define EMAC_FIFOCONTROL 0x16c +#define EMAC_MACCONFIG 0x170 +#define EMAC_SOFTRESET 0x174 +#define EMAC_MACSRCADDRLO 0x1d0 +#define EMAC_MACSRCADDRHI 0x1d4 +#define EMAC_MACHASH1 0x1d8 +#define EMAC_MACHASH2 0x1dc +#define EMAC_BOFFTEST 0x1e0 +#define EMAC_TPACETEST 0x1e4 +#define EMAC_RXPAUSE 0x1e8 +#define EMAC_TXPAUSE 0x1ec +#define EMAC_RXGOODFRAMES 0x200 +#define EMAC_RXBCASTFRAMES 0x204 +#define EMAC_RXMCASTFRAMES 0x208 +#define EMAC_RXPAUSEFRAMES 0x20c +#define EMAC_RXCRCERRORS 0x210 +#define EMAC_RXALIGNCODEERRORS 0x214 +#define EMAC_RXOVERSIZED 0x218 +#define EMAC_RXJABBER 0x21c +#define EMAC_RXUNDERSIZED 0x220 +#define EMAC_RXFRAGMENTS 0x224 +#define EMAC_RXFILTERED 0x228 +#define EMAC_RXQOSFILTERED 0x22c +#define EMAC_RXOCTETS 0x230 +#define EMAC_TXGOODFRAMES 0x234 +#define EMAC_TXBCASTFRAMES 0x238 +#define EMAC_TXMCASTFRAMES 0x23c +#define EMAC_TXPAUSEFRAMES 0x240 +#define EMAC_TXDEFERRED 0x244 +#define EMAC_TXCOLLISION 0x248 +#define EMAC_TXSINGLECOLL 0x24c +#define EMAC_TXMULTICOLL 0x250 +#define EMAC_TXEXCESSIVECOLL 0x254 +#define EMAC_TXLATECOLL 0x258 +#define EMAC_TXUNDERRUN 0x25c +#define EMAC_TXCARRIERSENSE 0x260 +#define EMAC_TXOCTETS 0x264 +#define EMAC_FRAME64 0x268 +#define EMAC_FRAME65T127 0x26c +#define EMAC_FRAME128T255 0x270 +#define EMAC_FRAME256T511 0x274 +#define EMAC_FRAME512T1023 0x278 +#define EMAC_FRAME1024TUP 0x27c +#define EMAC_NETOCTETS 0x280 +#define EMAC_RXSOFOVERRUNS 0x284 +#define EMAC_RXMOFOVERRUNS 0x288 +#define EMAC_RXDMAOVERRUNS 0x28c +#define EMAC_MACADDRLO 0x500 +#define EMAC_MACADDRHI 0x504 +#define EMAC_MACINDEX 0x508 +#define EMAC_TX0HDP 0x600 +#define EMAC_TX1HDP 0x604 +#define EMAC_TX2HDP 0x608 +#define EMAC_TX3HDP 0x60c +#define EMAC_TX4HDP 0x610 +#define EMAC_TX5HDP 0x614 +#define EMAC_TX6HDP 0x618 +#define EMAC_TX7HDP 0x61c +#define EMAC_RX0HDP 0x620 +#define EMAC_RX1HDP 0x624 +#define EMAC_RX2HDP 0x628 +#define EMAC_RX3HDP 0x62c +#define EMAC_RX4HDP 0x630 +#define EMAC_RX5HDP 0x634 +#define EMAC_RX6HDP 0x638 +#define EMAC_RX7HDP 0x63c +#define EMAC_TX0CP 0x640 +#define EMAC_TX1CP 0x644 +#define EMAC_TX2CP 0x648 +#define EMAC_TX3CP 0x64c +#define EMAC_TX4CP 0x650 +#define EMAC_TX5CP 0x654 +#define EMAC_TX6CP 0x658 +#define EMAC_TX7CP 0x65c +#define EMAC_RX0CP 0x660 +#define EMAC_RX1CP 0x664 +#define EMAC_RX2CP 0x668 +#define EMAC_RX3CP 0x66c +#define EMAC_RX4CP 0x670 +#define EMAC_RX5CP 0x674 +#define EMAC_RX6CP 0x678 +#define EMAC_RX7CP 0x67c + +/* EMAC Wrapper Registers */ +#define EMAC_EWRAP_IDVER 0x00 +#define EMAC_EWRAP_SOFTRESET 0x04 +#define EMAC_EWRAP_INTCTRL 0x0c +#define EMAC_EWRAP_C0RXTHRESHEN 0x10 +#define EMAC_EWRAP_C0RXEN 0x14 +#define EMAC_EWRAP_C0TXEN 0x18 +#define EMAC_EWRAP_C0MISCEN 0x1c +#define EMAC_EWRAP_C1RXTHRESHEN 0x20 +#define EMAC_EWRAP_C1RXEN 0x24 +#define EMAC_EWRAP_C1TXEN 0x28 +#define EMAC_EWRAP_C1MISCEN 0x2c +#define EMAC_EWRAP_C2RXTHRESHEN 0x30 +#define EMAC_EWRAP_C2RXEN 0x34 +#define EMAC_EWRAP_C2TXEN 0x38 +#define EMAC_EWRAP_C2MISCEN 0x3c +#define EMAC_EWRAP_C0RXTHRESHSTAT 0x40 +#define EMAC_EWRAP_C0RXSTAT 0x44 +#define EMAC_EWRAP_C0TXSTAT 0x48 +#define EMAC_EWRAP_C0MISCSTAT 0x4c +#define EMAC_EWRAP_C1RXTHRESHSTAT 0x50 +#define EMAC_EWRAP_C1RXSTAT 0x54 +#define EMAC_EWRAP_C1TXSTAT 0x58 +#define EMAC_EWRAP_C1MISCSTAT 0x5c +#define EMAC_EWRAP_C2RXTHRESHSTAT 0x60 +#define EMAC_EWRAP_C2RXSTAT 0x64 +#define EMAC_EWRAP_C2TXSTAT 0x68 +#define EMAC_EWRAP_C2MISCSTAT 0x6c +#define EMAC_EWRAP_C0RXIMAX 0x70 +#define EMAC_EWRAP_C0TXIMAX 0x74 +#define EMAC_EWRAP_C1RXIMAX 0x78 +#define EMAC_EWRAP_C1TXIMAX 0x7c +#define EMAC_EWRAP_C2RXIMAX 0x80 +#define EMAC_EWRAP_C2TXIMAX 0x84 + +/* EMAC MDIO Registers */ +#define EMAC_MDIO_VERSION 0x00 +#define EMAC_MDIO_CONTROL 0x04 +#define EMAC_MDIO_ALIVE 0x08 +#define EMAC_MDIO_LINK 0x0c +#define EMAC_MDIO_LINKINTRAW 0x10 +#define EMAC_MDIO_LINKINTMASKED 0x14 +#define EMAC_MDIO_USERINTRAW 0x20 +#define EMAC_MDIO_USERINTMASKED 0x24 +#define EMAC_MDIO_USERINTMASKSET 0x28 +#define EMAC_MDIO_USERINTMASKCLEAR 0x2c +#define EMAC_MDIO_USERACCESS0 0x80 +#define EMAC_MDIO_USERPHYSEL0 0x84 +#define EMAC_MDIO_USERACCESS1 0x88 +#define EMAC_MDIO_USERPHYSEL1 0x8c + +#endif /* _DAVINCI_EMAC_H_ */ diff --git a/drivers/net/designware.c b/drivers/net/designware.c index 379b4e39a5..2a87d26a51 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -29,15 +29,15 @@ #include <init.h> #include <io.h> #include <net.h> -#include <miidev.h> #include <asm/mmu.h> #include <net/designware.h> +#include <linux/phy.h> #include "designware.h" struct dw_eth_dev { struct eth_device netdev; - struct mii_device miidev; + struct mii_bus miibus; void (*fix_mac_speed)(int speed); u8 macaddr[6]; @@ -52,6 +52,7 @@ struct dw_eth_dev { struct eth_mac_regs *mac_regs_p; struct eth_dma_regs *dma_regs_p; + int phy_addr; }; /* Speed specific definitions */ @@ -64,9 +65,9 @@ struct dw_eth_dev { #define FULL_DUPLEX 2 -static int dwc_ether_mii_read(struct mii_device *dev, int addr, int reg) +static int dwc_ether_mii_read(struct mii_bus *dev, int addr, int reg) { - struct dw_eth_dev *priv = dev->edev->priv; + struct dw_eth_dev *priv = dev->priv; struct eth_mac_regs *mac_p = priv->mac_regs_p; u64 start; u32 miiaddr; @@ -86,9 +87,9 @@ static int dwc_ether_mii_read(struct mii_device *dev, int addr, int reg) return readl(&mac_p->miidata) & 0xffff; } -static int dwc_ether_mii_write(struct mii_device *dev, int addr, int reg, int val) +static int dwc_ether_mii_write(struct mii_bus *dev, int addr, int reg, u16 val) { - struct dw_eth_dev *priv = dev->edev->priv; + struct dw_eth_dev *priv = dev->priv; struct eth_mac_regs *mac_p = priv->mac_regs_p; u64 start; u32 miiaddr; @@ -222,34 +223,37 @@ static int dwc_ether_init(struct eth_device *dev) return 0; } -static int dwc_ether_open(struct eth_device *dev) +static void dwc_update_linkspeed(struct eth_device *edev) { - struct dw_eth_dev *priv = dev->priv; - struct eth_mac_regs *mac_p = priv->mac_regs_p; - struct eth_dma_regs *dma_p = priv->dma_regs_p; + struct dw_eth_dev *priv = edev->priv; u32 conf; - int link, speed; - miidev_wait_aneg(&priv->miidev); - miidev_print_status(&priv->miidev); - link = miidev_get_status(&priv->miidev); - - if (priv->fix_mac_speed) { - speed = link & MIIDEV_STATUS_IS_1000MBIT ? 1000 : - (link & MIIDEV_STATUS_IS_100MBIT ? 100 : 10); - priv->fix_mac_speed(speed); - } + if (priv->fix_mac_speed) + priv->fix_mac_speed(edev->phydev->speed); conf = readl(&mac_p->conf); - if (link & MIIDEV_STATUS_IS_FULL_DUPLEX) + if (edev->phydev->duplex) conf |= FULLDPLXMODE; else conf &= ~FULLDPLXMODE; - if (link & MIIDEV_STATUS_IS_1000MBIT) + if (edev->phydev->speed == SPEED_1000) conf &= ~MII_PORTSELECT; else conf |= MII_PORTSELECT; writel(conf, &mac_p->conf); +} + +static int dwc_ether_open(struct eth_device *dev) +{ + struct dw_eth_dev *priv = dev->priv; + struct eth_mac_regs *mac_p = priv->mac_regs_p; + struct eth_dma_regs *dma_p = priv->dma_regs_p; + int ret; + + ret = phy_device_connect(dev, &priv->miibus, priv->phy_addr, + 0, PHY_INTERFACE_MODE_NA); + if (ret) + return ret; descs_init(dev); @@ -373,7 +377,7 @@ static int dwc_ether_probe(struct device_d *dev) { struct dw_eth_dev *priv; struct eth_device *edev; - struct mii_device *miidev; + struct mii_bus *miibus; void __iomem *base; struct dwc_ether_platform_data *pdata = dev->platform_data; @@ -397,7 +401,7 @@ static int dwc_ether_probe(struct device_d *dev) priv->fix_mac_speed = pdata->fix_mac_speed; edev = &priv->netdev; - miidev = &priv->miidev; + miibus = &priv->miibus; edev->priv = priv; edev->init = dwc_ether_init; @@ -408,12 +412,12 @@ static int dwc_ether_probe(struct device_d *dev) edev->get_ethaddr = dwc_ether_get_ethaddr; edev->set_ethaddr = dwc_ether_set_ethaddr; - miidev->address = pdata->phy_addr; - miidev->read = dwc_ether_mii_read; - miidev->write = dwc_ether_mii_write; - miidev->edev = edev; + priv->phy_addr = pdata->phy_addr; + miibus->read = dwc_ether_mii_read; + miibus->write = dwc_ether_mii_write; + miibus->priv = priv; - mii_register(miidev); + mdiobus_register(miibus); eth_register(edev); return 0; } diff --git a/drivers/net/dm9k.c b/drivers/net/dm9k.c index 0222d98f7b..ac18be26c0 100644 --- a/drivers/net/dm9k.c +++ b/drivers/net/dm9k.c @@ -26,12 +26,12 @@ #include <init.h> #include <common.h> #include <driver.h> -#include <miidev.h> #include <net.h> #include <io.h> #include <xfuncs.h> #include <dm9000.h> #include <errno.h> +#include <linux/phy.h> #define DM9K_ID 0x90000A46 #define CHIPR_DM9000A 0x19 @@ -160,7 +160,7 @@ struct dm9k { void __iomem *iobase; void __iomem *iodata; - struct mii_device miidev; + struct mii_bus miibus; int buswidth; int srom; uint8_t pckt[2048]; @@ -353,12 +353,11 @@ static void dm9k_wd(int b_width, void __iomem *port, const void *src, int length /* ----------------- end of data move functions -------------------------- */ -static int dm9k_phy_read(struct mii_device *mdev, int addr, int reg) +static int dm9k_phy_read(struct mii_bus *bus, int addr, int reg) { unsigned val; - struct eth_device *edev = mdev->edev; - struct device_d *dev = edev->parent; - struct dm9k *priv = edev->priv; + struct dm9k *priv = bus->priv; + struct device_d *dev = &bus->dev; /* Fill the phyxcer register into REG_0C */ dm9k_iow(priv, DM9K_EPAR, DM9K_PHY | reg); @@ -374,11 +373,10 @@ static int dm9k_phy_read(struct mii_device *mdev, int addr, int reg) return val; } -static int dm9k_phy_write(struct mii_device *mdev, int addr, int reg, int val) +static int dm9k_phy_write(struct mii_bus *bus, int addr, int reg, u16 val) { - struct eth_device *edev = mdev->edev; - struct device_d *dev = edev->parent; - struct dm9k *priv = edev->priv; + struct dm9k *priv = bus->priv; + struct device_d *dev = &bus->dev; /* Fill the phyxcer register into REG_0C */ dm9k_iow(priv, DM9K_EPAR, DM9K_PHY | reg); @@ -397,7 +395,7 @@ static int dm9k_phy_write(struct mii_device *mdev, int addr, int reg, int val) static int dm9k_check_id(struct dm9k *priv) { - struct device_d *dev = priv->miidev.parent; + struct device_d *dev = priv->miibus.parent; u32 id; char c; @@ -461,7 +459,7 @@ static void dm9k_enable(struct dm9k *priv) static void dm9k_reset(struct dm9k *priv) { - struct device_d *dev = priv->miidev.parent; + struct device_d *dev = priv->miibus.parent; dev_dbg(dev, "%s\n", __func__); dm9k_iow(priv, DM9K_NCR, NCR_RST); @@ -472,9 +470,8 @@ static int dm9k_eth_open(struct eth_device *edev) { struct dm9k *priv = (struct dm9k *)edev->priv; - miidev_wait_aneg(&priv->miidev); - miidev_print_status(&priv->miidev); - return 0; + return phy_device_connect(edev, &priv->miibus, 0, NULL, + 0, PHY_INTERFACE_MODE_NA); } static void dm9k_write_length(struct dm9k *priv, unsigned length) @@ -485,7 +482,7 @@ static void dm9k_write_length(struct dm9k *priv, unsigned length) static int dm9k_wait_for_trans_end(struct dm9k *priv) { - struct device_d *dev = priv->miidev.parent; + struct device_d *dev = priv->miibus.parent; static const uint64_t toffs = 1 * SECOND; uint8_t status; uint64_t start = get_time_ns(); @@ -511,7 +508,7 @@ static int dm9k_wait_for_trans_end(struct dm9k *priv) static int dm9k_eth_send(struct eth_device *edev, void *packet, int length) { struct dm9k *priv = (struct dm9k *)edev->priv; - struct device_d *dev = priv->miidev.parent; + struct device_d *dev = priv->miibus.parent; dev_dbg(dev, "%s: %d bytes\n", __func__, length); @@ -537,7 +534,7 @@ static int dm9k_eth_send(struct eth_device *edev, void *packet, int length) static int dm9k_check_for_rx_packet(struct dm9k *priv) { uint8_t status; - struct device_d *dev = priv->miidev.parent; + struct device_d *dev = priv->miibus.parent; status = dm9k_ior(priv, DM9K_ISR); if (!(status & ISR_PR)) @@ -550,7 +547,7 @@ static int dm9k_check_for_rx_packet(struct dm9k *priv) static int dm9k_validate_entry(struct dm9k *priv) { - struct device_d *dev = priv->miidev.parent; + struct device_d *dev = priv->miibus.parent; uint8_t p_stat; /* @@ -696,9 +693,6 @@ static int dm9k_set_ethaddr(struct eth_device *edev, unsigned char *adr) static int dm9k_init_dev(struct eth_device *edev) { - struct dm9k *priv = (struct dm9k *)edev->priv; - - miidev_restart_aneg(&priv->miidev); return 0; } @@ -740,12 +734,10 @@ static int dm9k_probe(struct device_d *dev) edev->get_ethaddr = dm9k_get_ethaddr; edev->parent = dev; - priv->miidev.read = dm9k_phy_read; - priv->miidev.write = dm9k_phy_write; - priv->miidev.address = 0; - priv->miidev.flags = 0; - priv->miidev.edev = edev; - priv->miidev.parent = dev; + priv->miibus.read = dm9k_phy_read; + priv->miibus.write = dm9k_phy_write; + priv->miibus.priv = priv; + priv->miibus.parent = dev; /* RESET device */ dm9k_reset(priv); @@ -778,7 +770,7 @@ static int dm9k_probe(struct device_d *dev) dm9k_enable(priv); - mii_register(&priv->miidev); + mdiobus_register(&priv->miibus); eth_register(edev); return 0; diff --git a/drivers/net/ep93xx.c b/drivers/net/ep93xx.c index c28fb79e45..a14c0ea8e3 100644 --- a/drivers/net/ep93xx.c +++ b/drivers/net/ep93xx.c @@ -34,17 +34,17 @@ #include <command.h> #include <init.h> #include <malloc.h> -#include <miidev.h> #include <io.h> #include <linux/types.h> #include <mach/ep93xx-regs.h> +#include <linux/phy.h> #include "ep93xx.h" #define EP93XX_MAX_PKT_SIZE 1536 -static int ep93xx_phy_read(struct mii_device *mdev, int phy_addr, int phy_reg); -static int ep93xx_phy_write(struct mii_device *mdev, int phy_addr, int phy_reg, - int value); +static int ep93xx_phy_read(struct mii_bus *bus, int phy_addr, int phy_reg); +static int ep93xx_phy_write(struct mii_bus *bus, int phy_addr, int phy_reg, + u16 value); static inline struct ep93xx_eth_priv *ep93xx_get_priv(struct eth_device *edev) { @@ -199,9 +199,15 @@ static int ep93xx_eth_open(struct eth_device *edev) struct ep93xx_eth_priv *priv = ep93xx_get_priv(edev); struct mac_regs *regs = ep93xx_get_regs(edev); int i; + int ret; pr_debug("+ep93xx_eth_open\n"); + ret = phy_device_connect(edev, &priv->miibus, 0, NULL, + 0, PHY_INTERFACE_MODE_NA); + if (ret) + return ret; + ep93xx_eth_reset(edev); /* Reset the descriptor queues' current and end address values */ @@ -498,11 +504,10 @@ static int ep93xx_eth_probe(struct device_d *dev) edev->set_ethaddr = ep93xx_eth_set_ethaddr; edev->parent = dev; - priv->miidev.read = ep93xx_phy_read; - priv->miidev.write = ep93xx_phy_write; - priv->miidev.address = 0; - priv->miidev.flags = 0; - priv->miidev.parent = dev; + priv->miibus.read = ep93xx_phy_read; + priv->miibus.write = ep93xx_phy_write; + priv->miibus.parent = dev; + priv->miibus.priv = edev; priv->tx_dq.base = calloc(NUMTXDESC, sizeof(struct tx_descriptor)); @@ -532,7 +537,7 @@ static int ep93xx_eth_probe(struct device_d *dev) goto eth_probe_failed_3; } - mii_register(&priv->miidev); + mdiobus_register(&priv->miibus); eth_register(edev); ret = 0; @@ -575,9 +580,9 @@ eth_probe_done: /** * Read a 16-bit value from an MII register. */ -static int ep93xx_phy_read(struct mii_device *mdev, int phy_addr, int phy_reg) +static int ep93xx_phy_read(struct mii_bus *bus, int phy_addr, int phy_reg) { - struct mac_regs *regs = ep93xx_get_regs(mdev->edev); + struct mac_regs *regs = ep93xx_get_regs(bus->priv); int value = -1; uint32_t self_ctl; @@ -618,10 +623,10 @@ static int ep93xx_phy_read(struct mii_device *mdev, int phy_addr, int phy_reg) /** * Write a 16-bit value to an MII register. */ -static int ep93xx_phy_write(struct mii_device *mdev, int phy_addr, - int phy_reg, int value) +static int ep93xx_phy_write(struct mii_bus *bus, int phy_addr, + int phy_reg, u16 value) { - struct mac_regs *regs = ep93xx_get_regs(mdev->edev); + struct mac_regs *regs = ep93xx_get_regs(bus->priv); uint32_t self_ctl; pr_debug("+ep93xx_phy_write\n"); diff --git a/drivers/net/ep93xx.h b/drivers/net/ep93xx.h index 47622d6a24..89451b8a5f 100644 --- a/drivers/net/ep93xx.h +++ b/drivers/net/ep93xx.h @@ -137,7 +137,7 @@ struct ep93xx_eth_priv { struct tx_descriptor_queue tx_dq; struct tx_status_queue tx_sq; - struct mii_device miidev; + struct mii_bus miibus; }; #endif diff --git a/drivers/net/fec_imx.c b/drivers/net/fec_imx.c index c80cdc96a5..6acf6bdee4 100644 --- a/drivers/net/fec_imx.c +++ b/drivers/net/fec_imx.c @@ -19,11 +19,11 @@ #include <net.h> #include <init.h> #include <driver.h> -#include <miidev.h> #include <fec.h> #include <io.h> #include <clock.h> #include <xfuncs.h> +#include <linux/phy.h> #include <asm/mmu.h> @@ -46,10 +46,9 @@ struct fec_frame { /* * MII-interface related functions */ -static int fec_miidev_read(struct mii_device *mdev, int phyAddr, int regAddr) +static int fec_miibus_read(struct mii_bus *bus, int phyAddr, int regAddr) { - struct eth_device *edev = mdev->edev; - struct fec_priv *fec = (struct fec_priv *)edev->priv; + struct fec_priv *fec = (struct fec_priv *)bus->priv; uint32_t reg; /* convenient holder for the PHY register */ uint32_t phy; /* convenient holder for the PHY */ @@ -89,11 +88,10 @@ static int fec_miidev_read(struct mii_device *mdev, int phyAddr, int regAddr) return readl(fec->regs + FEC_MII_DATA); } -static int fec_miidev_write(struct mii_device *mdev, int phyAddr, - int regAddr, int data) +static int fec_miibus_write(struct mii_bus *bus, int phyAddr, + int regAddr, u16 data) { - struct eth_device *edev = mdev->edev; - struct fec_priv *fec = (struct fec_priv *)edev->priv; + struct fec_priv *fec = (struct fec_priv *)bus->priv; uint32_t reg; /* convenient holder for the PHY register */ uint32_t phy; /* convenient holder for the PHY */ @@ -343,12 +341,20 @@ static int fec_init(struct eth_device *dev) /* size of each buffer */ writel(FEC_MAX_PKT_SIZE, fec->regs + FEC_EMRBR); - if (fec->xcv_type != SEVENWIRE) - miidev_restart_aneg(&fec->miidev); - return 0; } +static void fec_update_linkspeed(struct eth_device *edev) +{ + struct fec_priv *fec = (struct fec_priv *)edev->priv; + + if (edev->phydev->speed == SPEED_10) { + u32 rcntl = readl(fec->regs + FEC_R_CNTRL); + rcntl |= FEC_R_CNTRL_RMII_10T; + writel(rcntl, fec->regs + FEC_R_CNTRL); + } +} + /** * Start the FEC engine * @param[in] edev Our device to handle @@ -359,6 +365,17 @@ static int fec_open(struct eth_device *edev) int ret; u32 ecr; + if (fec->xcv_type != SEVENWIRE) { + ret = phy_device_connect(edev, &fec->miibus, fec->phy_addr, + fec_update_linkspeed, fec->phy_flags, + fec->interface); + if (ret) + return ret; + + if (fec->phy_init) + fec->phy_init(edev->phydev); + } + /* * Initialize RxBD/TxBD rings */ @@ -384,24 +401,6 @@ static int fec_open(struct eth_device *edev) */ fec_rx_task_enable(fec); - if (fec->xcv_type != SEVENWIRE) { - ret = miidev_wait_aneg(&fec->miidev); - if (ret) - return ret; - - ret = miidev_get_status(&fec->miidev); - if (ret < 0) - return ret; - - if (ret & MIIDEV_STATUS_IS_10MBIT) { - u32 rcntl = readl(fec->regs + FEC_R_CNTRL); - rcntl |= FEC_R_CNTRL_RMII_10T; - writel(rcntl, fec->regs + FEC_R_CNTRL); - } - - miidev_print_status(&fec->miidev); - } - return 0; } @@ -655,14 +654,30 @@ static int fec_probe(struct device_d *dev) fec->xcv_type = pdata->xcv_type; if (fec->xcv_type != SEVENWIRE) { - fec->miidev.read = fec_miidev_read; - fec->miidev.write = fec_miidev_write; - fec->miidev.address = pdata->phy_addr; - fec->miidev.flags = pdata->xcv_type == MII10 ? MIIDEV_FORCE_10 : 0; - fec->miidev.edev = edev; - fec->miidev.parent = dev; - - mii_register(&fec->miidev); + fec->phy_init = pdata->phy_init; + fec->miibus.read = fec_miibus_read; + fec->miibus.write = fec_miibus_write; + fec->phy_addr = pdata->phy_addr; + switch (pdata->xcv_type) { + case RMII: + fec->interface = PHY_INTERFACE_MODE_RMII; + break; + case RGMII: + fec->interface = PHY_INTERFACE_MODE_RGMII; + break; + case MII10: + fec->phy_flags = PHYLIB_FORCE_10; + case MII100: + fec->interface = PHY_INTERFACE_MODE_MII; + break; + case SEVENWIRE: + fec->interface = PHY_INTERFACE_MODE_NA; + break; + } + fec->miibus.priv = fec; + fec->miibus.parent = dev; + + mdiobus_register(&fec->miibus); } eth_register(edev); diff --git a/drivers/net/fec_imx.h b/drivers/net/fec_imx.h index b979f12a5b..d10385a531 100644 --- a/drivers/net/fec_imx.h +++ b/drivers/net/fec_imx.h @@ -133,7 +133,11 @@ struct fec_priv { int rbd_index; /* next receive BD to read */ struct buffer_descriptor __iomem *tbd_base; /* TBD ring */ int tbd_index; /* next transmit BD to write */ - struct mii_device miidev; + int phy_addr; + phy_interface_t interface; + u32 phy_flags; + struct mii_bus miibus; + void (*phy_init)(struct phy_device *dev); }; /** diff --git a/drivers/net/fec_mpc5200.c b/drivers/net/fec_mpc5200.c index c3f2099b0f..c42d86f513 100644 --- a/drivers/net/fec_mpc5200.c +++ b/drivers/net/fec_mpc5200.c @@ -10,13 +10,12 @@ #include <mach/mpc5xxx.h> #include <malloc.h> #include <net.h> +#include <fec.h> #include <init.h> -#include <miidev.h> #include <driver.h> #include <mach/sdma.h> -#include <mach/fec.h> #include <mach/clocks.h> -#include <miidev.h> +#include <linux/phy.h> #include "fec_mpc5200.h" #define CONFIG_PHY_ADDR 1 /* FIXME */ @@ -31,10 +30,9 @@ typedef struct { /* * MII-interface related functions */ -static int fec5xxx_miidev_read(struct mii_device *mdev, int phyAddr, int regAddr) +static int fec5xxx_miibus_read(struct mii_bus *bus, int phyAddr, int regAddr) { - struct eth_device *edev = mdev->edev; - mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)edev->priv; + mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)bus->priv; uint32_t reg; /* convenient holder for the PHY register */ uint32_t phy; /* convenient holder for the PHY */ @@ -70,11 +68,10 @@ static int fec5xxx_miidev_read(struct mii_device *mdev, int phyAddr, int regAddr return fec->eth->mii_data; } -static int fec5xxx_miidev_write(struct mii_device *mdev, int phyAddr, - int regAddr, int data) +static int fec5xxx_miibus_write(struct mii_bus *bus, int phyAddr, + int regAddr, u16 data) { - struct eth_device *edev = mdev->edev; - mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)edev->priv; + mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)bus->priv; uint32_t reg; /* convenient holder for the PHY register */ uint32_t phy; /* convenient holder for the PHY */ @@ -381,9 +378,6 @@ static int mpc5xxx_fec_init(struct eth_device *dev) debug("mpc5xxx_fec_init... Done \n"); - if (fec->xcv_type != SEVENWIRE) - miidev_restart_aneg(&fec->miidev); - return 0; } @@ -413,8 +407,8 @@ static int mpc5xxx_fec_open(struct eth_device *edev) SDMA_TASK_ENABLE(FEC_RECV_TASK_NO); if (fec->xcv_type != SEVENWIRE) { - miidev_wait_aneg(&fec->miidev); - miidev_print_status(&fec->miidev); + return phy_device_connect(edev, &fec->miibus, CONFIG_PHY_ADDR, + NULL, fec->phy_flags, fec->interface); } return 0; @@ -556,7 +550,7 @@ static int mpc5xxx_fec_send(struct eth_device *dev, void *eth_data, */ if (fec->xcv_type != SEVENWIRE) { uint16_t phyStatus; - phyStatus = fec5xxx_miidev_read(&fec->miidev, 0, 0x1); + phyStatus = fec5xxx_miibus_read(&fec->miibus, 0, 0x1); } /* @@ -657,7 +651,7 @@ static int mpc5xxx_fec_recv(struct eth_device *dev) int mpc5xxx_fec_probe(struct device_d *dev) { - struct mpc5xxx_fec_platform_data *pdata = (struct mpc5xxx_fec_platform_data *)dev->platform_data; + struct fec_platform_data *pdata = dev->platform_data; struct eth_device *edev; mpc5xxx_fec_priv *fec; @@ -683,14 +677,28 @@ int mpc5xxx_fec_probe(struct device_d *dev) loadtask(0, 2); if (fec->xcv_type != SEVENWIRE) { - fec->miidev.read = fec5xxx_miidev_read; - fec->miidev.write = fec5xxx_miidev_write; - fec->miidev.address = CONFIG_PHY_ADDR; - fec->miidev.flags = pdata->xcv_type == MII10 ? MIIDEV_FORCE_10 : 0; - fec->miidev.edev = edev; - fec->miidev.parent = dev; - - mii_register(&fec->miidev); + fec->miibus.read = fec5xxx_miibus_read; + fec->miibus.write = fec5xxx_miibus_write; + switch (pdata->xcv_type) { + case RMII: + fec->interface = PHY_INTERFACE_MODE_RMII; + break; + case RGMII: + fec->interface = PHY_INTERFACE_MODE_RGMII; + break; + case MII10: + fec->phy_flags = PHYLIB_FORCE_10; + case MII100: + fec->interface = PHY_INTERFACE_MODE_MII; + break; + case SEVENWIRE: + fec->interface = PHY_INTERFACE_MODE_NA; + break; + } + fec->miibus.priv = fec; + fec->miibus.parent = dev; + + mdiobus_register(&fec->miibus); } eth_register(edev); diff --git a/drivers/net/fec_mpc5200.h b/drivers/net/fec_mpc5200.h index f6da3e598f..20ac60731c 100644 --- a/drivers/net/fec_mpc5200.h +++ b/drivers/net/fec_mpc5200.h @@ -260,7 +260,9 @@ typedef struct { uint16_t usedTbdIndex; /* next transmit BD to clean */ uint16_t cleanTbdNum; /* the number of available transmit BDs */ - struct mii_device miidev; + phy_interface_t interface; + u32 phy_flags; + struct mii_bus miibus; } mpc5xxx_fec_priv; /* Ethernet parameter area */ diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 19544de261..20fd102c7e 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -17,10 +17,10 @@ #include <net.h> #include <init.h> #include <driver.h> -#include <miidev.h> #include <command.h> #include <errno.h> #include <asm/io.h> +#include <linux/phy.h> #include "gianfar.h" /* 2 seems to be the minimum number of TX descriptors to make it work. */ @@ -80,22 +80,16 @@ static void gfar_init_registers(void __iomem *regs) static void gfar_adjust_link(struct eth_device *edev) { struct gfar_private *priv = edev->priv; - struct device_d *mdev = priv->miidev.parent; void __iomem *regs = priv->regs; u32 ecntrl, maccfg2; uint32_t status; - status = miidev_get_status(&priv->miidev); + priv->link = edev->phydev->link; + priv->duplexity =edev->phydev->duplex; - priv->link = status & MIIDEV_STATUS_IS_UP; - if (status & MIIDEV_STATUS_IS_FULL_DUPLEX) - priv->duplexity = 1; - else - priv->duplexity = 0; - - if (status & MIIDEV_STATUS_IS_1000MBIT) + if (edev->phydev->speed == SPEED_1000) priv->speed = 1000; - else if (status & MIIDEV_STATUS_IS_100MBIT) + if (edev->phydev->speed == SPEED_100) priv->speed = 100; else priv->speed = 10; @@ -128,18 +122,18 @@ static void gfar_adjust_link(struct eth_device *edev) ecntrl |= GFAR_ECNTRL_R100; break; default: - dev_info(mdev, "Speed is unknown\n"); + dev_info(&edev->dev, "Speed is unknown\n"); break; } out_be32(regs + GFAR_ECNTRL_OFFSET, ecntrl); out_be32(regs + GFAR_MACCFG2_OFFSET, maccfg2); - dev_info(mdev, "Speed: %d, %s duplex\n", priv->speed, + dev_info(&edev->dev, "Speed: %d, %s duplex\n", priv->speed, (priv->duplexity) ? "full" : "half"); } else { - dev_info(mdev, "No link.\n"); + dev_info(&edev->dev, "No link.\n"); } } @@ -184,8 +178,6 @@ static int gfar_init(struct eth_device *edev) gfar_init_registers(regs); - miidev_restart_aneg(&priv->miidev); - return 0; } @@ -194,6 +186,12 @@ static int gfar_open(struct eth_device *edev) int ix; struct gfar_private *priv = edev->priv; void __iomem *regs = priv->regs; + int ret; + + ret = phy_device_connect(edev, &priv->miibus, priv->phyaddr, + gfar_adjust_link, 0, PHY_INTERFACE_MODE_NA); + if (ret) + return ret; /* Point to the buffer descriptors */ out_be32(regs + GFAR_TBASE0_OFFSET, (unsigned int)priv->txbd); @@ -215,9 +213,6 @@ static int gfar_open(struct eth_device *edev) } priv->txbd[TX_BUF_CNT - 1].status |= TXBD_WRAP; - miidev_wait_aneg(&priv->miidev); - gfar_adjust_link(edev); - /* Enable Transmit and Receive */ setbits_be32(regs + GFAR_MACCFG1_OFFSET, GFAR_MACCFG1_RX_EN | GFAR_MACCFG1_TX_EN); @@ -437,11 +432,10 @@ static int gfar_recv(struct eth_device *edev) } /* Read a MII PHY register. */ -static int gfar_miiphy_read(struct mii_device *mdev, int addr, int reg) +static int gfar_miiphy_read(struct mii_bus *bus, int addr, int reg) { - struct eth_device *edev = mdev->edev; - struct device_d *dev = edev->parent; - struct gfar_private *priv = edev->priv; + struct device_d *dev = bus->parent; + struct gfar_private *priv = bus->priv; int ret; ret = gfar_local_mdio_read(priv->phyregs, addr, reg); @@ -452,12 +446,11 @@ static int gfar_miiphy_read(struct mii_device *mdev, int addr, int reg) } /* Write a MII PHY register. */ -static int gfar_miiphy_write(struct mii_device *mdev, int addr, int reg, - int value) +static int gfar_miiphy_write(struct mii_bus *bus, int addr, int reg, + u16 value) { - struct eth_device *edev = mdev->edev; - struct device_d *dev = edev->parent; - struct gfar_private *priv = edev->priv; + struct device_d *dev = bus->parent; + struct gfar_private *priv = bus->priv; unsigned short val = value; int ret; @@ -520,16 +513,14 @@ static int gfar_probe(struct device_d *dev) udelay(2); clrbits_be32(priv->regs + GFAR_MACCFG1_OFFSET, GFAR_MACCFG1_SOFT_RESET); - priv->miidev.read = gfar_miiphy_read; - priv->miidev.write = gfar_miiphy_write; - priv->miidev.address = priv->phyaddr; - priv->miidev.flags = 0; - priv->miidev.edev = edev; - priv->miidev.parent = dev; + priv->miibus.read = gfar_miiphy_read; + priv->miibus.write = gfar_miiphy_write; + priv->miibus.priv = priv; + priv->miibus.parent = dev; gfar_init_phy(edev); - mii_register(&priv->miidev); + mdiobus_register(&priv->miibus); return eth_register(edev); } diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h index a4ad99e3b7..b52cc5ab3b 100644 --- a/drivers/net/gianfar.h +++ b/drivers/net/gianfar.h @@ -269,7 +269,7 @@ struct gfar_private { void __iomem *phyregs; void __iomem *phyregs_sgmii; struct phy_info *phyinfo; - struct mii_device miidev; + struct mii_bus miibus; volatile struct txbd8 *txbd; volatile struct rxbd8 *rxbd; uint txidx; diff --git a/drivers/net/ks8851_mll.c b/drivers/net/ks8851_mll.c index ba4b8c3b14..c48e729dce 100644 --- a/drivers/net/ks8851_mll.c +++ b/drivers/net/ks8851_mll.c @@ -22,13 +22,13 @@ #include <command.h> #include <net.h> -#include <miidev.h> #include <malloc.h> #include <init.h> #include <xfuncs.h> #include <errno.h> #include <clock.h> #include <io.h> +#include <linux/phy.h> #define MAX_RECV_FRAMES 32 #define MAX_BUF_SIZE 2048 @@ -368,7 +368,7 @@ struct ks_net { struct eth_device edev; - struct mii_device miidev; + struct mii_bus miibus; void __iomem *hw_addr; void __iomem *hw_addr_cmd; struct platform_device *pdev; @@ -532,10 +532,9 @@ static int ks_phy_reg(int reg) * caller. The mii-tool that the driver was tested with takes any -ve error * as real PHY capabilities, thus displaying incorrect data to the user. */ -static int ks_phy_read(struct mii_device *mdev, int addr, int reg) +static int ks_phy_read(struct mii_bus *bus, int addr, int reg) { - struct eth_device *edev = mdev->edev; - struct ks_net *priv = (struct ks_net *)edev->priv; + struct ks_net *priv = (struct ks_net *)bus->priv; int ksreg; int result; @@ -548,10 +547,9 @@ static int ks_phy_read(struct mii_device *mdev, int addr, int reg) return result; } -static int ks_phy_write(struct mii_device *mdev, int addr, int reg, int val) +static int ks_phy_write(struct mii_bus *bus, int addr, int reg, u16 val) { - struct eth_device *edev = mdev->edev; - struct ks_net *priv = (struct ks_net *)edev->priv; + struct ks_net *priv = (struct ks_net *)bus->priv; int ksreg; ksreg = ks_phy_reg(reg); @@ -779,11 +777,14 @@ static int ks8851_eth_open(struct eth_device *edev) { struct ks_net *priv = (struct ks_net *)edev->priv; struct device_d *dev = &edev->dev; + int ret; ks_enable_qmu(priv); - miidev_wait_aneg(&priv->miidev); - miidev_print_status(&priv->miidev); + ret = phy_device_connect(edev, &priv->miibus, 1, NULL, + 0, PHY_INTERFACE_MODE_NA); + if (ret) + return ret; dev_dbg(dev, "eth_open\n"); @@ -792,9 +793,6 @@ static int ks8851_eth_open(struct eth_device *edev) static int ks8851_init_dev(struct eth_device *edev) { - struct ks_net *priv = (struct ks_net *)edev->priv; - - miidev_restart_aneg(&priv->miidev); return 0; } @@ -838,12 +836,10 @@ static int ks8851_probe(struct device_d *dev) edev->parent = dev; /* setup mii state */ - ks->miidev.read = ks_phy_read; - ks->miidev.write = ks_phy_write; - ks->miidev.address = 1; - ks->miidev.flags = 0; - ks->miidev.edev = edev; - ks->miidev.parent = dev; + ks->miibus.read = ks_phy_read; + ks->miibus.write = ks_phy_write; + ks->miibus.priv = ks; + ks->miibus.parent = dev; /* simple check for a valid chip being connected to the bus */ @@ -864,7 +860,7 @@ static int ks8851_probe(struct device_d *dev) ks_soft_reset(ks, GRR_GSR); ks_setup(ks); - mii_register(&ks->miidev); + mdiobus_register(&ks->miibus); eth_register(edev); dev_dbg(dev, "%s MARL 0x%04x MARM 0x%04x MARH 0x%04x\n", __func__, ks_rdreg16(ks, KS_MARL), ks_rdreg16(ks, KS_MARM), diff --git a/drivers/net/ksz8864rmn.c b/drivers/net/ksz8864rmn.c new file mode 100644 index 0000000000..5845960f6c --- /dev/null +++ b/drivers/net/ksz8864rmn.c @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2012 Jan Luebbe, Pengutronix + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#include <common.h> +#include <init.h> +#include <driver.h> +#include <spi/spi.h> +#include <errno.h> + +#define REG_ID0 0x00 +#define REG_ID1 0x01 + +#define REG_GC00 0x02 +#define REG_GC01 0x03 +#define REG_GC02 0x04 +#define REG_GC03 0x05 +#define REG_GC04 0x06 +#define REG_GC05 0x07 +#define REG_GC06 0x08 +#define REG_GC07 0x09 +#define REG_GC08 0x0a +#define REG_GC09 0x0b +#define REG_GC10 0x0c +#define REG_GC11 0x0d + +#define REG_PSTAT1(p) (0x10 * p + 0xe) +#define REG_PSTAT2(p) (0x10 * p + 0xf) + +#define CMD_WRITE 0x02 +#define CMD_READ 0x03 + +struct micrel_switch_priv { + struct cdev cdev; + struct spi_device *spi; +}; + +static int micrel_switch_read_reg(struct spi_device *spi, uint8_t reg) +{ + uint8_t tx[2]; + uint8_t rx[1]; + int ret; + + tx[0] = CMD_READ; + tx[1] = reg; + + ret = spi_write_then_read(spi, tx, 2, rx, 1); + if (ret < 0) + return ret; + + return rx[0]; +} + +static void micrel_switch_write_reg(struct spi_device *spi, uint8_t reg, uint8_t val) +{ + uint8_t tx[3]; + + tx[0] = CMD_WRITE; + tx[1] = reg; + tx[2] = val; + + spi_write_then_read(spi, tx, 3, NULL, 0); +} + +static int micrel_switch_enable_set(struct device_d *dev, struct param_d *param, + const char *val) +{ + struct spi_device *spi = (struct spi_device *)dev->type_data; + int enable; + char *new; + + if (!val) + return dev_param_set_generic(dev, param, NULL); + + enable = simple_strtoul(val, NULL, 0); + + if (enable) { + micrel_switch_write_reg(spi, REG_ID1, 1); + new = "1"; + } else { + micrel_switch_write_reg(spi, REG_ID1, 0); + new = "0"; + } + + dev_param_set_generic(dev, param, new); + + return 0; +} + +static ssize_t micel_switch_read(struct cdev *cdev, void *_buf, size_t count, loff_t offset, ulong flags) +{ + int i, ret; + uint8_t *buf = _buf; + struct micrel_switch_priv *priv = cdev->priv; + + for (i = 0; i < count; i++) { + ret = micrel_switch_read_reg(priv->spi, offset); + if (ret < 0) + return ret; + *buf = ret; + buf++; + offset++; + } + + return count; +} + +static ssize_t micel_switch_write(struct cdev *cdev, const void *_buf, size_t count, loff_t offset, ulong flags) +{ + int i; + const uint8_t *buf = _buf; + struct micrel_switch_priv *priv = cdev->priv; + + for (i = 0; i < count; i++) { + micrel_switch_write_reg(priv->spi, offset, *buf); + buf++; + offset++; + } + + return count; +} + +static struct file_operations micrel_switch_ops = { + .read = micel_switch_read, + .write = micel_switch_write, + .lseek = dev_lseek_default, +}; + +static int micrel_switch_probe(struct device_d *dev) +{ + struct micrel_switch_priv *priv; + int ret = 0; + + priv = xzalloc(sizeof(*priv)); + + dev->priv = priv; + + priv->spi = (struct spi_device *)dev->type_data; + priv->spi->mode = SPI_MODE_0; + priv->spi->bits_per_word = 8; + + ret = micrel_switch_read_reg(priv->spi, REG_ID0); + if (ret < 0) { + dev_err(&priv->spi->dev, "failed to read device id\n"); + return ret; + } + if (ret != 0x95) { + dev_err(&priv->spi->dev, "unknown device id: %02x\n", ret); + return -ENODEV; + } + + priv->cdev.name = asprintf("switch%d", dev->id); + priv->cdev.size = 256; + priv->cdev.ops = &micrel_switch_ops; + priv->cdev.priv = priv; + priv->cdev.dev = dev; + devfs_create(&priv->cdev); + + dev_add_param(dev, "enable", micrel_switch_enable_set, NULL, 0); + dev_set_param(dev, "enable", "1"); + + return 0; +} + +static struct driver_d micrel_switch_driver = { + .name = "ksz8864rmn", + .probe = micrel_switch_probe, +}; + +static int micrel_switch_init(void) +{ + spi_register_driver(&micrel_switch_driver); + return 0; +} +device_initcall(micrel_switch_init); diff --git a/drivers/net/macb.c b/drivers/net/macb.c index 53a3ed2ceb..64970ba09f 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -41,13 +41,13 @@ #include <malloc.h> #include <xfuncs.h> #include <init.h> -#include <miidev.h> #include <errno.h> #include <io.h> #include <mach/board.h> #include <linux/clk.h> #include <linux/err.h> #include <asm/mmu.h> +#include <linux/phy.h> #include "macb.h" @@ -94,12 +94,16 @@ struct macb_device { struct macb_dma_desc *rx_ring; struct macb_dma_desc *tx_ring; + int phy_addr; + const struct device *dev; struct eth_device netdev; - struct mii_device miidev; + phy_interface_t interface; + + struct mii_bus miibus; - unsigned int flags; + unsigned int phy_flags; }; static int macb_send(struct eth_device *edev, void *packet, @@ -211,26 +215,32 @@ static int macb_recv(struct eth_device *edev) return 0; } -static int macb_open(struct eth_device *edev) +static void macb_adjust_link(struct eth_device *edev) { struct macb_device *macb = edev->priv; - int duplex = 1, speed = 1; - u32 ncfgr; + u32 reg; - debug("%s\n", __func__); + reg = readl(macb->regs + MACB_NCFGR); + reg &= ~(MACB_BIT(SPD) | MACB_BIT(FD)); - miidev_wait_aneg(&macb->miidev); - miidev_print_status(&macb->miidev); + if (edev->phydev->duplex) + reg |= MACB_BIT(FD); + if (edev->phydev->speed == SPEED_100) + reg |= MACB_BIT(SPD); - ncfgr = readl(macb->regs + MACB_NCFGR); - ncfgr &= ~(MACB_BIT(SPD) | MACB_BIT(FD)); - if (speed) - ncfgr |= MACB_BIT(SPD); - if (duplex) - ncfgr |= MACB_BIT(FD); - writel(ncfgr, macb->regs + MACB_NCFGR); + writel(reg, macb->regs + MACB_NCFGR); +} - return 0; +static int macb_open(struct eth_device *edev) +{ + struct macb_device *macb = edev->priv; + + debug("%s\n", __func__); + + /* Obtain the PHY's address/id */ + return phy_device_connect(edev, &macb->miibus, macb->phy_addr, + macb_adjust_link, macb->phy_flags, + macb->interface); } static int macb_init(struct eth_device *edev) @@ -264,7 +274,7 @@ static int macb_init(struct eth_device *edev) writel((ulong)macb->rx_ring, macb->regs + MACB_RBQP); writel((ulong)macb->tx_ring, macb->regs + MACB_TBQP); - if (macb->flags & AT91SAM_ETHER_RMII) + if (macb->interface == PHY_INTERFACE_MODE_RMII) val |= MACB_BIT(RMII); else val &= ~MACB_BIT(RMII); @@ -298,10 +308,9 @@ static void macb_halt(struct eth_device *edev) writel(MACB_BIT(CLRSTAT), macb->regs + MACB_NCR); } -static int macb_phy_read(struct mii_device *mdev, int addr, int reg) +static int macb_phy_read(struct mii_bus *bus, int addr, int reg) { - struct eth_device *edev = mdev->edev; - struct macb_device *macb = edev->priv; + struct macb_device *macb = bus->priv; unsigned long netctl; unsigned long netstat; @@ -341,10 +350,9 @@ static int macb_phy_read(struct mii_device *mdev, int addr, int reg) return value; } -static int macb_phy_write(struct mii_device *mdev, int addr, int reg, int value) +static int macb_phy_write(struct mii_bus *bus, int addr, int reg, u16 value) { - struct eth_device *edev = mdev->edev; - struct macb_device *macb = edev->priv; + struct macb_device *macb = bus->priv; unsigned long netctl; unsigned long netstat; unsigned long frame; @@ -425,14 +433,19 @@ static int macb_probe(struct device_d *dev) edev->set_ethaddr = macb_set_ethaddr; edev->parent = dev; - macb->miidev.read = macb_phy_read; - macb->miidev.write = macb_phy_write; - macb->miidev.address = pdata->phy_addr; - macb->miidev.flags = pdata->flags & AT91SAM_ETHER_FORCE_LINK ? - MIIDEV_FORCE_LINK : 0; - macb->miidev.edev = edev; - macb->miidev.parent = dev; - macb->flags = pdata->flags; + macb->miibus.read = macb_phy_read; + macb->miibus.write = macb_phy_write; + macb->phy_addr = pdata->phy_addr; + macb->miibus.priv = macb; + macb->miibus.parent = dev; + + if (pdata->flags & AT91SAM_ETHER_RMII) + macb->interface = PHY_INTERFACE_MODE_RMII; + else + macb->interface = PHY_INTERFACE_MODE_MII; + + macb->phy_flags = pdata->flags & AT91SAM_ETHER_FORCE_LINK ? + PHYLIB_FORCE_LINK : 0; macb->rx_buffer = dma_alloc_coherent(CFG_MACB_RX_BUFFER_SIZE); macb->rx_ring = dma_alloc_coherent(CFG_MACB_RX_RING_SIZE * sizeof(struct macb_dma_desc)); @@ -467,7 +480,7 @@ static int macb_probe(struct device_d *dev) writel(ncfgr, macb->regs + MACB_NCFGR); - mii_register(&macb->miidev); + mdiobus_register(&macb->miibus); eth_register(edev); return 0; diff --git a/drivers/net/miidev.c b/drivers/net/miidev.c deleted file mode 100644 index 5d73ff5597..0000000000 --- a/drivers/net/miidev.c +++ /dev/null @@ -1,313 +0,0 @@ -/* - * miidev.c - generic phy abstraction - * - * Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * - * 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 version 2 - * as published by the Free Software Foundation. - * - * 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 <driver.h> -#include <init.h> -#include <miidev.h> -#include <clock.h> -#include <net.h> -#include <malloc.h> - -static LIST_HEAD(miidev_list); - -int miidev_restart_aneg(struct mii_device *mdev) -{ - int status, timeout; - uint64_t start; - - status = mii_write(mdev, mdev->address, MII_BMCR, BMCR_RESET); - if (status) - return status; - - start = get_time_ns(); - do { - status = mii_read(mdev, mdev->address, MII_BMCR); - if (status < 0) - return status; - - if (is_timeout(start, SECOND)) - return -ETIMEDOUT; - - } while (status & BMCR_RESET); - - if (mdev->flags & MIIDEV_FORCE_LINK) - return 0; - - if (mdev->flags & MIIDEV_FORCE_10) { - printf("Forcing 10 Mbps ethernet link... "); - - status = mii_read(mdev, mdev->address, MII_BMSR); - if (status < 0) - return status; - - status = mii_write(mdev, mdev->address, MII_BMCR, BMCR_FULLDPLX | BMCR_CTST); - if (status) - return status; - - timeout = 20; - do { /* wait for link status to go down */ - udelay(10000); - if ((timeout--) == 0) { - debug("hmmm, should not have waited..."); - break; - } - status = mii_read(mdev, mdev->address, MII_BMSR); - if (status < 0) - return status; - } while (status & BMSR_LSTATUS); - - } else { /* MII100 */ - /* - * Set the auto-negotiation advertisement register bits - */ - status = mii_read(mdev, mdev->address, MII_ADVERTISE); - if (status < 0) - return status; - - status |= ADVERTISE_ALL; - - status = mii_write(mdev, mdev->address, MII_ADVERTISE, status); - if (status) - return status; - - status = mii_write(mdev, mdev->address, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART); - if (status) - return status; - } - - return 0; -} - -int miidev_wait_aneg(struct mii_device *mdev) -{ - int status; - uint64_t start = get_time_ns(); - - if (mdev->flags & MIIDEV_FORCE_LINK) - return 0; - - do { - status = mii_read(mdev, mdev->address, MII_BMSR); - if (status < 0) - return status; - - if (is_timeout(start, 5 * SECOND)) { - printf("%s: Autonegotiation timeout\n", mdev->cdev.name); - return -ETIMEDOUT; - } - - } while (!(status & BMSR_ANEGCOMPLETE)); - - return 0; -} - -int miidev_get_status(struct mii_device *mdev) -{ - int ret, status, adv, lpa; - - ret = mii_read(mdev, mdev->address, MII_BMSR); - if (ret < 0) - goto err_out; - - status = ret & BMSR_LSTATUS ? MIIDEV_STATUS_IS_UP : 0; - - if (ret & BMSR_ESTATEN) { - ret = mii_read(mdev, mdev->address, MII_ESTATUS); - if (ret < 0) - goto err_out; - if (ret & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF)) - mdev->capabilities = MIIDEV_CAPABLE_1000M; - } - - ret = mii_read(mdev, mdev->address, MII_BMCR); - if (ret < 0) - goto err_out; - - if (ret & BMCR_ANENABLE) { - if (mdev->capabilities & MIIDEV_CAPABLE_1000M) { - lpa = mii_read(mdev, mdev->address, MII_STAT1000); - if (lpa < 0) - goto err_out; - adv = mii_read(mdev, mdev->address, MII_CTRL1000); - if (adv < 0) - goto err_out; - lpa &= adv << 2; - if (lpa & (LPA_1000FULL | LPA_1000HALF)) { - if (lpa & LPA_1000FULL) - status |= MIIDEV_STATUS_IS_FULL_DUPLEX; - status |= MIIDEV_STATUS_IS_1000MBIT; - return status; - } - } - lpa = mii_read(mdev, mdev->address, MII_LPA); - if (lpa < 0) - goto err_out; - adv = mii_read(mdev, mdev->address, MII_ADVERTISE); - if (adv < 0) - goto err_out; - lpa &= adv; - status |= lpa & LPA_DUPLEX ? MIIDEV_STATUS_IS_FULL_DUPLEX : 0; - status |= lpa & LPA_100 ? MIIDEV_STATUS_IS_100MBIT : - MIIDEV_STATUS_IS_10MBIT; - } else { - status |= ret & BMCR_FULLDPLX ? MIIDEV_STATUS_IS_FULL_DUPLEX : 0; - status |= ret & BMCR_SPEED100 ? MIIDEV_STATUS_IS_100MBIT : - MIIDEV_STATUS_IS_10MBIT; - } - - return status; -err_out: - printf("%s: failed to read (%d)\n", mdev->cdev.name, ret); - return ret; -} - -int miidev_print_status(struct mii_device *mdev) -{ - char *duplex; - int speed, status; - - if (mdev->flags & MIIDEV_FORCE_LINK) { - printf("Forcing link present...\n"); - return 0; - } - - status = miidev_get_status(mdev); - if (status < 0) - return status; - - duplex = status & MIIDEV_STATUS_IS_FULL_DUPLEX ? "Full" : "Half"; - speed = status & MIIDEV_STATUS_IS_1000MBIT ? 1000 : - (status & MIIDEV_STATUS_IS_100MBIT ? 100 : 10); - - printf("%s: Link is %s", mdev->cdev.name, - status & MIIDEV_STATUS_IS_UP ? "up" : "down"); - printf(" - %d/%s\n", speed, duplex); - - return 0; -} - -static ssize_t miidev_read(struct cdev *cdev, void *_buf, size_t count, loff_t offset, ulong flags) -{ - int i = count; - uint16_t *buf = _buf; - struct mii_device *mdev = cdev->priv; - - while (i > 0) { - *buf = mii_read(mdev, mdev->address, offset / 2); - buf++; - i -= 2; - offset += 2; - } - - return count; -} - -static ssize_t miidev_write(struct cdev *cdev, const void *_buf, size_t count, loff_t offset, ulong flags) -{ - int i = count; - const uint16_t *buf = _buf; - struct mii_device *mdev = cdev->priv; - - while (i > 0) { - mii_write(mdev, mdev->address, offset / 2, *buf); - buf++; - i -= 2; - offset += 2; - } - - return count; -} - -static struct file_operations miidev_ops = { - .read = miidev_read, - .write = miidev_write, - .lseek = dev_lseek_default, -}; - -static int miidev_probe(struct device_d *dev) -{ - struct mii_device *mdev = dev->priv; - - mdev->capabilities = 0; - mdev->cdev.name = asprintf("phy%d", dev->id); - mdev->cdev.size = 64; - mdev->cdev.ops = &miidev_ops; - mdev->cdev.priv = mdev; - mdev->cdev.dev = dev; - devfs_create(&mdev->cdev); - list_add_tail(&mdev->list, &miidev_list); - return 0; -} - -static void miidev_remove(struct device_d *dev) -{ - struct mii_device *mdev = dev->priv; - - list_del(&mdev->list); - - free(mdev->cdev.name); - devfs_remove(&mdev->cdev); -} - -struct mii_device *mii_open(const char *name) -{ - struct mii_device *mdev; - - list_for_each_entry(mdev, &miidev_list, list) { - if (!strcmp(name, mdev->cdev.name)) - return mdev; - } - return NULL; -} - -void mii_close(struct mii_device *mdev) -{ -} - -static struct driver_d miidev_drv = { - .name = "miidev", - .probe = miidev_probe, - .remove = miidev_remove, -}; - -int mii_register(struct mii_device *mdev) -{ - mdev->dev.priv = mdev; - mdev->dev.id = DEVICE_ID_DYNAMIC; - strcpy(mdev->dev.name, "miidev"); - if (mdev->parent) - dev_add_child(mdev->parent, &mdev->dev); - - return register_device(&mdev->dev); -} - -void mii_unregister(struct mii_device *mdev) -{ - unregister_device(&mdev->dev); -} - -static int miidev_init(void) -{ - register_driver(&miidev_drv); - return 0; -} - -device_initcall(miidev_init); - diff --git a/drivers/net/netx_eth.c b/drivers/net/netx_eth.c index 2d92a2e4dc..a4764258c1 100644 --- a/drivers/net/netx_eth.c +++ b/drivers/net/netx_eth.c @@ -2,13 +2,13 @@ #include <command.h> #include <net.h> #include <io.h> -#include <miidev.h> #include <mach/netx-xc.h> #include <mach/netx-eth.h> #include <mach/netx-regs.h> #include <xfuncs.h> #include <init.h> #include <driver.h> +#include <linux/phy.h> #define ETH_MAC_LOCAL_CONFIG 0x1560 #define ETH_MAC_4321 0x1564 @@ -47,7 +47,7 @@ #define CON_FIFO_PORT_LO(xcno) (6 + ((xcno) << 3)) /* Index of the FIFO where sent Data packages are confirmed */ struct netx_eth_priv { - struct mii_device miidev; + struct mii_bus miibus; int xcno; }; @@ -118,7 +118,7 @@ static int netx_eth_rx (struct eth_device *edev) return 0; } -static int netx_miidev_read(struct mii_device *mdev, int phy_addr, int reg) +static int netx_miibus_read(struct mii_bus *bus, int phy_addr, int reg) { int value; @@ -135,8 +135,8 @@ static int netx_miidev_read(struct mii_device *mdev, int phy_addr, int reg) return value; } -static int netx_miidev_write(struct mii_device *mdev, int phy_addr, - int reg, int val) +static int netx_miibus_write(struct mii_bus *bus, int phy_addr, + int reg, u16 val) { debug("%s: addr: 0x%02x reg: 0x%02x val: 0x%04x\n",__func__, phy_addr, reg, val); @@ -189,13 +189,15 @@ static int netx_eth_init_dev(struct eth_device *edev) for (i = 2; i <= 18; i++) PFIFO_REG( PFIFO_BASE(EMPTY_PTR_FIFO(xcno)) ) = FIFO_PTR_FRAMENO(i) | FIFO_PTR_SEGMENT(xcno); - miidev_restart_aneg(&priv->miidev); return 0; } static int netx_eth_open(struct eth_device *edev) { - return 0; + struct netx_eth_priv *priv = (struct netx_eth_priv *)edev->priv; + + return phy_device_connect(edev, &priv->miibus, 0, NULL, + 0, PHY_INTERFACE_MODE_NA); } static void netx_eth_halt (struct eth_device *edev) @@ -259,14 +261,12 @@ static int netx_eth_probe(struct device_d *dev) edev->set_ethaddr = netx_eth_set_ethaddr; edev->parent = dev; - priv->miidev.read = netx_miidev_read; - priv->miidev.write = netx_miidev_write; - priv->miidev.address = 0; - priv->miidev.flags = 0; - priv->miidev.parent = dev; + priv->miibus.read = netx_miibus_read; + priv->miibus.write = netx_miibus_write; + priv->miibus.parent = dev; netx_eth_init_phy(); - mii_register(&priv->miidev); + mdiobus_register(&priv->miibus); eth_register(edev); return 0; diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig new file mode 100644 index 0000000000..b66261ae99 --- /dev/null +++ b/drivers/net/phy/Kconfig @@ -0,0 +1,17 @@ +# +# PHY Layer Configuration +# + +menu "phylib " + +if PHYLIB + +comment "MII PHY device drivers" + +config GENERIC_PHY + bool "Drivers for the Generic PHYs" + default y + +endif + +endmenu diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile new file mode 100644 index 0000000000..82e90d426a --- /dev/null +++ b/drivers/net/phy/Makefile @@ -0,0 +1,2 @@ +obj-y += phy.o mdio_bus.o +obj-$(CONFIG_GENERIC_PHY) += generic.o diff --git a/drivers/net/phy/generic.c b/drivers/net/phy/generic.c new file mode 100644 index 0000000000..3f5f127065 --- /dev/null +++ b/drivers/net/phy/generic.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2009 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#include <common.h> +#include <linux/phy.h> +#include <init.h> + +static struct phy_driver generic_phy = { + .drv.name = "Generic PHY", + .phy_id = PHY_ANY_UID, + .phy_id_mask = PHY_ANY_UID, + .features = 0, +}; + +static int generic_phy_register(void) +{ + return phy_driver_register(&generic_phy); +} +device_initcall(generic_phy_register); diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c new file mode 100644 index 0000000000..93d6fe15e1 --- /dev/null +++ b/drivers/net/phy/mdio_bus.c @@ -0,0 +1,250 @@ +/* + * drivers/net/phy/mdio_bus.c + * + * MDIO Bus interface + * + * Author: Andy Fleming + * + * Copyright (c) 2004 Freescale Semiconductor, Inc. + * + * 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. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <common.h> +#include <driver.h> +#include <init.h> +#include <clock.h> +#include <net.h> +#include <errno.h> +#include <linux/phy.h> +#include <linux/err.h> + +/** + * mdiobus_register - bring up all the PHYs on a given bus and attach them to bus + * @bus: target mii_bus + * + * Description: Called by a bus driver to bring up all the PHYs + * on a given bus, and attach them to the bus. + * + * Returns 0 on success or < 0 on error. + */ +int mdiobus_register(struct mii_bus *bus) +{ + int i, err; + + if (NULL == bus || + NULL == bus->read || + NULL == bus->write) + return -EINVAL; + + bus->dev.priv = bus; + bus->dev.id = DEVICE_ID_DYNAMIC; + strcpy(bus->dev.name, "miibus"); + bus->dev.parent = bus->parent; + if (bus->parent) + dev_add_child(bus->parent, &bus->dev); + + err = register_device(&bus->dev); + if (err) { + pr_err("mii_bus %s failed to register\n", bus->dev.name); + return -EINVAL; + } + + if (bus->reset) + bus->reset(bus); + + for (i = 0; i < PHY_MAX_ADDR; i++) { + if ((bus->phy_mask & (1 << i)) == 0) { + struct phy_device *phydev; + + phydev = mdiobus_scan(bus, i); + if (IS_ERR(phydev)) { + err = PTR_ERR(phydev); + goto error; + } + } + } + + pr_info("%s: probed\n", dev_name(&bus->dev)); + return 0; + +error: + while (--i >= 0) { + if (bus->phy_map[i]) { + kfree(bus->phy_map[i]); + bus->phy_map[i] = NULL; + } + } + return err; +} +EXPORT_SYMBOL(mdiobus_register); + +void mdiobus_unregister(struct mii_bus *bus) +{ + int i; + + for (i = 0; i < PHY_MAX_ADDR; i++) { + if (bus->phy_map[i]) + unregister_device(&bus->phy_map[i]->dev); + bus->phy_map[i] = NULL; + } +} +EXPORT_SYMBOL(mdiobus_unregister); + +struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) +{ + struct phy_device *phydev; + + phydev = get_phy_device(bus, addr); + if (IS_ERR(phydev) || phydev == NULL) + return phydev; + + bus->phy_map[addr] = phydev; + + return phydev; +} +EXPORT_SYMBOL(mdiobus_scan); + +/** + * mdio_bus_match - determine if given PHY driver supports the given PHY device + * @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. + */ +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)); +} + +static ssize_t phydev_read(struct cdev *cdev, void *_buf, size_t count, loff_t offset, ulong flags) +{ + int i = count; + uint16_t *buf = _buf; + struct phy_device *phydev = cdev->priv; + + while (i > 0) { + *buf = phy_read(phydev, offset / 2); + buf++; + i -= 2; + offset += 2; + } + + return count; +} + +static ssize_t phydev_write(struct cdev *cdev, const void *_buf, size_t count, loff_t offset, ulong flags) +{ + int i = count; + const uint16_t *buf = _buf; + struct phy_device *phydev = cdev->priv; + + while (i > 0) { + phy_write(phydev, offset / 2, *buf); + buf++; + i -= 2; + offset += 2; + } + + return count; +} + +static struct file_operations phydev_ops = { + .read = phydev_read, + .write = phydev_write, + .lseek = dev_lseek_default, +}; + +static int mdio_bus_probe(struct device_d *_dev) +{ + struct phy_device *dev = to_phy_device(_dev); + struct phy_driver *drv = to_phy_driver(_dev->driver); + + char str[16]; + + dev->attached_dev->phydev = dev; + dev->dev.parent = &dev->attached_dev->dev; + dev_add_child(dev->dev.parent, _dev); + + if (drv->probe) { + int ret; + + ret = drv->probe(dev); + if (ret) { + dev->attached_dev->phydev = NULL; + dev->attached_dev = NULL; + return ret; + } + } + + if (dev->dev_flags) { + if (dev->dev_flags & PHYLIB_FORCE_10) { + dev->speed = SPEED_10; + dev->duplex = DUPLEX_FULL; + dev->autoneg = !AUTONEG_ENABLE; + } + } + + /* Start out supporting everything. Eventually, + * a controller will attach, and may modify one + * or both of these values */ + dev->supported = drv->features; + dev->advertising = drv->features; + + drv->config_init(dev); + + /* Sanitize settings based on PHY capabilities */ + if ((dev->supported & SUPPORTED_Autoneg) == 0) + dev->autoneg = AUTONEG_DISABLE; + + sprintf(str, "%d", dev->addr); + dev_add_param_fixed(&dev->dev, "phy_addr", str); + + dev->cdev.name = asprintf("phy%d", _dev->id); + dev->cdev.size = 64; + dev->cdev.ops = &phydev_ops; + dev->cdev.priv = dev; + dev->cdev.dev = _dev; + devfs_create(&dev->cdev); + + return 0; +} + +static void mdio_bus_remove(struct device_d *_dev) +{ + struct phy_device *dev = to_phy_device(_dev); + struct phy_driver *drv = to_phy_driver(_dev->driver); + + if (drv->remove) + drv->remove(dev); + + free(dev->cdev.name); + devfs_remove(&dev->cdev); +} + +struct bus_type mdio_bus_type = { + .name = "mdio_bus", + .match = mdio_bus_match, + .probe = mdio_bus_probe, + .remove = mdio_bus_remove, +}; +EXPORT_SYMBOL(mdio_bus_type); + +#if 0 +static int mdio_bus_init(void) +{ + return bus_register(&mdio_bus_type); +} +pure_initcall(mdio_bus_init); +#endif diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c new file mode 100644 index 0000000000..88c3ff7723 --- /dev/null +++ b/drivers/net/phy/phy.c @@ -0,0 +1,583 @@ +/* + * drivers/net/phy/phy.c + * + * Framework for finding and configuring PHYs. + * Also contains generic PHY driver + * + * Copyright (c) 2009-2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> + * + * Author: Andy Fleming + * + * Copyright (c) 2004 Freescale Semiconductor, Inc. + * + * 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. + * + */ + +#include <common.h> +#include <driver.h> +#include <net.h> +#include <malloc.h> +#include <linux/phy.h> +#include <linux/phy.h> +#include <linux/err.h> + +#define PHY_AN_TIMEOUT 10 + +static int genphy_config_init(struct phy_device *phydev); + +int phy_update_status(struct phy_device *dev) +{ + struct phy_driver *drv = to_phy_driver(dev->dev.driver); + struct eth_device *edev = dev->attached_dev; + int ret; + int oldspeed = dev->speed, oldduplex = dev->duplex; + + ret = drv->read_status(dev); + if (ret) + return ret; + + if (dev->speed == oldspeed && dev->duplex == oldduplex) + return 0; + + if (dev->adjust_link) + dev->adjust_link(edev); + + if (dev->link) + printf("%dMbps %s duplex link detected\n", dev->speed, + dev->duplex ? "full" : "half"); + + return 0; +} + +struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id) +{ + struct phy_device *dev; + + /* We allocate the device, and initialize the + * default values */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + + if (NULL == dev) + return (struct phy_device*) PTR_ERR((void*)-ENOMEM); + + dev->speed = 0; + dev->duplex = -1; + dev->pause = dev->asym_pause = 0; + dev->link = 1; + dev->autoneg = AUTONEG_ENABLE; + + dev->addr = addr; + dev->phy_id = phy_id; + + dev->bus = bus; + dev->dev.parent = bus->parent; + dev->dev.bus = &mdio_bus_type; + + strcpy(dev->dev.name, "phy"); + dev->dev.id = DEVICE_ID_DYNAMIC; + + return dev; +} +/** + * get_phy_id - reads the specified addr for its ID. + * @bus: the target MII bus + * @addr: PHY address on the MII bus + * @phy_id: where to store the ID retrieved. + * + * Description: Reads the ID registers of the PHY at @addr on the + * @bus, stores it in @phy_id and returns zero on success. + */ +int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id) +{ + int phy_reg; + + /* Grab the bits from PHYIR1, and put them + * in the upper half */ + phy_reg = mdiobus_read(bus, addr, MII_PHYSID1); + + if (phy_reg < 0) + return -EIO; + + *phy_id = (phy_reg & 0xffff) << 16; + + /* Grab the bits from PHYIR2, and put them in the lower half */ + phy_reg = mdiobus_read(bus, addr, MII_PHYSID2); + + if (phy_reg < 0) + return -EIO; + + *phy_id |= (phy_reg & 0xffff); + + return 0; +} + +/** + * get_phy_device - reads the specified PHY device and returns its @phy_device struct + * @bus: the target MII bus + * @addr: PHY address on the MII bus + * + * Description: Reads the ID registers of the PHY at @addr on the + * @bus, then allocates and returns the phy_device to represent it. + */ +struct phy_device *get_phy_device(struct mii_bus *bus, int addr) +{ + struct phy_device *dev = NULL; + u32 phy_id = 0; + int r; + + r = get_phy_id(bus, addr, &phy_id); + if (r) + return ERR_PTR(r); + + /* If the phy_id is mostly Fs, there is no device there */ + if ((phy_id & 0x1fffffff) == 0x1fffffff) + return NULL; + + dev = phy_device_create(bus, addr, phy_id); + + return dev; +} + +/* Automatically gets and returns the PHY device */ +int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, + void (*adjust_link) (struct eth_device *edev), + u32 flags, phy_interface_t interface) +{ + struct phy_driver* drv; + struct phy_device* dev = NULL; + unsigned int i; + int ret = -EINVAL; + + if (!edev->phydev) { + if (addr >= 0) { + dev = bus->phy_map[addr]; + if (!dev) { + ret = -EIO; + goto fail; + } + + dev->attached_dev = edev; + dev->interface = interface; + dev->dev_flags = flags; + + ret = register_device(&dev->dev); + if (ret) + goto fail; + } else { + for (i = 0; i < PHY_MAX_ADDR && !edev->phydev; i++) { + dev = bus->phy_map[i]; + if (!dev || dev->attached_dev) + continue; + + dev->attached_dev = edev; + dev->interface = interface; + dev->dev_flags = flags; + + ret = register_device(&dev->dev); + if (ret) + goto fail; + + break; + } + } + + if (!edev->phydev) { + ret = -EIO; + goto fail; + } + } + + dev = edev->phydev; + drv = to_phy_driver(dev->dev.driver); + + drv->config_aneg(dev); + + dev->adjust_link = adjust_link; + + return 0; + +fail: + if (dev) + dev->attached_dev = NULL; + puts("Unable to find a PHY (unknown ID?)\n"); + return ret; +} + +/* Generic PHY support and helper functions */ + +/** + * genphy_config_advert - sanitize and advertise auto-negotiation parameters + * @phydev: target phy_device struct + * + * Description: Writes MII_ADVERTISE with the appropriate values, + * after sanitizing the values to make sure we only advertise + * what is supported. Returns < 0 on error, 0 if the PHY's advertisement + * hasn't changed, and > 0 if it has changed. + */ +int genphy_config_advert(struct phy_device *phydev) +{ + u32 advertise; + int oldadv, adv; + int err, changed = 0; + + /* Only allow advertising what + * this PHY supports */ + phydev->advertising &= phydev->supported; + advertise = phydev->advertising; + + /* Setup standard advertisement */ + oldadv = adv = phy_read(phydev, MII_ADVERTISE); + + if (adv < 0) + return adv; + + adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP | + ADVERTISE_PAUSE_ASYM); + adv |= ethtool_adv_to_mii_adv_t(advertise); + + if (adv != oldadv) { + err = phy_write(phydev, MII_ADVERTISE, adv); + + if (err < 0) + return err; + changed = 1; + } + + /* Configure gigabit if it's supported */ + if (phydev->supported & (SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full)) { + oldadv = adv = phy_read(phydev, MII_CTRL1000); + + if (adv < 0) + return adv; + + adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); + adv |= ethtool_adv_to_mii_ctrl1000_t(advertise); + + if (adv != oldadv) { + err = phy_write(phydev, MII_CTRL1000, adv); + + if (err < 0) + return err; + changed = 1; + } + } + + return changed; +} + +/** + * genphy_setup_forced - configures/forces speed/duplex from @phydev + * @phydev: target phy_device struct + * + * Description: Configures MII_BMCR to force speed/duplex + * to the values in phydev. Assumes that the values are valid. + * Please see phy_sanitize_settings(). + */ +int genphy_setup_forced(struct phy_device *phydev) +{ + int err; + int ctl = 0; + + phydev->pause = phydev->asym_pause = 0; + + if (SPEED_1000 == phydev->speed) + ctl |= BMCR_SPEED1000; + else if (SPEED_100 == phydev->speed) + ctl |= BMCR_SPEED100; + + if (DUPLEX_FULL == phydev->duplex) + ctl |= BMCR_FULLDPLX; + + err = phy_write(phydev, MII_BMCR, ctl); + + return err; +} + +static int phy_aneg_done(struct phy_device *phydev) +{ + uint64_t start = get_time_ns(); + int ctl; + + while (!is_timeout(start, PHY_AN_TIMEOUT * SECOND)) { + ctl = phy_read(phydev, MII_BMSR); + if (ctl & BMSR_ANEGCOMPLETE) { + phydev->link = 1; + return 0; + } + + /* Restart auto-negotiation if remote fault */ + if (ctl & BMSR_RFAULT) { + puts("PHY remote fault detected\n" + "PHY restarting auto-negotiation\n"); + phy_write(phydev, MII_BMCR, + BMCR_ANENABLE | BMCR_ANRESTART); + } + } + + phydev->link = 0; + return -ETIMEDOUT; +} + +/** + * genphy_restart_aneg - Enable and Restart Autonegotiation + * @phydev: target phy_device struct + */ +int genphy_restart_aneg(struct phy_device *phydev) +{ + int ctl; + + ctl = phy_read(phydev, MII_BMCR); + + if (ctl < 0) + return ctl; + + ctl |= (BMCR_ANENABLE | BMCR_ANRESTART); + + /* Don't isolate the PHY if we're negotiating */ + ctl &= ~(BMCR_ISOLATE); + + ctl = phy_write(phydev, MII_BMCR, ctl); + + if (ctl < 0) + return ctl; + + return phy_aneg_done(phydev); +} + +/** + * genphy_config_aneg - restart auto-negotiation or write BMCR + * @phydev: target phy_device struct + * + * Description: If auto-negotiation is enabled, we configure the + * advertising, and then restart auto-negotiation. If it is not + * enabled, then we write the BMCR. + */ +int genphy_config_aneg(struct phy_device *phydev) +{ + int result; + + if (AUTONEG_ENABLE != phydev->autoneg) + return genphy_setup_forced(phydev); + + result = genphy_config_advert(phydev); + + if (result < 0) /* error */ + return result; + + if (result == 0) { + /* Advertisement hasn't changed, but maybe aneg was never on to + * begin with? Or maybe phy was isolated? */ + int ctl = phy_read(phydev, MII_BMCR); + + if (ctl < 0) + return ctl; + + if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE)) + result = 1; /* do restart aneg */ + } + + /* Only restart aneg if we are advertising something different + * than we were before. */ + if (result > 0) + result = genphy_restart_aneg(phydev); + + return result; +} + +/** + * genphy_update_link - update link status in @phydev + * @phydev: target phy_device struct + * + * Description: Update the value in phydev->link to reflect the + * current link value. In order to do this, we need to read + * the status register twice, keeping the second value. + */ +int genphy_update_link(struct phy_device *phydev) +{ + int status; + + /* Do a fake read */ + status = phy_read(phydev, MII_BMSR); + + if (status < 0) + return status; + + /* wait phy status update in the phy */ + udelay(1000); + + /* Read link and autonegotiation status */ + status = phy_read(phydev, MII_BMSR); + + if (status < 0) + return status; + + if ((status & BMSR_LSTATUS) == 0) + phydev->link = 0; + else + phydev->link = 1; + + return 0; +} + +/** + * genphy_read_status - check the link status and update current link state + * @phydev: target phy_device struct + * + * Description: Check the link, then figure out the current state + * by comparing what we advertise with what the link partner + * advertises. Start by checking the gigabit possibilities, + * then move on to 10/100. + */ +int genphy_read_status(struct phy_device *phydev) +{ + int adv; + int err; + int lpa; + int lpagb = 0; + + /* Update the link, but return if there + * was an error */ + err = genphy_update_link(phydev); + if (err) + return err; + + if (AUTONEG_ENABLE == phydev->autoneg) { + if (phydev->supported & (SUPPORTED_1000baseT_Half + | SUPPORTED_1000baseT_Full)) { + lpagb = phy_read(phydev, MII_STAT1000); + + if (lpagb < 0) + return lpagb; + + adv = phy_read(phydev, MII_CTRL1000); + + if (adv < 0) + return adv; + + lpagb &= adv << 2; + } + + lpa = phy_read(phydev, MII_LPA); + + if (lpa < 0) + return lpa; + + adv = phy_read(phydev, MII_ADVERTISE); + + if (adv < 0) + return adv; + + lpa &= adv; + + phydev->speed = SPEED_10; + phydev->duplex = DUPLEX_HALF; + phydev->pause = phydev->asym_pause = 0; + + if (lpagb & (LPA_1000FULL | LPA_1000HALF)) { + phydev->speed = SPEED_1000; + + if (lpagb & LPA_1000FULL) + phydev->duplex = DUPLEX_FULL; + } else if (lpa & (LPA_100FULL | LPA_100HALF)) { + phydev->speed = SPEED_100; + + if (lpa & LPA_100FULL) + phydev->duplex = DUPLEX_FULL; + } else + if (lpa & LPA_10FULL) + phydev->duplex = DUPLEX_FULL; + + if (phydev->duplex == DUPLEX_FULL) { + phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0; + phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0; + } + } else { + int bmcr = phy_read(phydev, MII_BMCR); + if (bmcr < 0) + return bmcr; + + if (bmcr & BMCR_FULLDPLX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + + if (bmcr & BMCR_SPEED1000) + phydev->speed = SPEED_1000; + else if (bmcr & BMCR_SPEED100) + phydev->speed = SPEED_100; + else + phydev->speed = SPEED_10; + + phydev->pause = phydev->asym_pause = 0; + } + + return 0; +} + +static int genphy_config_init(struct phy_device *phydev) +{ + int val; + u32 features; + + /* For now, I'll claim that the generic driver supports + * all possible port types */ + features = (SUPPORTED_TP | SUPPORTED_MII + | SUPPORTED_AUI | SUPPORTED_FIBRE | + SUPPORTED_BNC); + + /* Do we support autonegotiation? */ + val = phy_read(phydev, MII_BMSR); + + if (val < 0) + return val; + + if (val & BMSR_ANEGCAPABLE) + features |= SUPPORTED_Autoneg; + + if (val & BMSR_100FULL) + features |= SUPPORTED_100baseT_Full; + if (val & BMSR_100HALF) + features |= SUPPORTED_100baseT_Half; + if (val & BMSR_10FULL) + features |= SUPPORTED_10baseT_Full; + if (val & BMSR_10HALF) + features |= SUPPORTED_10baseT_Half; + + if (val & BMSR_ESTATEN) { + val = phy_read(phydev, MII_ESTATUS); + + if (val < 0) + return val; + + if (val & ESTATUS_1000_TFULL) + features |= SUPPORTED_1000baseT_Full; + if (val & ESTATUS_1000_THALF) + features |= SUPPORTED_1000baseT_Half; + } + + phydev->supported = features; + phydev->advertising = features; + + return 0; +} + +int phy_driver_register(struct phy_driver *phydrv) +{ + phydrv->drv.bus = &mdio_bus_type; + + if (!phydrv->config_init) + phydrv->config_init = genphy_config_init; + + if (!phydrv->config_aneg) + phydrv->config_aneg = genphy_config_aneg; + + if (!phydrv->read_status) + phydrv->read_status = genphy_read_status; + + return register_driver(&phydrv->drv); +} diff --git a/drivers/net/smc91111.c b/drivers/net/smc91111.c index 66ca9bca19..1f5a43191a 100644 --- a/drivers/net/smc91111.c +++ b/drivers/net/smc91111.c @@ -63,13 +63,13 @@ #include <command.h> #include <net.h> -#include <miidev.h> #include <malloc.h> #include <init.h> #include <xfuncs.h> #include <errno.h> #include <clock.h> #include <io.h> +#include <linux/phy.h> /*--------------------------------------------------------------- . @@ -447,7 +447,7 @@ struct accessors { }; struct smc91c111_priv { - struct mii_device miidev; + struct mii_bus miibus; struct accessors a; void __iomem *base; }; @@ -617,11 +617,10 @@ static void smc_wait_mmu_release_complete(struct smc91c111_priv *priv) } } -static int smc91c111_phy_write(struct mii_device *mdev, int phyaddr, - int phyreg, int phydata) +static int smc91c111_phy_write(struct mii_bus *bus, int phyaddr, + int phyreg, u16 phydata) { - struct eth_device *edev = mdev->edev; - struct smc91c111_priv *priv = (struct smc91c111_priv *)edev->priv; + struct smc91c111_priv *priv = (struct smc91c111_priv *)bus->priv; int oldBank; int i; unsigned mask; @@ -719,10 +718,9 @@ static int smc91c111_phy_write(struct mii_device *mdev, int phyaddr, return 0; } -static int smc91c111_phy_read(struct mii_device *mdev, int phyaddr, int phyreg) +static int smc91c111_phy_read(struct mii_bus *bus, int phyaddr, int phyreg) { - struct eth_device *edev = mdev->edev; - struct smc91c111_priv *priv = (struct smc91c111_priv *)edev->priv; + struct smc91c111_priv *priv = (struct smc91c111_priv *)bus->priv; int oldBank; int i; unsigned char mask; @@ -888,12 +886,15 @@ static void smc91c111_enable(struct eth_device *edev) static int smc91c111_eth_open(struct eth_device *edev) { struct smc91c111_priv *priv = (struct smc91c111_priv *)edev->priv; - smc91c111_enable(edev); - miidev_wait_aneg(&priv->miidev); - miidev_print_status(&priv->miidev); + /* Configure the Receive/Phy Control register */ + SMC_SELECT_BANK(priv, 0); + SMC_outw(priv, RPC_DEFAULT, RPC_REG); - return 0; + smc91c111_enable(edev); + + return phy_device_connect(edev, &priv->miibus, 0, NULL, + 0, PHY_INTERFACE_MODE_NA); } static int smc91c111_eth_send(struct eth_device *edev, void *packet, @@ -1275,14 +1276,6 @@ static void print_packet( unsigned char * buf, int length ) static int smc91c111_init_dev(struct eth_device *edev) { - struct smc91c111_priv *priv = (struct smc91c111_priv *)edev->priv; - - /* Configure the Receive/Phy Control register */ - SMC_SELECT_BANK(priv, 0); - SMC_outw(priv, RPC_DEFAULT, RPC_REG); - - miidev_restart_aneg(&priv->miidev); - return 0; } @@ -1308,17 +1301,15 @@ static int smc91c111_probe(struct device_d *dev) edev->set_ethaddr = smc91c111_set_ethaddr; edev->parent = dev; - priv->miidev.read = smc91c111_phy_read; - priv->miidev.write = smc91c111_phy_write; - priv->miidev.address = 0; - priv->miidev.flags = 0; - priv->miidev.edev = edev; - priv->miidev.parent = dev; + priv->miibus.read = smc91c111_phy_read; + priv->miibus.write = smc91c111_phy_write; + priv->miibus.priv = priv; + priv->miibus.parent = dev; priv->base = dev_request_mem_region(dev, 0); smc91c111_reset(edev); - mii_register(&priv->miidev); + mdiobus_register(&priv->miibus); eth_register(edev); return 0; diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c index 9dc857ea97..8df6980363 100644 --- a/drivers/net/smc911x.c +++ b/drivers/net/smc911x.c @@ -26,7 +26,6 @@ #include <command.h> #include <net.h> -#include <miidev.h> #include <malloc.h> #include <init.h> #include <xfuncs.h> @@ -34,12 +33,13 @@ #include <clock.h> #include <io.h> #include <smc911x.h> +#include <linux/phy.h> #include "smc911x.h" struct smc911x_priv { struct eth_device edev; - struct mii_device miidev; + struct mii_bus miibus; void __iomem *base; int shift; @@ -194,9 +194,9 @@ static int smc911x_set_ethaddr(struct eth_device *edev, unsigned char *m) return 0; } -static int smc911x_phy_read(struct mii_device *mdev, int phy_addr, int reg) +static int smc911x_phy_read(struct mii_bus *bus, int phy_addr, int reg) { - struct eth_device *edev = mdev->edev; + struct eth_device *edev = bus->priv; while (smc911x_get_mac_csr(edev, MII_ACC) & MII_ACC_MII_BUSY); @@ -208,10 +208,10 @@ static int smc911x_phy_read(struct mii_device *mdev, int phy_addr, int reg) return smc911x_get_mac_csr(edev, MII_DATA); } -static int smc911x_phy_write(struct mii_device *mdev, int phy_addr, - int reg, int val) +static int smc911x_phy_write(struct mii_bus *bus, int phy_addr, + int reg, u16 val) { - struct eth_device *edev = mdev->edev; + struct eth_device *edev = bus->priv; while (smc911x_get_mac_csr(edev, MII_ACC) & MII_ACC_MII_BUSY); @@ -304,9 +304,12 @@ static void smc911x_enable(struct eth_device *edev) static int smc911x_eth_open(struct eth_device *edev) { struct smc911x_priv *priv = (struct smc911x_priv *)edev->priv; + int ret; - miidev_wait_aneg(&priv->miidev); - miidev_print_status(&priv->miidev); + ret = phy_device_connect(edev, &priv->miibus, 1, NULL, + 0, PHY_INTERFACE_MODE_NA); + if (ret) + return ret; /* Turn on Tx + Rx */ smc911x_enable(edev); @@ -401,13 +404,9 @@ static int smc911x_eth_rx(struct eth_device *edev) static int smc911x_init_dev(struct eth_device *edev) { - struct smc911x_priv *priv = (struct smc911x_priv *)edev->priv; - smc911x_set_mac_csr(edev, MAC_CR, MAC_CR_TXEN | MAC_CR_RXEN | MAC_CR_HBDIS); - miidev_restart_aneg(&priv->miidev); - return 0; } @@ -532,17 +531,15 @@ static int smc911x_probe(struct device_d *dev) edev->set_ethaddr = smc911x_set_ethaddr; edev->parent = dev; - priv->miidev.read = smc911x_phy_read; - priv->miidev.write = smc911x_phy_write; - priv->miidev.address = 1; - priv->miidev.flags = 0; - priv->miidev.edev = edev; - priv->miidev.parent = dev; + priv->miibus.read = smc911x_phy_read; + priv->miibus.write = smc911x_phy_write; + priv->miibus.priv = edev; + priv->miibus.parent = dev; smc911x_reset(edev); smc911x_phy_reset(edev); - mii_register(&priv->miidev); + mdiobus_register(&priv->miibus); eth_register(edev); return 0; diff --git a/drivers/net/tap.c b/drivers/net/tap.c index 5c3a1e3f1c..d5c7031982 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -76,7 +76,7 @@ int tap_probe(struct device_d *dev) struct tap_priv *priv; int ret = 0; - priv = xmalloc(sizeof(struct tap_priv)); + priv = xzalloc(sizeof(struct tap_priv)); priv->name = "barebox"; priv->fd = tap_alloc(priv->name); @@ -85,7 +85,7 @@ int tap_probe(struct device_d *dev) goto out; } - edev = xmalloc(sizeof(struct eth_device)); + edev = xzalloc(sizeof(struct eth_device)); edev->priv = priv; edev->parent = dev; diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index b53dcc7c46..adb1b0b097 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -5,11 +5,11 @@ menuconfig NET_USB if NET_USB config NET_USB_ASIX - select MIIDEV + select PHYLIB bool "Asix compatible" config NET_USB_SMSC95XX - select MIIDEV + select PHYLIB bool "SMSC95xx" endif diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c index be5a170d71..97680cfb54 100644 --- a/drivers/net/usb/asix.c +++ b/drivers/net/usb/asix.c @@ -1,7 +1,7 @@ #include <common.h> #include <init.h> #include <net.h> -#include <miidev.h> +#include <linux/phy.h> #include <usb/usb.h> #include <usb/usbnet.h> #include <errno.h> @@ -231,10 +231,9 @@ static inline int asix_set_hw_mii(struct usbnet *dev) return ret; } -static int asix_mdio_read(struct mii_device *mdev, int phy_id, int loc) +static int asix_mdio_read(struct mii_bus *bus, int phy_id, int loc) { - struct eth_device *eth = mdev->edev; - struct usbnet *dev = eth->priv; + struct usbnet *dev = bus->priv; __le16 res; asix_set_sw_mii(dev); @@ -248,10 +247,9 @@ static int asix_mdio_read(struct mii_device *mdev, int phy_id, int loc) return le16_to_cpu(res); } -static int asix_mdio_write(struct mii_device *mdev, int phy_id, int loc, int val) +static int asix_mdio_write(struct mii_bus *bus, int phy_id, int loc, u16 val) { - struct eth_device *eth = mdev->edev; - struct usbnet *dev = eth->priv; + struct usbnet *dev = bus->priv; __le16 res = cpu_to_le16(val); dev_dbg(&dev->edev.dev, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x", @@ -469,14 +467,13 @@ static int asix_tx_fixup(struct usbnet *dev, static int asix_init_mii(struct usbnet *dev) { - dev->miidev.read = asix_mdio_read; - dev->miidev.write = asix_mdio_write; - dev->miidev.address = asix_get_phy_addr(dev); - dev->miidev.flags = 0; - dev->miidev.edev = &dev->edev; - dev->miidev.parent = &dev->udev->dev; - - return mii_register(&dev->miidev); + dev->miibus.read = asix_mdio_read; + dev->miibus.write = asix_mdio_write; + dev->phy_addr = asix_get_phy_addr(dev); + dev->miibus.priv = dev; + dev->miibus.parent = &dev->udev->dev; + + return mdiobus_register(&dev->miibus); } static int ax88172_link_reset(struct usbnet *dev) @@ -631,7 +628,7 @@ out: static void asix_unbind(struct usbnet *dev) { - mii_unregister(&dev->miidev); + mdiobus_unregister(&dev->miibus); } static struct driver_info ax8817x_info = { diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index d28a0dfe2b..38ca12f257 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -23,7 +23,7 @@ #include <malloc.h> #include <asm/byteorder.h> #include <errno.h> -#include <miidev.h> +#include <linux/phy.h> #include "smsc95xx.h" #define SMSC_CHIPNAME "smsc95xx" @@ -119,10 +119,9 @@ static int smsc95xx_phy_wait_not_busy(struct usbnet *dev) return -EIO; } -static int smsc95xx_mdio_read(struct mii_device *mdev, int phy_id, int idx) +static int smsc95xx_mdio_read(struct mii_bus *bus, int phy_id, int idx) { - struct eth_device *eth = mdev->edev; - struct usbnet *dev = eth->priv; + struct usbnet *dev = bus->priv; u32 val, addr; /* confirm MII not busy */ @@ -145,11 +144,10 @@ static int smsc95xx_mdio_read(struct mii_device *mdev, int phy_id, int idx) return val & 0xffff; } -static int smsc95xx_mdio_write(struct mii_device *mdev, int phy_id, int idx, - int regval) +static int smsc95xx_mdio_write(struct mii_bus *bus, int phy_id, int idx, + u16 regval) { - struct eth_device *eth = mdev->edev; - struct usbnet *dev = eth->priv; + struct usbnet *dev = bus->priv; u32 val, addr; /* confirm MII not busy */ @@ -435,20 +433,19 @@ static int smsc95xx_phy_initialize(struct usbnet *dev) uint16_t val, bmcr; /* Initialize MII structure */ - dev->miidev.read = smsc95xx_mdio_read; - dev->miidev.write = smsc95xx_mdio_write; - dev->miidev.address = 1; /* FIXME: asix_get_phy_addr(dev); */ - dev->miidev.flags = 0; - dev->miidev.edev = &dev->edev; - dev->miidev.parent = &dev->udev->dev; -// dev->miidev.name = dev->edev.name; + dev->miibus.read = smsc95xx_mdio_read; + dev->miibus.write = smsc95xx_mdio_write; + dev->phy_addr = 1; /* FIXME: asix_get_phy_addr(dev); */ + dev->miibus.priv = dev; + dev->miibus.parent = &dev->udev->dev; +// dev->miibus.name = dev->edev.name; /* reset phy and wait for reset to complete */ - smsc95xx_mdio_write(&dev->miidev, phy_id, MII_BMCR, BMCR_RESET); + smsc95xx_mdio_write(&dev->miibus, phy_id, MII_BMCR, BMCR_RESET); do { udelay(10 * 1000); - bmcr = smsc95xx_mdio_read(&dev->miidev, phy_id, MII_BMCR); + bmcr = smsc95xx_mdio_read(&dev->miibus, phy_id, MII_BMCR); timeout++; } while ((bmcr & MII_BMCR) && (timeout < 100)); @@ -457,14 +454,14 @@ static int smsc95xx_phy_initialize(struct usbnet *dev) return -EIO; } - smsc95xx_mdio_write(&dev->miidev, phy_id, MII_ADVERTISE, + smsc95xx_mdio_write(&dev->miibus, phy_id, MII_ADVERTISE, ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); /* read to clear */ - val = smsc95xx_mdio_read(&dev->miidev, phy_id, PHY_INT_SRC); + val = smsc95xx_mdio_read(&dev->miibus, phy_id, PHY_INT_SRC); - smsc95xx_mdio_write(&dev->miidev, phy_id, PHY_INT_MASK, + smsc95xx_mdio_write(&dev->miibus, phy_id, PHY_INT_MASK, PHY_INT_MASK_DEFAULT_); netif_dbg(dev, ifup, dev->net, "phy initialised successfully\n"); @@ -747,7 +744,7 @@ static int smsc95xx_bind(struct usbnet *dev) dev->edev.get_ethaddr = smsc95xx_get_ethaddr; dev->edev.set_ethaddr = smsc95xx_set_ethaddr; - mii_register(&dev->miidev); + mdiobus_register(&dev->miibus); return 0; } @@ -756,7 +753,7 @@ static void smsc95xx_unbind(struct usbnet *dev) { struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); - mii_unregister(&dev->miidev); + mdiobus_unregister(&dev->miibus); if (pdata) { netif_dbg(dev, ifdown, dev->net, "free pdata\n"); diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index c7e360690e..80b4ae7b9e 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -4,6 +4,7 @@ #include <asm/byteorder.h> #include <errno.h> #include <malloc.h> +#include <linux/phy.h> static inline int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd) { @@ -160,8 +161,6 @@ static int usbnet_init(struct eth_device *edev) return ret; } - miidev_restart_aneg(&dev->miidev); - return 0; } @@ -171,12 +170,8 @@ static int usbnet_open(struct eth_device *edev) dev_dbg(&edev->dev, "%s\n",__func__); - if (miidev_wait_aneg(&dev->miidev)) - return -1; - - miidev_print_status(&dev->miidev); - - return 0; + return phy_device_connect(edev, &dev->miibus, dev->phy_addr, NULL, + 0, PHY_INTERFACE_MODE_NA); } static void usbnet_halt(struct eth_device *edev) diff --git a/drivers/nor/cfi_flash.c b/drivers/nor/cfi_flash.c index 7ca36380a9..f65763a6e6 100644 --- a/drivers/nor/cfi_flash.c +++ b/drivers/nor/cfi_flash.c @@ -913,7 +913,6 @@ struct file_operations cfi_ops = { .memmap = generic_memmap_ro, }; -#ifdef CONFIG_PARTITION_NEED_MTD static int cfi_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { @@ -973,11 +972,11 @@ static void cfi_init_mtd(struct flash_info *info) mtd->flags = MTD_CAP_NORFLASH; info->cdev.mtd = mtd; } -#endif static int cfi_probe (struct device_d *dev) { struct flash_info *info = xzalloc(sizeof(*info)); + int cfinum; dev->priv = (void *)info; @@ -996,30 +995,46 @@ static int cfi_probe (struct device_d *dev) dev_info(dev, "found cfi flash at %p, size %ld\n", info->base, info->size); - info->cdev.name = asprintf("nor%d", dev->id); + if (dev->id < 0) + cfinum = cdev_find_free_index("nor"); + else + cfinum = dev->id; + + info->cdev.name = asprintf("nor%d", cfinum); info->cdev.size = info->size; info->cdev.dev = dev; info->cdev.ops = &cfi_ops; info->cdev.priv = info; -#ifdef CONFIG_PARTITION_NEED_MTD - cfi_init_mtd(info); -#endif + if (IS_ENABLED(CONFIG_PARTITION_NEED_MTD)) + cfi_init_mtd(info); + devfs_create(&info->cdev); + if (dev->device_node) + of_parse_partitions(info->cdev.name, dev->device_node); + return 0; } +static __maybe_unused struct of_device_id cfi_dt_ids[] = { + { + .compatible = "cfi-flash", + }, { + /* sentinel */ + } +}; + static struct driver_d cfi_driver = { - .name = "cfi_flash", - .probe = cfi_probe, - .info = cfi_info, + .name = "cfi_flash", + .probe = cfi_probe, + .info = cfi_info, + .of_compatible = DRV_OF_COMPAT(cfi_dt_ids), }; static int cfi_init(void) { - return register_driver(&cfi_driver); + return register_driver(&cfi_driver); } device_initcall(cfi_init); - diff --git a/drivers/nor/cfi_flash.h b/drivers/nor/cfi_flash.h index ee4ea38297..8f818ba4aa 100644 --- a/drivers/nor/cfi_flash.h +++ b/drivers/nor/cfi_flash.h @@ -71,9 +71,7 @@ struct flash_info { ulong addr_unlock2; /* unlock address 2 for AMD flash roms */ struct cfi_cmd_set *cfi_cmd_set; struct cdev cdev; -#ifdef CONFIG_PARTITION_NEED_MTD struct mtd_info mtd; -#endif int numeraseregions; struct mtd_erase_region_info *eraseregions; void *base; diff --git a/drivers/nor/m25p80.c b/drivers/nor/m25p80.c index 5713ad58fe..e3b5b95443 100644 --- a/drivers/nor/m25p80.c +++ b/drivers/nor/m25p80.c @@ -648,6 +648,9 @@ static const struct spi_device_id m25p_ids[] = { { "cat25c09", CAT25_INFO( 128, 8, 32, 2) }, { "cat25c17", CAT25_INFO( 256, 8, 32, 2) }, { "cat25128", CAT25_INFO(2048, 8, 64, 2) }, + + /* Micron */ + { "n25q128", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, { }, }; @@ -694,6 +697,74 @@ static struct file_operations m25p80_ops = { .lseek = dev_lseek_default, }; +static int m25p_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct m25p *flash = container_of(mtd, struct m25p, mtd); + ssize_t ret; + + ret = flash->cdev.ops->read(&flash->cdev, buf, len, from, 0); + if (ret < 0) { + *retlen = 0; + return ret; + } + + *retlen = ret; + return 0; +} + +static int m25p_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct m25p *flash = container_of(mtd, struct m25p, mtd); + ssize_t ret; + + ret = flash->cdev.ops->write(&flash->cdev, buf, len, to, 0); + if (ret < 0) { + *retlen = 0; + return ret; + } + + *retlen = ret; + return 0; +} + +static int m25p_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct m25p *flash = container_of(mtd, struct m25p, mtd); + ssize_t ret; + + ret = flash->cdev.ops->erase(&flash->cdev, instr->len, instr->addr); + + if (ret) { + instr->state = MTD_ERASE_FAILED; + return -EIO; + } + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return 0; +} + +static void m25p_init_mtd(struct m25p *flash) +{ + struct mtd_info *mtd = &flash->mtd; + + mtd->read = m25p_mtd_read; + mtd->write = m25p_mtd_write; + mtd->erase = m25p_mtd_erase; + mtd->size = flash->size; + mtd->name = flash->cdev.name; + mtd->erasesize = flash->erasesize; + mtd->writesize = 1; + mtd->subpage_sft = 0; + mtd->eraseregions = NULL; + mtd->numeraseregions = 0; + mtd->flags = MTD_CAP_NORFLASH; + flash->cdev.mtd = mtd; +} + /* * board specific setup should have ensured the SPI clock used here * matches what the READ command supports, at least until this driver @@ -825,6 +896,9 @@ static int m25p_probe(struct device_d *dev) dev_info(dev, "%s (%lld Kbytes)\n", id->name, (long long)flash->size >> 10); + if (IS_ENABLED(CONFIG_PARTITION_NEED_MTD)) + m25p_init_mtd(flash); + devfs_create(&flash->cdev); return 0; @@ -838,7 +912,7 @@ static struct driver_d epcs_flash_driver = { static int epcs_init(void) { - register_driver(&epcs_flash_driver); + spi_register_driver(&epcs_flash_driver); return 0; } diff --git a/drivers/nor/m25p80.h b/drivers/nor/m25p80.h index 34bf2e259a..957900e3a2 100644 --- a/drivers/nor/m25p80.h +++ b/drivers/nor/m25p80.h @@ -46,7 +46,7 @@ struct spi_device_id { struct m25p { struct spi_device *spi; struct flash_info *info; - struct mtd_info mtd; + struct mtd_info mtd; struct cdev cdev; char *name; u32 erasesize; diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig new file mode 100644 index 0000000000..95f10d025c --- /dev/null +++ b/drivers/of/Kconfig @@ -0,0 +1,2 @@ +config OFDEVICE + bool diff --git a/drivers/of/Makefile b/drivers/of/Makefile new file mode 100644 index 0000000000..c14aaec79f --- /dev/null +++ b/drivers/of/Makefile @@ -0,0 +1,3 @@ +obj-y += base.o +obj-y += gpio.o +obj-y += partition.o diff --git a/drivers/of/base.c b/drivers/of/base.c new file mode 100644 index 0000000000..ebbaef85c1 --- /dev/null +++ b/drivers/of/base.c @@ -0,0 +1,802 @@ +/* + * base.c - basic devicetree functions + * + * Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + * + * based on Linux devicetree support + * + * 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 version 2 + * as published by the Free Software Foundation. + * + * 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 <of.h> +#include <errno.h> +#include <libfdt.h> +#include <malloc.h> +#include <init.h> +#include <linux/ctype.h> + +/** + * struct alias_prop - Alias property in 'aliases' node + * @link: List node to link the structure in aliases_lookup list + * @alias: Alias property name + * @np: Pointer to device_node that the alias stands for + * @id: Index value from end of alias name + * @stem: Alias string without the index + * + * The structure represents one alias property of 'aliases' node as + * an entry in aliases_lookup list. + */ +struct alias_prop { + struct list_head link; + const char *alias; + struct device_node *np; + int id; + char stem[0]; +}; + +static LIST_HEAD(aliases_lookup); + +static LIST_HEAD(phandle_list); + +static LIST_HEAD(allnodes); + +struct device_node *root_node; + +struct device_node *of_aliases; + +int of_n_addr_cells(struct device_node *np) +{ + const __be32 *ip; + + do { + if (np->parent) + np = np->parent; + ip = of_get_property(np, "#address-cells", NULL); + if (ip) + return be32_to_cpup(ip); + } while (np->parent); + /* No #address-cells property for the root node */ + return OF_ROOT_NODE_ADDR_CELLS_DEFAULT; +} +EXPORT_SYMBOL(of_n_addr_cells); + +int of_n_size_cells(struct device_node *np) +{ + const __be32 *ip; + + do { + if (np->parent) + np = np->parent; + ip = of_get_property(np, "#size-cells", NULL); + if (ip) + return be32_to_cpup(ip); + } while (np->parent); + /* No #size-cells property for the root node */ + return OF_ROOT_NODE_SIZE_CELLS_DEFAULT; +} +EXPORT_SYMBOL(of_n_size_cells); + +static void of_bus_default_count_cells(struct device_node *dev, + int *addrc, int *sizec) +{ + if (addrc) + *addrc = of_n_addr_cells(dev); + if (sizec) + *sizec = of_n_size_cells(dev); +} + +void of_bus_count_cells(struct device_node *dev, + int *addrc, int *sizec) +{ + of_bus_default_count_cells(dev, addrc, sizec); +} + +struct property *of_find_property(const struct device_node *node, const char *name) +{ + struct property *p; + + list_for_each_entry(p, &node->properties, list) + if (!strcmp(p->name, name)) + return p; + return NULL; +} +EXPORT_SYMBOL(of_find_property); + +static void of_alias_add(struct alias_prop *ap, struct device_node *np, + int id, const char *stem, int stem_len) +{ + ap->np = np; + ap->id = id; + strncpy(ap->stem, stem, stem_len); + ap->stem[stem_len] = 0; + list_add_tail(&ap->link, &aliases_lookup); + pr_debug("adding DT alias:%s: stem=%s id=%i node=%s\n", + ap->alias, ap->stem, ap->id, np->full_name); +} + +/** + * of_alias_scan - Scan all properties of 'aliases' node + * + * The function scans all the properties of 'aliases' node and populates + * the global lookup table with the properties. It returns the + * number of alias_prop found, or error code in error case. + */ +void of_alias_scan(void) +{ + struct property *pp; + + of_aliases = of_find_node_by_path("/aliases"); + if (!of_aliases) + return; + + list_for_each_entry(pp, &of_aliases->properties, list) { + const char *start = pp->name; + const char *end = start + strlen(start); + struct device_node *np; + struct alias_prop *ap; + int id, len; + + /* Skip those we do not want to proceed */ + if (!strcmp(pp->name, "name") || + !strcmp(pp->name, "phandle") || + !strcmp(pp->name, "linux,phandle")) + continue; + + np = of_find_node_by_path(pp->value); + if (!np) + continue; + + /* walk the alias backwards to extract the id and work out + * the 'stem' string */ + while (isdigit(*(end-1)) && end > start) + end--; + len = end - start; + + id = simple_strtol(end, 0, 10); + if (id < 0) + continue; + + /* Allocate an alias_prop with enough space for the stem */ + ap = xzalloc(sizeof(*ap) + len + 1); + if (!ap) + continue; + ap->alias = start; + of_alias_add(ap, np, id, start, len); + } +} + +/** + * of_alias_get_id - Get alias id for the given device_node + * @np: Pointer to the given device_node + * @stem: Alias stem of the given device_node + * + * The function travels the lookup table to get alias id for the given + * device_node and alias stem. It returns the alias id if find it. + */ +int of_alias_get_id(struct device_node *np, const char *stem) +{ + struct alias_prop *app; + int id = -ENODEV; + + list_for_each_entry(app, &aliases_lookup, link) { + if (strcmp(app->stem, stem) != 0) + continue; + + if (np == app->np) { + id = app->id; + break; + } + } + + return id; +} +EXPORT_SYMBOL_GPL(of_alias_get_id); + +u64 of_translate_address(struct device_node *node, const __be32 *in_addr) +{ + struct property *p; + u64 addr = be32_to_cpu(*in_addr); + + while (1) { + int na, nc; + + if (!node->parent) + return addr; + + node = node->parent; + p = of_find_property(node, "ranges"); + if (!p && node->parent) + return OF_BAD_ADDR; + of_bus_count_cells(node, &na, &nc); + if (na != 1 || nc != 1) { + printk("%s: #size-cells != 1 or #address-cells != 1 " + "currently not supported\n", node->name); + return OF_BAD_ADDR; + } + } +} +EXPORT_SYMBOL(of_translate_address); + +/* + * of_find_node_by_phandle - Find a node given a phandle + * @handle: phandle of the node to find + */ +struct device_node *of_find_node_by_phandle(phandle phandle) +{ + struct device_node *node; + + list_for_each_entry(node, &phandle_list, phandles) + if (node->phandle == phandle) + return node; + return NULL; +} +EXPORT_SYMBOL(of_find_node_by_phandle); + +/* + * Find a property with a given name for a given node + * and return the value. + */ +const void *of_get_property(const struct device_node *np, const char *name, + int *lenp) +{ + struct property *pp = of_find_property(np, name); + + if (!pp) + return NULL; + + if (lenp) + *lenp = pp->length; + + return pp ? pp->value : NULL; +} +EXPORT_SYMBOL(of_get_property); + +/** Checks if the given "compat" string matches one of the strings in + * the device's "compatible" property + */ +int of_device_is_compatible(const struct device_node *device, + const char *compat) +{ + const char *cp; + int cplen, l; + + cp = of_get_property(device, "compatible", &cplen); + if (cp == NULL) + return 0; + while (cplen > 0) { + if (strcmp(cp, compat) == 0) + return 1; + l = strlen(cp) + 1; + cp += l; + cplen -= l; + } + + return 0; +} +EXPORT_SYMBOL(of_device_is_compatible); + +int of_match(struct device_d *dev, struct driver_d *drv) +{ + struct of_device_id *id; + + id = drv->of_compatible; + + while (id->compatible) { + if (of_device_is_compatible(dev->device_node, id->compatible) == 1) { + dev->of_id_entry = id; + return 0; + } + id++; + } + + return 1; +} +EXPORT_SYMBOL(of_match); + +/** + * of_property_read_u32_array - Find and read an array of 32 bit integers + * from a property. + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @out_value: pointer to return value, modified only if return value is 0. + * + * Search for a property in a device node and read 32-bit value(s) from + * it. Returns 0 on success, -EINVAL if the property does not exist, + * -ENODATA if property does not have a value, and -EOVERFLOW if the + * property data isn't large enough. + * + * The out_value is modified only if a valid u32 value can be decoded. + */ +int of_property_read_u32_array(const struct device_node *np, + const char *propname, u32 *out_values, + size_t sz) +{ + struct property *prop = of_find_property(np, propname); + const __be32 *val; + + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + if ((sz * sizeof(*out_values)) > prop->length) + return -EOVERFLOW; + + val = prop->value; + while (sz--) + *out_values++ = be32_to_cpup(val++); + return 0; +} +EXPORT_SYMBOL_GPL(of_property_read_u32_array); + +/** + * of_parse_phandles_with_args - Find a node pointed by phandle in a list + * @np: pointer to a device tree node containing a list + * @list_name: property name that contains a list + * @cells_name: property name that specifies phandles' arguments count + * @index: index of a phandle to parse out + * @out_node: optional pointer to device_node struct pointer (will be filled) + * @out_args: optional pointer to arguments pointer (will be filled) + * + * This function is useful to parse lists of phandles and their arguments. + * Returns 0 on success and fills out_node and out_args, on error returns + * appropriate errno value. + * + * Example: + * + * phandle1: node1 { + * #list-cells = <2>; + * } + * + * phandle2: node2 { + * #list-cells = <1>; + * } + * + * node3 { + * list = <&phandle1 1 2 &phandle2 3>; + * } + * + * To get a device_node of the `node2' node you may call this: + * of_parse_phandles_with_args(node3, "list", "#list-cells", 2, &node2, &args); + */ +int of_parse_phandles_with_args(struct device_node *np, const char *list_name, + const char *cells_name, int index, + struct device_node **out_node, + const void **out_args) +{ + int ret = -EINVAL; + const __be32 *list; + const __be32 *list_end; + int size; + int cur_index = 0; + struct device_node *node = NULL; + const void *args = NULL; + + list = of_get_property(np, list_name, &size); + if (!list) { + ret = -ENOENT; + goto err0; + } + list_end = list + size / sizeof(*list); + + while (list < list_end) { + const __be32 *cells; + phandle phandle; + + phandle = be32_to_cpup(list++); + args = list; + + /* one cell hole in the list = <>; */ + if (!phandle) + goto next; + + node = of_find_node_by_phandle(phandle); + if (!node) { + pr_debug("%s: could not find phandle %d\n", + np->full_name, phandle); + goto err0; + } + + cells = of_get_property(node, cells_name, &size); + if (!cells || size != sizeof(*cells)) { + pr_debug("%s: could not get %s for %s\n", + np->full_name, cells_name, node->full_name); + goto err1; + } + + list += be32_to_cpup(cells); + if (list > list_end) { + pr_debug("%s: insufficient arguments length\n", + np->full_name); + goto err1; + } +next: + if (cur_index == index) + break; + + node = NULL; + args = NULL; + cur_index++; + } + + if (!node) { + /* + * args w/o node indicates that the loop above has stopped at + * the 'hole' cell. Report this differently. + */ + if (args) + ret = -EEXIST; + else + ret = -ENOENT; + goto err0; + } + + if (out_node) + *out_node = node; + if (out_args) + *out_args = args; + + return 0; +err1: +err0: + pr_debug("%s failed with status %d\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL(of_parse_phandles_with_args); + +/** + * of_machine_is_compatible - Test root of device tree for a given compatible value + * @compat: compatible string to look for in root node's compatible property. + * + * Returns true if the root node has the given value in its + * compatible property. + */ +int of_machine_is_compatible(const char *compat) +{ + if (!root_node) + return 0; + + return of_device_is_compatible(root_node, compat); +} +EXPORT_SYMBOL(of_machine_is_compatible); + +/** + * of_find_node_by_path - Find a node matching a full OF path + * @path: The full path to match + * + * Returns a node pointer with refcount incremented, use + * of_node_put() on it when done. + */ +struct device_node *of_find_node_by_path(const char *path) +{ + struct device_node *np; + + list_for_each_entry(np, &allnodes, list) { + if (np->full_name && (strcmp(np->full_name, path) == 0)) + break; + } + return np; +} +EXPORT_SYMBOL(of_find_node_by_path); + +struct device_node *of_get_root_node(void) +{ + return root_node; +} + +static int of_node_disabled(struct device_node *node) +{ + struct property *p; + + p = of_find_property(node, "status"); + if (p) { + if (!strcmp("disabled", p->value)) + return 1; + } + return 0; +} + +void of_print_nodes(struct device_node *node, int indent) +{ + struct device_node *n; + struct property *p; + int i; + + if (!node) + return; + + if (of_node_disabled(node)) + return; + + for (i = 0; i < indent; i++) + printf("\t"); + + printf("%s%s\n", node->name, node->name ? " {" : "{"); + + list_for_each_entry(p, &node->properties, list) { + for (i = 0; i < indent + 1; i++) + printf("\t"); + printf("%s: ", p->name); + of_print_property(p->value, p->length); + printf("\n"); + } + + list_for_each_entry(n, &node->children, parent_list) { + of_print_nodes(n, indent + 1); + } + + for (i = 0; i < indent; i++) + printf("\t"); + printf("};\n"); +} + +static struct device_node *new_device_node(struct device_node *parent) +{ + struct device_node *node; + + node = xzalloc(sizeof(*node)); + node->parent = parent; + if (parent) + list_add_tail(&node->parent_list, &parent->children); + + INIT_LIST_HEAD(&node->children); + INIT_LIST_HEAD(&node->properties); + + return node; +} + +static struct property *new_property(struct device_node *node, const char *name, + const void *data, int len) +{ + struct property *prop; + + prop = xzalloc(sizeof(*prop)); + + prop->name = strdup(name); + prop->length = len; + prop->value = xzalloc(len); + memcpy(prop->value, data, len); + + list_add_tail(&prop->list, &node->properties); + + return prop; +} + +static struct device_d *add_of_device(struct device_node *node) +{ + struct device_d *dev; + char *name, *at; + const struct property *cp; + + if (of_node_disabled(node)) + return NULL; + + cp = of_get_property(node, "compatible", NULL); + if (!cp) + return NULL; + + dev = xzalloc(sizeof(*dev)); + + name = xstrdup(node->name); + at = strchr(name, '@'); + if (at) { + *at = 0; + snprintf(dev->name, MAX_DRIVER_NAME, "%s.%s", at + 1, name); + } else { + strncpy(dev->name, node->name, MAX_DRIVER_NAME); + } + + dev->id = DEVICE_ID_SINGLE; + dev->resource = node->resource; + dev->num_resources = 1; + dev->device_node = node; + node->device = dev; + + debug("register device 0x%08x\n", node->resource[0].start); + + register_device(dev); + + free(name); + + return dev; +} +EXPORT_SYMBOL(add_of_device); + +static int add_of_device_resource(struct device_node *node) +{ + struct property *reg; + u64 address, size; + struct resource *res; + struct device_d *dev; + phandle phandle; + int ret; + + ret = of_property_read_u32(node, "phandle", &phandle); + if (!ret) { + node->phandle = phandle; + list_add_tail(&node->phandles, &phandle_list); + } + + reg = of_find_property(node, "reg"); + if (!reg) + return -ENODEV; + + address = of_translate_address(node, reg->value); + if (address == OF_BAD_ADDR) + return -EINVAL; + + size = be32_to_cpu(((u32 *)reg->value)[1]); + + /* + * A device may already be registered as platform_device. + * Instead of registering the same device again, just + * add this node to the existing device. + */ + for_each_device(dev) { + if (!dev->resource) + continue; + if (dev->resource->start == address) { + debug("connecting %s to %s\n", node->name, dev_name(dev)); + node->device = dev; + dev->device_node = node; + node->resource = dev->resource; + return 0; + } + } + + res = xzalloc(sizeof(*res)); + res->start = address; + res->end = address + size - 1; + res->flags = IORESOURCE_MEM; + + node->resource = res; + + add_of_device(node); + + return 0; +} + +void of_free(struct device_node *node) +{ + struct device_node *n, *nt; + struct property *p, *pt; + + if (!node) + return; + + list_for_each_entry_safe(p, pt, &node->properties, list) { + list_del(&p->list); + free(p->name); + free(p->value); + free(p); + } + + list_for_each_entry_safe(n, nt, &node->children, parent_list) { + of_free(n); + } + + if (node->parent) + list_del(&node->parent_list); + + if (node->device) + node->device->device_node = NULL; + else + free(node->resource); + + free(node->name); + free(node->full_name); + free(node); +} + +static void __of_probe(struct device_node *node) +{ + struct device_node *n; + + if (node->device) + return; + + add_of_device_resource(node); + + list_for_each_entry(n, &node->children, parent_list) + __of_probe(n); +} + +int of_probe(void) +{ + if(!root_node) + return -ENODEV; + + __of_probe(root_node); + + return 0; +} + +/* + * Parse a flat device tree binary blob and store it in the barebox + * internal tree format, + */ +int of_parse_dtb(struct fdt_header *fdt) +{ + const void *nodep; /* property node pointer */ + int nodeoffset; /* node offset from libfdt */ + int nextoffset; /* next node offset from libfdt */ + uint32_t tag; /* tag */ + int len; /* length of the property */ + int level = 0; /* keep track of nesting level */ + const struct fdt_property *fdt_prop; + const char *pathp; + int depth = 10000; + struct device_node *node = NULL; + char buf[1024]; + int ret; + + if (root_node) + return -EBUSY; + + nodeoffset = fdt_path_offset(fdt, "/"); + if (nodeoffset < 0) { + /* + * Not found or something else bad happened. + */ + printf ("libfdt fdt_path_offset() returned %s\n", + fdt_strerror(nodeoffset)); + return -EINVAL; + } + + while (1) { + tag = fdt_next_tag(fdt, nodeoffset, &nextoffset); + switch (tag) { + case FDT_BEGIN_NODE: + pathp = fdt_get_name(fdt, nodeoffset, NULL); + + if (pathp == NULL) + pathp = "/* NULL pointer error */"; + + ret = fdt_get_path(fdt, nodeoffset, buf, 1024); + if (ret) + return -EINVAL; + + node = new_device_node(node); + if (!node->parent) + root_node = node; + node->full_name = xstrdup(buf); + node->name = xstrdup(pathp); + list_add_tail(&node->list, &allnodes); + break; + case FDT_END_NODE: + node = node->parent; + break; + case FDT_PROP: + fdt_prop = fdt_offset_ptr(fdt, nodeoffset, + sizeof(*fdt_prop)); + pathp = fdt_string(fdt, + fdt32_to_cpu(fdt_prop->nameoff)); + len = fdt32_to_cpu(fdt_prop->len); + nodep = fdt_prop->data; + new_property(node, pathp, nodep, len); + break; + case FDT_NOP: + break; + case FDT_END: + of_alias_scan(); + return 0; + default: + if (level <= depth) + printf("Unknown tag 0x%08X\n", tag); + return -EINVAL; + } + nodeoffset = nextoffset; + } + + return 0; +} diff --git a/drivers/of/gpio.c b/drivers/of/gpio.c new file mode 100644 index 0000000000..d4314f3e28 --- /dev/null +++ b/drivers/of/gpio.c @@ -0,0 +1,28 @@ +#define DEBUG + +#include <common.h> +#include <errno.h> +#include <of.h> +#include <gpio.h> + +int of_get_named_gpio(struct device_node *np, + const char *propname, int index) +{ + int ret; + struct device_node *gpio_np; + const void *gpio_spec; + + ret = of_parse_phandles_with_args(np, propname, "#gpio-cells", index, + &gpio_np, &gpio_spec); + if (ret) { + pr_debug("%s: can't parse gpios property: %d\n", __func__, ret); + return -EINVAL; + } + + ret = gpio_get_num(gpio_np->device, be32_to_cpup(gpio_spec)); + if (ret < 0) + return ret; + + return ret; +} + diff --git a/drivers/of/partition.c b/drivers/of/partition.c new file mode 100644 index 0000000000..6a57a6036e --- /dev/null +++ b/drivers/of/partition.c @@ -0,0 +1,64 @@ +/* + * partition.c - devicetree partition parsing + * + * Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + * + * based on Linux devicetree support + * + * 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 version 2 + * as published by the Free Software Foundation. + * + * 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 <of.h> +#include <malloc.h> +#include <linux/mtd/mtd.h> + +int of_parse_partitions(const char *cdevname, + struct device_node *node) +{ + struct device_node *n; + const char *partname; + char *filename; + + device_node_for_nach_child(node, n) { + const __be32 *reg; + unsigned long offset, size; + const char *name; + int len; + unsigned long flags = 0; + + reg = of_get_property(n, "reg", &len); + if (!reg) + continue; + + offset = be32_to_cpu(reg[0]); + size = be32_to_cpu(reg[1]); + + partname = of_get_property(n, "label", &len); + if (!partname) + partname = of_get_property(n, "name", &len); + name = (char *)partname; + + debug("add partition: %s.%s 0x%08lx 0x%08lx\n", cdevname, partname, offset, size); + + if (of_get_property(n, "read-only", &len)) + flags = DEVFS_PARTITION_READONLY; + + filename = asprintf("%s.%s", cdevname, partname); + + devfs_add_partition(cdevname, offset, size, flags, filename); + + free(filename); + } + + return 0; +} diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c index c6e61ee104..58c69e5c2d 100644 --- a/drivers/serial/amba-pl011.c +++ b/drivers/serial/amba-pl011.c @@ -31,6 +31,7 @@ #include <linux/amba/serial.h> #include <linux/clk.h> #include <linux/err.h> +#include <linux/amba/bus.h> /* * We wrap our port structure around the generic console_device. @@ -40,6 +41,23 @@ struct amba_uart_port { struct console_device uart; /* uart */ struct clk *clk; /* uart clock */ u32 uartclk; + struct vendor_data *vendor; +}; + +/* There is by now at least one vendor with differing details, so handle it */ +struct vendor_data { + unsigned int lcrh_tx; + unsigned int lcrh_rx; +}; + +static struct vendor_data vendor_arm = { + .lcrh_tx = UART011_LCRH, + .lcrh_rx = UART011_LCRH, +}; + +static struct vendor_data vendor_st = { + .lcrh_tx = ST_UART011_LCRH_TX, + .lcrh_rx = ST_UART011_LCRH_RX, }; static inline struct amba_uart_port * @@ -112,13 +130,27 @@ static int pl011_tstc(struct console_device *cdev) return !(readl(uart->base + UART01x_FR) & UART01x_FR_RXFE); } +static void pl011_rlcr(struct amba_uart_port *uart, u32 lcr) +{ + struct vendor_data *vendor = uart->vendor; + + writew(lcr, uart->base + vendor->lcrh_rx); + if (vendor->lcrh_tx != vendor->lcrh_rx) { + int i; + /* + * Wait 10 PCLKs before writing LCRH_TX register, + * to get this delay write read only register 10 times + */ + for (i = 0; i < 10; ++i) + writew(0xff, uart->base + UART011_MIS); + writew(lcr, uart->base + vendor->lcrh_tx); + } +} + int pl011_init_port (struct console_device *cdev) { - struct device_d *dev = cdev->dev; struct amba_uart_port *uart = to_amba_uart_port(cdev); - uart->base = dev_request_mem_region(dev, 0); - /* ** First, disable everything. */ @@ -138,8 +170,7 @@ int pl011_init_port (struct console_device *cdev) /* ** Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled. */ - writel((UART01x_LCRH_WLEN_8 | UART01x_LCRH_FEN), - uart->base + UART011_LCRH); + pl011_rlcr(uart, UART01x_LCRH_WLEN_8 | UART01x_LCRH_FEN); /* ** Finally, enable the UART @@ -150,19 +181,21 @@ int pl011_init_port (struct console_device *cdev) return 0; } -static int pl011_probe(struct device_d *dev) +static int pl011_probe(struct amba_device *dev, const struct amba_id *id) { struct amba_uart_port *uart; struct console_device *cdev; uart = xzalloc(sizeof(struct amba_uart_port)); - uart->clk = clk_get(dev, NULL); + uart->clk = clk_get(&dev->dev, NULL); + uart->base = amba_get_mem_region(dev); + uart->vendor = (void*)id->data; if (IS_ERR(uart->clk)) return PTR_ERR(uart->clk); cdev = &uart->uart; - cdev->dev = dev; + cdev->dev = &dev->dev; cdev->f_caps = CONSOLE_STDIN | CONSOLE_STDOUT | CONSOLE_STDERR; cdev->tstc = pl011_tstc; cdev->putc = pl011_putc; @@ -178,14 +211,31 @@ static int pl011_probe(struct device_d *dev) return 0; } -static struct driver_d pl011_driver = { - .name = "uart-pl011", - .probe = pl011_probe, +static struct amba_id pl011_ids[] = { + { + .id = 0x00041011, + .mask = 0x000fffff, + .data = &vendor_arm, + }, + { + .id = 0x00380802, + .mask = 0x00ffffff, + .data = &vendor_st, + }, + { 0, 0 }, +}; + +struct amba_driver pl011_driver = { + .drv = { + .name = "uart-pl011", + }, + .probe = pl011_probe, + .id_table = pl011_ids, }; static int pl011_init(void) { - register_driver(&pl011_driver); + amba_driver_register(&pl011_driver); return 0; } diff --git a/drivers/serial/serial_imx.c b/drivers/serial/serial_imx.c index 827e838ff2..012ab028a1 100644 --- a/drivers/serial/serial_imx.c +++ b/drivers/serial/serial_imx.c @@ -354,10 +354,23 @@ static void imx_serial_remove(struct device_d *dev) free(priv); } +static __maybe_unused struct of_device_id imx_serial_dt_ids[] = { + { + .compatible = "fsl,imx1-uart", + .data = 0, + }, { + .compatible = "fsl,imx21-uart", + .data = 1, + }, { + /* sentinel */ + } +}; + static struct driver_d imx_serial_driver = { - .name = "imx_serial", - .probe = imx_serial_probe, + .name = "imx_serial", + .probe = imx_serial_probe, .remove = imx_serial_remove, + .of_compatible = DRV_OF_COMPAT(imx_serial_dt_ids), }; static int imx_serial_init(void) diff --git a/drivers/spi/imx_spi.c b/drivers/spi/imx_spi.c index bdd6e8172a..c3dc6cc863 100644 --- a/drivers/spi/imx_spi.c +++ b/drivers/spi/imx_spi.c @@ -20,6 +20,7 @@ #include <spi/spi.h> #include <xfuncs.h> #include <io.h> +#include <errno.h> #include <gpio.h> #include <mach/spi.h> #include <mach/generic.h> @@ -491,6 +492,32 @@ static struct spi_imx_devtype_data spi_imx_devtype_data[] = { #endif }; +static int imx_spi_dt_probe(struct imx_spi *imx) +{ + struct device_node *node = imx->master.dev->device_node; + int ret, i; + u32 num_cs; + + if (!node) + return -ENODEV; + + ret = of_property_read_u32(node, "fsl,spi-num-chipselects", &num_cs); + if (ret) + return ret; + + imx->master.num_chipselect = num_cs; + imx->cs_array = xzalloc(sizeof(u32) * num_cs); + + for (i = 0; i < num_cs; i++) { + int cs_gpio = of_get_named_gpio(node, "cs-gpios", i); + imx->cs_array[i] = cs_gpio; + } + + spi_of_register_slaves(&imx->master, node); + + return 0; +} + static int imx_spi_probe(struct device_d *dev) { struct spi_master *master; @@ -505,8 +532,13 @@ static int imx_spi_probe(struct device_d *dev) master->setup = imx_spi_setup; master->transfer = imx_spi_transfer; - master->num_chipselect = pdata->num_chipselect; - imx->cs_array = pdata->chipselect; + if (pdata) { + master->num_chipselect = pdata->num_chipselect; + imx->cs_array = pdata->chipselect; + } else { + if (IS_ENABLED(CONFIG_OFDEVICE)) + imx_spi_dt_probe(imx); + } #ifdef CONFIG_DRIVER_SPI_IMX_0_0 if (cpu_is_mx27()) @@ -532,9 +564,22 @@ static int imx_spi_probe(struct device_d *dev) return 0; } +static __maybe_unused struct of_device_id imx_spi_dt_ids[] = { + { + .compatible = "fsl,imx27-cspi", + }, { + .compatible = "fsl,imx35-cspi", + }, { + .compatible = "fsl,imx51-ecspi", + }, { + /* sentinel */ + } +}; + static struct driver_d imx_spi_driver = { .name = "imx_spi", .probe = imx_spi_probe, + .of_compatible = DRV_OF_COMPAT(imx_spi_dt_ids), }; static int imx_spi_init(void) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 39eae4eed7..44040e5f62 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -23,6 +23,8 @@ #include <xfuncs.h> #include <malloc.h> #include <errno.h> +#include <init.h> +#include <of.h> /* SPI devices should normally not be created by SPI device drivers; that * would make them board-specific. Similarly with SPI master drivers. @@ -73,10 +75,12 @@ struct spi_device *spi_new_device(struct spi_master *master, proxy->mode = chip->mode; 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); /* allocate a free id for this chip */ proxy->dev.id = DEVICE_ID_DYNAMIC; proxy->dev.type_data = proxy; + proxy->dev.device_node = chip->device_node; dev_add_child(master->dev, &proxy->dev); /* drivers may modify this initial i/o setup */ @@ -96,6 +100,27 @@ fail: } EXPORT_SYMBOL(spi_new_device); +#ifdef CONFIG_OFDEVICE +void spi_of_register_slaves(struct spi_master *master, struct device_node *node) +{ + struct device_node *n; + struct spi_board_info chip; + struct property *reg; + + device_node_for_nach_child(node, n) { + chip.name = n->name; + chip.bus_num = master->bus_num; + chip.max_speed_hz = 300000; /* FIXME */ + reg = of_find_property(n, "reg"); + if (!reg) + continue; + chip.chip_select = of_read_number(reg->value, 1); + chip.device_node = n; + spi_register_board_info(&chip, 1); + } +} +#endif + /** * spi_register_board_info - register SPI devices for a given board * @info: array of chip descriptors @@ -150,6 +175,8 @@ static void scan_boardinfo(struct spi_master *master) } } +static LIST_HEAD(spi_master_list); + /** * spi_register_master - register SPI master controller * @master: initialized master, originally from spi_alloc_master() @@ -182,6 +209,8 @@ int spi_register_master(struct spi_master *master) if (master->num_chipselect == 0) return -EINVAL; + list_add_tail(&master->list, &spi_master_list); + /* populate children from any spi device tables */ scan_boardinfo(master); status = 0; @@ -236,3 +265,35 @@ int spi_write_then_read(struct spi_device *spi, return status; } EXPORT_SYMBOL(spi_write_then_read); + +static int spi_match(struct device_d *dev, struct driver_d *drv) +{ + if (IS_ENABLED(CONFIG_OFDEVICE) && dev->device_node && + drv->of_compatible) + return of_match(dev, drv); + + return strcmp(dev->name, drv->name) ? -1 : 0; +} + +static int spi_probe(struct device_d *dev) +{ + return dev->driver->probe(dev); +} + +static void spi_remove(struct device_d *dev) +{ + dev->driver->remove(dev); +} + +struct bus_type spi_bus = { + .name = "spi", + .match = spi_match, + .probe = spi_probe, + .remove = spi_remove, +}; + +static int spi_bus_init(void) +{ + return bus_register(&spi_bus); +} +pure_initcall(spi_bus_init); diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 662705ea5f..9dc931bdc6 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -1418,3 +1418,8 @@ struct bus_type usb_bus_type = { .remove = usb_remove, }; +static int usb_bus_init(void) +{ + return bus_register(&usb_bus_type); +} +pure_initcall(usb_bus_init); diff --git a/drivers/video/fb.c b/drivers/video/fb.c index d885570b24..ae6ff74cab 100644 --- a/drivers/video/fb.c +++ b/drivers/video/fb.c @@ -125,6 +125,7 @@ int register_framebuffer(struct fb_info *info) sprintf(dev->name, "fb"); + info->dev.bus = &fb_bus; register_device(&info->dev); dev_add_param(dev, "enable", fb_enable_set, NULL, 0); dev_set_param(dev, "enable", "0"); @@ -160,19 +161,41 @@ static void fb_info(struct device_d *dev) printf("\n"); } -static int fb_probe(struct device_d *hw_dev) +static struct driver_d fb_driver = { + .name = "fb", + .info = fb_info, +}; + +static int fb_match(struct device_d *dev, struct driver_d *drv) { return 0; } -static struct driver_d fb_driver = { - .name = "fb", +static int fb_probe(struct device_d *dev) +{ + return 0; +} + +static void fb_remove(struct device_d *dev) +{ +} + +struct bus_type fb_bus = { + .name = "fb", + .match = fb_match, .probe = fb_probe, - .info = fb_info, + .remove = fb_remove, }; +static int fb_bus_init(void) +{ + return bus_register(&fb_bus); +} +pure_initcall(fb_bus_init); + static int fb_init_driver(void) { + fb_driver.bus = &fb_bus; register_driver(&fb_driver); return 0; } |