OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [net/] [phy/] [mdio-bitbang.c] - Blame information for rev 62

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
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. */
43
static 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. */
55
static 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. */
68
static 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. */
77
static 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
 */
93
static 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
 
123
static 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
 
147
static 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
 
164
struct 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
 
181
void 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
}
188
 
189
MODULE_LICENSE("GPL");

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.