| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * GPIO driver for the ACCES 104-IDI-48 family |
|---|
| 3 | 4 | * Copyright (C) 2015 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 | * This driver supports the following ACCES devices: 104-IDI-48A, |
|---|
| 15 | 7 | * 104-IDI-48AC, 104-IDI-48B, and 104-IDI-48BC. |
|---|
| .. | .. |
|---|
| 61 | 53 | |
|---|
| 62 | 54 | static int idi_48_gpio_get_direction(struct gpio_chip *chip, unsigned offset) |
|---|
| 63 | 55 | { |
|---|
| 64 | | - return 1; |
|---|
| 56 | + return GPIO_LINE_DIRECTION_IN; |
|---|
| 65 | 57 | } |
|---|
| 66 | 58 | |
|---|
| 67 | 59 | static int idi_48_gpio_direction_input(struct gpio_chip *chip, unsigned offset) |
|---|
| .. | .. |
|---|
| 73 | 65 | { |
|---|
| 74 | 66 | struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip); |
|---|
| 75 | 67 | unsigned i; |
|---|
| 76 | | - const unsigned register_offset[6] = { 0, 1, 2, 4, 5, 6 }; |
|---|
| 68 | + static const unsigned int register_offset[6] = { 0, 1, 2, 4, 5, 6 }; |
|---|
| 77 | 69 | unsigned base_offset; |
|---|
| 78 | 70 | unsigned mask; |
|---|
| 79 | 71 | |
|---|
| .. | .. |
|---|
| 93 | 85 | unsigned long *bits) |
|---|
| 94 | 86 | { |
|---|
| 95 | 87 | struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip); |
|---|
| 96 | | - size_t i; |
|---|
| 88 | + unsigned long offset; |
|---|
| 89 | + unsigned long gpio_mask; |
|---|
| 97 | 90 | static const size_t ports[] = { 0, 1, 2, 4, 5, 6 }; |
|---|
| 98 | | - const unsigned int gpio_reg_size = 8; |
|---|
| 99 | | - unsigned int bits_offset; |
|---|
| 100 | | - size_t word_index; |
|---|
| 101 | | - unsigned int word_offset; |
|---|
| 102 | | - unsigned long word_mask; |
|---|
| 103 | | - const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0); |
|---|
| 91 | + unsigned int port_addr; |
|---|
| 104 | 92 | unsigned long port_state; |
|---|
| 105 | 93 | |
|---|
| 106 | 94 | /* clear bits array to a clean slate */ |
|---|
| 107 | 95 | bitmap_zero(bits, chip->ngpio); |
|---|
| 108 | 96 | |
|---|
| 109 | | - /* get bits are evaluated a gpio port register at a time */ |
|---|
| 110 | | - for (i = 0; i < ARRAY_SIZE(ports); i++) { |
|---|
| 111 | | - /* gpio offset in bits array */ |
|---|
| 112 | | - bits_offset = i * gpio_reg_size; |
|---|
| 97 | + for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) { |
|---|
| 98 | + port_addr = idi48gpio->base + ports[offset / 8]; |
|---|
| 99 | + port_state = inb(port_addr) & gpio_mask; |
|---|
| 113 | 100 | |
|---|
| 114 | | - /* word index for bits array */ |
|---|
| 115 | | - word_index = BIT_WORD(bits_offset); |
|---|
| 116 | | - |
|---|
| 117 | | - /* gpio offset within current word of bits array */ |
|---|
| 118 | | - word_offset = bits_offset % BITS_PER_LONG; |
|---|
| 119 | | - |
|---|
| 120 | | - /* mask of get bits for current gpio within current word */ |
|---|
| 121 | | - word_mask = mask[word_index] & (port_mask << word_offset); |
|---|
| 122 | | - if (!word_mask) { |
|---|
| 123 | | - /* no get bits in this port so skip to next one */ |
|---|
| 124 | | - continue; |
|---|
| 125 | | - } |
|---|
| 126 | | - |
|---|
| 127 | | - /* read bits from current gpio port */ |
|---|
| 128 | | - port_state = inb(idi48gpio->base + ports[i]); |
|---|
| 129 | | - |
|---|
| 130 | | - /* store acquired bits at respective bits array offset */ |
|---|
| 131 | | - bits[word_index] |= port_state << word_offset; |
|---|
| 101 | + bitmap_set_value8(bits, port_state, offset); |
|---|
| 132 | 102 | } |
|---|
| 133 | 103 | |
|---|
| 134 | 104 | return 0; |
|---|
| .. | .. |
|---|
| 277 | 247 | "Bit 18 B", "Bit 19 B", "Bit 20 B", "Bit 21 B", "Bit 22 B", "Bit 23 B" |
|---|
| 278 | 248 | }; |
|---|
| 279 | 249 | |
|---|
| 250 | +static int idi_48_irq_init_hw(struct gpio_chip *gc) |
|---|
| 251 | +{ |
|---|
| 252 | + struct idi_48_gpio *const idi48gpio = gpiochip_get_data(gc); |
|---|
| 253 | + |
|---|
| 254 | + /* Disable IRQ by default */ |
|---|
| 255 | + outb(0, idi48gpio->base + 7); |
|---|
| 256 | + inb(idi48gpio->base + 7); |
|---|
| 257 | + |
|---|
| 258 | + return 0; |
|---|
| 259 | +} |
|---|
| 260 | + |
|---|
| 280 | 261 | static int idi_48_probe(struct device *dev, unsigned int id) |
|---|
| 281 | 262 | { |
|---|
| 282 | 263 | struct idi_48_gpio *idi48gpio; |
|---|
| 283 | 264 | const char *const name = dev_name(dev); |
|---|
| 265 | + struct gpio_irq_chip *girq; |
|---|
| 284 | 266 | int err; |
|---|
| 285 | 267 | |
|---|
| 286 | 268 | idi48gpio = devm_kzalloc(dev, sizeof(*idi48gpio), GFP_KERNEL); |
|---|
| .. | .. |
|---|
| 305 | 287 | idi48gpio->chip.get_multiple = idi_48_gpio_get_multiple; |
|---|
| 306 | 288 | idi48gpio->base = base[id]; |
|---|
| 307 | 289 | |
|---|
| 290 | + girq = &idi48gpio->chip.irq; |
|---|
| 291 | + girq->chip = &idi_48_irqchip; |
|---|
| 292 | + /* This will let us handle the parent IRQ in the driver */ |
|---|
| 293 | + girq->parent_handler = NULL; |
|---|
| 294 | + girq->num_parents = 0; |
|---|
| 295 | + girq->parents = NULL; |
|---|
| 296 | + girq->default_type = IRQ_TYPE_NONE; |
|---|
| 297 | + girq->handler = handle_edge_irq; |
|---|
| 298 | + girq->init_hw = idi_48_irq_init_hw; |
|---|
| 299 | + |
|---|
| 308 | 300 | raw_spin_lock_init(&idi48gpio->lock); |
|---|
| 309 | 301 | spin_lock_init(&idi48gpio->ack_lock); |
|---|
| 310 | 302 | |
|---|
| 311 | 303 | err = devm_gpiochip_add_data(dev, &idi48gpio->chip, idi48gpio); |
|---|
| 312 | 304 | if (err) { |
|---|
| 313 | 305 | dev_err(dev, "GPIO registering failed (%d)\n", err); |
|---|
| 314 | | - return err; |
|---|
| 315 | | - } |
|---|
| 316 | | - |
|---|
| 317 | | - /* Disable IRQ by default */ |
|---|
| 318 | | - outb(0, base[id] + 7); |
|---|
| 319 | | - inb(base[id] + 7); |
|---|
| 320 | | - |
|---|
| 321 | | - err = gpiochip_irqchip_add(&idi48gpio->chip, &idi_48_irqchip, 0, |
|---|
| 322 | | - handle_edge_irq, IRQ_TYPE_NONE); |
|---|
| 323 | | - if (err) { |
|---|
| 324 | | - dev_err(dev, "Could not add irqchip (%d)\n", err); |
|---|
| 325 | 306 | return err; |
|---|
| 326 | 307 | } |
|---|
| 327 | 308 | |
|---|