xref: /linux/drivers/mtd/devices/powernv_flash.c (revision ca55b2fef3a9373fcfc30f82fd26bc7fccbda732)
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");
286