|
20 | 20 | #include <linux/pm_runtime.h> |
21 | 21 | #include <linux/reset.h> |
22 | 22 | #include <linux/spinlock.h> |
| 23 | +#include <linux/syscore_ops.h> |
23 | 24 |
|
24 | 25 | /* DT "interrupts" indexes */ |
25 | 26 | #define ICU_IRQ_START 1 |
|
89 | 90 | #define ICU_RZG3E_TSSEL_MAX_VAL 0x8c |
90 | 91 | #define ICU_RZV2H_TSSEL_MAX_VAL 0x55 |
91 | 92 |
|
| 93 | +/** |
| 94 | + * struct rzv2h_irqc_reg_cache - registers cache (necessary for suspend/resume) |
| 95 | + * @nitsr: ICU_NITSR register |
| 96 | + * @iitsr: ICU_IITSR register |
| 97 | + * @titsr: ICU_TITSR registers |
| 98 | + */ |
| 99 | +struct rzv2h_irqc_reg_cache { |
| 100 | + u32 nitsr; |
| 101 | + u32 iitsr; |
| 102 | + u32 titsr[2]; |
| 103 | +}; |
| 104 | + |
92 | 105 | /** |
93 | 106 | * struct rzv2h_hw_info - Interrupt Control Unit controller hardware info structure. |
94 | 107 | * @tssel_lut: TINT lookup table |
@@ -118,13 +131,15 @@ struct rzv2h_hw_info { |
118 | 131 | * @fwspec: IRQ firmware specific data |
119 | 132 | * @lock: Lock to serialize access to hardware registers |
120 | 133 | * @info: Pointer to struct rzv2h_hw_info |
| 134 | + * @cache: Registers cache for suspend/resume |
121 | 135 | */ |
122 | | -struct rzv2h_icu_priv { |
| 136 | +static struct rzv2h_icu_priv { |
123 | 137 | void __iomem *base; |
124 | 138 | struct irq_fwspec fwspec[ICU_NUM_IRQ]; |
125 | 139 | raw_spinlock_t lock; |
126 | 140 | const struct rzv2h_hw_info *info; |
127 | | -}; |
| 141 | + struct rzv2h_irqc_reg_cache cache; |
| 142 | +} *rzv2h_icu_data; |
128 | 143 |
|
129 | 144 | void rzv2h_icu_register_dma_req(struct platform_device *icu_dev, u8 dmac_index, u8 dmac_channel, |
130 | 145 | u16 req_no) |
@@ -412,6 +427,44 @@ static int rzv2h_icu_set_type(struct irq_data *d, unsigned int type) |
412 | 427 | return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH); |
413 | 428 | } |
414 | 429 |
|
| 430 | +static int rzv2h_irqc_irq_suspend(void *data) |
| 431 | +{ |
| 432 | + struct rzv2h_irqc_reg_cache *cache = &rzv2h_icu_data->cache; |
| 433 | + void __iomem *base = rzv2h_icu_data->base; |
| 434 | + |
| 435 | + cache->nitsr = readl_relaxed(base + ICU_NITSR); |
| 436 | + cache->iitsr = readl_relaxed(base + ICU_IITSR); |
| 437 | + for (unsigned int i = 0; i < 2; i++) |
| 438 | + cache->titsr[i] = readl_relaxed(base + rzv2h_icu_data->info->t_offs + ICU_TITSR(i)); |
| 439 | + |
| 440 | + return 0; |
| 441 | +} |
| 442 | + |
| 443 | +static void rzv2h_irqc_irq_resume(void *data) |
| 444 | +{ |
| 445 | + struct rzv2h_irqc_reg_cache *cache = &rzv2h_icu_data->cache; |
| 446 | + void __iomem *base = rzv2h_icu_data->base; |
| 447 | + |
| 448 | + /* |
| 449 | + * Restore only interrupt type. TSSRx will be restored at the |
| 450 | + * request of pin controller to avoid spurious interrupts due |
| 451 | + * to invalid PIN states. |
| 452 | + */ |
| 453 | + for (unsigned int i = 0; i < 2; i++) |
| 454 | + writel_relaxed(cache->titsr[i], base + rzv2h_icu_data->info->t_offs + ICU_TITSR(i)); |
| 455 | + writel_relaxed(cache->iitsr, base + ICU_IITSR); |
| 456 | + writel_relaxed(cache->nitsr, base + ICU_NITSR); |
| 457 | +} |
| 458 | + |
| 459 | +static const struct syscore_ops rzv2h_irqc_syscore_ops = { |
| 460 | + .suspend = rzv2h_irqc_irq_suspend, |
| 461 | + .resume = rzv2h_irqc_irq_resume, |
| 462 | +}; |
| 463 | + |
| 464 | +static struct syscore rzv2h_irqc_syscore = { |
| 465 | + .ops = &rzv2h_irqc_syscore_ops, |
| 466 | +}; |
| 467 | + |
415 | 468 | static const struct irq_chip rzv2h_icu_chip = { |
416 | 469 | .name = "rzv2h-icu", |
417 | 470 | .irq_eoi = rzv2h_icu_eoi, |
@@ -495,7 +548,6 @@ static int rzv2h_icu_probe_common(struct platform_device *pdev, struct device_no |
495 | 548 | { |
496 | 549 | struct irq_domain *irq_domain, *parent_domain; |
497 | 550 | struct device_node *node = pdev->dev.of_node; |
498 | | - struct rzv2h_icu_priv *rzv2h_icu_data; |
499 | 551 | struct reset_control *resetn; |
500 | 552 | int ret; |
501 | 553 |
|
@@ -553,6 +605,8 @@ static int rzv2h_icu_probe_common(struct platform_device *pdev, struct device_no |
553 | 605 |
|
554 | 606 | rzv2h_icu_data->info = hw_info; |
555 | 607 |
|
| 608 | + register_syscore(&rzv2h_irqc_syscore); |
| 609 | + |
556 | 610 | /* |
557 | 611 | * coccicheck complains about a missing put_device call before returning, but it's a false |
558 | 612 | * positive. We still need &pdev->dev after successfully returning from this function. |
|
0 commit comments