| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * GPIO driver for the ACCES PCI-IDIO-16 |
|---|
| 3 | 4 | * Copyright (C) 2017 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 | 5 | */ |
|---|
| 14 | 6 | #include <linux/bitmap.h> |
|---|
| 15 | 7 | #include <linux/bitops.h> |
|---|
| .. | .. |
|---|
| 69 | 61 | unsigned int offset) |
|---|
| 70 | 62 | { |
|---|
| 71 | 63 | if (offset > 15) |
|---|
| 72 | | - return 1; |
|---|
| 64 | + return GPIO_LINE_DIRECTION_IN; |
|---|
| 73 | 65 | |
|---|
| 74 | | - return 0; |
|---|
| 66 | + return GPIO_LINE_DIRECTION_OUT; |
|---|
| 75 | 67 | } |
|---|
| 76 | 68 | |
|---|
| 77 | 69 | static int idio_16_gpio_direction_input(struct gpio_chip *chip, |
|---|
| .. | .. |
|---|
| 108 | 100 | unsigned long *mask, unsigned long *bits) |
|---|
| 109 | 101 | { |
|---|
| 110 | 102 | struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); |
|---|
| 111 | | - size_t i; |
|---|
| 112 | | - const unsigned int gpio_reg_size = 8; |
|---|
| 113 | | - unsigned int bits_offset; |
|---|
| 114 | | - size_t word_index; |
|---|
| 115 | | - unsigned int word_offset; |
|---|
| 116 | | - unsigned long word_mask; |
|---|
| 117 | | - const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0); |
|---|
| 118 | | - unsigned long port_state; |
|---|
| 103 | + unsigned long offset; |
|---|
| 104 | + unsigned long gpio_mask; |
|---|
| 119 | 105 | void __iomem *ports[] = { |
|---|
| 120 | 106 | &idio16gpio->reg->out0_7, &idio16gpio->reg->out8_15, |
|---|
| 121 | 107 | &idio16gpio->reg->in0_7, &idio16gpio->reg->in8_15, |
|---|
| 122 | 108 | }; |
|---|
| 109 | + void __iomem *port_addr; |
|---|
| 110 | + unsigned long port_state; |
|---|
| 123 | 111 | |
|---|
| 124 | 112 | /* clear bits array to a clean slate */ |
|---|
| 125 | 113 | bitmap_zero(bits, chip->ngpio); |
|---|
| 126 | 114 | |
|---|
| 127 | | - /* get bits are evaluated a gpio port register at a time */ |
|---|
| 128 | | - for (i = 0; i < ARRAY_SIZE(ports); i++) { |
|---|
| 129 | | - /* gpio offset in bits array */ |
|---|
| 130 | | - bits_offset = i * gpio_reg_size; |
|---|
| 115 | + for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) { |
|---|
| 116 | + port_addr = ports[offset / 8]; |
|---|
| 117 | + port_state = ioread8(port_addr) & gpio_mask; |
|---|
| 131 | 118 | |
|---|
| 132 | | - /* word index for bits array */ |
|---|
| 133 | | - word_index = BIT_WORD(bits_offset); |
|---|
| 134 | | - |
|---|
| 135 | | - /* gpio offset within current word of bits array */ |
|---|
| 136 | | - word_offset = bits_offset % BITS_PER_LONG; |
|---|
| 137 | | - |
|---|
| 138 | | - /* mask of get bits for current gpio within current word */ |
|---|
| 139 | | - word_mask = mask[word_index] & (port_mask << word_offset); |
|---|
| 140 | | - if (!word_mask) { |
|---|
| 141 | | - /* no get bits in this port so skip to next one */ |
|---|
| 142 | | - continue; |
|---|
| 143 | | - } |
|---|
| 144 | | - |
|---|
| 145 | | - /* read bits from current gpio port */ |
|---|
| 146 | | - port_state = ioread8(ports[i]); |
|---|
| 147 | | - |
|---|
| 148 | | - /* store acquired bits at respective bits array offset */ |
|---|
| 149 | | - bits[word_index] |= port_state << word_offset; |
|---|
| 119 | + bitmap_set_value8(bits, port_state, offset); |
|---|
| 150 | 120 | } |
|---|
| 151 | 121 | |
|---|
| 152 | 122 | return 0; |
|---|
| .. | .. |
|---|
| 186 | 156 | unsigned long *mask, unsigned long *bits) |
|---|
| 187 | 157 | { |
|---|
| 188 | 158 | struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); |
|---|
| 159 | + unsigned long offset; |
|---|
| 160 | + unsigned long gpio_mask; |
|---|
| 161 | + void __iomem *ports[] = { |
|---|
| 162 | + &idio16gpio->reg->out0_7, &idio16gpio->reg->out8_15, |
|---|
| 163 | + }; |
|---|
| 164 | + size_t index; |
|---|
| 165 | + void __iomem *port_addr; |
|---|
| 166 | + unsigned long bitmask; |
|---|
| 189 | 167 | unsigned long flags; |
|---|
| 190 | | - unsigned int out_state; |
|---|
| 168 | + unsigned long out_state; |
|---|
| 191 | 169 | |
|---|
| 192 | | - raw_spin_lock_irqsave(&idio16gpio->lock, flags); |
|---|
| 170 | + for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) { |
|---|
| 171 | + index = offset / 8; |
|---|
| 172 | + port_addr = ports[index]; |
|---|
| 193 | 173 | |
|---|
| 194 | | - /* process output lines 0-7 */ |
|---|
| 195 | | - if (*mask & 0xFF) { |
|---|
| 196 | | - out_state = ioread8(&idio16gpio->reg->out0_7) & ~*mask; |
|---|
| 197 | | - out_state |= *mask & *bits; |
|---|
| 198 | | - iowrite8(out_state, &idio16gpio->reg->out0_7); |
|---|
| 174 | + bitmask = bitmap_get_value8(bits, offset) & gpio_mask; |
|---|
| 175 | + |
|---|
| 176 | + raw_spin_lock_irqsave(&idio16gpio->lock, flags); |
|---|
| 177 | + |
|---|
| 178 | + out_state = ioread8(port_addr) & ~gpio_mask; |
|---|
| 179 | + out_state |= bitmask; |
|---|
| 180 | + iowrite8(out_state, port_addr); |
|---|
| 181 | + |
|---|
| 182 | + raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); |
|---|
| 199 | 183 | } |
|---|
| 200 | | - |
|---|
| 201 | | - /* shift to next output line word */ |
|---|
| 202 | | - *mask >>= 8; |
|---|
| 203 | | - |
|---|
| 204 | | - /* process output lines 8-15 */ |
|---|
| 205 | | - if (*mask & 0xFF) { |
|---|
| 206 | | - *bits >>= 8; |
|---|
| 207 | | - out_state = ioread8(&idio16gpio->reg->out8_15) & ~*mask; |
|---|
| 208 | | - out_state |= *mask & *bits; |
|---|
| 209 | | - iowrite8(out_state, &idio16gpio->reg->out8_15); |
|---|
| 210 | | - } |
|---|
| 211 | | - |
|---|
| 212 | | - raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); |
|---|
| 213 | 184 | } |
|---|
| 214 | 185 | |
|---|
| 215 | 186 | static void idio_16_irq_ack(struct irq_data *data) |
|---|
| .. | .. |
|---|
| 309 | 280 | "IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15" |
|---|
| 310 | 281 | }; |
|---|
| 311 | 282 | |
|---|
| 283 | +static int idio_16_irq_init_hw(struct gpio_chip *gc) |
|---|
| 284 | +{ |
|---|
| 285 | + struct idio_16_gpio *const idio16gpio = gpiochip_get_data(gc); |
|---|
| 286 | + |
|---|
| 287 | + /* Disable IRQ by default and clear any pending interrupt */ |
|---|
| 288 | + iowrite8(0, &idio16gpio->reg->irq_ctl); |
|---|
| 289 | + iowrite8(0, &idio16gpio->reg->in0_7); |
|---|
| 290 | + |
|---|
| 291 | + return 0; |
|---|
| 292 | +} |
|---|
| 293 | + |
|---|
| 312 | 294 | static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id) |
|---|
| 313 | 295 | { |
|---|
| 314 | 296 | struct device *const dev = &pdev->dev; |
|---|
| .. | .. |
|---|
| 316 | 298 | int err; |
|---|
| 317 | 299 | const size_t pci_bar_index = 2; |
|---|
| 318 | 300 | const char *const name = pci_name(pdev); |
|---|
| 301 | + struct gpio_irq_chip *girq; |
|---|
| 319 | 302 | |
|---|
| 320 | 303 | idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL); |
|---|
| 321 | 304 | if (!idio16gpio) |
|---|
| .. | .. |
|---|
| 352 | 335 | idio16gpio->chip.set = idio_16_gpio_set; |
|---|
| 353 | 336 | idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple; |
|---|
| 354 | 337 | |
|---|
| 338 | + girq = &idio16gpio->chip.irq; |
|---|
| 339 | + girq->chip = &idio_16_irqchip; |
|---|
| 340 | + /* This will let us handle the parent IRQ in the driver */ |
|---|
| 341 | + girq->parent_handler = NULL; |
|---|
| 342 | + girq->num_parents = 0; |
|---|
| 343 | + girq->parents = NULL; |
|---|
| 344 | + girq->default_type = IRQ_TYPE_NONE; |
|---|
| 345 | + girq->handler = handle_edge_irq; |
|---|
| 346 | + girq->init_hw = idio_16_irq_init_hw; |
|---|
| 347 | + |
|---|
| 355 | 348 | raw_spin_lock_init(&idio16gpio->lock); |
|---|
| 356 | 349 | |
|---|
| 357 | 350 | err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio); |
|---|
| 358 | 351 | if (err) { |
|---|
| 359 | 352 | dev_err(dev, "GPIO registering failed (%d)\n", err); |
|---|
| 360 | | - return err; |
|---|
| 361 | | - } |
|---|
| 362 | | - |
|---|
| 363 | | - /* Disable IRQ by default and clear any pending interrupt */ |
|---|
| 364 | | - iowrite8(0, &idio16gpio->reg->irq_ctl); |
|---|
| 365 | | - iowrite8(0, &idio16gpio->reg->in0_7); |
|---|
| 366 | | - |
|---|
| 367 | | - err = gpiochip_irqchip_add(&idio16gpio->chip, &idio_16_irqchip, 0, |
|---|
| 368 | | - handle_edge_irq, IRQ_TYPE_NONE); |
|---|
| 369 | | - if (err) { |
|---|
| 370 | | - dev_err(dev, "Could not add irqchip (%d)\n", err); |
|---|
| 371 | 353 | return err; |
|---|
| 372 | 354 | } |
|---|
| 373 | 355 | |
|---|