1 // SPDX-License-Identifier: GPL-2.0-only
2 /* 10G controller driver for Samsung SoCs
4 * Copyright (C) 2013 Samsung Electronics Co., Ltd.
5 * http://www.samsung.com
7 * Author: Siva Reddy Kallam <siva.kallam@samsung.com>
10 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13 #include <linux/mii.h>
14 #include <linux/netdevice.h>
15 #include <linux/platform_device.h>
16 #include <linux/phy.h>
17 #include <linux/slab.h>
18 #include <linux/sxgbe_platform.h>
20 #include "sxgbe_common.h"
21 #include "sxgbe_reg.h"
23 #define SXGBE_SMA_WRITE_CMD 0x01 /* write command */
24 #define SXGBE_SMA_PREAD_CMD 0x02 /* post read increament address */
25 #define SXGBE_SMA_READ_CMD 0x03 /* read command */
26 #define SXGBE_SMA_SKIP_ADDRFRM 0x00040000 /* skip the address frame */
27 #define SXGBE_MII_BUSY 0x00400000 /* mii busy */
29 static int sxgbe_mdio_busy_wait(void __iomem
*ioaddr
, unsigned int mii_data
)
31 unsigned long fin_time
= jiffies
+ 3 * HZ
; /* 3 seconds */
33 while (!time_after(jiffies
, fin_time
)) {
34 if (!(readl(ioaddr
+ mii_data
) & SXGBE_MII_BUSY
))
42 static void sxgbe_mdio_ctrl_data(struct sxgbe_priv_data
*sp
, u32 cmd
,
47 reg
|= (cmd
<< 16) | SXGBE_SMA_SKIP_ADDRFRM
|
48 ((sp
->clk_csr
& 0x7) << 19) | SXGBE_MII_BUSY
;
49 writel(reg
, sp
->ioaddr
+ sp
->hw
->mii
.data
);
52 static void sxgbe_mdio_c45(struct sxgbe_priv_data
*sp
, u32 cmd
, int phyaddr
,
53 int devad
, int phyreg
, u16 phydata
)
57 /* set mdio address register */
58 reg
= (devad
& 0x1f) << 21;
59 reg
|= (phyaddr
<< 16) | (phyreg
& 0xffff);
60 writel(reg
, sp
->ioaddr
+ sp
->hw
->mii
.addr
);
62 sxgbe_mdio_ctrl_data(sp
, cmd
, phydata
);
65 static void sxgbe_mdio_c22(struct sxgbe_priv_data
*sp
, u32 cmd
, int phyaddr
,
66 int phyreg
, u16 phydata
)
70 writel(1 << phyaddr
, sp
->ioaddr
+ SXGBE_MDIO_CLAUSE22_PORT_REG
);
72 /* set mdio address register */
73 reg
= (phyaddr
<< 16) | (phyreg
& 0x1f);
74 writel(reg
, sp
->ioaddr
+ sp
->hw
->mii
.addr
);
76 sxgbe_mdio_ctrl_data(sp
, cmd
, phydata
);
79 static int sxgbe_mdio_access_c22(struct sxgbe_priv_data
*sp
, u32 cmd
,
80 int phyaddr
, int phyreg
, u16 phydata
)
82 const struct mii_regs
*mii
= &sp
->hw
->mii
;
85 rc
= sxgbe_mdio_busy_wait(sp
->ioaddr
, mii
->data
);
89 /* Ports 0-3 only support C22. */
93 sxgbe_mdio_c22(sp
, cmd
, phyaddr
, phyreg
, phydata
);
95 return sxgbe_mdio_busy_wait(sp
->ioaddr
, mii
->data
);
98 static int sxgbe_mdio_access_c45(struct sxgbe_priv_data
*sp
, u32 cmd
,
99 int phyaddr
, int devad
, int phyreg
,
102 const struct mii_regs
*mii
= &sp
->hw
->mii
;
105 rc
= sxgbe_mdio_busy_wait(sp
->ioaddr
, mii
->data
);
109 sxgbe_mdio_c45(sp
, cmd
, phyaddr
, devad
, phyreg
, phydata
);
111 return sxgbe_mdio_busy_wait(sp
->ioaddr
, mii
->data
);
115 * sxgbe_mdio_read_c22
116 * @bus: points to the mii_bus structure
117 * @phyaddr: address of phy port
118 * @phyreg: address of register with in phy register
119 * Description: this function used for C22 MDIO Read
121 static int sxgbe_mdio_read_c22(struct mii_bus
*bus
, int phyaddr
, int phyreg
)
123 struct net_device
*ndev
= bus
->priv
;
124 struct sxgbe_priv_data
*priv
= netdev_priv(ndev
);
127 rc
= sxgbe_mdio_access_c22(priv
, SXGBE_SMA_READ_CMD
, phyaddr
,
132 return readl(priv
->ioaddr
+ priv
->hw
->mii
.data
) & 0xffff;
136 * sxgbe_mdio_read_c45
137 * @bus: points to the mii_bus structure
138 * @phyaddr: address of phy port
139 * @devad: device (MMD) address
140 * @phyreg: address of register with in phy register
141 * Description: this function used for C45 MDIO Read
143 static int sxgbe_mdio_read_c45(struct mii_bus
*bus
, int phyaddr
, int devad
,
146 struct net_device
*ndev
= bus
->priv
;
147 struct sxgbe_priv_data
*priv
= netdev_priv(ndev
);
150 rc
= sxgbe_mdio_access_c45(priv
, SXGBE_SMA_READ_CMD
, phyaddr
,
155 return readl(priv
->ioaddr
+ priv
->hw
->mii
.data
) & 0xffff;
159 * sxgbe_mdio_write_c22
160 * @bus: points to the mii_bus structure
161 * @phyaddr: address of phy port
162 * @phyreg: address of phy registers
163 * @phydata: data to be written into phy register
164 * Description: this function is used for C22 MDIO write
166 static int sxgbe_mdio_write_c22(struct mii_bus
*bus
, int phyaddr
, int phyreg
,
169 struct net_device
*ndev
= bus
->priv
;
170 struct sxgbe_priv_data
*priv
= netdev_priv(ndev
);
172 return sxgbe_mdio_access_c22(priv
, SXGBE_SMA_WRITE_CMD
, phyaddr
, phyreg
,
177 * sxgbe_mdio_write_c45
178 * @bus: points to the mii_bus structure
179 * @phyaddr: address of phy port
180 * @phyreg: address of phy registers
181 * @devad: device (MMD) address
182 * @phydata: data to be written into phy register
183 * Description: this function is used for C45 MDIO write
185 static int sxgbe_mdio_write_c45(struct mii_bus
*bus
, int phyaddr
, int devad
,
186 int phyreg
, u16 phydata
)
188 struct net_device
*ndev
= bus
->priv
;
189 struct sxgbe_priv_data
*priv
= netdev_priv(ndev
);
191 return sxgbe_mdio_access_c45(priv
, SXGBE_SMA_WRITE_CMD
, phyaddr
,
192 devad
, phyreg
, phydata
);
195 int sxgbe_mdio_register(struct net_device
*ndev
)
197 struct mii_bus
*mdio_bus
;
198 struct sxgbe_priv_data
*priv
= netdev_priv(ndev
);
199 struct sxgbe_mdio_bus_data
*mdio_data
= priv
->plat
->mdio_bus_data
;
202 bool phy_found
= false;
205 /* allocate the new mdio bus */
206 mdio_bus
= mdiobus_alloc();
208 netdev_err(ndev
, "%s: mii bus allocation failed\n", __func__
);
213 irqlist
= mdio_data
->irqs
;
215 irqlist
= priv
->mii_irq
;
217 /* assign mii bus fields */
218 mdio_bus
->name
= "sxgbe";
219 mdio_bus
->read
= sxgbe_mdio_read_c22
;
220 mdio_bus
->write
= sxgbe_mdio_write_c22
;
221 mdio_bus
->read_c45
= sxgbe_mdio_read_c45
;
222 mdio_bus
->write_c45
= sxgbe_mdio_write_c45
;
223 snprintf(mdio_bus
->id
, MII_BUS_ID_SIZE
, "%s-%x",
224 mdio_bus
->name
, priv
->plat
->bus_id
);
225 mdio_bus
->priv
= ndev
;
226 mdio_bus
->phy_mask
= mdio_data
->phy_mask
;
227 mdio_bus
->parent
= priv
->device
;
229 /* register with kernel subsystem */
230 err
= mdiobus_register(mdio_bus
);
232 netdev_err(ndev
, "mdiobus register failed\n");
236 for (phy_addr
= 0; phy_addr
< PHY_MAX_ADDR
; phy_addr
++) {
237 struct phy_device
*phy
= mdiobus_get_phy(mdio_bus
, phy_addr
);
242 /* If an IRQ was provided to be assigned after
243 * the bus probe, do it here.
245 if ((mdio_data
->irqs
== NULL
) &&
246 (mdio_data
->probed_phy_irq
> 0)) {
247 irqlist
[phy_addr
] = mdio_data
->probed_phy_irq
;
248 phy
->irq
= mdio_data
->probed_phy_irq
;
251 /* If we're going to bind the MAC to this PHY bus,
252 * and no PHY number was provided to the MAC,
253 * use the one probed here.
255 if (priv
->plat
->phy_addr
== -1)
256 priv
->plat
->phy_addr
= phy_addr
;
258 act
= (priv
->plat
->phy_addr
== phy_addr
);
263 case PHY_MAC_INTERRUPT
:
267 sprintf(irq_num
, "%d", phy
->irq
);
271 netdev_info(ndev
, "PHY ID %08x at %d IRQ %s (%s)%s\n",
272 phy
->phy_id
, phy_addr
, irq_str
,
273 phydev_name(phy
), act
? " active" : "");
279 netdev_err(ndev
, "PHY not found\n");
283 priv
->mii
= mdio_bus
;
289 mdiobus_unregister(mdio_bus
);
291 mdiobus_free(mdio_bus
);
295 int sxgbe_mdio_unregister(struct net_device
*ndev
)
297 struct sxgbe_priv_data
*priv
= netdev_priv(ndev
);
302 mdiobus_unregister(priv
->mii
);
303 priv
->mii
->priv
= NULL
;
304 mdiobus_free(priv
->mii
);