aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKrzysztof Kozlowski <krzk@kernel.org>2018-07-23 13:52:58 -0400
committerKrzysztof Kozlowski <krzk@kernel.org>2018-07-24 15:56:41 -0400
commita8be2af0218cf037704dc2e733bf56d6560fa324 (patch)
tree415b4f0a874e0f9a4f73dd9b40ab7c865d5f09e2
parente5cda42c16d89720c29678f51d95a119490ef7d8 (diff)
pinctrl: samsung: Write external wakeup interrupt mask
The pinctrl driver defines an IRQ chip which handles external wakeup interrupts, therefore from logical point of view, it is the owner of external interrupt mask. The register controlling the mask belongs to Power Management Unit address space so it has to be accessed with PMU syscon regmap handle. This mask should be written to hardware during system suspend. Till now ARMv7 machine code was responsible for this which created a dependency between pin controller driver and arch/arm/mach code. Try to rework this dependency so the pinctrl driver will write external wakeup interrupt mask during late suspend. Impact on ARMv7 designs (S5Pv210 and Exynos) ============================================ This duplicates setting mask with existing machine code arch/arm/mach-exynos/suspend.c and arch/arm/mach-s5pv210/pm.c but it is not a problem - the wakeup mask register will be written twice. The machine code will be cleaned up later. The difference between implementation here and ARMv7 machine code (arch/arm/mach-*) is the time of writing the mask: 1. The machine code is writing the mask quite late during system suspend path, after offlining secondary CPUs and just before doing actual suspend. 2. The implementation in pinctrl driver uses late suspend ops, therefore it will write the mask much earlier. Hopefully late enough, after all drivers will enable or disable their interrupt wakeups (enable_irq_wake() etc). Impact on ARMv8 designs (Exynos5433 and Exynos7) ================================================ The Suspend to RAM was not supported and external wakeup interrupt mask was not written to HW. This change brings us one step closer to supporting Suspend to RAM. Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> Cc: Tomasz Figa <tomasz.figa@gmail.com> Cc: Sylwester Nawrocki <snawrocki@kernel.org> Acked-by: Tomasz Figa <tomasz.figa@gmail.com> Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
-rw-r--r--drivers/pinctrl/samsung/pinctrl-exynos.c50
-rw-r--r--drivers/pinctrl/samsung/pinctrl-samsung.h3
-rw-r--r--include/linux/soc/samsung/exynos-regs-pmu.h2
3 files changed, 54 insertions, 1 deletions
diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.c b/drivers/pinctrl/samsung/pinctrl-exynos.c
index 29d86d704b0c..40ef14956876 100644
--- a/drivers/pinctrl/samsung/pinctrl-exynos.c
+++ b/drivers/pinctrl/samsung/pinctrl-exynos.c
@@ -25,6 +25,7 @@
25#include <linux/regmap.h> 25#include <linux/regmap.h>
26#include <linux/err.h> 26#include <linux/err.h>
27#include <linux/soc/samsung/exynos-pmu.h> 27#include <linux/soc/samsung/exynos-pmu.h>
28#include <linux/soc/samsung/exynos-regs-pmu.h>
28 29
29#include <dt-bindings/pinctrl/samsung.h> 30#include <dt-bindings/pinctrl/samsung.h>
30 31
@@ -37,6 +38,8 @@ struct exynos_irq_chip {
37 u32 eint_con; 38 u32 eint_con;
38 u32 eint_mask; 39 u32 eint_mask;
39 u32 eint_pend; 40 u32 eint_pend;
41 u32 eint_wake_mask_value;
42 u32 eint_wake_mask_reg;
40}; 43};
41 44
42static inline struct exynos_irq_chip *to_exynos_irq_chip(struct irq_chip *chip) 45static inline struct exynos_irq_chip *to_exynos_irq_chip(struct irq_chip *chip)
@@ -215,6 +218,7 @@ static struct exynos_irq_chip exynos_gpio_irq_chip = {
215 .eint_con = EXYNOS_GPIO_ECON_OFFSET, 218 .eint_con = EXYNOS_GPIO_ECON_OFFSET,
216 .eint_mask = EXYNOS_GPIO_EMASK_OFFSET, 219 .eint_mask = EXYNOS_GPIO_EMASK_OFFSET,
217 .eint_pend = EXYNOS_GPIO_EPEND_OFFSET, 220 .eint_pend = EXYNOS_GPIO_EPEND_OFFSET,
221 /* eint_wake_mask_value not used */
218}; 222};
219 223
220static int exynos_eint_irq_map(struct irq_domain *h, unsigned int virq, 224static int exynos_eint_irq_map(struct irq_domain *h, unsigned int virq,
@@ -330,6 +334,8 @@ u32 exynos_get_eint_wake_mask(void)
330 334
331static int exynos_wkup_irq_set_wake(struct irq_data *irqd, unsigned int on) 335static int exynos_wkup_irq_set_wake(struct irq_data *irqd, unsigned int on)
332{ 336{
337 struct irq_chip *chip = irq_data_get_irq_chip(irqd);
338 struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
333 struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); 339 struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
334 unsigned long bit = 1UL << (2 * bank->eint_offset + irqd->hwirq); 340 unsigned long bit = 1UL << (2 * bank->eint_offset + irqd->hwirq);
335 341
@@ -339,6 +345,7 @@ static int exynos_wkup_irq_set_wake(struct irq_data *irqd, unsigned int on)
339 exynos_eint_wake_mask |= bit; 345 exynos_eint_wake_mask |= bit;
340 else 346 else
341 exynos_eint_wake_mask &= ~bit; 347 exynos_eint_wake_mask &= ~bit;
348 our_chip->eint_wake_mask_value = exynos_eint_wake_mask;
342 349
343 return 0; 350 return 0;
344} 351}
@@ -360,6 +367,9 @@ static const struct exynos_irq_chip s5pv210_wkup_irq_chip __initconst = {
360 .eint_con = EXYNOS_WKUP_ECON_OFFSET, 367 .eint_con = EXYNOS_WKUP_ECON_OFFSET,
361 .eint_mask = EXYNOS_WKUP_EMASK_OFFSET, 368 .eint_mask = EXYNOS_WKUP_EMASK_OFFSET,
362 .eint_pend = EXYNOS_WKUP_EPEND_OFFSET, 369 .eint_pend = EXYNOS_WKUP_EPEND_OFFSET,
370 .eint_wake_mask_value = EXYNOS_EINT_WAKEUP_MASK_DISABLED,
371 /* Only difference with exynos4210_wkup_irq_chip: */
372 .eint_wake_mask_reg = S5PV210_EINT_WAKEUP_MASK,
363}; 373};
364 374
365static const struct exynos_irq_chip exynos4210_wkup_irq_chip __initconst = { 375static const struct exynos_irq_chip exynos4210_wkup_irq_chip __initconst = {
@@ -376,6 +386,8 @@ static const struct exynos_irq_chip exynos4210_wkup_irq_chip __initconst = {
376 .eint_con = EXYNOS_WKUP_ECON_OFFSET, 386 .eint_con = EXYNOS_WKUP_ECON_OFFSET,
377 .eint_mask = EXYNOS_WKUP_EMASK_OFFSET, 387 .eint_mask = EXYNOS_WKUP_EMASK_OFFSET,
378 .eint_pend = EXYNOS_WKUP_EPEND_OFFSET, 388 .eint_pend = EXYNOS_WKUP_EPEND_OFFSET,
389 .eint_wake_mask_value = EXYNOS_EINT_WAKEUP_MASK_DISABLED,
390 .eint_wake_mask_reg = EXYNOS_EINT_WAKEUP_MASK,
379}; 391};
380 392
381static const struct exynos_irq_chip exynos7_wkup_irq_chip __initconst = { 393static const struct exynos_irq_chip exynos7_wkup_irq_chip __initconst = {
@@ -392,6 +404,8 @@ static const struct exynos_irq_chip exynos7_wkup_irq_chip __initconst = {
392 .eint_con = EXYNOS7_WKUP_ECON_OFFSET, 404 .eint_con = EXYNOS7_WKUP_ECON_OFFSET,
393 .eint_mask = EXYNOS7_WKUP_EMASK_OFFSET, 405 .eint_mask = EXYNOS7_WKUP_EMASK_OFFSET,
394 .eint_pend = EXYNOS7_WKUP_EPEND_OFFSET, 406 .eint_pend = EXYNOS7_WKUP_EPEND_OFFSET,
407 .eint_wake_mask_value = EXYNOS_EINT_WAKEUP_MASK_DISABLED,
408 .eint_wake_mask_reg = EXYNOS5433_EINT_WAKEUP_MASK,
395}; 409};
396 410
397/* list of external wakeup controllers supported */ 411/* list of external wakeup controllers supported */
@@ -560,6 +574,27 @@ int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
560 return 0; 574 return 0;
561} 575}
562 576
577static void
578exynos_pinctrl_set_eint_wakeup_mask(struct samsung_pinctrl_drv_data *drvdata,
579 struct exynos_irq_chip *irq_chip)
580{
581 struct regmap *pmu_regs;
582
583 if (!drvdata->retention_ctrl || !drvdata->retention_ctrl->priv) {
584 dev_warn(drvdata->dev,
585 "No retention data configured bank with external wakeup interrupt. Wake-up mask will not be set.\n");
586 return;
587 }
588
589 pmu_regs = drvdata->retention_ctrl->priv;
590 dev_info(drvdata->dev,
591 "Setting external wakeup interrupt wakeup mask: 0x%x\n",
592 irq_chip->eint_wake_mask_value);
593
594 regmap_write(pmu_regs, irq_chip->eint_wake_mask_reg,
595 irq_chip->eint_wake_mask_value);
596}
597
563static void exynos_pinctrl_suspend_bank( 598static void exynos_pinctrl_suspend_bank(
564 struct samsung_pinctrl_drv_data *drvdata, 599 struct samsung_pinctrl_drv_data *drvdata,
565 struct samsung_pin_bank *bank) 600 struct samsung_pin_bank *bank)
@@ -582,11 +617,24 @@ static void exynos_pinctrl_suspend_bank(
582void exynos_pinctrl_suspend(struct samsung_pinctrl_drv_data *drvdata) 617void exynos_pinctrl_suspend(struct samsung_pinctrl_drv_data *drvdata)
583{ 618{
584 struct samsung_pin_bank *bank = drvdata->pin_banks; 619 struct samsung_pin_bank *bank = drvdata->pin_banks;
620 struct exynos_irq_chip *irq_chip = NULL;
585 int i; 621 int i;
586 622
587 for (i = 0; i < drvdata->nr_banks; ++i, ++bank) 623 for (i = 0; i < drvdata->nr_banks; ++i, ++bank) {
588 if (bank->eint_type == EINT_TYPE_GPIO) 624 if (bank->eint_type == EINT_TYPE_GPIO)
589 exynos_pinctrl_suspend_bank(drvdata, bank); 625 exynos_pinctrl_suspend_bank(drvdata, bank);
626 else if (bank->eint_type == EINT_TYPE_WKUP) {
627 if (!irq_chip) {
628 irq_chip = bank->irq_chip;
629 exynos_pinctrl_set_eint_wakeup_mask(drvdata,
630 irq_chip);
631 } else if (bank->irq_chip != irq_chip) {
632 dev_warn(drvdata->dev,
633 "More than one external wakeup interrupt chip configured (bank: %s). This is not supported by hardware nor by driver.\n",
634 bank->name);
635 }
636 }
637 }
590} 638}
591 639
592static void exynos_pinctrl_resume_bank( 640static void exynos_pinctrl_resume_bank(
diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.h b/drivers/pinctrl/samsung/pinctrl-samsung.h
index aac16cc8362a..e571bbd7139b 100644
--- a/drivers/pinctrl/samsung/pinctrl-samsung.h
+++ b/drivers/pinctrl/samsung/pinctrl-samsung.h
@@ -227,6 +227,9 @@ struct samsung_retention_data {
227 * device suspend, see samsung_pinctrl_suspend() 227 * device suspend, see samsung_pinctrl_suspend()
228 * @resume: platform specific resume callback, executed during pin controller 228 * @resume: platform specific resume callback, executed during pin controller
229 * device suspend, see samsung_pinctrl_resume() 229 * device suspend, see samsung_pinctrl_resume()
230 *
231 * External wakeup interrupts must define at least eint_wkup_init,
232 * retention_data and suspend in order for proper suspend/resume to work.
230 */ 233 */
231struct samsung_pin_ctrl { 234struct samsung_pin_ctrl {
232 const struct samsung_pin_bank_data *pin_banks; 235 const struct samsung_pin_bank_data *pin_banks;
diff --git a/include/linux/soc/samsung/exynos-regs-pmu.h b/include/linux/soc/samsung/exynos-regs-pmu.h
index eb0d240df7a7..5addaf5ccbce 100644
--- a/include/linux/soc/samsung/exynos-regs-pmu.h
+++ b/include/linux/soc/samsung/exynos-regs-pmu.h
@@ -42,6 +42,8 @@
42#define EXYNOS_SWRESET 0x0400 42#define EXYNOS_SWRESET 0x0400
43 43
44#define S5P_WAKEUP_STAT 0x0600 44#define S5P_WAKEUP_STAT 0x0600
45/* Value for EXYNOS_EINT_WAKEUP_MASK disabling all external wakeup interrupts */
46#define EXYNOS_EINT_WAKEUP_MASK_DISABLED 0xffffffff
45#define EXYNOS_EINT_WAKEUP_MASK 0x0604 47#define EXYNOS_EINT_WAKEUP_MASK 0x0604
46#define S5P_WAKEUP_MASK 0x0608 48#define S5P_WAKEUP_MASK 0x0608
47#define S5P_WAKEUP_MASK2 0x0614 49#define S5P_WAKEUP_MASK2 0x0614