xref: /linux/drivers/gpio/gpio-104-idi-48.c (revision d6fd48ef)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * GPIO driver for the ACCES 104-IDI-48 family
4  * Copyright (C) 2015 William Breathitt Gray
5  *
6  * This driver supports the following ACCES devices: 104-IDI-48A,
7  * 104-IDI-48AC, 104-IDI-48B, and 104-IDI-48BC.
8  */
9 #include <linux/bits.h>
10 #include <linux/device.h>
11 #include <linux/err.h>
12 #include <linux/gpio/regmap.h>
13 #include <linux/interrupt.h>
14 #include <linux/ioport.h>
15 #include <linux/irq.h>
16 #include <linux/isa.h>
17 #include <linux/kernel.h>
18 #include <linux/module.h>
19 #include <linux/moduleparam.h>
20 #include <linux/regmap.h>
21 #include <linux/types.h>
22 
23 #define IDI_48_EXTENT 8
24 #define MAX_NUM_IDI_48 max_num_isa_dev(IDI_48_EXTENT)
25 
26 static unsigned int base[MAX_NUM_IDI_48];
27 static unsigned int num_idi_48;
28 module_param_hw_array(base, uint, ioport, &num_idi_48, 0);
29 MODULE_PARM_DESC(base, "ACCES 104-IDI-48 base addresses");
30 
31 static unsigned int irq[MAX_NUM_IDI_48];
32 static unsigned int num_irq;
33 module_param_hw_array(irq, uint, irq, &num_irq, 0);
34 MODULE_PARM_DESC(irq, "ACCES 104-IDI-48 interrupt line numbers");
35 
36 #define IDI48_IRQ_STATUS 0x7
37 #define IDI48_IRQ_ENABLE IDI48_IRQ_STATUS
38 
39 static int idi_48_reg_mask_xlate(struct gpio_regmap *gpio, unsigned int base,
40 				 unsigned int offset, unsigned int *reg,
41 				 unsigned int *mask)
42 {
43 	const unsigned int line = offset % 8;
44 	const unsigned int stride = offset / 8;
45 	const unsigned int port = (stride / 3) * 4;
46 	const unsigned int port_stride = stride % 3;
47 
48 	*reg = base + port + port_stride;
49 	*mask = BIT(line);
50 
51 	return 0;
52 }
53 
54 static const struct regmap_range idi_48_wr_ranges[] = {
55 	regmap_reg_range(0x0, 0x6),
56 };
57 static const struct regmap_range idi_48_rd_ranges[] = {
58 	regmap_reg_range(0x0, 0x2), regmap_reg_range(0x4, 0x7),
59 };
60 static const struct regmap_range idi_48_precious_ranges[] = {
61 	regmap_reg_range(0x7, 0x7),
62 };
63 static const struct regmap_access_table idi_48_wr_table = {
64 	.no_ranges = idi_48_wr_ranges,
65 	.n_no_ranges = ARRAY_SIZE(idi_48_wr_ranges),
66 };
67 static const struct regmap_access_table idi_48_rd_table = {
68 	.yes_ranges = idi_48_rd_ranges,
69 	.n_yes_ranges = ARRAY_SIZE(idi_48_rd_ranges),
70 };
71 static const struct regmap_access_table idi_48_precious_table = {
72 	.yes_ranges = idi_48_precious_ranges,
73 	.n_yes_ranges = ARRAY_SIZE(idi_48_precious_ranges),
74 };
75 static const struct regmap_config idi48_regmap_config = {
76 	.reg_bits = 8,
77 	.reg_stride = 1,
78 	.val_bits = 8,
79 	.io_port = true,
80 	.max_register = 0x6,
81 	.wr_table = &idi_48_wr_table,
82 	.rd_table = &idi_48_rd_table,
83 	.precious_table = &idi_48_precious_table,
84 };
85 
86 #define IDI48_NGPIO 48
87 
88 #define IDI48_REGMAP_IRQ(_id)						\
89 	[_id] = {							\
90 		.mask = BIT((_id) / 8),					\
91 		.type = { .types_supported = IRQ_TYPE_EDGE_BOTH },	\
92 	}
93 
94 static const struct regmap_irq idi48_regmap_irqs[IDI48_NGPIO] = {
95 	IDI48_REGMAP_IRQ(0), IDI48_REGMAP_IRQ(1), IDI48_REGMAP_IRQ(2), /* 0-2 */
96 	IDI48_REGMAP_IRQ(3), IDI48_REGMAP_IRQ(4), IDI48_REGMAP_IRQ(5), /* 3-5 */
97 	IDI48_REGMAP_IRQ(6), IDI48_REGMAP_IRQ(7), IDI48_REGMAP_IRQ(8), /* 6-8 */
98 	IDI48_REGMAP_IRQ(9), IDI48_REGMAP_IRQ(10), IDI48_REGMAP_IRQ(11), /* 9-11 */
99 	IDI48_REGMAP_IRQ(12), IDI48_REGMAP_IRQ(13), IDI48_REGMAP_IRQ(14), /* 12-14 */
100 	IDI48_REGMAP_IRQ(15), IDI48_REGMAP_IRQ(16), IDI48_REGMAP_IRQ(17), /* 15-17 */
101 	IDI48_REGMAP_IRQ(18), IDI48_REGMAP_IRQ(19), IDI48_REGMAP_IRQ(20), /* 18-20 */
102 	IDI48_REGMAP_IRQ(21), IDI48_REGMAP_IRQ(22), IDI48_REGMAP_IRQ(23), /* 21-23 */
103 	IDI48_REGMAP_IRQ(24), IDI48_REGMAP_IRQ(25), IDI48_REGMAP_IRQ(26), /* 24-26 */
104 	IDI48_REGMAP_IRQ(27), IDI48_REGMAP_IRQ(28), IDI48_REGMAP_IRQ(29), /* 27-29 */
105 	IDI48_REGMAP_IRQ(30), IDI48_REGMAP_IRQ(31), IDI48_REGMAP_IRQ(32), /* 30-32 */
106 	IDI48_REGMAP_IRQ(33), IDI48_REGMAP_IRQ(34), IDI48_REGMAP_IRQ(35), /* 33-35 */
107 	IDI48_REGMAP_IRQ(36), IDI48_REGMAP_IRQ(37), IDI48_REGMAP_IRQ(38), /* 36-38 */
108 	IDI48_REGMAP_IRQ(39), IDI48_REGMAP_IRQ(40), IDI48_REGMAP_IRQ(41), /* 39-41 */
109 	IDI48_REGMAP_IRQ(42), IDI48_REGMAP_IRQ(43), IDI48_REGMAP_IRQ(44), /* 42-44 */
110 	IDI48_REGMAP_IRQ(45), IDI48_REGMAP_IRQ(46), IDI48_REGMAP_IRQ(47), /* 45-47 */
111 };
112 
113 static const char *idi48_names[IDI48_NGPIO] = {
114 	"Bit 0 A", "Bit 1 A", "Bit 2 A", "Bit 3 A", "Bit 4 A", "Bit 5 A",
115 	"Bit 6 A", "Bit 7 A", "Bit 8 A", "Bit 9 A", "Bit 10 A", "Bit 11 A",
116 	"Bit 12 A", "Bit 13 A", "Bit 14 A", "Bit 15 A",	"Bit 16 A", "Bit 17 A",
117 	"Bit 18 A", "Bit 19 A", "Bit 20 A", "Bit 21 A", "Bit 22 A", "Bit 23 A",
118 	"Bit 0 B", "Bit 1 B", "Bit 2 B", "Bit 3 B", "Bit 4 B", "Bit 5 B",
119 	"Bit 6 B", "Bit 7 B", "Bit 8 B", "Bit 9 B", "Bit 10 B", "Bit 11 B",
120 	"Bit 12 B", "Bit 13 B", "Bit 14 B", "Bit 15 B",	"Bit 16 B", "Bit 17 B",
121 	"Bit 18 B", "Bit 19 B", "Bit 20 B", "Bit 21 B", "Bit 22 B", "Bit 23 B"
122 };
123 
124 static int idi_48_probe(struct device *dev, unsigned int id)
125 {
126 	const char *const name = dev_name(dev);
127 	struct gpio_regmap_config config = {};
128 	void __iomem *regs;
129 	struct regmap *map;
130 	struct regmap_irq_chip *chip;
131 	struct regmap_irq_chip_data *chip_data;
132 	int err;
133 
134 	if (!devm_request_region(dev, base[id], IDI_48_EXTENT, name)) {
135 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
136 			base[id], base[id] + IDI_48_EXTENT);
137 		return -EBUSY;
138 	}
139 
140 	regs = devm_ioport_map(dev, base[id], IDI_48_EXTENT);
141 	if (!regs)
142 		return -ENOMEM;
143 
144 	map = devm_regmap_init_mmio(dev, regs, &idi48_regmap_config);
145 	if (IS_ERR(map))
146 		return dev_err_probe(dev, PTR_ERR(map),
147 				     "Unable to initialize register map\n");
148 
149 	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
150 	if (!chip)
151 		return -ENOMEM;
152 
153 	chip->name = name;
154 	chip->status_base = IDI48_IRQ_STATUS;
155 	chip->unmask_base = IDI48_IRQ_ENABLE;
156 	chip->clear_on_unmask = true;
157 	chip->num_regs = 1;
158 	chip->irqs = idi48_regmap_irqs;
159 	chip->num_irqs = ARRAY_SIZE(idi48_regmap_irqs);
160 
161 	err = devm_regmap_add_irq_chip(dev, map, irq[id], IRQF_SHARED, 0, chip,
162 				       &chip_data);
163 	if (err)
164 		return dev_err_probe(dev, err, "IRQ registration failed\n");
165 
166 	config.parent = dev;
167 	config.regmap = map;
168 	config.ngpio = IDI48_NGPIO;
169 	config.names = idi48_names;
170 	config.reg_dat_base = GPIO_REGMAP_ADDR(0x0);
171 	config.ngpio_per_reg = 8;
172 	config.reg_mask_xlate = idi_48_reg_mask_xlate;
173 	config.irq_domain = regmap_irq_get_domain(chip_data);
174 
175 	return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &config));
176 }
177 
178 static struct isa_driver idi_48_driver = {
179 	.probe = idi_48_probe,
180 	.driver = {
181 		.name = "104-idi-48"
182 	},
183 };
184 module_isa_driver_with_irq(idi_48_driver, num_idi_48, num_irq);
185 
186 MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
187 MODULE_DESCRIPTION("ACCES 104-IDI-48 GPIO driver");
188 MODULE_LICENSE("GPL v2");
189