19c26df9bSWilliam Breathitt Gray /* 29c26df9bSWilliam Breathitt Gray * GPIO driver for the WinSystems WS16C48 39c26df9bSWilliam Breathitt Gray * Copyright (C) 2016 William Breathitt Gray 49c26df9bSWilliam Breathitt Gray * 59c26df9bSWilliam Breathitt Gray * This program is free software; you can redistribute it and/or modify 69c26df9bSWilliam Breathitt Gray * it under the terms of the GNU General Public License, version 2, as 79c26df9bSWilliam Breathitt Gray * published by the Free Software Foundation. 89c26df9bSWilliam Breathitt Gray * 99c26df9bSWilliam Breathitt Gray * This program is distributed in the hope that it will be useful, but 109c26df9bSWilliam Breathitt Gray * WITHOUT ANY WARRANTY; without even the implied warranty of 119c26df9bSWilliam Breathitt Gray * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 129c26df9bSWilliam Breathitt Gray * General Public License for more details. 139c26df9bSWilliam Breathitt Gray */ 149c26df9bSWilliam Breathitt Gray #include <linux/bitops.h> 159c26df9bSWilliam Breathitt Gray #include <linux/device.h> 169c26df9bSWilliam Breathitt Gray #include <linux/errno.h> 179c26df9bSWilliam Breathitt Gray #include <linux/gpio/driver.h> 189c26df9bSWilliam Breathitt Gray #include <linux/io.h> 199c26df9bSWilliam Breathitt Gray #include <linux/ioport.h> 209c26df9bSWilliam Breathitt Gray #include <linux/interrupt.h> 219c26df9bSWilliam Breathitt Gray #include <linux/irqdesc.h> 22cc736607SWilliam Breathitt Gray #include <linux/isa.h> 239c26df9bSWilliam Breathitt Gray #include <linux/kernel.h> 249c26df9bSWilliam Breathitt Gray #include <linux/module.h> 259c26df9bSWilliam Breathitt Gray #include <linux/moduleparam.h> 269c26df9bSWilliam Breathitt Gray #include <linux/spinlock.h> 279c26df9bSWilliam Breathitt Gray 28cc736607SWilliam Breathitt Gray #define WS16C48_EXTENT 16 29cc736607SWilliam Breathitt Gray #define MAX_NUM_WS16C48 max_num_isa_dev(WS16C48_EXTENT) 30cc736607SWilliam Breathitt Gray 31cc736607SWilliam Breathitt Gray static unsigned int base[MAX_NUM_WS16C48]; 32cc736607SWilliam Breathitt Gray static unsigned int num_ws16c48; 33cc736607SWilliam Breathitt Gray module_param_array(base, uint, &num_ws16c48, 0); 34cc736607SWilliam Breathitt Gray MODULE_PARM_DESC(base, "WinSystems WS16C48 base addresses"); 35cc736607SWilliam Breathitt Gray 36cc736607SWilliam Breathitt Gray static unsigned int irq[MAX_NUM_WS16C48]; 37cc736607SWilliam Breathitt Gray module_param_array(irq, uint, NULL, 0); 38cc736607SWilliam Breathitt Gray MODULE_PARM_DESC(irq, "WinSystems WS16C48 interrupt line numbers"); 399c26df9bSWilliam Breathitt Gray 409c26df9bSWilliam Breathitt Gray /** 419c26df9bSWilliam Breathitt Gray * struct ws16c48_gpio - GPIO device private data structure 429c26df9bSWilliam Breathitt Gray * @chip: instance of the gpio_chip 439c26df9bSWilliam Breathitt Gray * @io_state: bit I/O state (whether bit is set to input or output) 449c26df9bSWilliam Breathitt Gray * @out_state: output bits state 459c26df9bSWilliam Breathitt Gray * @lock: synchronization lock to prevent I/O race conditions 469c26df9bSWilliam Breathitt Gray * @irq_mask: I/O bits affected by interrupts 479c26df9bSWilliam Breathitt Gray * @flow_mask: IRQ flow type mask for the respective I/O bits 489c26df9bSWilliam Breathitt Gray * @base: base port address of the GPIO device 499c26df9bSWilliam Breathitt Gray */ 509c26df9bSWilliam Breathitt Gray struct ws16c48_gpio { 519c26df9bSWilliam Breathitt Gray struct gpio_chip chip; 529c26df9bSWilliam Breathitt Gray unsigned char io_state[6]; 539c26df9bSWilliam Breathitt Gray unsigned char out_state[6]; 54*a0a584f0SJulia Cartwright raw_spinlock_t lock; 559c26df9bSWilliam Breathitt Gray unsigned long irq_mask; 569c26df9bSWilliam Breathitt Gray unsigned long flow_mask; 579c26df9bSWilliam Breathitt Gray unsigned base; 589c26df9bSWilliam Breathitt Gray }; 599c26df9bSWilliam Breathitt Gray 609c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_get_direction(struct gpio_chip *chip, unsigned offset) 619c26df9bSWilliam Breathitt Gray { 629c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 639c26df9bSWilliam Breathitt Gray const unsigned port = offset / 8; 649c26df9bSWilliam Breathitt Gray const unsigned mask = BIT(offset % 8); 659c26df9bSWilliam Breathitt Gray 669c26df9bSWilliam Breathitt Gray return !!(ws16c48gpio->io_state[port] & mask); 679c26df9bSWilliam Breathitt Gray } 689c26df9bSWilliam Breathitt Gray 699c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_direction_input(struct gpio_chip *chip, unsigned offset) 709c26df9bSWilliam Breathitt Gray { 719c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 729c26df9bSWilliam Breathitt Gray const unsigned port = offset / 8; 739c26df9bSWilliam Breathitt Gray const unsigned mask = BIT(offset % 8); 749c26df9bSWilliam Breathitt Gray unsigned long flags; 759c26df9bSWilliam Breathitt Gray 76*a0a584f0SJulia Cartwright raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); 779c26df9bSWilliam Breathitt Gray 789c26df9bSWilliam Breathitt Gray ws16c48gpio->io_state[port] |= mask; 799c26df9bSWilliam Breathitt Gray ws16c48gpio->out_state[port] &= ~mask; 809c26df9bSWilliam Breathitt Gray outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port); 819c26df9bSWilliam Breathitt Gray 82*a0a584f0SJulia Cartwright raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 839c26df9bSWilliam Breathitt Gray 849c26df9bSWilliam Breathitt Gray return 0; 859c26df9bSWilliam Breathitt Gray } 869c26df9bSWilliam Breathitt Gray 879c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_direction_output(struct gpio_chip *chip, 889c26df9bSWilliam Breathitt Gray unsigned offset, int value) 899c26df9bSWilliam Breathitt Gray { 909c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 919c26df9bSWilliam Breathitt Gray const unsigned port = offset / 8; 929c26df9bSWilliam Breathitt Gray const unsigned mask = BIT(offset % 8); 939c26df9bSWilliam Breathitt Gray unsigned long flags; 949c26df9bSWilliam Breathitt Gray 95*a0a584f0SJulia Cartwright raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); 969c26df9bSWilliam Breathitt Gray 979c26df9bSWilliam Breathitt Gray ws16c48gpio->io_state[port] &= ~mask; 989c26df9bSWilliam Breathitt Gray if (value) 999c26df9bSWilliam Breathitt Gray ws16c48gpio->out_state[port] |= mask; 1009c26df9bSWilliam Breathitt Gray else 1019c26df9bSWilliam Breathitt Gray ws16c48gpio->out_state[port] &= ~mask; 1029c26df9bSWilliam Breathitt Gray outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port); 1039c26df9bSWilliam Breathitt Gray 104*a0a584f0SJulia Cartwright raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 1059c26df9bSWilliam Breathitt Gray 1069c26df9bSWilliam Breathitt Gray return 0; 1079c26df9bSWilliam Breathitt Gray } 1089c26df9bSWilliam Breathitt Gray 1099c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_get(struct gpio_chip *chip, unsigned offset) 1109c26df9bSWilliam Breathitt Gray { 1119c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 1129c26df9bSWilliam Breathitt Gray const unsigned port = offset / 8; 1139c26df9bSWilliam Breathitt Gray const unsigned mask = BIT(offset % 8); 1149c26df9bSWilliam Breathitt Gray unsigned long flags; 1159c26df9bSWilliam Breathitt Gray unsigned port_state; 1169c26df9bSWilliam Breathitt Gray 117*a0a584f0SJulia Cartwright raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); 1189c26df9bSWilliam Breathitt Gray 1199c26df9bSWilliam Breathitt Gray /* ensure that GPIO is set for input */ 1209c26df9bSWilliam Breathitt Gray if (!(ws16c48gpio->io_state[port] & mask)) { 121*a0a584f0SJulia Cartwright raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 1229c26df9bSWilliam Breathitt Gray return -EINVAL; 1239c26df9bSWilliam Breathitt Gray } 1249c26df9bSWilliam Breathitt Gray 1259c26df9bSWilliam Breathitt Gray port_state = inb(ws16c48gpio->base + port); 1269c26df9bSWilliam Breathitt Gray 127*a0a584f0SJulia Cartwright raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 1289c26df9bSWilliam Breathitt Gray 1299c26df9bSWilliam Breathitt Gray return !!(port_state & mask); 1309c26df9bSWilliam Breathitt Gray } 1319c26df9bSWilliam Breathitt Gray 1329c26df9bSWilliam Breathitt Gray static void ws16c48_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 1339c26df9bSWilliam Breathitt Gray { 1349c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 1359c26df9bSWilliam Breathitt Gray const unsigned port = offset / 8; 1369c26df9bSWilliam Breathitt Gray const unsigned mask = BIT(offset % 8); 1379c26df9bSWilliam Breathitt Gray unsigned long flags; 1389c26df9bSWilliam Breathitt Gray 139*a0a584f0SJulia Cartwright raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); 1409c26df9bSWilliam Breathitt Gray 1419c26df9bSWilliam Breathitt Gray /* ensure that GPIO is set for output */ 1429c26df9bSWilliam Breathitt Gray if (ws16c48gpio->io_state[port] & mask) { 143*a0a584f0SJulia Cartwright raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 1449c26df9bSWilliam Breathitt Gray return; 1459c26df9bSWilliam Breathitt Gray } 1469c26df9bSWilliam Breathitt Gray 1479c26df9bSWilliam Breathitt Gray if (value) 1489c26df9bSWilliam Breathitt Gray ws16c48gpio->out_state[port] |= mask; 1499c26df9bSWilliam Breathitt Gray else 1509c26df9bSWilliam Breathitt Gray ws16c48gpio->out_state[port] &= ~mask; 1519c26df9bSWilliam Breathitt Gray outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port); 1529c26df9bSWilliam Breathitt Gray 153*a0a584f0SJulia Cartwright raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 1549c26df9bSWilliam Breathitt Gray } 1559c26df9bSWilliam Breathitt Gray 15699c8ac95SWilliam Breathitt Gray static void ws16c48_gpio_set_multiple(struct gpio_chip *chip, 15799c8ac95SWilliam Breathitt Gray unsigned long *mask, unsigned long *bits) 15899c8ac95SWilliam Breathitt Gray { 15999c8ac95SWilliam Breathitt Gray struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 16099c8ac95SWilliam Breathitt Gray unsigned int i; 16199c8ac95SWilliam Breathitt Gray const unsigned int gpio_reg_size = 8; 16299c8ac95SWilliam Breathitt Gray unsigned int port; 16399c8ac95SWilliam Breathitt Gray unsigned int iomask; 16499c8ac95SWilliam Breathitt Gray unsigned int bitmask; 16599c8ac95SWilliam Breathitt Gray unsigned long flags; 16699c8ac95SWilliam Breathitt Gray 16799c8ac95SWilliam Breathitt Gray /* set bits are evaluated a gpio register size at a time */ 16899c8ac95SWilliam Breathitt Gray for (i = 0; i < chip->ngpio; i += gpio_reg_size) { 16999c8ac95SWilliam Breathitt Gray /* no more set bits in this mask word; skip to the next word */ 17099c8ac95SWilliam Breathitt Gray if (!mask[BIT_WORD(i)]) { 17199c8ac95SWilliam Breathitt Gray i = (BIT_WORD(i) + 1) * BITS_PER_LONG - gpio_reg_size; 17299c8ac95SWilliam Breathitt Gray continue; 17399c8ac95SWilliam Breathitt Gray } 17499c8ac95SWilliam Breathitt Gray 17599c8ac95SWilliam Breathitt Gray port = i / gpio_reg_size; 17699c8ac95SWilliam Breathitt Gray 17799c8ac95SWilliam Breathitt Gray /* mask out GPIO configured for input */ 17899c8ac95SWilliam Breathitt Gray iomask = mask[BIT_WORD(i)] & ~ws16c48gpio->io_state[port]; 17999c8ac95SWilliam Breathitt Gray bitmask = iomask & bits[BIT_WORD(i)]; 18099c8ac95SWilliam Breathitt Gray 181*a0a584f0SJulia Cartwright raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); 18299c8ac95SWilliam Breathitt Gray 18399c8ac95SWilliam Breathitt Gray /* update output state data and set device gpio register */ 18499c8ac95SWilliam Breathitt Gray ws16c48gpio->out_state[port] &= ~iomask; 18599c8ac95SWilliam Breathitt Gray ws16c48gpio->out_state[port] |= bitmask; 18699c8ac95SWilliam Breathitt Gray outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port); 18799c8ac95SWilliam Breathitt Gray 188*a0a584f0SJulia Cartwright raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 18999c8ac95SWilliam Breathitt Gray 19099c8ac95SWilliam Breathitt Gray /* prepare for next gpio register set */ 19199c8ac95SWilliam Breathitt Gray mask[BIT_WORD(i)] >>= gpio_reg_size; 19299c8ac95SWilliam Breathitt Gray bits[BIT_WORD(i)] >>= gpio_reg_size; 19399c8ac95SWilliam Breathitt Gray } 19499c8ac95SWilliam Breathitt Gray } 19599c8ac95SWilliam Breathitt Gray 1969c26df9bSWilliam Breathitt Gray static void ws16c48_irq_ack(struct irq_data *data) 1979c26df9bSWilliam Breathitt Gray { 1989c26df9bSWilliam Breathitt Gray struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 1999c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 2009c26df9bSWilliam Breathitt Gray const unsigned long offset = irqd_to_hwirq(data); 2019c26df9bSWilliam Breathitt Gray const unsigned port = offset / 8; 2029c26df9bSWilliam Breathitt Gray const unsigned mask = BIT(offset % 8); 2039c26df9bSWilliam Breathitt Gray unsigned long flags; 2049c26df9bSWilliam Breathitt Gray unsigned port_state; 2059c26df9bSWilliam Breathitt Gray 2069c26df9bSWilliam Breathitt Gray /* only the first 3 ports support interrupts */ 2079c26df9bSWilliam Breathitt Gray if (port > 2) 2089c26df9bSWilliam Breathitt Gray return; 2099c26df9bSWilliam Breathitt Gray 210*a0a584f0SJulia Cartwright raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); 2119c26df9bSWilliam Breathitt Gray 2129c26df9bSWilliam Breathitt Gray port_state = ws16c48gpio->irq_mask >> (8*port); 2139c26df9bSWilliam Breathitt Gray 2149c26df9bSWilliam Breathitt Gray outb(0x80, ws16c48gpio->base + 7); 2159c26df9bSWilliam Breathitt Gray outb(port_state & ~mask, ws16c48gpio->base + 8 + port); 2169c26df9bSWilliam Breathitt Gray outb(port_state | mask, ws16c48gpio->base + 8 + port); 2179c26df9bSWilliam Breathitt Gray outb(0xC0, ws16c48gpio->base + 7); 2189c26df9bSWilliam Breathitt Gray 219*a0a584f0SJulia Cartwright raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 2209c26df9bSWilliam Breathitt Gray } 2219c26df9bSWilliam Breathitt Gray 2229c26df9bSWilliam Breathitt Gray static void ws16c48_irq_mask(struct irq_data *data) 2239c26df9bSWilliam Breathitt Gray { 2249c26df9bSWilliam Breathitt Gray struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 2259c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 2269c26df9bSWilliam Breathitt Gray const unsigned long offset = irqd_to_hwirq(data); 2279c26df9bSWilliam Breathitt Gray const unsigned long mask = BIT(offset); 2289c26df9bSWilliam Breathitt Gray const unsigned port = offset / 8; 2299c26df9bSWilliam Breathitt Gray unsigned long flags; 2309c26df9bSWilliam Breathitt Gray 2319c26df9bSWilliam Breathitt Gray /* only the first 3 ports support interrupts */ 2329c26df9bSWilliam Breathitt Gray if (port > 2) 2339c26df9bSWilliam Breathitt Gray return; 2349c26df9bSWilliam Breathitt Gray 235*a0a584f0SJulia Cartwright raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); 2369c26df9bSWilliam Breathitt Gray 2379c26df9bSWilliam Breathitt Gray ws16c48gpio->irq_mask &= ~mask; 2389c26df9bSWilliam Breathitt Gray 2399c26df9bSWilliam Breathitt Gray outb(0x80, ws16c48gpio->base + 7); 2409c26df9bSWilliam Breathitt Gray outb(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port); 2419c26df9bSWilliam Breathitt Gray outb(0xC0, ws16c48gpio->base + 7); 2429c26df9bSWilliam Breathitt Gray 243*a0a584f0SJulia Cartwright raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 2449c26df9bSWilliam Breathitt Gray } 2459c26df9bSWilliam Breathitt Gray 2469c26df9bSWilliam Breathitt Gray static void ws16c48_irq_unmask(struct irq_data *data) 2479c26df9bSWilliam Breathitt Gray { 2489c26df9bSWilliam Breathitt Gray struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 2499c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 2509c26df9bSWilliam Breathitt Gray const unsigned long offset = irqd_to_hwirq(data); 2519c26df9bSWilliam Breathitt Gray const unsigned long mask = BIT(offset); 2529c26df9bSWilliam Breathitt Gray const unsigned port = offset / 8; 2539c26df9bSWilliam Breathitt Gray unsigned long flags; 2549c26df9bSWilliam Breathitt Gray 2559c26df9bSWilliam Breathitt Gray /* only the first 3 ports support interrupts */ 2569c26df9bSWilliam Breathitt Gray if (port > 2) 2579c26df9bSWilliam Breathitt Gray return; 2589c26df9bSWilliam Breathitt Gray 259*a0a584f0SJulia Cartwright raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); 2609c26df9bSWilliam Breathitt Gray 2619c26df9bSWilliam Breathitt Gray ws16c48gpio->irq_mask |= mask; 2629c26df9bSWilliam Breathitt Gray 2639c26df9bSWilliam Breathitt Gray outb(0x80, ws16c48gpio->base + 7); 2649c26df9bSWilliam Breathitt Gray outb(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port); 2659c26df9bSWilliam Breathitt Gray outb(0xC0, ws16c48gpio->base + 7); 2669c26df9bSWilliam Breathitt Gray 267*a0a584f0SJulia Cartwright raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 2689c26df9bSWilliam Breathitt Gray } 2699c26df9bSWilliam Breathitt Gray 2709c26df9bSWilliam Breathitt Gray static int ws16c48_irq_set_type(struct irq_data *data, unsigned flow_type) 2719c26df9bSWilliam Breathitt Gray { 2729c26df9bSWilliam Breathitt Gray struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 2739c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 2749c26df9bSWilliam Breathitt Gray const unsigned long offset = irqd_to_hwirq(data); 2759c26df9bSWilliam Breathitt Gray const unsigned long mask = BIT(offset); 2769c26df9bSWilliam Breathitt Gray const unsigned port = offset / 8; 2779c26df9bSWilliam Breathitt Gray unsigned long flags; 2789c26df9bSWilliam Breathitt Gray 2799c26df9bSWilliam Breathitt Gray /* only the first 3 ports support interrupts */ 2809c26df9bSWilliam Breathitt Gray if (port > 2) 2819c26df9bSWilliam Breathitt Gray return -EINVAL; 2829c26df9bSWilliam Breathitt Gray 283*a0a584f0SJulia Cartwright raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); 2849c26df9bSWilliam Breathitt Gray 2859c26df9bSWilliam Breathitt Gray switch (flow_type) { 2869c26df9bSWilliam Breathitt Gray case IRQ_TYPE_NONE: 2879c26df9bSWilliam Breathitt Gray break; 2889c26df9bSWilliam Breathitt Gray case IRQ_TYPE_EDGE_RISING: 2899c26df9bSWilliam Breathitt Gray ws16c48gpio->flow_mask |= mask; 2909c26df9bSWilliam Breathitt Gray break; 2919c26df9bSWilliam Breathitt Gray case IRQ_TYPE_EDGE_FALLING: 2929c26df9bSWilliam Breathitt Gray ws16c48gpio->flow_mask &= ~mask; 2939c26df9bSWilliam Breathitt Gray break; 2949c26df9bSWilliam Breathitt Gray default: 295*a0a584f0SJulia Cartwright raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 2969c26df9bSWilliam Breathitt Gray return -EINVAL; 2979c26df9bSWilliam Breathitt Gray } 2989c26df9bSWilliam Breathitt Gray 2999c26df9bSWilliam Breathitt Gray outb(0x40, ws16c48gpio->base + 7); 3009c26df9bSWilliam Breathitt Gray outb(ws16c48gpio->flow_mask >> (8*port), ws16c48gpio->base + 8 + port); 3019c26df9bSWilliam Breathitt Gray outb(0xC0, ws16c48gpio->base + 7); 3029c26df9bSWilliam Breathitt Gray 303*a0a584f0SJulia Cartwright raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 3049c26df9bSWilliam Breathitt Gray 3059c26df9bSWilliam Breathitt Gray return 0; 3069c26df9bSWilliam Breathitt Gray } 3079c26df9bSWilliam Breathitt Gray 3089c26df9bSWilliam Breathitt Gray static struct irq_chip ws16c48_irqchip = { 3099c26df9bSWilliam Breathitt Gray .name = "ws16c48", 3109c26df9bSWilliam Breathitt Gray .irq_ack = ws16c48_irq_ack, 3119c26df9bSWilliam Breathitt Gray .irq_mask = ws16c48_irq_mask, 3129c26df9bSWilliam Breathitt Gray .irq_unmask = ws16c48_irq_unmask, 3139c26df9bSWilliam Breathitt Gray .irq_set_type = ws16c48_irq_set_type 3149c26df9bSWilliam Breathitt Gray }; 3159c26df9bSWilliam Breathitt Gray 3169c26df9bSWilliam Breathitt Gray static irqreturn_t ws16c48_irq_handler(int irq, void *dev_id) 3179c26df9bSWilliam Breathitt Gray { 3189c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *const ws16c48gpio = dev_id; 3199c26df9bSWilliam Breathitt Gray struct gpio_chip *const chip = &ws16c48gpio->chip; 3209c26df9bSWilliam Breathitt Gray unsigned long int_pending; 3219c26df9bSWilliam Breathitt Gray unsigned long port; 3229c26df9bSWilliam Breathitt Gray unsigned long int_id; 3239c26df9bSWilliam Breathitt Gray unsigned long gpio; 3249c26df9bSWilliam Breathitt Gray 3259c26df9bSWilliam Breathitt Gray int_pending = inb(ws16c48gpio->base + 6) & 0x7; 3269c26df9bSWilliam Breathitt Gray if (!int_pending) 3279c26df9bSWilliam Breathitt Gray return IRQ_NONE; 3289c26df9bSWilliam Breathitt Gray 3299c26df9bSWilliam Breathitt Gray /* loop until all pending interrupts are handled */ 3309c26df9bSWilliam Breathitt Gray do { 3319c26df9bSWilliam Breathitt Gray for_each_set_bit(port, &int_pending, 3) { 3329c26df9bSWilliam Breathitt Gray int_id = inb(ws16c48gpio->base + 8 + port); 3339c26df9bSWilliam Breathitt Gray for_each_set_bit(gpio, &int_id, 8) 3349c26df9bSWilliam Breathitt Gray generic_handle_irq(irq_find_mapping( 3359c26df9bSWilliam Breathitt Gray chip->irqdomain, gpio + 8*port)); 3369c26df9bSWilliam Breathitt Gray } 3379c26df9bSWilliam Breathitt Gray 3389c26df9bSWilliam Breathitt Gray int_pending = inb(ws16c48gpio->base + 6) & 0x7; 3399c26df9bSWilliam Breathitt Gray } while (int_pending); 3409c26df9bSWilliam Breathitt Gray 3419c26df9bSWilliam Breathitt Gray return IRQ_HANDLED; 3429c26df9bSWilliam Breathitt Gray } 3439c26df9bSWilliam Breathitt Gray 3445238f60fSWilliam Breathitt Gray #define WS16C48_NGPIO 48 3455238f60fSWilliam Breathitt Gray static const char *ws16c48_names[WS16C48_NGPIO] = { 3465238f60fSWilliam Breathitt Gray "Port 0 Bit 0", "Port 0 Bit 1", "Port 0 Bit 2", "Port 0 Bit 3", 3475238f60fSWilliam Breathitt Gray "Port 0 Bit 4", "Port 0 Bit 5", "Port 0 Bit 6", "Port 0 Bit 7", 3485238f60fSWilliam Breathitt Gray "Port 1 Bit 0", "Port 1 Bit 1", "Port 1 Bit 2", "Port 1 Bit 3", 3495238f60fSWilliam Breathitt Gray "Port 1 Bit 4", "Port 1 Bit 5", "Port 1 Bit 6", "Port 1 Bit 7", 3505238f60fSWilliam Breathitt Gray "Port 2 Bit 0", "Port 2 Bit 1", "Port 2 Bit 2", "Port 2 Bit 3", 3515238f60fSWilliam Breathitt Gray "Port 2 Bit 4", "Port 2 Bit 5", "Port 2 Bit 6", "Port 2 Bit 7", 3525238f60fSWilliam Breathitt Gray "Port 3 Bit 0", "Port 3 Bit 1", "Port 3 Bit 2", "Port 3 Bit 3", 3535238f60fSWilliam Breathitt Gray "Port 3 Bit 4", "Port 3 Bit 5", "Port 3 Bit 6", "Port 3 Bit 7", 3545238f60fSWilliam Breathitt Gray "Port 4 Bit 0", "Port 4 Bit 1", "Port 4 Bit 2", "Port 4 Bit 3", 3555238f60fSWilliam Breathitt Gray "Port 4 Bit 4", "Port 4 Bit 5", "Port 4 Bit 6", "Port 4 Bit 7", 3565238f60fSWilliam Breathitt Gray "Port 5 Bit 0", "Port 5 Bit 1", "Port 5 Bit 2", "Port 5 Bit 3", 3575238f60fSWilliam Breathitt Gray "Port 5 Bit 4", "Port 5 Bit 5", "Port 5 Bit 6", "Port 5 Bit 7" 3585238f60fSWilliam Breathitt Gray }; 3595238f60fSWilliam Breathitt Gray 360cc736607SWilliam Breathitt Gray static int ws16c48_probe(struct device *dev, unsigned int id) 3619c26df9bSWilliam Breathitt Gray { 3629c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *ws16c48gpio; 3639c26df9bSWilliam Breathitt Gray const char *const name = dev_name(dev); 3649c26df9bSWilliam Breathitt Gray int err; 3659c26df9bSWilliam Breathitt Gray 3669c26df9bSWilliam Breathitt Gray ws16c48gpio = devm_kzalloc(dev, sizeof(*ws16c48gpio), GFP_KERNEL); 3679c26df9bSWilliam Breathitt Gray if (!ws16c48gpio) 3689c26df9bSWilliam Breathitt Gray return -ENOMEM; 3699c26df9bSWilliam Breathitt Gray 370cc736607SWilliam Breathitt Gray if (!devm_request_region(dev, base[id], WS16C48_EXTENT, name)) { 371148ad68bSWilliam Breathitt Gray dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", 372cc736607SWilliam Breathitt Gray base[id], base[id] + WS16C48_EXTENT); 373148ad68bSWilliam Breathitt Gray return -EBUSY; 3749c26df9bSWilliam Breathitt Gray } 3759c26df9bSWilliam Breathitt Gray 3769c26df9bSWilliam Breathitt Gray ws16c48gpio->chip.label = name; 3779c26df9bSWilliam Breathitt Gray ws16c48gpio->chip.parent = dev; 3789c26df9bSWilliam Breathitt Gray ws16c48gpio->chip.owner = THIS_MODULE; 3799c26df9bSWilliam Breathitt Gray ws16c48gpio->chip.base = -1; 3805238f60fSWilliam Breathitt Gray ws16c48gpio->chip.ngpio = WS16C48_NGPIO; 3815238f60fSWilliam Breathitt Gray ws16c48gpio->chip.names = ws16c48_names; 3829c26df9bSWilliam Breathitt Gray ws16c48gpio->chip.get_direction = ws16c48_gpio_get_direction; 3839c26df9bSWilliam Breathitt Gray ws16c48gpio->chip.direction_input = ws16c48_gpio_direction_input; 3849c26df9bSWilliam Breathitt Gray ws16c48gpio->chip.direction_output = ws16c48_gpio_direction_output; 3859c26df9bSWilliam Breathitt Gray ws16c48gpio->chip.get = ws16c48_gpio_get; 3869c26df9bSWilliam Breathitt Gray ws16c48gpio->chip.set = ws16c48_gpio_set; 38799c8ac95SWilliam Breathitt Gray ws16c48gpio->chip.set_multiple = ws16c48_gpio_set_multiple; 388cc736607SWilliam Breathitt Gray ws16c48gpio->base = base[id]; 3899c26df9bSWilliam Breathitt Gray 390*a0a584f0SJulia Cartwright raw_spin_lock_init(&ws16c48gpio->lock); 3919c26df9bSWilliam Breathitt Gray 392b4cad1bcSWilliam Breathitt Gray err = devm_gpiochip_add_data(dev, &ws16c48gpio->chip, ws16c48gpio); 3939c26df9bSWilliam Breathitt Gray if (err) { 3949c26df9bSWilliam Breathitt Gray dev_err(dev, "GPIO registering failed (%d)\n", err); 395148ad68bSWilliam Breathitt Gray return err; 3969c26df9bSWilliam Breathitt Gray } 3979c26df9bSWilliam Breathitt Gray 3989c26df9bSWilliam Breathitt Gray /* Disable IRQ by default */ 399cc736607SWilliam Breathitt Gray outb(0x80, base[id] + 7); 400cc736607SWilliam Breathitt Gray outb(0, base[id] + 8); 401cc736607SWilliam Breathitt Gray outb(0, base[id] + 9); 402cc736607SWilliam Breathitt Gray outb(0, base[id] + 10); 403cc736607SWilliam Breathitt Gray outb(0xC0, base[id] + 7); 4049c26df9bSWilliam Breathitt Gray 4059c26df9bSWilliam Breathitt Gray err = gpiochip_irqchip_add(&ws16c48gpio->chip, &ws16c48_irqchip, 0, 4069c26df9bSWilliam Breathitt Gray handle_edge_irq, IRQ_TYPE_NONE); 4079c26df9bSWilliam Breathitt Gray if (err) { 4089c26df9bSWilliam Breathitt Gray dev_err(dev, "Could not add irqchip (%d)\n", err); 4099c26df9bSWilliam Breathitt Gray return err; 4109c26df9bSWilliam Breathitt Gray } 4119c26df9bSWilliam Breathitt Gray 412b4cad1bcSWilliam Breathitt Gray err = devm_request_irq(dev, irq[id], ws16c48_irq_handler, IRQF_SHARED, 413b4cad1bcSWilliam Breathitt Gray name, ws16c48gpio); 414b4cad1bcSWilliam Breathitt Gray if (err) { 415b4cad1bcSWilliam Breathitt Gray dev_err(dev, "IRQ handler registering failed (%d)\n", err); 416b4cad1bcSWilliam Breathitt Gray return err; 417b4cad1bcSWilliam Breathitt Gray } 4189c26df9bSWilliam Breathitt Gray 4199c26df9bSWilliam Breathitt Gray return 0; 4209c26df9bSWilliam Breathitt Gray } 4219c26df9bSWilliam Breathitt Gray 422cc736607SWilliam Breathitt Gray static struct isa_driver ws16c48_driver = { 423cc736607SWilliam Breathitt Gray .probe = ws16c48_probe, 4249c26df9bSWilliam Breathitt Gray .driver = { 4259c26df9bSWilliam Breathitt Gray .name = "ws16c48" 4269c26df9bSWilliam Breathitt Gray }, 4279c26df9bSWilliam Breathitt Gray }; 4289c26df9bSWilliam Breathitt Gray 429cc736607SWilliam Breathitt Gray module_isa_driver(ws16c48_driver, num_ws16c48); 4309c26df9bSWilliam Breathitt Gray 4319c26df9bSWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); 4329c26df9bSWilliam Breathitt Gray MODULE_DESCRIPTION("WinSystems WS16C48 GPIO driver"); 43322aeddb5SWilliam Breathitt Gray MODULE_LICENSE("GPL v2"); 434