Skip to content

Commit

Permalink
usb: ohci-da8xx: add vbus and overcurrent gpios
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Bartosz Golaszewski authored and Sekhar Nori committed Feb 12, 2019
1 parent 1703cf5 commit d193abf
Showing 1 changed file with 50 additions and 49 deletions.
99 changes: 50 additions & 49 deletions drivers/usb/host/ohci-da8xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/

#include <linux/clk.h>
#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
Expand Down Expand Up @@ -40,6 +41,8 @@ struct da8xx_ohci_hcd {
struct regulator *vbus_reg;
struct notifier_block nb;
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)
Expand Down Expand Up @@ -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)
{
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);
struct device *dev = hcd->self.controller;
int ret;

if (hub && hub->set_power)
return hub->set_power(1, on);
if (da8xx_ohci->vbus_gpio) {
gpiod_set_value_cansleep(da8xx_ohci->vbus_gpio, on);
return 0;
}

if (!da8xx_ohci->vbus_reg)
return 0;
Expand Down Expand Up @@ -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)
{
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)
return hub->get_power(1);
if (da8xx_ohci->vbus_gpio)
return gpiod_get_value_cansleep(da8xx_ohci->vbus_gpio);

if (da8xx_ohci->vbus_reg)
return regulator_is_enabled(da8xx_ohci->vbus_reg);
Expand All @@ -134,13 +136,11 @@ static int ohci_da8xx_get_power(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 device *dev = hcd->self.controller;
struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev);
unsigned int flags;
int ret;

if (hub && hub->get_oci)
return hub->get_oci(1);
if (da8xx_ohci->oc_gpio)
return gpiod_get_value_cansleep(da8xx_ohci->oc_gpio);

if (!da8xx_ohci->vbus_reg)
return 0;
Expand All @@ -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)
{
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;

if (da8xx_ohci->vbus_reg)
Expand All @@ -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)
{
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;

if (da8xx_ohci->vbus_reg)
Expand All @@ -196,19 +192,6 @@ static int ohci_da8xx_has_potpgt(struct usb_hcd *hcd)
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,
unsigned long event, void *data)
{
Expand All @@ -223,16 +206,23 @@ static int ohci_da8xx_regulator_event(struct notifier_block *nb,
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)
{
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);
int ret = 0;

if (hub && hub->ocic_notify) {
ret = hub->ocic_notify(ohci_da8xx_ocic_handler);
} else if (da8xx_ohci->vbus_reg) {
if (!da8xx_ohci->oc_gpio && da8xx_ohci->vbus_reg) {
da8xx_ohci->nb.notifier_call = ohci_da8xx_regulator_event;
ret = devm_regulator_register_notifier(da8xx_ohci->vbus_reg,
&da8xx_ohci->nb);
Expand All @@ -244,15 +234,6 @@ static int ohci_da8xx_register_notify(struct usb_hcd *hcd)
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)
{
struct device *dev = hcd->self.controller;
Expand Down Expand Up @@ -403,9 +384,9 @@ static int ohci_da8xx_probe(struct platform_device *pdev)
{
struct da8xx_ohci_hcd *da8xx_ohci;
struct device *dev = &pdev->dev;
int error, hcd_irq, oc_irq;
struct usb_hcd *hcd;
struct resource *mem;
int error, irq;

hcd = usb_create_hcd(&ohci_da8xx_hc_driver, dev, dev_name(dev));
if (!hcd)
Expand Down Expand Up @@ -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);
hcd->regs = devm_ioremap_resource(dev, mem);
if (IS_ERR(hcd->regs)) {
Expand All @@ -452,13 +454,13 @@ static int ohci_da8xx_probe(struct platform_device *pdev)
hcd->rsrc_start = mem->start;
hcd->rsrc_len = resource_size(mem);

irq = platform_get_irq(pdev, 0);
if (irq < 0) {
hcd_irq = platform_get_irq(pdev, 0);
if (hcd_irq < 0) {
error = -ENODEV;
goto err;
}

error = usb_add_hcd(hcd, irq, 0);
error = usb_add_hcd(hcd, hcd_irq, 0);
if (error)
goto err;

Expand All @@ -481,7 +483,6 @@ static int ohci_da8xx_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);

ohci_da8xx_unregister_notify(hcd);
usb_remove_hcd(hcd);
usb_put_hcd(hcd);

Expand Down

0 comments on commit d193abf

Please sign in to comment.