@@ -29,8 +29,13 @@
*/
#define FEC_IEVENT 0x004 /* Interrupt event reg */
#define FEC_IMASK 0x008 /* Interrupt mask reg */
+#ifdef CONFIG_NET_DSA_MTIP_L2SW
+#define FEC_R_DES_ACTIVE_0 0x018 /* L2 switch Receive descriptor reg */
+#define FEC_X_DES_ACTIVE_0 0x01C /* L2 switch Transmit descriptor reg */
+#else
#define FEC_R_DES_ACTIVE_0 0x010 /* Receive descriptor reg */
#define FEC_X_DES_ACTIVE_0 0x014 /* Transmit descriptor reg */
+#endif
#define FEC_ECNTRL 0x024 /* Ethernet control reg */
#define FEC_MII_DATA 0x040 /* MII manage frame reg */
#define FEC_MII_SPEED 0x044 /* MII speed control reg */
@@ -59,8 +64,13 @@
#define FEC_R_DES_START_2 0x16c /* Receive descriptor ring 2 */
#define FEC_X_DES_START_2 0x170 /* Transmit descriptor ring 2 */
#define FEC_R_BUFF_SIZE_2 0x174 /* Maximum receive buff ring2 size */
+#ifdef CONFIG_NET_DSA_MTIP_L2SW
+#define FEC_R_DES_START_0 0x0C /* L2 switch Receive descriptor ring */
+#define FEC_X_DES_START_0 0x10 /* L2 switch Transmit descriptor ring */
+#else
#define FEC_R_DES_START_0 0x180 /* Receive descriptor ring */
#define FEC_X_DES_START_0 0x184 /* Transmit descriptor ring */
+#endif
#define FEC_R_BUFF_SIZE_0 0x188 /* Maximum receive buff size */
#define FEC_R_FIFO_RSFL 0x190 /* Receive FIFO section full threshold */
#define FEC_R_FIFO_RSEM 0x194 /* Receive FIFO section empty threshold */
@@ -363,13 +373,25 @@ struct bufdesc_ex {
#define FEC_ENET_BABR ((uint)0x40000000) /* Babbling receiver */
#define FEC_ENET_BABT ((uint)0x20000000) /* Babbling transmitter */
#define FEC_ENET_GRA ((uint)0x10000000) /* Graceful stop complete */
+#ifdef CONFIG_NET_DSA_MTIP_L2SW
+#define FEC_ENET_TXF_0 BIT(4)
+#define FEC_ENET_TXF_1 0
+#define FEC_ENET_TXF_2 0
+#else
#define FEC_ENET_TXF_0 ((uint)0x08000000) /* Full frame transmitted */
#define FEC_ENET_TXF_1 ((uint)0x00000008) /* Full frame transmitted */
#define FEC_ENET_TXF_2 ((uint)0x00000080) /* Full frame transmitted */
+#endif
#define FEC_ENET_TXB ((uint)0x04000000) /* A buffer was transmitted */
+#ifdef CONFIG_NET_DSA_MTIP_L2SW
+#define FEC_ENET_RXF_0 BIT(2)
+#define FEC_ENET_RXF_1 0
+#define FEC_ENET_RXF_2 0
+#else
#define FEC_ENET_RXF_0 ((uint)0x02000000) /* Full frame received */
#define FEC_ENET_RXF_1 ((uint)0x00000002) /* Full frame received */
#define FEC_ENET_RXF_2 ((uint)0x00000020) /* Full frame received */
+#endif
#define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */
#define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */
#define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */
@@ -380,6 +402,7 @@ struct bufdesc_ex {
#define FEC_ENET_TS_TIMER ((uint)0x00008000)
#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF)
+#define FEC_NAPI_IMASK FEC_ENET_MII
#define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF))
/* ENET interrupt coalescing macro define */
@@ -587,9 +610,25 @@ struct fec_enet_private {
int pps_enable;
unsigned int next_counter;
+ /* More Than IP L2 switch */
+ void __iomem *hwpsw;
+ bool l2switch;
+
u64 ethtool_stats[];
};
+/* L2 switch */
+#define L2SW_CTRL_REG_OFFSET (0x4)
+/* Get proper base address for either L2 switch or MAC ENET */
+static inline void __iomem *fec_hwp(struct fec_enet_private *fep)
+{
+#ifdef CONFIG_NET_DSA_MTIP_L2SW
+ if (fep->l2switch)
+ return fep->hwpsw;
+#endif
+ return fep->hwp;
+}
+
void fec_ptp_init(struct platform_device *pdev, int irq_idx);
void fec_ptp_stop(struct platform_device *pdev);
void fec_ptp_start_cyclecounter(struct net_device *ndev);
@@ -65,10 +65,12 @@
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <soc/imx/cpuidle.h>
+#include <net/dsa.h>
#include <asm/cacheflush.h>
#include "fec.h"
+#include "../../dsa/mtip-l2switch.h"
static void set_multicast_list(struct net_device *ndev);
static void fec_enet_itr_coal_init(struct net_device *ndev);
@@ -103,6 +105,11 @@ static const struct fec_devinfo fec_imx28_info = {
FEC_QUIRK_HAS_FRREG,
};
+static const struct fec_devinfo fec_imx28l2sw_info = {
+ .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME |
+ FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_FRREG,
+};
+
static const struct fec_devinfo fec_imx6q_info = {
.quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
@@ -144,6 +151,9 @@ static struct platform_device_id fec_devtype[] = {
}, {
.name = "imx28-fec",
.driver_data = (kernel_ulong_t)&fec_imx28_info,
+ }, {
+ .name = "imx28-l2switch",
+ .driver_data = (kernel_ulong_t)&fec_imx28l2sw_info,
}, {
.name = "imx6q-fec",
.driver_data = (kernel_ulong_t)&fec_imx6q_info,
@@ -166,6 +176,7 @@ enum imx_fec_type {
IMX25_FEC = 1, /* runs on i.mx25/50/53 */
IMX27_FEC, /* runs on i.mx27/35/51 */
IMX28_FEC,
+ IMX28_L2SWITCH,
IMX6Q_FEC,
MVF600_FEC,
IMX6SX_FEC,
@@ -176,6 +187,8 @@ static const struct of_device_id fec_dt_ids[] = {
{ .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], },
{ .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], },
{ .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], },
+ { .compatible = "fsl,imx28-l2switch",
+ .data = &fec_devtype[IMX28_L2SWITCH], },
{ .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], },
{ .compatible = "fsl,mvf600-fec", .data = &fec_devtype[MVF600_FEC], },
{ .compatible = "fsl,imx6sx-fec", .data = &fec_devtype[IMX6SX_FEC], },
@@ -893,7 +906,7 @@ static void fec_enet_enable_ring(struct net_device *ndev)
for (i = 0; i < fep->num_rx_queues; i++) {
rxq = fep->rx_queue[i];
- writel(rxq->bd.dma, fep->hwp + FEC_R_DES_START(i));
+ writel(rxq->bd.dma, fec_hwp(fep) + FEC_R_DES_START(i));
writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_R_BUFF_SIZE(i));
/* enable DMA1/2 */
@@ -904,7 +917,7 @@ static void fec_enet_enable_ring(struct net_device *ndev)
for (i = 0; i < fep->num_tx_queues; i++) {
txq = fep->tx_queue[i];
- writel(txq->bd.dma, fep->hwp + FEC_X_DES_START(i));
+ writel(txq->bd.dma, fec_hwp(fep) + FEC_X_DES_START(i));
/* enable DMA1/2 */
if (i)
@@ -1078,6 +1091,29 @@ fec_restart(struct net_device *ndev)
}
#endif /* !defined(CONFIG_M5272) */
+ if (fep->l2switch) {
+ /*
+ * Set PROMISC mode for MAC0/1 - it is necessary for L2 switch
+ * correct operation.
+ */
+ rcntl |= MCF_FEC_RCR_PROM;
+
+ /*
+ * In fec_restart, the FEC_R_CNTRL register is only configured
+ * for MAC0 controller (when 'mac1' DTS node is NOT enabled).
+ *
+ * For L2 switch we configure only one DTS node - 'eth_switch'
+ * so the same value shall be written to MAC1 corresponding
+ * register.
+ *
+ * The port configuration is finished in the DSA's 'link_adjust'
+ * callback - which also modifies FEC_R_CNTRL register to set
+ * proper link speed.
+ */
+ writel(rcntl,
+ fep->hwp + ENET_L2_SW_PORT1_OFFSET + FEC_R_CNTRL);
+ }
+
writel(rcntl, fep->hwp + FEC_R_CNTRL);
/* Setup multicast filter. */
@@ -1109,8 +1145,12 @@ fec_restart(struct net_device *ndev)
if (fep->bufdesc_ex)
fec_ptp_start_cyclecounter(ndev);
- /* Enable interrupts we wish to service */
- if (fep->link)
+ /*
+ * Enable interrupts we wish to service - for L2 switch only
+ * MII interrupts are required for MAC{01} to allow proper
+ * PHY configuration.
+ */
+ if (!fep->l2switch && fep->link)
writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
else
writel(0, fep->hwp + FEC_IMASK);
@@ -1407,7 +1447,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
unsigned short status;
struct sk_buff *skb_new = NULL;
struct sk_buff *skb;
- ushort pkt_len;
+ ushort pkt_len, l2pl;
__u8 *data;
int pkt_received = 0;
struct bufdesc_ex *ebdp = NULL;
@@ -1433,7 +1473,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
break;
pkt_received++;
- writel(FEC_ENET_RXF, fep->hwp + FEC_IEVENT);
+ writel(FEC_ENET_RXF, fec_hwp(fep) + FEC_IEVENT);
/* Check for errors. */
status ^= BD_ENET_RX_LAST;
@@ -1473,7 +1513,16 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
* include that when passing upstream as it messes up
* bridging applications.
*/
- is_copybreak = fec_enet_copybreak(ndev, &skb, bdp, pkt_len - 4,
+ /*
+ * When the L2 switch support is enabled the FCS is removed
+ * by it and hence the pkt_len shall be passed without
+ * substracting 4 bytes.
+ */
+ l2pl = pkt_len - 4;
+ if (fep->l2switch)
+ l2pl = pkt_len;
+
+ is_copybreak = fec_enet_copybreak(ndev, &skb, bdp, l2pl,
need_swap);
if (!is_copybreak) {
skb_new = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
@@ -1488,7 +1537,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
}
prefetch(skb->data - NET_IP_ALIGN);
- skb_put(skb, pkt_len - 4);
+ skb_put(skb, l2pl);
data = skb->data;
if (!is_copybreak && need_swap)
@@ -1605,12 +1654,12 @@ static bool fec_enet_collect_events(struct fec_enet_private *fep)
{
uint int_events;
- int_events = readl(fep->hwp + FEC_IEVENT);
+ int_events = readl(fec_hwp(fep) + FEC_IEVENT);
/* Don't clear MDIO events, we poll for those */
int_events &= ~FEC_ENET_MII;
- writel(int_events, fep->hwp + FEC_IEVENT);
+ writel(int_events, fec_hwp(fep) + FEC_IEVENT);
return int_events != 0;
}
@@ -1635,6 +1684,27 @@ fec_enet_interrupt(int irq, void *dev_id)
return ret;
}
+#ifdef CONFIG_NET_DSA_MTIP_L2SW
+static irqreturn_t
+l2switch_interrupt_handler(int irq, void *dev_id)
+{
+ struct net_device *ndev = dev_id;
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ irqreturn_t ret = IRQ_NONE;
+
+ if (fec_enet_collect_events(fep) && fep->link) {
+ ret = IRQ_HANDLED;
+
+ if (napi_schedule_prep(&fep->napi)) {
+ /* Disable NAPI interrupts */
+ writel(0, fep->hwpsw + FEC_IMASK);
+ __napi_schedule(&fep->napi);
+ }
+ }
+ return ret;
+}
+#endif
+
static int fec_enet_rx_napi(struct napi_struct *napi, int budget)
{
struct net_device *ndev = napi->dev;
@@ -1648,7 +1718,7 @@ static int fec_enet_rx_napi(struct napi_struct *napi, int budget)
if (done < budget) {
napi_complete_done(napi, done);
- writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
+ writel(FEC_DEFAULT_IMASK, fec_hwp(fep) + FEC_IMASK);
}
return done;
@@ -3065,6 +3135,10 @@ static void set_multicast_list(struct net_device *ndev)
unsigned char hash;
unsigned int hash_high = 0, hash_low = 0;
+ /* DSA subsystem will handle multicast/broadcast setup for L2 switch */
+ if (fep->l2switch)
+ return;
+
if (ndev->flags & IFF_PROMISC) {
tmp = readl(fep->hwp + FEC_R_CNTRL);
tmp |= 0x8;
@@ -3279,7 +3353,8 @@ static int fec_enet_init(struct net_device *ndev)
rxq->bd.dma = bd_dma;
rxq->bd.dsize = dsize;
rxq->bd.dsize_log2 = dsize_log2;
- rxq->bd.reg_desc_active = fep->hwp + offset_des_active_rxq[i];
+ rxq->bd.reg_desc_active =
+ fec_hwp(fep) + offset_des_active_rxq[i];
bd_dma += size;
cbd_base = (struct bufdesc *)(((void *)cbd_base) + size);
rxq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize);
@@ -3295,7 +3370,8 @@ static int fec_enet_init(struct net_device *ndev)
txq->bd.dma = bd_dma;
txq->bd.dsize = dsize;
txq->bd.dsize_log2 = dsize_log2;
- txq->bd.reg_desc_active = fep->hwp + offset_des_active_txq[i];
+ txq->bd.reg_desc_active =
+ fec_hwp(fep) + offset_des_active_txq[i];
bd_dma += size;
cbd_base = (struct bufdesc *)(((void *)cbd_base) + size);
txq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize);
@@ -3306,8 +3382,7 @@ static int fec_enet_init(struct net_device *ndev)
ndev->watchdog_timeo = TX_TIMEOUT;
ndev->netdev_ops = &fec_netdev_ops;
ndev->ethtool_ops = &fec_enet_ethtool_ops;
-
- writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK);
+ writel(FEC_RX_DISABLED_IMASK, fec_hwp(fep) + FEC_IMASK);
netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, NAPI_POLL_WEIGHT);
if (fep->quirks & FEC_QUIRK_HAS_VLAN)
@@ -3544,6 +3619,23 @@ fec_probe(struct platform_device *pdev)
fep->pdev = pdev;
fep->dev_id = dev_id++;
+ if (of_device_is_compatible(np, "fsl,imx28-l2switch")) {
+ fep->l2switch = true;
+ fep->hwpsw = devm_platform_ioremap_resource(pdev, 1);
+ /*
+ * MAC{01} interrupt and descriptors registers have 4 bytes
+ * offset when compared to L2 switch IP block.
+ *
+ * When L2 switch is added "between" ENET (eth0) and MAC{01}
+ * the code for interrupts and setting up descriptors is
+ * reused.
+ *
+ * As for example FEC_IMASK are used also on MAC{01} to
+ * perform MII transmission it is better to subtract the
+ * offset from the outset and reuse defines.
+ */
+ fep->hwpsw -= L2SW_CTRL_REG_OFFSET;
+ }
platform_set_drvdata(pdev, ndev);
if ((of_machine_is_compatible("fsl,imx6q") ||
@@ -3669,8 +3761,16 @@ fec_probe(struct platform_device *pdev)
ret = irq;
goto failed_irq;
}
- ret = devm_request_irq(&pdev->dev, irq, fec_enet_interrupt,
- 0, pdev->name, ndev);
+#ifdef CONFIG_NET_DSA_MTIP_L2SW
+ if (fep->l2switch && i == 0)
+ ret = devm_request_irq(&pdev->dev, irq,
+ l2switch_interrupt_handler,
+ 0, "l2switch", ndev);
+ else
+#endif
+ ret = devm_request_irq(&pdev->dev, irq,
+ fec_enet_interrupt,
+ 0, pdev->name, ndev);
if (ret)
goto failed_irq;
@@ -3683,7 +3783,16 @@ fec_probe(struct platform_device *pdev)
/* Carrier starts down, phylib will bring it up */
netif_carrier_off(ndev);
- fec_enet_clk_enable(ndev, false);
+ /*
+ * For L2 switch proper initialization this clk cannot be disabled,
+ * as it uses is to access shared MAC registers for proper MDIO
+ * operation.
+ *
+ * DSA switch will also use it afterwards to setup its ports.
+ */
+ if (!fep->l2switch)
+ fec_enet_clk_enable(ndev, false);
+
pinctrl_pm_select_sleep_state(&pdev->dev);
ndev->max_mtu = PKT_MAXBUF_SIZE - ETH_HLEN - ETH_FCS_LEN;