diff options
Diffstat (limited to 'drivers/mtd/devices')
| -rw-r--r-- | drivers/mtd/devices/docg3.h | 2 | ||||
| -rw-r--r-- | drivers/mtd/devices/mtd_intel_dg.c | 74 |
2 files changed, 63 insertions, 13 deletions
diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h index 2c0c5114e4e6..af6ef0ac1f11 100644 --- a/drivers/mtd/devices/docg3.h +++ b/drivers/mtd/devices/docg3.h @@ -274,11 +274,11 @@ struct docg3_cascade { * @cascade: the cascade this device belongs to * @device_id: number of the cascaded DoCG3 device (0, 1, 2 or 3) * @if_cfg: if true, reads are on 16bits, else reads are on 8bits - * @reliable: if 0, docg3 in normal mode, if 1 docg3 in fast mode, if 2 in * reliable mode * Fast mode implies more errors than normal mode. * Reliable mode implies that page 2*n and 2*n+1 are clones. + * @max_block: maximum block number for this device * @bbt: bad block table cache * @oob_write_ofs: offset of the MTD where this OOB should belong (ie. in next * page_write) diff --git a/drivers/mtd/devices/mtd_intel_dg.c b/drivers/mtd/devices/mtd_intel_dg.c index b438ee5aacc3..2bab30dcd35f 100644 --- a/drivers/mtd/devices/mtd_intel_dg.c +++ b/drivers/mtd/devices/mtd_intel_dg.c @@ -15,14 +15,18 @@ #include <linux/module.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> +#include <linux/pm_runtime.h> #include <linux/string.h> #include <linux/slab.h> #include <linux/sizes.h> #include <linux/types.h> +#define INTEL_DG_NVM_RPM_TIMEOUT_MS 500 + struct intel_dg_nvm { struct kref refcnt; struct mtd_info mtd; + struct device *dev; struct mutex lock; /* region access lock */ void __iomem *base; void __iomem *base2; @@ -421,6 +425,8 @@ static int intel_dg_nvm_init(struct intel_dg_nvm *nvm, struct device *device, unsigned int i, n; int ret; + nvm->dev = device; + /* clean error register, previous errors are ignored */ idg_nvm_error(nvm); @@ -498,6 +504,7 @@ static int intel_dg_mtd_erase(struct mtd_info *mtd, struct erase_info *info) size_t len; u8 region; u64 addr; + int ret; if (WARN_ON(!nvm)) return -EINVAL; @@ -512,20 +519,29 @@ static int intel_dg_mtd_erase(struct mtd_info *mtd, struct erase_info *info) total_len = info->len; addr = info->addr; + ret = pm_runtime_resume_and_get(nvm->dev); + if (ret < 0) { + dev_err(&mtd->dev, "rpm: get failed %d\n", ret); + return ret; + } + + ret = 0; guard(mutex)(&nvm->lock); while (total_len > 0) { if (!IS_ALIGNED(addr, SZ_4K) || !IS_ALIGNED(total_len, SZ_4K)) { dev_err(&mtd->dev, "unaligned erase %llx %zx\n", addr, total_len); info->fail_addr = addr; - return -ERANGE; + ret = -ERANGE; + break; } idx = idg_nvm_get_region(nvm, addr); if (idx >= nvm->nregions) { dev_err(&mtd->dev, "out of range"); info->fail_addr = MTD_FAIL_ADDR_UNKNOWN; - return -ERANGE; + ret = -ERANGE; + break; } from = addr - nvm->regions[idx].offset; @@ -541,14 +557,16 @@ static int intel_dg_mtd_erase(struct mtd_info *mtd, struct erase_info *info) if (bytes < 0) { dev_dbg(&mtd->dev, "erase failed with %zd\n", bytes); info->fail_addr += nvm->regions[idx].offset; - return bytes; + ret = bytes; + break; } addr += len; total_len -= len; } - return 0; + pm_runtime_put_autosuspend(nvm->dev); + return ret; } static int intel_dg_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, @@ -577,17 +595,24 @@ static int intel_dg_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, if (len > nvm->regions[idx].size - from) len = nvm->regions[idx].size - from; + ret = pm_runtime_resume_and_get(nvm->dev); + if (ret < 0) { + dev_err(&mtd->dev, "rpm: get failed %zd\n", ret); + return ret; + } + guard(mutex)(&nvm->lock); ret = idg_read(nvm, region, from, len, buf); if (ret < 0) { dev_dbg(&mtd->dev, "read failed with %zd\n", ret); - return ret; + } else { + *retlen = ret; + ret = 0; } - *retlen = ret; - - return 0; + pm_runtime_put_autosuspend(nvm->dev); + return ret; } static int intel_dg_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, @@ -616,17 +641,24 @@ static int intel_dg_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, if (len > nvm->regions[idx].size - to) len = nvm->regions[idx].size - to; + ret = pm_runtime_resume_and_get(nvm->dev); + if (ret < 0) { + dev_err(&mtd->dev, "rpm: get failed %zd\n", ret); + return ret; + } + guard(mutex)(&nvm->lock); ret = idg_write(nvm, region, to, len, buf); if (ret < 0) { dev_dbg(&mtd->dev, "write failed with %zd\n", ret); - return ret; + } else { + *retlen = ret; + ret = 0; } - *retlen = ret; - - return 0; + pm_runtime_put_autosuspend(nvm->dev); + return ret; } static void intel_dg_nvm_release(struct kref *kref) @@ -753,6 +785,21 @@ static int intel_dg_mtd_probe(struct auxiliary_device *aux_dev, } nvm->nregions = n; /* in case where kasprintf fail */ + ret = devm_pm_runtime_enable(device); + if (ret < 0) { + dev_err(device, "rpm: enable failed %d\n", ret); + goto err_norpm; + } + + pm_runtime_set_autosuspend_delay(device, INTEL_DG_NVM_RPM_TIMEOUT_MS); + pm_runtime_use_autosuspend(device); + + ret = pm_runtime_resume_and_get(device); + if (ret < 0) { + dev_err(device, "rpm: get failed %d\n", ret); + goto err_norpm; + } + nvm->base = devm_ioremap_resource(device, &invm->bar); if (IS_ERR(nvm->base)) { ret = PTR_ERR(nvm->base); @@ -781,9 +828,12 @@ static int intel_dg_mtd_probe(struct auxiliary_device *aux_dev, dev_set_drvdata(&aux_dev->dev, nvm); + pm_runtime_put(device); return 0; err: + pm_runtime_put(device); +err_norpm: kref_put(&nvm->refcnt, intel_dg_nvm_release); return ret; } |
