xref: /openbmc/linux/drivers/net/ethernet/litex/litex_liteeth.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
1ee7da21aSJoel Stanley // SPDX-License-Identifier: GPL-2.0-or-later
2ee7da21aSJoel Stanley /*
3ee7da21aSJoel Stanley  * LiteX Liteeth Ethernet
4ee7da21aSJoel Stanley  *
5ee7da21aSJoel Stanley  * Copyright 2017 Joel Stanley <joel@jms.id.au>
6ee7da21aSJoel Stanley  *
7ee7da21aSJoel Stanley  */
8ee7da21aSJoel Stanley 
9ee7da21aSJoel Stanley #include <linux/etherdevice.h>
10ee7da21aSJoel Stanley #include <linux/interrupt.h>
11ee7da21aSJoel Stanley #include <linux/litex.h>
12ee7da21aSJoel Stanley #include <linux/module.h>
13ee7da21aSJoel Stanley #include <linux/of_net.h>
14ee7da21aSJoel Stanley #include <linux/platform_device.h>
15ee7da21aSJoel Stanley 
16ee7da21aSJoel Stanley #define LITEETH_WRITER_SLOT       0x00
17ee7da21aSJoel Stanley #define LITEETH_WRITER_LENGTH     0x04
18ee7da21aSJoel Stanley #define LITEETH_WRITER_ERRORS     0x08
19ee7da21aSJoel Stanley #define LITEETH_WRITER_EV_STATUS  0x0C
20ee7da21aSJoel Stanley #define LITEETH_WRITER_EV_PENDING 0x10
21ee7da21aSJoel Stanley #define LITEETH_WRITER_EV_ENABLE  0x14
22ee7da21aSJoel Stanley #define LITEETH_READER_START      0x18
23ee7da21aSJoel Stanley #define LITEETH_READER_READY      0x1C
24ee7da21aSJoel Stanley #define LITEETH_READER_LEVEL      0x20
25ee7da21aSJoel Stanley #define LITEETH_READER_SLOT       0x24
26ee7da21aSJoel Stanley #define LITEETH_READER_LENGTH     0x28
27ee7da21aSJoel Stanley #define LITEETH_READER_EV_STATUS  0x2C
28ee7da21aSJoel Stanley #define LITEETH_READER_EV_PENDING 0x30
29ee7da21aSJoel Stanley #define LITEETH_READER_EV_ENABLE  0x34
30ee7da21aSJoel Stanley #define LITEETH_PREAMBLE_CRC      0x38
31ee7da21aSJoel Stanley #define LITEETH_PREAMBLE_ERRORS   0x3C
32ee7da21aSJoel Stanley #define LITEETH_CRC_ERRORS        0x40
33ee7da21aSJoel Stanley 
34ee7da21aSJoel Stanley #define LITEETH_PHY_CRG_RESET     0x00
35ee7da21aSJoel Stanley #define LITEETH_MDIO_W            0x04
36ee7da21aSJoel Stanley #define LITEETH_MDIO_R            0x0C
37ee7da21aSJoel Stanley 
38ee7da21aSJoel Stanley #define DRV_NAME	"liteeth"
39ee7da21aSJoel Stanley 
40ee7da21aSJoel Stanley struct liteeth {
41ee7da21aSJoel Stanley 	void __iomem *base;
42ee7da21aSJoel Stanley 	struct net_device *netdev;
43ee7da21aSJoel Stanley 	struct device *dev;
44ee7da21aSJoel Stanley 	u32 slot_size;
45ee7da21aSJoel Stanley 
46ee7da21aSJoel Stanley 	/* Tx */
47ee7da21aSJoel Stanley 	u32 tx_slot;
48ee7da21aSJoel Stanley 	u32 num_tx_slots;
49ee7da21aSJoel Stanley 	void __iomem *tx_base;
50ee7da21aSJoel Stanley 
51ee7da21aSJoel Stanley 	/* Rx */
52ee7da21aSJoel Stanley 	u32 rx_slot;
53ee7da21aSJoel Stanley 	u32 num_rx_slots;
54ee7da21aSJoel Stanley 	void __iomem *rx_base;
55ee7da21aSJoel Stanley };
56ee7da21aSJoel Stanley 
liteeth_rx(struct net_device * netdev)57ee7da21aSJoel Stanley static int liteeth_rx(struct net_device *netdev)
58ee7da21aSJoel Stanley {
59ee7da21aSJoel Stanley 	struct liteeth *priv = netdev_priv(netdev);
60ee7da21aSJoel Stanley 	struct sk_buff *skb;
61ee7da21aSJoel Stanley 	unsigned char *data;
62ee7da21aSJoel Stanley 	u8 rx_slot;
63ee7da21aSJoel Stanley 	int len;
64ee7da21aSJoel Stanley 
65ee7da21aSJoel Stanley 	rx_slot = litex_read8(priv->base + LITEETH_WRITER_SLOT);
66ee7da21aSJoel Stanley 	len = litex_read32(priv->base + LITEETH_WRITER_LENGTH);
67ee7da21aSJoel Stanley 
68ee7da21aSJoel Stanley 	if (len == 0 || len > 2048)
69ee7da21aSJoel Stanley 		goto rx_drop;
70ee7da21aSJoel Stanley 
71ee7da21aSJoel Stanley 	skb = netdev_alloc_skb_ip_align(netdev, len);
72ee7da21aSJoel Stanley 	if (!skb) {
73ee7da21aSJoel Stanley 		netdev_err(netdev, "couldn't get memory\n");
74ee7da21aSJoel Stanley 		goto rx_drop;
75ee7da21aSJoel Stanley 	}
76ee7da21aSJoel Stanley 
77ee7da21aSJoel Stanley 	data = skb_put(skb, len);
78ee7da21aSJoel Stanley 	memcpy_fromio(data, priv->rx_base + rx_slot * priv->slot_size, len);
79ee7da21aSJoel Stanley 	skb->protocol = eth_type_trans(skb, netdev);
80ee7da21aSJoel Stanley 
81*18da174dSJisheng Zhang 	dev_sw_netstats_rx_add(netdev, len);
82ee7da21aSJoel Stanley 
83ee7da21aSJoel Stanley 	return netif_rx(skb);
84ee7da21aSJoel Stanley 
85ee7da21aSJoel Stanley rx_drop:
86ee7da21aSJoel Stanley 	netdev->stats.rx_dropped++;
87ee7da21aSJoel Stanley 	netdev->stats.rx_errors++;
88ee7da21aSJoel Stanley 
89ee7da21aSJoel Stanley 	return NET_RX_DROP;
90ee7da21aSJoel Stanley }
91ee7da21aSJoel Stanley 
liteeth_interrupt(int irq,void * dev_id)92ee7da21aSJoel Stanley static irqreturn_t liteeth_interrupt(int irq, void *dev_id)
93ee7da21aSJoel Stanley {
94ee7da21aSJoel Stanley 	struct net_device *netdev = dev_id;
95ee7da21aSJoel Stanley 	struct liteeth *priv = netdev_priv(netdev);
96ee7da21aSJoel Stanley 	u8 reg;
97ee7da21aSJoel Stanley 
98ee7da21aSJoel Stanley 	reg = litex_read8(priv->base + LITEETH_READER_EV_PENDING);
99ee7da21aSJoel Stanley 	if (reg) {
100ee7da21aSJoel Stanley 		if (netif_queue_stopped(netdev))
101ee7da21aSJoel Stanley 			netif_wake_queue(netdev);
102ee7da21aSJoel Stanley 		litex_write8(priv->base + LITEETH_READER_EV_PENDING, reg);
103ee7da21aSJoel Stanley 	}
104ee7da21aSJoel Stanley 
105ee7da21aSJoel Stanley 	reg = litex_read8(priv->base + LITEETH_WRITER_EV_PENDING);
106ee7da21aSJoel Stanley 	if (reg) {
107ee7da21aSJoel Stanley 		liteeth_rx(netdev);
108ee7da21aSJoel Stanley 		litex_write8(priv->base + LITEETH_WRITER_EV_PENDING, reg);
109ee7da21aSJoel Stanley 	}
110ee7da21aSJoel Stanley 
111ee7da21aSJoel Stanley 	return IRQ_HANDLED;
112ee7da21aSJoel Stanley }
113ee7da21aSJoel Stanley 
liteeth_open(struct net_device * netdev)114ee7da21aSJoel Stanley static int liteeth_open(struct net_device *netdev)
115ee7da21aSJoel Stanley {
116ee7da21aSJoel Stanley 	struct liteeth *priv = netdev_priv(netdev);
117ee7da21aSJoel Stanley 	int err;
118ee7da21aSJoel Stanley 
119ee7da21aSJoel Stanley 	/* Clear pending events */
120ee7da21aSJoel Stanley 	litex_write8(priv->base + LITEETH_WRITER_EV_PENDING, 1);
121ee7da21aSJoel Stanley 	litex_write8(priv->base + LITEETH_READER_EV_PENDING, 1);
122ee7da21aSJoel Stanley 
123ee7da21aSJoel Stanley 	err = request_irq(netdev->irq, liteeth_interrupt, 0, netdev->name, netdev);
124ee7da21aSJoel Stanley 	if (err) {
125ee7da21aSJoel Stanley 		netdev_err(netdev, "failed to request irq %d\n", netdev->irq);
126ee7da21aSJoel Stanley 		return err;
127ee7da21aSJoel Stanley 	}
128ee7da21aSJoel Stanley 
129ee7da21aSJoel Stanley 	/* Enable IRQs */
130ee7da21aSJoel Stanley 	litex_write8(priv->base + LITEETH_WRITER_EV_ENABLE, 1);
131ee7da21aSJoel Stanley 	litex_write8(priv->base + LITEETH_READER_EV_ENABLE, 1);
132ee7da21aSJoel Stanley 
133ee7da21aSJoel Stanley 	netif_carrier_on(netdev);
134ee7da21aSJoel Stanley 	netif_start_queue(netdev);
135ee7da21aSJoel Stanley 
136ee7da21aSJoel Stanley 	return 0;
137ee7da21aSJoel Stanley }
138ee7da21aSJoel Stanley 
liteeth_stop(struct net_device * netdev)139ee7da21aSJoel Stanley static int liteeth_stop(struct net_device *netdev)
140ee7da21aSJoel Stanley {
141ee7da21aSJoel Stanley 	struct liteeth *priv = netdev_priv(netdev);
142ee7da21aSJoel Stanley 
143ee7da21aSJoel Stanley 	netif_stop_queue(netdev);
144ee7da21aSJoel Stanley 	netif_carrier_off(netdev);
145ee7da21aSJoel Stanley 
146ee7da21aSJoel Stanley 	litex_write8(priv->base + LITEETH_WRITER_EV_ENABLE, 0);
147ee7da21aSJoel Stanley 	litex_write8(priv->base + LITEETH_READER_EV_ENABLE, 0);
148ee7da21aSJoel Stanley 
149ee7da21aSJoel Stanley 	free_irq(netdev->irq, netdev);
150ee7da21aSJoel Stanley 
151ee7da21aSJoel Stanley 	return 0;
152ee7da21aSJoel Stanley }
153ee7da21aSJoel Stanley 
liteeth_start_xmit(struct sk_buff * skb,struct net_device * netdev)15440662333SNathan Huckleberry static netdev_tx_t liteeth_start_xmit(struct sk_buff *skb,
15540662333SNathan Huckleberry 				      struct net_device *netdev)
156ee7da21aSJoel Stanley {
157ee7da21aSJoel Stanley 	struct liteeth *priv = netdev_priv(netdev);
158ee7da21aSJoel Stanley 	void __iomem *txbuffer;
159ee7da21aSJoel Stanley 
160ee7da21aSJoel Stanley 	if (!litex_read8(priv->base + LITEETH_READER_READY)) {
161ee7da21aSJoel Stanley 		if (net_ratelimit())
162ee7da21aSJoel Stanley 			netdev_err(netdev, "LITEETH_READER_READY not ready\n");
163ee7da21aSJoel Stanley 
164ee7da21aSJoel Stanley 		netif_stop_queue(netdev);
165ee7da21aSJoel Stanley 
166ee7da21aSJoel Stanley 		return NETDEV_TX_BUSY;
167ee7da21aSJoel Stanley 	}
168ee7da21aSJoel Stanley 
169ee7da21aSJoel Stanley 	/* Reject oversize packets */
170ee7da21aSJoel Stanley 	if (unlikely(skb->len > priv->slot_size)) {
171ee7da21aSJoel Stanley 		if (net_ratelimit())
172ee7da21aSJoel Stanley 			netdev_err(netdev, "tx packet too big\n");
173ee7da21aSJoel Stanley 
174ee7da21aSJoel Stanley 		dev_kfree_skb_any(skb);
175ee7da21aSJoel Stanley 		netdev->stats.tx_dropped++;
176ee7da21aSJoel Stanley 		netdev->stats.tx_errors++;
177ee7da21aSJoel Stanley 
178ee7da21aSJoel Stanley 		return NETDEV_TX_OK;
179ee7da21aSJoel Stanley 	}
180ee7da21aSJoel Stanley 
181ee7da21aSJoel Stanley 	txbuffer = priv->tx_base + priv->tx_slot * priv->slot_size;
182ee7da21aSJoel Stanley 	memcpy_toio(txbuffer, skb->data, skb->len);
183ee7da21aSJoel Stanley 	litex_write8(priv->base + LITEETH_READER_SLOT, priv->tx_slot);
184ee7da21aSJoel Stanley 	litex_write16(priv->base + LITEETH_READER_LENGTH, skb->len);
185ee7da21aSJoel Stanley 	litex_write8(priv->base + LITEETH_READER_START, 1);
186ee7da21aSJoel Stanley 
187*18da174dSJisheng Zhang 	dev_sw_netstats_tx_add(netdev, 1, skb->len);
188ee7da21aSJoel Stanley 
189ee7da21aSJoel Stanley 	priv->tx_slot = (priv->tx_slot + 1) % priv->num_tx_slots;
190ee7da21aSJoel Stanley 	dev_kfree_skb_any(skb);
191ee7da21aSJoel Stanley 
192ee7da21aSJoel Stanley 	return NETDEV_TX_OK;
193ee7da21aSJoel Stanley }
194ee7da21aSJoel Stanley 
195*18da174dSJisheng Zhang static void
liteeth_get_stats64(struct net_device * netdev,struct rtnl_link_stats64 * stats)196*18da174dSJisheng Zhang liteeth_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats)
197*18da174dSJisheng Zhang {
198*18da174dSJisheng Zhang 	netdev_stats_to_stats64(stats, &netdev->stats);
199*18da174dSJisheng Zhang 	dev_fetch_sw_netstats(stats, netdev->tstats);
200*18da174dSJisheng Zhang }
201*18da174dSJisheng Zhang 
202ee7da21aSJoel Stanley static const struct net_device_ops liteeth_netdev_ops = {
203ee7da21aSJoel Stanley 	.ndo_open		= liteeth_open,
204ee7da21aSJoel Stanley 	.ndo_stop		= liteeth_stop,
205*18da174dSJisheng Zhang 	.ndo_get_stats64	= liteeth_get_stats64,
206ee7da21aSJoel Stanley 	.ndo_start_xmit         = liteeth_start_xmit,
207ee7da21aSJoel Stanley };
208ee7da21aSJoel Stanley 
liteeth_setup_slots(struct liteeth * priv)209ee7da21aSJoel Stanley static void liteeth_setup_slots(struct liteeth *priv)
210ee7da21aSJoel Stanley {
211ee7da21aSJoel Stanley 	struct device_node *np = priv->dev->of_node;
212ee7da21aSJoel Stanley 	int err;
213ee7da21aSJoel Stanley 
214ee7da21aSJoel Stanley 	err = of_property_read_u32(np, "litex,rx-slots", &priv->num_rx_slots);
215ee7da21aSJoel Stanley 	if (err) {
216ee7da21aSJoel Stanley 		dev_dbg(priv->dev, "unable to get litex,rx-slots, using 2\n");
217ee7da21aSJoel Stanley 		priv->num_rx_slots = 2;
218ee7da21aSJoel Stanley 	}
219ee7da21aSJoel Stanley 
220ee7da21aSJoel Stanley 	err = of_property_read_u32(np, "litex,tx-slots", &priv->num_tx_slots);
221ee7da21aSJoel Stanley 	if (err) {
222ee7da21aSJoel Stanley 		dev_dbg(priv->dev, "unable to get litex,tx-slots, using 2\n");
223ee7da21aSJoel Stanley 		priv->num_tx_slots = 2;
224ee7da21aSJoel Stanley 	}
225ee7da21aSJoel Stanley 
226ee7da21aSJoel Stanley 	err = of_property_read_u32(np, "litex,slot-size", &priv->slot_size);
227ee7da21aSJoel Stanley 	if (err) {
228ee7da21aSJoel Stanley 		dev_dbg(priv->dev, "unable to get litex,slot-size, using 0x800\n");
229ee7da21aSJoel Stanley 		priv->slot_size = 0x800;
230ee7da21aSJoel Stanley 	}
231ee7da21aSJoel Stanley }
232ee7da21aSJoel Stanley 
liteeth_probe(struct platform_device * pdev)233ee7da21aSJoel Stanley static int liteeth_probe(struct platform_device *pdev)
234ee7da21aSJoel Stanley {
235ee7da21aSJoel Stanley 	struct net_device *netdev;
236ee7da21aSJoel Stanley 	void __iomem *buf_base;
237ee7da21aSJoel Stanley 	struct liteeth *priv;
238ee7da21aSJoel Stanley 	int irq, err;
239ee7da21aSJoel Stanley 
240ee7da21aSJoel Stanley 	netdev = devm_alloc_etherdev(&pdev->dev, sizeof(*priv));
241ee7da21aSJoel Stanley 	if (!netdev)
242ee7da21aSJoel Stanley 		return -ENOMEM;
243ee7da21aSJoel Stanley 
244ee7da21aSJoel Stanley 	SET_NETDEV_DEV(netdev, &pdev->dev);
245ee7da21aSJoel Stanley 	platform_set_drvdata(pdev, netdev);
246ee7da21aSJoel Stanley 
247ee7da21aSJoel Stanley 	priv = netdev_priv(netdev);
248ee7da21aSJoel Stanley 	priv->netdev = netdev;
249ee7da21aSJoel Stanley 	priv->dev = &pdev->dev;
250ee7da21aSJoel Stanley 
251*18da174dSJisheng Zhang 	netdev->tstats = devm_netdev_alloc_pcpu_stats(&pdev->dev,
252*18da174dSJisheng Zhang 						      struct pcpu_sw_netstats);
253*18da174dSJisheng Zhang 	if (!netdev->tstats)
254*18da174dSJisheng Zhang 		return -ENOMEM;
255*18da174dSJisheng Zhang 
256ee7da21aSJoel Stanley 	irq = platform_get_irq(pdev, 0);
257827beb77SXu Wang 	if (irq < 0)
258ee7da21aSJoel Stanley 		return irq;
259ee7da21aSJoel Stanley 	netdev->irq = irq;
260ee7da21aSJoel Stanley 
261464a5728SCai Huoqing 	priv->base = devm_platform_ioremap_resource_byname(pdev, "mac");
262ee7da21aSJoel Stanley 	if (IS_ERR(priv->base))
263ee7da21aSJoel Stanley 		return PTR_ERR(priv->base);
264ee7da21aSJoel Stanley 
265464a5728SCai Huoqing 	buf_base = devm_platform_ioremap_resource_byname(pdev, "buffer");
266ee7da21aSJoel Stanley 	if (IS_ERR(buf_base))
267ee7da21aSJoel Stanley 		return PTR_ERR(buf_base);
268ee7da21aSJoel Stanley 
269ee7da21aSJoel Stanley 	liteeth_setup_slots(priv);
270ee7da21aSJoel Stanley 
271ee7da21aSJoel Stanley 	/* Rx slots */
272ee7da21aSJoel Stanley 	priv->rx_base = buf_base;
273ee7da21aSJoel Stanley 	priv->rx_slot = 0;
274ee7da21aSJoel Stanley 
275ee7da21aSJoel Stanley 	/* Tx slots come after Rx slots */
276ee7da21aSJoel Stanley 	priv->tx_base = buf_base + priv->num_rx_slots * priv->slot_size;
277ee7da21aSJoel Stanley 	priv->tx_slot = 0;
278ee7da21aSJoel Stanley 
2799ca01b25SJakub Kicinski 	err = of_get_ethdev_address(pdev->dev.of_node, netdev);
280ee7da21aSJoel Stanley 	if (err)
281ee7da21aSJoel Stanley 		eth_hw_addr_random(netdev);
282ee7da21aSJoel Stanley 
283ee7da21aSJoel Stanley 	netdev->netdev_ops = &liteeth_netdev_ops;
284ee7da21aSJoel Stanley 
285ee7da21aSJoel Stanley 	err = register_netdev(netdev);
286ee7da21aSJoel Stanley 	if (err) {
287ee7da21aSJoel Stanley 		dev_err(&pdev->dev, "Failed to register netdev %d\n", err);
288ee7da21aSJoel Stanley 		return err;
289ee7da21aSJoel Stanley 	}
290ee7da21aSJoel Stanley 
291ee7da21aSJoel Stanley 	netdev_info(netdev, "irq %d slots: tx %d rx %d size %d\n",
292ee7da21aSJoel Stanley 		    netdev->irq, priv->num_tx_slots, priv->num_rx_slots, priv->slot_size);
293ee7da21aSJoel Stanley 
294ee7da21aSJoel Stanley 	return 0;
295ee7da21aSJoel Stanley }
296ee7da21aSJoel Stanley 
liteeth_remove(struct platform_device * pdev)297ee7da21aSJoel Stanley static int liteeth_remove(struct platform_device *pdev)
298ee7da21aSJoel Stanley {
299ee7da21aSJoel Stanley 	struct net_device *netdev = platform_get_drvdata(pdev);
300ee7da21aSJoel Stanley 
301ee7da21aSJoel Stanley 	unregister_netdev(netdev);
302ee7da21aSJoel Stanley 
303ee7da21aSJoel Stanley 	return 0;
304ee7da21aSJoel Stanley }
305ee7da21aSJoel Stanley 
306ee7da21aSJoel Stanley static const struct of_device_id liteeth_of_match[] = {
307ee7da21aSJoel Stanley 	{ .compatible = "litex,liteeth" },
308ee7da21aSJoel Stanley 	{ }
309ee7da21aSJoel Stanley };
310ee7da21aSJoel Stanley MODULE_DEVICE_TABLE(of, liteeth_of_match);
311ee7da21aSJoel Stanley 
312ee7da21aSJoel Stanley static struct platform_driver liteeth_driver = {
313ee7da21aSJoel Stanley 	.probe = liteeth_probe,
314ee7da21aSJoel Stanley 	.remove = liteeth_remove,
315ee7da21aSJoel Stanley 	.driver = {
316ee7da21aSJoel Stanley 		.name = DRV_NAME,
317ee7da21aSJoel Stanley 		.of_match_table = liteeth_of_match,
318ee7da21aSJoel Stanley 	},
319ee7da21aSJoel Stanley };
320ee7da21aSJoel Stanley module_platform_driver(liteeth_driver);
321ee7da21aSJoel Stanley 
322ee7da21aSJoel Stanley MODULE_AUTHOR("Joel Stanley <joel@jms.id.au>");
323ee7da21aSJoel Stanley MODULE_LICENSE("GPL");
324