Merge branch 'kmemleak' of git://linux-arm.org/linux-2.6
[linux-2.6] / drivers / staging / octeon / ethernet-mdio.c
1 /**********************************************************************
2  * Author: Cavium Networks
3  *
4  * Contact: support@caviumnetworks.com
5  * This file is part of the OCTEON SDK
6  *
7  * Copyright (c) 2003-2007 Cavium Networks
8  *
9  * This file is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License, Version 2, as
11  * published by the Free Software Foundation.
12  *
13  * This file is distributed in the hope that it will be useful, but
14  * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
15  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
16  * NONINFRINGEMENT.  See the GNU General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this file; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22  * or visit http://www.gnu.org/licenses/.
23  *
24  * This file may also be available under a different license from Cavium.
25  * Contact Cavium Networks for more information
26 **********************************************************************/
27 #include <linux/kernel.h>
28 #include <linux/ethtool.h>
29 #include <linux/mii.h>
30 #include <net/dst.h>
31
32 #include <asm/octeon/octeon.h>
33
34 #include "ethernet-defines.h"
35 #include "octeon-ethernet.h"
36 #include "ethernet-mdio.h"
37
38 #include "cvmx-helper-board.h"
39
40 #include "cvmx-smix-defs.h"
41
42 DECLARE_MUTEX(mdio_sem);
43
44 /**
45  * Perform an MII read. Called by the generic MII routines
46  *
47  * @dev:      Device to perform read for
48  * @phy_id:   The MII phy id
49  * @location: Register location to read
50  * Returns Result from the read or zero on failure
51  */
52 static int cvm_oct_mdio_read(struct net_device *dev, int phy_id, int location)
53 {
54         union cvmx_smix_cmd smi_cmd;
55         union cvmx_smix_rd_dat smi_rd;
56
57         smi_cmd.u64 = 0;
58         smi_cmd.s.phy_op = 1;
59         smi_cmd.s.phy_adr = phy_id;
60         smi_cmd.s.reg_adr = location;
61         cvmx_write_csr(CVMX_SMIX_CMD(0), smi_cmd.u64);
62
63         do {
64                 if (!in_interrupt())
65                         yield();
66                 smi_rd.u64 = cvmx_read_csr(CVMX_SMIX_RD_DAT(0));
67         } while (smi_rd.s.pending);
68
69         if (smi_rd.s.val)
70                 return smi_rd.s.dat;
71         else
72                 return 0;
73 }
74
75 static int cvm_oct_mdio_dummy_read(struct net_device *dev, int phy_id,
76                                    int location)
77 {
78         return 0xffff;
79 }
80
81 /**
82  * Perform an MII write. Called by the generic MII routines
83  *
84  * @dev:      Device to perform write for
85  * @phy_id:   The MII phy id
86  * @location: Register location to write
87  * @val:      Value to write
88  */
89 static void cvm_oct_mdio_write(struct net_device *dev, int phy_id, int location,
90                                int val)
91 {
92         union cvmx_smix_cmd smi_cmd;
93         union cvmx_smix_wr_dat smi_wr;
94
95         smi_wr.u64 = 0;
96         smi_wr.s.dat = val;
97         cvmx_write_csr(CVMX_SMIX_WR_DAT(0), smi_wr.u64);
98
99         smi_cmd.u64 = 0;
100         smi_cmd.s.phy_op = 0;
101         smi_cmd.s.phy_adr = phy_id;
102         smi_cmd.s.reg_adr = location;
103         cvmx_write_csr(CVMX_SMIX_CMD(0), smi_cmd.u64);
104
105         do {
106                 if (!in_interrupt())
107                         yield();
108                 smi_wr.u64 = cvmx_read_csr(CVMX_SMIX_WR_DAT(0));
109         } while (smi_wr.s.pending);
110 }
111
112 static void cvm_oct_mdio_dummy_write(struct net_device *dev, int phy_id,
113                                      int location, int val)
114 {
115 }
116
117 static void cvm_oct_get_drvinfo(struct net_device *dev,
118                                 struct ethtool_drvinfo *info)
119 {
120         strcpy(info->driver, "cavium-ethernet");
121         strcpy(info->version, OCTEON_ETHERNET_VERSION);
122         strcpy(info->bus_info, "Builtin");
123 }
124
125 static int cvm_oct_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
126 {
127         struct octeon_ethernet *priv = netdev_priv(dev);
128         int ret;
129
130         down(&mdio_sem);
131         ret = mii_ethtool_gset(&priv->mii_info, cmd);
132         up(&mdio_sem);
133
134         return ret;
135 }
136
137 static int cvm_oct_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
138 {
139         struct octeon_ethernet *priv = netdev_priv(dev);
140         int ret;
141
142         down(&mdio_sem);
143         ret = mii_ethtool_sset(&priv->mii_info, cmd);
144         up(&mdio_sem);
145
146         return ret;
147 }
148
149 static int cvm_oct_nway_reset(struct net_device *dev)
150 {
151         struct octeon_ethernet *priv = netdev_priv(dev);
152         int ret;
153
154         down(&mdio_sem);
155         ret = mii_nway_restart(&priv->mii_info);
156         up(&mdio_sem);
157
158         return ret;
159 }
160
161 static u32 cvm_oct_get_link(struct net_device *dev)
162 {
163         struct octeon_ethernet *priv = netdev_priv(dev);
164         u32 ret;
165
166         down(&mdio_sem);
167         ret = mii_link_ok(&priv->mii_info);
168         up(&mdio_sem);
169
170         return ret;
171 }
172
173 struct ethtool_ops cvm_oct_ethtool_ops = {
174         .get_drvinfo = cvm_oct_get_drvinfo,
175         .get_settings = cvm_oct_get_settings,
176         .set_settings = cvm_oct_set_settings,
177         .nway_reset = cvm_oct_nway_reset,
178         .get_link = cvm_oct_get_link,
179         .get_sg = ethtool_op_get_sg,
180         .get_tx_csum = ethtool_op_get_tx_csum,
181 };
182
183 /**
184  * IOCTL support for PHY control
185  *
186  * @dev:    Device to change
187  * @rq:     the request
188  * @cmd:    the command
189  * Returns Zero on success
190  */
191 int cvm_oct_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
192 {
193         struct octeon_ethernet *priv = netdev_priv(dev);
194         struct mii_ioctl_data *data = if_mii(rq);
195         unsigned int duplex_chg;
196         int ret;
197
198         down(&mdio_sem);
199         ret = generic_mii_ioctl(&priv->mii_info, data, cmd, &duplex_chg);
200         up(&mdio_sem);
201
202         return ret;
203 }
204
205 /**
206  * Setup the MDIO device structures
207  *
208  * @dev:    Device to setup
209  *
210  * Returns Zero on success, negative on failure
211  */
212 int cvm_oct_mdio_setup_device(struct net_device *dev)
213 {
214         struct octeon_ethernet *priv = netdev_priv(dev);
215         int phy_id = cvmx_helper_board_get_mii_address(priv->port);
216         if (phy_id != -1) {
217                 priv->mii_info.dev = dev;
218                 priv->mii_info.phy_id = phy_id;
219                 priv->mii_info.phy_id_mask = 0xff;
220                 priv->mii_info.supports_gmii = 1;
221                 priv->mii_info.reg_num_mask = 0x1f;
222                 priv->mii_info.mdio_read = cvm_oct_mdio_read;
223                 priv->mii_info.mdio_write = cvm_oct_mdio_write;
224         } else {
225                 /* Supply dummy MDIO routines so the kernel won't crash
226                    if the user tries to read them */
227                 priv->mii_info.mdio_read = cvm_oct_mdio_dummy_read;
228                 priv->mii_info.mdio_write = cvm_oct_mdio_dummy_write;
229         }
230         return 0;
231 }