diff options
Diffstat (limited to 'drivers/gpio/gpio-ixp4xx.c')
| -rw-r--r-- | drivers/gpio/gpio-ixp4xx.c | 195 |
1 files changed, 117 insertions, 78 deletions
diff --git a/drivers/gpio/gpio-ixp4xx.c b/drivers/gpio/gpio-ixp4xx.c index b3b050604e0b..f34d87869c8b 100644 --- a/drivers/gpio/gpio-ixp4xx.c +++ b/drivers/gpio/gpio-ixp4xx.c @@ -6,18 +6,16 @@ // based on previous work and know-how from: // Deepak Saxena <dsaxena@plexity.net> +#include <linux/bitops.h> #include <linux/gpio/driver.h> +#include <linux/gpio/generic.h> #include <linux/io.h> #include <linux/irq.h> #include <linux/irqdomain.h> #include <linux/irqchip.h> #include <linux/of_irq.h> #include <linux/platform_device.h> -#include <linux/bitops.h> -/* Include that go away with DT transition */ -#include <linux/irqchip/irq-ixp4xx.h> - -#include <asm/mach-types.h> +#include <linux/property.h> #define IXP4XX_REG_GPOUT 0x00 #define IXP4XX_REG_GPOE 0x04 @@ -42,19 +40,29 @@ #define IXP4XX_GPIO_STYLE_MASK GENMASK(2, 0) #define IXP4XX_GPIO_STYLE_SIZE 3 +/* + * Clock output control register defines. + */ +#define IXP4XX_GPCLK_CLK0DC_SHIFT 0 +#define IXP4XX_GPCLK_CLK0TC_SHIFT 4 +#define IXP4XX_GPCLK_CLK0_MASK GENMASK(7, 0) +#define IXP4XX_GPCLK_MUX14 BIT(8) +#define IXP4XX_GPCLK_CLK1DC_SHIFT 16 +#define IXP4XX_GPCLK_CLK1TC_SHIFT 20 +#define IXP4XX_GPCLK_CLK1_MASK GENMASK(23, 16) +#define IXP4XX_GPCLK_MUX15 BIT(24) + /** * struct ixp4xx_gpio - IXP4 GPIO state container + * @chip: generic GPIO chip for this instance * @dev: containing device for this instance - * @fwnode: the fwnode for this GPIO chip - * @gc: gpiochip for this instance * @base: remapped I/O-memory base * @irq_edge: Each bit represents an IRQ: 1: edge-triggered, * 0: level triggered */ struct ixp4xx_gpio { + struct gpio_generic_chip chip; struct device *dev; - struct fwnode_handle *fwnode; - struct gpio_chip gc; void __iomem *base; unsigned long long irq_edge; }; @@ -67,6 +75,14 @@ static void ixp4xx_gpio_irq_ack(struct irq_data *d) __raw_writel(BIT(d->hwirq), g->base + IXP4XX_REG_GPIS); } +static void ixp4xx_gpio_mask_irq(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + + irq_chip_mask_parent(d); + gpiochip_disable_irq(gc, d->hwirq); +} + static void ixp4xx_gpio_irq_unmask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); @@ -76,6 +92,7 @@ static void ixp4xx_gpio_irq_unmask(struct irq_data *d) if (!(g->irq_edge & BIT(d->hwirq))) ixp4xx_gpio_irq_ack(d); + gpiochip_enable_irq(gc, d->hwirq); irq_chip_unmask_parent(d); } @@ -84,7 +101,6 @@ static int ixp4xx_gpio_irq_set_type(struct irq_data *d, unsigned int type) struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct ixp4xx_gpio *g = gpiochip_get_data(gc); int line = d->hwirq; - unsigned long flags; u32 int_style; u32 int_reg; u32 val; @@ -128,37 +144,37 @@ static int ixp4xx_gpio_irq_set_type(struct irq_data *d, unsigned int type) int_reg = IXP4XX_REG_GPIT1; } - spin_lock_irqsave(&g->gc.bgpio_lock, flags); + scoped_guard(gpio_generic_lock_irqsave, &g->chip) { + /* Clear the style for the appropriate pin */ + val = __raw_readl(g->base + int_reg); + val &= ~(IXP4XX_GPIO_STYLE_MASK << (line * IXP4XX_GPIO_STYLE_SIZE)); + __raw_writel(val, g->base + int_reg); - /* Clear the style for the appropriate pin */ - val = __raw_readl(g->base + int_reg); - val &= ~(IXP4XX_GPIO_STYLE_MASK << (line * IXP4XX_GPIO_STYLE_SIZE)); - __raw_writel(val, g->base + int_reg); + __raw_writel(BIT(line), g->base + IXP4XX_REG_GPIS); - __raw_writel(BIT(line), g->base + IXP4XX_REG_GPIS); + /* Set the new style */ + val = __raw_readl(g->base + int_reg); + val |= (int_style << (line * IXP4XX_GPIO_STYLE_SIZE)); + __raw_writel(val, g->base + int_reg); - /* Set the new style */ - val = __raw_readl(g->base + int_reg); - val |= (int_style << (line * IXP4XX_GPIO_STYLE_SIZE)); - __raw_writel(val, g->base + int_reg); - - /* Force-configure this line as an input */ - val = __raw_readl(g->base + IXP4XX_REG_GPOE); - val |= BIT(d->hwirq); - __raw_writel(val, g->base + IXP4XX_REG_GPOE); - - spin_unlock_irqrestore(&g->gc.bgpio_lock, flags); + /* Force-configure this line as an input */ + val = __raw_readl(g->base + IXP4XX_REG_GPOE); + val |= BIT(d->hwirq); + __raw_writel(val, g->base + IXP4XX_REG_GPOE); + } /* This parent only accept level high (asserted) */ return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH); } -static struct irq_chip ixp4xx_gpio_irqchip = { +static const struct irq_chip ixp4xx_gpio_irqchip = { .name = "IXP4GPIO", .irq_ack = ixp4xx_gpio_irq_ack, - .irq_mask = irq_chip_mask_parent, + .irq_mask = ixp4xx_gpio_mask_irq, .irq_unmask = ixp4xx_gpio_irq_unmask, .irq_set_type = ixp4xx_gpio_irq_set_type, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static int ixp4xx_gpio_child_to_parent_hwirq(struct gpio_chip *gc, @@ -188,13 +204,16 @@ static int ixp4xx_gpio_child_to_parent_hwirq(struct gpio_chip *gc, static int ixp4xx_gpio_probe(struct platform_device *pdev) { + struct gpio_generic_chip_config config; unsigned long flags; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct irq_domain *parent; - struct resource *res; struct ixp4xx_gpio *g; struct gpio_irq_chip *girq; + struct device_node *irq_parent; + bool clk_14, clk_15; + u32 val; int ret; g = devm_kzalloc(dev, sizeof(*g), GFP_KERNEL); @@ -202,46 +221,63 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev) return -ENOMEM; g->dev = dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - g->base = devm_ioremap_resource(dev, res); + g->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(g->base)) return PTR_ERR(g->base); + irq_parent = of_irq_find_parent(np); + if (!irq_parent) { + dev_err(dev, "no IRQ parent node\n"); + return -ENODEV; + } + parent = irq_find_host(irq_parent); + if (!parent) { + dev_err(dev, "no IRQ parent domain\n"); + return -ENODEV; + } + /* - * When we convert to device tree we will simply look up the - * parent irqdomain using irq_find_host(parent) as parent comes - * from IRQCHIP_DECLARE(), then use of_node_to_fwnode() to get - * the fwnode. For now we need this boardfile style code. + * If either clock output is enabled explicitly in the device tree + * we take full control of the clock by masking off all bits for + * the clock control and selectively enabling them. Otherwise + * we leave the hardware default settings. + * + * Enable clock outputs with default timings of requested clock. + * If you need control over TC and DC, add these to the device + * tree bindings and use them here. */ - if (np) { - struct device_node *irq_parent; - - irq_parent = of_irq_find_parent(np); - if (!irq_parent) { - dev_err(dev, "no IRQ parent node\n"); - return -ENODEV; - } - parent = irq_find_host(irq_parent); - if (!parent) { - dev_err(dev, "no IRQ parent domain\n"); - return -ENODEV; - } - g->fwnode = of_node_to_fwnode(np); - } else { - parent = ixp4xx_get_irq_domain(); - g->fwnode = irq_domain_alloc_fwnode(&res->start); - if (!g->fwnode) { - dev_err(dev, "no domain base\n"); - return -ENODEV; - } - } + clk_14 = of_property_read_bool(np, "intel,ixp4xx-gpio14-clkout"); + clk_15 = of_property_read_bool(np, "intel,ixp4xx-gpio15-clkout"); /* * Make sure GPIO 14 and 15 are NOT used as clocks but GPIO on * specific machines. */ - if (machine_is_dsmg600() || machine_is_nas100d()) - __raw_writel(0x0, g->base + IXP4XX_REG_GPCLK); + if (of_machine_is_compatible("dlink,dsm-g600-a") || + of_machine_is_compatible("iom,nas-100d")) + val = 0; + else { + val = __raw_readl(g->base + IXP4XX_REG_GPCLK); + + if (clk_14 || clk_15) { + val &= ~(IXP4XX_GPCLK_MUX14 | IXP4XX_GPCLK_MUX15); + val &= ~IXP4XX_GPCLK_CLK0_MASK; + val &= ~IXP4XX_GPCLK_CLK1_MASK; + if (clk_14) { + /* IXP4XX_GPCLK_CLK0DC implicit low */ + val |= (1 << IXP4XX_GPCLK_CLK0TC_SHIFT); + val |= IXP4XX_GPCLK_MUX14; + } + + if (clk_15) { + /* IXP4XX_GPCLK_CLK1DC implicit low */ + val |= (1 << IXP4XX_GPCLK_CLK1TC_SHIFT); + val |= IXP4XX_GPCLK_MUX15; + } + } + } + + __raw_writel(val, g->base + IXP4XX_REG_GPCLK); /* * This is a very special big-endian ARM issue: when the IXP4xx is @@ -253,43 +289,46 @@ static int ixp4xx_gpio_probe(struct platform_device *pdev) * for big endian. */ #if defined(CONFIG_CPU_BIG_ENDIAN) - flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER; + flags = GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER; #else flags = 0; #endif + config = (struct gpio_generic_chip_config) { + .dev = dev, + .sz = 4, + .dat = g->base + IXP4XX_REG_GPIN, + .set = g->base + IXP4XX_REG_GPOUT, + .dirin = g->base + IXP4XX_REG_GPOE, + .flags = flags, + }; + /* Populate and register gpio chip */ - ret = bgpio_init(&g->gc, dev, 4, - g->base + IXP4XX_REG_GPIN, - g->base + IXP4XX_REG_GPOUT, - NULL, - NULL, - g->base + IXP4XX_REG_GPOE, - flags); + ret = gpio_generic_chip_init(&g->chip, &config); if (ret) { dev_err(dev, "unable to init generic GPIO\n"); return ret; } - g->gc.ngpio = 16; - g->gc.label = "IXP4XX_GPIO_CHIP"; + g->chip.gc.ngpio = 16; + g->chip.gc.label = "IXP4XX_GPIO_CHIP"; /* * TODO: when we have migrated to device tree and all GPIOs * are fetched using phandles, set this to -1 to get rid of * the fixed gpiochip base. */ - g->gc.base = 0; - g->gc.parent = &pdev->dev; - g->gc.owner = THIS_MODULE; + g->chip.gc.base = 0; + g->chip.gc.parent = &pdev->dev; + g->chip.gc.owner = THIS_MODULE; - girq = &g->gc.irq; - girq->chip = &ixp4xx_gpio_irqchip; - girq->fwnode = g->fwnode; + girq = &g->chip.gc.irq; + gpio_irq_chip_set_chip(girq, &ixp4xx_gpio_irqchip); + girq->fwnode = dev_fwnode(dev); girq->parent_domain = parent; girq->child_to_parent_hwirq = ixp4xx_gpio_child_to_parent_hwirq; girq->handler = handle_bad_irq; girq->default_type = IRQ_TYPE_NONE; - ret = devm_gpiochip_add_data(dev, &g->gc, g); + ret = devm_gpiochip_add_data(dev, &g->chip.gc, g); if (ret) { dev_err(dev, "failed to add SoC gpiochip\n"); return ret; @@ -312,7 +351,7 @@ static const struct of_device_id ixp4xx_gpio_of_match[] = { static struct platform_driver ixp4xx_gpio_driver = { .driver = { .name = "ixp4xx-gpio", - .of_match_table = of_match_ptr(ixp4xx_gpio_of_match), + .of_match_table = ixp4xx_gpio_of_match, }, .probe = ixp4xx_gpio_probe, }; |
