1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Samsung SATA SerDes(PHY) driver
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2013 Samsung Electronics Co., Ltd.
6*4882a593Smuzhiyun * Authors: Girish K S <ks.giri@samsung.com>
7*4882a593Smuzhiyun * Yuvaraj Kumar C D <yuvaraj.cd@samsung.com>
8*4882a593Smuzhiyun */
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include <linux/clk.h>
11*4882a593Smuzhiyun #include <linux/delay.h>
12*4882a593Smuzhiyun #include <linux/io.h>
13*4882a593Smuzhiyun #include <linux/i2c.h>
14*4882a593Smuzhiyun #include <linux/kernel.h>
15*4882a593Smuzhiyun #include <linux/module.h>
16*4882a593Smuzhiyun #include <linux/of.h>
17*4882a593Smuzhiyun #include <linux/of_address.h>
18*4882a593Smuzhiyun #include <linux/phy/phy.h>
19*4882a593Smuzhiyun #include <linux/platform_device.h>
20*4882a593Smuzhiyun #include <linux/regmap.h>
21*4882a593Smuzhiyun #include <linux/spinlock.h>
22*4882a593Smuzhiyun #include <linux/mfd/syscon.h>
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun #define SATAPHY_CONTROL_OFFSET 0x0724
25*4882a593Smuzhiyun #define EXYNOS5_SATAPHY_PMU_ENABLE BIT(0)
26*4882a593Smuzhiyun #define EXYNOS5_SATA_RESET 0x4
27*4882a593Smuzhiyun #define RESET_GLOBAL_RST_N BIT(0)
28*4882a593Smuzhiyun #define RESET_CMN_RST_N BIT(1)
29*4882a593Smuzhiyun #define RESET_CMN_BLOCK_RST_N BIT(2)
30*4882a593Smuzhiyun #define RESET_CMN_I2C_RST_N BIT(3)
31*4882a593Smuzhiyun #define RESET_TX_RX_PIPE_RST_N BIT(4)
32*4882a593Smuzhiyun #define RESET_TX_RX_BLOCK_RST_N BIT(5)
33*4882a593Smuzhiyun #define RESET_TX_RX_I2C_RST_N (BIT(6) | BIT(7))
34*4882a593Smuzhiyun #define LINK_RESET 0xf0000
35*4882a593Smuzhiyun #define EXYNOS5_SATA_MODE0 0x10
36*4882a593Smuzhiyun #define SATA_SPD_GEN3 BIT(1)
37*4882a593Smuzhiyun #define EXYNOS5_SATA_CTRL0 0x14
38*4882a593Smuzhiyun #define CTRL0_P0_PHY_CALIBRATED_SEL BIT(9)
39*4882a593Smuzhiyun #define CTRL0_P0_PHY_CALIBRATED BIT(8)
40*4882a593Smuzhiyun #define EXYNOS5_SATA_PHSATA_CTRLM 0xe0
41*4882a593Smuzhiyun #define PHCTRLM_REF_RATE BIT(1)
42*4882a593Smuzhiyun #define PHCTRLM_HIGH_SPEED BIT(0)
43*4882a593Smuzhiyun #define EXYNOS5_SATA_PHSATA_STATM 0xf0
44*4882a593Smuzhiyun #define PHSTATM_PLL_LOCKED BIT(0)
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun #define PHY_PLL_TIMEOUT (usecs_to_jiffies(1000))
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun struct exynos_sata_phy {
49*4882a593Smuzhiyun struct phy *phy;
50*4882a593Smuzhiyun struct clk *phyclk;
51*4882a593Smuzhiyun void __iomem *regs;
52*4882a593Smuzhiyun struct regmap *pmureg;
53*4882a593Smuzhiyun struct i2c_client *client;
54*4882a593Smuzhiyun };
55*4882a593Smuzhiyun
wait_for_reg_status(void __iomem * base,u32 reg,u32 checkbit,u32 status)56*4882a593Smuzhiyun static int wait_for_reg_status(void __iomem *base, u32 reg, u32 checkbit,
57*4882a593Smuzhiyun u32 status)
58*4882a593Smuzhiyun {
59*4882a593Smuzhiyun unsigned long timeout = jiffies + PHY_PLL_TIMEOUT;
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun while (time_before(jiffies, timeout)) {
62*4882a593Smuzhiyun if ((readl(base + reg) & checkbit) == status)
63*4882a593Smuzhiyun return 0;
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun return -EFAULT;
67*4882a593Smuzhiyun }
68*4882a593Smuzhiyun
exynos_sata_phy_power_on(struct phy * phy)69*4882a593Smuzhiyun static int exynos_sata_phy_power_on(struct phy *phy)
70*4882a593Smuzhiyun {
71*4882a593Smuzhiyun struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
74*4882a593Smuzhiyun EXYNOS5_SATAPHY_PMU_ENABLE, true);
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun
exynos_sata_phy_power_off(struct phy * phy)78*4882a593Smuzhiyun static int exynos_sata_phy_power_off(struct phy *phy)
79*4882a593Smuzhiyun {
80*4882a593Smuzhiyun struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
83*4882a593Smuzhiyun EXYNOS5_SATAPHY_PMU_ENABLE, false);
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun
exynos_sata_phy_init(struct phy * phy)87*4882a593Smuzhiyun static int exynos_sata_phy_init(struct phy *phy)
88*4882a593Smuzhiyun {
89*4882a593Smuzhiyun u32 val = 0;
90*4882a593Smuzhiyun int ret = 0;
91*4882a593Smuzhiyun u8 buf[] = { 0x3a, 0x0b };
92*4882a593Smuzhiyun struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun ret = regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
95*4882a593Smuzhiyun EXYNOS5_SATAPHY_PMU_ENABLE, true);
96*4882a593Smuzhiyun if (ret != 0)
97*4882a593Smuzhiyun dev_err(&sata_phy->phy->dev, "phy init failed\n");
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
102*4882a593Smuzhiyun val |= RESET_GLOBAL_RST_N | RESET_CMN_RST_N | RESET_CMN_BLOCK_RST_N
103*4882a593Smuzhiyun | RESET_CMN_I2C_RST_N | RESET_TX_RX_PIPE_RST_N
104*4882a593Smuzhiyun | RESET_TX_RX_BLOCK_RST_N | RESET_TX_RX_I2C_RST_N;
105*4882a593Smuzhiyun writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
108*4882a593Smuzhiyun val |= LINK_RESET;
109*4882a593Smuzhiyun writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
112*4882a593Smuzhiyun val |= RESET_CMN_RST_N;
113*4882a593Smuzhiyun writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
116*4882a593Smuzhiyun val &= ~PHCTRLM_REF_RATE;
117*4882a593Smuzhiyun writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun /* High speed enable for Gen3 */
120*4882a593Smuzhiyun val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
121*4882a593Smuzhiyun val |= PHCTRLM_HIGH_SPEED;
122*4882a593Smuzhiyun writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun val = readl(sata_phy->regs + EXYNOS5_SATA_CTRL0);
125*4882a593Smuzhiyun val |= CTRL0_P0_PHY_CALIBRATED_SEL | CTRL0_P0_PHY_CALIBRATED;
126*4882a593Smuzhiyun writel(val, sata_phy->regs + EXYNOS5_SATA_CTRL0);
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun val = readl(sata_phy->regs + EXYNOS5_SATA_MODE0);
129*4882a593Smuzhiyun val |= SATA_SPD_GEN3;
130*4882a593Smuzhiyun writel(val, sata_phy->regs + EXYNOS5_SATA_MODE0);
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun ret = i2c_master_send(sata_phy->client, buf, sizeof(buf));
133*4882a593Smuzhiyun if (ret < 0)
134*4882a593Smuzhiyun return ret;
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun /* release cmu reset */
137*4882a593Smuzhiyun val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
138*4882a593Smuzhiyun val &= ~RESET_CMN_RST_N;
139*4882a593Smuzhiyun writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
142*4882a593Smuzhiyun val |= RESET_CMN_RST_N;
143*4882a593Smuzhiyun writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun ret = wait_for_reg_status(sata_phy->regs,
146*4882a593Smuzhiyun EXYNOS5_SATA_PHSATA_STATM,
147*4882a593Smuzhiyun PHSTATM_PLL_LOCKED, 1);
148*4882a593Smuzhiyun if (ret < 0)
149*4882a593Smuzhiyun dev_err(&sata_phy->phy->dev,
150*4882a593Smuzhiyun "PHY PLL locking failed\n");
151*4882a593Smuzhiyun return ret;
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun static const struct phy_ops exynos_sata_phy_ops = {
155*4882a593Smuzhiyun .init = exynos_sata_phy_init,
156*4882a593Smuzhiyun .power_on = exynos_sata_phy_power_on,
157*4882a593Smuzhiyun .power_off = exynos_sata_phy_power_off,
158*4882a593Smuzhiyun .owner = THIS_MODULE,
159*4882a593Smuzhiyun };
160*4882a593Smuzhiyun
exynos_sata_phy_probe(struct platform_device * pdev)161*4882a593Smuzhiyun static int exynos_sata_phy_probe(struct platform_device *pdev)
162*4882a593Smuzhiyun {
163*4882a593Smuzhiyun struct exynos_sata_phy *sata_phy;
164*4882a593Smuzhiyun struct device *dev = &pdev->dev;
165*4882a593Smuzhiyun struct resource *res;
166*4882a593Smuzhiyun struct phy_provider *phy_provider;
167*4882a593Smuzhiyun struct device_node *node;
168*4882a593Smuzhiyun int ret = 0;
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun sata_phy = devm_kzalloc(dev, sizeof(*sata_phy), GFP_KERNEL);
171*4882a593Smuzhiyun if (!sata_phy)
172*4882a593Smuzhiyun return -ENOMEM;
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun sata_phy->regs = devm_ioremap_resource(dev, res);
177*4882a593Smuzhiyun if (IS_ERR(sata_phy->regs))
178*4882a593Smuzhiyun return PTR_ERR(sata_phy->regs);
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun sata_phy->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
181*4882a593Smuzhiyun "samsung,syscon-phandle");
182*4882a593Smuzhiyun if (IS_ERR(sata_phy->pmureg)) {
183*4882a593Smuzhiyun dev_err(dev, "syscon regmap lookup failed.\n");
184*4882a593Smuzhiyun return PTR_ERR(sata_phy->pmureg);
185*4882a593Smuzhiyun }
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun node = of_parse_phandle(dev->of_node,
188*4882a593Smuzhiyun "samsung,exynos-sataphy-i2c-phandle", 0);
189*4882a593Smuzhiyun if (!node)
190*4882a593Smuzhiyun return -EINVAL;
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun sata_phy->client = of_find_i2c_device_by_node(node);
193*4882a593Smuzhiyun of_node_put(node);
194*4882a593Smuzhiyun if (!sata_phy->client)
195*4882a593Smuzhiyun return -EPROBE_DEFER;
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun dev_set_drvdata(dev, sata_phy);
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun sata_phy->phyclk = devm_clk_get(dev, "sata_phyctrl");
200*4882a593Smuzhiyun if (IS_ERR(sata_phy->phyclk)) {
201*4882a593Smuzhiyun dev_err(dev, "failed to get clk for PHY\n");
202*4882a593Smuzhiyun ret = PTR_ERR(sata_phy->phyclk);
203*4882a593Smuzhiyun goto put_dev;
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun ret = clk_prepare_enable(sata_phy->phyclk);
207*4882a593Smuzhiyun if (ret < 0) {
208*4882a593Smuzhiyun dev_err(dev, "failed to enable source clk\n");
209*4882a593Smuzhiyun goto put_dev;
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops);
213*4882a593Smuzhiyun if (IS_ERR(sata_phy->phy)) {
214*4882a593Smuzhiyun dev_err(dev, "failed to create PHY\n");
215*4882a593Smuzhiyun ret = PTR_ERR(sata_phy->phy);
216*4882a593Smuzhiyun goto clk_disable;
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun phy_set_drvdata(sata_phy->phy, sata_phy);
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun phy_provider = devm_of_phy_provider_register(dev,
222*4882a593Smuzhiyun of_phy_simple_xlate);
223*4882a593Smuzhiyun if (IS_ERR(phy_provider)) {
224*4882a593Smuzhiyun ret = PTR_ERR(phy_provider);
225*4882a593Smuzhiyun goto clk_disable;
226*4882a593Smuzhiyun }
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun return 0;
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun clk_disable:
231*4882a593Smuzhiyun clk_disable_unprepare(sata_phy->phyclk);
232*4882a593Smuzhiyun put_dev:
233*4882a593Smuzhiyun put_device(&sata_phy->client->dev);
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun return ret;
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun static const struct of_device_id exynos_sata_phy_of_match[] = {
239*4882a593Smuzhiyun { .compatible = "samsung,exynos5250-sata-phy" },
240*4882a593Smuzhiyun { },
241*4882a593Smuzhiyun };
242*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, exynos_sata_phy_of_match);
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun static struct platform_driver exynos_sata_phy_driver = {
245*4882a593Smuzhiyun .probe = exynos_sata_phy_probe,
246*4882a593Smuzhiyun .driver = {
247*4882a593Smuzhiyun .of_match_table = exynos_sata_phy_of_match,
248*4882a593Smuzhiyun .name = "samsung,sata-phy",
249*4882a593Smuzhiyun .suppress_bind_attrs = true,
250*4882a593Smuzhiyun }
251*4882a593Smuzhiyun };
252*4882a593Smuzhiyun module_platform_driver(exynos_sata_phy_driver);
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun MODULE_DESCRIPTION("Samsung SerDes PHY driver");
255*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
256*4882a593Smuzhiyun MODULE_AUTHOR("Girish K S <ks.giri@samsung.com>");
257*4882a593Smuzhiyun MODULE_AUTHOR("Yuvaraj C D <yuvaraj.cd@samsung.com>");
258