2 * OPAL PNOR flash MTD abstraction
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.
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.
17 #include <linux/kernel.h>
18 #include <linux/module.h>
19 #include <linux/errno.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>
28 #include <linux/debugfs.h>
29 #include <linux/seq_file.h>
35 * This driver creates the a Linux MTD abstraction for platform PNOR flash
36 * backed by OPAL calls
39 struct powernv_flash
{
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
)
53 struct powernv_flash
*info
= (struct powernv_flash
*)mtd
->priv
;
54 struct device
*dev
= &mtd
->dev
;
59 dev_dbg(dev
, "%s(op=%d, offset=0x%llx, len=%zu)\n",
60 __func__
, op
, offset
, len
);
62 token
= opal_async_get_token_interruptible();
64 if (token
!= -ERESTARTSYS
)
65 dev_err(dev
, "Failed to get an async token\n");
72 rc
= opal_flash_read(info
->id
, offset
, __pa(buf
), len
, token
);
75 rc
= opal_flash_write(info
->id
, offset
, __pa(buf
), len
, token
);
78 rc
= opal_flash_erase(info
->id
, offset
, len
, token
);
84 if (rc
!= OPAL_ASYNC_COMPLETION
) {
85 dev_err(dev
, "opal_flash_async_op(op=%d) failed (rc %d)\n",
87 opal_async_release_token(token
);
91 rc
= opal_async_wait_response(token
, &msg
);
92 opal_async_release_token(token
);
94 dev_err(dev
, "opal async wait failed (rc %d)\n", rc
);
98 rc
= be64_to_cpu(msg
.params
[1]);
99 if (rc
== OPAL_SUCCESS
) {
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
117 * Returns 0 if read successful, or -ERRNO if an error occurred
119 static int powernv_flash_read(struct mtd_info
*mtd
, loff_t from
, size_t len
,
120 size_t *retlen
, u_char
*buf
)
122 return powernv_flash_async_op(mtd
, FLASH_OP_READ
, from
,
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
133 * Returns 0 if write successful, -ERRNO if error occurred
135 static int powernv_flash_write(struct mtd_info
*mtd
, loff_t to
, size_t len
,
136 size_t *retlen
, const u_char
*buf
)
138 return powernv_flash_async_op(mtd
, FLASH_OP_WRITE
, to
,
139 len
, retlen
, (u_char
*)buf
);
144 * @erase: the erase info
145 * Returns 0 if erase successful or -ERRNO if an error occurred
147 static int powernv_flash_erase(struct mtd_info
*mtd
, struct erase_info
*erase
)
151 erase
->state
= MTD_ERASING
;
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
);
158 erase
->fail_addr
= erase
->addr
;
159 erase
->state
= MTD_ERASE_FAILED
;
161 erase
->state
= MTD_ERASE_DONE
;
163 mtd_erase_callback(erase
);
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
172 static int powernv_flash_set_driver_info(struct device
*dev
,
173 struct mtd_info
*mtd
)
179 rc
= of_property_read_u32(dev
->of_node
, "ibm,flash-block-size",
182 dev_err(dev
, "couldn't get resource block size information\n");
186 rc
= of_property_read_u64(dev
->of_node
, "reg", &size
);
188 dev_err(dev
, "couldn't get resource size information\n");
193 * Going to have to check what details I need to set and how to
196 mtd
->name
= of_get_property(dev
->of_node
, "name", NULL
);
197 mtd
->type
= MTD_NORFLASH
;
198 mtd
->flags
= MTD_WRITEABLE
;
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
;
211 * powernv_flash_probe
212 * @pdev: platform device
214 * Returns 0 on success, -ENOMEM, -ENXIO on error
216 static int powernv_flash_probe(struct platform_device
*pdev
)
218 struct device
*dev
= &pdev
->dev
;
219 struct powernv_flash
*data
;
222 data
= devm_kzalloc(dev
, sizeof(*data
), GFP_KERNEL
);
227 data
->mtd
.priv
= data
;
229 ret
= of_property_read_u32(dev
->of_node
, "ibm,opal-id", &(data
->id
));
231 dev_err(dev
, "no device property 'ibm,opal-id'\n");
235 ret
= powernv_flash_set_driver_info(dev
, &data
->mtd
);
239 dev_set_drvdata(dev
, data
);
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
246 ret
= mtd_device_register(&data
->mtd
, NULL
, 0);
253 * op_release - Release the driver
254 * @pdev: the platform device
258 static int powernv_flash_release(struct platform_device
*pdev
)
260 struct powernv_flash
*data
= dev_get_drvdata(&(pdev
->dev
));
262 /* All resources should be freed automatically */
263 return mtd_device_unregister(&(data
->mtd
));
266 static const struct of_device_id powernv_flash_match
[] = {
267 { .compatible
= "ibm,opal-flash" },
271 static struct platform_driver powernv_flash_driver
= {
273 .name
= "powernv_flash",
274 .of_match_table
= powernv_flash_match
,
276 .remove
= powernv_flash_release
,
277 .probe
= powernv_flash_probe
,
280 module_platform_driver(powernv_flash_driver
);
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");