Commit d193abf1 authored by Bartosz Golaszewski's avatar Bartosz Golaszewski Committed by Sekhar Nori

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: default avatarBartosz Golaszewski <bgolaszewski@baylibre.com>
Acked-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarSekhar Nori <nsekhar@ti.com>
parent 1703cf5d
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
*/ */
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/gpio/consumer.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
...@@ -40,6 +41,8 @@ struct da8xx_ohci_hcd { ...@@ -40,6 +41,8 @@ struct da8xx_ohci_hcd {
struct regulator *vbus_reg; struct regulator *vbus_reg;
struct notifier_block nb; struct notifier_block nb;
unsigned int reg_enabled; unsigned int reg_enabled;
struct gpio_desc *vbus_gpio;
struct gpio_desc *oc_gpio;
}; };
#define to_da8xx_ohci(hcd) (struct da8xx_ohci_hcd *)(hcd_to_ohci(hcd)->priv) #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,12 +89,13 @@ static void ohci_da8xx_disable(struct usb_hcd *hcd)
static int ohci_da8xx_set_power(struct usb_hcd *hcd, int on) static int ohci_da8xx_set_power(struct usb_hcd *hcd, int on)
{ {
struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
struct device *dev = hcd->self.controller; struct device *dev = hcd->self.controller;
struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
int ret; int ret;
if (hub && hub->set_power) if (da8xx_ohci->vbus_gpio) {
return hub->set_power(1, on); gpiod_set_value_cansleep(da8xx_ohci->vbus_gpio, on);
return 0;
}
if (!da8xx_ohci->vbus_reg) if (!da8xx_ohci->vbus_reg)
return 0; return 0;
...@@ -119,11 +123,9 @@ static int ohci_da8xx_set_power(struct usb_hcd *hcd, int on) ...@@ -119,11 +123,9 @@ static int ohci_da8xx_set_power(struct usb_hcd *hcd, int on)
static int ohci_da8xx_get_power(struct usb_hcd *hcd) static int ohci_da8xx_get_power(struct usb_hcd *hcd)
{ {
struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
struct device *dev = hcd->self.controller;
struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
if (hub && hub->get_power) if (da8xx_ohci->vbus_gpio)
return hub->get_power(1); return gpiod_get_value_cansleep(da8xx_ohci->vbus_gpio);
if (da8xx_ohci->vbus_reg) if (da8xx_ohci->vbus_reg)
return regulator_is_enabled(da8xx_ohci->vbus_reg); return regulator_is_enabled(da8xx_ohci->vbus_reg);
...@@ -134,13 +136,11 @@ static int ohci_da8xx_get_power(struct usb_hcd *hcd) ...@@ -134,13 +136,11 @@ static int ohci_da8xx_get_power(struct usb_hcd *hcd)
static int ohci_da8xx_get_oci(struct usb_hcd *hcd) static int ohci_da8xx_get_oci(struct usb_hcd *hcd)
{ {
struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
struct device *dev = hcd->self.controller;
struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
unsigned int flags; unsigned int flags;
int ret; int ret;
if (hub && hub->get_oci) if (da8xx_ohci->oc_gpio)
return hub->get_oci(1); return gpiod_get_value_cansleep(da8xx_ohci->oc_gpio);
if (!da8xx_ohci->vbus_reg) if (!da8xx_ohci->vbus_reg)
return 0; return 0;
...@@ -158,10 +158,8 @@ static int ohci_da8xx_get_oci(struct usb_hcd *hcd) ...@@ -158,10 +158,8 @@ static int ohci_da8xx_get_oci(struct usb_hcd *hcd)
static int ohci_da8xx_has_set_power(struct usb_hcd *hcd) static int ohci_da8xx_has_set_power(struct usb_hcd *hcd)
{ {
struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
struct device *dev = hcd->self.controller;
struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
if (hub && hub->set_power) if (da8xx_ohci->vbus_gpio)
return 1; return 1;
if (da8xx_ohci->vbus_reg) if (da8xx_ohci->vbus_reg)
...@@ -173,10 +171,8 @@ static int ohci_da8xx_has_set_power(struct usb_hcd *hcd) ...@@ -173,10 +171,8 @@ static int ohci_da8xx_has_set_power(struct usb_hcd *hcd)
static int ohci_da8xx_has_oci(struct usb_hcd *hcd) static int ohci_da8xx_has_oci(struct usb_hcd *hcd)
{ {
struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
struct device *dev = hcd->self.controller;
struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
if (hub && hub->get_oci) if (da8xx_ohci->oc_gpio)
return 1; return 1;
if (da8xx_ohci->vbus_reg) if (da8xx_ohci->vbus_reg)
...@@ -196,19 +192,6 @@ static int ohci_da8xx_has_potpgt(struct usb_hcd *hcd) ...@@ -196,19 +192,6 @@ static int ohci_da8xx_has_potpgt(struct usb_hcd *hcd)
return 0; return 0;
} }
/*
* Handle the port over-current indicator change.
*/
static void ohci_da8xx_ocic_handler(struct da8xx_ohci_root_hub *hub,
unsigned port)
{
ocic_mask |= 1 << port;
/* Once over-current is detected, the port needs to be powered down */
if (hub->get_oci(port) > 0)
hub->set_power(port, 0);
}
static int ohci_da8xx_regulator_event(struct notifier_block *nb, static int ohci_da8xx_regulator_event(struct notifier_block *nb,
unsigned long event, void *data) unsigned long event, void *data)
{ {
...@@ -223,16 +206,23 @@ static int ohci_da8xx_regulator_event(struct notifier_block *nb, ...@@ -223,16 +206,23 @@ static int ohci_da8xx_regulator_event(struct notifier_block *nb,
return 0; return 0;
} }
static irqreturn_t ohci_da8xx_oc_handler(int irq, void *data)
{
struct da8xx_ohci_hcd *da8xx_ohci = data;
if (gpiod_get_value(da8xx_ohci->oc_gpio))
gpiod_set_value(da8xx_ohci->vbus_gpio, 0);
return IRQ_HANDLED;
}
static int ohci_da8xx_register_notify(struct usb_hcd *hcd) static int ohci_da8xx_register_notify(struct usb_hcd *hcd)
{ {
struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd); struct da8xx_ohci_hcd *da8xx_ohci = to_da8xx_ohci(hcd);
struct device *dev = hcd->self.controller; struct device *dev = hcd->self.controller;
struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
int ret = 0; int ret = 0;
if (hub && hub->ocic_notify) { if (!da8xx_ohci->oc_gpio && da8xx_ohci->vbus_reg) {
ret = hub->ocic_notify(ohci_da8xx_ocic_handler);
} else if (da8xx_ohci->vbus_reg) {
da8xx_ohci->nb.notifier_call = ohci_da8xx_regulator_event; da8xx_ohci->nb.notifier_call = ohci_da8xx_regulator_event;
ret = devm_regulator_register_notifier(da8xx_ohci->vbus_reg, ret = devm_regulator_register_notifier(da8xx_ohci->vbus_reg,
&da8xx_ohci->nb); &da8xx_ohci->nb);
...@@ -244,15 +234,6 @@ static int ohci_da8xx_register_notify(struct usb_hcd *hcd) ...@@ -244,15 +234,6 @@ static int ohci_da8xx_register_notify(struct usb_hcd *hcd)
return ret; return ret;
} }
static void ohci_da8xx_unregister_notify(struct usb_hcd *hcd)
{
struct device *dev = hcd->self.controller;
struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
if (hub && hub->ocic_notify)
hub->ocic_notify(NULL);
}
static int ohci_da8xx_reset(struct usb_hcd *hcd) static int ohci_da8xx_reset(struct usb_hcd *hcd)
{ {
struct device *dev = hcd->self.controller; struct device *dev = hcd->self.controller;
...@@ -403,9 +384,9 @@ static int ohci_da8xx_probe(struct platform_device *pdev) ...@@ -403,9 +384,9 @@ static int ohci_da8xx_probe(struct platform_device *pdev)
{ {
struct da8xx_ohci_hcd *da8xx_ohci; struct da8xx_ohci_hcd *da8xx_ohci;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
int error, hcd_irq, oc_irq;
struct usb_hcd *hcd; struct usb_hcd *hcd;
struct resource *mem; struct resource *mem;
int error, irq;
hcd = usb_create_hcd(&ohci_da8xx_hc_driver, dev, dev_name(dev)); hcd = usb_create_hcd(&ohci_da8xx_hc_driver, dev, dev_name(dev));
if (!hcd) if (!hcd)
...@@ -443,6 +424,27 @@ static int ohci_da8xx_probe(struct platform_device *pdev) ...@@ -443,6 +424,27 @@ static int ohci_da8xx_probe(struct platform_device *pdev)
} }
} }
da8xx_ohci->vbus_gpio = devm_gpiod_get_optional(dev, "vbus",
GPIOD_OUT_HIGH);
if (IS_ERR(da8xx_ohci->vbus_gpio))
goto err;
da8xx_ohci->oc_gpio = devm_gpiod_get_optional(dev, "oc", GPIOD_IN);
if (IS_ERR(da8xx_ohci->oc_gpio))
goto err;
if (da8xx_ohci->oc_gpio) {
oc_irq = gpiod_to_irq(da8xx_ohci->oc_gpio);
if (oc_irq < 0)
goto err;
error = devm_request_irq(dev, oc_irq, ohci_da8xx_oc_handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
"OHCI over-current indicator", da8xx_ohci);
if (error)
goto err;
}
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
hcd->regs = devm_ioremap_resource(dev, mem); hcd->regs = devm_ioremap_resource(dev, mem);
if (IS_ERR(hcd->regs)) { if (IS_ERR(hcd->regs)) {
...@@ -452,13 +454,13 @@ static int ohci_da8xx_probe(struct platform_device *pdev) ...@@ -452,13 +454,13 @@ static int ohci_da8xx_probe(struct platform_device *pdev)
hcd->rsrc_start = mem->start; hcd->rsrc_start = mem->start;
hcd->rsrc_len = resource_size(mem); hcd->rsrc_len = resource_size(mem);
irq = platform_get_irq(pdev, 0); hcd_irq = platform_get_irq(pdev, 0);
if (irq < 0) { if (hcd_irq < 0) {
error = -ENODEV; error = -ENODEV;
goto err; goto err;
} }
error = usb_add_hcd(hcd, irq, 0); error = usb_add_hcd(hcd, hcd_irq, 0);
if (error) if (error)
goto err; goto err;
...@@ -481,7 +483,6 @@ static int ohci_da8xx_remove(struct platform_device *pdev) ...@@ -481,7 +483,6 @@ static int ohci_da8xx_remove(struct platform_device *pdev)
{ {
struct usb_hcd *hcd = platform_get_drvdata(pdev); struct usb_hcd *hcd = platform_get_drvdata(pdev);
ohci_da8xx_unregister_notify(hcd);
usb_remove_hcd(hcd); usb_remove_hcd(hcd);
usb_put_hcd(hcd); usb_put_hcd(hcd);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment