aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYuvaraj Kumar C D <yuvaraj.cd@gmail.com>2014-03-03 00:22:39 -0500
committerKishon Vijay Abraham I <kishon@ti.com>2014-03-03 08:20:09 -0500
commitbcff4cba41bcd7dfe535a2b7278b9fb8214a2a8f (patch)
tree879129d4ad195b2caa37d33b956248f5d8b10883
parenta50ce20ecabdb9310fde90af31a8adb79cf0805e (diff)
PHY: Exynos: Add Exynos5250 SATA PHY driver
This patch adds the SATA PHY driver for Exynos5250.This driver uses the generic PHY framework to deal with SATA PHY.Exynos5250 SATA PHY comprises of CMU and TRSV blocks which are of I2C register Map.So this driver configures the CMU and TRSV block of exynos5250 SATA PHY using i2c. Signed-off-by: Yuvaraj Kumar C D <yuvaraj.cd@samsung.com> Signed-off-by: Girish K S <ks.giri@samsung.com> Signed-off-by: Vasanth Ananthan <vasanth.a@samsung.com> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
-rw-r--r--drivers/phy/Kconfig15
-rw-r--r--drivers/phy/Makefile1
-rw-r--r--drivers/phy/phy-exynos5250-sata.c251
3 files changed, 267 insertions, 0 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 5bf9ed33e730..04312849501d 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -70,4 +70,19 @@ config BCM_KONA_USB2_PHY
70 help 70 help
71 Enable this to support the Broadcom Kona USB 2.0 PHY. 71 Enable this to support the Broadcom Kona USB 2.0 PHY.
72 72
73config PHY_EXYNOS5250_SATA
74 tristate "Exynos5250 Sata SerDes/PHY driver"
75 depends on SOC_EXYNOS5250
76 depends on HAS_IOMEM
77 depends on OF
78 select GENERIC_PHY
79 select I2C
80 select I2C_S3C2410
81 select MFD_SYSCON
82 help
83 Enable this to support SATA SerDes/Phy found on Samsung's
84 Exynos5250 based SoCs.This SerDes/Phy supports SATA 1.5 Gb/s,
85 SATA 3.0 Gb/s, SATA 6.0 Gb/s speeds. It supports one SATA host
86 port to accept one SATA device.
87
73endmenu 88endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index b57c25371cca..0d038224a102 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
9obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o 9obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
10obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o 10obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
11obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o 11obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
12obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o
diff --git a/drivers/phy/phy-exynos5250-sata.c b/drivers/phy/phy-exynos5250-sata.c
new file mode 100644
index 000000000000..c9361b731fa6
--- /dev/null
+++ b/drivers/phy/phy-exynos5250-sata.c
@@ -0,0 +1,251 @@
1/*
2 * Samsung SATA SerDes(PHY) driver
3 *
4 * Copyright (C) 2013 Samsung Electronics Co., Ltd.
5 * Authors: Girish K S <ks.giri@samsung.com>
6 * Yuvaraj Kumar C D <yuvaraj.cd@samsung.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#include <linux/clk.h>
14#include <linux/delay.h>
15#include <linux/io.h>
16#include <linux/i2c.h>
17#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/of.h>
20#include <linux/of_address.h>
21#include <linux/phy/phy.h>
22#include <linux/platform_device.h>
23#include <linux/regmap.h>
24#include <linux/spinlock.h>
25#include <linux/mfd/syscon.h>
26
27#define SATAPHY_CONTROL_OFFSET 0x0724
28#define EXYNOS5_SATAPHY_PMU_ENABLE BIT(0)
29#define EXYNOS5_SATA_RESET 0x4
30#define RESET_GLOBAL_RST_N BIT(0)
31#define RESET_CMN_RST_N BIT(1)
32#define RESET_CMN_BLOCK_RST_N BIT(2)
33#define RESET_CMN_I2C_RST_N BIT(3)
34#define RESET_TX_RX_PIPE_RST_N BIT(4)
35#define RESET_TX_RX_BLOCK_RST_N BIT(5)
36#define RESET_TX_RX_I2C_RST_N (BIT(6) | BIT(7))
37#define LINK_RESET 0xf0000
38#define EXYNOS5_SATA_MODE0 0x10
39#define SATA_SPD_GEN3 BIT(1)
40#define EXYNOS5_SATA_CTRL0 0x14
41#define CTRL0_P0_PHY_CALIBRATED_SEL BIT(9)
42#define CTRL0_P0_PHY_CALIBRATED BIT(8)
43#define EXYNOS5_SATA_PHSATA_CTRLM 0xe0
44#define PHCTRLM_REF_RATE BIT(1)
45#define PHCTRLM_HIGH_SPEED BIT(0)
46#define EXYNOS5_SATA_PHSATA_STATM 0xf0
47#define PHSTATM_PLL_LOCKED BIT(0)
48
49#define PHY_PLL_TIMEOUT (usecs_to_jiffies(1000))
50
51struct exynos_sata_phy {
52 struct phy *phy;
53 struct clk *phyclk;
54 void __iomem *regs;
55 struct regmap *pmureg;
56 struct i2c_client *client;
57};
58
59static int wait_for_reg_status(void __iomem *base, u32 reg, u32 checkbit,
60 u32 status)
61{
62 unsigned long timeout = jiffies + PHY_PLL_TIMEOUT;
63
64 while (time_before(jiffies, timeout)) {
65 if ((readl(base + reg) & checkbit) == status)
66 return 0;
67 }
68
69 return -EFAULT;
70}
71
72static int exynos_sata_phy_power_on(struct phy *phy)
73{
74 struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
75
76 return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
77 EXYNOS5_SATAPHY_PMU_ENABLE, true);
78
79}
80
81static int exynos_sata_phy_power_off(struct phy *phy)
82{
83 struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
84
85 return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
86 EXYNOS5_SATAPHY_PMU_ENABLE, false);
87
88}
89
90static int exynos_sata_phy_init(struct phy *phy)
91{
92 u32 val = 0;
93 int ret = 0;
94 u8 buf[] = { 0x3a, 0x0b };
95 struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
96
97 ret = regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
98 EXYNOS5_SATAPHY_PMU_ENABLE, true);
99 if (ret != 0)
100 dev_err(&sata_phy->phy->dev, "phy init failed\n");
101
102 writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
103
104 val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
105 val |= RESET_GLOBAL_RST_N | RESET_CMN_RST_N | RESET_CMN_BLOCK_RST_N
106 | RESET_CMN_I2C_RST_N | RESET_TX_RX_PIPE_RST_N
107 | RESET_TX_RX_BLOCK_RST_N | RESET_TX_RX_I2C_RST_N;
108 writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
109
110 val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
111 val |= LINK_RESET;
112 writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
113
114 val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
115 val |= RESET_CMN_RST_N;
116 writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
117
118 val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
119 val &= ~PHCTRLM_REF_RATE;
120 writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
121
122 /* High speed enable for Gen3 */
123 val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
124 val |= PHCTRLM_HIGH_SPEED;
125 writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
126
127 val = readl(sata_phy->regs + EXYNOS5_SATA_CTRL0);
128 val |= CTRL0_P0_PHY_CALIBRATED_SEL | CTRL0_P0_PHY_CALIBRATED;
129 writel(val, sata_phy->regs + EXYNOS5_SATA_CTRL0);
130
131 val = readl(sata_phy->regs + EXYNOS5_SATA_MODE0);
132 val |= SATA_SPD_GEN3;
133 writel(val, sata_phy->regs + EXYNOS5_SATA_MODE0);
134
135 ret = i2c_master_send(sata_phy->client, buf, sizeof(buf));
136 if (ret < 0)
137 return ret;
138
139 /* release cmu reset */
140 val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
141 val &= ~RESET_CMN_RST_N;
142 writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
143
144 val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
145 val |= RESET_CMN_RST_N;
146 writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
147
148 ret = wait_for_reg_status(sata_phy->regs,
149 EXYNOS5_SATA_PHSATA_STATM,
150 PHSTATM_PLL_LOCKED, 1);
151 if (ret < 0)
152 dev_err(&sata_phy->phy->dev,
153 "PHY PLL locking failed\n");
154 return ret;
155}
156
157static struct phy_ops exynos_sata_phy_ops = {
158 .init = exynos_sata_phy_init,
159 .power_on = exynos_sata_phy_power_on,
160 .power_off = exynos_sata_phy_power_off,
161 .owner = THIS_MODULE,
162};
163
164static int exynos_sata_phy_probe(struct platform_device *pdev)
165{
166 struct exynos_sata_phy *sata_phy;
167 struct device *dev = &pdev->dev;
168 struct resource *res;
169 struct phy_provider *phy_provider;
170 struct device_node *node;
171 int ret = 0;
172
173 sata_phy = devm_kzalloc(dev, sizeof(*sata_phy), GFP_KERNEL);
174 if (!sata_phy)
175 return -ENOMEM;
176
177 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
178
179 sata_phy->regs = devm_ioremap_resource(dev, res);
180 if (IS_ERR(sata_phy->regs))
181 return PTR_ERR(sata_phy->regs);
182
183 sata_phy->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
184 "samsung,syscon-phandle");
185 if (IS_ERR(sata_phy->pmureg)) {
186 dev_err(dev, "syscon regmap lookup failed.\n");
187 return PTR_ERR(sata_phy->pmureg);
188 }
189
190 node = of_parse_phandle(dev->of_node,
191 "samsung,exynos-sataphy-i2c-phandle", 0);
192 if (!node)
193 return -EINVAL;
194
195 sata_phy->client = of_find_i2c_device_by_node(node);
196 if (!sata_phy->client)
197 return -EPROBE_DEFER;
198
199 dev_set_drvdata(dev, sata_phy);
200
201 sata_phy->phyclk = devm_clk_get(dev, "sata_phyctrl");
202 if (IS_ERR(sata_phy->phyclk)) {
203 dev_err(dev, "failed to get clk for PHY\n");
204 return PTR_ERR(sata_phy->phyclk);
205 }
206
207 ret = clk_prepare_enable(sata_phy->phyclk);
208 if (ret < 0) {
209 dev_err(dev, "failed to enable source clk\n");
210 return ret;
211 }
212
213 sata_phy->phy = devm_phy_create(dev, &exynos_sata_phy_ops, NULL);
214 if (IS_ERR(sata_phy->phy)) {
215 clk_disable_unprepare(sata_phy->phyclk);
216 dev_err(dev, "failed to create PHY\n");
217 return PTR_ERR(sata_phy->phy);
218 }
219
220 phy_set_drvdata(sata_phy->phy, sata_phy);
221
222 phy_provider = devm_of_phy_provider_register(dev,
223 of_phy_simple_xlate);
224 if (IS_ERR(phy_provider)) {
225 clk_disable_unprepare(sata_phy->phyclk);
226 return PTR_ERR(phy_provider);
227 }
228
229 return 0;
230}
231
232static const struct of_device_id exynos_sata_phy_of_match[] = {
233 { .compatible = "samsung,exynos5250-sata-phy" },
234 { },
235};
236MODULE_DEVICE_TABLE(of, exynos_sata_phy_of_match);
237
238static struct platform_driver exynos_sata_phy_driver = {
239 .probe = exynos_sata_phy_probe,
240 .driver = {
241 .of_match_table = exynos_sata_phy_of_match,
242 .name = "samsung,sata-phy",
243 .owner = THIS_MODULE,
244 }
245};
246module_platform_driver(exynos_sata_phy_driver);
247
248MODULE_DESCRIPTION("Samsung SerDes PHY driver");
249MODULE_LICENSE("GPL V2");
250MODULE_AUTHOR("Girish K S <ks.giri@samsung.com>");
251MODULE_AUTHOR("Yuvaraj C D <yuvaraj.cd@samsung.com>");