blob: ba73ee9c0c291508516bf18839dba413e5933294 [file] [log] [blame]
Thomas Gleixner1802d0b2019-05-27 08:55:21 +02001// SPDX-License-Identifier: GPL-2.0-only
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -05002/*
3 * GPIO driver for the ACCES 104-IDI-48 family
4 * Copyright (C) 2015 William Breathitt Gray
5 *
William Breathitt Gray72bf7442016-05-01 18:44:55 -04006 * This driver supports the following ACCES devices: 104-IDI-48A,
7 * 104-IDI-48AC, 104-IDI-48B, and 104-IDI-48BC.
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -05008 */
William Breathitt Gray3ce632f2022-07-20 09:46:01 -04009#include <linux/bits.h>
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -050010#include <linux/device.h>
William Breathitt Graye28432a2022-12-27 09:09:41 -050011#include <linux/err.h>
William Breathitt Gray59e21312022-12-27 09:09:42 -050012#include <linux/gpio/regmap.h>
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -050013#include <linux/interrupt.h>
William Breathitt Graye28432a2022-12-27 09:09:41 -050014#include <linux/ioport.h>
15#include <linux/irq.h>
William Breathitt Gray72bf7442016-05-01 18:44:55 -040016#include <linux/isa.h>
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -050017#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/moduleparam.h>
William Breathitt Graye28432a2022-12-27 09:09:41 -050020#include <linux/regmap.h>
William Breathitt Gray3ce632f2022-07-20 09:46:01 -040021#include <linux/types.h>
22
William Breathitt Gray72bf7442016-05-01 18:44:55 -040023#define IDI_48_EXTENT 8
24#define MAX_NUM_IDI_48 max_num_isa_dev(IDI_48_EXTENT)
25
26static unsigned int base[MAX_NUM_IDI_48];
27static unsigned int num_idi_48;
David Howellsd759f902017-04-04 16:54:22 +010028module_param_hw_array(base, uint, ioport, &num_idi_48, 0);
William Breathitt Gray72bf7442016-05-01 18:44:55 -040029MODULE_PARM_DESC(base, "ACCES 104-IDI-48 base addresses");
30
31static unsigned int irq[MAX_NUM_IDI_48];
William Breathitt Gray99c3ac82022-08-18 12:28:13 -040032static unsigned int num_irq;
33module_param_hw_array(irq, uint, irq, &num_irq, 0);
William Breathitt Gray72bf7442016-05-01 18:44:55 -040034MODULE_PARM_DESC(irq, "ACCES 104-IDI-48 interrupt line numbers");
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -050035
William Breathitt Graye28432a2022-12-27 09:09:41 -050036#define IDI48_IRQ_STATUS 0x7
37#define IDI48_IRQ_ENABLE IDI48_IRQ_STATUS
38
William Breathitt Gray59e21312022-12-27 09:09:42 -050039static int idi_48_reg_mask_xlate(struct gpio_regmap *gpio, unsigned int base,
40 unsigned int offset, unsigned int *reg,
41 unsigned int *mask)
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -050042{
William Breathitt Gray59e21312022-12-27 09:09:42 -050043 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;
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -050047
William Breathitt Gray59e21312022-12-27 09:09:42 -050048 *reg = base + port + port_stride;
49 *mask = BIT(line);
William Breathitt Grayf72b1072018-03-22 09:00:31 -040050
51 return 0;
52}
53
William Breathitt Graye28432a2022-12-27 09:09:41 -050054static const struct regmap_range idi_48_wr_ranges[] = {
55 regmap_reg_range(0x0, 0x6),
56};
57static const struct regmap_range idi_48_rd_ranges[] = {
58 regmap_reg_range(0x0, 0x2), regmap_reg_range(0x4, 0x7),
59};
60static const struct regmap_range idi_48_precious_ranges[] = {
61 regmap_reg_range(0x7, 0x7),
62};
63static 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};
67static 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};
71static 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};
75static 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,
William Breathitt Gray2ce987d2023-04-03 13:53:14 -040084 .use_raw_spinlock = true,
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -050085};
86
William Breathitt Graya71dc252017-01-30 13:33:11 -050087#define IDI48_NGPIO 48
William Breathitt Graye28432a2022-12-27 09:09:41 -050088
89#define IDI48_REGMAP_IRQ(_id) \
90 [_id] = { \
91 .mask = BIT((_id) / 8), \
92 .type = { .types_supported = IRQ_TYPE_EDGE_BOTH }, \
93 }
94
95static const struct regmap_irq idi48_regmap_irqs[IDI48_NGPIO] = {
96 IDI48_REGMAP_IRQ(0), IDI48_REGMAP_IRQ(1), IDI48_REGMAP_IRQ(2), /* 0-2 */
97 IDI48_REGMAP_IRQ(3), IDI48_REGMAP_IRQ(4), IDI48_REGMAP_IRQ(5), /* 3-5 */
98 IDI48_REGMAP_IRQ(6), IDI48_REGMAP_IRQ(7), IDI48_REGMAP_IRQ(8), /* 6-8 */
99 IDI48_REGMAP_IRQ(9), IDI48_REGMAP_IRQ(10), IDI48_REGMAP_IRQ(11), /* 9-11 */
100 IDI48_REGMAP_IRQ(12), IDI48_REGMAP_IRQ(13), IDI48_REGMAP_IRQ(14), /* 12-14 */
101 IDI48_REGMAP_IRQ(15), IDI48_REGMAP_IRQ(16), IDI48_REGMAP_IRQ(17), /* 15-17 */
102 IDI48_REGMAP_IRQ(18), IDI48_REGMAP_IRQ(19), IDI48_REGMAP_IRQ(20), /* 18-20 */
103 IDI48_REGMAP_IRQ(21), IDI48_REGMAP_IRQ(22), IDI48_REGMAP_IRQ(23), /* 21-23 */
104 IDI48_REGMAP_IRQ(24), IDI48_REGMAP_IRQ(25), IDI48_REGMAP_IRQ(26), /* 24-26 */
105 IDI48_REGMAP_IRQ(27), IDI48_REGMAP_IRQ(28), IDI48_REGMAP_IRQ(29), /* 27-29 */
106 IDI48_REGMAP_IRQ(30), IDI48_REGMAP_IRQ(31), IDI48_REGMAP_IRQ(32), /* 30-32 */
107 IDI48_REGMAP_IRQ(33), IDI48_REGMAP_IRQ(34), IDI48_REGMAP_IRQ(35), /* 33-35 */
108 IDI48_REGMAP_IRQ(36), IDI48_REGMAP_IRQ(37), IDI48_REGMAP_IRQ(38), /* 36-38 */
109 IDI48_REGMAP_IRQ(39), IDI48_REGMAP_IRQ(40), IDI48_REGMAP_IRQ(41), /* 39-41 */
110 IDI48_REGMAP_IRQ(42), IDI48_REGMAP_IRQ(43), IDI48_REGMAP_IRQ(44), /* 42-44 */
111 IDI48_REGMAP_IRQ(45), IDI48_REGMAP_IRQ(46), IDI48_REGMAP_IRQ(47), /* 45-47 */
112};
113
William Breathitt Graya71dc252017-01-30 13:33:11 -0500114static const char *idi48_names[IDI48_NGPIO] = {
115 "Bit 0 A", "Bit 1 A", "Bit 2 A", "Bit 3 A", "Bit 4 A", "Bit 5 A",
116 "Bit 6 A", "Bit 7 A", "Bit 8 A", "Bit 9 A", "Bit 10 A", "Bit 11 A",
117 "Bit 12 A", "Bit 13 A", "Bit 14 A", "Bit 15 A", "Bit 16 A", "Bit 17 A",
118 "Bit 18 A", "Bit 19 A", "Bit 20 A", "Bit 21 A", "Bit 22 A", "Bit 23 A",
119 "Bit 0 B", "Bit 1 B", "Bit 2 B", "Bit 3 B", "Bit 4 B", "Bit 5 B",
120 "Bit 6 B", "Bit 7 B", "Bit 8 B", "Bit 9 B", "Bit 10 B", "Bit 11 B",
121 "Bit 12 B", "Bit 13 B", "Bit 14 B", "Bit 15 B", "Bit 16 B", "Bit 17 B",
122 "Bit 18 B", "Bit 19 B", "Bit 20 B", "Bit 21 B", "Bit 22 B", "Bit 23 B"
123};
124
William Breathitt Gray72bf7442016-05-01 18:44:55 -0400125static int idi_48_probe(struct device *dev, unsigned int id)
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500126{
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500127 const char *const name = dev_name(dev);
William Breathitt Gray59e21312022-12-27 09:09:42 -0500128 struct gpio_regmap_config config = {};
William Breathitt Graye28432a2022-12-27 09:09:41 -0500129 void __iomem *regs;
130 struct regmap *map;
131 struct regmap_irq_chip *chip;
132 struct regmap_irq_chip_data *chip_data;
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500133 int err;
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500134
William Breathitt Gray72bf7442016-05-01 18:44:55 -0400135 if (!devm_request_region(dev, base[id], IDI_48_EXTENT, name)) {
William Breathitt Gray5cfc0572016-02-03 15:15:22 -0500136 dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
William Breathitt Gray72bf7442016-05-01 18:44:55 -0400137 base[id], base[id] + IDI_48_EXTENT);
William Breathitt Gray5cfc0572016-02-03 15:15:22 -0500138 return -EBUSY;
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500139 }
140
William Breathitt Graye28432a2022-12-27 09:09:41 -0500141 regs = devm_ioport_map(dev, base[id], IDI_48_EXTENT);
142 if (!regs)
William Breathitt Graybed58062022-05-10 13:30:55 -0400143 return -ENOMEM;
William Breathitt Graye28432a2022-12-27 09:09:41 -0500144
145 map = devm_regmap_init_mmio(dev, regs, &idi48_regmap_config);
146 if (IS_ERR(map))
147 return dev_err_probe(dev, PTR_ERR(map),
148 "Unable to initialize register map\n");
149
150 chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
151 if (!chip)
152 return -ENOMEM;
153
154 chip->name = name;
155 chip->status_base = IDI48_IRQ_STATUS;
156 chip->unmask_base = IDI48_IRQ_ENABLE;
157 chip->clear_on_unmask = true;
158 chip->num_regs = 1;
159 chip->irqs = idi48_regmap_irqs;
160 chip->num_irqs = ARRAY_SIZE(idi48_regmap_irqs);
161
162 err = devm_regmap_add_irq_chip(dev, map, irq[id], IRQF_SHARED, 0, chip,
163 &chip_data);
164 if (err)
165 return dev_err_probe(dev, err, "IRQ registration failed\n");
William Breathitt Graybed58062022-05-10 13:30:55 -0400166
William Breathitt Gray59e21312022-12-27 09:09:42 -0500167 config.parent = dev;
168 config.regmap = map;
169 config.ngpio = IDI48_NGPIO;
170 config.names = idi48_names;
171 config.reg_dat_base = GPIO_REGMAP_ADDR(0x0);
172 config.ngpio_per_reg = 8;
173 config.reg_mask_xlate = idi_48_reg_mask_xlate;
174 config.irq_domain = regmap_irq_get_domain(chip_data);
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500175
William Breathitt Gray59e21312022-12-27 09:09:42 -0500176 return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &config));
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500177}
178
William Breathitt Gray72bf7442016-05-01 18:44:55 -0400179static struct isa_driver idi_48_driver = {
180 .probe = idi_48_probe,
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500181 .driver = {
182 .name = "104-idi-48"
183 },
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500184};
William Breathitt Gray99c3ac82022-08-18 12:28:13 -0400185module_isa_driver_with_irq(idi_48_driver, num_idi_48, num_irq);
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500186
187MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
188MODULE_DESCRIPTION("ACCES 104-IDI-48 GPIO driver");
William Breathitt Gray22aeddb2016-02-01 18:51:49 -0500189MODULE_LICENSE("GPL v2");