697 lines · c
1/*2 * B53 register access through Switch Register Access Bridge Registers3 *4 * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>5 *6 * Permission to use, copy, modify, and/or distribute this software for any7 * purpose with or without fee is hereby granted, provided that the above8 * copyright notice and this permission notice appear in all copies.9 *10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.17 */18 19#include <linux/kernel.h>20#include <linux/module.h>21#include <linux/delay.h>22#include <linux/interrupt.h>23#include <linux/platform_device.h>24#include <linux/platform_data/b53.h>25#include <linux/of.h>26 27#include "b53_priv.h"28#include "b53_serdes.h"29 30/* command and status register of the SRAB */31#define B53_SRAB_CMDSTAT 0x2c32#define B53_SRAB_CMDSTAT_RST BIT(2)33#define B53_SRAB_CMDSTAT_WRITE BIT(1)34#define B53_SRAB_CMDSTAT_GORDYN BIT(0)35#define B53_SRAB_CMDSTAT_PAGE 2436#define B53_SRAB_CMDSTAT_REG 1637 38/* high order word of write data to switch registe */39#define B53_SRAB_WD_H 0x3040 41/* low order word of write data to switch registe */42#define B53_SRAB_WD_L 0x3443 44/* high order word of read data from switch register */45#define B53_SRAB_RD_H 0x3846 47/* low order word of read data from switch register */48#define B53_SRAB_RD_L 0x3c49 50/* command and status register of the SRAB */51#define B53_SRAB_CTRLS 0x4052#define B53_SRAB_CTRLS_HOST_INTR BIT(1)53#define B53_SRAB_CTRLS_RCAREQ BIT(3)54#define B53_SRAB_CTRLS_RCAGNT BIT(4)55#define B53_SRAB_CTRLS_SW_INIT_DONE BIT(6)56 57/* the register captures interrupt pulses from the switch */58#define B53_SRAB_INTR 0x4459#define B53_SRAB_INTR_P(x) BIT(x)60#define B53_SRAB_SWITCH_PHY BIT(8)61#define B53_SRAB_1588_SYNC BIT(9)62#define B53_SRAB_IMP1_SLEEP_TIMER BIT(10)63#define B53_SRAB_P7_SLEEP_TIMER BIT(11)64#define B53_SRAB_IMP0_SLEEP_TIMER BIT(12)65 66/* Port mux configuration registers */67#define B53_MUX_CONFIG_P5 0x0068#define MUX_CONFIG_SGMII 069#define MUX_CONFIG_MII_LITE 170#define MUX_CONFIG_RGMII 271#define MUX_CONFIG_GMII 372#define MUX_CONFIG_GPHY 473#define MUX_CONFIG_INTERNAL 574#define MUX_CONFIG_MASK 0x775#define B53_MUX_CONFIG_P4 0x0476 77struct b53_srab_port_priv {78 int irq;79 bool irq_enabled;80 struct b53_device *dev;81 unsigned int num;82 phy_interface_t mode;83};84 85struct b53_srab_priv {86 void __iomem *regs;87 void __iomem *mux_config;88 struct b53_srab_port_priv port_intrs[B53_N_PORTS];89};90 91static int b53_srab_request_grant(struct b53_device *dev)92{93 struct b53_srab_priv *priv = dev->priv;94 u8 __iomem *regs = priv->regs;95 u32 ctrls;96 int i;97 98 ctrls = readl(regs + B53_SRAB_CTRLS);99 ctrls |= B53_SRAB_CTRLS_RCAREQ;100 writel(ctrls, regs + B53_SRAB_CTRLS);101 102 for (i = 0; i < 20; i++) {103 ctrls = readl(regs + B53_SRAB_CTRLS);104 if (ctrls & B53_SRAB_CTRLS_RCAGNT)105 break;106 usleep_range(10, 100);107 }108 if (WARN_ON(i == 5))109 return -EIO;110 111 return 0;112}113 114static void b53_srab_release_grant(struct b53_device *dev)115{116 struct b53_srab_priv *priv = dev->priv;117 u8 __iomem *regs = priv->regs;118 u32 ctrls;119 120 ctrls = readl(regs + B53_SRAB_CTRLS);121 ctrls &= ~B53_SRAB_CTRLS_RCAREQ;122 writel(ctrls, regs + B53_SRAB_CTRLS);123}124 125static int b53_srab_op(struct b53_device *dev, u8 page, u8 reg, u32 op)126{127 struct b53_srab_priv *priv = dev->priv;128 u8 __iomem *regs = priv->regs;129 int i;130 u32 cmdstat;131 132 /* set register address */133 cmdstat = (page << B53_SRAB_CMDSTAT_PAGE) |134 (reg << B53_SRAB_CMDSTAT_REG) |135 B53_SRAB_CMDSTAT_GORDYN |136 op;137 writel(cmdstat, regs + B53_SRAB_CMDSTAT);138 139 /* check if operation completed */140 for (i = 0; i < 5; ++i) {141 cmdstat = readl(regs + B53_SRAB_CMDSTAT);142 if (!(cmdstat & B53_SRAB_CMDSTAT_GORDYN))143 break;144 usleep_range(10, 100);145 }146 147 if (WARN_ON(i == 5))148 return -EIO;149 150 return 0;151}152 153static int b53_srab_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)154{155 struct b53_srab_priv *priv = dev->priv;156 u8 __iomem *regs = priv->regs;157 int ret = 0;158 159 ret = b53_srab_request_grant(dev);160 if (ret)161 goto err;162 163 ret = b53_srab_op(dev, page, reg, 0);164 if (ret)165 goto err;166 167 *val = readl(regs + B53_SRAB_RD_L) & 0xff;168 169err:170 b53_srab_release_grant(dev);171 172 return ret;173}174 175static int b53_srab_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)176{177 struct b53_srab_priv *priv = dev->priv;178 u8 __iomem *regs = priv->regs;179 int ret = 0;180 181 ret = b53_srab_request_grant(dev);182 if (ret)183 goto err;184 185 ret = b53_srab_op(dev, page, reg, 0);186 if (ret)187 goto err;188 189 *val = readl(regs + B53_SRAB_RD_L) & 0xffff;190 191err:192 b53_srab_release_grant(dev);193 194 return ret;195}196 197static int b53_srab_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)198{199 struct b53_srab_priv *priv = dev->priv;200 u8 __iomem *regs = priv->regs;201 int ret = 0;202 203 ret = b53_srab_request_grant(dev);204 if (ret)205 goto err;206 207 ret = b53_srab_op(dev, page, reg, 0);208 if (ret)209 goto err;210 211 *val = readl(regs + B53_SRAB_RD_L);212 213err:214 b53_srab_release_grant(dev);215 216 return ret;217}218 219static int b53_srab_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)220{221 struct b53_srab_priv *priv = dev->priv;222 u8 __iomem *regs = priv->regs;223 int ret = 0;224 225 ret = b53_srab_request_grant(dev);226 if (ret)227 goto err;228 229 ret = b53_srab_op(dev, page, reg, 0);230 if (ret)231 goto err;232 233 *val = readl(regs + B53_SRAB_RD_L);234 *val += ((u64)readl(regs + B53_SRAB_RD_H) & 0xffff) << 32;235 236err:237 b53_srab_release_grant(dev);238 239 return ret;240}241 242static int b53_srab_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)243{244 struct b53_srab_priv *priv = dev->priv;245 u8 __iomem *regs = priv->regs;246 int ret = 0;247 248 ret = b53_srab_request_grant(dev);249 if (ret)250 goto err;251 252 ret = b53_srab_op(dev, page, reg, 0);253 if (ret)254 goto err;255 256 *val = readl(regs + B53_SRAB_RD_L);257 *val += (u64)readl(regs + B53_SRAB_RD_H) << 32;258 259err:260 b53_srab_release_grant(dev);261 262 return ret;263}264 265static int b53_srab_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)266{267 struct b53_srab_priv *priv = dev->priv;268 u8 __iomem *regs = priv->regs;269 int ret = 0;270 271 ret = b53_srab_request_grant(dev);272 if (ret)273 goto err;274 275 writel(value, regs + B53_SRAB_WD_L);276 277 ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);278 279err:280 b53_srab_release_grant(dev);281 282 return ret;283}284 285static int b53_srab_write16(struct b53_device *dev, u8 page, u8 reg,286 u16 value)287{288 struct b53_srab_priv *priv = dev->priv;289 u8 __iomem *regs = priv->regs;290 int ret = 0;291 292 ret = b53_srab_request_grant(dev);293 if (ret)294 goto err;295 296 writel(value, regs + B53_SRAB_WD_L);297 298 ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);299 300err:301 b53_srab_release_grant(dev);302 303 return ret;304}305 306static int b53_srab_write32(struct b53_device *dev, u8 page, u8 reg,307 u32 value)308{309 struct b53_srab_priv *priv = dev->priv;310 u8 __iomem *regs = priv->regs;311 int ret = 0;312 313 ret = b53_srab_request_grant(dev);314 if (ret)315 goto err;316 317 writel(value, regs + B53_SRAB_WD_L);318 319 ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);320 321err:322 b53_srab_release_grant(dev);323 324 return ret;325}326 327static int b53_srab_write48(struct b53_device *dev, u8 page, u8 reg,328 u64 value)329{330 struct b53_srab_priv *priv = dev->priv;331 u8 __iomem *regs = priv->regs;332 int ret = 0;333 334 ret = b53_srab_request_grant(dev);335 if (ret)336 goto err;337 338 writel((u32)value, regs + B53_SRAB_WD_L);339 writel((u16)(value >> 32), regs + B53_SRAB_WD_H);340 341 ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);342 343err:344 b53_srab_release_grant(dev);345 346 return ret;347}348 349static int b53_srab_write64(struct b53_device *dev, u8 page, u8 reg,350 u64 value)351{352 struct b53_srab_priv *priv = dev->priv;353 u8 __iomem *regs = priv->regs;354 int ret = 0;355 356 ret = b53_srab_request_grant(dev);357 if (ret)358 goto err;359 360 writel((u32)value, regs + B53_SRAB_WD_L);361 writel((u32)(value >> 32), regs + B53_SRAB_WD_H);362 363 ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);364 365err:366 b53_srab_release_grant(dev);367 368 return ret;369}370 371static irqreturn_t b53_srab_port_thread(int irq, void *dev_id)372{373 struct b53_srab_port_priv *port = dev_id;374 struct b53_device *dev = port->dev;375 376 if (port->mode == PHY_INTERFACE_MODE_SGMII)377 b53_port_event(dev->ds, port->num);378 379 return IRQ_HANDLED;380}381 382static irqreturn_t b53_srab_port_isr(int irq, void *dev_id)383{384 struct b53_srab_port_priv *port = dev_id;385 struct b53_device *dev = port->dev;386 struct b53_srab_priv *priv = dev->priv;387 388 /* Acknowledge the interrupt */389 writel(BIT(port->num), priv->regs + B53_SRAB_INTR);390 391 return IRQ_WAKE_THREAD;392}393 394#if IS_ENABLED(CONFIG_B53_SERDES)395static u8 b53_srab_serdes_map_lane(struct b53_device *dev, int port)396{397 struct b53_srab_priv *priv = dev->priv;398 struct b53_srab_port_priv *p = &priv->port_intrs[port];399 400 if (p->mode != PHY_INTERFACE_MODE_SGMII)401 return B53_INVALID_LANE;402 403 switch (port) {404 case 5:405 return 0;406 case 4:407 return 1;408 default:409 return B53_INVALID_LANE;410 }411}412#endif413 414static int b53_srab_irq_enable(struct b53_device *dev, int port)415{416 struct b53_srab_priv *priv = dev->priv;417 struct b53_srab_port_priv *p = &priv->port_intrs[port];418 int ret = 0;419 420 /* Interrupt is optional and was not specified, do not make421 * this fatal422 */423 if (p->irq == -ENXIO)424 return ret;425 426 ret = request_threaded_irq(p->irq, b53_srab_port_isr,427 b53_srab_port_thread, 0,428 dev_name(dev->dev), p);429 if (!ret)430 p->irq_enabled = true;431 432 return ret;433}434 435static void b53_srab_irq_disable(struct b53_device *dev, int port)436{437 struct b53_srab_priv *priv = dev->priv;438 struct b53_srab_port_priv *p = &priv->port_intrs[port];439 440 if (p->irq_enabled) {441 free_irq(p->irq, p);442 p->irq_enabled = false;443 }444}445 446static void b53_srab_phylink_get_caps(struct b53_device *dev, int port,447 struct phylink_config *config)448{449 struct b53_srab_priv *priv = dev->priv;450 struct b53_srab_port_priv *p = &priv->port_intrs[port];451 452 switch (p->mode) {453 case PHY_INTERFACE_MODE_SGMII:454#if IS_ENABLED(CONFIG_B53_SERDES)455 /* If p->mode indicates SGMII mode, that essentially means we456 * are using a serdes. As the serdes for the capabilities.457 */458 b53_serdes_phylink_get_caps(dev, port, config);459#endif460 break;461 462 case PHY_INTERFACE_MODE_NA:463 break;464 465 case PHY_INTERFACE_MODE_RGMII:466 /* If we support RGMII, support all RGMII modes, since467 * that dictates the PHY delay settings.468 */469 phy_interface_set_rgmii(config->supported_interfaces);470 break;471 472 default:473 /* Some other mode (e.g. MII, GMII etc) */474 __set_bit(p->mode, config->supported_interfaces);475 break;476 }477}478 479static const struct b53_io_ops b53_srab_ops = {480 .read8 = b53_srab_read8,481 .read16 = b53_srab_read16,482 .read32 = b53_srab_read32,483 .read48 = b53_srab_read48,484 .read64 = b53_srab_read64,485 .write8 = b53_srab_write8,486 .write16 = b53_srab_write16,487 .write32 = b53_srab_write32,488 .write48 = b53_srab_write48,489 .write64 = b53_srab_write64,490 .irq_enable = b53_srab_irq_enable,491 .irq_disable = b53_srab_irq_disable,492 .phylink_get_caps = b53_srab_phylink_get_caps,493#if IS_ENABLED(CONFIG_B53_SERDES)494 .phylink_mac_select_pcs = b53_serdes_phylink_mac_select_pcs,495 .serdes_map_lane = b53_srab_serdes_map_lane,496 .serdes_link_set = b53_serdes_link_set,497#endif498};499 500static const struct of_device_id b53_srab_of_match[] = {501 { .compatible = "brcm,bcm53010-srab" },502 { .compatible = "brcm,bcm53011-srab" },503 { .compatible = "brcm,bcm53012-srab" },504 { .compatible = "brcm,bcm53018-srab" },505 { .compatible = "brcm,bcm53019-srab" },506 { .compatible = "brcm,bcm5301x-srab" },507 { .compatible = "brcm,bcm11360-srab", .data = (void *)BCM583XX_DEVICE_ID },508 { .compatible = "brcm,bcm58522-srab", .data = (void *)BCM58XX_DEVICE_ID },509 { .compatible = "brcm,bcm58525-srab", .data = (void *)BCM58XX_DEVICE_ID },510 { .compatible = "brcm,bcm58535-srab", .data = (void *)BCM58XX_DEVICE_ID },511 { .compatible = "brcm,bcm58622-srab", .data = (void *)BCM58XX_DEVICE_ID },512 { .compatible = "brcm,bcm58623-srab", .data = (void *)BCM58XX_DEVICE_ID },513 { .compatible = "brcm,bcm58625-srab", .data = (void *)BCM58XX_DEVICE_ID },514 { .compatible = "brcm,bcm88312-srab", .data = (void *)BCM58XX_DEVICE_ID },515 { .compatible = "brcm,cygnus-srab", .data = (void *)BCM583XX_DEVICE_ID },516 { .compatible = "brcm,nsp-srab", .data = (void *)BCM58XX_DEVICE_ID },517 { .compatible = "brcm,omega-srab", .data = (void *)BCM583XX_DEVICE_ID },518 { /* sentinel */ },519};520MODULE_DEVICE_TABLE(of, b53_srab_of_match);521 522static void b53_srab_intr_set(struct b53_srab_priv *priv, bool set)523{524 u32 reg;525 526 reg = readl(priv->regs + B53_SRAB_CTRLS);527 if (set)528 reg |= B53_SRAB_CTRLS_HOST_INTR;529 else530 reg &= ~B53_SRAB_CTRLS_HOST_INTR;531 writel(reg, priv->regs + B53_SRAB_CTRLS);532}533 534static void b53_srab_prepare_irq(struct platform_device *pdev)535{536 struct b53_device *dev = platform_get_drvdata(pdev);537 struct b53_srab_priv *priv = dev->priv;538 struct b53_srab_port_priv *port;539 unsigned int i;540 char *name;541 542 /* Clear all pending interrupts */543 writel(0xffffffff, priv->regs + B53_SRAB_INTR);544 545 for (i = 0; i < B53_N_PORTS; i++) {546 port = &priv->port_intrs[i];547 548 /* There is no port 6 */549 if (i == 6)550 continue;551 552 name = kasprintf(GFP_KERNEL, "link_state_p%d", i);553 if (!name)554 return;555 556 port->num = i;557 port->dev = dev;558 port->irq = platform_get_irq_byname_optional(pdev, name);559 kfree(name);560 }561 562 b53_srab_intr_set(priv, true);563}564 565static void b53_srab_mux_init(struct platform_device *pdev)566{567 struct b53_device *dev = platform_get_drvdata(pdev);568 struct b53_srab_priv *priv = dev->priv;569 struct b53_srab_port_priv *p;570 unsigned int port;571 u32 reg, off = 0;572 int ret;573 574 if (dev->pdata && dev->pdata->chip_id != BCM58XX_DEVICE_ID)575 return;576 577 priv->mux_config = devm_platform_ioremap_resource(pdev, 1);578 if (IS_ERR(priv->mux_config))579 return;580 581 /* Obtain the port mux configuration so we know which lanes582 * actually map to SerDes lanes583 */584 for (port = 5; port > 3; port--, off += 4) {585 p = &priv->port_intrs[port];586 587 reg = readl(priv->mux_config + B53_MUX_CONFIG_P5 + off);588 switch (reg & MUX_CONFIG_MASK) {589 case MUX_CONFIG_SGMII:590 p->mode = PHY_INTERFACE_MODE_SGMII;591 ret = b53_serdes_init(dev, port);592 if (ret)593 continue;594 break;595 case MUX_CONFIG_MII_LITE:596 p->mode = PHY_INTERFACE_MODE_MII;597 break;598 case MUX_CONFIG_GMII:599 p->mode = PHY_INTERFACE_MODE_GMII;600 break;601 case MUX_CONFIG_RGMII:602 p->mode = PHY_INTERFACE_MODE_RGMII;603 break;604 case MUX_CONFIG_INTERNAL:605 p->mode = PHY_INTERFACE_MODE_INTERNAL;606 break;607 default:608 p->mode = PHY_INTERFACE_MODE_NA;609 break;610 }611 612 if (p->mode != PHY_INTERFACE_MODE_NA)613 dev_info(&pdev->dev, "Port %d mode: %s\n",614 port, phy_modes(p->mode));615 }616}617 618static int b53_srab_probe(struct platform_device *pdev)619{620 struct b53_platform_data *pdata = pdev->dev.platform_data;621 struct device_node *dn = pdev->dev.of_node;622 const struct of_device_id *of_id = NULL;623 struct b53_srab_priv *priv;624 struct b53_device *dev;625 626 if (dn)627 of_id = of_match_node(b53_srab_of_match, dn);628 629 if (of_id) {630 pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);631 if (!pdata)632 return -ENOMEM;633 634 pdata->chip_id = (u32)(unsigned long)of_id->data;635 }636 637 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);638 if (!priv)639 return -ENOMEM;640 641 priv->regs = devm_platform_ioremap_resource(pdev, 0);642 if (IS_ERR(priv->regs))643 return PTR_ERR(priv->regs);644 645 dev = b53_switch_alloc(&pdev->dev, &b53_srab_ops, priv);646 if (!dev)647 return -ENOMEM;648 649 if (pdata)650 dev->pdata = pdata;651 652 platform_set_drvdata(pdev, dev);653 654 b53_srab_prepare_irq(pdev);655 b53_srab_mux_init(pdev);656 657 return b53_switch_register(dev);658}659 660static void b53_srab_remove(struct platform_device *pdev)661{662 struct b53_device *dev = platform_get_drvdata(pdev);663 664 if (!dev)665 return;666 667 b53_srab_intr_set(dev->priv, false);668 b53_switch_remove(dev);669}670 671static void b53_srab_shutdown(struct platform_device *pdev)672{673 struct b53_device *dev = platform_get_drvdata(pdev);674 675 if (!dev)676 return;677 678 b53_switch_shutdown(dev);679 680 platform_set_drvdata(pdev, NULL);681}682 683static struct platform_driver b53_srab_driver = {684 .probe = b53_srab_probe,685 .remove_new = b53_srab_remove,686 .shutdown = b53_srab_shutdown,687 .driver = {688 .name = "b53-srab-switch",689 .of_match_table = b53_srab_of_match,690 },691};692 693module_platform_driver(b53_srab_driver);694MODULE_AUTHOR("Hauke Mehrtens <hauke@hauke-m.de>");695MODULE_DESCRIPTION("B53 Switch Register Access Bridge Registers (SRAB) access driver");696MODULE_LICENSE("Dual BSD/GPL");697