diff options
author | Peter Griffin <peter.griffin@linaro.org> | 2014-09-08 06:33:00 -0400 |
---|---|---|
committer | Kishon Vijay Abraham I <kishon@ti.com> | 2014-09-24 05:48:33 -0400 |
commit | 3f8da2e36c04577a39dc210255f53bdc9e4ca0e4 (patch) | |
tree | 8c6b7c6a0d7fa8ab691f4c4770b40d381dd58d86 /drivers/phy | |
parent | 6da969a5fe9768f4735480c91e4885cf9babf023 (diff) |
phy: phy-stih41x-usb: Add usb phy support for STiH41x SoCs.
This driver adds support for USB (1.1 and 2.0) phy for STiH415 and
STiH416 System-On-Chips from STMicroelectronics.
Signed-off-by: Maxime Coquelin <maxime.coquelin@st.com>
Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Diffstat (limited to 'drivers/phy')
-rw-r--r-- | drivers/phy/Kconfig | 8 | ||||
-rw-r--r-- | drivers/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/phy/phy-stih41x-usb.c | 187 |
3 files changed, 196 insertions, 0 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 11c66d7ae252..2a436e607f99 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig | |||
@@ -248,4 +248,12 @@ config PHY_STIH407_USB | |||
248 | Enable this support to enable the picoPHY device used by USB2 | 248 | Enable this support to enable the picoPHY device used by USB2 |
249 | and USB3 controllers on STMicroelectronics STiH407 SoC families. | 249 | and USB3 controllers on STMicroelectronics STiH407 SoC families. |
250 | 250 | ||
251 | config PHY_STIH41X_USB | ||
252 | tristate "STMicroelectronics USB2 PHY driver for STiH41x series" | ||
253 | depends on ARCH_STI | ||
254 | select GENERIC_PHY | ||
255 | help | ||
256 | Enable this to support the USB transceiver that is part of | ||
257 | STMicroelectronics STiH41x SoC series. | ||
258 | |||
251 | endmenu | 259 | endmenu |
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index a8d1e114a8af..c4590fce082f 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile | |||
@@ -30,3 +30,4 @@ obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o | |||
30 | obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o | 30 | obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o |
31 | obj-$(CONFIG_PHY_XGENE) += phy-xgene.o | 31 | obj-$(CONFIG_PHY_XGENE) += phy-xgene.o |
32 | obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o | 32 | obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o |
33 | obj-$(CONFIG_PHY_STIH41X_USB) += phy-stih41x-usb.o | ||
diff --git a/drivers/phy/phy-stih41x-usb.c b/drivers/phy/phy-stih41x-usb.c new file mode 100644 index 000000000000..9f16cb8e01f4 --- /dev/null +++ b/drivers/phy/phy-stih41x-usb.c | |||
@@ -0,0 +1,187 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014 STMicroelectronics | ||
3 | * | ||
4 | * STMicroelectronics PHY driver for STiH41x USB. | ||
5 | * | ||
6 | * Author: Maxime Coquelin <maxime.coquelin@st.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2, as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/io.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/of.h> | ||
19 | #include <linux/of_platform.h> | ||
20 | #include <linux/clk.h> | ||
21 | #include <linux/phy/phy.h> | ||
22 | #include <linux/regmap.h> | ||
23 | #include <linux/mfd/syscon.h> | ||
24 | |||
25 | #define SYSCFG332 0x80 | ||
26 | #define SYSCFG2520 0x820 | ||
27 | |||
28 | /** | ||
29 | * struct stih41x_usb_cfg - SoC specific PHY register mapping | ||
30 | * @syscfg: Offset in syscfg registers bank | ||
31 | * @cfg_mask: Bits mask for PHY configuration | ||
32 | * @cfg: Static configuration value for PHY | ||
33 | * @oscok: Notify the PHY oscillator clock is ready | ||
34 | * Setting this bit enable the PHY | ||
35 | */ | ||
36 | struct stih41x_usb_cfg { | ||
37 | u32 syscfg; | ||
38 | u32 cfg_mask; | ||
39 | u32 cfg; | ||
40 | u32 oscok; | ||
41 | }; | ||
42 | |||
43 | /** | ||
44 | * struct stih41x_usb_phy - Private data for the PHY | ||
45 | * @dev: device for this controller | ||
46 | * @regmap: Syscfg registers bank in which PHY is configured | ||
47 | * @cfg: SoC specific PHY register mapping | ||
48 | * @clk: Oscillator used by the PHY | ||
49 | */ | ||
50 | struct stih41x_usb_phy { | ||
51 | struct device *dev; | ||
52 | struct regmap *regmap; | ||
53 | const struct stih41x_usb_cfg *cfg; | ||
54 | struct clk *clk; | ||
55 | }; | ||
56 | |||
57 | static struct stih41x_usb_cfg stih415_usb_phy_cfg = { | ||
58 | .syscfg = SYSCFG332, | ||
59 | .cfg_mask = 0x3f, | ||
60 | .cfg = 0x38, | ||
61 | .oscok = BIT(6), | ||
62 | }; | ||
63 | |||
64 | static struct stih41x_usb_cfg stih416_usb_phy_cfg = { | ||
65 | .syscfg = SYSCFG2520, | ||
66 | .cfg_mask = 0x33f, | ||
67 | .cfg = 0x238, | ||
68 | .oscok = BIT(6), | ||
69 | }; | ||
70 | |||
71 | static int stih41x_usb_phy_init(struct phy *phy) | ||
72 | { | ||
73 | struct stih41x_usb_phy *phy_dev = phy_get_drvdata(phy); | ||
74 | |||
75 | return regmap_update_bits(phy_dev->regmap, phy_dev->cfg->syscfg, | ||
76 | phy_dev->cfg->cfg_mask, phy_dev->cfg->cfg); | ||
77 | } | ||
78 | |||
79 | static int stih41x_usb_phy_power_on(struct phy *phy) | ||
80 | { | ||
81 | struct stih41x_usb_phy *phy_dev = phy_get_drvdata(phy); | ||
82 | int ret; | ||
83 | |||
84 | ret = clk_prepare_enable(phy_dev->clk); | ||
85 | if (ret) { | ||
86 | dev_err(phy_dev->dev, "Failed to enable osc_phy clock\n"); | ||
87 | return ret; | ||
88 | } | ||
89 | |||
90 | return regmap_update_bits(phy_dev->regmap, phy_dev->cfg->syscfg, | ||
91 | phy_dev->cfg->oscok, phy_dev->cfg->oscok); | ||
92 | } | ||
93 | |||
94 | static int stih41x_usb_phy_power_off(struct phy *phy) | ||
95 | { | ||
96 | struct stih41x_usb_phy *phy_dev = phy_get_drvdata(phy); | ||
97 | int ret; | ||
98 | |||
99 | ret = regmap_update_bits(phy_dev->regmap, phy_dev->cfg->syscfg, | ||
100 | phy_dev->cfg->oscok, 0); | ||
101 | if (ret) { | ||
102 | dev_err(phy_dev->dev, "Failed to clear oscok bit\n"); | ||
103 | return ret; | ||
104 | } | ||
105 | |||
106 | clk_disable_unprepare(phy_dev->clk); | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | static struct phy_ops stih41x_usb_phy_ops = { | ||
112 | .init = stih41x_usb_phy_init, | ||
113 | .power_on = stih41x_usb_phy_power_on, | ||
114 | .power_off = stih41x_usb_phy_power_off, | ||
115 | .owner = THIS_MODULE, | ||
116 | }; | ||
117 | |||
118 | static const struct of_device_id stih41x_usb_phy_of_match[]; | ||
119 | |||
120 | static int stih41x_usb_phy_probe(struct platform_device *pdev) | ||
121 | { | ||
122 | struct device_node *np = pdev->dev.of_node; | ||
123 | const struct of_device_id *match; | ||
124 | struct stih41x_usb_phy *phy_dev; | ||
125 | struct device *dev = &pdev->dev; | ||
126 | struct phy_provider *phy_provider; | ||
127 | struct phy *phy; | ||
128 | |||
129 | phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL); | ||
130 | if (!phy_dev) | ||
131 | return -ENOMEM; | ||
132 | |||
133 | match = of_match_device(stih41x_usb_phy_of_match, &pdev->dev); | ||
134 | if (!match) | ||
135 | return -ENODEV; | ||
136 | |||
137 | phy_dev->cfg = match->data; | ||
138 | |||
139 | phy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); | ||
140 | if (IS_ERR(phy_dev->regmap)) { | ||
141 | dev_err(dev, "No syscfg phandle specified\n"); | ||
142 | return PTR_ERR(phy_dev->regmap); | ||
143 | } | ||
144 | |||
145 | phy_dev->clk = devm_clk_get(dev, "osc_phy"); | ||
146 | if (IS_ERR(phy_dev->clk)) { | ||
147 | dev_err(dev, "osc_phy clk not found\n"); | ||
148 | return PTR_ERR(phy_dev->clk); | ||
149 | } | ||
150 | |||
151 | phy = devm_phy_create(dev, NULL, &stih41x_usb_phy_ops, NULL); | ||
152 | |||
153 | if (IS_ERR(phy)) { | ||
154 | dev_err(dev, "failed to create phy\n"); | ||
155 | return PTR_ERR(phy); | ||
156 | } | ||
157 | |||
158 | phy_dev->dev = dev; | ||
159 | |||
160 | phy_set_drvdata(phy, phy_dev); | ||
161 | |||
162 | phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); | ||
163 | if (IS_ERR(phy_provider)) | ||
164 | return PTR_ERR(phy_provider); | ||
165 | |||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | static const struct of_device_id stih41x_usb_phy_of_match[] = { | ||
170 | { .compatible = "st,stih415-usb-phy", .data = &stih415_usb_phy_cfg }, | ||
171 | { .compatible = "st,stih416-usb-phy", .data = &stih416_usb_phy_cfg }, | ||
172 | { /* sentinel */ }, | ||
173 | }; | ||
174 | MODULE_DEVICE_TABLE(of, stih41x_usb_phy_of_match); | ||
175 | |||
176 | static struct platform_driver stih41x_usb_phy_driver = { | ||
177 | .probe = stih41x_usb_phy_probe, | ||
178 | .driver = { | ||
179 | .name = "stih41x-usb-phy", | ||
180 | .of_match_table = stih41x_usb_phy_of_match, | ||
181 | } | ||
182 | }; | ||
183 | module_platform_driver(stih41x_usb_phy_driver); | ||
184 | |||
185 | MODULE_AUTHOR("Maxime Coquelin <maxime.coquelin@st.com>"); | ||
186 | MODULE_DESCRIPTION("STMicroelectronics USB PHY driver for STiH41x series"); | ||
187 | MODULE_LICENSE("GPL v2"); | ||