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 v2.6.29-rc3 380 lines 9.4 kB view raw
1/* 2 * net/dsa/mv88e6131.c - Marvell 88e6131 switch chip support 3 * Copyright (c) 2008 Marvell Semiconductor 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 */ 10 11#include <linux/list.h> 12#include <linux/netdevice.h> 13#include <linux/phy.h> 14#include "dsa_priv.h" 15#include "mv88e6xxx.h" 16 17static char *mv88e6131_probe(struct mii_bus *bus, int sw_addr) 18{ 19 int ret; 20 21 ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03); 22 if (ret >= 0) { 23 ret &= 0xfff0; 24 if (ret == 0x1060) 25 return "Marvell 88E6131"; 26 } 27 28 return NULL; 29} 30 31static int mv88e6131_switch_reset(struct dsa_switch *ds) 32{ 33 int i; 34 int ret; 35 36 /* 37 * Set all ports to the disabled state. 38 */ 39 for (i = 0; i < 8; i++) { 40 ret = REG_READ(REG_PORT(i), 0x04); 41 REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc); 42 } 43 44 /* 45 * Wait for transmit queues to drain. 46 */ 47 msleep(2); 48 49 /* 50 * Reset the switch. 51 */ 52 REG_WRITE(REG_GLOBAL, 0x04, 0xc400); 53 54 /* 55 * Wait up to one second for reset to complete. 56 */ 57 for (i = 0; i < 1000; i++) { 58 ret = REG_READ(REG_GLOBAL, 0x00); 59 if ((ret & 0xc800) == 0xc800) 60 break; 61 62 msleep(1); 63 } 64 if (i == 1000) 65 return -ETIMEDOUT; 66 67 return 0; 68} 69 70static int mv88e6131_setup_global(struct dsa_switch *ds) 71{ 72 int ret; 73 int i; 74 75 /* 76 * Enable the PHY polling unit, don't discard packets with 77 * excessive collisions, use a weighted fair queueing scheme 78 * to arbitrate between packet queues, set the maximum frame 79 * size to 1632, and mask all interrupt sources. 80 */ 81 REG_WRITE(REG_GLOBAL, 0x04, 0x4400); 82 83 /* 84 * Set the default address aging time to 5 minutes, and 85 * enable address learn messages to be sent to all message 86 * ports. 87 */ 88 REG_WRITE(REG_GLOBAL, 0x0a, 0x0148); 89 90 /* 91 * Configure the priority mapping registers. 92 */ 93 ret = mv88e6xxx_config_prio(ds); 94 if (ret < 0) 95 return ret; 96 97 /* 98 * Set the VLAN ethertype to 0x8100. 99 */ 100 REG_WRITE(REG_GLOBAL, 0x19, 0x8100); 101 102 /* 103 * Disable ARP mirroring, and configure the cpu port as the 104 * port to which ingress and egress monitor frames are to be 105 * sent. 106 */ 107 REG_WRITE(REG_GLOBAL, 0x1a, (ds->cpu_port * 0x1100) | 0x00f0); 108 109 /* 110 * Disable cascade port functionality, and set the switch's 111 * DSA device number to zero. 112 */ 113 REG_WRITE(REG_GLOBAL, 0x1c, 0xe000); 114 115 /* 116 * Send all frames with destination addresses matching 117 * 01:80:c2:00:00:0x to the CPU port. 118 */ 119 REG_WRITE(REG_GLOBAL2, 0x03, 0xffff); 120 121 /* 122 * Ignore removed tag data on doubly tagged packets, disable 123 * flow control messages, force flow control priority to the 124 * highest, and send all special multicast frames to the CPU 125 * port at the higest priority. 126 */ 127 REG_WRITE(REG_GLOBAL2, 0x05, 0x00ff); 128 129 /* 130 * Map all DSA device IDs to the CPU port. 131 */ 132 for (i = 0; i < 32; i++) 133 REG_WRITE(REG_GLOBAL2, 0x06, 0x8000 | (i << 8) | ds->cpu_port); 134 135 /* 136 * Clear all trunk masks. 137 */ 138 for (i = 0; i < 8; i++) 139 REG_WRITE(REG_GLOBAL2, 0x07, 0x8000 | (i << 12) | 0xff); 140 141 /* 142 * Clear all trunk mappings. 143 */ 144 for (i = 0; i < 16; i++) 145 REG_WRITE(REG_GLOBAL2, 0x08, 0x8000 | (i << 11)); 146 147 /* 148 * Force the priority of IGMP/MLD snoop frames and ARP frames 149 * to the highest setting. 150 */ 151 REG_WRITE(REG_GLOBAL2, 0x0f, 0x00ff); 152 153 return 0; 154} 155 156static int mv88e6131_setup_port(struct dsa_switch *ds, int p) 157{ 158 int addr = REG_PORT(p); 159 160 /* 161 * MAC Forcing register: don't force link, speed, duplex 162 * or flow control state to any particular values. 163 */ 164 REG_WRITE(addr, 0x01, 0x0003); 165 166 /* 167 * Port Control: disable Core Tag, disable Drop-on-Lock, 168 * transmit frames unmodified, disable Header mode, 169 * enable IGMP/MLD snoop, disable DoubleTag, disable VLAN 170 * tunneling, determine priority by looking at 802.1p and 171 * IP priority fields (IP prio has precedence), and set STP 172 * state to Forwarding. Finally, if this is the CPU port, 173 * additionally enable DSA tagging and forwarding of unknown 174 * unicast addresses. 175 */ 176 REG_WRITE(addr, 0x04, (p == ds->cpu_port) ? 0x0537 : 0x0433); 177 178 /* 179 * Port Control 1: disable trunking. Also, if this is the 180 * CPU port, enable learn messages to be sent to this port. 181 */ 182 REG_WRITE(addr, 0x05, (p == ds->cpu_port) ? 0x8000 : 0x0000); 183 184 /* 185 * Port based VLAN map: give each port its own address 186 * database, allow the CPU port to talk to each of the 'real' 187 * ports, and allow each of the 'real' ports to only talk to 188 * the CPU port. 189 */ 190 REG_WRITE(addr, 0x06, 191 ((p & 0xf) << 12) | 192 ((p == ds->cpu_port) ? 193 ds->valid_port_mask : 194 (1 << ds->cpu_port))); 195 196 /* 197 * Default VLAN ID and priority: don't set a default VLAN 198 * ID, and set the default packet priority to zero. 199 */ 200 REG_WRITE(addr, 0x07, 0x0000); 201 202 /* 203 * Port Control 2: don't force a good FCS, don't use 204 * VLAN-based, source address-based or destination 205 * address-based priority overrides, don't let the switch 206 * add or strip 802.1q tags, don't discard tagged or 207 * untagged frames on this port, do a destination address 208 * lookup on received packets as usual, don't send a copy 209 * of all transmitted/received frames on this port to the 210 * CPU, and configure the CPU port number. Also, if this 211 * is the CPU port, enable forwarding of unknown multicast 212 * addresses. 213 */ 214 REG_WRITE(addr, 0x08, 215 ((p == ds->cpu_port) ? 0x00c0 : 0x0080) | 216 ds->cpu_port); 217 218 /* 219 * Rate Control: disable ingress rate limiting. 220 */ 221 REG_WRITE(addr, 0x09, 0x0000); 222 223 /* 224 * Rate Control 2: disable egress rate limiting. 225 */ 226 REG_WRITE(addr, 0x0a, 0x0000); 227 228 /* 229 * Port Association Vector: when learning source addresses 230 * of packets, add the address to the address database using 231 * a port bitmap that has only the bit for this port set and 232 * the other bits clear. 233 */ 234 REG_WRITE(addr, 0x0b, 1 << p); 235 236 /* 237 * Tag Remap: use an identity 802.1p prio -> switch prio 238 * mapping. 239 */ 240 REG_WRITE(addr, 0x18, 0x3210); 241 242 /* 243 * Tag Remap 2: use an identity 802.1p prio -> switch prio 244 * mapping. 245 */ 246 REG_WRITE(addr, 0x19, 0x7654); 247 248 return 0; 249} 250 251static int mv88e6131_setup(struct dsa_switch *ds) 252{ 253 struct mv88e6xxx_priv_state *ps = (void *)(ds + 1); 254 int i; 255 int ret; 256 257 mutex_init(&ps->smi_mutex); 258 mv88e6xxx_ppu_state_init(ds); 259 mutex_init(&ps->stats_mutex); 260 261 ret = mv88e6131_switch_reset(ds); 262 if (ret < 0) 263 return ret; 264 265 /* @@@ initialise vtu and atu */ 266 267 ret = mv88e6131_setup_global(ds); 268 if (ret < 0) 269 return ret; 270 271 for (i = 0; i < 6; i++) { 272 ret = mv88e6131_setup_port(ds, i); 273 if (ret < 0) 274 return ret; 275 } 276 277 return 0; 278} 279 280static int mv88e6131_port_to_phy_addr(int port) 281{ 282 if (port >= 0 && port != 3 && port <= 7) 283 return port; 284 return -1; 285} 286 287static int 288mv88e6131_phy_read(struct dsa_switch *ds, int port, int regnum) 289{ 290 int addr = mv88e6131_port_to_phy_addr(port); 291 return mv88e6xxx_phy_read_ppu(ds, addr, regnum); 292} 293 294static int 295mv88e6131_phy_write(struct dsa_switch *ds, 296 int port, int regnum, u16 val) 297{ 298 int addr = mv88e6131_port_to_phy_addr(port); 299 return mv88e6xxx_phy_write_ppu(ds, addr, regnum, val); 300} 301 302static struct mv88e6xxx_hw_stat mv88e6131_hw_stats[] = { 303 { "in_good_octets", 8, 0x00, }, 304 { "in_bad_octets", 4, 0x02, }, 305 { "in_unicast", 4, 0x04, }, 306 { "in_broadcasts", 4, 0x06, }, 307 { "in_multicasts", 4, 0x07, }, 308 { "in_pause", 4, 0x16, }, 309 { "in_undersize", 4, 0x18, }, 310 { "in_fragments", 4, 0x19, }, 311 { "in_oversize", 4, 0x1a, }, 312 { "in_jabber", 4, 0x1b, }, 313 { "in_rx_error", 4, 0x1c, }, 314 { "in_fcs_error", 4, 0x1d, }, 315 { "out_octets", 8, 0x0e, }, 316 { "out_unicast", 4, 0x10, }, 317 { "out_broadcasts", 4, 0x13, }, 318 { "out_multicasts", 4, 0x12, }, 319 { "out_pause", 4, 0x15, }, 320 { "excessive", 4, 0x11, }, 321 { "collisions", 4, 0x1e, }, 322 { "deferred", 4, 0x05, }, 323 { "single", 4, 0x14, }, 324 { "multiple", 4, 0x17, }, 325 { "out_fcs_error", 4, 0x03, }, 326 { "late", 4, 0x1f, }, 327 { "hist_64bytes", 4, 0x08, }, 328 { "hist_65_127bytes", 4, 0x09, }, 329 { "hist_128_255bytes", 4, 0x0a, }, 330 { "hist_256_511bytes", 4, 0x0b, }, 331 { "hist_512_1023bytes", 4, 0x0c, }, 332 { "hist_1024_max_bytes", 4, 0x0d, }, 333}; 334 335static void 336mv88e6131_get_strings(struct dsa_switch *ds, int port, uint8_t *data) 337{ 338 mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6131_hw_stats), 339 mv88e6131_hw_stats, port, data); 340} 341 342static void 343mv88e6131_get_ethtool_stats(struct dsa_switch *ds, 344 int port, uint64_t *data) 345{ 346 mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6131_hw_stats), 347 mv88e6131_hw_stats, port, data); 348} 349 350static int mv88e6131_get_sset_count(struct dsa_switch *ds) 351{ 352 return ARRAY_SIZE(mv88e6131_hw_stats); 353} 354 355static struct dsa_switch_driver mv88e6131_switch_driver = { 356 .tag_protocol = __constant_htons(ETH_P_DSA), 357 .priv_size = sizeof(struct mv88e6xxx_priv_state), 358 .probe = mv88e6131_probe, 359 .setup = mv88e6131_setup, 360 .set_addr = mv88e6xxx_set_addr_direct, 361 .phy_read = mv88e6131_phy_read, 362 .phy_write = mv88e6131_phy_write, 363 .poll_link = mv88e6xxx_poll_link, 364 .get_strings = mv88e6131_get_strings, 365 .get_ethtool_stats = mv88e6131_get_ethtool_stats, 366 .get_sset_count = mv88e6131_get_sset_count, 367}; 368 369static int __init mv88e6131_init(void) 370{ 371 register_switch_driver(&mv88e6131_switch_driver); 372 return 0; 373} 374module_init(mv88e6131_init); 375 376static void __exit mv88e6131_cleanup(void) 377{ 378 unregister_switch_driver(&mv88e6131_switch_driver); 379} 380module_exit(mv88e6131_cleanup);