aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorScott Wood <scottwood@freescale.com>2007-10-01 15:20:56 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2007-10-10 19:54:03 -0400
commite2ec4581adf7e288c193e981c39ca01cdb20a272 (patch)
tree354c0afb64104457d2690e656f2d7ae883ff5722 /drivers/net
parent976de6a8c304dcc43e38efcb8a0bace7866b6242 (diff)
Generic bitbanged MDIO library
Previously, bitbanged MDIO was only supported in individual hardware-specific drivers. This code factors out the higher level protocol implementation, reducing the hardware-specific portion to functions setting direction, data, and clock. Signed-off-by: Scott Wood <scottwood@freescale.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/phy/Kconfig9
-rw-r--r--drivers/net/phy/Makefile1
-rw-r--r--drivers/net/phy/mdio-bitbang.c187
3 files changed, 197 insertions, 0 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 432c210513be..54b2ba996640 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -90,4 +90,13 @@ config FIXED_MII_AMNT
90 This control will have specified number allocated for each fixed 90 This control will have specified number allocated for each fixed
91 PHY type enabled. 91 PHY type enabled.
92 92
93config MDIO_BITBANG
94 tristate "Support for bitbanged MDIO buses"
95 help
96 This module implements the MDIO bus protocol in software,
97 for use by low level drivers that export the ability to
98 drive the relevant pins.
99
100 If in doubt, say N.
101
93endif # PHYLIB 102endif # PHYLIB
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 8885650647ff..3d6cc7b67a80 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_VITESSE_PHY) += vitesse.o
13obj-$(CONFIG_BROADCOM_PHY) += broadcom.o 13obj-$(CONFIG_BROADCOM_PHY) += broadcom.o
14obj-$(CONFIG_ICPLUS_PHY) += icplus.o 14obj-$(CONFIG_ICPLUS_PHY) += icplus.o
15obj-$(CONFIG_FIXED_PHY) += fixed.o 15obj-$(CONFIG_FIXED_PHY) += fixed.o
16obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o
diff --git a/drivers/net/phy/mdio-bitbang.c b/drivers/net/phy/mdio-bitbang.c
new file mode 100644
index 000000000000..8cd243d92af3
--- /dev/null
+++ b/drivers/net/phy/mdio-bitbang.c
@@ -0,0 +1,187 @@
1/*
2 * Bitbanged MDIO support.
3 *
4 * Author: Scott Wood <scottwood@freescale.com>
5 * Copyright (c) 2007 Freescale Semiconductor
6 *
7 * Based on CPM2 MDIO code which is:
8 *
9 * Copyright (c) 2003 Intracom S.A.
10 * by Pantelis Antoniou <panto@intracom.gr>
11 *
12 * 2005 (c) MontaVista Software, Inc.
13 * Vitaly Bordug <vbordug@ru.mvista.com>
14 *
15 * This file is licensed under the terms of the GNU General Public License
16 * version 2. This program is licensed "as is" without any warranty of any
17 * kind, whether express or implied.
18 */
19
20#include <linux/module.h>
21#include <linux/mdio-bitbang.h>
22#include <linux/slab.h>
23#include <linux/types.h>
24#include <linux/delay.h>
25
26#define MDIO_READ 1
27#define MDIO_WRITE 0
28
29#define MDIO_SETUP_TIME 10
30#define MDIO_HOLD_TIME 10
31
32/* Minimum MDC period is 400 ns, plus some margin for error. MDIO_DELAY
33 * is done twice per period.
34 */
35#define MDIO_DELAY 250
36
37/* The PHY may take up to 300 ns to produce data, plus some margin
38 * for error.
39 */
40#define MDIO_READ_DELAY 350
41
42/* MDIO must already be configured as output. */
43static void mdiobb_send_bit(struct mdiobb_ctrl *ctrl, int val)
44{
45 const struct mdiobb_ops *ops = ctrl->ops;
46
47 ops->set_mdio_data(ctrl, val);
48 ndelay(MDIO_DELAY);
49 ops->set_mdc(ctrl, 1);
50 ndelay(MDIO_DELAY);
51 ops->set_mdc(ctrl, 0);
52}
53
54/* MDIO must already be configured as input. */
55static int mdiobb_get_bit(struct mdiobb_ctrl *ctrl)
56{
57 const struct mdiobb_ops *ops = ctrl->ops;
58
59 ndelay(MDIO_DELAY);
60 ops->set_mdc(ctrl, 1);
61 ndelay(MDIO_READ_DELAY);
62 ops->set_mdc(ctrl, 0);
63
64 return ops->get_mdio_data(ctrl);
65}
66
67/* MDIO must already be configured as output. */
68static void mdiobb_send_num(struct mdiobb_ctrl *ctrl, u16 val, int bits)
69{
70 int i;
71
72 for (i = bits - 1; i >= 0; i--)
73 mdiobb_send_bit(ctrl, (val >> i) & 1);
74}
75
76/* MDIO must already be configured as input. */
77static u16 mdiobb_get_num(struct mdiobb_ctrl *ctrl, int bits)
78{
79 int i;
80 u16 ret = 0;
81
82 for (i = bits - 1; i >= 0; i--) {
83 ret <<= 1;
84 ret |= mdiobb_get_bit(ctrl);
85 }
86
87 return ret;
88}
89
90/* Utility to send the preamble, address, and
91 * register (common to read and write).
92 */
93static void mdiobb_cmd(struct mdiobb_ctrl *ctrl, int read, u8 phy, u8 reg)
94{
95 const struct mdiobb_ops *ops = ctrl->ops;
96 int i;
97
98 ops->set_mdio_dir(ctrl, 1);
99
100 /*
101 * Send a 32 bit preamble ('1's) with an extra '1' bit for good
102 * measure. The IEEE spec says this is a PHY optional
103 * requirement. The AMD 79C874 requires one after power up and
104 * one after a MII communications error. This means that we are
105 * doing more preambles than we need, but it is safer and will be
106 * much more robust.
107 */
108
109 for (i = 0; i < 32; i++)
110 mdiobb_send_bit(ctrl, 1);
111
112 /* send the start bit (01) and the read opcode (10) or write (10) */
113 mdiobb_send_bit(ctrl, 0);
114 mdiobb_send_bit(ctrl, 1);
115 mdiobb_send_bit(ctrl, read);
116 mdiobb_send_bit(ctrl, !read);
117
118 mdiobb_send_num(ctrl, phy, 5);
119 mdiobb_send_num(ctrl, reg, 5);
120}
121
122
123static int mdiobb_read(struct mii_bus *bus, int phy, int reg)
124{
125 struct mdiobb_ctrl *ctrl = bus->priv;
126 int ret, i;
127
128 mdiobb_cmd(ctrl, MDIO_READ, phy, reg);
129 ctrl->ops->set_mdio_dir(ctrl, 0);
130
131 /* check the turnaround bit: the PHY should be driving it to zero */
132 if (mdiobb_get_bit(ctrl) != 0) {
133 /* PHY didn't drive TA low -- flush any bits it
134 * may be trying to send.
135 */
136 for (i = 0; i < 32; i++)
137 mdiobb_get_bit(ctrl);
138
139 return 0xffff;
140 }
141
142 ret = mdiobb_get_num(ctrl, 16);
143 mdiobb_get_bit(ctrl);
144 return ret;
145}
146
147static int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val)
148{
149 struct mdiobb_ctrl *ctrl = bus->priv;
150
151 mdiobb_cmd(ctrl, MDIO_WRITE, phy, reg);
152
153 /* send the turnaround (10) */
154 mdiobb_send_bit(ctrl, 1);
155 mdiobb_send_bit(ctrl, 0);
156
157 mdiobb_send_num(ctrl, val, 16);
158
159 ctrl->ops->set_mdio_dir(ctrl, 0);
160 mdiobb_get_bit(ctrl);
161 return 0;
162}
163
164struct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl)
165{
166 struct mii_bus *bus;
167
168 bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL);
169 if (!bus)
170 return NULL;
171
172 __module_get(ctrl->ops->owner);
173
174 bus->read = mdiobb_read;
175 bus->write = mdiobb_write;
176 bus->priv = ctrl;
177
178 return bus;
179}
180
181void free_mdio_bitbang(struct mii_bus *bus)
182{
183 struct mdiobb_ctrl *ctrl = bus->priv;
184
185 module_put(ctrl->ops->owner);
186 kfree(bus);
187}