Commit ccea5e8a authored by Linus Walleij's avatar Linus Walleij
Browse files

bus: Add driver for Integrator/AP logic modules



The logic modules on the Integrator/AP (Application Platform)
are logic tiles with (typically) one or a few peripheral
devices. They are most commonly used for FPGA prototyping.

Using the device tree node for logic tiles, we probe them
in order and check if the special system controller register
confirm their presence before populating the node for a tile.

This supercedes the code in arch/arm/mach-integrator/lm.[c|h]
and makes it possible to populate the tiles using the device
tree instead of boardfile-based descriptions.

Tested with all peripherals including graphics and MMC card
working fine with the IM-PD1 example tile from Arm.

Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent 03d679bf
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1336,6 +1336,7 @@ F: arch/arm/mach-integrator/
F:	arch/arm/mach-realview/
F:	arch/arm/mach-versatile/
F:	arch/arm/plat-versatile/
F:	drivers/bus/arm-integrator-lm.c
F:	drivers/clk/versatile/
F:	drivers/i2c/busses/i2c-versatile.c
F:	drivers/irqchip/irq-versatile-fpga.c
+9 −0
Original line number Diff line number Diff line
@@ -20,6 +20,15 @@ config ARM_CCI400_PORT_CTRL
	  Low level power management driver for CCI400 cache coherent
	  interconnect for ARM platforms.

config ARM_INTEGRATOR_LM
	bool "ARM Integrator Logic Module bus"
	depends on HAS_IOMEM
	depends on ARCH_INTEGRATOR || COMPILE_TEST
	default ARCH_INTEGRATOR
	help
	  Say y here to enable support for the ARM Logic Module bus
	  found on the ARM Integrator AP (Application Platform)

config BRCMSTB_GISB_ARB
	bool "Broadcom STB GISB bus arbiter"
	depends on ARM || ARM64 || MIPS
+1 −1
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@

# Interconnect bus drivers for ARM platforms
obj-$(CONFIG_ARM_CCI)		+= arm-cci.o

obj-$(CONFIG_ARM_INTEGRATOR_LM)	+= arm-integrator-lm.o
obj-$(CONFIG_HISILICON_LPC)	+= hisi_lpc.o
obj-$(CONFIG_BRCMSTB_GISB_ARB)	+= brcmstb_gisb.o
obj-$(CONFIG_MOXTET)		+= moxtet.o
+128 −0
Original line number Diff line number Diff line
// 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 (IS_ERR(syscon)) {
		dev_err(dev,
			"could not find Integrator/AP system controller\n");
		return PTR_ERR(syscon);
	}
	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");