phy: rockchip-emmc: add init function
[firefly-linux-kernel-4.4.55.git] / drivers / phy / phy-rockchip-emmc.c
1 /*
2  * Rockchip emmc PHY driver
3  *
4  * Copyright (C) 2016 Shawn Lin <shawn.lin@rock-chips.com>
5  * Copyright (C) 2016 ROCKCHIP, Inc.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  */
16
17 #include <linux/delay.h>
18 #include <linux/mfd/syscon.h>
19 #include <linux/module.h>
20 #include <linux/of.h>
21 #include <linux/of_address.h>
22 #include <linux/phy/phy.h>
23 #include <linux/platform_device.h>
24 #include <linux/regmap.h>
25
26 /*
27  * The higher 16-bit of this register is used for write protection
28  * only if BIT(x + 16) set to 1 the BIT(x) can be written.
29  */
30 #define HIWORD_UPDATE(val, mask, shift) \
31                 ((val) << (shift) | (mask) << ((shift) + 16))
32
33 /* Register definition */
34 #define GRF_EMMCPHY_CON0        0x0
35 #define GRF_EMMCPHY_CON1        0x4
36 #define GRF_EMMCPHY_CON2        0x8
37 #define GRF_EMMCPHY_CON3        0xc
38 #define GRF_EMMCPHY_CON4        0x10
39 #define GRF_EMMCPHY_CON5        0x14
40 #define GRF_EMMCPHY_CON6        0x18
41 #define GRF_EMMCPHY_STATUS      0x20
42
43 #define PHYCTRL_PDB_MASK        0x1
44 #define PHYCTRL_PDB_SHIFT       0x0
45 #define PHYCTRL_PDB_PWR_ON      0x1
46 #define PHYCTRL_PDB_PWR_OFF     0x0
47 #define PHYCTRL_ENDLL_MASK      0x1
48 #define PHYCTRL_ENDLL_SHIFT     0x1
49 #define PHYCTRL_ENDLL_ENABLE    0x1
50 #define PHYCTRL_ENDLL_DISABLE   0x0
51 #define PHYCTRL_CALDONE_MASK    0x1
52 #define PHYCTRL_CALDONE_SHIFT   0x6
53 #define PHYCTRL_CALDONE_DONE    0x1
54 #define PHYCTRL_CALDONE_GOING   0x0
55 #define PHYCTRL_DLLRDY_MASK     0x1
56 #define PHYCTRL_DLLRDY_SHIFT    0x5
57 #define PHYCTRL_DLLRDY_DONE     0x1
58 #define PHYCTRL_DLLRDY_GOING    0x0
59 #define PHYCTRL_FREQSEL_200M    0x0
60 #define PHYCTRL_FREQSEL_50M     0x1
61 #define PHYCTRL_FREQSEL_100M    0x2
62 #define PHYCTRL_FREQSEL_150M    0x3
63 #define PHYCTRL_FREQSEL_MASK    0x3
64 #define PHYCTRL_FREQSEL_SHIFT   0xc
65 #define PHYCTRL_DR_MASK         0x7
66 #define PHYCTRL_DR_SHIFT        0x4
67 #define PHYCTRL_DR_50OHM        0x0
68 #define PHYCTRL_DR_33OHM        0x1
69 #define PHYCTRL_DR_66OHM        0x2
70 #define PHYCTRL_DR_100OHM       0x3
71 #define PHYCTRL_DR_40OHM        0x4
72
73 struct rockchip_emmc_phy {
74         unsigned int    reg_offset;
75         struct regmap   *reg_base;
76 };
77
78 static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy,
79                                    bool on_off)
80 {
81         unsigned int caldone;
82         unsigned int dllrdy;
83
84         /*
85          * Keep phyctrl_pdb and phyctrl_endll low to allow
86          * initialization of CALIO state M/C DFFs
87          */
88         regmap_write(rk_phy->reg_base,
89                      rk_phy->reg_offset + GRF_EMMCPHY_CON6,
90                      HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF,
91                                    PHYCTRL_PDB_MASK,
92                                    PHYCTRL_PDB_SHIFT));
93         regmap_write(rk_phy->reg_base,
94                      rk_phy->reg_offset + GRF_EMMCPHY_CON6,
95                      HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE,
96                                    PHYCTRL_ENDLL_MASK,
97                                    PHYCTRL_ENDLL_SHIFT));
98
99         /* Already finish power_off above */
100         if (on_off == PHYCTRL_PDB_PWR_OFF)
101                 return 0;
102
103         /*
104          * According to the user manual, calpad calibration
105          * cycle takes more than 2us without the minimal recommended
106          * value, so we may need a little margin here
107          */
108         udelay(3);
109         regmap_write(rk_phy->reg_base,
110                      rk_phy->reg_offset + GRF_EMMCPHY_CON6,
111                      HIWORD_UPDATE(PHYCTRL_PDB_PWR_ON,
112                                    PHYCTRL_PDB_MASK,
113                                    PHYCTRL_PDB_SHIFT));
114
115         /*
116          * According to the user manual, it asks driver to
117          * wait 5us for calpad busy trimming
118          */
119         udelay(5);
120         regmap_read(rk_phy->reg_base,
121                     rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
122                     &caldone);
123         caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK;
124         if (caldone != PHYCTRL_CALDONE_DONE) {
125                 pr_err("rockchip_emmc_phy_power: caldone timeout.\n");
126                 return -ETIMEDOUT;
127         }
128
129         regmap_write(rk_phy->reg_base,
130                      rk_phy->reg_offset + GRF_EMMCPHY_CON6,
131                      HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE,
132                                    PHYCTRL_ENDLL_MASK,
133                                    PHYCTRL_ENDLL_SHIFT));
134         /*
135          * After enable analog DLL circuits, we need extra 10.2us
136          * for dll to be ready for work.
137          */
138         udelay(11);
139         regmap_read(rk_phy->reg_base,
140                     rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
141                     &dllrdy);
142         dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK;
143         if (dllrdy != PHYCTRL_DLLRDY_DONE) {
144                 pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n");
145                 return -ETIMEDOUT;
146         }
147
148         return 0;
149 }
150
151 static int rockchip_emmc_phy_init(struct phy*phy)
152 {
153         struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
154
155         regmap_write(rk_phy->reg_base,
156                      rk_phy->reg_offset + GRF_EMMCPHY_CON0,
157                      HIWORD_UPDATE(PHYCTRL_FREQSEL_200M,
158                                    PHYCTRL_FREQSEL_MASK,
159                                    PHYCTRL_FREQSEL_SHIFT));
160
161         regmap_write(rk_phy->reg_base,
162                      rk_phy->reg_offset + GRF_EMMCPHY_CON6,
163                      HIWORD_UPDATE(PHYCTRL_DR_100OHM,
164                                    PHYCTRL_DR_MASK,
165                                    PHYCTRL_DR_SHIFT));
166
167         return 0;
168 }
169
170 static int rockchip_emmc_phy_power_off(struct phy *phy)
171 {
172         struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
173         int ret = 0;
174
175         /* Power down emmc phy analog blocks */
176         ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_OFF);
177         if (ret)
178                 return ret;
179
180         return 0;
181 }
182
183 static int rockchip_emmc_phy_power_on(struct phy *phy)
184 {
185         struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
186         int ret = 0;
187
188         /* Power up emmc phy analog blocks */
189         ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON);
190         if (ret)
191                 return ret;
192
193         return 0;
194 }
195
196 static const struct phy_ops ops = {
197         .init           = rockchip_emmc_phy_init,
198         .power_on       = rockchip_emmc_phy_power_on,
199         .power_off      = rockchip_emmc_phy_power_off,
200         .owner          = THIS_MODULE,
201 };
202
203 static int rockchip_emmc_phy_probe(struct platform_device *pdev)
204 {
205         struct device *dev = &pdev->dev;
206         struct rockchip_emmc_phy *rk_phy;
207         struct phy *generic_phy;
208         struct phy_provider *phy_provider;
209         struct regmap *grf;
210         unsigned int reg_offset;
211
212         grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
213         if (IS_ERR(grf)) {
214                 dev_err(dev, "Missing rockchip,grf property\n");
215                 return PTR_ERR(grf);
216         }
217
218         rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL);
219         if (!rk_phy)
220                 return -ENOMEM;
221
222         if (of_property_read_u32(dev->of_node, "reg-offset", &reg_offset)) {
223                 dev_err(dev, "missing reg property in node %s\n",
224                         dev->of_node->name);
225                 return -EINVAL;
226         }
227
228         rk_phy->reg_offset = reg_offset;
229         rk_phy->reg_base = grf;
230
231         generic_phy = devm_phy_create(dev, dev->of_node, &ops);
232         if (IS_ERR(generic_phy)) {
233                 dev_err(dev, "failed to create PHY\n");
234                 return PTR_ERR(generic_phy);
235         }
236
237         phy_set_drvdata(generic_phy, rk_phy);
238         phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
239
240         return PTR_ERR_OR_ZERO(phy_provider);
241 }
242
243 static const struct of_device_id rockchip_emmc_phy_dt_ids[] = {
244         { .compatible = "rockchip,rk3399-emmc-phy" },
245         {}
246 };
247
248 MODULE_DEVICE_TABLE(of, rockchip_emmc_phy_dt_ids);
249
250 static struct platform_driver rockchip_emmc_driver = {
251         .probe          = rockchip_emmc_phy_probe,
252         .driver         = {
253                 .name   = "rockchip-emmc-phy",
254                 .of_match_table = rockchip_emmc_phy_dt_ids,
255         },
256 };
257
258 module_platform_driver(rockchip_emmc_driver);
259
260 MODULE_AUTHOR("Shawn Lin <shawn.lin@rock-chips.com>");
261 MODULE_DESCRIPTION("Rockchip EMMC PHY driver");
262 MODULE_LICENSE("GPL v2");