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