.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * GPIO driver for the WinSystems WS16C48 |
---|
3 | 4 | * 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 | 5 | */ |
---|
14 | 6 | #include <linux/bitmap.h> |
---|
15 | 7 | #include <linux/bitops.h> |
---|
.. | .. |
---|
64 | 56 | const unsigned port = offset / 8; |
---|
65 | 57 | const unsigned mask = BIT(offset % 8); |
---|
66 | 58 | |
---|
67 | | - return !!(ws16c48gpio->io_state[port] & mask); |
---|
| 59 | + if (ws16c48gpio->io_state[port] & mask) |
---|
| 60 | + return GPIO_LINE_DIRECTION_IN; |
---|
| 61 | + |
---|
| 62 | + return GPIO_LINE_DIRECTION_OUT; |
---|
68 | 63 | } |
---|
69 | 64 | |
---|
70 | 65 | static int ws16c48_gpio_direction_input(struct gpio_chip *chip, unsigned offset) |
---|
.. | .. |
---|
134 | 129 | unsigned long *mask, unsigned long *bits) |
---|
135 | 130 | { |
---|
136 | 131 | struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); |
---|
137 | | - const unsigned int gpio_reg_size = 8; |
---|
138 | | - size_t i; |
---|
139 | | - const size_t num_ports = chip->ngpio / gpio_reg_size; |
---|
140 | | - unsigned int bits_offset; |
---|
141 | | - size_t word_index; |
---|
142 | | - unsigned int word_offset; |
---|
143 | | - unsigned long word_mask; |
---|
144 | | - const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0); |
---|
| 132 | + unsigned long offset; |
---|
| 133 | + unsigned long gpio_mask; |
---|
| 134 | + unsigned int port_addr; |
---|
145 | 135 | unsigned long port_state; |
---|
146 | 136 | |
---|
147 | 137 | /* clear bits array to a clean slate */ |
---|
148 | 138 | bitmap_zero(bits, chip->ngpio); |
---|
149 | 139 | |
---|
150 | | - /* get bits are evaluated a gpio port register at a time */ |
---|
151 | | - for (i = 0; i < num_ports; i++) { |
---|
152 | | - /* gpio offset in bits array */ |
---|
153 | | - bits_offset = i * gpio_reg_size; |
---|
| 140 | + for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) { |
---|
| 141 | + port_addr = ws16c48gpio->base + offset / 8; |
---|
| 142 | + port_state = inb(port_addr) & gpio_mask; |
---|
154 | 143 | |
---|
155 | | - /* word index for bits array */ |
---|
156 | | - word_index = BIT_WORD(bits_offset); |
---|
157 | | - |
---|
158 | | - /* gpio offset within current word of bits array */ |
---|
159 | | - word_offset = bits_offset % BITS_PER_LONG; |
---|
160 | | - |
---|
161 | | - /* mask of get bits for current gpio within current word */ |
---|
162 | | - word_mask = mask[word_index] & (port_mask << word_offset); |
---|
163 | | - if (!word_mask) { |
---|
164 | | - /* no get bits in this port so skip to next one */ |
---|
165 | | - continue; |
---|
166 | | - } |
---|
167 | | - |
---|
168 | | - /* read bits from current gpio port */ |
---|
169 | | - port_state = inb(ws16c48gpio->base + i); |
---|
170 | | - |
---|
171 | | - /* store acquired bits at respective bits array offset */ |
---|
172 | | - bits[word_index] |= port_state << word_offset; |
---|
| 144 | + bitmap_set_value8(bits, port_state, offset); |
---|
173 | 145 | } |
---|
174 | 146 | |
---|
175 | 147 | return 0; |
---|
.. | .. |
---|
203 | 175 | unsigned long *mask, unsigned long *bits) |
---|
204 | 176 | { |
---|
205 | 177 | struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); |
---|
206 | | - unsigned int i; |
---|
207 | | - const unsigned int gpio_reg_size = 8; |
---|
208 | | - unsigned int port; |
---|
209 | | - unsigned int iomask; |
---|
210 | | - unsigned int bitmask; |
---|
| 178 | + unsigned long offset; |
---|
| 179 | + unsigned long gpio_mask; |
---|
| 180 | + size_t index; |
---|
| 181 | + unsigned int port_addr; |
---|
| 182 | + unsigned long bitmask; |
---|
211 | 183 | unsigned long flags; |
---|
212 | 184 | |
---|
213 | | - /* set bits are evaluated a gpio register size at a time */ |
---|
214 | | - for (i = 0; i < chip->ngpio; i += gpio_reg_size) { |
---|
215 | | - /* no more set bits in this mask word; skip to the next word */ |
---|
216 | | - if (!mask[BIT_WORD(i)]) { |
---|
217 | | - i = (BIT_WORD(i) + 1) * BITS_PER_LONG - gpio_reg_size; |
---|
218 | | - continue; |
---|
219 | | - } |
---|
220 | | - |
---|
221 | | - port = i / gpio_reg_size; |
---|
| 185 | + for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) { |
---|
| 186 | + index = offset / 8; |
---|
| 187 | + port_addr = ws16c48gpio->base + index; |
---|
222 | 188 | |
---|
223 | 189 | /* mask out GPIO configured for input */ |
---|
224 | | - iomask = mask[BIT_WORD(i)] & ~ws16c48gpio->io_state[port]; |
---|
225 | | - bitmask = iomask & bits[BIT_WORD(i)]; |
---|
| 190 | + gpio_mask &= ~ws16c48gpio->io_state[index]; |
---|
| 191 | + bitmask = bitmap_get_value8(bits, offset) & gpio_mask; |
---|
226 | 192 | |
---|
227 | 193 | raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); |
---|
228 | 194 | |
---|
229 | 195 | /* update output state data and set device gpio register */ |
---|
230 | | - ws16c48gpio->out_state[port] &= ~iomask; |
---|
231 | | - ws16c48gpio->out_state[port] |= bitmask; |
---|
232 | | - outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port); |
---|
| 196 | + ws16c48gpio->out_state[index] &= ~gpio_mask; |
---|
| 197 | + ws16c48gpio->out_state[index] |= bitmask; |
---|
| 198 | + outb(ws16c48gpio->out_state[index], port_addr); |
---|
233 | 199 | |
---|
234 | 200 | raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); |
---|
235 | | - |
---|
236 | | - /* prepare for next gpio register set */ |
---|
237 | | - mask[BIT_WORD(i)] >>= gpio_reg_size; |
---|
238 | | - bits[BIT_WORD(i)] >>= gpio_reg_size; |
---|
239 | 201 | } |
---|
240 | 202 | } |
---|
241 | 203 | |
---|
.. | .. |
---|
403 | 365 | "Port 5 Bit 4", "Port 5 Bit 5", "Port 5 Bit 6", "Port 5 Bit 7" |
---|
404 | 366 | }; |
---|
405 | 367 | |
---|
| 368 | +static int ws16c48_irq_init_hw(struct gpio_chip *gc) |
---|
| 369 | +{ |
---|
| 370 | + struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(gc); |
---|
| 371 | + |
---|
| 372 | + /* Disable IRQ by default */ |
---|
| 373 | + outb(0x80, ws16c48gpio->base + 7); |
---|
| 374 | + outb(0, ws16c48gpio->base + 8); |
---|
| 375 | + outb(0, ws16c48gpio->base + 9); |
---|
| 376 | + outb(0, ws16c48gpio->base + 10); |
---|
| 377 | + outb(0xC0, ws16c48gpio->base + 7); |
---|
| 378 | + |
---|
| 379 | + return 0; |
---|
| 380 | +} |
---|
| 381 | + |
---|
406 | 382 | static int ws16c48_probe(struct device *dev, unsigned int id) |
---|
407 | 383 | { |
---|
408 | 384 | struct ws16c48_gpio *ws16c48gpio; |
---|
409 | 385 | const char *const name = dev_name(dev); |
---|
| 386 | + struct gpio_irq_chip *girq; |
---|
410 | 387 | int err; |
---|
411 | 388 | |
---|
412 | 389 | ws16c48gpio = devm_kzalloc(dev, sizeof(*ws16c48gpio), GFP_KERNEL); |
---|
.. | .. |
---|
434 | 411 | ws16c48gpio->chip.set_multiple = ws16c48_gpio_set_multiple; |
---|
435 | 412 | ws16c48gpio->base = base[id]; |
---|
436 | 413 | |
---|
| 414 | + girq = &ws16c48gpio->chip.irq; |
---|
| 415 | + girq->chip = &ws16c48_irqchip; |
---|
| 416 | + /* This will let us handle the parent IRQ in the driver */ |
---|
| 417 | + girq->parent_handler = NULL; |
---|
| 418 | + girq->num_parents = 0; |
---|
| 419 | + girq->parents = NULL; |
---|
| 420 | + girq->default_type = IRQ_TYPE_NONE; |
---|
| 421 | + girq->handler = handle_edge_irq; |
---|
| 422 | + girq->init_hw = ws16c48_irq_init_hw; |
---|
| 423 | + |
---|
437 | 424 | raw_spin_lock_init(&ws16c48gpio->lock); |
---|
438 | 425 | |
---|
439 | 426 | err = devm_gpiochip_add_data(dev, &ws16c48gpio->chip, ws16c48gpio); |
---|
440 | 427 | if (err) { |
---|
441 | 428 | dev_err(dev, "GPIO registering failed (%d)\n", err); |
---|
442 | | - return err; |
---|
443 | | - } |
---|
444 | | - |
---|
445 | | - /* Disable IRQ by default */ |
---|
446 | | - outb(0x80, base[id] + 7); |
---|
447 | | - outb(0, base[id] + 8); |
---|
448 | | - outb(0, base[id] + 9); |
---|
449 | | - outb(0, base[id] + 10); |
---|
450 | | - outb(0xC0, base[id] + 7); |
---|
451 | | - |
---|
452 | | - err = gpiochip_irqchip_add(&ws16c48gpio->chip, &ws16c48_irqchip, 0, |
---|
453 | | - handle_edge_irq, IRQ_TYPE_NONE); |
---|
454 | | - if (err) { |
---|
455 | | - dev_err(dev, "Could not add irqchip (%d)\n", err); |
---|
456 | 429 | return err; |
---|
457 | 430 | } |
---|
458 | 431 | |
---|