1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * GPIO library for the ACCES IDIO-16 family |
4 | * Copyright (C) 2022 William Breathitt Gray |
5 | */ |
6 | #include <linux/bits.h> |
7 | #include <linux/device.h> |
8 | #include <linux/err.h> |
9 | #include <linux/export.h> |
10 | #include <linux/gpio/regmap.h> |
11 | #include <linux/module.h> |
12 | #include <linux/regmap.h> |
13 | #include <linux/types.h> |
14 | |
15 | #include "gpio-idio-16.h" |
16 | |
17 | #define DEFAULT_SYMBOL_NAMESPACE GPIO_IDIO_16 |
18 | |
19 | #define IDIO_16_DAT_BASE 0x0 |
20 | #define IDIO_16_OUT_BASE IDIO_16_DAT_BASE |
21 | #define IDIO_16_IN_BASE (IDIO_16_DAT_BASE + 1) |
22 | #define IDIO_16_CLEAR_INTERRUPT 0x1 |
23 | #define IDIO_16_ENABLE_IRQ 0x2 |
24 | #define IDIO_16_DEACTIVATE_INPUT_FILTERS 0x3 |
25 | #define IDIO_16_DISABLE_IRQ IDIO_16_ENABLE_IRQ |
26 | #define IDIO_16_INTERRUPT_STATUS 0x6 |
27 | |
28 | #define IDIO_16_NGPIO 32 |
29 | #define IDIO_16_NGPIO_PER_REG 8 |
30 | #define IDIO_16_REG_STRIDE 4 |
31 | |
32 | struct idio_16_data { |
33 | struct regmap *map; |
34 | unsigned int irq_mask; |
35 | }; |
36 | |
37 | static int idio_16_handle_mask_sync(const int index, const unsigned int mask_buf_def, |
38 | const unsigned int mask_buf, void *const irq_drv_data) |
39 | { |
40 | struct idio_16_data *const data = irq_drv_data; |
41 | const unsigned int prev_mask = data->irq_mask; |
42 | int err; |
43 | unsigned int val; |
44 | |
45 | /* exit early if no change since the previous mask */ |
46 | if (mask_buf == prev_mask) |
47 | return 0; |
48 | |
49 | /* remember the current mask for the next mask sync */ |
50 | data->irq_mask = mask_buf; |
51 | |
52 | /* if all previously masked, enable interrupts when unmasking */ |
53 | if (prev_mask == mask_buf_def) { |
54 | err = regmap_write(map: data->map, IDIO_16_CLEAR_INTERRUPT, val: 0x00); |
55 | if (err) |
56 | return err; |
57 | return regmap_read(map: data->map, IDIO_16_ENABLE_IRQ, val: &val); |
58 | } |
59 | |
60 | /* if all are currently masked, disable interrupts */ |
61 | if (mask_buf == mask_buf_def) |
62 | return regmap_write(map: data->map, IDIO_16_DISABLE_IRQ, val: 0x00); |
63 | |
64 | return 0; |
65 | } |
66 | |
67 | static int idio_16_reg_mask_xlate(struct gpio_regmap *const gpio, const unsigned int base, |
68 | const unsigned int offset, unsigned int *const reg, |
69 | unsigned int *const mask) |
70 | { |
71 | unsigned int stride; |
72 | |
73 | /* Input lines start at GPIO 16 */ |
74 | if (offset < 16) { |
75 | stride = offset / IDIO_16_NGPIO_PER_REG; |
76 | *reg = IDIO_16_OUT_BASE + stride * IDIO_16_REG_STRIDE; |
77 | } else { |
78 | stride = (offset - 16) / IDIO_16_NGPIO_PER_REG; |
79 | *reg = IDIO_16_IN_BASE + stride * IDIO_16_REG_STRIDE; |
80 | } |
81 | |
82 | *mask = BIT(offset % IDIO_16_NGPIO_PER_REG); |
83 | |
84 | return 0; |
85 | } |
86 | |
87 | static const char *idio_16_names[IDIO_16_NGPIO] = { |
88 | "OUT0" , "OUT1" , "OUT2" , "OUT3" , "OUT4" , "OUT5" , "OUT6" , "OUT7" , |
89 | "OUT8" , "OUT9" , "OUT10" , "OUT11" , "OUT12" , "OUT13" , "OUT14" , "OUT15" , |
90 | "IIN0" , "IIN1" , "IIN2" , "IIN3" , "IIN4" , "IIN5" , "IIN6" , "IIN7" , |
91 | "IIN8" , "IIN9" , "IIN10" , "IIN11" , "IIN12" , "IIN13" , "IIN14" , "IIN15" , |
92 | }; |
93 | |
94 | /** |
95 | * devm_idio_16_regmap_register - Register an IDIO-16 GPIO device |
96 | * @dev: device that is registering this IDIO-16 GPIO device |
97 | * @config: configuration for idio_16_regmap_config |
98 | * |
99 | * Registers an IDIO-16 GPIO device. Returns 0 on success and negative error number on failure. |
100 | */ |
101 | int devm_idio_16_regmap_register(struct device *const dev, |
102 | const struct idio_16_regmap_config *const config) |
103 | { |
104 | struct gpio_regmap_config gpio_config = {}; |
105 | int err; |
106 | struct idio_16_data *data; |
107 | struct regmap_irq_chip *chip; |
108 | struct regmap_irq_chip_data *chip_data; |
109 | |
110 | if (!config->parent) |
111 | return -EINVAL; |
112 | |
113 | if (!config->map) |
114 | return -EINVAL; |
115 | |
116 | if (!config->regmap_irqs) |
117 | return -EINVAL; |
118 | |
119 | data = devm_kzalloc(dev, size: sizeof(*data), GFP_KERNEL); |
120 | if (!data) |
121 | return -ENOMEM; |
122 | data->map = config->map; |
123 | |
124 | chip = devm_kzalloc(dev, size: sizeof(*chip), GFP_KERNEL); |
125 | if (!chip) |
126 | return -ENOMEM; |
127 | |
128 | chip->name = dev_name(dev); |
129 | chip->status_base = IDIO_16_INTERRUPT_STATUS; |
130 | chip->mask_base = IDIO_16_ENABLE_IRQ; |
131 | chip->ack_base = IDIO_16_CLEAR_INTERRUPT; |
132 | chip->no_status = config->no_status; |
133 | chip->num_regs = 1; |
134 | chip->irqs = config->regmap_irqs; |
135 | chip->num_irqs = config->num_regmap_irqs; |
136 | chip->handle_mask_sync = idio_16_handle_mask_sync; |
137 | chip->irq_drv_data = data; |
138 | |
139 | /* Disable IRQ to prevent spurious interrupts before we're ready */ |
140 | err = regmap_write(map: data->map, IDIO_16_DISABLE_IRQ, val: 0x00); |
141 | if (err) |
142 | return err; |
143 | |
144 | err = devm_regmap_add_irq_chip(dev, map: data->map, irq: config->irq, irq_flags: 0, irq_base: 0, chip, data: &chip_data); |
145 | if (err) |
146 | return dev_err_probe(dev, err, fmt: "IRQ registration failed\n" ); |
147 | |
148 | if (config->filters) { |
149 | /* Deactivate input filters */ |
150 | err = regmap_write(map: data->map, IDIO_16_DEACTIVATE_INPUT_FILTERS, val: 0x00); |
151 | if (err) |
152 | return err; |
153 | } |
154 | |
155 | gpio_config.parent = config->parent; |
156 | gpio_config.regmap = data->map; |
157 | gpio_config.ngpio = IDIO_16_NGPIO; |
158 | gpio_config.names = idio_16_names; |
159 | gpio_config.reg_dat_base = GPIO_REGMAP_ADDR(IDIO_16_DAT_BASE); |
160 | gpio_config.reg_set_base = GPIO_REGMAP_ADDR(IDIO_16_DAT_BASE); |
161 | gpio_config.ngpio_per_reg = IDIO_16_NGPIO_PER_REG; |
162 | gpio_config.reg_stride = IDIO_16_REG_STRIDE; |
163 | gpio_config.irq_domain = regmap_irq_get_domain(data: chip_data); |
164 | gpio_config.reg_mask_xlate = idio_16_reg_mask_xlate; |
165 | |
166 | return PTR_ERR_OR_ZERO(ptr: devm_gpio_regmap_register(dev, config: &gpio_config)); |
167 | } |
168 | EXPORT_SYMBOL_GPL(devm_idio_16_regmap_register); |
169 | |
170 | MODULE_AUTHOR("William Breathitt Gray" ); |
171 | MODULE_DESCRIPTION("ACCES IDIO-16 GPIO Library" ); |
172 | MODULE_LICENSE("GPL" ); |
173 | |