Loading...
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * GPIO driver for the Diamond Systems GPIO-MM
4 * Copyright (C) 2016 William Breathitt Gray
5 *
6 * This driver supports the following Diamond Systems devices: GPIO-MM and
7 * GPIO-MM-12.
8 */
9#include <linux/device.h>
10#include <linux/errno.h>
11#include <linux/gpio/driver.h>
12#include <linux/io.h>
13#include <linux/ioport.h>
14#include <linux/isa.h>
15#include <linux/kernel.h>
16#include <linux/module.h>
17#include <linux/moduleparam.h>
18
19#include "gpio-i8255.h"
20
21MODULE_IMPORT_NS(I8255);
22
23#define GPIOMM_EXTENT 8
24#define MAX_NUM_GPIOMM max_num_isa_dev(GPIOMM_EXTENT)
25
26static unsigned int base[MAX_NUM_GPIOMM];
27static unsigned int num_gpiomm;
28module_param_hw_array(base, uint, ioport, &num_gpiomm, 0);
29MODULE_PARM_DESC(base, "Diamond Systems GPIO-MM base addresses");
30
31#define GPIOMM_NUM_PPI 2
32
33/**
34 * struct gpiomm_gpio - GPIO device private data structure
35 * @chip: instance of the gpio_chip
36 * @ppi_state: Programmable Peripheral Interface group states
37 * @ppi: Programmable Peripheral Interface groups
38 */
39struct gpiomm_gpio {
40 struct gpio_chip chip;
41 struct i8255_state ppi_state[GPIOMM_NUM_PPI];
42 struct i8255 __iomem *ppi;
43};
44
45static int gpiomm_gpio_get_direction(struct gpio_chip *chip,
46 unsigned int offset)
47{
48 struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
49
50 if (i8255_get_direction(gpiommgpio->ppi_state, offset))
51 return GPIO_LINE_DIRECTION_IN;
52
53 return GPIO_LINE_DIRECTION_OUT;
54}
55
56static int gpiomm_gpio_direction_input(struct gpio_chip *chip,
57 unsigned int offset)
58{
59 struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
60
61 i8255_direction_input(gpiommgpio->ppi, gpiommgpio->ppi_state, offset);
62
63 return 0;
64}
65
66static int gpiomm_gpio_direction_output(struct gpio_chip *chip,
67 unsigned int offset, int value)
68{
69 struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
70
71 i8255_direction_output(gpiommgpio->ppi, gpiommgpio->ppi_state, offset,
72 value);
73
74 return 0;
75}
76
77static int gpiomm_gpio_get(struct gpio_chip *chip, unsigned int offset)
78{
79 struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
80
81 return i8255_get(gpiommgpio->ppi, offset);
82}
83
84static int gpiomm_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
85 unsigned long *bits)
86{
87 struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
88
89 i8255_get_multiple(gpiommgpio->ppi, mask, bits, chip->ngpio);
90
91 return 0;
92}
93
94static void gpiomm_gpio_set(struct gpio_chip *chip, unsigned int offset,
95 int value)
96{
97 struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
98
99 i8255_set(gpiommgpio->ppi, gpiommgpio->ppi_state, offset, value);
100}
101
102static void gpiomm_gpio_set_multiple(struct gpio_chip *chip,
103 unsigned long *mask, unsigned long *bits)
104{
105 struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
106
107 i8255_set_multiple(gpiommgpio->ppi, gpiommgpio->ppi_state, mask, bits,
108 chip->ngpio);
109}
110
111#define GPIOMM_NGPIO 48
112static const char *gpiomm_names[GPIOMM_NGPIO] = {
113 "Port 1A0", "Port 1A1", "Port 1A2", "Port 1A3", "Port 1A4", "Port 1A5",
114 "Port 1A6", "Port 1A7", "Port 1B0", "Port 1B1", "Port 1B2", "Port 1B3",
115 "Port 1B4", "Port 1B5", "Port 1B6", "Port 1B7", "Port 1C0", "Port 1C1",
116 "Port 1C2", "Port 1C3", "Port 1C4", "Port 1C5", "Port 1C6", "Port 1C7",
117 "Port 2A0", "Port 2A1", "Port 2A2", "Port 2A3", "Port 2A4", "Port 2A5",
118 "Port 2A6", "Port 2A7", "Port 2B0", "Port 2B1", "Port 2B2", "Port 2B3",
119 "Port 2B4", "Port 2B5", "Port 2B6", "Port 2B7", "Port 2C0", "Port 2C1",
120 "Port 2C2", "Port 2C3", "Port 2C4", "Port 2C5", "Port 2C6", "Port 2C7",
121};
122
123static void gpiomm_init_dio(struct i8255 __iomem *const ppi,
124 struct i8255_state *const ppi_state)
125{
126 const unsigned long ngpio = 24;
127 const unsigned long mask = GENMASK(ngpio - 1, 0);
128 const unsigned long bits = 0;
129 unsigned long i;
130
131 /* Initialize all GPIO to output 0 */
132 for (i = 0; i < GPIOMM_NUM_PPI; i++) {
133 i8255_mode0_output(&ppi[i]);
134 i8255_set_multiple(&ppi[i], &ppi_state[i], &mask, &bits, ngpio);
135 }
136}
137
138static int gpiomm_probe(struct device *dev, unsigned int id)
139{
140 struct gpiomm_gpio *gpiommgpio;
141 const char *const name = dev_name(dev);
142 int err;
143
144 gpiommgpio = devm_kzalloc(dev, sizeof(*gpiommgpio), GFP_KERNEL);
145 if (!gpiommgpio)
146 return -ENOMEM;
147
148 if (!devm_request_region(dev, base[id], GPIOMM_EXTENT, name)) {
149 dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
150 base[id], base[id] + GPIOMM_EXTENT);
151 return -EBUSY;
152 }
153
154 gpiommgpio->ppi = devm_ioport_map(dev, base[id], GPIOMM_EXTENT);
155 if (!gpiommgpio->ppi)
156 return -ENOMEM;
157
158 gpiommgpio->chip.label = name;
159 gpiommgpio->chip.parent = dev;
160 gpiommgpio->chip.owner = THIS_MODULE;
161 gpiommgpio->chip.base = -1;
162 gpiommgpio->chip.ngpio = GPIOMM_NGPIO;
163 gpiommgpio->chip.names = gpiomm_names;
164 gpiommgpio->chip.get_direction = gpiomm_gpio_get_direction;
165 gpiommgpio->chip.direction_input = gpiomm_gpio_direction_input;
166 gpiommgpio->chip.direction_output = gpiomm_gpio_direction_output;
167 gpiommgpio->chip.get = gpiomm_gpio_get;
168 gpiommgpio->chip.get_multiple = gpiomm_gpio_get_multiple;
169 gpiommgpio->chip.set = gpiomm_gpio_set;
170 gpiommgpio->chip.set_multiple = gpiomm_gpio_set_multiple;
171
172 i8255_state_init(gpiommgpio->ppi_state, GPIOMM_NUM_PPI);
173 gpiomm_init_dio(gpiommgpio->ppi, gpiommgpio->ppi_state);
174
175 err = devm_gpiochip_add_data(dev, &gpiommgpio->chip, gpiommgpio);
176 if (err) {
177 dev_err(dev, "GPIO registering failed (%d)\n", err);
178 return err;
179 }
180
181 return 0;
182}
183
184static struct isa_driver gpiomm_driver = {
185 .probe = gpiomm_probe,
186 .driver = {
187 .name = "gpio-mm"
188 },
189};
190
191module_isa_driver(gpiomm_driver, num_gpiomm);
192
193MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
194MODULE_DESCRIPTION("Diamond Systems GPIO-MM GPIO driver");
195MODULE_LICENSE("GPL v2");
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
32static unsigned int base[MAX_NUM_GPIOMM];
33static unsigned int num_gpiomm;
34module_param_array(base, uint, &num_gpiomm, 0);
35MODULE_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 */
46struct 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
55static 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
65static 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
103static 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
150static 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
174static 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
195static 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
246static 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
255static struct isa_driver gpiomm_driver = {
256 .probe = gpiomm_probe,
257 .driver = {
258 .name = "gpio-mm"
259 },
260 .remove = gpiomm_remove
261};
262
263module_isa_driver(gpiomm_driver, num_gpiomm);
264
265MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
266MODULE_DESCRIPTION("Diamond Systems GPIO-MM GPIO driver");
267MODULE_LICENSE("GPL v2");