diff options
author | Varun Sethi <Varun.Sethi@freescale.com> | 2014-04-06 18:28:32 (GMT) |
---|---|---|
committer | Jose Rivera <German.Rivera@freescale.com> | 2014-04-07 18:14:18 (GMT) |
commit | 0cff9ad872c8aede1d76f6c87e1d6fa217a718e3 (patch) | |
tree | 48e82862d1f41f4a874bb7880bd3a7437960457c /drivers/iommu | |
parent | fb7f27080adc65cd5f341bdf56a1d0c14f316c1b (diff) | |
download | linux-fsl-qoriq-0cff9ad872c8aede1d76f6c87e1d6fa217a718e3.tar.xz |
iommu/fsl: PAMU power management support.
PAMU driver suspend and resume support.
Signed-off-by: Varun Sethi <Varun.Sethi@freescale.com>
Change-Id: I00eb16aa0fe8a591399349396aaced3df1dbb33b
Reviewed-on: http://git.am.freescale.net:8181/10716
Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com>
Reviewed-by: Jeffrey Ladouceur <Jeffrey.Ladouceur@freescale.com>
Reviewed-by: Jose Rivera <German.Rivera@freescale.com>
Diffstat (limited to 'drivers/iommu')
-rw-r--r-- | drivers/iommu/fsl_pamu.c | 153 |
1 files changed, 121 insertions, 32 deletions
diff --git a/drivers/iommu/fsl_pamu.c b/drivers/iommu/fsl_pamu.c index e7b3d45..e9843f0 100644 --- a/drivers/iommu/fsl_pamu.c +++ b/drivers/iommu/fsl_pamu.c @@ -33,6 +33,7 @@ #include <asm/bitops.h> #include <asm/fsl_guts.h> #include <asm/fsl_kibo.h> +#include <linux/syscore_ops.h> #include "fsl_pamu.h" @@ -44,10 +45,13 @@ #define make64(high, low) (((u64)(high) << 32) | (low)) -struct pamu_isr_data { +struct pamu_info { void __iomem *pamu_reg_base; /* Base address of PAMU regs*/ unsigned int count; /* The number of PAMUs */ -}; +} pamu_info_data; + +/* Pointer to the device configuration space */ +static struct ccsr_guts __iomem *guts_regs; static struct paace *ppaact; static struct paace *spaact; @@ -123,6 +127,34 @@ static struct paace *pamu_get_ppaace(int liodn) } /** + * set_dcfg_liodn() - set the device LIODN in DCFG + * @np: device tree node pointer + * @liodn: liodn value to program + * + * Returns 0 upon success else error code < 0 returned + */ +static int set_dcfg_liodn(struct device_node *np, int liodn) +{ + const __be32 *prop; + u32 liodn_reg_offset; + int len; + void __iomem *dcfg_region = (void *)guts_regs; + + if (!dcfg_region) + return -ENODEV; + + prop = of_get_property(np, "fsl,liodn-reg", &len); + if (!prop || len != 8) + return -EINVAL; + + liodn_reg_offset = be32_to_cpup(&prop[1]); + + out_be32((u32 *)(dcfg_region + liodn_reg_offset), liodn); + + return 0; +} + +/** * pamu_enable_liodn() - Set valid bit of PACCE * @liodn: liodn PAACT index for desired PAACE * @@ -823,7 +855,7 @@ static void __init setup_omt(struct ome *omt) * Get the maximum number of PAACT table entries * and subwindows supported by PAMU */ -static void get_pamu_cap_values(unsigned long pamu_reg_base) +static void get_pamu_cap_values(void *pamu_reg_base) { u32 pc_val; @@ -833,9 +865,8 @@ static void get_pamu_cap_values(unsigned long pamu_reg_base) } /* Setup PAMU registers pointing to PAACT, SPAACT and OMT */ -int setup_one_pamu(unsigned long pamu_reg_base, unsigned long pamu_reg_size, - phys_addr_t ppaact_phys, phys_addr_t spaact_phys, - phys_addr_t omt_phys) +int setup_one_pamu(void *pamu_reg_base, phys_addr_t ppaact_phys, + phys_addr_t spaact_phys, phys_addr_t omt_phys) { u32 *pc; struct pamu_mmap_regs *pamu_regs; @@ -959,7 +990,7 @@ static void __init setup_liodns(void) irqreturn_t pamu_av_isr(int irq, void *arg) { - struct pamu_isr_data *data = arg; + struct pamu_info *data = arg; phys_addr_t phys; unsigned int i, j, ret; @@ -1204,11 +1235,9 @@ static const struct { static int __init fsl_pamu_probe(struct platform_device *pdev) { void __iomem *pamu_regs = NULL; - struct ccsr_guts __iomem *guts_regs = NULL; u32 pamubypenr, pamu_counter; + void __iomem *pamu_reg_base; unsigned long pamu_reg_off; - unsigned long pamu_reg_base; - struct pamu_isr_data *data = NULL; struct device_node *guts_node; u64 size; struct page *p; @@ -1234,23 +1263,17 @@ static int __init fsl_pamu_probe(struct platform_device *pdev) } of_get_address(pdev->dev.of_node, 0, &size, NULL); + pamu_info_data.pamu_reg_base = pamu_regs; + pamu_info_data.count = size / PAMU_OFFSET; + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); if (irq == NO_IRQ) { dev_warn(&pdev->dev, "no interrupts listed in PAMU node\n"); goto error; } - data = kzalloc(sizeof(struct pamu_isr_data), GFP_KERNEL); - if (!data) { - dev_err(&pdev->dev, "PAMU isr data memory allocation failed\n"); - ret = -ENOMEM; - goto error; - } - data->pamu_reg_base = pamu_regs; - data->count = size / PAMU_OFFSET; - /* The ISR needs access to the regs, so we won't iounmap them */ - ret = request_irq(irq, pamu_av_isr, 0, "pamu", data); + ret = request_irq(irq, pamu_av_isr, 0, "pamu", &pamu_info_data); if (ret < 0) { dev_err(&pdev->dev, "error %i installing ISR for irq %i\n", ret, irq); @@ -1274,7 +1297,7 @@ static int __init fsl_pamu_probe(struct platform_device *pdev) } /* read in the PAMU capability registers */ - get_pamu_cap_values((unsigned long)pamu_regs); + get_pamu_cap_values(pamu_regs); /* * To simplify the allocation of a coherency domain, we allocate the * PAACT and the OMT in the same memory buffer. Unfortunately, this @@ -1353,9 +1376,9 @@ static int __init fsl_pamu_probe(struct platform_device *pdev) for (pamu_reg_off = 0, pamu_counter = 0x80000000; pamu_reg_off < size; pamu_reg_off += PAMU_OFFSET, pamu_counter >>= 1) { - pamu_reg_base = (unsigned long) pamu_regs + pamu_reg_off; - setup_one_pamu(pamu_reg_base, pamu_reg_off, ppaact_phys, - spaact_phys, omt_phys); + pamu_reg_base = pamu_regs + pamu_reg_off; + setup_one_pamu(pamu_reg_base, ppaact_phys, spaact_phys, + omt_phys); /* Disable PAMU bypass for this PAMU */ pamubypenr &= ~pamu_counter; } @@ -1365,8 +1388,6 @@ static int __init fsl_pamu_probe(struct platform_device *pdev) /* Enable all relevant PAMU(s) */ out_be32(&guts_regs->pamubypenr, pamubypenr); - iounmap(guts_regs); - /* Enable DMA for the LIODNs in the device tree*/ setup_liodns(); @@ -1378,12 +1399,7 @@ error_genpool: error: if (irq != NO_IRQ) - free_irq(irq, data); - - if (data) { - memset(data, 0, sizeof(struct pamu_isr_data)); - kfree(data); - } + free_irq(irq, &pamu_info_data); if (pamu_regs) iounmap(pamu_regs); @@ -1417,6 +1433,77 @@ static struct platform_driver fsl_of_pamu_driver = { .probe = fsl_pamu_probe, }; +#ifdef CONFIG_SUSPEND +static int iommu_suspend(void) +{ + int i; + + for (i = 0; i < pamu_info_data.count; i++) { + u32 val; + void __iomem *p; + + p = pamu_info_data.pamu_reg_base + i * PAMU_OFFSET; + val = in_be32((u32 *)(p + PAMU_PICS)); + /* Disable access violation interrupts */ + out_be32((u32 *)(p + PAMU_PICS), + val & ~PAMU_ACCESS_VIOLATION_ENABLE); + } + + return 0; +} + +static void restore_dcfg_liodns(void) +{ + struct device_node *node; + const __be32 *prop; + int ret, liodn; + + for_each_node_with_property(node, "fsl,liodn-reg") { + prop = of_get_property(node, "fsl,liodn", 0); + if (!prop) + continue; + liodn = be32_to_cpup(prop); + ret = set_dcfg_liodn(node, liodn); + if (ret) + pr_debug("LIODN restore failed for %s\n", + node->full_name); + } +} + +static void iommu_resume(void) +{ + int i; + u32 pamubypenr, pamu_counter; + + restore_dcfg_liodns(); + pamubypenr = in_be32(&guts_regs->pamubypenr); + for (i = 0, pamu_counter = 0x80000000; i < pamu_info_data.count; + i++, pamu_counter >>= 1) { + void __iomem *p; + + p = pamu_info_data.pamu_reg_base + i * PAMU_OFFSET; + setup_one_pamu(p, virt_to_phys(ppaact), virt_to_phys(spaact), + virt_to_phys(omt)); + pamubypenr &= ~pamu_counter; + } + /* Enable all PAMUs */ + out_be32(&guts_regs->pamubypenr, pamubypenr); +} + +static struct syscore_ops iommu_syscore_ops = { + .resume = iommu_resume, + .suspend = iommu_suspend, +}; + +static void __init init_iommu_pm_ops(void) +{ + register_syscore_ops(&iommu_syscore_ops); +} + +#else +static inline void init_iommu_pm_ops(void) {} +#endif /* CONFIG_SUSPEND */ + static __init int fsl_pamu_init(void) { struct platform_device *pdev = NULL; @@ -1474,6 +1561,8 @@ static __init int fsl_pamu_init(void) goto error_device_add; } + init_iommu_pm_ops(); + return 0; error_device_add: |