root/drivers/mtd/devices/powernv_flash.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. powernv_flash_async_op
  2. powernv_flash_read
  3. powernv_flash_write
  4. powernv_flash_erase
  5. powernv_flash_set_driver_info
  6. powernv_flash_probe
  7. powernv_flash_release

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * OPAL PNOR flash MTD abstraction
   4  *
   5  * Copyright IBM 2015
   6  */
   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 flash
  27  * backed by OPAL calls
  28  */
  29 
  30 struct powernv_flash {
  31         struct mtd_info mtd;
  32         u32 id;
  33 };
  34 
  35 enum 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 core
  43  * might have split up the call from userspace and called into the
  44  * driver more than once, we'll already have done some amount of work.
  45  */
  46 static 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                 else
  63                         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 the
  88                          * buffer we've just passed to OPAL but OPAL
  89                          * will continue to read or write from that
  90                          * memory.
  91                          * It may be tempting to ultimately return 0
  92                          * if we're doing a read or a write since we
  93                          * are going to end up waiting until OPAL is
  94                          * done. However, because the MTD core sends
  95                          * us the userspace request in chunks, we need
  96                          * 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 return
 108          * OPAL_BUSY.
 109          * During firmware updates by the service processor OPAL may
 110          * be (temporarily) prevented from accessing the flash, in
 111          * this case OPAL will also return OPAL_BUSY.
 112          * Both cases aren't errors exactly but the flash could have
 113          * 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);
 123 out:
 124         opal_async_release_token(token);
 125         return rc;
 126 }
 127 
 128 /**
 129  * @mtd: the device
 130  * @from: the offset to read from
 131  * @len: the number of bytes to read
 132  * @retlen: the number of bytes actually read
 133  * @buf: the filled in buffer
 134  *
 135  * Returns 0 if read successful, or -ERRNO if an error occurred
 136  */
 137 static int powernv_flash_read(struct mtd_info *mtd, loff_t from, size_t len,
 138              size_t *retlen, u_char *buf)
 139 {
 140         return powernv_flash_async_op(mtd, FLASH_OP_READ, from,
 141                         len, retlen, buf);
 142 }
 143 
 144 /**
 145  * @mtd: the device
 146  * @to: the offset to write to
 147  * @len: the number of bytes to write
 148  * @retlen: the number of bytes actually written
 149  * @buf: the buffer to get bytes from
 150  *
 151  * Returns 0 if write successful, -ERRNO if error occurred
 152  */
 153 static int powernv_flash_write(struct mtd_info *mtd, loff_t to, size_t len,
 154                      size_t *retlen, const u_char *buf)
 155 {
 156         return powernv_flash_async_op(mtd, FLASH_OP_WRITE, to,
 157                         len, retlen, (u_char *)buf);
 158 }
 159 
 160 /**
 161  * @mtd: the device
 162  * @erase: the erase info
 163  * Returns 0 if erase successful or -ERRNO if an error occurred
 164  */
 165 static int powernv_flash_erase(struct mtd_info *mtd, struct erase_info *erase)
 166 {
 167         int rc;
 168 
 169         rc =  powernv_flash_async_op(mtd, FLASH_OP_ERASE, erase->addr,
 170                         erase->len, NULL, NULL);
 171         if (rc)
 172                 erase->fail_addr = erase->addr;
 173 
 174         return rc;
 175 }
 176 
 177 /**
 178  * powernv_flash_set_driver_info - Fill the mtd_info structure and docg3
 179  * structure @pdev: The platform device
 180  * @mtd: The structure to fill
 181  */
 182 static int powernv_flash_set_driver_info(struct device *dev,
 183                 struct mtd_info *mtd)
 184 {
 185         u64 size;
 186         u32 erase_size;
 187         int rc;
 188 
 189         rc = of_property_read_u32(dev->of_node, "ibm,flash-block-size",
 190                         &erase_size);
 191         if (rc) {
 192                 dev_err(dev, "couldn't get resource block size information\n");
 193                 return rc;
 194         }
 195 
 196         rc = of_property_read_u64(dev->of_node, "reg", &size);
 197         if (rc) {
 198                 dev_err(dev, "couldn't get resource size information\n");
 199                 return rc;
 200         }
 201 
 202         /*
 203          * Going to have to check what details I need to set and how to
 204          * get them
 205          */
 206         mtd->name = devm_kasprintf(dev, GFP_KERNEL, "%pOFP", dev->of_node);
 207         mtd->type = MTD_NORFLASH;
 208         mtd->flags = MTD_WRITEABLE;
 209         mtd->size = size;
 210         mtd->erasesize = erase_size;
 211         mtd->writebufsize = mtd->writesize = 1;
 212         mtd->owner = THIS_MODULE;
 213         mtd->_erase = powernv_flash_erase;
 214         mtd->_read = powernv_flash_read;
 215         mtd->_write = powernv_flash_write;
 216         mtd->dev.parent = dev;
 217         mtd_set_of_node(mtd, dev->of_node);
 218         return 0;
 219 }
 220 
 221 /**
 222  * powernv_flash_probe
 223  * @pdev: platform device
 224  *
 225  * Returns 0 on success, -ENOMEM, -ENXIO on error
 226  */
 227 static int powernv_flash_probe(struct platform_device *pdev)
 228 {
 229         struct device *dev = &pdev->dev;
 230         struct powernv_flash *data;
 231         int ret;
 232 
 233         data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
 234         if (!data)
 235                 return -ENOMEM;
 236 
 237         data->mtd.priv = data;
 238 
 239         ret = of_property_read_u32(dev->of_node, "ibm,opal-id", &(data->id));
 240         if (ret) {
 241                 dev_err(dev, "no device property 'ibm,opal-id'\n");
 242                 return ret;
 243         }
 244 
 245         ret = powernv_flash_set_driver_info(dev, &data->mtd);
 246         if (ret)
 247                 return ret;
 248 
 249         dev_set_drvdata(dev, data);
 250 
 251         /*
 252          * The current flash that skiboot exposes is one contiguous flash chip
 253          * with an ffs partition at the start, it should prove easier for users
 254          * to deal with partitions or not as they see fit
 255          */
 256         return mtd_device_register(&data->mtd, NULL, 0);
 257 }
 258 
 259 /**
 260  * op_release - Release the driver
 261  * @pdev: the platform device
 262  *
 263  * Returns 0
 264  */
 265 static int powernv_flash_release(struct platform_device *pdev)
 266 {
 267         struct powernv_flash *data = dev_get_drvdata(&(pdev->dev));
 268 
 269         /* All resources should be freed automatically */
 270         return mtd_device_unregister(&(data->mtd));
 271 }
 272 
 273 static const struct of_device_id powernv_flash_match[] = {
 274         { .compatible = "ibm,opal-flash" },
 275         {}
 276 };
 277 
 278 static struct platform_driver powernv_flash_driver = {
 279         .driver         = {
 280                 .name           = "powernv_flash",
 281                 .of_match_table = powernv_flash_match,
 282         },
 283         .remove         = powernv_flash_release,
 284         .probe          = powernv_flash_probe,
 285 };
 286 
 287 module_platform_driver(powernv_flash_driver);
 288 
 289 MODULE_DEVICE_TABLE(of, powernv_flash_match);
 290 MODULE_LICENSE("GPL");
 291 MODULE_AUTHOR("Cyril Bur <cyril.bur@au1.ibm.com>");
 292 MODULE_DESCRIPTION("MTD abstraction for OPAL flash");

/* [<][>][^][v][top][bottom][index][help] */