xref: /openbmc/linux/drivers/mtd/devices/powernv_flash.c (revision 34d6f206a88c2651d216bd3487ac956a40b2ba8e)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21cbb4a1cSCyril Bur /*
31cbb4a1cSCyril Bur  * OPAL PNOR flash MTD abstraction
41cbb4a1cSCyril Bur  *
51cbb4a1cSCyril Bur  * Copyright IBM 2015
61cbb4a1cSCyril Bur  */
71cbb4a1cSCyril Bur 
81cbb4a1cSCyril Bur #include <linux/kernel.h>
91cbb4a1cSCyril Bur #include <linux/module.h>
101cbb4a1cSCyril Bur #include <linux/errno.h>
111cbb4a1cSCyril Bur #include <linux/of.h>
121cbb4a1cSCyril Bur #include <linux/of_address.h>
131cbb4a1cSCyril Bur #include <linux/platform_device.h>
141cbb4a1cSCyril Bur #include <linux/string.h>
151cbb4a1cSCyril Bur #include <linux/slab.h>
161cbb4a1cSCyril Bur #include <linux/mtd/mtd.h>
171cbb4a1cSCyril Bur #include <linux/mtd/partitions.h>
181cbb4a1cSCyril Bur 
191cbb4a1cSCyril Bur #include <linux/debugfs.h>
201cbb4a1cSCyril Bur #include <linux/seq_file.h>
211cbb4a1cSCyril Bur 
221cbb4a1cSCyril Bur #include <asm/opal.h>
231cbb4a1cSCyril Bur 
241cbb4a1cSCyril Bur 
251cbb4a1cSCyril Bur /*
261cbb4a1cSCyril Bur  * This driver creates the a Linux MTD abstraction for platform PNOR flash
271cbb4a1cSCyril Bur  * backed by OPAL calls
281cbb4a1cSCyril Bur  */
291cbb4a1cSCyril Bur 
301cbb4a1cSCyril Bur struct powernv_flash {
311cbb4a1cSCyril Bur 	struct mtd_info	mtd;
321cbb4a1cSCyril Bur 	u32 id;
331cbb4a1cSCyril Bur };
341cbb4a1cSCyril Bur 
351cbb4a1cSCyril Bur enum flash_op {
361cbb4a1cSCyril Bur 	FLASH_OP_READ,
371cbb4a1cSCyril Bur 	FLASH_OP_WRITE,
381cbb4a1cSCyril Bur 	FLASH_OP_ERASE,
391cbb4a1cSCyril Bur };
401cbb4a1cSCyril Bur 
41efe69414SCyril Bur /*
42efe69414SCyril Bur  * Don't return -ERESTARTSYS if we can't get a token, the MTD core
43efe69414SCyril Bur  * might have split up the call from userspace and called into the
44efe69414SCyril Bur  * driver more than once, we'll already have done some amount of work.
45efe69414SCyril Bur  */
powernv_flash_async_op(struct mtd_info * mtd,enum flash_op op,loff_t offset,size_t len,size_t * retlen,u_char * buf)461cbb4a1cSCyril Bur static int powernv_flash_async_op(struct mtd_info *mtd, enum flash_op op,
471cbb4a1cSCyril Bur 		loff_t offset, size_t len, size_t *retlen, u_char *buf)
481cbb4a1cSCyril Bur {
491cbb4a1cSCyril Bur 	struct powernv_flash *info = (struct powernv_flash *)mtd->priv;
501cbb4a1cSCyril Bur 	struct device *dev = &mtd->dev;
511cbb4a1cSCyril Bur 	int token;
521cbb4a1cSCyril Bur 	struct opal_msg msg;
531cbb4a1cSCyril Bur 	int rc;
541cbb4a1cSCyril Bur 
551cbb4a1cSCyril Bur 	dev_dbg(dev, "%s(op=%d, offset=0x%llx, len=%zu)\n",
561cbb4a1cSCyril Bur 			__func__, op, offset, len);
571cbb4a1cSCyril Bur 
581cbb4a1cSCyril Bur 	token = opal_async_get_token_interruptible();
591cbb4a1cSCyril Bur 	if (token < 0) {
601cbb4a1cSCyril Bur 		if (token != -ERESTARTSYS)
611cbb4a1cSCyril Bur 			dev_err(dev, "Failed to get an async token\n");
62efe69414SCyril Bur 		else
63efe69414SCyril Bur 			token = -EINTR;
641cbb4a1cSCyril Bur 		return token;
651cbb4a1cSCyril Bur 	}
661cbb4a1cSCyril Bur 
671cbb4a1cSCyril Bur 	switch (op) {
681cbb4a1cSCyril Bur 	case FLASH_OP_READ:
691cbb4a1cSCyril Bur 		rc = opal_flash_read(info->id, offset, __pa(buf), len, token);
701cbb4a1cSCyril Bur 		break;
711cbb4a1cSCyril Bur 	case FLASH_OP_WRITE:
721cbb4a1cSCyril Bur 		rc = opal_flash_write(info->id, offset, __pa(buf), len, token);
731cbb4a1cSCyril Bur 		break;
741cbb4a1cSCyril Bur 	case FLASH_OP_ERASE:
751cbb4a1cSCyril Bur 		rc = opal_flash_erase(info->id, offset, len, token);
761cbb4a1cSCyril Bur 		break;
771cbb4a1cSCyril Bur 	default:
7844e2aa2bSCyril Bur 		WARN_ON_ONCE(1);
7944e2aa2bSCyril Bur 		opal_async_release_token(token);
8044e2aa2bSCyril Bur 		return -EIO;
811cbb4a1cSCyril Bur 	}
821cbb4a1cSCyril Bur 
836f469b67SCyril Bur 	if (rc == OPAL_ASYNC_COMPLETION) {
846f469b67SCyril Bur 		rc = opal_async_wait_response_interruptible(token, &msg);
856f469b67SCyril Bur 		if (rc) {
866f469b67SCyril Bur 			/*
876f469b67SCyril Bur 			 * If we return the mtd core will free the
886f469b67SCyril Bur 			 * buffer we've just passed to OPAL but OPAL
896f469b67SCyril Bur 			 * will continue to read or write from that
906f469b67SCyril Bur 			 * memory.
916f469b67SCyril Bur 			 * It may be tempting to ultimately return 0
926f469b67SCyril Bur 			 * if we're doing a read or a write since we
936f469b67SCyril Bur 			 * are going to end up waiting until OPAL is
946f469b67SCyril Bur 			 * done. However, because the MTD core sends
956f469b67SCyril Bur 			 * us the userspace request in chunks, we need
966f469b67SCyril Bur 			 * it to know we've been interrupted.
976f469b67SCyril Bur 			 */
986f469b67SCyril Bur 			rc = -EINTR;
996f469b67SCyril Bur 			if (opal_async_wait_response(token, &msg))
1006f469b67SCyril Bur 				dev_err(dev, "opal_async_wait_response() failed\n");
1016f469b67SCyril Bur 			goto out;
1026f469b67SCyril Bur 		}
1036f469b67SCyril Bur 		rc = opal_get_async_rc(msg);
1046f469b67SCyril Bur 	}
10525ee52e6SCyril Bur 
1066f469b67SCyril Bur 	/*
1076f469b67SCyril Bur 	 * OPAL does mutual exclusion on the flash, it will return
1086f469b67SCyril Bur 	 * OPAL_BUSY.
1096f469b67SCyril Bur 	 * During firmware updates by the service processor OPAL may
1106f469b67SCyril Bur 	 * be (temporarily) prevented from accessing the flash, in
1116f469b67SCyril Bur 	 * this case OPAL will also return OPAL_BUSY.
1126f469b67SCyril Bur 	 * Both cases aren't errors exactly but the flash could have
1136f469b67SCyril Bur 	 * changed, userspace should be informed.
1146f469b67SCyril Bur 	 */
1156f469b67SCyril Bur 	if (rc != OPAL_SUCCESS && rc != OPAL_BUSY)
1161cbb4a1cSCyril Bur 		dev_err(dev, "opal_flash_async_op(op=%d) failed (rc %d)\n",
1171cbb4a1cSCyril Bur 				op, rc);
1181cbb4a1cSCyril Bur 
1196f469b67SCyril Bur 	if (rc == OPAL_SUCCESS && retlen)
1201cbb4a1cSCyril Bur 		*retlen = len;
1211cbb4a1cSCyril Bur 
1226f469b67SCyril Bur 	rc = opal_error_code(rc);
12325ee52e6SCyril Bur out:
12425ee52e6SCyril Bur 	opal_async_release_token(token);
1251cbb4a1cSCyril Bur 	return rc;
1261cbb4a1cSCyril Bur }
1271cbb4a1cSCyril Bur 
1281cbb4a1cSCyril Bur /**
12922ca56a3SLee Jones  * powernv_flash_read
1301cbb4a1cSCyril Bur  * @mtd: the device
1311cbb4a1cSCyril Bur  * @from: the offset to read from
1321cbb4a1cSCyril Bur  * @len: the number of bytes to read
1331cbb4a1cSCyril Bur  * @retlen: the number of bytes actually read
1341cbb4a1cSCyril Bur  * @buf: the filled in buffer
1351cbb4a1cSCyril Bur  *
1361cbb4a1cSCyril Bur  * Returns 0 if read successful, or -ERRNO if an error occurred
1371cbb4a1cSCyril Bur  */
powernv_flash_read(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,u_char * buf)1381cbb4a1cSCyril Bur static int powernv_flash_read(struct mtd_info *mtd, loff_t from, size_t len,
1391cbb4a1cSCyril Bur 	     size_t *retlen, u_char *buf)
1401cbb4a1cSCyril Bur {
1411cbb4a1cSCyril Bur 	return powernv_flash_async_op(mtd, FLASH_OP_READ, from,
1421cbb4a1cSCyril Bur 			len, retlen, buf);
1431cbb4a1cSCyril Bur }
1441cbb4a1cSCyril Bur 
1451cbb4a1cSCyril Bur /**
14622ca56a3SLee Jones  * powernv_flash_write
1471cbb4a1cSCyril Bur  * @mtd: the device
1481cbb4a1cSCyril Bur  * @to: the offset to write to
1491cbb4a1cSCyril Bur  * @len: the number of bytes to write
1501cbb4a1cSCyril Bur  * @retlen: the number of bytes actually written
1511cbb4a1cSCyril Bur  * @buf: the buffer to get bytes from
1521cbb4a1cSCyril Bur  *
1531cbb4a1cSCyril Bur  * Returns 0 if write successful, -ERRNO if error occurred
1541cbb4a1cSCyril Bur  */
powernv_flash_write(struct mtd_info * mtd,loff_t to,size_t len,size_t * retlen,const u_char * buf)1551cbb4a1cSCyril Bur static int powernv_flash_write(struct mtd_info *mtd, loff_t to, size_t len,
1561cbb4a1cSCyril Bur 		     size_t *retlen, const u_char *buf)
1571cbb4a1cSCyril Bur {
1581cbb4a1cSCyril Bur 	return powernv_flash_async_op(mtd, FLASH_OP_WRITE, to,
1591cbb4a1cSCyril Bur 			len, retlen, (u_char *)buf);
1601cbb4a1cSCyril Bur }
1611cbb4a1cSCyril Bur 
1621cbb4a1cSCyril Bur /**
16322ca56a3SLee Jones  * powernv_flash_erase
1641cbb4a1cSCyril Bur  * @mtd: the device
1651cbb4a1cSCyril Bur  * @erase: the erase info
1661cbb4a1cSCyril Bur  * Returns 0 if erase successful or -ERRNO if an error occurred
1671cbb4a1cSCyril Bur  */
powernv_flash_erase(struct mtd_info * mtd,struct erase_info * erase)1681cbb4a1cSCyril Bur static int powernv_flash_erase(struct mtd_info *mtd, struct erase_info *erase)
1691cbb4a1cSCyril Bur {
1701cbb4a1cSCyril Bur 	int rc;
1711cbb4a1cSCyril Bur 
1721cbb4a1cSCyril Bur 	rc =  powernv_flash_async_op(mtd, FLASH_OP_ERASE, erase->addr,
1731cbb4a1cSCyril Bur 			erase->len, NULL, NULL);
174e7bfb3fdSBoris Brezillon 	if (rc)
1751cbb4a1cSCyril Bur 		erase->fail_addr = erase->addr;
176e7bfb3fdSBoris Brezillon 
1771cbb4a1cSCyril Bur 	return rc;
1781cbb4a1cSCyril Bur }
1791cbb4a1cSCyril Bur 
1801cbb4a1cSCyril Bur /**
1811cbb4a1cSCyril Bur  * powernv_flash_set_driver_info - Fill the mtd_info structure and docg3
18222ca56a3SLee Jones  * @dev: The device structure
1831cbb4a1cSCyril Bur  * @mtd: The structure to fill
1841cbb4a1cSCyril Bur  */
powernv_flash_set_driver_info(struct device * dev,struct mtd_info * mtd)1851cbb4a1cSCyril Bur static int powernv_flash_set_driver_info(struct device *dev,
1861cbb4a1cSCyril Bur 		struct mtd_info *mtd)
1871cbb4a1cSCyril Bur {
1881cbb4a1cSCyril Bur 	u64 size;
1891cbb4a1cSCyril Bur 	u32 erase_size;
1901cbb4a1cSCyril Bur 	int rc;
1911cbb4a1cSCyril Bur 
1921cbb4a1cSCyril Bur 	rc = of_property_read_u32(dev->of_node, "ibm,flash-block-size",
1931cbb4a1cSCyril Bur 			&erase_size);
1941cbb4a1cSCyril Bur 	if (rc) {
1951cbb4a1cSCyril Bur 		dev_err(dev, "couldn't get resource block size information\n");
1961cbb4a1cSCyril Bur 		return rc;
1971cbb4a1cSCyril Bur 	}
1981cbb4a1cSCyril Bur 
1991cbb4a1cSCyril Bur 	rc = of_property_read_u64(dev->of_node, "reg", &size);
2001cbb4a1cSCyril Bur 	if (rc) {
2011cbb4a1cSCyril Bur 		dev_err(dev, "couldn't get resource size information\n");
2021cbb4a1cSCyril Bur 		return rc;
2031cbb4a1cSCyril Bur 	}
2041cbb4a1cSCyril Bur 
2051cbb4a1cSCyril Bur 	/*
2061cbb4a1cSCyril Bur 	 * Going to have to check what details I need to set and how to
2071cbb4a1cSCyril Bur 	 * get them
2081cbb4a1cSCyril Bur 	 */
2093e35730dSAneesh Kumar K.V 	mtd->name = devm_kasprintf(dev, GFP_KERNEL, "%pOFP", dev->of_node);
210*4e31e504SCharles Han 	if (!mtd->name)
211*4e31e504SCharles Han 		return -ENOMEM;
212*4e31e504SCharles Han 
2131cbb4a1cSCyril Bur 	mtd->type = MTD_NORFLASH;
2141cbb4a1cSCyril Bur 	mtd->flags = MTD_WRITEABLE;
2151cbb4a1cSCyril Bur 	mtd->size = size;
2161cbb4a1cSCyril Bur 	mtd->erasesize = erase_size;
2171cbb4a1cSCyril Bur 	mtd->writebufsize = mtd->writesize = 1;
2181cbb4a1cSCyril Bur 	mtd->owner = THIS_MODULE;
2191cbb4a1cSCyril Bur 	mtd->_erase = powernv_flash_erase;
2201cbb4a1cSCyril Bur 	mtd->_read = powernv_flash_read;
2211cbb4a1cSCyril Bur 	mtd->_write = powernv_flash_write;
2221cbb4a1cSCyril Bur 	mtd->dev.parent = dev;
2239ea97a7dSRafał Miłecki 	mtd_set_of_node(mtd, dev->of_node);
2241cbb4a1cSCyril Bur 	return 0;
2251cbb4a1cSCyril Bur }
2261cbb4a1cSCyril Bur 
2271cbb4a1cSCyril Bur /**
2281cbb4a1cSCyril Bur  * powernv_flash_probe
2291cbb4a1cSCyril Bur  * @pdev: platform device
2301cbb4a1cSCyril Bur  *
2311cbb4a1cSCyril Bur  * Returns 0 on success, -ENOMEM, -ENXIO on error
2321cbb4a1cSCyril Bur  */
powernv_flash_probe(struct platform_device * pdev)2331cbb4a1cSCyril Bur static int powernv_flash_probe(struct platform_device *pdev)
2341cbb4a1cSCyril Bur {
2351cbb4a1cSCyril Bur 	struct device *dev = &pdev->dev;
2361cbb4a1cSCyril Bur 	struct powernv_flash *data;
2371cbb4a1cSCyril Bur 	int ret;
2381cbb4a1cSCyril Bur 
2391cbb4a1cSCyril Bur 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
240e32ec15aSCyril Bur 	if (!data)
241e32ec15aSCyril Bur 		return -ENOMEM;
242e32ec15aSCyril Bur 
2431cbb4a1cSCyril Bur 	data->mtd.priv = data;
2441cbb4a1cSCyril Bur 
2451cbb4a1cSCyril Bur 	ret = of_property_read_u32(dev->of_node, "ibm,opal-id", &(data->id));
2461cbb4a1cSCyril Bur 	if (ret) {
2471cbb4a1cSCyril Bur 		dev_err(dev, "no device property 'ibm,opal-id'\n");
248e32ec15aSCyril Bur 		return ret;
2491cbb4a1cSCyril Bur 	}
2501cbb4a1cSCyril Bur 
2511cbb4a1cSCyril Bur 	ret = powernv_flash_set_driver_info(dev, &data->mtd);
2521cbb4a1cSCyril Bur 	if (ret)
253e32ec15aSCyril Bur 		return ret;
2541cbb4a1cSCyril Bur 
2551cbb4a1cSCyril Bur 	dev_set_drvdata(dev, data);
2561cbb4a1cSCyril Bur 
2571cbb4a1cSCyril Bur 	/*
2581cbb4a1cSCyril Bur 	 * The current flash that skiboot exposes is one contiguous flash chip
2591cbb4a1cSCyril Bur 	 * with an ffs partition at the start, it should prove easier for users
2601cbb4a1cSCyril Bur 	 * to deal with partitions or not as they see fit
2611cbb4a1cSCyril Bur 	 */
262e32ec15aSCyril Bur 	return mtd_device_register(&data->mtd, NULL, 0);
2631cbb4a1cSCyril Bur }
2641cbb4a1cSCyril Bur 
2651cbb4a1cSCyril Bur /**
2661cbb4a1cSCyril Bur  * op_release - Release the driver
2671cbb4a1cSCyril Bur  * @pdev: the platform device
2681cbb4a1cSCyril Bur  *
2691cbb4a1cSCyril Bur  * Returns 0
2701cbb4a1cSCyril Bur  */
powernv_flash_release(struct platform_device * pdev)2711cbb4a1cSCyril Bur static int powernv_flash_release(struct platform_device *pdev)
2721cbb4a1cSCyril Bur {
2731cbb4a1cSCyril Bur 	struct powernv_flash *data = dev_get_drvdata(&(pdev->dev));
2741cbb4a1cSCyril Bur 
2751cbb4a1cSCyril Bur 	/* All resources should be freed automatically */
27690914b4eSUwe Kleine-König 	WARN_ON(mtd_device_unregister(&data->mtd));
27790914b4eSUwe Kleine-König 
27890914b4eSUwe Kleine-König 	return 0;
2791cbb4a1cSCyril Bur }
2801cbb4a1cSCyril Bur 
2811cbb4a1cSCyril Bur static const struct of_device_id powernv_flash_match[] = {
2821cbb4a1cSCyril Bur 	{ .compatible = "ibm,opal-flash" },
2831cbb4a1cSCyril Bur 	{}
2841cbb4a1cSCyril Bur };
2851cbb4a1cSCyril Bur 
2861cbb4a1cSCyril Bur static struct platform_driver powernv_flash_driver = {
2871cbb4a1cSCyril Bur 	.driver		= {
2881cbb4a1cSCyril Bur 		.name		= "powernv_flash",
2891cbb4a1cSCyril Bur 		.of_match_table	= powernv_flash_match,
2901cbb4a1cSCyril Bur 	},
2911cbb4a1cSCyril Bur 	.remove		= powernv_flash_release,
2921cbb4a1cSCyril Bur 	.probe		= powernv_flash_probe,
2931cbb4a1cSCyril Bur };
2941cbb4a1cSCyril Bur 
2951cbb4a1cSCyril Bur module_platform_driver(powernv_flash_driver);
2961cbb4a1cSCyril Bur 
2971cbb4a1cSCyril Bur MODULE_DEVICE_TABLE(of, powernv_flash_match);
2981cbb4a1cSCyril Bur MODULE_LICENSE("GPL");
2991cbb4a1cSCyril Bur MODULE_AUTHOR("Cyril Bur <cyril.bur@au1.ibm.com>");
3001cbb4a1cSCyril Bur MODULE_DESCRIPTION("MTD abstraction for OPAL flash");
301