1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
4 * Author(s): Patrice Chotard, <patrice.chotard@foss.st.com> for STMicroelectronics.
5 */
6
7#include <log.h>
8#include <asm/global_data.h>
9#include <asm/io.h>
10#include <bitfield.h>
11#include <dm.h>
12#include <errno.h>
13#include <fdtdec.h>
14#include <generic-phy.h>
15#include <linux/libfdt.h>
16#include <regmap.h>
17#include <reset-uclass.h>
18#include <syscon.h>
19#include <wait_bit.h>
20#include <linux/printk.h>
21
22#include <linux/bitops.h>
23#include <linux/compat.h>
24
25DECLARE_GLOBAL_DATA_PTR;
26
27/* Default PHY_SEL and REFCLKSEL configuration */
28#define STIH407_USB_PICOPHY_CTRL_PORT_CONF	0x6
29
30/* ports parameters overriding */
31#define STIH407_USB_PICOPHY_PARAM_DEF		0x39a4dc
32
33#define PHYPARAM_REG	1
34#define PHYCTRL_REG	2
35#define PHYPARAM_NB	3
36
37struct sti_usb_phy {
38	struct regmap *regmap;
39	struct reset_ctl global_ctl;
40	struct reset_ctl port_ctl;
41	int param;
42	int ctrl;
43};
44
45static int sti_usb_phy_deassert(struct sti_usb_phy *phy)
46{
47	int ret;
48
49	ret = reset_deassert(&phy->global_ctl);
50	if (ret < 0) {
51		pr_err("PHY global deassert failed: %d", ret);
52		return ret;
53	}
54
55	ret = reset_deassert(&phy->port_ctl);
56	if (ret < 0)
57		pr_err("PHY port deassert failed: %d", ret);
58
59	return ret;
60}
61
62static int sti_usb_phy_init(struct phy *usb_phy)
63{
64	struct udevice *dev = usb_phy->dev;
65	struct sti_usb_phy *phy = dev_get_priv(dev);
66	void __iomem *reg;
67
68	/* set ctrl picophy value */
69	reg = (void __iomem *)phy->regmap->ranges[0].start + phy->ctrl;
70	/* CTRL_PORT mask is 0x1f */
71	clrsetbits_le32(reg, 0x1f, STIH407_USB_PICOPHY_CTRL_PORT_CONF);
72
73	/* set ports parameters overriding */
74	reg = (void __iomem *)phy->regmap->ranges[0].start + phy->param;
75	/* PARAM_DEF mask is 0xffffffff */
76	clrsetbits_le32(reg, 0xffffffff, STIH407_USB_PICOPHY_PARAM_DEF);
77
78	return sti_usb_phy_deassert(phy);
79}
80
81static int sti_usb_phy_exit(struct phy *usb_phy)
82{
83	struct udevice *dev = usb_phy->dev;
84	struct sti_usb_phy *phy = dev_get_priv(dev);
85	int ret;
86
87	ret = reset_assert(&phy->port_ctl);
88	if (ret < 0) {
89		pr_err("PHY port assert failed: %d", ret);
90		return ret;
91	}
92
93	ret = reset_assert(&phy->global_ctl);
94	if (ret < 0)
95		pr_err("PHY global assert failed: %d", ret);
96
97	return ret;
98}
99
100struct phy_ops sti_usb_phy_ops = {
101	.init = sti_usb_phy_init,
102	.exit = sti_usb_phy_exit,
103};
104
105int sti_usb_phy_probe(struct udevice *dev)
106{
107	struct sti_usb_phy *priv = dev_get_priv(dev);
108	struct udevice *syscon;
109	struct ofnode_phandle_args syscfg_phandle;
110	u32 cells[PHYPARAM_NB];
111	int ret, count;
112
113	/* get corresponding syscon phandle */
114	ret = dev_read_phandle_with_args(dev, "st,syscfg", NULL, 0, 0,
115					 &syscfg_phandle);
116
117	if (ret < 0) {
118		pr_err("Can't get syscfg phandle: %d\n", ret);
119		return ret;
120	}
121
122	ret = uclass_get_device_by_ofnode(UCLASS_SYSCON, syscfg_phandle.node,
123					  &syscon);
124	if (ret) {
125		pr_err("unable to find syscon device (%d)\n", ret);
126		return ret;
127	}
128
129	priv->regmap = syscon_get_regmap(syscon);
130	if (!priv->regmap) {
131		pr_err("unable to find regmap\n");
132		return -ENODEV;
133	}
134
135	/* get phy param offset */
136	count = fdtdec_get_int_array_count(gd->fdt_blob, dev_of_offset(dev),
137					   "st,syscfg", cells,
138					   ARRAY_SIZE(cells));
139
140	if (count < 0) {
141		pr_err("Bad PHY st,syscfg property %d\n", count);
142		return -EINVAL;
143	}
144
145	if (count > PHYPARAM_NB) {
146		pr_err("Unsupported PHY param count %d\n", count);
147		return -EINVAL;
148	}
149
150	priv->param = cells[PHYPARAM_REG];
151	priv->ctrl = cells[PHYCTRL_REG];
152
153	/* get global reset control */
154	ret = reset_get_by_name(dev, "global", &priv->global_ctl);
155	if (ret) {
156		pr_err("can't get global reset for %s (%d)", dev->name, ret);
157		return ret;
158	}
159
160	/* get port reset control */
161	ret = reset_get_by_name(dev, "port", &priv->port_ctl);
162	if (ret) {
163		pr_err("can't get port reset for %s (%d)", dev->name, ret);
164		return ret;
165	}
166
167	return 0;
168}
169
170static const struct udevice_id sti_usb_phy_ids[] = {
171	{ .compatible = "st,stih407-usb2-phy" },
172	{ }
173};
174
175U_BOOT_DRIVER(sti_usb_phy) = {
176	.name = "sti_usb_phy",
177	.id = UCLASS_PHY,
178	.of_match = sti_usb_phy_ids,
179	.probe = sti_usb_phy_probe,
180	.ops = &sti_usb_phy_ops,
181	.priv_auto	= sizeof(struct sti_usb_phy),
182};
183