/drivers/mfd/mfd-core.c

http://github.com/mirrors/linux · C · 349 lines · 257 code · 52 blank · 40 comment · 45 complexity · dca60ca7a586ec182a5d7ef771c79a9d MD5 · raw file

  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * drivers/mfd/mfd-core.c
  4. *
  5. * core MFD support
  6. * Copyright (c) 2006 Ian Molton
  7. * Copyright (c) 2007,2008 Dmitry Baryshkov
  8. */
  9. #include <linux/kernel.h>
  10. #include <linux/platform_device.h>
  11. #include <linux/acpi.h>
  12. #include <linux/property.h>
  13. #include <linux/mfd/core.h>
  14. #include <linux/pm_runtime.h>
  15. #include <linux/slab.h>
  16. #include <linux/module.h>
  17. #include <linux/irqdomain.h>
  18. #include <linux/of.h>
  19. #include <linux/regulator/consumer.h>
  20. static struct device_type mfd_dev_type = {
  21. .name = "mfd_device",
  22. };
  23. int mfd_cell_enable(struct platform_device *pdev)
  24. {
  25. const struct mfd_cell *cell = mfd_get_cell(pdev);
  26. if (!cell->enable) {
  27. dev_dbg(&pdev->dev, "No .enable() call-back registered\n");
  28. return 0;
  29. }
  30. return cell->enable(pdev);
  31. }
  32. EXPORT_SYMBOL(mfd_cell_enable);
  33. int mfd_cell_disable(struct platform_device *pdev)
  34. {
  35. const struct mfd_cell *cell = mfd_get_cell(pdev);
  36. if (!cell->disable) {
  37. dev_dbg(&pdev->dev, "No .disable() call-back registered\n");
  38. return 0;
  39. }
  40. return cell->disable(pdev);
  41. }
  42. EXPORT_SYMBOL(mfd_cell_disable);
  43. #if IS_ENABLED(CONFIG_ACPI)
  44. static void mfd_acpi_add_device(const struct mfd_cell *cell,
  45. struct platform_device *pdev)
  46. {
  47. const struct mfd_cell_acpi_match *match = cell->acpi_match;
  48. struct acpi_device *parent, *child;
  49. struct acpi_device *adev;
  50. parent = ACPI_COMPANION(pdev->dev.parent);
  51. if (!parent)
  52. return;
  53. /*
  54. * MFD child device gets its ACPI handle either from the ACPI device
  55. * directly under the parent that matches the either _HID or _CID, or
  56. * _ADR or it will use the parent handle if is no ID is given.
  57. *
  58. * Note that use of _ADR is a grey area in the ACPI specification,
  59. * though Intel Galileo Gen2 is using it to distinguish the children
  60. * devices.
  61. */
  62. adev = parent;
  63. if (match) {
  64. if (match->pnpid) {
  65. struct acpi_device_id ids[2] = {};
  66. strlcpy(ids[0].id, match->pnpid, sizeof(ids[0].id));
  67. list_for_each_entry(child, &parent->children, node) {
  68. if (!acpi_match_device_ids(child, ids)) {
  69. adev = child;
  70. break;
  71. }
  72. }
  73. } else {
  74. unsigned long long adr;
  75. acpi_status status;
  76. list_for_each_entry(child, &parent->children, node) {
  77. status = acpi_evaluate_integer(child->handle,
  78. "_ADR", NULL,
  79. &adr);
  80. if (ACPI_SUCCESS(status) && match->adr == adr) {
  81. adev = child;
  82. break;
  83. }
  84. }
  85. }
  86. }
  87. ACPI_COMPANION_SET(&pdev->dev, adev);
  88. }
  89. #else
  90. static inline void mfd_acpi_add_device(const struct mfd_cell *cell,
  91. struct platform_device *pdev)
  92. {
  93. }
  94. #endif
  95. static int mfd_add_device(struct device *parent, int id,
  96. const struct mfd_cell *cell,
  97. struct resource *mem_base,
  98. int irq_base, struct irq_domain *domain)
  99. {
  100. struct resource *res;
  101. struct platform_device *pdev;
  102. struct device_node *np = NULL;
  103. int ret = -ENOMEM;
  104. int platform_id;
  105. int r;
  106. if (id == PLATFORM_DEVID_AUTO)
  107. platform_id = id;
  108. else
  109. platform_id = id + cell->id;
  110. pdev = platform_device_alloc(cell->name, platform_id);
  111. if (!pdev)
  112. goto fail_alloc;
  113. pdev->mfd_cell = kmemdup(cell, sizeof(*cell), GFP_KERNEL);
  114. if (!pdev->mfd_cell)
  115. goto fail_device;
  116. res = kcalloc(cell->num_resources, sizeof(*res), GFP_KERNEL);
  117. if (!res)
  118. goto fail_device;
  119. pdev->dev.parent = parent;
  120. pdev->dev.type = &mfd_dev_type;
  121. pdev->dev.dma_mask = parent->dma_mask;
  122. pdev->dev.dma_parms = parent->dma_parms;
  123. pdev->dev.coherent_dma_mask = parent->coherent_dma_mask;
  124. ret = regulator_bulk_register_supply_alias(
  125. &pdev->dev, cell->parent_supplies,
  126. parent, cell->parent_supplies,
  127. cell->num_parent_supplies);
  128. if (ret < 0)
  129. goto fail_res;
  130. if (parent->of_node && cell->of_compatible) {
  131. for_each_child_of_node(parent->of_node, np) {
  132. if (of_device_is_compatible(np, cell->of_compatible)) {
  133. if (!of_device_is_available(np)) {
  134. /* Ignore disabled devices error free */
  135. ret = 0;
  136. goto fail_alias;
  137. }
  138. pdev->dev.of_node = np;
  139. pdev->dev.fwnode = &np->fwnode;
  140. break;
  141. }
  142. }
  143. }
  144. mfd_acpi_add_device(cell, pdev);
  145. if (cell->pdata_size) {
  146. ret = platform_device_add_data(pdev,
  147. cell->platform_data, cell->pdata_size);
  148. if (ret)
  149. goto fail_alias;
  150. }
  151. if (cell->properties) {
  152. ret = platform_device_add_properties(pdev, cell->properties);
  153. if (ret)
  154. goto fail_alias;
  155. }
  156. for (r = 0; r < cell->num_resources; r++) {
  157. res[r].name = cell->resources[r].name;
  158. res[r].flags = cell->resources[r].flags;
  159. /* Find out base to use */
  160. if ((cell->resources[r].flags & IORESOURCE_MEM) && mem_base) {
  161. res[r].parent = mem_base;
  162. res[r].start = mem_base->start +
  163. cell->resources[r].start;
  164. res[r].end = mem_base->start +
  165. cell->resources[r].end;
  166. } else if (cell->resources[r].flags & IORESOURCE_IRQ) {
  167. if (domain) {
  168. /* Unable to create mappings for IRQ ranges. */
  169. WARN_ON(cell->resources[r].start !=
  170. cell->resources[r].end);
  171. res[r].start = res[r].end = irq_create_mapping(
  172. domain, cell->resources[r].start);
  173. } else {
  174. res[r].start = irq_base +
  175. cell->resources[r].start;
  176. res[r].end = irq_base +
  177. cell->resources[r].end;
  178. }
  179. } else {
  180. res[r].parent = cell->resources[r].parent;
  181. res[r].start = cell->resources[r].start;
  182. res[r].end = cell->resources[r].end;
  183. }
  184. if (!cell->ignore_resource_conflicts) {
  185. if (has_acpi_companion(&pdev->dev)) {
  186. ret = acpi_check_resource_conflict(&res[r]);
  187. if (ret)
  188. goto fail_alias;
  189. }
  190. }
  191. }
  192. ret = platform_device_add_resources(pdev, res, cell->num_resources);
  193. if (ret)
  194. goto fail_alias;
  195. ret = platform_device_add(pdev);
  196. if (ret)
  197. goto fail_alias;
  198. if (cell->pm_runtime_no_callbacks)
  199. pm_runtime_no_callbacks(&pdev->dev);
  200. kfree(res);
  201. return 0;
  202. fail_alias:
  203. regulator_bulk_unregister_supply_alias(&pdev->dev,
  204. cell->parent_supplies,
  205. cell->num_parent_supplies);
  206. fail_res:
  207. kfree(res);
  208. fail_device:
  209. platform_device_put(pdev);
  210. fail_alloc:
  211. return ret;
  212. }
  213. /**
  214. * mfd_add_devices - register child devices
  215. *
  216. * @parent: Pointer to parent device.
  217. * @id: Can be PLATFORM_DEVID_AUTO to let the Platform API take care
  218. * of device numbering, or will be added to a device's cell_id.
  219. * @cells: Array of (struct mfd_cell)s describing child devices.
  220. * @n_devs: Number of child devices to register.
  221. * @mem_base: Parent register range resource for child devices.
  222. * @irq_base: Base of the range of virtual interrupt numbers allocated for
  223. * this MFD device. Unused if @domain is specified.
  224. * @domain: Interrupt domain to create mappings for hardware interrupts.
  225. */
  226. int mfd_add_devices(struct device *parent, int id,
  227. const struct mfd_cell *cells, int n_devs,
  228. struct resource *mem_base,
  229. int irq_base, struct irq_domain *domain)
  230. {
  231. int i;
  232. int ret;
  233. for (i = 0; i < n_devs; i++) {
  234. ret = mfd_add_device(parent, id, cells + i, mem_base,
  235. irq_base, domain);
  236. if (ret)
  237. goto fail;
  238. }
  239. return 0;
  240. fail:
  241. if (i)
  242. mfd_remove_devices(parent);
  243. return ret;
  244. }
  245. EXPORT_SYMBOL(mfd_add_devices);
  246. static int mfd_remove_devices_fn(struct device *dev, void *data)
  247. {
  248. struct platform_device *pdev;
  249. const struct mfd_cell *cell;
  250. if (dev->type != &mfd_dev_type)
  251. return 0;
  252. pdev = to_platform_device(dev);
  253. cell = mfd_get_cell(pdev);
  254. regulator_bulk_unregister_supply_alias(dev, cell->parent_supplies,
  255. cell->num_parent_supplies);
  256. platform_device_unregister(pdev);
  257. return 0;
  258. }
  259. void mfd_remove_devices(struct device *parent)
  260. {
  261. device_for_each_child_reverse(parent, NULL, mfd_remove_devices_fn);
  262. }
  263. EXPORT_SYMBOL(mfd_remove_devices);
  264. static void devm_mfd_dev_release(struct device *dev, void *res)
  265. {
  266. mfd_remove_devices(dev);
  267. }
  268. /**
  269. * devm_mfd_add_devices - Resource managed version of mfd_add_devices()
  270. *
  271. * Returns 0 on success or an appropriate negative error number on failure.
  272. * All child-devices of the MFD will automatically be removed when it gets
  273. * unbinded.
  274. */
  275. int devm_mfd_add_devices(struct device *dev, int id,
  276. const struct mfd_cell *cells, int n_devs,
  277. struct resource *mem_base,
  278. int irq_base, struct irq_domain *domain)
  279. {
  280. struct device **ptr;
  281. int ret;
  282. ptr = devres_alloc(devm_mfd_dev_release, sizeof(*ptr), GFP_KERNEL);
  283. if (!ptr)
  284. return -ENOMEM;
  285. ret = mfd_add_devices(dev, id, cells, n_devs, mem_base,
  286. irq_base, domain);
  287. if (ret < 0) {
  288. devres_free(ptr);
  289. return ret;
  290. }
  291. *ptr = dev;
  292. devres_add(dev, ptr);
  293. return ret;
  294. }
  295. EXPORT_SYMBOL(devm_mfd_add_devices);
  296. MODULE_LICENSE("GPL");
  297. MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov");