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