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 of_node_put(child); 58 return ret; 59 } 60 } 61 } 62 63 return 0; 64 } 65 66 static const struct of_device_id integrator_ap_syscon_match[] = { 67 { .compatible = "arm,integrator-ap-syscon"}, 68 { }, 69 }; 70 71 static int integrator_ap_lm_probe(struct platform_device *pdev) 72 { 73 struct device *dev = &pdev->dev; 74 struct device_node *syscon; 75 static struct regmap *map; 76 u32 val; 77 int ret; 78 int i; 79 80 /* Look up the system controller */ 81 syscon = of_find_matching_node(NULL, integrator_ap_syscon_match); 82 if (!syscon) { 83 dev_err(dev, 84 "could not find Integrator/AP system controller\n"); 85 return -ENODEV; 86 } 87 map = syscon_node_to_regmap(syscon); 88 of_node_put(syscon); 89 if (IS_ERR(map)) { 90 dev_err(dev, 91 "could not find Integrator/AP system controller\n"); 92 return PTR_ERR(map); 93 } 94 95 ret = regmap_read(map, INTEGRATOR_SC_DEC_OFFSET, &val); 96 if (ret) { 97 dev_err(dev, "could not read from Integrator/AP syscon\n"); 98 return ret; 99 } 100 101 /* Loop over the connected modules */ 102 for (i = 0; i < 4; i++) { 103 if (!(val & BIT(4 + i))) 104 continue; 105 106 dev_info(dev, "detected module in slot %d\n", i); 107 ret = integrator_lm_populate(i, dev); 108 if (ret) 109 return ret; 110 } 111 112 return 0; 113 } 114 115 static const struct of_device_id integrator_ap_lm_match[] = { 116 { .compatible = "arm,integrator-ap-lm"}, 117 { }, 118 }; 119 120 static struct platform_driver integrator_ap_lm_driver = { 121 .probe = integrator_ap_lm_probe, 122 .driver = { 123 .name = "integratorap-lm", 124 .of_match_table = integrator_ap_lm_match, 125 }, 126 }; 127 module_platform_driver(integrator_ap_lm_driver); 128 MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); 129 MODULE_DESCRIPTION("Integrator AP Logical Module driver"); 130