aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCyril Bur <cyrilbur@gmail.com>2015-06-02 00:26:09 -0400
committerMichael Ellerman <mpe@ellerman.id.au>2015-06-10 23:23:30 -0400
commit1cbb4a1c433a1ca054ef5363f4e6597b43d208cc (patch)
tree5e67c63ea8b1daf051b6a2fd10feaa3c02e6371d
parentcfcb3d80a28380ba027331eb548ba309c4b66559 (diff)
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>
-rw-r--r--drivers/mtd/devices/Kconfig8
-rw-r--r--drivers/mtd/devices/Makefile1
-rw-r--r--drivers/mtd/devices/powernv_flash.c285
3 files changed, 294 insertions, 0 deletions
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index c49d0b127fef..f73c41697a00 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -195,6 +195,14 @@ config MTD_BLOCK2MTD
195 Testing MTD users (eg JFFS2) on large media and media that might 195 Testing MTD users (eg JFFS2) on large media and media that might
196 be removed during a write (using the floppy drive). 196 be removed during a write (using the floppy drive).
197 197
198config 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
198comment "Disk-On-Chip Device Drivers" 206comment "Disk-On-Chip Device Drivers"
199 207
200config MTD_DOCG3 208config MTD_DOCG3
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index f0b0e611d1d6..7912d3a0ee34 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
16obj-$(CONFIG_MTD_SST25L) += sst25l.o 16obj-$(CONFIG_MTD_SST25L) += sst25l.o
17obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o 17obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o
18obj-$(CONFIG_MTD_ST_SPI_FSM) += st_spi_fsm.o 18obj-$(CONFIG_MTD_ST_SPI_FSM) += st_spi_fsm.o
19obj-$(CONFIG_MTD_POWERNV_FLASH) += powernv_flash.o
19 20
20 21
21CFLAGS_docg3.o += -I$(src) 22CFLAGS_docg3.o += -I$(src)
diff --git a/drivers/mtd/devices/powernv_flash.c b/drivers/mtd/devices/powernv_flash.c
new file mode 100644
index 000000000000..d5b870b3fd4e
--- /dev/null
+++ b/drivers/mtd/devices/powernv_flash.c
@@ -0,0 +1,285 @@
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
39struct powernv_flash {
40 struct mtd_info mtd;
41 u32 id;
42};
43
44enum flash_op {
45 FLASH_OP_READ,
46 FLASH_OP_WRITE,
47 FLASH_OP_ERASE,
48};
49
50static 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 */
119static 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 */
135static 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 */
147static 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 */
172static 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 */
216static 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
248out:
249 return ret;
250}
251
252/**
253 * op_release - Release the driver
254 * @pdev: the platform device
255 *
256 * Returns 0
257 */
258static 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
266static const struct of_device_id powernv_flash_match[] = {
267 { .compatible = "ibm,opal-flash" },
268 {}
269};
270
271static 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
280module_platform_driver(powernv_flash_driver);
281
282MODULE_DEVICE_TABLE(of, powernv_flash_match);
283MODULE_LICENSE("GPL");
284MODULE_AUTHOR("Cyril Bur <cyril.bur@au1.ibm.com>");
285MODULE_DESCRIPTION("MTD abstraction for OPAL flash");