diff options
Diffstat (limited to 'drivers/net/ethernet/allwinner')
-rw-r--r-- | drivers/net/ethernet/allwinner/Kconfig | 4 | ||||
-rw-r--r-- | drivers/net/ethernet/allwinner/sun4i-emac.c | 282 | ||||
-rw-r--r-- | drivers/net/ethernet/allwinner/sun4i-emac.h | 18 |
3 files changed, 252 insertions, 52 deletions
diff --git a/drivers/net/ethernet/allwinner/Kconfig b/drivers/net/ethernet/allwinner/Kconfig index 264a482ec31d..3e81059f8693 100644 --- a/drivers/net/ethernet/allwinner/Kconfig +++ b/drivers/net/ethernet/allwinner/Kconfig @@ -8,7 +8,7 @@ config NET_VENDOR_ALLWINNER default y depends on ARCH_SUNXI - ---help--- + help If you have a network (Ethernet) card belonging to this class, say Y here. @@ -28,7 +28,7 @@ config SUN4I_EMAC select MII select PHYLIB select MDIO_SUN4I - ---help--- + help Support for Allwinner A10 EMAC ethernet driver. To compile this driver as a module, choose M here. The module diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c index 22cadfbeedfb..a94c62956eed 100644 --- a/drivers/net/ethernet/allwinner/sun4i-emac.c +++ b/drivers/net/ethernet/allwinner/sun4i-emac.c @@ -29,11 +29,11 @@ #include <linux/platform_device.h> #include <linux/phy.h> #include <linux/soc/sunxi/sunxi_sram.h> +#include <linux/dmaengine.h> #include "sun4i-emac.h" #define DRV_NAME "sun4i-emac" -#define DRV_VERSION "1.02" #define EMAC_MAX_FRAME_LEN 0x0600 @@ -77,7 +77,6 @@ struct emac_board_info { void __iomem *membase; u32 msg_enable; struct net_device *ndev; - struct sk_buff *skb_last; u16 tx_fifo_stat; int emacrx_completed_flag; @@ -88,6 +87,16 @@ struct emac_board_info { unsigned int duplex; phy_interface_t phy_interface; + struct dma_chan *rx_chan; + phys_addr_t emac_rx_fifo; +}; + +struct emac_dma_req { + struct emac_board_info *db; + struct dma_async_tx_descriptor *desc; + struct sk_buff *skb; + dma_addr_t rxbuf; + int count; }; static void emac_update_speed(struct net_device *dev) @@ -97,9 +106,9 @@ static void emac_update_speed(struct net_device *dev) /* set EMAC SPEED, depend on PHY */ reg_val = readl(db->membase + EMAC_MAC_SUPP_REG); - reg_val &= ~(0x1 << 8); + reg_val &= ~EMAC_MAC_SUPP_100M; if (db->speed == SPEED_100) - reg_val |= 1 << 8; + reg_val |= EMAC_MAC_SUPP_100M; writel(reg_val, db->membase + EMAC_MAC_SUPP_REG); } @@ -207,13 +216,123 @@ static void emac_inblk_32bit(void __iomem *reg, void *data, int count) readsl(reg, data, round_up(count, 4) / 4); } +static struct emac_dma_req * +emac_alloc_dma_req(struct emac_board_info *db, + struct dma_async_tx_descriptor *desc, struct sk_buff *skb, + dma_addr_t rxbuf, int count) +{ + struct emac_dma_req *req; + + req = kzalloc(sizeof(struct emac_dma_req), GFP_ATOMIC); + if (!req) + return NULL; + + req->db = db; + req->desc = desc; + req->skb = skb; + req->rxbuf = rxbuf; + req->count = count; + return req; +} + +static void emac_free_dma_req(struct emac_dma_req *req) +{ + kfree(req); +} + +static void emac_dma_done_callback(void *arg) +{ + struct emac_dma_req *req = arg; + struct emac_board_info *db = req->db; + struct sk_buff *skb = req->skb; + struct net_device *dev = db->ndev; + int rxlen = req->count; + u32 reg_val; + + dma_unmap_single(db->dev, req->rxbuf, rxlen, DMA_FROM_DEVICE); + + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->stats.rx_bytes += rxlen; + /* Pass to upper layer */ + dev->stats.rx_packets++; + + /* re enable cpu receive */ + reg_val = readl(db->membase + EMAC_RX_CTL_REG); + reg_val &= ~EMAC_RX_CTL_DMA_EN; + writel(reg_val, db->membase + EMAC_RX_CTL_REG); + + /* re enable interrupt */ + reg_val = readl(db->membase + EMAC_INT_CTL_REG); + reg_val |= EMAC_INT_CTL_RX_EN; + writel(reg_val, db->membase + EMAC_INT_CTL_REG); + + db->emacrx_completed_flag = 1; + emac_free_dma_req(req); +} + +static int emac_dma_inblk_32bit(struct emac_board_info *db, + struct sk_buff *skb, void *rdptr, int count) +{ + struct dma_async_tx_descriptor *desc; + dma_cookie_t cookie; + dma_addr_t rxbuf; + struct emac_dma_req *req; + int ret = 0; + + rxbuf = dma_map_single(db->dev, rdptr, count, DMA_FROM_DEVICE); + ret = dma_mapping_error(db->dev, rxbuf); + if (ret) { + dev_err(db->dev, "dma mapping error.\n"); + return ret; + } + + desc = dmaengine_prep_slave_single(db->rx_chan, rxbuf, count, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_err(db->dev, "prepare slave single failed\n"); + ret = -ENOMEM; + goto prepare_err; + } + + req = emac_alloc_dma_req(db, desc, skb, rxbuf, count); + if (!req) { + dev_err(db->dev, "alloc emac dma req error.\n"); + ret = -ENOMEM; + goto alloc_req_err; + } + + desc->callback_param = req; + desc->callback = emac_dma_done_callback; + + cookie = dmaengine_submit(desc); + ret = dma_submit_error(cookie); + if (ret) { + dev_err(db->dev, "dma submit error.\n"); + goto submit_err; + } + + dma_async_issue_pending(db->rx_chan); + return ret; + +submit_err: + emac_free_dma_req(req); + +alloc_req_err: + dmaengine_desc_free(desc); + +prepare_err: + dma_unmap_single(db->dev, rxbuf, count, DMA_FROM_DEVICE); + return ret; +} + /* ethtool ops */ static void emac_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, dev_name(&dev->dev), sizeof(info->bus_info)); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, dev_name(&dev->dev), sizeof(info->bus_info)); } static u32 emac_get_msglevel(struct net_device *dev) @@ -310,7 +429,7 @@ static unsigned int emac_powerup(struct net_device *ndev) /* initial EMAC */ /* flush RX FIFO */ reg_val = readl(db->membase + EMAC_RX_CTL_REG); - reg_val |= 0x8; + reg_val |= EMAC_RX_CTL_FLUSH_FIFO; writel(reg_val, db->membase + EMAC_RX_CTL_REG); udelay(1); @@ -322,8 +441,8 @@ static unsigned int emac_powerup(struct net_device *ndev) /* set MII clock */ reg_val = readl(db->membase + EMAC_MAC_MCFG_REG); - reg_val &= (~(0xf << 2)); - reg_val |= (0xD << 2); + reg_val &= ~EMAC_MAC_MCFG_MII_CLKD_MASK; + reg_val |= EMAC_MAC_MCFG_MII_CLKD_72; writel(reg_val, db->membase + EMAC_MAC_MCFG_REG); /* clear RX counter */ @@ -358,7 +477,7 @@ static int emac_set_mac_address(struct net_device *dev, void *p) if (netif_running(dev)) return -EBUSY; - memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); + eth_hw_addr_set(dev, addr->sa_data); writel(dev->dev_addr[0] << 16 | dev->dev_addr[1] << 8 | dev-> dev_addr[2], db->membase + EMAC_MAC_A1_REG); @@ -387,7 +506,7 @@ static void emac_init_device(struct net_device *dev) /* enable RX/TX0/RX Hlevel interrup */ reg_val = readl(db->membase + EMAC_INT_CTL_REG); - reg_val |= (0xf << 0) | (0x01 << 8); + reg_val |= (EMAC_INT_CTL_TX_EN | EMAC_INT_CTL_TX_ABRT_EN | EMAC_INT_CTL_RX_EN); writel(reg_val, db->membase + EMAC_INT_CTL_REG); spin_unlock_irqrestore(&db->lock, flags); @@ -419,7 +538,7 @@ static void emac_timeout(struct net_device *dev, unsigned int txqueue) /* Hardware start transmission. * Send a packet to media from the upper layer. */ -static int emac_start_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t emac_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct emac_board_info *db = netdev_priv(dev); unsigned long channel; @@ -427,7 +546,7 @@ static int emac_start_xmit(struct sk_buff *skb, struct net_device *dev) channel = db->tx_fifo_stat & 3; if (channel == 3) - return 1; + return NETDEV_TX_BUSY; channel = (channel == 1 ? 1 : 0); @@ -501,7 +620,6 @@ static void emac_rx(struct net_device *dev) struct sk_buff *skb; u8 *rdptr; bool good_packet; - static int rxlen_last; unsigned int reg_val; u32 rxhdr, rxstatus, rxcount, rxlen; @@ -516,26 +634,12 @@ static void emac_rx(struct net_device *dev) if (netif_msg_rx_status(db)) dev_dbg(db->dev, "RXCount: %x\n", rxcount); - if ((db->skb_last != NULL) && (rxlen_last > 0)) { - dev->stats.rx_bytes += rxlen_last; - - /* Pass to upper layer */ - db->skb_last->protocol = eth_type_trans(db->skb_last, - dev); - netif_rx(db->skb_last); - dev->stats.rx_packets++; - db->skb_last = NULL; - rxlen_last = 0; - - reg_val = readl(db->membase + EMAC_RX_CTL_REG); - reg_val &= ~EMAC_RX_CTL_DMA_EN; - writel(reg_val, db->membase + EMAC_RX_CTL_REG); - } - if (!rxcount) { db->emacrx_completed_flag = 1; reg_val = readl(db->membase + EMAC_INT_CTL_REG); - reg_val |= (0xf << 0) | (0x01 << 8); + reg_val |= (EMAC_INT_CTL_TX_EN | + EMAC_INT_CTL_TX_ABRT_EN | + EMAC_INT_CTL_RX_EN); writel(reg_val, db->membase + EMAC_INT_CTL_REG); /* had one stuck? */ @@ -567,7 +671,9 @@ static void emac_rx(struct net_device *dev) writel(reg_val | EMAC_CTL_RX_EN, db->membase + EMAC_CTL_REG); reg_val = readl(db->membase + EMAC_INT_CTL_REG); - reg_val |= (0xf << 0) | (0x01 << 8); + reg_val |= (EMAC_INT_CTL_TX_EN | + EMAC_INT_CTL_TX_ABRT_EN | + EMAC_INT_CTL_RX_EN); writel(reg_val, db->membase + EMAC_INT_CTL_REG); db->emacrx_completed_flag = 1; @@ -625,6 +731,19 @@ static void emac_rx(struct net_device *dev) if (netif_msg_rx_status(db)) dev_dbg(db->dev, "RxLen %x\n", rxlen); + if (rxlen >= dev->mtu && db->rx_chan) { + reg_val = readl(db->membase + EMAC_RX_CTL_REG); + reg_val |= EMAC_RX_CTL_DMA_EN; + writel(reg_val, db->membase + EMAC_RX_CTL_REG); + if (!emac_dma_inblk_32bit(db, skb, rdptr, rxlen)) + break; + + /* re enable cpu receive. then try to receive by emac_inblk_32bit */ + reg_val = readl(db->membase + EMAC_RX_CTL_REG); + reg_val &= ~EMAC_RX_CTL_DMA_EN; + writel(reg_val, db->membase + EMAC_RX_CTL_REG); + } + emac_inblk_32bit(db->membase + EMAC_RX_IO_DATA_REG, rdptr, rxlen); dev->stats.rx_bytes += rxlen; @@ -642,13 +761,11 @@ static irqreturn_t emac_interrupt(int irq, void *dev_id) struct net_device *dev = dev_id; struct emac_board_info *db = netdev_priv(dev); int int_status; - unsigned long flags; unsigned int reg_val; /* A real interrupt coming */ - /* holders of db->lock must always block IRQs */ - spin_lock_irqsave(&db->lock, flags); + spin_lock(&db->lock); /* Disable all interrupts */ writel(0, db->membase + EMAC_INT_CTL_REG); @@ -670,19 +787,24 @@ static irqreturn_t emac_interrupt(int irq, void *dev_id) } /* Transmit Interrupt check */ - if (int_status & (0x01 | 0x02)) + if (int_status & EMAC_INT_STA_TX_COMPLETE) emac_tx_done(dev, db, int_status); - if (int_status & (0x04 | 0x08)) + if (int_status & EMAC_INT_STA_TX_ABRT) netdev_info(dev, " ab : %x\n", int_status); /* Re-enable interrupt mask */ if (db->emacrx_completed_flag == 1) { reg_val = readl(db->membase + EMAC_INT_CTL_REG); - reg_val |= (0xf << 0) | (0x01 << 8); + reg_val |= (EMAC_INT_CTL_TX_EN | EMAC_INT_CTL_TX_ABRT_EN | EMAC_INT_CTL_RX_EN); + writel(reg_val, db->membase + EMAC_INT_CTL_REG); + } else { + reg_val = readl(db->membase + EMAC_INT_CTL_REG); + reg_val |= (EMAC_INT_CTL_TX_EN | EMAC_INT_CTL_TX_ABRT_EN); writel(reg_val, db->membase + EMAC_INT_CTL_REG); } - spin_unlock_irqrestore(&db->lock, flags); + + spin_unlock(&db->lock); return IRQ_HANDLED; } @@ -778,7 +900,7 @@ static const struct net_device_ops emac_netdev_ops = { .ndo_start_xmit = emac_start_xmit, .ndo_tx_timeout = emac_timeout, .ndo_set_rx_mode = emac_set_rx_mode, - .ndo_do_ioctl = phy_do_ioctl_running, + .ndo_eth_ioctl = phy_do_ioctl_running, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = emac_set_mac_address, #ifdef CONFIG_NET_POLL_CONTROLLER @@ -786,6 +908,58 @@ static const struct net_device_ops emac_netdev_ops = { #endif }; +static int emac_configure_dma(struct emac_board_info *db) +{ + struct platform_device *pdev = db->pdev; + struct net_device *ndev = db->ndev; + struct dma_slave_config conf = {}; + struct resource *regs; + int err = 0; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + netdev_err(ndev, "get io resource from device failed.\n"); + err = -ENOMEM; + goto out_clear_chan; + } + + netdev_info(ndev, "get io resource from device: %pa, size = %u\n", + ®s->start, (unsigned int)resource_size(regs)); + db->emac_rx_fifo = regs->start + EMAC_RX_IO_DATA_REG; + + db->rx_chan = dma_request_chan(&pdev->dev, "rx"); + if (IS_ERR(db->rx_chan)) { + netdev_err(ndev, + "failed to request dma channel. dma is disabled\n"); + err = PTR_ERR(db->rx_chan); + goto out_clear_chan; + } + + conf.direction = DMA_DEV_TO_MEM; + conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + conf.src_addr = db->emac_rx_fifo; + conf.dst_maxburst = 4; + conf.src_maxburst = 4; + conf.device_fc = false; + + err = dmaengine_slave_config(db->rx_chan, &conf); + if (err) { + netdev_err(ndev, "config dma slave failed\n"); + err = -EINVAL; + goto out_slave_configure_err; + } + + return err; + +out_slave_configure_err: + dma_release_channel(db->rx_chan); + +out_clear_chan: + db->rx_chan = NULL; + return err; +} + /* Search EMAC board, allocate space and register it */ static int emac_probe(struct platform_device *pdev) @@ -794,7 +968,6 @@ static int emac_probe(struct platform_device *pdev) struct emac_board_info *db; struct net_device *ndev; int ret = 0; - const char *mac_addr; ndev = alloc_etherdev(sizeof(struct emac_board_info)); if (!ndev) { @@ -829,16 +1002,19 @@ static int emac_probe(struct platform_device *pdev) goto out_iounmap; } + if (emac_configure_dma(db)) + netdev_info(ndev, "configure dma failed. disable dma.\n"); + db->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(db->clk)) { ret = PTR_ERR(db->clk); - goto out_iounmap; + goto out_dispose_mapping; } ret = clk_prepare_enable(db->clk); if (ret) { dev_err(&pdev->dev, "Error couldn't enable clock (%d)\n", ret); - goto out_iounmap; + goto out_dispose_mapping; } ret = sunxi_sram_claim(&pdev->dev); @@ -857,12 +1033,9 @@ static int emac_probe(struct platform_device *pdev) } /* Read MAC-address from DT */ - mac_addr = of_get_mac_address(np); - if (!IS_ERR(mac_addr)) - ether_addr_copy(ndev->dev_addr, mac_addr); - - /* Check if the MAC address is valid, if not get a random one */ - if (!is_valid_ether_addr(ndev->dev_addr)) { + ret = of_get_ethdev_address(np, ndev); + if (ret) { + /* if the MAC address is invalid get a random one */ eth_hw_addr_random(ndev); dev_warn(&pdev->dev, "using random MAC address %pM\n", ndev->dev_addr); @@ -897,6 +1070,9 @@ out_release_sram: sunxi_sram_release(&pdev->dev); out_clk_disable_unprepare: clk_disable_unprepare(db->clk); +out_dispose_mapping: + irq_dispose_mapping(ndev->irq); + dma_release_channel(db->rx_chan); out_iounmap: iounmap(db->membase); out: @@ -912,9 +1088,15 @@ static int emac_remove(struct platform_device *pdev) struct net_device *ndev = platform_get_drvdata(pdev); struct emac_board_info *db = netdev_priv(ndev); + if (db->rx_chan) { + dmaengine_terminate_all(db->rx_chan); + dma_release_channel(db->rx_chan); + } + unregister_netdev(ndev); sunxi_sram_release(&pdev->dev); clk_disable_unprepare(db->clk); + irq_dispose_mapping(ndev->irq); iounmap(db->membase); free_netdev(ndev); diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.h b/drivers/net/ethernet/allwinner/sun4i-emac.h index 38c72d9ec600..90bd9ad77607 100644 --- a/drivers/net/ethernet/allwinner/sun4i-emac.h +++ b/drivers/net/ethernet/allwinner/sun4i-emac.h @@ -38,6 +38,7 @@ #define EMAC_RX_CTL_REG (0x3c) #define EMAC_RX_CTL_AUTO_DRQ_EN (1 << 1) #define EMAC_RX_CTL_DMA_EN (1 << 2) +#define EMAC_RX_CTL_FLUSH_FIFO (1 << 3) #define EMAC_RX_CTL_PASS_ALL_EN (1 << 4) #define EMAC_RX_CTL_PASS_CTL_EN (1 << 5) #define EMAC_RX_CTL_PASS_CRC_ERR_EN (1 << 6) @@ -61,7 +62,21 @@ #define EMAC_RX_IO_DATA_STATUS_OK (1 << 7) #define EMAC_RX_FBC_REG (0x50) #define EMAC_INT_CTL_REG (0x54) +#define EMAC_INT_CTL_RX_EN (1 << 8) +#define EMAC_INT_CTL_TX0_EN (1) +#define EMAC_INT_CTL_TX1_EN (1 << 1) +#define EMAC_INT_CTL_TX_EN (EMAC_INT_CTL_TX0_EN | EMAC_INT_CTL_TX1_EN) +#define EMAC_INT_CTL_TX0_ABRT_EN (0x1 << 2) +#define EMAC_INT_CTL_TX1_ABRT_EN (0x1 << 3) +#define EMAC_INT_CTL_TX_ABRT_EN (EMAC_INT_CTL_TX0_ABRT_EN | EMAC_INT_CTL_TX1_ABRT_EN) #define EMAC_INT_STA_REG (0x58) +#define EMAC_INT_STA_TX0_COMPLETE (0x1) +#define EMAC_INT_STA_TX1_COMPLETE (0x1 << 1) +#define EMAC_INT_STA_TX_COMPLETE (EMAC_INT_STA_TX0_COMPLETE | EMAC_INT_STA_TX1_COMPLETE) +#define EMAC_INT_STA_TX0_ABRT (0x1 << 2) +#define EMAC_INT_STA_TX1_ABRT (0x1 << 3) +#define EMAC_INT_STA_TX_ABRT (EMAC_INT_STA_TX0_ABRT | EMAC_INT_STA_TX1_ABRT) +#define EMAC_INT_STA_RX_COMPLETE (0x1 << 8) #define EMAC_MAC_CTL0_REG (0x5c) #define EMAC_MAC_CTL0_RX_FLOW_CTL_EN (1 << 2) #define EMAC_MAC_CTL0_TX_FLOW_CTL_EN (1 << 3) @@ -87,8 +102,11 @@ #define EMAC_MAC_CLRT_RM (0x0f) #define EMAC_MAC_MAXF_REG (0x70) #define EMAC_MAC_SUPP_REG (0x74) +#define EMAC_MAC_SUPP_100M (0x1 << 8) #define EMAC_MAC_TEST_REG (0x78) #define EMAC_MAC_MCFG_REG (0x7c) +#define EMAC_MAC_MCFG_MII_CLKD_MASK (0xff << 2) +#define EMAC_MAC_MCFG_MII_CLKD_72 (0x0d << 2) #define EMAC_MAC_A0_REG (0x98) #define EMAC_MAC_A1_REG (0x9c) #define EMAC_MAC_A2_REG (0xa0) |