18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Broadcom Northstar USB 3.0 PHY Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Rafał Miłecki <rafal@milecki.pl> 68c2ecf20Sopenharmony_ci * Copyright (C) 2016 Broadcom 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * All magic values used for initialization (and related comments) were obtained 98c2ecf20Sopenharmony_ci * from Broadcom's SDK: 108c2ecf20Sopenharmony_ci * Copyright (c) Broadcom Corp, 2012 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/bcma/bcma.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/err.h> 168c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 178c2ecf20Sopenharmony_ci#include <linux/mdio.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/of_address.h> 208c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define BCM_NS_USB3_MII_MNG_TIMEOUT_US 1000 /* usecs */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define BCM_NS_USB3_PHY_BASE_ADDR_REG 0x1f 288c2ecf20Sopenharmony_ci#define BCM_NS_USB3_PHY_PLL30_BLOCK 0x8000 298c2ecf20Sopenharmony_ci#define BCM_NS_USB3_PHY_TX_PMD_BLOCK 0x8040 308c2ecf20Sopenharmony_ci#define BCM_NS_USB3_PHY_PIPE_BLOCK 0x8060 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* Registers of PLL30 block */ 338c2ecf20Sopenharmony_ci#define BCM_NS_USB3_PLL_CONTROL 0x01 348c2ecf20Sopenharmony_ci#define BCM_NS_USB3_PLLA_CONTROL0 0x0a 358c2ecf20Sopenharmony_ci#define BCM_NS_USB3_PLLA_CONTROL1 0x0b 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* Registers of TX PMD block */ 388c2ecf20Sopenharmony_ci#define BCM_NS_USB3_TX_PMD_CONTROL1 0x01 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* Registers of PIPE block */ 418c2ecf20Sopenharmony_ci#define BCM_NS_USB3_LFPS_CMP 0x02 428c2ecf20Sopenharmony_ci#define BCM_NS_USB3_LFPS_DEGLITCH 0x03 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cienum bcm_ns_family { 458c2ecf20Sopenharmony_ci BCM_NS_UNKNOWN, 468c2ecf20Sopenharmony_ci BCM_NS_AX, 478c2ecf20Sopenharmony_ci BCM_NS_BX, 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct bcm_ns_usb3 { 518c2ecf20Sopenharmony_ci struct device *dev; 528c2ecf20Sopenharmony_ci enum bcm_ns_family family; 538c2ecf20Sopenharmony_ci void __iomem *dmp; 548c2ecf20Sopenharmony_ci void __iomem *ccb_mii; 558c2ecf20Sopenharmony_ci struct mdio_device *mdiodev; 568c2ecf20Sopenharmony_ci struct phy *phy; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci int (*phy_write)(struct bcm_ns_usb3 *usb3, u16 reg, u16 value); 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic const struct of_device_id bcm_ns_usb3_id_table[] = { 628c2ecf20Sopenharmony_ci { 638c2ecf20Sopenharmony_ci .compatible = "brcm,ns-ax-usb3-phy", 648c2ecf20Sopenharmony_ci .data = (int *)BCM_NS_AX, 658c2ecf20Sopenharmony_ci }, 668c2ecf20Sopenharmony_ci { 678c2ecf20Sopenharmony_ci .compatible = "brcm,ns-bx-usb3-phy", 688c2ecf20Sopenharmony_ci .data = (int *)BCM_NS_BX, 698c2ecf20Sopenharmony_ci }, 708c2ecf20Sopenharmony_ci {}, 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, bcm_ns_usb3_id_table); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic int bcm_ns_usb3_mdio_phy_write(struct bcm_ns_usb3 *usb3, u16 reg, 758c2ecf20Sopenharmony_ci u16 value) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci return usb3->phy_write(usb3, reg, value); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic int bcm_ns_usb3_phy_init_ns_bx(struct bcm_ns_usb3 *usb3) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci int err; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* USB3 PLL Block */ 858c2ecf20Sopenharmony_ci err = bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, 868c2ecf20Sopenharmony_ci BCM_NS_USB3_PHY_PLL30_BLOCK); 878c2ecf20Sopenharmony_ci if (err < 0) 888c2ecf20Sopenharmony_ci return err; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* Assert Ana_Pllseq start */ 918c2ecf20Sopenharmony_ci bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLL_CONTROL, 0x1000); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* Assert CML Divider ratio to 26 */ 948c2ecf20Sopenharmony_ci bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL0, 0x6400); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* Asserting PLL Reset */ 978c2ecf20Sopenharmony_ci bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL1, 0xc000); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* Deaaserting PLL Reset */ 1008c2ecf20Sopenharmony_ci bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL1, 0x8000); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* Deasserting USB3 system reset */ 1038c2ecf20Sopenharmony_ci writel(0, usb3->dmp + BCMA_RESET_CTL); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* PLL frequency monitor enable */ 1068c2ecf20Sopenharmony_ci bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLL_CONTROL, 0x9000); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* PIPE Block */ 1098c2ecf20Sopenharmony_ci bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, 1108c2ecf20Sopenharmony_ci BCM_NS_USB3_PHY_PIPE_BLOCK); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* CMPMAX & CMPMINTH setting */ 1138c2ecf20Sopenharmony_ci bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_LFPS_CMP, 0xf30d); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* DEGLITCH MIN & MAX setting */ 1168c2ecf20Sopenharmony_ci bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_LFPS_DEGLITCH, 0x6302); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* TXPMD block */ 1198c2ecf20Sopenharmony_ci bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, 1208c2ecf20Sopenharmony_ci BCM_NS_USB3_PHY_TX_PMD_BLOCK); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* Enabling SSC */ 1238c2ecf20Sopenharmony_ci bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_TX_PMD_CONTROL1, 0x1003); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return 0; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic int bcm_ns_usb3_phy_init_ns_ax(struct bcm_ns_usb3 *usb3) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci int err; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* PLL30 block */ 1338c2ecf20Sopenharmony_ci err = bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, 1348c2ecf20Sopenharmony_ci BCM_NS_USB3_PHY_PLL30_BLOCK); 1358c2ecf20Sopenharmony_ci if (err < 0) 1368c2ecf20Sopenharmony_ci return err; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL0, 0x6400); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, 0x80e0); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci bcm_ns_usb3_mdio_phy_write(usb3, 0x02, 0x009c); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* Enable SSC */ 1458c2ecf20Sopenharmony_ci bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, 1468c2ecf20Sopenharmony_ci BCM_NS_USB3_PHY_TX_PMD_BLOCK); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci bcm_ns_usb3_mdio_phy_write(usb3, 0x02, 0x21d3); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_TX_PMD_CONTROL1, 0x1003); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* Deasserting USB3 system reset */ 1538c2ecf20Sopenharmony_ci writel(0, usb3->dmp + BCMA_RESET_CTL); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int bcm_ns_usb3_phy_init(struct phy *phy) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct bcm_ns_usb3 *usb3 = phy_get_drvdata(phy); 1618c2ecf20Sopenharmony_ci int err; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* Perform USB3 system soft reset */ 1648c2ecf20Sopenharmony_ci writel(BCMA_RESET_CTL_RESET, usb3->dmp + BCMA_RESET_CTL); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci switch (usb3->family) { 1678c2ecf20Sopenharmony_ci case BCM_NS_AX: 1688c2ecf20Sopenharmony_ci err = bcm_ns_usb3_phy_init_ns_ax(usb3); 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci case BCM_NS_BX: 1718c2ecf20Sopenharmony_ci err = bcm_ns_usb3_phy_init_ns_bx(usb3); 1728c2ecf20Sopenharmony_ci break; 1738c2ecf20Sopenharmony_ci default: 1748c2ecf20Sopenharmony_ci WARN_ON(1); 1758c2ecf20Sopenharmony_ci err = -ENOTSUPP; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return err; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic const struct phy_ops ops = { 1828c2ecf20Sopenharmony_ci .init = bcm_ns_usb3_phy_init, 1838c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1848c2ecf20Sopenharmony_ci}; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/************************************************** 1878c2ecf20Sopenharmony_ci * MDIO driver code 1888c2ecf20Sopenharmony_ci **************************************************/ 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic int bcm_ns_usb3_mdiodev_phy_write(struct bcm_ns_usb3 *usb3, u16 reg, 1918c2ecf20Sopenharmony_ci u16 value) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct mdio_device *mdiodev = usb3->mdiodev; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return mdiobus_write(mdiodev->bus, mdiodev->addr, reg, value); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic int bcm_ns_usb3_mdio_probe(struct mdio_device *mdiodev) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct device *dev = &mdiodev->dev; 2018c2ecf20Sopenharmony_ci const struct of_device_id *of_id; 2028c2ecf20Sopenharmony_ci struct phy_provider *phy_provider; 2038c2ecf20Sopenharmony_ci struct device_node *syscon_np; 2048c2ecf20Sopenharmony_ci struct bcm_ns_usb3 *usb3; 2058c2ecf20Sopenharmony_ci struct resource res; 2068c2ecf20Sopenharmony_ci int err; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci usb3 = devm_kzalloc(dev, sizeof(*usb3), GFP_KERNEL); 2098c2ecf20Sopenharmony_ci if (!usb3) 2108c2ecf20Sopenharmony_ci return -ENOMEM; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci usb3->dev = dev; 2138c2ecf20Sopenharmony_ci usb3->mdiodev = mdiodev; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci of_id = of_match_device(bcm_ns_usb3_id_table, dev); 2168c2ecf20Sopenharmony_ci if (!of_id) 2178c2ecf20Sopenharmony_ci return -EINVAL; 2188c2ecf20Sopenharmony_ci usb3->family = (enum bcm_ns_family)of_id->data; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci syscon_np = of_parse_phandle(dev->of_node, "usb3-dmp-syscon", 0); 2218c2ecf20Sopenharmony_ci err = of_address_to_resource(syscon_np, 0, &res); 2228c2ecf20Sopenharmony_ci of_node_put(syscon_np); 2238c2ecf20Sopenharmony_ci if (err) 2248c2ecf20Sopenharmony_ci return err; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci usb3->dmp = devm_ioremap_resource(dev, &res); 2278c2ecf20Sopenharmony_ci if (IS_ERR(usb3->dmp)) { 2288c2ecf20Sopenharmony_ci dev_err(dev, "Failed to map DMP regs\n"); 2298c2ecf20Sopenharmony_ci return PTR_ERR(usb3->dmp); 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci usb3->phy_write = bcm_ns_usb3_mdiodev_phy_write; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci usb3->phy = devm_phy_create(dev, NULL, &ops); 2358c2ecf20Sopenharmony_ci if (IS_ERR(usb3->phy)) { 2368c2ecf20Sopenharmony_ci dev_err(dev, "Failed to create PHY\n"); 2378c2ecf20Sopenharmony_ci return PTR_ERR(usb3->phy); 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci phy_set_drvdata(usb3->phy, usb3); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(phy_provider); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic struct mdio_driver bcm_ns_usb3_mdio_driver = { 2488c2ecf20Sopenharmony_ci .mdiodrv = { 2498c2ecf20Sopenharmony_ci .driver = { 2508c2ecf20Sopenharmony_ci .name = "bcm_ns_mdio_usb3", 2518c2ecf20Sopenharmony_ci .of_match_table = bcm_ns_usb3_id_table, 2528c2ecf20Sopenharmony_ci }, 2538c2ecf20Sopenharmony_ci }, 2548c2ecf20Sopenharmony_ci .probe = bcm_ns_usb3_mdio_probe, 2558c2ecf20Sopenharmony_ci}; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci/************************************************** 2588c2ecf20Sopenharmony_ci * Platform driver code 2598c2ecf20Sopenharmony_ci **************************************************/ 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic int bcm_ns_usb3_wait_reg(struct bcm_ns_usb3 *usb3, void __iomem *addr, 2628c2ecf20Sopenharmony_ci u32 mask, u32 value, int usec) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci u32 val; 2658c2ecf20Sopenharmony_ci int ret; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci ret = readl_poll_timeout_atomic(addr, val, ((val & mask) == value), 2688c2ecf20Sopenharmony_ci 10, usec); 2698c2ecf20Sopenharmony_ci if (ret) 2708c2ecf20Sopenharmony_ci dev_err(usb3->dev, "Timeout waiting for register %p\n", addr); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci return ret; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic inline int bcm_ns_usb3_mii_mng_wait_idle(struct bcm_ns_usb3 *usb3) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci return bcm_ns_usb3_wait_reg(usb3, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL, 2788c2ecf20Sopenharmony_ci 0x0100, 0x0000, 2798c2ecf20Sopenharmony_ci BCM_NS_USB3_MII_MNG_TIMEOUT_US); 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic int bcm_ns_usb3_platform_phy_write(struct bcm_ns_usb3 *usb3, u16 reg, 2838c2ecf20Sopenharmony_ci u16 value) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci u32 tmp = 0; 2868c2ecf20Sopenharmony_ci int err; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci err = bcm_ns_usb3_mii_mng_wait_idle(usb3); 2898c2ecf20Sopenharmony_ci if (err < 0) { 2908c2ecf20Sopenharmony_ci dev_err(usb3->dev, "Couldn't write 0x%08x value\n", value); 2918c2ecf20Sopenharmony_ci return err; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* TODO: Use a proper MDIO bus layer */ 2958c2ecf20Sopenharmony_ci tmp |= 0x58020000; /* Magic value for MDIO PHY write */ 2968c2ecf20Sopenharmony_ci tmp |= reg << 18; 2978c2ecf20Sopenharmony_ci tmp |= value; 2988c2ecf20Sopenharmony_ci writel(tmp, usb3->ccb_mii + BCMA_CCB_MII_MNG_CMD_DATA); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return bcm_ns_usb3_mii_mng_wait_idle(usb3); 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic int bcm_ns_usb3_probe(struct platform_device *pdev) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 3068c2ecf20Sopenharmony_ci const struct of_device_id *of_id; 3078c2ecf20Sopenharmony_ci struct bcm_ns_usb3 *usb3; 3088c2ecf20Sopenharmony_ci struct resource *res; 3098c2ecf20Sopenharmony_ci struct phy_provider *phy_provider; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci usb3 = devm_kzalloc(dev, sizeof(*usb3), GFP_KERNEL); 3128c2ecf20Sopenharmony_ci if (!usb3) 3138c2ecf20Sopenharmony_ci return -ENOMEM; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci usb3->dev = dev; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci of_id = of_match_device(bcm_ns_usb3_id_table, dev); 3188c2ecf20Sopenharmony_ci if (!of_id) 3198c2ecf20Sopenharmony_ci return -EINVAL; 3208c2ecf20Sopenharmony_ci usb3->family = (enum bcm_ns_family)of_id->data; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmp"); 3238c2ecf20Sopenharmony_ci usb3->dmp = devm_ioremap_resource(dev, res); 3248c2ecf20Sopenharmony_ci if (IS_ERR(usb3->dmp)) { 3258c2ecf20Sopenharmony_ci dev_err(dev, "Failed to map DMP regs\n"); 3268c2ecf20Sopenharmony_ci return PTR_ERR(usb3->dmp); 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ccb-mii"); 3308c2ecf20Sopenharmony_ci usb3->ccb_mii = devm_ioremap_resource(dev, res); 3318c2ecf20Sopenharmony_ci if (IS_ERR(usb3->ccb_mii)) { 3328c2ecf20Sopenharmony_ci dev_err(dev, "Failed to map ChipCommon B MII regs\n"); 3338c2ecf20Sopenharmony_ci return PTR_ERR(usb3->ccb_mii); 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* Enable MDIO. Setting MDCDIV as 26 */ 3378c2ecf20Sopenharmony_ci writel(0x0000009a, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* Wait for MDIO? */ 3408c2ecf20Sopenharmony_ci udelay(2); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci usb3->phy_write = bcm_ns_usb3_platform_phy_write; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci usb3->phy = devm_phy_create(dev, NULL, &ops); 3458c2ecf20Sopenharmony_ci if (IS_ERR(usb3->phy)) { 3468c2ecf20Sopenharmony_ci dev_err(dev, "Failed to create PHY\n"); 3478c2ecf20Sopenharmony_ci return PTR_ERR(usb3->phy); 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci phy_set_drvdata(usb3->phy, usb3); 3518c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, usb3); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 3548c2ecf20Sopenharmony_ci if (!IS_ERR(phy_provider)) 3558c2ecf20Sopenharmony_ci dev_info(dev, "Registered Broadcom Northstar USB 3.0 PHY driver\n"); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(phy_provider); 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic struct platform_driver bcm_ns_usb3_driver = { 3618c2ecf20Sopenharmony_ci .probe = bcm_ns_usb3_probe, 3628c2ecf20Sopenharmony_ci .driver = { 3638c2ecf20Sopenharmony_ci .name = "bcm_ns_usb3", 3648c2ecf20Sopenharmony_ci .of_match_table = bcm_ns_usb3_id_table, 3658c2ecf20Sopenharmony_ci }, 3668c2ecf20Sopenharmony_ci}; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic int __init bcm_ns_usb3_module_init(void) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci int err; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* 3738c2ecf20Sopenharmony_ci * For backward compatibility we register as MDIO and platform driver. 3748c2ecf20Sopenharmony_ci * After getting MDIO binding commonly used (e.g. switching all DT files 3758c2ecf20Sopenharmony_ci * to use it) we should deprecate the old binding and eventually drop 3768c2ecf20Sopenharmony_ci * support for it. 3778c2ecf20Sopenharmony_ci */ 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci err = mdio_driver_register(&bcm_ns_usb3_mdio_driver); 3808c2ecf20Sopenharmony_ci if (err) 3818c2ecf20Sopenharmony_ci return err; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci err = platform_driver_register(&bcm_ns_usb3_driver); 3848c2ecf20Sopenharmony_ci if (err) 3858c2ecf20Sopenharmony_ci mdio_driver_unregister(&bcm_ns_usb3_mdio_driver); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci return err; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_cimodule_init(bcm_ns_usb3_module_init); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic void __exit bcm_ns_usb3_module_exit(void) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci platform_driver_unregister(&bcm_ns_usb3_driver); 3948c2ecf20Sopenharmony_ci mdio_driver_unregister(&bcm_ns_usb3_mdio_driver); 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_cimodule_exit(bcm_ns_usb3_module_exit) 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 399