Linus Walleij | d865295 | 2018-07-14 11:45:55 +0200 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* Realtek Simple Management Interface (SMI) driver |
| 3 | * It can be discussed how "simple" this interface is. |
| 4 | * |
| 5 | * The SMI protocol piggy-backs the MDIO MDC and MDIO signals levels |
| 6 | * but the protocol is not MDIO at all. Instead it is a Realtek |
| 7 | * pecularity that need to bit-bang the lines in a special way to |
| 8 | * communicate with the switch. |
| 9 | * |
| 10 | * ASICs we intend to support with this driver: |
| 11 | * |
| 12 | * RTL8366 - The original version, apparently |
| 13 | * RTL8369 - Similar enough to have the same datsheet as RTL8366 |
| 14 | * RTL8366RB - Probably reads out "RTL8366 revision B", has a quite |
| 15 | * different register layout from the other two |
| 16 | * RTL8366S - Is this "RTL8366 super"? |
| 17 | * RTL8367 - Has an OpenWRT driver as well |
| 18 | * RTL8368S - Seems to be an alternative name for RTL8366RB |
| 19 | * RTL8370 - Also uses SMI |
| 20 | * |
| 21 | * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> |
| 22 | * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com> |
| 23 | * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv> |
| 24 | * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com> |
| 25 | * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org> |
| 26 | */ |
| 27 | |
| 28 | #include <linux/kernel.h> |
| 29 | #include <linux/module.h> |
| 30 | #include <linux/device.h> |
| 31 | #include <linux/spinlock.h> |
| 32 | #include <linux/skbuff.h> |
| 33 | #include <linux/of.h> |
| 34 | #include <linux/of_device.h> |
| 35 | #include <linux/of_mdio.h> |
| 36 | #include <linux/delay.h> |
| 37 | #include <linux/gpio/consumer.h> |
| 38 | #include <linux/platform_device.h> |
| 39 | #include <linux/regmap.h> |
| 40 | #include <linux/bitops.h> |
| 41 | #include <linux/if_bridge.h> |
| 42 | |
| 43 | #include "realtek-smi.h" |
| 44 | |
| 45 | #define REALTEK_SMI_ACK_RETRY_COUNT 5 |
| 46 | #define REALTEK_SMI_HW_STOP_DELAY 25 /* msecs */ |
| 47 | #define REALTEK_SMI_HW_START_DELAY 100 /* msecs */ |
| 48 | |
| 49 | static inline void realtek_smi_clk_delay(struct realtek_smi *smi) |
| 50 | { |
| 51 | ndelay(smi->clk_delay); |
| 52 | } |
| 53 | |
| 54 | static void realtek_smi_start(struct realtek_smi *smi) |
| 55 | { |
| 56 | /* Set GPIO pins to output mode, with initial state: |
| 57 | * SCK = 0, SDA = 1 |
| 58 | */ |
| 59 | gpiod_direction_output(smi->mdc, 0); |
| 60 | gpiod_direction_output(smi->mdio, 1); |
| 61 | realtek_smi_clk_delay(smi); |
| 62 | |
| 63 | /* CLK 1: 0 -> 1, 1 -> 0 */ |
| 64 | gpiod_set_value(smi->mdc, 1); |
| 65 | realtek_smi_clk_delay(smi); |
| 66 | gpiod_set_value(smi->mdc, 0); |
| 67 | realtek_smi_clk_delay(smi); |
| 68 | |
| 69 | /* CLK 2: */ |
| 70 | gpiod_set_value(smi->mdc, 1); |
| 71 | realtek_smi_clk_delay(smi); |
| 72 | gpiod_set_value(smi->mdio, 0); |
| 73 | realtek_smi_clk_delay(smi); |
| 74 | gpiod_set_value(smi->mdc, 0); |
| 75 | realtek_smi_clk_delay(smi); |
| 76 | gpiod_set_value(smi->mdio, 1); |
| 77 | } |
| 78 | |
| 79 | static void realtek_smi_stop(struct realtek_smi *smi) |
| 80 | { |
| 81 | realtek_smi_clk_delay(smi); |
| 82 | gpiod_set_value(smi->mdio, 0); |
| 83 | gpiod_set_value(smi->mdc, 1); |
| 84 | realtek_smi_clk_delay(smi); |
| 85 | gpiod_set_value(smi->mdio, 1); |
| 86 | realtek_smi_clk_delay(smi); |
| 87 | gpiod_set_value(smi->mdc, 1); |
| 88 | realtek_smi_clk_delay(smi); |
| 89 | gpiod_set_value(smi->mdc, 0); |
| 90 | realtek_smi_clk_delay(smi); |
| 91 | gpiod_set_value(smi->mdc, 1); |
| 92 | |
| 93 | /* Add a click */ |
| 94 | realtek_smi_clk_delay(smi); |
| 95 | gpiod_set_value(smi->mdc, 0); |
| 96 | realtek_smi_clk_delay(smi); |
| 97 | gpiod_set_value(smi->mdc, 1); |
| 98 | |
| 99 | /* Set GPIO pins to input mode */ |
| 100 | gpiod_direction_input(smi->mdio); |
| 101 | gpiod_direction_input(smi->mdc); |
| 102 | } |
| 103 | |
| 104 | static void realtek_smi_write_bits(struct realtek_smi *smi, u32 data, u32 len) |
| 105 | { |
| 106 | for (; len > 0; len--) { |
| 107 | realtek_smi_clk_delay(smi); |
| 108 | |
| 109 | /* Prepare data */ |
| 110 | gpiod_set_value(smi->mdio, !!(data & (1 << (len - 1)))); |
| 111 | realtek_smi_clk_delay(smi); |
| 112 | |
| 113 | /* Clocking */ |
| 114 | gpiod_set_value(smi->mdc, 1); |
| 115 | realtek_smi_clk_delay(smi); |
| 116 | gpiod_set_value(smi->mdc, 0); |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | static void realtek_smi_read_bits(struct realtek_smi *smi, u32 len, u32 *data) |
| 121 | { |
| 122 | gpiod_direction_input(smi->mdio); |
| 123 | |
| 124 | for (*data = 0; len > 0; len--) { |
| 125 | u32 u; |
| 126 | |
| 127 | realtek_smi_clk_delay(smi); |
| 128 | |
| 129 | /* Clocking */ |
| 130 | gpiod_set_value(smi->mdc, 1); |
| 131 | realtek_smi_clk_delay(smi); |
| 132 | u = !!gpiod_get_value(smi->mdio); |
| 133 | gpiod_set_value(smi->mdc, 0); |
| 134 | |
| 135 | *data |= (u << (len - 1)); |
| 136 | } |
| 137 | |
| 138 | gpiod_direction_output(smi->mdio, 0); |
| 139 | } |
| 140 | |
| 141 | static int realtek_smi_wait_for_ack(struct realtek_smi *smi) |
| 142 | { |
| 143 | int retry_cnt; |
| 144 | |
| 145 | retry_cnt = 0; |
| 146 | do { |
| 147 | u32 ack; |
| 148 | |
| 149 | realtek_smi_read_bits(smi, 1, &ack); |
| 150 | if (ack == 0) |
| 151 | break; |
| 152 | |
| 153 | if (++retry_cnt > REALTEK_SMI_ACK_RETRY_COUNT) { |
| 154 | dev_err(smi->dev, "ACK timeout\n"); |
| 155 | return -ETIMEDOUT; |
| 156 | } |
| 157 | } while (1); |
| 158 | |
| 159 | return 0; |
| 160 | } |
| 161 | |
| 162 | static int realtek_smi_write_byte(struct realtek_smi *smi, u8 data) |
| 163 | { |
| 164 | realtek_smi_write_bits(smi, data, 8); |
| 165 | return realtek_smi_wait_for_ack(smi); |
| 166 | } |
| 167 | |
| 168 | static int realtek_smi_write_byte_noack(struct realtek_smi *smi, u8 data) |
| 169 | { |
| 170 | realtek_smi_write_bits(smi, data, 8); |
| 171 | return 0; |
| 172 | } |
| 173 | |
| 174 | static int realtek_smi_read_byte0(struct realtek_smi *smi, u8 *data) |
| 175 | { |
| 176 | u32 t; |
| 177 | |
| 178 | /* Read data */ |
| 179 | realtek_smi_read_bits(smi, 8, &t); |
| 180 | *data = (t & 0xff); |
| 181 | |
| 182 | /* Send an ACK */ |
| 183 | realtek_smi_write_bits(smi, 0x00, 1); |
| 184 | |
| 185 | return 0; |
| 186 | } |
| 187 | |
| 188 | static int realtek_smi_read_byte1(struct realtek_smi *smi, u8 *data) |
| 189 | { |
| 190 | u32 t; |
| 191 | |
| 192 | /* Read data */ |
| 193 | realtek_smi_read_bits(smi, 8, &t); |
| 194 | *data = (t & 0xff); |
| 195 | |
| 196 | /* Send an ACK */ |
| 197 | realtek_smi_write_bits(smi, 0x01, 1); |
| 198 | |
| 199 | return 0; |
| 200 | } |
| 201 | |
| 202 | static int realtek_smi_read_reg(struct realtek_smi *smi, u32 addr, u32 *data) |
| 203 | { |
| 204 | unsigned long flags; |
| 205 | u8 lo = 0; |
| 206 | u8 hi = 0; |
| 207 | int ret; |
| 208 | |
| 209 | spin_lock_irqsave(&smi->lock, flags); |
| 210 | |
| 211 | realtek_smi_start(smi); |
| 212 | |
| 213 | /* Send READ command */ |
| 214 | ret = realtek_smi_write_byte(smi, smi->cmd_read); |
| 215 | if (ret) |
| 216 | goto out; |
| 217 | |
| 218 | /* Set ADDR[7:0] */ |
| 219 | ret = realtek_smi_write_byte(smi, addr & 0xff); |
| 220 | if (ret) |
| 221 | goto out; |
| 222 | |
| 223 | /* Set ADDR[15:8] */ |
| 224 | ret = realtek_smi_write_byte(smi, addr >> 8); |
| 225 | if (ret) |
| 226 | goto out; |
| 227 | |
| 228 | /* Read DATA[7:0] */ |
| 229 | realtek_smi_read_byte0(smi, &lo); |
| 230 | /* Read DATA[15:8] */ |
| 231 | realtek_smi_read_byte1(smi, &hi); |
| 232 | |
| 233 | *data = ((u32)lo) | (((u32)hi) << 8); |
| 234 | |
| 235 | ret = 0; |
| 236 | |
| 237 | out: |
| 238 | realtek_smi_stop(smi); |
| 239 | spin_unlock_irqrestore(&smi->lock, flags); |
| 240 | |
| 241 | return ret; |
| 242 | } |
| 243 | |
| 244 | static int realtek_smi_write_reg(struct realtek_smi *smi, |
| 245 | u32 addr, u32 data, bool ack) |
| 246 | { |
| 247 | unsigned long flags; |
| 248 | int ret; |
| 249 | |
| 250 | spin_lock_irqsave(&smi->lock, flags); |
| 251 | |
| 252 | realtek_smi_start(smi); |
| 253 | |
| 254 | /* Send WRITE command */ |
| 255 | ret = realtek_smi_write_byte(smi, smi->cmd_write); |
| 256 | if (ret) |
| 257 | goto out; |
| 258 | |
| 259 | /* Set ADDR[7:0] */ |
| 260 | ret = realtek_smi_write_byte(smi, addr & 0xff); |
| 261 | if (ret) |
| 262 | goto out; |
| 263 | |
| 264 | /* Set ADDR[15:8] */ |
| 265 | ret = realtek_smi_write_byte(smi, addr >> 8); |
| 266 | if (ret) |
| 267 | goto out; |
| 268 | |
| 269 | /* Write DATA[7:0] */ |
| 270 | ret = realtek_smi_write_byte(smi, data & 0xff); |
| 271 | if (ret) |
| 272 | goto out; |
| 273 | |
| 274 | /* Write DATA[15:8] */ |
| 275 | if (ack) |
| 276 | ret = realtek_smi_write_byte(smi, data >> 8); |
| 277 | else |
| 278 | ret = realtek_smi_write_byte_noack(smi, data >> 8); |
| 279 | if (ret) |
| 280 | goto out; |
| 281 | |
| 282 | ret = 0; |
| 283 | |
| 284 | out: |
| 285 | realtek_smi_stop(smi); |
| 286 | spin_unlock_irqrestore(&smi->lock, flags); |
| 287 | |
| 288 | return ret; |
| 289 | } |
| 290 | |
| 291 | /* There is one single case when we need to use this accessor and that |
| 292 | * is when issueing soft reset. Since the device reset as soon as we write |
| 293 | * that bit, no ACK will come back for natural reasons. |
| 294 | */ |
| 295 | int realtek_smi_write_reg_noack(struct realtek_smi *smi, u32 addr, |
| 296 | u32 data) |
| 297 | { |
| 298 | return realtek_smi_write_reg(smi, addr, data, false); |
| 299 | } |
| 300 | EXPORT_SYMBOL_GPL(realtek_smi_write_reg_noack); |
| 301 | |
| 302 | /* Regmap accessors */ |
| 303 | |
| 304 | static int realtek_smi_write(void *ctx, u32 reg, u32 val) |
| 305 | { |
| 306 | struct realtek_smi *smi = ctx; |
| 307 | |
| 308 | return realtek_smi_write_reg(smi, reg, val, true); |
| 309 | } |
| 310 | |
| 311 | static int realtek_smi_read(void *ctx, u32 reg, u32 *val) |
| 312 | { |
| 313 | struct realtek_smi *smi = ctx; |
| 314 | |
| 315 | return realtek_smi_read_reg(smi, reg, val); |
| 316 | } |
| 317 | |
| 318 | static const struct regmap_config realtek_smi_mdio_regmap_config = { |
| 319 | .reg_bits = 10, /* A4..A0 R4..R0 */ |
| 320 | .val_bits = 16, |
| 321 | .reg_stride = 1, |
| 322 | /* PHY regs are at 0x8000 */ |
| 323 | .max_register = 0xffff, |
| 324 | .reg_format_endian = REGMAP_ENDIAN_BIG, |
| 325 | .reg_read = realtek_smi_read, |
| 326 | .reg_write = realtek_smi_write, |
| 327 | .cache_type = REGCACHE_NONE, |
| 328 | }; |
| 329 | |
| 330 | static int realtek_smi_mdio_read(struct mii_bus *bus, int addr, int regnum) |
| 331 | { |
| 332 | struct realtek_smi *smi = bus->priv; |
| 333 | |
| 334 | return smi->ops->phy_read(smi, addr, regnum); |
| 335 | } |
| 336 | |
| 337 | static int realtek_smi_mdio_write(struct mii_bus *bus, int addr, int regnum, |
| 338 | u16 val) |
| 339 | { |
| 340 | struct realtek_smi *smi = bus->priv; |
| 341 | |
| 342 | return smi->ops->phy_write(smi, addr, regnum, val); |
| 343 | } |
| 344 | |
| 345 | int realtek_smi_setup_mdio(struct realtek_smi *smi) |
| 346 | { |
| 347 | struct device_node *mdio_np; |
| 348 | int ret; |
| 349 | |
| 350 | mdio_np = of_find_compatible_node(smi->dev->of_node, NULL, |
| 351 | "realtek,smi-mdio"); |
| 352 | if (!mdio_np) { |
| 353 | dev_err(smi->dev, "no MDIO bus node\n"); |
| 354 | return -ENODEV; |
| 355 | } |
| 356 | |
| 357 | smi->slave_mii_bus = devm_mdiobus_alloc(smi->dev); |
| 358 | if (!smi->slave_mii_bus) |
| 359 | return -ENOMEM; |
| 360 | smi->slave_mii_bus->priv = smi; |
| 361 | smi->slave_mii_bus->name = "SMI slave MII"; |
| 362 | smi->slave_mii_bus->read = realtek_smi_mdio_read; |
| 363 | smi->slave_mii_bus->write = realtek_smi_mdio_write; |
| 364 | snprintf(smi->slave_mii_bus->id, MII_BUS_ID_SIZE, "SMI-%d", |
| 365 | smi->ds->index); |
| 366 | smi->slave_mii_bus->dev.of_node = mdio_np; |
| 367 | smi->slave_mii_bus->parent = smi->dev; |
| 368 | smi->ds->slave_mii_bus = smi->slave_mii_bus; |
| 369 | |
| 370 | ret = of_mdiobus_register(smi->slave_mii_bus, mdio_np); |
| 371 | if (ret) { |
| 372 | dev_err(smi->dev, "unable to register MDIO bus %s\n", |
| 373 | smi->slave_mii_bus->id); |
| 374 | of_node_put(mdio_np); |
| 375 | } |
| 376 | |
| 377 | return 0; |
| 378 | } |
| 379 | |
| 380 | static int realtek_smi_probe(struct platform_device *pdev) |
| 381 | { |
| 382 | const struct realtek_smi_variant *var; |
| 383 | struct device *dev = &pdev->dev; |
| 384 | struct realtek_smi *smi; |
| 385 | struct device_node *np; |
| 386 | int ret; |
| 387 | |
| 388 | var = of_device_get_match_data(dev); |
| 389 | np = dev->of_node; |
| 390 | |
| 391 | smi = devm_kzalloc(dev, sizeof(*smi), GFP_KERNEL); |
| 392 | if (!smi) |
| 393 | return -ENOMEM; |
| 394 | smi->map = devm_regmap_init(dev, NULL, smi, |
| 395 | &realtek_smi_mdio_regmap_config); |
| 396 | if (IS_ERR(smi->map)) { |
| 397 | ret = PTR_ERR(smi->map); |
| 398 | dev_err(dev, "regmap init failed: %d\n", ret); |
| 399 | return ret; |
| 400 | } |
| 401 | |
| 402 | /* Link forward and backward */ |
| 403 | smi->dev = dev; |
| 404 | smi->clk_delay = var->clk_delay; |
| 405 | smi->cmd_read = var->cmd_read; |
| 406 | smi->cmd_write = var->cmd_write; |
| 407 | smi->ops = var->ops; |
| 408 | |
| 409 | dev_set_drvdata(dev, smi); |
| 410 | spin_lock_init(&smi->lock); |
| 411 | |
| 412 | /* TODO: if power is software controlled, set up any regulators here */ |
| 413 | |
| 414 | /* Assert then deassert RESET */ |
| 415 | smi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); |
| 416 | if (IS_ERR(smi->reset)) { |
| 417 | dev_err(dev, "failed to get RESET GPIO\n"); |
| 418 | return PTR_ERR(smi->reset); |
| 419 | } |
| 420 | msleep(REALTEK_SMI_HW_STOP_DELAY); |
| 421 | gpiod_set_value(smi->reset, 0); |
| 422 | msleep(REALTEK_SMI_HW_START_DELAY); |
| 423 | dev_info(dev, "deasserted RESET\n"); |
| 424 | |
| 425 | /* Fetch MDIO pins */ |
| 426 | smi->mdc = devm_gpiod_get_optional(dev, "mdc", GPIOD_OUT_LOW); |
| 427 | if (IS_ERR(smi->mdc)) |
| 428 | return PTR_ERR(smi->mdc); |
| 429 | smi->mdio = devm_gpiod_get_optional(dev, "mdio", GPIOD_OUT_LOW); |
| 430 | if (IS_ERR(smi->mdio)) |
| 431 | return PTR_ERR(smi->mdio); |
| 432 | |
| 433 | smi->leds_disabled = of_property_read_bool(np, "realtek,disable-leds"); |
| 434 | |
| 435 | ret = smi->ops->detect(smi); |
| 436 | if (ret) { |
| 437 | dev_err(dev, "unable to detect switch\n"); |
| 438 | return ret; |
| 439 | } |
| 440 | |
| 441 | smi->ds = dsa_switch_alloc(dev, smi->num_ports); |
| 442 | if (!smi->ds) |
| 443 | return -ENOMEM; |
| 444 | smi->ds->priv = smi; |
| 445 | |
| 446 | smi->ds->ops = var->ds_ops; |
| 447 | ret = dsa_register_switch(smi->ds); |
| 448 | if (ret) { |
| 449 | dev_err(dev, "unable to register switch ret = %d\n", ret); |
| 450 | return ret; |
| 451 | } |
| 452 | return 0; |
| 453 | } |
| 454 | |
| 455 | static int realtek_smi_remove(struct platform_device *pdev) |
| 456 | { |
| 457 | struct realtek_smi *smi = dev_get_drvdata(&pdev->dev); |
| 458 | |
| 459 | dsa_unregister_switch(smi->ds); |
| 460 | gpiod_set_value(smi->reset, 1); |
| 461 | |
| 462 | return 0; |
| 463 | } |
| 464 | |
| 465 | static const struct of_device_id realtek_smi_of_match[] = { |
| 466 | { |
| 467 | .compatible = "realtek,rtl8366rb", |
| 468 | .data = &rtl8366rb_variant, |
| 469 | }, |
| 470 | { |
| 471 | /* FIXME: add support for RTL8366S and more */ |
| 472 | .compatible = "realtek,rtl8366s", |
| 473 | .data = NULL, |
| 474 | }, |
| 475 | { /* sentinel */ }, |
| 476 | }; |
| 477 | MODULE_DEVICE_TABLE(of, realtek_smi_of_match); |
| 478 | |
| 479 | static struct platform_driver realtek_smi_driver = { |
| 480 | .driver = { |
| 481 | .name = "realtek-smi", |
| 482 | .of_match_table = of_match_ptr(realtek_smi_of_match), |
| 483 | }, |
| 484 | .probe = realtek_smi_probe, |
| 485 | .remove = realtek_smi_remove, |
| 486 | }; |
| 487 | module_platform_driver(realtek_smi_driver); |
Randy Dunlap | be5a8ff | 2018-07-20 09:16:02 -0700 | [diff] [blame] | 488 | |
| 489 | MODULE_LICENSE("GPL"); |