Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.2-rc5 398 lines 8.2 kB view raw
1/* 2 * net/dsa/mv88e6352.c - Marvell 88e6352 switch chip support 3 * 4 * Copyright (c) 2014 Guenter Roeck 5 * 6 * Derived from mv88e6123_61_65.c 7 * Copyright (c) 2008-2009 Marvell Semiconductor 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 */ 14 15#include <linux/delay.h> 16#include <linux/jiffies.h> 17#include <linux/list.h> 18#include <linux/module.h> 19#include <linux/netdevice.h> 20#include <linux/platform_device.h> 21#include <linux/phy.h> 22#include <net/dsa.h> 23#include "mv88e6xxx.h" 24 25static char *mv88e6352_probe(struct device *host_dev, int sw_addr) 26{ 27 struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev); 28 int ret; 29 30 if (bus == NULL) 31 return NULL; 32 33 ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID); 34 if (ret >= 0) { 35 if ((ret & 0xfff0) == PORT_SWITCH_ID_6172) 36 return "Marvell 88E6172"; 37 if ((ret & 0xfff0) == PORT_SWITCH_ID_6176) 38 return "Marvell 88E6176"; 39 if (ret == PORT_SWITCH_ID_6352_A0) 40 return "Marvell 88E6352 (A0)"; 41 if (ret == PORT_SWITCH_ID_6352_A1) 42 return "Marvell 88E6352 (A1)"; 43 if ((ret & 0xfff0) == PORT_SWITCH_ID_6352) 44 return "Marvell 88E6352"; 45 } 46 47 return NULL; 48} 49 50static int mv88e6352_setup_global(struct dsa_switch *ds) 51{ 52 u32 upstream_port = dsa_upstream_port(ds); 53 int ret; 54 u32 reg; 55 56 ret = mv88e6xxx_setup_global(ds); 57 if (ret) 58 return ret; 59 60 /* Discard packets with excessive collisions, 61 * mask all interrupt sources, enable PPU (bit 14, undocumented). 62 */ 63 REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL, 64 GLOBAL_CONTROL_PPU_ENABLE | GLOBAL_CONTROL_DISCARD_EXCESS); 65 66 /* Configure the upstream port, and configure the upstream 67 * port as the port to which ingress and egress monitor frames 68 * are to be sent. 69 */ 70 reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT | 71 upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT | 72 upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT; 73 REG_WRITE(REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg); 74 75 /* Disable remote management for now, and set the switch's 76 * DSA device number. 77 */ 78 REG_WRITE(REG_GLOBAL, 0x1c, ds->index & 0x1f); 79 80 return 0; 81} 82 83#ifdef CONFIG_NET_DSA_HWMON 84 85static int mv88e6352_get_temp(struct dsa_switch *ds, int *temp) 86{ 87 int ret; 88 89 *temp = 0; 90 91 ret = mv88e6xxx_phy_page_read(ds, 0, 6, 27); 92 if (ret < 0) 93 return ret; 94 95 *temp = (ret & 0xff) - 25; 96 97 return 0; 98} 99 100static int mv88e6352_get_temp_limit(struct dsa_switch *ds, int *temp) 101{ 102 int ret; 103 104 *temp = 0; 105 106 ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26); 107 if (ret < 0) 108 return ret; 109 110 *temp = (((ret >> 8) & 0x1f) * 5) - 25; 111 112 return 0; 113} 114 115static int mv88e6352_set_temp_limit(struct dsa_switch *ds, int temp) 116{ 117 int ret; 118 119 ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26); 120 if (ret < 0) 121 return ret; 122 temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f); 123 return mv88e6xxx_phy_page_write(ds, 0, 6, 26, 124 (ret & 0xe0ff) | (temp << 8)); 125} 126 127static int mv88e6352_get_temp_alarm(struct dsa_switch *ds, bool *alarm) 128{ 129 int ret; 130 131 *alarm = false; 132 133 ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26); 134 if (ret < 0) 135 return ret; 136 137 *alarm = !!(ret & 0x40); 138 139 return 0; 140} 141#endif /* CONFIG_NET_DSA_HWMON */ 142 143static int mv88e6352_setup(struct dsa_switch *ds) 144{ 145 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); 146 int ret; 147 148 ret = mv88e6xxx_setup_common(ds); 149 if (ret < 0) 150 return ret; 151 152 ps->num_ports = 7; 153 154 mutex_init(&ps->eeprom_mutex); 155 156 ret = mv88e6xxx_switch_reset(ds, true); 157 if (ret < 0) 158 return ret; 159 160 ret = mv88e6352_setup_global(ds); 161 if (ret < 0) 162 return ret; 163 164 return mv88e6xxx_setup_ports(ds); 165} 166 167static int mv88e6352_read_eeprom_word(struct dsa_switch *ds, int addr) 168{ 169 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); 170 int ret; 171 172 mutex_lock(&ps->eeprom_mutex); 173 174 ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, 0x14, 175 0xc000 | (addr & 0xff)); 176 if (ret < 0) 177 goto error; 178 179 ret = mv88e6xxx_eeprom_busy_wait(ds); 180 if (ret < 0) 181 goto error; 182 183 ret = mv88e6xxx_reg_read(ds, REG_GLOBAL2, 0x15); 184error: 185 mutex_unlock(&ps->eeprom_mutex); 186 return ret; 187} 188 189static int mv88e6352_get_eeprom(struct dsa_switch *ds, 190 struct ethtool_eeprom *eeprom, u8 *data) 191{ 192 int offset; 193 int len; 194 int ret; 195 196 offset = eeprom->offset; 197 len = eeprom->len; 198 eeprom->len = 0; 199 200 eeprom->magic = 0xc3ec4951; 201 202 ret = mv88e6xxx_eeprom_load_wait(ds); 203 if (ret < 0) 204 return ret; 205 206 if (offset & 1) { 207 int word; 208 209 word = mv88e6352_read_eeprom_word(ds, offset >> 1); 210 if (word < 0) 211 return word; 212 213 *data++ = (word >> 8) & 0xff; 214 215 offset++; 216 len--; 217 eeprom->len++; 218 } 219 220 while (len >= 2) { 221 int word; 222 223 word = mv88e6352_read_eeprom_word(ds, offset >> 1); 224 if (word < 0) 225 return word; 226 227 *data++ = word & 0xff; 228 *data++ = (word >> 8) & 0xff; 229 230 offset += 2; 231 len -= 2; 232 eeprom->len += 2; 233 } 234 235 if (len) { 236 int word; 237 238 word = mv88e6352_read_eeprom_word(ds, offset >> 1); 239 if (word < 0) 240 return word; 241 242 *data++ = word & 0xff; 243 244 offset++; 245 len--; 246 eeprom->len++; 247 } 248 249 return 0; 250} 251 252static int mv88e6352_eeprom_is_readonly(struct dsa_switch *ds) 253{ 254 int ret; 255 256 ret = mv88e6xxx_reg_read(ds, REG_GLOBAL2, 0x14); 257 if (ret < 0) 258 return ret; 259 260 if (!(ret & 0x0400)) 261 return -EROFS; 262 263 return 0; 264} 265 266static int mv88e6352_write_eeprom_word(struct dsa_switch *ds, int addr, 267 u16 data) 268{ 269 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); 270 int ret; 271 272 mutex_lock(&ps->eeprom_mutex); 273 274 ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, 0x15, data); 275 if (ret < 0) 276 goto error; 277 278 ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, 0x14, 279 0xb000 | (addr & 0xff)); 280 if (ret < 0) 281 goto error; 282 283 ret = mv88e6xxx_eeprom_busy_wait(ds); 284error: 285 mutex_unlock(&ps->eeprom_mutex); 286 return ret; 287} 288 289static int mv88e6352_set_eeprom(struct dsa_switch *ds, 290 struct ethtool_eeprom *eeprom, u8 *data) 291{ 292 int offset; 293 int ret; 294 int len; 295 296 if (eeprom->magic != 0xc3ec4951) 297 return -EINVAL; 298 299 ret = mv88e6352_eeprom_is_readonly(ds); 300 if (ret) 301 return ret; 302 303 offset = eeprom->offset; 304 len = eeprom->len; 305 eeprom->len = 0; 306 307 ret = mv88e6xxx_eeprom_load_wait(ds); 308 if (ret < 0) 309 return ret; 310 311 if (offset & 1) { 312 int word; 313 314 word = mv88e6352_read_eeprom_word(ds, offset >> 1); 315 if (word < 0) 316 return word; 317 318 word = (*data++ << 8) | (word & 0xff); 319 320 ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word); 321 if (ret < 0) 322 return ret; 323 324 offset++; 325 len--; 326 eeprom->len++; 327 } 328 329 while (len >= 2) { 330 int word; 331 332 word = *data++; 333 word |= *data++ << 8; 334 335 ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word); 336 if (ret < 0) 337 return ret; 338 339 offset += 2; 340 len -= 2; 341 eeprom->len += 2; 342 } 343 344 if (len) { 345 int word; 346 347 word = mv88e6352_read_eeprom_word(ds, offset >> 1); 348 if (word < 0) 349 return word; 350 351 word = (word & 0xff00) | *data++; 352 353 ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word); 354 if (ret < 0) 355 return ret; 356 357 offset++; 358 len--; 359 eeprom->len++; 360 } 361 362 return 0; 363} 364 365struct dsa_switch_driver mv88e6352_switch_driver = { 366 .tag_protocol = DSA_TAG_PROTO_EDSA, 367 .priv_size = sizeof(struct mv88e6xxx_priv_state), 368 .probe = mv88e6352_probe, 369 .setup = mv88e6352_setup, 370 .set_addr = mv88e6xxx_set_addr_indirect, 371 .phy_read = mv88e6xxx_phy_read_indirect, 372 .phy_write = mv88e6xxx_phy_write_indirect, 373 .poll_link = mv88e6xxx_poll_link, 374 .get_strings = mv88e6xxx_get_strings, 375 .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, 376 .get_sset_count = mv88e6xxx_get_sset_count, 377 .set_eee = mv88e6xxx_set_eee, 378 .get_eee = mv88e6xxx_get_eee, 379#ifdef CONFIG_NET_DSA_HWMON 380 .get_temp = mv88e6352_get_temp, 381 .get_temp_limit = mv88e6352_get_temp_limit, 382 .set_temp_limit = mv88e6352_set_temp_limit, 383 .get_temp_alarm = mv88e6352_get_temp_alarm, 384#endif 385 .get_eeprom = mv88e6352_get_eeprom, 386 .set_eeprom = mv88e6352_set_eeprom, 387 .get_regs_len = mv88e6xxx_get_regs_len, 388 .get_regs = mv88e6xxx_get_regs, 389 .port_join_bridge = mv88e6xxx_join_bridge, 390 .port_leave_bridge = mv88e6xxx_leave_bridge, 391 .port_stp_update = mv88e6xxx_port_stp_update, 392 .fdb_add = mv88e6xxx_port_fdb_add, 393 .fdb_del = mv88e6xxx_port_fdb_del, 394 .fdb_getnext = mv88e6xxx_port_fdb_getnext, 395}; 396 397MODULE_ALIAS("platform:mv88e6352"); 398MODULE_ALIAS("platform:mv88e6172");