diff options
-rw-r--r-- | drivers/staging/fsl_qbman/bman_config.c | 37 | ||||
-rw-r--r-- | drivers/staging/fsl_qbman/bman_driver.c | 48 | ||||
-rw-r--r-- | drivers/staging/fsl_qbman/bman_high.c | 54 | ||||
-rw-r--r-- | drivers/staging/fsl_qbman/bman_private.h | 9 | ||||
-rw-r--r-- | drivers/staging/fsl_qbman/qman_config.c | 35 | ||||
-rw-r--r-- | drivers/staging/fsl_qbman/qman_driver.c | 43 | ||||
-rw-r--r-- | drivers/staging/fsl_qbman/qman_high.c | 42 | ||||
-rw-r--r-- | drivers/staging/fsl_qbman/qman_private.h | 8 |
8 files changed, 274 insertions, 2 deletions
diff --git a/drivers/staging/fsl_qbman/bman_config.c b/drivers/staging/fsl_qbman/bman_config.c index a00b3d4..efba359 100644 --- a/drivers/staging/fsl_qbman/bman_config.c +++ b/drivers/staging/fsl_qbman/bman_config.c @@ -43,6 +43,8 @@ struct bman; #define REG_POOL_HWDXT(n) (0x0300 + ((n) * 0x04)) #define REG_POOL_CONTENT(n) (0x0600 + ((n) * 0x04)) #define REG_FBPR_FPC 0x0800 +#define REG_STATE_IDLE 0x960 +#define REG_STATE_STOP 0x964 #define REG_ECSR 0x0a00 #define REG_ECIR 0x0a04 #define REG_EADR 0x0a08 @@ -663,11 +665,46 @@ static struct of_device_id of_fsl_bman_ids[] = { }; MODULE_DEVICE_TABLE(of, of_fsl_bman_ids); +#ifdef CONFIG_SUSPEND +static u32 saved_isdr; + +static int bman_pm_suspend_noirq(struct device *dev) +{ + suspend_unused_bportal(); + /* save isdr, disable all, clear isr */ + saved_isdr = bm_err_isr_disable_read(bm); + bm_err_isr_disable_write(bm, 0xffffffff); + bm_err_isr_status_clear(bm, 0xffffffff); + /* should be idle, otherwise abort ? */ +#ifdef CONFIG_PM_DEBUG + pr_info("Bman suspend code, IDLE_STAT = 0x%x\n", bm_in(STATE_IDLE)); +#endif + return 0; +} + +static int bman_pm_resume_noirq(struct device *dev) +{ + /* restore isdr */ + bm_err_isr_disable_write(bm, saved_isdr); + resume_unused_bportal(); + return 0; +} +#else +#define bman_pm_suspend_noirq NULL +#define bman_pm_resume_noirq NULL +#endif + +static const struct dev_pm_ops bman_pm_ops = { + .suspend_noirq = bman_pm_suspend_noirq, + .resume_noirq = bman_pm_resume_noirq, +}; + static struct platform_driver of_fsl_bman_driver = { .driver = { .owner = THIS_MODULE, .name = DRV_NAME, .of_match_table = of_fsl_bman_ids, + .pm = &bman_pm_ops, }, .probe = of_fsl_bman_probe, .remove = of_fsl_bman_remove, diff --git a/drivers/staging/fsl_qbman/bman_driver.c b/drivers/staging/fsl_qbman/bman_driver.c index bdf257d..7ef20e3 100644 --- a/drivers/staging/fsl_qbman/bman_driver.c +++ b/drivers/staging/fsl_qbman/bman_driver.c @@ -28,12 +28,10 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "bman_private.h" #ifdef CONFIG_HOTPLUG_CPU #include <linux/cpu.h> #endif - /* * Global variables of the max portal/pool number this bman version supported */ @@ -186,6 +184,10 @@ static struct bm_portal_config * __init parse_pcfg(struct device_node *node) pcfg->addr_phys[DPA_PORTAL_CI].start, resource_size(&pcfg->addr_phys[DPA_PORTAL_CI]), _PAGE_GUARDED | _PAGE_NO_CACHE); + + /* disable bp depletion */ + __raw_writel(0x0, pcfg->addr_virt[DPA_PORTAL_CI] + 0x200); + __raw_writel(0x0, pcfg->addr_virt[DPA_PORTAL_CI] + 0x204); return pcfg; err: kfree(pcfg); @@ -501,3 +503,45 @@ __init int bman_resource_init(void) } return 0; } + +#ifdef CONFIG_SUSPEND +void suspend_unused_bportal(void) +{ + struct bm_portal_config *pcfg; + + if (list_empty(&unused_pcfgs)) + return; + + list_for_each_entry(pcfg, &unused_pcfgs, list) { +#ifdef CONFIG_PM_DEBUG + pr_info("Need to save bportal %d\n", pcfg->public_cfg.index); +#endif + /* save isdr, disable all via isdr, clear isr */ + pcfg->saved_isdr = + __raw_readl(pcfg->addr_virt[DPA_PORTAL_CI] + 0xe08); + __raw_writel(0xffffffff, pcfg->addr_virt[DPA_PORTAL_CI] + + 0xe08); + __raw_writel(0xffffffff, pcfg->addr_virt[DPA_PORTAL_CI] + + 0xe00); + } + return; +} + +void resume_unused_bportal(void) +{ + struct bm_portal_config *pcfg; + + if (list_empty(&unused_pcfgs)) + return; + + list_for_each_entry(pcfg, &unused_pcfgs, list) { +#ifdef CONFIG_PM_DEBUG + pr_info("Need to resume bportal %d\n", pcfg->public_cfg.index); +#endif + /* restore isdr */ + __raw_writel(pcfg->saved_isdr, + pcfg->addr_virt[DPA_PORTAL_CI] + 0xe08); + } + return; +} +#endif diff --git a/drivers/staging/fsl_qbman/bman_high.c b/drivers/staging/fsl_qbman/bman_high.c index 16445a7..cc25de4 100644 --- a/drivers/staging/fsl_qbman/bman_high.c +++ b/drivers/staging/fsl_qbman/bman_high.c @@ -53,6 +53,8 @@ struct bman_portal { #endif /* When the cpu-affine portal is activated, this is non-NULL */ const struct bm_portal_config *config; + /* This is needed for power management */ + struct platform_device *pdev; /* 64-entry hash-table of pool objects that are tracking depletion * entry/exit (ie. BMAN_POOL_FLAG_DEPLETION). This isn't fast-path, so * we're not fussy about cache-misses and so forth - whereas the above @@ -63,6 +65,8 @@ struct bman_portal { char irqname[MAX_IRQNAME]; /* Track if the portal was alloced by the driver */ u8 alloced; + /* power management data */ + u32 save_isdr; }; /* For an explanation of the locking, redirection, or affine-portal logic, @@ -193,6 +197,42 @@ static irqreturn_t portal_isr(__always_unused int irq, void *ptr) return IRQ_HANDLED; } +#ifdef CONFIG_SUSPEND +static int _bman_portal_suspend_noirq(struct device *dev) +{ + struct bman_portal *p = (struct bman_portal *)dev->platform_data; +#ifdef CONFIG_PM_DEBUG + struct platform_device *pdev = to_platform_device(dev); +#endif + p->save_isdr = bm_isr_disable_read(&p->p); + bm_isr_disable_write(&p->p, 0xffffffff); + bm_isr_status_clear(&p->p, 0xffffffff); +#ifdef CONFIG_PM_DEBUG + pr_info("Suspend for %s\n", pdev->name); +#endif + return 0; +} + +static int _bman_portal_resume_noirq(struct device *dev) +{ + struct bman_portal *p = (struct bman_portal *)dev->platform_data; + + /* restore isdr */ + bm_isr_disable_write(&p->p, p->save_isdr); + return 0; +} +#else +#define _bman_portal_suspend_noirq NULL +#define _bman_portal_resume_noirq NULL +#endif + +struct dev_pm_domain bman_portal_device_pm_domain = { + .ops = { + USE_PLATFORM_PM_SLEEP_OPS + .suspend_noirq = _bman_portal_suspend_noirq, + .resume_noirq = _bman_portal_resume_noirq, + } +}; struct bman_portal *bman_create_portal( struct bman_portal *portal, @@ -202,6 +242,7 @@ struct bman_portal *bman_create_portal( const struct bman_depletion *pools = &config->public_cfg.mask; int ret; u8 bpid = 0; + char buf[16]; if (!portal) { portal = kmalloc(sizeof(*portal), GFP_KERNEL); @@ -250,6 +291,15 @@ struct bman_portal *bman_create_portal( portal->is_shared = config->public_cfg.is_shared; portal->sharing_redirect = NULL; #endif + sprintf(buf, "bportal-%u", config->public_cfg.index); + portal->pdev = platform_device_alloc(buf, -1); + if (!portal->pdev) + goto fail_devalloc; + portal->pdev->dev.pm_domain = &bman_portal_device_pm_domain; + portal->pdev->dev.platform_data = portal; + ret = platform_device_add(portal->pdev); + if (ret) + goto fail_devadd; memset(&portal->cb, 0, sizeof(portal->cb)); /* Write-to-clear any stale interrupt status bits */ bm_isr_disable_write(__p, 0xffffffff); @@ -286,6 +336,10 @@ fail_rcr_empty: fail_affinity: free_irq(config->public_cfg.irq, portal); fail_irq: + platform_device_del(portal->pdev); +fail_devadd: + platform_device_put(portal->pdev); +fail_devalloc: kfree(portal->pools); fail_pools: bm_isr_finish(__p); diff --git a/drivers/staging/fsl_qbman/bman_private.h b/drivers/staging/fsl_qbman/bman_private.h index fea7a2b..34e4127 100644 --- a/drivers/staging/fsl_qbman/bman_private.h +++ b/drivers/staging/fsl_qbman/bman_private.h @@ -60,6 +60,8 @@ struct bm_portal_config { struct list_head list; /* User-visible portal configuration settings */ struct bman_portal_config public_cfg; + /* power management saved data */ + u32 saved_isdr; }; #ifdef CONFIG_FSL_BMAN_CONFIG @@ -153,4 +155,11 @@ __init int bman_resource_init(void); const struct bm_portal_config *bman_get_bm_portal_config( struct bman_portal *portal); + +/* power management */ +#ifdef CONFIG_SUSPEND +void suspend_unused_bportal(void); +void resume_unused_bportal(void); +#endif + #endif /* CONFIG_FSL_BMAN_CONFIG */ diff --git a/drivers/staging/fsl_qbman/qman_config.c b/drivers/staging/fsl_qbman/qman_config.c index 4c90f8b..8a10c22 100644 --- a/drivers/staging/fsl_qbman/qman_config.c +++ b/drivers/staging/fsl_qbman/qman_config.c @@ -1085,11 +1085,46 @@ static struct of_device_id of_fsl_qman_ids[] = { }; MODULE_DEVICE_TABLE(of, of_fsl_qman_ids); +#ifdef CONFIG_PM + +static u32 saved_isdr; +static int qman_pm_suspend_noirq(struct device *dev) +{ + suspend_unused_qportal(); + /* save isdr, disable all, clear isr */ + saved_isdr = qm_err_isr_disable_read(qm); + qm_err_isr_disable_write(qm, 0xffffffff); + qm_err_isr_status_clear(qm, 0xffffffff); + /* should be idle, otherwise abort ? */ +#ifdef CONFIG_PM_DEBUG + pr_info("Qman suspend code, IDLE_STAT = 0x%x\n", qm_in(IDLE_STAT)); +#endif + return 0; +} + +static int qman_pm_resume_noirq(struct device *dev) +{ + /* restore isdr */ + qm_err_isr_disable_write(qm, saved_isdr); + resume_unused_qportal(); + return 0; +} +#else +#define qman_pm_suspend_noirq NULL +#define qman_pm_resume_noirq NULL +#endif + +static const struct dev_pm_ops qman_pm_ops = { + .suspend_noirq = qman_pm_suspend_noirq, + .resume_noirq = qman_pm_resume_noirq, +}; + static struct platform_driver of_fsl_qman_driver = { .driver = { .owner = THIS_MODULE, .name = DRV_NAME, .of_match_table = of_fsl_qman_ids, + .pm = &qman_pm_ops, }, .probe = of_fsl_qman_probe, .remove = of_fsl_qman_remove, diff --git a/drivers/staging/fsl_qbman/qman_driver.c b/drivers/staging/fsl_qbman/qman_driver.c index dc220b2..f5049b1 100644 --- a/drivers/staging/fsl_qbman/qman_driver.c +++ b/drivers/staging/fsl_qbman/qman_driver.c @@ -897,3 +897,46 @@ __init int qman_resource_init(void) } return 0; } + +#ifdef CONFIG_SUSPEND +void suspend_unused_qportal(void) +{ + struct qm_portal_config *pcfg; + + if (list_empty(&unused_pcfgs)) + return; + + list_for_each_entry(pcfg, &unused_pcfgs, list) { +#ifdef CONFIG_PM_DEBUG + pr_info("Need to save qportal %d\n", pcfg->public_cfg.index); +#endif + /* save isdr, disable all via isdr, clear isr */ + pcfg->saved_isdr = + __raw_readl(pcfg->addr_virt[DPA_PORTAL_CI] + 0xe08); + __raw_writel(0xffffffff, pcfg->addr_virt[DPA_PORTAL_CI] + + 0xe08); + __raw_writel(0xffffffff, pcfg->addr_virt[DPA_PORTAL_CI] + + 0xe00); + } + return; +} + +void resume_unused_qportal(void) +{ + struct qm_portal_config *pcfg; + + if (list_empty(&unused_pcfgs)) + return; + + list_for_each_entry(pcfg, &unused_pcfgs, list) { +#ifdef CONFIG_PM_DEBUG + pr_info("Need to resume qportal %d\n", pcfg->public_cfg.index); +#endif + /* restore isdr */ + __raw_writel(pcfg->saved_isdr, + pcfg->addr_virt[DPA_PORTAL_CI] + 0xe08); + } + return; +} +#endif + diff --git a/drivers/staging/fsl_qbman/qman_high.c b/drivers/staging/fsl_qbman/qman_high.c index c9de06a..6b9f56d 100644 --- a/drivers/staging/fsl_qbman/qman_high.c +++ b/drivers/staging/fsl_qbman/qman_high.c @@ -123,6 +123,8 @@ struct qman_portal { spinlock_t ccgr_lock; /* track if memory was allocated by the driver */ u8 alloced; + /* power management data */ + u32 save_isdr; }; #ifdef CONFIG_FSL_DPA_PORTAL_SHARE @@ -361,6 +363,44 @@ loop: goto loop; } +#ifdef CONFIG_SUSPEND +static int _qman_portal_suspend_noirq(struct device *dev) +{ + struct qman_portal *p = (struct qman_portal *)dev->platform_data; +#ifdef CONFIG_PM_DEBUG + struct platform_device *pdev = to_platform_device(dev); +#endif + + p->save_isdr = qm_isr_disable_read(&p->p); + qm_isr_disable_write(&p->p, 0xffffffff); + qm_isr_status_clear(&p->p, 0xffffffff); +#ifdef CONFIG_PM_DEBUG + pr_info("Suspend for %s\n", pdev->name); +#endif + return 0; +} + +static int _qman_portal_resume_noirq(struct device *dev) +{ + struct qman_portal *p = (struct qman_portal *)dev->platform_data; + + /* restore isdr */ + qm_isr_disable_write(&p->p, p->save_isdr); + return 0; +} +#else +#define _qman_portal_suspend_noirq NULL +#define _qman_portal_resume_noirq NULL +#endif + +struct dev_pm_domain qman_portal_device_pm_domain = { + .ops = { + USE_PLATFORM_PM_SLEEP_OPS + .suspend_noirq = _qman_portal_suspend_noirq, + .resume_noirq = _qman_portal_resume_noirq, + } +}; + struct qman_portal *qman_create_portal( struct qman_portal *portal, const struct qm_portal_config *config, @@ -464,6 +504,8 @@ struct qman_portal *qman_create_portal( goto fail_devalloc; if (dma_set_mask(&portal->pdev->dev, DMA_BIT_MASK(40))) goto fail_devadd; + portal->pdev->dev.pm_domain = &qman_portal_device_pm_domain; + portal->pdev->dev.platform_data = portal; ret = platform_device_add(portal->pdev); if (ret) goto fail_devadd; diff --git a/drivers/staging/fsl_qbman/qman_private.h b/drivers/staging/fsl_qbman/qman_private.h index 92bd201..b6e7e62 100644 --- a/drivers/staging/fsl_qbman/qman_private.h +++ b/drivers/staging/fsl_qbman/qman_private.h @@ -183,6 +183,8 @@ struct qm_portal_config { struct list_head list; /* User-visible portal configuration settings */ struct qman_portal_config public_cfg; + /* power management saved data */ + u32 saved_isdr; }; /* Revision info (for errata and feature handling) */ @@ -369,3 +371,9 @@ int qman_ceetm_get_xsfdr(enum qm_dc_portal portal, unsigned int *num); extern void *affine_portals[NR_CPUS]; const struct qm_portal_config *qman_get_qm_portal_config( struct qman_portal *portal); + +/* power management */ +#ifdef CONFIG_SUSPEND +void suspend_unused_qportal(void); +void resume_unused_qportal(void); +#endif |