Commit 3ef0fdb2 authored by Cyril Chemparathy's avatar Cyril Chemparathy Committed by Kevin Hilman

net: davinci_emac: switch to new cpdma layer

This patch hooks up the emac driver with the newly separated cpdma driver.

Key differences introduced here:

 - The old buffer list scheme is no longer required

 - The original code maintained mac address per rx channel, even if only one
   rx channel was being used.  With this change, mac address is maintained
   device wide.  If support for multiple rx channels is added in future, this
   will need to be reworked a bit.

 - The new CPDMA code handles short packets better than before.  The
   earlier code was adjusting the length up, without ensuring that the tail
   end of the padding was cleared - a possible security issue.  This has been
   fixed to use skb_padto().
Signed-off-by: default avatarCyril Chemparathy <cyril@ti.com>
Tested-by: default avatarMichael Williamson <michael.williamson@criticallink.com>
Tested-by: default avatarCaglar Akyuz <caglarakyuz@gmail.com>
Signed-off-by: default avatarKevin Hilman <khilman@deeprootsystems.com>
parent ef8c2dab
...@@ -63,6 +63,8 @@ ...@@ -63,6 +63,8 @@
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/page.h> #include <asm/page.h>
#include "davinci_cpdma.h"
static int debug_level; static int debug_level;
module_param(debug_level, int, 0); module_param(debug_level, int, 0);
MODULE_PARM_DESC(debug_level, "DaVinci EMAC debug level (NETIF_MSG bits)"); MODULE_PARM_DESC(debug_level, "DaVinci EMAC debug level (NETIF_MSG bits)");
...@@ -113,6 +115,7 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1"; ...@@ -113,6 +115,7 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1";
#define EMAC_DEF_MAX_FRAME_SIZE (1500 + 14 + 4 + 4) #define EMAC_DEF_MAX_FRAME_SIZE (1500 + 14 + 4 + 4)
#define EMAC_DEF_TX_CH (0) /* Default 0th channel */ #define EMAC_DEF_TX_CH (0) /* Default 0th channel */
#define EMAC_DEF_RX_CH (0) /* Default 0th channel */ #define EMAC_DEF_RX_CH (0) /* Default 0th channel */
#define EMAC_DEF_RX_NUM_DESC (128)
#define EMAC_DEF_MAX_TX_CH (1) /* Max TX channels configured */ #define EMAC_DEF_MAX_TX_CH (1) /* Max TX channels configured */
#define EMAC_DEF_MAX_RX_CH (1) /* Max RX channels configured */ #define EMAC_DEF_MAX_RX_CH (1) /* Max RX channels configured */
#define EMAC_POLL_WEIGHT (64) /* Default NAPI poll weight */ #define EMAC_POLL_WEIGHT (64) /* Default NAPI poll weight */
...@@ -460,6 +463,9 @@ struct emac_priv { ...@@ -460,6 +463,9 @@ struct emac_priv {
u32 hw_ram_addr; u32 hw_ram_addr;
struct emac_txch *txch[EMAC_DEF_MAX_TX_CH]; struct emac_txch *txch[EMAC_DEF_MAX_TX_CH];
struct emac_rxch *rxch[EMAC_DEF_MAX_RX_CH]; struct emac_rxch *rxch[EMAC_DEF_MAX_RX_CH];
struct cpdma_ctlr *dma;
struct cpdma_chan *txchan;
struct cpdma_chan *rxchan;
u32 link; /* 1=link on, 0=link off */ u32 link; /* 1=link on, 0=link off */
u32 speed; /* 0=Auto Neg, 1=No PHY, 10,100, 1000 - mbps */ u32 speed; /* 0=Auto Neg, 1=No PHY, 10,100, 1000 - mbps */
u32 duplex; /* Link duplex: 0=Half, 1=Full */ u32 duplex; /* Link duplex: 0=Half, 1=Full */
...@@ -624,6 +630,8 @@ static void emac_dump_regs(struct emac_priv *priv) ...@@ -624,6 +630,8 @@ static void emac_dump_regs(struct emac_priv *priv)
emac_read(EMAC_RXMOFOVERRUNS)); emac_read(EMAC_RXMOFOVERRUNS));
dev_info(emac_dev, "EMAC: rx_dma_overruns:%d\n", dev_info(emac_dev, "EMAC: rx_dma_overruns:%d\n",
emac_read(EMAC_RXDMAOVERRUNS)); emac_read(EMAC_RXDMAOVERRUNS));
cpdma_ctlr_dump(priv->dma);
} }
/** /**
...@@ -1151,6 +1159,70 @@ static irqreturn_t emac_irq(int irq, void *dev_id) ...@@ -1151,6 +1159,70 @@ static irqreturn_t emac_irq(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static struct sk_buff *emac_rx_alloc(struct emac_priv *priv)
{
struct sk_buff *skb = dev_alloc_skb(priv->rx_buf_size);
if (WARN_ON(!skb))
return NULL;
skb->dev = priv->ndev;
skb_reserve(skb, NET_IP_ALIGN);
return skb;
}
static void emac_rx_handler(void *token, int len, int status)
{
struct sk_buff *skb = token;
struct net_device *ndev = skb->dev;
struct emac_priv *priv = netdev_priv(ndev);
struct device *emac_dev = &ndev->dev;
int ret;
/* free and bail if we are shutting down */
if (unlikely(!netif_running(ndev))) {
dev_kfree_skb_any(skb);
return;
}
/* recycle on recieve error */
if (status < 0) {
ndev->stats.rx_errors++;
goto recycle;
}
/* feed received packet up the stack */
skb_put(skb, len);
skb->protocol = eth_type_trans(skb, ndev);
netif_receive_skb(skb);
ndev->stats.rx_bytes += len;
ndev->stats.rx_packets++;
/* alloc a new packet for receive */
skb = emac_rx_alloc(priv);
if (!skb) {
if (netif_msg_rx_err(priv) && net_ratelimit())
dev_err(emac_dev, "failed rx buffer alloc\n");
return;
}
recycle:
ret = cpdma_chan_submit(priv->rxchan, skb, skb->data,
skb_tailroom(skb), GFP_KERNEL);
if (WARN_ON(ret < 0))
dev_kfree_skb_any(skb);
}
static void emac_tx_handler(void *token, int len, int status)
{
struct sk_buff *skb = token;
struct net_device *ndev = skb->dev;
if (unlikely(netif_queue_stopped(ndev)))
netif_start_queue(ndev);
ndev->stats.tx_packets++;
ndev->stats.tx_bytes += len;
dev_kfree_skb_any(skb);
}
/** EMAC on-chip buffer descriptor memory /** EMAC on-chip buffer descriptor memory
* *
* WARNING: Please note that the on chip memory is used for both TX and RX * WARNING: Please note that the on chip memory is used for both TX and RX
...@@ -1532,42 +1604,36 @@ static int emac_dev_xmit(struct sk_buff *skb, struct net_device *ndev) ...@@ -1532,42 +1604,36 @@ static int emac_dev_xmit(struct sk_buff *skb, struct net_device *ndev)
{ {
struct device *emac_dev = &ndev->dev; struct device *emac_dev = &ndev->dev;
int ret_code; int ret_code;
struct emac_netbufobj tx_buf; /* buffer obj-only single frame support */
struct emac_netpktobj tx_packet; /* packet object */
struct emac_priv *priv = netdev_priv(ndev); struct emac_priv *priv = netdev_priv(ndev);
/* If no link, return */ /* If no link, return */
if (unlikely(!priv->link)) { if (unlikely(!priv->link)) {
if (netif_msg_tx_err(priv) && net_ratelimit()) if (netif_msg_tx_err(priv) && net_ratelimit())
dev_err(emac_dev, "DaVinci EMAC: No link to transmit"); dev_err(emac_dev, "DaVinci EMAC: No link to transmit");
return NETDEV_TX_BUSY; goto fail_tx;
} }
/* Build the buffer and packet objects - Since only single fragment is ret_code = skb_padto(skb, EMAC_DEF_MIN_ETHPKTSIZE);
* supported, need not set length and token in both packet & object. if (unlikely(ret_code < 0)) {
* Doing so for completeness sake & to show that this needs to be done
* in multifragment case
*/
tx_packet.buf_list = &tx_buf;
tx_packet.num_bufs = 1; /* only single fragment supported */
tx_packet.pkt_length = skb->len;
tx_packet.pkt_token = (void *)skb;
tx_buf.length = skb->len;
tx_buf.buf_token = (void *)skb;
tx_buf.data_ptr = skb->data;
ret_code = emac_send(priv, &tx_packet, EMAC_DEF_TX_CH);
if (unlikely(ret_code != 0)) {
if (ret_code == EMAC_ERR_TX_OUT_OF_BD) {
if (netif_msg_tx_err(priv) && net_ratelimit()) if (netif_msg_tx_err(priv) && net_ratelimit())
dev_err(emac_dev, "DaVinci EMAC: xmit() fatal"\ dev_err(emac_dev, "DaVinci EMAC: packet pad failed");
" err. Out of TX BD's"); goto fail_tx;
netif_stop_queue(priv->ndev);
} }
ndev->stats.tx_dropped++;
return NETDEV_TX_BUSY; ret_code = cpdma_chan_submit(priv->txchan, skb, skb->data, skb->len,
GFP_KERNEL);
if (unlikely(ret_code != 0)) {
if (netif_msg_tx_err(priv) && net_ratelimit())
dev_err(emac_dev, "DaVinci EMAC: desc submit failed");
goto fail_tx;
} }
return NETDEV_TX_OK; return NETDEV_TX_OK;
fail_tx:
ndev->stats.tx_dropped++;
netif_stop_queue(ndev);
return NETDEV_TX_BUSY;
} }
/** /**
...@@ -1588,13 +1654,12 @@ static void emac_dev_tx_timeout(struct net_device *ndev) ...@@ -1588,13 +1654,12 @@ static void emac_dev_tx_timeout(struct net_device *ndev)
if (netif_msg_tx_err(priv)) if (netif_msg_tx_err(priv))
dev_err(emac_dev, "DaVinci EMAC: xmit timeout, restarting TX"); dev_err(emac_dev, "DaVinci EMAC: xmit timeout, restarting TX");
emac_dump_regs(priv);
ndev->stats.tx_errors++; ndev->stats.tx_errors++;
emac_int_disable(priv); emac_int_disable(priv);
emac_stop_txch(priv, EMAC_DEF_TX_CH); cpdma_chan_stop(priv->txchan);
emac_cleanup_txch(priv, EMAC_DEF_TX_CH); cpdma_chan_start(priv->txchan);
emac_init_txch(priv, EMAC_DEF_TX_CH);
emac_write(EMAC_TXHDP(0), 0);
emac_write(EMAC_TXINTMASKSET, BIT(EMAC_DEF_TX_CH));
emac_int_enable(priv); emac_int_enable(priv);
} }
...@@ -1915,7 +1980,6 @@ static void emac_setmac(struct emac_priv *priv, u32 ch, char *mac_addr) ...@@ -1915,7 +1980,6 @@ static void emac_setmac(struct emac_priv *priv, u32 ch, char *mac_addr)
static int emac_dev_setmac_addr(struct net_device *ndev, void *addr) static int emac_dev_setmac_addr(struct net_device *ndev, void *addr)
{ {
struct emac_priv *priv = netdev_priv(ndev); struct emac_priv *priv = netdev_priv(ndev);
struct emac_rxch *rxch = priv->rxch[EMAC_DEF_RX_CH];
struct device *emac_dev = &priv->ndev->dev; struct device *emac_dev = &priv->ndev->dev;
struct sockaddr *sa = addr; struct sockaddr *sa = addr;
...@@ -1926,11 +1990,10 @@ static int emac_dev_setmac_addr(struct net_device *ndev, void *addr) ...@@ -1926,11 +1990,10 @@ static int emac_dev_setmac_addr(struct net_device *ndev, void *addr)
memcpy(priv->mac_addr, sa->sa_data, ndev->addr_len); memcpy(priv->mac_addr, sa->sa_data, ndev->addr_len);
memcpy(ndev->dev_addr, sa->sa_data, ndev->addr_len); memcpy(ndev->dev_addr, sa->sa_data, ndev->addr_len);
/* If the interface is down - rxch is NULL. */
/* MAC address is configured only after the interface is enabled. */ /* MAC address is configured only after the interface is enabled. */
if (netif_running(ndev)) { if (netif_running(ndev)) {
memcpy(rxch->mac_addr, sa->sa_data, ndev->addr_len); memcpy(priv->mac_addr, sa->sa_data, ndev->addr_len);
emac_setmac(priv, EMAC_DEF_RX_CH, rxch->mac_addr); emac_setmac(priv, EMAC_DEF_RX_CH, priv->mac_addr);
} }
if (netif_msg_drv(priv)) if (netif_msg_drv(priv))
...@@ -2139,7 +2202,7 @@ static int emac_rx_bdproc(struct emac_priv *priv, u32 ch, u32 budget) ...@@ -2139,7 +2202,7 @@ static int emac_rx_bdproc(struct emac_priv *priv, u32 ch, u32 budget)
*/ */
static int emac_hw_enable(struct emac_priv *priv) static int emac_hw_enable(struct emac_priv *priv)
{ {
u32 ch, val, mbp_enable, mac_control; u32 val, mbp_enable, mac_control;
/* Soft reset */ /* Soft reset */
emac_write(EMAC_SOFTRESET, 1); emac_write(EMAC_SOFTRESET, 1);
...@@ -2182,26 +2245,9 @@ static int emac_hw_enable(struct emac_priv *priv) ...@@ -2182,26 +2245,9 @@ static int emac_hw_enable(struct emac_priv *priv)
emac_write(EMAC_RXUNICASTCLEAR, EMAC_RX_UNICAST_CLEAR_ALL); emac_write(EMAC_RXUNICASTCLEAR, EMAC_RX_UNICAST_CLEAR_ALL);
priv->rx_addr_type = (emac_read(EMAC_MACCONFIG) >> 8) & 0xFF; priv->rx_addr_type = (emac_read(EMAC_MACCONFIG) >> 8) & 0xFF;
val = emac_read(EMAC_TXCONTROL);
val |= EMAC_TX_CONTROL_TX_ENABLE_VAL;
emac_write(EMAC_TXCONTROL, val);
val = emac_read(EMAC_RXCONTROL);
val |= EMAC_RX_CONTROL_RX_ENABLE_VAL;
emac_write(EMAC_RXCONTROL, val);
emac_write(EMAC_MACINTMASKSET, EMAC_MAC_HOST_ERR_INTMASK_VAL); emac_write(EMAC_MACINTMASKSET, EMAC_MAC_HOST_ERR_INTMASK_VAL);
for (ch = 0; ch < EMAC_DEF_MAX_TX_CH; ch++) { emac_setmac(priv, EMAC_DEF_RX_CH, priv->mac_addr);
emac_write(EMAC_TXHDP(ch), 0);
emac_write(EMAC_TXINTMASKSET, BIT(ch));
}
for (ch = 0; ch < EMAC_DEF_MAX_RX_CH; ch++) {
struct emac_rxch *rxch = priv->rxch[ch];
emac_setmac(priv, ch, rxch->mac_addr);
emac_write(EMAC_RXINTMASKSET, BIT(ch));
rxch->queue_active = 1;
emac_write(EMAC_RXHDP(ch),
emac_virt_to_phys(rxch->active_queue_head, priv));
}
/* Enable MII */ /* Enable MII */
val = emac_read(EMAC_MACCONTROL); val = emac_read(EMAC_MACCONTROL);
...@@ -2246,7 +2292,7 @@ static int emac_poll(struct napi_struct *napi, int budget) ...@@ -2246,7 +2292,7 @@ static int emac_poll(struct napi_struct *napi, int budget)
mask = EMAC_DM646X_MAC_IN_VECTOR_TX_INT_VEC; mask = EMAC_DM646X_MAC_IN_VECTOR_TX_INT_VEC;
if (status & mask) { if (status & mask) {
num_tx_pkts = emac_tx_bdproc(priv, EMAC_DEF_TX_CH, num_tx_pkts = cpdma_chan_process(priv->txchan,
EMAC_DEF_TX_MAX_SERVICE); EMAC_DEF_TX_MAX_SERVICE);
} /* TX processing */ } /* TX processing */
...@@ -2256,7 +2302,7 @@ static int emac_poll(struct napi_struct *napi, int budget) ...@@ -2256,7 +2302,7 @@ static int emac_poll(struct napi_struct *napi, int budget)
mask = EMAC_DM646X_MAC_IN_VECTOR_RX_INT_VEC; mask = EMAC_DM646X_MAC_IN_VECTOR_RX_INT_VEC;
if (status & mask) { if (status & mask) {
num_rx_pkts = emac_rx_bdproc(priv, EMAC_DEF_RX_CH, budget); num_rx_pkts = cpdma_chan_process(priv->rxchan, budget);
} /* RX processing */ } /* RX processing */
mask = EMAC_DM644X_MAC_IN_VECTOR_HOST_INT; mask = EMAC_DM644X_MAC_IN_VECTOR_HOST_INT;
...@@ -2397,9 +2443,9 @@ static int match_first_device(struct device *dev, void *data) ...@@ -2397,9 +2443,9 @@ static int match_first_device(struct device *dev, void *data)
static int emac_dev_open(struct net_device *ndev) static int emac_dev_open(struct net_device *ndev)
{ {
struct device *emac_dev = &ndev->dev; struct device *emac_dev = &ndev->dev;
u32 rc, cnt, ch; u32 cnt;
struct resource *res; struct resource *res;
int q, m; int q, m, ret;
int i = 0; int i = 0;
int k = 0; int k = 0;
struct emac_priv *priv = netdev_priv(ndev); struct emac_priv *priv = netdev_priv(ndev);
...@@ -2411,29 +2457,21 @@ static int emac_dev_open(struct net_device *ndev) ...@@ -2411,29 +2457,21 @@ static int emac_dev_open(struct net_device *ndev)
/* Configuration items */ /* Configuration items */
priv->rx_buf_size = EMAC_DEF_MAX_FRAME_SIZE + NET_IP_ALIGN; priv->rx_buf_size = EMAC_DEF_MAX_FRAME_SIZE + NET_IP_ALIGN;
/* Clear basic hardware */
for (ch = 0; ch < EMAC_MAX_TXRX_CHANNELS; ch++) {
emac_write(EMAC_TXHDP(ch), 0);
emac_write(EMAC_RXHDP(ch), 0);
emac_write(EMAC_RXHDP(ch), 0);
emac_write(EMAC_RXINTMASKCLEAR, EMAC_INT_MASK_CLEAR);
emac_write(EMAC_TXINTMASKCLEAR, EMAC_INT_MASK_CLEAR);
}
priv->mac_hash1 = 0; priv->mac_hash1 = 0;
priv->mac_hash2 = 0; priv->mac_hash2 = 0;
emac_write(EMAC_MACHASH1, 0); emac_write(EMAC_MACHASH1, 0);
emac_write(EMAC_MACHASH2, 0); emac_write(EMAC_MACHASH2, 0);
/* multi ch not supported - open 1 TX, 1RX ch by default */ for (i = 0; i < EMAC_DEF_RX_NUM_DESC; i++) {
rc = emac_init_txch(priv, EMAC_DEF_TX_CH); struct sk_buff *skb = emac_rx_alloc(priv);
if (0 != rc) {
dev_err(emac_dev, "DaVinci EMAC: emac_init_txch() failed"); if (!skb)
return rc; break;
}
rc = emac_init_rxch(priv, EMAC_DEF_RX_CH, priv->mac_addr); ret = cpdma_chan_submit(priv->rxchan, skb, skb->data,
if (0 != rc) { skb_tailroom(skb), GFP_KERNEL);
dev_err(emac_dev, "DaVinci EMAC: emac_init_rxch() failed"); if (WARN_ON(ret < 0))
return rc; break;
} }
/* Request IRQ */ /* Request IRQ */
...@@ -2458,6 +2496,8 @@ static int emac_dev_open(struct net_device *ndev) ...@@ -2458,6 +2496,8 @@ static int emac_dev_open(struct net_device *ndev)
emac_set_coalesce(ndev, &coal); emac_set_coalesce(ndev, &coal);
} }
cpdma_ctlr_start(priv->dma);
priv->phydev = NULL; priv->phydev = NULL;
/* use the first phy on the bus if pdata did not give us a phy id */ /* use the first phy on the bus if pdata did not give us a phy id */
if (!priv->phy_id) { if (!priv->phy_id) {
...@@ -2545,10 +2585,7 @@ static int emac_dev_stop(struct net_device *ndev) ...@@ -2545,10 +2585,7 @@ static int emac_dev_stop(struct net_device *ndev)
netif_carrier_off(ndev); netif_carrier_off(ndev);
emac_int_disable(priv); emac_int_disable(priv);
emac_stop_txch(priv, EMAC_DEF_TX_CH); cpdma_ctlr_stop(priv->dma);
emac_stop_rxch(priv, EMAC_DEF_RX_CH);
emac_cleanup_txch(priv, EMAC_DEF_TX_CH);
emac_cleanup_rxch(priv, EMAC_DEF_RX_CH);
emac_write(EMAC_SOFTRESET, 1); emac_write(EMAC_SOFTRESET, 1);
if (priv->phydev) if (priv->phydev)
...@@ -2653,9 +2690,10 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev) ...@@ -2653,9 +2690,10 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev)
struct resource *res; struct resource *res;
struct net_device *ndev; struct net_device *ndev;
struct emac_priv *priv; struct emac_priv *priv;
unsigned long size; unsigned long size, hw_ram_addr;
struct emac_platform_data *pdata; struct emac_platform_data *pdata;
struct device *emac_dev; struct device *emac_dev;
struct cpdma_params dma_params;
/* obtain emac clock from kernel */ /* obtain emac clock from kernel */
emac_clk = clk_get(&pdev->dev, NULL); emac_clk = clk_get(&pdev->dev, NULL);
...@@ -2731,11 +2769,40 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev) ...@@ -2731,11 +2769,40 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev)
priv->ctrl_ram_size = pdata->ctrl_ram_size; priv->ctrl_ram_size = pdata->ctrl_ram_size;
priv->emac_ctrl_ram = priv->remap_addr + pdata->ctrl_ram_offset; priv->emac_ctrl_ram = priv->remap_addr + pdata->ctrl_ram_offset;
if (pdata->hw_ram_addr) hw_ram_addr = pdata->hw_ram_addr;
priv->hw_ram_addr = pdata->hw_ram_addr; if (!hw_ram_addr)
else hw_ram_addr = (u32 __force)res->start + pdata->ctrl_ram_offset;
priv->hw_ram_addr = (u32 __force)res->start +
pdata->ctrl_ram_offset; memset(&dma_params, 0, sizeof(dma_params));
dma_params.dev = emac_dev;
dma_params.dmaregs = priv->emac_base;
dma_params.rxthresh = priv->emac_base + 0x120;
dma_params.rxfree = priv->emac_base + 0x140;
dma_params.txhdp = priv->emac_base + 0x600;
dma_params.rxhdp = priv->emac_base + 0x620;
dma_params.txcp = priv->emac_base + 0x640;
dma_params.rxcp = priv->emac_base + 0x660;
dma_params.num_chan = EMAC_MAX_TXRX_CHANNELS;
dma_params.min_packet_size = EMAC_DEF_MIN_ETHPKTSIZE;
dma_params.desc_mem_phys = hw_ram_addr;
dma_params.desc_mem_size = pdata->ctrl_ram_size;
dma_params.desc_align = 16;
priv->dma = cpdma_ctlr_create(&dma_params);
if (!priv->dma) {
dev_err(emac_dev, "DaVinci EMAC: Error initializing DMA\n");
rc = -ENOMEM;
goto no_dma;
}
priv->txchan = cpdma_chan_create(priv->dma, tx_chan_num(EMAC_DEF_TX_CH),
emac_tx_handler);
priv->rxchan = cpdma_chan_create(priv->dma, rx_chan_num(EMAC_DEF_RX_CH),
emac_rx_handler);
if (WARN_ON(!priv->txchan || !priv->rxchan)) {
rc = -ENOMEM;
goto no_irq_res;
}
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) { if (!res) {
...@@ -2778,6 +2845,12 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev) ...@@ -2778,6 +2845,12 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev)
netdev_reg_err: netdev_reg_err:
clk_disable(emac_clk); clk_disable(emac_clk);
no_irq_res: no_irq_res:
if (priv->txchan)
cpdma_chan_destroy(priv->txchan);
if (priv->rxchan)
cpdma_chan_destroy(priv->rxchan);
cpdma_ctlr_destroy(priv->dma);
no_dma:
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, res->end - res->start + 1); release_mem_region(res->start, res->end - res->start + 1);
iounmap(priv->remap_addr); iounmap(priv->remap_addr);
...@@ -2806,6 +2879,12 @@ static int __devexit davinci_emac_remove(struct platform_device *pdev) ...@@ -2806,6 +2879,12 @@ static int __devexit davinci_emac_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (priv->txchan)
cpdma_chan_destroy(priv->txchan);
if (priv->rxchan)
cpdma_chan_destroy(priv->rxchan);
cpdma_ctlr_destroy(priv->dma);
release_mem_region(res->start, res->end - res->start + 1); release_mem_region(res->start, res->end - res->start + 1);
unregister_netdev(ndev); unregister_netdev(ndev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment