linux/drivers/phy/broadcom/phy-bcm-ns-usb3.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Broadcom Northstar USB 3.0 PHY Driver
   4 *
   5 * Copyright (C) 2016 Rafał Miłecki <rafal@milecki.pl>
   6 * Copyright (C) 2016 Broadcom
   7 *
   8 * All magic values used for initialization (and related comments) were obtained
   9 * from Broadcom's SDK:
  10 * Copyright (c) Broadcom Corp, 2012
  11 */
  12
  13#include <linux/bcma/bcma.h>
  14#include <linux/delay.h>
  15#include <linux/err.h>
  16#include <linux/iopoll.h>
  17#include <linux/mdio.h>
  18#include <linux/module.h>
  19#include <linux/of_address.h>
  20#include <linux/of_platform.h>
  21#include <linux/platform_device.h>
  22#include <linux/phy/phy.h>
  23#include <linux/slab.h>
  24
  25#define BCM_NS_USB3_PHY_BASE_ADDR_REG   0x1f
  26#define BCM_NS_USB3_PHY_PLL30_BLOCK     0x8000
  27#define BCM_NS_USB3_PHY_TX_PMD_BLOCK    0x8040
  28#define BCM_NS_USB3_PHY_PIPE_BLOCK      0x8060
  29
  30/* Registers of PLL30 block */
  31#define BCM_NS_USB3_PLL_CONTROL         0x01
  32#define BCM_NS_USB3_PLLA_CONTROL0       0x0a
  33#define BCM_NS_USB3_PLLA_CONTROL1       0x0b
  34
  35/* Registers of TX PMD block */
  36#define BCM_NS_USB3_TX_PMD_CONTROL1     0x01
  37
  38/* Registers of PIPE block */
  39#define BCM_NS_USB3_LFPS_CMP            0x02
  40#define BCM_NS_USB3_LFPS_DEGLITCH       0x03
  41
  42enum bcm_ns_family {
  43        BCM_NS_UNKNOWN,
  44        BCM_NS_AX,
  45        BCM_NS_BX,
  46};
  47
  48struct bcm_ns_usb3 {
  49        struct device *dev;
  50        enum bcm_ns_family family;
  51        void __iomem *dmp;
  52        struct mdio_device *mdiodev;
  53        struct phy *phy;
  54};
  55
  56static const struct of_device_id bcm_ns_usb3_id_table[] = {
  57        {
  58                .compatible = "brcm,ns-ax-usb3-phy",
  59                .data = (int *)BCM_NS_AX,
  60        },
  61        {
  62                .compatible = "brcm,ns-bx-usb3-phy",
  63                .data = (int *)BCM_NS_BX,
  64        },
  65        {},
  66};
  67
  68static int bcm_ns_usb3_mdio_phy_write(struct bcm_ns_usb3 *usb3, u16 reg,
  69                                      u16 value);
  70
  71static int bcm_ns_usb3_phy_init_ns_bx(struct bcm_ns_usb3 *usb3)
  72{
  73        int err;
  74
  75        /* USB3 PLL Block */
  76        err = bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG,
  77                                         BCM_NS_USB3_PHY_PLL30_BLOCK);
  78        if (err < 0)
  79                return err;
  80
  81        /* Assert Ana_Pllseq start */
  82        bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLL_CONTROL, 0x1000);
  83
  84        /* Assert CML Divider ratio to 26 */
  85        bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL0, 0x6400);
  86
  87        /* Asserting PLL Reset */
  88        bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL1, 0xc000);
  89
  90        /* Deaaserting PLL Reset */
  91        bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL1, 0x8000);
  92
  93        /* Deasserting USB3 system reset */
  94        writel(0, usb3->dmp + BCMA_RESET_CTL);
  95
  96        /* PLL frequency monitor enable */
  97        bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLL_CONTROL, 0x9000);
  98
  99        /* PIPE Block */
 100        bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG,
 101                                   BCM_NS_USB3_PHY_PIPE_BLOCK);
 102
 103        /* CMPMAX & CMPMINTH setting */
 104        bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_LFPS_CMP, 0xf30d);
 105
 106        /* DEGLITCH MIN & MAX setting */
 107        bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_LFPS_DEGLITCH, 0x6302);
 108
 109        /* TXPMD block */
 110        bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG,
 111                                   BCM_NS_USB3_PHY_TX_PMD_BLOCK);
 112
 113        /* Enabling SSC */
 114        bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_TX_PMD_CONTROL1, 0x1003);
 115
 116        return 0;
 117}
 118
 119static int bcm_ns_usb3_phy_init_ns_ax(struct bcm_ns_usb3 *usb3)
 120{
 121        int err;
 122
 123        /* PLL30 block */
 124        err = bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG,
 125                                         BCM_NS_USB3_PHY_PLL30_BLOCK);
 126        if (err < 0)
 127                return err;
 128
 129        bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL0, 0x6400);
 130
 131        bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, 0x80e0);
 132
 133        bcm_ns_usb3_mdio_phy_write(usb3, 0x02, 0x009c);
 134
 135        /* Enable SSC */
 136        bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG,
 137                                   BCM_NS_USB3_PHY_TX_PMD_BLOCK);
 138
 139        bcm_ns_usb3_mdio_phy_write(usb3, 0x02, 0x21d3);
 140
 141        bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_TX_PMD_CONTROL1, 0x1003);
 142
 143        /* Deasserting USB3 system reset */
 144        writel(0, usb3->dmp + BCMA_RESET_CTL);
 145
 146        return 0;
 147}
 148
 149static int bcm_ns_usb3_phy_init(struct phy *phy)
 150{
 151        struct bcm_ns_usb3 *usb3 = phy_get_drvdata(phy);
 152        int err;
 153
 154        /* Perform USB3 system soft reset */
 155        writel(BCMA_RESET_CTL_RESET, usb3->dmp + BCMA_RESET_CTL);
 156
 157        switch (usb3->family) {
 158        case BCM_NS_AX:
 159                err = bcm_ns_usb3_phy_init_ns_ax(usb3);
 160                break;
 161        case BCM_NS_BX:
 162                err = bcm_ns_usb3_phy_init_ns_bx(usb3);
 163                break;
 164        default:
 165                WARN_ON(1);
 166                err = -ENOTSUPP;
 167        }
 168
 169        return err;
 170}
 171
 172static const struct phy_ops ops = {
 173        .init           = bcm_ns_usb3_phy_init,
 174        .owner          = THIS_MODULE,
 175};
 176
 177/**************************************************
 178 * MDIO driver code
 179 **************************************************/
 180
 181static int bcm_ns_usb3_mdio_phy_write(struct bcm_ns_usb3 *usb3, u16 reg,
 182                                      u16 value)
 183{
 184        struct mdio_device *mdiodev = usb3->mdiodev;
 185
 186        return mdiobus_write(mdiodev->bus, mdiodev->addr, reg, value);
 187}
 188
 189static int bcm_ns_usb3_mdio_probe(struct mdio_device *mdiodev)
 190{
 191        struct device *dev = &mdiodev->dev;
 192        const struct of_device_id *of_id;
 193        struct phy_provider *phy_provider;
 194        struct device_node *syscon_np;
 195        struct bcm_ns_usb3 *usb3;
 196        struct resource res;
 197        int err;
 198
 199        usb3 = devm_kzalloc(dev, sizeof(*usb3), GFP_KERNEL);
 200        if (!usb3)
 201                return -ENOMEM;
 202
 203        usb3->dev = dev;
 204        usb3->mdiodev = mdiodev;
 205
 206        of_id = of_match_device(bcm_ns_usb3_id_table, dev);
 207        if (!of_id)
 208                return -EINVAL;
 209        usb3->family = (enum bcm_ns_family)of_id->data;
 210
 211        syscon_np = of_parse_phandle(dev->of_node, "usb3-dmp-syscon", 0);
 212        err = of_address_to_resource(syscon_np, 0, &res);
 213        of_node_put(syscon_np);
 214        if (err)
 215                return err;
 216
 217        usb3->dmp = devm_ioremap_resource(dev, &res);
 218        if (IS_ERR(usb3->dmp))
 219                return PTR_ERR(usb3->dmp);
 220
 221        usb3->phy = devm_phy_create(dev, NULL, &ops);
 222        if (IS_ERR(usb3->phy)) {
 223                dev_err(dev, "Failed to create PHY\n");
 224                return PTR_ERR(usb3->phy);
 225        }
 226
 227        phy_set_drvdata(usb3->phy, usb3);
 228
 229        phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 230
 231        return PTR_ERR_OR_ZERO(phy_provider);
 232}
 233
 234static struct mdio_driver bcm_ns_usb3_mdio_driver = {
 235        .mdiodrv = {
 236                .driver = {
 237                        .name = "bcm_ns_mdio_usb3",
 238                        .of_match_table = bcm_ns_usb3_id_table,
 239                },
 240        },
 241        .probe = bcm_ns_usb3_mdio_probe,
 242};
 243
 244mdio_module_driver(bcm_ns_usb3_mdio_driver);
 245
 246MODULE_LICENSE("GPL v2");
 247MODULE_DEVICE_TABLE(of, bcm_ns_usb3_id_table);
 248