]> Git Repo - u-boot.git/blob - common/usb_onboard_hub.c
Merge branch 'master' of https://source.denx.de/u-boot/custodians/u-boot-sh
[u-boot.git] / common / usb_onboard_hub.c
1 // 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
10 #include <asm/gpio.h>
11 #include <dm.h>
12 #include <dm/device_compat.h>
13 #include <i2c.h>
14 #include <linux/delay.h>
15 #include <power/regulator.h>
16
17 #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
22 struct onboard_hub {
23         struct udevice *vdd;
24         struct gpio_desc *reset_gpio;
25 };
26
27 struct onboard_hub_data {
28         unsigned long reset_us;
29         unsigned long power_on_delay_us;
30         int (*init)(struct udevice *dev);
31 };
32
33 static 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
113 int 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
141 static int usb_onboard_hub_probe(struct udevice *dev)
142 {
143         struct onboard_hub_data *data =
144                 (struct onboard_hub_data *)dev_get_driver_data(dev);
145         struct onboard_hub *hub = dev_get_priv(dev);
146         int ret;
147
148         ret = device_get_supply_regulator(dev, "vdd-supply", &hub->vdd);
149         if (ret && ret != -ENOENT) {
150                 dev_err(dev, "can't get vdd-supply: %d\n", ret);
151                 return ret;
152         }
153
154         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         }
161
162         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;
174 err:
175         dm_gpio_set_value(hub->reset_gpio, 0);
176         return ret;
177 }
178
179 static 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 == -ENOENT) {
187                 dev_dbg(dev, "peer-hub property not present\n");
188                 return 0;
189         }
190
191         if (ret) {
192                 dev_err(dev, "peer-hub not specified\n");
193                 return ret;
194         }
195
196         off = ofnode_to_offset(phandle.node);
197         ret = fdt_node_check_compatible(fdt, off, "usb424,5744");
198         if (!ret)
199                 return 0;
200
201         return -ENODEV;
202 }
203
204 static int usb_onboard_hub_remove(struct udevice *dev)
205 {
206         struct onboard_hub *hub = dev_get_priv(dev);
207         int ret;
208
209         if (hub->reset_gpio)
210                 dm_gpio_free(hub->reset_gpio->dev, hub->reset_gpio);
211
212         ret = regulator_set_enable_if_allowed(hub->vdd, false);
213         if (ret)
214                 dev_err(dev, "can't disable vdd-supply: %d\n", ret);
215
216         return ret;
217 }
218
219 static const struct onboard_hub_data usb2514_data = {
220         .power_on_delay_us = 500,
221         .reset_us = 1,
222 };
223
224 static const struct onboard_hub_data usb5744_data = {
225         .init = usb5744_i2c_init,
226         .power_on_delay_us = 1000,
227         .reset_us = 5,
228 };
229
230 static const struct udevice_id usb_onboard_hub_ids[] = {
231         /* Use generic usbVID,PID dt-bindings (usb-device.yaml) */
232         {       .compatible = "usb424,2514",    /* USB2514B USB 2.0 */
233                 .data = (ulong)&usb2514_data,
234         }, {
235                 .compatible = "usb424,2744",    /* USB2744 USB 2.0 */
236                 .data = (ulong)&usb5744_data,
237         }, {
238                 .compatible = "usb424,5744",    /* USB5744 USB 3.0 */
239                 .data = (ulong)&usb5744_data,
240         }
241 };
242
243 U_BOOT_DRIVER(usb_onboard_hub) = {
244         .name   = "usb_onboard_hub",
245         .id     = UCLASS_USB_HUB,
246         .bind   = usb_onboard_hub_bind,
247         .probe = usb_onboard_hub_probe,
248         .remove = usb_onboard_hub_remove,
249         .of_match = usb_onboard_hub_ids,
250         .priv_auto = sizeof(struct onboard_hub),
251 };
This page took 0.038416 seconds and 4 git commands to generate.