Commit | Line | Data |
---|---|---|
64562e99 PA |
1 | /* |
2 | * ST spear1340-miphy driver | |
3 | * | |
4 | * Copyright (C) 2014 ST Microelectronics | |
e34cadde | 5 | * Pratyush Anand <pratyush.anand@gmail.com> |
9c5dcdd0 | 6 | * Mohit Kumar <mohit.kumar.dhaka@gmail.com> |
64562e99 PA |
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/bitops.h> | |
15 | #include <linux/delay.h> | |
16 | #include <linux/dma-mapping.h> | |
17 | #include <linux/kernel.h> | |
18 | #include <linux/mfd/syscon.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/of_device.h> | |
21 | #include <linux/phy/phy.h> | |
22 | #include <linux/regmap.h> | |
23 | ||
24 | /* SPEAr1340 Registers */ | |
25 | /* Power Management Registers */ | |
26 | #define SPEAR1340_PCM_CFG 0x100 | |
27 | #define SPEAR1340_PCM_CFG_SATA_POWER_EN BIT(11) | |
28 | #define SPEAR1340_PCM_WKUP_CFG 0x104 | |
29 | #define SPEAR1340_SWITCH_CTR 0x108 | |
30 | ||
31 | #define SPEAR1340_PERIP1_SW_RST 0x318 | |
32 | #define SPEAR1340_PERIP1_SW_RSATA BIT(12) | |
33 | #define SPEAR1340_PERIP2_SW_RST 0x31C | |
34 | #define SPEAR1340_PERIP3_SW_RST 0x320 | |
35 | ||
36 | /* PCIE - SATA configuration registers */ | |
37 | #define SPEAR1340_PCIE_SATA_CFG 0x424 | |
38 | /* PCIE CFG MASks */ | |
39 | #define SPEAR1340_PCIE_CFG_DEVICE_PRESENT BIT(11) | |
40 | #define SPEAR1340_PCIE_CFG_POWERUP_RESET BIT(10) | |
41 | #define SPEAR1340_PCIE_CFG_CORE_CLK_EN BIT(9) | |
42 | #define SPEAR1340_PCIE_CFG_AUX_CLK_EN BIT(8) | |
43 | #define SPEAR1340_SATA_CFG_TX_CLK_EN BIT(4) | |
44 | #define SPEAR1340_SATA_CFG_RX_CLK_EN BIT(3) | |
45 | #define SPEAR1340_SATA_CFG_POWERUP_RESET BIT(2) | |
46 | #define SPEAR1340_SATA_CFG_PM_CLK_EN BIT(1) | |
47 | #define SPEAR1340_PCIE_SATA_SEL_PCIE (0) | |
48 | #define SPEAR1340_PCIE_SATA_SEL_SATA (1) | |
49 | #define SPEAR1340_PCIE_SATA_CFG_MASK 0xF1F | |
50 | #define SPEAR1340_PCIE_CFG_VAL (SPEAR1340_PCIE_SATA_SEL_PCIE | \ | |
51 | SPEAR1340_PCIE_CFG_AUX_CLK_EN | \ | |
52 | SPEAR1340_PCIE_CFG_CORE_CLK_EN | \ | |
53 | SPEAR1340_PCIE_CFG_POWERUP_RESET | \ | |
54 | SPEAR1340_PCIE_CFG_DEVICE_PRESENT) | |
55 | #define SPEAR1340_SATA_CFG_VAL (SPEAR1340_PCIE_SATA_SEL_SATA | \ | |
56 | SPEAR1340_SATA_CFG_PM_CLK_EN | \ | |
57 | SPEAR1340_SATA_CFG_POWERUP_RESET | \ | |
58 | SPEAR1340_SATA_CFG_RX_CLK_EN | \ | |
59 | SPEAR1340_SATA_CFG_TX_CLK_EN) | |
60 | ||
61 | #define SPEAR1340_PCIE_MIPHY_CFG 0x428 | |
62 | #define SPEAR1340_MIPHY_OSC_BYPASS_EXT BIT(31) | |
63 | #define SPEAR1340_MIPHY_CLK_REF_DIV2 BIT(27) | |
64 | #define SPEAR1340_MIPHY_CLK_REF_DIV4 (2 << 27) | |
65 | #define SPEAR1340_MIPHY_CLK_REF_DIV8 (3 << 27) | |
66 | #define SPEAR1340_MIPHY_PLL_RATIO_TOP(x) (x << 0) | |
67 | #define SPEAR1340_PCIE_MIPHY_CFG_MASK 0xF80000FF | |
68 | #define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA \ | |
69 | (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \ | |
70 | SPEAR1340_MIPHY_CLK_REF_DIV2 | \ | |
71 | SPEAR1340_MIPHY_PLL_RATIO_TOP(60)) | |
72 | #define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK \ | |
73 | (SPEAR1340_MIPHY_PLL_RATIO_TOP(120)) | |
74 | #define SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE \ | |
75 | (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \ | |
76 | SPEAR1340_MIPHY_PLL_RATIO_TOP(25)) | |
77 | ||
78 | enum spear1340_miphy_mode { | |
79 | SATA, | |
80 | PCIE, | |
81 | }; | |
82 | ||
83 | struct spear1340_miphy_priv { | |
84 | /* phy mode: 0 for SATA 1 for PCIe */ | |
85 | enum spear1340_miphy_mode mode; | |
86 | /* regmap for any soc specific misc registers */ | |
87 | struct regmap *misc; | |
88 | /* phy struct pointer */ | |
89 | struct phy *phy; | |
90 | }; | |
91 | ||
92 | static int spear1340_miphy_sata_init(struct spear1340_miphy_priv *priv) | |
93 | { | |
94 | regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG, | |
95 | SPEAR1340_PCIE_SATA_CFG_MASK, | |
96 | SPEAR1340_SATA_CFG_VAL); | |
97 | regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG, | |
98 | SPEAR1340_PCIE_MIPHY_CFG_MASK, | |
99 | SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK); | |
100 | /* Switch on sata power domain */ | |
101 | regmap_update_bits(priv->misc, SPEAR1340_PCM_CFG, | |
102 | SPEAR1340_PCM_CFG_SATA_POWER_EN, | |
103 | SPEAR1340_PCM_CFG_SATA_POWER_EN); | |
104 | /* Wait for SATA power domain on */ | |
105 | msleep(20); | |
106 | ||
107 | /* Disable PCIE SATA Controller reset */ | |
108 | regmap_update_bits(priv->misc, SPEAR1340_PERIP1_SW_RST, | |
109 | SPEAR1340_PERIP1_SW_RSATA, 0); | |
110 | /* Wait for SATA reset de-assert completion */ | |
111 | msleep(20); | |
112 | ||
113 | return 0; | |
114 | } | |
115 | ||
116 | static int spear1340_miphy_sata_exit(struct spear1340_miphy_priv *priv) | |
117 | { | |
118 | regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG, | |
119 | SPEAR1340_PCIE_SATA_CFG_MASK, 0); | |
120 | regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG, | |
121 | SPEAR1340_PCIE_MIPHY_CFG_MASK, 0); | |
122 | ||
123 | /* Enable PCIE SATA Controller reset */ | |
124 | regmap_update_bits(priv->misc, SPEAR1340_PERIP1_SW_RST, | |
125 | SPEAR1340_PERIP1_SW_RSATA, | |
126 | SPEAR1340_PERIP1_SW_RSATA); | |
127 | /* Wait for SATA power domain off */ | |
128 | msleep(20); | |
129 | /* Switch off sata power domain */ | |
130 | regmap_update_bits(priv->misc, SPEAR1340_PCM_CFG, | |
131 | SPEAR1340_PCM_CFG_SATA_POWER_EN, 0); | |
132 | /* Wait for SATA reset assert completion */ | |
133 | msleep(20); | |
134 | ||
135 | return 0; | |
136 | } | |
137 | ||
138 | static int spear1340_miphy_pcie_init(struct spear1340_miphy_priv *priv) | |
139 | { | |
140 | regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG, | |
141 | SPEAR1340_PCIE_MIPHY_CFG_MASK, | |
142 | SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE); | |
143 | regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG, | |
144 | SPEAR1340_PCIE_SATA_CFG_MASK, | |
145 | SPEAR1340_PCIE_CFG_VAL); | |
146 | ||
147 | return 0; | |
148 | } | |
149 | ||
150 | static int spear1340_miphy_pcie_exit(struct spear1340_miphy_priv *priv) | |
151 | { | |
152 | regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG, | |
153 | SPEAR1340_PCIE_MIPHY_CFG_MASK, 0); | |
154 | regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG, | |
155 | SPEAR1340_PCIE_SATA_CFG_MASK, 0); | |
156 | ||
157 | return 0; | |
158 | } | |
159 | ||
160 | static int spear1340_miphy_init(struct phy *phy) | |
161 | { | |
162 | struct spear1340_miphy_priv *priv = phy_get_drvdata(phy); | |
163 | int ret = 0; | |
164 | ||
165 | if (priv->mode == SATA) | |
166 | ret = spear1340_miphy_sata_init(priv); | |
167 | else if (priv->mode == PCIE) | |
168 | ret = spear1340_miphy_pcie_init(priv); | |
169 | ||
170 | return ret; | |
171 | } | |
172 | ||
173 | static int spear1340_miphy_exit(struct phy *phy) | |
174 | { | |
175 | struct spear1340_miphy_priv *priv = phy_get_drvdata(phy); | |
176 | int ret = 0; | |
177 | ||
178 | if (priv->mode == SATA) | |
179 | ret = spear1340_miphy_sata_exit(priv); | |
180 | else if (priv->mode == PCIE) | |
181 | ret = spear1340_miphy_pcie_exit(priv); | |
182 | ||
183 | return ret; | |
184 | } | |
185 | ||
186 | static const struct of_device_id spear1340_miphy_of_match[] = { | |
187 | { .compatible = "st,spear1340-miphy" }, | |
188 | { }, | |
189 | }; | |
190 | MODULE_DEVICE_TABLE(of, spear1340_miphy_of_match); | |
191 | ||
4a9e5ca1 | 192 | static const struct phy_ops spear1340_miphy_ops = { |
64562e99 PA |
193 | .init = spear1340_miphy_init, |
194 | .exit = spear1340_miphy_exit, | |
195 | .owner = THIS_MODULE, | |
196 | }; | |
197 | ||
198 | #ifdef CONFIG_PM_SLEEP | |
199 | static int spear1340_miphy_suspend(struct device *dev) | |
200 | { | |
201 | struct spear1340_miphy_priv *priv = dev_get_drvdata(dev); | |
202 | int ret = 0; | |
203 | ||
204 | if (priv->mode == SATA) | |
205 | ret = spear1340_miphy_sata_exit(priv); | |
206 | ||
207 | return ret; | |
208 | } | |
209 | ||
210 | static int spear1340_miphy_resume(struct device *dev) | |
211 | { | |
212 | struct spear1340_miphy_priv *priv = dev_get_drvdata(dev); | |
213 | int ret = 0; | |
214 | ||
215 | if (priv->mode == SATA) | |
216 | ret = spear1340_miphy_sata_init(priv); | |
217 | ||
218 | return ret; | |
219 | } | |
220 | #endif | |
221 | ||
222 | static SIMPLE_DEV_PM_OPS(spear1340_miphy_pm_ops, spear1340_miphy_suspend, | |
223 | spear1340_miphy_resume); | |
224 | ||
225 | static struct phy *spear1340_miphy_xlate(struct device *dev, | |
226 | struct of_phandle_args *args) | |
227 | { | |
228 | struct spear1340_miphy_priv *priv = dev_get_drvdata(dev); | |
229 | ||
230 | if (args->args_count < 1) { | |
231 | dev_err(dev, "DT did not pass correct no of args\n"); | |
407ed835 | 232 | return ERR_PTR(-ENODEV); |
64562e99 PA |
233 | } |
234 | ||
235 | priv->mode = args->args[0]; | |
236 | ||
237 | if (priv->mode != SATA && priv->mode != PCIE) { | |
238 | dev_err(dev, "DT did not pass correct phy mode\n"); | |
407ed835 | 239 | return ERR_PTR(-ENODEV); |
64562e99 PA |
240 | } |
241 | ||
242 | return priv->phy; | |
243 | } | |
244 | ||
245 | static int spear1340_miphy_probe(struct platform_device *pdev) | |
246 | { | |
247 | struct device *dev = &pdev->dev; | |
248 | struct spear1340_miphy_priv *priv; | |
249 | struct phy_provider *phy_provider; | |
250 | ||
251 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | |
556bdebb | 252 | if (!priv) |
64562e99 | 253 | return -ENOMEM; |
64562e99 PA |
254 | |
255 | priv->misc = | |
256 | syscon_regmap_lookup_by_phandle(dev->of_node, "misc"); | |
257 | if (IS_ERR(priv->misc)) { | |
258 | dev_err(dev, "failed to find misc regmap\n"); | |
259 | return PTR_ERR(priv->misc); | |
260 | } | |
261 | ||
dbc98635 | 262 | priv->phy = devm_phy_create(dev, NULL, &spear1340_miphy_ops); |
64562e99 PA |
263 | if (IS_ERR(priv->phy)) { |
264 | dev_err(dev, "failed to create SATA PCIe PHY\n"); | |
265 | return PTR_ERR(priv->phy); | |
266 | } | |
267 | ||
268 | dev_set_drvdata(dev, priv); | |
269 | phy_set_drvdata(priv->phy, priv); | |
270 | ||
271 | phy_provider = | |
272 | devm_of_phy_provider_register(dev, spear1340_miphy_xlate); | |
273 | if (IS_ERR(phy_provider)) { | |
274 | dev_err(dev, "failed to register phy provider\n"); | |
275 | return PTR_ERR(phy_provider); | |
276 | } | |
277 | ||
278 | return 0; | |
279 | } | |
280 | ||
281 | static struct platform_driver spear1340_miphy_driver = { | |
282 | .probe = spear1340_miphy_probe, | |
283 | .driver = { | |
284 | .name = "spear1340-miphy", | |
64562e99 PA |
285 | .pm = &spear1340_miphy_pm_ops, |
286 | .of_match_table = of_match_ptr(spear1340_miphy_of_match), | |
287 | }, | |
288 | }; | |
289 | ||
25451e5c | 290 | module_platform_driver(spear1340_miphy_driver); |
64562e99 PA |
291 | |
292 | MODULE_DESCRIPTION("ST SPEAR1340-MIPHY driver"); | |
e34cadde | 293 | MODULE_AUTHOR("Pratyush Anand <pratyush.anand@gmail.com>"); |
64562e99 | 294 | MODULE_LICENSE("GPL v2"); |