diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/input/keyboard/davinci_keyscan.c | 4 | ||||
-rw-r--r-- | drivers/irqchip/Kconfig | 10 | ||||
-rw-r--r-- | drivers/irqchip/Makefile | 2 | ||||
-rw-r--r-- | drivers/irqchip/irq-davinci-aintc.c | 163 | ||||
-rw-r--r-- | drivers/irqchip/irq-davinci-cp-intc.c | 260 | ||||
-rw-r--r-- | drivers/usb/host/ohci-da8xx.c | 118 |
6 files changed, 495 insertions, 62 deletions
diff --git a/drivers/input/keyboard/davinci_keyscan.c b/drivers/input/keyboard/davinci_keyscan.c index b20a5d044caa..b4db72f833ca 100644 --- a/drivers/input/keyboard/davinci_keyscan.c +++ b/drivers/input/keyboard/davinci_keyscan.c @@ -32,10 +32,6 @@ #include <linux/errno.h> #include <linux/slab.h> -#include <asm/irq.h> - -#include <mach/hardware.h> -#include <mach/irqs.h> #include <linux/platform_data/keyscan-davinci.h> /* Key scan registers */ diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 3d1e60779078..48fc5024c073 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -129,6 +129,16 @@ config BRCMSTB_L2_IRQ select GENERIC_IRQ_CHIP select IRQ_DOMAIN +config DAVINCI_AINTC + bool + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN + +config DAVINCI_CP_INTC + bool + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN + config DW_APB_ICTL bool select GENERIC_IRQ_CHIP diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index c93713d24b86..e6cd0c98eff2 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -6,6 +6,8 @@ obj-$(CONFIG_ATH79) += irq-ath79-cpu.o obj-$(CONFIG_ATH79) += irq-ath79-misc.o obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2836.o +obj-$(CONFIG_DAVINCI_AINTC) += irq-davinci-aintc.o +obj-$(CONFIG_DAVINCI_CP_INTC) += irq-davinci-cp-intc.o obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o obj-$(CONFIG_FARADAY_FTINTC010) += irq-ftintc010.o obj-$(CONFIG_ARCH_HIP04) += irq-hip04.o diff --git a/drivers/irqchip/irq-davinci-aintc.c b/drivers/irqchip/irq-davinci-aintc.c new file mode 100644 index 000000000000..810ccc4fe476 --- /dev/null +++ b/drivers/irqchip/irq-davinci-aintc.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Copyright (C) 2006, 2019 Texas Instruments. +// +// Interrupt handler for DaVinci boards. + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqchip/irq-davinci-aintc.h> +#include <linux/io.h> +#include <linux/irqdomain.h> + +#include <asm/exception.h> + +#define DAVINCI_AINTC_FIQ_REG0 0x00 +#define DAVINCI_AINTC_FIQ_REG1 0x04 +#define DAVINCI_AINTC_IRQ_REG0 0x08 +#define DAVINCI_AINTC_IRQ_REG1 0x0c +#define DAVINCI_AINTC_IRQ_IRQENTRY 0x14 +#define DAVINCI_AINTC_IRQ_ENT_REG0 0x18 +#define DAVINCI_AINTC_IRQ_ENT_REG1 0x1c +#define DAVINCI_AINTC_IRQ_INCTL_REG 0x20 +#define DAVINCI_AINTC_IRQ_EABASE_REG 0x24 +#define DAVINCI_AINTC_IRQ_INTPRI0_REG 0x30 +#define DAVINCI_AINTC_IRQ_INTPRI7_REG 0x4c + +static void __iomem *davinci_aintc_base; +static struct irq_domain *davinci_aintc_irq_domain; + +static inline void davinci_aintc_writel(unsigned long value, int offset) +{ + writel_relaxed(value, davinci_aintc_base + offset); +} + +static inline unsigned long davinci_aintc_readl(int offset) +{ + return readl_relaxed(davinci_aintc_base + offset); +} + +static __init void +davinci_aintc_setup_gc(void __iomem *base, + unsigned int irq_start, unsigned int num) +{ + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + + gc = irq_get_domain_generic_chip(davinci_aintc_irq_domain, irq_start); + gc->reg_base = base; + gc->irq_base = irq_start; + + ct = gc->chip_types; + ct->chip.irq_ack = irq_gc_ack_set_bit; + ct->chip.irq_mask = irq_gc_mask_clr_bit; + ct->chip.irq_unmask = irq_gc_mask_set_bit; + + ct->regs.ack = DAVINCI_AINTC_IRQ_REG0; + ct->regs.mask = DAVINCI_AINTC_IRQ_ENT_REG0; + irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, + IRQ_NOREQUEST | IRQ_NOPROBE, 0); +} + +static asmlinkage void __exception_irq_entry +davinci_aintc_handle_irq(struct pt_regs *regs) +{ + int irqnr = davinci_aintc_readl(DAVINCI_AINTC_IRQ_IRQENTRY); + + /* + * Use the formula for entry vector index generation from section + * 8.3.3 of the manual. + */ + irqnr >>= 2; + irqnr -= 1; + + handle_domain_irq(davinci_aintc_irq_domain, irqnr, regs); +} + +/* ARM Interrupt Controller Initialization */ +void __init davinci_aintc_init(const struct davinci_aintc_config *config) +{ + unsigned int irq_off, reg_off, prio, shift; + void __iomem *req; + int ret, irq_base; + const u8 *prios; + + req = request_mem_region(config->reg.start, + resource_size(&config->reg), + "davinci-cp-intc"); + if (!req) { + pr_err("%s: register range busy\n", __func__); + return; + } + + davinci_aintc_base = ioremap(config->reg.start, + resource_size(&config->reg)); + if (!davinci_aintc_base) { + pr_err("%s: unable to ioremap register range\n", __func__); + return; + } + + /* Clear all interrupt requests */ + davinci_aintc_writel(~0x0, DAVINCI_AINTC_FIQ_REG0); + davinci_aintc_writel(~0x0, DAVINCI_AINTC_FIQ_REG1); + davinci_aintc_writel(~0x0, DAVINCI_AINTC_IRQ_REG0); + davinci_aintc_writel(~0x0, DAVINCI_AINTC_IRQ_REG1); + + /* Disable all interrupts */ + davinci_aintc_writel(0x0, DAVINCI_AINTC_IRQ_ENT_REG0); + davinci_aintc_writel(0x0, DAVINCI_AINTC_IRQ_ENT_REG1); + + /* Interrupts disabled immediately, IRQ entry reflects all */ + davinci_aintc_writel(0x0, DAVINCI_AINTC_IRQ_INCTL_REG); + + /* we don't use the hardware vector table, just its entry addresses */ + davinci_aintc_writel(0, DAVINCI_AINTC_IRQ_EABASE_REG); + + /* Clear all interrupt requests */ + davinci_aintc_writel(~0x0, DAVINCI_AINTC_FIQ_REG0); + davinci_aintc_writel(~0x0, DAVINCI_AINTC_FIQ_REG1); + davinci_aintc_writel(~0x0, DAVINCI_AINTC_IRQ_REG0); + davinci_aintc_writel(~0x0, DAVINCI_AINTC_IRQ_REG1); + + prios = config->prios; + for (reg_off = DAVINCI_AINTC_IRQ_INTPRI0_REG; + reg_off <= DAVINCI_AINTC_IRQ_INTPRI7_REG; reg_off += 4) { + for (shift = 0, prio = 0; shift < 32; shift += 4, prios++) + prio |= (*prios & 0x07) << shift; + davinci_aintc_writel(prio, reg_off); + } + + irq_base = irq_alloc_descs(-1, 0, config->num_irqs, 0); + if (irq_base < 0) { + pr_err("%s: unable to allocate interrupt descriptors: %d\n", + __func__, irq_base); + return; + } + + davinci_aintc_irq_domain = irq_domain_add_legacy(NULL, + config->num_irqs, irq_base, 0, + &irq_domain_simple_ops, NULL); + if (!davinci_aintc_irq_domain) { + pr_err("%s: unable to create interrupt domain\n", __func__); + return; + } + + ret = irq_alloc_domain_generic_chips(davinci_aintc_irq_domain, 32, 1, + "AINTC", handle_edge_irq, + IRQ_NOREQUEST | IRQ_NOPROBE, 0, 0); + if (ret) { + pr_err("%s: unable to allocate generic irq chips for domain\n", + __func__); + return; + } + + for (irq_off = 0, reg_off = 0; + irq_off < config->num_irqs; + irq_off += 32, reg_off += 0x04) + davinci_aintc_setup_gc(davinci_aintc_base + reg_off, + irq_base + irq_off, 32); + + set_handle_irq(davinci_aintc_handle_irq); +} diff --git a/drivers/irqchip/irq-davinci-cp-intc.c b/drivers/irqchip/irq-davinci-cp-intc.c new file mode 100644 index 000000000000..276da2772e7f --- /dev/null +++ b/drivers/irqchip/irq-davinci-cp-intc.c @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Author: Steve Chen <schen@mvista.com> +// Copyright (C) 2008-2009, MontaVista Software, Inc. <source@mvista.com> +// Author: Bartosz Golaszewski <bgolaszewski@baylibre.com> +// Copyright (C) 2019, Texas Instruments +// +// TI Common Platform Interrupt Controller (cp_intc) driver + +#include <linux/export.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/irqchip.h> +#include <linux/irqchip/irq-davinci-cp-intc.h> +#include <linux/irqdomain.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> + +#include <asm/exception.h> + +#define DAVINCI_CP_INTC_CTRL 0x04 +#define DAVINCI_CP_INTC_HOST_CTRL 0x0c +#define DAVINCI_CP_INTC_GLOBAL_ENABLE 0x10 +#define DAVINCI_CP_INTC_SYS_STAT_IDX_CLR 0x24 +#define DAVINCI_CP_INTC_SYS_ENABLE_IDX_SET 0x28 +#define DAVINCI_CP_INTC_SYS_ENABLE_IDX_CLR 0x2c +#define DAVINCI_CP_INTC_HOST_ENABLE_IDX_SET 0x34 +#define DAVINCI_CP_INTC_HOST_ENABLE_IDX_CLR 0x38 +#define DAVINCI_CP_INTC_PRIO_IDX 0x80 +#define DAVINCI_CP_INTC_SYS_STAT_CLR(n) (0x0280 + (n << 2)) +#define DAVINCI_CP_INTC_SYS_ENABLE_CLR(n) (0x0380 + (n << 2)) +#define DAVINCI_CP_INTC_CHAN_MAP(n) (0x0400 + (n << 2)) +#define DAVINCI_CP_INTC_SYS_POLARITY(n) (0x0d00 + (n << 2)) +#define DAVINCI_CP_INTC_SYS_TYPE(n) (0x0d80 + (n << 2)) +#define DAVINCI_CP_INTC_HOST_ENABLE(n) (0x1500 + (n << 2)) +#define DAVINCI_CP_INTC_PRI_INDX_MASK GENMASK(9, 0) +#define DAVINCI_CP_INTC_GPIR_NONE BIT(31) + +static void __iomem *davinci_cp_intc_base; +static struct irq_domain *davinci_cp_intc_irq_domain; + +static inline unsigned int davinci_cp_intc_read(unsigned int offset) +{ + return readl_relaxed(davinci_cp_intc_base + offset); +} + +static inline void davinci_cp_intc_write(unsigned long value, + unsigned int offset) +{ + writel_relaxed(value, davinci_cp_intc_base + offset); +} + +static void davinci_cp_intc_ack_irq(struct irq_data *d) +{ + davinci_cp_intc_write(d->hwirq, DAVINCI_CP_INTC_SYS_STAT_IDX_CLR); +} + +static void davinci_cp_intc_mask_irq(struct irq_data *d) +{ + /* XXX don't know why we need to disable nIRQ here... */ + davinci_cp_intc_write(1, DAVINCI_CP_INTC_HOST_ENABLE_IDX_CLR); + davinci_cp_intc_write(d->hwirq, DAVINCI_CP_INTC_SYS_ENABLE_IDX_CLR); + davinci_cp_intc_write(1, DAVINCI_CP_INTC_HOST_ENABLE_IDX_SET); +} + +static void davinci_cp_intc_unmask_irq(struct irq_data *d) +{ + davinci_cp_intc_write(d->hwirq, DAVINCI_CP_INTC_SYS_ENABLE_IDX_SET); +} + +static int davinci_cp_intc_set_irq_type(struct irq_data *d, + unsigned int flow_type) +{ + unsigned int reg, mask, polarity, type; + + reg = BIT_WORD(d->hwirq); + mask = BIT_MASK(d->hwirq); + polarity = davinci_cp_intc_read(DAVINCI_CP_INTC_SYS_POLARITY(reg)); + type = davinci_cp_intc_read(DAVINCI_CP_INTC_SYS_TYPE(reg)); + + switch (flow_type) { + case IRQ_TYPE_EDGE_RISING: + polarity |= mask; + type |= mask; + break; + case IRQ_TYPE_EDGE_FALLING: + polarity &= ~mask; + type |= mask; + break; + case IRQ_TYPE_LEVEL_HIGH: + polarity |= mask; + type &= ~mask; + break; + case IRQ_TYPE_LEVEL_LOW: + polarity &= ~mask; + type &= ~mask; + break; + default: + return -EINVAL; + } + + davinci_cp_intc_write(polarity, DAVINCI_CP_INTC_SYS_POLARITY(reg)); + davinci_cp_intc_write(type, DAVINCI_CP_INTC_SYS_TYPE(reg)); + + return 0; +} + +static struct irq_chip davinci_cp_intc_irq_chip = { + .name = "cp_intc", + .irq_ack = davinci_cp_intc_ack_irq, + .irq_mask = davinci_cp_intc_mask_irq, + .irq_unmask = davinci_cp_intc_unmask_irq, + .irq_set_type = davinci_cp_intc_set_irq_type, + .flags = IRQCHIP_SKIP_SET_WAKE, +}; + +static asmlinkage void __exception_irq_entry +davinci_cp_intc_handle_irq(struct pt_regs *regs) +{ + int gpir, irqnr, none; + + /* + * The interrupt number is in first ten bits. The NONE field set to 1 + * indicates a spurious irq. + */ + + gpir = davinci_cp_intc_read(DAVINCI_CP_INTC_PRIO_IDX); + irqnr = gpir & DAVINCI_CP_INTC_PRI_INDX_MASK; + none = gpir & DAVINCI_CP_INTC_GPIR_NONE; + + if (unlikely(none)) { + pr_err_once("%s: spurious irq!\n", __func__); + return; + } + + handle_domain_irq(davinci_cp_intc_irq_domain, irqnr, regs); +} + +static int davinci_cp_intc_host_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + pr_debug("cp_intc_host_map(%d, 0x%lx)\n", virq, hw); + + irq_set_chip(virq, &davinci_cp_intc_irq_chip); + irq_set_probe(virq); + irq_set_handler(virq, handle_edge_irq); + + return 0; +} + +static const struct irq_domain_ops davinci_cp_intc_irq_domain_ops = { + .map = davinci_cp_intc_host_map, + .xlate = irq_domain_xlate_onetwocell, +}; + +static int __init +davinci_cp_intc_do_init(const struct davinci_cp_intc_config *config, + struct device_node *node) +{ + unsigned int num_regs = BITS_TO_LONGS(config->num_irqs); + int offset, irq_base; + void __iomem *req; + + req = request_mem_region(config->reg.start, + resource_size(&config->reg), + "davinci-cp-intc"); + if (!req) { + pr_err("%s: register range busy\n", __func__); + return -EBUSY; + } + + davinci_cp_intc_base = ioremap(config->reg.start, + resource_size(&config->reg)); + if (!davinci_cp_intc_base) { + pr_err("%s: unable to ioremap register range\n", __func__); + return -EINVAL; + } + + davinci_cp_intc_write(0, DAVINCI_CP_INTC_GLOBAL_ENABLE); + + /* Disable all host interrupts */ + davinci_cp_intc_write(0, DAVINCI_CP_INTC_HOST_ENABLE(0)); + + /* Disable system interrupts */ + for (offset = 0; offset < num_regs; offset++) + davinci_cp_intc_write(~0, + DAVINCI_CP_INTC_SYS_ENABLE_CLR(offset)); + + /* Set to normal mode, no nesting, no priority hold */ + davinci_cp_intc_write(0, DAVINCI_CP_INTC_CTRL); + davinci_cp_intc_write(0, DAVINCI_CP_INTC_HOST_CTRL); + + /* Clear system interrupt status */ + for (offset = 0; offset < num_regs; offset++) + davinci_cp_intc_write(~0, + DAVINCI_CP_INTC_SYS_STAT_CLR(offset)); + + /* Enable nIRQ (what about nFIQ?) */ + davinci_cp_intc_write(1, DAVINCI_CP_INTC_HOST_ENABLE_IDX_SET); + + /* Default all priorities to channel 7. */ + num_regs = (config->num_irqs + 3) >> 2; /* 4 channels per register */ + for (offset = 0; offset < num_regs; offset++) + davinci_cp_intc_write(0x07070707, + DAVINCI_CP_INTC_CHAN_MAP(offset)); + + irq_base = irq_alloc_descs(-1, 0, config->num_irqs, 0); + if (irq_base < 0) { + pr_err("%s: unable to allocate interrupt descriptors: %d\n", + __func__, irq_base); + return irq_base; + } + + davinci_cp_intc_irq_domain = irq_domain_add_legacy( + node, config->num_irqs, irq_base, 0, + &davinci_cp_intc_irq_domain_ops, NULL); + + if (!davinci_cp_intc_irq_domain) { + pr_err("%s: unable to create an interrupt domain\n", __func__); + return -EINVAL; + } + + set_handle_irq(davinci_cp_intc_handle_irq); + + /* Enable global interrupt */ + davinci_cp_intc_write(1, DAVINCI_CP_INTC_GLOBAL_ENABLE); + + return 0; +} + +int __init davinci_cp_intc_init(const struct davinci_cp_intc_config *config) +{ + return davinci_cp_intc_do_init(config, NULL); +} + +static int __init davinci_cp_intc_of_init(struct device_node *node, + struct device_node *parent) +{ + struct davinci_cp_intc_config config = { }; + int ret; + + ret = of_address_to_resource(node, 0, &config.reg); + if (ret) { + pr_err("%s: unable to get the register range from device-tree\n", + __func__); + return ret; + } + + ret = of_property_read_u32(node, "ti,intc-size", &config.num_irqs); + if (ret) { + pr_err("%s: unable to read the 'ti,intc-size' property\n", + __func__); + return ret; + } + + return davinci_cp_intc_do_init(&config, node); +} +IRQCHIP_DECLARE(cp_intc, "ti,cp-intc", davinci_cp_intc_of_init); diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index a55cbba40a5a..ca8a94f15ac0 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -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> @@ -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) @@ -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; @@ -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); @@ -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; @@ -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) @@ -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) @@ -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) { @@ -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); @@ -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; @@ -402,34 +383,35 @@ MODULE_DEVICE_TABLE(of, da8xx_ohci_ids); 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, &pdev->dev, - dev_name(&pdev->dev)); + + hcd = usb_create_hcd(&ohci_da8xx_hc_driver, dev, dev_name(dev)); if (!hcd) return -ENOMEM; da8xx_ohci = to_da8xx_ohci(hcd); da8xx_ohci->hcd = hcd; - da8xx_ohci->usb11_clk = devm_clk_get(&pdev->dev, NULL); + da8xx_ohci->usb11_clk = devm_clk_get(dev, NULL); if (IS_ERR(da8xx_ohci->usb11_clk)) { error = PTR_ERR(da8xx_ohci->usb11_clk); if (error != -EPROBE_DEFER) - dev_err(&pdev->dev, "Failed to get clock.\n"); + dev_err(dev, "Failed to get clock.\n"); goto err; } - da8xx_ohci->usb11_phy = devm_phy_get(&pdev->dev, "usb-phy"); + da8xx_ohci->usb11_phy = devm_phy_get(dev, "usb-phy"); if (IS_ERR(da8xx_ohci->usb11_phy)) { error = PTR_ERR(da8xx_ohci->usb11_phy); if (error != -EPROBE_DEFER) - dev_err(&pdev->dev, "Failed to get phy.\n"); + dev_err(dev, "Failed to get phy.\n"); goto err; } - da8xx_ohci->vbus_reg = devm_regulator_get_optional(&pdev->dev, "vbus"); + da8xx_ohci->vbus_reg = devm_regulator_get_optional(dev, "vbus"); if (IS_ERR(da8xx_ohci->vbus_reg)) { error = PTR_ERR(da8xx_ohci->vbus_reg); if (error == -ENODEV) { @@ -437,13 +419,34 @@ static int ohci_da8xx_probe(struct platform_device *pdev) } else if (error == -EPROBE_DEFER) { goto err; } else { - dev_err(&pdev->dev, "Failed to get regulator\n"); + dev_err(dev, "Failed to get regulator\n"); goto err; } } + 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(&pdev->dev, mem); + hcd->regs = devm_ioremap_resource(dev, mem); if (IS_ERR(hcd->regs)) { error = PTR_ERR(hcd->regs); goto err; @@ -451,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; @@ -480,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); |