aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/phy
diff options
context:
space:
mode:
authorRob Herring <robh@kernel.org>2015-05-29 12:38:43 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-06-09 15:20:30 -0400
commit10d9029bcb2e27a812da81d3e9b598e307be4fe1 (patch)
treed344fbb58c771e78bee3e09b4c162be3b38702f7 /drivers/phy
parent603c5f9d9c52ff5744b7f35ead1891478fb6a569 (diff)
phy: add Marvell HSIC 28nm PHY
Add PHY driver for the Marvell HSIC 28nm PHY. This PHY is found in PXA1928 SOC. Signed-off-by: Rob Herring <robh@kernel.org> Cc: Kishon Vijay Abraham I <kishon@ti.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/phy')
-rw-r--r--drivers/phy/Kconfig10
-rw-r--r--drivers/phy/Makefile1
-rw-r--r--drivers/phy/phy-pxa-28nm-hsic.c220
3 files changed, 231 insertions, 0 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index e3785e1ac91e..487d057431cc 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -54,6 +54,16 @@ config PHY_EXYNOS_MIPI_VIDEO
54 Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P 54 Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P
55 and EXYNOS SoCs. 55 and EXYNOS SoCs.
56 56
57config PHY_PXA_28NM_HSIC
58 tristate "Marvell USB HSIC 28nm PHY Driver"
59 select GENERIC_PHY
60 help
61 Enable this to support Marvell USB HSIC PHY driver for Marvell
62 SoC. This driver will do the PHY initialization and shutdown.
63 The PHY driver will be used by Marvell ehci driver.
64
65 To compile this driver as a module, choose M here.
66
57config PHY_PXA_28NM_USB2 67config PHY_PXA_28NM_USB2
58 tristate "Marvell USB 2.0 28nm PHY Driver" 68 tristate "Marvell USB 2.0 28nm PHY Driver"
59 select GENERIC_PHY 69 select GENERIC_PHY
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index f2670df66c07..42f58e95aff0 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o
11obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o 11obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o
12obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o 12obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
13obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o 13obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o
14obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o
14obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o 15obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
15obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o 16obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o
16obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o 17obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o
diff --git a/drivers/phy/phy-pxa-28nm-hsic.c b/drivers/phy/phy-pxa-28nm-hsic.c
new file mode 100644
index 000000000000..234aacf4db20
--- /dev/null
+++ b/drivers/phy/phy-pxa-28nm-hsic.c
@@ -0,0 +1,220 @@
1/*
2 * Copyright (C) 2015 Linaro, Ltd.
3 * Rob Herring <robh@kernel.org>
4 *
5 * Based on vendor driver:
6 * Copyright (C) 2013 Marvell Inc.
7 * Author: Chao Xie <xiechao.mail@gmail.com>
8 *
9 * This software is licensed under the terms of the GNU General Public
10 * License version 2, as published by the Free Software Foundation, and
11 * may be copied, distributed, and modified under those terms.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 */
19
20#include <linux/delay.h>
21#include <linux/slab.h>
22#include <linux/of.h>
23#include <linux/io.h>
24#include <linux/err.h>
25#include <linux/clk.h>
26#include <linux/module.h>
27#include <linux/platform_device.h>
28#include <linux/phy/phy.h>
29
30#define PHY_28NM_HSIC_CTRL 0x08
31#define PHY_28NM_HSIC_IMPCAL_CAL 0x18
32#define PHY_28NM_HSIC_PLL_CTRL01 0x1c
33#define PHY_28NM_HSIC_PLL_CTRL2 0x20
34#define PHY_28NM_HSIC_INT 0x28
35
36#define PHY_28NM_HSIC_PLL_SELLPFR_SHIFT 26
37#define PHY_28NM_HSIC_PLL_FBDIV_SHIFT 0
38#define PHY_28NM_HSIC_PLL_REFDIV_SHIFT 9
39
40#define PHY_28NM_HSIC_S2H_PU_PLL BIT(10)
41#define PHY_28NM_HSIC_H2S_PLL_LOCK BIT(15)
42#define PHY_28NM_HSIC_S2H_HSIC_EN BIT(7)
43#define S2H_DRV_SE0_4RESUME BIT(14)
44#define PHY_28NM_HSIC_H2S_IMPCAL_DONE BIT(27)
45
46#define PHY_28NM_HSIC_CONNECT_INT BIT(1)
47#define PHY_28NM_HSIC_HS_READY_INT BIT(2)
48
49struct mv_hsic_phy {
50 struct phy *phy;
51 struct platform_device *pdev;
52 void __iomem *base;
53 struct clk *clk;
54};
55
56static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout)
57{
58 timeout += jiffies;
59 while (time_is_after_eq_jiffies(timeout)) {
60 if ((readl(reg) & mask) == mask)
61 return true;
62 msleep(1);
63 }
64 return false;
65}
66
67static int mv_hsic_phy_init(struct phy *phy)
68{
69 struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy);
70 struct platform_device *pdev = mv_phy->pdev;
71 void __iomem *base = mv_phy->base;
72
73 clk_prepare_enable(mv_phy->clk);
74
75 /* Set reference clock */
76 writel(0x1 << PHY_28NM_HSIC_PLL_SELLPFR_SHIFT |
77 0xf0 << PHY_28NM_HSIC_PLL_FBDIV_SHIFT |
78 0xd << PHY_28NM_HSIC_PLL_REFDIV_SHIFT,
79 base + PHY_28NM_HSIC_PLL_CTRL01);
80
81 /* Turn on PLL */
82 writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) |
83 PHY_28NM_HSIC_S2H_PU_PLL,
84 base + PHY_28NM_HSIC_PLL_CTRL2);
85
86 /* Make sure PHY PLL is locked */
87 if (!wait_for_reg(base + PHY_28NM_HSIC_PLL_CTRL2,
88 PHY_28NM_HSIC_H2S_PLL_LOCK, HZ / 10)) {
89 dev_err(&pdev->dev, "HSIC PHY PLL not locked after 100mS.");
90 clk_disable_unprepare(mv_phy->clk);
91 return -ETIMEDOUT;
92 }
93
94 return 0;
95}
96
97static int mv_hsic_phy_power_on(struct phy *phy)
98{
99 struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy);
100 struct platform_device *pdev = mv_phy->pdev;
101 void __iomem *base = mv_phy->base;
102 u32 reg;
103
104 reg = readl(base + PHY_28NM_HSIC_CTRL);
105 /* Avoid SE0 state when resume for some device will take it as reset */
106 reg &= ~S2H_DRV_SE0_4RESUME;
107 reg |= PHY_28NM_HSIC_S2H_HSIC_EN; /* Enable HSIC PHY */
108 writel(reg, base + PHY_28NM_HSIC_CTRL);
109
110 /*
111 * Calibration Timing
112 * ____________________________
113 * CAL START ___|
114 * ____________________
115 * CAL_DONE ___________|
116 * | 400us |
117 */
118
119 /* Make sure PHY Calibration is ready */
120 if (!wait_for_reg(base + PHY_28NM_HSIC_IMPCAL_CAL,
121 PHY_28NM_HSIC_H2S_IMPCAL_DONE, HZ / 10)) {
122 dev_warn(&pdev->dev, "HSIC PHY READY not set after 100mS.");
123 return -ETIMEDOUT;
124 }
125
126 /* Waiting for HSIC connect int*/
127 if (!wait_for_reg(base + PHY_28NM_HSIC_INT,
128 PHY_28NM_HSIC_CONNECT_INT, HZ / 5)) {
129 dev_warn(&pdev->dev, "HSIC wait for connect interrupt timeout.");
130 return -ETIMEDOUT;
131 }
132
133 return 0;
134}
135
136static int mv_hsic_phy_power_off(struct phy *phy)
137{
138 struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy);
139 void __iomem *base = mv_phy->base;
140
141 writel(readl(base + PHY_28NM_HSIC_CTRL) & ~PHY_28NM_HSIC_S2H_HSIC_EN,
142 base + PHY_28NM_HSIC_CTRL);
143
144 return 0;
145}
146
147static int mv_hsic_phy_exit(struct phy *phy)
148{
149 struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy);
150 void __iomem *base = mv_phy->base;
151
152 /* Turn off PLL */
153 writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) &
154 ~PHY_28NM_HSIC_S2H_PU_PLL,
155 base + PHY_28NM_HSIC_PLL_CTRL2);
156
157 clk_disable_unprepare(mv_phy->clk);
158 return 0;
159}
160
161
162static const struct phy_ops hsic_ops = {
163 .init = mv_hsic_phy_init,
164 .power_on = mv_hsic_phy_power_on,
165 .power_off = mv_hsic_phy_power_off,
166 .exit = mv_hsic_phy_exit,
167 .owner = THIS_MODULE,
168};
169
170static int mv_hsic_phy_probe(struct platform_device *pdev)
171{
172 struct phy_provider *phy_provider;
173 struct mv_hsic_phy *mv_phy;
174 struct resource *r;
175
176 mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL);
177 if (!mv_phy)
178 return -ENOMEM;
179
180 mv_phy->pdev = pdev;
181
182 mv_phy->clk = devm_clk_get(&pdev->dev, NULL);
183 if (IS_ERR(mv_phy->clk)) {
184 dev_err(&pdev->dev, "failed to get clock.\n");
185 return PTR_ERR(mv_phy->clk);
186 }
187
188 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
189 mv_phy->base = devm_ioremap_resource(&pdev->dev, r);
190 if (IS_ERR(mv_phy->base))
191 return PTR_ERR(mv_phy->base);
192
193 mv_phy->phy = devm_phy_create(&pdev->dev, pdev->dev.of_node, &hsic_ops);
194 if (IS_ERR(mv_phy->phy))
195 return PTR_ERR(mv_phy->phy);
196
197 phy_set_drvdata(mv_phy->phy, mv_phy);
198
199 phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
200 return PTR_ERR_OR_ZERO(phy_provider);
201}
202
203static const struct of_device_id mv_hsic_phy_dt_match[] = {
204 { .compatible = "marvell,pxa1928-hsic-phy", },
205 {},
206};
207MODULE_DEVICE_TABLE(of, mv_hsic_phy_dt_match);
208
209static struct platform_driver mv_hsic_phy_driver = {
210 .probe = mv_hsic_phy_probe,
211 .driver = {
212 .name = "mv-hsic-phy",
213 .of_match_table = of_match_ptr(mv_hsic_phy_dt_match),
214 },
215};
216module_platform_driver(mv_hsic_phy_driver);
217
218MODULE_AUTHOR("Rob Herring <robh@kernel.org>");
219MODULE_DESCRIPTION("Marvell HSIC phy driver");
220MODULE_LICENSE("GPL v2");