|
11 | 11 | #include <linux/module.h> |
12 | 12 | #include <linux/bitops.h> |
13 | 13 | #include <linux/errno.h> |
| 14 | +#include <linux/interrupt.h> |
14 | 15 | #include <linux/platform_device.h> |
15 | 16 | #include <linux/gpio/driver.h> |
16 | 17 | #include <linux/mfd/kempld.h> |
|
19 | 20 | #define KEMPLD_GPIO_MASK(x) (BIT((x) % 8)) |
20 | 21 | #define KEMPLD_GPIO_DIR 0x40 |
21 | 22 | #define KEMPLD_GPIO_LVL 0x42 |
| 23 | +#define KEMPLD_GPIO_STS 0x44 |
22 | 24 | #define KEMPLD_GPIO_EVT_LVL_EDGE 0x46 |
| 25 | +#define KEMPLD_GPIO_EVT_LOW_HIGH 0x48 |
23 | 26 | #define KEMPLD_GPIO_IEN 0x4A |
| 27 | +#define KEMPLD_GPIO_OUT_LVL 0x4E |
| 28 | + |
| 29 | +/* The IRQ to use if none was configured in the BIOS */ |
| 30 | +static unsigned int gpio_irq; |
| 31 | +module_param_hw(gpio_irq, uint, irq, 0444); |
| 32 | +MODULE_PARM_DESC(gpio_irq, "Set legacy GPIO IRQ (1-15)"); |
24 | 33 |
|
25 | 34 | struct kempld_gpio_data { |
26 | 35 | struct gpio_chip chip; |
27 | 36 | struct kempld_device_data *pld; |
28 | 37 | u8 out_lvl_reg; |
| 38 | + |
| 39 | + struct mutex irq_lock; |
| 40 | + u16 ien; |
| 41 | + u16 evt_low_high; |
| 42 | + u16 evt_lvl_edge; |
29 | 43 | }; |
30 | 44 |
|
31 | 45 | /* |
@@ -193,6 +207,180 @@ static int kempld_gpio_pincount(struct kempld_device_data *pld) |
193 | 207 | return evt ? __ffs(evt) : 16; |
194 | 208 | } |
195 | 209 |
|
| 210 | +static void kempld_irq_mask(struct irq_data *data) |
| 211 | +{ |
| 212 | + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); |
| 213 | + struct kempld_gpio_data *gpio = gpiochip_get_data(chip); |
| 214 | + |
| 215 | + gpio->ien &= ~BIT(irqd_to_hwirq(data)); |
| 216 | + gpiochip_disable_irq(chip, irqd_to_hwirq(data)); |
| 217 | +} |
| 218 | + |
| 219 | +static void kempld_irq_unmask(struct irq_data *data) |
| 220 | +{ |
| 221 | + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); |
| 222 | + struct kempld_gpio_data *gpio = gpiochip_get_data(chip); |
| 223 | + |
| 224 | + gpiochip_enable_irq(chip, irqd_to_hwirq(data)); |
| 225 | + gpio->ien |= BIT(irqd_to_hwirq(data)); |
| 226 | +} |
| 227 | + |
| 228 | +static int kempld_irq_set_type(struct irq_data *data, unsigned int type) |
| 229 | +{ |
| 230 | + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); |
| 231 | + struct kempld_gpio_data *gpio = gpiochip_get_data(chip); |
| 232 | + |
| 233 | + switch (type) { |
| 234 | + case IRQ_TYPE_EDGE_RISING: |
| 235 | + gpio->evt_low_high |= BIT(data->hwirq); |
| 236 | + gpio->evt_lvl_edge |= BIT(data->hwirq); |
| 237 | + break; |
| 238 | + case IRQ_TYPE_EDGE_FALLING: |
| 239 | + gpio->evt_low_high &= ~BIT(data->hwirq); |
| 240 | + gpio->evt_lvl_edge |= BIT(data->hwirq); |
| 241 | + break; |
| 242 | + case IRQ_TYPE_LEVEL_HIGH: |
| 243 | + gpio->evt_low_high |= BIT(data->hwirq); |
| 244 | + gpio->evt_lvl_edge &= ~BIT(data->hwirq); |
| 245 | + break; |
| 246 | + case IRQ_TYPE_LEVEL_LOW: |
| 247 | + gpio->evt_low_high &= ~BIT(data->hwirq); |
| 248 | + gpio->evt_lvl_edge &= ~BIT(data->hwirq); |
| 249 | + break; |
| 250 | + default: |
| 251 | + return -EINVAL; |
| 252 | + } |
| 253 | + |
| 254 | + return 0; |
| 255 | +} |
| 256 | + |
| 257 | +static void kempld_irq_bus_lock(struct irq_data *data) |
| 258 | +{ |
| 259 | + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); |
| 260 | + struct kempld_gpio_data *gpio = gpiochip_get_data(chip); |
| 261 | + |
| 262 | + mutex_lock(&gpio->irq_lock); |
| 263 | +} |
| 264 | + |
| 265 | +static void kempld_irq_bus_sync_unlock(struct irq_data *data) |
| 266 | +{ |
| 267 | + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); |
| 268 | + struct kempld_gpio_data *gpio = gpiochip_get_data(chip); |
| 269 | + struct kempld_device_data *pld = gpio->pld; |
| 270 | + |
| 271 | + kempld_get_mutex(pld); |
| 272 | + kempld_write16(pld, KEMPLD_GPIO_EVT_LVL_EDGE, gpio->evt_lvl_edge); |
| 273 | + kempld_write16(pld, KEMPLD_GPIO_EVT_LOW_HIGH, gpio->evt_low_high); |
| 274 | + kempld_write16(pld, KEMPLD_GPIO_IEN, gpio->ien); |
| 275 | + kempld_release_mutex(pld); |
| 276 | + |
| 277 | + mutex_unlock(&gpio->irq_lock); |
| 278 | +} |
| 279 | + |
| 280 | +static const struct irq_chip kempld_irqchip = { |
| 281 | + .name = "kempld-gpio", |
| 282 | + .irq_mask = kempld_irq_mask, |
| 283 | + .irq_unmask = kempld_irq_unmask, |
| 284 | + .irq_set_type = kempld_irq_set_type, |
| 285 | + .irq_bus_lock = kempld_irq_bus_lock, |
| 286 | + .irq_bus_sync_unlock = kempld_irq_bus_sync_unlock, |
| 287 | + .flags = IRQCHIP_IMMUTABLE, |
| 288 | + GPIOCHIP_IRQ_RESOURCE_HELPERS, |
| 289 | +}; |
| 290 | + |
| 291 | +static irqreturn_t kempld_gpio_irq_handler(int irq, void *data) |
| 292 | +{ |
| 293 | + struct kempld_gpio_data *gpio = data; |
| 294 | + struct gpio_chip *chip = &gpio->chip; |
| 295 | + unsigned int pin, child_irq; |
| 296 | + unsigned long status; |
| 297 | + |
| 298 | + kempld_get_mutex(gpio->pld); |
| 299 | + |
| 300 | + status = kempld_read16(gpio->pld, KEMPLD_GPIO_STS); |
| 301 | + if (status) |
| 302 | + kempld_write16(gpio->pld, KEMPLD_GPIO_STS, status); |
| 303 | + |
| 304 | + kempld_release_mutex(gpio->pld); |
| 305 | + |
| 306 | + status &= gpio->ien; |
| 307 | + if (!status) |
| 308 | + return IRQ_NONE; |
| 309 | + |
| 310 | + for_each_set_bit(pin, &status, chip->ngpio) { |
| 311 | + child_irq = irq_find_mapping(chip->irq.domain, pin); |
| 312 | + handle_nested_irq(child_irq); |
| 313 | + } |
| 314 | + |
| 315 | + return IRQ_HANDLED; |
| 316 | +} |
| 317 | + |
| 318 | +static int kempld_gpio_irq_init(struct device *dev, |
| 319 | + struct kempld_gpio_data *gpio) |
| 320 | +{ |
| 321 | + struct kempld_device_data *pld = gpio->pld; |
| 322 | + struct gpio_chip *chip = &gpio->chip; |
| 323 | + struct gpio_irq_chip *girq; |
| 324 | + unsigned int irq; |
| 325 | + int ret; |
| 326 | + |
| 327 | + /* Get the IRQ configured by the BIOS in the PLD */ |
| 328 | + kempld_get_mutex(pld); |
| 329 | + irq = kempld_read8(pld, KEMPLD_IRQ_GPIO); |
| 330 | + kempld_release_mutex(pld); |
| 331 | + |
| 332 | + if (irq == 0xff) { |
| 333 | + dev_info(dev, "GPIO controller has no IRQ support\n"); |
| 334 | + return 0; |
| 335 | + } |
| 336 | + |
| 337 | + /* Allow overriding the IRQ with the module parameter */ |
| 338 | + if (gpio_irq > 0) { |
| 339 | + dev_warn(dev, "Forcing IRQ to %d\n", gpio_irq); |
| 340 | + irq &= ~KEMPLD_IRQ_GPIO_MASK; |
| 341 | + irq |= gpio_irq & KEMPLD_IRQ_GPIO_MASK; |
| 342 | + } |
| 343 | + |
| 344 | + if (!(irq & KEMPLD_IRQ_GPIO_MASK)) { |
| 345 | + dev_warn(dev, "No IRQ configured\n"); |
| 346 | + return 0; |
| 347 | + } |
| 348 | + |
| 349 | + /* Get the current config, disable all child interrupts, clear them |
| 350 | + * and set the parent IRQ |
| 351 | + */ |
| 352 | + kempld_get_mutex(pld); |
| 353 | + gpio->evt_low_high = kempld_read16(pld, KEMPLD_GPIO_EVT_LOW_HIGH); |
| 354 | + gpio->evt_lvl_edge = kempld_read16(pld, KEMPLD_GPIO_EVT_LVL_EDGE); |
| 355 | + kempld_write16(pld, KEMPLD_GPIO_IEN, 0); |
| 356 | + kempld_write16(pld, KEMPLD_GPIO_STS, 0xFFFF); |
| 357 | + kempld_write16(pld, KEMPLD_IRQ_GPIO, irq); |
| 358 | + kempld_release_mutex(pld); |
| 359 | + |
| 360 | + girq = &chip->irq; |
| 361 | + gpio_irq_chip_set_chip(girq, &kempld_irqchip); |
| 362 | + |
| 363 | + girq->parent_handler = NULL; |
| 364 | + girq->num_parents = 0; |
| 365 | + girq->parents = NULL; |
| 366 | + girq->default_type = IRQ_TYPE_NONE; |
| 367 | + girq->handler = handle_simple_irq; |
| 368 | + girq->threaded = true; |
| 369 | + |
| 370 | + mutex_init(&gpio->irq_lock); |
| 371 | + |
| 372 | + ret = devm_request_threaded_irq(dev, irq & KEMPLD_IRQ_GPIO_MASK, |
| 373 | + NULL, kempld_gpio_irq_handler, |
| 374 | + IRQF_ONESHOT, chip->label, |
| 375 | + gpio); |
| 376 | + if (ret) { |
| 377 | + dev_err(dev, "failed to request irq %d\n", irq); |
| 378 | + return ret; |
| 379 | + } |
| 380 | + |
| 381 | + return 0; |
| 382 | +} |
| 383 | + |
196 | 384 | static int kempld_gpio_probe(struct platform_device *pdev) |
197 | 385 | { |
198 | 386 | struct device *dev = &pdev->dev; |
@@ -247,6 +435,10 @@ static int kempld_gpio_probe(struct platform_device *pdev) |
247 | 435 | return -ENODEV; |
248 | 436 | } |
249 | 437 |
|
| 438 | + ret = kempld_gpio_irq_init(dev, gpio); |
| 439 | + if (ret) |
| 440 | + return ret; |
| 441 | + |
250 | 442 | ret = devm_gpiochip_add_data(dev, chip, gpio); |
251 | 443 | if (ret) { |
252 | 444 | dev_err(dev, "Could not register GPIO chip\n"); |
|
0 commit comments