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 const struct mv88e6xxx_switch_id mv88e6352_table[] = { 26 { PORT_SWITCH_ID_6172, "Marvell 88E6172" }, 27 { PORT_SWITCH_ID_6176, "Marvell 88E6176" }, 28 { PORT_SWITCH_ID_6320, "Marvell 88E6320" }, 29 { PORT_SWITCH_ID_6320_A1, "Marvell 88E6320 (A1)" }, 30 { PORT_SWITCH_ID_6320_A2, "Marvell 88e6320 (A2)" }, 31 { PORT_SWITCH_ID_6321, "Marvell 88E6321" }, 32 { PORT_SWITCH_ID_6321_A1, "Marvell 88E6321 (A1)" }, 33 { PORT_SWITCH_ID_6321_A2, "Marvell 88e6321 (A2)" }, 34 { PORT_SWITCH_ID_6352, "Marvell 88E6352" }, 35 { PORT_SWITCH_ID_6352_A0, "Marvell 88E6352 (A0)" }, 36 { PORT_SWITCH_ID_6352_A1, "Marvell 88E6352 (A1)" }, 37}; 38 39static char *mv88e6352_probe(struct device *host_dev, int sw_addr) 40{ 41 return mv88e6xxx_lookup_name(host_dev, sw_addr, mv88e6352_table, 42 ARRAY_SIZE(mv88e6352_table)); 43} 44 45static int mv88e6352_setup_global(struct dsa_switch *ds) 46{ 47 u32 upstream_port = dsa_upstream_port(ds); 48 int ret; 49 u32 reg; 50 51 ret = mv88e6xxx_setup_global(ds); 52 if (ret) 53 return ret; 54 55 /* Discard packets with excessive collisions, 56 * mask all interrupt sources, enable PPU (bit 14, undocumented). 57 */ 58 REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL, 59 GLOBAL_CONTROL_PPU_ENABLE | GLOBAL_CONTROL_DISCARD_EXCESS); 60 61 /* Configure the upstream port, and configure the upstream 62 * port as the port to which ingress and egress monitor frames 63 * are to be sent. 64 */ 65 reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT | 66 upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT | 67 upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT; 68 REG_WRITE(REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg); 69 70 /* Disable remote management for now, and set the switch's 71 * DSA device number. 72 */ 73 REG_WRITE(REG_GLOBAL, 0x1c, ds->index & 0x1f); 74 75 return 0; 76} 77 78static int mv88e6352_setup(struct dsa_switch *ds) 79{ 80 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); 81 int ret; 82 83 ret = mv88e6xxx_setup_common(ds); 84 if (ret < 0) 85 return ret; 86 87 ps->num_ports = 7; 88 89 mutex_init(&ps->eeprom_mutex); 90 91 ret = mv88e6xxx_switch_reset(ds, true); 92 if (ret < 0) 93 return ret; 94 95 ret = mv88e6352_setup_global(ds); 96 if (ret < 0) 97 return ret; 98 99 return mv88e6xxx_setup_ports(ds); 100} 101 102static int mv88e6352_read_eeprom_word(struct dsa_switch *ds, int addr) 103{ 104 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); 105 int ret; 106 107 mutex_lock(&ps->eeprom_mutex); 108 109 ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP, 110 GLOBAL2_EEPROM_OP_READ | 111 (addr & GLOBAL2_EEPROM_OP_ADDR_MASK)); 112 if (ret < 0) 113 goto error; 114 115 ret = mv88e6xxx_eeprom_busy_wait(ds); 116 if (ret < 0) 117 goto error; 118 119 ret = mv88e6xxx_reg_read(ds, REG_GLOBAL2, GLOBAL2_EEPROM_DATA); 120error: 121 mutex_unlock(&ps->eeprom_mutex); 122 return ret; 123} 124 125static int mv88e6352_get_eeprom(struct dsa_switch *ds, 126 struct ethtool_eeprom *eeprom, u8 *data) 127{ 128 int offset; 129 int len; 130 int ret; 131 132 offset = eeprom->offset; 133 len = eeprom->len; 134 eeprom->len = 0; 135 136 eeprom->magic = 0xc3ec4951; 137 138 ret = mv88e6xxx_eeprom_load_wait(ds); 139 if (ret < 0) 140 return ret; 141 142 if (offset & 1) { 143 int word; 144 145 word = mv88e6352_read_eeprom_word(ds, offset >> 1); 146 if (word < 0) 147 return word; 148 149 *data++ = (word >> 8) & 0xff; 150 151 offset++; 152 len--; 153 eeprom->len++; 154 } 155 156 while (len >= 2) { 157 int word; 158 159 word = mv88e6352_read_eeprom_word(ds, offset >> 1); 160 if (word < 0) 161 return word; 162 163 *data++ = word & 0xff; 164 *data++ = (word >> 8) & 0xff; 165 166 offset += 2; 167 len -= 2; 168 eeprom->len += 2; 169 } 170 171 if (len) { 172 int word; 173 174 word = mv88e6352_read_eeprom_word(ds, offset >> 1); 175 if (word < 0) 176 return word; 177 178 *data++ = word & 0xff; 179 180 offset++; 181 len--; 182 eeprom->len++; 183 } 184 185 return 0; 186} 187 188static int mv88e6352_eeprom_is_readonly(struct dsa_switch *ds) 189{ 190 int ret; 191 192 ret = mv88e6xxx_reg_read(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP); 193 if (ret < 0) 194 return ret; 195 196 if (!(ret & GLOBAL2_EEPROM_OP_WRITE_EN)) 197 return -EROFS; 198 199 return 0; 200} 201 202static int mv88e6352_write_eeprom_word(struct dsa_switch *ds, int addr, 203 u16 data) 204{ 205 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); 206 int ret; 207 208 mutex_lock(&ps->eeprom_mutex); 209 210 ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data); 211 if (ret < 0) 212 goto error; 213 214 ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP, 215 GLOBAL2_EEPROM_OP_WRITE | 216 (addr & GLOBAL2_EEPROM_OP_ADDR_MASK)); 217 if (ret < 0) 218 goto error; 219 220 ret = mv88e6xxx_eeprom_busy_wait(ds); 221error: 222 mutex_unlock(&ps->eeprom_mutex); 223 return ret; 224} 225 226static int mv88e6352_set_eeprom(struct dsa_switch *ds, 227 struct ethtool_eeprom *eeprom, u8 *data) 228{ 229 int offset; 230 int ret; 231 int len; 232 233 if (eeprom->magic != 0xc3ec4951) 234 return -EINVAL; 235 236 ret = mv88e6352_eeprom_is_readonly(ds); 237 if (ret) 238 return ret; 239 240 offset = eeprom->offset; 241 len = eeprom->len; 242 eeprom->len = 0; 243 244 ret = mv88e6xxx_eeprom_load_wait(ds); 245 if (ret < 0) 246 return ret; 247 248 if (offset & 1) { 249 int word; 250 251 word = mv88e6352_read_eeprom_word(ds, offset >> 1); 252 if (word < 0) 253 return word; 254 255 word = (*data++ << 8) | (word & 0xff); 256 257 ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word); 258 if (ret < 0) 259 return ret; 260 261 offset++; 262 len--; 263 eeprom->len++; 264 } 265 266 while (len >= 2) { 267 int word; 268 269 word = *data++; 270 word |= *data++ << 8; 271 272 ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word); 273 if (ret < 0) 274 return ret; 275 276 offset += 2; 277 len -= 2; 278 eeprom->len += 2; 279 } 280 281 if (len) { 282 int word; 283 284 word = mv88e6352_read_eeprom_word(ds, offset >> 1); 285 if (word < 0) 286 return word; 287 288 word = (word & 0xff00) | *data++; 289 290 ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word); 291 if (ret < 0) 292 return ret; 293 294 offset++; 295 len--; 296 eeprom->len++; 297 } 298 299 return 0; 300} 301 302struct dsa_switch_driver mv88e6352_switch_driver = { 303 .tag_protocol = DSA_TAG_PROTO_EDSA, 304 .priv_size = sizeof(struct mv88e6xxx_priv_state), 305 .probe = mv88e6352_probe, 306 .setup = mv88e6352_setup, 307 .set_addr = mv88e6xxx_set_addr_indirect, 308 .phy_read = mv88e6xxx_phy_read_indirect, 309 .phy_write = mv88e6xxx_phy_write_indirect, 310 .get_strings = mv88e6xxx_get_strings, 311 .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, 312 .get_sset_count = mv88e6xxx_get_sset_count, 313 .adjust_link = mv88e6xxx_adjust_link, 314 .set_eee = mv88e6xxx_set_eee, 315 .get_eee = mv88e6xxx_get_eee, 316#ifdef CONFIG_NET_DSA_HWMON 317 .get_temp = mv88e6xxx_get_temp, 318 .get_temp_limit = mv88e6xxx_get_temp_limit, 319 .set_temp_limit = mv88e6xxx_set_temp_limit, 320 .get_temp_alarm = mv88e6xxx_get_temp_alarm, 321#endif 322 .get_eeprom = mv88e6352_get_eeprom, 323 .set_eeprom = mv88e6352_set_eeprom, 324 .get_regs_len = mv88e6xxx_get_regs_len, 325 .get_regs = mv88e6xxx_get_regs, 326 .port_join_bridge = mv88e6xxx_port_bridge_join, 327 .port_leave_bridge = mv88e6xxx_port_bridge_leave, 328 .port_stp_update = mv88e6xxx_port_stp_update, 329 .port_pvid_get = mv88e6xxx_port_pvid_get, 330 .port_vlan_prepare = mv88e6xxx_port_vlan_prepare, 331 .port_vlan_add = mv88e6xxx_port_vlan_add, 332 .port_vlan_del = mv88e6xxx_port_vlan_del, 333 .vlan_getnext = mv88e6xxx_vlan_getnext, 334 .port_fdb_prepare = mv88e6xxx_port_fdb_prepare, 335 .port_fdb_add = mv88e6xxx_port_fdb_add, 336 .port_fdb_del = mv88e6xxx_port_fdb_del, 337 .port_fdb_dump = mv88e6xxx_port_fdb_dump, 338}; 339 340MODULE_ALIAS("platform:mv88e6172"); 341MODULE_ALIAS("platform:mv88e6176"); 342MODULE_ALIAS("platform:mv88e6320"); 343MODULE_ALIAS("platform:mv88e6321"); 344MODULE_ALIAS("platform:mv88e6352"); 345