summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/staging/fsl_qbman/bman_config.c37
-rw-r--r--drivers/staging/fsl_qbman/bman_driver.c48
-rw-r--r--drivers/staging/fsl_qbman/bman_high.c54
-rw-r--r--drivers/staging/fsl_qbman/bman_private.h9
-rw-r--r--drivers/staging/fsl_qbman/qman_config.c35
-rw-r--r--drivers/staging/fsl_qbman/qman_driver.c43
-rw-r--r--drivers/staging/fsl_qbman/qman_high.c42
-rw-r--r--drivers/staging/fsl_qbman/qman_private.h8
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