diff options
Diffstat (limited to 'drivers/net/dsa/mv88e6060.c')
| -rw-r--r-- | drivers/net/dsa/mv88e6060.c | 114 |
1 files changed, 38 insertions, 76 deletions
diff --git a/drivers/net/dsa/mv88e6060.c b/drivers/net/dsa/mv88e6060.c index 9093577755f6..0527f485c3dc 100644 --- a/drivers/net/dsa/mv88e6060.c +++ b/drivers/net/dsa/mv88e6060.c | |||
| @@ -15,9 +15,7 @@ | |||
| 15 | #include <linux/netdevice.h> | 15 | #include <linux/netdevice.h> |
| 16 | #include <linux/phy.h> | 16 | #include <linux/phy.h> |
| 17 | #include <net/dsa.h> | 17 | #include <net/dsa.h> |
| 18 | 18 | #include "mv88e6060.h" | |
| 19 | #define REG_PORT(p) (8 + (p)) | ||
| 20 | #define REG_GLOBAL 0x0f | ||
| 21 | 19 | ||
| 22 | static int reg_read(struct dsa_switch *ds, int addr, int reg) | 20 | static int reg_read(struct dsa_switch *ds, int addr, int reg) |
| 23 | { | 21 | { |
| @@ -67,13 +65,14 @@ static char *mv88e6060_probe(struct device *host_dev, int sw_addr) | |||
| 67 | if (bus == NULL) | 65 | if (bus == NULL) |
| 68 | return NULL; | 66 | return NULL; |
| 69 | 67 | ||
| 70 | ret = mdiobus_read(bus, sw_addr + REG_PORT(0), 0x03); | 68 | ret = mdiobus_read(bus, sw_addr + REG_PORT(0), PORT_SWITCH_ID); |
| 71 | if (ret >= 0) { | 69 | if (ret >= 0) { |
| 72 | if (ret == 0x0600) | 70 | if (ret == PORT_SWITCH_ID_6060) |
| 73 | return "Marvell 88E6060 (A0)"; | 71 | return "Marvell 88E6060 (A0)"; |
| 74 | if (ret == 0x0601 || ret == 0x0602) | 72 | if (ret == PORT_SWITCH_ID_6060_R1 || |
| 73 | ret == PORT_SWITCH_ID_6060_R2) | ||
| 75 | return "Marvell 88E6060 (B0)"; | 74 | return "Marvell 88E6060 (B0)"; |
| 76 | if ((ret & 0xfff0) == 0x0600) | 75 | if ((ret & PORT_SWITCH_ID_6060_MASK) == PORT_SWITCH_ID_6060) |
| 77 | return "Marvell 88E6060"; | 76 | return "Marvell 88E6060"; |
| 78 | } | 77 | } |
| 79 | 78 | ||
| @@ -87,22 +86,26 @@ static int mv88e6060_switch_reset(struct dsa_switch *ds) | |||
| 87 | unsigned long timeout; | 86 | unsigned long timeout; |
| 88 | 87 | ||
| 89 | /* Set all ports to the disabled state. */ | 88 | /* Set all ports to the disabled state. */ |
| 90 | for (i = 0; i < 6; i++) { | 89 | for (i = 0; i < MV88E6060_PORTS; i++) { |
| 91 | ret = REG_READ(REG_PORT(i), 0x04); | 90 | ret = REG_READ(REG_PORT(i), PORT_CONTROL); |
| 92 | REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc); | 91 | REG_WRITE(REG_PORT(i), PORT_CONTROL, |
| 92 | ret & ~PORT_CONTROL_STATE_MASK); | ||
| 93 | } | 93 | } |
| 94 | 94 | ||
| 95 | /* Wait for transmit queues to drain. */ | 95 | /* Wait for transmit queues to drain. */ |
| 96 | usleep_range(2000, 4000); | 96 | usleep_range(2000, 4000); |
| 97 | 97 | ||
| 98 | /* Reset the switch. */ | 98 | /* Reset the switch. */ |
| 99 | REG_WRITE(REG_GLOBAL, 0x0a, 0xa130); | 99 | REG_WRITE(REG_GLOBAL, GLOBAL_ATU_CONTROL, |
| 100 | GLOBAL_ATU_CONTROL_SWRESET | | ||
| 101 | GLOBAL_ATU_CONTROL_ATUSIZE_1024 | | ||
| 102 | GLOBAL_ATU_CONTROL_ATE_AGE_5MIN); | ||
| 100 | 103 | ||
| 101 | /* Wait up to one second for reset to complete. */ | 104 | /* Wait up to one second for reset to complete. */ |
| 102 | timeout = jiffies + 1 * HZ; | 105 | timeout = jiffies + 1 * HZ; |
| 103 | while (time_before(jiffies, timeout)) { | 106 | while (time_before(jiffies, timeout)) { |
| 104 | ret = REG_READ(REG_GLOBAL, 0x00); | 107 | ret = REG_READ(REG_GLOBAL, GLOBAL_STATUS); |
| 105 | if ((ret & 0x8000) == 0x0000) | 108 | if (ret & GLOBAL_STATUS_INIT_READY) |
| 106 | break; | 109 | break; |
| 107 | 110 | ||
| 108 | usleep_range(1000, 2000); | 111 | usleep_range(1000, 2000); |
| @@ -119,13 +122,15 @@ static int mv88e6060_setup_global(struct dsa_switch *ds) | |||
| 119 | * set the maximum frame size to 1536 bytes, and mask all | 122 | * set the maximum frame size to 1536 bytes, and mask all |
| 120 | * interrupt sources. | 123 | * interrupt sources. |
| 121 | */ | 124 | */ |
| 122 | REG_WRITE(REG_GLOBAL, 0x04, 0x0800); | 125 | REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL, GLOBAL_CONTROL_MAX_FRAME_1536); |
| 123 | 126 | ||
| 124 | /* Enable automatic address learning, set the address | 127 | /* Enable automatic address learning, set the address |
| 125 | * database size to 1024 entries, and set the default aging | 128 | * database size to 1024 entries, and set the default aging |
| 126 | * time to 5 minutes. | 129 | * time to 5 minutes. |
| 127 | */ | 130 | */ |
| 128 | REG_WRITE(REG_GLOBAL, 0x0a, 0x2130); | 131 | REG_WRITE(REG_GLOBAL, GLOBAL_ATU_CONTROL, |
| 132 | GLOBAL_ATU_CONTROL_ATUSIZE_1024 | | ||
| 133 | GLOBAL_ATU_CONTROL_ATE_AGE_5MIN); | ||
| 129 | 134 | ||
| 130 | return 0; | 135 | return 0; |
| 131 | } | 136 | } |
| @@ -139,25 +144,30 @@ static int mv88e6060_setup_port(struct dsa_switch *ds, int p) | |||
| 139 | * state to Forwarding. Additionally, if this is the CPU | 144 | * state to Forwarding. Additionally, if this is the CPU |
| 140 | * port, enable Ingress and Egress Trailer tagging mode. | 145 | * port, enable Ingress and Egress Trailer tagging mode. |
| 141 | */ | 146 | */ |
| 142 | REG_WRITE(addr, 0x04, dsa_is_cpu_port(ds, p) ? 0x4103 : 0x0003); | 147 | REG_WRITE(addr, PORT_CONTROL, |
| 148 | dsa_is_cpu_port(ds, p) ? | ||
| 149 | PORT_CONTROL_TRAILER | | ||
| 150 | PORT_CONTROL_INGRESS_MODE | | ||
| 151 | PORT_CONTROL_STATE_FORWARDING : | ||
| 152 | PORT_CONTROL_STATE_FORWARDING); | ||
| 143 | 153 | ||
| 144 | /* Port based VLAN map: give each port its own address | 154 | /* Port based VLAN map: give each port its own address |
| 145 | * database, allow the CPU port to talk to each of the 'real' | 155 | * database, allow the CPU port to talk to each of the 'real' |
| 146 | * ports, and allow each of the 'real' ports to only talk to | 156 | * ports, and allow each of the 'real' ports to only talk to |
| 147 | * the CPU port. | 157 | * the CPU port. |
| 148 | */ | 158 | */ |
| 149 | REG_WRITE(addr, 0x06, | 159 | REG_WRITE(addr, PORT_VLAN_MAP, |
| 150 | ((p & 0xf) << 12) | | 160 | ((p & 0xf) << PORT_VLAN_MAP_DBNUM_SHIFT) | |
| 151 | (dsa_is_cpu_port(ds, p) ? | 161 | (dsa_is_cpu_port(ds, p) ? |
| 152 | ds->phys_port_mask : | 162 | ds->phys_port_mask : |
| 153 | (1 << ds->dst->cpu_port))); | 163 | BIT(ds->dst->cpu_port))); |
| 154 | 164 | ||
| 155 | /* Port Association Vector: when learning source addresses | 165 | /* Port Association Vector: when learning source addresses |
| 156 | * of packets, add the address to the address database using | 166 | * of packets, add the address to the address database using |
| 157 | * a port bitmap that has only the bit for this port set and | 167 | * a port bitmap that has only the bit for this port set and |
| 158 | * the other bits clear. | 168 | * the other bits clear. |
| 159 | */ | 169 | */ |
| 160 | REG_WRITE(addr, 0x0b, 1 << p); | 170 | REG_WRITE(addr, PORT_ASSOC_VECTOR, BIT(p)); |
| 161 | 171 | ||
| 162 | return 0; | 172 | return 0; |
| 163 | } | 173 | } |
| @@ -177,7 +187,7 @@ static int mv88e6060_setup(struct dsa_switch *ds) | |||
| 177 | if (ret < 0) | 187 | if (ret < 0) |
| 178 | return ret; | 188 | return ret; |
| 179 | 189 | ||
| 180 | for (i = 0; i < 6; i++) { | 190 | for (i = 0; i < MV88E6060_PORTS; i++) { |
| 181 | ret = mv88e6060_setup_port(ds, i); | 191 | ret = mv88e6060_setup_port(ds, i); |
| 182 | if (ret < 0) | 192 | if (ret < 0) |
| 183 | return ret; | 193 | return ret; |
| @@ -188,16 +198,17 @@ static int mv88e6060_setup(struct dsa_switch *ds) | |||
| 188 | 198 | ||
| 189 | static int mv88e6060_set_addr(struct dsa_switch *ds, u8 *addr) | 199 | static int mv88e6060_set_addr(struct dsa_switch *ds, u8 *addr) |
| 190 | { | 200 | { |
| 191 | REG_WRITE(REG_GLOBAL, 0x01, (addr[0] << 8) | addr[1]); | 201 | /* Use the same MAC Address as FD Pause frames for all ports */ |
| 192 | REG_WRITE(REG_GLOBAL, 0x02, (addr[2] << 8) | addr[3]); | 202 | REG_WRITE(REG_GLOBAL, GLOBAL_MAC_01, (addr[0] << 9) | addr[1]); |
| 193 | REG_WRITE(REG_GLOBAL, 0x03, (addr[4] << 8) | addr[5]); | 203 | REG_WRITE(REG_GLOBAL, GLOBAL_MAC_23, (addr[2] << 8) | addr[3]); |
| 204 | REG_WRITE(REG_GLOBAL, GLOBAL_MAC_45, (addr[4] << 8) | addr[5]); | ||
| 194 | 205 | ||
| 195 | return 0; | 206 | return 0; |
| 196 | } | 207 | } |
| 197 | 208 | ||
| 198 | static int mv88e6060_port_to_phy_addr(int port) | 209 | static int mv88e6060_port_to_phy_addr(int port) |
| 199 | { | 210 | { |
| 200 | if (port >= 0 && port <= 5) | 211 | if (port >= 0 && port < MV88E6060_PORTS) |
| 201 | return port; | 212 | return port; |
| 202 | return -1; | 213 | return -1; |
| 203 | } | 214 | } |
| @@ -225,54 +236,6 @@ mv88e6060_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val) | |||
| 225 | return reg_write(ds, addr, regnum, val); | 236 | return reg_write(ds, addr, regnum, val); |
| 226 | } | 237 | } |
| 227 | 238 | ||
| 228 | static void mv88e6060_poll_link(struct dsa_switch *ds) | ||
| 229 | { | ||
| 230 | int i; | ||
| 231 | |||
| 232 | for (i = 0; i < DSA_MAX_PORTS; i++) { | ||
| 233 | struct net_device *dev; | ||
| 234 | int uninitialized_var(port_status); | ||
| 235 | int link; | ||
| 236 | int speed; | ||
| 237 | int duplex; | ||
| 238 | int fc; | ||
| 239 | |||
| 240 | dev = ds->ports[i]; | ||
| 241 | if (dev == NULL) | ||
| 242 | continue; | ||
| 243 | |||
| 244 | link = 0; | ||
| 245 | if (dev->flags & IFF_UP) { | ||
| 246 | port_status = reg_read(ds, REG_PORT(i), 0x00); | ||
| 247 | if (port_status < 0) | ||
| 248 | continue; | ||
| 249 | |||
| 250 | link = !!(port_status & 0x1000); | ||
| 251 | } | ||
| 252 | |||
| 253 | if (!link) { | ||
| 254 | if (netif_carrier_ok(dev)) { | ||
| 255 | netdev_info(dev, "link down\n"); | ||
| 256 | netif_carrier_off(dev); | ||
| 257 | } | ||
| 258 | continue; | ||
| 259 | } | ||
| 260 | |||
| 261 | speed = (port_status & 0x0100) ? 100 : 10; | ||
| 262 | duplex = (port_status & 0x0200) ? 1 : 0; | ||
| 263 | fc = ((port_status & 0xc000) == 0xc000) ? 1 : 0; | ||
| 264 | |||
| 265 | if (!netif_carrier_ok(dev)) { | ||
| 266 | netdev_info(dev, | ||
| 267 | "link up, %d Mb/s, %s duplex, flow control %sabled\n", | ||
| 268 | speed, | ||
| 269 | duplex ? "full" : "half", | ||
| 270 | fc ? "en" : "dis"); | ||
| 271 | netif_carrier_on(dev); | ||
| 272 | } | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | static struct dsa_switch_driver mv88e6060_switch_driver = { | 239 | static struct dsa_switch_driver mv88e6060_switch_driver = { |
| 277 | .tag_protocol = DSA_TAG_PROTO_TRAILER, | 240 | .tag_protocol = DSA_TAG_PROTO_TRAILER, |
| 278 | .probe = mv88e6060_probe, | 241 | .probe = mv88e6060_probe, |
| @@ -280,7 +243,6 @@ static struct dsa_switch_driver mv88e6060_switch_driver = { | |||
| 280 | .set_addr = mv88e6060_set_addr, | 243 | .set_addr = mv88e6060_set_addr, |
| 281 | .phy_read = mv88e6060_phy_read, | 244 | .phy_read = mv88e6060_phy_read, |
| 282 | .phy_write = mv88e6060_phy_write, | 245 | .phy_write = mv88e6060_phy_write, |
| 283 | .poll_link = mv88e6060_poll_link, | ||
| 284 | }; | 246 | }; |
| 285 | 247 | ||
| 286 | static int __init mv88e6060_init(void) | 248 | static int __init mv88e6060_init(void) |
