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