Linux Audio

Check our new training course

Loading...
v6.13.7
  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/ioport.h>
 12#include <linux/isa.h>
 13#include <linux/kernel.h>
 14#include <linux/module.h>
 15#include <linux/moduleparam.h>
 16#include <linux/regmap.h>
 17#include <linux/types.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
 33static const struct regmap_range gpiomm_volatile_ranges[] = {
 34	i8255_volatile_regmap_range(0x0), i8255_volatile_regmap_range(0x4),
 35};
 36static const struct regmap_access_table gpiomm_volatile_table = {
 37	.yes_ranges = gpiomm_volatile_ranges,
 38	.n_yes_ranges = ARRAY_SIZE(gpiomm_volatile_ranges),
 39};
 40static const struct regmap_config gpiomm_regmap_config = {
 41	.reg_bits = 8,
 42	.reg_stride = 1,
 43	.val_bits = 8,
 44	.io_port = true,
 45	.max_register = 0x7,
 46	.volatile_table = &gpiomm_volatile_table,
 47	.cache_type = REGCACHE_FLAT,
 48};
 49
 50#define GPIOMM_NGPIO 48
 51static const char *gpiomm_names[GPIOMM_NGPIO] = {
 52	"Port 1A0", "Port 1A1", "Port 1A2", "Port 1A3", "Port 1A4", "Port 1A5",
 53	"Port 1A6", "Port 1A7", "Port 1B0", "Port 1B1", "Port 1B2", "Port 1B3",
 54	"Port 1B4", "Port 1B5", "Port 1B6", "Port 1B7", "Port 1C0", "Port 1C1",
 55	"Port 1C2", "Port 1C3", "Port 1C4", "Port 1C5", "Port 1C6", "Port 1C7",
 56	"Port 2A0", "Port 2A1", "Port 2A2", "Port 2A3", "Port 2A4", "Port 2A5",
 57	"Port 2A6", "Port 2A7", "Port 2B0", "Port 2B1", "Port 2B2", "Port 2B3",
 58	"Port 2B4", "Port 2B5", "Port 2B6", "Port 2B7", "Port 2C0", "Port 2C1",
 59	"Port 2C2", "Port 2C3", "Port 2C4", "Port 2C5", "Port 2C6", "Port 2C7",
 60};
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 61
 62static int gpiomm_probe(struct device *dev, unsigned int id)
 63{
 
 64	const char *const name = dev_name(dev);
 65	struct i8255_regmap_config config = {};
 66	void __iomem *regs;
 
 
 
 67
 68	if (!devm_request_region(dev, base[id], GPIOMM_EXTENT, name)) {
 69		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
 70			base[id], base[id] + GPIOMM_EXTENT);
 71		return -EBUSY;
 72	}
 73
 74	regs = devm_ioport_map(dev, base[id], GPIOMM_EXTENT);
 75	if (!regs)
 76		return -ENOMEM;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 77
 78	config.map = devm_regmap_init_mmio(dev, regs, &gpiomm_regmap_config);
 79	if (IS_ERR(config.map))
 80		return dev_err_probe(dev, PTR_ERR(config.map),
 81				     "Unable to initialize register map\n");
 82
 83	config.parent = dev;
 84	config.num_ppi = GPIOMM_NUM_PPI;
 85	config.names = gpiomm_names;
 86
 87	return devm_i8255_regmap_register(dev, &config);
 88}
 89
 90static struct isa_driver gpiomm_driver = {
 91	.probe = gpiomm_probe,
 92	.driver = {
 93		.name = "gpio-mm"
 94	},
 
 95};
 96
 97module_isa_driver(gpiomm_driver, num_gpiomm);
 98
 99MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
100MODULE_DESCRIPTION("Diamond Systems GPIO-MM GPIO driver");
101MODULE_LICENSE("GPL v2");
v4.10.11
 
  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");