454-nvmem-implement-block-NVMEM-provider.patch 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. From 9703951cdfe868b130e64d6122420396c2807be8 Mon Sep 17 00:00:00 2001
  2. From: Daniel Golle <[email protected]>
  3. Date: Thu, 30 May 2024 03:15:02 +0100
  4. Subject: [PATCH 5/9] nvmem: implement block NVMEM provider
  5. On embedded devices using an eMMC it is common that one or more partitions
  6. on the eMMC are used to store MAC addresses and Wi-Fi calibration EEPROM
  7. data. Allow referencing any block device or partition in Device Tree to
  8. allow e.g. Ethernet and Wi-Fi drivers accessing them via the NVMEM layer.
  9. Signed-off-by: Daniel Golle <[email protected]>
  10. ---
  11. drivers/nvmem/Kconfig | 11 +++
  12. drivers/nvmem/Makefile | 2 +
  13. drivers/nvmem/block.c | 197 +++++++++++++++++++++++++++++++++++++++++
  14. 3 files changed, 210 insertions(+)
  15. create mode 100644 drivers/nvmem/block.c
  16. --- a/drivers/nvmem/Kconfig
  17. +++ b/drivers/nvmem/Kconfig
  18. @@ -40,6 +40,17 @@ config NVMEM_APPLE_EFUSES
  19. This driver can also be built as a module. If so, the module will
  20. be called nvmem-apple-efuses.
  21. +config NVMEM_BLOCK
  22. + tristate "Block device NVMEM provider"
  23. + depends on BLOCK
  24. + depends on OF
  25. + depends on NVMEM
  26. + select BLOCK_NOTIFIERS
  27. + help
  28. + Allow block devices (or partitions) to act as NVMEM prodivers,
  29. + typically used with eMMC to store MAC addresses or Wi-Fi
  30. + calibration data on embedded devices.
  31. +
  32. config NVMEM_BCM_OCOTP
  33. tristate "Broadcom On-Chip OTP Controller support"
  34. depends on ARCH_BCM_IPROC || COMPILE_TEST
  35. --- a/drivers/nvmem/Makefile
  36. +++ b/drivers/nvmem/Makefile
  37. @@ -14,6 +14,8 @@ obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvme
  38. nvmem-apple-efuses-y := apple-efuses.o
  39. obj-$(CONFIG_NVMEM_BCM_OCOTP) += nvmem-bcm-ocotp.o
  40. nvmem-bcm-ocotp-y := bcm-ocotp.o
  41. +obj-$(CONFIG_NVMEM_BLOCK) += nvmem-block.o
  42. +nvmem-block-y := block.o
  43. obj-$(CONFIG_NVMEM_BRCM_NVRAM) += nvmem_brcm_nvram.o
  44. nvmem_brcm_nvram-y := brcm_nvram.o
  45. obj-$(CONFIG_NVMEM_IMX_IIM) += nvmem-imx-iim.o
  46. --- /dev/null
  47. +++ b/drivers/nvmem/block.c
  48. @@ -0,0 +1,208 @@
  49. +// SPDX-License-Identifier: GPL-2.0-or-later
  50. +/*
  51. + * block device NVMEM provider
  52. + *
  53. + * Copyright (c) 2024 Daniel Golle <[email protected]>
  54. + *
  55. + * Useful on devices using a partition on an eMMC for MAC addresses or
  56. + * Wi-Fi calibration EEPROM data.
  57. + */
  58. +
  59. +#include <linux/blkdev.h>
  60. +#include <linux/nvmem-provider.h>
  61. +#include <linux/of.h>
  62. +#include <linux/pagemap.h>
  63. +#include <linux/property.h>
  64. +
  65. +/* List of all NVMEM devices */
  66. +static LIST_HEAD(nvmem_devices);
  67. +static DEFINE_MUTEX(devices_mutex);
  68. +
  69. +struct blk_nvmem {
  70. + struct nvmem_device *nvmem;
  71. + struct block_device *bdev;
  72. + struct list_head list;
  73. +};
  74. +
  75. +static int blk_nvmem_reg_read(void *priv, unsigned int from,
  76. + void *val, size_t bytes)
  77. +{
  78. + unsigned long offs = from & ~PAGE_MASK, to_read;
  79. + pgoff_t f_index = from >> PAGE_SHIFT;
  80. + struct address_space *mapping;
  81. + struct blk_nvmem *bnv = priv;
  82. + size_t bytes_left = bytes;
  83. + struct folio *folio;
  84. + void *p;
  85. + int ret;
  86. +
  87. + if (!bnv->bdev)
  88. + return -ENODEV;
  89. +
  90. + if (!bnv->bdev->bd_disk)
  91. + return -EINVAL;
  92. +
  93. + if (!bnv->bdev->bd_disk->fops)
  94. + return -EIO;
  95. +
  96. + if (!bnv->bdev->bd_disk->fops->open)
  97. + return -EIO;
  98. +
  99. + ret = bnv->bdev->bd_disk->fops->open(bnv->bdev->bd_disk, BLK_OPEN_READ);
  100. + if (ret)
  101. + return ret;
  102. +
  103. + mapping = bnv->bdev->bd_inode->i_mapping;
  104. +
  105. + while (bytes_left) {
  106. + folio = read_mapping_folio(mapping, f_index++, NULL);
  107. + if (IS_ERR(folio)) {
  108. + ret = PTR_ERR(folio);
  109. + goto err_release_bdev;
  110. + }
  111. + to_read = min_t(unsigned long, bytes_left, PAGE_SIZE - offs);
  112. + p = folio_address(folio) + offset_in_folio(folio, offs);
  113. + memcpy(val, p, to_read);
  114. + offs = 0;
  115. + bytes_left -= to_read;
  116. + val += to_read;
  117. + folio_put(folio);
  118. + }
  119. +
  120. +err_release_bdev:
  121. + bnv->bdev->bd_disk->fops->release(bnv->bdev->bd_disk);
  122. +
  123. + return ret;
  124. +}
  125. +
  126. +static int blk_nvmem_register(struct device *dev)
  127. +{
  128. + struct device_node *np = dev_of_node(dev);
  129. + struct block_device *bdev = dev_to_bdev(dev);
  130. + struct nvmem_config config = {};
  131. + struct blk_nvmem *bnv;
  132. +
  133. + /* skip devices which do not have a device tree node */
  134. + if (!np)
  135. + return 0;
  136. +
  137. + /* skip devices without an nvmem layout defined */
  138. + if (!of_get_child_by_name(np, "nvmem-layout"))
  139. + return 0;
  140. +
  141. + /*
  142. + * skip devices which don't have GENHD_FL_NVMEM set
  143. + *
  144. + * This flag is used for mtdblock and ubiblock devices because
  145. + * both, MTD and UBI already implement their own NVMEM provider.
  146. + * To avoid registering multiple NVMEM providers for the same
  147. + * device node, don't register the block NVMEM provider for them.
  148. + */
  149. + if (!(bdev->bd_disk->flags & GENHD_FL_NVMEM))
  150. + return 0;
  151. +
  152. + /*
  153. + * skip block device too large to be represented as NVMEM devices
  154. + * which are using an 'int' as address
  155. + */
  156. + if (bdev_nr_bytes(bdev) > INT_MAX)
  157. + return -EFBIG;
  158. +
  159. + bnv = kzalloc(sizeof(struct blk_nvmem), GFP_KERNEL);
  160. + if (!bnv)
  161. + return -ENOMEM;
  162. +
  163. + config.id = NVMEM_DEVID_NONE;
  164. + config.dev = &bdev->bd_device;
  165. + config.name = dev_name(&bdev->bd_device);
  166. + config.owner = THIS_MODULE;
  167. + config.priv = bnv;
  168. + config.reg_read = blk_nvmem_reg_read;
  169. + config.size = bdev_nr_bytes(bdev);
  170. + config.word_size = 1;
  171. + config.stride = 1;
  172. + config.read_only = true;
  173. + config.root_only = true;
  174. + config.ignore_wp = true;
  175. + config.of_node = to_of_node(dev->fwnode);
  176. +
  177. + bnv->bdev = bdev;
  178. + bnv->nvmem = nvmem_register(&config);
  179. + if (IS_ERR(bnv->nvmem)) {
  180. + dev_err_probe(&bdev->bd_device, PTR_ERR(bnv->nvmem),
  181. + "Failed to register NVMEM device\n");
  182. +
  183. + kfree(bnv);
  184. + return PTR_ERR(bnv->nvmem);
  185. + }
  186. +
  187. + mutex_lock(&devices_mutex);
  188. + list_add_tail(&bnv->list, &nvmem_devices);
  189. + mutex_unlock(&devices_mutex);
  190. +
  191. + return 0;
  192. +}
  193. +
  194. +static void blk_nvmem_unregister(struct device *dev)
  195. +{
  196. + struct block_device *bdev = dev_to_bdev(dev);
  197. + struct blk_nvmem *bnv_c, *bnv = NULL;
  198. +
  199. + mutex_lock(&devices_mutex);
  200. + list_for_each_entry(bnv_c, &nvmem_devices, list) {
  201. + if (bnv_c->bdev == bdev) {
  202. + bnv = bnv_c;
  203. + break;
  204. + }
  205. + }
  206. +
  207. + if (!bnv) {
  208. + mutex_unlock(&devices_mutex);
  209. + return;
  210. + }
  211. +
  212. + list_del(&bnv->list);
  213. + mutex_unlock(&devices_mutex);
  214. + nvmem_unregister(bnv->nvmem);
  215. + kfree(bnv);
  216. +}
  217. +
  218. +static int blk_nvmem_handler(struct notifier_block *this, unsigned long code, void *obj)
  219. +{
  220. + struct device *dev = (struct device *)obj;
  221. +
  222. + switch (code) {
  223. + case BLK_DEVICE_ADD:
  224. + return blk_nvmem_register(dev);
  225. + case BLK_DEVICE_REMOVE:
  226. + blk_nvmem_unregister(dev);
  227. + break;
  228. + default:
  229. + return -EINVAL;
  230. + }
  231. +
  232. + return 0;
  233. +}
  234. +
  235. +static struct notifier_block blk_nvmem_notifier = {
  236. + .notifier_call = blk_nvmem_handler,
  237. +};
  238. +
  239. +static int __init blk_nvmem_init(void)
  240. +{
  241. + blk_register_notify(&blk_nvmem_notifier);
  242. +
  243. + return 0;
  244. +}
  245. +
  246. +static void __exit blk_nvmem_exit(void)
  247. +{
  248. + blk_unregister_notify(&blk_nvmem_notifier);
  249. +}
  250. +
  251. +module_init(blk_nvmem_init);
  252. +module_exit(blk_nvmem_exit);
  253. +
  254. +MODULE_LICENSE("GPL");
  255. +MODULE_AUTHOR("Daniel Golle <[email protected]>");
  256. +MODULE_DESCRIPTION("block device NVMEM provider");