Loading Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt +4 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,10 @@ Optional occupied by the redistributors. Required if more than one such region is present. - ignored-save-restore-irqs: Array of u32 elements, specifying the interrupts which are ignored while doing gicd save/restore. Maximum of 10 elements is supported at present. Sub-nodes: PPI affinity can be expressed as a single "ppi-partitions" node, Loading arch/arm/include/asm/arch_gicv3.h +9 −0 Original line number Diff line number Diff line Loading @@ -242,6 +242,15 @@ static inline void gic_write_irouter(u64 val, volatile void __iomem *addr) writel_relaxed((u32)(val >> 32), addr + 4); } static inline u64 gic_read_irouter(const volatile void __iomem *addr) { u64 val; val = readl_relaxed(addr); val |= (u64)readl_relaxed(addr + 4) << 32; return val; } static inline u64 gic_read_typer(const volatile void __iomem *addr) { u64 val; Loading arch/arm64/include/asm/arch_gicv3.h +1 −0 Original line number Diff line number Diff line Loading @@ -228,6 +228,7 @@ static inline void gic_write_bpr1(u32 val) } #define gic_read_typer(c) readq_relaxed_no_log(c) #define gic_read_irouter(c) readq_relaxed_no_log(c) #define gic_write_irouter(v, c) writeq_relaxed_no_log(v, c) #endif /* __ASSEMBLY__ */ Loading drivers/irqchip/irq-gic-v3.c +323 −1 Original line number Diff line number Diff line Loading @@ -44,6 +44,34 @@ #include "irq-gic-common.h" #define MAX_IRQ 1020U /* Max number of SGI+PPI+SPI */ #define SPI_START_IRQ 32 /* SPI start irq number */ #define GICD_ICFGR_BITS 2 /* 2 bits per irq in GICD_ICFGR */ #define GICD_ISENABLER_BITS 1 /* 1 bit per irq in GICD_ISENABLER */ #define GICD_IPRIORITYR_BITS 8 /* 8 bits per irq in GICD_IPRIORITYR */ /* 32 bit mask with lower n bits set */ #define UMASK_LOW(n) (~0U >> (32 - (n))) /* Number of 32-bit words required to store all irqs, for * registers where each word stores configuration for each irq * in bits_per_irq bits. */ #define NUM_IRQ_WORDS(bits_per_irq) (DIV_ROUND_UP(MAX_IRQ, \ 32 / (bits_per_irq))) #define MAX_IRQS_IGNORE 10 #define IRQ_NR_BOUND(nr) min((nr), MAX_IRQ) /* Bitmap to irqs, which are restored */ static DECLARE_BITMAP(irqs_restore, MAX_IRQ); /* Bitmap to irqs, for which restore is ignored. * Presently, only GICD_IROUTER mismatches are * ignored. */ static DECLARE_BITMAP(irqs_ignore_restore, MAX_IRQ); struct redist_region { void __iomem *redist_base; phys_addr_t phys_base; Loading @@ -60,6 +88,16 @@ struct gic_chip_data { u32 nr_redist_regions; unsigned int irq_nr; struct partition_desc *ppi_descs[16]; u64 saved_spi_router[MAX_IRQ]; u32 saved_spi_enable[NUM_IRQ_WORDS(GICD_ISENABLER_BITS)]; u32 saved_spi_cfg[NUM_IRQ_WORDS(GICD_ICFGR_BITS)]; u32 saved_spi_priority[NUM_IRQ_WORDS(GICD_IPRIORITYR_BITS)]; u64 changed_spi_router[MAX_IRQ]; u32 changed_spi_enable[NUM_IRQ_WORDS(GICD_ISENABLER_BITS)]; u32 changed_spi_cfg[NUM_IRQ_WORDS(GICD_ICFGR_BITS)]; u32 changed_spi_priority[NUM_IRQ_WORDS(GICD_IPRIORITYR_BITS)]; }; static struct gic_chip_data gic_data __read_mostly; Loading @@ -67,6 +105,58 @@ static struct static_key supports_deactivate = STATIC_KEY_INIT_TRUE; static struct gic_kvm_info gic_v3_kvm_info; enum gicd_save_restore_reg { SAVED_ICFGR, SAVED_IS_ENABLER, SAVED_IPRIORITYR, NUM_SAVED_GICD_REGS, }; /* Stores start address of spi config for saved gicd regs */ static u32 *saved_spi_regs_start[NUM_SAVED_GICD_REGS] = { [SAVED_ICFGR] = gic_data.saved_spi_cfg, [SAVED_IS_ENABLER] = gic_data.saved_spi_enable, [SAVED_IPRIORITYR] = gic_data.saved_spi_priority, }; /* Stores start address of spi config for changed gicd regs */ static u32 *changed_spi_regs_start[NUM_SAVED_GICD_REGS] = { [SAVED_ICFGR] = gic_data.changed_spi_cfg, [SAVED_IS_ENABLER] = gic_data.changed_spi_enable, [SAVED_IPRIORITYR] = gic_data.changed_spi_priority, }; /* GICD offset for saved registers */ static u32 gicd_offset[NUM_SAVED_GICD_REGS] = { [SAVED_ICFGR] = GICD_ICFGR, [SAVED_IS_ENABLER] = GICD_ISENABLER, [SAVED_IPRIORITYR] = GICD_IPRIORITYR, }; /* Bits per irq word, for gicd saved registers */ static u32 gicd_reg_bits_per_irq[NUM_SAVED_GICD_REGS] = { [SAVED_ICFGR] = GICD_ICFGR_BITS, [SAVED_IS_ENABLER] = GICD_ISENABLER_BITS, [SAVED_IPRIORITYR] = GICD_IPRIORITYR_BITS, }; #define for_each_spi_irq_word(i, reg) \ for (i = 0; \ i < DIV_ROUND_UP(IRQ_NR_BOUND(gic_data.irq_nr) - SPI_START_IRQ, \ 32 / gicd_reg_bits_per_irq[reg]); \ i++) #define read_spi_word_offset(base, reg, i) \ readl_relaxed_no_log( \ base + gicd_offset[reg] + i * 4 + \ SPI_START_IRQ * gicd_reg_bits_per_irq[reg] / 8) #define restore_spi_word_offset(base, reg, i) \ writel_relaxed_no_log( \ saved_spi_regs_start[reg][i],\ base + gicd_offset[reg] + i * 4 + \ SPI_START_IRQ * gicd_reg_bits_per_irq[reg] / 8) #define gic_data_rdist() (this_cpu_ptr(gic_data.rdists.rdist)) #define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base) #define gic_data_rdist_sgi_base() (gic_data_rdist_rd_base() + SZ_64K) Loading Loading @@ -134,6 +224,229 @@ static u64 __maybe_unused gic_read_iar(void) } #endif void gic_v3_dist_save(void) { void __iomem *base = gic_data.dist_base; int reg, i; for (reg = SAVED_ICFGR; reg < NUM_SAVED_GICD_REGS; reg++) { for_each_spi_irq_word(i, reg) { saved_spi_regs_start[reg][i] = read_spi_word_offset(base, reg, i); } } for (i = 32; i < IRQ_NR_BOUND(gic_data.irq_nr); i++) gic_data.saved_spi_router[i] = gic_read_irouter(base + GICD_IROUTER + i * 8); } static void _gicd_check_reg(enum gicd_save_restore_reg reg) { void __iomem *base = gic_data.dist_base; u32 *saved_spi_cfg = saved_spi_regs_start[reg]; u32 *changed_spi_cfg = changed_spi_regs_start[reg]; u32 bits_per_irq = gicd_reg_bits_per_irq[reg]; u32 current_cfg = 0; int i, j = SPI_START_IRQ, l; u32 k; for_each_spi_irq_word(i, reg) { current_cfg = read_spi_word_offset(base, reg, i); if (current_cfg != saved_spi_cfg[i]) { for (k = current_cfg ^ saved_spi_cfg[i], l = 0; k ; k >>= bits_per_irq, l++) { if (k & UMASK_LOW(bits_per_irq)) set_bit(j+l, irqs_restore); } changed_spi_cfg[i] = current_cfg ^ saved_spi_cfg[i]; } j += 32 / bits_per_irq; } } #define _gic_v3_dist_check_icfgr() \ _gicd_check_reg(SAVED_ICFGR) #define _gic_v3_dist_check_ipriorityr() \ _gicd_check_reg(SAVED_IPRIORITYR) #define _gic_v3_dist_check_isenabler() \ _gicd_check_reg(SAVED_IS_ENABLER) static void _gic_v3_dist_check_irouter(void) { void __iomem *base = gic_data.dist_base; u64 current_irouter_cfg = 0; int i; for (i = 32; i < IRQ_NR_BOUND(gic_data.irq_nr); i++) { if (test_bit(i, irqs_ignore_restore)) continue; current_irouter_cfg = gic_read_irouter( base + GICD_IROUTER + i * 8); if (current_irouter_cfg != gic_data.saved_spi_router[i]) { set_bit(i, irqs_restore); gic_data.changed_spi_router[i] = current_irouter_cfg ^ gic_data.saved_spi_router[i]; } } } static void _gic_v3_dist_restore_reg(enum gicd_save_restore_reg reg) { void __iomem *base = gic_data.dist_base; int i; for_each_spi_irq_word(i, reg) { if (changed_spi_regs_start[reg][i]) restore_spi_word_offset(base, reg, i); } /* Commit all restored configurations before subsequent writes */ wmb(); } #define _gic_v3_dist_restore_icfgr() _gic_v3_dist_restore_reg(SAVED_ICFGR) #define _gic_v3_dist_restore_ipriorityr() \ _gic_v3_dist_restore_reg(SAVED_IPRIORITYR) static void _gic_v3_dist_restore_set_reg(u32 offset) { void __iomem *base = gic_data.dist_base; int i, j = SPI_START_IRQ, l; int irq_nr = IRQ_NR_BOUND(gic_data.irq_nr) - SPI_START_IRQ; for (i = 0; i < DIV_ROUND_UP(irq_nr, 32); i++, j += 32) { u32 reg_val = readl_relaxed_no_log(base + offset + i * 4 + 4); bool irqs_restore_updated = 0; for (l = 0; l < 32; l++) { if (test_bit(j+l, irqs_restore)) { reg_val |= BIT(l); irqs_restore_updated = 1; } } if (irqs_restore_updated) { writel_relaxed_no_log( reg_val, base + offset + i * 4 + 4); } } /* Commit restored configuration updates before subsequent writes */ wmb(); } #define _gic_v3_dist_restore_isenabler() \ _gic_v3_dist_restore_set_reg(GICD_ISENABLER) #define _gic_v3_dist_restore_ispending() \ _gic_v3_dist_restore_set_reg(GICD_ISPENDR) static void _gic_v3_dist_restore_irouter(void) { void __iomem *base = gic_data.dist_base; int i; for (i = 32; i < IRQ_NR_BOUND(gic_data.irq_nr); i++) { if (test_bit(i, irqs_ignore_restore)) continue; if (gic_data.changed_spi_router[i]) { gic_write_irouter(gic_data.saved_spi_router[i], base + GICD_IROUTER + i * 8); } } /* Commit GICD_IROUTER writes before subsequent writes */ wmb(); } static void _gic_v3_dist_clear_reg(u32 offset) { void __iomem *base = gic_data.dist_base; int i, j = SPI_START_IRQ, l; int irq_nr = IRQ_NR_BOUND(gic_data.irq_nr) - SPI_START_IRQ; for (i = 0; i < DIV_ROUND_UP(irq_nr, 32); i++, j += 32) { u32 clear = 0; bool irqs_restore_updated = 0; for (l = 0; l < 32; l++) { if (test_bit(j+l, irqs_restore)) { clear |= BIT(l); irqs_restore_updated = 1; } } if (irqs_restore_updated) { writel_relaxed_no_log( clear, base + offset + i * 4 + 4); } } /* Commit clearing of irq config before subsequent writes */ wmb(); } #define _gic_v3_dist_set_icenabler() \ _gic_v3_dist_clear_reg(GICD_ICENABLER) #define _gic_v3_dist_set_icpending() \ _gic_v3_dist_clear_reg(GICD_ICPENDR) #define _gic_v3_dist_set_icactive() \ _gic_v3_dist_clear_reg(GICD_ICACTIVER) /* Restore GICD state for SPIs. SPI configuration is restored * for GICD_ICFGR, GICD_ISENABLER, GICD_IPRIORITYR, GICD_IROUTER * registers. Following is the sequence for restore: * * 1. For SPIs, check whether any of GICD_ICFGR, GICD_ISENABLER, * GICD_IPRIORITYR, GICD_IROUTER, current configuration is * different from saved configuration. * * For all irqs, with mismatched configurations, * * 2. Set GICD_ICENABLER and wait for its completion. * * 3. Restore any changed GICD_ICFGR, GICD_IPRIORITYR, GICD_IROUTER * configurations. * * 4. Set GICD_ICACTIVER. * * 5. Set pending for the interrupt. * * 6. Enable interrupt and wait for its completion. * */ void gic_v3_dist_restore(void) { _gic_v3_dist_check_icfgr(); _gic_v3_dist_check_ipriorityr(); _gic_v3_dist_check_isenabler(); _gic_v3_dist_check_irouter(); if (bitmap_empty(irqs_restore, IRQ_NR_BOUND(gic_data.irq_nr))) return; _gic_v3_dist_set_icenabler(); gic_dist_wait_for_rwp(); _gic_v3_dist_restore_icfgr(); _gic_v3_dist_restore_ipriorityr(); _gic_v3_dist_restore_irouter(); _gic_v3_dist_set_icactive(); _gic_v3_dist_set_icpending(); _gic_v3_dist_restore_ispending(); _gic_v3_dist_restore_isenabler(); gic_dist_wait_for_rwp(); /* Commit all writes before proceeding */ wmb(); } /* * gic_show_pending_irq - Shows the pending interrupts * Note: Interrupts should be disabled on the cpu from which Loading Loading @@ -1244,7 +1557,8 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare struct redist_region *rdist_regs; u64 redist_stride; u32 nr_redist_regions; int err, i; int err, i, ignore_irqs_len; u32 ignore_restore_irqs[MAX_IRQS_IGNORE] = {0}; dist_base = of_iomap(node, 0); if (!dist_base) { Loading Loading @@ -1294,6 +1608,14 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare gic_populate_ppi_partitions(node); gic_of_setup_kvm_info(node); ignore_irqs_len = of_property_read_variable_u32_array(node, "ignored-save-restore-irqs", ignore_restore_irqs, 0, MAX_IRQS_IGNORE); for (i = 0; i < ignore_irqs_len; i++) set_bit(ignore_restore_irqs[i], irqs_ignore_restore); return 0; out_unmap_rdist: Loading drivers/soc/qcom/system_pm.c +9 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,13 @@ #define PDC_TIME_VALID_SHIFT 31 #define PDC_TIME_UPPER_MASK 0xFFFFFF #ifdef CONFIG_ARM_GIC_V3 #include <linux/irqchip/arm-gic-v3.h> #else static inline void gic_v3_dist_restore(void) {} static inline void gic_v3_dist_save(void) {} #endif static struct rpmh_client *rpmh_client; static int setup_wakeup(uint32_t lo, uint32_t hi) Loading Loading @@ -61,6 +68,7 @@ static bool system_sleep_allowed(void) */ static int system_sleep_enter(struct cpumask *mask) { gic_v3_dist_save(); return rpmh_flush(rpmh_client); } Loading @@ -70,6 +78,7 @@ static int system_sleep_enter(struct cpumask *mask) static void system_sleep_exit(void) { msm_rpmh_master_stats_update(); gic_v3_dist_restore(); } static struct system_pm_ops pm_ops = { Loading Loading
Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt +4 −0 Original line number Diff line number Diff line Loading @@ -57,6 +57,10 @@ Optional occupied by the redistributors. Required if more than one such region is present. - ignored-save-restore-irqs: Array of u32 elements, specifying the interrupts which are ignored while doing gicd save/restore. Maximum of 10 elements is supported at present. Sub-nodes: PPI affinity can be expressed as a single "ppi-partitions" node, Loading
arch/arm/include/asm/arch_gicv3.h +9 −0 Original line number Diff line number Diff line Loading @@ -242,6 +242,15 @@ static inline void gic_write_irouter(u64 val, volatile void __iomem *addr) writel_relaxed((u32)(val >> 32), addr + 4); } static inline u64 gic_read_irouter(const volatile void __iomem *addr) { u64 val; val = readl_relaxed(addr); val |= (u64)readl_relaxed(addr + 4) << 32; return val; } static inline u64 gic_read_typer(const volatile void __iomem *addr) { u64 val; Loading
arch/arm64/include/asm/arch_gicv3.h +1 −0 Original line number Diff line number Diff line Loading @@ -228,6 +228,7 @@ static inline void gic_write_bpr1(u32 val) } #define gic_read_typer(c) readq_relaxed_no_log(c) #define gic_read_irouter(c) readq_relaxed_no_log(c) #define gic_write_irouter(v, c) writeq_relaxed_no_log(v, c) #endif /* __ASSEMBLY__ */ Loading
drivers/irqchip/irq-gic-v3.c +323 −1 Original line number Diff line number Diff line Loading @@ -44,6 +44,34 @@ #include "irq-gic-common.h" #define MAX_IRQ 1020U /* Max number of SGI+PPI+SPI */ #define SPI_START_IRQ 32 /* SPI start irq number */ #define GICD_ICFGR_BITS 2 /* 2 bits per irq in GICD_ICFGR */ #define GICD_ISENABLER_BITS 1 /* 1 bit per irq in GICD_ISENABLER */ #define GICD_IPRIORITYR_BITS 8 /* 8 bits per irq in GICD_IPRIORITYR */ /* 32 bit mask with lower n bits set */ #define UMASK_LOW(n) (~0U >> (32 - (n))) /* Number of 32-bit words required to store all irqs, for * registers where each word stores configuration for each irq * in bits_per_irq bits. */ #define NUM_IRQ_WORDS(bits_per_irq) (DIV_ROUND_UP(MAX_IRQ, \ 32 / (bits_per_irq))) #define MAX_IRQS_IGNORE 10 #define IRQ_NR_BOUND(nr) min((nr), MAX_IRQ) /* Bitmap to irqs, which are restored */ static DECLARE_BITMAP(irqs_restore, MAX_IRQ); /* Bitmap to irqs, for which restore is ignored. * Presently, only GICD_IROUTER mismatches are * ignored. */ static DECLARE_BITMAP(irqs_ignore_restore, MAX_IRQ); struct redist_region { void __iomem *redist_base; phys_addr_t phys_base; Loading @@ -60,6 +88,16 @@ struct gic_chip_data { u32 nr_redist_regions; unsigned int irq_nr; struct partition_desc *ppi_descs[16]; u64 saved_spi_router[MAX_IRQ]; u32 saved_spi_enable[NUM_IRQ_WORDS(GICD_ISENABLER_BITS)]; u32 saved_spi_cfg[NUM_IRQ_WORDS(GICD_ICFGR_BITS)]; u32 saved_spi_priority[NUM_IRQ_WORDS(GICD_IPRIORITYR_BITS)]; u64 changed_spi_router[MAX_IRQ]; u32 changed_spi_enable[NUM_IRQ_WORDS(GICD_ISENABLER_BITS)]; u32 changed_spi_cfg[NUM_IRQ_WORDS(GICD_ICFGR_BITS)]; u32 changed_spi_priority[NUM_IRQ_WORDS(GICD_IPRIORITYR_BITS)]; }; static struct gic_chip_data gic_data __read_mostly; Loading @@ -67,6 +105,58 @@ static struct static_key supports_deactivate = STATIC_KEY_INIT_TRUE; static struct gic_kvm_info gic_v3_kvm_info; enum gicd_save_restore_reg { SAVED_ICFGR, SAVED_IS_ENABLER, SAVED_IPRIORITYR, NUM_SAVED_GICD_REGS, }; /* Stores start address of spi config for saved gicd regs */ static u32 *saved_spi_regs_start[NUM_SAVED_GICD_REGS] = { [SAVED_ICFGR] = gic_data.saved_spi_cfg, [SAVED_IS_ENABLER] = gic_data.saved_spi_enable, [SAVED_IPRIORITYR] = gic_data.saved_spi_priority, }; /* Stores start address of spi config for changed gicd regs */ static u32 *changed_spi_regs_start[NUM_SAVED_GICD_REGS] = { [SAVED_ICFGR] = gic_data.changed_spi_cfg, [SAVED_IS_ENABLER] = gic_data.changed_spi_enable, [SAVED_IPRIORITYR] = gic_data.changed_spi_priority, }; /* GICD offset for saved registers */ static u32 gicd_offset[NUM_SAVED_GICD_REGS] = { [SAVED_ICFGR] = GICD_ICFGR, [SAVED_IS_ENABLER] = GICD_ISENABLER, [SAVED_IPRIORITYR] = GICD_IPRIORITYR, }; /* Bits per irq word, for gicd saved registers */ static u32 gicd_reg_bits_per_irq[NUM_SAVED_GICD_REGS] = { [SAVED_ICFGR] = GICD_ICFGR_BITS, [SAVED_IS_ENABLER] = GICD_ISENABLER_BITS, [SAVED_IPRIORITYR] = GICD_IPRIORITYR_BITS, }; #define for_each_spi_irq_word(i, reg) \ for (i = 0; \ i < DIV_ROUND_UP(IRQ_NR_BOUND(gic_data.irq_nr) - SPI_START_IRQ, \ 32 / gicd_reg_bits_per_irq[reg]); \ i++) #define read_spi_word_offset(base, reg, i) \ readl_relaxed_no_log( \ base + gicd_offset[reg] + i * 4 + \ SPI_START_IRQ * gicd_reg_bits_per_irq[reg] / 8) #define restore_spi_word_offset(base, reg, i) \ writel_relaxed_no_log( \ saved_spi_regs_start[reg][i],\ base + gicd_offset[reg] + i * 4 + \ SPI_START_IRQ * gicd_reg_bits_per_irq[reg] / 8) #define gic_data_rdist() (this_cpu_ptr(gic_data.rdists.rdist)) #define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base) #define gic_data_rdist_sgi_base() (gic_data_rdist_rd_base() + SZ_64K) Loading Loading @@ -134,6 +224,229 @@ static u64 __maybe_unused gic_read_iar(void) } #endif void gic_v3_dist_save(void) { void __iomem *base = gic_data.dist_base; int reg, i; for (reg = SAVED_ICFGR; reg < NUM_SAVED_GICD_REGS; reg++) { for_each_spi_irq_word(i, reg) { saved_spi_regs_start[reg][i] = read_spi_word_offset(base, reg, i); } } for (i = 32; i < IRQ_NR_BOUND(gic_data.irq_nr); i++) gic_data.saved_spi_router[i] = gic_read_irouter(base + GICD_IROUTER + i * 8); } static void _gicd_check_reg(enum gicd_save_restore_reg reg) { void __iomem *base = gic_data.dist_base; u32 *saved_spi_cfg = saved_spi_regs_start[reg]; u32 *changed_spi_cfg = changed_spi_regs_start[reg]; u32 bits_per_irq = gicd_reg_bits_per_irq[reg]; u32 current_cfg = 0; int i, j = SPI_START_IRQ, l; u32 k; for_each_spi_irq_word(i, reg) { current_cfg = read_spi_word_offset(base, reg, i); if (current_cfg != saved_spi_cfg[i]) { for (k = current_cfg ^ saved_spi_cfg[i], l = 0; k ; k >>= bits_per_irq, l++) { if (k & UMASK_LOW(bits_per_irq)) set_bit(j+l, irqs_restore); } changed_spi_cfg[i] = current_cfg ^ saved_spi_cfg[i]; } j += 32 / bits_per_irq; } } #define _gic_v3_dist_check_icfgr() \ _gicd_check_reg(SAVED_ICFGR) #define _gic_v3_dist_check_ipriorityr() \ _gicd_check_reg(SAVED_IPRIORITYR) #define _gic_v3_dist_check_isenabler() \ _gicd_check_reg(SAVED_IS_ENABLER) static void _gic_v3_dist_check_irouter(void) { void __iomem *base = gic_data.dist_base; u64 current_irouter_cfg = 0; int i; for (i = 32; i < IRQ_NR_BOUND(gic_data.irq_nr); i++) { if (test_bit(i, irqs_ignore_restore)) continue; current_irouter_cfg = gic_read_irouter( base + GICD_IROUTER + i * 8); if (current_irouter_cfg != gic_data.saved_spi_router[i]) { set_bit(i, irqs_restore); gic_data.changed_spi_router[i] = current_irouter_cfg ^ gic_data.saved_spi_router[i]; } } } static void _gic_v3_dist_restore_reg(enum gicd_save_restore_reg reg) { void __iomem *base = gic_data.dist_base; int i; for_each_spi_irq_word(i, reg) { if (changed_spi_regs_start[reg][i]) restore_spi_word_offset(base, reg, i); } /* Commit all restored configurations before subsequent writes */ wmb(); } #define _gic_v3_dist_restore_icfgr() _gic_v3_dist_restore_reg(SAVED_ICFGR) #define _gic_v3_dist_restore_ipriorityr() \ _gic_v3_dist_restore_reg(SAVED_IPRIORITYR) static void _gic_v3_dist_restore_set_reg(u32 offset) { void __iomem *base = gic_data.dist_base; int i, j = SPI_START_IRQ, l; int irq_nr = IRQ_NR_BOUND(gic_data.irq_nr) - SPI_START_IRQ; for (i = 0; i < DIV_ROUND_UP(irq_nr, 32); i++, j += 32) { u32 reg_val = readl_relaxed_no_log(base + offset + i * 4 + 4); bool irqs_restore_updated = 0; for (l = 0; l < 32; l++) { if (test_bit(j+l, irqs_restore)) { reg_val |= BIT(l); irqs_restore_updated = 1; } } if (irqs_restore_updated) { writel_relaxed_no_log( reg_val, base + offset + i * 4 + 4); } } /* Commit restored configuration updates before subsequent writes */ wmb(); } #define _gic_v3_dist_restore_isenabler() \ _gic_v3_dist_restore_set_reg(GICD_ISENABLER) #define _gic_v3_dist_restore_ispending() \ _gic_v3_dist_restore_set_reg(GICD_ISPENDR) static void _gic_v3_dist_restore_irouter(void) { void __iomem *base = gic_data.dist_base; int i; for (i = 32; i < IRQ_NR_BOUND(gic_data.irq_nr); i++) { if (test_bit(i, irqs_ignore_restore)) continue; if (gic_data.changed_spi_router[i]) { gic_write_irouter(gic_data.saved_spi_router[i], base + GICD_IROUTER + i * 8); } } /* Commit GICD_IROUTER writes before subsequent writes */ wmb(); } static void _gic_v3_dist_clear_reg(u32 offset) { void __iomem *base = gic_data.dist_base; int i, j = SPI_START_IRQ, l; int irq_nr = IRQ_NR_BOUND(gic_data.irq_nr) - SPI_START_IRQ; for (i = 0; i < DIV_ROUND_UP(irq_nr, 32); i++, j += 32) { u32 clear = 0; bool irqs_restore_updated = 0; for (l = 0; l < 32; l++) { if (test_bit(j+l, irqs_restore)) { clear |= BIT(l); irqs_restore_updated = 1; } } if (irqs_restore_updated) { writel_relaxed_no_log( clear, base + offset + i * 4 + 4); } } /* Commit clearing of irq config before subsequent writes */ wmb(); } #define _gic_v3_dist_set_icenabler() \ _gic_v3_dist_clear_reg(GICD_ICENABLER) #define _gic_v3_dist_set_icpending() \ _gic_v3_dist_clear_reg(GICD_ICPENDR) #define _gic_v3_dist_set_icactive() \ _gic_v3_dist_clear_reg(GICD_ICACTIVER) /* Restore GICD state for SPIs. SPI configuration is restored * for GICD_ICFGR, GICD_ISENABLER, GICD_IPRIORITYR, GICD_IROUTER * registers. Following is the sequence for restore: * * 1. For SPIs, check whether any of GICD_ICFGR, GICD_ISENABLER, * GICD_IPRIORITYR, GICD_IROUTER, current configuration is * different from saved configuration. * * For all irqs, with mismatched configurations, * * 2. Set GICD_ICENABLER and wait for its completion. * * 3. Restore any changed GICD_ICFGR, GICD_IPRIORITYR, GICD_IROUTER * configurations. * * 4. Set GICD_ICACTIVER. * * 5. Set pending for the interrupt. * * 6. Enable interrupt and wait for its completion. * */ void gic_v3_dist_restore(void) { _gic_v3_dist_check_icfgr(); _gic_v3_dist_check_ipriorityr(); _gic_v3_dist_check_isenabler(); _gic_v3_dist_check_irouter(); if (bitmap_empty(irqs_restore, IRQ_NR_BOUND(gic_data.irq_nr))) return; _gic_v3_dist_set_icenabler(); gic_dist_wait_for_rwp(); _gic_v3_dist_restore_icfgr(); _gic_v3_dist_restore_ipriorityr(); _gic_v3_dist_restore_irouter(); _gic_v3_dist_set_icactive(); _gic_v3_dist_set_icpending(); _gic_v3_dist_restore_ispending(); _gic_v3_dist_restore_isenabler(); gic_dist_wait_for_rwp(); /* Commit all writes before proceeding */ wmb(); } /* * gic_show_pending_irq - Shows the pending interrupts * Note: Interrupts should be disabled on the cpu from which Loading Loading @@ -1244,7 +1557,8 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare struct redist_region *rdist_regs; u64 redist_stride; u32 nr_redist_regions; int err, i; int err, i, ignore_irqs_len; u32 ignore_restore_irqs[MAX_IRQS_IGNORE] = {0}; dist_base = of_iomap(node, 0); if (!dist_base) { Loading Loading @@ -1294,6 +1608,14 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare gic_populate_ppi_partitions(node); gic_of_setup_kvm_info(node); ignore_irqs_len = of_property_read_variable_u32_array(node, "ignored-save-restore-irqs", ignore_restore_irqs, 0, MAX_IRQS_IGNORE); for (i = 0; i < ignore_irqs_len; i++) set_bit(ignore_restore_irqs[i], irqs_ignore_restore); return 0; out_unmap_rdist: Loading
drivers/soc/qcom/system_pm.c +9 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,13 @@ #define PDC_TIME_VALID_SHIFT 31 #define PDC_TIME_UPPER_MASK 0xFFFFFF #ifdef CONFIG_ARM_GIC_V3 #include <linux/irqchip/arm-gic-v3.h> #else static inline void gic_v3_dist_restore(void) {} static inline void gic_v3_dist_save(void) {} #endif static struct rpmh_client *rpmh_client; static int setup_wakeup(uint32_t lo, uint32_t hi) Loading Loading @@ -61,6 +68,7 @@ static bool system_sleep_allowed(void) */ static int system_sleep_enter(struct cpumask *mask) { gic_v3_dist_save(); return rpmh_flush(rpmh_client); } Loading @@ -70,6 +78,7 @@ static int system_sleep_enter(struct cpumask *mask) static void system_sleep_exit(void) { msm_rpmh_master_stats_update(); gic_v3_dist_restore(); } static struct system_pm_ops pm_ops = { Loading