1df482650SStephan Linz /*
2df482650SStephan Linz * Xilinx xps_ll_temac ethernet driver for u-boot
3df482650SStephan Linz *
4df482650SStephan Linz * supports SDMA or FIFO access and MDIO bus communication
5df482650SStephan Linz *
6df482650SStephan Linz * Copyright (C) 2011 - 2012 Stephan Linz <linz@li-pro.net>
7df482650SStephan Linz * Copyright (C) 2008 - 2011 Michal Simek <monstr@monstr.eu>
8df482650SStephan Linz * Copyright (C) 2008 - 2011 PetaLogix
9df482650SStephan Linz *
10df482650SStephan Linz * Based on Yoshio Kashiwagi kashiwagi@co-nss.co.jp driver
11df482650SStephan Linz * Copyright (C) 2008 Nissin Systems Co.,Ltd.
12df482650SStephan Linz * March 2008 created
13df482650SStephan Linz *
141a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+
15df482650SStephan Linz *
16df482650SStephan Linz * [0]: http://www.xilinx.com/support/documentation
17df482650SStephan Linz *
18df482650SStephan Linz * [S]: [0]/ip_documentation/xps_ll_temac.pdf
19df482650SStephan Linz * [A]: [0]/application_notes/xapp1041.pdf
20df482650SStephan Linz */
21df482650SStephan Linz
22df482650SStephan Linz #include <config.h>
23df482650SStephan Linz #include <common.h>
24df482650SStephan Linz #include <net.h>
25df482650SStephan Linz #include <netdev.h>
26df482650SStephan Linz #include <malloc.h>
27df482650SStephan Linz #include <asm/io.h>
28df482650SStephan Linz #include <miiphy.h>
29df482650SStephan Linz
30df482650SStephan Linz #include "xilinx_ll_temac.h"
31df482650SStephan Linz #include "xilinx_ll_temac_fifo.h"
32df482650SStephan Linz #include "xilinx_ll_temac_sdma.h"
33df482650SStephan Linz #include "xilinx_ll_temac_mdio.h"
34df482650SStephan Linz
35df482650SStephan Linz #if !defined(CONFIG_MII)
36df482650SStephan Linz # error "LL_TEMAC requires MII -- missing CONFIG_MII"
37df482650SStephan Linz #endif
38df482650SStephan Linz
39df482650SStephan Linz #if !defined(CONFIG_PHYLIB)
40df482650SStephan Linz # error "LL_TEMAC requires PHYLIB -- missing CONFIG_PHYLIB"
41df482650SStephan Linz #endif
42df482650SStephan Linz
43df482650SStephan Linz struct ll_temac_info {
44df482650SStephan Linz int flags;
45df482650SStephan Linz unsigned long base_addr;
46df482650SStephan Linz unsigned long ctrl_addr;
47df482650SStephan Linz char *devname;
48df482650SStephan Linz unsigned int phyaddr;
49df482650SStephan Linz char *mdio_busname;
50df482650SStephan Linz };
51df482650SStephan Linz
52df482650SStephan Linz /* Ethernet interface ready status */
ll_temac_check_status(struct temac_reg * regs,u32 mask)53df482650SStephan Linz int ll_temac_check_status(struct temac_reg *regs, u32 mask)
54df482650SStephan Linz {
55df482650SStephan Linz unsigned timeout = 50; /* 1usec * 50 = 50usec */
56df482650SStephan Linz
57df482650SStephan Linz /*
58df482650SStephan Linz * Quote from LL TEMAC documentation: The bits in the RDY
59df482650SStephan Linz * register are asserted when there is no access in progress.
60df482650SStephan Linz * When an access is in progress, a bit corresponding to the
61df482650SStephan Linz * type of access is automatically de-asserted. The bit is
62df482650SStephan Linz * automatically re-asserted when the access is complete.
63df482650SStephan Linz */
64df482650SStephan Linz while (timeout && (!(in_be32(®s->rdy) & mask))) {
65df482650SStephan Linz timeout--;
66df482650SStephan Linz udelay(1);
67df482650SStephan Linz }
68df482650SStephan Linz
69df482650SStephan Linz if (!timeout) {
70df482650SStephan Linz printf("%s: Timeout on 0x%08x @%p\n", __func__,
71df482650SStephan Linz mask, ®s->rdy);
72df482650SStephan Linz return 1;
73df482650SStephan Linz }
74df482650SStephan Linz
75df482650SStephan Linz return 0;
76df482650SStephan Linz }
77df482650SStephan Linz
78df482650SStephan Linz /*
79df482650SStephan Linz * Indirect write to ll_temac.
80df482650SStephan Linz *
81df482650SStephan Linz * http://www.xilinx.com/support/documentation/ip_documentation/xps_ll_temac.pdf
82df482650SStephan Linz * page 23, second paragraph, The use of CTL0 register or CTL1 register
83df482650SStephan Linz */
ll_temac_indirect_set(struct temac_reg * regs,u16 regn,u32 reg_data)84df482650SStephan Linz int ll_temac_indirect_set(struct temac_reg *regs, u16 regn, u32 reg_data)
85df482650SStephan Linz {
86df482650SStephan Linz out_be32(®s->lsw, (reg_data & MLSW_MASK));
87df482650SStephan Linz out_be32(®s->ctl, CTL_WEN | (regn & CTL_ADDR_MASK));
88df482650SStephan Linz
89df482650SStephan Linz if (ll_temac_check_status(regs, RSE_CFG_WR))
90df482650SStephan Linz return 0;
91df482650SStephan Linz
92df482650SStephan Linz return 1;
93df482650SStephan Linz }
94df482650SStephan Linz
95df482650SStephan Linz /*
96df482650SStephan Linz * Indirect read from ll_temac.
97df482650SStephan Linz *
98df482650SStephan Linz * http://www.xilinx.com/support/documentation/ip_documentation/xps_ll_temac.pdf
99df482650SStephan Linz * page 23, second paragraph, The use of CTL0 register or CTL1 register
100df482650SStephan Linz */
ll_temac_indirect_get(struct temac_reg * regs,u16 regn,u32 * reg_data)101df482650SStephan Linz int ll_temac_indirect_get(struct temac_reg *regs, u16 regn, u32* reg_data)
102df482650SStephan Linz {
103df482650SStephan Linz out_be32(®s->ctl, (regn & CTL_ADDR_MASK));
104df482650SStephan Linz
105df482650SStephan Linz if (ll_temac_check_status(regs, RSE_CFG_RR))
106df482650SStephan Linz return 0;
107df482650SStephan Linz
108df482650SStephan Linz *reg_data = in_be32(®s->lsw) & MLSW_MASK;
109df482650SStephan Linz return 1;
110df482650SStephan Linz }
111df482650SStephan Linz
112df482650SStephan Linz /* setting sub-controller and ll_temac to proper setting */
ll_temac_setup_ctrl(struct eth_device * dev)113df482650SStephan Linz static int ll_temac_setup_ctrl(struct eth_device *dev)
114df482650SStephan Linz {
115df482650SStephan Linz struct ll_temac *ll_temac = dev->priv;
116df482650SStephan Linz struct temac_reg *regs = (struct temac_reg *)dev->iobase;
117df482650SStephan Linz
118df482650SStephan Linz if (ll_temac->ctrlreset && ll_temac->ctrlreset(dev))
119df482650SStephan Linz return 0;
120df482650SStephan Linz
121df482650SStephan Linz if (ll_temac->ctrlinit && ll_temac->ctrlinit(dev))
122df482650SStephan Linz return 0;
123df482650SStephan Linz
124df482650SStephan Linz /* Promiscuous mode disable */
125df482650SStephan Linz if (!ll_temac_indirect_set(regs, TEMAC_AFM, 0))
126df482650SStephan Linz return 0;
127df482650SStephan Linz
128df482650SStephan Linz /* Enable Receiver - RX bit */
129df482650SStephan Linz if (!ll_temac_indirect_set(regs, TEMAC_RCW1, RCW1_RX))
130df482650SStephan Linz return 0;
131df482650SStephan Linz
132df482650SStephan Linz /* Enable Transmitter - TX bit */
133df482650SStephan Linz if (!ll_temac_indirect_set(regs, TEMAC_TC, TC_TX))
134df482650SStephan Linz return 0;
135df482650SStephan Linz
136df482650SStephan Linz return 1;
137df482650SStephan Linz }
138df482650SStephan Linz
139df482650SStephan Linz /*
140df482650SStephan Linz * Configure ll_temac based on negotiated speed and duplex
141df482650SStephan Linz * reported by PHY handling code
142df482650SStephan Linz */
ll_temac_adjust_link(struct eth_device * dev)143df482650SStephan Linz static int ll_temac_adjust_link(struct eth_device *dev)
144df482650SStephan Linz {
145df482650SStephan Linz unsigned int speed, emmc_reg;
146df482650SStephan Linz struct temac_reg *regs = (struct temac_reg *)dev->iobase;
147df482650SStephan Linz struct ll_temac *ll_temac = dev->priv;
148df482650SStephan Linz struct phy_device *phydev = ll_temac->phydev;
149df482650SStephan Linz
150df482650SStephan Linz if (!phydev->link) {
151df482650SStephan Linz printf("%s: No link.\n", phydev->dev->name);
152df482650SStephan Linz return 0;
153df482650SStephan Linz }
154df482650SStephan Linz
155df482650SStephan Linz switch (phydev->speed) {
156df482650SStephan Linz case 1000:
157df482650SStephan Linz speed = EMMC_LSPD_1000;
158df482650SStephan Linz break;
159df482650SStephan Linz case 100:
160df482650SStephan Linz speed = EMMC_LSPD_100;
161df482650SStephan Linz break;
162df482650SStephan Linz case 10:
163df482650SStephan Linz speed = EMMC_LSPD_10;
164df482650SStephan Linz break;
165df482650SStephan Linz default:
166df482650SStephan Linz return 0;
167df482650SStephan Linz }
168df482650SStephan Linz
169df482650SStephan Linz if (!ll_temac_indirect_get(regs, TEMAC_EMMC, &emmc_reg))
170df482650SStephan Linz return 0;
171df482650SStephan Linz
172df482650SStephan Linz emmc_reg &= ~EMMC_LSPD_MASK;
173df482650SStephan Linz emmc_reg |= speed;
174df482650SStephan Linz
175df482650SStephan Linz if (!ll_temac_indirect_set(regs, TEMAC_EMMC, emmc_reg))
176df482650SStephan Linz return 0;
177df482650SStephan Linz
178df482650SStephan Linz printf("%s: PHY is %s with %dbase%s, %s%s\n",
179df482650SStephan Linz dev->name, phydev->drv->name,
180df482650SStephan Linz phydev->speed, (phydev->port == PORT_TP) ? "T" : "X",
181df482650SStephan Linz (phydev->duplex) ? "FDX" : "HDX",
182df482650SStephan Linz (phydev->port == PORT_OTHER) ? ", unkown mode" : "");
183df482650SStephan Linz
184df482650SStephan Linz return 1;
185df482650SStephan Linz }
186df482650SStephan Linz
187df482650SStephan Linz /* setup mac addr */
ll_temac_setup_mac_addr(struct eth_device * dev)188df482650SStephan Linz static int ll_temac_setup_mac_addr(struct eth_device *dev)
189df482650SStephan Linz {
190df482650SStephan Linz struct temac_reg *regs = (struct temac_reg *)dev->iobase;
191df482650SStephan Linz u32 val;
192df482650SStephan Linz
193df482650SStephan Linz /* set up unicast MAC address filter */
194df482650SStephan Linz val = ((dev->enetaddr[3] << 24) | (dev->enetaddr[2] << 16) |
195df482650SStephan Linz (dev->enetaddr[1] << 8) | (dev->enetaddr[0]));
196df482650SStephan Linz val &= UAW0_UADDR_MASK;
197df482650SStephan Linz
198df482650SStephan Linz if (!ll_temac_indirect_set(regs, TEMAC_UAW0, val))
199df482650SStephan Linz return 1;
200df482650SStephan Linz
201df482650SStephan Linz val = ((dev->enetaddr[5] << 8) | dev->enetaddr[4]);
202df482650SStephan Linz val &= UAW1_UADDR_MASK;
203df482650SStephan Linz
204df482650SStephan Linz if (!ll_temac_indirect_set(regs, TEMAC_UAW1, val))
205df482650SStephan Linz return 1;
206df482650SStephan Linz
207df482650SStephan Linz return 0;
208df482650SStephan Linz }
209df482650SStephan Linz
210df482650SStephan Linz /* halt device */
ll_temac_halt(struct eth_device * dev)211df482650SStephan Linz static void ll_temac_halt(struct eth_device *dev)
212df482650SStephan Linz {
213df482650SStephan Linz struct ll_temac *ll_temac = dev->priv;
214df482650SStephan Linz struct temac_reg *regs = (struct temac_reg *)dev->iobase;
215df482650SStephan Linz
216df482650SStephan Linz /* Disable Receiver */
217df482650SStephan Linz ll_temac_indirect_set(regs, TEMAC_RCW0, 0);
218df482650SStephan Linz
219df482650SStephan Linz /* Disable Transmitter */
220df482650SStephan Linz ll_temac_indirect_set(regs, TEMAC_TC, 0);
221df482650SStephan Linz
222df482650SStephan Linz if (ll_temac->ctrlhalt)
223df482650SStephan Linz ll_temac->ctrlhalt(dev);
224df482650SStephan Linz
225df482650SStephan Linz /* Shut down the PHY, as needed */
226df482650SStephan Linz phy_shutdown(ll_temac->phydev);
227df482650SStephan Linz }
228df482650SStephan Linz
ll_temac_init(struct eth_device * dev,bd_t * bis)229df482650SStephan Linz static int ll_temac_init(struct eth_device *dev, bd_t *bis)
230df482650SStephan Linz {
231df482650SStephan Linz struct ll_temac *ll_temac = dev->priv;
23211af8d65STimur Tabi int ret;
233df482650SStephan Linz
234527cd43dSMichal Simek printf("%s: Xilinx XPS LocalLink Tri-Mode Ether MAC #%d at 0x%08lx.\n",
235df482650SStephan Linz dev->name, dev->index, dev->iobase);
236df482650SStephan Linz
237df482650SStephan Linz if (!ll_temac_setup_ctrl(dev))
238df482650SStephan Linz return -1;
239df482650SStephan Linz
240df482650SStephan Linz /* Start up the PHY */
24111af8d65STimur Tabi ret = phy_startup(ll_temac->phydev);
24211af8d65STimur Tabi if (ret) {
24311af8d65STimur Tabi printf("%s: Could not initialize PHY %s\n",
24411af8d65STimur Tabi dev->name, ll_temac->phydev->dev->name);
24511af8d65STimur Tabi return ret;
24611af8d65STimur Tabi }
247df482650SStephan Linz
248df482650SStephan Linz if (!ll_temac_adjust_link(dev)) {
249df482650SStephan Linz ll_temac_halt(dev);
250df482650SStephan Linz return -1;
251df482650SStephan Linz }
252df482650SStephan Linz
253df482650SStephan Linz /* If there's no link, fail */
254df482650SStephan Linz return ll_temac->phydev->link ? 0 : -1;
255df482650SStephan Linz }
256df482650SStephan Linz
257df482650SStephan Linz /*
258df482650SStephan Linz * Discover which PHY is attached to the device, and configure it
259df482650SStephan Linz * properly. If the PHY is not recognized, then return 0
260df482650SStephan Linz * (failure). Otherwise, return 1
261df482650SStephan Linz */
ll_temac_phy_init(struct eth_device * dev)262df482650SStephan Linz static int ll_temac_phy_init(struct eth_device *dev)
263df482650SStephan Linz {
264df482650SStephan Linz struct ll_temac *ll_temac = dev->priv;
265df482650SStephan Linz struct phy_device *phydev;
266df482650SStephan Linz unsigned int supported = PHY_GBIT_FEATURES;
267df482650SStephan Linz
268df482650SStephan Linz /* interface - look at driver/net/tsec.c */
269df482650SStephan Linz phydev = phy_connect(ll_temac->bus, ll_temac->phyaddr,
270df482650SStephan Linz dev, PHY_INTERFACE_MODE_NONE);
271df482650SStephan Linz
272df482650SStephan Linz phydev->supported &= supported;
273df482650SStephan Linz phydev->advertising = phydev->supported;
274df482650SStephan Linz
275df482650SStephan Linz ll_temac->phydev = phydev;
276df482650SStephan Linz
277df482650SStephan Linz phy_config(phydev);
278df482650SStephan Linz
279df482650SStephan Linz return 1;
280df482650SStephan Linz }
281df482650SStephan Linz
282df482650SStephan Linz /*
283df482650SStephan Linz * Initialize a single ll_temac devices
284df482650SStephan Linz *
285df482650SStephan Linz * Returns the result of ll_temac phy interface that were initialized
286df482650SStephan Linz */
xilinx_ll_temac_initialize(bd_t * bis,struct ll_temac_info * devinf)287df482650SStephan Linz int xilinx_ll_temac_initialize(bd_t *bis, struct ll_temac_info *devinf)
288df482650SStephan Linz {
289df482650SStephan Linz struct eth_device *dev;
290df482650SStephan Linz struct ll_temac *ll_temac;
291df482650SStephan Linz
292df482650SStephan Linz dev = calloc(1, sizeof(*dev));
293df482650SStephan Linz if (dev == NULL)
294df482650SStephan Linz return 0;
295df482650SStephan Linz
296df482650SStephan Linz ll_temac = calloc(1, sizeof(struct ll_temac));
297df482650SStephan Linz if (ll_temac == NULL) {
298df482650SStephan Linz free(dev);
299df482650SStephan Linz return 0;
300df482650SStephan Linz }
301df482650SStephan Linz
302df482650SStephan Linz /* use given name or generate its own unique name */
303df482650SStephan Linz if (devinf->devname) {
30434921d04SStephan Linz strncpy(dev->name, devinf->devname, sizeof(dev->name));
305df482650SStephan Linz } else {
306*21909bafSRicardo Ribalda Delgado snprintf(dev->name, sizeof(dev->name), "ll_tem.%lx",
307*21909bafSRicardo Ribalda Delgado devinf->base_addr);
308df482650SStephan Linz devinf->devname = dev->name;
309df482650SStephan Linz }
310df482650SStephan Linz
311df482650SStephan Linz dev->iobase = devinf->base_addr;
312df482650SStephan Linz
313df482650SStephan Linz dev->priv = ll_temac;
314df482650SStephan Linz dev->init = ll_temac_init;
315df482650SStephan Linz dev->halt = ll_temac_halt;
316df482650SStephan Linz dev->write_hwaddr = ll_temac_setup_mac_addr;
317df482650SStephan Linz
318df482650SStephan Linz ll_temac->ctrladdr = devinf->ctrl_addr;
319df482650SStephan Linz if (devinf->flags & XILINX_LL_TEMAC_M_SDMA_PLB) {
320df482650SStephan Linz ll_temac_collect_xlplb_sdma_reg_addr(dev);
321df482650SStephan Linz ll_temac->in32 = ll_temac_xlplb_in32;
322df482650SStephan Linz ll_temac->out32 = ll_temac_xlplb_out32;
323df482650SStephan Linz ll_temac->ctrlinit = ll_temac_init_sdma;
324df482650SStephan Linz ll_temac->ctrlhalt = ll_temac_halt_sdma;
325df482650SStephan Linz ll_temac->ctrlreset = ll_temac_reset_sdma;
326df482650SStephan Linz dev->recv = ll_temac_recv_sdma;
327df482650SStephan Linz dev->send = ll_temac_send_sdma;
328df482650SStephan Linz } else {
329df482650SStephan Linz ll_temac->in32 = NULL;
330df482650SStephan Linz ll_temac->out32 = NULL;
331df482650SStephan Linz ll_temac->ctrlinit = NULL;
332df482650SStephan Linz ll_temac->ctrlhalt = NULL;
333df482650SStephan Linz ll_temac->ctrlreset = ll_temac_reset_fifo;
334df482650SStephan Linz dev->recv = ll_temac_recv_fifo;
335df482650SStephan Linz dev->send = ll_temac_send_fifo;
336df482650SStephan Linz }
337df482650SStephan Linz
338df482650SStephan Linz /* Link to specified MDIO bus */
339df482650SStephan Linz strncpy(ll_temac->mdio_busname, devinf->mdio_busname, MDIO_NAME_LEN);
340df482650SStephan Linz ll_temac->bus = miiphy_get_dev_by_name(ll_temac->mdio_busname);
341df482650SStephan Linz
342df482650SStephan Linz /* Looking for a valid PHY address if it is not yet set */
343df482650SStephan Linz if (devinf->phyaddr == -1)
344df482650SStephan Linz ll_temac->phyaddr = ll_temac_phy_addr(ll_temac->bus);
345df482650SStephan Linz else
346df482650SStephan Linz ll_temac->phyaddr = devinf->phyaddr;
347df482650SStephan Linz
348df482650SStephan Linz eth_register(dev);
349df482650SStephan Linz
350df482650SStephan Linz /* Try to initialize PHY here, and return */
351df482650SStephan Linz return ll_temac_phy_init(dev);
352df482650SStephan Linz }
353df482650SStephan Linz
354df482650SStephan Linz /*
355df482650SStephan Linz * Initialize a single ll_temac device with its mdio bus behind ll_temac
356df482650SStephan Linz *
357df482650SStephan Linz * Returns 1 if the ll_temac device and the mdio bus were initialized
358df482650SStephan Linz * otherwise returns 0
359df482650SStephan Linz */
xilinx_ll_temac_eth_init(bd_t * bis,unsigned long base_addr,int flags,unsigned long ctrl_addr)360df482650SStephan Linz int xilinx_ll_temac_eth_init(bd_t *bis, unsigned long base_addr, int flags,
361df482650SStephan Linz unsigned long ctrl_addr)
362df482650SStephan Linz {
363df482650SStephan Linz struct ll_temac_info devinf;
364df482650SStephan Linz struct ll_temac_mdio_info mdioinf;
365df482650SStephan Linz int ret;
366df482650SStephan Linz
367df482650SStephan Linz /* prepare the internal driver informations */
368df482650SStephan Linz devinf.flags = flags;
369df482650SStephan Linz devinf.base_addr = base_addr;
370df482650SStephan Linz devinf.ctrl_addr = ctrl_addr;
371df482650SStephan Linz devinf.devname = NULL;
372df482650SStephan Linz devinf.phyaddr = -1;
373df482650SStephan Linz
374df482650SStephan Linz mdioinf.name = devinf.mdio_busname = NULL;
375df482650SStephan Linz mdioinf.regs = (struct temac_reg *)devinf.base_addr;
376df482650SStephan Linz
377df482650SStephan Linz ret = xilinx_ll_temac_mdio_initialize(bis, &mdioinf);
378df482650SStephan Linz if (ret >= 0) {
379df482650SStephan Linz
380df482650SStephan Linz /*
381df482650SStephan Linz * If there was no MDIO bus name then take over the
382df482650SStephan Linz * new automaticaly generated by the MDIO init code.
383df482650SStephan Linz */
384df482650SStephan Linz if (mdioinf.name != devinf.mdio_busname)
385df482650SStephan Linz devinf.mdio_busname = mdioinf.name;
386df482650SStephan Linz
387df482650SStephan Linz ret = xilinx_ll_temac_initialize(bis, &devinf);
388df482650SStephan Linz if (ret > 0)
389df482650SStephan Linz return 1;
390df482650SStephan Linz
391df482650SStephan Linz }
392df482650SStephan Linz
393df482650SStephan Linz return 0;
394df482650SStephan Linz }
395