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>
This commit is contained in:
parent
1703cf5d4f
commit
d193abf1c9
|
@ -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 {
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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,
|
||||||
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)
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
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)
|
||||||
{
|
{
|
||||||
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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue