aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/phy
diff options
context:
space:
mode:
authorPeter Griffin <peter.griffin@linaro.org>2014-09-08 06:33:00 -0400
committerKishon Vijay Abraham I <kishon@ti.com>2014-09-24 05:48:33 -0400
commit3f8da2e36c04577a39dc210255f53bdc9e4ca0e4 (patch)
tree8c6b7c6a0d7fa8ab691f4c4770b40d381dd58d86 /drivers/phy
parent6da969a5fe9768f4735480c91e4885cf9babf023 (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/Kconfig8
-rw-r--r--drivers/phy/Makefile1
-rw-r--r--drivers/phy/phy-stih41x-usb.c187
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
251config 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
251endmenu 259endmenu
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
30obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o 30obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o
31obj-$(CONFIG_PHY_XGENE) += phy-xgene.o 31obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
32obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o 32obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o
33obj-$(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 */
36struct 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 */
50struct stih41x_usb_phy {
51 struct device *dev;
52 struct regmap *regmap;
53 const struct stih41x_usb_cfg *cfg;
54 struct clk *clk;
55};
56
57static struct stih41x_usb_cfg stih415_usb_phy_cfg = {
58 .syscfg = SYSCFG332,
59 .cfg_mask = 0x3f,
60 .cfg = 0x38,
61 .oscok = BIT(6),
62};
63
64static struct stih41x_usb_cfg stih416_usb_phy_cfg = {
65 .syscfg = SYSCFG2520,
66 .cfg_mask = 0x33f,
67 .cfg = 0x238,
68 .oscok = BIT(6),
69};
70
71static 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
79static 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
94static 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
111static 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
118static const struct of_device_id stih41x_usb_phy_of_match[];
119
120static 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
169static 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};
174MODULE_DEVICE_TABLE(of, stih41x_usb_phy_of_match);
175
176static 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};
183module_platform_driver(stih41x_usb_phy_driver);
184
185MODULE_AUTHOR("Maxime Coquelin <maxime.coquelin@st.com>");
186MODULE_DESCRIPTION("STMicroelectronics USB PHY driver for STiH41x series");
187MODULE_LICENSE("GPL v2");