1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 20b56e9a7SVivek Gautam /* 30b56e9a7SVivek Gautam * Broadcom Northstar USB 3.0 PHY Driver 40b56e9a7SVivek Gautam * 50b56e9a7SVivek Gautam * Copyright (C) 2016 Rafał Miłecki <rafal@milecki.pl> 60b56e9a7SVivek Gautam * Copyright (C) 2016 Broadcom 70b56e9a7SVivek Gautam * 80b56e9a7SVivek Gautam * All magic values used for initialization (and related comments) were obtained 90b56e9a7SVivek Gautam * from Broadcom's SDK: 100b56e9a7SVivek Gautam * Copyright (c) Broadcom Corp, 2012 110b56e9a7SVivek Gautam */ 120b56e9a7SVivek Gautam 130b56e9a7SVivek Gautam #include <linux/bcma/bcma.h> 140b56e9a7SVivek Gautam #include <linux/delay.h> 150b56e9a7SVivek Gautam #include <linux/err.h> 1647da6aa7SChunfeng Yun #include <linux/iopoll.h> 17af850e14SRafał Miłecki #include <linux/mdio.h> 180b56e9a7SVivek Gautam #include <linux/module.h> 19af850e14SRafał Miłecki #include <linux/of_address.h> 200b56e9a7SVivek Gautam #include <linux/of_platform.h> 210b56e9a7SVivek Gautam #include <linux/platform_device.h> 220b56e9a7SVivek Gautam #include <linux/phy/phy.h> 230b56e9a7SVivek Gautam #include <linux/slab.h> 240b56e9a7SVivek Gautam 250b56e9a7SVivek Gautam #define BCM_NS_USB3_PHY_BASE_ADDR_REG 0x1f 260b56e9a7SVivek Gautam #define BCM_NS_USB3_PHY_PLL30_BLOCK 0x8000 270b56e9a7SVivek Gautam #define BCM_NS_USB3_PHY_TX_PMD_BLOCK 0x8040 280b56e9a7SVivek Gautam #define BCM_NS_USB3_PHY_PIPE_BLOCK 0x8060 290b56e9a7SVivek Gautam 300b56e9a7SVivek Gautam /* Registers of PLL30 block */ 310b56e9a7SVivek Gautam #define BCM_NS_USB3_PLL_CONTROL 0x01 320b56e9a7SVivek Gautam #define BCM_NS_USB3_PLLA_CONTROL0 0x0a 330b56e9a7SVivek Gautam #define BCM_NS_USB3_PLLA_CONTROL1 0x0b 340b56e9a7SVivek Gautam 350b56e9a7SVivek Gautam /* Registers of TX PMD block */ 360b56e9a7SVivek Gautam #define BCM_NS_USB3_TX_PMD_CONTROL1 0x01 370b56e9a7SVivek Gautam 380b56e9a7SVivek Gautam /* Registers of PIPE block */ 390b56e9a7SVivek Gautam #define BCM_NS_USB3_LFPS_CMP 0x02 400b56e9a7SVivek Gautam #define BCM_NS_USB3_LFPS_DEGLITCH 0x03 410b56e9a7SVivek Gautam 420b56e9a7SVivek Gautam enum bcm_ns_family { 430b56e9a7SVivek Gautam BCM_NS_UNKNOWN, 440b56e9a7SVivek Gautam BCM_NS_AX, 450b56e9a7SVivek Gautam BCM_NS_BX, 460b56e9a7SVivek Gautam }; 470b56e9a7SVivek Gautam 480b56e9a7SVivek Gautam struct bcm_ns_usb3 { 490b56e9a7SVivek Gautam struct device *dev; 500b56e9a7SVivek Gautam enum bcm_ns_family family; 510b56e9a7SVivek Gautam void __iomem *dmp; 52af850e14SRafał Miłecki struct mdio_device *mdiodev; 530b56e9a7SVivek Gautam struct phy *phy; 540b56e9a7SVivek Gautam }; 550b56e9a7SVivek Gautam 560b56e9a7SVivek Gautam static const struct of_device_id bcm_ns_usb3_id_table[] = { 570b56e9a7SVivek Gautam { 580b56e9a7SVivek Gautam .compatible = "brcm,ns-ax-usb3-phy", 590b56e9a7SVivek Gautam .data = (int *)BCM_NS_AX, 600b56e9a7SVivek Gautam }, 610b56e9a7SVivek Gautam { 620b56e9a7SVivek Gautam .compatible = "brcm,ns-bx-usb3-phy", 630b56e9a7SVivek Gautam .data = (int *)BCM_NS_BX, 640b56e9a7SVivek Gautam }, 650b56e9a7SVivek Gautam {}, 660b56e9a7SVivek Gautam }; 670b56e9a7SVivek Gautam 680b56e9a7SVivek Gautam static int bcm_ns_usb3_mdio_phy_write(struct bcm_ns_usb3 *usb3, u16 reg, 6936a94760SRafał Miłecki u16 value); 700b56e9a7SVivek Gautam 710b56e9a7SVivek Gautam static int bcm_ns_usb3_phy_init_ns_bx(struct bcm_ns_usb3 *usb3) 720b56e9a7SVivek Gautam { 730b56e9a7SVivek Gautam int err; 740b56e9a7SVivek Gautam 750b56e9a7SVivek Gautam /* USB3 PLL Block */ 760b56e9a7SVivek Gautam err = bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, 770b56e9a7SVivek Gautam BCM_NS_USB3_PHY_PLL30_BLOCK); 780b56e9a7SVivek Gautam if (err < 0) 790b56e9a7SVivek Gautam return err; 800b56e9a7SVivek Gautam 810b56e9a7SVivek Gautam /* Assert Ana_Pllseq start */ 820b56e9a7SVivek Gautam bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLL_CONTROL, 0x1000); 830b56e9a7SVivek Gautam 840b56e9a7SVivek Gautam /* Assert CML Divider ratio to 26 */ 850b56e9a7SVivek Gautam bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL0, 0x6400); 860b56e9a7SVivek Gautam 870b56e9a7SVivek Gautam /* Asserting PLL Reset */ 880b56e9a7SVivek Gautam bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL1, 0xc000); 890b56e9a7SVivek Gautam 900b56e9a7SVivek Gautam /* Deaaserting PLL Reset */ 910b56e9a7SVivek Gautam bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL1, 0x8000); 920b56e9a7SVivek Gautam 930b56e9a7SVivek Gautam /* Deasserting USB3 system reset */ 940b56e9a7SVivek Gautam writel(0, usb3->dmp + BCMA_RESET_CTL); 950b56e9a7SVivek Gautam 960b56e9a7SVivek Gautam /* PLL frequency monitor enable */ 970b56e9a7SVivek Gautam bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLL_CONTROL, 0x9000); 980b56e9a7SVivek Gautam 990b56e9a7SVivek Gautam /* PIPE Block */ 1000b56e9a7SVivek Gautam bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, 1010b56e9a7SVivek Gautam BCM_NS_USB3_PHY_PIPE_BLOCK); 1020b56e9a7SVivek Gautam 1030b56e9a7SVivek Gautam /* CMPMAX & CMPMINTH setting */ 1040b56e9a7SVivek Gautam bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_LFPS_CMP, 0xf30d); 1050b56e9a7SVivek Gautam 1060b56e9a7SVivek Gautam /* DEGLITCH MIN & MAX setting */ 1070b56e9a7SVivek Gautam bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_LFPS_DEGLITCH, 0x6302); 1080b56e9a7SVivek Gautam 1090b56e9a7SVivek Gautam /* TXPMD block */ 1100b56e9a7SVivek Gautam bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, 1110b56e9a7SVivek Gautam BCM_NS_USB3_PHY_TX_PMD_BLOCK); 1120b56e9a7SVivek Gautam 1130b56e9a7SVivek Gautam /* Enabling SSC */ 1140b56e9a7SVivek Gautam bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_TX_PMD_CONTROL1, 0x1003); 1150b56e9a7SVivek Gautam 1160b56e9a7SVivek Gautam return 0; 1170b56e9a7SVivek Gautam } 1180b56e9a7SVivek Gautam 1190b56e9a7SVivek Gautam static int bcm_ns_usb3_phy_init_ns_ax(struct bcm_ns_usb3 *usb3) 1200b56e9a7SVivek Gautam { 1210b56e9a7SVivek Gautam int err; 1220b56e9a7SVivek Gautam 1230b56e9a7SVivek Gautam /* PLL30 block */ 1240b56e9a7SVivek Gautam err = bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, 1250b56e9a7SVivek Gautam BCM_NS_USB3_PHY_PLL30_BLOCK); 1260b56e9a7SVivek Gautam if (err < 0) 1270b56e9a7SVivek Gautam return err; 1280b56e9a7SVivek Gautam 1290b56e9a7SVivek Gautam bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL0, 0x6400); 1300b56e9a7SVivek Gautam 1310b56e9a7SVivek Gautam bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, 0x80e0); 1320b56e9a7SVivek Gautam 1330b56e9a7SVivek Gautam bcm_ns_usb3_mdio_phy_write(usb3, 0x02, 0x009c); 1340b56e9a7SVivek Gautam 1350b56e9a7SVivek Gautam /* Enable SSC */ 1360b56e9a7SVivek Gautam bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, 1370b56e9a7SVivek Gautam BCM_NS_USB3_PHY_TX_PMD_BLOCK); 1380b56e9a7SVivek Gautam 1390b56e9a7SVivek Gautam bcm_ns_usb3_mdio_phy_write(usb3, 0x02, 0x21d3); 1400b56e9a7SVivek Gautam 1410b56e9a7SVivek Gautam bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_TX_PMD_CONTROL1, 0x1003); 1420b56e9a7SVivek Gautam 1430b56e9a7SVivek Gautam /* Deasserting USB3 system reset */ 1440b56e9a7SVivek Gautam writel(0, usb3->dmp + BCMA_RESET_CTL); 1450b56e9a7SVivek Gautam 1460b56e9a7SVivek Gautam return 0; 1470b56e9a7SVivek Gautam } 1480b56e9a7SVivek Gautam 1490b56e9a7SVivek Gautam static int bcm_ns_usb3_phy_init(struct phy *phy) 1500b56e9a7SVivek Gautam { 1510b56e9a7SVivek Gautam struct bcm_ns_usb3 *usb3 = phy_get_drvdata(phy); 1520b56e9a7SVivek Gautam int err; 1530b56e9a7SVivek Gautam 1540b56e9a7SVivek Gautam /* Perform USB3 system soft reset */ 1550b56e9a7SVivek Gautam writel(BCMA_RESET_CTL_RESET, usb3->dmp + BCMA_RESET_CTL); 1560b56e9a7SVivek Gautam 1570b56e9a7SVivek Gautam switch (usb3->family) { 1580b56e9a7SVivek Gautam case BCM_NS_AX: 1590b56e9a7SVivek Gautam err = bcm_ns_usb3_phy_init_ns_ax(usb3); 1600b56e9a7SVivek Gautam break; 1610b56e9a7SVivek Gautam case BCM_NS_BX: 1620b56e9a7SVivek Gautam err = bcm_ns_usb3_phy_init_ns_bx(usb3); 1630b56e9a7SVivek Gautam break; 1640b56e9a7SVivek Gautam default: 1650b56e9a7SVivek Gautam WARN_ON(1); 1660b56e9a7SVivek Gautam err = -ENOTSUPP; 1670b56e9a7SVivek Gautam } 1680b56e9a7SVivek Gautam 1690b56e9a7SVivek Gautam return err; 1700b56e9a7SVivek Gautam } 1710b56e9a7SVivek Gautam 1720b56e9a7SVivek Gautam static const struct phy_ops ops = { 1730b56e9a7SVivek Gautam .init = bcm_ns_usb3_phy_init, 1740b56e9a7SVivek Gautam .owner = THIS_MODULE, 1750b56e9a7SVivek Gautam }; 1760b56e9a7SVivek Gautam 177b20f506fSRafał Miłecki /************************************************** 178af850e14SRafał Miłecki * MDIO driver code 179af850e14SRafał Miłecki **************************************************/ 180af850e14SRafał Miłecki 18136a94760SRafał Miłecki static int bcm_ns_usb3_mdio_phy_write(struct bcm_ns_usb3 *usb3, u16 reg, 182af850e14SRafał Miłecki u16 value) 183af850e14SRafał Miłecki { 184af850e14SRafał Miłecki struct mdio_device *mdiodev = usb3->mdiodev; 185af850e14SRafał Miłecki 186af850e14SRafał Miłecki return mdiobus_write(mdiodev->bus, mdiodev->addr, reg, value); 187af850e14SRafał Miłecki } 188af850e14SRafał Miłecki 189af850e14SRafał Miłecki static int bcm_ns_usb3_mdio_probe(struct mdio_device *mdiodev) 190af850e14SRafał Miłecki { 191af850e14SRafał Miłecki struct device *dev = &mdiodev->dev; 192af850e14SRafał Miłecki const struct of_device_id *of_id; 193af850e14SRafał Miłecki struct phy_provider *phy_provider; 194af850e14SRafał Miłecki struct device_node *syscon_np; 195af850e14SRafał Miłecki struct bcm_ns_usb3 *usb3; 196af850e14SRafał Miłecki struct resource res; 197af850e14SRafał Miłecki int err; 198af850e14SRafał Miłecki 199af850e14SRafał Miłecki usb3 = devm_kzalloc(dev, sizeof(*usb3), GFP_KERNEL); 200af850e14SRafał Miłecki if (!usb3) 201af850e14SRafał Miłecki return -ENOMEM; 202af850e14SRafał Miłecki 203af850e14SRafał Miłecki usb3->dev = dev; 204af850e14SRafał Miłecki usb3->mdiodev = mdiodev; 205af850e14SRafał Miłecki 206af850e14SRafał Miłecki of_id = of_match_device(bcm_ns_usb3_id_table, dev); 207af850e14SRafał Miłecki if (!of_id) 208af850e14SRafał Miłecki return -EINVAL; 209af850e14SRafał Miłecki usb3->family = (enum bcm_ns_family)of_id->data; 210af850e14SRafał Miłecki 211af850e14SRafał Miłecki syscon_np = of_parse_phandle(dev->of_node, "usb3-dmp-syscon", 0); 212af850e14SRafał Miłecki err = of_address_to_resource(syscon_np, 0, &res); 213af850e14SRafał Miłecki of_node_put(syscon_np); 214af850e14SRafał Miłecki if (err) 215af850e14SRafał Miłecki return err; 216af850e14SRafał Miłecki 217af850e14SRafał Miłecki usb3->dmp = devm_ioremap_resource(dev, &res); 218*185ff019SHe Ying if (IS_ERR(usb3->dmp)) 219af850e14SRafał Miłecki return PTR_ERR(usb3->dmp); 220af850e14SRafał Miłecki 221af850e14SRafał Miłecki usb3->phy = devm_phy_create(dev, NULL, &ops); 222af850e14SRafał Miłecki if (IS_ERR(usb3->phy)) { 223af850e14SRafał Miłecki dev_err(dev, "Failed to create PHY\n"); 224af850e14SRafał Miłecki return PTR_ERR(usb3->phy); 225af850e14SRafał Miłecki } 226af850e14SRafał Miłecki 227af850e14SRafał Miłecki phy_set_drvdata(usb3->phy, usb3); 228af850e14SRafał Miłecki 229af850e14SRafał Miłecki phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 230af850e14SRafał Miłecki 231af850e14SRafał Miłecki return PTR_ERR_OR_ZERO(phy_provider); 232af850e14SRafał Miłecki } 233af850e14SRafał Miłecki 234af850e14SRafał Miłecki static struct mdio_driver bcm_ns_usb3_mdio_driver = { 235af850e14SRafał Miłecki .mdiodrv = { 236af850e14SRafał Miłecki .driver = { 237af850e14SRafał Miłecki .name = "bcm_ns_mdio_usb3", 238af850e14SRafał Miłecki .of_match_table = bcm_ns_usb3_id_table, 239af850e14SRafał Miłecki }, 240af850e14SRafał Miłecki }, 241af850e14SRafał Miłecki .probe = bcm_ns_usb3_mdio_probe, 242af850e14SRafał Miłecki }; 243af850e14SRafał Miłecki 24436a94760SRafał Miłecki mdio_module_driver(bcm_ns_usb3_mdio_driver); 2450b56e9a7SVivek Gautam 2460b56e9a7SVivek Gautam MODULE_LICENSE("GPL v2"); 24736a94760SRafał Miłecki MODULE_DEVICE_TABLE(of, bcm_ns_usb3_id_table); 248