Commit b0d3969d authored by Niklas Söderlund's avatar Niklas Söderlund Committed by David S. Miller

net: ethernet: rtsn: Add support for Renesas Ethernet-TSN

Add initial support for Renesas Ethernet-TSN End-station device of R-Car
V4H. The Ethernet End-station can connect to an Ethernet network using a
10 Mbps, 100 Mbps, or 1 Gbps full-duplex link via MII/GMII/RMII/RGMII.
Depending on the connected PHY.

The driver supports Rx checksum and offload and hardware timestamps.

While full power management and suspend/resume is not yet supported the
driver enables runtime PM in order to enable the module clock. While
explicit clock management using clk_enable() would suffice for the
supported SoC, the module could be reused on SoCs where the module is
part of a power domain.
Signed-off-by: default avatarNiklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d7527fe9
...@@ -19032,6 +19032,14 @@ F: drivers/net/ethernet/renesas/Makefile ...@@ -19032,6 +19032,14 @@ F: drivers/net/ethernet/renesas/Makefile
F: drivers/net/ethernet/renesas/rcar_gen4* F: drivers/net/ethernet/renesas/rcar_gen4*
F: drivers/net/ethernet/renesas/rswitch* F: drivers/net/ethernet/renesas/rswitch*
RENESAS ETHERNET TSN DRIVER
M: Niklas Söderlund <niklas.soderlund@ragnatech.se>
L: netdev@vger.kernel.org
L: linux-renesas-soc@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/net/renesas,ethertsn.yaml
F: drivers/net/ethernet/renesas/rtsn.*
RENESAS IDT821034 ASoC CODEC RENESAS IDT821034 ASoC CODEC
M: Herve Codina <herve.codina@bootlin.com> M: Herve Codina <herve.codina@bootlin.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: alsa-devel@alsa-project.org (moderated for non-subscribers)
......
...@@ -59,4 +59,14 @@ config RENESAS_GEN4_PTP ...@@ -59,4 +59,14 @@ config RENESAS_GEN4_PTP
help help
Renesas R-Car Gen4 gPTP device driver. Renesas R-Car Gen4 gPTP device driver.
config RTSN
tristate "Renesas Ethernet-TSN support"
depends on ARCH_RENESAS || COMPILE_TEST
depends on PTP_1588_CLOCK
select CRC32
select PHYLIB
select RENESAS_GEN4_PTP
help
Renesas Ethernet-TSN device driver.
endif # NET_VENDOR_RENESAS endif # NET_VENDOR_RENESAS
...@@ -11,3 +11,5 @@ obj-$(CONFIG_RAVB) += ravb.o ...@@ -11,3 +11,5 @@ obj-$(CONFIG_RAVB) += ravb.o
obj-$(CONFIG_RENESAS_ETHER_SWITCH) += rswitch.o obj-$(CONFIG_RENESAS_ETHER_SWITCH) += rswitch.o
obj-$(CONFIG_RENESAS_GEN4_PTP) += rcar_gen4_ptp.o obj-$(CONFIG_RENESAS_GEN4_PTP) += rcar_gen4_ptp.o
obj-$(CONFIG_RTSN) += rtsn.o
// SPDX-License-Identifier: GPL-2.0
/* Renesas Ethernet-TSN device driver
*
* Copyright (C) 2022 Renesas Electronics Corporation
* Copyright (C) 2023 Niklas Söderlund <niklas.soderlund@ragnatech.se>
*/
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/module.h>
#include <linux/net_tstamp.h>
#include <linux/of.h>
#include <linux/of_mdio.h>
#include <linux/of_net.h>
#include <linux/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/spinlock.h>
#include "rtsn.h"
#include "rcar_gen4_ptp.h"
struct rtsn_private {
struct net_device *ndev;
struct platform_device *pdev;
void __iomem *base;
struct rcar_gen4_ptp_private *ptp_priv;
struct clk *clk;
struct reset_control *reset;
u32 num_tx_ring;
u32 num_rx_ring;
u32 tx_desc_bat_size;
dma_addr_t tx_desc_bat_dma;
struct rtsn_desc *tx_desc_bat;
u32 rx_desc_bat_size;
dma_addr_t rx_desc_bat_dma;
struct rtsn_desc *rx_desc_bat;
dma_addr_t tx_desc_dma;
dma_addr_t rx_desc_dma;
struct rtsn_ext_desc *tx_ring;
struct rtsn_ext_ts_desc *rx_ring;
struct sk_buff **tx_skb;
struct sk_buff **rx_skb;
spinlock_t lock; /* Register access lock */
u32 cur_tx;
u32 dirty_tx;
u32 cur_rx;
u32 dirty_rx;
u8 ts_tag;
struct napi_struct napi;
struct rtnl_link_stats64 stats;
struct mii_bus *mii;
phy_interface_t iface;
int link;
int speed;
int tx_data_irq;
int rx_data_irq;
};
static u32 rtsn_read(struct rtsn_private *priv, enum rtsn_reg reg)
{
return ioread32(priv->base + reg);
}
static void rtsn_write(struct rtsn_private *priv, enum rtsn_reg reg, u32 data)
{
iowrite32(data, priv->base + reg);
}
static void rtsn_modify(struct rtsn_private *priv, enum rtsn_reg reg,
u32 clear, u32 set)
{
rtsn_write(priv, reg, (rtsn_read(priv, reg) & ~clear) | set);
}
static int rtsn_reg_wait(struct rtsn_private *priv, enum rtsn_reg reg,
u32 mask, u32 expected)
{
u32 val;
return readl_poll_timeout(priv->base + reg, val,
(val & mask) == expected,
RTSN_INTERVAL_US, RTSN_TIMEOUT_US);
}
static void rtsn_ctrl_data_irq(struct rtsn_private *priv, bool enable)
{
if (enable) {
rtsn_write(priv, TDIE0, TDIE_TDID_TDX(TX_CHAIN_IDX));
rtsn_write(priv, RDIE0, RDIE_RDID_RDX(RX_CHAIN_IDX));
} else {
rtsn_write(priv, TDID0, TDIE_TDID_TDX(TX_CHAIN_IDX));
rtsn_write(priv, RDID0, RDIE_RDID_RDX(RX_CHAIN_IDX));
}
}
static void rtsn_get_timestamp(struct rtsn_private *priv, struct timespec64 *ts)
{
struct rcar_gen4_ptp_private *ptp_priv = priv->ptp_priv;
ptp_priv->info.gettime64(&ptp_priv->info, ts);
}
static int rtsn_tx_free(struct net_device *ndev, bool free_txed_only)
{
struct rtsn_private *priv = netdev_priv(ndev);
struct rtsn_ext_desc *desc;
struct sk_buff *skb;
int free_num = 0;
int entry, size;
for (; priv->cur_tx - priv->dirty_tx > 0; priv->dirty_tx++) {
entry = priv->dirty_tx % priv->num_tx_ring;
desc = &priv->tx_ring[entry];
if (free_txed_only && (desc->die_dt & DT_MASK) != DT_FEMPTY)
break;
dma_rmb();
size = le16_to_cpu(desc->info_ds) & TX_DS;
skb = priv->tx_skb[entry];
if (skb) {
if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) {
struct skb_shared_hwtstamps shhwtstamps;
struct timespec64 ts;
rtsn_get_timestamp(priv, &ts);
memset(&shhwtstamps, 0, sizeof(shhwtstamps));
shhwtstamps.hwtstamp = timespec64_to_ktime(ts);
skb_tstamp_tx(skb, &shhwtstamps);
}
dma_unmap_single(ndev->dev.parent,
le32_to_cpu(desc->dptr),
size, DMA_TO_DEVICE);
dev_kfree_skb_any(priv->tx_skb[entry]);
free_num++;
priv->stats.tx_packets++;
priv->stats.tx_bytes += size;
}
desc->die_dt = DT_EEMPTY;
}
desc = &priv->tx_ring[priv->num_tx_ring];
desc->die_dt = DT_LINK;
return free_num;
}
static int rtsn_rx(struct net_device *ndev, int budget)
{
struct rtsn_private *priv = netdev_priv(ndev);
unsigned int ndescriptors;
unsigned int rx_packets;
unsigned int i;
bool get_ts;
get_ts = priv->ptp_priv->tstamp_rx_ctrl &
RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT;
ndescriptors = priv->dirty_rx + priv->num_rx_ring - priv->cur_rx;
rx_packets = 0;
for (i = 0; i < ndescriptors; i++) {
const unsigned int entry = priv->cur_rx % priv->num_rx_ring;
struct rtsn_ext_ts_desc *desc = &priv->rx_ring[entry];
struct sk_buff *skb;
dma_addr_t dma_addr;
u16 pkt_len;
/* Stop processing descriptors if budget is consumed. */
if (rx_packets >= budget)
break;
/* Stop processing descriptors on first empty. */
if ((desc->die_dt & DT_MASK) == DT_FEMPTY)
break;
dma_rmb();
pkt_len = le16_to_cpu(desc->info_ds) & RX_DS;
skb = priv->rx_skb[entry];
priv->rx_skb[entry] = NULL;
dma_addr = le32_to_cpu(desc->dptr);
dma_unmap_single(ndev->dev.parent, dma_addr, PKT_BUF_SZ,
DMA_FROM_DEVICE);
/* Get timestamp if enabled. */
if (get_ts) {
struct skb_shared_hwtstamps *shhwtstamps;
struct timespec64 ts;
shhwtstamps = skb_hwtstamps(skb);
memset(shhwtstamps, 0, sizeof(*shhwtstamps));
ts.tv_sec = (u64)le32_to_cpu(desc->ts_sec);
ts.tv_nsec = le32_to_cpu(desc->ts_nsec & cpu_to_le32(0x3fffffff));
shhwtstamps->hwtstamp = timespec64_to_ktime(ts);
}
skb_put(skb, pkt_len);
skb->protocol = eth_type_trans(skb, ndev);
napi_gro_receive(&priv->napi, skb);
/* Update statistics. */
priv->stats.rx_packets++;
priv->stats.rx_bytes += pkt_len;
/* Update counters. */
priv->cur_rx++;
rx_packets++;
}
/* Refill the RX ring buffers */
for (; priv->cur_rx - priv->dirty_rx > 0; priv->dirty_rx++) {
const unsigned int entry = priv->dirty_rx % priv->num_rx_ring;
struct rtsn_ext_ts_desc *desc = &priv->rx_ring[entry];
struct sk_buff *skb;
dma_addr_t dma_addr;
desc->info_ds = cpu_to_le16(PKT_BUF_SZ);
if (!priv->rx_skb[entry]) {
skb = napi_alloc_skb(&priv->napi,
PKT_BUF_SZ + RTSN_ALIGN - 1);
if (!skb)
break;
skb_reserve(skb, NET_IP_ALIGN);
dma_addr = dma_map_single(ndev->dev.parent, skb->data,
le16_to_cpu(desc->info_ds),
DMA_FROM_DEVICE);
if (dma_mapping_error(ndev->dev.parent, dma_addr))
desc->info_ds = cpu_to_le16(0);
desc->dptr = cpu_to_le32(dma_addr);
skb_checksum_none_assert(skb);
priv->rx_skb[entry] = skb;
}
dma_wmb();
desc->die_dt = DT_FEMPTY | D_DIE;
}
priv->rx_ring[priv->num_rx_ring].die_dt = DT_LINK;
return rx_packets;
}
static int rtsn_poll(struct napi_struct *napi, int budget)
{
struct rtsn_private *priv;
struct net_device *ndev;
unsigned long flags;
int work_done;
ndev = napi->dev;
priv = netdev_priv(ndev);
/* Processing RX Descriptor Ring */
work_done = rtsn_rx(ndev, budget);
/* Processing TX Descriptor Ring */
spin_lock_irqsave(&priv->lock, flags);
rtsn_tx_free(ndev, true);
netif_wake_subqueue(ndev, 0);
spin_unlock_irqrestore(&priv->lock, flags);
/* Re-enable TX/RX interrupts */
if (work_done < budget && napi_complete_done(napi, work_done)) {
spin_lock_irqsave(&priv->lock, flags);
rtsn_ctrl_data_irq(priv, true);
spin_unlock_irqrestore(&priv->lock, flags);
}
return work_done;
}
static int rtsn_desc_alloc(struct rtsn_private *priv)
{
struct device *dev = &priv->pdev->dev;
unsigned int i;
priv->tx_desc_bat_size = sizeof(struct rtsn_desc) * TX_NUM_CHAINS;
priv->tx_desc_bat = dma_alloc_coherent(dev, priv->tx_desc_bat_size,
&priv->tx_desc_bat_dma,
GFP_KERNEL);
if (!priv->tx_desc_bat)
return -ENOMEM;
for (i = 0; i < TX_NUM_CHAINS; i++)
priv->tx_desc_bat[i].die_dt = DT_EOS;
priv->rx_desc_bat_size = sizeof(struct rtsn_desc) * RX_NUM_CHAINS;
priv->rx_desc_bat = dma_alloc_coherent(dev, priv->rx_desc_bat_size,
&priv->rx_desc_bat_dma,
GFP_KERNEL);
if (!priv->rx_desc_bat)
return -ENOMEM;
for (i = 0; i < RX_NUM_CHAINS; i++)
priv->rx_desc_bat[i].die_dt = DT_EOS;
return 0;
}
static void rtsn_desc_free(struct rtsn_private *priv)
{
if (priv->tx_desc_bat)
dma_free_coherent(&priv->pdev->dev, priv->tx_desc_bat_size,
priv->tx_desc_bat, priv->tx_desc_bat_dma);
priv->tx_desc_bat = NULL;
if (priv->rx_desc_bat)
dma_free_coherent(&priv->pdev->dev, priv->rx_desc_bat_size,
priv->rx_desc_bat, priv->rx_desc_bat_dma);
priv->rx_desc_bat = NULL;
}
static void rtsn_chain_free(struct rtsn_private *priv)
{
struct device *dev = &priv->pdev->dev;
dma_free_coherent(dev,
sizeof(struct rtsn_ext_desc) * (priv->num_tx_ring + 1),
priv->tx_ring, priv->tx_desc_dma);
priv->tx_ring = NULL;
dma_free_coherent(dev,
sizeof(struct rtsn_ext_ts_desc) * (priv->num_rx_ring + 1),
priv->rx_ring, priv->rx_desc_dma);
priv->rx_ring = NULL;
kfree(priv->tx_skb);
priv->tx_skb = NULL;
kfree(priv->rx_skb);
priv->rx_skb = NULL;
}
static int rtsn_chain_init(struct rtsn_private *priv, int tx_size, int rx_size)
{
struct net_device *ndev = priv->ndev;
struct sk_buff *skb;
int i;
priv->num_tx_ring = tx_size;
priv->num_rx_ring = rx_size;
priv->tx_skb = kcalloc(tx_size, sizeof(*priv->tx_skb), GFP_KERNEL);
priv->rx_skb = kcalloc(rx_size, sizeof(*priv->rx_skb), GFP_KERNEL);
if (!priv->rx_skb || !priv->tx_skb)
goto error;
for (i = 0; i < rx_size; i++) {
skb = netdev_alloc_skb(ndev, PKT_BUF_SZ + RTSN_ALIGN - 1);
if (!skb)
goto error;
skb_reserve(skb, NET_IP_ALIGN);
priv->rx_skb[i] = skb;
}
/* Allocate TX, RX descriptors */
priv->tx_ring = dma_alloc_coherent(ndev->dev.parent,
sizeof(struct rtsn_ext_desc) * (tx_size + 1),
&priv->tx_desc_dma, GFP_KERNEL);
priv->rx_ring = dma_alloc_coherent(ndev->dev.parent,
sizeof(struct rtsn_ext_ts_desc) * (rx_size + 1),
&priv->rx_desc_dma, GFP_KERNEL);
if (!priv->tx_ring || !priv->rx_ring)
goto error;
return 0;
error:
rtsn_chain_free(priv);
return -ENOMEM;
}
static void rtsn_chain_format(struct rtsn_private *priv)
{
struct net_device *ndev = priv->ndev;
struct rtsn_ext_ts_desc *rx_desc;
struct rtsn_ext_desc *tx_desc;
struct rtsn_desc *bat_desc;
dma_addr_t dma_addr;
unsigned int i;
priv->cur_tx = 0;
priv->cur_rx = 0;
priv->dirty_rx = 0;
priv->dirty_tx = 0;
/* TX */
memset(priv->tx_ring, 0, sizeof(*tx_desc) * priv->num_tx_ring);
for (i = 0, tx_desc = priv->tx_ring; i < priv->num_tx_ring; i++, tx_desc++)
tx_desc->die_dt = DT_EEMPTY | D_DIE;
tx_desc->dptr = cpu_to_le32((u32)priv->tx_desc_dma);
tx_desc->die_dt = DT_LINK;
bat_desc = &priv->tx_desc_bat[TX_CHAIN_IDX];
bat_desc->die_dt = DT_LINK;
bat_desc->dptr = cpu_to_le32((u32)priv->tx_desc_dma);
/* RX */
memset(priv->rx_ring, 0, sizeof(*rx_desc) * priv->num_rx_ring);
for (i = 0, rx_desc = priv->rx_ring; i < priv->num_rx_ring; i++, rx_desc++) {
dma_addr = dma_map_single(ndev->dev.parent,
priv->rx_skb[i]->data, PKT_BUF_SZ,
DMA_FROM_DEVICE);
if (!dma_mapping_error(ndev->dev.parent, dma_addr))
rx_desc->info_ds = cpu_to_le16(PKT_BUF_SZ);
rx_desc->dptr = cpu_to_le32((u32)dma_addr);
rx_desc->die_dt = DT_FEMPTY | D_DIE;
}
rx_desc->dptr = cpu_to_le32((u32)priv->rx_desc_dma);
rx_desc->die_dt = DT_LINK;
bat_desc = &priv->rx_desc_bat[RX_CHAIN_IDX];
bat_desc->die_dt = DT_LINK;
bat_desc->dptr = cpu_to_le32((u32)priv->rx_desc_dma);
}
static int rtsn_dmac_init(struct rtsn_private *priv)
{
int ret;
ret = rtsn_chain_init(priv, TX_CHAIN_SIZE, RX_CHAIN_SIZE);
if (ret)
return ret;
rtsn_chain_format(priv);
return 0;
}
static enum rtsn_mode rtsn_read_mode(struct rtsn_private *priv)
{
return (rtsn_read(priv, OSR) & OSR_OPS) >> 1;
}
static int rtsn_wait_mode(struct rtsn_private *priv, enum rtsn_mode mode)
{
unsigned int i;
/* Need to busy loop as mode changes can happen in atomic context. */
for (i = 0; i < RTSN_TIMEOUT_US / RTSN_INTERVAL_US; i++) {
if (rtsn_read_mode(priv) == mode)
return 0;
udelay(RTSN_INTERVAL_US);
}
return -ETIMEDOUT;
}
static int rtsn_change_mode(struct rtsn_private *priv, enum rtsn_mode mode)
{
int ret;
rtsn_write(priv, OCR, mode);
ret = rtsn_wait_mode(priv, mode);
if (ret)
netdev_err(priv->ndev, "Failed to switch operation mode\n");
return ret;
}
static int rtsn_get_data_irq_status(struct rtsn_private *priv)
{
u32 val;
val = rtsn_read(priv, TDIS0) | TDIS_TDS(TX_CHAIN_IDX);
val |= rtsn_read(priv, RDIS0) | RDIS_RDS(RX_CHAIN_IDX);
return val;
}
static irqreturn_t rtsn_irq(int irq, void *dev_id)
{
struct rtsn_private *priv = dev_id;
int ret = IRQ_NONE;
spin_lock(&priv->lock);
if (rtsn_get_data_irq_status(priv)) {
/* Clear TX/RX irq status */
rtsn_write(priv, TDIS0, TDIS_TDS(TX_CHAIN_IDX));
rtsn_write(priv, RDIS0, RDIS_RDS(RX_CHAIN_IDX));
if (napi_schedule_prep(&priv->napi)) {
/* Disable TX/RX interrupts */
rtsn_ctrl_data_irq(priv, false);
__napi_schedule(&priv->napi);
}
ret = IRQ_HANDLED;
}
spin_unlock(&priv->lock);
return ret;
}
static int rtsn_request_irq(unsigned int irq, irq_handler_t handler,
unsigned long flags, struct rtsn_private *priv,
const char *ch)
{
char *name;
int ret;
name = devm_kasprintf(&priv->pdev->dev, GFP_KERNEL, "%s:%s",
priv->ndev->name, ch);
if (!name)
return -ENOMEM;
ret = request_irq(irq, handler, flags, name, priv);
if (ret)
netdev_err(priv->ndev, "Cannot request IRQ %s\n", name);
return ret;
}
static void rtsn_free_irqs(struct rtsn_private *priv)
{
free_irq(priv->tx_data_irq, priv);
free_irq(priv->rx_data_irq, priv);
}
static int rtsn_request_irqs(struct rtsn_private *priv)
{
int ret;
priv->rx_data_irq = platform_get_irq_byname(priv->pdev, "rx");
if (priv->rx_data_irq < 0)
return priv->rx_data_irq;
priv->tx_data_irq = platform_get_irq_byname(priv->pdev, "tx");
if (priv->tx_data_irq < 0)
return priv->tx_data_irq;
ret = rtsn_request_irq(priv->tx_data_irq, rtsn_irq, 0, priv, "tx");
if (ret)
return ret;
ret = rtsn_request_irq(priv->rx_data_irq, rtsn_irq, 0, priv, "rx");
if (ret) {
free_irq(priv->tx_data_irq, priv);
return ret;
}
return 0;
}
static int rtsn_reset(struct rtsn_private *priv)
{
reset_control_reset(priv->reset);
mdelay(1);
return rtsn_wait_mode(priv, OCR_OPC_DISABLE);
}
static int rtsn_axibmi_init(struct rtsn_private *priv)
{
int ret;
ret = rtsn_reg_wait(priv, RR, RR_RST, RR_RST_COMPLETE);
if (ret)
return ret;
/* Set AXIWC */
rtsn_write(priv, AXIWC, AXIWC_DEFAULT);
/* Set AXIRC */
rtsn_write(priv, AXIRC, AXIRC_DEFAULT);
/* TX Descriptor chain setting */
rtsn_write(priv, TATLS0, TATLS0_TEDE | TATLS0_TATEN(TX_CHAIN_IDX));
rtsn_write(priv, TATLS1, priv->tx_desc_bat_dma + TX_CHAIN_ADDR_OFFSET);
rtsn_write(priv, TATLR, TATLR_TATL);
ret = rtsn_reg_wait(priv, TATLR, TATLR_TATL, 0);
if (ret)
return ret;
/* RX Descriptor chain setting */
rtsn_write(priv, RATLS0,
RATLS0_RETS | RATLS0_REDE | RATLS0_RATEN(RX_CHAIN_IDX));
rtsn_write(priv, RATLS1, priv->rx_desc_bat_dma + RX_CHAIN_ADDR_OFFSET);
rtsn_write(priv, RATLR, RATLR_RATL);
ret = rtsn_reg_wait(priv, RATLR, RATLR_RATL, 0);
if (ret)
return ret;
/* Enable TX/RX interrupts */
rtsn_ctrl_data_irq(priv, true);
return 0;
}
static void rtsn_mhd_init(struct rtsn_private *priv)
{
/* TX General setting */
rtsn_write(priv, TGC1, TGC1_STTV_DEFAULT | TGC1_TQTM_SFM);
rtsn_write(priv, TMS0, TMS_MFS_MAX);
/* RX Filter IP */
rtsn_write(priv, CFCR0, CFCR_SDID(RX_CHAIN_IDX));
rtsn_write(priv, FMSCR, FMSCR_FMSIE(RX_CHAIN_IDX));
}
static int rtsn_get_phy_params(struct rtsn_private *priv)
{
int ret;
ret = of_get_phy_mode(priv->pdev->dev.of_node, &priv->iface);
if (ret)
return ret;
switch (priv->iface) {
case PHY_INTERFACE_MODE_MII:
priv->speed = 100;
break;
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_TXID:
priv->speed = 1000;
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static void rtsn_set_phy_interface(struct rtsn_private *priv)
{
u32 val;
switch (priv->iface) {
case PHY_INTERFACE_MODE_MII:
val = MPIC_PIS_MII;
break;
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_TXID:
val = MPIC_PIS_GMII;
break;
default:
return;
}
rtsn_modify(priv, MPIC, MPIC_PIS_MASK, val);
}
static void rtsn_set_rate(struct rtsn_private *priv)
{
u32 val;
switch (priv->speed) {
case 10:
val = MPIC_LSC_10M;
break;
case 100:
val = MPIC_LSC_100M;
break;
case 1000:
val = MPIC_LSC_1G;
break;
default:
return;
}
rtsn_modify(priv, MPIC, MPIC_LSC_MASK, val);
}
static int rtsn_rmac_init(struct rtsn_private *priv)
{
const u8 *mac_addr = priv->ndev->dev_addr;
int ret;
/* Set MAC address */
rtsn_write(priv, MRMAC0, (mac_addr[0] << 8) | mac_addr[1]);
rtsn_write(priv, MRMAC1, (mac_addr[2] << 24) | (mac_addr[3] << 16) |
(mac_addr[4] << 8) | mac_addr[5]);
/* Set xMII type */
rtsn_set_phy_interface(priv);
rtsn_set_rate(priv);
/* Enable MII */
rtsn_modify(priv, MPIC, MPIC_PSMCS_MASK | MPIC_PSMHT_MASK,
MPIC_PSMCS_DEFAULT | MPIC_PSMHT_DEFAULT);
/* Link verification */
rtsn_modify(priv, MLVC, MLVC_PLV, MLVC_PLV);
ret = rtsn_reg_wait(priv, MLVC, MLVC_PLV, 0);
if (ret)
return ret;
return ret;
}
static int rtsn_hw_init(struct rtsn_private *priv)
{
int ret;
ret = rtsn_reset(priv);
if (ret)
return ret;
/* Change to CONFIG mode */
ret = rtsn_change_mode(priv, OCR_OPC_CONFIG);
if (ret)
return ret;
ret = rtsn_axibmi_init(priv);
if (ret)
return ret;
rtsn_mhd_init(priv);
ret = rtsn_rmac_init(priv);
if (ret)
return ret;
ret = rtsn_change_mode(priv, OCR_OPC_DISABLE);
if (ret)
return ret;
/* Change to OPERATION mode */
ret = rtsn_change_mode(priv, OCR_OPC_OPERATION);
return ret;
}
static int rtsn_mii_access(struct mii_bus *bus, bool read, int phyad,
int regad, u16 data)
{
struct rtsn_private *priv = bus->priv;
u32 val;
int ret;
val = MPSM_PDA(phyad) | MPSM_PRA(regad) | MPSM_PSME;
if (!read)
val |= MPSM_PSMAD | MPSM_PRD_SET(data);
rtsn_write(priv, MPSM, val);
ret = rtsn_reg_wait(priv, MPSM, MPSM_PSME, 0);
if (ret)
return ret;
if (read)
ret = MPSM_PRD_GET(rtsn_read(priv, MPSM));
return ret;
}
static int rtsn_mii_read(struct mii_bus *bus, int addr, int regnum)
{
return rtsn_mii_access(bus, true, addr, regnum, 0);
}
static int rtsn_mii_write(struct mii_bus *bus, int addr, int regnum, u16 val)
{
return rtsn_mii_access(bus, false, addr, regnum, val);
}
static int rtsn_mdio_alloc(struct rtsn_private *priv)
{
struct platform_device *pdev = priv->pdev;
struct device *dev = &pdev->dev;
struct device_node *mdio_node;
struct mii_bus *mii;
int ret;
mii = mdiobus_alloc();
if (!mii)
return -ENOMEM;
mdio_node = of_get_child_by_name(dev->of_node, "mdio");
if (!mdio_node) {
ret = -ENODEV;
goto out_free_bus;
}
/* Enter config mode before registering the MDIO bus */
ret = rtsn_reset(priv);
if (ret)
goto out_free_bus;
ret = rtsn_change_mode(priv, OCR_OPC_CONFIG);
if (ret)
goto out_free_bus;
rtsn_modify(priv, MPIC, MPIC_PSMCS_MASK | MPIC_PSMHT_MASK,
MPIC_PSMCS_DEFAULT | MPIC_PSMHT_DEFAULT);
/* Register the MDIO bus */
mii->name = "rtsn_mii";
snprintf(mii->id, MII_BUS_ID_SIZE, "%s-%x",
pdev->name, pdev->id);
mii->priv = priv;
mii->read = rtsn_mii_read;
mii->write = rtsn_mii_write;
mii->parent = dev;
ret = of_mdiobus_register(mii, mdio_node);
of_node_put(mdio_node);
if (ret)
goto out_free_bus;
priv->mii = mii;
return 0;
out_free_bus:
mdiobus_free(mii);
return ret;
}
static void rtsn_mdio_free(struct rtsn_private *priv)
{
mdiobus_unregister(priv->mii);
mdiobus_free(priv->mii);
priv->mii = NULL;
}
static void rtsn_adjust_link(struct net_device *ndev)
{
struct rtsn_private *priv = netdev_priv(ndev);
struct phy_device *phydev = ndev->phydev;
bool new_state = false;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
if (phydev->link) {
if (phydev->speed != priv->speed) {
new_state = true;
priv->speed = phydev->speed;
}
if (!priv->link) {
new_state = true;
priv->link = phydev->link;
}
} else if (priv->link) {
new_state = true;
priv->link = 0;
priv->speed = 0;
}
if (new_state) {
/* Need to transition to CONFIG mode before reconfiguring and
* then back to the original mode. Any state change to/from
* CONFIG or OPERATION must go over DISABLED to stop Rx/Tx.
*/
enum rtsn_mode orgmode = rtsn_read_mode(priv);
/* Transit to CONFIG */
if (orgmode != OCR_OPC_CONFIG) {
if (orgmode != OCR_OPC_DISABLE &&
rtsn_change_mode(priv, OCR_OPC_DISABLE))
goto out;
if (rtsn_change_mode(priv, OCR_OPC_CONFIG))
goto out;
}
rtsn_set_rate(priv);
/* Transition to original mode */
if (orgmode != OCR_OPC_CONFIG) {
if (rtsn_change_mode(priv, OCR_OPC_DISABLE))
goto out;
if (orgmode != OCR_OPC_DISABLE &&
rtsn_change_mode(priv, orgmode))
goto out;
}
}
out:
spin_unlock_irqrestore(&priv->lock, flags);
if (new_state)
phy_print_status(phydev);
}
static int rtsn_phy_init(struct rtsn_private *priv)
{
struct device_node *np = priv->ndev->dev.parent->of_node;
struct phy_device *phydev;
struct device_node *phy;
priv->link = 0;
phy = of_parse_phandle(np, "phy-handle", 0);
if (!phy)
return -ENOENT;
phydev = of_phy_connect(priv->ndev, phy, rtsn_adjust_link, 0,
priv->iface);
of_node_put(phy);
if (!phydev)
return -ENOENT;
/* Only support full-duplex mode */
phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_10baseT_Half_BIT);
phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_100baseT_Half_BIT);
phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Half_BIT);
phy_attached_info(phydev);
return 0;
}
static void rtsn_phy_deinit(struct rtsn_private *priv)
{
phy_disconnect(priv->ndev->phydev);
priv->ndev->phydev = NULL;
}
static int rtsn_init(struct rtsn_private *priv)
{
int ret;
ret = rtsn_desc_alloc(priv);
if (ret)
return ret;
ret = rtsn_dmac_init(priv);
if (ret)
goto error_free_desc;
ret = rtsn_hw_init(priv);
if (ret)
goto error_free_chain;
ret = rtsn_phy_init(priv);
if (ret)
goto error_free_chain;
ret = rtsn_request_irqs(priv);
if (ret)
goto error_free_phy;
return 0;
error_free_phy:
rtsn_phy_deinit(priv);
error_free_chain:
rtsn_chain_free(priv);
error_free_desc:
rtsn_desc_free(priv);
return ret;
}
static void rtsn_deinit(struct rtsn_private *priv)
{
rtsn_free_irqs(priv);
rtsn_phy_deinit(priv);
rtsn_chain_free(priv);
rtsn_desc_free(priv);
}
static void rtsn_parse_mac_address(struct device_node *np,
struct net_device *ndev)
{
struct rtsn_private *priv = netdev_priv(ndev);
u8 addr[ETH_ALEN];
u32 mrmac0;
u32 mrmac1;
/* Try to read address from Device Tree. */
if (!of_get_mac_address(np, addr)) {
eth_hw_addr_set(ndev, addr);
return;
}
/* Try to read address from device. */
mrmac0 = rtsn_read(priv, MRMAC0);
mrmac1 = rtsn_read(priv, MRMAC1);
addr[0] = (mrmac0 >> 8) & 0xff;
addr[1] = (mrmac0 >> 0) & 0xff;
addr[2] = (mrmac1 >> 24) & 0xff;
addr[3] = (mrmac1 >> 16) & 0xff;
addr[4] = (mrmac1 >> 8) & 0xff;
addr[5] = (mrmac1 >> 0) & 0xff;
if (is_valid_ether_addr(addr)) {
eth_hw_addr_set(ndev, addr);
return;
}
/* Fallback to a random address */
eth_hw_addr_random(ndev);
}
static int rtsn_open(struct net_device *ndev)
{
struct rtsn_private *priv = netdev_priv(ndev);
int ret;
napi_enable(&priv->napi);
ret = rtsn_init(priv);
if (ret) {
napi_disable(&priv->napi);
return ret;
}
phy_start(ndev->phydev);
netif_start_queue(ndev);
return 0;
}
static int rtsn_stop(struct net_device *ndev)
{
struct rtsn_private *priv = netdev_priv(ndev);
phy_stop(priv->ndev->phydev);
napi_disable(&priv->napi);
rtsn_change_mode(priv, OCR_OPC_DISABLE);
rtsn_deinit(priv);
return 0;
}
static netdev_tx_t rtsn_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct rtsn_private *priv = netdev_priv(ndev);
struct rtsn_ext_desc *desc;
int ret = NETDEV_TX_OK;
unsigned long flags;
dma_addr_t dma_addr;
int entry;
spin_lock_irqsave(&priv->lock, flags);
/* Drop packet if it won't fit in a single descriptor. */
if (skb->len >= TX_DS) {
priv->stats.tx_dropped++;
priv->stats.tx_errors++;
goto out;
}
if (priv->cur_tx - priv->dirty_tx > priv->num_tx_ring) {
netif_stop_subqueue(ndev, 0);
ret = NETDEV_TX_BUSY;
goto out;
}
if (skb_put_padto(skb, ETH_ZLEN))
goto out;
dma_addr = dma_map_single(ndev->dev.parent, skb->data, skb->len,
DMA_TO_DEVICE);
if (dma_mapping_error(ndev->dev.parent, dma_addr)) {
dev_kfree_skb_any(skb);
goto out;
}
entry = priv->cur_tx % priv->num_tx_ring;
priv->tx_skb[entry] = skb;
desc = &priv->tx_ring[entry];
desc->dptr = cpu_to_le32(dma_addr);
desc->info_ds = cpu_to_le16(skb->len);
desc->info1 = cpu_to_le64(skb->len);
if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) {
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
priv->ts_tag++;
desc->info_ds |= cpu_to_le16(TXC);
desc->info = priv->ts_tag;
}
skb_tx_timestamp(skb);
dma_wmb();
desc->die_dt = DT_FSINGLE | D_DIE;
priv->cur_tx++;
/* Start xmit */
rtsn_write(priv, TRCR0, BIT(TX_CHAIN_IDX));
out:
spin_unlock_irqrestore(&priv->lock, flags);
return ret;
}
static void rtsn_get_stats64(struct net_device *ndev,
struct rtnl_link_stats64 *storage)
{
struct rtsn_private *priv = netdev_priv(ndev);
*storage = priv->stats;
}
static int rtsn_do_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
{
if (!netif_running(ndev))
return -ENODEV;
return phy_do_ioctl_running(ndev, ifr, cmd);
}
static int rtsn_hwtstamp_get(struct net_device *ndev,
struct kernel_hwtstamp_config *config)
{
struct rcar_gen4_ptp_private *ptp_priv;
struct rtsn_private *priv;
if (!netif_running(ndev))
return -ENODEV;
priv = netdev_priv(ndev);
ptp_priv = priv->ptp_priv;
config->flags = 0;
config->tx_type =
ptp_priv->tstamp_tx_ctrl ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
switch (ptp_priv->tstamp_rx_ctrl & RCAR_GEN4_RXTSTAMP_TYPE) {
case RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT:
config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
break;
case RCAR_GEN4_RXTSTAMP_TYPE_ALL:
config->rx_filter = HWTSTAMP_FILTER_ALL;
break;
default:
config->rx_filter = HWTSTAMP_FILTER_NONE;
break;
}
return 0;
}
static int rtsn_hwtstamp_set(struct net_device *ndev,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack)
{
struct rcar_gen4_ptp_private *ptp_priv;
struct rtsn_private *priv;
u32 tstamp_rx_ctrl;
u32 tstamp_tx_ctrl;
if (!netif_running(ndev))
return -ENODEV;
priv = netdev_priv(ndev);
ptp_priv = priv->ptp_priv;
if (config->flags)
return -EINVAL;
switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
tstamp_tx_ctrl = 0;
break;
case HWTSTAMP_TX_ON:
tstamp_tx_ctrl = RCAR_GEN4_TXTSTAMP_ENABLED;
break;
default:
return -ERANGE;
}
switch (config->rx_filter) {
case HWTSTAMP_FILTER_NONE:
tstamp_rx_ctrl = 0;
break;
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
tstamp_rx_ctrl = RCAR_GEN4_RXTSTAMP_ENABLED |
RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT;
break;
default:
config->rx_filter = HWTSTAMP_FILTER_ALL;
tstamp_rx_ctrl = RCAR_GEN4_RXTSTAMP_ENABLED |
RCAR_GEN4_RXTSTAMP_TYPE_ALL;
break;
}
ptp_priv->tstamp_tx_ctrl = tstamp_tx_ctrl;
ptp_priv->tstamp_rx_ctrl = tstamp_rx_ctrl;
return 0;
}
static const struct net_device_ops rtsn_netdev_ops = {
.ndo_open = rtsn_open,
.ndo_stop = rtsn_stop,
.ndo_start_xmit = rtsn_start_xmit,
.ndo_get_stats64 = rtsn_get_stats64,
.ndo_eth_ioctl = rtsn_do_ioctl,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_hwtstamp_set = rtsn_hwtstamp_set,
.ndo_hwtstamp_get = rtsn_hwtstamp_get,
};
static int rtsn_get_ts_info(struct net_device *ndev,
struct ethtool_ts_info *info)
{
struct rtsn_private *priv = netdev_priv(ndev);
info->phc_index = ptp_clock_index(priv->ptp_priv->clock);
info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE |
SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON);
info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL);
return 0;
}
static const struct ethtool_ops rtsn_ethtool_ops = {
.nway_reset = phy_ethtool_nway_reset,
.get_link = ethtool_op_get_link,
.get_ts_info = rtsn_get_ts_info,
.get_link_ksettings = phy_ethtool_get_link_ksettings,
.set_link_ksettings = phy_ethtool_set_link_ksettings,
};
static const struct of_device_id rtsn_match_table[] = {
{ .compatible = "renesas,r8a779g0-ethertsn", },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, rtsn_match_table);
static int rtsn_probe(struct platform_device *pdev)
{
struct rtsn_private *priv;
struct net_device *ndev;
struct resource *res;
int ret;
ndev = alloc_etherdev_mqs(sizeof(struct rtsn_private), TX_NUM_CHAINS,
RX_NUM_CHAINS);
if (!ndev)
return -ENOMEM;
priv = netdev_priv(ndev);
priv->pdev = pdev;
priv->ndev = ndev;
priv->ptp_priv = rcar_gen4_ptp_alloc(pdev);
spin_lock_init(&priv->lock);
platform_set_drvdata(pdev, priv);
priv->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(priv->clk)) {
ret = PTR_ERR(priv->clk);
goto error_free;
}
priv->reset = devm_reset_control_get(&pdev->dev, NULL);
if (IS_ERR(priv->reset)) {
ret = PTR_ERR(priv->reset);
goto error_free;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tsnes");
if (!res) {
dev_err(&pdev->dev, "Can't find tsnes resource\n");
ret = -EINVAL;
goto error_free;
}
priv->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->base)) {
ret = PTR_ERR(priv->base);
goto error_free;
}
SET_NETDEV_DEV(ndev, &pdev->dev);
ndev->features = NETIF_F_RXCSUM;
ndev->hw_features = NETIF_F_RXCSUM;
ndev->base_addr = res->start;
ndev->netdev_ops = &rtsn_netdev_ops;
ndev->ethtool_ops = &rtsn_ethtool_ops;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gptp");
if (!res) {
dev_err(&pdev->dev, "Can't find gptp resource\n");
ret = -EINVAL;
goto error_free;
}
priv->ptp_priv->addr = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->ptp_priv->addr)) {
ret = PTR_ERR(priv->ptp_priv->addr);
goto error_free;
}
ret = rtsn_get_phy_params(priv);
if (ret)
goto error_free;
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
netif_napi_add(ndev, &priv->napi, rtsn_poll);
rtsn_parse_mac_address(pdev->dev.of_node, ndev);
dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
device_set_wakeup_capable(&pdev->dev, 1);
ret = rcar_gen4_ptp_register(priv->ptp_priv, RCAR_GEN4_PTP_REG_LAYOUT,
clk_get_rate(priv->clk));
if (ret)
goto error_pm;
ret = rtsn_mdio_alloc(priv);
if (ret)
goto error_ptp;
ret = register_netdev(ndev);
if (ret)
goto error_mdio;
netdev_info(ndev, "MAC address %pM\n", ndev->dev_addr);
return 0;
error_mdio:
rtsn_mdio_free(priv);
error_ptp:
rcar_gen4_ptp_unregister(priv->ptp_priv);
error_pm:
netif_napi_del(&priv->napi);
rtsn_change_mode(priv, OCR_OPC_DISABLE);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
error_free:
free_netdev(ndev);
return ret;
}
static int rtsn_remove(struct platform_device *pdev)
{
struct rtsn_private *priv = platform_get_drvdata(pdev);
unregister_netdev(priv->ndev);
rtsn_mdio_free(priv);
rcar_gen4_ptp_unregister(priv->ptp_priv);
rtsn_change_mode(priv, OCR_OPC_DISABLE);
netif_napi_del(&priv->napi);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
free_netdev(priv->ndev);
return 0;
}
static struct platform_driver rtsn_driver = {
.probe = rtsn_probe,
.remove = rtsn_remove,
.driver = {
.name = "rtsn",
.of_match_table = rtsn_match_table,
}
};
module_platform_driver(rtsn_driver);
MODULE_AUTHOR("Phong Hoang, Niklas Söderlund");
MODULE_DESCRIPTION("Renesas Ethernet-TSN device driver");
MODULE_LICENSE("GPL");
/* SPDX-License-Identifier: GPL-2.0 */
/* Renesas Ethernet-TSN device driver
*
* Copyright (C) 2022 Renesas Electronics Corporation
* Copyright (C) 2023 Niklas Söderlund <niklas.soderlund@ragnatech.se>
*/
#ifndef __RTSN_H__
#define __RTSN_H__
#include <linux/types.h>
#define AXIBMI 0x0000
#define TSNMHD 0x1000
#define RMSO 0x2000
#define RMRO 0x3800
enum rtsn_reg {
AXIWC = AXIBMI + 0x0000,
AXIRC = AXIBMI + 0x0004,
TDPC0 = AXIBMI + 0x0010,
TFT = AXIBMI + 0x0090,
TATLS0 = AXIBMI + 0x00a0,
TATLS1 = AXIBMI + 0x00a4,
TATLR = AXIBMI + 0x00a8,
RATLS0 = AXIBMI + 0x00b0,
RATLS1 = AXIBMI + 0x00b4,
RATLR = AXIBMI + 0x00b8,
TSA0 = AXIBMI + 0x00c0,
TSS0 = AXIBMI + 0x00c4,
TRCR0 = AXIBMI + 0x0140,
RIDAUAS0 = AXIBMI + 0x0180,
RR = AXIBMI + 0x0200,
TATS = AXIBMI + 0x0210,
TATSR0 = AXIBMI + 0x0214,
TATSR1 = AXIBMI + 0x0218,
TATSR2 = AXIBMI + 0x021c,
RATS = AXIBMI + 0x0220,
RATSR0 = AXIBMI + 0x0224,
RATSR1 = AXIBMI + 0x0228,
RATSR2 = AXIBMI + 0x022c,
RIDASM0 = AXIBMI + 0x0240,
RIDASAM0 = AXIBMI + 0x0244,
RIDACAM0 = AXIBMI + 0x0248,
EIS0 = AXIBMI + 0x0300,
EIE0 = AXIBMI + 0x0304,
EID0 = AXIBMI + 0x0308,
EIS1 = AXIBMI + 0x0310,
EIE1 = AXIBMI + 0x0314,
EID1 = AXIBMI + 0x0318,
TCEIS0 = AXIBMI + 0x0340,
TCEIE0 = AXIBMI + 0x0344,
TCEID0 = AXIBMI + 0x0348,
RFSEIS0 = AXIBMI + 0x04c0,
RFSEIE0 = AXIBMI + 0x04c4,
RFSEID0 = AXIBMI + 0x04c8,
RFEIS0 = AXIBMI + 0x0540,
RFEIE0 = AXIBMI + 0x0544,
RFEID0 = AXIBMI + 0x0548,
RCEIS0 = AXIBMI + 0x05c0,
RCEIE0 = AXIBMI + 0x05c4,
RCEID0 = AXIBMI + 0x05c8,
RIDAOIS = AXIBMI + 0x0640,
RIDAOIE = AXIBMI + 0x0644,
RIDAOID = AXIBMI + 0x0648,
TSFEIS = AXIBMI + 0x06c0,
TSFEIE = AXIBMI + 0x06c4,
TSFEID = AXIBMI + 0x06c8,
TSCEIS = AXIBMI + 0x06d0,
TSCEIE = AXIBMI + 0x06d4,
TSCEID = AXIBMI + 0x06d8,
DIS = AXIBMI + 0x0b00,
DIE = AXIBMI + 0x0b04,
DID = AXIBMI + 0x0b08,
TDIS0 = AXIBMI + 0x0b10,
TDIE0 = AXIBMI + 0x0b14,
TDID0 = AXIBMI + 0x0b18,
RDIS0 = AXIBMI + 0x0b90,
RDIE0 = AXIBMI + 0x0b94,
RDID0 = AXIBMI + 0x0b98,
TSDIS = AXIBMI + 0x0c10,
TSDIE = AXIBMI + 0x0c14,
TSDID = AXIBMI + 0x0c18,
GPOUT = AXIBMI + 0x6000,
OCR = TSNMHD + 0x0000,
OSR = TSNMHD + 0x0004,
SWR = TSNMHD + 0x0008,
SIS = TSNMHD + 0x000c,
GIS = TSNMHD + 0x0010,
GIE = TSNMHD + 0x0014,
GID = TSNMHD + 0x0018,
TIS1 = TSNMHD + 0x0020,
TIE1 = TSNMHD + 0x0024,
TID1 = TSNMHD + 0x0028,
TIS2 = TSNMHD + 0x0030,
TIE2 = TSNMHD + 0x0034,
TID2 = TSNMHD + 0x0038,
RIS = TSNMHD + 0x0040,
RIE = TSNMHD + 0x0044,
RID = TSNMHD + 0x0048,
TGC1 = TSNMHD + 0x0050,
TGC2 = TSNMHD + 0x0054,
TFS0 = TSNMHD + 0x0060,
TCF0 = TSNMHD + 0x0070,
TCR1 = TSNMHD + 0x0080,
TCR2 = TSNMHD + 0x0084,
TCR3 = TSNMHD + 0x0088,
TCR4 = TSNMHD + 0x008c,
TMS0 = TSNMHD + 0x0090,
TSR1 = TSNMHD + 0x00b0,
TSR2 = TSNMHD + 0x00b4,
TSR3 = TSNMHD + 0x00b8,
TSR4 = TSNMHD + 0x00bc,
TSR5 = TSNMHD + 0x00c0,
RGC = TSNMHD + 0x00d0,
RDFCR = TSNMHD + 0x00d4,
RCFCR = TSNMHD + 0x00d8,
REFCNCR = TSNMHD + 0x00dc,
RSR1 = TSNMHD + 0x00e0,
RSR2 = TSNMHD + 0x00e4,
RSR3 = TSNMHD + 0x00e8,
TCIS = TSNMHD + 0x01e0,
TCIE = TSNMHD + 0x01e4,
TCID = TSNMHD + 0x01e8,
TPTPC = TSNMHD + 0x01f0,
TTML = TSNMHD + 0x01f4,
TTJ = TSNMHD + 0x01f8,
TCC = TSNMHD + 0x0200,
TCS = TSNMHD + 0x0204,
TGS = TSNMHD + 0x020c,
TACST0 = TSNMHD + 0x0210,
TACST1 = TSNMHD + 0x0214,
TACST2 = TSNMHD + 0x0218,
TALIT0 = TSNMHD + 0x0220,
TALIT1 = TSNMHD + 0x0224,
TALIT2 = TSNMHD + 0x0228,
TAEN0 = TSNMHD + 0x0230,
TAEN1 = TSNMHD + 0x0234,
TASFE = TSNMHD + 0x0240,
TACLL0 = TSNMHD + 0x0250,
TACLL1 = TSNMHD + 0x0254,
TACLL2 = TSNMHD + 0x0258,
CACC = TSNMHD + 0x0260,
CCS = TSNMHD + 0x0264,
CAIV0 = TSNMHD + 0x0270,
CAUL0 = TSNMHD + 0x0290,
TOCST0 = TSNMHD + 0x0300,
TOCST1 = TSNMHD + 0x0304,
TOCST2 = TSNMHD + 0x0308,
TOLIT0 = TSNMHD + 0x0310,
TOLIT1 = TSNMHD + 0x0314,
TOLIT2 = TSNMHD + 0x0318,
TOEN0 = TSNMHD + 0x0320,
TOEN1 = TSNMHD + 0x0324,
TOSFE = TSNMHD + 0x0330,
TCLR0 = TSNMHD + 0x0340,
TCLR1 = TSNMHD + 0x0344,
TCLR2 = TSNMHD + 0x0348,
TSMS = TSNMHD + 0x0350,
COCC = TSNMHD + 0x0360,
COIV0 = TSNMHD + 0x03b0,
COUL0 = TSNMHD + 0x03d0,
QSTMACU0 = TSNMHD + 0x0400,
QSTMACD0 = TSNMHD + 0x0404,
QSTMAMU0 = TSNMHD + 0x0408,
QSTMAMD0 = TSNMHD + 0x040c,
QSFTVL0 = TSNMHD + 0x0410,
QSFTVLM0 = TSNMHD + 0x0414,
QSFTMSD0 = TSNMHD + 0x0418,
QSFTGMI0 = TSNMHD + 0x041c,
QSFTLS = TSNMHD + 0x0600,
QSFTLIS = TSNMHD + 0x0604,
QSFTLIE = TSNMHD + 0x0608,
QSFTLID = TSNMHD + 0x060c,
QSMSMC = TSNMHD + 0x0610,
QSGTMC = TSNMHD + 0x0614,
QSEIS = TSNMHD + 0x0618,
QSEIE = TSNMHD + 0x061c,
QSEID = TSNMHD + 0x0620,
QGACST0 = TSNMHD + 0x0630,
QGACST1 = TSNMHD + 0x0634,
QGACST2 = TSNMHD + 0x0638,
QGALIT1 = TSNMHD + 0x0640,
QGALIT2 = TSNMHD + 0x0644,
QGAEN0 = TSNMHD + 0x0648,
QGAEN1 = TSNMHD + 0x074c,
QGIGS = TSNMHD + 0x0650,
QGGC = TSNMHD + 0x0654,
QGATL0 = TSNMHD + 0x0664,
QGATL1 = TSNMHD + 0x0668,
QGATL2 = TSNMHD + 0x066c,
QGOCST0 = TSNMHD + 0x0670,
QGOCST1 = TSNMHD + 0x0674,
QGOCST2 = TSNMHD + 0x0678,
QGOLIT0 = TSNMHD + 0x067c,
QGOLIT1 = TSNMHD + 0x0680,
QGOLIT2 = TSNMHD + 0x0684,
QGOEN0 = TSNMHD + 0x0688,
QGOEN1 = TSNMHD + 0x068c,
QGTRO = TSNMHD + 0x0690,
QGTR1 = TSNMHD + 0x0694,
QGTR2 = TSNMHD + 0x0698,
QGFSMS = TSNMHD + 0x069c,
QTMIS = TSNMHD + 0x06e0,
QTMIE = TSNMHD + 0x06e4,
QTMID = TSNMHD + 0x06e8,
QMEC = TSNMHD + 0x0700,
QMMC = TSNMHD + 0x0704,
QRFDC = TSNMHD + 0x0708,
QYFDC = TSNMHD + 0x070c,
QVTCMC0 = TSNMHD + 0x0710,
QMCBSC0 = TSNMHD + 0x0750,
QMCIRC0 = TSNMHD + 0x0790,
QMEBSC0 = TSNMHD + 0x07d0,
QMEIRC0 = TSNMHD + 0x0710,
QMCFC = TSNMHD + 0x0850,
QMEIS = TSNMHD + 0x0860,
QMEIE = TSNMHD + 0x0864,
QMEID = TSNMHD + 0x086c,
QSMFC0 = TSNMHD + 0x0870,
QMSPPC0 = TSNMHD + 0x08b0,
QMSRPC0 = TSNMHD + 0x08f0,
QGPPC0 = TSNMHD + 0x0930,
QGRPC0 = TSNMHD + 0x0950,
QMDPC0 = TSNMHD + 0x0970,
QMGPC0 = TSNMHD + 0x09b0,
QMYPC0 = TSNMHD + 0x09f0,
QMRPC0 = TSNMHD + 0x0a30,
MQSTMACU = TSNMHD + 0x0a70,
MQSTMACD = TSNMHD + 0x0a74,
MQSTMAMU = TSNMHD + 0x0a78,
MQSTMAMD = TSNMHD + 0x0a7c,
MQSFTVL = TSNMHD + 0x0a80,
MQSFTVLM = TSNMHD + 0x0a84,
MQSFTMSD = TSNMHD + 0x0a88,
MQSFTGMI = TSNMHD + 0x0a8c,
CFCR0 = RMSO + 0x0800,
FMSCR = RMSO + 0x0c10,
MMC = RMRO + 0x0000,
MPSM = RMRO + 0x0010,
MPIC = RMRO + 0x0014,
MTFFC = RMRO + 0x0020,
MTPFC = RMRO + 0x0024,
MTATC0 = RMRO + 0x0040,
MRGC = RMRO + 0x0080,
MRMAC0 = RMRO + 0x0084,
MRMAC1 = RMRO + 0x0088,
MRAFC = RMRO + 0x008c,
MRSCE = RMRO + 0x0090,
MRSCP = RMRO + 0x0094,
MRSCC = RMRO + 0x0098,
MRFSCE = RMRO + 0x009c,
MRFSCP = RMRO + 0x00a0,
MTRC = RMRO + 0x00a4,
MPFC = RMRO + 0x0100,
MLVC = RMRO + 0x0340,
MEEEC = RMRO + 0x0350,
MLBC = RMRO + 0x0360,
MGMR = RMRO + 0x0400,
MMPFTCT = RMRO + 0x0410,
MAPFTCT = RMRO + 0x0414,
MPFRCT = RMRO + 0x0418,
MFCICT = RMRO + 0x041c,
MEEECT = RMRO + 0x0420,
MEIS = RMRO + 0x0500,
MEIE = RMRO + 0x0504,
MEID = RMRO + 0x0508,
MMIS0 = RMRO + 0x0510,
MMIE0 = RMRO + 0x0514,
MMID0 = RMRO + 0x0518,
MMIS1 = RMRO + 0x0520,
MMIE1 = RMRO + 0x0524,
MMID1 = RMRO + 0x0528,
MMIS2 = RMRO + 0x0530,
MMIE2 = RMRO + 0x0534,
MMID2 = RMRO + 0x0538,
MXMS = RMRO + 0x0600,
};
/* AXIBMI */
#define RR_RATRR BIT(0)
#define RR_TATRR BIT(1)
#define RR_RST (RR_RATRR | RR_TATRR)
#define RR_RST_COMPLETE 0x03
#define AXIWC_DEFAULT 0xffff
#define AXIRC_DEFAULT 0xffff
#define TATLS0_TEDE BIT(1)
#define TATLS0_TATEN_SHIFT 24
#define TATLS0_TATEN(n) ((n) << TATLS0_TATEN_SHIFT)
#define TATLR_TATL BIT(31)
#define RATLS0_RETS BIT(2)
#define RATLS0_REDE BIT(3)
#define RATLS0_RATEN_SHIFT 24
#define RATLS0_RATEN(n) ((n) << RATLS0_RATEN_SHIFT)
#define RATLR_RATL BIT(31)
#define DIE_DID_TDICX(n) BIT((n))
#define DIE_DID_RDICX(n) BIT((n) + 8)
#define TDIE_TDID_TDX(n) BIT(n)
#define RDIE_RDID_RDX(n) BIT(n)
#define TDIS_TDS(n) BIT(n)
#define RDIS_RDS(n) BIT(n)
/* MHD */
#define OSR_OPS 0x07
#define SWR_SWR BIT(0)
#define TGC1_TQTM_SFM 0xff00
#define TGC1_STTV_DEFAULT 0x03
#define TMS_MFS_MAX 0x2800
/* RMAC System */
#define CFCR_SDID(n) ((n) << 16)
#define FMSCR_FMSIE(n) ((n) << 0)
/* RMAC */
#define MPIC_PIS_MASK GENMASK(1, 0)
#define MPIC_PIS_MII 0
#define MPIC_PIS_RMII 0x01
#define MPIC_PIS_GMII 0x02
#define MPIC_PIS_RGMII 0x03
#define MPIC_LSC_SHIFT 2
#define MPIC_LSC_MASK GENMASK(3, MPIC_LSC_SHIFT)
#define MPIC_LSC_10M (0 << MPIC_LSC_SHIFT)
#define MPIC_LSC_100M (0x01 << MPIC_LSC_SHIFT)
#define MPIC_LSC_1G (0x02 << MPIC_LSC_SHIFT)
#define MPIC_PSMCS_SHIFT 16
#define MPIC_PSMCS_MASK GENMASK(21, MPIC_PSMCS_SHIFT)
#define MPIC_PSMCS_DEFAULT (0x0a << MPIC_PSMCS_SHIFT)
#define MPIC_PSMHT_SHIFT 24
#define MPIC_PSMHT_MASK GENMASK(26, MPIC_PSMHT_SHIFT)
#define MPIC_PSMHT_DEFAULT (0x07 << MPIC_PSMHT_SHIFT)
#define MLVC_PASE BIT(8)
#define MLVC_PSE BIT(16)
#define MLVC_PLV BIT(17)
#define MPSM_PSME BIT(0)
#define MPSM_PSMAD BIT(1)
#define MPSM_PDA_SHIFT 3
#define MPSM_PDA_MASK GENMASK(7, 3)
#define MPSM_PDA(n) (((n) << MPSM_PDA_SHIFT) & MPSM_PDA_MASK)
#define MPSM_PRA_SHIFT 8
#define MPSM_PRA_MASK GENMASK(12, 8)
#define MPSM_PRA(n) (((n) << MPSM_PRA_SHIFT) & MPSM_PRA_MASK)
#define MPSM_PRD_SHIFT 16
#define MPSM_PRD_SET(n) ((n) << MPSM_PRD_SHIFT)
#define MPSM_PRD_GET(n) ((n) >> MPSM_PRD_SHIFT)
#define GPOUT_RDM BIT(13)
#define GPOUT_TDM BIT(14)
/* RTSN */
#define RTSN_INTERVAL_US 1000
#define RTSN_TIMEOUT_US 1000000
#define TX_NUM_CHAINS 1
#define RX_NUM_CHAINS 1
#define TX_CHAIN_SIZE 1024
#define RX_CHAIN_SIZE 1024
#define TX_CHAIN_IDX 0
#define RX_CHAIN_IDX 0
#define TX_CHAIN_ADDR_OFFSET (sizeof(struct rtsn_desc) * TX_CHAIN_IDX)
#define RX_CHAIN_ADDR_OFFSET (sizeof(struct rtsn_desc) * RX_CHAIN_IDX)
#define PKT_BUF_SZ 1584
#define RTSN_ALIGN 128
enum rtsn_mode {
OCR_OPC_DISABLE,
OCR_OPC_CONFIG,
OCR_OPC_OPERATION,
};
/* Descriptors */
enum RX_DS_CC_BIT {
RX_DS = 0x0fff, /* Data size */
RX_TR = 0x1000, /* Truncation indication */
RX_EI = 0x2000, /* Error indication */
RX_PS = 0xc000, /* Padding selection */
};
enum TX_FS_TAGL_BIT {
TX_DS = 0x0fff, /* Data size */
TX_TAGL = 0xf000, /* Frame tag LSBs */
};
enum DIE_DT {
/* HW/SW arbitration */
DT_FEMPTY_IS = 0x10,
DT_FEMPTY_IC = 0x20,
DT_FEMPTY_ND = 0x30,
DT_FEMPTY = 0x40,
DT_FEMPTY_START = 0x50,
DT_FEMPTY_MID = 0x60,
DT_FEMPTY_END = 0x70,
/* Frame data */
DT_FSINGLE = 0x80,
DT_FSTART = 0x90,
DT_FMID = 0xa0,
DT_FEND = 0xb0,
/* Chain control */
DT_LEMPTY = 0xc0,
DT_EEMPTY = 0xd0,
DT_LINK = 0xe0,
DT_EOS = 0xf0,
DT_MASK = 0xf0,
D_DIE = 0x08,
};
struct rtsn_desc {
__le16 info_ds;
__u8 info;
u8 die_dt;
__le32 dptr;
} __packed;
struct rtsn_ts_desc {
__le16 info_ds;
__u8 info;
u8 die_dt;
__le32 dptr;
__le32 ts_nsec;
__le32 ts_sec;
} __packed;
struct rtsn_ext_desc {
__le16 info_ds;
__u8 info;
u8 die_dt;
__le32 dptr;
__le64 info1;
} __packed;
struct rtsn_ext_ts_desc {
__le16 info_ds;
__u8 info;
u8 die_dt;
__le32 dptr;
__le64 info1;
__le32 ts_nsec;
__le32 ts_sec;
} __packed;
enum EXT_INFO_DS_BIT {
TXC = 0x4000,
};
#endif
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