|
12 | 12 |
|
13 | 13 | #include <linux/delay.h>
|
14 | 14 | #include <linux/gpio/consumer.h>
|
| 15 | +#include <linux/gpio/driver.h> |
15 | 16 | #include <linux/i2c.h>
|
16 | 17 | #include <linux/module.h>
|
17 | 18 | #include <linux/nls.h>
|
@@ -222,18 +223,53 @@ static const struct usb251xb_data usb2517i_data = {
|
222 | 223 | .product_str = "USB2517i",
|
223 | 224 | };
|
224 | 225 |
|
| 226 | +static int usb251xb_check_dev_children(struct device *dev, void *child) |
| 227 | +{ |
| 228 | + if (dev->type == &i2c_adapter_type) { |
| 229 | + return device_for_each_child(dev, child, |
| 230 | + usb251xb_check_dev_children); |
| 231 | + } |
| 232 | + |
| 233 | + return (dev == child); |
| 234 | +} |
| 235 | + |
| 236 | +static int usb251x_check_gpio_chip(struct usb251xb *hub) |
| 237 | +{ |
| 238 | + struct gpio_chip *gc = gpiod_to_chip(hub->gpio_reset); |
| 239 | + struct i2c_adapter *adap = hub->i2c->adapter; |
| 240 | + int ret; |
| 241 | + |
| 242 | + if (!hub->gpio_reset) |
| 243 | + return 0; |
| 244 | + |
| 245 | + if (!gc) |
| 246 | + return -EINVAL; |
| 247 | + |
| 248 | + ret = usb251xb_check_dev_children(&adap->dev, gc->parent); |
| 249 | + if (ret) { |
| 250 | + dev_err(hub->dev, "Reset GPIO chip is at the same i2c-bus\n"); |
| 251 | + return -EINVAL; |
| 252 | + } |
| 253 | + |
| 254 | + return 0; |
| 255 | +} |
| 256 | + |
225 | 257 | static void usb251xb_reset(struct usb251xb *hub, int state)
|
226 | 258 | {
|
227 | 259 | if (!hub->gpio_reset)
|
228 | 260 | return;
|
229 | 261 |
|
| 262 | + i2c_lock_bus(hub->i2c->adapter, I2C_LOCK_SEGMENT); |
| 263 | + |
230 | 264 | gpiod_set_value_cansleep(hub->gpio_reset, state);
|
231 | 265 |
|
232 | 266 | /* wait for hub recovery/stabilization */
|
233 | 267 | if (!state)
|
234 | 268 | usleep_range(500, 750); /* >=500us at power on */
|
235 | 269 | else
|
236 | 270 | usleep_range(1, 10); /* >=1us at power down */
|
| 271 | + |
| 272 | + i2c_unlock_bus(hub->i2c->adapter, I2C_LOCK_SEGMENT); |
237 | 273 | }
|
238 | 274 |
|
239 | 275 | static int usb251xb_connect(struct usb251xb *hub)
|
@@ -621,6 +657,25 @@ static int usb251xb_probe(struct usb251xb *hub)
|
621 | 657 | }
|
622 | 658 | }
|
623 | 659 |
|
| 660 | + /* |
| 661 | + * usb251x SMBus-slave SCL lane is muxed with CFG_SEL0 pin. So if anyone |
| 662 | + * tries to work with the bus at the moment the hub reset is released, |
| 663 | + * it may cause an invalid config being latched by usb251x. Particularly |
| 664 | + * one of the config modes makes the hub loading a default registers |
| 665 | + * value without SMBus-slave interface activation. If the hub |
| 666 | + * accidentally gets this mode, this will cause the driver SMBus- |
| 667 | + * functions failure. Normally we could just lock the SMBus-segment the |
| 668 | + * hub i2c-interface resides for the device-specific reset timing. But |
| 669 | + * the GPIO controller, which is used to handle the hub reset, might be |
| 670 | + * placed at the same i2c-bus segment. In this case an error should be |
| 671 | + * returned since we can't safely use the GPIO controller to clear the |
| 672 | + * reset state (it may affect the hub configuration) and we can't lock |
| 673 | + * the i2c-bus segment (it will cause a deadlock). |
| 674 | + */ |
| 675 | + err = usb251x_check_gpio_chip(hub); |
| 676 | + if (err) |
| 677 | + return err; |
| 678 | + |
624 | 679 | err = usb251xb_connect(hub);
|
625 | 680 | if (err) {
|
626 | 681 | dev_err(dev, "Failed to connect hub (%d)\n", err);
|
|
0 commit comments