linux/drivers/mtd/devices/powernv_flash.c
<<
>>
Prefs
   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
  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 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 */
  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                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);
 123out:
 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 */
 137static 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 */
 153static 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 */
 165static 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 */
 182static 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 */
 227static 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 */
 265static 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
 273static const struct of_device_id powernv_flash_match[] = {
 274        { .compatible = "ibm,opal-flash" },
 275        {}
 276};
 277
 278static 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
 287module_platform_driver(powernv_flash_driver);
 288
 289MODULE_DEVICE_TABLE(of, powernv_flash_match);
 290MODULE_LICENSE("GPL");
 291MODULE_AUTHOR("Cyril Bur <cyril.bur@au1.ibm.com>");
 292MODULE_DESCRIPTION("MTD abstraction for OPAL flash");
 293