xref: /openbmc/linux/drivers/gpio/gpio-ws16c48.c (revision 33f83d13)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * GPIO driver for the WinSystems WS16C48
4  * Copyright (C) 2016 William Breathitt Gray
5  */
6 #include <linux/bitfield.h>
7 #include <linux/bits.h>
8 #include <linux/device.h>
9 #include <linux/err.h>
10 #include <linux/gpio/regmap.h>
11 #include <linux/irq.h>
12 #include <linux/isa.h>
13 #include <linux/kernel.h>
14 #include <linux/module.h>
15 #include <linux/moduleparam.h>
16 #include <linux/spinlock.h>
17 #include <linux/regmap.h>
18 #include <linux/types.h>
19 
20 #define WS16C48_EXTENT 11
21 #define MAX_NUM_WS16C48 max_num_isa_dev(WS16C48_EXTENT)
22 
23 static unsigned int base[MAX_NUM_WS16C48];
24 static unsigned int num_ws16c48;
25 module_param_hw_array(base, uint, ioport, &num_ws16c48, 0);
26 MODULE_PARM_DESC(base, "WinSystems WS16C48 base addresses");
27 
28 static unsigned int irq[MAX_NUM_WS16C48];
29 static unsigned int num_irq;
30 module_param_hw_array(irq, uint, irq, &num_irq, 0);
31 MODULE_PARM_DESC(irq, "WinSystems WS16C48 interrupt line numbers");
32 
33 #define WS16C48_DAT_BASE 0x0
34 #define WS16C48_PAGE_LOCK 0x7
35 #define WS16C48_PAGE_BASE 0x8
36 #define WS16C48_POL WS16C48_PAGE_BASE
37 #define WS16C48_ENAB WS16C48_PAGE_BASE
38 #define WS16C48_INT_ID WS16C48_PAGE_BASE
39 
40 #define PAGE_LOCK_PAGE_FIELD GENMASK(7, 6)
41 #define POL_PAGE u8_encode_bits(1, PAGE_LOCK_PAGE_FIELD)
42 #define ENAB_PAGE u8_encode_bits(2, PAGE_LOCK_PAGE_FIELD)
43 #define INT_ID_PAGE u8_encode_bits(3, PAGE_LOCK_PAGE_FIELD)
44 
45 static const struct regmap_range ws16c48_wr_ranges[] = {
46 	regmap_reg_range(0x0, 0x5), regmap_reg_range(0x7, 0xA),
47 };
48 static const struct regmap_range ws16c48_rd_ranges[] = {
49 	regmap_reg_range(0x0, 0xA),
50 };
51 static const struct regmap_range ws16c48_volatile_ranges[] = {
52 	regmap_reg_range(0x0, 0x6), regmap_reg_range(0x8, 0xA),
53 };
54 static const struct regmap_access_table ws16c48_wr_table = {
55 	.yes_ranges = ws16c48_wr_ranges,
56 	.n_yes_ranges = ARRAY_SIZE(ws16c48_wr_ranges),
57 };
58 static const struct regmap_access_table ws16c48_rd_table = {
59 	.yes_ranges = ws16c48_rd_ranges,
60 	.n_yes_ranges = ARRAY_SIZE(ws16c48_rd_ranges),
61 };
62 static const struct regmap_access_table ws16c48_volatile_table = {
63 	.yes_ranges = ws16c48_volatile_ranges,
64 	.n_yes_ranges = ARRAY_SIZE(ws16c48_volatile_ranges),
65 };
66 static const struct regmap_config ws16c48_regmap_config = {
67 	.reg_bits = 8,
68 	.reg_stride = 1,
69 	.val_bits = 8,
70 	.io_port = true,
71 	.wr_table = &ws16c48_wr_table,
72 	.rd_table = &ws16c48_rd_table,
73 	.volatile_table = &ws16c48_volatile_table,
74 	.cache_type = REGCACHE_FLAT,
75 	.use_raw_spinlock = true,
76 };
77 
78 #define WS16C48_NGPIO_PER_REG 8
79 #define WS16C48_REGMAP_IRQ(_id)							\
80 	[_id] = {								\
81 		.reg_offset = (_id) / WS16C48_NGPIO_PER_REG,			\
82 		.mask = BIT((_id) % WS16C48_NGPIO_PER_REG),			\
83 		.type = {							\
84 			.type_reg_offset = (_id) / WS16C48_NGPIO_PER_REG,	\
85 			.types_supported = IRQ_TYPE_EDGE_BOTH,			\
86 		},								\
87 	}
88 
89 /* Only the first 24 lines (Port 0-2) support interrupts */
90 #define WS16C48_NUM_IRQS 24
91 static const struct regmap_irq ws16c48_regmap_irqs[WS16C48_NUM_IRQS] = {
92 	WS16C48_REGMAP_IRQ(0), WS16C48_REGMAP_IRQ(1), WS16C48_REGMAP_IRQ(2), /* 0-2 */
93 	WS16C48_REGMAP_IRQ(3), WS16C48_REGMAP_IRQ(4), WS16C48_REGMAP_IRQ(5), /* 3-5 */
94 	WS16C48_REGMAP_IRQ(6), WS16C48_REGMAP_IRQ(7), WS16C48_REGMAP_IRQ(8), /* 6-8 */
95 	WS16C48_REGMAP_IRQ(9), WS16C48_REGMAP_IRQ(10), WS16C48_REGMAP_IRQ(11), /* 9-11 */
96 	WS16C48_REGMAP_IRQ(12), WS16C48_REGMAP_IRQ(13), WS16C48_REGMAP_IRQ(14), /* 12-14 */
97 	WS16C48_REGMAP_IRQ(15), WS16C48_REGMAP_IRQ(16), WS16C48_REGMAP_IRQ(17), /* 15-17 */
98 	WS16C48_REGMAP_IRQ(18), WS16C48_REGMAP_IRQ(19), WS16C48_REGMAP_IRQ(20), /* 18-20 */
99 	WS16C48_REGMAP_IRQ(21), WS16C48_REGMAP_IRQ(22), WS16C48_REGMAP_IRQ(23), /* 21-23 */
100 };
101 
102 /**
103  * struct ws16c48_gpio - GPIO device private data structure
104  * @map:	regmap for the device
105  * @lock:	synchronization lock to prevent I/O race conditions
106  * @irq_mask:	I/O bits affected by interrupts
107  */
108 struct ws16c48_gpio {
109 	struct regmap *map;
110 	raw_spinlock_t lock;
111 	u8 irq_mask[WS16C48_NUM_IRQS / WS16C48_NGPIO_PER_REG];
112 };
113 
ws16c48_handle_pre_irq(void * const irq_drv_data)114 static int ws16c48_handle_pre_irq(void *const irq_drv_data) __acquires(&ws16c48gpio->lock)
115 {
116 	struct ws16c48_gpio *const ws16c48gpio = irq_drv_data;
117 
118 	/* Lock to prevent Page/Lock register change while we handle IRQ */
119 	raw_spin_lock(&ws16c48gpio->lock);
120 
121 	return 0;
122 }
123 
ws16c48_handle_post_irq(void * const irq_drv_data)124 static int ws16c48_handle_post_irq(void *const irq_drv_data) __releases(&ws16c48gpio->lock)
125 {
126 	struct ws16c48_gpio *const ws16c48gpio = irq_drv_data;
127 
128 	raw_spin_unlock(&ws16c48gpio->lock);
129 
130 	return 0;
131 }
132 
ws16c48_handle_mask_sync(const int index,const unsigned int mask_buf_def,const unsigned int mask_buf,void * const irq_drv_data)133 static int ws16c48_handle_mask_sync(const int index, const unsigned int mask_buf_def,
134 				    const unsigned int mask_buf, void *const irq_drv_data)
135 {
136 	struct ws16c48_gpio *const ws16c48gpio = irq_drv_data;
137 	unsigned long flags;
138 	int ret = 0;
139 
140 	raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
141 
142 	/* exit early if no change since the last mask sync */
143 	if (mask_buf == ws16c48gpio->irq_mask[index])
144 		goto exit_unlock;
145 	ws16c48gpio->irq_mask[index] = mask_buf;
146 
147 	ret = regmap_write(ws16c48gpio->map, WS16C48_PAGE_LOCK, ENAB_PAGE);
148 	if (ret)
149 		goto exit_unlock;
150 
151 	/* Update ENAB register (inverted mask) */
152 	ret = regmap_write(ws16c48gpio->map, WS16C48_ENAB + index, ~mask_buf);
153 	if (ret)
154 		goto exit_unlock;
155 
156 	ret = regmap_write(ws16c48gpio->map, WS16C48_PAGE_LOCK, INT_ID_PAGE);
157 	if (ret)
158 		goto exit_unlock;
159 
160 exit_unlock:
161 	raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
162 
163 	return ret;
164 }
165 
ws16c48_set_type_config(unsigned int ** const buf,const unsigned int type,const struct regmap_irq * const irq_data,const int idx,void * const irq_drv_data)166 static int ws16c48_set_type_config(unsigned int **const buf, const unsigned int type,
167 				   const struct regmap_irq *const irq_data, const int idx,
168 				   void *const irq_drv_data)
169 {
170 	struct ws16c48_gpio *const ws16c48gpio = irq_drv_data;
171 	unsigned int polarity;
172 	unsigned long flags;
173 	int ret;
174 
175 	switch (type) {
176 	case IRQ_TYPE_EDGE_RISING:
177 		polarity = irq_data->mask;
178 		break;
179 	case IRQ_TYPE_EDGE_FALLING:
180 		polarity = 0;
181 		break;
182 	default:
183 		return -EINVAL;
184 	}
185 
186 	raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
187 
188 	ret = regmap_write(ws16c48gpio->map, WS16C48_PAGE_LOCK, POL_PAGE);
189 	if (ret)
190 		goto exit_unlock;
191 
192 	/* Set interrupt polarity */
193 	ret = regmap_update_bits(ws16c48gpio->map, WS16C48_POL + idx, irq_data->mask, polarity);
194 	if (ret)
195 		goto exit_unlock;
196 
197 	ret = regmap_write(ws16c48gpio->map, WS16C48_PAGE_LOCK, INT_ID_PAGE);
198 	if (ret)
199 		goto exit_unlock;
200 
201 exit_unlock:
202 	raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
203 
204 	return ret;
205 }
206 
207 #define WS16C48_NGPIO 48
208 static const char *ws16c48_names[WS16C48_NGPIO] = {
209 	"Port 0 Bit 0", "Port 0 Bit 1", "Port 0 Bit 2", "Port 0 Bit 3",
210 	"Port 0 Bit 4", "Port 0 Bit 5", "Port 0 Bit 6", "Port 0 Bit 7",
211 	"Port 1 Bit 0", "Port 1 Bit 1", "Port 1 Bit 2", "Port 1 Bit 3",
212 	"Port 1 Bit 4", "Port 1 Bit 5", "Port 1 Bit 6", "Port 1 Bit 7",
213 	"Port 2 Bit 0", "Port 2 Bit 1", "Port 2 Bit 2", "Port 2 Bit 3",
214 	"Port 2 Bit 4", "Port 2 Bit 5", "Port 2 Bit 6", "Port 2 Bit 7",
215 	"Port 3 Bit 0", "Port 3 Bit 1", "Port 3 Bit 2", "Port 3 Bit 3",
216 	"Port 3 Bit 4", "Port 3 Bit 5", "Port 3 Bit 6", "Port 3 Bit 7",
217 	"Port 4 Bit 0", "Port 4 Bit 1", "Port 4 Bit 2", "Port 4 Bit 3",
218 	"Port 4 Bit 4", "Port 4 Bit 5", "Port 4 Bit 6", "Port 4 Bit 7",
219 	"Port 5 Bit 0", "Port 5 Bit 1", "Port 5 Bit 2", "Port 5 Bit 3",
220 	"Port 5 Bit 4", "Port 5 Bit 5", "Port 5 Bit 6", "Port 5 Bit 7"
221 };
222 
ws16c48_irq_init_hw(struct regmap * const map)223 static int ws16c48_irq_init_hw(struct regmap *const map)
224 {
225 	int err;
226 
227 	err = regmap_write(map, WS16C48_PAGE_LOCK, ENAB_PAGE);
228 	if (err)
229 		return err;
230 
231 	/* Disable interrupts for all lines */
232 	err = regmap_write(map, WS16C48_ENAB + 0, 0x00);
233 	if (err)
234 		return err;
235 	err = regmap_write(map, WS16C48_ENAB + 1, 0x00);
236 	if (err)
237 		return err;
238 	err = regmap_write(map, WS16C48_ENAB + 2, 0x00);
239 	if (err)
240 		return err;
241 
242 	return regmap_write(map, WS16C48_PAGE_LOCK, INT_ID_PAGE);
243 }
244 
ws16c48_probe(struct device * dev,unsigned int id)245 static int ws16c48_probe(struct device *dev, unsigned int id)
246 {
247 	struct ws16c48_gpio *ws16c48gpio;
248 	const char *const name = dev_name(dev);
249 	int err;
250 	struct gpio_regmap_config gpio_config = {};
251 	void __iomem *regs;
252 	struct regmap_irq_chip *chip;
253 	struct regmap_irq_chip_data *chip_data;
254 
255 	ws16c48gpio = devm_kzalloc(dev, sizeof(*ws16c48gpio), GFP_KERNEL);
256 	if (!ws16c48gpio)
257 		return -ENOMEM;
258 
259 	if (!devm_request_region(dev, base[id], WS16C48_EXTENT, name)) {
260 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
261 			base[id], base[id] + WS16C48_EXTENT);
262 		return -EBUSY;
263 	}
264 
265 	regs = devm_ioport_map(dev, base[id], WS16C48_EXTENT);
266 	if (!regs)
267 		return -ENOMEM;
268 
269 	ws16c48gpio->map = devm_regmap_init_mmio(dev, regs, &ws16c48_regmap_config);
270 	if (IS_ERR(ws16c48gpio->map))
271 		return dev_err_probe(dev, PTR_ERR(ws16c48gpio->map),
272 				     "Unable to initialize register map\n");
273 
274 	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
275 	if (!chip)
276 		return -ENOMEM;
277 
278 	chip->name = name;
279 	chip->status_base = WS16C48_INT_ID;
280 	chip->mask_base = WS16C48_ENAB;
281 	chip->ack_base = WS16C48_INT_ID;
282 	chip->num_regs = 3;
283 	chip->irqs = ws16c48_regmap_irqs;
284 	chip->num_irqs = ARRAY_SIZE(ws16c48_regmap_irqs);
285 	chip->handle_pre_irq = ws16c48_handle_pre_irq;
286 	chip->handle_post_irq = ws16c48_handle_post_irq;
287 	chip->handle_mask_sync = ws16c48_handle_mask_sync;
288 	chip->set_type_config = ws16c48_set_type_config;
289 	chip->irq_drv_data = ws16c48gpio;
290 
291 	raw_spin_lock_init(&ws16c48gpio->lock);
292 
293 	/* Initialize to prevent spurious interrupts before we're ready */
294 	err = ws16c48_irq_init_hw(ws16c48gpio->map);
295 	if (err)
296 		return err;
297 
298 	err = devm_regmap_add_irq_chip(dev, ws16c48gpio->map, irq[id], 0, 0, chip, &chip_data);
299 	if (err)
300 		return dev_err_probe(dev, err, "IRQ registration failed\n");
301 
302 	gpio_config.parent = dev;
303 	gpio_config.regmap = ws16c48gpio->map;
304 	gpio_config.ngpio = WS16C48_NGPIO;
305 	gpio_config.names = ws16c48_names;
306 	gpio_config.reg_dat_base = GPIO_REGMAP_ADDR(WS16C48_DAT_BASE);
307 	gpio_config.reg_set_base = GPIO_REGMAP_ADDR(WS16C48_DAT_BASE);
308 	/* Setting a GPIO to 0 allows it to be used as an input */
309 	gpio_config.reg_dir_out_base = GPIO_REGMAP_ADDR(WS16C48_DAT_BASE);
310 	gpio_config.ngpio_per_reg = WS16C48_NGPIO_PER_REG;
311 	gpio_config.irq_domain = regmap_irq_get_domain(chip_data);
312 
313 	return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config));
314 }
315 
316 static struct isa_driver ws16c48_driver = {
317 	.probe = ws16c48_probe,
318 	.driver = {
319 		.name = "ws16c48"
320 	},
321 };
322 
323 module_isa_driver_with_irq(ws16c48_driver, num_ws16c48, num_irq);
324 
325 MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
326 MODULE_DESCRIPTION("WinSystems WS16C48 GPIO driver");
327 MODULE_LICENSE("GPL v2");
328