| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * PPC4xx gpio driver |
| * |
| * Copyright (c) 2008 Harris Corporation |
| * Copyright (c) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix |
| * Copyright (c) MontaVista Software, Inc. 2008. |
| * |
| * Author: Steve Falco <sfalco@harris.com> |
| */ |
| |
| #include <linux/kernel.h> |
| #include <linux/init.h> |
| #include <linux/spinlock.h> |
| #include <linux/io.h> |
| #include <linux/of.h> |
| #include <linux/of_gpio.h> |
| #include <linux/gpio/driver.h> |
| #include <linux/types.h> |
| #include <linux/slab.h> |
| |
| #define GPIO_MASK(gpio) (0x80000000 >> (gpio)) |
| #define GPIO_MASK2(gpio) (0xc0000000 >> ((gpio) * 2)) |
| |
| /* Physical GPIO register layout */ |
| struct ppc4xx_gpio { |
| __be32 or; |
| __be32 tcr; |
| __be32 osrl; |
| __be32 osrh; |
| __be32 tsrl; |
| __be32 tsrh; |
| __be32 odr; |
| __be32 ir; |
| __be32 rr1; |
| __be32 rr2; |
| __be32 rr3; |
| __be32 reserved1; |
| __be32 isr1l; |
| __be32 isr1h; |
| __be32 isr2l; |
| __be32 isr2h; |
| __be32 isr3l; |
| __be32 isr3h; |
| }; |
| |
| struct ppc4xx_gpio_chip { |
| struct of_mm_gpio_chip mm_gc; |
| spinlock_t lock; |
| }; |
| |
| /* |
| * GPIO LIB API implementation for GPIOs |
| * |
| * There are a maximum of 32 gpios in each gpio controller. |
| */ |
| |
| static int ppc4xx_gpio_get(struct gpio_chip *gc, unsigned int gpio) |
| { |
| struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); |
| struct ppc4xx_gpio __iomem *regs = mm_gc->regs; |
| |
| return !!(in_be32(®s->ir) & GPIO_MASK(gpio)); |
| } |
| |
| static inline void |
| __ppc4xx_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) |
| { |
| struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); |
| struct ppc4xx_gpio __iomem *regs = mm_gc->regs; |
| |
| if (val) |
| setbits32(®s->or, GPIO_MASK(gpio)); |
| else |
| clrbits32(®s->or, GPIO_MASK(gpio)); |
| } |
| |
| static void |
| ppc4xx_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) |
| { |
| struct ppc4xx_gpio_chip *chip = gpiochip_get_data(gc); |
| unsigned long flags; |
| |
| spin_lock_irqsave(&chip->lock, flags); |
| |
| __ppc4xx_gpio_set(gc, gpio, val); |
| |
| spin_unlock_irqrestore(&chip->lock, flags); |
| |
| pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val); |
| } |
| |
| static int ppc4xx_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) |
| { |
| struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); |
| struct ppc4xx_gpio_chip *chip = gpiochip_get_data(gc); |
| struct ppc4xx_gpio __iomem *regs = mm_gc->regs; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&chip->lock, flags); |
| |
| /* Disable open-drain function */ |
| clrbits32(®s->odr, GPIO_MASK(gpio)); |
| |
| /* Float the pin */ |
| clrbits32(®s->tcr, GPIO_MASK(gpio)); |
| |
| /* Bits 0-15 use TSRL/OSRL, bits 16-31 use TSRH/OSRH */ |
| if (gpio < 16) { |
| clrbits32(®s->osrl, GPIO_MASK2(gpio)); |
| clrbits32(®s->tsrl, GPIO_MASK2(gpio)); |
| } else { |
| clrbits32(®s->osrh, GPIO_MASK2(gpio)); |
| clrbits32(®s->tsrh, GPIO_MASK2(gpio)); |
| } |
| |
| spin_unlock_irqrestore(&chip->lock, flags); |
| |
| return 0; |
| } |
| |
| static int |
| ppc4xx_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) |
| { |
| struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); |
| struct ppc4xx_gpio_chip *chip = gpiochip_get_data(gc); |
| struct ppc4xx_gpio __iomem *regs = mm_gc->regs; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&chip->lock, flags); |
| |
| /* First set initial value */ |
| __ppc4xx_gpio_set(gc, gpio, val); |
| |
| /* Disable open-drain function */ |
| clrbits32(®s->odr, GPIO_MASK(gpio)); |
| |
| /* Drive the pin */ |
| setbits32(®s->tcr, GPIO_MASK(gpio)); |
| |
| /* Bits 0-15 use TSRL, bits 16-31 use TSRH */ |
| if (gpio < 16) { |
| clrbits32(®s->osrl, GPIO_MASK2(gpio)); |
| clrbits32(®s->tsrl, GPIO_MASK2(gpio)); |
| } else { |
| clrbits32(®s->osrh, GPIO_MASK2(gpio)); |
| clrbits32(®s->tsrh, GPIO_MASK2(gpio)); |
| } |
| |
| spin_unlock_irqrestore(&chip->lock, flags); |
| |
| pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val); |
| |
| return 0; |
| } |
| |
| static int __init ppc4xx_add_gpiochips(void) |
| { |
| struct device_node *np; |
| |
| for_each_compatible_node(np, NULL, "ibm,ppc4xx-gpio") { |
| int ret; |
| struct ppc4xx_gpio_chip *ppc4xx_gc; |
| struct of_mm_gpio_chip *mm_gc; |
| struct gpio_chip *gc; |
| |
| ppc4xx_gc = kzalloc(sizeof(*ppc4xx_gc), GFP_KERNEL); |
| if (!ppc4xx_gc) { |
| ret = -ENOMEM; |
| goto err; |
| } |
| |
| spin_lock_init(&ppc4xx_gc->lock); |
| |
| mm_gc = &ppc4xx_gc->mm_gc; |
| gc = &mm_gc->gc; |
| |
| gc->ngpio = 32; |
| gc->direction_input = ppc4xx_gpio_dir_in; |
| gc->direction_output = ppc4xx_gpio_dir_out; |
| gc->get = ppc4xx_gpio_get; |
| gc->set = ppc4xx_gpio_set; |
| |
| ret = of_mm_gpiochip_add_data(np, mm_gc, ppc4xx_gc); |
| if (ret) |
| goto err; |
| continue; |
| err: |
| pr_err("%pOF: registration failed with status %d\n", np, ret); |
| kfree(ppc4xx_gc); |
| /* try others anyway */ |
| } |
| return 0; |
| } |
| arch_initcall(ppc4xx_add_gpiochips); |