aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/phy
diff options
context:
space:
mode:
authorRafał Miłecki <zajec5@gmail.com>2016-04-14 05:37:43 -0400
committerKishon Vijay Abraham I <kishon@ti.com>2016-04-30 10:42:28 -0400
commitd3feb406733544dbf0e239ef945a09decdceac88 (patch)
tree51c30c9924be5a90fa334a822581dddb585a685c /drivers/phy
parent444525d4f5dd272f695379f5d22660c96ad4fc23 (diff)
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>
Diffstat (limited to 'drivers/phy')
-rw-r--r--drivers/phy/Kconfig9
-rw-r--r--drivers/phy/Makefile1
-rw-r--r--drivers/phy/phy-bcm-ns-usb2.c137
3 files changed, 147 insertions, 0 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index f6ff76ec89dc..f2b458fc0c89 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -15,6 +15,15 @@ config GENERIC_PHY
15 phy users can obtain reference to the PHY. All the users of this 15 phy users can obtain reference to the PHY. All the users of this
16 framework should select this config. 16 framework should select this config.
17 17
18config 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
18config PHY_BERLIN_USB 27config PHY_BERLIN_USB
19 tristate "Marvell Berlin USB PHY Driver" 28 tristate "Marvell Berlin USB PHY Driver"
20 depends on ARCH_BERLIN && RESET_CONTROLLER && HAS_IOMEM && OF 29 depends on ARCH_BERLIN && RESET_CONTROLLER && HAS_IOMEM && OF
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index f03fa1fdf322..0de09e13fdbc 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -3,6 +3,7 @@
3# 3#
4 4
5obj-$(CONFIG_GENERIC_PHY) += phy-core.o 5obj-$(CONFIG_GENERIC_PHY) += phy-core.o
6obj-$(CONFIG_PHY_BCM_NS_USB2) += phy-bcm-ns-usb2.o
6obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o 7obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o
7obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o 8obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o
8obj-$(CONFIG_PHY_DM816X_USB) += phy-dm816x-usb.o 9obj-$(CONFIG_PHY_DM816X_USB) += phy-dm816x-usb.o
diff --git a/drivers/phy/phy-bcm-ns-usb2.c b/drivers/phy/phy-bcm-ns-usb2.c
new file mode 100644
index 000000000000..95ab6b2a0de5
--- /dev/null
+++ b/drivers/phy/phy-bcm-ns-usb2.c
@@ -0,0 +1,137 @@
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
23struct bcm_ns_usb2 {
24 struct device *dev;
25 struct clk *ref_clk;
26 struct phy *phy;
27 void __iomem *dmu;
28};
29
30static 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
75err_clk_off:
76 clk_disable_unprepare(usb2->ref_clk);
77err_out:
78 return err;
79}
80
81static const struct phy_ops ops = {
82 .init = bcm_ns_usb2_phy_init,
83 .owner = THIS_MODULE,
84};
85
86static 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
122static const struct of_device_id bcm_ns_usb2_id_table[] = {
123 { .compatible = "brcm,ns-usb2-phy", },
124 {},
125};
126MODULE_DEVICE_TABLE(of, bcm_ns_usb2_id_table);
127
128static 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};
135module_platform_driver(bcm_ns_usb2_driver);
136
137MODULE_LICENSE("GPL v2");