1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * ARM Integrator Logical Module bus driver 4 * Copyright (C) 2020 Linaro Ltd. 5 * Author: Linus Walleij <linus.walleij@linaro.org> 6 * 7 * See the device tree bindings for this block for more details on the 8 * hardware. 9 */ 10 11 #include <linux/module.h> 12 #include <linux/err.h> 13 #include <linux/io.h> 14 #include <linux/of.h> 15 #include <linux/of_address.h> 16 #include <linux/of_platform.h> 17 #include <linux/init.h> 18 #include <linux/slab.h> 19 #include <linux/platform_device.h> 20 #include <linux/bitops.h> 21 #include <linux/mfd/syscon.h> 22 #include <linux/regmap.h> 23 24 /* All information about the connected logic modules are in here */ 25 #define INTEGRATOR_SC_DEC_OFFSET 0x10 26 27 /* Base address for the expansion modules */ 28 #define INTEGRATOR_AP_EXP_BASE 0xc0000000 29 #define INTEGRATOR_AP_EXP_STRIDE 0x10000000 30 31 static int integrator_lm_populate(int num, struct device *dev) 32 { 33 struct device_node *np = dev->of_node; 34 struct device_node *child; 35 u32 base; 36 int ret; 37 38 base = INTEGRATOR_AP_EXP_BASE + (num * INTEGRATOR_AP_EXP_STRIDE); 39 40 /* Walk over the child nodes and see what chipselects we use */ 41 for_each_available_child_of_node(np, child) { 42 struct resource res; 43 44 ret = of_address_to_resource(child, 0, &res); 45 if (ret) { 46 dev_info(dev, "no valid address on child\n"); 47 continue; 48 } 49 50 /* First populate the syscon then any devices */ 51 if (res.start == base) { 52 dev_info(dev, "populate module @0x%08x from DT\n", 53 base); 54 ret = of_platform_default_populate(child, NULL, dev); 55 if (ret) { 56 dev_err(dev, "failed to populate module\n"); 57 return ret; 58 } 59 } 60 } 61 62 return 0; 63 } 64 65 static const struct of_device_id integrator_ap_syscon_match[] = { 66 { .compatible = "arm,integrator-ap-syscon"}, 67 { }, 68 }; 69 70 static int integrator_ap_lm_probe(struct platform_device *pdev) 71 { 72 struct device *dev = &pdev->dev; 73 struct device_node *syscon; 74 static struct regmap *map; 75 u32 val; 76 int ret; 77 int i; 78 79 /* Look up the system controller */ 80 syscon = of_find_matching_node(NULL, integrator_ap_syscon_match); 81 if (!syscon) { 82 dev_err(dev, 83 "could not find Integrator/AP system controller\n"); 84 return -ENODEV; 85 } 86 map = syscon_node_to_regmap(syscon); 87 if (IS_ERR(map)) { 88 dev_err(dev, 89 "could not find Integrator/AP system controller\n"); 90 return PTR_ERR(map); 91 } 92 93 ret = regmap_read(map, INTEGRATOR_SC_DEC_OFFSET, &val); 94 if (ret) { 95 dev_err(dev, "could not read from Integrator/AP syscon\n"); 96 return ret; 97 } 98 99 /* Loop over the connected modules */ 100 for (i = 0; i < 4; i++) { 101 if (!(val & BIT(4 + i))) 102 continue; 103 104 dev_info(dev, "detected module in slot %d\n", i); 105 ret = integrator_lm_populate(i, dev); 106 if (ret) 107 return ret; 108 } 109 110 return 0; 111 } 112 113 static const struct of_device_id integrator_ap_lm_match[] = { 114 { .compatible = "arm,integrator-ap-lm"}, 115 { }, 116 }; 117 118 static struct platform_driver integrator_ap_lm_driver = { 119 .probe = integrator_ap_lm_probe, 120 .driver = { 121 .name = "integratorap-lm", 122 .of_match_table = integrator_ap_lm_match, 123 }, 124 }; 125 module_platform_driver(integrator_ap_lm_driver); 126 MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); 127 MODULE_DESCRIPTION("Integrator AP Logical Module driver"); 128 MODULE_LICENSE("GPL v2"); 129