18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Intel Whiskey Cove PMIC GPIO Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This driver is written based on gpio-crystalcove.c 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2016 Intel Corporation. All rights reserved. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/bitops.h> 118c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/mfd/intel_soc_pmic.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/regmap.h> 178c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* 208c2ecf20Sopenharmony_ci * Whiskey Cove PMIC has 13 physical GPIO pins divided into 3 banks: 218c2ecf20Sopenharmony_ci * Bank 0: Pin 0 - 6 228c2ecf20Sopenharmony_ci * Bank 1: Pin 7 - 10 238c2ecf20Sopenharmony_ci * Bank 2: Pin 11 - 12 248c2ecf20Sopenharmony_ci * Each pin has one output control register and one input control register. 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_ci#define BANK0_NR_PINS 7 278c2ecf20Sopenharmony_ci#define BANK1_NR_PINS 4 288c2ecf20Sopenharmony_ci#define BANK2_NR_PINS 2 298c2ecf20Sopenharmony_ci#define WCOVE_GPIO_NUM (BANK0_NR_PINS + BANK1_NR_PINS + BANK2_NR_PINS) 308c2ecf20Sopenharmony_ci#define WCOVE_VGPIO_NUM 94 318c2ecf20Sopenharmony_ci/* GPIO output control registers (one per pin): 0x4e44 - 0x4e50 */ 328c2ecf20Sopenharmony_ci#define GPIO_OUT_CTRL_BASE 0x4e44 338c2ecf20Sopenharmony_ci/* GPIO input control registers (one per pin): 0x4e51 - 0x4e5d */ 348c2ecf20Sopenharmony_ci#define GPIO_IN_CTRL_BASE 0x4e51 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * GPIO interrupts are organized in two groups: 388c2ecf20Sopenharmony_ci * Group 0: Bank 0 pins (Pin 0 - 6) 398c2ecf20Sopenharmony_ci * Group 1: Bank 1 and Bank 2 pins (Pin 7 - 12) 408c2ecf20Sopenharmony_ci * Each group has two registers (one bit per pin): status and mask. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ci#define GROUP0_NR_IRQS 7 438c2ecf20Sopenharmony_ci#define GROUP1_NR_IRQS 6 448c2ecf20Sopenharmony_ci#define IRQ_MASK_BASE 0x4e19 458c2ecf20Sopenharmony_ci#define IRQ_STATUS_BASE 0x4e0b 468c2ecf20Sopenharmony_ci#define GPIO_IRQ0_MASK GENMASK(6, 0) 478c2ecf20Sopenharmony_ci#define GPIO_IRQ1_MASK GENMASK(5, 0) 488c2ecf20Sopenharmony_ci#define UPDATE_IRQ_TYPE BIT(0) 498c2ecf20Sopenharmony_ci#define UPDATE_IRQ_MASK BIT(1) 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define CTLI_INTCNT_DIS (0 << 1) 528c2ecf20Sopenharmony_ci#define CTLI_INTCNT_NE (1 << 1) 538c2ecf20Sopenharmony_ci#define CTLI_INTCNT_PE (2 << 1) 548c2ecf20Sopenharmony_ci#define CTLI_INTCNT_BE (3 << 1) 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define CTLO_DIR_IN (0 << 5) 578c2ecf20Sopenharmony_ci#define CTLO_DIR_OUT (1 << 5) 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#define CTLO_DRV_MASK (1 << 4) 608c2ecf20Sopenharmony_ci#define CTLO_DRV_OD (0 << 4) 618c2ecf20Sopenharmony_ci#define CTLO_DRV_CMOS (1 << 4) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define CTLO_DRV_REN (1 << 3) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define CTLO_RVAL_2KDOWN (0 << 1) 668c2ecf20Sopenharmony_ci#define CTLO_RVAL_2KUP (1 << 1) 678c2ecf20Sopenharmony_ci#define CTLO_RVAL_50KDOWN (2 << 1) 688c2ecf20Sopenharmony_ci#define CTLO_RVAL_50KUP (3 << 1) 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define CTLO_INPUT_SET (CTLO_DRV_CMOS | CTLO_DRV_REN | CTLO_RVAL_2KUP) 718c2ecf20Sopenharmony_ci#define CTLO_OUTPUT_SET (CTLO_DIR_OUT | CTLO_INPUT_SET) 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cienum ctrl_register { 748c2ecf20Sopenharmony_ci CTRL_IN, 758c2ecf20Sopenharmony_ci CTRL_OUT, 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* 798c2ecf20Sopenharmony_ci * struct wcove_gpio - Whiskey Cove GPIO controller 808c2ecf20Sopenharmony_ci * @buslock: for bus lock/sync and unlock. 818c2ecf20Sopenharmony_ci * @chip: the abstract gpio_chip structure. 828c2ecf20Sopenharmony_ci * @dev: the gpio device 838c2ecf20Sopenharmony_ci * @regmap: the regmap from the parent device. 848c2ecf20Sopenharmony_ci * @regmap_irq_chip: the regmap of the gpio irq chip. 858c2ecf20Sopenharmony_ci * @update: pending IRQ setting update, to be written to the chip upon unlock. 868c2ecf20Sopenharmony_ci * @intcnt: the Interrupt Detect value to be written. 878c2ecf20Sopenharmony_ci * @set_irq_mask: true if the IRQ mask needs to be set, false to clear. 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_cistruct wcove_gpio { 908c2ecf20Sopenharmony_ci struct mutex buslock; 918c2ecf20Sopenharmony_ci struct gpio_chip chip; 928c2ecf20Sopenharmony_ci struct device *dev; 938c2ecf20Sopenharmony_ci struct regmap *regmap; 948c2ecf20Sopenharmony_ci struct regmap_irq_chip_data *regmap_irq_chip; 958c2ecf20Sopenharmony_ci int update; 968c2ecf20Sopenharmony_ci int intcnt; 978c2ecf20Sopenharmony_ci bool set_irq_mask; 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic inline int to_reg(int gpio, enum ctrl_register reg_type) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci unsigned int reg; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (gpio >= WCOVE_GPIO_NUM) 1058c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (reg_type == CTRL_IN) 1088c2ecf20Sopenharmony_ci reg = GPIO_IN_CTRL_BASE + gpio; 1098c2ecf20Sopenharmony_ci else 1108c2ecf20Sopenharmony_ci reg = GPIO_OUT_CTRL_BASE + gpio; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return reg; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic void wcove_update_irq_mask(struct wcove_gpio *wg, int gpio) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci unsigned int reg, mask; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (gpio < GROUP0_NR_IRQS) { 1208c2ecf20Sopenharmony_ci reg = IRQ_MASK_BASE; 1218c2ecf20Sopenharmony_ci mask = BIT(gpio % GROUP0_NR_IRQS); 1228c2ecf20Sopenharmony_ci } else { 1238c2ecf20Sopenharmony_ci reg = IRQ_MASK_BASE + 1; 1248c2ecf20Sopenharmony_ci mask = BIT((gpio - GROUP0_NR_IRQS) % GROUP1_NR_IRQS); 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (wg->set_irq_mask) 1288c2ecf20Sopenharmony_ci regmap_update_bits(wg->regmap, reg, mask, mask); 1298c2ecf20Sopenharmony_ci else 1308c2ecf20Sopenharmony_ci regmap_update_bits(wg->regmap, reg, mask, 0); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic void wcove_update_irq_ctrl(struct wcove_gpio *wg, int gpio) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci int reg = to_reg(gpio, CTRL_IN); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (reg < 0) 1388c2ecf20Sopenharmony_ci return; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci regmap_update_bits(wg->regmap, reg, CTLI_INTCNT_BE, wg->intcnt); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic int wcove_gpio_dir_in(struct gpio_chip *chip, unsigned int gpio) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct wcove_gpio *wg = gpiochip_get_data(chip); 1468c2ecf20Sopenharmony_ci int reg = to_reg(gpio, CTRL_OUT); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (reg < 0) 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return regmap_write(wg->regmap, reg, CTLO_INPUT_SET); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int wcove_gpio_dir_out(struct gpio_chip *chip, unsigned int gpio, 1558c2ecf20Sopenharmony_ci int value) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct wcove_gpio *wg = gpiochip_get_data(chip); 1588c2ecf20Sopenharmony_ci int reg = to_reg(gpio, CTRL_OUT); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (reg < 0) 1618c2ecf20Sopenharmony_ci return 0; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci return regmap_write(wg->regmap, reg, CTLO_OUTPUT_SET | value); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int wcove_gpio_get_direction(struct gpio_chip *chip, unsigned int gpio) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct wcove_gpio *wg = gpiochip_get_data(chip); 1698c2ecf20Sopenharmony_ci unsigned int val; 1708c2ecf20Sopenharmony_ci int ret, reg = to_reg(gpio, CTRL_OUT); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (reg < 0) 1738c2ecf20Sopenharmony_ci return GPIO_LINE_DIRECTION_OUT; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci ret = regmap_read(wg->regmap, reg, &val); 1768c2ecf20Sopenharmony_ci if (ret) 1778c2ecf20Sopenharmony_ci return ret; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (val & CTLO_DIR_OUT) 1808c2ecf20Sopenharmony_ci return GPIO_LINE_DIRECTION_OUT; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return GPIO_LINE_DIRECTION_IN; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int wcove_gpio_get(struct gpio_chip *chip, unsigned int gpio) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct wcove_gpio *wg = gpiochip_get_data(chip); 1888c2ecf20Sopenharmony_ci unsigned int val; 1898c2ecf20Sopenharmony_ci int ret, reg = to_reg(gpio, CTRL_IN); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (reg < 0) 1928c2ecf20Sopenharmony_ci return 0; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci ret = regmap_read(wg->regmap, reg, &val); 1958c2ecf20Sopenharmony_ci if (ret) 1968c2ecf20Sopenharmony_ci return ret; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return val & 0x1; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic void wcove_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct wcove_gpio *wg = gpiochip_get_data(chip); 2048c2ecf20Sopenharmony_ci int reg = to_reg(gpio, CTRL_OUT); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (reg < 0) 2078c2ecf20Sopenharmony_ci return; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (value) 2108c2ecf20Sopenharmony_ci regmap_update_bits(wg->regmap, reg, 1, 1); 2118c2ecf20Sopenharmony_ci else 2128c2ecf20Sopenharmony_ci regmap_update_bits(wg->regmap, reg, 1, 0); 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int wcove_gpio_set_config(struct gpio_chip *chip, unsigned int gpio, 2168c2ecf20Sopenharmony_ci unsigned long config) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct wcove_gpio *wg = gpiochip_get_data(chip); 2198c2ecf20Sopenharmony_ci int reg = to_reg(gpio, CTRL_OUT); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (reg < 0) 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci switch (pinconf_to_config_param(config)) { 2258c2ecf20Sopenharmony_ci case PIN_CONFIG_DRIVE_OPEN_DRAIN: 2268c2ecf20Sopenharmony_ci return regmap_update_bits(wg->regmap, reg, CTLO_DRV_MASK, 2278c2ecf20Sopenharmony_ci CTLO_DRV_OD); 2288c2ecf20Sopenharmony_ci case PIN_CONFIG_DRIVE_PUSH_PULL: 2298c2ecf20Sopenharmony_ci return regmap_update_bits(wg->regmap, reg, CTLO_DRV_MASK, 2308c2ecf20Sopenharmony_ci CTLO_DRV_CMOS); 2318c2ecf20Sopenharmony_ci default: 2328c2ecf20Sopenharmony_ci break; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return -ENOTSUPP; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic int wcove_irq_type(struct irq_data *data, unsigned int type) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 2418c2ecf20Sopenharmony_ci struct wcove_gpio *wg = gpiochip_get_data(chip); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (data->hwirq >= WCOVE_GPIO_NUM) 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci switch (type) { 2478c2ecf20Sopenharmony_ci case IRQ_TYPE_NONE: 2488c2ecf20Sopenharmony_ci wg->intcnt = CTLI_INTCNT_DIS; 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 2518c2ecf20Sopenharmony_ci wg->intcnt = CTLI_INTCNT_BE; 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 2548c2ecf20Sopenharmony_ci wg->intcnt = CTLI_INTCNT_PE; 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 2578c2ecf20Sopenharmony_ci wg->intcnt = CTLI_INTCNT_NE; 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci default: 2608c2ecf20Sopenharmony_ci return -EINVAL; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci wg->update |= UPDATE_IRQ_TYPE; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic void wcove_bus_lock(struct irq_data *data) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 2718c2ecf20Sopenharmony_ci struct wcove_gpio *wg = gpiochip_get_data(chip); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci mutex_lock(&wg->buslock); 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic void wcove_bus_sync_unlock(struct irq_data *data) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 2798c2ecf20Sopenharmony_ci struct wcove_gpio *wg = gpiochip_get_data(chip); 2808c2ecf20Sopenharmony_ci int gpio = data->hwirq; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (wg->update & UPDATE_IRQ_TYPE) 2838c2ecf20Sopenharmony_ci wcove_update_irq_ctrl(wg, gpio); 2848c2ecf20Sopenharmony_ci if (wg->update & UPDATE_IRQ_MASK) 2858c2ecf20Sopenharmony_ci wcove_update_irq_mask(wg, gpio); 2868c2ecf20Sopenharmony_ci wg->update = 0; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci mutex_unlock(&wg->buslock); 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic void wcove_irq_unmask(struct irq_data *data) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 2948c2ecf20Sopenharmony_ci struct wcove_gpio *wg = gpiochip_get_data(chip); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (data->hwirq >= WCOVE_GPIO_NUM) 2978c2ecf20Sopenharmony_ci return; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci wg->set_irq_mask = false; 3008c2ecf20Sopenharmony_ci wg->update |= UPDATE_IRQ_MASK; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic void wcove_irq_mask(struct irq_data *data) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 3068c2ecf20Sopenharmony_ci struct wcove_gpio *wg = gpiochip_get_data(chip); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (data->hwirq >= WCOVE_GPIO_NUM) 3098c2ecf20Sopenharmony_ci return; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci wg->set_irq_mask = true; 3128c2ecf20Sopenharmony_ci wg->update |= UPDATE_IRQ_MASK; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic struct irq_chip wcove_irqchip = { 3168c2ecf20Sopenharmony_ci .name = "Whiskey Cove", 3178c2ecf20Sopenharmony_ci .irq_mask = wcove_irq_mask, 3188c2ecf20Sopenharmony_ci .irq_unmask = wcove_irq_unmask, 3198c2ecf20Sopenharmony_ci .irq_set_type = wcove_irq_type, 3208c2ecf20Sopenharmony_ci .irq_bus_lock = wcove_bus_lock, 3218c2ecf20Sopenharmony_ci .irq_bus_sync_unlock = wcove_bus_sync_unlock, 3228c2ecf20Sopenharmony_ci}; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic irqreturn_t wcove_gpio_irq_handler(int irq, void *data) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct wcove_gpio *wg = (struct wcove_gpio *)data; 3278c2ecf20Sopenharmony_ci unsigned int pending, virq, gpio, mask, offset; 3288c2ecf20Sopenharmony_ci u8 p[2]; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (regmap_bulk_read(wg->regmap, IRQ_STATUS_BASE, p, 2)) { 3318c2ecf20Sopenharmony_ci dev_err(wg->dev, "Failed to read irq status register\n"); 3328c2ecf20Sopenharmony_ci return IRQ_NONE; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci pending = (p[0] & GPIO_IRQ0_MASK) | ((p[1] & GPIO_IRQ1_MASK) << 7); 3368c2ecf20Sopenharmony_ci if (!pending) 3378c2ecf20Sopenharmony_ci return IRQ_NONE; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* Iterate until no interrupt is pending */ 3408c2ecf20Sopenharmony_ci while (pending) { 3418c2ecf20Sopenharmony_ci /* One iteration is for all pending bits */ 3428c2ecf20Sopenharmony_ci for_each_set_bit(gpio, (const unsigned long *)&pending, 3438c2ecf20Sopenharmony_ci WCOVE_GPIO_NUM) { 3448c2ecf20Sopenharmony_ci offset = (gpio > GROUP0_NR_IRQS) ? 1 : 0; 3458c2ecf20Sopenharmony_ci mask = (offset == 1) ? BIT(gpio - GROUP0_NR_IRQS) : 3468c2ecf20Sopenharmony_ci BIT(gpio); 3478c2ecf20Sopenharmony_ci virq = irq_find_mapping(wg->chip.irq.domain, gpio); 3488c2ecf20Sopenharmony_ci handle_nested_irq(virq); 3498c2ecf20Sopenharmony_ci regmap_update_bits(wg->regmap, IRQ_STATUS_BASE + offset, 3508c2ecf20Sopenharmony_ci mask, mask); 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* Next iteration */ 3548c2ecf20Sopenharmony_ci if (regmap_bulk_read(wg->regmap, IRQ_STATUS_BASE, p, 2)) { 3558c2ecf20Sopenharmony_ci dev_err(wg->dev, "Failed to read irq status\n"); 3568c2ecf20Sopenharmony_ci break; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci pending = (p[0] & GPIO_IRQ0_MASK) | ((p[1] & GPIO_IRQ1_MASK) << 7); 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic void wcove_gpio_dbg_show(struct seq_file *s, 3668c2ecf20Sopenharmony_ci struct gpio_chip *chip) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci unsigned int ctlo, ctli, irq_mask, irq_status; 3698c2ecf20Sopenharmony_ci struct wcove_gpio *wg = gpiochip_get_data(chip); 3708c2ecf20Sopenharmony_ci int gpio, offset, group, ret = 0; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci for (gpio = 0; gpio < WCOVE_GPIO_NUM; gpio++) { 3738c2ecf20Sopenharmony_ci group = gpio < GROUP0_NR_IRQS ? 0 : 1; 3748c2ecf20Sopenharmony_ci ret += regmap_read(wg->regmap, to_reg(gpio, CTRL_OUT), &ctlo); 3758c2ecf20Sopenharmony_ci ret += regmap_read(wg->regmap, to_reg(gpio, CTRL_IN), &ctli); 3768c2ecf20Sopenharmony_ci ret += regmap_read(wg->regmap, IRQ_MASK_BASE + group, 3778c2ecf20Sopenharmony_ci &irq_mask); 3788c2ecf20Sopenharmony_ci ret += regmap_read(wg->regmap, IRQ_STATUS_BASE + group, 3798c2ecf20Sopenharmony_ci &irq_status); 3808c2ecf20Sopenharmony_ci if (ret) { 3818c2ecf20Sopenharmony_ci pr_err("Failed to read registers: ctrl out/in or irq status/mask\n"); 3828c2ecf20Sopenharmony_ci break; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci offset = gpio % 8; 3868c2ecf20Sopenharmony_ci seq_printf(s, " gpio-%-2d %s %s %s %s ctlo=%2x,%s %s\n", 3878c2ecf20Sopenharmony_ci gpio, ctlo & CTLO_DIR_OUT ? "out" : "in ", 3888c2ecf20Sopenharmony_ci ctli & 0x1 ? "hi" : "lo", 3898c2ecf20Sopenharmony_ci ctli & CTLI_INTCNT_NE ? "fall" : " ", 3908c2ecf20Sopenharmony_ci ctli & CTLI_INTCNT_PE ? "rise" : " ", 3918c2ecf20Sopenharmony_ci ctlo, 3928c2ecf20Sopenharmony_ci irq_mask & BIT(offset) ? "mask " : "unmask", 3938c2ecf20Sopenharmony_ci irq_status & BIT(offset) ? "pending" : " "); 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic int wcove_gpio_probe(struct platform_device *pdev) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci struct intel_soc_pmic *pmic; 4008c2ecf20Sopenharmony_ci struct wcove_gpio *wg; 4018c2ecf20Sopenharmony_ci int virq, ret, irq; 4028c2ecf20Sopenharmony_ci struct device *dev; 4038c2ecf20Sopenharmony_ci struct gpio_irq_chip *girq; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* 4068c2ecf20Sopenharmony_ci * This gpio platform device is created by a mfd device (see 4078c2ecf20Sopenharmony_ci * drivers/mfd/intel_soc_pmic_bxtwc.c for details). Information 4088c2ecf20Sopenharmony_ci * shared by all sub-devices created by the mfd device, the regmap 4098c2ecf20Sopenharmony_ci * pointer for instance, is stored as driver data of the mfd device 4108c2ecf20Sopenharmony_ci * driver. 4118c2ecf20Sopenharmony_ci */ 4128c2ecf20Sopenharmony_ci pmic = dev_get_drvdata(pdev->dev.parent); 4138c2ecf20Sopenharmony_ci if (!pmic) 4148c2ecf20Sopenharmony_ci return -ENODEV; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 4178c2ecf20Sopenharmony_ci if (irq < 0) 4188c2ecf20Sopenharmony_ci return irq; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci dev = &pdev->dev; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci wg = devm_kzalloc(dev, sizeof(*wg), GFP_KERNEL); 4238c2ecf20Sopenharmony_ci if (!wg) 4248c2ecf20Sopenharmony_ci return -ENOMEM; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci wg->regmap_irq_chip = pmic->irq_chip_data; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, wg); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci mutex_init(&wg->buslock); 4318c2ecf20Sopenharmony_ci wg->chip.label = KBUILD_MODNAME; 4328c2ecf20Sopenharmony_ci wg->chip.direction_input = wcove_gpio_dir_in; 4338c2ecf20Sopenharmony_ci wg->chip.direction_output = wcove_gpio_dir_out; 4348c2ecf20Sopenharmony_ci wg->chip.get_direction = wcove_gpio_get_direction; 4358c2ecf20Sopenharmony_ci wg->chip.get = wcove_gpio_get; 4368c2ecf20Sopenharmony_ci wg->chip.set = wcove_gpio_set; 4378c2ecf20Sopenharmony_ci wg->chip.set_config = wcove_gpio_set_config, 4388c2ecf20Sopenharmony_ci wg->chip.base = -1; 4398c2ecf20Sopenharmony_ci wg->chip.ngpio = WCOVE_VGPIO_NUM; 4408c2ecf20Sopenharmony_ci wg->chip.can_sleep = true; 4418c2ecf20Sopenharmony_ci wg->chip.parent = pdev->dev.parent; 4428c2ecf20Sopenharmony_ci wg->chip.dbg_show = wcove_gpio_dbg_show; 4438c2ecf20Sopenharmony_ci wg->dev = dev; 4448c2ecf20Sopenharmony_ci wg->regmap = pmic->regmap; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci virq = regmap_irq_get_virq(wg->regmap_irq_chip, irq); 4478c2ecf20Sopenharmony_ci if (virq < 0) { 4488c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get virq by irq %d\n", irq); 4498c2ecf20Sopenharmony_ci return virq; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci girq = &wg->chip.irq; 4538c2ecf20Sopenharmony_ci girq->chip = &wcove_irqchip; 4548c2ecf20Sopenharmony_ci /* This will let us handle the parent IRQ in the driver */ 4558c2ecf20Sopenharmony_ci girq->parent_handler = NULL; 4568c2ecf20Sopenharmony_ci girq->num_parents = 0; 4578c2ecf20Sopenharmony_ci girq->parents = NULL; 4588c2ecf20Sopenharmony_ci girq->default_type = IRQ_TYPE_NONE; 4598c2ecf20Sopenharmony_ci girq->handler = handle_simple_irq; 4608c2ecf20Sopenharmony_ci girq->threaded = true; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(dev, virq, NULL, wcove_gpio_irq_handler, 4638c2ecf20Sopenharmony_ci IRQF_ONESHOT, pdev->name, wg); 4648c2ecf20Sopenharmony_ci if (ret) { 4658c2ecf20Sopenharmony_ci dev_err(dev, "Failed to request irq %d\n", virq); 4668c2ecf20Sopenharmony_ci return ret; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci ret = devm_gpiochip_add_data(dev, &wg->chip, wg); 4708c2ecf20Sopenharmony_ci if (ret) { 4718c2ecf20Sopenharmony_ci dev_err(dev, "Failed to add gpiochip: %d\n", ret); 4728c2ecf20Sopenharmony_ci return ret; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* Enable GPIO0 interrupts */ 4768c2ecf20Sopenharmony_ci ret = regmap_update_bits(wg->regmap, IRQ_MASK_BASE, GPIO_IRQ0_MASK, 4778c2ecf20Sopenharmony_ci 0x00); 4788c2ecf20Sopenharmony_ci if (ret) 4798c2ecf20Sopenharmony_ci return ret; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* Enable GPIO1 interrupts */ 4828c2ecf20Sopenharmony_ci ret = regmap_update_bits(wg->regmap, IRQ_MASK_BASE + 1, GPIO_IRQ1_MASK, 4838c2ecf20Sopenharmony_ci 0x00); 4848c2ecf20Sopenharmony_ci if (ret) 4858c2ecf20Sopenharmony_ci return ret; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci return 0; 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci/* 4918c2ecf20Sopenharmony_ci * Whiskey Cove PMIC itself is a analog device(but with digital control 4928c2ecf20Sopenharmony_ci * interface) providing power management support for other devices in 4938c2ecf20Sopenharmony_ci * the accompanied SoC, so we have no .pm for Whiskey Cove GPIO driver. 4948c2ecf20Sopenharmony_ci */ 4958c2ecf20Sopenharmony_cistatic struct platform_driver wcove_gpio_driver = { 4968c2ecf20Sopenharmony_ci .driver = { 4978c2ecf20Sopenharmony_ci .name = "bxt_wcove_gpio", 4988c2ecf20Sopenharmony_ci }, 4998c2ecf20Sopenharmony_ci .probe = wcove_gpio_probe, 5008c2ecf20Sopenharmony_ci}; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cimodule_platform_driver(wcove_gpio_driver); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ajay Thomas <ajay.thomas.david.rajamanickam@intel.com>"); 5058c2ecf20Sopenharmony_ciMODULE_AUTHOR("Bin Gao <bin.gao@intel.com>"); 5068c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel Whiskey Cove GPIO Driver"); 5078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 5088c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:bxt_wcove_gpio"); 509