| xj | b04a402 | 2021-11-25 15:01:52 +0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * ADM6996 switch driver | 
|  | 3 | * | 
|  | 4 | * swconfig interface based on ar8216.c | 
|  | 5 | * | 
|  | 6 | * Copyright (c) 2008 Felix Fietkau <nbd@nbd.name> | 
|  | 7 | * VLAN support Copyright (c) 2010, 2011 Peter Lebbing <peter@digitalbrains.com> | 
|  | 8 | * Copyright (c) 2013 Hauke Mehrtens <hauke@hauke-m.de> | 
|  | 9 | * Copyright (c) 2014 Matti Laakso <malaakso@elisanet.fi> | 
|  | 10 | * | 
|  | 11 | * This program is free software; you can redistribute  it and/or modify it | 
|  | 12 | * under  the terms of the GNU General Public License v2 as published by the | 
|  | 13 | * Free Software Foundation | 
|  | 14 | */ | 
|  | 15 |  | 
|  | 16 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 
|  | 17 |  | 
|  | 18 | /*#define DEBUG 1*/ | 
|  | 19 | #include <linux/kernel.h> | 
|  | 20 | #include <linux/string.h> | 
|  | 21 | #include <linux/errno.h> | 
|  | 22 | #include <linux/unistd.h> | 
|  | 23 | #include <linux/slab.h> | 
|  | 24 | #include <linux/interrupt.h> | 
|  | 25 | #include <linux/init.h> | 
|  | 26 | #include <linux/delay.h> | 
|  | 27 | #include <linux/gpio.h> | 
|  | 28 | #include <linux/netdevice.h> | 
|  | 29 | #include <linux/etherdevice.h> | 
|  | 30 | #include <linux/skbuff.h> | 
|  | 31 | #include <linux/spinlock.h> | 
|  | 32 | #include <linux/mm.h> | 
|  | 33 | #include <linux/module.h> | 
|  | 34 | #include <linux/mii.h> | 
|  | 35 | #include <linux/platform_device.h> | 
|  | 36 | #include <linux/platform_data/adm6996-gpio.h> | 
|  | 37 | #include <linux/ethtool.h> | 
|  | 38 | #include <linux/phy.h> | 
|  | 39 | #include <linux/switch.h> | 
|  | 40 |  | 
|  | 41 | #include <asm/io.h> | 
|  | 42 | #include <asm/irq.h> | 
|  | 43 | #include <asm/uaccess.h> | 
|  | 44 | #include "adm6996.h" | 
|  | 45 |  | 
|  | 46 | MODULE_DESCRIPTION("Infineon ADM6996 Switch"); | 
|  | 47 | MODULE_AUTHOR("Felix Fietkau, Peter Lebbing <peter@digitalbrains.com>"); | 
|  | 48 | MODULE_LICENSE("GPL"); | 
|  | 49 |  | 
|  | 50 | static const char * const adm6996_model_name[] = | 
|  | 51 | { | 
|  | 52 | NULL, | 
|  | 53 | "ADM6996FC", | 
|  | 54 | "ADM6996M", | 
|  | 55 | "ADM6996L" | 
|  | 56 | }; | 
|  | 57 |  | 
|  | 58 | struct adm6996_mib_desc { | 
|  | 59 | unsigned int offset; | 
|  | 60 | const char *name; | 
|  | 61 | }; | 
|  | 62 |  | 
|  | 63 | struct adm6996_priv { | 
|  | 64 | struct switch_dev dev; | 
|  | 65 | void *priv; | 
|  | 66 |  | 
|  | 67 | u8 eecs; | 
|  | 68 | u8 eesk; | 
|  | 69 | u8 eedi; | 
|  | 70 |  | 
|  | 71 | enum adm6996_model model; | 
|  | 72 |  | 
|  | 73 | bool enable_vlan; | 
|  | 74 | bool vlan_enabled;	/* Current hardware state */ | 
|  | 75 |  | 
|  | 76 | #ifdef DEBUG | 
|  | 77 | u16 addr;		/* Debugging: register address to operate on */ | 
|  | 78 | #endif | 
|  | 79 |  | 
|  | 80 | u16 pvid[ADM_NUM_PORTS];	/* Primary VLAN ID */ | 
|  | 81 | u8 tagged_ports; | 
|  | 82 |  | 
|  | 83 | u16 vlan_id[ADM_NUM_VLANS]; | 
|  | 84 | u8 vlan_table[ADM_NUM_VLANS];	/* bitmap, 1 = port is member */ | 
|  | 85 | u8 vlan_tagged[ADM_NUM_VLANS];	/* bitmap, 1 = tagged member */ | 
|  | 86 |  | 
|  | 87 | struct mutex mib_lock; | 
|  | 88 | char buf[2048]; | 
|  | 89 |  | 
|  | 90 | struct mutex reg_mutex; | 
|  | 91 |  | 
|  | 92 | /* use abstraction for regops, we want to add gpio support in the future */ | 
|  | 93 | u16 (*read)(struct adm6996_priv *priv, enum admreg reg); | 
|  | 94 | void (*write)(struct adm6996_priv *priv, enum admreg reg, u16 val); | 
|  | 95 | }; | 
|  | 96 |  | 
|  | 97 | #define to_adm(_dev) container_of(_dev, struct adm6996_priv, dev) | 
|  | 98 | #define phy_to_adm(_phy) ((struct adm6996_priv *) (_phy)->priv) | 
|  | 99 |  | 
|  | 100 | #define MIB_DESC(_o, _n)	\ | 
|  | 101 | {			\ | 
|  | 102 | .offset = (_o),	\ | 
|  | 103 | .name = (_n),	\ | 
|  | 104 | } | 
|  | 105 |  | 
|  | 106 | static const struct adm6996_mib_desc adm6996_mibs[] = { | 
|  | 107 | MIB_DESC(ADM_CL0, "RxPacket"), | 
|  | 108 | MIB_DESC(ADM_CL6, "RxByte"), | 
|  | 109 | MIB_DESC(ADM_CL12, "TxPacket"), | 
|  | 110 | MIB_DESC(ADM_CL18, "TxByte"), | 
|  | 111 | MIB_DESC(ADM_CL24, "Collision"), | 
|  | 112 | MIB_DESC(ADM_CL30, "Error"), | 
|  | 113 | }; | 
|  | 114 |  | 
|  | 115 | #define ADM6996_MIB_RXB_ID	1 | 
|  | 116 | #define ADM6996_MIB_TXB_ID	3 | 
|  | 117 |  | 
|  | 118 | static inline u16 | 
|  | 119 | r16(struct adm6996_priv *priv, enum admreg reg) | 
|  | 120 | { | 
|  | 121 | return priv->read(priv, reg); | 
|  | 122 | } | 
|  | 123 |  | 
|  | 124 | static inline void | 
|  | 125 | w16(struct adm6996_priv *priv, enum admreg reg, u16 val) | 
|  | 126 | { | 
|  | 127 | priv->write(priv, reg, val); | 
|  | 128 | } | 
|  | 129 |  | 
|  | 130 | /* Minimum timing constants */ | 
|  | 131 | #define EECK_EDGE_TIME  3   /* 3us - max(adm 2.5us, 93c 1us) */ | 
|  | 132 | #define EEDI_SETUP_TIME 1   /* 1us - max(adm 10ns, 93c 400ns) */ | 
|  | 133 | #define EECS_SETUP_TIME 1   /* 1us - max(adm no, 93c 200ns) */ | 
|  | 134 |  | 
|  | 135 | static void adm6996_gpio_write(struct adm6996_priv *priv, int cs, char *buf, unsigned int bits) | 
|  | 136 | { | 
|  | 137 | int i, len = (bits + 7) / 8; | 
|  | 138 | u8 mask; | 
|  | 139 |  | 
|  | 140 | gpio_set_value(priv->eecs, cs); | 
|  | 141 | udelay(EECK_EDGE_TIME); | 
|  | 142 |  | 
|  | 143 | /* Byte assemble from MSB to LSB */ | 
|  | 144 | for (i = 0; i < len; i++) { | 
|  | 145 | /* Bit bang from MSB to LSB */ | 
|  | 146 | for (mask = 0x80; mask && bits > 0; mask >>= 1, bits --) { | 
|  | 147 | /* Clock low */ | 
|  | 148 | gpio_set_value(priv->eesk, 0); | 
|  | 149 | udelay(EECK_EDGE_TIME); | 
|  | 150 |  | 
|  | 151 | /* Output on rising edge */ | 
|  | 152 | gpio_set_value(priv->eedi, (mask & buf[i])); | 
|  | 153 | udelay(EEDI_SETUP_TIME); | 
|  | 154 |  | 
|  | 155 | /* Clock high */ | 
|  | 156 | gpio_set_value(priv->eesk, 1); | 
|  | 157 | udelay(EECK_EDGE_TIME); | 
|  | 158 | } | 
|  | 159 | } | 
|  | 160 |  | 
|  | 161 | /* Clock low */ | 
|  | 162 | gpio_set_value(priv->eesk, 0); | 
|  | 163 | udelay(EECK_EDGE_TIME); | 
|  | 164 |  | 
|  | 165 | if (cs) | 
|  | 166 | gpio_set_value(priv->eecs, 0); | 
|  | 167 | } | 
|  | 168 |  | 
|  | 169 | static void adm6996_gpio_read(struct adm6996_priv *priv, int cs, char *buf, unsigned int bits) | 
|  | 170 | { | 
|  | 171 | int i, len = (bits + 7) / 8; | 
|  | 172 | u8 mask; | 
|  | 173 |  | 
|  | 174 | gpio_set_value(priv->eecs, cs); | 
|  | 175 | udelay(EECK_EDGE_TIME); | 
|  | 176 |  | 
|  | 177 | /* Byte assemble from MSB to LSB */ | 
|  | 178 | for (i = 0; i < len; i++) { | 
|  | 179 | u8 byte; | 
|  | 180 |  | 
|  | 181 | /* Bit bang from MSB to LSB */ | 
|  | 182 | for (mask = 0x80, byte = 0; mask && bits > 0; mask >>= 1, bits --) { | 
|  | 183 | u8 gp; | 
|  | 184 |  | 
|  | 185 | /* Clock low */ | 
|  | 186 | gpio_set_value(priv->eesk, 0); | 
|  | 187 | udelay(EECK_EDGE_TIME); | 
|  | 188 |  | 
|  | 189 | /* Input on rising edge */ | 
|  | 190 | gp = gpio_get_value(priv->eedi); | 
|  | 191 | if (gp) | 
|  | 192 | byte |= mask; | 
|  | 193 |  | 
|  | 194 | /* Clock high */ | 
|  | 195 | gpio_set_value(priv->eesk, 1); | 
|  | 196 | udelay(EECK_EDGE_TIME); | 
|  | 197 | } | 
|  | 198 |  | 
|  | 199 | *buf++ = byte; | 
|  | 200 | } | 
|  | 201 |  | 
|  | 202 | /* Clock low */ | 
|  | 203 | gpio_set_value(priv->eesk, 0); | 
|  | 204 | udelay(EECK_EDGE_TIME); | 
|  | 205 |  | 
|  | 206 | if (cs) | 
|  | 207 | gpio_set_value(priv->eecs, 0); | 
|  | 208 | } | 
|  | 209 |  | 
|  | 210 | /* Advance clock(s) */ | 
|  | 211 | static void adm6996_gpio_adclk(struct adm6996_priv *priv, int clocks) | 
|  | 212 | { | 
|  | 213 | int i; | 
|  | 214 | for (i = 0; i < clocks; i++) { | 
|  | 215 | /* Clock high */ | 
|  | 216 | gpio_set_value(priv->eesk, 1); | 
|  | 217 | udelay(EECK_EDGE_TIME); | 
|  | 218 |  | 
|  | 219 | /* Clock low */ | 
|  | 220 | gpio_set_value(priv->eesk, 0); | 
|  | 221 | udelay(EECK_EDGE_TIME); | 
|  | 222 | } | 
|  | 223 | } | 
|  | 224 |  | 
|  | 225 | static u16 | 
|  | 226 | adm6996_read_gpio_reg(struct adm6996_priv *priv, enum admreg reg) | 
|  | 227 | { | 
|  | 228 | /* cmd: 01 10 T DD R RRRRRR */ | 
|  | 229 | u8 bits[6] = { | 
|  | 230 | 0xFF, 0xFF, 0xFF, 0xFF, | 
|  | 231 | (0x06 << 4) | ((0 & 0x01) << 3 | (reg&64)>>6), | 
|  | 232 | ((reg&63)<<2) | 
|  | 233 | }; | 
|  | 234 |  | 
|  | 235 | u8 rbits[4]; | 
|  | 236 |  | 
|  | 237 | /* Enable GPIO outputs with all pins to 0 */ | 
|  | 238 | gpio_direction_output(priv->eecs, 0); | 
|  | 239 | gpio_direction_output(priv->eesk, 0); | 
|  | 240 | gpio_direction_output(priv->eedi, 0); | 
|  | 241 |  | 
|  | 242 | adm6996_gpio_write(priv, 0, bits, 46); | 
|  | 243 | gpio_direction_input(priv->eedi); | 
|  | 244 | adm6996_gpio_adclk(priv, 2); | 
|  | 245 | adm6996_gpio_read(priv, 0, rbits, 32); | 
|  | 246 |  | 
|  | 247 | /* Extra clock(s) required per datasheet */ | 
|  | 248 | adm6996_gpio_adclk(priv, 2); | 
|  | 249 |  | 
|  | 250 | /* Disable GPIO outputs */ | 
|  | 251 | gpio_direction_input(priv->eecs); | 
|  | 252 | gpio_direction_input(priv->eesk); | 
|  | 253 |  | 
|  | 254 | /* EEPROM has 16-bit registers, but pumps out two registers in one request */ | 
|  | 255 | return (reg & 0x01 ?  (rbits[0]<<8) | rbits[1] : (rbits[2]<<8) | (rbits[3])); | 
|  | 256 | } | 
|  | 257 |  | 
|  | 258 | /* Write chip configuration register */ | 
|  | 259 | /* Follow 93c66 timing and chip's min EEPROM timing requirement */ | 
|  | 260 | static void | 
|  | 261 | adm6996_write_gpio_reg(struct adm6996_priv *priv, enum admreg reg, u16 val) | 
|  | 262 | { | 
|  | 263 | /* cmd(27bits): sb(1) + opc(01) + addr(bbbbbbbb) + data(bbbbbbbbbbbbbbbb) */ | 
|  | 264 | u8 bits[4] = { | 
|  | 265 | (0x05 << 5) | (reg >> 3), | 
|  | 266 | (reg << 5) | (u8)(val >> 11), | 
|  | 267 | (u8)(val >> 3), | 
|  | 268 | (u8)(val << 5) | 
|  | 269 | }; | 
|  | 270 |  | 
|  | 271 | /* Enable GPIO outputs with all pins to 0 */ | 
|  | 272 | gpio_direction_output(priv->eecs, 0); | 
|  | 273 | gpio_direction_output(priv->eesk, 0); | 
|  | 274 | gpio_direction_output(priv->eedi, 0); | 
|  | 275 |  | 
|  | 276 | /* Write cmd. Total 27 bits */ | 
|  | 277 | adm6996_gpio_write(priv, 1, bits, 27); | 
|  | 278 |  | 
|  | 279 | /* Extra clock(s) required per datasheet */ | 
|  | 280 | adm6996_gpio_adclk(priv, 2); | 
|  | 281 |  | 
|  | 282 | /* Disable GPIO outputs */ | 
|  | 283 | gpio_direction_input(priv->eecs); | 
|  | 284 | gpio_direction_input(priv->eesk); | 
|  | 285 | gpio_direction_input(priv->eedi); | 
|  | 286 | } | 
|  | 287 |  | 
|  | 288 | static u16 | 
|  | 289 | adm6996_read_mii_reg(struct adm6996_priv *priv, enum admreg reg) | 
|  | 290 | { | 
|  | 291 | struct phy_device *phydev = priv->priv; | 
|  | 292 | struct mii_bus *bus = phydev->mdio.bus; | 
|  | 293 |  | 
|  | 294 | return bus->read(bus, PHYADDR(reg)); | 
|  | 295 | } | 
|  | 296 |  | 
|  | 297 | static void | 
|  | 298 | adm6996_write_mii_reg(struct adm6996_priv *priv, enum admreg reg, u16 val) | 
|  | 299 | { | 
|  | 300 | struct phy_device *phydev = priv->priv; | 
|  | 301 | struct mii_bus *bus = phydev->mdio.bus; | 
|  | 302 |  | 
|  | 303 | bus->write(bus, PHYADDR(reg), val); | 
|  | 304 | } | 
|  | 305 |  | 
|  | 306 | static int | 
|  | 307 | adm6996_set_enable_vlan(struct switch_dev *dev, const struct switch_attr *attr, | 
|  | 308 | struct switch_val *val) | 
|  | 309 | { | 
|  | 310 | struct adm6996_priv *priv = to_adm(dev); | 
|  | 311 |  | 
|  | 312 | if (val->value.i > 1) | 
|  | 313 | return -EINVAL; | 
|  | 314 |  | 
|  | 315 | priv->enable_vlan = val->value.i; | 
|  | 316 |  | 
|  | 317 | return 0; | 
|  | 318 | }; | 
|  | 319 |  | 
|  | 320 | static int | 
|  | 321 | adm6996_get_enable_vlan(struct switch_dev *dev, const struct switch_attr *attr, | 
|  | 322 | struct switch_val *val) | 
|  | 323 | { | 
|  | 324 | struct adm6996_priv *priv = to_adm(dev); | 
|  | 325 |  | 
|  | 326 | val->value.i = priv->enable_vlan; | 
|  | 327 |  | 
|  | 328 | return 0; | 
|  | 329 | }; | 
|  | 330 |  | 
|  | 331 | #ifdef DEBUG | 
|  | 332 |  | 
|  | 333 | static int | 
|  | 334 | adm6996_set_addr(struct switch_dev *dev, const struct switch_attr *attr, | 
|  | 335 | struct switch_val *val) | 
|  | 336 | { | 
|  | 337 | struct adm6996_priv *priv = to_adm(dev); | 
|  | 338 |  | 
|  | 339 | if (val->value.i > 1023) | 
|  | 340 | return -EINVAL; | 
|  | 341 |  | 
|  | 342 | priv->addr = val->value.i; | 
|  | 343 |  | 
|  | 344 | return 0; | 
|  | 345 | }; | 
|  | 346 |  | 
|  | 347 | static int | 
|  | 348 | adm6996_get_addr(struct switch_dev *dev, const struct switch_attr *attr, | 
|  | 349 | struct switch_val *val) | 
|  | 350 | { | 
|  | 351 | struct adm6996_priv *priv = to_adm(dev); | 
|  | 352 |  | 
|  | 353 | val->value.i = priv->addr; | 
|  | 354 |  | 
|  | 355 | return 0; | 
|  | 356 | }; | 
|  | 357 |  | 
|  | 358 | static int | 
|  | 359 | adm6996_set_data(struct switch_dev *dev, const struct switch_attr *attr, | 
|  | 360 | struct switch_val *val) | 
|  | 361 | { | 
|  | 362 | struct adm6996_priv *priv = to_adm(dev); | 
|  | 363 |  | 
|  | 364 | if (val->value.i > 65535) | 
|  | 365 | return -EINVAL; | 
|  | 366 |  | 
|  | 367 | w16(priv, priv->addr, val->value.i); | 
|  | 368 |  | 
|  | 369 | return 0; | 
|  | 370 | }; | 
|  | 371 |  | 
|  | 372 | static int | 
|  | 373 | adm6996_get_data(struct switch_dev *dev, const struct switch_attr *attr, | 
|  | 374 | struct switch_val *val) | 
|  | 375 | { | 
|  | 376 | struct adm6996_priv *priv = to_adm(dev); | 
|  | 377 |  | 
|  | 378 | val->value.i = r16(priv, priv->addr); | 
|  | 379 |  | 
|  | 380 | return 0; | 
|  | 381 | }; | 
|  | 382 |  | 
|  | 383 | #endif /* def DEBUG */ | 
|  | 384 |  | 
|  | 385 | static int | 
|  | 386 | adm6996_set_pvid(struct switch_dev *dev, int port, int vlan) | 
|  | 387 | { | 
|  | 388 | struct adm6996_priv *priv = to_adm(dev); | 
|  | 389 |  | 
|  | 390 | pr_devel("set_pvid port %d vlan %d\n", port, vlan); | 
|  | 391 |  | 
|  | 392 | if (vlan > ADM_VLAN_MAX_ID) | 
|  | 393 | return -EINVAL; | 
|  | 394 |  | 
|  | 395 | priv->pvid[port] = vlan; | 
|  | 396 |  | 
|  | 397 | return 0; | 
|  | 398 | } | 
|  | 399 |  | 
|  | 400 | static int | 
|  | 401 | adm6996_get_pvid(struct switch_dev *dev, int port, int *vlan) | 
|  | 402 | { | 
|  | 403 | struct adm6996_priv *priv = to_adm(dev); | 
|  | 404 |  | 
|  | 405 | pr_devel("get_pvid port %d\n", port); | 
|  | 406 | *vlan = priv->pvid[port]; | 
|  | 407 |  | 
|  | 408 | return 0; | 
|  | 409 | } | 
|  | 410 |  | 
|  | 411 | static int | 
|  | 412 | adm6996_set_vid(struct switch_dev *dev, const struct switch_attr *attr, | 
|  | 413 | struct switch_val *val) | 
|  | 414 | { | 
|  | 415 | struct adm6996_priv *priv = to_adm(dev); | 
|  | 416 |  | 
|  | 417 | pr_devel("set_vid port %d vid %d\n", val->port_vlan, val->value.i); | 
|  | 418 |  | 
|  | 419 | if (val->value.i > ADM_VLAN_MAX_ID) | 
|  | 420 | return -EINVAL; | 
|  | 421 |  | 
|  | 422 | priv->vlan_id[val->port_vlan] = val->value.i; | 
|  | 423 |  | 
|  | 424 | return 0; | 
|  | 425 | }; | 
|  | 426 |  | 
|  | 427 | static int | 
|  | 428 | adm6996_get_vid(struct switch_dev *dev, const struct switch_attr *attr, | 
|  | 429 | struct switch_val *val) | 
|  | 430 | { | 
|  | 431 | struct adm6996_priv *priv = to_adm(dev); | 
|  | 432 |  | 
|  | 433 | pr_devel("get_vid port %d\n", val->port_vlan); | 
|  | 434 |  | 
|  | 435 | val->value.i = priv->vlan_id[val->port_vlan]; | 
|  | 436 |  | 
|  | 437 | return 0; | 
|  | 438 | }; | 
|  | 439 |  | 
|  | 440 | static int | 
|  | 441 | adm6996_get_ports(struct switch_dev *dev, struct switch_val *val) | 
|  | 442 | { | 
|  | 443 | struct adm6996_priv *priv = to_adm(dev); | 
|  | 444 | u8 ports = priv->vlan_table[val->port_vlan]; | 
|  | 445 | u8 tagged = priv->vlan_tagged[val->port_vlan]; | 
|  | 446 | int i; | 
|  | 447 |  | 
|  | 448 | pr_devel("get_ports port_vlan %d\n", val->port_vlan); | 
|  | 449 |  | 
|  | 450 | val->len = 0; | 
|  | 451 |  | 
|  | 452 | for (i = 0; i < ADM_NUM_PORTS; i++) { | 
|  | 453 | struct switch_port *p; | 
|  | 454 |  | 
|  | 455 | if (!(ports & (1 << i))) | 
|  | 456 | continue; | 
|  | 457 |  | 
|  | 458 | p = &val->value.ports[val->len++]; | 
|  | 459 | p->id = i; | 
|  | 460 | if (tagged & (1 << i)) | 
|  | 461 | p->flags = (1 << SWITCH_PORT_FLAG_TAGGED); | 
|  | 462 | else | 
|  | 463 | p->flags = 0; | 
|  | 464 | } | 
|  | 465 |  | 
|  | 466 | return 0; | 
|  | 467 | }; | 
|  | 468 |  | 
|  | 469 | static int | 
|  | 470 | adm6996_set_ports(struct switch_dev *dev, struct switch_val *val) | 
|  | 471 | { | 
|  | 472 | struct adm6996_priv *priv = to_adm(dev); | 
|  | 473 | u8 *ports = &priv->vlan_table[val->port_vlan]; | 
|  | 474 | u8 *tagged = &priv->vlan_tagged[val->port_vlan]; | 
|  | 475 | int i; | 
|  | 476 |  | 
|  | 477 | pr_devel("set_ports port_vlan %d ports", val->port_vlan); | 
|  | 478 |  | 
|  | 479 | *ports = 0; | 
|  | 480 | *tagged = 0; | 
|  | 481 |  | 
|  | 482 | for (i = 0; i < val->len; i++) { | 
|  | 483 | struct switch_port *p = &val->value.ports[i]; | 
|  | 484 |  | 
|  | 485 | #ifdef DEBUG | 
|  | 486 | pr_cont(" %d%s", p->id, | 
|  | 487 | ((p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) ? "T" : | 
|  | 488 | "")); | 
|  | 489 | #endif | 
|  | 490 |  | 
|  | 491 | if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) { | 
|  | 492 | *tagged |= (1 << p->id); | 
|  | 493 | priv->tagged_ports |= (1 << p->id); | 
|  | 494 | } | 
|  | 495 |  | 
|  | 496 | *ports |= (1 << p->id); | 
|  | 497 | } | 
|  | 498 |  | 
|  | 499 | #ifdef DEBUG | 
|  | 500 | pr_cont("\n"); | 
|  | 501 | #endif | 
|  | 502 |  | 
|  | 503 | return 0; | 
|  | 504 | }; | 
|  | 505 |  | 
|  | 506 | /* | 
|  | 507 | * Precondition: reg_mutex must be held | 
|  | 508 | */ | 
|  | 509 | static void | 
|  | 510 | adm6996_enable_vlan(struct adm6996_priv *priv) | 
|  | 511 | { | 
|  | 512 | u16 reg; | 
|  | 513 |  | 
|  | 514 | reg = r16(priv, ADM_OTBE_P2_PVID); | 
|  | 515 | reg &= ~(ADM_OTBE_MASK); | 
|  | 516 | w16(priv, ADM_OTBE_P2_PVID, reg); | 
|  | 517 | reg = r16(priv, ADM_IFNTE); | 
|  | 518 | reg &= ~(ADM_IFNTE_MASK); | 
|  | 519 | w16(priv, ADM_IFNTE, reg); | 
|  | 520 | reg = r16(priv, ADM_VID_CHECK); | 
|  | 521 | reg |= ADM_VID_CHECK_MASK; | 
|  | 522 | w16(priv, ADM_VID_CHECK, reg); | 
|  | 523 | reg = r16(priv, ADM_SYSC0); | 
|  | 524 | reg |= ADM_NTTE; | 
|  | 525 | reg &= ~(ADM_RVID1); | 
|  | 526 | w16(priv, ADM_SYSC0, reg); | 
|  | 527 | reg = r16(priv, ADM_SYSC3); | 
|  | 528 | reg |= ADM_TBV; | 
|  | 529 | w16(priv, ADM_SYSC3, reg); | 
|  | 530 | } | 
|  | 531 |  | 
|  | 532 | static void | 
|  | 533 | adm6996_enable_vlan_6996l(struct adm6996_priv *priv) | 
|  | 534 | { | 
|  | 535 | u16 reg; | 
|  | 536 |  | 
|  | 537 | reg = r16(priv, ADM_SYSC3); | 
|  | 538 | reg |= ADM_TBV; | 
|  | 539 | reg |= ADM_MAC_CLONE; | 
|  | 540 | w16(priv, ADM_SYSC3, reg); | 
|  | 541 | } | 
|  | 542 |  | 
|  | 543 | /* | 
|  | 544 | * Disable VLANs | 
|  | 545 | * | 
|  | 546 | * Sets VLAN mapping for port-based VLAN with all ports connected to | 
|  | 547 | * eachother (this is also the power-on default). | 
|  | 548 | * | 
|  | 549 | * Precondition: reg_mutex must be held | 
|  | 550 | */ | 
|  | 551 | static void | 
|  | 552 | adm6996_disable_vlan(struct adm6996_priv *priv) | 
|  | 553 | { | 
|  | 554 | u16 reg; | 
|  | 555 | int i; | 
|  | 556 |  | 
|  | 557 | for (i = 0; i < ADM_NUM_VLANS; i++) { | 
|  | 558 | reg = ADM_VLAN_FILT_MEMBER_MASK; | 
|  | 559 | w16(priv, ADM_VLAN_FILT_L(i), reg); | 
|  | 560 | reg = ADM_VLAN_FILT_VALID | ADM_VLAN_FILT_VID(1); | 
|  | 561 | w16(priv, ADM_VLAN_FILT_H(i), reg); | 
|  | 562 | } | 
|  | 563 |  | 
|  | 564 | reg = r16(priv, ADM_OTBE_P2_PVID); | 
|  | 565 | reg |= ADM_OTBE_MASK; | 
|  | 566 | w16(priv, ADM_OTBE_P2_PVID, reg); | 
|  | 567 | reg = r16(priv, ADM_IFNTE); | 
|  | 568 | reg |= ADM_IFNTE_MASK; | 
|  | 569 | w16(priv, ADM_IFNTE, reg); | 
|  | 570 | reg = r16(priv, ADM_VID_CHECK); | 
|  | 571 | reg &= ~(ADM_VID_CHECK_MASK); | 
|  | 572 | w16(priv, ADM_VID_CHECK, reg); | 
|  | 573 | reg = r16(priv, ADM_SYSC0); | 
|  | 574 | reg &= ~(ADM_NTTE); | 
|  | 575 | reg |= ADM_RVID1; | 
|  | 576 | w16(priv, ADM_SYSC0, reg); | 
|  | 577 | reg = r16(priv, ADM_SYSC3); | 
|  | 578 | reg &= ~(ADM_TBV); | 
|  | 579 | w16(priv, ADM_SYSC3, reg); | 
|  | 580 | } | 
|  | 581 |  | 
|  | 582 | /* | 
|  | 583 | * Disable VLANs | 
|  | 584 | * | 
|  | 585 | * Sets VLAN mapping for port-based VLAN with all ports connected to | 
|  | 586 | * eachother (this is also the power-on default). | 
|  | 587 | * | 
|  | 588 | * Precondition: reg_mutex must be held | 
|  | 589 | */ | 
|  | 590 | static void | 
|  | 591 | adm6996_disable_vlan_6996l(struct adm6996_priv *priv) | 
|  | 592 | { | 
|  | 593 | u16 reg; | 
|  | 594 | int i; | 
|  | 595 |  | 
|  | 596 | for (i = 0; i < ADM_NUM_VLANS; i++) { | 
|  | 597 | w16(priv, ADM_VLAN_MAP(i), 0); | 
|  | 598 | } | 
|  | 599 |  | 
|  | 600 | reg = r16(priv, ADM_SYSC3); | 
|  | 601 | reg &= ~(ADM_TBV); | 
|  | 602 | reg &= ~(ADM_MAC_CLONE); | 
|  | 603 | w16(priv, ADM_SYSC3, reg); | 
|  | 604 | } | 
|  | 605 |  | 
|  | 606 | /* | 
|  | 607 | * Precondition: reg_mutex must be held | 
|  | 608 | */ | 
|  | 609 | static void | 
|  | 610 | adm6996_apply_port_pvids(struct adm6996_priv *priv) | 
|  | 611 | { | 
|  | 612 | u16 reg; | 
|  | 613 | int i; | 
|  | 614 |  | 
|  | 615 | for (i = 0; i < ADM_NUM_PORTS; i++) { | 
|  | 616 | reg = r16(priv, adm_portcfg[i]); | 
|  | 617 | reg &= ~(ADM_PORTCFG_PVID_MASK); | 
|  | 618 | reg |= ADM_PORTCFG_PVID(priv->pvid[i]); | 
|  | 619 | if (priv->model == ADM6996L) { | 
|  | 620 | if (priv->tagged_ports & (1 << i)) | 
|  | 621 | reg |= (1 << 4); | 
|  | 622 | else | 
|  | 623 | reg &= ~(1 << 4); | 
|  | 624 | } | 
|  | 625 | w16(priv, adm_portcfg[i], reg); | 
|  | 626 | } | 
|  | 627 |  | 
|  | 628 | w16(priv, ADM_P0_PVID, ADM_P0_PVID_VAL(priv->pvid[0])); | 
|  | 629 | w16(priv, ADM_P1_PVID, ADM_P1_PVID_VAL(priv->pvid[1])); | 
|  | 630 | reg = r16(priv, ADM_OTBE_P2_PVID); | 
|  | 631 | reg &= ~(ADM_P2_PVID_MASK); | 
|  | 632 | reg |= ADM_P2_PVID_VAL(priv->pvid[2]); | 
|  | 633 | w16(priv, ADM_OTBE_P2_PVID, reg); | 
|  | 634 | reg = ADM_P3_PVID_VAL(priv->pvid[3]); | 
|  | 635 | reg |= ADM_P4_PVID_VAL(priv->pvid[4]); | 
|  | 636 | w16(priv, ADM_P3_P4_PVID, reg); | 
|  | 637 | reg = r16(priv, ADM_P5_PVID); | 
|  | 638 | reg &= ~(ADM_P2_PVID_MASK); | 
|  | 639 | reg |= ADM_P5_PVID_VAL(priv->pvid[5]); | 
|  | 640 | w16(priv, ADM_P5_PVID, reg); | 
|  | 641 | } | 
|  | 642 |  | 
|  | 643 | /* | 
|  | 644 | * Precondition: reg_mutex must be held | 
|  | 645 | */ | 
|  | 646 | static void | 
|  | 647 | adm6996_apply_vlan_filters(struct adm6996_priv *priv) | 
|  | 648 | { | 
|  | 649 | u8 ports, tagged; | 
|  | 650 | u16 vid, reg; | 
|  | 651 | int i; | 
|  | 652 |  | 
|  | 653 | for (i = 0; i < ADM_NUM_VLANS; i++) { | 
|  | 654 | vid = priv->vlan_id[i]; | 
|  | 655 | ports = priv->vlan_table[i]; | 
|  | 656 | tagged = priv->vlan_tagged[i]; | 
|  | 657 |  | 
|  | 658 | if (ports == 0) { | 
|  | 659 | /* Disable VLAN entry */ | 
|  | 660 | w16(priv, ADM_VLAN_FILT_H(i), 0); | 
|  | 661 | w16(priv, ADM_VLAN_FILT_L(i), 0); | 
|  | 662 | continue; | 
|  | 663 | } | 
|  | 664 |  | 
|  | 665 | reg = ADM_VLAN_FILT_MEMBER(ports); | 
|  | 666 | reg |= ADM_VLAN_FILT_TAGGED(tagged); | 
|  | 667 | w16(priv, ADM_VLAN_FILT_L(i), reg); | 
|  | 668 | reg = ADM_VLAN_FILT_VALID | ADM_VLAN_FILT_VID(vid); | 
|  | 669 | w16(priv, ADM_VLAN_FILT_H(i), reg); | 
|  | 670 | } | 
|  | 671 | } | 
|  | 672 |  | 
|  | 673 | static void | 
|  | 674 | adm6996_apply_vlan_filters_6996l(struct adm6996_priv *priv) | 
|  | 675 | { | 
|  | 676 | u8 ports; | 
|  | 677 | u16 reg; | 
|  | 678 | int i; | 
|  | 679 |  | 
|  | 680 | for (i = 0; i < ADM_NUM_VLANS; i++) { | 
|  | 681 | ports = priv->vlan_table[i]; | 
|  | 682 |  | 
|  | 683 | if (ports == 0) { | 
|  | 684 | /* Disable VLAN entry */ | 
|  | 685 | w16(priv, ADM_VLAN_MAP(i), 0); | 
|  | 686 | continue; | 
|  | 687 | } else { | 
|  | 688 | reg = ADM_VLAN_FILT(ports); | 
|  | 689 | w16(priv, ADM_VLAN_MAP(i), reg); | 
|  | 690 | } | 
|  | 691 | } | 
|  | 692 | } | 
|  | 693 |  | 
|  | 694 | static int | 
|  | 695 | adm6996_hw_apply(struct switch_dev *dev) | 
|  | 696 | { | 
|  | 697 | struct adm6996_priv *priv = to_adm(dev); | 
|  | 698 |  | 
|  | 699 | pr_devel("hw_apply\n"); | 
|  | 700 |  | 
|  | 701 | mutex_lock(&priv->reg_mutex); | 
|  | 702 |  | 
|  | 703 | if (!priv->enable_vlan) { | 
|  | 704 | if (priv->vlan_enabled) { | 
|  | 705 | if (priv->model == ADM6996L) | 
|  | 706 | adm6996_disable_vlan_6996l(priv); | 
|  | 707 | else | 
|  | 708 | adm6996_disable_vlan(priv); | 
|  | 709 | priv->vlan_enabled = 0; | 
|  | 710 | } | 
|  | 711 | goto out; | 
|  | 712 | } | 
|  | 713 |  | 
|  | 714 | if (!priv->vlan_enabled) { | 
|  | 715 | if (priv->model == ADM6996L) | 
|  | 716 | adm6996_enable_vlan_6996l(priv); | 
|  | 717 | else | 
|  | 718 | adm6996_enable_vlan(priv); | 
|  | 719 | priv->vlan_enabled = 1; | 
|  | 720 | } | 
|  | 721 |  | 
|  | 722 | adm6996_apply_port_pvids(priv); | 
|  | 723 | if (priv->model == ADM6996L) | 
|  | 724 | adm6996_apply_vlan_filters_6996l(priv); | 
|  | 725 | else | 
|  | 726 | adm6996_apply_vlan_filters(priv); | 
|  | 727 |  | 
|  | 728 | out: | 
|  | 729 | mutex_unlock(&priv->reg_mutex); | 
|  | 730 |  | 
|  | 731 | return 0; | 
|  | 732 | } | 
|  | 733 |  | 
|  | 734 | /* | 
|  | 735 | * Reset the switch | 
|  | 736 | * | 
|  | 737 | * The ADM6996 can't do a software-initiated reset, so we just initialise the | 
|  | 738 | * registers we support in this driver. | 
|  | 739 | * | 
|  | 740 | * Precondition: reg_mutex must be held | 
|  | 741 | */ | 
|  | 742 | static void | 
|  | 743 | adm6996_perform_reset (struct adm6996_priv *priv) | 
|  | 744 | { | 
|  | 745 | int i; | 
|  | 746 |  | 
|  | 747 | /* initialize port and vlan settings */ | 
|  | 748 | for (i = 0; i < ADM_NUM_PORTS - 1; i++) { | 
|  | 749 | w16(priv, adm_portcfg[i], ADM_PORTCFG_INIT | | 
|  | 750 | ADM_PORTCFG_PVID(0)); | 
|  | 751 | } | 
|  | 752 | w16(priv, adm_portcfg[5], ADM_PORTCFG_CPU); | 
|  | 753 |  | 
|  | 754 | if (priv->model == ADM6996M || priv->model == ADM6996FC) { | 
|  | 755 | /* reset all PHY ports */ | 
|  | 756 | for (i = 0; i < ADM_PHY_PORTS; i++) { | 
|  | 757 | w16(priv, ADM_PHY_PORT(i), ADM_PHYCFG_INIT); | 
|  | 758 | } | 
|  | 759 | } | 
|  | 760 |  | 
|  | 761 | priv->enable_vlan = 0; | 
|  | 762 | priv->vlan_enabled = 0; | 
|  | 763 |  | 
|  | 764 | for (i = 0; i < ADM_NUM_PORTS; i++) { | 
|  | 765 | priv->pvid[i] = 0; | 
|  | 766 | } | 
|  | 767 |  | 
|  | 768 | for (i = 0; i < ADM_NUM_VLANS; i++) { | 
|  | 769 | priv->vlan_id[i] = i; | 
|  | 770 | priv->vlan_table[i] = 0; | 
|  | 771 | priv->vlan_tagged[i] = 0; | 
|  | 772 | } | 
|  | 773 |  | 
|  | 774 | if (priv->model == ADM6996M) { | 
|  | 775 | /* Clear VLAN priority map so prio's are unused */ | 
|  | 776 | w16 (priv, ADM_VLAN_PRIOMAP, 0); | 
|  | 777 |  | 
|  | 778 | adm6996_disable_vlan(priv); | 
|  | 779 | adm6996_apply_port_pvids(priv); | 
|  | 780 | } else if (priv->model == ADM6996L) { | 
|  | 781 | /* Clear VLAN priority map so prio's are unused */ | 
|  | 782 | w16 (priv, ADM_VLAN_PRIOMAP, 0); | 
|  | 783 |  | 
|  | 784 | adm6996_disable_vlan_6996l(priv); | 
|  | 785 | adm6996_apply_port_pvids(priv); | 
|  | 786 | } | 
|  | 787 | } | 
|  | 788 |  | 
|  | 789 | static int | 
|  | 790 | adm6996_reset_switch(struct switch_dev *dev) | 
|  | 791 | { | 
|  | 792 | struct adm6996_priv *priv = to_adm(dev); | 
|  | 793 |  | 
|  | 794 | pr_devel("reset\n"); | 
|  | 795 |  | 
|  | 796 | mutex_lock(&priv->reg_mutex); | 
|  | 797 | adm6996_perform_reset (priv); | 
|  | 798 | mutex_unlock(&priv->reg_mutex); | 
|  | 799 | return 0; | 
|  | 800 | } | 
|  | 801 |  | 
|  | 802 | static int | 
|  | 803 | adm6996_get_port_link(struct switch_dev *dev, int port, | 
|  | 804 | struct switch_port_link *link) | 
|  | 805 | { | 
|  | 806 | struct adm6996_priv *priv = to_adm(dev); | 
|  | 807 |  | 
|  | 808 | u16 reg = 0; | 
|  | 809 |  | 
|  | 810 | if (port >= ADM_NUM_PORTS) | 
|  | 811 | return -EINVAL; | 
|  | 812 |  | 
|  | 813 | switch (port) { | 
|  | 814 | case 0: | 
|  | 815 | reg = r16(priv, ADM_PS0); | 
|  | 816 | break; | 
|  | 817 | case 1: | 
|  | 818 | reg = r16(priv, ADM_PS0); | 
|  | 819 | reg = reg >> 8; | 
|  | 820 | break; | 
|  | 821 | case 2: | 
|  | 822 | reg = r16(priv, ADM_PS1); | 
|  | 823 | break; | 
|  | 824 | case 3: | 
|  | 825 | reg = r16(priv, ADM_PS1); | 
|  | 826 | reg = reg >> 8; | 
|  | 827 | break; | 
|  | 828 | case 4: | 
|  | 829 | reg = r16(priv, ADM_PS1); | 
|  | 830 | reg = reg >> 12; | 
|  | 831 | break; | 
|  | 832 | case 5: | 
|  | 833 | reg = r16(priv, ADM_PS2); | 
|  | 834 | /* Bits 0, 1, 3 and 4. */ | 
|  | 835 | reg = (reg & 3) | ((reg & 24) >> 1); | 
|  | 836 | break; | 
|  | 837 | default: | 
|  | 838 | return -EINVAL; | 
|  | 839 | } | 
|  | 840 |  | 
|  | 841 | link->link = reg & ADM_PS_LS; | 
|  | 842 | if (!link->link) | 
|  | 843 | return 0; | 
|  | 844 | link->aneg = true; | 
|  | 845 | link->duplex = reg & ADM_PS_DS; | 
|  | 846 | link->tx_flow = reg & ADM_PS_FCS; | 
|  | 847 | link->rx_flow = reg & ADM_PS_FCS; | 
|  | 848 | if (reg & ADM_PS_SS) | 
|  | 849 | link->speed = SWITCH_PORT_SPEED_100; | 
|  | 850 | else | 
|  | 851 | link->speed = SWITCH_PORT_SPEED_10; | 
|  | 852 |  | 
|  | 853 | return 0; | 
|  | 854 | } | 
|  | 855 |  | 
|  | 856 | static int | 
|  | 857 | adm6996_sw_get_port_mib(struct switch_dev *dev, | 
|  | 858 | const struct switch_attr *attr, | 
|  | 859 | struct switch_val *val) | 
|  | 860 | { | 
|  | 861 | struct adm6996_priv *priv = to_adm(dev); | 
|  | 862 | int port; | 
|  | 863 | char *buf = priv->buf; | 
|  | 864 | int i, len = 0; | 
|  | 865 | u32 reg = 0; | 
|  | 866 |  | 
|  | 867 | port = val->port_vlan; | 
|  | 868 | if (port >= ADM_NUM_PORTS) | 
|  | 869 | return -EINVAL; | 
|  | 870 |  | 
|  | 871 | mutex_lock(&priv->mib_lock); | 
|  | 872 |  | 
|  | 873 | len += snprintf(buf + len, sizeof(priv->buf) - len, | 
|  | 874 | "Port %d MIB counters\n", | 
|  | 875 | port); | 
|  | 876 |  | 
|  | 877 | for (i = 0; i < ARRAY_SIZE(adm6996_mibs); i++) { | 
|  | 878 | reg = r16(priv, adm6996_mibs[i].offset + ADM_OFFSET_PORT(port)); | 
|  | 879 | reg += r16(priv, adm6996_mibs[i].offset + ADM_OFFSET_PORT(port) + 1) << 16; | 
|  | 880 | len += snprintf(buf + len, sizeof(priv->buf) - len, | 
|  | 881 | "%-12s: %u\n", | 
|  | 882 | adm6996_mibs[i].name, | 
|  | 883 | reg); | 
|  | 884 | } | 
|  | 885 |  | 
|  | 886 | mutex_unlock(&priv->mib_lock); | 
|  | 887 |  | 
|  | 888 | val->value.s = buf; | 
|  | 889 | val->len = len; | 
|  | 890 |  | 
|  | 891 | return 0; | 
|  | 892 | } | 
|  | 893 |  | 
|  | 894 | static int | 
|  | 895 | adm6996_get_port_stats(struct switch_dev *dev, int port, | 
|  | 896 | struct switch_port_stats *stats) | 
|  | 897 | { | 
|  | 898 | struct adm6996_priv *priv = to_adm(dev); | 
|  | 899 | int id; | 
|  | 900 | u32 reg = 0; | 
|  | 901 |  | 
|  | 902 | if (port >= ADM_NUM_PORTS) | 
|  | 903 | return -EINVAL; | 
|  | 904 |  | 
|  | 905 | mutex_lock(&priv->mib_lock); | 
|  | 906 |  | 
|  | 907 | id = ADM6996_MIB_TXB_ID; | 
|  | 908 | reg = r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port)); | 
|  | 909 | reg += r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port) + 1) << 16; | 
|  | 910 | stats->tx_bytes = reg; | 
|  | 911 |  | 
|  | 912 | id = ADM6996_MIB_RXB_ID; | 
|  | 913 | reg = r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port)); | 
|  | 914 | reg += r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port) + 1) << 16; | 
|  | 915 | stats->rx_bytes = reg; | 
|  | 916 |  | 
|  | 917 | mutex_unlock(&priv->mib_lock); | 
|  | 918 |  | 
|  | 919 | return 0; | 
|  | 920 | } | 
|  | 921 |  | 
|  | 922 | static struct switch_attr adm6996_globals[] = { | 
|  | 923 | { | 
|  | 924 | .type = SWITCH_TYPE_INT, | 
|  | 925 | .name = "enable_vlan", | 
|  | 926 | .description = "Enable VLANs", | 
|  | 927 | .set = adm6996_set_enable_vlan, | 
|  | 928 | .get = adm6996_get_enable_vlan, | 
|  | 929 | }, | 
|  | 930 | #ifdef DEBUG | 
|  | 931 | { | 
|  | 932 | .type = SWITCH_TYPE_INT, | 
|  | 933 | .name = "addr", | 
|  | 934 | .description = | 
|  | 935 | "Direct register access: set register address (0 - 1023)", | 
|  | 936 | .set = adm6996_set_addr, | 
|  | 937 | .get = adm6996_get_addr, | 
|  | 938 | }, | 
|  | 939 | { | 
|  | 940 | .type = SWITCH_TYPE_INT, | 
|  | 941 | .name = "data", | 
|  | 942 | .description = | 
|  | 943 | "Direct register access: read/write to register (0 - 65535)", | 
|  | 944 | .set = adm6996_set_data, | 
|  | 945 | .get = adm6996_get_data, | 
|  | 946 | }, | 
|  | 947 | #endif /* def DEBUG */ | 
|  | 948 | }; | 
|  | 949 |  | 
|  | 950 | static struct switch_attr adm6996_port[] = { | 
|  | 951 | { | 
|  | 952 | .type = SWITCH_TYPE_STRING, | 
|  | 953 | .name = "mib", | 
|  | 954 | .description = "Get port's MIB counters", | 
|  | 955 | .set = NULL, | 
|  | 956 | .get = adm6996_sw_get_port_mib, | 
|  | 957 | }, | 
|  | 958 | }; | 
|  | 959 |  | 
|  | 960 | static struct switch_attr adm6996_vlan[] = { | 
|  | 961 | { | 
|  | 962 | .type = SWITCH_TYPE_INT, | 
|  | 963 | .name = "vid", | 
|  | 964 | .description = "VLAN ID", | 
|  | 965 | .set = adm6996_set_vid, | 
|  | 966 | .get = adm6996_get_vid, | 
|  | 967 | }, | 
|  | 968 | }; | 
|  | 969 |  | 
|  | 970 | static struct switch_dev_ops adm6996_ops = { | 
|  | 971 | .attr_global = { | 
|  | 972 | .attr = adm6996_globals, | 
|  | 973 | .n_attr = ARRAY_SIZE(adm6996_globals), | 
|  | 974 | }, | 
|  | 975 | .attr_port = { | 
|  | 976 | .attr = adm6996_port, | 
|  | 977 | .n_attr = ARRAY_SIZE(adm6996_port), | 
|  | 978 | }, | 
|  | 979 | .attr_vlan = { | 
|  | 980 | .attr = adm6996_vlan, | 
|  | 981 | .n_attr = ARRAY_SIZE(adm6996_vlan), | 
|  | 982 | }, | 
|  | 983 | .get_port_pvid = adm6996_get_pvid, | 
|  | 984 | .set_port_pvid = adm6996_set_pvid, | 
|  | 985 | .get_vlan_ports = adm6996_get_ports, | 
|  | 986 | .set_vlan_ports = adm6996_set_ports, | 
|  | 987 | .apply_config = adm6996_hw_apply, | 
|  | 988 | .reset_switch = adm6996_reset_switch, | 
|  | 989 | .get_port_link = adm6996_get_port_link, | 
|  | 990 | .get_port_stats = adm6996_get_port_stats, | 
|  | 991 | }; | 
|  | 992 |  | 
|  | 993 | static int adm6996_switch_init(struct adm6996_priv *priv, const char *alias, struct net_device *netdev) | 
|  | 994 | { | 
|  | 995 | struct switch_dev *swdev; | 
|  | 996 | u16 test, old; | 
|  | 997 |  | 
|  | 998 | if (!priv->model) { | 
|  | 999 | /* Detect type of chip */ | 
|  | 1000 | old = r16(priv, ADM_VID_CHECK); | 
|  | 1001 | test = old ^ (1 << 12); | 
|  | 1002 | w16(priv, ADM_VID_CHECK, test); | 
|  | 1003 | test ^= r16(priv, ADM_VID_CHECK); | 
|  | 1004 | if (test & (1 << 12)) { | 
|  | 1005 | /* | 
|  | 1006 | * Bit 12 of this register is read-only. | 
|  | 1007 | * This is the FC model. | 
|  | 1008 | */ | 
|  | 1009 | priv->model = ADM6996FC; | 
|  | 1010 | } else { | 
|  | 1011 | /* Bit 12 is read-write. This is the M model. */ | 
|  | 1012 | priv->model = ADM6996M; | 
|  | 1013 | w16(priv, ADM_VID_CHECK, old); | 
|  | 1014 | } | 
|  | 1015 | } | 
|  | 1016 |  | 
|  | 1017 | swdev = &priv->dev; | 
|  | 1018 | swdev->name = (adm6996_model_name[priv->model]); | 
|  | 1019 | swdev->cpu_port = ADM_CPU_PORT; | 
|  | 1020 | swdev->ports = ADM_NUM_PORTS; | 
|  | 1021 | swdev->vlans = ADM_NUM_VLANS; | 
|  | 1022 | swdev->ops = &adm6996_ops; | 
|  | 1023 | swdev->alias = alias; | 
|  | 1024 |  | 
|  | 1025 | /* The ADM6996L connected through GPIOs does not support any switch | 
|  | 1026 | status calls */ | 
|  | 1027 | if (priv->model == ADM6996L) { | 
|  | 1028 | adm6996_ops.attr_port.n_attr = 0; | 
|  | 1029 | adm6996_ops.get_port_link = NULL; | 
|  | 1030 | } | 
|  | 1031 |  | 
|  | 1032 | pr_info ("%s: %s model PHY found.\n", alias, swdev->name); | 
|  | 1033 |  | 
|  | 1034 | mutex_lock(&priv->reg_mutex); | 
|  | 1035 | adm6996_perform_reset (priv); | 
|  | 1036 | mutex_unlock(&priv->reg_mutex); | 
|  | 1037 |  | 
|  | 1038 | if (priv->model == ADM6996M || priv->model == ADM6996L) { | 
|  | 1039 | return register_switch(swdev, netdev); | 
|  | 1040 | } | 
|  | 1041 |  | 
|  | 1042 | return -ENODEV; | 
|  | 1043 | } | 
|  | 1044 |  | 
|  | 1045 | static int adm6996_config_init(struct phy_device *pdev) | 
|  | 1046 | { | 
|  | 1047 | struct adm6996_priv *priv; | 
|  | 1048 | int ret; | 
|  | 1049 |  | 
|  | 1050 | pdev->supported = ADVERTISED_100baseT_Full; | 
|  | 1051 | pdev->advertising = ADVERTISED_100baseT_Full; | 
|  | 1052 |  | 
|  | 1053 | if (pdev->mdio.addr != 0) { | 
|  | 1054 | pr_info ("%s: PHY overlaps ADM6996, providing fixed PHY 0x%x.\n" | 
|  | 1055 | , pdev->attached_dev->name, pdev->mdio.addr); | 
|  | 1056 | return 0; | 
|  | 1057 | } | 
|  | 1058 |  | 
|  | 1059 | priv = devm_kzalloc(&pdev->mdio.dev, sizeof(struct adm6996_priv), GFP_KERNEL); | 
|  | 1060 | if (!priv) | 
|  | 1061 | return -ENOMEM; | 
|  | 1062 |  | 
|  | 1063 | mutex_init(&priv->reg_mutex); | 
|  | 1064 | mutex_init(&priv->mib_lock); | 
|  | 1065 | priv->priv = pdev; | 
|  | 1066 | priv->read = adm6996_read_mii_reg; | 
|  | 1067 | priv->write = adm6996_write_mii_reg; | 
|  | 1068 |  | 
|  | 1069 | ret = adm6996_switch_init(priv, pdev->attached_dev->name, pdev->attached_dev); | 
|  | 1070 | if (ret < 0) | 
|  | 1071 | return ret; | 
|  | 1072 |  | 
|  | 1073 | pdev->priv = priv; | 
|  | 1074 |  | 
|  | 1075 | return 0; | 
|  | 1076 | } | 
|  | 1077 |  | 
|  | 1078 | /* | 
|  | 1079 | * Warning: phydev->priv is NULL if phydev->mdio.addr != 0 | 
|  | 1080 | */ | 
|  | 1081 | static int adm6996_read_status(struct phy_device *phydev) | 
|  | 1082 | { | 
|  | 1083 | phydev->speed = SPEED_100; | 
|  | 1084 | phydev->duplex = DUPLEX_FULL; | 
|  | 1085 | phydev->link = 1; | 
|  | 1086 |  | 
|  | 1087 | phydev->state = PHY_RUNNING; | 
|  | 1088 | netif_carrier_on(phydev->attached_dev); | 
|  | 1089 | phydev->adjust_link(phydev->attached_dev); | 
|  | 1090 |  | 
|  | 1091 | return 0; | 
|  | 1092 | } | 
|  | 1093 |  | 
|  | 1094 | /* | 
|  | 1095 | * Warning: phydev->priv is NULL if phydev->mdio.addr != 0 | 
|  | 1096 | */ | 
|  | 1097 | static int adm6996_config_aneg(struct phy_device *phydev) | 
|  | 1098 | { | 
|  | 1099 | return 0; | 
|  | 1100 | } | 
|  | 1101 |  | 
|  | 1102 | static int adm6996_fixup(struct phy_device *dev) | 
|  | 1103 | { | 
|  | 1104 | struct mii_bus *bus = dev->mdio.bus; | 
|  | 1105 | u16 reg; | 
|  | 1106 |  | 
|  | 1107 | /* Our custom registers are at PHY addresses 0-10. Claim those. */ | 
|  | 1108 | if (dev->mdio.addr > 10) | 
|  | 1109 | return 0; | 
|  | 1110 |  | 
|  | 1111 | /* look for the switch on the bus */ | 
|  | 1112 | reg = bus->read(bus, PHYADDR(ADM_SIG0)) & ADM_SIG0_MASK; | 
|  | 1113 | if (reg != ADM_SIG0_VAL) | 
|  | 1114 | return 0; | 
|  | 1115 |  | 
|  | 1116 | reg = bus->read(bus, PHYADDR(ADM_SIG1)) & ADM_SIG1_MASK; | 
|  | 1117 | if (reg != ADM_SIG1_VAL) | 
|  | 1118 | return 0; | 
|  | 1119 |  | 
|  | 1120 | dev->phy_id = (ADM_SIG0_VAL << 16) | ADM_SIG1_VAL; | 
|  | 1121 |  | 
|  | 1122 | return 0; | 
|  | 1123 | } | 
|  | 1124 |  | 
|  | 1125 | static int adm6996_probe(struct phy_device *pdev) | 
|  | 1126 | { | 
|  | 1127 | return 0; | 
|  | 1128 | } | 
|  | 1129 |  | 
|  | 1130 | static void adm6996_remove(struct phy_device *pdev) | 
|  | 1131 | { | 
|  | 1132 | struct adm6996_priv *priv = phy_to_adm(pdev); | 
|  | 1133 |  | 
|  | 1134 | if (priv && (priv->model == ADM6996M || priv->model == ADM6996L)) | 
|  | 1135 | unregister_switch(&priv->dev); | 
|  | 1136 | } | 
|  | 1137 |  | 
|  | 1138 | static int adm6996_soft_reset(struct phy_device *phydev) | 
|  | 1139 | { | 
|  | 1140 | /* we don't need an extra reset */ | 
|  | 1141 | return 0; | 
|  | 1142 | } | 
|  | 1143 |  | 
|  | 1144 | static struct phy_driver adm6996_phy_driver = { | 
|  | 1145 | .name		= "Infineon ADM6996", | 
|  | 1146 | .phy_id		= (ADM_SIG0_VAL << 16) | ADM_SIG1_VAL, | 
|  | 1147 | .phy_id_mask	= 0xffffffff, | 
|  | 1148 | .features	= PHY_BASIC_FEATURES, | 
|  | 1149 | .probe		= adm6996_probe, | 
|  | 1150 | .remove		= adm6996_remove, | 
|  | 1151 | .config_init	= &adm6996_config_init, | 
|  | 1152 | .config_aneg	= &adm6996_config_aneg, | 
|  | 1153 | .read_status	= &adm6996_read_status, | 
|  | 1154 | .soft_reset	= adm6996_soft_reset, | 
|  | 1155 | }; | 
|  | 1156 |  | 
|  | 1157 | static int adm6996_gpio_probe(struct platform_device *pdev) | 
|  | 1158 | { | 
|  | 1159 | struct adm6996_gpio_platform_data *pdata = pdev->dev.platform_data; | 
|  | 1160 | struct adm6996_priv *priv; | 
|  | 1161 | int ret; | 
|  | 1162 |  | 
|  | 1163 | if (!pdata) | 
|  | 1164 | return -EINVAL; | 
|  | 1165 |  | 
|  | 1166 | priv = devm_kzalloc(&pdev->dev, sizeof(struct adm6996_priv), GFP_KERNEL); | 
|  | 1167 | if (!priv) | 
|  | 1168 | return -ENOMEM; | 
|  | 1169 |  | 
|  | 1170 | mutex_init(&priv->reg_mutex); | 
|  | 1171 | mutex_init(&priv->mib_lock); | 
|  | 1172 |  | 
|  | 1173 | priv->eecs = pdata->eecs; | 
|  | 1174 | priv->eedi = pdata->eedi; | 
|  | 1175 | priv->eesk = pdata->eesk; | 
|  | 1176 |  | 
|  | 1177 | priv->model = pdata->model; | 
|  | 1178 | priv->read = adm6996_read_gpio_reg; | 
|  | 1179 | priv->write = adm6996_write_gpio_reg; | 
|  | 1180 |  | 
|  | 1181 | ret = devm_gpio_request(&pdev->dev, priv->eecs, "adm_eecs"); | 
|  | 1182 | if (ret) | 
|  | 1183 | return ret; | 
|  | 1184 | ret = devm_gpio_request(&pdev->dev, priv->eedi, "adm_eedi"); | 
|  | 1185 | if (ret) | 
|  | 1186 | return ret; | 
|  | 1187 | ret = devm_gpio_request(&pdev->dev, priv->eesk, "adm_eesk"); | 
|  | 1188 | if (ret) | 
|  | 1189 | return ret; | 
|  | 1190 |  | 
|  | 1191 | ret = adm6996_switch_init(priv, dev_name(&pdev->dev), NULL); | 
|  | 1192 | if (ret < 0) | 
|  | 1193 | return ret; | 
|  | 1194 |  | 
|  | 1195 | platform_set_drvdata(pdev, priv); | 
|  | 1196 |  | 
|  | 1197 | return 0; | 
|  | 1198 | } | 
|  | 1199 |  | 
|  | 1200 | static int adm6996_gpio_remove(struct platform_device *pdev) | 
|  | 1201 | { | 
|  | 1202 | struct adm6996_priv *priv = platform_get_drvdata(pdev); | 
|  | 1203 |  | 
|  | 1204 | if (priv && (priv->model == ADM6996M || priv->model == ADM6996L)) | 
|  | 1205 | unregister_switch(&priv->dev); | 
|  | 1206 |  | 
|  | 1207 | return 0; | 
|  | 1208 | } | 
|  | 1209 |  | 
|  | 1210 | static struct platform_driver adm6996_gpio_driver = { | 
|  | 1211 | .probe = adm6996_gpio_probe, | 
|  | 1212 | .remove = adm6996_gpio_remove, | 
|  | 1213 | .driver = { | 
|  | 1214 | .name = "adm6996_gpio", | 
|  | 1215 | }, | 
|  | 1216 | }; | 
|  | 1217 |  | 
|  | 1218 | static int __init adm6996_init(void) | 
|  | 1219 | { | 
|  | 1220 | int err; | 
|  | 1221 |  | 
|  | 1222 | phy_register_fixup_for_id(PHY_ANY_ID, adm6996_fixup); | 
|  | 1223 | err = phy_driver_register(&adm6996_phy_driver, THIS_MODULE); | 
|  | 1224 | if (err) | 
|  | 1225 | return err; | 
|  | 1226 |  | 
|  | 1227 | err = platform_driver_register(&adm6996_gpio_driver); | 
|  | 1228 | if (err) | 
|  | 1229 | phy_driver_unregister(&adm6996_phy_driver); | 
|  | 1230 |  | 
|  | 1231 | return err; | 
|  | 1232 | } | 
|  | 1233 |  | 
|  | 1234 | static void __exit adm6996_exit(void) | 
|  | 1235 | { | 
|  | 1236 | platform_driver_unregister(&adm6996_gpio_driver); | 
|  | 1237 | phy_driver_unregister(&adm6996_phy_driver); | 
|  | 1238 | } | 
|  | 1239 |  | 
|  | 1240 | module_init(adm6996_init); | 
|  | 1241 | module_exit(adm6996_exit); |