1/* 2 * net/dsa/mv88e6063.c - Driver for Marvell 88e6063 switch chips 3 * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org> 4 * 5 * This driver was base on: net/dsa/mv88e6060.c 6 * net/dsa/mv88e6063.c - Driver for Marvell 88e6060 switch chips 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/list.h> 16#include <linux/netdevice.h> 17#include <linux/phy.h> 18#include "dsa_priv.h" 19 20#define REG_BASE 0x10 21#define REG_PHY(p) (REG_BASE + (p)) 22#define REG_PORT(p) (REG_BASE + 8 + (p)) 23#define REG_GLOBAL (REG_BASE + 0x0f) 24#define NUM_PORTS 7 25 26static int reg_read(struct dsa_switch *ds, int addr, int reg) 27{ 28 return mdiobus_read(ds->master_mii_bus, addr, reg); 29} 30 31#define REG_READ(addr, reg) \ 32 ({ \ 33 int __ret; \ 34 \ 35 __ret = reg_read(ds, addr, reg); \ 36 if (__ret < 0) \ 37 return __ret; \ 38 __ret; \ 39 }) 40 41 42static int reg_write(struct dsa_switch *ds, int addr, int reg, u16 val) 43{ 44 return mdiobus_write(ds->master_mii_bus, addr, reg, val); 45} 46 47#define REG_WRITE(addr, reg, val) \ 48 ({ \ 49 int __ret; \ 50 \ 51 __ret = reg_write(ds, addr, reg, val); \ 52 if (__ret < 0) \ 53 return __ret; \ 54 }) 55 56static char *mv88e6063_probe(struct mii_bus *bus, int sw_addr) 57{ 58 int ret; 59 60 ret = mdiobus_read(bus, REG_PORT(0), 0x03); 61 if (ret >= 0) { 62 ret &= 0xfff0; 63 if (ret == 0x1530) 64 return "Marvell 88E6063"; 65 } 66 67 return NULL; 68} 69 70static int mv88e6063_switch_reset(struct dsa_switch *ds) 71{ 72 int i; 73 int ret; 74 75 /* 76 * Set all ports to the disabled state. 77 */ 78 for (i = 0; i < NUM_PORTS; i++) { 79 ret = REG_READ(REG_PORT(i), 0x04); 80 REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc); 81 } 82 83 /* 84 * Wait for transmit queues to drain. 85 */ 86 msleep(2); 87 88 /* 89 * Reset the switch. 90 */ 91 REG_WRITE(REG_GLOBAL, 0x0a, 0xa130); 92 93 /* 94 * Wait up to one second for reset to complete. 95 */ 96 for (i = 0; i < 1000; i++) { 97 ret = REG_READ(REG_GLOBAL, 0x00); 98 if ((ret & 0x8000) == 0x0000) 99 break; 100 101 msleep(1); 102 } 103 if (i == 1000) 104 return -ETIMEDOUT; 105 106 return 0; 107} 108 109static int mv88e6063_setup_global(struct dsa_switch *ds) 110{ 111 /* 112 * Disable discarding of frames with excessive collisions, 113 * set the maximum frame size to 1536 bytes, and mask all 114 * interrupt sources. 115 */ 116 REG_WRITE(REG_GLOBAL, 0x04, 0x0800); 117 118 /* 119 * Enable automatic address learning, set the address 120 * database size to 1024 entries, and set the default aging 121 * time to 5 minutes. 122 */ 123 REG_WRITE(REG_GLOBAL, 0x0a, 0x2130); 124 125 return 0; 126} 127 128static int mv88e6063_setup_port(struct dsa_switch *ds, int p) 129{ 130 int addr = REG_PORT(p); 131 132 /* 133 * Do not force flow control, disable Ingress and Egress 134 * Header tagging, disable VLAN tunneling, and set the port 135 * state to Forwarding. Additionally, if this is the CPU 136 * port, enable Ingress and Egress Trailer tagging mode. 137 */ 138 REG_WRITE(addr, 0x04, dsa_is_cpu_port(ds, p) ? 0x4103 : 0x0003); 139 140 /* 141 * Port based VLAN map: give each port its own address 142 * database, allow the CPU port to talk to each of the 'real' 143 * ports, and allow each of the 'real' ports to only talk to 144 * the CPU port. 145 */ 146 REG_WRITE(addr, 0x06, 147 ((p & 0xf) << 12) | 148 (dsa_is_cpu_port(ds, p) ? 149 ds->phys_port_mask : 150 (1 << ds->dst->cpu_port))); 151 152 /* 153 * Port Association Vector: when learning source addresses 154 * of packets, add the address to the address database using 155 * a port bitmap that has only the bit for this port set and 156 * the other bits clear. 157 */ 158 REG_WRITE(addr, 0x0b, 1 << p); 159 160 return 0; 161} 162 163static int mv88e6063_setup(struct dsa_switch *ds) 164{ 165 int i; 166 int ret; 167 168 ret = mv88e6063_switch_reset(ds); 169 if (ret < 0) 170 return ret; 171 172 /* @@@ initialise atu */ 173 174 ret = mv88e6063_setup_global(ds); 175 if (ret < 0) 176 return ret; 177 178 for (i = 0; i < NUM_PORTS; i++) { 179 ret = mv88e6063_setup_port(ds, i); 180 if (ret < 0) 181 return ret; 182 } 183 184 return 0; 185} 186 187static int mv88e6063_set_addr(struct dsa_switch *ds, u8 *addr) 188{ 189 REG_WRITE(REG_GLOBAL, 0x01, (addr[0] << 8) | addr[1]); 190 REG_WRITE(REG_GLOBAL, 0x02, (addr[2] << 8) | addr[3]); 191 REG_WRITE(REG_GLOBAL, 0x03, (addr[4] << 8) | addr[5]); 192 193 return 0; 194} 195 196static int mv88e6063_port_to_phy_addr(int port) 197{ 198 if (port >= 0 && port <= NUM_PORTS) 199 return REG_PHY(port); 200 return -1; 201} 202 203static int mv88e6063_phy_read(struct dsa_switch *ds, int port, int regnum) 204{ 205 int addr; 206 207 addr = mv88e6063_port_to_phy_addr(port); 208 if (addr == -1) 209 return 0xffff; 210 211 return reg_read(ds, addr, regnum); 212} 213 214static int 215mv88e6063_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val) 216{ 217 int addr; 218 219 addr = mv88e6063_port_to_phy_addr(port); 220 if (addr == -1) 221 return 0xffff; 222 223 return reg_write(ds, addr, regnum, val); 224} 225 226static void mv88e6063_poll_link(struct dsa_switch *ds) 227{ 228 int i; 229 230 for (i = 0; i < DSA_MAX_PORTS; i++) { 231 struct net_device *dev; 232 int uninitialized_var(port_status); 233 int link; 234 int speed; 235 int duplex; 236 int fc; 237 238 dev = ds->ports[i]; 239 if (dev == NULL) 240 continue; 241 242 link = 0; 243 if (dev->flags & IFF_UP) { 244 port_status = reg_read(ds, REG_PORT(i), 0x00); 245 if (port_status < 0) 246 continue; 247 248 link = !!(port_status & 0x1000); 249 } 250 251 if (!link) { 252 if (netif_carrier_ok(dev)) { 253 printk(KERN_INFO "%s: link down\n", dev->name); 254 netif_carrier_off(dev); 255 } 256 continue; 257 } 258 259 speed = (port_status & 0x0100) ? 100 : 10; 260 duplex = (port_status & 0x0200) ? 1 : 0; 261 fc = ((port_status & 0xc000) == 0xc000) ? 1 : 0; 262 263 if (!netif_carrier_ok(dev)) { 264 printk(KERN_INFO "%s: link up, %d Mb/s, %s duplex, " 265 "flow control %sabled\n", dev->name, 266 speed, duplex ? "full" : "half", 267 fc ? "en" : "dis"); 268 netif_carrier_on(dev); 269 } 270 } 271} 272 273static struct dsa_switch_driver mv88e6063_switch_driver = { 274 .tag_protocol = htons(ETH_P_TRAILER), 275 .probe = mv88e6063_probe, 276 .setup = mv88e6063_setup, 277 .set_addr = mv88e6063_set_addr, 278 .phy_read = mv88e6063_phy_read, 279 .phy_write = mv88e6063_phy_write, 280 .poll_link = mv88e6063_poll_link, 281}; 282 283static int __init mv88e6063_init(void) 284{ 285 register_switch_driver(&mv88e6063_switch_driver); 286 return 0; 287} 288module_init(mv88e6063_init); 289 290static void __exit mv88e6063_cleanup(void) 291{ 292 unregister_switch_driver(&mv88e6063_switch_driver); 293} 294module_exit(mv88e6063_cleanup); 295