summaryrefslogtreecommitdiffstats
path: root/drivers/phy
diff options
context:
space:
mode:
authorAntoine Ténart <antoine.tenart@free-electrons.com>2014-07-07 06:16:07 -0400
committerKishon Vijay Abraham I <kishon@ti.com>2014-07-22 03:16:11 -0400
commit942a31b521911b0f162a37844c29f91022d129f0 (patch)
treebdaab986d9beb01f0432ef764c463d0723556403 /drivers/phy
parent74d64b59b49b322c8bbc73cdaba37348faf59582 (diff)
phy: add a driver for the Berlin SATA PHY
The Berlin SoC has a two SATA ports. Add a PHY driver to handle them. The mode selection can let us think this PHY can be configured to fit other purposes. But there are reasons to think the SATA mode will be the only one usable: the PHY registers are only accessible indirectly through two registers in the SATA range, the PHY seems to be integrated and no information tells us the contrary. For these reasons, make the driver a SATA PHY driver. Signed-off-by: Antoine Ténart <antoine.tenart@free-electrons.com> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Diffstat (limited to 'drivers/phy')
-rw-r--r--drivers/phy/Kconfig7
-rw-r--r--drivers/phy/Makefile1
-rw-r--r--drivers/phy/phy-berlin-sata.c284
3 files changed, 292 insertions, 0 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 261ad18854c1..1704fd4be99e 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -15,6 +15,13 @@ config GENERIC_PHY
15 phy users can obtain reference to the PHY. All the users of this 15 phy users can obtain reference to the PHY. All the users of this
16 framework should select this config. 16 framework should select this config.
17 17
18config PHY_BERLIN_SATA
19 tristate "Marvell Berlin SATA PHY driver"
20 depends on ARCH_BERLIN && HAS_IOMEM && OF
21 select GENERIC_PHY
22 help
23 Enable this to support the SATA PHY on Marvell Berlin SoCs.
24
18config PHY_EXYNOS_MIPI_VIDEO 25config PHY_EXYNOS_MIPI_VIDEO
19 tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver" 26 tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
20 depends on HAS_IOMEM 27 depends on HAS_IOMEM
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index a4819d378619..3c2ad5915be2 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -3,6 +3,7 @@
3# 3#
4 4
5obj-$(CONFIG_GENERIC_PHY) += phy-core.o 5obj-$(CONFIG_GENERIC_PHY) += phy-core.o
6obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o
6obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o 7obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o
7obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o 8obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o
8obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o 9obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
diff --git a/drivers/phy/phy-berlin-sata.c b/drivers/phy/phy-berlin-sata.c
new file mode 100644
index 000000000000..c5e688b0899f
--- /dev/null
+++ b/drivers/phy/phy-berlin-sata.c
@@ -0,0 +1,284 @@
1/*
2 * Marvell Berlin SATA PHY driver
3 *
4 * Copyright (C) 2014 Marvell Technology Group Ltd.
5 *
6 * Antoine Ténart <antoine.tenart@free-electrons.com>
7 *
8 * This file is licensed under the terms of the GNU General Public
9 * License version 2. This program is licensed "as is" without any
10 * warranty of any kind, whether express or implied.
11 */
12
13#include <linux/clk.h>
14#include <linux/module.h>
15#include <linux/phy/phy.h>
16#include <linux/io.h>
17#include <linux/platform_device.h>
18
19#define HOST_VSA_ADDR 0x0
20#define HOST_VSA_DATA 0x4
21#define PORT_SCR_CTL 0x2c
22#define PORT_VSR_ADDR 0x78
23#define PORT_VSR_DATA 0x7c
24
25#define CONTROL_REGISTER 0x0
26#define MBUS_SIZE_CONTROL 0x4
27
28#define POWER_DOWN_PHY0 BIT(6)
29#define POWER_DOWN_PHY1 BIT(14)
30#define MBUS_WRITE_REQUEST_SIZE_128 (BIT(2) << 16)
31#define MBUS_READ_REQUEST_SIZE_128 (BIT(2) << 19)
32
33#define PHY_BASE 0x200
34
35/* register 0x01 */
36#define REF_FREF_SEL_25 BIT(0)
37#define PHY_MODE_SATA (0x0 << 5)
38
39/* register 0x02 */
40#define USE_MAX_PLL_RATE BIT(12)
41
42/* register 0x23 */
43#define DATA_BIT_WIDTH_10 (0x0 << 10)
44#define DATA_BIT_WIDTH_20 (0x1 << 10)
45#define DATA_BIT_WIDTH_40 (0x2 << 10)
46
47/* register 0x25 */
48#define PHY_GEN_MAX_1_5 (0x0 << 10)
49#define PHY_GEN_MAX_3_0 (0x1 << 10)
50#define PHY_GEN_MAX_6_0 (0x2 << 10)
51
52struct phy_berlin_desc {
53 struct phy *phy;
54 u32 power_bit;
55 unsigned index;
56};
57
58struct phy_berlin_priv {
59 void __iomem *base;
60 spinlock_t lock;
61 struct clk *clk;
62 struct phy_berlin_desc **phys;
63 unsigned nphys;
64};
65
66static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg, u32 reg,
67 u32 mask, u32 val)
68{
69 u32 regval;
70
71 /* select register */
72 writel(PHY_BASE + reg, ctrl_reg + PORT_VSR_ADDR);
73
74 /* set bits */
75 regval = readl(ctrl_reg + PORT_VSR_DATA);
76 regval &= ~mask;
77 regval |= val;
78 writel(regval, ctrl_reg + PORT_VSR_DATA);
79}
80
81static int phy_berlin_sata_power_on(struct phy *phy)
82{
83 struct phy_berlin_desc *desc = phy_get_drvdata(phy);
84 struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent);
85 void __iomem *ctrl_reg = priv->base + 0x60 + (desc->index * 0x80);
86 int ret = 0;
87 u32 regval;
88
89 clk_prepare_enable(priv->clk);
90
91 spin_lock(&priv->lock);
92
93 /* Power on PHY */
94 writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR);
95 regval = readl(priv->base + HOST_VSA_DATA);
96 regval &= ~desc->power_bit;
97 writel(regval, priv->base + HOST_VSA_DATA);
98
99 /* Configure MBus */
100 writel(MBUS_SIZE_CONTROL, priv->base + HOST_VSA_ADDR);
101 regval = readl(priv->base + HOST_VSA_DATA);
102 regval |= MBUS_WRITE_REQUEST_SIZE_128 | MBUS_READ_REQUEST_SIZE_128;
103 writel(regval, priv->base + HOST_VSA_DATA);
104
105 /* set PHY mode and ref freq to 25 MHz */
106 phy_berlin_sata_reg_setbits(ctrl_reg, 0x1, 0xff,
107 REF_FREF_SEL_25 | PHY_MODE_SATA);
108
109 /* set PHY up to 6 Gbps */
110 phy_berlin_sata_reg_setbits(ctrl_reg, 0x25, 0xc00, PHY_GEN_MAX_6_0);
111
112 /* set 40 bits width */
113 phy_berlin_sata_reg_setbits(ctrl_reg, 0x23, 0xc00, DATA_BIT_WIDTH_40);
114
115 /* use max pll rate */
116 phy_berlin_sata_reg_setbits(ctrl_reg, 0x2, 0x0, USE_MAX_PLL_RATE);
117
118 /* set Gen3 controller speed */
119 regval = readl(ctrl_reg + PORT_SCR_CTL);
120 regval &= ~GENMASK(7, 4);
121 regval |= 0x30;
122 writel(regval, ctrl_reg + PORT_SCR_CTL);
123
124 spin_unlock(&priv->lock);
125
126 clk_disable_unprepare(priv->clk);
127
128 return ret;
129}
130
131static int phy_berlin_sata_power_off(struct phy *phy)
132{
133 struct phy_berlin_desc *desc = phy_get_drvdata(phy);
134 struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent);
135 u32 regval;
136
137 clk_prepare_enable(priv->clk);
138
139 spin_lock(&priv->lock);
140
141 /* Power down PHY */
142 writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR);
143 regval = readl(priv->base + HOST_VSA_DATA);
144 regval |= desc->power_bit;
145 writel(regval, priv->base + HOST_VSA_DATA);
146
147 spin_unlock(&priv->lock);
148
149 clk_disable_unprepare(priv->clk);
150
151 return 0;
152}
153
154static struct phy *phy_berlin_sata_phy_xlate(struct device *dev,
155 struct of_phandle_args *args)
156{
157 struct phy_berlin_priv *priv = dev_get_drvdata(dev);
158 int i;
159
160 if (WARN_ON(args->args[0] >= priv->nphys))
161 return ERR_PTR(-ENODEV);
162
163 for (i = 0; i < priv->nphys; i++) {
164 if (priv->phys[i]->index == args->args[0])
165 break;
166 }
167
168 if (i == priv->nphys)
169 return ERR_PTR(-ENODEV);
170
171 return priv->phys[i]->phy;
172}
173
174static struct phy_ops phy_berlin_sata_ops = {
175 .power_on = phy_berlin_sata_power_on,
176 .power_off = phy_berlin_sata_power_off,
177 .owner = THIS_MODULE,
178};
179
180static u32 phy_berlin_power_down_bits[] = {
181 POWER_DOWN_PHY0,
182 POWER_DOWN_PHY1,
183};
184
185static int phy_berlin_sata_probe(struct platform_device *pdev)
186{
187 struct device *dev = &pdev->dev;
188 struct device_node *child;
189 struct phy *phy;
190 struct phy_provider *phy_provider;
191 struct phy_berlin_priv *priv;
192 struct resource *res;
193 int i = 0;
194 u32 phy_id;
195
196 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
197 if (!priv)
198 return -ENOMEM;
199
200 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
201 if (!res)
202 return -EINVAL;
203
204 priv->base = devm_ioremap(dev, res->start, resource_size(res));
205 if (!priv->base)
206 return -ENOMEM;
207
208 priv->clk = devm_clk_get(dev, NULL);
209 if (IS_ERR(priv->clk))
210 return PTR_ERR(priv->clk);
211
212 priv->nphys = of_get_child_count(dev->of_node);
213 if (priv->nphys == 0)
214 return -ENODEV;
215
216 priv->phys = devm_kzalloc(dev, priv->nphys * sizeof(*priv->phys),
217 GFP_KERNEL);
218 if (!priv->phys)
219 return -ENOMEM;
220
221 dev_set_drvdata(dev, priv);
222 spin_lock_init(&priv->lock);
223
224 for_each_available_child_of_node(dev->of_node, child) {
225 struct phy_berlin_desc *phy_desc;
226
227 if (of_property_read_u32(child, "reg", &phy_id)) {
228 dev_err(dev, "missing reg property in node %s\n",
229 child->name);
230 return -EINVAL;
231 }
232
233 if (phy_id >= ARRAY_SIZE(phy_berlin_power_down_bits)) {
234 dev_err(dev, "invalid reg in node %s\n", child->name);
235 return -EINVAL;
236 }
237
238 phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL);
239 if (!phy_desc)
240 return -ENOMEM;
241
242 phy = devm_phy_create(dev, &phy_berlin_sata_ops, NULL);
243 if (IS_ERR(phy)) {
244 dev_err(dev, "failed to create PHY %d\n", phy_id);
245 return PTR_ERR(phy);
246 }
247
248 phy_desc->phy = phy;
249 phy_desc->power_bit = phy_berlin_power_down_bits[phy_id];
250 phy_desc->index = phy_id;
251 phy_set_drvdata(phy, phy_desc);
252
253 priv->phys[i++] = phy_desc;
254
255 /* Make sure the PHY is off */
256 phy_berlin_sata_power_off(phy);
257 }
258
259 phy_provider =
260 devm_of_phy_provider_register(dev, phy_berlin_sata_phy_xlate);
261 if (IS_ERR(phy_provider))
262 return PTR_ERR(phy_provider);
263
264 return 0;
265}
266
267static const struct of_device_id phy_berlin_sata_of_match[] = {
268 { .compatible = "marvell,berlin2q-sata-phy" },
269 { },
270};
271
272static struct platform_driver phy_berlin_sata_driver = {
273 .probe = phy_berlin_sata_probe,
274 .driver = {
275 .name = "phy-berlin-sata",
276 .owner = THIS_MODULE,
277 .of_match_table = phy_berlin_sata_of_match,
278 },
279};
280module_platform_driver(phy_berlin_sata_driver);
281
282MODULE_DESCRIPTION("Marvell Berlin SATA PHY driver");
283MODULE_AUTHOR("Antoine Ténart <antoine.tenart@free-electrons.com>");
284MODULE_LICENSE("GPL v2");