From fb7f27080adc65cd5f341bdf56a1d0c14f316c1b Mon Sep 17 00:00:00 2001 From: Jeffrey Ladouceur Date: Fri, 4 Apr 2014 15:05:46 -0400 Subject: fsl_qbman: Add support for deepsleep In order for supported SoCs to enter deepsleep the QBMan ip blocks must not have any pending interrupt status bits set in the Portals. Therefore, in the pm callbacks for each portal, save the isdr, and clear the isr. On the resume side, reset the save isdr. Also fix the buffer pool depletion notification in all bman portals. The default value is to notify them. This is an issue for unused portals, as the portal will have the corresponding isr bit set and will prevent deepsleep for occuring. The unused portals need to have their ISDR and ISR cleared in order for the qman block to enter idle state. Also re-added the setting of ISDR clear ISR for the qbman ccsr block. Signed-off-by: Jeffrey Ladouceur Change-Id: Icd908215ed10d39e3f112b939c4d6b2758a97a76 Reviewed-on: http://git.am.freescale.net:8181/10717 Tested-by: Review Code-CDREVIEW Reviewed-by: Haiying Wang Reviewed-by: Roy Pledge Reviewed-by: Jose Rivera 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 #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 -- cgit v0.10.2