// SPDX-License-Identifier: GPL-2.0+ /* * DaVinci Ethernet Medium Access Controller * * DaVinci EMAC is based upon CPPI 3.0 TI DMA engine * * Copyright (C) 2009 Texas Instruments. * * --------------------------------------------------------------------------- * History: * 0-5 A number of folks worked on this driver in bits and pieces but the major * contribution came from Suraj Iyer and Anant Gole * 6.0 Anant Gole - rewrote the driver as per Linux conventions * 6.1 Chaithrika U S - added support for Gigabit and RMII features, * PHY layer usage
*/
if (coal_intvl < EMAC_DM646X_CMINTMIN_INTVL)
coal_intvl = EMAC_DM646X_CMINTMIN_INTVL;
if (coal_intvl > EMAC_DM646X_CMINTMAX_INTVL) { /* * Interrupt pacer works with 4us Pulse, we can * throttle further by dilating the 4us pulse.
*/
addnl_dvdr = EMAC_DM646X_INTPRESCALE_MASK / prescale;
/* We get called only if link has changed (speed/duplex/status) */ if ((priv->link) && (new_duplex != cur_duplex)) {
priv->duplex = new_duplex; if (DUPLEX_FULL == priv->duplex)
mac_control |= (EMAC_MACCONTROL_FULLDUPLEXEN); else
mac_control &= ~(EMAC_MACCONTROL_FULLDUPLEXEN);
}
if (priv->speed == SPEED_1000 && (priv->version == EMAC_VERSION_2)) {
mac_control = emac_read(EMAC_MACCONTROL);
mac_control |= (EMAC_DM646X_MACCONTORL_GIG |
EMAC_DM646X_MACCONTORL_GIGFORCE);
} else { /* Clear the GIG bit and GIGFORCE bit */
mac_control &= ~(EMAC_DM646X_MACCONTORL_GIGFORCE |
EMAC_DM646X_MACCONTORL_GIG);
/* Update mac_control if changed */
emac_write(EMAC_MACCONTROL, mac_control);
if (priv->link) { /* link ON */ if (!netif_carrier_ok(ndev))
netif_carrier_on(ndev); /* reactivate the transmit queue if it is stopped */ if (netif_running(ndev) && netif_queue_stopped(ndev))
netif_wake_queue(ndev);
} else { /* link OFF */ if (netif_carrier_ok(ndev))
netif_carrier_off(ndev); if (!netif_queue_stopped(ndev))
netif_stop_queue(ndev);
}
}
/** * hash_get - Calculate hash value from mac address * @addr: mac address to delete from hash table * * Calculates hash value from mac address *
*/ static u32 hash_get(u8 *addr)
{
u32 hash;
u8 tmpval; int cnt;
hash = 0;
/** * emac_hash_add - Hash function to add mac addr from hash table * @priv: The DaVinci EMAC private adapter structure * @mac_addr: mac address to delete from hash table * * Adds mac address to the internal hash table *
*/ staticint emac_hash_add(struct emac_priv *priv, u8 *mac_addr)
{ struct device *emac_dev = &priv->ndev->dev;
u32 rc = 0;
u32 hash_bit;
u32 hash_value = hash_get(mac_addr);
if (hash_value >= EMAC_NUM_MULTICAST_BITS) { if (netif_msg_drv(priv)) {
dev_err(emac_dev, "DaVinci EMAC: emac_hash_add(): Invalid "\ "Hash %08x, should not be greater than %08x",
hash_value, (EMAC_NUM_MULTICAST_BITS - 1));
} return -1;
}
/* set the hash bit only if not previously set */ if (priv->multicast_hash_cnt[hash_value] == 0) {
rc = 1; /* hash value changed */ if (hash_value < 32) {
hash_bit = BIT(hash_value);
priv->mac_hash1 |= hash_bit;
} else {
hash_bit = BIT((hash_value - 32));
priv->mac_hash2 |= hash_bit;
}
}
/* incr counter for num of mcast addr's mapped to "this" hash bit */
++priv->multicast_hash_cnt[hash_value];
return rc;
}
/** * emac_hash_del - Hash function to delete mac addr from hash table * @priv: The DaVinci EMAC private adapter structure * @mac_addr: mac address to delete from hash table * * Removes mac address from the internal hash table *
*/ staticint emac_hash_del(struct emac_priv *priv, u8 *mac_addr)
{
u32 hash_value;
u32 hash_bit;
hash_value = hash_get(mac_addr); if (priv->multicast_hash_cnt[hash_value] > 0) { /* dec cntr for num of mcast addr's mapped to this hash bit */
--priv->multicast_hash_cnt[hash_value];
}
/* if counter still > 0, at least one multicast address refers
* to this hash bit. so return 0 */ if (priv->multicast_hash_cnt[hash_value] > 0) return 0;
/** * emac_add_mcast - Set multicast address in the EMAC adapter (Internal) * @priv: The DaVinci EMAC private adapter structure * @action: multicast operation to perform * @mac_addr: mac address to set * * Set multicast addresses in EMAC adapter - internal function *
*/ staticvoid emac_add_mcast(struct emac_priv *priv, u32 action, u8 *mac_addr)
{ struct device *emac_dev = &priv->ndev->dev; int update = -1;
switch (action) { case EMAC_MULTICAST_ADD:
update = emac_hash_add(priv, mac_addr); break; case EMAC_MULTICAST_DEL:
update = emac_hash_del(priv, mac_addr); break; case EMAC_ALL_MULTI_SET:
update = 1;
priv->mac_hash1 = EMAC_ALL_MULTI_REG_VALUE;
priv->mac_hash2 = EMAC_ALL_MULTI_REG_VALUE; break; case EMAC_ALL_MULTI_CLR:
update = 1;
priv->mac_hash1 = 0;
priv->mac_hash2 = 0;
memset(&(priv->multicast_hash_cnt[0]), 0, sizeof(priv->multicast_hash_cnt[0]) *
EMAC_NUM_MULTICAST_BITS); break; default: if (netif_msg_drv(priv))
dev_err(emac_dev, "DaVinci EMAC: add_mcast"\ ": bad operation %d", action); break;
}
/* write to the hardware only if the register status chances */ if (update > 0) {
emac_write(EMAC_MACHASH1, priv->mac_hash1);
emac_write(EMAC_MACHASH2, priv->mac_hash2);
}
}
/** * emac_dev_mcast_set - Set multicast address in the EMAC adapter * @ndev: The DaVinci EMAC network adapter * * Set multicast addresses in EMAC adapter *
*/ staticvoid emac_dev_mcast_set(struct net_device *ndev)
{
u32 mbp_enable; struct emac_priv *priv = netdev_priv(ndev);
/** * emac_int_disable - Disable EMAC module interrupt (from adapter) * @priv: The DaVinci EMAC private adapter structure * * Disable EMAC interrupt on the adapter *
*/ staticvoid emac_int_disable(struct emac_priv *priv)
{ if (priv->version == EMAC_VERSION_2) { unsignedlong flags;
local_irq_save(flags);
/* Program C0_Int_En to zero to turn off
* interrupts to the CPU */
emac_ctrl_write(EMAC_DM646X_CMRXINTEN, 0x0);
emac_ctrl_write(EMAC_DM646X_CMTXINTEN, 0x0); /* NOTE: Rx Threshold and Misc interrupts are not disabled */ if (priv->int_disable)
priv->int_disable();
/* NOTE: Rx Threshold and Misc interrupts are not enabled */
/* ack rxen only then a new pulse will be generated */
emac_write(EMAC_DM646X_MACEOIVECTOR,
EMAC_DM646X_MAC_EOI_C0_RXEN);
/* ack txen- only then a new pulse will be generated */
emac_write(EMAC_DM646X_MACEOIVECTOR,
EMAC_DM646X_MAC_EOI_C0_TXEN);
local_irq_restore(flags);
} else { /* Set DM644x control registers for interrupt control */
emac_ctrl_write(EMAC_CTRL_EWCTL, 0x0);
}
}
/** * emac_int_enable - Enable EMAC module interrupt (from adapter) * @priv: The DaVinci EMAC private adapter structure * * Enable EMAC interrupt on the adapter *
*/ staticvoid emac_int_enable(struct emac_priv *priv)
{ if (priv->version == EMAC_VERSION_2) { if (priv->int_enable)
priv->int_enable();
/* In addition to turning on interrupt Enable, we need * ack by writing appropriate values to the EOI
* register */
/* NOTE: Rx Threshold and Misc interrupts are not enabled */
} else { /* Set DM644x control registers for interrupt control */
emac_ctrl_write(EMAC_CTRL_EWCTL, 0x1);
}
}
/** * emac_irq - EMAC interrupt handler * @irq: interrupt number * @dev_id: EMAC network adapter data structure ptr * * EMAC Interrupt handler - we only schedule NAPI and not process any packets * here. EVen the interrupt status is checked (TX/RX/Err) in NAPI poll function * * Returns interrupt handled condition
*/ static irqreturn_t emac_irq(int irq, void *dev_id)
{ struct net_device *ndev = (struct net_device *)dev_id; struct emac_priv *priv = netdev_priv(ndev);
++priv->isr_count; if (likely(netif_running(priv->ndev))) {
emac_int_disable(priv);
napi_schedule(&priv->napi);
} else { /* we are closing down, so dont process anything */
} return IRQ_HANDLED;
}
staticvoid 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 receive 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), 0);
WARN_ON(ret == -ENOMEM); if (unlikely(ret < 0))
dev_kfree_skb_any(skb);
}
staticvoid emac_tx_handler(void *token, int len, int status)
{ struct sk_buff *skb = token; struct net_device *ndev = skb->dev;
/* Check whether the queue is stopped due to stalled tx dma, if the * queue is stopped then start the queue as we have free desc for tx
*/ if (unlikely(netif_queue_stopped(ndev)))
netif_wake_queue(ndev);
ndev->stats.tx_packets++;
ndev->stats.tx_bytes += len;
dev_kfree_skb_any(skb);
}
/** * emac_dev_xmit - EMAC Transmit function * @skb: SKB pointer * @ndev: The DaVinci EMAC network adapter * * Called by the system to transmit a packet - we queue the packet in * EMAC hardware transmit queue * * Returns success(NETDEV_TX_OK) or error code (typically out of desc's)
*/ static netdev_tx_t emac_dev_xmit(struct sk_buff *skb, struct net_device *ndev)
{ struct device *emac_dev = &ndev->dev; int ret_code; struct emac_priv *priv = netdev_priv(ndev);
/* If no link, return */ if (unlikely(!priv->link)) { if (netif_msg_tx_err(priv) && net_ratelimit())
dev_err(emac_dev, "DaVinci EMAC: No link to transmit"); goto fail_tx;
}
ret_code = skb_put_padto(skb, EMAC_DEF_MIN_ETHPKTSIZE); if (unlikely(ret_code < 0)) { if (netif_msg_tx_err(priv) && net_ratelimit())
dev_err(emac_dev, "DaVinci EMAC: packet pad failed"); goto fail_tx;
}
/* If there is no more tx desc left free then we need to * tell the kernel to stop sending us tx frames.
*/ if (unlikely(!cpdma_check_free_tx_desc(priv->txchan)))
netif_stop_queue(ndev);
/** * emac_dev_tx_timeout - EMAC Transmit timeout function * @ndev: The DaVinci EMAC network adapter * @txqueue: the index of the hung transmit queue * * Called when system detects that a skb timeout period has expired * potentially due to a fault in the adapter in not being able to send * it out on the wire. We teardown the TX channel assuming a hardware * error and re-initialize the TX channel for hardware operation *
*/ staticvoid emac_dev_tx_timeout(struct net_device *ndev, unsignedint txqueue)
{ struct emac_priv *priv = netdev_priv(ndev); struct device *emac_dev = &ndev->dev;
if (netif_msg_tx_err(priv))
dev_err(emac_dev, "DaVinci EMAC: xmit timeout, restarting TX");
/** * emac_set_type0addr - Set EMAC Type0 mac address * @priv: The DaVinci EMAC private adapter structure * @ch: RX channel number * @mac_addr: MAC address to set in device * * Called internally to set Type0 mac address of the adapter (Device) * * Returns success (0) or appropriate error code (none as of now)
*/ staticvoid emac_set_type0addr(struct emac_priv *priv, u32 ch, char *mac_addr)
{
u32 val;
val = ((mac_addr[5] << 8) | (mac_addr[4]));
emac_write(EMAC_MACSRCADDRLO, val);
val = ((mac_addr[3] << 24) | (mac_addr[2] << 16) | \
(mac_addr[1] << 8) | (mac_addr[0]));
emac_write(EMAC_MACSRCADDRHI, val);
val = emac_read(EMAC_RXUNICASTSET);
val |= BIT(ch);
emac_write(EMAC_RXUNICASTSET, val);
val = emac_read(EMAC_RXUNICASTCLEAR);
val &= ~BIT(ch);
emac_write(EMAC_RXUNICASTCLEAR, val);
}
/** * emac_set_type1addr - Set EMAC Type1 mac address * @priv: The DaVinci EMAC private adapter structure * @ch: RX channel number * @mac_addr: MAC address to set in device * * Called internally to set Type1 mac address of the adapter (Device) * * Returns success (0) or appropriate error code (none as of now)
*/ staticvoid emac_set_type1addr(struct emac_priv *priv, u32 ch, char *mac_addr)
{
u32 val;
emac_write(EMAC_MACINDEX, ch);
val = ((mac_addr[5] << 8) | mac_addr[4]);
emac_write(EMAC_MACADDRLO, val);
val = ((mac_addr[3] << 24) | (mac_addr[2] << 16) | \
(mac_addr[1] << 8) | (mac_addr[0]));
emac_write(EMAC_MACADDRHI, val);
emac_set_type0addr(priv, ch, mac_addr);
}
/** * emac_set_type2addr - Set EMAC Type2 mac address * @priv: The DaVinci EMAC private adapter structure * @ch: RX channel number * @mac_addr: MAC address to set in device * @index: index into RX address entries * @match: match parameter for RX address matching logic * * Called internally to set Type2 mac address of the adapter (Device) * * Returns success (0) or appropriate error code (none as of now)
*/ staticvoid emac_set_type2addr(struct emac_priv *priv, u32 ch, char *mac_addr, int index, int match)
{
u32 val;
emac_write(EMAC_MACINDEX, index);
val = ((mac_addr[3] << 24) | (mac_addr[2] << 16) | \
(mac_addr[1] << 8) | (mac_addr[0]));
emac_write(EMAC_MACADDRHI, val);
val = ((mac_addr[5] << 8) | mac_addr[4] | ((ch & 0x7) << 16) | \
(match << 19) | BIT(20));
emac_write(EMAC_MACADDRLO, val);
emac_set_type0addr(priv, ch, mac_addr);
}
/** * emac_setmac - Set mac address in the adapter (internal function) * @priv: The DaVinci EMAC private adapter structure * @ch: RX channel number * @mac_addr: MAC address to set in device * * Called internally to set the mac address of the adapter (Device) * * Returns success (0) or appropriate error code (none as of now)
*/ staticvoid emac_setmac(struct emac_priv *priv, u32 ch, char *mac_addr)
{ struct device *emac_dev = &priv->ndev->dev;
/** * emac_dev_setmac_addr - Set mac address in the adapter * @ndev: The DaVinci EMAC network adapter * @addr: MAC address to set in device * * Called by the system to set the mac address of the adapter (Device) * * Returns success (0) or appropriate error code (none as of now)
*/ staticint emac_dev_setmac_addr(struct net_device *ndev, void *addr)
{ struct emac_priv *priv = netdev_priv(ndev); struct device *emac_dev = &priv->ndev->dev; struct sockaddr *sa = addr;
if (!is_valid_ether_addr(sa->sa_data)) return -EADDRNOTAVAIL;
/* Store mac addr in priv and rx channel and set it in EMAC hw */
memcpy(priv->mac_addr, sa->sa_data, ndev->addr_len);
eth_hw_addr_set(ndev, sa->sa_data);
/* MAC address is configured only after the interface is enabled. */ if (netif_running(ndev)) {
emac_setmac(priv, EMAC_DEF_RX_CH, priv->mac_addr);
}
if (netif_msg_drv(priv))
dev_notice(emac_dev, "DaVinci EMAC: emac_dev_setmac_addr %pM\n",
priv->mac_addr);
return 0;
}
/** * emac_hw_enable - Enable EMAC hardware for packet transmission/reception * @priv: The DaVinci EMAC private adapter structure * * Enables EMAC hardware for packet processing - enables PHY, enables RX * for packet reception and enables device interrupts and then NAPI * * Returns success (0) or appropriate error code (none right now)
*/ staticint emac_hw_enable(struct emac_priv *priv)
{
u32 val, mbp_enable, mac_control;
/* Soft reset */
emac_write(EMAC_SOFTRESET, 1); while (emac_read(EMAC_SOFTRESET))
cpu_relax();
/* Disable interrupt & Set pacing for more interrupts initially */
emac_int_disable(priv);
/* Full duplex enable bit set when auto negotiation happens */
mac_control =
(((EMAC_DEF_TXPRIO_FIXED) ? (EMAC_MACCONTROL_TXPTYPE) : 0x0) |
((priv->speed == 1000) ? EMAC_MACCONTROL_GIGABITEN : 0x0) |
((EMAC_DEF_TXPACING_EN) ? (EMAC_MACCONTROL_TXPACEEN) : 0x0) |
((priv->duplex == DUPLEX_FULL) ? 0x1 : 0));
emac_write(EMAC_MACCONTROL, mac_control);
/* Enable MII */
val = emac_read(EMAC_MACCONTROL);
val |= (EMAC_MACCONTROL_GMIIEN);
emac_write(EMAC_MACCONTROL, val);
/* Enable NAPI and interrupts */
napi_enable(&priv->napi);
emac_int_enable(priv); return 0;
}
/** * emac_poll - EMAC NAPI Poll function * @napi: pointer to the napi_struct containing The DaVinci EMAC network adapter * @budget: Number of receive packets to process (as told by NAPI layer) * * NAPI Poll function implemented to process packets as per budget. We check * the type of interrupt on the device and accordingly call the TX or RX * packet processing functions. We follow the budget for RX processing and * also put a cap on number of TX pkts processed through config param. The * NAPI schedule function is called if more packets pending. * * Returns number of packets received (in most cases; else TX pkts - rarely)
*/ staticint emac_poll(struct napi_struct *napi, int budget)
{ unsignedint mask; struct emac_priv *priv = container_of(napi, struct emac_priv, napi); struct net_device *ndev = priv->ndev; struct device *emac_dev = &ndev->dev;
u32 status = 0;
u32 num_rx_pkts = 0;
/* Check interrupt vectors and call packet processing */
status = emac_read(EMAC_MACINVECTOR);
mask = EMAC_DM644X_MAC_IN_VECTOR_TX_INT_VEC;
if (priv->version == EMAC_VERSION_2)
mask = EMAC_DM646X_MAC_IN_VECTOR_TX_INT_VEC;
/************************************************************************* * Linux Driver Model
*************************************************************************/
/** * emac_dev_open - EMAC device open * @ndev: The DaVinci EMAC network adapter * * Called when system wants to start the interface. We init TX/RX channels * and enable the hardware for packet reception/transmission and start the * network queue. * * Returns 0 for a successful open, or appropriate error code
*/ staticint emac_dev_open(struct net_device *ndev)
{ struct device *emac_dev = &ndev->dev; struct resource *res; int q, m, ret; int res_num = 0, irq_num = 0; int i = 0; struct emac_priv *priv = netdev_priv(ndev); struct phy_device *phydev = NULL; struct device *phy = NULL;
ret = pm_runtime_resume_and_get(&priv->pdev->dev); if (ret < 0) {
dev_err(&priv->pdev->dev, "%s: failed to get_sync(%d)\n",
__func__, ret); return ret;
}
if (priv->phy_node) {
phydev = of_phy_connect(ndev, priv->phy_node,
&emac_adjust_link, 0, 0); if (!phydev) {
dev_err(emac_dev, "could not connect to phy %pOF\n",
priv->phy_node);
ret = -ENODEV; goto err;
}
}
/* use the first phy on the bus if pdata did not give us a phy id */ if (!phydev && !priv->phy_id) { /* NOTE: we can't use bus_find_device_by_name() here because * the device name is not guaranteed to be 'davinci_mdio'. On * some systems it can be 'davinci_mdio.0' so we need to use * strncmp() against the first part of the string to correctly * match it.
*/
phy = bus_find_device(&mdio_bus_type, NULL, NULL,
match_first_device); if (phy) {
priv->phy_id = dev_name(phy); if (!priv->phy_id || !*priv->phy_id)
put_device(phy);
}
}
if (!phydev && priv->phy_id && *priv->phy_id) {
phydev = phy_connect(ndev, priv->phy_id,
&emac_adjust_link,
PHY_INTERFACE_MODE_MII);
put_device(phy); /* reference taken by bus_find_device */ if (IS_ERR(phydev)) {
dev_err(emac_dev, "could not connect to phy %s\n",
priv->phy_id);
ret = PTR_ERR(phydev); goto err;
}
rollback: if (dev_of_node(&priv->pdev->dev)) { for (q = res_num - 1; q >= 0; q--) {
irq_num = platform_get_irq(priv->pdev, q); if (irq_num > 0)
free_irq(irq_num, ndev);
}
} else { for (q = res_num; q >= 0; q--) {
res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, q); /* at the first iteration, irq_num is already set to the * right value
*/ if (q != res_num)
irq_num = res->end;
for (m = irq_num; m >= res->start; m--)
free_irq(m, ndev);
}
}
cpdma_ctlr_stop(priv->dma);
pm_runtime_put(&priv->pdev->dev); return ret;
}
/** * emac_dev_stop - EMAC device stop * @ndev: The DaVinci EMAC network adapter * * Called when system wants to stop or down the interface. We stop the network * queue, disable interrupts and cleanup TX/RX channels. * * We return the statistics in net_device_stats structure pulled from emac
*/ staticint emac_dev_stop(struct net_device *ndev)
{ struct resource *res; int i = 0; int irq_num; struct emac_priv *priv = netdev_priv(ndev); struct device *emac_dev = &ndev->dev; int ret = 0;
/* inform the upper layers. */
netif_stop_queue(ndev);
napi_disable(&priv->napi);
/* Free IRQ */ if (dev_of_node(&priv->pdev->dev)) { do {
ret = platform_get_irq_optional(priv->pdev, i); if (ret < 0 && ret != -ENXIO) break; if (ret > 0) {
free_irq(ret, priv->ndev);
} else {
ret = 0; break;
}
} while (++i);
} else { while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, i))) { for (irq_num = res->start; irq_num <= res->end; irq_num++)
free_irq(irq_num, priv->ndev);
i++;
}
}
if (netif_msg_drv(priv))
dev_notice(emac_dev, "DaVinci EMAC: %s stopped\n", ndev->name);
pm_runtime_put(&priv->pdev->dev); return ret;
}
/** * emac_dev_getnetstats - EMAC get statistics function * @ndev: The DaVinci EMAC network adapter * * Called when system wants to get statistics from the device. * * We return the statistics in net_device_stats structure pulled from emac
*/ staticstruct net_device_stats *emac_dev_getnetstats(struct net_device *ndev)
{ struct emac_priv *priv = netdev_priv(ndev);
u32 mac_control;
u32 stats_clear_mask; int err;
err = pm_runtime_resume_and_get(&priv->pdev->dev); if (err < 0) {
dev_err(&priv->pdev->dev, "%s: failed to get_sync(%d)\n",
__func__, err); return &ndev->stats;
}
/* update emac hardware stats and reset the registers*/
/** * davinci_emac_probe - EMAC device probe * @pdev: The DaVinci EMAC device that we are removing * * Called when probing for emac devicesr. We get details of instances and * resource information from platform init and register a network device * and allocate resources necessary for driver to perform
*/ staticint davinci_emac_probe(struct platform_device *pdev)
{ struct device_node *np = pdev->dev.of_node; int rc = 0; struct resource *res, *res_ctrl; struct net_device *ndev; struct emac_priv *priv; unsignedlong hw_ram_addr; struct emac_platform_data *pdata; struct cpdma_params dma_params; struct clk *emac_clk; unsignedlong emac_bus_frequency;
/* obtain emac clock from kernel */
emac_clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(emac_clk)) {
dev_err(&pdev->dev, "failed to get EMAC clock\n"); return -EBUSY;
}
emac_bus_frequency = clk_get_rate(emac_clk);
devm_clk_put(&pdev->dev, emac_clk);
/* TODO: Probe PHY here if possible */
ndev = alloc_etherdev(sizeof(struct emac_priv)); if (!ndev) return -ENOMEM;
/* If the MAC address is not present, read the registers from the SoC */ if (!is_valid_ether_addr(priv->mac_addr)) {
rc = davinci_emac_try_get_mac(pdev, res_ctrl ? 0 : 1, priv->mac_addr); if (!rc)
eth_hw_addr_set(ndev, priv->mac_addr);
if (!is_valid_ether_addr(priv->mac_addr)) { /* Use random MAC if still none obtained. */
eth_hw_addr_random(ndev);
memcpy(priv->mac_addr, ndev->dev_addr, ndev->addr_len);
dev_warn(&pdev->dev, "using random MAC addr: %pM\n",
priv->mac_addr);
}
}
ndev->netdev_ops = &emac_netdev_ops;
ndev->ethtool_ops = ðtool_ops;
netif_napi_add(ndev, &priv->napi, emac_poll);
pm_runtime_enable(&pdev->dev);
rc = pm_runtime_resume_and_get(&pdev->dev); if (rc < 0) {
dev_err(&pdev->dev, "%s: failed to get_sync(%d)\n",
__func__, rc); goto err_napi_del;
}
/* register the network device */
SET_NETDEV_DEV(ndev, &pdev->dev);
rc = register_netdev(ndev); if (rc) {
dev_err(&pdev->dev, "error in register_netdev\n");
rc = -ENODEV;
pm_runtime_put(&pdev->dev); goto err_napi_del;
}
if (netif_msg_probe(priv)) {
dev_notice(&pdev->dev, "DaVinci EMAC Probe found device " "(regs: %pa, irq: %d)\n",
&priv->emac_base_phys, ndev->irq);
}
pm_runtime_put(&pdev->dev);
/** * davinci_emac_remove - EMAC device remove * @pdev: The DaVinci EMAC device that we are removing * * Called when removing the device driver. We disable clock usage and release * the resources taken up by the driver and unregister network device
*/ staticvoid davinci_emac_remove(struct platform_device *pdev)
{ struct net_device *ndev = platform_get_drvdata(pdev); struct emac_priv *priv = netdev_priv(ndev); struct device_node *np = pdev->dev.of_node;
/** * davinci_emac_init - EMAC driver module init * * Called when initializing the driver. We register the driver with * the platform.
*/ staticint __init davinci_emac_init(void)
{ return platform_driver_register(&davinci_emac_driver);
}
late_initcall(davinci_emac_init);
/** * davinci_emac_exit - EMAC driver module exit * * Called when exiting the driver completely. We unregister the driver with * the platform and exit
*/ staticvoid __exit davinci_emac_exit(void)
{
platform_driver_unregister(&davinci_emac_driver);
}
module_exit(davinci_emac_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("DaVinci EMAC Maintainer: Anant Gole <anantgole@ti.com>");
MODULE_AUTHOR("DaVinci EMAC Maintainer: Chaithrika U S <chaithrika@ti.com>");
MODULE_DESCRIPTION("DaVinci EMAC Ethernet driver");
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.31 Sekunden
(vorverarbeitet am 2026-04-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.