18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * GPIO driver for the WinSystems WS16C48 48c2ecf20Sopenharmony_ci * Copyright (C) 2016 William Breathitt Gray 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include <linux/bitmap.h> 78c2ecf20Sopenharmony_ci#include <linux/bitops.h> 88c2ecf20Sopenharmony_ci#include <linux/device.h> 98c2ecf20Sopenharmony_ci#include <linux/errno.h> 108c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 118c2ecf20Sopenharmony_ci#include <linux/io.h> 128c2ecf20Sopenharmony_ci#include <linux/ioport.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/irqdesc.h> 158c2ecf20Sopenharmony_ci#include <linux/isa.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 198c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define WS16C48_EXTENT 16 228c2ecf20Sopenharmony_ci#define MAX_NUM_WS16C48 max_num_isa_dev(WS16C48_EXTENT) 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic unsigned int base[MAX_NUM_WS16C48]; 258c2ecf20Sopenharmony_cistatic unsigned int num_ws16c48; 268c2ecf20Sopenharmony_cimodule_param_hw_array(base, uint, ioport, &num_ws16c48, 0); 278c2ecf20Sopenharmony_ciMODULE_PARM_DESC(base, "WinSystems WS16C48 base addresses"); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic unsigned int irq[MAX_NUM_WS16C48]; 308c2ecf20Sopenharmony_cimodule_param_hw_array(irq, uint, irq, NULL, 0); 318c2ecf20Sopenharmony_ciMODULE_PARM_DESC(irq, "WinSystems WS16C48 interrupt line numbers"); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/** 348c2ecf20Sopenharmony_ci * struct ws16c48_gpio - GPIO device private data structure 358c2ecf20Sopenharmony_ci * @chip: instance of the gpio_chip 368c2ecf20Sopenharmony_ci * @io_state: bit I/O state (whether bit is set to input or output) 378c2ecf20Sopenharmony_ci * @out_state: output bits state 388c2ecf20Sopenharmony_ci * @lock: synchronization lock to prevent I/O race conditions 398c2ecf20Sopenharmony_ci * @irq_mask: I/O bits affected by interrupts 408c2ecf20Sopenharmony_ci * @flow_mask: IRQ flow type mask for the respective I/O bits 418c2ecf20Sopenharmony_ci * @base: base port address of the GPIO device 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_cistruct ws16c48_gpio { 448c2ecf20Sopenharmony_ci struct gpio_chip chip; 458c2ecf20Sopenharmony_ci unsigned char io_state[6]; 468c2ecf20Sopenharmony_ci unsigned char out_state[6]; 478c2ecf20Sopenharmony_ci raw_spinlock_t lock; 488c2ecf20Sopenharmony_ci unsigned long irq_mask; 498c2ecf20Sopenharmony_ci unsigned long flow_mask; 508c2ecf20Sopenharmony_ci unsigned base; 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int ws16c48_gpio_get_direction(struct gpio_chip *chip, unsigned offset) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 568c2ecf20Sopenharmony_ci const unsigned port = offset / 8; 578c2ecf20Sopenharmony_ci const unsigned mask = BIT(offset % 8); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (ws16c48gpio->io_state[port] & mask) 608c2ecf20Sopenharmony_ci return GPIO_LINE_DIRECTION_IN; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return GPIO_LINE_DIRECTION_OUT; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int ws16c48_gpio_direction_input(struct gpio_chip *chip, unsigned offset) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 688c2ecf20Sopenharmony_ci const unsigned port = offset / 8; 698c2ecf20Sopenharmony_ci const unsigned mask = BIT(offset % 8); 708c2ecf20Sopenharmony_ci unsigned long flags; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci ws16c48gpio->io_state[port] |= mask; 758c2ecf20Sopenharmony_ci ws16c48gpio->out_state[port] &= ~mask; 768c2ecf20Sopenharmony_ci outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return 0; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int ws16c48_gpio_direction_output(struct gpio_chip *chip, 848c2ecf20Sopenharmony_ci unsigned offset, int value) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 878c2ecf20Sopenharmony_ci const unsigned port = offset / 8; 888c2ecf20Sopenharmony_ci const unsigned mask = BIT(offset % 8); 898c2ecf20Sopenharmony_ci unsigned long flags; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci ws16c48gpio->io_state[port] &= ~mask; 948c2ecf20Sopenharmony_ci if (value) 958c2ecf20Sopenharmony_ci ws16c48gpio->out_state[port] |= mask; 968c2ecf20Sopenharmony_ci else 978c2ecf20Sopenharmony_ci ws16c48gpio->out_state[port] &= ~mask; 988c2ecf20Sopenharmony_ci outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return 0; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int ws16c48_gpio_get(struct gpio_chip *chip, unsigned offset) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 1088c2ecf20Sopenharmony_ci const unsigned port = offset / 8; 1098c2ecf20Sopenharmony_ci const unsigned mask = BIT(offset % 8); 1108c2ecf20Sopenharmony_ci unsigned long flags; 1118c2ecf20Sopenharmony_ci unsigned port_state; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* ensure that GPIO is set for input */ 1168c2ecf20Sopenharmony_ci if (!(ws16c48gpio->io_state[port] & mask)) { 1178c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 1188c2ecf20Sopenharmony_ci return -EINVAL; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci port_state = inb(ws16c48gpio->base + port); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return !!(port_state & mask); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic int ws16c48_gpio_get_multiple(struct gpio_chip *chip, 1298c2ecf20Sopenharmony_ci unsigned long *mask, unsigned long *bits) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 1328c2ecf20Sopenharmony_ci unsigned long offset; 1338c2ecf20Sopenharmony_ci unsigned long gpio_mask; 1348c2ecf20Sopenharmony_ci unsigned int port_addr; 1358c2ecf20Sopenharmony_ci unsigned long port_state; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* clear bits array to a clean slate */ 1388c2ecf20Sopenharmony_ci bitmap_zero(bits, chip->ngpio); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) { 1418c2ecf20Sopenharmony_ci port_addr = ws16c48gpio->base + offset / 8; 1428c2ecf20Sopenharmony_ci port_state = inb(port_addr) & gpio_mask; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci bitmap_set_value8(bits, port_state, offset); 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return 0; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic void ws16c48_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 1538c2ecf20Sopenharmony_ci const unsigned port = offset / 8; 1548c2ecf20Sopenharmony_ci const unsigned mask = BIT(offset % 8); 1558c2ecf20Sopenharmony_ci unsigned long flags; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* ensure that GPIO is set for output */ 1608c2ecf20Sopenharmony_ci if (ws16c48gpio->io_state[port] & mask) { 1618c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 1628c2ecf20Sopenharmony_ci return; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (value) 1668c2ecf20Sopenharmony_ci ws16c48gpio->out_state[port] |= mask; 1678c2ecf20Sopenharmony_ci else 1688c2ecf20Sopenharmony_ci ws16c48gpio->out_state[port] &= ~mask; 1698c2ecf20Sopenharmony_ci outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic void ws16c48_gpio_set_multiple(struct gpio_chip *chip, 1758c2ecf20Sopenharmony_ci unsigned long *mask, unsigned long *bits) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 1788c2ecf20Sopenharmony_ci unsigned long offset; 1798c2ecf20Sopenharmony_ci unsigned long gpio_mask; 1808c2ecf20Sopenharmony_ci size_t index; 1818c2ecf20Sopenharmony_ci unsigned int port_addr; 1828c2ecf20Sopenharmony_ci unsigned long bitmask; 1838c2ecf20Sopenharmony_ci unsigned long flags; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) { 1868c2ecf20Sopenharmony_ci index = offset / 8; 1878c2ecf20Sopenharmony_ci port_addr = ws16c48gpio->base + index; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* mask out GPIO configured for input */ 1908c2ecf20Sopenharmony_ci gpio_mask &= ~ws16c48gpio->io_state[index]; 1918c2ecf20Sopenharmony_ci bitmask = bitmap_get_value8(bits, offset) & gpio_mask; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* update output state data and set device gpio register */ 1968c2ecf20Sopenharmony_ci ws16c48gpio->out_state[index] &= ~gpio_mask; 1978c2ecf20Sopenharmony_ci ws16c48gpio->out_state[index] |= bitmask; 1988c2ecf20Sopenharmony_ci outb(ws16c48gpio->out_state[index], port_addr); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic void ws16c48_irq_ack(struct irq_data *data) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 2078c2ecf20Sopenharmony_ci struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 2088c2ecf20Sopenharmony_ci const unsigned long offset = irqd_to_hwirq(data); 2098c2ecf20Sopenharmony_ci const unsigned port = offset / 8; 2108c2ecf20Sopenharmony_ci const unsigned mask = BIT(offset % 8); 2118c2ecf20Sopenharmony_ci unsigned long flags; 2128c2ecf20Sopenharmony_ci unsigned port_state; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* only the first 3 ports support interrupts */ 2158c2ecf20Sopenharmony_ci if (port > 2) 2168c2ecf20Sopenharmony_ci return; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci port_state = ws16c48gpio->irq_mask >> (8*port); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci outb(0x80, ws16c48gpio->base + 7); 2238c2ecf20Sopenharmony_ci outb(port_state & ~mask, ws16c48gpio->base + 8 + port); 2248c2ecf20Sopenharmony_ci outb(port_state | mask, ws16c48gpio->base + 8 + port); 2258c2ecf20Sopenharmony_ci outb(0xC0, ws16c48gpio->base + 7); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic void ws16c48_irq_mask(struct irq_data *data) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 2338c2ecf20Sopenharmony_ci struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 2348c2ecf20Sopenharmony_ci const unsigned long offset = irqd_to_hwirq(data); 2358c2ecf20Sopenharmony_ci const unsigned long mask = BIT(offset); 2368c2ecf20Sopenharmony_ci const unsigned port = offset / 8; 2378c2ecf20Sopenharmony_ci unsigned long flags; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* only the first 3 ports support interrupts */ 2408c2ecf20Sopenharmony_ci if (port > 2) 2418c2ecf20Sopenharmony_ci return; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci ws16c48gpio->irq_mask &= ~mask; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci outb(0x80, ws16c48gpio->base + 7); 2488c2ecf20Sopenharmony_ci outb(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port); 2498c2ecf20Sopenharmony_ci outb(0xC0, ws16c48gpio->base + 7); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic void ws16c48_irq_unmask(struct irq_data *data) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 2578c2ecf20Sopenharmony_ci struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 2588c2ecf20Sopenharmony_ci const unsigned long offset = irqd_to_hwirq(data); 2598c2ecf20Sopenharmony_ci const unsigned long mask = BIT(offset); 2608c2ecf20Sopenharmony_ci const unsigned port = offset / 8; 2618c2ecf20Sopenharmony_ci unsigned long flags; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* only the first 3 ports support interrupts */ 2648c2ecf20Sopenharmony_ci if (port > 2) 2658c2ecf20Sopenharmony_ci return; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci ws16c48gpio->irq_mask |= mask; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci outb(0x80, ws16c48gpio->base + 7); 2728c2ecf20Sopenharmony_ci outb(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port); 2738c2ecf20Sopenharmony_ci outb(0xC0, ws16c48gpio->base + 7); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic int ws16c48_irq_set_type(struct irq_data *data, unsigned flow_type) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 2818c2ecf20Sopenharmony_ci struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 2828c2ecf20Sopenharmony_ci const unsigned long offset = irqd_to_hwirq(data); 2838c2ecf20Sopenharmony_ci const unsigned long mask = BIT(offset); 2848c2ecf20Sopenharmony_ci const unsigned port = offset / 8; 2858c2ecf20Sopenharmony_ci unsigned long flags; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* only the first 3 ports support interrupts */ 2888c2ecf20Sopenharmony_ci if (port > 2) 2898c2ecf20Sopenharmony_ci return -EINVAL; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci switch (flow_type) { 2948c2ecf20Sopenharmony_ci case IRQ_TYPE_NONE: 2958c2ecf20Sopenharmony_ci break; 2968c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 2978c2ecf20Sopenharmony_ci ws16c48gpio->flow_mask |= mask; 2988c2ecf20Sopenharmony_ci break; 2998c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 3008c2ecf20Sopenharmony_ci ws16c48gpio->flow_mask &= ~mask; 3018c2ecf20Sopenharmony_ci break; 3028c2ecf20Sopenharmony_ci default: 3038c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 3048c2ecf20Sopenharmony_ci return -EINVAL; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci outb(0x40, ws16c48gpio->base + 7); 3088c2ecf20Sopenharmony_ci outb(ws16c48gpio->flow_mask >> (8*port), ws16c48gpio->base + 8 + port); 3098c2ecf20Sopenharmony_ci outb(0xC0, ws16c48gpio->base + 7); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci return 0; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic struct irq_chip ws16c48_irqchip = { 3178c2ecf20Sopenharmony_ci .name = "ws16c48", 3188c2ecf20Sopenharmony_ci .irq_ack = ws16c48_irq_ack, 3198c2ecf20Sopenharmony_ci .irq_mask = ws16c48_irq_mask, 3208c2ecf20Sopenharmony_ci .irq_unmask = ws16c48_irq_unmask, 3218c2ecf20Sopenharmony_ci .irq_set_type = ws16c48_irq_set_type 3228c2ecf20Sopenharmony_ci}; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic irqreturn_t ws16c48_irq_handler(int irq, void *dev_id) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct ws16c48_gpio *const ws16c48gpio = dev_id; 3278c2ecf20Sopenharmony_ci struct gpio_chip *const chip = &ws16c48gpio->chip; 3288c2ecf20Sopenharmony_ci unsigned long int_pending; 3298c2ecf20Sopenharmony_ci unsigned long port; 3308c2ecf20Sopenharmony_ci unsigned long int_id; 3318c2ecf20Sopenharmony_ci unsigned long gpio; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci int_pending = inb(ws16c48gpio->base + 6) & 0x7; 3348c2ecf20Sopenharmony_ci if (!int_pending) 3358c2ecf20Sopenharmony_ci return IRQ_NONE; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* loop until all pending interrupts are handled */ 3388c2ecf20Sopenharmony_ci do { 3398c2ecf20Sopenharmony_ci for_each_set_bit(port, &int_pending, 3) { 3408c2ecf20Sopenharmony_ci int_id = inb(ws16c48gpio->base + 8 + port); 3418c2ecf20Sopenharmony_ci for_each_set_bit(gpio, &int_id, 8) 3428c2ecf20Sopenharmony_ci generic_handle_irq(irq_find_mapping( 3438c2ecf20Sopenharmony_ci chip->irq.domain, gpio + 8*port)); 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci int_pending = inb(ws16c48gpio->base + 6) & 0x7; 3478c2ecf20Sopenharmony_ci } while (int_pending); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci#define WS16C48_NGPIO 48 3538c2ecf20Sopenharmony_cistatic const char *ws16c48_names[WS16C48_NGPIO] = { 3548c2ecf20Sopenharmony_ci "Port 0 Bit 0", "Port 0 Bit 1", "Port 0 Bit 2", "Port 0 Bit 3", 3558c2ecf20Sopenharmony_ci "Port 0 Bit 4", "Port 0 Bit 5", "Port 0 Bit 6", "Port 0 Bit 7", 3568c2ecf20Sopenharmony_ci "Port 1 Bit 0", "Port 1 Bit 1", "Port 1 Bit 2", "Port 1 Bit 3", 3578c2ecf20Sopenharmony_ci "Port 1 Bit 4", "Port 1 Bit 5", "Port 1 Bit 6", "Port 1 Bit 7", 3588c2ecf20Sopenharmony_ci "Port 2 Bit 0", "Port 2 Bit 1", "Port 2 Bit 2", "Port 2 Bit 3", 3598c2ecf20Sopenharmony_ci "Port 2 Bit 4", "Port 2 Bit 5", "Port 2 Bit 6", "Port 2 Bit 7", 3608c2ecf20Sopenharmony_ci "Port 3 Bit 0", "Port 3 Bit 1", "Port 3 Bit 2", "Port 3 Bit 3", 3618c2ecf20Sopenharmony_ci "Port 3 Bit 4", "Port 3 Bit 5", "Port 3 Bit 6", "Port 3 Bit 7", 3628c2ecf20Sopenharmony_ci "Port 4 Bit 0", "Port 4 Bit 1", "Port 4 Bit 2", "Port 4 Bit 3", 3638c2ecf20Sopenharmony_ci "Port 4 Bit 4", "Port 4 Bit 5", "Port 4 Bit 6", "Port 4 Bit 7", 3648c2ecf20Sopenharmony_ci "Port 5 Bit 0", "Port 5 Bit 1", "Port 5 Bit 2", "Port 5 Bit 3", 3658c2ecf20Sopenharmony_ci "Port 5 Bit 4", "Port 5 Bit 5", "Port 5 Bit 6", "Port 5 Bit 7" 3668c2ecf20Sopenharmony_ci}; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic int ws16c48_irq_init_hw(struct gpio_chip *gc) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(gc); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* Disable IRQ by default */ 3738c2ecf20Sopenharmony_ci outb(0x80, ws16c48gpio->base + 7); 3748c2ecf20Sopenharmony_ci outb(0, ws16c48gpio->base + 8); 3758c2ecf20Sopenharmony_ci outb(0, ws16c48gpio->base + 9); 3768c2ecf20Sopenharmony_ci outb(0, ws16c48gpio->base + 10); 3778c2ecf20Sopenharmony_ci outb(0xC0, ws16c48gpio->base + 7); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci return 0; 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic int ws16c48_probe(struct device *dev, unsigned int id) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci struct ws16c48_gpio *ws16c48gpio; 3858c2ecf20Sopenharmony_ci const char *const name = dev_name(dev); 3868c2ecf20Sopenharmony_ci struct gpio_irq_chip *girq; 3878c2ecf20Sopenharmony_ci int err; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci ws16c48gpio = devm_kzalloc(dev, sizeof(*ws16c48gpio), GFP_KERNEL); 3908c2ecf20Sopenharmony_ci if (!ws16c48gpio) 3918c2ecf20Sopenharmony_ci return -ENOMEM; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (!devm_request_region(dev, base[id], WS16C48_EXTENT, name)) { 3948c2ecf20Sopenharmony_ci dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", 3958c2ecf20Sopenharmony_ci base[id], base[id] + WS16C48_EXTENT); 3968c2ecf20Sopenharmony_ci return -EBUSY; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci ws16c48gpio->chip.label = name; 4008c2ecf20Sopenharmony_ci ws16c48gpio->chip.parent = dev; 4018c2ecf20Sopenharmony_ci ws16c48gpio->chip.owner = THIS_MODULE; 4028c2ecf20Sopenharmony_ci ws16c48gpio->chip.base = -1; 4038c2ecf20Sopenharmony_ci ws16c48gpio->chip.ngpio = WS16C48_NGPIO; 4048c2ecf20Sopenharmony_ci ws16c48gpio->chip.names = ws16c48_names; 4058c2ecf20Sopenharmony_ci ws16c48gpio->chip.get_direction = ws16c48_gpio_get_direction; 4068c2ecf20Sopenharmony_ci ws16c48gpio->chip.direction_input = ws16c48_gpio_direction_input; 4078c2ecf20Sopenharmony_ci ws16c48gpio->chip.direction_output = ws16c48_gpio_direction_output; 4088c2ecf20Sopenharmony_ci ws16c48gpio->chip.get = ws16c48_gpio_get; 4098c2ecf20Sopenharmony_ci ws16c48gpio->chip.get_multiple = ws16c48_gpio_get_multiple; 4108c2ecf20Sopenharmony_ci ws16c48gpio->chip.set = ws16c48_gpio_set; 4118c2ecf20Sopenharmony_ci ws16c48gpio->chip.set_multiple = ws16c48_gpio_set_multiple; 4128c2ecf20Sopenharmony_ci ws16c48gpio->base = base[id]; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci girq = &ws16c48gpio->chip.irq; 4158c2ecf20Sopenharmony_ci girq->chip = &ws16c48_irqchip; 4168c2ecf20Sopenharmony_ci /* This will let us handle the parent IRQ in the driver */ 4178c2ecf20Sopenharmony_ci girq->parent_handler = NULL; 4188c2ecf20Sopenharmony_ci girq->num_parents = 0; 4198c2ecf20Sopenharmony_ci girq->parents = NULL; 4208c2ecf20Sopenharmony_ci girq->default_type = IRQ_TYPE_NONE; 4218c2ecf20Sopenharmony_ci girq->handler = handle_edge_irq; 4228c2ecf20Sopenharmony_ci girq->init_hw = ws16c48_irq_init_hw; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci raw_spin_lock_init(&ws16c48gpio->lock); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci err = devm_gpiochip_add_data(dev, &ws16c48gpio->chip, ws16c48gpio); 4278c2ecf20Sopenharmony_ci if (err) { 4288c2ecf20Sopenharmony_ci dev_err(dev, "GPIO registering failed (%d)\n", err); 4298c2ecf20Sopenharmony_ci return err; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci err = devm_request_irq(dev, irq[id], ws16c48_irq_handler, IRQF_SHARED, 4338c2ecf20Sopenharmony_ci name, ws16c48gpio); 4348c2ecf20Sopenharmony_ci if (err) { 4358c2ecf20Sopenharmony_ci dev_err(dev, "IRQ handler registering failed (%d)\n", err); 4368c2ecf20Sopenharmony_ci return err; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return 0; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic struct isa_driver ws16c48_driver = { 4438c2ecf20Sopenharmony_ci .probe = ws16c48_probe, 4448c2ecf20Sopenharmony_ci .driver = { 4458c2ecf20Sopenharmony_ci .name = "ws16c48" 4468c2ecf20Sopenharmony_ci }, 4478c2ecf20Sopenharmony_ci}; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cimodule_isa_driver(ws16c48_driver, num_ws16c48); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ciMODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); 4528c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("WinSystems WS16C48 GPIO driver"); 4538c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 454