1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (c) 2023, Linaro Limited |
4 | */ |
5 | |
6 | #include <linux/module.h> |
7 | #include <linux/platform_device.h> |
8 | #include <linux/regulator/consumer.h> |
9 | #include <linux/regmap.h> |
10 | #include <linux/of.h> |
11 | #include <linux/phy/phy.h> |
12 | |
13 | /* eUSB2 status registers */ |
14 | #define EUSB2_RPTR_STATUS 0x08 |
15 | #define RPTR_OK BIT(7) |
16 | |
17 | /* eUSB2 control registers */ |
18 | #define EUSB2_EN_CTL1 0x46 |
19 | #define EUSB2_RPTR_EN BIT(7) |
20 | |
21 | #define EUSB2_FORCE_EN_5 0xe8 |
22 | #define F_CLK_19P2M_EN BIT(6) |
23 | |
24 | #define EUSB2_FORCE_VAL_5 0xeD |
25 | #define V_CLK_19P2M_EN BIT(6) |
26 | |
27 | #define EUSB2_TUNE_USB2_CROSSOVER 0x50 |
28 | #define EUSB2_TUNE_IUSB2 0x51 |
29 | #define EUSB2_TUNE_RES_FSDIF 0x52 |
30 | #define EUSB2_TUNE_HSDISC 0x53 |
31 | #define EUSB2_TUNE_SQUELCH_U 0x54 |
32 | #define EUSB2_TUNE_USB2_SLEW 0x55 |
33 | #define EUSB2_TUNE_USB2_EQU 0x56 |
34 | #define EUSB2_TUNE_USB2_PREEM 0x57 |
35 | #define EUSB2_TUNE_USB2_HS_COMP_CUR 0x58 |
36 | #define EUSB2_TUNE_EUSB_SLEW 0x59 |
37 | #define EUSB2_TUNE_EUSB_EQU 0x5A |
38 | #define EUSB2_TUNE_EUSB_HS_COMP_CUR 0x5B |
39 | |
40 | enum eusb2_reg_layout { |
41 | TUNE_EUSB_HS_COMP_CUR, |
42 | TUNE_EUSB_EQU, |
43 | TUNE_EUSB_SLEW, |
44 | TUNE_USB2_HS_COMP_CUR, |
45 | TUNE_USB2_PREEM, |
46 | TUNE_USB2_EQU, |
47 | TUNE_USB2_SLEW, |
48 | TUNE_SQUELCH_U, |
49 | TUNE_HSDISC, |
50 | TUNE_RES_FSDIF, |
51 | TUNE_IUSB2, |
52 | TUNE_USB2_CROSSOVER, |
53 | NUM_TUNE_FIELDS, |
54 | |
55 | FORCE_VAL_5 = NUM_TUNE_FIELDS, |
56 | FORCE_EN_5, |
57 | |
58 | EN_CTL1, |
59 | |
60 | RPTR_STATUS, |
61 | LAYOUT_SIZE, |
62 | }; |
63 | |
64 | struct eusb2_repeater_cfg { |
65 | const u32 *init_tbl; |
66 | int init_tbl_num; |
67 | const char * const *vreg_list; |
68 | int num_vregs; |
69 | }; |
70 | |
71 | struct eusb2_repeater { |
72 | struct device *dev; |
73 | struct regmap *regmap; |
74 | struct phy *phy; |
75 | struct regulator_bulk_data *vregs; |
76 | const struct eusb2_repeater_cfg *cfg; |
77 | u32 base; |
78 | enum phy_mode mode; |
79 | }; |
80 | |
81 | static const char * const pm8550b_vreg_l[] = { |
82 | "vdd18" , "vdd3" , |
83 | }; |
84 | |
85 | static const u32 pm8550b_init_tbl[NUM_TUNE_FIELDS] = { |
86 | [TUNE_IUSB2] = 0x8, |
87 | [TUNE_SQUELCH_U] = 0x3, |
88 | [TUNE_USB2_PREEM] = 0x5, |
89 | }; |
90 | |
91 | static const struct eusb2_repeater_cfg pm8550b_eusb2_cfg = { |
92 | .init_tbl = pm8550b_init_tbl, |
93 | .init_tbl_num = ARRAY_SIZE(pm8550b_init_tbl), |
94 | .vreg_list = pm8550b_vreg_l, |
95 | .num_vregs = ARRAY_SIZE(pm8550b_vreg_l), |
96 | }; |
97 | |
98 | static int eusb2_repeater_init_vregs(struct eusb2_repeater *rptr) |
99 | { |
100 | int num = rptr->cfg->num_vregs; |
101 | struct device *dev = rptr->dev; |
102 | int i; |
103 | |
104 | rptr->vregs = devm_kcalloc(dev, n: num, size: sizeof(*rptr->vregs), GFP_KERNEL); |
105 | if (!rptr->vregs) |
106 | return -ENOMEM; |
107 | |
108 | for (i = 0; i < num; i++) |
109 | rptr->vregs[i].supply = rptr->cfg->vreg_list[i]; |
110 | |
111 | return devm_regulator_bulk_get(dev, num_consumers: num, consumers: rptr->vregs); |
112 | } |
113 | |
114 | static int eusb2_repeater_init(struct phy *phy) |
115 | { |
116 | struct eusb2_repeater *rptr = phy_get_drvdata(phy); |
117 | struct device_node *np = rptr->dev->of_node; |
118 | struct regmap *regmap = rptr->regmap; |
119 | const u32 *init_tbl = rptr->cfg->init_tbl; |
120 | u8 tune_usb2_preem = init_tbl[TUNE_USB2_PREEM]; |
121 | u8 tune_hsdisc = init_tbl[TUNE_HSDISC]; |
122 | u8 tune_iusb2 = init_tbl[TUNE_IUSB2]; |
123 | u32 base = rptr->base; |
124 | u32 val; |
125 | int ret; |
126 | |
127 | of_property_read_u8(np, propname: "qcom,tune-usb2-amplitude" , out_value: &tune_iusb2); |
128 | of_property_read_u8(np, propname: "qcom,tune-usb2-disc-thres" , out_value: &tune_hsdisc); |
129 | of_property_read_u8(np, propname: "qcom,tune-usb2-preem" , out_value: &tune_usb2_preem); |
130 | |
131 | ret = regulator_bulk_enable(num_consumers: rptr->cfg->num_vregs, consumers: rptr->vregs); |
132 | if (ret) |
133 | return ret; |
134 | |
135 | regmap_write(map: regmap, reg: base + EUSB2_EN_CTL1, EUSB2_RPTR_EN); |
136 | |
137 | regmap_write(map: regmap, reg: base + EUSB2_TUNE_EUSB_HS_COMP_CUR, val: init_tbl[TUNE_EUSB_HS_COMP_CUR]); |
138 | regmap_write(map: regmap, reg: base + EUSB2_TUNE_EUSB_EQU, val: init_tbl[TUNE_EUSB_EQU]); |
139 | regmap_write(map: regmap, reg: base + EUSB2_TUNE_EUSB_SLEW, val: init_tbl[TUNE_EUSB_SLEW]); |
140 | regmap_write(map: regmap, reg: base + EUSB2_TUNE_USB2_HS_COMP_CUR, val: init_tbl[TUNE_USB2_HS_COMP_CUR]); |
141 | regmap_write(map: regmap, reg: base + EUSB2_TUNE_USB2_EQU, val: init_tbl[TUNE_USB2_EQU]); |
142 | regmap_write(map: regmap, reg: base + EUSB2_TUNE_USB2_SLEW, val: init_tbl[TUNE_USB2_SLEW]); |
143 | regmap_write(map: regmap, reg: base + EUSB2_TUNE_SQUELCH_U, val: init_tbl[TUNE_SQUELCH_U]); |
144 | regmap_write(map: regmap, reg: base + EUSB2_TUNE_RES_FSDIF, val: init_tbl[TUNE_RES_FSDIF]); |
145 | regmap_write(map: regmap, reg: base + EUSB2_TUNE_USB2_CROSSOVER, val: init_tbl[TUNE_USB2_CROSSOVER]); |
146 | |
147 | regmap_write(map: regmap, reg: base + EUSB2_TUNE_USB2_PREEM, val: tune_usb2_preem); |
148 | regmap_write(map: regmap, reg: base + EUSB2_TUNE_HSDISC, val: tune_hsdisc); |
149 | regmap_write(map: regmap, reg: base + EUSB2_TUNE_IUSB2, val: tune_iusb2); |
150 | |
151 | ret = regmap_read_poll_timeout(regmap, base + EUSB2_RPTR_STATUS, val, val & RPTR_OK, 10, 5); |
152 | if (ret) |
153 | dev_err(rptr->dev, "initialization timed-out\n" ); |
154 | |
155 | return ret; |
156 | } |
157 | |
158 | static int eusb2_repeater_set_mode(struct phy *phy, |
159 | enum phy_mode mode, int submode) |
160 | { |
161 | struct eusb2_repeater *rptr = phy_get_drvdata(phy); |
162 | struct regmap *regmap = rptr->regmap; |
163 | u32 base = rptr->base; |
164 | |
165 | switch (mode) { |
166 | case PHY_MODE_USB_HOST: |
167 | /* |
168 | * CM.Lx is prohibited when repeater is already into Lx state as |
169 | * per eUSB 1.2 Spec. Below implement software workaround until |
170 | * PHY and controller is fixing seen observation. |
171 | */ |
172 | regmap_write(map: regmap, reg: base + EUSB2_FORCE_EN_5, F_CLK_19P2M_EN); |
173 | regmap_write(map: regmap, reg: base + EUSB2_FORCE_VAL_5, V_CLK_19P2M_EN); |
174 | break; |
175 | case PHY_MODE_USB_DEVICE: |
176 | /* |
177 | * In device mode clear host mode related workaround as there |
178 | * is no repeater reset available, and enable/disable of |
179 | * repeater doesn't clear previous value due to shared |
180 | * regulators (say host <-> device mode switch). |
181 | */ |
182 | regmap_write(map: regmap, reg: base + EUSB2_FORCE_EN_5, val: 0); |
183 | regmap_write(map: regmap, reg: base + EUSB2_FORCE_VAL_5, val: 0); |
184 | break; |
185 | default: |
186 | return -EINVAL; |
187 | } |
188 | |
189 | return 0; |
190 | } |
191 | |
192 | static int eusb2_repeater_exit(struct phy *phy) |
193 | { |
194 | struct eusb2_repeater *rptr = phy_get_drvdata(phy); |
195 | |
196 | return regulator_bulk_disable(num_consumers: rptr->cfg->num_vregs, consumers: rptr->vregs); |
197 | } |
198 | |
199 | static const struct phy_ops eusb2_repeater_ops = { |
200 | .init = eusb2_repeater_init, |
201 | .exit = eusb2_repeater_exit, |
202 | .set_mode = eusb2_repeater_set_mode, |
203 | .owner = THIS_MODULE, |
204 | }; |
205 | |
206 | static int eusb2_repeater_probe(struct platform_device *pdev) |
207 | { |
208 | struct eusb2_repeater *rptr; |
209 | struct device *dev = &pdev->dev; |
210 | struct phy_provider *phy_provider; |
211 | struct device_node *np = dev->of_node; |
212 | u32 res; |
213 | int ret; |
214 | |
215 | rptr = devm_kzalloc(dev, size: sizeof(*rptr), GFP_KERNEL); |
216 | if (!rptr) |
217 | return -ENOMEM; |
218 | |
219 | rptr->dev = dev; |
220 | dev_set_drvdata(dev, data: rptr); |
221 | |
222 | rptr->cfg = of_device_get_match_data(dev); |
223 | if (!rptr->cfg) |
224 | return -EINVAL; |
225 | |
226 | rptr->regmap = dev_get_regmap(dev: dev->parent, NULL); |
227 | if (!rptr->regmap) |
228 | return -ENODEV; |
229 | |
230 | ret = of_property_read_u32(np, propname: "reg" , out_value: &res); |
231 | if (ret < 0) |
232 | return ret; |
233 | |
234 | rptr->base = res; |
235 | |
236 | ret = eusb2_repeater_init_vregs(rptr); |
237 | if (ret < 0) { |
238 | dev_err(dev, "unable to get supplies\n" ); |
239 | return ret; |
240 | } |
241 | |
242 | rptr->phy = devm_phy_create(dev, node: np, ops: &eusb2_repeater_ops); |
243 | if (IS_ERR(ptr: rptr->phy)) { |
244 | dev_err(dev, "failed to create PHY: %d\n" , ret); |
245 | return PTR_ERR(ptr: rptr->phy); |
246 | } |
247 | |
248 | phy_set_drvdata(phy: rptr->phy, data: rptr); |
249 | |
250 | phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); |
251 | if (IS_ERR(ptr: phy_provider)) |
252 | return PTR_ERR(ptr: phy_provider); |
253 | |
254 | dev_info(dev, "Registered Qcom-eUSB2 repeater\n" ); |
255 | |
256 | return 0; |
257 | } |
258 | |
259 | static void eusb2_repeater_remove(struct platform_device *pdev) |
260 | { |
261 | struct eusb2_repeater *rptr = platform_get_drvdata(pdev); |
262 | |
263 | if (!rptr) |
264 | return; |
265 | |
266 | eusb2_repeater_exit(phy: rptr->phy); |
267 | } |
268 | |
269 | static const struct of_device_id eusb2_repeater_of_match_table[] = { |
270 | { |
271 | .compatible = "qcom,pm8550b-eusb2-repeater" , |
272 | .data = &pm8550b_eusb2_cfg, |
273 | }, |
274 | { }, |
275 | }; |
276 | MODULE_DEVICE_TABLE(of, eusb2_repeater_of_match_table); |
277 | |
278 | static struct platform_driver eusb2_repeater_driver = { |
279 | .probe = eusb2_repeater_probe, |
280 | .remove_new = eusb2_repeater_remove, |
281 | .driver = { |
282 | .name = "qcom-eusb2-repeater" , |
283 | .of_match_table = eusb2_repeater_of_match_table, |
284 | }, |
285 | }; |
286 | |
287 | module_platform_driver(eusb2_repeater_driver); |
288 | |
289 | MODULE_DESCRIPTION("Qualcomm PMIC eUSB2 Repeater driver" ); |
290 | MODULE_LICENSE("GPL" ); |
291 | |