18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2014-2017 Broadcom 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci/* 78c2ecf20Sopenharmony_ci * This file contains the Broadcom Iproc GPIO driver that supports 3 88c2ecf20Sopenharmony_ci * GPIO controllers on Iproc including the ASIU GPIO controller, the 98c2ecf20Sopenharmony_ci * chipCommonG GPIO controller, and the always-on GPIO controller. Basic 108c2ecf20Sopenharmony_ci * PINCONF such as bias pull up/down, and drive strength are also supported 118c2ecf20Sopenharmony_ci * in this driver. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * It provides the functionality where pins from the GPIO can be 148c2ecf20Sopenharmony_ci * individually muxed to GPIO function, if individual pad 158c2ecf20Sopenharmony_ci * configuration is supported, through the interaction with respective 168c2ecf20Sopenharmony_ci * SoCs IOMUX controller. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/kernel.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 228c2ecf20Sopenharmony_ci#include <linux/io.h> 238c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 248c2ecf20Sopenharmony_ci#include <linux/ioport.h> 258c2ecf20Sopenharmony_ci#include <linux/of_device.h> 268c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 278c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinctrl.h> 288c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf.h> 298c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf-generic.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include "../pinctrl-utils.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define IPROC_GPIO_DATA_IN_OFFSET 0x00 348c2ecf20Sopenharmony_ci#define IPROC_GPIO_DATA_OUT_OFFSET 0x04 358c2ecf20Sopenharmony_ci#define IPROC_GPIO_OUT_EN_OFFSET 0x08 368c2ecf20Sopenharmony_ci#define IPROC_GPIO_INT_TYPE_OFFSET 0x0c 378c2ecf20Sopenharmony_ci#define IPROC_GPIO_INT_DE_OFFSET 0x10 388c2ecf20Sopenharmony_ci#define IPROC_GPIO_INT_EDGE_OFFSET 0x14 398c2ecf20Sopenharmony_ci#define IPROC_GPIO_INT_MSK_OFFSET 0x18 408c2ecf20Sopenharmony_ci#define IPROC_GPIO_INT_STAT_OFFSET 0x1c 418c2ecf20Sopenharmony_ci#define IPROC_GPIO_INT_MSTAT_OFFSET 0x20 428c2ecf20Sopenharmony_ci#define IPROC_GPIO_INT_CLR_OFFSET 0x24 438c2ecf20Sopenharmony_ci#define IPROC_GPIO_PAD_RES_OFFSET 0x34 448c2ecf20Sopenharmony_ci#define IPROC_GPIO_RES_EN_OFFSET 0x38 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* drive strength control for ASIU GPIO */ 478c2ecf20Sopenharmony_ci#define IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET 0x58 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* pinconf for CCM GPIO */ 508c2ecf20Sopenharmony_ci#define IPROC_GPIO_PULL_DN_OFFSET 0x10 518c2ecf20Sopenharmony_ci#define IPROC_GPIO_PULL_UP_OFFSET 0x14 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* pinconf for CRMU(aon) GPIO and CCM GPIO*/ 548c2ecf20Sopenharmony_ci#define IPROC_GPIO_DRV_CTRL_OFFSET 0x00 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define GPIO_BANK_SIZE 0x200 578c2ecf20Sopenharmony_ci#define NGPIOS_PER_BANK 32 588c2ecf20Sopenharmony_ci#define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define IPROC_GPIO_REG(pin, reg) (GPIO_BANK(pin) * GPIO_BANK_SIZE + (reg)) 618c2ecf20Sopenharmony_ci#define IPROC_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define GPIO_DRV_STRENGTH_BIT_SHIFT 20 648c2ecf20Sopenharmony_ci#define GPIO_DRV_STRENGTH_BITS 3 658c2ecf20Sopenharmony_ci#define GPIO_DRV_STRENGTH_BIT_MASK ((1 << GPIO_DRV_STRENGTH_BITS) - 1) 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cienum iproc_pinconf_param { 688c2ecf20Sopenharmony_ci IPROC_PINCONF_DRIVE_STRENGTH = 0, 698c2ecf20Sopenharmony_ci IPROC_PINCONF_BIAS_DISABLE, 708c2ecf20Sopenharmony_ci IPROC_PINCONF_BIAS_PULL_UP, 718c2ecf20Sopenharmony_ci IPROC_PINCONF_BIAS_PULL_DOWN, 728c2ecf20Sopenharmony_ci IPROC_PINCON_MAX, 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cienum iproc_pinconf_ctrl_type { 768c2ecf20Sopenharmony_ci IOCTRL_TYPE_AON = 1, 778c2ecf20Sopenharmony_ci IOCTRL_TYPE_CDRU, 788c2ecf20Sopenharmony_ci IOCTRL_TYPE_INVALID, 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* 828c2ecf20Sopenharmony_ci * Iproc GPIO core 838c2ecf20Sopenharmony_ci * 848c2ecf20Sopenharmony_ci * @dev: pointer to device 858c2ecf20Sopenharmony_ci * @base: I/O register base for Iproc GPIO controller 868c2ecf20Sopenharmony_ci * @io_ctrl: I/O register base for certain type of Iproc GPIO controller that 878c2ecf20Sopenharmony_ci * has the PINCONF support implemented outside of the GPIO block 888c2ecf20Sopenharmony_ci * @lock: lock to protect access to I/O registers 898c2ecf20Sopenharmony_ci * @gc: GPIO chip 908c2ecf20Sopenharmony_ci * @num_banks: number of GPIO banks, each bank supports up to 32 GPIOs 918c2ecf20Sopenharmony_ci * @pinmux_is_supported: flag to indicate this GPIO controller contains pins 928c2ecf20Sopenharmony_ci * that can be individually muxed to GPIO 938c2ecf20Sopenharmony_ci * @pinconf_disable: contains a list of PINCONF parameters that need to be 948c2ecf20Sopenharmony_ci * disabled 958c2ecf20Sopenharmony_ci * @nr_pinconf_disable: total number of PINCONF parameters that need to be 968c2ecf20Sopenharmony_ci * disabled 978c2ecf20Sopenharmony_ci * @pctl: pointer to pinctrl_dev 988c2ecf20Sopenharmony_ci * @pctldesc: pinctrl descriptor 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_cistruct iproc_gpio { 1018c2ecf20Sopenharmony_ci struct device *dev; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci void __iomem *base; 1048c2ecf20Sopenharmony_ci void __iomem *io_ctrl; 1058c2ecf20Sopenharmony_ci enum iproc_pinconf_ctrl_type io_ctrl_type; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci raw_spinlock_t lock; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci struct irq_chip irqchip; 1108c2ecf20Sopenharmony_ci struct gpio_chip gc; 1118c2ecf20Sopenharmony_ci unsigned num_banks; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci bool pinmux_is_supported; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci enum pin_config_param *pinconf_disable; 1168c2ecf20Sopenharmony_ci unsigned int nr_pinconf_disable; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci struct pinctrl_dev *pctl; 1198c2ecf20Sopenharmony_ci struct pinctrl_desc pctldesc; 1208c2ecf20Sopenharmony_ci}; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/* 1238c2ecf20Sopenharmony_ci * Mapping from PINCONF pins to GPIO pins is 1-to-1 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_cistatic inline unsigned iproc_pin_to_gpio(unsigned pin) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci return pin; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/** 1318c2ecf20Sopenharmony_ci * iproc_set_bit - set or clear one bit (corresponding to the GPIO pin) in a 1328c2ecf20Sopenharmony_ci * Iproc GPIO register 1338c2ecf20Sopenharmony_ci * 1348c2ecf20Sopenharmony_ci * @chip: Iproc GPIO device 1358c2ecf20Sopenharmony_ci * @reg: register offset 1368c2ecf20Sopenharmony_ci * @gpio: GPIO pin 1378c2ecf20Sopenharmony_ci * @set: set or clear 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_cistatic inline void iproc_set_bit(struct iproc_gpio *chip, unsigned int reg, 1408c2ecf20Sopenharmony_ci unsigned gpio, bool set) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci unsigned int offset = IPROC_GPIO_REG(gpio, reg); 1438c2ecf20Sopenharmony_ci unsigned int shift = IPROC_GPIO_SHIFT(gpio); 1448c2ecf20Sopenharmony_ci u32 val; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci val = readl(chip->base + offset); 1478c2ecf20Sopenharmony_ci if (set) 1488c2ecf20Sopenharmony_ci val |= BIT(shift); 1498c2ecf20Sopenharmony_ci else 1508c2ecf20Sopenharmony_ci val &= ~BIT(shift); 1518c2ecf20Sopenharmony_ci writel(val, chip->base + offset); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic inline bool iproc_get_bit(struct iproc_gpio *chip, unsigned int reg, 1558c2ecf20Sopenharmony_ci unsigned gpio) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci unsigned int offset = IPROC_GPIO_REG(gpio, reg); 1588c2ecf20Sopenharmony_ci unsigned int shift = IPROC_GPIO_SHIFT(gpio); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return !!(readl(chip->base + offset) & BIT(shift)); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic void iproc_gpio_irq_handler(struct irq_desc *desc) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_desc_get_handler_data(desc); 1668c2ecf20Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 1678c2ecf20Sopenharmony_ci struct irq_chip *irq_chip = irq_desc_get_chip(desc); 1688c2ecf20Sopenharmony_ci int i, bit; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci chained_irq_enter(irq_chip, desc); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* go through the entire GPIO banks and handle all interrupts */ 1738c2ecf20Sopenharmony_ci for (i = 0; i < chip->num_banks; i++) { 1748c2ecf20Sopenharmony_ci unsigned long val = readl(chip->base + (i * GPIO_BANK_SIZE) + 1758c2ecf20Sopenharmony_ci IPROC_GPIO_INT_MSTAT_OFFSET); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci for_each_set_bit(bit, &val, NGPIOS_PER_BANK) { 1788c2ecf20Sopenharmony_ci unsigned pin = NGPIOS_PER_BANK * i + bit; 1798c2ecf20Sopenharmony_ci int child_irq = irq_find_mapping(gc->irq.domain, pin); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* 1828c2ecf20Sopenharmony_ci * Clear the interrupt before invoking the 1838c2ecf20Sopenharmony_ci * handler, so we do not leave any window 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ci writel(BIT(bit), chip->base + (i * GPIO_BANK_SIZE) + 1868c2ecf20Sopenharmony_ci IPROC_GPIO_INT_CLR_OFFSET); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci generic_handle_irq(child_irq); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci chained_irq_exit(irq_chip, desc); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic void iproc_gpio_irq_ack(struct irq_data *d) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 1998c2ecf20Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 2008c2ecf20Sopenharmony_ci unsigned gpio = d->hwirq; 2018c2ecf20Sopenharmony_ci unsigned int offset = IPROC_GPIO_REG(gpio, 2028c2ecf20Sopenharmony_ci IPROC_GPIO_INT_CLR_OFFSET); 2038c2ecf20Sopenharmony_ci unsigned int shift = IPROC_GPIO_SHIFT(gpio); 2048c2ecf20Sopenharmony_ci u32 val = BIT(shift); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci writel(val, chip->base + offset); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/** 2108c2ecf20Sopenharmony_ci * iproc_gpio_irq_set_mask - mask/unmask a GPIO interrupt 2118c2ecf20Sopenharmony_ci * 2128c2ecf20Sopenharmony_ci * @d: IRQ chip data 2138c2ecf20Sopenharmony_ci * @unmask: mask/unmask GPIO interrupt 2148c2ecf20Sopenharmony_ci */ 2158c2ecf20Sopenharmony_cistatic void iproc_gpio_irq_set_mask(struct irq_data *d, bool unmask) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 2188c2ecf20Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 2198c2ecf20Sopenharmony_ci unsigned gpio = d->hwirq; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci iproc_set_bit(chip, IPROC_GPIO_INT_MSK_OFFSET, gpio, unmask); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic void iproc_gpio_irq_mask(struct irq_data *d) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 2278c2ecf20Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 2288c2ecf20Sopenharmony_ci unsigned long flags; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 2318c2ecf20Sopenharmony_ci iproc_gpio_irq_set_mask(d, false); 2328c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic void iproc_gpio_irq_unmask(struct irq_data *d) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 2388c2ecf20Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 2398c2ecf20Sopenharmony_ci unsigned long flags; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 2428c2ecf20Sopenharmony_ci iproc_gpio_irq_set_mask(d, true); 2438c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic int iproc_gpio_irq_set_type(struct irq_data *d, unsigned int type) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 2498c2ecf20Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 2508c2ecf20Sopenharmony_ci unsigned gpio = d->hwirq; 2518c2ecf20Sopenharmony_ci bool level_triggered = false; 2528c2ecf20Sopenharmony_ci bool dual_edge = false; 2538c2ecf20Sopenharmony_ci bool rising_or_high = false; 2548c2ecf20Sopenharmony_ci unsigned long flags; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci switch (type & IRQ_TYPE_SENSE_MASK) { 2578c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 2588c2ecf20Sopenharmony_ci rising_or_high = true; 2598c2ecf20Sopenharmony_ci break; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 2628c2ecf20Sopenharmony_ci break; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 2658c2ecf20Sopenharmony_ci dual_edge = true; 2668c2ecf20Sopenharmony_ci break; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 2698c2ecf20Sopenharmony_ci level_triggered = true; 2708c2ecf20Sopenharmony_ci rising_or_high = true; 2718c2ecf20Sopenharmony_ci break; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 2748c2ecf20Sopenharmony_ci level_triggered = true; 2758c2ecf20Sopenharmony_ci break; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci default: 2788c2ecf20Sopenharmony_ci dev_err(chip->dev, "invalid GPIO IRQ type 0x%x\n", 2798c2ecf20Sopenharmony_ci type); 2808c2ecf20Sopenharmony_ci return -EINVAL; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 2848c2ecf20Sopenharmony_ci iproc_set_bit(chip, IPROC_GPIO_INT_TYPE_OFFSET, gpio, 2858c2ecf20Sopenharmony_ci level_triggered); 2868c2ecf20Sopenharmony_ci iproc_set_bit(chip, IPROC_GPIO_INT_DE_OFFSET, gpio, dual_edge); 2878c2ecf20Sopenharmony_ci iproc_set_bit(chip, IPROC_GPIO_INT_EDGE_OFFSET, gpio, 2888c2ecf20Sopenharmony_ci rising_or_high); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (type & IRQ_TYPE_EDGE_BOTH) 2918c2ecf20Sopenharmony_ci irq_set_handler_locked(d, handle_edge_irq); 2928c2ecf20Sopenharmony_ci else 2938c2ecf20Sopenharmony_ci irq_set_handler_locked(d, handle_level_irq); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci dev_dbg(chip->dev, 2988c2ecf20Sopenharmony_ci "gpio:%u level_triggered:%d dual_edge:%d rising_or_high:%d\n", 2998c2ecf20Sopenharmony_ci gpio, level_triggered, dual_edge, rising_or_high); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return 0; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci/* 3058c2ecf20Sopenharmony_ci * Request the Iproc IOMUX pinmux controller to mux individual pins to GPIO 3068c2ecf20Sopenharmony_ci */ 3078c2ecf20Sopenharmony_cistatic int iproc_gpio_request(struct gpio_chip *gc, unsigned offset) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 3108c2ecf20Sopenharmony_ci unsigned gpio = gc->base + offset; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* not all Iproc GPIO pins can be muxed individually */ 3138c2ecf20Sopenharmony_ci if (!chip->pinmux_is_supported) 3148c2ecf20Sopenharmony_ci return 0; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci return pinctrl_gpio_request(gpio); 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic void iproc_gpio_free(struct gpio_chip *gc, unsigned offset) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 3228c2ecf20Sopenharmony_ci unsigned gpio = gc->base + offset; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (!chip->pinmux_is_supported) 3258c2ecf20Sopenharmony_ci return; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci pinctrl_gpio_free(gpio); 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic int iproc_gpio_direction_input(struct gpio_chip *gc, unsigned gpio) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 3338c2ecf20Sopenharmony_ci unsigned long flags; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 3368c2ecf20Sopenharmony_ci iproc_set_bit(chip, IPROC_GPIO_OUT_EN_OFFSET, gpio, false); 3378c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci dev_dbg(chip->dev, "gpio:%u set input\n", gpio); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci return 0; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic int iproc_gpio_direction_output(struct gpio_chip *gc, unsigned gpio, 3458c2ecf20Sopenharmony_ci int val) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 3488c2ecf20Sopenharmony_ci unsigned long flags; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 3518c2ecf20Sopenharmony_ci iproc_set_bit(chip, IPROC_GPIO_OUT_EN_OFFSET, gpio, true); 3528c2ecf20Sopenharmony_ci iproc_set_bit(chip, IPROC_GPIO_DATA_OUT_OFFSET, gpio, !!(val)); 3538c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci dev_dbg(chip->dev, "gpio:%u set output, value:%d\n", gpio, val); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci return 0; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic int iproc_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 3638c2ecf20Sopenharmony_ci unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_OUT_EN_OFFSET); 3648c2ecf20Sopenharmony_ci unsigned int shift = IPROC_GPIO_SHIFT(gpio); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (readl(chip->base + offset) & BIT(shift)) 3678c2ecf20Sopenharmony_ci return GPIO_LINE_DIRECTION_OUT; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci return GPIO_LINE_DIRECTION_IN; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic void iproc_gpio_set(struct gpio_chip *gc, unsigned gpio, int val) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 3758c2ecf20Sopenharmony_ci unsigned long flags; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 3788c2ecf20Sopenharmony_ci iproc_set_bit(chip, IPROC_GPIO_DATA_OUT_OFFSET, gpio, !!(val)); 3798c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci dev_dbg(chip->dev, "gpio:%u set, value:%d\n", gpio, val); 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic int iproc_gpio_get(struct gpio_chip *gc, unsigned gpio) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct iproc_gpio *chip = gpiochip_get_data(gc); 3878c2ecf20Sopenharmony_ci unsigned int offset = IPROC_GPIO_REG(gpio, 3888c2ecf20Sopenharmony_ci IPROC_GPIO_DATA_IN_OFFSET); 3898c2ecf20Sopenharmony_ci unsigned int shift = IPROC_GPIO_SHIFT(gpio); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci return !!(readl(chip->base + offset) & BIT(shift)); 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci/* 3958c2ecf20Sopenharmony_ci * Mapping of the iProc PINCONF parameters to the generic pin configuration 3968c2ecf20Sopenharmony_ci * parameters 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_cistatic const enum pin_config_param iproc_pinconf_disable_map[] = { 3998c2ecf20Sopenharmony_ci [IPROC_PINCONF_DRIVE_STRENGTH] = PIN_CONFIG_DRIVE_STRENGTH, 4008c2ecf20Sopenharmony_ci [IPROC_PINCONF_BIAS_DISABLE] = PIN_CONFIG_BIAS_DISABLE, 4018c2ecf20Sopenharmony_ci [IPROC_PINCONF_BIAS_PULL_UP] = PIN_CONFIG_BIAS_PULL_UP, 4028c2ecf20Sopenharmony_ci [IPROC_PINCONF_BIAS_PULL_DOWN] = PIN_CONFIG_BIAS_PULL_DOWN, 4038c2ecf20Sopenharmony_ci}; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic bool iproc_pinconf_param_is_disabled(struct iproc_gpio *chip, 4068c2ecf20Sopenharmony_ci enum pin_config_param param) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci unsigned int i; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (!chip->nr_pinconf_disable) 4118c2ecf20Sopenharmony_ci return false; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci for (i = 0; i < chip->nr_pinconf_disable; i++) 4148c2ecf20Sopenharmony_ci if (chip->pinconf_disable[i] == param) 4158c2ecf20Sopenharmony_ci return true; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return false; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic int iproc_pinconf_disable_map_create(struct iproc_gpio *chip, 4218c2ecf20Sopenharmony_ci unsigned long disable_mask) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci unsigned int map_size = ARRAY_SIZE(iproc_pinconf_disable_map); 4248c2ecf20Sopenharmony_ci unsigned int bit, nbits = 0; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* figure out total number of PINCONF parameters to disable */ 4278c2ecf20Sopenharmony_ci for_each_set_bit(bit, &disable_mask, map_size) 4288c2ecf20Sopenharmony_ci nbits++; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (!nbits) 4318c2ecf20Sopenharmony_ci return 0; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci /* 4348c2ecf20Sopenharmony_ci * Allocate an array to store PINCONF parameters that need to be 4358c2ecf20Sopenharmony_ci * disabled 4368c2ecf20Sopenharmony_ci */ 4378c2ecf20Sopenharmony_ci chip->pinconf_disable = devm_kcalloc(chip->dev, nbits, 4388c2ecf20Sopenharmony_ci sizeof(*chip->pinconf_disable), 4398c2ecf20Sopenharmony_ci GFP_KERNEL); 4408c2ecf20Sopenharmony_ci if (!chip->pinconf_disable) 4418c2ecf20Sopenharmony_ci return -ENOMEM; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci chip->nr_pinconf_disable = nbits; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci /* now store these parameters */ 4468c2ecf20Sopenharmony_ci nbits = 0; 4478c2ecf20Sopenharmony_ci for_each_set_bit(bit, &disable_mask, map_size) 4488c2ecf20Sopenharmony_ci chip->pinconf_disable[nbits++] = iproc_pinconf_disable_map[bit]; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci return 0; 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic int iproc_get_groups_count(struct pinctrl_dev *pctldev) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci return 1; 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci/* 4598c2ecf20Sopenharmony_ci * Only one group: "gpio_grp", since this local pinctrl device only performs 4608c2ecf20Sopenharmony_ci * GPIO specific PINCONF configurations 4618c2ecf20Sopenharmony_ci */ 4628c2ecf20Sopenharmony_cistatic const char *iproc_get_group_name(struct pinctrl_dev *pctldev, 4638c2ecf20Sopenharmony_ci unsigned selector) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci return "gpio_grp"; 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic const struct pinctrl_ops iproc_pctrl_ops = { 4698c2ecf20Sopenharmony_ci .get_groups_count = iproc_get_groups_count, 4708c2ecf20Sopenharmony_ci .get_group_name = iproc_get_group_name, 4718c2ecf20Sopenharmony_ci .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, 4728c2ecf20Sopenharmony_ci .dt_free_map = pinctrl_utils_free_map, 4738c2ecf20Sopenharmony_ci}; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic int iproc_gpio_set_pull(struct iproc_gpio *chip, unsigned gpio, 4768c2ecf20Sopenharmony_ci bool disable, bool pull_up) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci void __iomem *base; 4798c2ecf20Sopenharmony_ci unsigned long flags; 4808c2ecf20Sopenharmony_ci unsigned int shift; 4818c2ecf20Sopenharmony_ci u32 val_1, val_2; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 4848c2ecf20Sopenharmony_ci if (chip->io_ctrl_type == IOCTRL_TYPE_CDRU) { 4858c2ecf20Sopenharmony_ci base = chip->io_ctrl; 4868c2ecf20Sopenharmony_ci shift = IPROC_GPIO_SHIFT(gpio); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci val_1 = readl(base + IPROC_GPIO_PULL_UP_OFFSET); 4898c2ecf20Sopenharmony_ci val_2 = readl(base + IPROC_GPIO_PULL_DN_OFFSET); 4908c2ecf20Sopenharmony_ci if (disable) { 4918c2ecf20Sopenharmony_ci /* no pull-up or pull-down */ 4928c2ecf20Sopenharmony_ci val_1 &= ~BIT(shift); 4938c2ecf20Sopenharmony_ci val_2 &= ~BIT(shift); 4948c2ecf20Sopenharmony_ci } else if (pull_up) { 4958c2ecf20Sopenharmony_ci val_1 |= BIT(shift); 4968c2ecf20Sopenharmony_ci val_2 &= ~BIT(shift); 4978c2ecf20Sopenharmony_ci } else { 4988c2ecf20Sopenharmony_ci val_1 &= ~BIT(shift); 4998c2ecf20Sopenharmony_ci val_2 |= BIT(shift); 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci writel(val_1, base + IPROC_GPIO_PULL_UP_OFFSET); 5028c2ecf20Sopenharmony_ci writel(val_2, base + IPROC_GPIO_PULL_DN_OFFSET); 5038c2ecf20Sopenharmony_ci } else { 5048c2ecf20Sopenharmony_ci if (disable) { 5058c2ecf20Sopenharmony_ci iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio, 5068c2ecf20Sopenharmony_ci false); 5078c2ecf20Sopenharmony_ci } else { 5088c2ecf20Sopenharmony_ci iproc_set_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio, 5098c2ecf20Sopenharmony_ci pull_up); 5108c2ecf20Sopenharmony_ci iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio, 5118c2ecf20Sopenharmony_ci true); 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 5168c2ecf20Sopenharmony_ci dev_dbg(chip->dev, "gpio:%u set pullup:%d\n", gpio, pull_up); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return 0; 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic void iproc_gpio_get_pull(struct iproc_gpio *chip, unsigned gpio, 5228c2ecf20Sopenharmony_ci bool *disable, bool *pull_up) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci void __iomem *base; 5258c2ecf20Sopenharmony_ci unsigned long flags; 5268c2ecf20Sopenharmony_ci unsigned int shift; 5278c2ecf20Sopenharmony_ci u32 val_1, val_2; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 5308c2ecf20Sopenharmony_ci if (chip->io_ctrl_type == IOCTRL_TYPE_CDRU) { 5318c2ecf20Sopenharmony_ci base = chip->io_ctrl; 5328c2ecf20Sopenharmony_ci shift = IPROC_GPIO_SHIFT(gpio); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci val_1 = readl(base + IPROC_GPIO_PULL_UP_OFFSET) & BIT(shift); 5358c2ecf20Sopenharmony_ci val_2 = readl(base + IPROC_GPIO_PULL_DN_OFFSET) & BIT(shift); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci *pull_up = val_1 ? true : false; 5388c2ecf20Sopenharmony_ci *disable = (val_1 | val_2) ? false : true; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci } else { 5418c2ecf20Sopenharmony_ci *disable = !iproc_get_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio); 5428c2ecf20Sopenharmony_ci *pull_up = iproc_get_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio); 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci#define DRV_STRENGTH_OFFSET(gpio, bit, type) ((type) == IOCTRL_TYPE_AON ? \ 5488c2ecf20Sopenharmony_ci ((2 - (bit)) * 4 + IPROC_GPIO_DRV_CTRL_OFFSET) : \ 5498c2ecf20Sopenharmony_ci ((type) == IOCTRL_TYPE_CDRU) ? \ 5508c2ecf20Sopenharmony_ci ((bit) * 4 + IPROC_GPIO_DRV_CTRL_OFFSET) : \ 5518c2ecf20Sopenharmony_ci ((bit) * 4 + IPROC_GPIO_REG(gpio, IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET))) 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic int iproc_gpio_set_strength(struct iproc_gpio *chip, unsigned gpio, 5548c2ecf20Sopenharmony_ci unsigned strength) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci void __iomem *base; 5578c2ecf20Sopenharmony_ci unsigned int i, offset, shift; 5588c2ecf20Sopenharmony_ci u32 val; 5598c2ecf20Sopenharmony_ci unsigned long flags; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* make sure drive strength is supported */ 5628c2ecf20Sopenharmony_ci if (strength < 2 || strength > 16 || (strength % 2)) 5638c2ecf20Sopenharmony_ci return -ENOTSUPP; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (chip->io_ctrl) { 5668c2ecf20Sopenharmony_ci base = chip->io_ctrl; 5678c2ecf20Sopenharmony_ci } else { 5688c2ecf20Sopenharmony_ci base = chip->base; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci shift = IPROC_GPIO_SHIFT(gpio); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci dev_dbg(chip->dev, "gpio:%u set drive strength:%d mA\n", gpio, 5748c2ecf20Sopenharmony_ci strength); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 5778c2ecf20Sopenharmony_ci strength = (strength / 2) - 1; 5788c2ecf20Sopenharmony_ci for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) { 5798c2ecf20Sopenharmony_ci offset = DRV_STRENGTH_OFFSET(gpio, i, chip->io_ctrl_type); 5808c2ecf20Sopenharmony_ci val = readl(base + offset); 5818c2ecf20Sopenharmony_ci val &= ~BIT(shift); 5828c2ecf20Sopenharmony_ci val |= ((strength >> i) & 0x1) << shift; 5838c2ecf20Sopenharmony_ci writel(val, base + offset); 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci return 0; 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cistatic int iproc_gpio_get_strength(struct iproc_gpio *chip, unsigned gpio, 5918c2ecf20Sopenharmony_ci u16 *strength) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci void __iomem *base; 5948c2ecf20Sopenharmony_ci unsigned int i, offset, shift; 5958c2ecf20Sopenharmony_ci u32 val; 5968c2ecf20Sopenharmony_ci unsigned long flags; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci if (chip->io_ctrl) { 5998c2ecf20Sopenharmony_ci base = chip->io_ctrl; 6008c2ecf20Sopenharmony_ci } else { 6018c2ecf20Sopenharmony_ci base = chip->base; 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci shift = IPROC_GPIO_SHIFT(gpio); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&chip->lock, flags); 6078c2ecf20Sopenharmony_ci *strength = 0; 6088c2ecf20Sopenharmony_ci for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) { 6098c2ecf20Sopenharmony_ci offset = DRV_STRENGTH_OFFSET(gpio, i, chip->io_ctrl_type); 6108c2ecf20Sopenharmony_ci val = readl(base + offset) & BIT(shift); 6118c2ecf20Sopenharmony_ci val >>= shift; 6128c2ecf20Sopenharmony_ci *strength += (val << i); 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci /* convert to mA */ 6168c2ecf20Sopenharmony_ci *strength = (*strength + 1) * 2; 6178c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->lock, flags); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci return 0; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic int iproc_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin, 6238c2ecf20Sopenharmony_ci unsigned long *config) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci struct iproc_gpio *chip = pinctrl_dev_get_drvdata(pctldev); 6268c2ecf20Sopenharmony_ci enum pin_config_param param = pinconf_to_config_param(*config); 6278c2ecf20Sopenharmony_ci unsigned gpio = iproc_pin_to_gpio(pin); 6288c2ecf20Sopenharmony_ci u16 arg; 6298c2ecf20Sopenharmony_ci bool disable, pull_up; 6308c2ecf20Sopenharmony_ci int ret; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci if (iproc_pinconf_param_is_disabled(chip, param)) 6338c2ecf20Sopenharmony_ci return -ENOTSUPP; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci switch (param) { 6368c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_DISABLE: 6378c2ecf20Sopenharmony_ci iproc_gpio_get_pull(chip, gpio, &disable, &pull_up); 6388c2ecf20Sopenharmony_ci if (disable) 6398c2ecf20Sopenharmony_ci return 0; 6408c2ecf20Sopenharmony_ci else 6418c2ecf20Sopenharmony_ci return -EINVAL; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 6448c2ecf20Sopenharmony_ci iproc_gpio_get_pull(chip, gpio, &disable, &pull_up); 6458c2ecf20Sopenharmony_ci if (!disable && pull_up) 6468c2ecf20Sopenharmony_ci return 0; 6478c2ecf20Sopenharmony_ci else 6488c2ecf20Sopenharmony_ci return -EINVAL; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_DOWN: 6518c2ecf20Sopenharmony_ci iproc_gpio_get_pull(chip, gpio, &disable, &pull_up); 6528c2ecf20Sopenharmony_ci if (!disable && !pull_up) 6538c2ecf20Sopenharmony_ci return 0; 6548c2ecf20Sopenharmony_ci else 6558c2ecf20Sopenharmony_ci return -EINVAL; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci case PIN_CONFIG_DRIVE_STRENGTH: 6588c2ecf20Sopenharmony_ci ret = iproc_gpio_get_strength(chip, gpio, &arg); 6598c2ecf20Sopenharmony_ci if (ret) 6608c2ecf20Sopenharmony_ci return ret; 6618c2ecf20Sopenharmony_ci *config = pinconf_to_config_packed(param, arg); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci return 0; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci default: 6668c2ecf20Sopenharmony_ci return -ENOTSUPP; 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci return -ENOTSUPP; 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic int iproc_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, 6738c2ecf20Sopenharmony_ci unsigned long *configs, unsigned num_configs) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci struct iproc_gpio *chip = pinctrl_dev_get_drvdata(pctldev); 6768c2ecf20Sopenharmony_ci enum pin_config_param param; 6778c2ecf20Sopenharmony_ci u32 arg; 6788c2ecf20Sopenharmony_ci unsigned i, gpio = iproc_pin_to_gpio(pin); 6798c2ecf20Sopenharmony_ci int ret = -ENOTSUPP; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci for (i = 0; i < num_configs; i++) { 6828c2ecf20Sopenharmony_ci param = pinconf_to_config_param(configs[i]); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci if (iproc_pinconf_param_is_disabled(chip, param)) 6858c2ecf20Sopenharmony_ci return -ENOTSUPP; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci arg = pinconf_to_config_argument(configs[i]); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci switch (param) { 6908c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_DISABLE: 6918c2ecf20Sopenharmony_ci ret = iproc_gpio_set_pull(chip, gpio, true, false); 6928c2ecf20Sopenharmony_ci if (ret < 0) 6938c2ecf20Sopenharmony_ci goto out; 6948c2ecf20Sopenharmony_ci break; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_UP: 6978c2ecf20Sopenharmony_ci ret = iproc_gpio_set_pull(chip, gpio, false, true); 6988c2ecf20Sopenharmony_ci if (ret < 0) 6998c2ecf20Sopenharmony_ci goto out; 7008c2ecf20Sopenharmony_ci break; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci case PIN_CONFIG_BIAS_PULL_DOWN: 7038c2ecf20Sopenharmony_ci ret = iproc_gpio_set_pull(chip, gpio, false, false); 7048c2ecf20Sopenharmony_ci if (ret < 0) 7058c2ecf20Sopenharmony_ci goto out; 7068c2ecf20Sopenharmony_ci break; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci case PIN_CONFIG_DRIVE_STRENGTH: 7098c2ecf20Sopenharmony_ci ret = iproc_gpio_set_strength(chip, gpio, arg); 7108c2ecf20Sopenharmony_ci if (ret < 0) 7118c2ecf20Sopenharmony_ci goto out; 7128c2ecf20Sopenharmony_ci break; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci default: 7158c2ecf20Sopenharmony_ci dev_err(chip->dev, "invalid configuration\n"); 7168c2ecf20Sopenharmony_ci return -ENOTSUPP; 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci } /* for each config */ 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ciout: 7218c2ecf20Sopenharmony_ci return ret; 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cistatic const struct pinconf_ops iproc_pconf_ops = { 7258c2ecf20Sopenharmony_ci .is_generic = true, 7268c2ecf20Sopenharmony_ci .pin_config_get = iproc_pin_config_get, 7278c2ecf20Sopenharmony_ci .pin_config_set = iproc_pin_config_set, 7288c2ecf20Sopenharmony_ci}; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci/* 7318c2ecf20Sopenharmony_ci * Iproc GPIO controller supports some PINCONF related configurations such as 7328c2ecf20Sopenharmony_ci * pull up, pull down, and drive strength, when the pin is configured to GPIO 7338c2ecf20Sopenharmony_ci * 7348c2ecf20Sopenharmony_ci * Here a local pinctrl device is created with simple 1-to-1 pin mapping to the 7358c2ecf20Sopenharmony_ci * local GPIO pins 7368c2ecf20Sopenharmony_ci */ 7378c2ecf20Sopenharmony_cistatic int iproc_gpio_register_pinconf(struct iproc_gpio *chip) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci struct pinctrl_desc *pctldesc = &chip->pctldesc; 7408c2ecf20Sopenharmony_ci struct pinctrl_pin_desc *pins; 7418c2ecf20Sopenharmony_ci struct gpio_chip *gc = &chip->gc; 7428c2ecf20Sopenharmony_ci int i; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci pins = devm_kcalloc(chip->dev, gc->ngpio, sizeof(*pins), GFP_KERNEL); 7458c2ecf20Sopenharmony_ci if (!pins) 7468c2ecf20Sopenharmony_ci return -ENOMEM; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci for (i = 0; i < gc->ngpio; i++) { 7498c2ecf20Sopenharmony_ci pins[i].number = i; 7508c2ecf20Sopenharmony_ci pins[i].name = devm_kasprintf(chip->dev, GFP_KERNEL, 7518c2ecf20Sopenharmony_ci "gpio-%d", i); 7528c2ecf20Sopenharmony_ci if (!pins[i].name) 7538c2ecf20Sopenharmony_ci return -ENOMEM; 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci pctldesc->name = dev_name(chip->dev); 7578c2ecf20Sopenharmony_ci pctldesc->pctlops = &iproc_pctrl_ops; 7588c2ecf20Sopenharmony_ci pctldesc->pins = pins; 7598c2ecf20Sopenharmony_ci pctldesc->npins = gc->ngpio; 7608c2ecf20Sopenharmony_ci pctldesc->confops = &iproc_pconf_ops; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci chip->pctl = devm_pinctrl_register(chip->dev, pctldesc, chip); 7638c2ecf20Sopenharmony_ci if (IS_ERR(chip->pctl)) { 7648c2ecf20Sopenharmony_ci dev_err(chip->dev, "unable to register pinctrl device\n"); 7658c2ecf20Sopenharmony_ci return PTR_ERR(chip->pctl); 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci return 0; 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_cistatic const struct of_device_id iproc_gpio_of_match[] = { 7728c2ecf20Sopenharmony_ci { .compatible = "brcm,iproc-gpio" }, 7738c2ecf20Sopenharmony_ci { .compatible = "brcm,cygnus-ccm-gpio" }, 7748c2ecf20Sopenharmony_ci { .compatible = "brcm,cygnus-asiu-gpio" }, 7758c2ecf20Sopenharmony_ci { .compatible = "brcm,cygnus-crmu-gpio" }, 7768c2ecf20Sopenharmony_ci { .compatible = "brcm,iproc-nsp-gpio" }, 7778c2ecf20Sopenharmony_ci { .compatible = "brcm,iproc-stingray-gpio" }, 7788c2ecf20Sopenharmony_ci { /* sentinel */ } 7798c2ecf20Sopenharmony_ci}; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cistatic int iproc_gpio_probe(struct platform_device *pdev) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 7848c2ecf20Sopenharmony_ci struct resource *res; 7858c2ecf20Sopenharmony_ci struct iproc_gpio *chip; 7868c2ecf20Sopenharmony_ci struct gpio_chip *gc; 7878c2ecf20Sopenharmony_ci u32 ngpios, pinconf_disable_mask = 0; 7888c2ecf20Sopenharmony_ci int irq, ret; 7898c2ecf20Sopenharmony_ci bool no_pinconf = false; 7908c2ecf20Sopenharmony_ci enum iproc_pinconf_ctrl_type io_ctrl_type = IOCTRL_TYPE_INVALID; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci /* NSP does not support drive strength config */ 7938c2ecf20Sopenharmony_ci if (of_device_is_compatible(dev->of_node, "brcm,iproc-nsp-gpio")) 7948c2ecf20Sopenharmony_ci pinconf_disable_mask = BIT(IPROC_PINCONF_DRIVE_STRENGTH); 7958c2ecf20Sopenharmony_ci /* Stingray does not support pinconf in this controller */ 7968c2ecf20Sopenharmony_ci else if (of_device_is_compatible(dev->of_node, 7978c2ecf20Sopenharmony_ci "brcm,iproc-stingray-gpio")) 7988c2ecf20Sopenharmony_ci no_pinconf = true; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); 8018c2ecf20Sopenharmony_ci if (!chip) 8028c2ecf20Sopenharmony_ci return -ENOMEM; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci chip->dev = dev; 8058c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, chip); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci chip->base = devm_platform_ioremap_resource(pdev, 0); 8088c2ecf20Sopenharmony_ci if (IS_ERR(chip->base)) { 8098c2ecf20Sopenharmony_ci dev_err(dev, "unable to map I/O memory\n"); 8108c2ecf20Sopenharmony_ci return PTR_ERR(chip->base); 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 8148c2ecf20Sopenharmony_ci if (res) { 8158c2ecf20Sopenharmony_ci chip->io_ctrl = devm_ioremap_resource(dev, res); 8168c2ecf20Sopenharmony_ci if (IS_ERR(chip->io_ctrl)) { 8178c2ecf20Sopenharmony_ci dev_err(dev, "unable to map I/O memory\n"); 8188c2ecf20Sopenharmony_ci return PTR_ERR(chip->io_ctrl); 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci if (of_device_is_compatible(dev->of_node, 8218c2ecf20Sopenharmony_ci "brcm,cygnus-ccm-gpio")) 8228c2ecf20Sopenharmony_ci io_ctrl_type = IOCTRL_TYPE_CDRU; 8238c2ecf20Sopenharmony_ci else 8248c2ecf20Sopenharmony_ci io_ctrl_type = IOCTRL_TYPE_AON; 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci chip->io_ctrl_type = io_ctrl_type; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci if (of_property_read_u32(dev->of_node, "ngpios", &ngpios)) { 8308c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "missing ngpios DT property\n"); 8318c2ecf20Sopenharmony_ci return -ENODEV; 8328c2ecf20Sopenharmony_ci } 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci raw_spin_lock_init(&chip->lock); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci gc = &chip->gc; 8378c2ecf20Sopenharmony_ci gc->base = -1; 8388c2ecf20Sopenharmony_ci gc->ngpio = ngpios; 8398c2ecf20Sopenharmony_ci chip->num_banks = (ngpios + NGPIOS_PER_BANK - 1) / NGPIOS_PER_BANK; 8408c2ecf20Sopenharmony_ci gc->label = dev_name(dev); 8418c2ecf20Sopenharmony_ci gc->parent = dev; 8428c2ecf20Sopenharmony_ci gc->of_node = dev->of_node; 8438c2ecf20Sopenharmony_ci gc->request = iproc_gpio_request; 8448c2ecf20Sopenharmony_ci gc->free = iproc_gpio_free; 8458c2ecf20Sopenharmony_ci gc->direction_input = iproc_gpio_direction_input; 8468c2ecf20Sopenharmony_ci gc->direction_output = iproc_gpio_direction_output; 8478c2ecf20Sopenharmony_ci gc->get_direction = iproc_gpio_get_direction; 8488c2ecf20Sopenharmony_ci gc->set = iproc_gpio_set; 8498c2ecf20Sopenharmony_ci gc->get = iproc_gpio_get; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci chip->pinmux_is_supported = of_property_read_bool(dev->of_node, 8528c2ecf20Sopenharmony_ci "gpio-ranges"); 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci /* optional GPIO interrupt support */ 8558c2ecf20Sopenharmony_ci irq = platform_get_irq_optional(pdev, 0); 8568c2ecf20Sopenharmony_ci if (irq > 0) { 8578c2ecf20Sopenharmony_ci struct irq_chip *irqc; 8588c2ecf20Sopenharmony_ci struct gpio_irq_chip *girq; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci irqc = &chip->irqchip; 8618c2ecf20Sopenharmony_ci irqc->name = dev_name(dev); 8628c2ecf20Sopenharmony_ci irqc->irq_ack = iproc_gpio_irq_ack; 8638c2ecf20Sopenharmony_ci irqc->irq_mask = iproc_gpio_irq_mask; 8648c2ecf20Sopenharmony_ci irqc->irq_unmask = iproc_gpio_irq_unmask; 8658c2ecf20Sopenharmony_ci irqc->irq_set_type = iproc_gpio_irq_set_type; 8668c2ecf20Sopenharmony_ci irqc->irq_enable = iproc_gpio_irq_unmask; 8678c2ecf20Sopenharmony_ci irqc->irq_disable = iproc_gpio_irq_mask; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci girq = &gc->irq; 8708c2ecf20Sopenharmony_ci girq->chip = irqc; 8718c2ecf20Sopenharmony_ci girq->parent_handler = iproc_gpio_irq_handler; 8728c2ecf20Sopenharmony_ci girq->num_parents = 1; 8738c2ecf20Sopenharmony_ci girq->parents = devm_kcalloc(dev, 1, 8748c2ecf20Sopenharmony_ci sizeof(*girq->parents), 8758c2ecf20Sopenharmony_ci GFP_KERNEL); 8768c2ecf20Sopenharmony_ci if (!girq->parents) 8778c2ecf20Sopenharmony_ci return -ENOMEM; 8788c2ecf20Sopenharmony_ci girq->parents[0] = irq; 8798c2ecf20Sopenharmony_ci girq->default_type = IRQ_TYPE_NONE; 8808c2ecf20Sopenharmony_ci girq->handler = handle_bad_irq; 8818c2ecf20Sopenharmony_ci } 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci ret = gpiochip_add_data(gc, chip); 8848c2ecf20Sopenharmony_ci if (ret < 0) { 8858c2ecf20Sopenharmony_ci dev_err(dev, "unable to add GPIO chip\n"); 8868c2ecf20Sopenharmony_ci return ret; 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci if (!no_pinconf) { 8908c2ecf20Sopenharmony_ci ret = iproc_gpio_register_pinconf(chip); 8918c2ecf20Sopenharmony_ci if (ret) { 8928c2ecf20Sopenharmony_ci dev_err(dev, "unable to register pinconf\n"); 8938c2ecf20Sopenharmony_ci goto err_rm_gpiochip; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci if (pinconf_disable_mask) { 8978c2ecf20Sopenharmony_ci ret = iproc_pinconf_disable_map_create(chip, 8988c2ecf20Sopenharmony_ci pinconf_disable_mask); 8998c2ecf20Sopenharmony_ci if (ret) { 9008c2ecf20Sopenharmony_ci dev_err(dev, 9018c2ecf20Sopenharmony_ci "unable to create pinconf disable map\n"); 9028c2ecf20Sopenharmony_ci goto err_rm_gpiochip; 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci } 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci return 0; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_cierr_rm_gpiochip: 9108c2ecf20Sopenharmony_ci gpiochip_remove(gc); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci return ret; 9138c2ecf20Sopenharmony_ci} 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_cistatic struct platform_driver iproc_gpio_driver = { 9168c2ecf20Sopenharmony_ci .driver = { 9178c2ecf20Sopenharmony_ci .name = "iproc-gpio", 9188c2ecf20Sopenharmony_ci .of_match_table = iproc_gpio_of_match, 9198c2ecf20Sopenharmony_ci }, 9208c2ecf20Sopenharmony_ci .probe = iproc_gpio_probe, 9218c2ecf20Sopenharmony_ci}; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_cistatic int __init iproc_gpio_init(void) 9248c2ecf20Sopenharmony_ci{ 9258c2ecf20Sopenharmony_ci return platform_driver_register(&iproc_gpio_driver); 9268c2ecf20Sopenharmony_ci} 9278c2ecf20Sopenharmony_ciarch_initcall_sync(iproc_gpio_init); 928