1a9770eacSAndrew Lunn // SPDX-License-Identifier: GPL-2.0
2a9770eacSAndrew Lunn /*
3a9770eacSAndrew Lunn * Bitbanged MDIO support.
4a9770eacSAndrew Lunn *
5a9770eacSAndrew Lunn * Author: Scott Wood <scottwood@freescale.com>
6a9770eacSAndrew Lunn * Copyright (c) 2007 Freescale Semiconductor
7a9770eacSAndrew Lunn *
8a9770eacSAndrew Lunn * Based on CPM2 MDIO code which is:
9a9770eacSAndrew Lunn *
10a9770eacSAndrew Lunn * Copyright (c) 2003 Intracom S.A.
11a9770eacSAndrew Lunn * by Pantelis Antoniou <panto@intracom.gr>
12a9770eacSAndrew Lunn *
13a9770eacSAndrew Lunn * 2005 (c) MontaVista Software, Inc.
14a9770eacSAndrew Lunn * Vitaly Bordug <vbordug@ru.mvista.com>
15a9770eacSAndrew Lunn */
16a9770eacSAndrew Lunn
17a9770eacSAndrew Lunn #include <linux/delay.h>
181bf34366SCalvin Johnson #include <linux/mdio-bitbang.h>
191bf34366SCalvin Johnson #include <linux/module.h>
201bf34366SCalvin Johnson #include <linux/types.h>
21a9770eacSAndrew Lunn
22a9770eacSAndrew Lunn #define MDIO_READ 2
23a9770eacSAndrew Lunn #define MDIO_WRITE 1
24a9770eacSAndrew Lunn
25a9770eacSAndrew Lunn #define MDIO_C45 (1<<15)
26a9770eacSAndrew Lunn #define MDIO_C45_ADDR (MDIO_C45 | 0)
27a9770eacSAndrew Lunn #define MDIO_C45_READ (MDIO_C45 | 3)
28a9770eacSAndrew Lunn #define MDIO_C45_WRITE (MDIO_C45 | 1)
29a9770eacSAndrew Lunn
30a9770eacSAndrew Lunn #define MDIO_SETUP_TIME 10
31a9770eacSAndrew Lunn #define MDIO_HOLD_TIME 10
32a9770eacSAndrew Lunn
33a9770eacSAndrew Lunn /* Minimum MDC period is 400 ns, plus some margin for error. MDIO_DELAY
34a9770eacSAndrew Lunn * is done twice per period.
35a9770eacSAndrew Lunn */
36a9770eacSAndrew Lunn #define MDIO_DELAY 250
37a9770eacSAndrew Lunn
38a9770eacSAndrew Lunn /* The PHY may take up to 300 ns to produce data, plus some margin
39a9770eacSAndrew Lunn * for error.
40a9770eacSAndrew Lunn */
41a9770eacSAndrew Lunn #define MDIO_READ_DELAY 350
42a9770eacSAndrew Lunn
43a9770eacSAndrew Lunn /* MDIO must already be configured as output. */
mdiobb_send_bit(struct mdiobb_ctrl * ctrl,int val)44a9770eacSAndrew Lunn static void mdiobb_send_bit(struct mdiobb_ctrl *ctrl, int val)
45a9770eacSAndrew Lunn {
46a9770eacSAndrew Lunn const struct mdiobb_ops *ops = ctrl->ops;
47a9770eacSAndrew Lunn
48a9770eacSAndrew Lunn ops->set_mdio_data(ctrl, val);
49a9770eacSAndrew Lunn ndelay(MDIO_DELAY);
50a9770eacSAndrew Lunn ops->set_mdc(ctrl, 1);
51a9770eacSAndrew Lunn ndelay(MDIO_DELAY);
52a9770eacSAndrew Lunn ops->set_mdc(ctrl, 0);
53a9770eacSAndrew Lunn }
54a9770eacSAndrew Lunn
55a9770eacSAndrew Lunn /* MDIO must already be configured as input. */
mdiobb_get_bit(struct mdiobb_ctrl * ctrl)56a9770eacSAndrew Lunn static int mdiobb_get_bit(struct mdiobb_ctrl *ctrl)
57a9770eacSAndrew Lunn {
58a9770eacSAndrew Lunn const struct mdiobb_ops *ops = ctrl->ops;
59a9770eacSAndrew Lunn
60a9770eacSAndrew Lunn ndelay(MDIO_DELAY);
61a9770eacSAndrew Lunn ops->set_mdc(ctrl, 1);
62a9770eacSAndrew Lunn ndelay(MDIO_READ_DELAY);
63a9770eacSAndrew Lunn ops->set_mdc(ctrl, 0);
64a9770eacSAndrew Lunn
65a9770eacSAndrew Lunn return ops->get_mdio_data(ctrl);
66a9770eacSAndrew Lunn }
67a9770eacSAndrew Lunn
68a9770eacSAndrew Lunn /* MDIO must already be configured as output. */
mdiobb_send_num(struct mdiobb_ctrl * ctrl,u16 val,int bits)69a9770eacSAndrew Lunn static void mdiobb_send_num(struct mdiobb_ctrl *ctrl, u16 val, int bits)
70a9770eacSAndrew Lunn {
71a9770eacSAndrew Lunn int i;
72a9770eacSAndrew Lunn
73a9770eacSAndrew Lunn for (i = bits - 1; i >= 0; i--)
74a9770eacSAndrew Lunn mdiobb_send_bit(ctrl, (val >> i) & 1);
75a9770eacSAndrew Lunn }
76a9770eacSAndrew Lunn
77a9770eacSAndrew Lunn /* MDIO must already be configured as input. */
mdiobb_get_num(struct mdiobb_ctrl * ctrl,int bits)78a9770eacSAndrew Lunn static u16 mdiobb_get_num(struct mdiobb_ctrl *ctrl, int bits)
79a9770eacSAndrew Lunn {
80a9770eacSAndrew Lunn int i;
81a9770eacSAndrew Lunn u16 ret = 0;
82a9770eacSAndrew Lunn
83a9770eacSAndrew Lunn for (i = bits - 1; i >= 0; i--) {
84a9770eacSAndrew Lunn ret <<= 1;
85a9770eacSAndrew Lunn ret |= mdiobb_get_bit(ctrl);
86a9770eacSAndrew Lunn }
87a9770eacSAndrew Lunn
88a9770eacSAndrew Lunn return ret;
89a9770eacSAndrew Lunn }
90a9770eacSAndrew Lunn
91a9770eacSAndrew Lunn /* Utility to send the preamble, address, and
92a9770eacSAndrew Lunn * register (common to read and write).
93a9770eacSAndrew Lunn */
mdiobb_cmd(struct mdiobb_ctrl * ctrl,int op,u8 phy,u8 reg)94a9770eacSAndrew Lunn static void mdiobb_cmd(struct mdiobb_ctrl *ctrl, int op, u8 phy, u8 reg)
95a9770eacSAndrew Lunn {
96a9770eacSAndrew Lunn const struct mdiobb_ops *ops = ctrl->ops;
97a9770eacSAndrew Lunn int i;
98a9770eacSAndrew Lunn
99a9770eacSAndrew Lunn ops->set_mdio_dir(ctrl, 1);
100a9770eacSAndrew Lunn
101a9770eacSAndrew Lunn /*
102a9770eacSAndrew Lunn * Send a 32 bit preamble ('1's) with an extra '1' bit for good
103a9770eacSAndrew Lunn * measure. The IEEE spec says this is a PHY optional
104a9770eacSAndrew Lunn * requirement. The AMD 79C874 requires one after power up and
105a9770eacSAndrew Lunn * one after a MII communications error. This means that we are
106a9770eacSAndrew Lunn * doing more preambles than we need, but it is safer and will be
107a9770eacSAndrew Lunn * much more robust.
108a9770eacSAndrew Lunn */
109a9770eacSAndrew Lunn
110a9770eacSAndrew Lunn for (i = 0; i < 32; i++)
111a9770eacSAndrew Lunn mdiobb_send_bit(ctrl, 1);
112a9770eacSAndrew Lunn
113a9770eacSAndrew Lunn /* send the start bit (01) and the read opcode (10) or write (01).
114a9770eacSAndrew Lunn Clause 45 operation uses 00 for the start and 11, 10 for
115a9770eacSAndrew Lunn read/write */
116a9770eacSAndrew Lunn mdiobb_send_bit(ctrl, 0);
117a9770eacSAndrew Lunn if (op & MDIO_C45)
118a9770eacSAndrew Lunn mdiobb_send_bit(ctrl, 0);
119a9770eacSAndrew Lunn else
120a9770eacSAndrew Lunn mdiobb_send_bit(ctrl, 1);
121a9770eacSAndrew Lunn mdiobb_send_bit(ctrl, (op >> 1) & 1);
122a9770eacSAndrew Lunn mdiobb_send_bit(ctrl, (op >> 0) & 1);
123a9770eacSAndrew Lunn
124a9770eacSAndrew Lunn mdiobb_send_num(ctrl, phy, 5);
125a9770eacSAndrew Lunn mdiobb_send_num(ctrl, reg, 5);
126a9770eacSAndrew Lunn }
127a9770eacSAndrew Lunn
128a9770eacSAndrew Lunn /* In clause 45 mode all commands are prefixed by MDIO_ADDR to specify the
129a9770eacSAndrew Lunn lower 16 bits of the 21 bit address. This transfer is done identically to a
130002dd3deSAndrew Lunn MDIO_WRITE except for a different code. Theoretically clause 45 and normal
131002dd3deSAndrew Lunn devices can exist on the same bus. Normal devices should ignore the MDIO_ADDR
132a9770eacSAndrew Lunn phase. */
mdiobb_cmd_addr(struct mdiobb_ctrl * ctrl,int phy,int dev_addr,int reg)133002dd3deSAndrew Lunn static void mdiobb_cmd_addr(struct mdiobb_ctrl *ctrl, int phy, int dev_addr,
134002dd3deSAndrew Lunn int reg)
135a9770eacSAndrew Lunn {
136a9770eacSAndrew Lunn mdiobb_cmd(ctrl, MDIO_C45_ADDR, phy, dev_addr);
137a9770eacSAndrew Lunn
138a9770eacSAndrew Lunn /* send the turnaround (10) */
139a9770eacSAndrew Lunn mdiobb_send_bit(ctrl, 1);
140a9770eacSAndrew Lunn mdiobb_send_bit(ctrl, 0);
141a9770eacSAndrew Lunn
142a9770eacSAndrew Lunn mdiobb_send_num(ctrl, reg, 16);
143a9770eacSAndrew Lunn
144a9770eacSAndrew Lunn ctrl->ops->set_mdio_dir(ctrl, 0);
145a9770eacSAndrew Lunn mdiobb_get_bit(ctrl);
146a9770eacSAndrew Lunn }
147a9770eacSAndrew Lunn
mdiobb_read_common(struct mii_bus * bus,int phy)148002dd3deSAndrew Lunn static int mdiobb_read_common(struct mii_bus *bus, int phy)
149a9770eacSAndrew Lunn {
150a9770eacSAndrew Lunn struct mdiobb_ctrl *ctrl = bus->priv;
151a9770eacSAndrew Lunn int ret, i;
152a9770eacSAndrew Lunn
153a9770eacSAndrew Lunn ctrl->ops->set_mdio_dir(ctrl, 0);
154a9770eacSAndrew Lunn
155a9770eacSAndrew Lunn /* check the turnaround bit: the PHY should be driving it to zero, if this
156a9770eacSAndrew Lunn * PHY is listed in phy_ignore_ta_mask as having broken TA, skip that
157a9770eacSAndrew Lunn */
158a9770eacSAndrew Lunn if (mdiobb_get_bit(ctrl) != 0 &&
159a9770eacSAndrew Lunn !(bus->phy_ignore_ta_mask & (1 << phy))) {
160a9770eacSAndrew Lunn /* PHY didn't drive TA low -- flush any bits it
161a9770eacSAndrew Lunn * may be trying to send.
162a9770eacSAndrew Lunn */
163a9770eacSAndrew Lunn for (i = 0; i < 32; i++)
164a9770eacSAndrew Lunn mdiobb_get_bit(ctrl);
165a9770eacSAndrew Lunn
166a9770eacSAndrew Lunn return 0xffff;
167a9770eacSAndrew Lunn }
168a9770eacSAndrew Lunn
169a9770eacSAndrew Lunn ret = mdiobb_get_num(ctrl, 16);
170a9770eacSAndrew Lunn mdiobb_get_bit(ctrl);
171a9770eacSAndrew Lunn return ret;
172a9770eacSAndrew Lunn }
173a9770eacSAndrew Lunn
mdiobb_read_c22(struct mii_bus * bus,int phy,int reg)174002dd3deSAndrew Lunn int mdiobb_read_c22(struct mii_bus *bus, int phy, int reg)
175a9770eacSAndrew Lunn {
176a9770eacSAndrew Lunn struct mdiobb_ctrl *ctrl = bus->priv;
177a9770eacSAndrew Lunn
178002dd3deSAndrew Lunn mdiobb_cmd(ctrl, ctrl->op_c22_read, phy, reg);
179002dd3deSAndrew Lunn
180002dd3deSAndrew Lunn return mdiobb_read_common(bus, phy);
181002dd3deSAndrew Lunn }
182002dd3deSAndrew Lunn EXPORT_SYMBOL(mdiobb_read_c22);
183002dd3deSAndrew Lunn
mdiobb_read_c45(struct mii_bus * bus,int phy,int devad,int reg)184002dd3deSAndrew Lunn int mdiobb_read_c45(struct mii_bus *bus, int phy, int devad, int reg)
185002dd3deSAndrew Lunn {
186002dd3deSAndrew Lunn struct mdiobb_ctrl *ctrl = bus->priv;
187002dd3deSAndrew Lunn
188002dd3deSAndrew Lunn mdiobb_cmd_addr(ctrl, phy, devad, reg);
189*2572ce62SSerge Semin mdiobb_cmd(ctrl, MDIO_C45_READ, phy, devad);
190002dd3deSAndrew Lunn
191002dd3deSAndrew Lunn return mdiobb_read_common(bus, phy);
192002dd3deSAndrew Lunn }
193002dd3deSAndrew Lunn EXPORT_SYMBOL(mdiobb_read_c45);
194002dd3deSAndrew Lunn
mdiobb_write_common(struct mii_bus * bus,u16 val)195002dd3deSAndrew Lunn static int mdiobb_write_common(struct mii_bus *bus, u16 val)
196002dd3deSAndrew Lunn {
197002dd3deSAndrew Lunn struct mdiobb_ctrl *ctrl = bus->priv;
198a9770eacSAndrew Lunn
199a9770eacSAndrew Lunn /* send the turnaround (10) */
200a9770eacSAndrew Lunn mdiobb_send_bit(ctrl, 1);
201a9770eacSAndrew Lunn mdiobb_send_bit(ctrl, 0);
202a9770eacSAndrew Lunn
203a9770eacSAndrew Lunn mdiobb_send_num(ctrl, val, 16);
204a9770eacSAndrew Lunn
205a9770eacSAndrew Lunn ctrl->ops->set_mdio_dir(ctrl, 0);
206a9770eacSAndrew Lunn mdiobb_get_bit(ctrl);
207a9770eacSAndrew Lunn return 0;
208a9770eacSAndrew Lunn }
209002dd3deSAndrew Lunn
mdiobb_write_c22(struct mii_bus * bus,int phy,int reg,u16 val)210002dd3deSAndrew Lunn int mdiobb_write_c22(struct mii_bus *bus, int phy, int reg, u16 val)
211002dd3deSAndrew Lunn {
212002dd3deSAndrew Lunn struct mdiobb_ctrl *ctrl = bus->priv;
213002dd3deSAndrew Lunn
214002dd3deSAndrew Lunn mdiobb_cmd(ctrl, ctrl->op_c22_write, phy, reg);
215002dd3deSAndrew Lunn
216002dd3deSAndrew Lunn return mdiobb_write_common(bus, val);
217002dd3deSAndrew Lunn }
218002dd3deSAndrew Lunn EXPORT_SYMBOL(mdiobb_write_c22);
219002dd3deSAndrew Lunn
mdiobb_write_c45(struct mii_bus * bus,int phy,int devad,int reg,u16 val)220002dd3deSAndrew Lunn int mdiobb_write_c45(struct mii_bus *bus, int phy, int devad, int reg, u16 val)
221002dd3deSAndrew Lunn {
222002dd3deSAndrew Lunn struct mdiobb_ctrl *ctrl = bus->priv;
223002dd3deSAndrew Lunn
224002dd3deSAndrew Lunn mdiobb_cmd_addr(ctrl, phy, devad, reg);
225*2572ce62SSerge Semin mdiobb_cmd(ctrl, MDIO_C45_WRITE, phy, devad);
226002dd3deSAndrew Lunn
227002dd3deSAndrew Lunn return mdiobb_write_common(bus, val);
228002dd3deSAndrew Lunn }
229002dd3deSAndrew Lunn EXPORT_SYMBOL(mdiobb_write_c45);
230a9770eacSAndrew Lunn
alloc_mdio_bitbang(struct mdiobb_ctrl * ctrl)231a9770eacSAndrew Lunn struct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl)
232a9770eacSAndrew Lunn {
233a9770eacSAndrew Lunn struct mii_bus *bus;
234a9770eacSAndrew Lunn
235a9770eacSAndrew Lunn bus = mdiobus_alloc();
236a9770eacSAndrew Lunn if (!bus)
237a9770eacSAndrew Lunn return NULL;
238a9770eacSAndrew Lunn
239a9770eacSAndrew Lunn __module_get(ctrl->ops->owner);
240a9770eacSAndrew Lunn
241002dd3deSAndrew Lunn bus->read = mdiobb_read_c22;
242002dd3deSAndrew Lunn bus->write = mdiobb_write_c22;
243002dd3deSAndrew Lunn bus->read_c45 = mdiobb_read_c45;
244002dd3deSAndrew Lunn bus->write_c45 = mdiobb_write_c45;
245002dd3deSAndrew Lunn
246a9770eacSAndrew Lunn bus->priv = ctrl;
247800fcab8SAndrew Lunn if (!ctrl->override_op_c22) {
248800fcab8SAndrew Lunn ctrl->op_c22_read = MDIO_READ;
249800fcab8SAndrew Lunn ctrl->op_c22_write = MDIO_WRITE;
250800fcab8SAndrew Lunn }
251a9770eacSAndrew Lunn
252a9770eacSAndrew Lunn return bus;
253a9770eacSAndrew Lunn }
254a9770eacSAndrew Lunn EXPORT_SYMBOL(alloc_mdio_bitbang);
255a9770eacSAndrew Lunn
free_mdio_bitbang(struct mii_bus * bus)256a9770eacSAndrew Lunn void free_mdio_bitbang(struct mii_bus *bus)
257a9770eacSAndrew Lunn {
258a9770eacSAndrew Lunn struct mdiobb_ctrl *ctrl = bus->priv;
259a9770eacSAndrew Lunn
260a9770eacSAndrew Lunn module_put(ctrl->ops->owner);
261a9770eacSAndrew Lunn mdiobus_free(bus);
262a9770eacSAndrew Lunn }
263a9770eacSAndrew Lunn EXPORT_SYMBOL(free_mdio_bitbang);
264a9770eacSAndrew Lunn
265a9770eacSAndrew Lunn MODULE_LICENSE("GPL v2");
266