Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mtd: powernv: Add powernv flash MTD abstraction driver

Powerpc powernv platforms allow access to certain system flash devices
through a firmwarwe interface. This change adds an mtd driver for these
flash devices.

Minor updates from Jeremy Kerr and Joel Stanley.

Signed-off-by: Cyril Bur <cyrilbur@gmail.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Reviewed-by: Neelesh Gupta <neelegup@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>

authored by

Cyril Bur and committed by
Michael Ellerman
1cbb4a1c cfcb3d80

+294
+8
drivers/mtd/devices/Kconfig
··· 195 195 Testing MTD users (eg JFFS2) on large media and media that might 196 196 be removed during a write (using the floppy drive). 197 197 198 + config MTD_POWERNV_FLASH 199 + tristate "powernv flash MTD driver" 200 + depends on PPC_POWERNV 201 + help 202 + This provides an MTD device to access flash on powernv OPAL 203 + platforms from Linux. This device abstracts away the 204 + firmware interface for flash access. 205 + 198 206 comment "Disk-On-Chip Device Drivers" 199 207 200 208 config MTD_DOCG3
+1
drivers/mtd/devices/Makefile
··· 16 16 obj-$(CONFIG_MTD_SST25L) += sst25l.o 17 17 obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o 18 18 obj-$(CONFIG_MTD_ST_SPI_FSM) += st_spi_fsm.o 19 + obj-$(CONFIG_MTD_POWERNV_FLASH) += powernv_flash.o 19 20 20 21 21 22 CFLAGS_docg3.o += -I$(src)
+285
drivers/mtd/devices/powernv_flash.c
··· 1 + /* 2 + * OPAL PNOR flash MTD abstraction 3 + * 4 + * Copyright IBM 2015 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation; either version 2 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * This program is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + */ 16 + 17 + #include <linux/kernel.h> 18 + #include <linux/module.h> 19 + #include <linux/errno.h> 20 + #include <linux/of.h> 21 + #include <linux/of_address.h> 22 + #include <linux/platform_device.h> 23 + #include <linux/string.h> 24 + #include <linux/slab.h> 25 + #include <linux/mtd/mtd.h> 26 + #include <linux/mtd/partitions.h> 27 + 28 + #include <linux/debugfs.h> 29 + #include <linux/seq_file.h> 30 + 31 + #include <asm/opal.h> 32 + 33 + 34 + /* 35 + * This driver creates the a Linux MTD abstraction for platform PNOR flash 36 + * backed by OPAL calls 37 + */ 38 + 39 + struct powernv_flash { 40 + struct mtd_info mtd; 41 + u32 id; 42 + }; 43 + 44 + enum flash_op { 45 + FLASH_OP_READ, 46 + FLASH_OP_WRITE, 47 + FLASH_OP_ERASE, 48 + }; 49 + 50 + static int powernv_flash_async_op(struct mtd_info *mtd, enum flash_op op, 51 + loff_t offset, size_t len, size_t *retlen, u_char *buf) 52 + { 53 + struct powernv_flash *info = (struct powernv_flash *)mtd->priv; 54 + struct device *dev = &mtd->dev; 55 + int token; 56 + struct opal_msg msg; 57 + int rc; 58 + 59 + dev_dbg(dev, "%s(op=%d, offset=0x%llx, len=%zu)\n", 60 + __func__, op, offset, len); 61 + 62 + token = opal_async_get_token_interruptible(); 63 + if (token < 0) { 64 + if (token != -ERESTARTSYS) 65 + dev_err(dev, "Failed to get an async token\n"); 66 + 67 + return token; 68 + } 69 + 70 + switch (op) { 71 + case FLASH_OP_READ: 72 + rc = opal_flash_read(info->id, offset, __pa(buf), len, token); 73 + break; 74 + case FLASH_OP_WRITE: 75 + rc = opal_flash_write(info->id, offset, __pa(buf), len, token); 76 + break; 77 + case FLASH_OP_ERASE: 78 + rc = opal_flash_erase(info->id, offset, len, token); 79 + break; 80 + default: 81 + BUG_ON(1); 82 + } 83 + 84 + if (rc != OPAL_ASYNC_COMPLETION) { 85 + dev_err(dev, "opal_flash_async_op(op=%d) failed (rc %d)\n", 86 + op, rc); 87 + opal_async_release_token(token); 88 + return -EIO; 89 + } 90 + 91 + rc = opal_async_wait_response(token, &msg); 92 + opal_async_release_token(token); 93 + if (rc) { 94 + dev_err(dev, "opal async wait failed (rc %d)\n", rc); 95 + return -EIO; 96 + } 97 + 98 + rc = be64_to_cpu(msg.params[1]); 99 + if (rc == OPAL_SUCCESS) { 100 + rc = 0; 101 + if (retlen) 102 + *retlen = len; 103 + } else { 104 + rc = -EIO; 105 + } 106 + 107 + return rc; 108 + } 109 + 110 + /** 111 + * @mtd: the device 112 + * @from: the offset to read from 113 + * @len: the number of bytes to read 114 + * @retlen: the number of bytes actually read 115 + * @buf: the filled in buffer 116 + * 117 + * Returns 0 if read successful, or -ERRNO if an error occurred 118 + */ 119 + static int powernv_flash_read(struct mtd_info *mtd, loff_t from, size_t len, 120 + size_t *retlen, u_char *buf) 121 + { 122 + return powernv_flash_async_op(mtd, FLASH_OP_READ, from, 123 + len, retlen, buf); 124 + } 125 + 126 + /** 127 + * @mtd: the device 128 + * @to: the offset to write to 129 + * @len: the number of bytes to write 130 + * @retlen: the number of bytes actually written 131 + * @buf: the buffer to get bytes from 132 + * 133 + * Returns 0 if write successful, -ERRNO if error occurred 134 + */ 135 + static int powernv_flash_write(struct mtd_info *mtd, loff_t to, size_t len, 136 + size_t *retlen, const u_char *buf) 137 + { 138 + return powernv_flash_async_op(mtd, FLASH_OP_WRITE, to, 139 + len, retlen, (u_char *)buf); 140 + } 141 + 142 + /** 143 + * @mtd: the device 144 + * @erase: the erase info 145 + * Returns 0 if erase successful or -ERRNO if an error occurred 146 + */ 147 + static int powernv_flash_erase(struct mtd_info *mtd, struct erase_info *erase) 148 + { 149 + int rc; 150 + 151 + erase->state = MTD_ERASING; 152 + 153 + /* todo: register our own notifier to do a true async implementation */ 154 + rc = powernv_flash_async_op(mtd, FLASH_OP_ERASE, erase->addr, 155 + erase->len, NULL, NULL); 156 + 157 + if (rc) { 158 + erase->fail_addr = erase->addr; 159 + erase->state = MTD_ERASE_FAILED; 160 + } else { 161 + erase->state = MTD_ERASE_DONE; 162 + } 163 + mtd_erase_callback(erase); 164 + return rc; 165 + } 166 + 167 + /** 168 + * powernv_flash_set_driver_info - Fill the mtd_info structure and docg3 169 + * structure @pdev: The platform device 170 + * @mtd: The structure to fill 171 + */ 172 + static int powernv_flash_set_driver_info(struct device *dev, 173 + struct mtd_info *mtd) 174 + { 175 + u64 size; 176 + u32 erase_size; 177 + int rc; 178 + 179 + rc = of_property_read_u32(dev->of_node, "ibm,flash-block-size", 180 + &erase_size); 181 + if (rc) { 182 + dev_err(dev, "couldn't get resource block size information\n"); 183 + return rc; 184 + } 185 + 186 + rc = of_property_read_u64(dev->of_node, "reg", &size); 187 + if (rc) { 188 + dev_err(dev, "couldn't get resource size information\n"); 189 + return rc; 190 + } 191 + 192 + /* 193 + * Going to have to check what details I need to set and how to 194 + * get them 195 + */ 196 + mtd->name = of_get_property(dev->of_node, "name", NULL); 197 + mtd->type = MTD_NORFLASH; 198 + mtd->flags = MTD_WRITEABLE; 199 + mtd->size = size; 200 + mtd->erasesize = erase_size; 201 + mtd->writebufsize = mtd->writesize = 1; 202 + mtd->owner = THIS_MODULE; 203 + mtd->_erase = powernv_flash_erase; 204 + mtd->_read = powernv_flash_read; 205 + mtd->_write = powernv_flash_write; 206 + mtd->dev.parent = dev; 207 + return 0; 208 + } 209 + 210 + /** 211 + * powernv_flash_probe 212 + * @pdev: platform device 213 + * 214 + * Returns 0 on success, -ENOMEM, -ENXIO on error 215 + */ 216 + static int powernv_flash_probe(struct platform_device *pdev) 217 + { 218 + struct device *dev = &pdev->dev; 219 + struct powernv_flash *data; 220 + int ret; 221 + 222 + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 223 + if (!data) { 224 + ret = -ENOMEM; 225 + goto out; 226 + } 227 + data->mtd.priv = data; 228 + 229 + ret = of_property_read_u32(dev->of_node, "ibm,opal-id", &(data->id)); 230 + if (ret) { 231 + dev_err(dev, "no device property 'ibm,opal-id'\n"); 232 + goto out; 233 + } 234 + 235 + ret = powernv_flash_set_driver_info(dev, &data->mtd); 236 + if (ret) 237 + goto out; 238 + 239 + dev_set_drvdata(dev, data); 240 + 241 + /* 242 + * The current flash that skiboot exposes is one contiguous flash chip 243 + * with an ffs partition at the start, it should prove easier for users 244 + * to deal with partitions or not as they see fit 245 + */ 246 + ret = mtd_device_register(&data->mtd, NULL, 0); 247 + 248 + out: 249 + return ret; 250 + } 251 + 252 + /** 253 + * op_release - Release the driver 254 + * @pdev: the platform device 255 + * 256 + * Returns 0 257 + */ 258 + static int powernv_flash_release(struct platform_device *pdev) 259 + { 260 + struct powernv_flash *data = dev_get_drvdata(&(pdev->dev)); 261 + 262 + /* All resources should be freed automatically */ 263 + return mtd_device_unregister(&(data->mtd)); 264 + } 265 + 266 + static const struct of_device_id powernv_flash_match[] = { 267 + { .compatible = "ibm,opal-flash" }, 268 + {} 269 + }; 270 + 271 + static struct platform_driver powernv_flash_driver = { 272 + .driver = { 273 + .name = "powernv_flash", 274 + .of_match_table = powernv_flash_match, 275 + }, 276 + .remove = powernv_flash_release, 277 + .probe = powernv_flash_probe, 278 + }; 279 + 280 + module_platform_driver(powernv_flash_driver); 281 + 282 + MODULE_DEVICE_TABLE(of, powernv_flash_match); 283 + MODULE_LICENSE("GPL"); 284 + MODULE_AUTHOR("Cyril Bur <cyril.bur@au1.ibm.com>"); 285 + MODULE_DESCRIPTION("MTD abstraction for OPAL flash");