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> 229c26df9bSWilliam Breathitt Gray #include <linux/kernel.h> 239c26df9bSWilliam Breathitt Gray #include <linux/module.h> 249c26df9bSWilliam Breathitt Gray #include <linux/moduleparam.h> 259c26df9bSWilliam Breathitt Gray #include <linux/platform_device.h> 269c26df9bSWilliam Breathitt Gray #include <linux/spinlock.h> 279c26df9bSWilliam Breathitt Gray 289c26df9bSWilliam Breathitt Gray static unsigned ws16c48_base; 299c26df9bSWilliam Breathitt Gray module_param(ws16c48_base, uint, 0); 309c26df9bSWilliam Breathitt Gray MODULE_PARM_DESC(ws16c48_base, "WinSystems WS16C48 base address"); 319c26df9bSWilliam Breathitt Gray static unsigned ws16c48_irq; 329c26df9bSWilliam Breathitt Gray module_param(ws16c48_irq, uint, 0); 339c26df9bSWilliam Breathitt Gray MODULE_PARM_DESC(ws16c48_irq, "WinSystems WS16C48 interrupt line number"); 349c26df9bSWilliam Breathitt Gray 359c26df9bSWilliam Breathitt Gray /** 369c26df9bSWilliam Breathitt Gray * struct ws16c48_gpio - GPIO device private data structure 379c26df9bSWilliam Breathitt Gray * @chip: instance of the gpio_chip 389c26df9bSWilliam Breathitt Gray * @io_state: bit I/O state (whether bit is set to input or output) 399c26df9bSWilliam Breathitt Gray * @out_state: output bits state 409c26df9bSWilliam Breathitt Gray * @lock: synchronization lock to prevent I/O race conditions 419c26df9bSWilliam Breathitt Gray * @irq_mask: I/O bits affected by interrupts 429c26df9bSWilliam Breathitt Gray * @flow_mask: IRQ flow type mask for the respective I/O bits 439c26df9bSWilliam Breathitt Gray * @base: base port address of the GPIO device 449c26df9bSWilliam Breathitt Gray * @extent: extent of port address region of the GPIO device 459c26df9bSWilliam Breathitt Gray * @irq: Interrupt line number 469c26df9bSWilliam Breathitt Gray */ 479c26df9bSWilliam Breathitt Gray struct ws16c48_gpio { 489c26df9bSWilliam Breathitt Gray struct gpio_chip chip; 499c26df9bSWilliam Breathitt Gray unsigned char io_state[6]; 509c26df9bSWilliam Breathitt Gray unsigned char out_state[6]; 519c26df9bSWilliam Breathitt Gray spinlock_t lock; 529c26df9bSWilliam Breathitt Gray unsigned long irq_mask; 539c26df9bSWilliam Breathitt Gray unsigned long flow_mask; 549c26df9bSWilliam Breathitt Gray unsigned base; 559c26df9bSWilliam Breathitt Gray unsigned extent; 569c26df9bSWilliam Breathitt Gray unsigned irq; 579c26df9bSWilliam Breathitt Gray }; 589c26df9bSWilliam Breathitt Gray 599c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_get_direction(struct gpio_chip *chip, unsigned offset) 609c26df9bSWilliam Breathitt Gray { 619c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 629c26df9bSWilliam Breathitt Gray const unsigned port = offset / 8; 639c26df9bSWilliam Breathitt Gray const unsigned mask = BIT(offset % 8); 649c26df9bSWilliam Breathitt Gray 659c26df9bSWilliam Breathitt Gray return !!(ws16c48gpio->io_state[port] & mask); 669c26df9bSWilliam Breathitt Gray } 679c26df9bSWilliam Breathitt Gray 689c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_direction_input(struct gpio_chip *chip, unsigned offset) 699c26df9bSWilliam Breathitt Gray { 709c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 719c26df9bSWilliam Breathitt Gray const unsigned port = offset / 8; 729c26df9bSWilliam Breathitt Gray const unsigned mask = BIT(offset % 8); 739c26df9bSWilliam Breathitt Gray unsigned long flags; 749c26df9bSWilliam Breathitt Gray 759c26df9bSWilliam Breathitt Gray spin_lock_irqsave(&ws16c48gpio->lock, flags); 769c26df9bSWilliam Breathitt Gray 779c26df9bSWilliam Breathitt Gray ws16c48gpio->io_state[port] |= mask; 789c26df9bSWilliam Breathitt Gray ws16c48gpio->out_state[port] &= ~mask; 799c26df9bSWilliam Breathitt Gray outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port); 809c26df9bSWilliam Breathitt Gray 819c26df9bSWilliam Breathitt Gray spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 829c26df9bSWilliam Breathitt Gray 839c26df9bSWilliam Breathitt Gray return 0; 849c26df9bSWilliam Breathitt Gray } 859c26df9bSWilliam Breathitt Gray 869c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_direction_output(struct gpio_chip *chip, 879c26df9bSWilliam Breathitt Gray unsigned offset, int value) 889c26df9bSWilliam Breathitt Gray { 899c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 909c26df9bSWilliam Breathitt Gray const unsigned port = offset / 8; 919c26df9bSWilliam Breathitt Gray const unsigned mask = BIT(offset % 8); 929c26df9bSWilliam Breathitt Gray unsigned long flags; 939c26df9bSWilliam Breathitt Gray 949c26df9bSWilliam Breathitt Gray spin_lock_irqsave(&ws16c48gpio->lock, flags); 959c26df9bSWilliam Breathitt Gray 969c26df9bSWilliam Breathitt Gray ws16c48gpio->io_state[port] &= ~mask; 979c26df9bSWilliam Breathitt Gray if (value) 989c26df9bSWilliam Breathitt Gray ws16c48gpio->out_state[port] |= mask; 999c26df9bSWilliam Breathitt Gray else 1009c26df9bSWilliam Breathitt Gray ws16c48gpio->out_state[port] &= ~mask; 1019c26df9bSWilliam Breathitt Gray outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port); 1029c26df9bSWilliam Breathitt Gray 1039c26df9bSWilliam Breathitt Gray spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 1049c26df9bSWilliam Breathitt Gray 1059c26df9bSWilliam Breathitt Gray return 0; 1069c26df9bSWilliam Breathitt Gray } 1079c26df9bSWilliam Breathitt Gray 1089c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_get(struct gpio_chip *chip, unsigned offset) 1099c26df9bSWilliam Breathitt Gray { 1109c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 1119c26df9bSWilliam Breathitt Gray const unsigned port = offset / 8; 1129c26df9bSWilliam Breathitt Gray const unsigned mask = BIT(offset % 8); 1139c26df9bSWilliam Breathitt Gray unsigned long flags; 1149c26df9bSWilliam Breathitt Gray unsigned port_state; 1159c26df9bSWilliam Breathitt Gray 1169c26df9bSWilliam Breathitt Gray spin_lock_irqsave(&ws16c48gpio->lock, flags); 1179c26df9bSWilliam Breathitt Gray 1189c26df9bSWilliam Breathitt Gray /* ensure that GPIO is set for input */ 1199c26df9bSWilliam Breathitt Gray if (!(ws16c48gpio->io_state[port] & mask)) { 1209c26df9bSWilliam Breathitt Gray spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 1219c26df9bSWilliam Breathitt Gray return -EINVAL; 1229c26df9bSWilliam Breathitt Gray } 1239c26df9bSWilliam Breathitt Gray 1249c26df9bSWilliam Breathitt Gray port_state = inb(ws16c48gpio->base + port); 1259c26df9bSWilliam Breathitt Gray 1269c26df9bSWilliam Breathitt Gray spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 1279c26df9bSWilliam Breathitt Gray 1289c26df9bSWilliam Breathitt Gray return !!(port_state & mask); 1299c26df9bSWilliam Breathitt Gray } 1309c26df9bSWilliam Breathitt Gray 1319c26df9bSWilliam Breathitt Gray static void ws16c48_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 1329c26df9bSWilliam Breathitt Gray { 1339c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 1349c26df9bSWilliam Breathitt Gray const unsigned port = offset / 8; 1359c26df9bSWilliam Breathitt Gray const unsigned mask = BIT(offset % 8); 1369c26df9bSWilliam Breathitt Gray unsigned long flags; 1379c26df9bSWilliam Breathitt Gray 1389c26df9bSWilliam Breathitt Gray spin_lock_irqsave(&ws16c48gpio->lock, flags); 1399c26df9bSWilliam Breathitt Gray 1409c26df9bSWilliam Breathitt Gray /* ensure that GPIO is set for output */ 1419c26df9bSWilliam Breathitt Gray if (ws16c48gpio->io_state[port] & mask) { 1429c26df9bSWilliam Breathitt Gray spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 1439c26df9bSWilliam Breathitt Gray return; 1449c26df9bSWilliam Breathitt Gray } 1459c26df9bSWilliam Breathitt Gray 1469c26df9bSWilliam Breathitt Gray if (value) 1479c26df9bSWilliam Breathitt Gray ws16c48gpio->out_state[port] |= mask; 1489c26df9bSWilliam Breathitt Gray else 1499c26df9bSWilliam Breathitt Gray ws16c48gpio->out_state[port] &= ~mask; 1509c26df9bSWilliam Breathitt Gray outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port); 1519c26df9bSWilliam Breathitt Gray 1529c26df9bSWilliam Breathitt Gray spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 1539c26df9bSWilliam Breathitt Gray } 1549c26df9bSWilliam Breathitt Gray 1559c26df9bSWilliam Breathitt Gray static void ws16c48_irq_ack(struct irq_data *data) 1569c26df9bSWilliam Breathitt Gray { 1579c26df9bSWilliam Breathitt Gray struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 1589c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 1599c26df9bSWilliam Breathitt Gray const unsigned long offset = irqd_to_hwirq(data); 1609c26df9bSWilliam Breathitt Gray const unsigned port = offset / 8; 1619c26df9bSWilliam Breathitt Gray const unsigned mask = BIT(offset % 8); 1629c26df9bSWilliam Breathitt Gray unsigned long flags; 1639c26df9bSWilliam Breathitt Gray unsigned port_state; 1649c26df9bSWilliam Breathitt Gray 1659c26df9bSWilliam Breathitt Gray /* only the first 3 ports support interrupts */ 1669c26df9bSWilliam Breathitt Gray if (port > 2) 1679c26df9bSWilliam Breathitt Gray return; 1689c26df9bSWilliam Breathitt Gray 1699c26df9bSWilliam Breathitt Gray spin_lock_irqsave(&ws16c48gpio->lock, flags); 1709c26df9bSWilliam Breathitt Gray 1719c26df9bSWilliam Breathitt Gray port_state = ws16c48gpio->irq_mask >> (8*port); 1729c26df9bSWilliam Breathitt Gray 1739c26df9bSWilliam Breathitt Gray outb(0x80, ws16c48gpio->base + 7); 1749c26df9bSWilliam Breathitt Gray outb(port_state & ~mask, ws16c48gpio->base + 8 + port); 1759c26df9bSWilliam Breathitt Gray outb(port_state | mask, ws16c48gpio->base + 8 + port); 1769c26df9bSWilliam Breathitt Gray outb(0xC0, ws16c48gpio->base + 7); 1779c26df9bSWilliam Breathitt Gray 1789c26df9bSWilliam Breathitt Gray spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 1799c26df9bSWilliam Breathitt Gray } 1809c26df9bSWilliam Breathitt Gray 1819c26df9bSWilliam Breathitt Gray static void ws16c48_irq_mask(struct irq_data *data) 1829c26df9bSWilliam Breathitt Gray { 1839c26df9bSWilliam Breathitt Gray struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 1849c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 1859c26df9bSWilliam Breathitt Gray const unsigned long offset = irqd_to_hwirq(data); 1869c26df9bSWilliam Breathitt Gray const unsigned long mask = BIT(offset); 1879c26df9bSWilliam Breathitt Gray const unsigned port = offset / 8; 1889c26df9bSWilliam Breathitt Gray unsigned long flags; 1899c26df9bSWilliam Breathitt Gray 1909c26df9bSWilliam Breathitt Gray /* only the first 3 ports support interrupts */ 1919c26df9bSWilliam Breathitt Gray if (port > 2) 1929c26df9bSWilliam Breathitt Gray return; 1939c26df9bSWilliam Breathitt Gray 1949c26df9bSWilliam Breathitt Gray spin_lock_irqsave(&ws16c48gpio->lock, flags); 1959c26df9bSWilliam Breathitt Gray 1969c26df9bSWilliam Breathitt Gray ws16c48gpio->irq_mask &= ~mask; 1979c26df9bSWilliam Breathitt Gray 1989c26df9bSWilliam Breathitt Gray outb(0x80, ws16c48gpio->base + 7); 1999c26df9bSWilliam Breathitt Gray outb(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port); 2009c26df9bSWilliam Breathitt Gray outb(0xC0, ws16c48gpio->base + 7); 2019c26df9bSWilliam Breathitt Gray 2029c26df9bSWilliam Breathitt Gray spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 2039c26df9bSWilliam Breathitt Gray } 2049c26df9bSWilliam Breathitt Gray 2059c26df9bSWilliam Breathitt Gray static void ws16c48_irq_unmask(struct irq_data *data) 2069c26df9bSWilliam Breathitt Gray { 2079c26df9bSWilliam Breathitt Gray struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 2089c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 2099c26df9bSWilliam Breathitt Gray const unsigned long offset = irqd_to_hwirq(data); 2109c26df9bSWilliam Breathitt Gray const unsigned long mask = BIT(offset); 2119c26df9bSWilliam Breathitt Gray const unsigned port = offset / 8; 2129c26df9bSWilliam Breathitt Gray unsigned long flags; 2139c26df9bSWilliam Breathitt Gray 2149c26df9bSWilliam Breathitt Gray /* only the first 3 ports support interrupts */ 2159c26df9bSWilliam Breathitt Gray if (port > 2) 2169c26df9bSWilliam Breathitt Gray return; 2179c26df9bSWilliam Breathitt Gray 2189c26df9bSWilliam Breathitt Gray spin_lock_irqsave(&ws16c48gpio->lock, flags); 2199c26df9bSWilliam Breathitt Gray 2209c26df9bSWilliam Breathitt Gray ws16c48gpio->irq_mask |= mask; 2219c26df9bSWilliam Breathitt Gray 2229c26df9bSWilliam Breathitt Gray outb(0x80, ws16c48gpio->base + 7); 2239c26df9bSWilliam Breathitt Gray outb(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port); 2249c26df9bSWilliam Breathitt Gray outb(0xC0, ws16c48gpio->base + 7); 2259c26df9bSWilliam Breathitt Gray 2269c26df9bSWilliam Breathitt Gray spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 2279c26df9bSWilliam Breathitt Gray } 2289c26df9bSWilliam Breathitt Gray 2299c26df9bSWilliam Breathitt Gray static int ws16c48_irq_set_type(struct irq_data *data, unsigned flow_type) 2309c26df9bSWilliam Breathitt Gray { 2319c26df9bSWilliam Breathitt Gray struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 2329c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); 2339c26df9bSWilliam Breathitt Gray const unsigned long offset = irqd_to_hwirq(data); 2349c26df9bSWilliam Breathitt Gray const unsigned long mask = BIT(offset); 2359c26df9bSWilliam Breathitt Gray const unsigned port = offset / 8; 2369c26df9bSWilliam Breathitt Gray unsigned long flags; 2379c26df9bSWilliam Breathitt Gray 2389c26df9bSWilliam Breathitt Gray /* only the first 3 ports support interrupts */ 2399c26df9bSWilliam Breathitt Gray if (port > 2) 2409c26df9bSWilliam Breathitt Gray return -EINVAL; 2419c26df9bSWilliam Breathitt Gray 2429c26df9bSWilliam Breathitt Gray spin_lock_irqsave(&ws16c48gpio->lock, flags); 2439c26df9bSWilliam Breathitt Gray 2449c26df9bSWilliam Breathitt Gray switch (flow_type) { 2459c26df9bSWilliam Breathitt Gray case IRQ_TYPE_NONE: 2469c26df9bSWilliam Breathitt Gray break; 2479c26df9bSWilliam Breathitt Gray case IRQ_TYPE_EDGE_RISING: 2489c26df9bSWilliam Breathitt Gray ws16c48gpio->flow_mask |= mask; 2499c26df9bSWilliam Breathitt Gray break; 2509c26df9bSWilliam Breathitt Gray case IRQ_TYPE_EDGE_FALLING: 2519c26df9bSWilliam Breathitt Gray ws16c48gpio->flow_mask &= ~mask; 2529c26df9bSWilliam Breathitt Gray break; 2539c26df9bSWilliam Breathitt Gray default: 2549c26df9bSWilliam Breathitt Gray spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 2559c26df9bSWilliam Breathitt Gray return -EINVAL; 2569c26df9bSWilliam Breathitt Gray } 2579c26df9bSWilliam Breathitt Gray 2589c26df9bSWilliam Breathitt Gray outb(0x40, ws16c48gpio->base + 7); 2599c26df9bSWilliam Breathitt Gray outb(ws16c48gpio->flow_mask >> (8*port), ws16c48gpio->base + 8 + port); 2609c26df9bSWilliam Breathitt Gray outb(0xC0, ws16c48gpio->base + 7); 2619c26df9bSWilliam Breathitt Gray 2629c26df9bSWilliam Breathitt Gray spin_unlock_irqrestore(&ws16c48gpio->lock, flags); 2639c26df9bSWilliam Breathitt Gray 2649c26df9bSWilliam Breathitt Gray return 0; 2659c26df9bSWilliam Breathitt Gray } 2669c26df9bSWilliam Breathitt Gray 2679c26df9bSWilliam Breathitt Gray static struct irq_chip ws16c48_irqchip = { 2689c26df9bSWilliam Breathitt Gray .name = "ws16c48", 2699c26df9bSWilliam Breathitt Gray .irq_ack = ws16c48_irq_ack, 2709c26df9bSWilliam Breathitt Gray .irq_mask = ws16c48_irq_mask, 2719c26df9bSWilliam Breathitt Gray .irq_unmask = ws16c48_irq_unmask, 2729c26df9bSWilliam Breathitt Gray .irq_set_type = ws16c48_irq_set_type 2739c26df9bSWilliam Breathitt Gray }; 2749c26df9bSWilliam Breathitt Gray 2759c26df9bSWilliam Breathitt Gray static irqreturn_t ws16c48_irq_handler(int irq, void *dev_id) 2769c26df9bSWilliam Breathitt Gray { 2779c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *const ws16c48gpio = dev_id; 2789c26df9bSWilliam Breathitt Gray struct gpio_chip *const chip = &ws16c48gpio->chip; 2799c26df9bSWilliam Breathitt Gray unsigned long int_pending; 2809c26df9bSWilliam Breathitt Gray unsigned long port; 2819c26df9bSWilliam Breathitt Gray unsigned long int_id; 2829c26df9bSWilliam Breathitt Gray unsigned long gpio; 2839c26df9bSWilliam Breathitt Gray 2849c26df9bSWilliam Breathitt Gray int_pending = inb(ws16c48gpio->base + 6) & 0x7; 2859c26df9bSWilliam Breathitt Gray if (!int_pending) 2869c26df9bSWilliam Breathitt Gray return IRQ_NONE; 2879c26df9bSWilliam Breathitt Gray 2889c26df9bSWilliam Breathitt Gray /* loop until all pending interrupts are handled */ 2899c26df9bSWilliam Breathitt Gray do { 2909c26df9bSWilliam Breathitt Gray for_each_set_bit(port, &int_pending, 3) { 2919c26df9bSWilliam Breathitt Gray int_id = inb(ws16c48gpio->base + 8 + port); 2929c26df9bSWilliam Breathitt Gray for_each_set_bit(gpio, &int_id, 8) 2939c26df9bSWilliam Breathitt Gray generic_handle_irq(irq_find_mapping( 2949c26df9bSWilliam Breathitt Gray chip->irqdomain, gpio + 8*port)); 2959c26df9bSWilliam Breathitt Gray } 2969c26df9bSWilliam Breathitt Gray 2979c26df9bSWilliam Breathitt Gray int_pending = inb(ws16c48gpio->base + 6) & 0x7; 2989c26df9bSWilliam Breathitt Gray } while (int_pending); 2999c26df9bSWilliam Breathitt Gray 3009c26df9bSWilliam Breathitt Gray return IRQ_HANDLED; 3019c26df9bSWilliam Breathitt Gray } 3029c26df9bSWilliam Breathitt Gray 3039c26df9bSWilliam Breathitt Gray static int __init ws16c48_probe(struct platform_device *pdev) 3049c26df9bSWilliam Breathitt Gray { 3059c26df9bSWilliam Breathitt Gray struct device *dev = &pdev->dev; 3069c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *ws16c48gpio; 3079c26df9bSWilliam Breathitt Gray const unsigned base = ws16c48_base; 3089c26df9bSWilliam Breathitt Gray const unsigned extent = 16; 3099c26df9bSWilliam Breathitt Gray const char *const name = dev_name(dev); 3109c26df9bSWilliam Breathitt Gray int err; 3119c26df9bSWilliam Breathitt Gray const unsigned irq = ws16c48_irq; 3129c26df9bSWilliam Breathitt Gray 3139c26df9bSWilliam Breathitt Gray ws16c48gpio = devm_kzalloc(dev, sizeof(*ws16c48gpio), GFP_KERNEL); 3149c26df9bSWilliam Breathitt Gray if (!ws16c48gpio) 3159c26df9bSWilliam Breathitt Gray return -ENOMEM; 3169c26df9bSWilliam Breathitt Gray 3179c26df9bSWilliam Breathitt Gray if (!request_region(base, extent, name)) { 3189c26df9bSWilliam Breathitt Gray dev_err(dev, "Unable to lock %s port addresses (0x%X-0x%X)\n", 3199c26df9bSWilliam Breathitt Gray name, base, base + extent); 3209c26df9bSWilliam Breathitt Gray err = -EBUSY; 3219c26df9bSWilliam Breathitt Gray goto err_lock_io_port; 3229c26df9bSWilliam Breathitt Gray } 3239c26df9bSWilliam Breathitt Gray 3249c26df9bSWilliam Breathitt Gray ws16c48gpio->chip.label = name; 3259c26df9bSWilliam Breathitt Gray ws16c48gpio->chip.parent = dev; 3269c26df9bSWilliam Breathitt Gray ws16c48gpio->chip.owner = THIS_MODULE; 3279c26df9bSWilliam Breathitt Gray ws16c48gpio->chip.base = -1; 3289c26df9bSWilliam Breathitt Gray ws16c48gpio->chip.ngpio = 48; 3299c26df9bSWilliam Breathitt Gray ws16c48gpio->chip.get_direction = ws16c48_gpio_get_direction; 3309c26df9bSWilliam Breathitt Gray ws16c48gpio->chip.direction_input = ws16c48_gpio_direction_input; 3319c26df9bSWilliam Breathitt Gray ws16c48gpio->chip.direction_output = ws16c48_gpio_direction_output; 3329c26df9bSWilliam Breathitt Gray ws16c48gpio->chip.get = ws16c48_gpio_get; 3339c26df9bSWilliam Breathitt Gray ws16c48gpio->chip.set = ws16c48_gpio_set; 3349c26df9bSWilliam Breathitt Gray ws16c48gpio->base = base; 3359c26df9bSWilliam Breathitt Gray ws16c48gpio->extent = extent; 3369c26df9bSWilliam Breathitt Gray ws16c48gpio->irq = irq; 3379c26df9bSWilliam Breathitt Gray 3389c26df9bSWilliam Breathitt Gray spin_lock_init(&ws16c48gpio->lock); 3399c26df9bSWilliam Breathitt Gray 3409c26df9bSWilliam Breathitt Gray dev_set_drvdata(dev, ws16c48gpio); 3419c26df9bSWilliam Breathitt Gray 3429c26df9bSWilliam Breathitt Gray err = gpiochip_add_data(&ws16c48gpio->chip, ws16c48gpio); 3439c26df9bSWilliam Breathitt Gray if (err) { 3449c26df9bSWilliam Breathitt Gray dev_err(dev, "GPIO registering failed (%d)\n", err); 3459c26df9bSWilliam Breathitt Gray goto err_gpio_register; 3469c26df9bSWilliam Breathitt Gray } 3479c26df9bSWilliam Breathitt Gray 3489c26df9bSWilliam Breathitt Gray /* Disable IRQ by default */ 3499c26df9bSWilliam Breathitt Gray outb(0x80, base + 7); 3509c26df9bSWilliam Breathitt Gray outb(0, base + 8); 3519c26df9bSWilliam Breathitt Gray outb(0, base + 9); 3529c26df9bSWilliam Breathitt Gray outb(0, base + 10); 3539c26df9bSWilliam Breathitt Gray outb(0xC0, base + 7); 3549c26df9bSWilliam Breathitt Gray 3559c26df9bSWilliam Breathitt Gray err = gpiochip_irqchip_add(&ws16c48gpio->chip, &ws16c48_irqchip, 0, 3569c26df9bSWilliam Breathitt Gray handle_edge_irq, IRQ_TYPE_NONE); 3579c26df9bSWilliam Breathitt Gray if (err) { 3589c26df9bSWilliam Breathitt Gray dev_err(dev, "Could not add irqchip (%d)\n", err); 3599c26df9bSWilliam Breathitt Gray goto err_gpiochip_irqchip_add; 3609c26df9bSWilliam Breathitt Gray } 3619c26df9bSWilliam Breathitt Gray 3629c26df9bSWilliam Breathitt Gray err = request_irq(irq, ws16c48_irq_handler, IRQF_SHARED, name, 3639c26df9bSWilliam Breathitt Gray ws16c48gpio); 3649c26df9bSWilliam Breathitt Gray if (err) { 3659c26df9bSWilliam Breathitt Gray dev_err(dev, "IRQ handler registering failed (%d)\n", err); 3669c26df9bSWilliam Breathitt Gray goto err_request_irq; 3679c26df9bSWilliam Breathitt Gray } 3689c26df9bSWilliam Breathitt Gray 3699c26df9bSWilliam Breathitt Gray return 0; 3709c26df9bSWilliam Breathitt Gray 3719c26df9bSWilliam Breathitt Gray err_request_irq: 3729c26df9bSWilliam Breathitt Gray err_gpiochip_irqchip_add: 3739c26df9bSWilliam Breathitt Gray gpiochip_remove(&ws16c48gpio->chip); 3749c26df9bSWilliam Breathitt Gray err_gpio_register: 3759c26df9bSWilliam Breathitt Gray release_region(base, extent); 3769c26df9bSWilliam Breathitt Gray err_lock_io_port: 3779c26df9bSWilliam Breathitt Gray return err; 3789c26df9bSWilliam Breathitt Gray } 3799c26df9bSWilliam Breathitt Gray 3809c26df9bSWilliam Breathitt Gray static int ws16c48_remove(struct platform_device *pdev) 3819c26df9bSWilliam Breathitt Gray { 3829c26df9bSWilliam Breathitt Gray struct ws16c48_gpio *const ws16c48gpio = platform_get_drvdata(pdev); 3839c26df9bSWilliam Breathitt Gray 3849c26df9bSWilliam Breathitt Gray free_irq(ws16c48gpio->irq, ws16c48gpio); 3859c26df9bSWilliam Breathitt Gray gpiochip_remove(&ws16c48gpio->chip); 3869c26df9bSWilliam Breathitt Gray release_region(ws16c48gpio->base, ws16c48gpio->extent); 3879c26df9bSWilliam Breathitt Gray 3889c26df9bSWilliam Breathitt Gray return 0; 3899c26df9bSWilliam Breathitt Gray } 3909c26df9bSWilliam Breathitt Gray 3919c26df9bSWilliam Breathitt Gray static struct platform_device *ws16c48_device; 3929c26df9bSWilliam Breathitt Gray 3939c26df9bSWilliam Breathitt Gray static struct platform_driver ws16c48_driver = { 3949c26df9bSWilliam Breathitt Gray .driver = { 3959c26df9bSWilliam Breathitt Gray .name = "ws16c48" 3969c26df9bSWilliam Breathitt Gray }, 3979c26df9bSWilliam Breathitt Gray .remove = ws16c48_remove 3989c26df9bSWilliam Breathitt Gray }; 3999c26df9bSWilliam Breathitt Gray 4009c26df9bSWilliam Breathitt Gray static void __exit ws16c48_exit(void) 4019c26df9bSWilliam Breathitt Gray { 4029c26df9bSWilliam Breathitt Gray platform_device_unregister(ws16c48_device); 4039c26df9bSWilliam Breathitt Gray platform_driver_unregister(&ws16c48_driver); 4049c26df9bSWilliam Breathitt Gray } 4059c26df9bSWilliam Breathitt Gray 4069c26df9bSWilliam Breathitt Gray static int __init ws16c48_init(void) 4079c26df9bSWilliam Breathitt Gray { 4089c26df9bSWilliam Breathitt Gray int err; 4099c26df9bSWilliam Breathitt Gray 4109c26df9bSWilliam Breathitt Gray ws16c48_device = platform_device_alloc(ws16c48_driver.driver.name, -1); 4119c26df9bSWilliam Breathitt Gray if (!ws16c48_device) 4129c26df9bSWilliam Breathitt Gray return -ENOMEM; 4139c26df9bSWilliam Breathitt Gray 4149c26df9bSWilliam Breathitt Gray err = platform_device_add(ws16c48_device); 4159c26df9bSWilliam Breathitt Gray if (err) 4169c26df9bSWilliam Breathitt Gray goto err_platform_device; 4179c26df9bSWilliam Breathitt Gray 4189c26df9bSWilliam Breathitt Gray err = platform_driver_probe(&ws16c48_driver, ws16c48_probe); 4199c26df9bSWilliam Breathitt Gray if (err) 4209c26df9bSWilliam Breathitt Gray goto err_platform_driver; 4219c26df9bSWilliam Breathitt Gray 4229c26df9bSWilliam Breathitt Gray return 0; 4239c26df9bSWilliam Breathitt Gray 4249c26df9bSWilliam Breathitt Gray err_platform_driver: 4259c26df9bSWilliam Breathitt Gray platform_device_del(ws16c48_device); 4269c26df9bSWilliam Breathitt Gray err_platform_device: 4279c26df9bSWilliam Breathitt Gray platform_device_put(ws16c48_device); 4289c26df9bSWilliam Breathitt Gray return err; 4299c26df9bSWilliam Breathitt Gray } 4309c26df9bSWilliam Breathitt Gray 4319c26df9bSWilliam Breathitt Gray module_init(ws16c48_init); 4329c26df9bSWilliam Breathitt Gray module_exit(ws16c48_exit); 4339c26df9bSWilliam Breathitt Gray 4349c26df9bSWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); 4359c26df9bSWilliam Breathitt Gray MODULE_DESCRIPTION("WinSystems WS16C48 GPIO driver"); 436*22aeddb5SWilliam Breathitt Gray MODULE_LICENSE("GPL v2"); 437