[PATCH 1/1]nand/denali: Add runtime pm framework for MRST Denali NAND controller
Chuanxiao.Dong
chuanxiao.dong at intel.com
Thu Aug 26 04:12:01 EDT 2010
From 5e28cd55f0e78d55e9fd1e707155ac2e16bb01ac Mon Sep 17 00:00:00 2001
From: Chuanxiao Dong <chuanxiao.dong at intel.com>
Date: Thu, 26 Aug 2010 15:56:41 +0800
Subject: [PATCH] nand/denali: Add runtime pm framework in denali.c
Denali NAND controller has no capability to shut power down
in MRST platform, so now driver do nothing in runtime_suspend/
runtime_resume routine. This patch add a framework to implement
runtime power management
Signed-off-by: Chuanxiao Dong <chuanxiao.dong at intel.com>
---
drivers/mtd/nand/denali.c | 125 +++++++++++++++++++++++++++++++++++++++++++++
drivers/mtd/nand/denali.h | 7 +++
2 files changed, 132 insertions(+), 0 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 532fe07..ab870a2 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -25,6 +25,10 @@
#include <linux/pci.h>
#include <linux/mtd/mtd.h>
#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/jiffies.h>
+#include <linux/param.h>
#include "denali.h"
@@ -119,6 +123,9 @@ static const uint32_t reset_complete[4] = {INTR_STATUS0__RST_COMP,
INTR_STATUS2__RST_COMP,
INTR_STATUS3__RST_COMP};
+/* record interrupt jiffies */
+static unsigned long last_intr;
+
/* forward declarations */
static void clear_interrupts(struct denali_nand_info *denali);
static uint32_t wait_for_irq(struct denali_nand_info *denali,
@@ -856,6 +863,91 @@ static int read_data_from_flash_mem(struct denali_nand_info *denali,
return i*4; /* intent is to return the number of bytes read */
}
+/* NOW denali NAND controller in MRST has no way to cut power off
+ * once it is power on, so just let these functions be empty
+ * */
+#ifdef CONFIG_PM_RUNTIME
+static int denali_runtime_suspend(struct device *dev)
+{
+ dev_info(dev, "%s: disable_depth %d usage_count %d", __func__,
+ dev->power.disable_depth, dev->power.usage_count.counter);
+ dev_info(dev, "%s called", __func__);
+ /* Denali Controller in MRST doesn't have any capbility
+ * to shut power down or resume back, no register or GPIO
+ * can do this, so here do nothing
+ * */
+ return 0;
+}
+
+static int denali_runtime_resume(struct device *dev)
+{
+ dev_info(dev, "%s: disable_depth %d usage_count %d", __func__,
+ dev->power.disable_depth, dev->power.usage_count.counter);
+ dev_info(dev, "%s called", __func__);
+ /* Denali Controller in MRST doesn't have any capbility
+ * to shut power down or resume back, no register or GPIO
+ * can do this, so here do nothing
+ * */
+ return 0;
+}
+
+static int denali_runtime_idle(struct device *dev)
+{
+ dev_info(dev, "%s: disable_depth %d usage_count %d", __func__,
+ dev->power.disable_depth, dev->power.usage_count.counter);
+ dev_info(dev, "%s called", __func__);
+ /* denali controller will only be resume once during
+ * read/write/erase operation, so if call runtime idle,
+ * means device can be suspend
+ * */
+ return 0;
+}
+
+static struct dev_pm_ops denali_pm = {
+ .runtime_suspend = denali_runtime_suspend,
+ .runtime_resume = denali_runtime_resume,
+ .runtime_idle = denali_runtime_idle,
+};
+#define DENALI_PM (&denali_pm)
+/* support denali runtime pm */
+static void denali_timer_add(struct denali_nand_info *denali)
+{
+ int ret;
+ struct device *dev = &denali->dev->dev;
+ if (denali->pm_status == DENALI_OFF) {
+ ret = pm_runtime_get_sync(dev);
+ denali->pm_status = DENALI_ON;
+ denali->timer.expires = jiffies + 30 * HZ;
+ add_timer(&denali->timer);
+ }
+}
+
+static void denali_timer_func(unsigned long ptr)
+{
+ struct denali_nand_info *denali =
+ (struct denali_nand_info *)ptr;
+ struct device *dev = &denali->dev->dev;
+ struct nand_chip *chip = &denali->nand;
+ int ret;
+
+ if (denali->pm_status == DENALI_OFF)
+ BUG();
+
+ if (chip->state != FL_READY ||
+ jiffies - last_intr < 1000) {
+ denali->timer.expires = jiffies + 30 * HZ;
+ add_timer(&denali->timer);
+ } else {
+ ret = pm_runtime_put_sync(dev);
+ denali->pm_status = DENALI_OFF;
+ }
+}
+#else
+#define DENALI_PM NULL
+static inline void denali_timer_add(struct denali_nand_info *denali) {}
+static inline void denali_timer_func(unsigned long ptr) {}
+#endif
+
/* writes OOB data to the device */
static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
{
@@ -865,6 +957,8 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
INTR_STATUS0__PROGRAM_FAIL;
int status = 0;
+ denali_timer_add(denali);
+
denali->page = page;
if (denali_send_pipeline_cmd(denali, false, false, SPARE_ACCESS,
@@ -892,6 +986,8 @@ static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
uint32_t irq_mask = INTR_STATUS0__LOAD_COMP,
irq_status = 0, addr = 0x0, cmd = 0x0;
+ denali_timer_add(denali);
+
denali->page = page;
if (denali_send_pipeline_cmd(denali, false, true, SPARE_ACCESS,
@@ -1054,6 +1150,8 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t irq_mask = INTR_STATUS0__DMA_CMD_COMP |
INTR_STATUS0__PROGRAM_FAIL;
+ denali_timer_add(denali);
+
/* if it is a raw xfer, we want to disable ecc, and send
* the spare area.
* !raw_xfer - enable ecc
@@ -1149,6 +1247,8 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
INTR_STATUS0__ECC_ERR;
bool check_erased_page = false;
+ denali_timer_add(denali);
+
if (page != denali->page) {
dev_err(&denali->dev->dev, "IN %s: page %d is not"
" equal to denali->page %d, investigate!!",
@@ -1200,6 +1300,8 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t irq_status = 0;
uint32_t irq_mask = INTR_STATUS0__DMA_CMD_COMP;
+ denali_timer_add(denali);
+
if (page != denali->page) {
dev_err(&denali->dev->dev, "IN %s: page %d is not"
" equal to denali->page %d, investigate!!",
@@ -1263,6 +1365,8 @@ static void denali_erase(struct mtd_info *mtd, int page)
uint32_t cmd = 0x0, irq_status = 0;
+ denali_timer_add(denali);
+
/* clear interrupts */
clear_interrupts(denali);
@@ -1312,6 +1416,7 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
denali->page = page;
break;
case NAND_CMD_RESET:
+ denali_timer_add(denali);
reset_bank(denali);
break;
case NAND_CMD_READOOB:
@@ -1435,6 +1540,12 @@ void denali_drv_init(struct denali_nand_info *denali)
/* initialize our irq_status variable to indicate no interrupts */
denali->irq_status = 0;
+
+ /* Initilize denali timer */
+ init_timer(&denali->timer);
+ denali->timer.data = (unsigned long)denali;
+ denali->timer.function = &denali_timer_func;
+ denali->pm_status = DENALI_ON;
}
/* driver entry point */
@@ -1669,6 +1780,12 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
ret);
goto failed_req_irq;
}
+
+ /* init pm runtime */
+ denali->pm_status = DENALI_OFF;
+ pm_runtime_allow(&dev->dev);
+ pm_runtime_put_noidle(&dev->dev);
+
return 0;
failed_req_irq:
@@ -1694,6 +1811,10 @@ static void denali_pci_remove(struct pci_dev *dev)
{
struct denali_nand_info *denali = pci_get_drvdata(dev);
+ del_timer(&denali->timer);
+ if (denali->pm_status == DENALI_ON)
+ pm_runtime_put_sync(&dev->dev);
+
nand_release(&denali->mtd);
del_mtd_device(&denali->mtd);
@@ -1707,11 +1828,15 @@ static void denali_pci_remove(struct pci_dev *dev)
PCI_DMA_BIDIRECTIONAL);
pci_set_drvdata(dev, NULL);
kfree(denali);
+ pm_runtime_get_noresume(&dev->dev);
}
MODULE_DEVICE_TABLE(pci, denali_pci_ids);
static struct pci_driver denali_pci_driver = {
+ .driver = {
+ .pm = DENALI_PM,
+ },
.name = DENALI_NAND_NAME,
.id_table = denali_pci_ids,
.probe = denali_pci_probe,
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 3918bcb..4885944 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -725,6 +725,9 @@ struct nand_buf {
#define INTEL_CE4100 1
#define INTEL_MRST 2
+#define DENALI_OFF 0
+#define DENALI_ON 1
+
struct denali_nand_info {
struct mtd_info mtd;
struct nand_chip nand;
@@ -751,6 +754,10 @@ struct denali_nand_info {
uint32_t totalblks;
uint32_t blksperchip;
uint32_t bbtskipbytes;
+
+ /* used for runtime pm */
+ struct timer_list timer;
+ uint8_t pm_status;
};
#endif /*_LLD_NAND_*/
--
1.6.6.1
More information about the linux-mtd
mailing list