|
9 | 9 | #include <linux/device.h> |
10 | 10 | #include <linux/errno.h> |
11 | 11 | #include <linux/gpio/driver.h> |
12 | | -#include <linux/init.h> |
| 12 | +#include <linux/interrupt.h> |
13 | 13 | #include <linux/mod_devicetable.h> |
| 14 | +#include <linux/of_irq.h> |
14 | 15 | #include <linux/platform_device.h> |
15 | 16 | #include <linux/property.h> |
16 | 17 | #include <linux/regmap.h> |
17 | 18 | #include <linux/spinlock.h> |
18 | 19 |
|
19 | 20 | #define MPFS_GPIO_CTRL(i) (0x4 * (i)) |
20 | 21 | #define MPFS_MAX_NUM_GPIO 32 |
21 | | -#define MPFS_GPIO_EN_INT 3 |
| 22 | +#define MPFS_GPIO_EN_INT BIT(3) |
22 | 23 | #define MPFS_GPIO_EN_OUT_BUF BIT(2) |
23 | 24 | #define MPFS_GPIO_EN_IN BIT(1) |
24 | 25 | #define MPFS_GPIO_EN_OUT BIT(0) |
@@ -52,6 +53,7 @@ static const struct regmap_config mpfs_gpio_regmap_config = { |
52 | 53 | .reg_bits = 32, |
53 | 54 | .reg_stride = 4, |
54 | 55 | .val_bits = 32, |
| 56 | + .use_raw_spinlock = true, |
55 | 57 | }; |
56 | 58 |
|
57 | 59 | static int mpfs_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio_index) |
@@ -114,13 +116,98 @@ static int mpfs_gpio_set(struct gpio_chip *gc, unsigned int gpio_index, int valu |
114 | 116 | return ret; |
115 | 117 | } |
116 | 118 |
|
| 119 | +static int mpfs_gpio_irq_set_type(struct irq_data *data, unsigned int type) |
| 120 | +{ |
| 121 | + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); |
| 122 | + struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); |
| 123 | + int gpio_index = irqd_to_hwirq(data) % 32; |
| 124 | + u32 interrupt_type; |
| 125 | + |
| 126 | + switch (type) { |
| 127 | + case IRQ_TYPE_EDGE_BOTH: |
| 128 | + interrupt_type = MPFS_GPIO_TYPE_INT_EDGE_BOTH; |
| 129 | + break; |
| 130 | + case IRQ_TYPE_EDGE_FALLING: |
| 131 | + interrupt_type = MPFS_GPIO_TYPE_INT_EDGE_NEG; |
| 132 | + break; |
| 133 | + case IRQ_TYPE_EDGE_RISING: |
| 134 | + interrupt_type = MPFS_GPIO_TYPE_INT_EDGE_POS; |
| 135 | + break; |
| 136 | + case IRQ_TYPE_LEVEL_HIGH: |
| 137 | + interrupt_type = MPFS_GPIO_TYPE_INT_LEVEL_HIGH; |
| 138 | + break; |
| 139 | + case IRQ_TYPE_LEVEL_LOW: |
| 140 | + interrupt_type = MPFS_GPIO_TYPE_INT_LEVEL_LOW; |
| 141 | + break; |
| 142 | + } |
| 143 | + |
| 144 | + regmap_update_bits(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index), |
| 145 | + MPFS_GPIO_TYPE_INT_MASK, interrupt_type); |
| 146 | + |
| 147 | + return 0; |
| 148 | +} |
| 149 | + |
| 150 | +static void mpfs_gpio_irq_unmask(struct irq_data *data) |
| 151 | +{ |
| 152 | + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); |
| 153 | + struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); |
| 154 | + int gpio_index = irqd_to_hwirq(data) % 32; |
| 155 | + |
| 156 | + gpiochip_enable_irq(gc, gpio_index); |
| 157 | + mpfs_gpio_direction_input(gc, gpio_index); |
| 158 | + regmap_update_bits(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index), |
| 159 | + MPFS_GPIO_EN_INT, MPFS_GPIO_EN_INT); |
| 160 | +} |
| 161 | + |
| 162 | +static void mpfs_gpio_irq_mask(struct irq_data *data) |
| 163 | +{ |
| 164 | + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); |
| 165 | + struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); |
| 166 | + int gpio_index = irqd_to_hwirq(data) % 32; |
| 167 | + |
| 168 | + regmap_update_bits(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index), |
| 169 | + MPFS_GPIO_EN_INT, 0); |
| 170 | + gpiochip_disable_irq(gc, gpio_index); |
| 171 | +} |
| 172 | + |
| 173 | +static const struct irq_chip mpfs_gpio_irqchip = { |
| 174 | + .name = "MPFS GPIO", |
| 175 | + .irq_set_type = mpfs_gpio_irq_set_type, |
| 176 | + .irq_mask = mpfs_gpio_irq_mask, |
| 177 | + .irq_unmask = mpfs_gpio_irq_unmask, |
| 178 | + .flags = IRQCHIP_IMMUTABLE | IRQCHIP_MASK_ON_SUSPEND, |
| 179 | + GPIOCHIP_IRQ_RESOURCE_HELPERS, |
| 180 | +}; |
| 181 | + |
| 182 | +static void mpfs_gpio_irq_handler(struct irq_desc *desc) |
| 183 | +{ |
| 184 | + struct irq_chip *irqchip = irq_desc_get_chip(desc); |
| 185 | + struct mpfs_gpio_chip *mpfs_gpio = irq_desc_get_handler_data(desc); |
| 186 | + unsigned long status; |
| 187 | + u32 val; |
| 188 | + int i; |
| 189 | + |
| 190 | + chained_irq_enter(irqchip, desc); |
| 191 | + |
| 192 | + regmap_read(mpfs_gpio->regs, MPFS_IRQ_REG, &val); |
| 193 | + status = val; |
| 194 | + for_each_set_bit(i, &status, MPFS_MAX_NUM_GPIO) { |
| 195 | + regmap_write(mpfs_gpio->regs, MPFS_IRQ_REG, BIT(i)); |
| 196 | + generic_handle_domain_irq(mpfs_gpio->gc.irq.domain, i); |
| 197 | + } |
| 198 | + |
| 199 | + chained_irq_exit(irqchip, desc); |
| 200 | +} |
| 201 | + |
117 | 202 | static int mpfs_gpio_probe(struct platform_device *pdev) |
118 | 203 | { |
119 | 204 | struct device *dev = &pdev->dev; |
| 205 | + struct device_node *node = dev->of_node; |
120 | 206 | struct mpfs_gpio_chip *mpfs_gpio; |
| 207 | + struct gpio_irq_chip *girq; |
121 | 208 | struct clk *clk; |
122 | 209 | void __iomem *base; |
123 | | - int ngpios; |
| 210 | + int ngpios, nirqs, ret; |
124 | 211 |
|
125 | 212 | mpfs_gpio = devm_kzalloc(dev, sizeof(*mpfs_gpio), GFP_KERNEL); |
126 | 213 | if (!mpfs_gpio) |
@@ -157,6 +244,35 @@ static int mpfs_gpio_probe(struct platform_device *pdev) |
157 | 244 | mpfs_gpio->gc.parent = dev; |
158 | 245 | mpfs_gpio->gc.owner = THIS_MODULE; |
159 | 246 |
|
| 247 | + nirqs = of_irq_count(node); |
| 248 | + if (nirqs > MPFS_MAX_NUM_GPIO) |
| 249 | + return -ENXIO; |
| 250 | + |
| 251 | + if (nirqs) { |
| 252 | + girq = &mpfs_gpio->gc.irq; |
| 253 | + |
| 254 | + gpio_irq_chip_set_chip(girq, &mpfs_gpio_irqchip); |
| 255 | + |
| 256 | + girq->num_parents = nirqs; |
| 257 | + girq->parents = devm_kcalloc(&pdev->dev, girq->num_parents, |
| 258 | + sizeof(*girq->parents), GFP_KERNEL); |
| 259 | + if (!girq->parents) |
| 260 | + return -ENOMEM; |
| 261 | + |
| 262 | + for (int i = 0; i < nirqs; i++) { |
| 263 | + ret = platform_get_irq(pdev, i); |
| 264 | + if (ret < 0) |
| 265 | + return ret; |
| 266 | + |
| 267 | + girq->parents[i] = ret; |
| 268 | + girq->parent_handler_data = mpfs_gpio; |
| 269 | + girq->parent_handler = mpfs_gpio_irq_handler; |
| 270 | + } |
| 271 | + |
| 272 | + girq->handler = handle_level_irq; |
| 273 | + girq->default_type = IRQ_TYPE_NONE; |
| 274 | + } |
| 275 | + |
160 | 276 | return devm_gpiochip_add_data(dev, &mpfs_gpio->gc, mpfs_gpio); |
161 | 277 | } |
162 | 278 |
|
|
0 commit comments