Blame view

drivers/gpio/gpio-104-idio-16.c 7.19 KB
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
1
2
3
4
5
6
7
8
9
10
11
12
  /*
   * GPIO driver for the ACCES 104-IDIO-16 family
   * Copyright (C) 2015 William Breathitt Gray
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License, version 2, as
   * published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * General Public License for more details.
86ea8a95a   William Breathitt Gray   gpio: 104-idio-16...
13
14
15
   *
   * This driver supports the following ACCES devices: 104-IDIO-16,
   * 104-IDIO-16E, 104-IDO-16, 104-IDIO-8, 104-IDIO-8E, and 104-IDO-8.
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
16
   */
a11841477   William Breathitt Gray   gpio: Add IRQ sup...
17
  #include <linux/bitops.h>
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
18
19
20
21
22
  #include <linux/device.h>
  #include <linux/errno.h>
  #include <linux/gpio/driver.h>
  #include <linux/io.h>
  #include <linux/ioport.h>
a11841477   William Breathitt Gray   gpio: Add IRQ sup...
23
24
  #include <linux/interrupt.h>
  #include <linux/irqdesc.h>
86ea8a95a   William Breathitt Gray   gpio: 104-idio-16...
25
  #include <linux/isa.h>
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
26
27
28
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/moduleparam.h>
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
29
  #include <linux/spinlock.h>
86ea8a95a   William Breathitt Gray   gpio: 104-idio-16...
30
31
32
33
34
35
36
37
38
39
40
  #define IDIO_16_EXTENT 8
  #define MAX_NUM_IDIO_16 max_num_isa_dev(IDIO_16_EXTENT)
  
  static unsigned int base[MAX_NUM_IDIO_16];
  static unsigned int num_idio_16;
  module_param_array(base, uint, &num_idio_16, 0);
  MODULE_PARM_DESC(base, "ACCES 104-IDIO-16 base addresses");
  
  static unsigned int irq[MAX_NUM_IDIO_16];
  module_param_array(irq, uint, NULL, 0);
  MODULE_PARM_DESC(irq, "ACCES 104-IDIO-16 interrupt line numbers");
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
41
42
43
44
  
  /**
   * struct idio_16_gpio - GPIO device private data structure
   * @chip:	instance of the gpio_chip
a11841477   William Breathitt Gray   gpio: Add IRQ sup...
45
46
   * @lock:	synchronization lock to prevent I/O race conditions
   * @irq_mask:	I/O bits affected by interrupts
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
47
   * @base:	base port address of the GPIO device
a11841477   William Breathitt Gray   gpio: Add IRQ sup...
48
   * @irq:	Interrupt line number
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
49
50
51
52
53
   * @out_state:	output bits state
   */
  struct idio_16_gpio {
  	struct gpio_chip chip;
  	spinlock_t lock;
a11841477   William Breathitt Gray   gpio: Add IRQ sup...
54
  	unsigned long irq_mask;
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
55
  	unsigned base;
a11841477   William Breathitt Gray   gpio: Add IRQ sup...
56
  	unsigned irq;
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
  	unsigned out_state;
  };
  
  static int idio_16_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
  {
  	if (offset > 15)
  		return 1;
  
  	return 0;
  }
  
  static int idio_16_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
  {
  	return 0;
  }
  
  static int idio_16_gpio_direction_output(struct gpio_chip *chip,
  	unsigned offset, int value)
  {
  	chip->set(chip, offset, value);
  	return 0;
  }
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
79
80
  static int idio_16_gpio_get(struct gpio_chip *chip, unsigned offset)
  {
d602ae90a   Linus Walleij   gpio: 104-idio-16...
81
  	struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
6e0171b40   William Breathitt Gray   gpio: 104-idio-16...
82
  	const unsigned mask = BIT(offset-16);
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
83
84
85
86
87
  
  	if (offset < 16)
  		return -EINVAL;
  
  	if (offset < 24)
6e0171b40   William Breathitt Gray   gpio: 104-idio-16...
88
  		return !!(inb(idio16gpio->base + 1) & mask);
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
89

6e0171b40   William Breathitt Gray   gpio: 104-idio-16...
90
  	return !!(inb(idio16gpio->base + 5) & (mask>>8));
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
91
92
93
94
  }
  
  static void idio_16_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
  {
d602ae90a   Linus Walleij   gpio: 104-idio-16...
95
  	struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
6e0171b40   William Breathitt Gray   gpio: 104-idio-16...
96
  	const unsigned mask = BIT(offset);
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
97
98
99
100
101
102
103
104
  	unsigned long flags;
  
  	if (offset > 15)
  		return;
  
  	spin_lock_irqsave(&idio16gpio->lock, flags);
  
  	if (value)
6e0171b40   William Breathitt Gray   gpio: 104-idio-16...
105
  		idio16gpio->out_state |= mask;
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
106
  	else
6e0171b40   William Breathitt Gray   gpio: 104-idio-16...
107
  		idio16gpio->out_state &= ~mask;
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
108
109
110
111
112
113
114
115
  
  	if (offset > 7)
  		outb(idio16gpio->out_state >> 8, idio16gpio->base + 4);
  	else
  		outb(idio16gpio->out_state, idio16gpio->base);
  
  	spin_unlock_irqrestore(&idio16gpio->lock, flags);
  }
a11841477   William Breathitt Gray   gpio: Add IRQ sup...
116
117
  static void idio_16_irq_ack(struct irq_data *data)
  {
a11841477   William Breathitt Gray   gpio: Add IRQ sup...
118
119
120
121
122
  }
  
  static void idio_16_irq_mask(struct irq_data *data)
  {
  	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
d602ae90a   Linus Walleij   gpio: 104-idio-16...
123
  	struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
a11841477   William Breathitt Gray   gpio: Add IRQ sup...
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
  	const unsigned long mask = BIT(irqd_to_hwirq(data));
  	unsigned long flags;
  
  	idio16gpio->irq_mask &= ~mask;
  
  	if (!idio16gpio->irq_mask) {
  		spin_lock_irqsave(&idio16gpio->lock, flags);
  
  		outb(0, idio16gpio->base + 2);
  
  		spin_unlock_irqrestore(&idio16gpio->lock, flags);
  	}
  }
  
  static void idio_16_irq_unmask(struct irq_data *data)
  {
  	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
d602ae90a   Linus Walleij   gpio: 104-idio-16...
141
  	struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
a11841477   William Breathitt Gray   gpio: Add IRQ sup...
142
143
144
145
146
147
148
149
  	const unsigned long mask = BIT(irqd_to_hwirq(data));
  	const unsigned long prev_irq_mask = idio16gpio->irq_mask;
  	unsigned long flags;
  
  	idio16gpio->irq_mask |= mask;
  
  	if (!prev_irq_mask) {
  		spin_lock_irqsave(&idio16gpio->lock, flags);
a11841477   William Breathitt Gray   gpio: Add IRQ sup...
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
  		inb(idio16gpio->base + 2);
  
  		spin_unlock_irqrestore(&idio16gpio->lock, flags);
  	}
  }
  
  static int idio_16_irq_set_type(struct irq_data *data, unsigned flow_type)
  {
  	/* The only valid irq types are none and both-edges */
  	if (flow_type != IRQ_TYPE_NONE &&
  		(flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
  		return -EINVAL;
  
  	return 0;
  }
  
  static struct irq_chip idio_16_irqchip = {
  	.name = "104-idio-16",
  	.irq_ack = idio_16_irq_ack,
  	.irq_mask = idio_16_irq_mask,
  	.irq_unmask = idio_16_irq_unmask,
  	.irq_set_type = idio_16_irq_set_type
  };
  
  static irqreturn_t idio_16_irq_handler(int irq, void *dev_id)
  {
  	struct idio_16_gpio *const idio16gpio = dev_id;
  	struct gpio_chip *const chip = &idio16gpio->chip;
  	int gpio;
  
  	for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio)
  		generic_handle_irq(irq_find_mapping(chip->irqdomain, gpio));
12b61c9d7   William Breathitt Gray   gpio: 104-idio-16...
182
183
184
185
186
  	spin_lock(&idio16gpio->lock);
  
  	outb(0, idio16gpio->base + 1);
  
  	spin_unlock(&idio16gpio->lock);
a11841477   William Breathitt Gray   gpio: Add IRQ sup...
187
188
  	return IRQ_HANDLED;
  }
86ea8a95a   William Breathitt Gray   gpio: 104-idio-16...
189
  static int idio_16_probe(struct device *dev, unsigned int id)
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
190
  {
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
191
  	struct idio_16_gpio *idio16gpio;
6e0171b40   William Breathitt Gray   gpio: 104-idio-16...
192
  	const char *const name = dev_name(dev);
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
193
  	int err;
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
194
195
196
197
  
  	idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL);
  	if (!idio16gpio)
  		return -ENOMEM;
86ea8a95a   William Breathitt Gray   gpio: 104-idio-16...
198
  	if (!devm_request_region(dev, base[id], IDIO_16_EXTENT, name)) {
cb32389cb   William Breathitt Gray   gpio: 104-idio-16...
199
200
  		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)
  ",
86ea8a95a   William Breathitt Gray   gpio: 104-idio-16...
201
  			base[id], base[id] + IDIO_16_EXTENT);
cb32389cb   William Breathitt Gray   gpio: 104-idio-16...
202
  		return -EBUSY;
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
203
  	}
6e0171b40   William Breathitt Gray   gpio: 104-idio-16...
204
  	idio16gpio->chip.label = name;
58383c784   Linus Walleij   gpio: change memb...
205
  	idio16gpio->chip.parent = dev;
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
206
207
208
209
210
211
212
213
  	idio16gpio->chip.owner = THIS_MODULE;
  	idio16gpio->chip.base = -1;
  	idio16gpio->chip.ngpio = 32;
  	idio16gpio->chip.get_direction = idio_16_gpio_get_direction;
  	idio16gpio->chip.direction_input = idio_16_gpio_direction_input;
  	idio16gpio->chip.direction_output = idio_16_gpio_direction_output;
  	idio16gpio->chip.get = idio_16_gpio_get;
  	idio16gpio->chip.set = idio_16_gpio_set;
86ea8a95a   William Breathitt Gray   gpio: 104-idio-16...
214
215
  	idio16gpio->base = base[id];
  	idio16gpio->irq = irq[id];
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
216
217
218
219
220
  	idio16gpio->out_state = 0xFFFF;
  
  	spin_lock_init(&idio16gpio->lock);
  
  	dev_set_drvdata(dev, idio16gpio);
d602ae90a   Linus Walleij   gpio: 104-idio-16...
221
  	err = gpiochip_add_data(&idio16gpio->chip, idio16gpio);
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
222
223
224
  	if (err) {
  		dev_err(dev, "GPIO registering failed (%d)
  ", err);
cb32389cb   William Breathitt Gray   gpio: 104-idio-16...
225
  		return err;
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
226
  	}
fb50cdfee   William Breathitt Gray   gpio: 104-idio-16...
227
  	/* Disable IRQ by default */
86ea8a95a   William Breathitt Gray   gpio: 104-idio-16...
228
229
  	outb(0, base[id] + 2);
  	outb(0, base[id] + 1);
fb50cdfee   William Breathitt Gray   gpio: 104-idio-16...
230

a11841477   William Breathitt Gray   gpio: Add IRQ sup...
231
232
233
234
235
  	err = gpiochip_irqchip_add(&idio16gpio->chip, &idio_16_irqchip, 0,
  		handle_edge_irq, IRQ_TYPE_NONE);
  	if (err) {
  		dev_err(dev, "Could not add irqchip (%d)
  ", err);
cb32389cb   William Breathitt Gray   gpio: 104-idio-16...
236
  		goto err_gpiochip_remove;
a11841477   William Breathitt Gray   gpio: Add IRQ sup...
237
  	}
86ea8a95a   William Breathitt Gray   gpio: 104-idio-16...
238
  	err = request_irq(irq[id], idio_16_irq_handler, 0, name, idio16gpio);
a11841477   William Breathitt Gray   gpio: Add IRQ sup...
239
240
241
  	if (err) {
  		dev_err(dev, "IRQ handler registering failed (%d)
  ", err);
cb32389cb   William Breathitt Gray   gpio: 104-idio-16...
242
  		goto err_gpiochip_remove;
a11841477   William Breathitt Gray   gpio: Add IRQ sup...
243
  	}
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
244
  	return 0;
cb32389cb   William Breathitt Gray   gpio: 104-idio-16...
245
  err_gpiochip_remove:
a11841477   William Breathitt Gray   gpio: Add IRQ sup...
246
  	gpiochip_remove(&idio16gpio->chip);
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
247
248
  	return err;
  }
86ea8a95a   William Breathitt Gray   gpio: 104-idio-16...
249
  static int idio_16_remove(struct device *dev, unsigned int id)
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
250
  {
86ea8a95a   William Breathitt Gray   gpio: 104-idio-16...
251
  	struct idio_16_gpio *const idio16gpio = dev_get_drvdata(dev);
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
252

a11841477   William Breathitt Gray   gpio: Add IRQ sup...
253
  	free_irq(idio16gpio->irq, idio16gpio);
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
254
  	gpiochip_remove(&idio16gpio->chip);
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
255
256
257
  
  	return 0;
  }
86ea8a95a   William Breathitt Gray   gpio: 104-idio-16...
258
259
  static struct isa_driver idio_16_driver = {
  	.probe = idio_16_probe,
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
260
261
262
263
264
  	.driver = {
  		.name = "104-idio-16"
  	},
  	.remove = idio_16_remove
  };
86ea8a95a   William Breathitt Gray   gpio: 104-idio-16...
265
  module_isa_driver(idio_16_driver, num_idio_16);
1ceacea22   William Breathitt Gray   gpio: Add GPIO su...
266
267
268
  
  MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
  MODULE_DESCRIPTION("ACCES 104-IDIO-16 GPIO driver");
22aeddb58   William Breathitt Gray   gpio: misc: Pass ...
269
  MODULE_LICENSE("GPL v2");