File Index
Files and directories
Sign in
FILE

phy-qcom-eusb2-repeater.c

File preview with syntax highlighting.

linux-6.19.14/drivers/phy/qualcomm/phy-qcom-eusb2-repeater.c C
  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
 40struct eusb2_repeater_init_tbl_reg {
 41	unsigned int reg;
 42	unsigned int value;
 43};
 44
 45struct eusb2_repeater_cfg {
 46	const struct eusb2_repeater_init_tbl_reg *init_tbl;
 47	int init_tbl_num;
 48	const char * const *vreg_list;
 49	int num_vregs;
 50};
 51
 52struct eusb2_repeater {
 53	struct device *dev;
 54	struct regmap *regmap;
 55	struct phy *phy;
 56	struct regulator_bulk_data *vregs;
 57	const struct eusb2_repeater_cfg *cfg;
 58	u32 base;
 59	enum phy_mode mode;
 60};
 61
 62static const char * const pm8550b_vreg_l[] = {
 63	"vdd18", "vdd3",
 64};
 65
 66static const struct eusb2_repeater_init_tbl_reg pm8550b_init_tbl[] = {
 67	{ EUSB2_TUNE_IUSB2, 0x8 },
 68	{ EUSB2_TUNE_SQUELCH_U, 0x3 },
 69	{ EUSB2_TUNE_USB2_PREEM, 0x5 },
 70};
 71
 72static const struct eusb2_repeater_init_tbl_reg smb2360_init_tbl[] = {
 73	{ EUSB2_TUNE_IUSB2, 0x5 },
 74	{ EUSB2_TUNE_SQUELCH_U, 0x3 },
 75	{ EUSB2_TUNE_USB2_PREEM, 0x2 },
 76};
 77
 78static const struct eusb2_repeater_cfg pm8550b_eusb2_cfg = {
 79	.init_tbl	= pm8550b_init_tbl,
 80	.init_tbl_num	= ARRAY_SIZE(pm8550b_init_tbl),
 81	.vreg_list	= pm8550b_vreg_l,
 82	.num_vregs	= ARRAY_SIZE(pm8550b_vreg_l),
 83};
 84
 85static const struct eusb2_repeater_cfg pmiv0104_eusb2_cfg = {
 86	/* No PMIC-specific init sequence, only board level tuning via DT */
 87	.init_tbl	= (struct eusb2_repeater_init_tbl_reg[]) {},
 88	.init_tbl_num	= 0,
 89	.vreg_list	= pm8550b_vreg_l,
 90	.num_vregs	= ARRAY_SIZE(pm8550b_vreg_l),
 91};
 92
 93static const struct eusb2_repeater_cfg smb2360_eusb2_cfg = {
 94	.init_tbl	= smb2360_init_tbl,
 95	.init_tbl_num	= ARRAY_SIZE(smb2360_init_tbl),
 96	.vreg_list	= pm8550b_vreg_l,
 97	.num_vregs	= ARRAY_SIZE(pm8550b_vreg_l),
 98};
 99
100static int eusb2_repeater_init_vregs(struct eusb2_repeater *rptr)
101{
102	int num = rptr->cfg->num_vregs;
103	struct device *dev = rptr->dev;
104	int i;
105
106	rptr->vregs = devm_kcalloc(dev, num, sizeof(*rptr->vregs), GFP_KERNEL);
107	if (!rptr->vregs)
108		return -ENOMEM;
109
110	for (i = 0; i < num; i++)
111		rptr->vregs[i].supply = rptr->cfg->vreg_list[i];
112
113	return devm_regulator_bulk_get(dev, num, rptr->vregs);
114}
115
116static int eusb2_repeater_init(struct phy *phy)
117{
118	struct eusb2_repeater *rptr = phy_get_drvdata(phy);
119	struct device_node *np = rptr->dev->of_node;
120	struct regmap *regmap = rptr->regmap;
121	u32 base = rptr->base;
122	u32 poll_val;
123	int ret;
124	u8 val;
125
126	ret = regulator_bulk_enable(rptr->cfg->num_vregs, rptr->vregs);
127	if (ret)
128		return ret;
129
130	regmap_write(regmap, base + EUSB2_EN_CTL1, EUSB2_RPTR_EN);
131
132	/* Write registers from init table */
133	for (int i = 0; i < rptr->cfg->init_tbl_num; i++)
134		regmap_write(regmap, base + rptr->cfg->init_tbl[i].reg,
135			     rptr->cfg->init_tbl[i].value);
136
137	/* Override registers from devicetree values */
138	if (!of_property_read_u8(np, "qcom,tune-usb2-preem", &val))
139		regmap_write(regmap, base + EUSB2_TUNE_USB2_PREEM, val);
140
141	if (!of_property_read_u8(np, "qcom,tune-usb2-disc-thres", &val))
142		regmap_write(regmap, base + EUSB2_TUNE_HSDISC, val);
143
144	if (!of_property_read_u8(np, "qcom,tune-usb2-amplitude", &val))
145		regmap_write(regmap, base + EUSB2_TUNE_IUSB2, val);
146
147	if (!of_property_read_u8(np, "qcom,tune-res-fsdif", &val))
148		regmap_write(regmap, base + EUSB2_TUNE_RES_FSDIF, val);
149
150	/* Wait for status OK */
151	ret = regmap_read_poll_timeout(regmap, base + EUSB2_RPTR_STATUS, poll_val,
152				       poll_val & RPTR_OK, 10, 5);
153	if (ret)
154		dev_err(rptr->dev, "initialization timed-out\n");
155
156	return ret;
157}
158
159static int eusb2_repeater_set_mode(struct phy *phy,
160				   enum phy_mode mode, int submode)
161{
162	struct eusb2_repeater *rptr = phy_get_drvdata(phy);
163	struct regmap *regmap = rptr->regmap;
164	u32 base = rptr->base;
165
166	switch (mode) {
167	case PHY_MODE_USB_HOST:
168		/*
169		 * CM.Lx is prohibited when repeater is already into Lx state as
170		 * per eUSB 1.2 Spec. Below implement software workaround until
171		 * PHY and controller is fixing seen observation.
172		 */
173		regmap_write(regmap, base + EUSB2_FORCE_EN_5, F_CLK_19P2M_EN);
174		regmap_write(regmap, base + EUSB2_FORCE_VAL_5, V_CLK_19P2M_EN);
175		break;
176	case PHY_MODE_USB_DEVICE:
177		/*
178		 * In device mode clear host mode related workaround as there
179		 * is no repeater reset available, and enable/disable of
180		 * repeater doesn't clear previous value due to shared
181		 * regulators (say host <-> device mode switch).
182		 */
183		regmap_write(regmap, base + EUSB2_FORCE_EN_5, 0);
184		regmap_write(regmap, base + EUSB2_FORCE_VAL_5, 0);
185		break;
186	default:
187		return -EINVAL;
188	}
189
190	return 0;
191}
192
193static int eusb2_repeater_exit(struct phy *phy)
194{
195	struct eusb2_repeater *rptr = phy_get_drvdata(phy);
196
197	return regulator_bulk_disable(rptr->cfg->num_vregs, rptr->vregs);
198}
199
200static const struct phy_ops eusb2_repeater_ops = {
201	.init		= eusb2_repeater_init,
202	.exit		= eusb2_repeater_exit,
203	.set_mode	= eusb2_repeater_set_mode,
204	.owner		= THIS_MODULE,
205};
206
207static int eusb2_repeater_probe(struct platform_device *pdev)
208{
209	struct eusb2_repeater *rptr;
210	struct device *dev = &pdev->dev;
211	struct phy_provider *phy_provider;
212	struct device_node *np = dev->of_node;
213	u32 res;
214	int ret;
215
216	rptr = devm_kzalloc(dev, sizeof(*rptr), GFP_KERNEL);
217	if (!rptr)
218		return -ENOMEM;
219
220	rptr->dev = dev;
221	dev_set_drvdata(dev, rptr);
222
223	rptr->cfg = of_device_get_match_data(dev);
224	if (!rptr->cfg)
225		return -EINVAL;
226
227	rptr->regmap = dev_get_regmap(dev->parent, NULL);
228	if (!rptr->regmap)
229		return -ENODEV;
230
231	ret = of_property_read_u32(np, "reg", &res);
232	if (ret < 0)
233		return ret;
234
235	rptr->base = res;
236
237	ret = eusb2_repeater_init_vregs(rptr);
238	if (ret < 0) {
239		dev_err(dev, "unable to get supplies\n");
240		return ret;
241	}
242
243	rptr->phy = devm_phy_create(dev, np, &eusb2_repeater_ops);
244	if (IS_ERR(rptr->phy)) {
245		dev_err(dev, "failed to create PHY: %d\n", ret);
246		return PTR_ERR(rptr->phy);
247	}
248
249	phy_set_drvdata(rptr->phy, rptr);
250
251	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
252	if (IS_ERR(phy_provider))
253		return PTR_ERR(phy_provider);
254
255	return 0;
256}
257
258static void eusb2_repeater_remove(struct platform_device *pdev)
259{
260	struct eusb2_repeater *rptr = platform_get_drvdata(pdev);
261
262	if (!rptr)
263		return;
264
265	eusb2_repeater_exit(rptr->phy);
266}
267
268static const struct of_device_id eusb2_repeater_of_match_table[] = {
269	{
270		.compatible = "qcom,pm8550b-eusb2-repeater",
271		.data = &pm8550b_eusb2_cfg,
272	},
273	{
274		.compatible = "qcom,pmiv0104-eusb2-repeater",
275		.data = &pmiv0104_eusb2_cfg,
276	},
277	{
278		.compatible = "qcom,smb2360-eusb2-repeater",
279		.data = &smb2360_eusb2_cfg,
280	},
281	{ },
282};
283MODULE_DEVICE_TABLE(of, eusb2_repeater_of_match_table);
284
285static struct platform_driver eusb2_repeater_driver = {
286	.probe		= eusb2_repeater_probe,
287	.remove		= eusb2_repeater_remove,
288	.driver = {
289		.name	= "qcom-eusb2-repeater",
290		.of_match_table = eusb2_repeater_of_match_table,
291	},
292};
293
294module_platform_driver(eusb2_repeater_driver);
295
296MODULE_DESCRIPTION("Qualcomm PMIC eUSB2 Repeater driver");
297MODULE_LICENSE("GPL");
~/ Read-only file preview.