blob: 6f28036e095c025ccb034f43b32e9c6fa00894ff [file] [log] [blame]
Fabrice Gasniercd809b12022-12-12 11:44:35 +01001// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Driver for onboard USB hubs
4 *
5 * Copyright (C) 2022, STMicroelectronics - All Rights Reserved
6 *
7 * Mostly inspired by Linux kernel v6.1 onboard_usb_hub driver
8 */
9
Venkatesh Yadav Abbarapuab690c72024-11-25 09:41:58 +053010#include <asm/gpio.h>
Fabrice Gasniercd809b12022-12-12 11:44:35 +010011#include <dm.h>
12#include <dm/device_compat.h>
Venkatesh Yadav Abbarapu60ef2ff2024-11-25 09:42:01 +053013#include <i2c.h>
Venkatesh Yadav Abbarapuab690c72024-11-25 09:41:58 +053014#include <linux/delay.h>
Fabrice Gasniercd809b12022-12-12 11:44:35 +010015#include <power/regulator.h>
16
Venkatesh Yadav Abbarapu60ef2ff2024-11-25 09:42:01 +053017#define USB5744_COMMAND_ATTACH 0x0056
18#define USB5744_COMMAND_ATTACH_LSB 0xAA
19#define USB5744_CONFIG_REG_ACCESS 0x0037
20#define USB5744_CONFIG_REG_ACCESS_LSB 0x99
21
Fabrice Gasniercd809b12022-12-12 11:44:35 +010022struct onboard_hub {
23 struct udevice *vdd;
Venkatesh Yadav Abbarapuab690c72024-11-25 09:41:58 +053024 struct gpio_desc *reset_gpio;
Fabrice Gasniercd809b12022-12-12 11:44:35 +010025};
26
Venkatesh Yadav Abbarapuab690c72024-11-25 09:41:58 +053027struct onboard_hub_data {
28 unsigned long reset_us;
29 unsigned long power_on_delay_us;
Venkatesh Yadav Abbarapu60ef2ff2024-11-25 09:42:01 +053030 int (*init)(struct udevice *dev);
Venkatesh Yadav Abbarapuab690c72024-11-25 09:41:58 +053031};
32
Venkatesh Yadav Abbarapu60ef2ff2024-11-25 09:42:01 +053033static int usb5744_i2c_init(struct udevice *dev)
34{
35 /*
36 * Prevent the MCU from the putting the HUB in suspend mode through register write.
37 * The BYPASS_UDC_SUSPEND bit (Bit 3) of the RuntimeFlags2 register at address
38 * 0x411D controls this aspect of the hub.
39 * Format to write to hub registers via SMBus- 2D 00 00 05 00 01 41 1D 08
40 * Byte 0: Address of slave 2D
41 * Byte 1: Memory address 00
42 * Byte 2: Memory address 00
43 * Byte 3: Number of bytes to write to memory
44 * Byte 4: Write configuration register (00)
45 * Byte 5: Write the number of data bytes (01- 1 data byte)
46 * Byte 6: LSB of register address 0x41
47 * Byte 7: MSB of register address 0x1D
48 * Byte 8: value to be written to the register
49 */
50 u8 data_buf[8] = {0x0, 0x5, 0x0, 0x1, 0x41, 0x1D, 0x08};
51 u8 config_reg_access_buf = USB5744_CONFIG_REG_ACCESS;
52 struct udevice *i2c_bus = NULL, *i2c_dev;
53 struct ofnode_phandle_args phandle;
54 u8 buf = USB5744_COMMAND_ATTACH;
55 struct dm_i2c_chip *i2c_chip;
56 int ret, slave_addr;
57
58 ret = dev_read_phandle_with_args(dev, "i2c-bus", NULL, 0, 0, &phandle);
59 if (ret) {
60 dev_err(dev, "i2c-bus not specified\n");
61 return ret;
62 }
63
64 ret = device_get_global_by_ofnode(ofnode_get_parent(phandle.node), &i2c_bus);
65 if (ret) {
66 dev_err(dev, "Failed to get i2c node, err: %d\n", ret);
67 return ret;
68 }
69
70 ret = ofnode_read_u32(phandle.node, "reg", &slave_addr);
71 if (ret)
72 return ret;
73
74 ret = i2c_get_chip(i2c_bus, slave_addr, 1, &i2c_dev);
75 if (ret) {
76 dev_err(dev, "%s: can't find i2c chip device for addr 0x%x\n", __func__,
77 slave_addr);
78 return ret;
79 }
80
81 i2c_chip = dev_get_parent_plat(i2c_dev);
82 if (!i2c_chip) {
83 dev_err(dev, "parent platform data not found\n");
84 return -EINVAL;
85 }
86
87 i2c_chip->flags &= ~DM_I2C_CHIP_WR_ADDRESS;
88 /* SMBus write command */
89 ret = dm_i2c_write(i2c_dev, 0, (uint8_t *)&data_buf, 8);
90 if (ret) {
91 dev_err(dev, "data_buf i2c_write failed, err:%d\n", ret);
92 return ret;
93 }
94
95 /* Configuration register access command */
96 ret = dm_i2c_write(i2c_dev, USB5744_CONFIG_REG_ACCESS_LSB,
97 &config_reg_access_buf, 2);
98 if (ret) {
99 dev_err(dev, "config_reg_access i2c_write failed, err: %d\n", ret);
100 return ret;
101 }
102
103 /* USB Attach with SMBus */
104 ret = dm_i2c_write(i2c_dev, USB5744_COMMAND_ATTACH_LSB, &buf, 2);
105 if (ret) {
106 dev_err(dev, "usb_attach i2c_write failed, err: %d\n", ret);
107 return ret;
108 }
109
110 return 0;
111}
112
Venkatesh Yadav Abbarapuab690c72024-11-25 09:41:58 +0530113int usb_onboard_hub_reset(struct udevice *dev)
114{
115 struct onboard_hub_data *data =
116 (struct onboard_hub_data *)dev_get_driver_data(dev);
117 struct onboard_hub *hub = dev_get_priv(dev);
118 int ret;
119
120 hub->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_IS_OUT);
121
122 /* property is optional, don't return error! */
123 if (!hub->reset_gpio)
124 return 0;
125
126 ret = dm_gpio_set_value(hub->reset_gpio, 1);
127 if (ret)
128 return ret;
129
130 udelay(data->reset_us);
131
132 ret = dm_gpio_set_value(hub->reset_gpio, 0);
133 if (ret)
134 return ret;
135
136 udelay(data->power_on_delay_us);
137
138 return 0;
139}
140
Fabrice Gasniercd809b12022-12-12 11:44:35 +0100141static int usb_onboard_hub_probe(struct udevice *dev)
142{
Venkatesh Yadav Abbarapu60ef2ff2024-11-25 09:42:01 +0530143 struct onboard_hub_data *data =
144 (struct onboard_hub_data *)dev_get_driver_data(dev);
Fabrice Gasniercd809b12022-12-12 11:44:35 +0100145 struct onboard_hub *hub = dev_get_priv(dev);
146 int ret;
147
148 ret = device_get_supply_regulator(dev, "vdd-supply", &hub->vdd);
Venkatesh Yadav Abbarapu20a61da2024-11-25 09:41:59 +0530149 if (ret && ret != -ENOENT) {
Fabrice Gasniercd809b12022-12-12 11:44:35 +0100150 dev_err(dev, "can't get vdd-supply: %d\n", ret);
151 return ret;
152 }
153
Venkatesh Yadav Abbarapu20a61da2024-11-25 09:41:59 +0530154 if (hub->vdd) {
155 ret = regulator_set_enable_if_allowed(hub->vdd, true);
156 if (ret && ret != -ENOSYS) {
157 dev_err(dev, "can't enable vdd-supply: %d\n", ret);
158 return ret;
159 }
160 }
Fabrice Gasniercd809b12022-12-12 11:44:35 +0100161
Venkatesh Yadav Abbarapu60ef2ff2024-11-25 09:42:01 +0530162 ret = usb_onboard_hub_reset(dev);
163 if (ret)
164 return ret;
165
166 if (data->init) {
167 ret = data->init(dev);
168 if (ret) {
169 dev_err(dev, "onboard i2c init failed: %d\n", ret);
170 goto err;
171 }
172 }
173 return 0;
174err:
175 dm_gpio_set_value(hub->reset_gpio, 0);
176 return ret;
Fabrice Gasniercd809b12022-12-12 11:44:35 +0100177}
178
Venkatesh Yadav Abbarapu7c3d3e92024-11-25 09:42:02 +0530179static int usb_onboard_hub_bind(struct udevice *dev)
180{
181 struct ofnode_phandle_args phandle;
182 const void *fdt = gd->fdt_blob;
183 int ret, off;
184
185 ret = dev_read_phandle_with_args(dev, "peer-hub", NULL, 0, 0, &phandle);
186 if (ret) {
187 dev_err(dev, "peer-hub not specified\n");
188 return ret;
189 }
190
191 off = ofnode_to_offset(phandle.node);
192 ret = fdt_node_check_compatible(fdt, off, "usb424,5744");
193 if (!ret)
194 return 0;
195
196 return -ENODEV;
197}
198
Fabrice Gasniercd809b12022-12-12 11:44:35 +0100199static int usb_onboard_hub_remove(struct udevice *dev)
200{
201 struct onboard_hub *hub = dev_get_priv(dev);
202 int ret;
203
Venkatesh Yadav Abbarapuab690c72024-11-25 09:41:58 +0530204 if (hub->reset_gpio)
205 dm_gpio_free(hub->reset_gpio->dev, hub->reset_gpio);
206
Fabrice Gasniercd809b12022-12-12 11:44:35 +0100207 ret = regulator_set_enable_if_allowed(hub->vdd, false);
208 if (ret)
209 dev_err(dev, "can't disable vdd-supply: %d\n", ret);
210
211 return ret;
212}
213
Venkatesh Yadav Abbarapuab690c72024-11-25 09:41:58 +0530214static const struct onboard_hub_data usb2514_data = {
215 .power_on_delay_us = 500,
216 .reset_us = 1,
217};
218
Venkatesh Yadav Abbarapuc7075c22024-11-25 09:42:00 +0530219static const struct onboard_hub_data usb5744_data = {
Venkatesh Yadav Abbarapu60ef2ff2024-11-25 09:42:01 +0530220 .init = usb5744_i2c_init,
Venkatesh Yadav Abbarapuc7075c22024-11-25 09:42:00 +0530221 .power_on_delay_us = 1000,
222 .reset_us = 5,
223};
224
Fabrice Gasniercd809b12022-12-12 11:44:35 +0100225static const struct udevice_id usb_onboard_hub_ids[] = {
226 /* Use generic usbVID,PID dt-bindings (usb-device.yaml) */
Venkatesh Yadav Abbarapuab690c72024-11-25 09:41:58 +0530227 { .compatible = "usb424,2514", /* USB2514B USB 2.0 */
228 .data = (ulong)&usb2514_data,
Venkatesh Yadav Abbarapuc7075c22024-11-25 09:42:00 +0530229 }, {
230 .compatible = "usb424,2744", /* USB2744 USB 2.0 */
231 .data = (ulong)&usb5744_data,
232 }, {
233 .compatible = "usb424,5744", /* USB5744 USB 3.0 */
234 .data = (ulong)&usb5744_data,
Venkatesh Yadav Abbarapuab690c72024-11-25 09:41:58 +0530235 }
Fabrice Gasniercd809b12022-12-12 11:44:35 +0100236};
237
238U_BOOT_DRIVER(usb_onboard_hub) = {
239 .name = "usb_onboard_hub",
240 .id = UCLASS_USB_HUB,
Venkatesh Yadav Abbarapu7c3d3e92024-11-25 09:42:02 +0530241 .bind = usb_onboard_hub_bind,
Fabrice Gasniercd809b12022-12-12 11:44:35 +0100242 .probe = usb_onboard_hub_probe,
243 .remove = usb_onboard_hub_remove,
244 .of_match = usb_onboard_hub_ids,
245 .priv_auto = sizeof(struct onboard_hub),
246};