summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Bresticker <abrestic@chromium.org>2015-04-07 18:04:17 -0400
committerRalf Baechle <ralf@linux-mips.org>2015-06-21 15:53:38 -0400
commit57991ebaf91b1eda2abb586618aea6806fa86724 (patch)
tree94e2ee66c668d01cc294419e2650d00a4659619b
parent90bc35c5da64d05378b15c26a664a0ecedc984ac (diff)
PHY: Add driver for Pistachio USB2.0 PHY
Add a driver for the USB2.0 PHY found on the IMG Pistachio SoC. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Cc: Kishon Vijay Abraham I <kishon@ti.com> Cc: devicetree@vger.kernel.org Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Cc: James Hartley <james.hartley@imgtec.com> Cc: Damien Horsley <Damien.Horsley@imgtec.com> Patchwork: https://patchwork.linux-mips.org/patch/9728/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
-rw-r--r--drivers/phy/Kconfig7
-rw-r--r--drivers/phy/Makefile1
-rw-r--r--drivers/phy/phy-pistachio-usb.c206
3 files changed, 214 insertions, 0 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index fc9b9f0ea91e..e8402600fe9f 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -247,6 +247,13 @@ config PHY_EXYNOS5_USBDRD
247 This driver provides PHY interface for USB 3.0 DRD controller 247 This driver provides PHY interface for USB 3.0 DRD controller
248 present on Exynos5 SoC series. 248 present on Exynos5 SoC series.
249 249
250config PHY_PISTACHIO_USB
251 tristate "IMG Pistachio USB2.0 PHY driver"
252 depends on MACH_PISTACHIO
253 select GENERIC_PHY
254 help
255 Enable this to support the USB2.0 PHY on the IMG Pistachio SoC.
256
250config PHY_QCOM_APQ8064_SATA 257config PHY_QCOM_APQ8064_SATA
251 tristate "Qualcomm APQ8064 SATA SerDes/PHY driver" 258 tristate "Qualcomm APQ8064 SATA SerDes/PHY driver"
252 depends on ARCH_QCOM 259 depends on ARCH_QCOM
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index f12625178780..75a37dc952f5 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -40,3 +40,4 @@ obj-$(CONFIG_PHY_STIH41X_USB) += phy-stih41x-usb.o
40obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o 40obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o
41obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o 41obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o
42obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o 42obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o
43obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
diff --git a/drivers/phy/phy-pistachio-usb.c b/drivers/phy/phy-pistachio-usb.c
new file mode 100644
index 000000000000..c6db35e6bb63
--- /dev/null
+++ b/drivers/phy/phy-pistachio-usb.c
@@ -0,0 +1,206 @@
1/*
2 * IMG Pistachio USB PHY driver
3 *
4 * Copyright (C) 2015 Google, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 */
10
11#include <linux/clk.h>
12#include <linux/delay.h>
13#include <linux/io.h>
14#include <linux/kernel.h>
15#include <linux/mfd/syscon.h>
16#include <linux/module.h>
17#include <linux/of.h>
18#include <linux/phy/phy.h>
19#include <linux/platform_device.h>
20#include <linux/regmap.h>
21
22#include <dt-bindings/phy/phy-pistachio-usb.h>
23
24#define USB_PHY_CONTROL1 0x04
25#define USB_PHY_CONTROL1_FSEL_SHIFT 2
26#define USB_PHY_CONTROL1_FSEL_MASK 0x7
27
28#define USB_PHY_STRAP_CONTROL 0x10
29#define USB_PHY_STRAP_CONTROL_REFCLK_SHIFT 4
30#define USB_PHY_STRAP_CONTROL_REFCLK_MASK 0x3
31
32#define USB_PHY_STATUS 0x14
33#define USB_PHY_STATUS_RX_PHY_CLK BIT(9)
34#define USB_PHY_STATUS_RX_UTMI_CLK BIT(8)
35#define USB_PHY_STATUS_VBUS_FAULT BIT(7)
36
37struct pistachio_usb_phy {
38 struct device *dev;
39 struct regmap *cr_top;
40 struct clk *phy_clk;
41 unsigned int refclk;
42};
43
44static const unsigned long fsel_rate_map[] = {
45 9600000,
46 10000000,
47 12000000,
48 19200000,
49 20000000,
50 24000000,
51 0,
52 50000000,
53};
54
55static int pistachio_usb_phy_power_on(struct phy *phy)
56{
57 struct pistachio_usb_phy *p_phy = phy_get_drvdata(phy);
58 unsigned long timeout, rate;
59 unsigned int i;
60 int ret;
61
62 ret = clk_prepare_enable(p_phy->phy_clk);
63 if (ret < 0) {
64 dev_err(p_phy->dev, "Failed to enable PHY clock: %d\n", ret);
65 return ret;
66 }
67
68 regmap_update_bits(p_phy->cr_top, USB_PHY_STRAP_CONTROL,
69 USB_PHY_STRAP_CONTROL_REFCLK_MASK <<
70 USB_PHY_STRAP_CONTROL_REFCLK_SHIFT,
71 p_phy->refclk << USB_PHY_STRAP_CONTROL_REFCLK_SHIFT);
72
73 rate = clk_get_rate(p_phy->phy_clk);
74 if (p_phy->refclk == REFCLK_XO_CRYSTAL && rate != 12000000) {
75 dev_err(p_phy->dev, "Unsupported rate for XO crystal: %ld\n",
76 rate);
77 ret = -EINVAL;
78 goto disable_clk;
79 }
80
81 for (i = 0; i < ARRAY_SIZE(fsel_rate_map); i++) {
82 if (rate == fsel_rate_map[i])
83 break;
84 }
85 if (i == ARRAY_SIZE(fsel_rate_map)) {
86 dev_err(p_phy->dev, "Unsupported clock rate: %lu\n", rate);
87 ret = -EINVAL;
88 goto disable_clk;
89 }
90
91 regmap_update_bits(p_phy->cr_top, USB_PHY_CONTROL1,
92 USB_PHY_CONTROL1_FSEL_MASK <<
93 USB_PHY_CONTROL1_FSEL_SHIFT,
94 i << USB_PHY_CONTROL1_FSEL_SHIFT);
95
96 timeout = jiffies + msecs_to_jiffies(200);
97 while (time_before(jiffies, timeout)) {
98 unsigned int val;
99
100 regmap_read(p_phy->cr_top, USB_PHY_STATUS, &val);
101 if (val & USB_PHY_STATUS_VBUS_FAULT) {
102 dev_err(p_phy->dev, "VBUS fault detected\n");
103 ret = -EIO;
104 goto disable_clk;
105 }
106 if ((val & USB_PHY_STATUS_RX_PHY_CLK) &&
107 (val & USB_PHY_STATUS_RX_UTMI_CLK))
108 return 0;
109 usleep_range(1000, 1500);
110 }
111
112 dev_err(p_phy->dev, "Timed out waiting for PHY to power on\n");
113 ret = -ETIMEDOUT;
114
115disable_clk:
116 clk_disable_unprepare(p_phy->phy_clk);
117 return ret;
118}
119
120static int pistachio_usb_phy_power_off(struct phy *phy)
121{
122 struct pistachio_usb_phy *p_phy = phy_get_drvdata(phy);
123
124 clk_disable_unprepare(p_phy->phy_clk);
125
126 return 0;
127}
128
129static const struct phy_ops pistachio_usb_phy_ops = {
130 .power_on = pistachio_usb_phy_power_on,
131 .power_off = pistachio_usb_phy_power_off,
132 .owner = THIS_MODULE,
133};
134
135static int pistachio_usb_phy_probe(struct platform_device *pdev)
136{
137 struct pistachio_usb_phy *p_phy;
138 struct phy_provider *provider;
139 struct phy *phy;
140 int ret;
141
142 p_phy = devm_kzalloc(&pdev->dev, sizeof(*p_phy), GFP_KERNEL);
143 if (!p_phy)
144 return -ENOMEM;
145 p_phy->dev = &pdev->dev;
146 platform_set_drvdata(pdev, p_phy);
147
148 p_phy->cr_top = syscon_regmap_lookup_by_phandle(p_phy->dev->of_node,
149 "img,cr-top");
150 if (IS_ERR(p_phy->cr_top)) {
151 dev_err(p_phy->dev, "Failed to get CR_TOP registers: %ld\n",
152 PTR_ERR(p_phy->cr_top));
153 return PTR_ERR(p_phy->cr_top);
154 }
155
156 p_phy->phy_clk = devm_clk_get(p_phy->dev, "usb_phy");
157 if (IS_ERR(p_phy->phy_clk)) {
158 dev_err(p_phy->dev, "Failed to get usb_phy clock: %ld\n",
159 PTR_ERR(p_phy->phy_clk));
160 return PTR_ERR(p_phy->phy_clk);
161 }
162
163 ret = of_property_read_u32(p_phy->dev->of_node, "img,refclk",
164 &p_phy->refclk);
165 if (ret < 0) {
166 dev_err(p_phy->dev, "No reference clock selector specified\n");
167 return ret;
168 }
169
170 phy = devm_phy_create(p_phy->dev, NULL, &pistachio_usb_phy_ops);
171 if (IS_ERR(phy)) {
172 dev_err(p_phy->dev, "Failed to create PHY: %ld\n",
173 PTR_ERR(phy));
174 return PTR_ERR(phy);
175 }
176 phy_set_drvdata(phy, p_phy);
177
178 provider = devm_of_phy_provider_register(p_phy->dev,
179 of_phy_simple_xlate);
180 if (IS_ERR(provider)) {
181 dev_err(p_phy->dev, "Failed to register PHY provider: %ld\n",
182 PTR_ERR(provider));
183 return PTR_ERR(provider);
184 }
185
186 return 0;
187}
188
189static const struct of_device_id pistachio_usb_phy_of_match[] = {
190 { .compatible = "img,pistachio-usb-phy", },
191 { },
192};
193MODULE_DEVICE_TABLE(of, pistachio_usb_phy_of_match);
194
195static struct platform_driver pistachio_usb_phy_driver = {
196 .probe = pistachio_usb_phy_probe,
197 .driver = {
198 .name = "pistachio-usb-phy",
199 .of_match_table = pistachio_usb_phy_of_match,
200 },
201};
202module_platform_driver(pistachio_usb_phy_driver);
203
204MODULE_AUTHOR("Andrew Bresticker <abrestic@chromium.org>");
205MODULE_DESCRIPTION("IMG Pistachio USB2.0 PHY driver");
206MODULE_LICENSE("GPL v2");