diff options
author | Rob Herring <robh@kernel.org> | 2015-05-29 12:38:43 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-06-09 15:20:30 -0400 |
commit | 10d9029bcb2e27a812da81d3e9b598e307be4fe1 (patch) | |
tree | d344fbb58c771e78bee3e09b4c162be3b38702f7 /drivers/phy | |
parent | 603c5f9d9c52ff5744b7f35ead1891478fb6a569 (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/Kconfig | 10 | ||||
-rw-r--r-- | drivers/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/phy/phy-pxa-28nm-hsic.c | 220 |
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 | ||
57 | config 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 | |||
57 | config PHY_PXA_28NM_USB2 | 67 | config 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 | |||
11 | obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o | 11 | obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o |
12 | obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o | 12 | obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o |
13 | obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o | 13 | obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o |
14 | obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o | ||
14 | obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o | 15 | obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o |
15 | obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o | 16 | obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o |
16 | obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o | 17 | obj-$(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 | |||
49 | struct mv_hsic_phy { | ||
50 | struct phy *phy; | ||
51 | struct platform_device *pdev; | ||
52 | void __iomem *base; | ||
53 | struct clk *clk; | ||
54 | }; | ||
55 | |||
56 | static 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 | |||
67 | static 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 | |||
97 | static 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 | |||
136 | static 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 | |||
147 | static 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 | |||
162 | static 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 | |||
170 | static 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 | |||
203 | static const struct of_device_id mv_hsic_phy_dt_match[] = { | ||
204 | { .compatible = "marvell,pxa1928-hsic-phy", }, | ||
205 | {}, | ||
206 | }; | ||
207 | MODULE_DEVICE_TABLE(of, mv_hsic_phy_dt_match); | ||
208 | |||
209 | static 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 | }; | ||
216 | module_platform_driver(mv_hsic_phy_driver); | ||
217 | |||
218 | MODULE_AUTHOR("Rob Herring <robh@kernel.org>"); | ||
219 | MODULE_DESCRIPTION("Marvell HSIC phy driver"); | ||
220 | MODULE_LICENSE("GPL v2"); | ||