| // SPDX-License-Identifier: GPL-2.0-only | 
| /* | 
|  * ARM Integrator Logical Module bus driver | 
|  * Copyright (C) 2020 Linaro Ltd. | 
|  * Author: Linus Walleij <linus.walleij@linaro.org> | 
|  * | 
|  * See the device tree bindings for this block for more details on the | 
|  * hardware. | 
|  */ | 
|   | 
| #include <linux/module.h> | 
| #include <linux/err.h> | 
| #include <linux/io.h> | 
| #include <linux/of.h> | 
| #include <linux/of_address.h> | 
| #include <linux/of_platform.h> | 
| #include <linux/init.h> | 
| #include <linux/slab.h> | 
| #include <linux/platform_device.h> | 
| #include <linux/bitops.h> | 
| #include <linux/mfd/syscon.h> | 
| #include <linux/regmap.h> | 
|   | 
| /* All information about the connected logic modules are in here */ | 
| #define INTEGRATOR_SC_DEC_OFFSET    0x10 | 
|   | 
| /* Base address for the expansion modules */ | 
| #define INTEGRATOR_AP_EXP_BASE        0xc0000000 | 
| #define INTEGRATOR_AP_EXP_STRIDE    0x10000000 | 
|   | 
| static int integrator_lm_populate(int num, struct device *dev) | 
| { | 
|     struct device_node *np = dev->of_node; | 
|     struct device_node *child; | 
|     u32 base; | 
|     int ret; | 
|   | 
|     base = INTEGRATOR_AP_EXP_BASE + (num * INTEGRATOR_AP_EXP_STRIDE); | 
|   | 
|     /* Walk over the child nodes and see what chipselects we use */ | 
|     for_each_available_child_of_node(np, child) { | 
|         struct resource res; | 
|   | 
|         ret = of_address_to_resource(child, 0, &res); | 
|         if (ret) { | 
|             dev_info(dev, "no valid address on child\n"); | 
|             continue; | 
|         } | 
|   | 
|         /* First populate the syscon then any devices */ | 
|         if (res.start == base) { | 
|             dev_info(dev, "populate module @0x%08x from DT\n", | 
|                  base); | 
|             ret = of_platform_default_populate(child, NULL, dev); | 
|             if (ret) { | 
|                 dev_err(dev, "failed to populate module\n"); | 
|                 return ret; | 
|             } | 
|         } | 
|     } | 
|   | 
|     return 0; | 
| } | 
|   | 
| static const struct of_device_id integrator_ap_syscon_match[] = { | 
|     { .compatible = "arm,integrator-ap-syscon"}, | 
|     { }, | 
| }; | 
|   | 
| static int integrator_ap_lm_probe(struct platform_device *pdev) | 
| { | 
|     struct device *dev = &pdev->dev; | 
|     struct device_node *syscon; | 
|     static struct regmap *map; | 
|     u32 val; | 
|     int ret; | 
|     int i; | 
|   | 
|     /* Look up the system controller */ | 
|     syscon = of_find_matching_node(NULL, integrator_ap_syscon_match); | 
|     if (!syscon) { | 
|         dev_err(dev, | 
|             "could not find Integrator/AP system controller\n"); | 
|         return -ENODEV; | 
|     } | 
|     map = syscon_node_to_regmap(syscon); | 
|     if (IS_ERR(map)) { | 
|         dev_err(dev, | 
|             "could not find Integrator/AP system controller\n"); | 
|         return PTR_ERR(map); | 
|     } | 
|   | 
|     ret = regmap_read(map, INTEGRATOR_SC_DEC_OFFSET, &val); | 
|     if (ret) { | 
|         dev_err(dev, "could not read from Integrator/AP syscon\n"); | 
|         return ret; | 
|     } | 
|   | 
|     /* Loop over the connected modules */ | 
|     for (i = 0; i < 4; i++) { | 
|         if (!(val & BIT(4 + i))) | 
|             continue; | 
|   | 
|         dev_info(dev, "detected module in slot %d\n", i); | 
|         ret = integrator_lm_populate(i, dev); | 
|         if (ret) | 
|             return ret; | 
|     } | 
|   | 
|     return 0; | 
| } | 
|   | 
| static const struct of_device_id integrator_ap_lm_match[] = { | 
|     { .compatible = "arm,integrator-ap-lm"}, | 
|     { }, | 
| }; | 
|   | 
| static struct platform_driver integrator_ap_lm_driver = { | 
|     .probe = integrator_ap_lm_probe, | 
|     .driver = { | 
|         .name = "integratorap-lm", | 
|         .of_match_table = integrator_ap_lm_match, | 
|     }, | 
| }; | 
| module_platform_driver(integrator_ap_lm_driver); | 
| MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); | 
| MODULE_DESCRIPTION("Integrator AP Logical Module driver"); | 
| MODULE_LICENSE("GPL v2"); |