// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Actions Semi Owl SoCs Ethernet MAC driver
*
* Copyright (c) 2012 Actions Semi Inc.
* Copyright (c) 2021 Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
*/
#include <linux/circ_buf.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/etherdevice.h>
#include <linux/of_mdio.h>
#include <linux/of_net.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/reset.h>
#include "owl-emac.h"
#define OWL_EMAC_DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | \
NETIF_MSG_PROBE | \
NETIF_MSG_LINK)
static u32 owl_emac_reg_read(struct owl_emac_priv *priv, u32 reg)
{
return readl(priv->base + reg);
}
static void owl_emac_reg_write(struct owl_emac_priv *priv, u32 reg, u32 data)
{
writel(data, priv->base + reg);
}
static u32 owl_emac_reg_update(struct owl_emac_priv *priv,
u32 reg, u32 mask, u32 val)
{
u32 data, old_val;
data = owl_emac_reg_read(priv, reg);
old_val = data & mask;
data &= ~mask;
data |= val & mask;
owl_emac_reg_write(priv, reg, data);
return old_val;
}
static void owl_emac_reg_set(struct owl_emac_priv *priv, u32 reg, u32 bits)
{
owl_emac_reg_update(priv, reg, bits, bits);
}
static void owl_emac_reg_clear(struct owl_emac_priv *priv, u32 reg, u32 bits)
{
owl_emac_reg_update(priv, reg, bits, 0);
}
static struct device *owl_emac_get_dev(struct owl_emac_priv *priv)
{
return priv->netdev->dev.parent;
}
static void owl_emac_irq_enable(struct owl_emac_priv *priv)
{
/* Enable all interrupts except TU.
*
* Note the NIE and AIE bits shall also be set in order to actually
* enable the selected interrupts.
*/
owl_emac_reg_write(priv, OWL_EMAC_REG_MAC_CSR7,
OWL_EMAC_BIT_MAC_CSR7_NIE |
OWL_EMAC_BIT_MAC_CSR7_AIE |
OWL_EMAC_BIT_MAC_CSR7_ALL_NOT_TUE);
}
static void owl_emac_irq_disable(struct owl_emac_priv *priv)
{
/* Disable all interrupts.
*
* WARNING: Unset only the NIE and AIE bits in CSR7 to workaround an
* unexpected side effect (MAC hardware bug?!) where some bits in the
* status register (CSR5) are cleared automatically before being able
* to read them via owl_emac_irq_clear().
*/
owl_emac_reg_write(priv, OWL_EMAC_REG_MAC_CSR7,
OWL_EMAC_BIT_MAC_CSR7_ALL_NOT_TUE);
}
static u32 owl_emac_irq_status(struct owl_emac_priv *priv)
{
return owl_emac_reg_read(priv, OWL_EMAC_REG_MAC_CSR5);
}
static u32 owl_emac_irq_clear(struct owl_emac_priv *priv)
{
u32 val = owl_emac_irq_status(priv);
owl_emac_reg_write(priv, OWL_EMAC_REG_MAC_CSR5, val);
return val;
}
static dma_addr_t owl_emac_dma_map_rx(struct owl_emac_priv *priv,
struct sk_buff *skb)
{
struct device *dev = owl_emac_get_dev(priv);
/* Buffer pointer for the RX DMA descriptor must be word aligned. */
return dma_map_single(dev, skb_tail_pointer(skb),
skb_tailroom(skb), DMA_FROM_DEVICE);
}
static void owl_emac_dma_unmap_rx(struct owl_emac_priv *priv,
struct sk_buff *skb, dma_addr_t dma_addr)
{
struct device *dev = owl_emac_get_dev(priv);
dma_unmap_single(dev, dma_addr, skb_tailroom(skb), DMA_FROM_DEVICE);
}
static dma_addr_t owl_emac_dma_map_tx(struct owl_emac_priv *priv,
struct sk_buff *skb)
{
struct device *dev = owl_emac_get_dev(priv);
return dma_map_single(dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE);
}
static void owl_emac_dma_unmap_tx(struct owl_emac_priv *priv,
struct sk_buff *skb, dma_addr_t dma_addr)
{
struct device *dev = owl_emac_get_dev(priv);
dma_unmap_single(dev, dma_addr, skb_headlen(skb), DMA_TO_DEVICE);
}
static unsigned int owl_emac_ring_num_unused(struct owl_emac_ring *ring)
{
return CIRC_SPACE(ring->head, ring->tail, ring->size);
}
static unsigned int owl_emac_ring_get_next(struct owl_emac_ring *ring,
unsigned int cur)
{
return (cur + 1) & (ring->size - 1);
}
static void owl_emac_ring_push_head(struct owl_emac_ring *ring)
{
ring->head = owl_emac_ring_get_next(ring, ring->head);
}
static void owl_emac_ring_pop_tail(struct owl_emac_ring *ring)
{
ring->tail = owl_emac_ring_get_next(ring, ring->tail);
}
static struct sk_buff *owl_emac_alloc_skb(struct net_device *netdev)
{
struct sk_buff *skb;
int offset;
skb = netdev_alloc_skb(netdev, OWL_EMAC_RX_FRAME_MAX_LEN +
OWL_EMAC_SKB_RESERVE);
if (unlikely(!skb))
return NULL;
/* Ensure 4 bytes DMA alignment. */
offset = ((uintptr_t)skb->data) & (OWL_EMAC_SKB_ALIGN - 1);
if (unlikely(offset))
skb_reserve(skb, OWL_EMAC_SKB_ALIGN - offset);
return skb;
}
static int owl_emac_ring_prepare_rx(struct owl_emac_priv *priv)
{
struct owl_emac_ring *
|