diff options
| author | Bartosz Golaszewski <bgolaszewski@baylibre.com> | 2019-02-11 05:36:59 -0500 |
|---|---|---|
| committer | Sekhar Nori <nsekhar@ti.com> | 2019-02-12 03:18:51 -0500 |
| commit | d193abf1c91307d8a2d5a3b984ddcf66f5dcfbea (patch) | |
| tree | 5b71978ebf3bed2ab7188a3faccd338f7ac04597 | |
| parent | 1703cf5d4fc064852d09a877ef71befb6ba386e6 (diff) | |
usb: ohci-da8xx: add vbus and overcurrent gpios
There are two users upstream which register external callbacks for
switching the port power on/off and overcurrent protection. Both
users only use two GPIOs for that. Instead of having that functionality
in the board files, move the logic into the OHCI driver - including
the interrupt handler for overcurrent detection.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Sekhar Nori <nsekhar@ti.com>
| -rw-r--r-- | drivers/usb/host/ohci-da8xx.c | 99 |
1 files changed, 50 insertions, 49 deletions
diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index e8ede0b5e3f0..ca8a94f15ac0 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | */ | 9 | */ |
| 10 | 10 | ||
| 11 | #include <linux/clk.h> | 11 | #include <linux/clk.h> |
| 12 | #include <linux/gpio/consumer.h> | ||
| 12 | #include <linux/io.h> | 13 | #include <linux/io.h> |
| 13 | #include <linux/interrupt.h> | 14 | #include <linux/interrupt.h> |
| 14 | #include <linux/jiffies.h> | 15 | #include <linux/jiffies.h> |
| @@ -40,6 +41,8 @@ struct da8xx_ohci_hcd { | |||
| 40 | struct regulator *vbus_reg; | 41 | struct regulator *vbus_reg; |
| 41 | struct notifier_block nb; | 42 | struct notifier_block nb; |
| 42 | unsigned int reg_enabled; | 43 | unsigned int reg_enabled; |
| 44 | struct gpio_desc *vbus_gpio; | ||
| 45 | struct gpio_desc *oc_gpio; | ||
| 43 | }; | 46 | }; |
| 44 | 47 | ||
| 45 | #define to_da8xx_ohci(hcd) (struct da8xx_ohci_hcd *)(hcd_to_ohci(hcd)->priv) | 48 | #define to_da8xx_ohci(hcd) (struct da8xx_ohci_hcd *)(hcd_to_ohci(hcd)->priv) |
| @@ -86,12 +89,13 @@ static void ohci_da8xx_disable(struct usb_hcd *hcd) | |||
| 86 | static int ohci_da8xx_set_power(struct usb_hcd *hcd, int on) | 89 | static int ohci_da8xx_set_power(struct usb_hcd *hcd, int on) |
| 87 | { | 90 | { |
| 88 | struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); | 91 | struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); |
| 89 | struct device *dev = hcd->self.controller; | 92 | struct device *dev = hcd->self.controller; |
| 90 | struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); | ||
| 91 | int ret; | 93 | int ret; |
| 92 | 94 | ||
| 93 | if (hub && hub->set_power) | 95 | if (da8xx_ohci->vbus_gpio) { |
| 94 | return hub->set_power(1, on); | 96 | gpiod_set_value_cansleep(da8xx_ohci->vbus_gpio, on); |
| 97 | return 0; | ||
| 98 | } | ||
| 95 | 99 | ||
| 96 | if (!da8xx_ohci->vbus_reg) | 100 | if (!da8xx_ohci->vbus_reg) |
| 97 | return 0; | 101 | return 0; |
| @@ -119,11 +123,9 @@ static int ohci_da8xx_set_power(struct usb_hcd *hcd, int on) | |||
| 119 | static int ohci_da8xx_get_power(struct usb_hcd *hcd) | 123 | static int ohci_da8xx_get_power(struct usb_hcd *hcd) |
| 120 | { | 124 | { |
| 121 | struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); | 125 | struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); |
| 122 | struct device *dev = hcd->self.controller; | ||
| 123 | struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); | ||
| 124 | 126 | ||
| 125 | if (hub && hub->get_power) | 127 | if (da8xx_ohci->vbus_gpio) |
| 126 | return hub->get_power(1); | 128 | return gpiod_get_value_cansleep(da8xx_ohci->vbus_gpio); |
| 127 | 129 | ||
| 128 | if (da8xx_ohci->vbus_reg) | 130 | if (da8xx_ohci->vbus_reg) |
| 129 | return regulator_is_enabled(da8xx_ohci->vbus_reg); | 131 | return regulator_is_enabled(da8xx_ohci->vbus_reg); |
| @@ -134,13 +136,11 @@ static int ohci_da8xx_get_power(struct usb_hcd *hcd) | |||
| 134 | static int ohci_da8xx_get_oci(struct usb_hcd *hcd) | 136 | static int ohci_da8xx_get_oci(struct usb_hcd *hcd) |
| 135 | { | 137 | { |
| 136 | struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); | 138 | struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); |
| 137 | struct device *dev = hcd->self.controller; | ||
| 138 | struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); | ||
| 139 | unsigned int flags; | 139 | unsigned int flags; |
| 140 | int ret; | 140 | int ret; |
| 141 | 141 | ||
| 142 | if (hub && hub->get_oci) | 142 | if (da8xx_ohci->oc_gpio) |
| 143 | return hub->get_oci(1); | 143 | return gpiod_get_value_cansleep(da8xx_ohci->oc_gpio); |
| 144 | 144 | ||
| 145 | if (!da8xx_ohci->vbus_reg) | 145 | if (!da8xx_ohci->vbus_reg) |
| 146 | return 0; | 146 | return 0; |
| @@ -158,10 +158,8 @@ static int ohci_da8xx_get_oci(struct usb_hcd *hcd) | |||
| 158 | static int ohci_da8xx_has_set_power(struct usb_hcd *hcd) | 158 | static int ohci_da8xx_has_set_power(struct usb_hcd *hcd) |
| 159 | { | 159 | { |
| 160 | struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); | 160 | struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); |
| 161 | struct device *dev = hcd->self.controller; | ||
| 162 | struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); | ||
| 163 | 161 | ||
| 164 | if (hub && hub->set_power) | 162 | if (da8xx_ohci->vbus_gpio) |
| 165 | return 1; | 163 | return 1; |
| 166 | 164 | ||
| 167 | if (da8xx_ohci->vbus_reg) | 165 | if (da8xx_ohci->vbus_reg) |
| @@ -173,10 +171,8 @@ static int ohci_da8xx_has_set_power(struct usb_hcd *hcd) | |||
| 173 | static int ohci_da8xx_has_oci(struct usb_hcd *hcd) | 171 | static int ohci_da8xx_has_oci(struct usb_hcd *hcd) |
| 174 | { | 172 | { |
| 175 | struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); | 173 | struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); |
| 176 | struct device *dev = hcd->self.controller; | ||
| 177 | struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); | ||
| 178 | 174 | ||
| 179 | if (hub && hub->get_oci) | 175 | if (da8xx_ohci->oc_gpio) |
| 180 | return 1; | 176 | return 1; |
| 181 | 177 | ||
| 182 | if (da8xx_ohci->vbus_reg) | 178 | if (da8xx_ohci->vbus_reg) |
| @@ -196,19 +192,6 @@ static int ohci_da8xx_has_potpgt(struct usb_hcd *hcd) | |||
| 196 | return 0; | 192 | return 0; |
| 197 | } | 193 | } |
| 198 | 194 | ||
| 199 | /* | ||
| 200 | * Handle the port over-current indicator change. | ||
| 201 | */ | ||
| 202 | static void ohci_da8xx_ocic_handler(struct da8xx_ohci_root_hub *hub, | ||
| 203 | unsigned port) | ||
| 204 | { | ||
| 205 | ocic_mask |= 1 << port; | ||
| 206 | |||
| 207 | /* Once over-current is detected, the port needs to be powered down */ | ||
| 208 | if (hub->get_oci(port) > 0) | ||
| 209 | hub->set_power(port, 0); | ||
| 210 | } | ||
| 211 | |||
| 212 | static int ohci_da8xx_regulator_event(struct notifier_block *nb, | 195 | static int ohci_da8xx_regulator_event(struct notifier_block *nb, |
| 213 | unsigned long event, void *data) | 196 | unsigned long event, void *data) |
| 214 | { | 197 | { |
| @@ -223,16 +206,23 @@ static int ohci_da8xx_regulator_event(struct notifier_block *nb, | |||
| 223 | return 0; | 206 | return 0; |
| 224 | } | 207 | } |
| 225 | 208 | ||
| 209 | static irqreturn_t ohci_da8xx_oc_handler(int irq, void *data) | ||
| 210 | { | ||
| 211 | struct da8xx_ohci_hcd *da8xx_ohci = data; | ||
| 212 | |||
| 213 | if (gpiod_get_value(da8xx_ohci->oc_gpio)) | ||
| 214 | gpiod_set_value(da8xx_ohci->vbus_gpio, 0); | ||
| 215 | |||
| 216 | return IRQ_HANDLED; | ||
| 217 | } | ||
| 218 | |||
| 226 | static int ohci_da8xx_register_notify(struct usb_hcd *hcd) | 219 | static int ohci_da8xx_register_notify(struct usb_hcd *hcd) |
| 227 | { | 220 | { |
| 228 | struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); | 221 | struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); |
| 229 | struct device *dev = hcd->self.controller; | 222 | struct device *dev = hcd->self.controller; |
| 230 | struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); | ||
| 231 | int ret = 0; | 223 | int ret = 0; |
| 232 | 224 | ||
| 233 | if (hub && hub->ocic_notify) { | 225 | if (!da8xx_ohci->oc_gpio && da8xx_ohci->vbus_reg) { |
| 234 | ret = hub->ocic_notify(ohci_da8xx_ocic_handler); | ||
| 235 | } else if (da8xx_ohci->vbus_reg) { | ||
| 236 | da8xx_ohci->nb.notifier_call = ohci_da8xx_regulator_event; | 226 | da8xx_ohci->nb.notifier_call = ohci_da8xx_regulator_event; |
| 237 | ret = devm_regulator_register_notifier(da8xx_ohci->vbus_reg, | 227 | ret = devm_regulator_register_notifier(da8xx_ohci->vbus_reg, |
| 238 | &da8xx_ohci->nb); | 228 | &da8xx_ohci->nb); |
| @@ -244,15 +234,6 @@ static int ohci_da8xx_register_notify(struct usb_hcd *hcd) | |||
| 244 | return ret; | 234 | return ret; |
| 245 | } | 235 | } |
| 246 | 236 | ||
| 247 | static void ohci_da8xx_unregister_notify(struct usb_hcd *hcd) | ||
| 248 | { | ||
| 249 | struct device *dev = hcd->self.controller; | ||
| 250 | struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); | ||
| 251 | |||
| 252 | if (hub && hub->ocic_notify) | ||
| 253 | hub->ocic_notify(NULL); | ||
| 254 | } | ||
| 255 | |||
| 256 | static int ohci_da8xx_reset(struct usb_hcd *hcd) | 237 | static int ohci_da8xx_reset(struct usb_hcd *hcd) |
| 257 | { | 238 | { |
| 258 | struct device *dev = hcd->self.controller; | 239 | struct device *dev = hcd->self.controller; |
| @@ -403,9 +384,9 @@ static int ohci_da8xx_probe(struct platform_device *pdev) | |||
| 403 | { | 384 | { |
| 404 | struct da8xx_ohci_hcd *da8xx_ohci; | 385 | struct da8xx_ohci_hcd *da8xx_ohci; |
| 405 | struct device *dev = &pdev->dev; | 386 | struct device *dev = &pdev->dev; |
| 387 | int error, hcd_irq, oc_irq; | ||
| 406 | struct usb_hcd *hcd; | 388 | struct usb_hcd *hcd; |
| 407 | struct resource *mem; | 389 | struct resource *mem; |
| 408 | int error, irq; | ||
| 409 | 390 | ||
| 410 | hcd = usb_create_hcd(&ohci_da8xx_hc_driver, dev, dev_name(dev)); | 391 | hcd = usb_create_hcd(&ohci_da8xx_hc_driver, dev, dev_name(dev)); |
| 411 | if (!hcd) | 392 | if (!hcd) |
| @@ -443,6 +424,27 @@ static int ohci_da8xx_probe(struct platform_device *pdev) | |||
| 443 | } | 424 | } |
| 444 | } | 425 | } |
| 445 | 426 | ||
| 427 | da8xx_ohci->vbus_gpio = devm_gpiod_get_optional(dev, "vbus", | ||
| 428 | GPIOD_OUT_HIGH); | ||
| 429 | if (IS_ERR(da8xx_ohci->vbus_gpio)) | ||
| 430 | goto err; | ||
| 431 | |||
| 432 | da8xx_ohci->oc_gpio = devm_gpiod_get_optional(dev, "oc", GPIOD_IN); | ||
| 433 | if (IS_ERR(da8xx_ohci->oc_gpio)) | ||
| 434 | goto err; | ||
| 435 | |||
| 436 | if (da8xx_ohci->oc_gpio) { | ||
| 437 | oc_irq = gpiod_to_irq(da8xx_ohci->oc_gpio); | ||
| 438 | if (oc_irq < 0) | ||
| 439 | goto err; | ||
| 440 | |||
| 441 | error = devm_request_irq(dev, oc_irq, ohci_da8xx_oc_handler, | ||
| 442 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, | ||
| 443 | "OHCI over-current indicator", da8xx_ohci); | ||
| 444 | if (error) | ||
| 445 | goto err; | ||
| 446 | } | ||
| 447 | |||
| 446 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 448 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 447 | hcd->regs = devm_ioremap_resource(dev, mem); | 449 | hcd->regs = devm_ioremap_resource(dev, mem); |
| 448 | if (IS_ERR(hcd->regs)) { | 450 | if (IS_ERR(hcd->regs)) { |
| @@ -452,13 +454,13 @@ static int ohci_da8xx_probe(struct platform_device *pdev) | |||
| 452 | hcd->rsrc_start = mem->start; | 454 | hcd->rsrc_start = mem->start; |
| 453 | hcd->rsrc_len = resource_size(mem); | 455 | hcd->rsrc_len = resource_size(mem); |
| 454 | 456 | ||
| 455 | irq = platform_get_irq(pdev, 0); | 457 | hcd_irq = platform_get_irq(pdev, 0); |
| 456 | if (irq < 0) { | 458 | if (hcd_irq < 0) { |
| 457 | error = -ENODEV; | 459 | error = -ENODEV; |
| 458 | goto err; | 460 | goto err; |
| 459 | } | 461 | } |
| 460 | 462 | ||
| 461 | error = usb_add_hcd(hcd, irq, 0); | 463 | error = usb_add_hcd(hcd, hcd_irq, 0); |
| 462 | if (error) | 464 | if (error) |
| 463 | goto err; | 465 | goto err; |
| 464 | 466 | ||
| @@ -481,7 +483,6 @@ static int ohci_da8xx_remove(struct platform_device *pdev) | |||
| 481 | { | 483 | { |
| 482 | struct usb_hcd *hcd = platform_get_drvdata(pdev); | 484 | struct usb_hcd *hcd = platform_get_drvdata(pdev); |
| 483 | 485 | ||
| 484 | ohci_da8xx_unregister_notify(hcd); | ||
| 485 | usb_remove_hcd(hcd); | 486 | usb_remove_hcd(hcd); |
| 486 | usb_put_hcd(hcd); | 487 | usb_put_hcd(hcd); |
| 487 | 488 | ||
