Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

phy: bcm-ns-usb2: new driver for USB 2.0 PHY on Northstar

Northstar is a family of SoCs used in home routers. They have USB 2.0
and 3.0 controllers with PHYs that need to be properly initialized.
This driver provides PHY init support in a generic way and can be bound
with an EHCI controller driver.
There are (just a few) registers being defined in bcma header. It's
because DMU/CRU registers will be also needed in other drivers. We will
need them e.g. in PCIe controller/PHY driver and at some point probably
in clock driver for BCM53573 chipset. By using include/linux/bcma/ we
avoid code duplication.

Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>

authored by

Rafał Miłecki and committed by
Kishon Vijay Abraham I
d3feb406 444525d4

+184
+21
Documentation/devicetree/bindings/phy/bcm-ns-usb2-phy.txt
··· 1 + Driver for Broadcom Northstar USB 2.0 PHY 2 + 3 + Required properties: 4 + - compatible: brcm,ns-usb2-phy 5 + - reg: iomem address range of DMU (Device Management Unit) 6 + - reg-names: "dmu", the only needed & supported reg right now 7 + - clocks: USB PHY reference clock 8 + - clock-names: "phy-ref-clk", the only needed & supported clock right now 9 + 10 + To initialize USB 2.0 PHY driver needs to setup PLL correctly. To do this it 11 + requires passing phandle to the USB PHY reference clock. 12 + 13 + Example: 14 + usb2-phy { 15 + compatible = "brcm,ns-usb2-phy"; 16 + reg = <0x1800c000 0x1000>; 17 + reg-names = "dmu"; 18 + #phy-cells = <0>; 19 + clocks = <&genpll BCM_NSP_GENPLL_USB_PHY_REF_CLK>; 20 + clock-names = "phy-ref-clk"; 21 + };
+9
drivers/phy/Kconfig
··· 15 15 phy users can obtain reference to the PHY. All the users of this 16 16 framework should select this config. 17 17 18 + config PHY_BCM_NS_USB2 19 + tristate "Broadcom Northstar USB 2.0 PHY Driver" 20 + depends on ARCH_BCM_IPROC || COMPILE_TEST 21 + depends on HAS_IOMEM && OF 22 + select GENERIC_PHY 23 + help 24 + Enable this to support Broadcom USB 2.0 PHY connected to the USB 25 + controller on Northstar family. 26 + 18 27 config PHY_BERLIN_USB 19 28 tristate "Marvell Berlin USB PHY Driver" 20 29 depends on ARCH_BERLIN && RESET_CONTROLLER && HAS_IOMEM && OF
+1
drivers/phy/Makefile
··· 3 3 # 4 4 5 5 obj-$(CONFIG_GENERIC_PHY) += phy-core.o 6 + obj-$(CONFIG_PHY_BCM_NS_USB2) += phy-bcm-ns-usb2.o 6 7 obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o 7 8 obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o 8 9 obj-$(CONFIG_PHY_DM816X_USB) += phy-dm816x-usb.o
+137
drivers/phy/phy-bcm-ns-usb2.c
··· 1 + /* 2 + * Broadcom Northstar USB 2.0 PHY Driver 3 + * 4 + * Copyright (C) 2016 Rafał Miłecki <zajec5@gmail.com> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundation. 9 + * 10 + */ 11 + 12 + #include <linux/bcma/bcma.h> 13 + #include <linux/clk.h> 14 + #include <linux/delay.h> 15 + #include <linux/err.h> 16 + #include <linux/module.h> 17 + #include <linux/of_address.h> 18 + #include <linux/of_platform.h> 19 + #include <linux/phy/phy.h> 20 + #include <linux/platform_device.h> 21 + #include <linux/slab.h> 22 + 23 + struct bcm_ns_usb2 { 24 + struct device *dev; 25 + struct clk *ref_clk; 26 + struct phy *phy; 27 + void __iomem *dmu; 28 + }; 29 + 30 + static int bcm_ns_usb2_phy_init(struct phy *phy) 31 + { 32 + struct bcm_ns_usb2 *usb2 = phy_get_drvdata(phy); 33 + struct device *dev = usb2->dev; 34 + void __iomem *dmu = usb2->dmu; 35 + u32 ref_clk_rate, usb2ctl, usb_pll_ndiv, usb_pll_pdiv; 36 + int err = 0; 37 + 38 + err = clk_prepare_enable(usb2->ref_clk); 39 + if (err < 0) { 40 + dev_err(dev, "Failed to prepare ref clock: %d\n", err); 41 + goto err_out; 42 + } 43 + 44 + ref_clk_rate = clk_get_rate(usb2->ref_clk); 45 + if (!ref_clk_rate) { 46 + dev_err(dev, "Failed to get ref clock rate\n"); 47 + err = -EINVAL; 48 + goto err_clk_off; 49 + } 50 + 51 + usb2ctl = readl(dmu + BCMA_DMU_CRU_USB2_CONTROL); 52 + 53 + if (usb2ctl & BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK) { 54 + usb_pll_pdiv = usb2ctl; 55 + usb_pll_pdiv &= BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK; 56 + usb_pll_pdiv >>= BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_SHIFT; 57 + } else { 58 + usb_pll_pdiv = 1 << 3; 59 + } 60 + 61 + /* Calculate ndiv based on a solid 1920 MHz that is for USB2 PHY */ 62 + usb_pll_ndiv = (1920000000 * usb_pll_pdiv) / ref_clk_rate; 63 + 64 + /* Unlock DMU PLL settings with some magic value */ 65 + writel(0x0000ea68, dmu + BCMA_DMU_CRU_CLKSET_KEY); 66 + 67 + /* Write USB 2.0 PLL control setting */ 68 + usb2ctl &= ~BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_MASK; 69 + usb2ctl |= usb_pll_ndiv << BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_SHIFT; 70 + writel(usb2ctl, dmu + BCMA_DMU_CRU_USB2_CONTROL); 71 + 72 + /* Lock DMU PLL settings */ 73 + writel(0x00000000, dmu + BCMA_DMU_CRU_CLKSET_KEY); 74 + 75 + err_clk_off: 76 + clk_disable_unprepare(usb2->ref_clk); 77 + err_out: 78 + return err; 79 + } 80 + 81 + static const struct phy_ops ops = { 82 + .init = bcm_ns_usb2_phy_init, 83 + .owner = THIS_MODULE, 84 + }; 85 + 86 + static int bcm_ns_usb2_probe(struct platform_device *pdev) 87 + { 88 + struct device *dev = &pdev->dev; 89 + struct bcm_ns_usb2 *usb2; 90 + struct resource *res; 91 + struct phy_provider *phy_provider; 92 + 93 + usb2 = devm_kzalloc(&pdev->dev, sizeof(*usb2), GFP_KERNEL); 94 + if (!usb2) 95 + return -ENOMEM; 96 + usb2->dev = dev; 97 + 98 + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmu"); 99 + usb2->dmu = devm_ioremap_resource(dev, res); 100 + if (IS_ERR(usb2->dmu)) { 101 + dev_err(dev, "Failed to map DMU regs\n"); 102 + return PTR_ERR(usb2->dmu); 103 + } 104 + 105 + usb2->ref_clk = devm_clk_get(dev, "phy-ref-clk"); 106 + if (IS_ERR(usb2->ref_clk)) { 107 + dev_err(dev, "Clock not defined\n"); 108 + return PTR_ERR(usb2->ref_clk); 109 + } 110 + 111 + usb2->phy = devm_phy_create(dev, NULL, &ops); 112 + if (IS_ERR(dev)) 113 + return PTR_ERR(dev); 114 + 115 + phy_set_drvdata(usb2->phy, usb2); 116 + platform_set_drvdata(pdev, usb2); 117 + 118 + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 119 + return PTR_ERR_OR_ZERO(phy_provider); 120 + } 121 + 122 + static const struct of_device_id bcm_ns_usb2_id_table[] = { 123 + { .compatible = "brcm,ns-usb2-phy", }, 124 + {}, 125 + }; 126 + MODULE_DEVICE_TABLE(of, bcm_ns_usb2_id_table); 127 + 128 + static struct platform_driver bcm_ns_usb2_driver = { 129 + .probe = bcm_ns_usb2_probe, 130 + .driver = { 131 + .name = "bcm_ns_usb2", 132 + .of_match_table = bcm_ns_usb2_id_table, 133 + }, 134 + }; 135 + module_platform_driver(bcm_ns_usb2_driver); 136 + 137 + MODULE_LICENSE("GPL v2");
+1
include/linux/bcma/bcma.h
··· 4 4 #include <linux/pci.h> 5 5 #include <linux/mod_devicetable.h> 6 6 7 + #include <linux/bcma/bcma_driver_arm_c9.h> 7 8 #include <linux/bcma/bcma_driver_chipcommon.h> 8 9 #include <linux/bcma/bcma_driver_pci.h> 9 10 #include <linux/bcma/bcma_driver_pcie2.h>
+15
include/linux/bcma/bcma_driver_arm_c9.h
··· 1 + #ifndef LINUX_BCMA_DRIVER_ARM_C9_H_ 2 + #define LINUX_BCMA_DRIVER_ARM_C9_H_ 3 + 4 + /* DMU (Device Management Unit) */ 5 + #define BCMA_DMU_CRU_USB2_CONTROL 0x0164 6 + #define BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_MASK 0x00000FFC 7 + #define BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_SHIFT 2 8 + #define BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK 0x00007000 9 + #define BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_SHIFT 12 10 + #define BCMA_DMU_CRU_CLKSET_KEY 0x0180 11 + #define BCMA_DMU_CRU_STRAPS_CTRL 0x02A0 12 + #define BCMA_DMU_CRU_STRAPS_CTRL_USB3 0x00000010 13 + #define BCMA_DMU_CRU_STRAPS_CTRL_4BYTE 0x00008000 14 + 15 + #endif /* LINUX_BCMA_DRIVER_ARM_C9_H_ */