18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2011-2012 Avionic Design GmbH 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 78c2ecf20Sopenharmony_ci#include <linux/i2c.h> 88c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 118c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define GPIO_DDR(gpio) (0x00 << (gpio)->reg_shift) 158c2ecf20Sopenharmony_ci#define GPIO_PLR(gpio) (0x01 << (gpio)->reg_shift) 168c2ecf20Sopenharmony_ci#define GPIO_IER(gpio) (0x02 << (gpio)->reg_shift) 178c2ecf20Sopenharmony_ci#define GPIO_ISR(gpio) (0x03 << (gpio)->reg_shift) 188c2ecf20Sopenharmony_ci#define GPIO_PTR(gpio) (0x04 << (gpio)->reg_shift) 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct adnp { 218c2ecf20Sopenharmony_ci struct i2c_client *client; 228c2ecf20Sopenharmony_ci struct gpio_chip gpio; 238c2ecf20Sopenharmony_ci unsigned int reg_shift; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci struct mutex i2c_lock; 268c2ecf20Sopenharmony_ci struct mutex irq_lock; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci u8 *irq_enable; 298c2ecf20Sopenharmony_ci u8 *irq_level; 308c2ecf20Sopenharmony_ci u8 *irq_rise; 318c2ecf20Sopenharmony_ci u8 *irq_fall; 328c2ecf20Sopenharmony_ci u8 *irq_high; 338c2ecf20Sopenharmony_ci u8 *irq_low; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int adnp_read(struct adnp *adnp, unsigned offset, uint8_t *value) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci int err; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci err = i2c_smbus_read_byte_data(adnp->client, offset); 418c2ecf20Sopenharmony_ci if (err < 0) { 428c2ecf20Sopenharmony_ci dev_err(adnp->gpio.parent, "%s failed: %d\n", 438c2ecf20Sopenharmony_ci "i2c_smbus_read_byte_data()", err); 448c2ecf20Sopenharmony_ci return err; 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci *value = err; 488c2ecf20Sopenharmony_ci return 0; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic int adnp_write(struct adnp *adnp, unsigned offset, uint8_t value) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci int err; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci err = i2c_smbus_write_byte_data(adnp->client, offset, value); 568c2ecf20Sopenharmony_ci if (err < 0) { 578c2ecf20Sopenharmony_ci dev_err(adnp->gpio.parent, "%s failed: %d\n", 588c2ecf20Sopenharmony_ci "i2c_smbus_write_byte_data()", err); 598c2ecf20Sopenharmony_ci return err; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return 0; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int adnp_gpio_get(struct gpio_chip *chip, unsigned offset) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct adnp *adnp = gpiochip_get_data(chip); 688c2ecf20Sopenharmony_ci unsigned int reg = offset >> adnp->reg_shift; 698c2ecf20Sopenharmony_ci unsigned int pos = offset & 7; 708c2ecf20Sopenharmony_ci u8 value; 718c2ecf20Sopenharmony_ci int err; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci err = adnp_read(adnp, GPIO_PLR(adnp) + reg, &value); 748c2ecf20Sopenharmony_ci if (err < 0) 758c2ecf20Sopenharmony_ci return err; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return (value & BIT(pos)) ? 1 : 0; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic void __adnp_gpio_set(struct adnp *adnp, unsigned offset, int value) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci unsigned int reg = offset >> adnp->reg_shift; 838c2ecf20Sopenharmony_ci unsigned int pos = offset & 7; 848c2ecf20Sopenharmony_ci int err; 858c2ecf20Sopenharmony_ci u8 val; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci err = adnp_read(adnp, GPIO_PLR(adnp) + reg, &val); 888c2ecf20Sopenharmony_ci if (err < 0) 898c2ecf20Sopenharmony_ci return; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (value) 928c2ecf20Sopenharmony_ci val |= BIT(pos); 938c2ecf20Sopenharmony_ci else 948c2ecf20Sopenharmony_ci val &= ~BIT(pos); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci adnp_write(adnp, GPIO_PLR(adnp) + reg, val); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic void adnp_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct adnp *adnp = gpiochip_get_data(chip); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci mutex_lock(&adnp->i2c_lock); 1048c2ecf20Sopenharmony_ci __adnp_gpio_set(adnp, offset, value); 1058c2ecf20Sopenharmony_ci mutex_unlock(&adnp->i2c_lock); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic int adnp_gpio_direction_input(struct gpio_chip *chip, unsigned offset) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct adnp *adnp = gpiochip_get_data(chip); 1118c2ecf20Sopenharmony_ci unsigned int reg = offset >> adnp->reg_shift; 1128c2ecf20Sopenharmony_ci unsigned int pos = offset & 7; 1138c2ecf20Sopenharmony_ci u8 value; 1148c2ecf20Sopenharmony_ci int err; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci mutex_lock(&adnp->i2c_lock); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &value); 1198c2ecf20Sopenharmony_ci if (err < 0) 1208c2ecf20Sopenharmony_ci goto out; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci value &= ~BIT(pos); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci err = adnp_write(adnp, GPIO_DDR(adnp) + reg, value); 1258c2ecf20Sopenharmony_ci if (err < 0) 1268c2ecf20Sopenharmony_ci goto out; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &value); 1298c2ecf20Sopenharmony_ci if (err < 0) 1308c2ecf20Sopenharmony_ci goto out; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (value & BIT(pos)) { 1338c2ecf20Sopenharmony_ci err = -EPERM; 1348c2ecf20Sopenharmony_ci goto out; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci err = 0; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ciout: 1408c2ecf20Sopenharmony_ci mutex_unlock(&adnp->i2c_lock); 1418c2ecf20Sopenharmony_ci return err; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic int adnp_gpio_direction_output(struct gpio_chip *chip, unsigned offset, 1458c2ecf20Sopenharmony_ci int value) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct adnp *adnp = gpiochip_get_data(chip); 1488c2ecf20Sopenharmony_ci unsigned int reg = offset >> adnp->reg_shift; 1498c2ecf20Sopenharmony_ci unsigned int pos = offset & 7; 1508c2ecf20Sopenharmony_ci int err; 1518c2ecf20Sopenharmony_ci u8 val; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci mutex_lock(&adnp->i2c_lock); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &val); 1568c2ecf20Sopenharmony_ci if (err < 0) 1578c2ecf20Sopenharmony_ci goto out; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci val |= BIT(pos); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci err = adnp_write(adnp, GPIO_DDR(adnp) + reg, val); 1628c2ecf20Sopenharmony_ci if (err < 0) 1638c2ecf20Sopenharmony_ci goto out; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci err = adnp_read(adnp, GPIO_DDR(adnp) + reg, &val); 1668c2ecf20Sopenharmony_ci if (err < 0) 1678c2ecf20Sopenharmony_ci goto out; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (!(val & BIT(pos))) { 1708c2ecf20Sopenharmony_ci err = -EPERM; 1718c2ecf20Sopenharmony_ci goto out; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci __adnp_gpio_set(adnp, offset, value); 1758c2ecf20Sopenharmony_ci err = 0; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ciout: 1788c2ecf20Sopenharmony_ci mutex_unlock(&adnp->i2c_lock); 1798c2ecf20Sopenharmony_ci return err; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic void adnp_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct adnp *adnp = gpiochip_get_data(chip); 1858c2ecf20Sopenharmony_ci unsigned int num_regs = 1 << adnp->reg_shift, i, j; 1868c2ecf20Sopenharmony_ci int err; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci for (i = 0; i < num_regs; i++) { 1898c2ecf20Sopenharmony_ci u8 ddr, plr, ier, isr; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci mutex_lock(&adnp->i2c_lock); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci err = adnp_read(adnp, GPIO_DDR(adnp) + i, &ddr); 1948c2ecf20Sopenharmony_ci if (err < 0) 1958c2ecf20Sopenharmony_ci goto unlock; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci err = adnp_read(adnp, GPIO_PLR(adnp) + i, &plr); 1988c2ecf20Sopenharmony_ci if (err < 0) 1998c2ecf20Sopenharmony_ci goto unlock; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci err = adnp_read(adnp, GPIO_IER(adnp) + i, &ier); 2028c2ecf20Sopenharmony_ci if (err < 0) 2038c2ecf20Sopenharmony_ci goto unlock; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci err = adnp_read(adnp, GPIO_ISR(adnp) + i, &isr); 2068c2ecf20Sopenharmony_ci if (err < 0) 2078c2ecf20Sopenharmony_ci goto unlock; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci mutex_unlock(&adnp->i2c_lock); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci for (j = 0; j < 8; j++) { 2128c2ecf20Sopenharmony_ci unsigned int bit = (i << adnp->reg_shift) + j; 2138c2ecf20Sopenharmony_ci const char *direction = "input "; 2148c2ecf20Sopenharmony_ci const char *level = "low "; 2158c2ecf20Sopenharmony_ci const char *interrupt = "disabled"; 2168c2ecf20Sopenharmony_ci const char *pending = ""; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (ddr & BIT(j)) 2198c2ecf20Sopenharmony_ci direction = "output"; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (plr & BIT(j)) 2228c2ecf20Sopenharmony_ci level = "high"; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (ier & BIT(j)) 2258c2ecf20Sopenharmony_ci interrupt = "enabled "; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (isr & BIT(j)) 2288c2ecf20Sopenharmony_ci pending = "pending"; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci seq_printf(s, "%2u: %s %s IRQ %s %s\n", bit, 2318c2ecf20Sopenharmony_ci direction, level, interrupt, pending); 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ciunlock: 2388c2ecf20Sopenharmony_ci mutex_unlock(&adnp->i2c_lock); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic irqreturn_t adnp_irq(int irq, void *data) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct adnp *adnp = data; 2448c2ecf20Sopenharmony_ci unsigned int num_regs, i; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci num_regs = 1 << adnp->reg_shift; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci for (i = 0; i < num_regs; i++) { 2498c2ecf20Sopenharmony_ci unsigned int base = i << adnp->reg_shift, bit; 2508c2ecf20Sopenharmony_ci u8 changed, level, isr, ier; 2518c2ecf20Sopenharmony_ci unsigned long pending; 2528c2ecf20Sopenharmony_ci int err; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci mutex_lock(&adnp->i2c_lock); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci err = adnp_read(adnp, GPIO_PLR(adnp) + i, &level); 2578c2ecf20Sopenharmony_ci if (err < 0) { 2588c2ecf20Sopenharmony_ci mutex_unlock(&adnp->i2c_lock); 2598c2ecf20Sopenharmony_ci continue; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci err = adnp_read(adnp, GPIO_ISR(adnp) + i, &isr); 2638c2ecf20Sopenharmony_ci if (err < 0) { 2648c2ecf20Sopenharmony_ci mutex_unlock(&adnp->i2c_lock); 2658c2ecf20Sopenharmony_ci continue; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci err = adnp_read(adnp, GPIO_IER(adnp) + i, &ier); 2698c2ecf20Sopenharmony_ci if (err < 0) { 2708c2ecf20Sopenharmony_ci mutex_unlock(&adnp->i2c_lock); 2718c2ecf20Sopenharmony_ci continue; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci mutex_unlock(&adnp->i2c_lock); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* determine pins that changed levels */ 2778c2ecf20Sopenharmony_ci changed = level ^ adnp->irq_level[i]; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* compute edge-triggered interrupts */ 2808c2ecf20Sopenharmony_ci pending = changed & ((adnp->irq_fall[i] & ~level) | 2818c2ecf20Sopenharmony_ci (adnp->irq_rise[i] & level)); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* add in level-triggered interrupts */ 2848c2ecf20Sopenharmony_ci pending |= (adnp->irq_high[i] & level) | 2858c2ecf20Sopenharmony_ci (adnp->irq_low[i] & ~level); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* mask out non-pending and disabled interrupts */ 2888c2ecf20Sopenharmony_ci pending &= isr & ier; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci for_each_set_bit(bit, &pending, 8) { 2918c2ecf20Sopenharmony_ci unsigned int child_irq; 2928c2ecf20Sopenharmony_ci child_irq = irq_find_mapping(adnp->gpio.irq.domain, 2938c2ecf20Sopenharmony_ci base + bit); 2948c2ecf20Sopenharmony_ci handle_nested_irq(child_irq); 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic void adnp_irq_mask(struct irq_data *d) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 3048c2ecf20Sopenharmony_ci struct adnp *adnp = gpiochip_get_data(gc); 3058c2ecf20Sopenharmony_ci unsigned int reg = d->hwirq >> adnp->reg_shift; 3068c2ecf20Sopenharmony_ci unsigned int pos = d->hwirq & 7; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci adnp->irq_enable[reg] &= ~BIT(pos); 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic void adnp_irq_unmask(struct irq_data *d) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 3148c2ecf20Sopenharmony_ci struct adnp *adnp = gpiochip_get_data(gc); 3158c2ecf20Sopenharmony_ci unsigned int reg = d->hwirq >> adnp->reg_shift; 3168c2ecf20Sopenharmony_ci unsigned int pos = d->hwirq & 7; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci adnp->irq_enable[reg] |= BIT(pos); 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic int adnp_irq_set_type(struct irq_data *d, unsigned int type) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 3248c2ecf20Sopenharmony_ci struct adnp *adnp = gpiochip_get_data(gc); 3258c2ecf20Sopenharmony_ci unsigned int reg = d->hwirq >> adnp->reg_shift; 3268c2ecf20Sopenharmony_ci unsigned int pos = d->hwirq & 7; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (type & IRQ_TYPE_EDGE_RISING) 3298c2ecf20Sopenharmony_ci adnp->irq_rise[reg] |= BIT(pos); 3308c2ecf20Sopenharmony_ci else 3318c2ecf20Sopenharmony_ci adnp->irq_rise[reg] &= ~BIT(pos); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (type & IRQ_TYPE_EDGE_FALLING) 3348c2ecf20Sopenharmony_ci adnp->irq_fall[reg] |= BIT(pos); 3358c2ecf20Sopenharmony_ci else 3368c2ecf20Sopenharmony_ci adnp->irq_fall[reg] &= ~BIT(pos); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (type & IRQ_TYPE_LEVEL_HIGH) 3398c2ecf20Sopenharmony_ci adnp->irq_high[reg] |= BIT(pos); 3408c2ecf20Sopenharmony_ci else 3418c2ecf20Sopenharmony_ci adnp->irq_high[reg] &= ~BIT(pos); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (type & IRQ_TYPE_LEVEL_LOW) 3448c2ecf20Sopenharmony_ci adnp->irq_low[reg] |= BIT(pos); 3458c2ecf20Sopenharmony_ci else 3468c2ecf20Sopenharmony_ci adnp->irq_low[reg] &= ~BIT(pos); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return 0; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic void adnp_irq_bus_lock(struct irq_data *d) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 3548c2ecf20Sopenharmony_ci struct adnp *adnp = gpiochip_get_data(gc); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci mutex_lock(&adnp->irq_lock); 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic void adnp_irq_bus_unlock(struct irq_data *d) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 3628c2ecf20Sopenharmony_ci struct adnp *adnp = gpiochip_get_data(gc); 3638c2ecf20Sopenharmony_ci unsigned int num_regs = 1 << adnp->reg_shift, i; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci mutex_lock(&adnp->i2c_lock); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci for (i = 0; i < num_regs; i++) 3688c2ecf20Sopenharmony_ci adnp_write(adnp, GPIO_IER(adnp) + i, adnp->irq_enable[i]); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci mutex_unlock(&adnp->i2c_lock); 3718c2ecf20Sopenharmony_ci mutex_unlock(&adnp->irq_lock); 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic struct irq_chip adnp_irq_chip = { 3758c2ecf20Sopenharmony_ci .name = "gpio-adnp", 3768c2ecf20Sopenharmony_ci .irq_mask = adnp_irq_mask, 3778c2ecf20Sopenharmony_ci .irq_unmask = adnp_irq_unmask, 3788c2ecf20Sopenharmony_ci .irq_set_type = adnp_irq_set_type, 3798c2ecf20Sopenharmony_ci .irq_bus_lock = adnp_irq_bus_lock, 3808c2ecf20Sopenharmony_ci .irq_bus_sync_unlock = adnp_irq_bus_unlock, 3818c2ecf20Sopenharmony_ci}; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic int adnp_irq_setup(struct adnp *adnp) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci unsigned int num_regs = 1 << adnp->reg_shift, i; 3868c2ecf20Sopenharmony_ci struct gpio_chip *chip = &adnp->gpio; 3878c2ecf20Sopenharmony_ci int err; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci mutex_init(&adnp->irq_lock); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* 3928c2ecf20Sopenharmony_ci * Allocate memory to keep track of the current level and trigger 3938c2ecf20Sopenharmony_ci * modes of the interrupts. To avoid multiple allocations, a single 3948c2ecf20Sopenharmony_ci * large buffer is allocated and pointers are setup to point at the 3958c2ecf20Sopenharmony_ci * corresponding offsets. For consistency, the layout of the buffer 3968c2ecf20Sopenharmony_ci * is chosen to match the register layout of the hardware in that 3978c2ecf20Sopenharmony_ci * each segment contains the corresponding bits for all interrupts. 3988c2ecf20Sopenharmony_ci */ 3998c2ecf20Sopenharmony_ci adnp->irq_enable = devm_kcalloc(chip->parent, num_regs, 6, 4008c2ecf20Sopenharmony_ci GFP_KERNEL); 4018c2ecf20Sopenharmony_ci if (!adnp->irq_enable) 4028c2ecf20Sopenharmony_ci return -ENOMEM; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci adnp->irq_level = adnp->irq_enable + (num_regs * 1); 4058c2ecf20Sopenharmony_ci adnp->irq_rise = adnp->irq_enable + (num_regs * 2); 4068c2ecf20Sopenharmony_ci adnp->irq_fall = adnp->irq_enable + (num_regs * 3); 4078c2ecf20Sopenharmony_ci adnp->irq_high = adnp->irq_enable + (num_regs * 4); 4088c2ecf20Sopenharmony_ci adnp->irq_low = adnp->irq_enable + (num_regs * 5); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci for (i = 0; i < num_regs; i++) { 4118c2ecf20Sopenharmony_ci /* 4128c2ecf20Sopenharmony_ci * Read the initial level of all pins to allow the emulation 4138c2ecf20Sopenharmony_ci * of edge triggered interrupts. 4148c2ecf20Sopenharmony_ci */ 4158c2ecf20Sopenharmony_ci err = adnp_read(adnp, GPIO_PLR(adnp) + i, &adnp->irq_level[i]); 4168c2ecf20Sopenharmony_ci if (err < 0) 4178c2ecf20Sopenharmony_ci return err; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* disable all interrupts */ 4208c2ecf20Sopenharmony_ci err = adnp_write(adnp, GPIO_IER(adnp) + i, 0); 4218c2ecf20Sopenharmony_ci if (err < 0) 4228c2ecf20Sopenharmony_ci return err; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci adnp->irq_enable[i] = 0x00; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci err = devm_request_threaded_irq(chip->parent, adnp->client->irq, 4288c2ecf20Sopenharmony_ci NULL, adnp_irq, 4298c2ecf20Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_ONESHOT, 4308c2ecf20Sopenharmony_ci dev_name(chip->parent), adnp); 4318c2ecf20Sopenharmony_ci if (err != 0) { 4328c2ecf20Sopenharmony_ci dev_err(chip->parent, "can't request IRQ#%d: %d\n", 4338c2ecf20Sopenharmony_ci adnp->client->irq, err); 4348c2ecf20Sopenharmony_ci return err; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci return 0; 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios, 4418c2ecf20Sopenharmony_ci bool is_irq_controller) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci struct gpio_chip *chip = &adnp->gpio; 4448c2ecf20Sopenharmony_ci int err; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci adnp->reg_shift = get_count_order(num_gpios) - 3; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci chip->direction_input = adnp_gpio_direction_input; 4498c2ecf20Sopenharmony_ci chip->direction_output = adnp_gpio_direction_output; 4508c2ecf20Sopenharmony_ci chip->get = adnp_gpio_get; 4518c2ecf20Sopenharmony_ci chip->set = adnp_gpio_set; 4528c2ecf20Sopenharmony_ci chip->can_sleep = true; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_DEBUG_FS)) 4558c2ecf20Sopenharmony_ci chip->dbg_show = adnp_gpio_dbg_show; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci chip->base = -1; 4588c2ecf20Sopenharmony_ci chip->ngpio = num_gpios; 4598c2ecf20Sopenharmony_ci chip->label = adnp->client->name; 4608c2ecf20Sopenharmony_ci chip->parent = &adnp->client->dev; 4618c2ecf20Sopenharmony_ci chip->of_node = chip->parent->of_node; 4628c2ecf20Sopenharmony_ci chip->owner = THIS_MODULE; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (is_irq_controller) { 4658c2ecf20Sopenharmony_ci struct gpio_irq_chip *girq; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci err = adnp_irq_setup(adnp); 4688c2ecf20Sopenharmony_ci if (err) 4698c2ecf20Sopenharmony_ci return err; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci girq = &chip->irq; 4728c2ecf20Sopenharmony_ci girq->chip = &adnp_irq_chip; 4738c2ecf20Sopenharmony_ci /* This will let us handle the parent IRQ in the driver */ 4748c2ecf20Sopenharmony_ci girq->parent_handler = NULL; 4758c2ecf20Sopenharmony_ci girq->num_parents = 0; 4768c2ecf20Sopenharmony_ci girq->parents = NULL; 4778c2ecf20Sopenharmony_ci girq->default_type = IRQ_TYPE_NONE; 4788c2ecf20Sopenharmony_ci girq->handler = handle_simple_irq; 4798c2ecf20Sopenharmony_ci girq->threaded = true; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci err = devm_gpiochip_add_data(&adnp->client->dev, chip, adnp); 4838c2ecf20Sopenharmony_ci if (err) 4848c2ecf20Sopenharmony_ci return err; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci return 0; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic int adnp_i2c_probe(struct i2c_client *client, 4908c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct device_node *np = client->dev.of_node; 4938c2ecf20Sopenharmony_ci struct adnp *adnp; 4948c2ecf20Sopenharmony_ci u32 num_gpios; 4958c2ecf20Sopenharmony_ci int err; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci err = of_property_read_u32(np, "nr-gpios", &num_gpios); 4988c2ecf20Sopenharmony_ci if (err < 0) 4998c2ecf20Sopenharmony_ci return err; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci client->irq = irq_of_parse_and_map(np, 0); 5028c2ecf20Sopenharmony_ci if (!client->irq) 5038c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci adnp = devm_kzalloc(&client->dev, sizeof(*adnp), GFP_KERNEL); 5068c2ecf20Sopenharmony_ci if (!adnp) 5078c2ecf20Sopenharmony_ci return -ENOMEM; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci mutex_init(&adnp->i2c_lock); 5108c2ecf20Sopenharmony_ci adnp->client = client; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci err = adnp_gpio_setup(adnp, num_gpios, 5138c2ecf20Sopenharmony_ci of_property_read_bool(np, "interrupt-controller")); 5148c2ecf20Sopenharmony_ci if (err) 5158c2ecf20Sopenharmony_ci return err; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci i2c_set_clientdata(client, adnp); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci return 0; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic const struct i2c_device_id adnp_i2c_id[] = { 5238c2ecf20Sopenharmony_ci { "gpio-adnp" }, 5248c2ecf20Sopenharmony_ci { }, 5258c2ecf20Sopenharmony_ci}; 5268c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, adnp_i2c_id); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cistatic const struct of_device_id adnp_of_match[] = { 5298c2ecf20Sopenharmony_ci { .compatible = "ad,gpio-adnp", }, 5308c2ecf20Sopenharmony_ci { }, 5318c2ecf20Sopenharmony_ci}; 5328c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, adnp_of_match); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic struct i2c_driver adnp_i2c_driver = { 5358c2ecf20Sopenharmony_ci .driver = { 5368c2ecf20Sopenharmony_ci .name = "gpio-adnp", 5378c2ecf20Sopenharmony_ci .of_match_table = adnp_of_match, 5388c2ecf20Sopenharmony_ci }, 5398c2ecf20Sopenharmony_ci .probe = adnp_i2c_probe, 5408c2ecf20Sopenharmony_ci .id_table = adnp_i2c_id, 5418c2ecf20Sopenharmony_ci}; 5428c2ecf20Sopenharmony_cimodule_i2c_driver(adnp_i2c_driver); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Avionic Design N-bit GPIO expander"); 5458c2ecf20Sopenharmony_ciMODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>"); 5468c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 547