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