diff options
Diffstat (limited to 'drivers/net/cpsw.c')
-rw-r--r-- | drivers/net/cpsw.c | 205 |
1 files changed, 140 insertions, 65 deletions
diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index 4a8f9e67d6..31ca61a230 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -9,7 +9,6 @@ #include <command.h> #include <dma.h> -#include <net.h> #include <malloc.h> #include <net.h> #include <linux/phy.h> @@ -23,7 +22,7 @@ #include <asm/system.h> #include <linux/err.h> -#include <mach/cpsw.h> +#include <mach/omap/cpsw.h> #define CPSW_VERSION_1 0x19010a #define CPSW_VERSION_2 0x19010c @@ -176,7 +175,7 @@ struct cpsw_slave { phy_interface_t phy_if; struct eth_device edev; struct cpsw_priv *cpsw; - struct device_d dev; + struct device dev; }; struct cpdma_desc { @@ -196,7 +195,7 @@ struct cpdma_chan { }; struct cpsw_priv { - struct device_d *dev; + struct device *dev; u32 version; struct cpsw_platform_data data; @@ -216,6 +215,8 @@ struct cpsw_priv { unsigned int slave_size; unsigned int sliver_ofs; + void *rx_buffer[PKTBUFSRX - 2]; + struct cpdma_desc *descs; struct cpdma_desc *desc_free; struct cpdma_chan rx_chan, tx_chan; @@ -224,7 +225,7 @@ struct cpsw_priv { }; struct cpsw_mdio_priv { - struct device_d *dev; + struct device *dev; struct mii_bus miibus; struct cpsw_mdio_regs *mdio_regs; }; @@ -582,7 +583,7 @@ static int cpsw_mdio_write(struct mii_bus *bus, int phy_id, int phy_reg, u16 val return 0; } -static int cpsw_mdio_probe(struct device_d *dev) +static int cpsw_mdio_probe(struct device *dev) { struct resource *iores; struct cpsw_mdio_priv *priv; @@ -592,7 +593,12 @@ static int cpsw_mdio_probe(struct device_d *dev) priv = xzalloc(sizeof(*priv)); + /* If we can't request I/O memory region, we'll assume parent did + * it for us + */ iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores) && PTR_ERR(iores) == -EBUSY) + iores = dev_get_resource(dev, IORESOURCE_MEM, 0); if (IS_ERR(iores)) return PTR_ERR(iores); priv->mdio_regs = IOMEM(iores->start); @@ -616,7 +622,7 @@ static int cpsw_mdio_probe(struct device_d *dev) * silicon. Since the effect of (b) was found to be largely * negligible, we keep things simple here. */ - udelay(1000); + udelay(2000); start = get_time_ns(); while (1) { @@ -649,8 +655,9 @@ static __maybe_unused struct of_device_id cpsw_mdio_dt_ids[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, cpsw_mdio_dt_ids); -static struct driver_d cpsw_mdio_driver = { +static struct driver cpsw_mdio_driver = { .name = "cpsw-mdio", .probe = cpsw_mdio_probe, .of_compatible = DRV_OF_COMPAT(cpsw_mdio_dt_ids), @@ -797,7 +804,8 @@ static void cpdma_desc_free(struct cpsw_priv *priv, struct cpdma_desc *desc) } static int cpdma_submit(struct cpsw_priv *priv, struct cpdma_chan *chan, - void *buffer, int len, int port) + void *sw_buffer, dma_addr_t hw_buffer, + int len, int port) { struct cpdma_desc *desc, *prev; u32 mode; @@ -816,10 +824,10 @@ static int cpdma_submit(struct cpsw_priv *priv, struct cpdma_chan *chan, (port << CPDMA_FROM_TO_PORT_SHIFT); writel(0, &desc->hw_next); - writel((u32)buffer, &desc->hw_buffer); + writel(hw_buffer, &desc->hw_buffer); writel(len, &desc->hw_len); writel(mode | len, &desc->hw_mode); - writel((u32)buffer, &desc->sw_buffer); + writel((u32)sw_buffer, &desc->sw_buffer); writel((u32)len, &desc->sw_len); if (!chan->head) { @@ -846,7 +854,7 @@ done: } static int cpdma_process(struct cpsw_slave *slave, struct cpdma_chan *chan, - void **buffer, int *len) + void **buffer, dma_addr_t *dma, int *len) { struct cpdma_desc *desc = chan->head; struct cpsw_priv *priv = slave->cpsw; @@ -860,6 +868,8 @@ static int cpdma_process(struct cpsw_slave *slave, struct cpdma_chan *chan, if (len) *len = status & 0x7ff; + if (dma) + *dma = readl(&desc->hw_buffer); if (buffer) *buffer = (void *)readl(&desc->sw_buffer); @@ -911,7 +921,7 @@ static int cpsw_open(struct eth_device *edev) return 0; } -static int cpsw_setup(struct device_d *dev) +static int cpsw_setup(struct device *dev) { struct cpsw_priv *priv = dev->priv; int i, ret; @@ -975,8 +985,15 @@ static int cpsw_setup(struct device_d *dev) /* submit rx descs */ for (i = 0; i < PKTBUFSRX - 2; i++) { - ret = cpdma_submit(priv, &priv->rx_chan, NetRxPackets[i], - PKTSIZE, 0); + void *buffer = priv->rx_buffer[i]; + unsigned len = PKTSIZE; + dma_addr_t dma; + + dma = dma_map_single(priv->dev, buffer, len, DMA_FROM_DEVICE); + if (dma_mapping_error(priv->dev, dma)) + return -EFAULT; + + ret = cpdma_submit(priv, &priv->rx_chan, buffer, dma, len, 0); if (ret < 0) { dev_err(dev, "error %d submitting rx desc\n", ret); break; @@ -1007,20 +1024,21 @@ static int cpsw_send(struct eth_device *edev, void *packet, int length) { struct cpsw_slave *slave = edev->priv; struct cpsw_priv *priv = slave->cpsw; - void *buffer; - int ret, len; + dma_addr_t dma; + int ret; dev_dbg(&slave->dev, "* %s slave %d\n", __func__, slave->slave_num); /* first reap completed packets */ - while (cpdma_process(slave, &priv->tx_chan, &buffer, &len) >= 0); + while (cpdma_process(slave, &priv->tx_chan, NULL, NULL, NULL) >= 0) + ; dev_dbg(&slave->dev, "%s: %i bytes @ 0x%p\n", __func__, length, packet); - dma_sync_single_for_device((unsigned long)packet, length, DMA_TO_DEVICE); - ret = cpdma_submit(priv, &priv->tx_chan, packet, + dma = dma_map_single(priv->dev, packet, length, DMA_TO_DEVICE); + ret = cpdma_submit(priv, &priv->tx_chan, packet, dma, length, BIT(slave->slave_num)); - dma_sync_single_for_cpu((unsigned long)packet, length, DMA_TO_DEVICE); + dma_unmap_single(priv->dev, dma, length, DMA_TO_DEVICE); return ret; } @@ -1029,16 +1047,15 @@ static int cpsw_recv(struct eth_device *edev) { struct cpsw_slave *slave = edev->priv; struct cpsw_priv *priv = slave->cpsw; + dma_addr_t dma; void *buffer; int len; - while (cpdma_process(slave, &priv->rx_chan, &buffer, &len) >= 0) { - dma_sync_single_for_cpu((unsigned long)buffer, len, - DMA_FROM_DEVICE); + while (cpdma_process(slave, &priv->rx_chan, &buffer, &dma, &len) >= 0) { + dma_sync_single_for_cpu(priv->dev, dma, len, DMA_FROM_DEVICE); net_receive(edev, buffer, len); - dma_sync_single_for_device((unsigned long)buffer, len, - DMA_FROM_DEVICE); - cpdma_submit(priv, &priv->rx_chan, buffer, PKTSIZE, 0); + dma_sync_single_for_device(priv->dev, dma, len, DMA_FROM_DEVICE); + cpdma_submit(priv, &priv->rx_chan, buffer, dma, PKTSIZE, 0); } return 0; @@ -1058,7 +1075,7 @@ static int cpsw_slave_setup(struct cpsw_slave *slave, int slave_num, { void *regs = priv->regs; struct eth_device *edev = &slave->edev; - struct device_d *dev = &slave->dev; + struct device *dev = &slave->dev; int ret; edev->parent = dev; @@ -1214,11 +1231,27 @@ static void cpsw_gmii_sel_am335x(struct cpsw_slave *slave) writel(reg, phy_sel_addr); } -static int cpsw_probe_dt(struct cpsw_priv *priv) +static void cpsw_add_slave(struct cpsw_slave *slave, struct device_node *child, int i) { - struct device_d *dev = priv->dev; - struct device_node *np = dev->device_node, *child; - struct device_node *physel; + uint32_t phy_id[2] = {-1, -1}; + int ret; + + if (!of_find_node_by_name_address(child, "fixed-link")) { + ret = of_property_read_u32_array(child, "phy_id", phy_id, 2); + if (!ret) + dev_warn(slave->cpsw->dev, "phy_id is deprecated, use phy-handle\n"); + } + + slave->dev.of_node = child; + slave->phy_id = phy_id[1]; + slave->phy_if = of_get_phy_mode(child); + slave->slave_num = i; +} + +static int cpsw_legacy_probe_dt(struct cpsw_priv *priv) +{ + struct device *dev = priv->dev; + struct device_node *np = dev->of_node, *child; int ret, i = 0; ret = of_property_read_u32(np, "slaves", &priv->num_slaves); @@ -1227,15 +1260,6 @@ static int cpsw_probe_dt(struct cpsw_priv *priv) priv->slaves = xzalloc(sizeof(struct cpsw_slave) * priv->num_slaves); - physel = of_find_compatible_node(NULL, NULL, "ti,am3352-phy-gmii-sel"); - if (!physel) { - dev_err(dev, "Cannot find ti,am3352-phy-gmii-sel node\n"); - return -EINVAL; - } - ret = cpsw_phy_sel_init(priv, physel); - if (ret) - return ret; - for_each_child_of_node(np, child) { if (of_device_is_compatible(child, "ti,davinci_mdio")) { ret = of_pinctrl_select_state_default(child); @@ -1244,34 +1268,78 @@ static int cpsw_probe_dt(struct cpsw_priv *priv) } if (i < priv->num_slaves && !strncmp(child->name, "slave", 5)) { - struct cpsw_slave *slave = &priv->slaves[i]; - uint32_t phy_id[2] = {-1, -1}; + cpsw_add_slave(&priv->slaves[i], child, i); + i++; + } + } - if (!of_find_node_by_name(child, "fixed-link")) { - ret = of_property_read_u32_array(child, "phy_id", phy_id, 2); - if (!ret) - dev_warn(dev, "phy_id is deprecated, use phy-handle\n"); - } + return 0; +} - slave->dev.device_node = child; - slave->phy_id = phy_id[1]; - slave->phy_if = of_get_phy_mode(child); - slave->slave_num = i; +static int cpsw_switch_probe_dt(struct cpsw_priv *priv) +{ + struct device *dev = priv->dev; + struct device_node *np = dev->of_node, *child; + struct device_node *ports = NULL; + int ret, i = 0; - i++; + for_each_child_of_node(np, child) { + if (of_device_is_compatible(child, "ti,davinci_mdio")) { + ret = of_pinctrl_select_state_default(child); + if (ret) + return ret; + } + + if (!strcmp(child->name, "ethernet-ports")) { + ports = child; + priv->num_slaves = of_get_available_child_count(ports); } } - for (i = 0; i < priv->num_slaves; i++) { - struct cpsw_slave *slave = &priv->slaves[i]; + if (!ports) + return -EINVAL; + + priv->slaves = xzalloc(sizeof(struct cpsw_slave) * priv->num_slaves); + + for_each_available_child_of_node(ports, child) { + cpsw_add_slave(&priv->slaves[i], child, i); + i++; + } - cpsw_gmii_sel_am335x(slave); + return 0; +} + +static int cpsw_probe_dt(struct cpsw_priv *priv) +{ + struct device *dev = priv->dev; + struct device_node *physel; + int (*probe_slaves_dt)(struct cpsw_priv *priv); + int ret, i = 0; + + physel = of_find_compatible_node(NULL, NULL, "ti,am3352-phy-gmii-sel"); + if (!physel) { + dev_err(dev, "Cannot find ti,am3352-phy-gmii-sel node\n"); + return -EINVAL; } + ret = cpsw_phy_sel_init(priv, physel); + if (ret) + return ret; + + probe_slaves_dt = device_get_match_data(dev); + if (!probe_slaves_dt) + return -EINVAL; + + ret = probe_slaves_dt(priv); + if (ret < 0) + return ret; + + for (i = 0; i < priv->num_slaves; i++) + cpsw_gmii_sel_am335x(&priv->slaves[i]); return 0; } -static int cpsw_probe(struct device_d *dev) +static int cpsw_probe(struct device *dev) { struct resource *iores; struct cpsw_platform_data *data = (struct cpsw_platform_data *)dev->platform_data; @@ -1282,19 +1350,23 @@ static int cpsw_probe(struct device_d *dev) dev_dbg(dev, "* %s\n", __func__); - ret = of_platform_populate(dev->device_node, NULL, dev); - if (ret) - return ret; - iores = dev_request_mem_resource(dev, 0); if (IS_ERR(iores)) return PTR_ERR(iores); regs = IOMEM(iores->start); + ret = of_platform_populate(dev->of_node, NULL, dev); + if (ret) + return ret; + priv = xzalloc(sizeof(*priv)); priv->dev = dev; - if (dev->device_node) { + ret = net_alloc_packets(priv->rx_buffer, ARRAY_SIZE(priv->rx_buffer)); + if (ret) + goto out; + + if (dev->of_node) { ret = cpsw_probe_dt(priv); if (ret) goto out; @@ -1355,7 +1427,7 @@ out: return ret; } -static void cpsw_remove(struct device_d *dev) +static void cpsw_remove(struct device *dev) { struct cpsw_priv *priv = dev->priv; int i; @@ -1371,13 +1443,16 @@ static void cpsw_remove(struct device_d *dev) static __maybe_unused struct of_device_id cpsw_dt_ids[] = { { - .compatible = "ti,cpsw", + .compatible = "ti,cpsw", .data = cpsw_legacy_probe_dt + }, { + .compatible = "ti,cpsw-switch", .data = cpsw_switch_probe_dt }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, cpsw_dt_ids); -static struct driver_d cpsw_driver = { +static struct driver cpsw_driver = { .name = "cpsw", .probe = cpsw_probe, .remove = cpsw_remove, |