299 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * OPAL PNOR flash MTD abstraction4 *5 * Copyright IBM 20156 */7 8#include <linux/kernel.h>9#include <linux/module.h>10#include <linux/errno.h>11#include <linux/of.h>12#include <linux/of_address.h>13#include <linux/platform_device.h>14#include <linux/string.h>15#include <linux/slab.h>16#include <linux/mtd/mtd.h>17#include <linux/mtd/partitions.h>18 19#include <linux/debugfs.h>20#include <linux/seq_file.h>21 22#include <asm/opal.h>23 24 25/*26 * This driver creates the a Linux MTD abstraction for platform PNOR flash27 * backed by OPAL calls28 */29 30struct powernv_flash {31 struct mtd_info mtd;32 u32 id;33};34 35enum flash_op {36 FLASH_OP_READ,37 FLASH_OP_WRITE,38 FLASH_OP_ERASE,39};40 41/*42 * Don't return -ERESTARTSYS if we can't get a token, the MTD core43 * might have split up the call from userspace and called into the44 * driver more than once, we'll already have done some amount of work.45 */46static int powernv_flash_async_op(struct mtd_info *mtd, enum flash_op op,47 loff_t offset, size_t len, size_t *retlen, u_char *buf)48{49 struct powernv_flash *info = (struct powernv_flash *)mtd->priv;50 struct device *dev = &mtd->dev;51 int token;52 struct opal_msg msg;53 int rc;54 55 dev_dbg(dev, "%s(op=%d, offset=0x%llx, len=%zu)\n",56 __func__, op, offset, len);57 58 token = opal_async_get_token_interruptible();59 if (token < 0) {60 if (token != -ERESTARTSYS)61 dev_err(dev, "Failed to get an async token\n");62 else63 token = -EINTR;64 return token;65 }66 67 switch (op) {68 case FLASH_OP_READ:69 rc = opal_flash_read(info->id, offset, __pa(buf), len, token);70 break;71 case FLASH_OP_WRITE:72 rc = opal_flash_write(info->id, offset, __pa(buf), len, token);73 break;74 case FLASH_OP_ERASE:75 rc = opal_flash_erase(info->id, offset, len, token);76 break;77 default:78 WARN_ON_ONCE(1);79 opal_async_release_token(token);80 return -EIO;81 }82 83 if (rc == OPAL_ASYNC_COMPLETION) {84 rc = opal_async_wait_response_interruptible(token, &msg);85 if (rc) {86 /*87 * If we return the mtd core will free the88 * buffer we've just passed to OPAL but OPAL89 * will continue to read or write from that90 * memory.91 * It may be tempting to ultimately return 092 * if we're doing a read or a write since we93 * are going to end up waiting until OPAL is94 * done. However, because the MTD core sends95 * us the userspace request in chunks, we need96 * it to know we've been interrupted.97 */98 rc = -EINTR;99 if (opal_async_wait_response(token, &msg))100 dev_err(dev, "opal_async_wait_response() failed\n");101 goto out;102 }103 rc = opal_get_async_rc(msg);104 }105 106 /*107 * OPAL does mutual exclusion on the flash, it will return108 * OPAL_BUSY.109 * During firmware updates by the service processor OPAL may110 * be (temporarily) prevented from accessing the flash, in111 * this case OPAL will also return OPAL_BUSY.112 * Both cases aren't errors exactly but the flash could have113 * changed, userspace should be informed.114 */115 if (rc != OPAL_SUCCESS && rc != OPAL_BUSY)116 dev_err(dev, "opal_flash_async_op(op=%d) failed (rc %d)\n",117 op, rc);118 119 if (rc == OPAL_SUCCESS && retlen)120 *retlen = len;121 122 rc = opal_error_code(rc);123out:124 opal_async_release_token(token);125 return rc;126}127 128/**129 * powernv_flash_read130 * @mtd: the device131 * @from: the offset to read from132 * @len: the number of bytes to read133 * @retlen: the number of bytes actually read134 * @buf: the filled in buffer135 *136 * Returns 0 if read successful, or -ERRNO if an error occurred137 */138static int powernv_flash_read(struct mtd_info *mtd, loff_t from, size_t len,139 size_t *retlen, u_char *buf)140{141 return powernv_flash_async_op(mtd, FLASH_OP_READ, from,142 len, retlen, buf);143}144 145/**146 * powernv_flash_write147 * @mtd: the device148 * @to: the offset to write to149 * @len: the number of bytes to write150 * @retlen: the number of bytes actually written151 * @buf: the buffer to get bytes from152 *153 * Returns 0 if write successful, -ERRNO if error occurred154 */155static int powernv_flash_write(struct mtd_info *mtd, loff_t to, size_t len,156 size_t *retlen, const u_char *buf)157{158 return powernv_flash_async_op(mtd, FLASH_OP_WRITE, to,159 len, retlen, (u_char *)buf);160}161 162/**163 * powernv_flash_erase164 * @mtd: the device165 * @erase: the erase info166 * Returns 0 if erase successful or -ERRNO if an error occurred167 */168static int powernv_flash_erase(struct mtd_info *mtd, struct erase_info *erase)169{170 int rc;171 172 rc = powernv_flash_async_op(mtd, FLASH_OP_ERASE, erase->addr,173 erase->len, NULL, NULL);174 if (rc)175 erase->fail_addr = erase->addr;176 177 return rc;178}179 180/**181 * powernv_flash_set_driver_info - Fill the mtd_info structure and docg3182 * @dev: The device structure183 * @mtd: The structure to fill184 */185static int powernv_flash_set_driver_info(struct device *dev,186 struct mtd_info *mtd)187{188 u64 size;189 u32 erase_size;190 int rc;191 192 rc = of_property_read_u32(dev->of_node, "ibm,flash-block-size",193 &erase_size);194 if (rc) {195 dev_err(dev, "couldn't get resource block size information\n");196 return rc;197 }198 199 rc = of_property_read_u64(dev->of_node, "reg", &size);200 if (rc) {201 dev_err(dev, "couldn't get resource size information\n");202 return rc;203 }204 205 /*206 * Going to have to check what details I need to set and how to207 * get them208 */209 mtd->name = devm_kasprintf(dev, GFP_KERNEL, "%pOFP", dev->of_node);210 if (!mtd->name)211 return -ENOMEM;212 213 mtd->type = MTD_NORFLASH;214 mtd->flags = MTD_WRITEABLE;215 mtd->size = size;216 mtd->erasesize = erase_size;217 mtd->writebufsize = mtd->writesize = 1;218 mtd->owner = THIS_MODULE;219 mtd->_erase = powernv_flash_erase;220 mtd->_read = powernv_flash_read;221 mtd->_write = powernv_flash_write;222 mtd->dev.parent = dev;223 mtd_set_of_node(mtd, dev->of_node);224 return 0;225}226 227/**228 * powernv_flash_probe229 * @pdev: platform device230 *231 * Returns 0 on success, -ENOMEM, -ENXIO on error232 */233static int powernv_flash_probe(struct platform_device *pdev)234{235 struct device *dev = &pdev->dev;236 struct powernv_flash *data;237 int ret;238 239 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);240 if (!data)241 return -ENOMEM;242 243 data->mtd.priv = data;244 245 ret = of_property_read_u32(dev->of_node, "ibm,opal-id", &(data->id));246 if (ret) {247 dev_err(dev, "no device property 'ibm,opal-id'\n");248 return ret;249 }250 251 ret = powernv_flash_set_driver_info(dev, &data->mtd);252 if (ret)253 return ret;254 255 dev_set_drvdata(dev, data);256 257 /*258 * The current flash that skiboot exposes is one contiguous flash chip259 * with an ffs partition at the start, it should prove easier for users260 * to deal with partitions or not as they see fit261 */262 return mtd_device_register(&data->mtd, NULL, 0);263}264 265/**266 * op_release - Release the driver267 * @pdev: the platform device268 *269 * Returns 0270 */271static void powernv_flash_release(struct platform_device *pdev)272{273 struct powernv_flash *data = dev_get_drvdata(&(pdev->dev));274 275 /* All resources should be freed automatically */276 WARN_ON(mtd_device_unregister(&data->mtd));277}278 279static const struct of_device_id powernv_flash_match[] = {280 { .compatible = "ibm,opal-flash" },281 {}282};283 284static struct platform_driver powernv_flash_driver = {285 .driver = {286 .name = "powernv_flash",287 .of_match_table = powernv_flash_match,288 },289 .remove_new = powernv_flash_release,290 .probe = powernv_flash_probe,291};292 293module_platform_driver(powernv_flash_driver);294 295MODULE_DEVICE_TABLE(of, powernv_flash_match);296MODULE_LICENSE("GPL");297MODULE_AUTHOR("Cyril Bur <cyril.bur@au1.ibm.com>");298MODULE_DESCRIPTION("MTD abstraction for OPAL flash");299