1 /* 2 * GPIO driver for the Diamond Systems GPIO-MM 3 * Copyright (C) 2016 William Breathitt Gray 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License, version 2, as 7 * published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * General Public License for more details. 13 * 14 * This driver supports the following Diamond Systems devices: GPIO-MM and 15 * GPIO-MM-12. 16 */ 17 #include <linux/bitops.h> 18 #include <linux/device.h> 19 #include <linux/errno.h> 20 #include <linux/gpio/driver.h> 21 #include <linux/io.h> 22 #include <linux/ioport.h> 23 #include <linux/isa.h> 24 #include <linux/kernel.h> 25 #include <linux/module.h> 26 #include <linux/moduleparam.h> 27 #include <linux/spinlock.h> 28 29 #define GPIOMM_EXTENT 8 30 #define MAX_NUM_GPIOMM max_num_isa_dev(GPIOMM_EXTENT) 31 32 static unsigned int base[MAX_NUM_GPIOMM]; 33 static unsigned int num_gpiomm; 34 module_param_array(base, uint, &num_gpiomm, 0); 35 MODULE_PARM_DESC(base, "Diamond Systems GPIO-MM base addresses"); 36 37 /** 38 * struct gpiomm_gpio - GPIO device private data structure 39 * @chip: instance of the gpio_chip 40 * @io_state: bit I/O state (whether bit is set to input or output) 41 * @out_state: output bits state 42 * @control: Control registers state 43 * @lock: synchronization lock to prevent I/O race conditions 44 * @base: base port address of the GPIO device 45 */ 46 struct gpiomm_gpio { 47 struct gpio_chip chip; 48 unsigned char io_state[6]; 49 unsigned char out_state[6]; 50 unsigned char control[2]; 51 spinlock_t lock; 52 unsigned int base; 53 }; 54 55 static int gpiomm_gpio_get_direction(struct gpio_chip *chip, 56 unsigned int offset) 57 { 58 struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); 59 const unsigned int port = offset / 8; 60 const unsigned int mask = BIT(offset % 8); 61 62 return !!(gpiommgpio->io_state[port] & mask); 63 } 64 65 static int gpiomm_gpio_direction_input(struct gpio_chip *chip, 66 unsigned int offset) 67 { 68 struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); 69 const unsigned int io_port = offset / 8; 70 const unsigned int control_port = io_port / 3; 71 const unsigned int control_addr = gpiommgpio->base + 3 + control_port*4; 72 unsigned long flags; 73 unsigned int control; 74 75 spin_lock_irqsave(&gpiommgpio->lock, flags); 76 77 /* Check if configuring Port C */ 78 if (io_port == 2 || io_port == 5) { 79 /* Port C can be configured by nibble */ 80 if (offset % 8 > 3) { 81 gpiommgpio->io_state[io_port] |= 0xF0; 82 gpiommgpio->control[control_port] |= BIT(3); 83 } else { 84 gpiommgpio->io_state[io_port] |= 0x0F; 85 gpiommgpio->control[control_port] |= BIT(0); 86 } 87 } else { 88 gpiommgpio->io_state[io_port] |= 0xFF; 89 if (io_port == 0 || io_port == 3) 90 gpiommgpio->control[control_port] |= BIT(4); 91 else 92 gpiommgpio->control[control_port] |= BIT(1); 93 } 94 95 control = BIT(7) | gpiommgpio->control[control_port]; 96 outb(control, control_addr); 97 98 spin_unlock_irqrestore(&gpiommgpio->lock, flags); 99 100 return 0; 101 } 102 103 static int gpiomm_gpio_direction_output(struct gpio_chip *chip, 104 unsigned int offset, int value) 105 { 106 struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); 107 const unsigned int io_port = offset / 8; 108 const unsigned int control_port = io_port / 3; 109 const unsigned int mask = BIT(offset % 8); 110 const unsigned int control_addr = gpiommgpio->base + 3 + control_port*4; 111 const unsigned int out_port = (io_port > 2) ? io_port + 1 : io_port; 112 unsigned long flags; 113 unsigned int control; 114 115 spin_lock_irqsave(&gpiommgpio->lock, flags); 116 117 /* Check if configuring Port C */ 118 if (io_port == 2 || io_port == 5) { 119 /* Port C can be configured by nibble */ 120 if (offset % 8 > 3) { 121 gpiommgpio->io_state[io_port] &= 0x0F; 122 gpiommgpio->control[control_port] &= ~BIT(3); 123 } else { 124 gpiommgpio->io_state[io_port] &= 0xF0; 125 gpiommgpio->control[control_port] &= ~BIT(0); 126 } 127 } else { 128 gpiommgpio->io_state[io_port] &= 0x00; 129 if (io_port == 0 || io_port == 3) 130 gpiommgpio->control[control_port] &= ~BIT(4); 131 else 132 gpiommgpio->control[control_port] &= ~BIT(1); 133 } 134 135 if (value) 136 gpiommgpio->out_state[io_port] |= mask; 137 else 138 gpiommgpio->out_state[io_port] &= ~mask; 139 140 control = BIT(7) | gpiommgpio->control[control_port]; 141 outb(control, control_addr); 142 143 outb(gpiommgpio->out_state[io_port], gpiommgpio->base + out_port); 144 145 spin_unlock_irqrestore(&gpiommgpio->lock, flags); 146 147 return 0; 148 } 149 150 static int gpiomm_gpio_get(struct gpio_chip *chip, unsigned int offset) 151 { 152 struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); 153 const unsigned int port = offset / 8; 154 const unsigned int mask = BIT(offset % 8); 155 const unsigned int in_port = (port > 2) ? port + 1 : port; 156 unsigned long flags; 157 unsigned int port_state; 158 159 spin_lock_irqsave(&gpiommgpio->lock, flags); 160 161 /* ensure that GPIO is set for input */ 162 if (!(gpiommgpio->io_state[port] & mask)) { 163 spin_unlock_irqrestore(&gpiommgpio->lock, flags); 164 return -EINVAL; 165 } 166 167 port_state = inb(gpiommgpio->base + in_port); 168 169 spin_unlock_irqrestore(&gpiommgpio->lock, flags); 170 171 return !!(port_state & mask); 172 } 173 174 static void gpiomm_gpio_set(struct gpio_chip *chip, unsigned int offset, 175 int value) 176 { 177 struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); 178 const unsigned int port = offset / 8; 179 const unsigned int mask = BIT(offset % 8); 180 const unsigned int out_port = (port > 2) ? port + 1 : port; 181 unsigned long flags; 182 183 spin_lock_irqsave(&gpiommgpio->lock, flags); 184 185 if (value) 186 gpiommgpio->out_state[port] |= mask; 187 else 188 gpiommgpio->out_state[port] &= ~mask; 189 190 outb(gpiommgpio->out_state[port], gpiommgpio->base + out_port); 191 192 spin_unlock_irqrestore(&gpiommgpio->lock, flags); 193 } 194 195 static int gpiomm_probe(struct device *dev, unsigned int id) 196 { 197 struct gpiomm_gpio *gpiommgpio; 198 const char *const name = dev_name(dev); 199 int err; 200 201 gpiommgpio = devm_kzalloc(dev, sizeof(*gpiommgpio), GFP_KERNEL); 202 if (!gpiommgpio) 203 return -ENOMEM; 204 205 if (!devm_request_region(dev, base[id], GPIOMM_EXTENT, name)) { 206 dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", 207 base[id], base[id] + GPIOMM_EXTENT); 208 return -EBUSY; 209 } 210 211 gpiommgpio->chip.label = name; 212 gpiommgpio->chip.parent = dev; 213 gpiommgpio->chip.owner = THIS_MODULE; 214 gpiommgpio->chip.base = -1; 215 gpiommgpio->chip.ngpio = 48; 216 gpiommgpio->chip.get_direction = gpiomm_gpio_get_direction; 217 gpiommgpio->chip.direction_input = gpiomm_gpio_direction_input; 218 gpiommgpio->chip.direction_output = gpiomm_gpio_direction_output; 219 gpiommgpio->chip.get = gpiomm_gpio_get; 220 gpiommgpio->chip.set = gpiomm_gpio_set; 221 gpiommgpio->base = base[id]; 222 223 spin_lock_init(&gpiommgpio->lock); 224 225 dev_set_drvdata(dev, gpiommgpio); 226 227 err = gpiochip_add_data(&gpiommgpio->chip, gpiommgpio); 228 if (err) { 229 dev_err(dev, "GPIO registering failed (%d)\n", err); 230 return err; 231 } 232 233 /* initialize all GPIO as output */ 234 outb(0x80, base[id] + 3); 235 outb(0x00, base[id]); 236 outb(0x00, base[id] + 1); 237 outb(0x00, base[id] + 2); 238 outb(0x80, base[id] + 7); 239 outb(0x00, base[id] + 4); 240 outb(0x00, base[id] + 5); 241 outb(0x00, base[id] + 6); 242 243 return 0; 244 } 245 246 static int gpiomm_remove(struct device *dev, unsigned int id) 247 { 248 struct gpiomm_gpio *const gpiommgpio = dev_get_drvdata(dev); 249 250 gpiochip_remove(&gpiommgpio->chip); 251 252 return 0; 253 } 254 255 static struct isa_driver gpiomm_driver = { 256 .probe = gpiomm_probe, 257 .driver = { 258 .name = "gpio-mm" 259 }, 260 .remove = gpiomm_remove 261 }; 262 263 module_isa_driver(gpiomm_driver, num_gpiomm); 264 265 MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); 266 MODULE_DESCRIPTION("Diamond Systems GPIO-MM GPIO driver"); 267 MODULE_LICENSE("GPL v2"); 268