18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * SPEAr platform PLGPIO driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2012 ST Microelectronics 58c2ecf20Sopenharmony_ci * Viresh Kumar <viresh.kumar@linaro.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 88c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any 98c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/clk.h> 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/of.h> 188c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 198c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h> 208c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 218c2ecf20Sopenharmony_ci#include <linux/pm.h> 228c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define MAX_GPIO_PER_REG 32 258c2ecf20Sopenharmony_ci#define PIN_OFFSET(pin) (pin % MAX_GPIO_PER_REG) 268c2ecf20Sopenharmony_ci#define REG_OFFSET(base, reg, pin) (base + reg + (pin / MAX_GPIO_PER_REG) \ 278c2ecf20Sopenharmony_ci * sizeof(int *)) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* 308c2ecf20Sopenharmony_ci * plgpio pins in all machines are not one to one mapped, bitwise with registers 318c2ecf20Sopenharmony_ci * bits. These set of macros define register masks for which below functions 328c2ecf20Sopenharmony_ci * (pin_to_offset and offset_to_pin) are required to be called. 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci#define PTO_ENB_REG 0x001 358c2ecf20Sopenharmony_ci#define PTO_WDATA_REG 0x002 368c2ecf20Sopenharmony_ci#define PTO_DIR_REG 0x004 378c2ecf20Sopenharmony_ci#define PTO_IE_REG 0x008 388c2ecf20Sopenharmony_ci#define PTO_RDATA_REG 0x010 398c2ecf20Sopenharmony_ci#define PTO_MIS_REG 0x020 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistruct plgpio_regs { 428c2ecf20Sopenharmony_ci u32 enb; /* enable register */ 438c2ecf20Sopenharmony_ci u32 wdata; /* write data register */ 448c2ecf20Sopenharmony_ci u32 dir; /* direction set register */ 458c2ecf20Sopenharmony_ci u32 rdata; /* read data register */ 468c2ecf20Sopenharmony_ci u32 ie; /* interrupt enable register */ 478c2ecf20Sopenharmony_ci u32 mis; /* mask interrupt status register */ 488c2ecf20Sopenharmony_ci u32 eit; /* edge interrupt type */ 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* 528c2ecf20Sopenharmony_ci * struct plgpio: plgpio driver specific structure 538c2ecf20Sopenharmony_ci * 548c2ecf20Sopenharmony_ci * lock: lock for guarding gpio registers 558c2ecf20Sopenharmony_ci * base: base address of plgpio block 568c2ecf20Sopenharmony_ci * chip: gpio framework specific chip information structure 578c2ecf20Sopenharmony_ci * p2o: function ptr for pin to offset conversion. This is required only for 588c2ecf20Sopenharmony_ci * machines where mapping b/w pin and offset is not 1-to-1. 598c2ecf20Sopenharmony_ci * o2p: function ptr for offset to pin conversion. This is required only for 608c2ecf20Sopenharmony_ci * machines where mapping b/w pin and offset is not 1-to-1. 618c2ecf20Sopenharmony_ci * p2o_regs: mask of registers for which p2o and o2p are applicable 628c2ecf20Sopenharmony_ci * regs: register offsets 638c2ecf20Sopenharmony_ci * csave_regs: context save registers for standby/sleep/hibernate cases 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_cistruct plgpio { 668c2ecf20Sopenharmony_ci spinlock_t lock; 678c2ecf20Sopenharmony_ci void __iomem *base; 688c2ecf20Sopenharmony_ci struct clk *clk; 698c2ecf20Sopenharmony_ci struct gpio_chip chip; 708c2ecf20Sopenharmony_ci int (*p2o)(int pin); /* pin_to_offset */ 718c2ecf20Sopenharmony_ci int (*o2p)(int offset); /* offset_to_pin */ 728c2ecf20Sopenharmony_ci u32 p2o_regs; 738c2ecf20Sopenharmony_ci struct plgpio_regs regs; 748c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 758c2ecf20Sopenharmony_ci struct plgpio_regs *csave_regs; 768c2ecf20Sopenharmony_ci#endif 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* register manipulation inline functions */ 808c2ecf20Sopenharmony_cistatic inline u32 is_plgpio_set(void __iomem *base, u32 pin, u32 reg) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci u32 offset = PIN_OFFSET(pin); 838c2ecf20Sopenharmony_ci void __iomem *reg_off = REG_OFFSET(base, reg, pin); 848c2ecf20Sopenharmony_ci u32 val = readl_relaxed(reg_off); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return !!(val & (1 << offset)); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic inline void plgpio_reg_set(void __iomem *base, u32 pin, u32 reg) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci u32 offset = PIN_OFFSET(pin); 928c2ecf20Sopenharmony_ci void __iomem *reg_off = REG_OFFSET(base, reg, pin); 938c2ecf20Sopenharmony_ci u32 val = readl_relaxed(reg_off); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci writel_relaxed(val | (1 << offset), reg_off); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic inline void plgpio_reg_reset(void __iomem *base, u32 pin, u32 reg) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci u32 offset = PIN_OFFSET(pin); 1018c2ecf20Sopenharmony_ci void __iomem *reg_off = REG_OFFSET(base, reg, pin); 1028c2ecf20Sopenharmony_ci u32 val = readl_relaxed(reg_off); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci writel_relaxed(val & ~(1 << offset), reg_off); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/* gpio framework specific routines */ 1088c2ecf20Sopenharmony_cistatic int plgpio_direction_input(struct gpio_chip *chip, unsigned offset) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct plgpio *plgpio = gpiochip_get_data(chip); 1118c2ecf20Sopenharmony_ci unsigned long flags; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* get correct offset for "offset" pin */ 1148c2ecf20Sopenharmony_ci if (plgpio->p2o && (plgpio->p2o_regs & PTO_DIR_REG)) { 1158c2ecf20Sopenharmony_ci offset = plgpio->p2o(offset); 1168c2ecf20Sopenharmony_ci if (offset == -1) 1178c2ecf20Sopenharmony_ci return -EINVAL; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci spin_lock_irqsave(&plgpio->lock, flags); 1218c2ecf20Sopenharmony_ci plgpio_reg_set(plgpio->base, offset, plgpio->regs.dir); 1228c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&plgpio->lock, flags); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return 0; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int plgpio_direction_output(struct gpio_chip *chip, unsigned offset, 1288c2ecf20Sopenharmony_ci int value) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct plgpio *plgpio = gpiochip_get_data(chip); 1318c2ecf20Sopenharmony_ci unsigned long flags; 1328c2ecf20Sopenharmony_ci unsigned dir_offset = offset, wdata_offset = offset, tmp; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* get correct offset for "offset" pin */ 1358c2ecf20Sopenharmony_ci if (plgpio->p2o && (plgpio->p2o_regs & (PTO_DIR_REG | PTO_WDATA_REG))) { 1368c2ecf20Sopenharmony_ci tmp = plgpio->p2o(offset); 1378c2ecf20Sopenharmony_ci if (tmp == -1) 1388c2ecf20Sopenharmony_ci return -EINVAL; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (plgpio->p2o_regs & PTO_DIR_REG) 1418c2ecf20Sopenharmony_ci dir_offset = tmp; 1428c2ecf20Sopenharmony_ci if (plgpio->p2o_regs & PTO_WDATA_REG) 1438c2ecf20Sopenharmony_ci wdata_offset = tmp; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci spin_lock_irqsave(&plgpio->lock, flags); 1478c2ecf20Sopenharmony_ci if (value) 1488c2ecf20Sopenharmony_ci plgpio_reg_set(plgpio->base, wdata_offset, 1498c2ecf20Sopenharmony_ci plgpio->regs.wdata); 1508c2ecf20Sopenharmony_ci else 1518c2ecf20Sopenharmony_ci plgpio_reg_reset(plgpio->base, wdata_offset, 1528c2ecf20Sopenharmony_ci plgpio->regs.wdata); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci plgpio_reg_reset(plgpio->base, dir_offset, plgpio->regs.dir); 1558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&plgpio->lock, flags); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return 0; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic int plgpio_get_value(struct gpio_chip *chip, unsigned offset) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct plgpio *plgpio = gpiochip_get_data(chip); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (offset >= chip->ngpio) 1658c2ecf20Sopenharmony_ci return -EINVAL; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* get correct offset for "offset" pin */ 1688c2ecf20Sopenharmony_ci if (plgpio->p2o && (plgpio->p2o_regs & PTO_RDATA_REG)) { 1698c2ecf20Sopenharmony_ci offset = plgpio->p2o(offset); 1708c2ecf20Sopenharmony_ci if (offset == -1) 1718c2ecf20Sopenharmony_ci return -EINVAL; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return is_plgpio_set(plgpio->base, offset, plgpio->regs.rdata); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void plgpio_set_value(struct gpio_chip *chip, unsigned offset, int value) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct plgpio *plgpio = gpiochip_get_data(chip); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (offset >= chip->ngpio) 1828c2ecf20Sopenharmony_ci return; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* get correct offset for "offset" pin */ 1858c2ecf20Sopenharmony_ci if (plgpio->p2o && (plgpio->p2o_regs & PTO_WDATA_REG)) { 1868c2ecf20Sopenharmony_ci offset = plgpio->p2o(offset); 1878c2ecf20Sopenharmony_ci if (offset == -1) 1888c2ecf20Sopenharmony_ci return; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (value) 1928c2ecf20Sopenharmony_ci plgpio_reg_set(plgpio->base, offset, plgpio->regs.wdata); 1938c2ecf20Sopenharmony_ci else 1948c2ecf20Sopenharmony_ci plgpio_reg_reset(plgpio->base, offset, plgpio->regs.wdata); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int plgpio_request(struct gpio_chip *chip, unsigned offset) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct plgpio *plgpio = gpiochip_get_data(chip); 2008c2ecf20Sopenharmony_ci int gpio = chip->base + offset; 2018c2ecf20Sopenharmony_ci unsigned long flags; 2028c2ecf20Sopenharmony_ci int ret = 0; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (offset >= chip->ngpio) 2058c2ecf20Sopenharmony_ci return -EINVAL; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci ret = pinctrl_gpio_request(gpio); 2088c2ecf20Sopenharmony_ci if (ret) 2098c2ecf20Sopenharmony_ci return ret; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (!IS_ERR(plgpio->clk)) { 2128c2ecf20Sopenharmony_ci ret = clk_enable(plgpio->clk); 2138c2ecf20Sopenharmony_ci if (ret) 2148c2ecf20Sopenharmony_ci goto err0; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (plgpio->regs.enb == -1) 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* 2218c2ecf20Sopenharmony_ci * put gpio in IN mode before enabling it. This make enabling gpio safe 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_ci ret = plgpio_direction_input(chip, offset); 2248c2ecf20Sopenharmony_ci if (ret) 2258c2ecf20Sopenharmony_ci goto err1; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* get correct offset for "offset" pin */ 2288c2ecf20Sopenharmony_ci if (plgpio->p2o && (plgpio->p2o_regs & PTO_ENB_REG)) { 2298c2ecf20Sopenharmony_ci offset = plgpio->p2o(offset); 2308c2ecf20Sopenharmony_ci if (offset == -1) { 2318c2ecf20Sopenharmony_ci ret = -EINVAL; 2328c2ecf20Sopenharmony_ci goto err1; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci spin_lock_irqsave(&plgpio->lock, flags); 2378c2ecf20Sopenharmony_ci plgpio_reg_set(plgpio->base, offset, plgpio->regs.enb); 2388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&plgpio->lock, flags); 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cierr1: 2428c2ecf20Sopenharmony_ci if (!IS_ERR(plgpio->clk)) 2438c2ecf20Sopenharmony_ci clk_disable(plgpio->clk); 2448c2ecf20Sopenharmony_cierr0: 2458c2ecf20Sopenharmony_ci pinctrl_gpio_free(gpio); 2468c2ecf20Sopenharmony_ci return ret; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic void plgpio_free(struct gpio_chip *chip, unsigned offset) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct plgpio *plgpio = gpiochip_get_data(chip); 2528c2ecf20Sopenharmony_ci int gpio = chip->base + offset; 2538c2ecf20Sopenharmony_ci unsigned long flags; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (offset >= chip->ngpio) 2568c2ecf20Sopenharmony_ci return; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (plgpio->regs.enb == -1) 2598c2ecf20Sopenharmony_ci goto disable_clk; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* get correct offset for "offset" pin */ 2628c2ecf20Sopenharmony_ci if (plgpio->p2o && (plgpio->p2o_regs & PTO_ENB_REG)) { 2638c2ecf20Sopenharmony_ci offset = plgpio->p2o(offset); 2648c2ecf20Sopenharmony_ci if (offset == -1) 2658c2ecf20Sopenharmony_ci return; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci spin_lock_irqsave(&plgpio->lock, flags); 2698c2ecf20Sopenharmony_ci plgpio_reg_reset(plgpio->base, offset, plgpio->regs.enb); 2708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&plgpio->lock, flags); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cidisable_clk: 2738c2ecf20Sopenharmony_ci if (!IS_ERR(plgpio->clk)) 2748c2ecf20Sopenharmony_ci clk_disable(plgpio->clk); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci pinctrl_gpio_free(gpio); 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci/* PLGPIO IRQ */ 2808c2ecf20Sopenharmony_cistatic void plgpio_irq_disable(struct irq_data *d) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 2838c2ecf20Sopenharmony_ci struct plgpio *plgpio = gpiochip_get_data(gc); 2848c2ecf20Sopenharmony_ci int offset = d->hwirq; 2858c2ecf20Sopenharmony_ci unsigned long flags; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* get correct offset for "offset" pin */ 2888c2ecf20Sopenharmony_ci if (plgpio->p2o && (plgpio->p2o_regs & PTO_IE_REG)) { 2898c2ecf20Sopenharmony_ci offset = plgpio->p2o(offset); 2908c2ecf20Sopenharmony_ci if (offset == -1) 2918c2ecf20Sopenharmony_ci return; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci spin_lock_irqsave(&plgpio->lock, flags); 2958c2ecf20Sopenharmony_ci plgpio_reg_set(plgpio->base, offset, plgpio->regs.ie); 2968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&plgpio->lock, flags); 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic void plgpio_irq_enable(struct irq_data *d) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 3028c2ecf20Sopenharmony_ci struct plgpio *plgpio = gpiochip_get_data(gc); 3038c2ecf20Sopenharmony_ci int offset = d->hwirq; 3048c2ecf20Sopenharmony_ci unsigned long flags; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* get correct offset for "offset" pin */ 3078c2ecf20Sopenharmony_ci if (plgpio->p2o && (plgpio->p2o_regs & PTO_IE_REG)) { 3088c2ecf20Sopenharmony_ci offset = plgpio->p2o(offset); 3098c2ecf20Sopenharmony_ci if (offset == -1) 3108c2ecf20Sopenharmony_ci return; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci spin_lock_irqsave(&plgpio->lock, flags); 3148c2ecf20Sopenharmony_ci plgpio_reg_reset(plgpio->base, offset, plgpio->regs.ie); 3158c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&plgpio->lock, flags); 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic int plgpio_irq_set_type(struct irq_data *d, unsigned trigger) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 3218c2ecf20Sopenharmony_ci struct plgpio *plgpio = gpiochip_get_data(gc); 3228c2ecf20Sopenharmony_ci int offset = d->hwirq; 3238c2ecf20Sopenharmony_ci void __iomem *reg_off; 3248c2ecf20Sopenharmony_ci unsigned int supported_type = 0, val; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (offset >= plgpio->chip.ngpio) 3278c2ecf20Sopenharmony_ci return -EINVAL; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (plgpio->regs.eit == -1) 3308c2ecf20Sopenharmony_ci supported_type = IRQ_TYPE_LEVEL_HIGH; 3318c2ecf20Sopenharmony_ci else 3328c2ecf20Sopenharmony_ci supported_type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (!(trigger & supported_type)) 3358c2ecf20Sopenharmony_ci return -EINVAL; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (plgpio->regs.eit == -1) 3388c2ecf20Sopenharmony_ci return 0; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci reg_off = REG_OFFSET(plgpio->base, plgpio->regs.eit, offset); 3418c2ecf20Sopenharmony_ci val = readl_relaxed(reg_off); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci offset = PIN_OFFSET(offset); 3448c2ecf20Sopenharmony_ci if (trigger & IRQ_TYPE_EDGE_RISING) 3458c2ecf20Sopenharmony_ci writel_relaxed(val | (1 << offset), reg_off); 3468c2ecf20Sopenharmony_ci else 3478c2ecf20Sopenharmony_ci writel_relaxed(val & ~(1 << offset), reg_off); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci return 0; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic struct irq_chip plgpio_irqchip = { 3538c2ecf20Sopenharmony_ci .name = "PLGPIO", 3548c2ecf20Sopenharmony_ci .irq_enable = plgpio_irq_enable, 3558c2ecf20Sopenharmony_ci .irq_disable = plgpio_irq_disable, 3568c2ecf20Sopenharmony_ci .irq_set_type = plgpio_irq_set_type, 3578c2ecf20Sopenharmony_ci}; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic void plgpio_irq_handler(struct irq_desc *desc) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_desc_get_handler_data(desc); 3628c2ecf20Sopenharmony_ci struct plgpio *plgpio = gpiochip_get_data(gc); 3638c2ecf20Sopenharmony_ci struct irq_chip *irqchip = irq_desc_get_chip(desc); 3648c2ecf20Sopenharmony_ci int regs_count, count, pin, offset, i = 0; 3658c2ecf20Sopenharmony_ci unsigned long pending; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci count = plgpio->chip.ngpio; 3688c2ecf20Sopenharmony_ci regs_count = DIV_ROUND_UP(count, MAX_GPIO_PER_REG); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci chained_irq_enter(irqchip, desc); 3718c2ecf20Sopenharmony_ci /* check all plgpio MIS registers for a possible interrupt */ 3728c2ecf20Sopenharmony_ci for (; i < regs_count; i++) { 3738c2ecf20Sopenharmony_ci pending = readl_relaxed(plgpio->base + plgpio->regs.mis + 3748c2ecf20Sopenharmony_ci i * sizeof(int *)); 3758c2ecf20Sopenharmony_ci if (!pending) 3768c2ecf20Sopenharmony_ci continue; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* clear interrupts */ 3798c2ecf20Sopenharmony_ci writel_relaxed(~pending, plgpio->base + plgpio->regs.mis + 3808c2ecf20Sopenharmony_ci i * sizeof(int *)); 3818c2ecf20Sopenharmony_ci /* 3828c2ecf20Sopenharmony_ci * clear extra bits in last register having gpios < MAX/REG 3838c2ecf20Sopenharmony_ci * ex: Suppose there are max 102 plgpios. then last register 3848c2ecf20Sopenharmony_ci * must have only (102 - MAX_GPIO_PER_REG * 3) = 6 relevant bits 3858c2ecf20Sopenharmony_ci * so, we must not take other 28 bits into consideration for 3868c2ecf20Sopenharmony_ci * checking interrupt. so clear those bits. 3878c2ecf20Sopenharmony_ci */ 3888c2ecf20Sopenharmony_ci count = count - i * MAX_GPIO_PER_REG; 3898c2ecf20Sopenharmony_ci if (count < MAX_GPIO_PER_REG) 3908c2ecf20Sopenharmony_ci pending &= (1 << count) - 1; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci for_each_set_bit(offset, &pending, MAX_GPIO_PER_REG) { 3938c2ecf20Sopenharmony_ci /* get correct pin for "offset" */ 3948c2ecf20Sopenharmony_ci if (plgpio->o2p && (plgpio->p2o_regs & PTO_MIS_REG)) { 3958c2ecf20Sopenharmony_ci pin = plgpio->o2p(offset); 3968c2ecf20Sopenharmony_ci if (pin == -1) 3978c2ecf20Sopenharmony_ci continue; 3988c2ecf20Sopenharmony_ci } else 3998c2ecf20Sopenharmony_ci pin = offset; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* get correct irq line number */ 4028c2ecf20Sopenharmony_ci pin = i * MAX_GPIO_PER_REG + pin; 4038c2ecf20Sopenharmony_ci generic_handle_irq( 4048c2ecf20Sopenharmony_ci irq_find_mapping(gc->irq.domain, pin)); 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci chained_irq_exit(irqchip, desc); 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci/* 4118c2ecf20Sopenharmony_ci * pin to offset and offset to pin converter functions 4128c2ecf20Sopenharmony_ci * 4138c2ecf20Sopenharmony_ci * In spear310 there is inconsistency among bit positions in plgpio regiseters, 4148c2ecf20Sopenharmony_ci * for different plgpio pins. For example: for pin 27, bit offset is 23, pin 4158c2ecf20Sopenharmony_ci * 28-33 are not supported, pin 95 has offset bit 95, bit 100 has offset bit 1 4168c2ecf20Sopenharmony_ci */ 4178c2ecf20Sopenharmony_cistatic int spear310_p2o(int pin) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci int offset = pin; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (pin <= 27) 4228c2ecf20Sopenharmony_ci offset += 4; 4238c2ecf20Sopenharmony_ci else if (pin <= 33) 4248c2ecf20Sopenharmony_ci offset = -1; 4258c2ecf20Sopenharmony_ci else if (pin <= 97) 4268c2ecf20Sopenharmony_ci offset -= 2; 4278c2ecf20Sopenharmony_ci else if (pin <= 101) 4288c2ecf20Sopenharmony_ci offset = 101 - pin; 4298c2ecf20Sopenharmony_ci else 4308c2ecf20Sopenharmony_ci offset = -1; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci return offset; 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic int spear310_o2p(int offset) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci if (offset <= 3) 4388c2ecf20Sopenharmony_ci return 101 - offset; 4398c2ecf20Sopenharmony_ci else if (offset <= 31) 4408c2ecf20Sopenharmony_ci return offset - 4; 4418c2ecf20Sopenharmony_ci else 4428c2ecf20Sopenharmony_ci return offset + 2; 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic int plgpio_probe_dt(struct platform_device *pdev, struct plgpio *plgpio) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 4488c2ecf20Sopenharmony_ci int ret = -EINVAL; 4498c2ecf20Sopenharmony_ci u32 val; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (of_machine_is_compatible("st,spear310")) { 4528c2ecf20Sopenharmony_ci plgpio->p2o = spear310_p2o; 4538c2ecf20Sopenharmony_ci plgpio->o2p = spear310_o2p; 4548c2ecf20Sopenharmony_ci plgpio->p2o_regs = PTO_WDATA_REG | PTO_DIR_REG | PTO_IE_REG | 4558c2ecf20Sopenharmony_ci PTO_RDATA_REG | PTO_MIS_REG; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "st-plgpio,ngpio", &val)) { 4598c2ecf20Sopenharmony_ci plgpio->chip.ngpio = val; 4608c2ecf20Sopenharmony_ci } else { 4618c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "DT: Invalid ngpio field\n"); 4628c2ecf20Sopenharmony_ci goto end; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "st-plgpio,enb-reg", &val)) 4668c2ecf20Sopenharmony_ci plgpio->regs.enb = val; 4678c2ecf20Sopenharmony_ci else 4688c2ecf20Sopenharmony_ci plgpio->regs.enb = -1; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "st-plgpio,wdata-reg", &val)) { 4718c2ecf20Sopenharmony_ci plgpio->regs.wdata = val; 4728c2ecf20Sopenharmony_ci } else { 4738c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "DT: Invalid wdata reg\n"); 4748c2ecf20Sopenharmony_ci goto end; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "st-plgpio,dir-reg", &val)) { 4788c2ecf20Sopenharmony_ci plgpio->regs.dir = val; 4798c2ecf20Sopenharmony_ci } else { 4808c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "DT: Invalid dir reg\n"); 4818c2ecf20Sopenharmony_ci goto end; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "st-plgpio,ie-reg", &val)) { 4858c2ecf20Sopenharmony_ci plgpio->regs.ie = val; 4868c2ecf20Sopenharmony_ci } else { 4878c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "DT: Invalid ie reg\n"); 4888c2ecf20Sopenharmony_ci goto end; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "st-plgpio,rdata-reg", &val)) { 4928c2ecf20Sopenharmony_ci plgpio->regs.rdata = val; 4938c2ecf20Sopenharmony_ci } else { 4948c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "DT: Invalid rdata reg\n"); 4958c2ecf20Sopenharmony_ci goto end; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "st-plgpio,mis-reg", &val)) { 4998c2ecf20Sopenharmony_ci plgpio->regs.mis = val; 5008c2ecf20Sopenharmony_ci } else { 5018c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "DT: Invalid mis reg\n"); 5028c2ecf20Sopenharmony_ci goto end; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "st-plgpio,eit-reg", &val)) 5068c2ecf20Sopenharmony_ci plgpio->regs.eit = val; 5078c2ecf20Sopenharmony_ci else 5088c2ecf20Sopenharmony_ci plgpio->regs.eit = -1; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return 0; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ciend: 5138c2ecf20Sopenharmony_ci return ret; 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_cistatic int plgpio_probe(struct platform_device *pdev) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci struct plgpio *plgpio; 5188c2ecf20Sopenharmony_ci int ret, irq; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci plgpio = devm_kzalloc(&pdev->dev, sizeof(*plgpio), GFP_KERNEL); 5218c2ecf20Sopenharmony_ci if (!plgpio) 5228c2ecf20Sopenharmony_ci return -ENOMEM; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci plgpio->base = devm_platform_ioremap_resource(pdev, 0); 5258c2ecf20Sopenharmony_ci if (IS_ERR(plgpio->base)) 5268c2ecf20Sopenharmony_ci return PTR_ERR(plgpio->base); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci ret = plgpio_probe_dt(pdev, plgpio); 5298c2ecf20Sopenharmony_ci if (ret) { 5308c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "DT probe failed\n"); 5318c2ecf20Sopenharmony_ci return ret; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci plgpio->clk = devm_clk_get(&pdev->dev, NULL); 5358c2ecf20Sopenharmony_ci if (IS_ERR(plgpio->clk)) 5368c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "clk_get() failed, work without it\n"); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 5398c2ecf20Sopenharmony_ci plgpio->csave_regs = devm_kcalloc(&pdev->dev, 5408c2ecf20Sopenharmony_ci DIV_ROUND_UP(plgpio->chip.ngpio, MAX_GPIO_PER_REG), 5418c2ecf20Sopenharmony_ci sizeof(*plgpio->csave_regs), 5428c2ecf20Sopenharmony_ci GFP_KERNEL); 5438c2ecf20Sopenharmony_ci if (!plgpio->csave_regs) 5448c2ecf20Sopenharmony_ci return -ENOMEM; 5458c2ecf20Sopenharmony_ci#endif 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, plgpio); 5488c2ecf20Sopenharmony_ci spin_lock_init(&plgpio->lock); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci plgpio->chip.base = -1; 5518c2ecf20Sopenharmony_ci plgpio->chip.request = plgpio_request; 5528c2ecf20Sopenharmony_ci plgpio->chip.free = plgpio_free; 5538c2ecf20Sopenharmony_ci plgpio->chip.direction_input = plgpio_direction_input; 5548c2ecf20Sopenharmony_ci plgpio->chip.direction_output = plgpio_direction_output; 5558c2ecf20Sopenharmony_ci plgpio->chip.get = plgpio_get_value; 5568c2ecf20Sopenharmony_ci plgpio->chip.set = plgpio_set_value; 5578c2ecf20Sopenharmony_ci plgpio->chip.label = dev_name(&pdev->dev); 5588c2ecf20Sopenharmony_ci plgpio->chip.parent = &pdev->dev; 5598c2ecf20Sopenharmony_ci plgpio->chip.owner = THIS_MODULE; 5608c2ecf20Sopenharmony_ci plgpio->chip.of_node = pdev->dev.of_node; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (!IS_ERR(plgpio->clk)) { 5638c2ecf20Sopenharmony_ci ret = clk_prepare(plgpio->clk); 5648c2ecf20Sopenharmony_ci if (ret) { 5658c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "clk prepare failed\n"); 5668c2ecf20Sopenharmony_ci return ret; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 5718c2ecf20Sopenharmony_ci if (irq > 0) { 5728c2ecf20Sopenharmony_ci struct gpio_irq_chip *girq; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci girq = &plgpio->chip.irq; 5758c2ecf20Sopenharmony_ci girq->chip = &plgpio_irqchip; 5768c2ecf20Sopenharmony_ci girq->parent_handler = plgpio_irq_handler; 5778c2ecf20Sopenharmony_ci girq->num_parents = 1; 5788c2ecf20Sopenharmony_ci girq->parents = devm_kcalloc(&pdev->dev, 1, 5798c2ecf20Sopenharmony_ci sizeof(*girq->parents), 5808c2ecf20Sopenharmony_ci GFP_KERNEL); 5818c2ecf20Sopenharmony_ci if (!girq->parents) 5828c2ecf20Sopenharmony_ci return -ENOMEM; 5838c2ecf20Sopenharmony_ci girq->parents[0] = irq; 5848c2ecf20Sopenharmony_ci girq->default_type = IRQ_TYPE_NONE; 5858c2ecf20Sopenharmony_ci girq->handler = handle_simple_irq; 5868c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "PLGPIO registering with IRQs\n"); 5878c2ecf20Sopenharmony_ci } else { 5888c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "PLGPIO registering without IRQs\n"); 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci ret = gpiochip_add_data(&plgpio->chip, plgpio); 5928c2ecf20Sopenharmony_ci if (ret) { 5938c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "unable to add gpio chip\n"); 5948c2ecf20Sopenharmony_ci goto unprepare_clk; 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci return 0; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ciunprepare_clk: 6008c2ecf20Sopenharmony_ci if (!IS_ERR(plgpio->clk)) 6018c2ecf20Sopenharmony_ci clk_unprepare(plgpio->clk); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci return ret; 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 6078c2ecf20Sopenharmony_cistatic int plgpio_suspend(struct device *dev) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci struct plgpio *plgpio = dev_get_drvdata(dev); 6108c2ecf20Sopenharmony_ci int i, reg_count = DIV_ROUND_UP(plgpio->chip.ngpio, MAX_GPIO_PER_REG); 6118c2ecf20Sopenharmony_ci void __iomem *off; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci for (i = 0; i < reg_count; i++) { 6148c2ecf20Sopenharmony_ci off = plgpio->base + i * sizeof(int *); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci if (plgpio->regs.enb != -1) 6178c2ecf20Sopenharmony_ci plgpio->csave_regs[i].enb = 6188c2ecf20Sopenharmony_ci readl_relaxed(plgpio->regs.enb + off); 6198c2ecf20Sopenharmony_ci if (plgpio->regs.eit != -1) 6208c2ecf20Sopenharmony_ci plgpio->csave_regs[i].eit = 6218c2ecf20Sopenharmony_ci readl_relaxed(plgpio->regs.eit + off); 6228c2ecf20Sopenharmony_ci plgpio->csave_regs[i].wdata = readl_relaxed(plgpio->regs.wdata + 6238c2ecf20Sopenharmony_ci off); 6248c2ecf20Sopenharmony_ci plgpio->csave_regs[i].dir = readl_relaxed(plgpio->regs.dir + 6258c2ecf20Sopenharmony_ci off); 6268c2ecf20Sopenharmony_ci plgpio->csave_regs[i].ie = readl_relaxed(plgpio->regs.ie + off); 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return 0; 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci/* 6338c2ecf20Sopenharmony_ci * This is used to correct the values in end registers. End registers contain 6348c2ecf20Sopenharmony_ci * extra bits that might be used for other purpose in platform. So, we shouldn't 6358c2ecf20Sopenharmony_ci * overwrite these bits. This macro, reads given register again, preserves other 6368c2ecf20Sopenharmony_ci * bit values (non-plgpio bits), and retain captured value (plgpio bits). 6378c2ecf20Sopenharmony_ci */ 6388c2ecf20Sopenharmony_ci#define plgpio_prepare_reg(__reg, _off, _mask, _tmp) \ 6398c2ecf20Sopenharmony_ci{ \ 6408c2ecf20Sopenharmony_ci _tmp = readl_relaxed(plgpio->regs.__reg + _off); \ 6418c2ecf20Sopenharmony_ci _tmp &= ~_mask; \ 6428c2ecf20Sopenharmony_ci plgpio->csave_regs[i].__reg = \ 6438c2ecf20Sopenharmony_ci _tmp | (plgpio->csave_regs[i].__reg & _mask); \ 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_cistatic int plgpio_resume(struct device *dev) 6478c2ecf20Sopenharmony_ci{ 6488c2ecf20Sopenharmony_ci struct plgpio *plgpio = dev_get_drvdata(dev); 6498c2ecf20Sopenharmony_ci int i, reg_count = DIV_ROUND_UP(plgpio->chip.ngpio, MAX_GPIO_PER_REG); 6508c2ecf20Sopenharmony_ci void __iomem *off; 6518c2ecf20Sopenharmony_ci u32 mask, tmp; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci for (i = 0; i < reg_count; i++) { 6548c2ecf20Sopenharmony_ci off = plgpio->base + i * sizeof(int *); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci if (i == reg_count - 1) { 6578c2ecf20Sopenharmony_ci mask = (1 << (plgpio->chip.ngpio - i * 6588c2ecf20Sopenharmony_ci MAX_GPIO_PER_REG)) - 1; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (plgpio->regs.enb != -1) 6618c2ecf20Sopenharmony_ci plgpio_prepare_reg(enb, off, mask, tmp); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci if (plgpio->regs.eit != -1) 6648c2ecf20Sopenharmony_ci plgpio_prepare_reg(eit, off, mask, tmp); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci plgpio_prepare_reg(wdata, off, mask, tmp); 6678c2ecf20Sopenharmony_ci plgpio_prepare_reg(dir, off, mask, tmp); 6688c2ecf20Sopenharmony_ci plgpio_prepare_reg(ie, off, mask, tmp); 6698c2ecf20Sopenharmony_ci } 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci writel_relaxed(plgpio->csave_regs[i].wdata, plgpio->regs.wdata + 6728c2ecf20Sopenharmony_ci off); 6738c2ecf20Sopenharmony_ci writel_relaxed(plgpio->csave_regs[i].dir, plgpio->regs.dir + 6748c2ecf20Sopenharmony_ci off); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (plgpio->regs.eit != -1) 6778c2ecf20Sopenharmony_ci writel_relaxed(plgpio->csave_regs[i].eit, 6788c2ecf20Sopenharmony_ci plgpio->regs.eit + off); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci writel_relaxed(plgpio->csave_regs[i].ie, plgpio->regs.ie + off); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci if (plgpio->regs.enb != -1) 6838c2ecf20Sopenharmony_ci writel_relaxed(plgpio->csave_regs[i].enb, 6848c2ecf20Sopenharmony_ci plgpio->regs.enb + off); 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci return 0; 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ci#endif 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(plgpio_dev_pm_ops, plgpio_suspend, plgpio_resume); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic const struct of_device_id plgpio_of_match[] = { 6948c2ecf20Sopenharmony_ci { .compatible = "st,spear-plgpio" }, 6958c2ecf20Sopenharmony_ci {} 6968c2ecf20Sopenharmony_ci}; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_cistatic struct platform_driver plgpio_driver = { 6998c2ecf20Sopenharmony_ci .probe = plgpio_probe, 7008c2ecf20Sopenharmony_ci .driver = { 7018c2ecf20Sopenharmony_ci .name = "spear-plgpio", 7028c2ecf20Sopenharmony_ci .pm = &plgpio_dev_pm_ops, 7038c2ecf20Sopenharmony_ci .of_match_table = plgpio_of_match, 7048c2ecf20Sopenharmony_ci }, 7058c2ecf20Sopenharmony_ci}; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic int __init plgpio_init(void) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci return platform_driver_register(&plgpio_driver); 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_cisubsys_initcall(plgpio_init); 712