162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2014-2017 Broadcom 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci/* 762306a36Sopenharmony_ci * This file contains the Broadcom Iproc GPIO driver that supports 3 862306a36Sopenharmony_ci * GPIO controllers on Iproc including the ASIU GPIO controller, the 962306a36Sopenharmony_ci * chipCommonG GPIO controller, and the always-on GPIO controller. Basic 1062306a36Sopenharmony_ci * PINCONF such as bias pull up/down, and drive strength are also supported 1162306a36Sopenharmony_ci * in this driver. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * It provides the functionality where pins from the GPIO can be 1462306a36Sopenharmony_ci * individually muxed to GPIO function, if individual pad 1562306a36Sopenharmony_ci * configuration is supported, through the interaction with respective 1662306a36Sopenharmony_ci * SoCs IOMUX controller. 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/gpio/driver.h> 2062306a36Sopenharmony_ci#include <linux/interrupt.h> 2162306a36Sopenharmony_ci#include <linux/io.h> 2262306a36Sopenharmony_ci#include <linux/ioport.h> 2362306a36Sopenharmony_ci#include <linux/kernel.h> 2462306a36Sopenharmony_ci#include <linux/of.h> 2562306a36Sopenharmony_ci#include <linux/platform_device.h> 2662306a36Sopenharmony_ci#include <linux/seq_file.h> 2762306a36Sopenharmony_ci#include <linux/slab.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h> 3062306a36Sopenharmony_ci#include <linux/pinctrl/pinconf-generic.h> 3162306a36Sopenharmony_ci#include <linux/pinctrl/pinconf.h> 3262306a36Sopenharmony_ci#include <linux/pinctrl/pinctrl.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include "../pinctrl-utils.h" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define IPROC_GPIO_DATA_IN_OFFSET 0x00 3762306a36Sopenharmony_ci#define IPROC_GPIO_DATA_OUT_OFFSET 0x04 3862306a36Sopenharmony_ci#define IPROC_GPIO_OUT_EN_OFFSET 0x08 3962306a36Sopenharmony_ci#define IPROC_GPIO_INT_TYPE_OFFSET 0x0c 4062306a36Sopenharmony_ci#define IPROC_GPIO_INT_DE_OFFSET 0x10 4162306a36Sopenharmony_ci#define IPROC_GPIO_INT_EDGE_OFFSET 0x14 4262306a36Sopenharmony_ci#define IPROC_GPIO_INT_MSK_OFFSET 0x18 4362306a36Sopenharmony_ci#define IPROC_GPIO_INT_STAT_OFFSET 0x1c 4462306a36Sopenharmony_ci#define IPROC_GPIO_INT_MSTAT_OFFSET 0x20 4562306a36Sopenharmony_ci#define IPROC_GPIO_INT_CLR_OFFSET 0x24 4662306a36Sopenharmony_ci#define IPROC_GPIO_PAD_RES_OFFSET 0x34 4762306a36Sopenharmony_ci#define IPROC_GPIO_RES_EN_OFFSET 0x38 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* drive strength control for ASIU GPIO */ 5062306a36Sopenharmony_ci#define IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET 0x58 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* pinconf for CCM GPIO */ 5362306a36Sopenharmony_ci#define IPROC_GPIO_PULL_DN_OFFSET 0x10 5462306a36Sopenharmony_ci#define IPROC_GPIO_PULL_UP_OFFSET 0x14 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* pinconf for CRMU(aon) GPIO and CCM GPIO*/ 5762306a36Sopenharmony_ci#define IPROC_GPIO_DRV_CTRL_OFFSET 0x00 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define GPIO_BANK_SIZE 0x200 6062306a36Sopenharmony_ci#define NGPIOS_PER_BANK 32 6162306a36Sopenharmony_ci#define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK) 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define IPROC_GPIO_REG(pin, reg) (GPIO_BANK(pin) * GPIO_BANK_SIZE + (reg)) 6462306a36Sopenharmony_ci#define IPROC_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK) 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define GPIO_DRV_STRENGTH_BIT_SHIFT 20 6762306a36Sopenharmony_ci#define GPIO_DRV_STRENGTH_BITS 3 6862306a36Sopenharmony_ci#define GPIO_DRV_STRENGTH_BIT_MASK ((1 << GPIO_DRV_STRENGTH_BITS) - 1) 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cienum iproc_pinconf_param { 7162306a36Sopenharmony_ci IPROC_PINCONF_DRIVE_STRENGTH = 0, 7262306a36Sopenharmony_ci IPROC_PINCONF_BIAS_DISABLE, 7362306a36Sopenharmony_ci IPROC_PINCONF_BIAS_PULL_UP, 7462306a36Sopenharmony_ci IPROC_PINCONF_BIAS_PULL_DOWN, 7562306a36Sopenharmony_ci IPROC_PINCON_MAX, 7662306a36Sopenharmony_ci}; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cienum iproc_pinconf_ctrl_type { 7962306a36Sopenharmony_ci IOCTRL_TYPE_AON = 1, 8062306a36Sopenharmony_ci IOCTRL_TYPE_CDRU, 8162306a36Sopenharmony_ci IOCTRL_TYPE_INVALID, 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* 8562306a36Sopenharmony_ci * Iproc GPIO core 8662306a36Sopenharmony_ci * 8762306a36Sopenharmony_ci * @dev: pointer to device 8862306a36Sopenharmony_ci * @base: I/O register base for Iproc GPIO controller 8962306a36Sopenharmony_ci * @io_ctrl: I/O register base for certain type of Iproc GPIO controller that 9062306a36Sopenharmony_ci * has the PINCONF support implemented outside of the GPIO block 9162306a36Sopenharmony_ci * @lock: lock to protect access to I/O registers 9262306a36Sopenharmony_ci * @gc: GPIO chip 9362306a36Sopenharmony_ci * @num_banks: number of GPIO banks, each bank supports up to 32 GPIOs 9462306a36Sopenharmony_ci * @pinmux_is_supported: flag to indicate this GPIO controller contains pins 9562306a36Sopenharmony_ci * that can be individually muxed to GPIO 9662306a36Sopenharmony_ci * @pinconf_disable: contains a list of PINCONF parameters that need to be 9762306a36Sopenharmony_ci * disabled 9862306a36Sopenharmony_ci * @nr_pinconf_disable: total number of PINCONF parameters that need to be 9962306a36Sopenharmony_ci * disabled 10062306a36Sopenharmony_ci * @pctl: pointer to pinctrl_dev 10162306a36Sopenharmony_ci * @pctldesc: pinctrl descriptor 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_cistruct iproc_gpio { 10462306a36Sopenharmony_ci struct device *dev; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci void __iomem *base; 10762306a36Sopenharmony_ci void __iomem *io_ctrl; 10862306a36Sopenharmony_ci enum iproc_pinconf_ctrl_type io_ctrl_type; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci raw_spinlock_t lock; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci struct gpio_chip gc; 11362306a36Sopenharmony_ci unsigned num_banks; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci bool pinmux_is_supported; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci enum pin_config_param *pinconf_disable; 11862306a36Sopenharmony_ci unsigned int nr_pinconf_disable; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci struct pinctrl_dev *pctl; 12162306a36Sopenharmony_ci struct pinctrl_desc pctldesc; 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* 12562306a36Sopenharmony_ci * Mapping from PINCONF pins to GPIO pins is 1-to-1 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_cistatic inline unsigned iproc_pin_to_gpio(unsigned pin) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci return pin; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/** 13362306a36Sopenharmony_ci * iproc_set_bit - set or clear one bit (corresponding to the GPIO pin) in a 13462306a36Sopenharmony_ci * Iproc GPIO register 13562306a36Sopenharmony_ci * 13662306a36Sopenharmony_ci * @chip: Iproc GPIO device 13762306a36Sopenharmony_ci * @reg: register offset 13862306a36Sopenharmony_ci * @gpio: GPIO pin 13962306a36Sopenharmony_ci * @set: set or clear 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_cistatic inline void iproc_set_bit(struct iproc_gpio *chip, unsigned int reg, 14262306a36Sopenharmony_ci unsigned gpio, bool set) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci unsigned int offset = IPROC_GPIO_REG(gpio, reg); 14562306a36Sopenharmony_ci unsigned int shift = IPROC_GPIO_SHIFT(gpio); 14662306a36Sopenharmony_ci u32 val; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci val = readl(chip->base + offset); 14962306a36Sopenharmony_ci if (set) 15062306a36Sopenharmony_ci val |= BIT(shift); 15162306a36Sopenharmony_ci else 15262306a36Sopenharmony_ci val &= ~BIT(shift); 15362306a36Sopenharmony_ci writel(val, chip->base + offset); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic inline bool iproc_get_bit(struct iproc_gpio *chip, unsigned int reg, 15762306a36Sopenharmony_ci unsigned gpio) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci unsigned int offset = IPROC_GPIO_REG(gpio, reg); 16062306a36Sopenharmony_ci unsigned int shift = IPROC_GPIO_SHIFT(gpio); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci return !!(readl(chip->base + offset) & BIT(shift)); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic void iproc_gpio_irq_handler(struct irq_desc *desc) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct gpio_chip *gc = irq_desc_get_handler_data(desc); 16862306a36Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 16962306a36Sopenharmony_ci struct irq_chip *irq_chip = irq_desc_get_chip(desc); 17062306a36Sopenharmony_ci int i, bit; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci chained_irq_enter(irq_chip, desc); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* go through the entire GPIO banks and handle all interrupts */ 17562306a36Sopenharmony_ci for (i = 0; i < chip->num_banks; i++) { 17662306a36Sopenharmony_ci unsigned long val = readl(chip->base + (i * GPIO_BANK_SIZE) + 17762306a36Sopenharmony_ci IPROC_GPIO_INT_MSTAT_OFFSET); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci for_each_set_bit(bit, &val, NGPIOS_PER_BANK) { 18062306a36Sopenharmony_ci unsigned pin = NGPIOS_PER_BANK * i + bit; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* 18362306a36Sopenharmony_ci * Clear the interrupt before invoking the 18462306a36Sopenharmony_ci * handler, so we do not leave any window 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci writel(BIT(bit), chip->base + (i * GPIO_BANK_SIZE) + 18762306a36Sopenharmony_ci IPROC_GPIO_INT_CLR_OFFSET); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci generic_handle_domain_irq(gc->irq.domain, pin); 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci chained_irq_exit(irq_chip, desc); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void iproc_gpio_irq_ack(struct irq_data *d) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 20062306a36Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 20162306a36Sopenharmony_ci unsigned gpio = d->hwirq; 20262306a36Sopenharmony_ci unsigned int offset = IPROC_GPIO_REG(gpio, 20362306a36Sopenharmony_ci IPROC_GPIO_INT_CLR_OFFSET); 20462306a36Sopenharmony_ci unsigned int shift = IPROC_GPIO_SHIFT(gpio); 20562306a36Sopenharmony_ci u32 val = BIT(shift); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci writel(val, chip->base + offset); 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci/** 21162306a36Sopenharmony_ci * iproc_gpio_irq_set_mask - mask/unmask a GPIO interrupt 21262306a36Sopenharmony_ci * 21362306a36Sopenharmony_ci * @d: IRQ chip data 21462306a36Sopenharmony_ci * @unmask: mask/unmask GPIO interrupt 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_cistatic void iproc_gpio_irq_set_mask(struct irq_data *d, bool unmask) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 21962306a36Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 22062306a36Sopenharmony_ci unsigned gpio = irqd_to_hwirq(d); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci iproc_set_bit(chip, IPROC_GPIO_INT_MSK_OFFSET, gpio, unmask); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic void iproc_gpio_irq_mask(struct irq_data *d) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 22862306a36Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 22962306a36Sopenharmony_ci unsigned long flags; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 23262306a36Sopenharmony_ci iproc_gpio_irq_set_mask(d, false); 23362306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 23462306a36Sopenharmony_ci gpiochip_disable_irq(gc, irqd_to_hwirq(d)); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic void iproc_gpio_irq_unmask(struct irq_data *d) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 24062306a36Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 24162306a36Sopenharmony_ci unsigned long flags; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci gpiochip_enable_irq(gc, irqd_to_hwirq(d)); 24462306a36Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 24562306a36Sopenharmony_ci iproc_gpio_irq_set_mask(d, true); 24662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic int iproc_gpio_irq_set_type(struct irq_data *d, unsigned int type) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 25262306a36Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 25362306a36Sopenharmony_ci unsigned gpio = d->hwirq; 25462306a36Sopenharmony_ci bool level_triggered = false; 25562306a36Sopenharmony_ci bool dual_edge = false; 25662306a36Sopenharmony_ci bool rising_or_high = false; 25762306a36Sopenharmony_ci unsigned long flags; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci switch (type & IRQ_TYPE_SENSE_MASK) { 26062306a36Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 26162306a36Sopenharmony_ci rising_or_high = true; 26262306a36Sopenharmony_ci break; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 26562306a36Sopenharmony_ci break; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 26862306a36Sopenharmony_ci dual_edge = true; 26962306a36Sopenharmony_ci break; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 27262306a36Sopenharmony_ci level_triggered = true; 27362306a36Sopenharmony_ci rising_or_high = true; 27462306a36Sopenharmony_ci break; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 27762306a36Sopenharmony_ci level_triggered = true; 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci default: 28162306a36Sopenharmony_ci dev_err(chip->dev, "invalid GPIO IRQ type 0x%x\n", 28262306a36Sopenharmony_ci type); 28362306a36Sopenharmony_ci return -EINVAL; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 28762306a36Sopenharmony_ci iproc_set_bit(chip, IPROC_GPIO_INT_TYPE_OFFSET, gpio, 28862306a36Sopenharmony_ci level_triggered); 28962306a36Sopenharmony_ci iproc_set_bit(chip, IPROC_GPIO_INT_DE_OFFSET, gpio, dual_edge); 29062306a36Sopenharmony_ci iproc_set_bit(chip, IPROC_GPIO_INT_EDGE_OFFSET, gpio, 29162306a36Sopenharmony_ci rising_or_high); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (type & IRQ_TYPE_EDGE_BOTH) 29462306a36Sopenharmony_ci irq_set_handler_locked(d, handle_edge_irq); 29562306a36Sopenharmony_ci else 29662306a36Sopenharmony_ci irq_set_handler_locked(d, handle_level_irq); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci dev_dbg(chip->dev, 30162306a36Sopenharmony_ci "gpio:%u level_triggered:%d dual_edge:%d rising_or_high:%d\n", 30262306a36Sopenharmony_ci gpio, level_triggered, dual_edge, rising_or_high); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic void iproc_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 31062306a36Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci seq_printf(p, dev_name(chip->dev)); 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic const struct irq_chip iproc_gpio_irq_chip = { 31662306a36Sopenharmony_ci .irq_ack = iproc_gpio_irq_ack, 31762306a36Sopenharmony_ci .irq_mask = iproc_gpio_irq_mask, 31862306a36Sopenharmony_ci .irq_unmask = iproc_gpio_irq_unmask, 31962306a36Sopenharmony_ci .irq_set_type = iproc_gpio_irq_set_type, 32062306a36Sopenharmony_ci .irq_enable = iproc_gpio_irq_unmask, 32162306a36Sopenharmony_ci .irq_disable = iproc_gpio_irq_mask, 32262306a36Sopenharmony_ci .irq_print_chip = iproc_gpio_irq_print_chip, 32362306a36Sopenharmony_ci .flags = IRQCHIP_IMMUTABLE, 32462306a36Sopenharmony_ci GPIOCHIP_IRQ_RESOURCE_HELPERS, 32562306a36Sopenharmony_ci}; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci/* 32862306a36Sopenharmony_ci * Request the Iproc IOMUX pinmux controller to mux individual pins to GPIO 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_cistatic int iproc_gpio_request(struct gpio_chip *gc, unsigned offset) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 33362306a36Sopenharmony_ci unsigned gpio = gc->base + offset; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* not all Iproc GPIO pins can be muxed individually */ 33662306a36Sopenharmony_ci if (!chip->pinmux_is_supported) 33762306a36Sopenharmony_ci return 0; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci return pinctrl_gpio_request(gpio); 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic void iproc_gpio_free(struct gpio_chip *gc, unsigned offset) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 34562306a36Sopenharmony_ci unsigned gpio = gc->base + offset; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (!chip->pinmux_is_supported) 34862306a36Sopenharmony_ci return; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci pinctrl_gpio_free(gpio); 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic int iproc_gpio_direction_input(struct gpio_chip *gc, unsigned gpio) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 35662306a36Sopenharmony_ci unsigned long flags; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 35962306a36Sopenharmony_ci iproc_set_bit(chip, IPROC_GPIO_OUT_EN_OFFSET, gpio, false); 36062306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci dev_dbg(chip->dev, "gpio:%u set input\n", gpio); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return 0; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic int iproc_gpio_direction_output(struct gpio_chip *gc, unsigned gpio, 36862306a36Sopenharmony_ci int val) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 37162306a36Sopenharmony_ci unsigned long flags; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 37462306a36Sopenharmony_ci iproc_set_bit(chip, IPROC_GPIO_OUT_EN_OFFSET, gpio, true); 37562306a36Sopenharmony_ci iproc_set_bit(chip, IPROC_GPIO_DATA_OUT_OFFSET, gpio, !!(val)); 37662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci dev_dbg(chip->dev, "gpio:%u set output, value:%d\n", gpio, val); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci return 0; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic int iproc_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 38662306a36Sopenharmony_ci unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_OUT_EN_OFFSET); 38762306a36Sopenharmony_ci unsigned int shift = IPROC_GPIO_SHIFT(gpio); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (readl(chip->base + offset) & BIT(shift)) 39062306a36Sopenharmony_ci return GPIO_LINE_DIRECTION_OUT; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci return GPIO_LINE_DIRECTION_IN; 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic void iproc_gpio_set(struct gpio_chip *gc, unsigned gpio, int val) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 39862306a36Sopenharmony_ci unsigned long flags; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 40162306a36Sopenharmony_ci iproc_set_bit(chip, IPROC_GPIO_DATA_OUT_OFFSET, gpio, !!(val)); 40262306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci dev_dbg(chip->dev, "gpio:%u set, value:%d\n", gpio, val); 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic int iproc_gpio_get(struct gpio_chip *gc, unsigned gpio) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 41062306a36Sopenharmony_ci unsigned int offset = IPROC_GPIO_REG(gpio, 41162306a36Sopenharmony_ci IPROC_GPIO_DATA_IN_OFFSET); 41262306a36Sopenharmony_ci unsigned int shift = IPROC_GPIO_SHIFT(gpio); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci return !!(readl(chip->base + offset) & BIT(shift)); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci/* 41862306a36Sopenharmony_ci * Mapping of the iProc PINCONF parameters to the generic pin configuration 41962306a36Sopenharmony_ci * parameters 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_cistatic const enum pin_config_param iproc_pinconf_disable_map[] = { 42262306a36Sopenharmony_ci [IPROC_PINCONF_DRIVE_STRENGTH] = PIN_CONFIG_DRIVE_STRENGTH, 42362306a36Sopenharmony_ci [IPROC_PINCONF_BIAS_DISABLE] = PIN_CONFIG_BIAS_DISABLE, 42462306a36Sopenharmony_ci [IPROC_PINCONF_BIAS_PULL_UP] = PIN_CONFIG_BIAS_PULL_UP, 42562306a36Sopenharmony_ci [IPROC_PINCONF_BIAS_PULL_DOWN] = PIN_CONFIG_BIAS_PULL_DOWN, 42662306a36Sopenharmony_ci}; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic bool iproc_pinconf_param_is_disabled(struct iproc_gpio *chip, 42962306a36Sopenharmony_ci enum pin_config_param param) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci unsigned int i; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (!chip->nr_pinconf_disable) 43462306a36Sopenharmony_ci return false; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci for (i = 0; i < chip->nr_pinconf_disable; i++) 43762306a36Sopenharmony_ci if (chip->pinconf_disable[i] == param) 43862306a36Sopenharmony_ci return true; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci return false; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic int iproc_pinconf_disable_map_create(struct iproc_gpio *chip, 44462306a36Sopenharmony_ci unsigned long disable_mask) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci unsigned int map_size = ARRAY_SIZE(iproc_pinconf_disable_map); 44762306a36Sopenharmony_ci unsigned int bit, nbits = 0; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* figure out total number of PINCONF parameters to disable */ 45062306a36Sopenharmony_ci for_each_set_bit(bit, &disable_mask, map_size) 45162306a36Sopenharmony_ci nbits++; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (!nbits) 45462306a36Sopenharmony_ci return 0; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* 45762306a36Sopenharmony_ci * Allocate an array to store PINCONF parameters that need to be 45862306a36Sopenharmony_ci * disabled 45962306a36Sopenharmony_ci */ 46062306a36Sopenharmony_ci chip->pinconf_disable = devm_kcalloc(chip->dev, nbits, 46162306a36Sopenharmony_ci sizeof(*chip->pinconf_disable), 46262306a36Sopenharmony_ci GFP_KERNEL); 46362306a36Sopenharmony_ci if (!chip->pinconf_disable) 46462306a36Sopenharmony_ci return -ENOMEM; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci chip->nr_pinconf_disable = nbits; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* now store these parameters */ 46962306a36Sopenharmony_ci nbits = 0; 47062306a36Sopenharmony_ci for_each_set_bit(bit, &disable_mask, map_size) 47162306a36Sopenharmony_ci chip->pinconf_disable[nbits++] = iproc_pinconf_disable_map[bit]; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci return 0; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic int iproc_get_groups_count(struct pinctrl_dev *pctldev) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci return 1; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci/* 48262306a36Sopenharmony_ci * Only one group: "gpio_grp", since this local pinctrl device only performs 48362306a36Sopenharmony_ci * GPIO specific PINCONF configurations 48462306a36Sopenharmony_ci */ 48562306a36Sopenharmony_cistatic const char *iproc_get_group_name(struct pinctrl_dev *pctldev, 48662306a36Sopenharmony_ci unsigned selector) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci return "gpio_grp"; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic const struct pinctrl_ops iproc_pctrl_ops = { 49262306a36Sopenharmony_ci .get_groups_count = iproc_get_groups_count, 49362306a36Sopenharmony_ci .get_group_name = iproc_get_group_name, 49462306a36Sopenharmony_ci .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, 49562306a36Sopenharmony_ci .dt_free_map = pinctrl_utils_free_map, 49662306a36Sopenharmony_ci}; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic int iproc_gpio_set_pull(struct iproc_gpio *chip, unsigned gpio, 49962306a36Sopenharmony_ci bool disable, bool pull_up) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci void __iomem *base; 50262306a36Sopenharmony_ci unsigned long flags; 50362306a36Sopenharmony_ci unsigned int shift; 50462306a36Sopenharmony_ci u32 val_1, val_2; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 50762306a36Sopenharmony_ci if (chip->io_ctrl_type == IOCTRL_TYPE_CDRU) { 50862306a36Sopenharmony_ci base = chip->io_ctrl; 50962306a36Sopenharmony_ci shift = IPROC_GPIO_SHIFT(gpio); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci val_1 = readl(base + IPROC_GPIO_PULL_UP_OFFSET); 51262306a36Sopenharmony_ci val_2 = readl(base + IPROC_GPIO_PULL_DN_OFFSET); 51362306a36Sopenharmony_ci if (disable) { 51462306a36Sopenharmony_ci /* no pull-up or pull-down */ 51562306a36Sopenharmony_ci val_1 &= ~BIT(shift); 51662306a36Sopenharmony_ci val_2 &= ~BIT(shift); 51762306a36Sopenharmony_ci } else if (pull_up) { 51862306a36Sopenharmony_ci val_1 |= BIT(shift); 51962306a36Sopenharmony_ci val_2 &= ~BIT(shift); 52062306a36Sopenharmony_ci } else { 52162306a36Sopenharmony_ci val_1 &= ~BIT(shift); 52262306a36Sopenharmony_ci val_2 |= BIT(shift); 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci writel(val_1, base + IPROC_GPIO_PULL_UP_OFFSET); 52562306a36Sopenharmony_ci writel(val_2, base + IPROC_GPIO_PULL_DN_OFFSET); 52662306a36Sopenharmony_ci } else { 52762306a36Sopenharmony_ci if (disable) { 52862306a36Sopenharmony_ci iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio, 52962306a36Sopenharmony_ci false); 53062306a36Sopenharmony_ci } else { 53162306a36Sopenharmony_ci iproc_set_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio, 53262306a36Sopenharmony_ci pull_up); 53362306a36Sopenharmony_ci iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio, 53462306a36Sopenharmony_ci true); 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 53962306a36Sopenharmony_ci dev_dbg(chip->dev, "gpio:%u set pullup:%d\n", gpio, pull_up); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci return 0; 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic void iproc_gpio_get_pull(struct iproc_gpio *chip, unsigned gpio, 54562306a36Sopenharmony_ci bool *disable, bool *pull_up) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci void __iomem *base; 54862306a36Sopenharmony_ci unsigned long flags; 54962306a36Sopenharmony_ci unsigned int shift; 55062306a36Sopenharmony_ci u32 val_1, val_2; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 55362306a36Sopenharmony_ci if (chip->io_ctrl_type == IOCTRL_TYPE_CDRU) { 55462306a36Sopenharmony_ci base = chip->io_ctrl; 55562306a36Sopenharmony_ci shift = IPROC_GPIO_SHIFT(gpio); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci val_1 = readl(base + IPROC_GPIO_PULL_UP_OFFSET) & BIT(shift); 55862306a36Sopenharmony_ci val_2 = readl(base + IPROC_GPIO_PULL_DN_OFFSET) & BIT(shift); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci *pull_up = val_1 ? true : false; 56162306a36Sopenharmony_ci *disable = (val_1 | val_2) ? false : true; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci } else { 56462306a36Sopenharmony_ci *disable = !iproc_get_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio); 56562306a36Sopenharmony_ci *pull_up = iproc_get_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio); 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci#define DRV_STRENGTH_OFFSET(gpio, bit, type) ((type) == IOCTRL_TYPE_AON ? \ 57162306a36Sopenharmony_ci ((2 - (bit)) * 4 + IPROC_GPIO_DRV_CTRL_OFFSET) : \ 57262306a36Sopenharmony_ci ((type) == IOCTRL_TYPE_CDRU) ? \ 57362306a36Sopenharmony_ci ((bit) * 4 + IPROC_GPIO_DRV_CTRL_OFFSET) : \ 57462306a36Sopenharmony_ci ((bit) * 4 + IPROC_GPIO_REG(gpio, IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET))) 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_cistatic int iproc_gpio_set_strength(struct iproc_gpio *chip, unsigned gpio, 57762306a36Sopenharmony_ci unsigned strength) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci void __iomem *base; 58062306a36Sopenharmony_ci unsigned int i, offset, shift; 58162306a36Sopenharmony_ci u32 val; 58262306a36Sopenharmony_ci unsigned long flags; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* make sure drive strength is supported */ 58562306a36Sopenharmony_ci if (strength < 2 || strength > 16 || (strength % 2)) 58662306a36Sopenharmony_ci return -ENOTSUPP; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (chip->io_ctrl) { 58962306a36Sopenharmony_ci base = chip->io_ctrl; 59062306a36Sopenharmony_ci } else { 59162306a36Sopenharmony_ci base = chip->base; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci shift = IPROC_GPIO_SHIFT(gpio); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci dev_dbg(chip->dev, "gpio:%u set drive strength:%d mA\n", gpio, 59762306a36Sopenharmony_ci strength); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 60062306a36Sopenharmony_ci strength = (strength / 2) - 1; 60162306a36Sopenharmony_ci for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) { 60262306a36Sopenharmony_ci offset = DRV_STRENGTH_OFFSET(gpio, i, chip->io_ctrl_type); 60362306a36Sopenharmony_ci val = readl(base + offset); 60462306a36Sopenharmony_ci val &= ~BIT(shift); 60562306a36Sopenharmony_ci val |= ((strength >> i) & 0x1) << shift; 60662306a36Sopenharmony_ci writel(val, base + offset); 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci return 0; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic int iproc_gpio_get_strength(struct iproc_gpio *chip, unsigned gpio, 61462306a36Sopenharmony_ci u16 *strength) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci void __iomem *base; 61762306a36Sopenharmony_ci unsigned int i, offset, shift; 61862306a36Sopenharmony_ci u32 val; 61962306a36Sopenharmony_ci unsigned long flags; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (chip->io_ctrl) { 62262306a36Sopenharmony_ci base = chip->io_ctrl; 62362306a36Sopenharmony_ci } else { 62462306a36Sopenharmony_ci base = chip->base; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci shift = IPROC_GPIO_SHIFT(gpio); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 63062306a36Sopenharmony_ci *strength = 0; 63162306a36Sopenharmony_ci for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) { 63262306a36Sopenharmony_ci offset = DRV_STRENGTH_OFFSET(gpio, i, chip->io_ctrl_type); 63362306a36Sopenharmony_ci val = readl(base + offset) & BIT(shift); 63462306a36Sopenharmony_ci val >>= shift; 63562306a36Sopenharmony_ci *strength += (val << i); 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci /* convert to mA */ 63962306a36Sopenharmony_ci *strength = (*strength + 1) * 2; 64062306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci return 0; 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistatic int iproc_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin, 64662306a36Sopenharmony_ci unsigned long *config) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci struct iproc_gpio *chip = pinctrl_dev_get_drvdata(pctldev); 64962306a36Sopenharmony_ci enum pin_config_param param = pinconf_to_config_param(*config); 65062306a36Sopenharmony_ci unsigned gpio = iproc_pin_to_gpio(pin); 65162306a36Sopenharmony_ci u16 arg; 65262306a36Sopenharmony_ci bool disable, pull_up; 65362306a36Sopenharmony_ci int ret; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (iproc_pinconf_param_is_disabled(chip, param)) 65662306a36Sopenharmony_ci return -ENOTSUPP; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci switch (param) { 65962306a36Sopenharmony_ci case PIN_CONFIG_BIAS_DISABLE: 66062306a36Sopenharmony_ci iproc_gpio_get_pull(chip, gpio, &disable, &pull_up); 66162306a36Sopenharmony_ci if (disable) 66262306a36Sopenharmony_ci return 0; 66362306a36Sopenharmony_ci else 66462306a36Sopenharmony_ci return -EINVAL; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 66762306a36Sopenharmony_ci iproc_gpio_get_pull(chip, gpio, &disable, &pull_up); 66862306a36Sopenharmony_ci if (!disable && pull_up) 66962306a36Sopenharmony_ci return 0; 67062306a36Sopenharmony_ci else 67162306a36Sopenharmony_ci return -EINVAL; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_DOWN: 67462306a36Sopenharmony_ci iproc_gpio_get_pull(chip, gpio, &disable, &pull_up); 67562306a36Sopenharmony_ci if (!disable && !pull_up) 67662306a36Sopenharmony_ci return 0; 67762306a36Sopenharmony_ci else 67862306a36Sopenharmony_ci return -EINVAL; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci case PIN_CONFIG_DRIVE_STRENGTH: 68162306a36Sopenharmony_ci ret = iproc_gpio_get_strength(chip, gpio, &arg); 68262306a36Sopenharmony_ci if (ret) 68362306a36Sopenharmony_ci return ret; 68462306a36Sopenharmony_ci *config = pinconf_to_config_packed(param, arg); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci return 0; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci default: 68962306a36Sopenharmony_ci return -ENOTSUPP; 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci return -ENOTSUPP; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic int iproc_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, 69662306a36Sopenharmony_ci unsigned long *configs, unsigned num_configs) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci struct iproc_gpio *chip = pinctrl_dev_get_drvdata(pctldev); 69962306a36Sopenharmony_ci enum pin_config_param param; 70062306a36Sopenharmony_ci u32 arg; 70162306a36Sopenharmony_ci unsigned i, gpio = iproc_pin_to_gpio(pin); 70262306a36Sopenharmony_ci int ret = -ENOTSUPP; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci for (i = 0; i < num_configs; i++) { 70562306a36Sopenharmony_ci param = pinconf_to_config_param(configs[i]); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if (iproc_pinconf_param_is_disabled(chip, param)) 70862306a36Sopenharmony_ci return -ENOTSUPP; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci arg = pinconf_to_config_argument(configs[i]); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci switch (param) { 71362306a36Sopenharmony_ci case PIN_CONFIG_BIAS_DISABLE: 71462306a36Sopenharmony_ci ret = iproc_gpio_set_pull(chip, gpio, true, false); 71562306a36Sopenharmony_ci if (ret < 0) 71662306a36Sopenharmony_ci goto out; 71762306a36Sopenharmony_ci break; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 72062306a36Sopenharmony_ci ret = iproc_gpio_set_pull(chip, gpio, false, true); 72162306a36Sopenharmony_ci if (ret < 0) 72262306a36Sopenharmony_ci goto out; 72362306a36Sopenharmony_ci break; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_DOWN: 72662306a36Sopenharmony_ci ret = iproc_gpio_set_pull(chip, gpio, false, false); 72762306a36Sopenharmony_ci if (ret < 0) 72862306a36Sopenharmony_ci goto out; 72962306a36Sopenharmony_ci break; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci case PIN_CONFIG_DRIVE_STRENGTH: 73262306a36Sopenharmony_ci ret = iproc_gpio_set_strength(chip, gpio, arg); 73362306a36Sopenharmony_ci if (ret < 0) 73462306a36Sopenharmony_ci goto out; 73562306a36Sopenharmony_ci break; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci default: 73862306a36Sopenharmony_ci dev_err(chip->dev, "invalid configuration\n"); 73962306a36Sopenharmony_ci return -ENOTSUPP; 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci } /* for each config */ 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ciout: 74462306a36Sopenharmony_ci return ret; 74562306a36Sopenharmony_ci} 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_cistatic const struct pinconf_ops iproc_pconf_ops = { 74862306a36Sopenharmony_ci .is_generic = true, 74962306a36Sopenharmony_ci .pin_config_get = iproc_pin_config_get, 75062306a36Sopenharmony_ci .pin_config_set = iproc_pin_config_set, 75162306a36Sopenharmony_ci}; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci/* 75462306a36Sopenharmony_ci * Iproc GPIO controller supports some PINCONF related configurations such as 75562306a36Sopenharmony_ci * pull up, pull down, and drive strength, when the pin is configured to GPIO 75662306a36Sopenharmony_ci * 75762306a36Sopenharmony_ci * Here a local pinctrl device is created with simple 1-to-1 pin mapping to the 75862306a36Sopenharmony_ci * local GPIO pins 75962306a36Sopenharmony_ci */ 76062306a36Sopenharmony_cistatic int iproc_gpio_register_pinconf(struct iproc_gpio *chip) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci struct pinctrl_desc *pctldesc = &chip->pctldesc; 76362306a36Sopenharmony_ci struct pinctrl_pin_desc *pins; 76462306a36Sopenharmony_ci struct gpio_chip *gc = &chip->gc; 76562306a36Sopenharmony_ci int i; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci pins = devm_kcalloc(chip->dev, gc->ngpio, sizeof(*pins), GFP_KERNEL); 76862306a36Sopenharmony_ci if (!pins) 76962306a36Sopenharmony_ci return -ENOMEM; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci for (i = 0; i < gc->ngpio; i++) { 77262306a36Sopenharmony_ci pins[i].number = i; 77362306a36Sopenharmony_ci pins[i].name = devm_kasprintf(chip->dev, GFP_KERNEL, 77462306a36Sopenharmony_ci "gpio-%d", i); 77562306a36Sopenharmony_ci if (!pins[i].name) 77662306a36Sopenharmony_ci return -ENOMEM; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci pctldesc->name = dev_name(chip->dev); 78062306a36Sopenharmony_ci pctldesc->pctlops = &iproc_pctrl_ops; 78162306a36Sopenharmony_ci pctldesc->pins = pins; 78262306a36Sopenharmony_ci pctldesc->npins = gc->ngpio; 78362306a36Sopenharmony_ci pctldesc->confops = &iproc_pconf_ops; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci chip->pctl = devm_pinctrl_register(chip->dev, pctldesc, chip); 78662306a36Sopenharmony_ci if (IS_ERR(chip->pctl)) { 78762306a36Sopenharmony_ci dev_err(chip->dev, "unable to register pinctrl device\n"); 78862306a36Sopenharmony_ci return PTR_ERR(chip->pctl); 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci return 0; 79262306a36Sopenharmony_ci} 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cistatic const struct of_device_id iproc_gpio_of_match[] = { 79562306a36Sopenharmony_ci { .compatible = "brcm,iproc-gpio" }, 79662306a36Sopenharmony_ci { .compatible = "brcm,cygnus-ccm-gpio" }, 79762306a36Sopenharmony_ci { .compatible = "brcm,cygnus-asiu-gpio" }, 79862306a36Sopenharmony_ci { .compatible = "brcm,cygnus-crmu-gpio" }, 79962306a36Sopenharmony_ci { .compatible = "brcm,iproc-nsp-gpio" }, 80062306a36Sopenharmony_ci { .compatible = "brcm,iproc-stingray-gpio" }, 80162306a36Sopenharmony_ci { /* sentinel */ } 80262306a36Sopenharmony_ci}; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_cistatic int iproc_gpio_probe(struct platform_device *pdev) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci struct device *dev = &pdev->dev; 80762306a36Sopenharmony_ci struct resource *res; 80862306a36Sopenharmony_ci struct iproc_gpio *chip; 80962306a36Sopenharmony_ci struct gpio_chip *gc; 81062306a36Sopenharmony_ci u32 ngpios, pinconf_disable_mask = 0; 81162306a36Sopenharmony_ci int irq, ret; 81262306a36Sopenharmony_ci bool no_pinconf = false; 81362306a36Sopenharmony_ci enum iproc_pinconf_ctrl_type io_ctrl_type = IOCTRL_TYPE_INVALID; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci /* NSP does not support drive strength config */ 81662306a36Sopenharmony_ci if (of_device_is_compatible(dev->of_node, "brcm,iproc-nsp-gpio")) 81762306a36Sopenharmony_ci pinconf_disable_mask = BIT(IPROC_PINCONF_DRIVE_STRENGTH); 81862306a36Sopenharmony_ci /* Stingray does not support pinconf in this controller */ 81962306a36Sopenharmony_ci else if (of_device_is_compatible(dev->of_node, 82062306a36Sopenharmony_ci "brcm,iproc-stingray-gpio")) 82162306a36Sopenharmony_ci no_pinconf = true; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); 82462306a36Sopenharmony_ci if (!chip) 82562306a36Sopenharmony_ci return -ENOMEM; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci chip->dev = dev; 82862306a36Sopenharmony_ci platform_set_drvdata(pdev, chip); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci chip->base = devm_platform_ioremap_resource(pdev, 0); 83162306a36Sopenharmony_ci if (IS_ERR(chip->base)) { 83262306a36Sopenharmony_ci dev_err(dev, "unable to map I/O memory\n"); 83362306a36Sopenharmony_ci return PTR_ERR(chip->base); 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 83762306a36Sopenharmony_ci if (res) { 83862306a36Sopenharmony_ci chip->io_ctrl = devm_ioremap_resource(dev, res); 83962306a36Sopenharmony_ci if (IS_ERR(chip->io_ctrl)) 84062306a36Sopenharmony_ci return PTR_ERR(chip->io_ctrl); 84162306a36Sopenharmony_ci if (of_device_is_compatible(dev->of_node, 84262306a36Sopenharmony_ci "brcm,cygnus-ccm-gpio")) 84362306a36Sopenharmony_ci io_ctrl_type = IOCTRL_TYPE_CDRU; 84462306a36Sopenharmony_ci else 84562306a36Sopenharmony_ci io_ctrl_type = IOCTRL_TYPE_AON; 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci chip->io_ctrl_type = io_ctrl_type; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci if (of_property_read_u32(dev->of_node, "ngpios", &ngpios)) { 85162306a36Sopenharmony_ci dev_err(&pdev->dev, "missing ngpios DT property\n"); 85262306a36Sopenharmony_ci return -ENODEV; 85362306a36Sopenharmony_ci } 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci raw_spin_lock_init(&chip->lock); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci gc = &chip->gc; 85862306a36Sopenharmony_ci gc->base = -1; 85962306a36Sopenharmony_ci gc->ngpio = ngpios; 86062306a36Sopenharmony_ci chip->num_banks = (ngpios + NGPIOS_PER_BANK - 1) / NGPIOS_PER_BANK; 86162306a36Sopenharmony_ci gc->label = dev_name(dev); 86262306a36Sopenharmony_ci gc->parent = dev; 86362306a36Sopenharmony_ci gc->request = iproc_gpio_request; 86462306a36Sopenharmony_ci gc->free = iproc_gpio_free; 86562306a36Sopenharmony_ci gc->direction_input = iproc_gpio_direction_input; 86662306a36Sopenharmony_ci gc->direction_output = iproc_gpio_direction_output; 86762306a36Sopenharmony_ci gc->get_direction = iproc_gpio_get_direction; 86862306a36Sopenharmony_ci gc->set = iproc_gpio_set; 86962306a36Sopenharmony_ci gc->get = iproc_gpio_get; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci chip->pinmux_is_supported = of_property_read_bool(dev->of_node, 87262306a36Sopenharmony_ci "gpio-ranges"); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci /* optional GPIO interrupt support */ 87562306a36Sopenharmony_ci irq = platform_get_irq_optional(pdev, 0); 87662306a36Sopenharmony_ci if (irq > 0) { 87762306a36Sopenharmony_ci struct gpio_irq_chip *girq; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci girq = &gc->irq; 88062306a36Sopenharmony_ci gpio_irq_chip_set_chip(girq, &iproc_gpio_irq_chip); 88162306a36Sopenharmony_ci girq->parent_handler = iproc_gpio_irq_handler; 88262306a36Sopenharmony_ci girq->num_parents = 1; 88362306a36Sopenharmony_ci girq->parents = devm_kcalloc(dev, 1, 88462306a36Sopenharmony_ci sizeof(*girq->parents), 88562306a36Sopenharmony_ci GFP_KERNEL); 88662306a36Sopenharmony_ci if (!girq->parents) 88762306a36Sopenharmony_ci return -ENOMEM; 88862306a36Sopenharmony_ci girq->parents[0] = irq; 88962306a36Sopenharmony_ci girq->default_type = IRQ_TYPE_NONE; 89062306a36Sopenharmony_ci girq->handler = handle_bad_irq; 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci ret = gpiochip_add_data(gc, chip); 89462306a36Sopenharmony_ci if (ret < 0) 89562306a36Sopenharmony_ci return dev_err_probe(dev, ret, "unable to add GPIO chip\n"); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci if (!no_pinconf) { 89862306a36Sopenharmony_ci ret = iproc_gpio_register_pinconf(chip); 89962306a36Sopenharmony_ci if (ret) { 90062306a36Sopenharmony_ci dev_err(dev, "unable to register pinconf\n"); 90162306a36Sopenharmony_ci goto err_rm_gpiochip; 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci if (pinconf_disable_mask) { 90562306a36Sopenharmony_ci ret = iproc_pinconf_disable_map_create(chip, 90662306a36Sopenharmony_ci pinconf_disable_mask); 90762306a36Sopenharmony_ci if (ret) { 90862306a36Sopenharmony_ci dev_err(dev, 90962306a36Sopenharmony_ci "unable to create pinconf disable map\n"); 91062306a36Sopenharmony_ci goto err_rm_gpiochip; 91162306a36Sopenharmony_ci } 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci return 0; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_cierr_rm_gpiochip: 91862306a36Sopenharmony_ci gpiochip_remove(gc); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci return ret; 92162306a36Sopenharmony_ci} 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_cistatic struct platform_driver iproc_gpio_driver = { 92462306a36Sopenharmony_ci .driver = { 92562306a36Sopenharmony_ci .name = "iproc-gpio", 92662306a36Sopenharmony_ci .of_match_table = iproc_gpio_of_match, 92762306a36Sopenharmony_ci }, 92862306a36Sopenharmony_ci .probe = iproc_gpio_probe, 92962306a36Sopenharmony_ci}; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_cistatic int __init iproc_gpio_init(void) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci return platform_driver_register(&iproc_gpio_driver); 93462306a36Sopenharmony_ci} 93562306a36Sopenharmony_ciarch_initcall_sync(iproc_gpio_init); 936