Commit | Line | Data |
---|---|---|
80ff0fd3 DD |
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> | |
f6ed1b3b DD |
29 | #include <linux/phy.h> |
30 | ||
80ff0fd3 DD |
31 | #include <net/dst.h> |
32 | ||
33 | #include <asm/octeon/octeon.h> | |
34 | ||
35 | #include "ethernet-defines.h" | |
36 | #include "octeon-ethernet.h" | |
37 | #include "ethernet-mdio.h" | |
f6ed1b3b | 38 | #include "ethernet-util.h" |
80ff0fd3 DD |
39 | |
40 | #include "cvmx-helper-board.h" | |
41 | ||
42 | #include "cvmx-smix-defs.h" | |
43 | ||
80ff0fd3 DD |
44 | static void cvm_oct_get_drvinfo(struct net_device *dev, |
45 | struct ethtool_drvinfo *info) | |
46 | { | |
47 | strcpy(info->driver, "cavium-ethernet"); | |
48 | strcpy(info->version, OCTEON_ETHERNET_VERSION); | |
49 | strcpy(info->bus_info, "Builtin"); | |
50 | } | |
51 | ||
52 | static int cvm_oct_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) | |
53 | { | |
54 | struct octeon_ethernet *priv = netdev_priv(dev); | |
80ff0fd3 | 55 | |
f6ed1b3b DD |
56 | if (priv->phydev) |
57 | return phy_ethtool_gset(priv->phydev, cmd); | |
80ff0fd3 | 58 | |
f6ed1b3b | 59 | return -EINVAL; |
80ff0fd3 DD |
60 | } |
61 | ||
62 | static int cvm_oct_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) | |
63 | { | |
64 | struct octeon_ethernet *priv = netdev_priv(dev); | |
80ff0fd3 | 65 | |
f6ed1b3b DD |
66 | if (!capable(CAP_NET_ADMIN)) |
67 | return -EPERM; | |
68 | ||
69 | if (priv->phydev) | |
70 | return phy_ethtool_sset(priv->phydev, cmd); | |
80ff0fd3 | 71 | |
f6ed1b3b | 72 | return -EINVAL; |
80ff0fd3 DD |
73 | } |
74 | ||
75 | static int cvm_oct_nway_reset(struct net_device *dev) | |
76 | { | |
77 | struct octeon_ethernet *priv = netdev_priv(dev); | |
80ff0fd3 | 78 | |
f6ed1b3b DD |
79 | if (!capable(CAP_NET_ADMIN)) |
80 | return -EPERM; | |
80ff0fd3 | 81 | |
f6ed1b3b DD |
82 | if (priv->phydev) |
83 | return phy_start_aneg(priv->phydev); | |
80ff0fd3 | 84 | |
f6ed1b3b | 85 | return -EINVAL; |
80ff0fd3 DD |
86 | } |
87 | ||
9326e361 | 88 | const struct ethtool_ops cvm_oct_ethtool_ops = { |
80ff0fd3 DD |
89 | .get_drvinfo = cvm_oct_get_drvinfo, |
90 | .get_settings = cvm_oct_get_settings, | |
91 | .set_settings = cvm_oct_set_settings, | |
92 | .nway_reset = cvm_oct_nway_reset, | |
f6ed1b3b | 93 | .get_link = ethtool_op_get_link, |
80ff0fd3 DD |
94 | .get_sg = ethtool_op_get_sg, |
95 | .get_tx_csum = ethtool_op_get_tx_csum, | |
96 | }; | |
97 | ||
98 | /** | |
99 | * IOCTL support for PHY control | |
100 | * | |
101 | * @dev: Device to change | |
102 | * @rq: the request | |
103 | * @cmd: the command | |
104 | * Returns Zero on success | |
105 | */ | |
106 | int cvm_oct_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) | |
107 | { | |
108 | struct octeon_ethernet *priv = netdev_priv(dev); | |
80ff0fd3 | 109 | |
f6ed1b3b DD |
110 | if (!netif_running(dev)) |
111 | return -EINVAL; | |
112 | ||
113 | if (!priv->phydev) | |
114 | return -EINVAL; | |
115 | ||
116 | return phy_mii_ioctl(priv->phydev, if_mii(rq), cmd); | |
117 | } | |
80ff0fd3 | 118 | |
f6ed1b3b DD |
119 | static void cvm_oct_adjust_link(struct net_device *dev) |
120 | { | |
121 | struct octeon_ethernet *priv = netdev_priv(dev); | |
122 | cvmx_helper_link_info_t link_info; | |
123 | ||
124 | if (priv->last_link != priv->phydev->link) { | |
125 | priv->last_link = priv->phydev->link; | |
126 | link_info.u64 = 0; | |
127 | link_info.s.link_up = priv->last_link ? 1 : 0; | |
128 | link_info.s.full_duplex = priv->phydev->duplex ? 1 : 0; | |
129 | link_info.s.speed = priv->phydev->speed; | |
130 | cvmx_helper_link_set( priv->port, link_info); | |
131 | if (priv->last_link) { | |
132 | netif_carrier_on(dev); | |
133 | if (priv->queue != -1) | |
134 | DEBUGPRINT("%s: %u Mbps %s duplex, " | |
135 | "port %2d, queue %2d\n", | |
136 | dev->name, priv->phydev->speed, | |
137 | priv->phydev->duplex ? | |
138 | "Full" : "Half", | |
139 | priv->port, priv->queue); | |
140 | else | |
141 | DEBUGPRINT("%s: %u Mbps %s duplex, " | |
142 | "port %2d, POW\n", | |
143 | dev->name, priv->phydev->speed, | |
144 | priv->phydev->duplex ? | |
145 | "Full" : "Half", | |
146 | priv->port); | |
147 | } else { | |
148 | netif_carrier_off(dev); | |
149 | DEBUGPRINT("%s: Link down\n", dev->name); | |
150 | } | |
151 | } | |
80ff0fd3 DD |
152 | } |
153 | ||
f6ed1b3b | 154 | |
80ff0fd3 | 155 | /** |
f6ed1b3b | 156 | * Setup the PHY |
80ff0fd3 DD |
157 | * |
158 | * @dev: Device to setup | |
159 | * | |
160 | * Returns Zero on success, negative on failure | |
161 | */ | |
f6ed1b3b | 162 | int cvm_oct_phy_setup_device(struct net_device *dev) |
80ff0fd3 DD |
163 | { |
164 | struct octeon_ethernet *priv = netdev_priv(dev); | |
f6ed1b3b DD |
165 | |
166 | int phy_addr = cvmx_helper_board_get_mii_address(priv->port); | |
167 | if (phy_addr != -1) { | |
168 | char phy_id[20]; | |
169 | ||
170 | snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, "0", phy_addr); | |
171 | ||
172 | priv->phydev = phy_connect(dev, phy_id, cvm_oct_adjust_link, 0, | |
173 | PHY_INTERFACE_MODE_GMII); | |
174 | ||
175 | if (IS_ERR(priv->phydev)) { | |
176 | priv->phydev = NULL; | |
177 | return -1; | |
178 | } | |
179 | priv->last_link = 0; | |
180 | phy_start_aneg(priv->phydev); | |
80ff0fd3 DD |
181 | } |
182 | return 0; | |
183 | } |