mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 12:11:40 +00:00
VExpress modularization
This series enables building various Versatile Express platform drivers as modules. The primary target is the Fast Model FVP which is supported in Android. As Android is moving towards their GKI, or generic kernel, the hardware support has to be in modules. Currently ARCH_VEXPRESS enables several built-in only drivers. Some of these are needed, but some are only needed for older 32-bit VExpress platforms and can just be disabled. -----BEGIN PGP SIGNATURE----- iQJEBAABCgAuFiEEktVUI4SxYhzZyEuo+vtdtY28YcMFAl68MeUQHHJvYmhAa2Vy bmVsLm9yZwAKCRD6+121jbxhw/96EACb8MVXgss/RBPfcfKeb46tgdP6XfxlDqma /lWdd88KM3YZI0ym8uBQZX/XwUmuU1bcbxv9E/j0i+i/YER7qrdbsYfeU5CLhAbA vidC1fRuqXNPZRsnc5PnVP913PvRiNgNfGM4BUxz5i7aLfl9IGcujdY/uekEoo2i 9nyAYxMmZBZsHU28y0nXuZaUK7mC7YDZFXM4z6u6Q0nnbS4r5C8b+cUCeTk0w8Ex pA1pTWjRFvnpT1wZZU65FRaxv33dO3MbReT84rbQvrRo/IDKFi+VfAw4/UJFWBoF Ck1cmEchjPcTf7ut/clET+LqCuCVESwmDGmOhJ78m7m8WxsdoaUSfJSsPNMF7dxE +ePIvl/jovqMnCCR+RKbpcIzQvOckk6zp1xnqQNDii46BSCayXQEYtoxRj0B0X3k c4izH58Z7NTUa+IbVf02bwqOl2qMlGSp2KocXNTrBqznRkmCiWB+HHmrX/TQusWL 22sDHuxGRjOhD2yINOMQGeol7fXmIH7M2rjjpoGR1cWGRT/Xj7xU3Eme/VAE0nQv VHFoWW6YDVAfsuwJePgPrHysZcH96mhTCRVo9Gx1xC0IaZpcxFPQkk0LTKtu5CWY jYA1ml1vLDCl7l/yzfQjdtSm6lLg15ihZ+M6jbPdPacdBqmBL5UmPf30sW53XXAG BagmwNHCNQ== =c5eS -----END PGP SIGNATURE----- Merge tag 'vexpress-modules-for-soc-v2' of git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux into arm/soc VExpress modularization This series enables building various Versatile Express platform drivers as modules. The primary target is the Fast Model FVP which is supported in Android. As Android is moving towards their GKI, or generic kernel, the hardware support has to be in modules. Currently ARCH_VEXPRESS enables several built-in only drivers. Some of these are needed, but some are only needed for older 32-bit VExpress platforms and can just be disabled. * tag 'vexpress-modules-for-soc-v2' of git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux: ARM: vexpress: Don't select VEXPRESS_CONFIG bus: vexpress-config: Support building as module vexpress: Move setting master site to vexpress-config bus bus: vexpress-config: simplify config bus probing bus: vexpress-config: Merge vexpress-syscfg into vexpress-config mfd: vexpress-sysreg: Support building as a module mfd: vexpress-sysreg: Use devres API variants mfd: vexpress-sysreg: Drop unused syscon child devices mfd: vexpress-sysreg: Drop selecting CONFIG_CLKSRC_MMIO clk: vexpress-osc: Support building as a module clk: vexpress-osc: Use the devres clock API variants clk: versatile: Only enable SP810 on 32-bit by default clk: versatile: Rework kconfig structure amba: Retry adding deferred devices at late_initcall arm64: vexpress: Don't select CONFIG_POWER_RESET_VEXPRESS ARM: vexpress: Move vexpress_flags_set() into arch code Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
commit
a875e0e5a2
@ -3,7 +3,6 @@ menuconfig ARCH_INTEGRATOR
|
||||
bool "ARM Ltd. Integrator family"
|
||||
depends on ARCH_MULTI_V4T || ARCH_MULTI_V5 || ARCH_MULTI_V6
|
||||
select ARM_AMBA
|
||||
select COMMON_CLK_VERSATILE
|
||||
select CMA
|
||||
select DMA_CMA
|
||||
select HAVE_TCM
|
||||
|
@ -6,7 +6,6 @@ menuconfig ARCH_REALVIEW
|
||||
select ARM_GIC
|
||||
select ARM_TIMER_SP804
|
||||
select CLK_SP810
|
||||
select COMMON_CLK_VERSATILE
|
||||
select GPIO_PL061 if GPIOLIB
|
||||
select HAVE_ARM_SCU if SMP
|
||||
select HAVE_ARM_TWD if SMP
|
||||
|
@ -6,7 +6,6 @@ config ARCH_VERSATILE
|
||||
select ARM_TIMER_SP804
|
||||
select ARM_VIC
|
||||
select CLKSRC_VERSATILE
|
||||
select COMMON_CLK_VERSATILE
|
||||
select CPU_ARM926T
|
||||
select ICST
|
||||
select MFD_SYSCON
|
||||
|
@ -7,7 +7,6 @@ menuconfig ARCH_VEXPRESS
|
||||
select ARM_GIC
|
||||
select ARM_GLOBAL_TIMER
|
||||
select ARM_TIMER_SP804
|
||||
select COMMON_CLK_VERSATILE
|
||||
select GPIOLIB
|
||||
select HAVE_ARM_SCU if SMP
|
||||
select HAVE_ARM_TWD if SMP
|
||||
@ -20,9 +19,6 @@ menuconfig ARCH_VEXPRESS
|
||||
select POWER_SUPPLY
|
||||
select REGULATOR if MMC_ARMMMCI
|
||||
select REGULATOR_FIXED_VOLTAGE if REGULATOR
|
||||
select VEXPRESS_CONFIG
|
||||
select VEXPRESS_SYSCFG
|
||||
select MFD_VEXPRESS_SYSREG
|
||||
help
|
||||
This option enables support for systems using Cortex processor based
|
||||
ARM core and logic (FPGA) tiles on the Versatile Express motherboard,
|
||||
|
@ -1,3 +1,4 @@
|
||||
bool vexpress_smp_init_ops(void);
|
||||
void vexpress_flags_set(u32 data);
|
||||
|
||||
extern const struct smp_operations vexpress_smp_dt_ops;
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <asm/cputype.h>
|
||||
#include <asm/cp15.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
#define RST_HOLD0 0x0
|
||||
#define RST_HOLD1 0x4
|
||||
|
@ -1,8 +1,31 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <asm/mach/arch.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
#define SYS_FLAGSSET 0x030
|
||||
#define SYS_FLAGSCLR 0x034
|
||||
|
||||
void vexpress_flags_set(u32 data)
|
||||
{
|
||||
static void __iomem *base;
|
||||
|
||||
if (!base) {
|
||||
struct device_node *node = of_find_compatible_node(NULL, NULL,
|
||||
"arm,vexpress-sysreg");
|
||||
|
||||
base = of_iomap(node, 0);
|
||||
}
|
||||
|
||||
if (WARN_ON(!base))
|
||||
return;
|
||||
|
||||
writel(~0, base + SYS_FLAGSCLR);
|
||||
writel(data, base + SYS_FLAGSSET);
|
||||
}
|
||||
|
||||
static const char * const v2m_dt_match[] __initconst = {
|
||||
"arm,vexpress",
|
||||
NULL,
|
||||
|
@ -274,12 +274,9 @@ config ARCH_UNIPHIER
|
||||
|
||||
config ARCH_VEXPRESS
|
||||
bool "ARMv8 software model (Versatile Express)"
|
||||
select COMMON_CLK_VERSATILE
|
||||
select GPIOLIB
|
||||
select PM
|
||||
select PM_GENERIC_DOMAINS
|
||||
select POWER_RESET_VEXPRESS
|
||||
select VEXPRESS_CONFIG
|
||||
help
|
||||
This enables support for the ARMv8 software model (Versatile
|
||||
Express).
|
||||
|
@ -505,7 +505,7 @@ static DECLARE_DELAYED_WORK(deferred_retry_work, amba_deferred_retry_func);
|
||||
|
||||
#define DEFERRED_DEVICE_TIMEOUT (msecs_to_jiffies(5 * 1000))
|
||||
|
||||
static void amba_deferred_retry_func(struct work_struct *dummy)
|
||||
static int amba_deferred_retry(void)
|
||||
{
|
||||
struct deferred_device *ddev, *tmp;
|
||||
|
||||
@ -521,11 +521,19 @@ static void amba_deferred_retry_func(struct work_struct *dummy)
|
||||
kfree(ddev);
|
||||
}
|
||||
|
||||
mutex_unlock(&deferred_devices_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
late_initcall(amba_deferred_retry);
|
||||
|
||||
static void amba_deferred_retry_func(struct work_struct *dummy)
|
||||
{
|
||||
amba_deferred_retry();
|
||||
|
||||
if (!list_empty(&deferred_devices))
|
||||
schedule_delayed_work(&deferred_retry_work,
|
||||
DEFERRED_DEVICE_TIMEOUT);
|
||||
|
||||
mutex_unlock(&deferred_devices_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -192,7 +192,7 @@ config UNIPHIER_SYSTEM_BUS
|
||||
needed to use on-board devices connected to UniPhier SoCs.
|
||||
|
||||
config VEXPRESS_CONFIG
|
||||
bool "Versatile Express configuration bus"
|
||||
tristate "Versatile Express configuration bus"
|
||||
default y if ARCH_VEXPRESS
|
||||
depends on ARM || ARM64
|
||||
depends on OF
|
||||
|
@ -6,10 +6,61 @@
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vexpress.h>
|
||||
|
||||
#define SYS_MISC 0x0
|
||||
#define SYS_MISC_MASTERSITE (1 << 14)
|
||||
|
||||
#define SYS_PROCID0 0x24
|
||||
#define SYS_PROCID1 0x28
|
||||
#define SYS_HBI_MASK 0xfff
|
||||
#define SYS_PROCIDx_HBI_SHIFT 0
|
||||
|
||||
#define SYS_CFGDATA 0x40
|
||||
|
||||
#define SYS_CFGCTRL 0x44
|
||||
#define SYS_CFGCTRL_START (1 << 31)
|
||||
#define SYS_CFGCTRL_WRITE (1 << 30)
|
||||
#define SYS_CFGCTRL_DCC(n) (((n) & 0xf) << 26)
|
||||
#define SYS_CFGCTRL_FUNC(n) (((n) & 0x3f) << 20)
|
||||
#define SYS_CFGCTRL_SITE(n) (((n) & 0x3) << 16)
|
||||
#define SYS_CFGCTRL_POSITION(n) (((n) & 0xf) << 12)
|
||||
#define SYS_CFGCTRL_DEVICE(n) (((n) & 0xfff) << 0)
|
||||
|
||||
#define SYS_CFGSTAT 0x48
|
||||
#define SYS_CFGSTAT_ERR (1 << 1)
|
||||
#define SYS_CFGSTAT_COMPLETE (1 << 0)
|
||||
|
||||
#define VEXPRESS_SITE_MB 0
|
||||
#define VEXPRESS_SITE_DB1 1
|
||||
#define VEXPRESS_SITE_DB2 2
|
||||
#define VEXPRESS_SITE_MASTER 0xf
|
||||
|
||||
struct vexpress_syscfg {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct list_head funcs;
|
||||
};
|
||||
|
||||
struct vexpress_syscfg_func {
|
||||
struct list_head list;
|
||||
struct vexpress_syscfg *syscfg;
|
||||
struct regmap *regmap;
|
||||
int num_templates;
|
||||
u32 template[]; /* Keep it last! */
|
||||
};
|
||||
|
||||
struct vexpress_config_bridge_ops {
|
||||
struct regmap * (*regmap_init)(struct device *dev, void *context);
|
||||
void (*regmap_exit)(struct regmap *regmap, void *context);
|
||||
};
|
||||
|
||||
struct vexpress_config_bridge {
|
||||
struct vexpress_config_bridge_ops *ops;
|
||||
@ -18,26 +69,20 @@ struct vexpress_config_bridge {
|
||||
|
||||
|
||||
static DEFINE_MUTEX(vexpress_config_mutex);
|
||||
static struct class *vexpress_config_class;
|
||||
static u32 vexpress_config_site_master = VEXPRESS_SITE_MASTER;
|
||||
|
||||
|
||||
void vexpress_config_set_master(u32 site)
|
||||
static void vexpress_config_set_master(u32 site)
|
||||
{
|
||||
vexpress_config_site_master = site;
|
||||
}
|
||||
|
||||
u32 vexpress_config_get_master(void)
|
||||
{
|
||||
return vexpress_config_site_master;
|
||||
}
|
||||
|
||||
void vexpress_config_lock(void *arg)
|
||||
static void vexpress_config_lock(void *arg)
|
||||
{
|
||||
mutex_lock(&vexpress_config_mutex);
|
||||
}
|
||||
|
||||
void vexpress_config_unlock(void *arg)
|
||||
static void vexpress_config_unlock(void *arg)
|
||||
{
|
||||
mutex_unlock(&vexpress_config_mutex);
|
||||
}
|
||||
@ -59,7 +104,7 @@ static void vexpress_config_find_prop(struct device_node *node,
|
||||
}
|
||||
}
|
||||
|
||||
int vexpress_config_get_topo(struct device_node *node, u32 *site,
|
||||
static int vexpress_config_get_topo(struct device_node *node, u32 *site,
|
||||
u32 *position, u32 *dcc)
|
||||
{
|
||||
vexpress_config_find_prop(node, "arm,vexpress,site", site);
|
||||
@ -88,9 +133,6 @@ struct regmap *devm_regmap_init_vexpress_config(struct device *dev)
|
||||
struct regmap *regmap;
|
||||
struct regmap **res;
|
||||
|
||||
if (WARN_ON(dev->parent->class != vexpress_config_class))
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
bridge = dev_get_drvdata(dev->parent);
|
||||
if (WARN_ON(!bridge))
|
||||
return ERR_PTR(-EINVAL);
|
||||
@ -113,91 +155,265 @@ struct regmap *devm_regmap_init_vexpress_config(struct device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_regmap_init_vexpress_config);
|
||||
|
||||
struct device *vexpress_config_bridge_register(struct device *parent,
|
||||
struct vexpress_config_bridge_ops *ops, void *context)
|
||||
static int vexpress_syscfg_exec(struct vexpress_syscfg_func *func,
|
||||
int index, bool write, u32 *data)
|
||||
{
|
||||
struct device *dev;
|
||||
struct vexpress_config_bridge *bridge;
|
||||
struct vexpress_syscfg *syscfg = func->syscfg;
|
||||
u32 command, status;
|
||||
int tries;
|
||||
long timeout;
|
||||
|
||||
if (!vexpress_config_class) {
|
||||
vexpress_config_class = class_create(THIS_MODULE,
|
||||
"vexpress-config");
|
||||
if (IS_ERR(vexpress_config_class))
|
||||
return (void *)vexpress_config_class;
|
||||
}
|
||||
|
||||
dev = device_create(vexpress_config_class, parent, 0,
|
||||
NULL, "%s.bridge", dev_name(parent));
|
||||
|
||||
if (IS_ERR(dev))
|
||||
return dev;
|
||||
|
||||
bridge = devm_kmalloc(dev, sizeof(*bridge), GFP_KERNEL);
|
||||
if (!bridge) {
|
||||
put_device(dev);
|
||||
device_unregister(dev);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
bridge->ops = ops;
|
||||
bridge->context = context;
|
||||
|
||||
dev_set_drvdata(dev, bridge);
|
||||
|
||||
dev_dbg(parent, "Registered bridge '%s', parent node %p\n",
|
||||
dev_name(dev), parent->of_node);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
|
||||
static int vexpress_config_node_match(struct device *dev, const void *data)
|
||||
{
|
||||
const struct device_node *node = data;
|
||||
|
||||
dev_dbg(dev, "Parent node %p, looking for %p\n",
|
||||
dev->parent->of_node, node);
|
||||
|
||||
return dev->parent->of_node == node;
|
||||
}
|
||||
|
||||
static int vexpress_config_populate(struct device_node *node)
|
||||
{
|
||||
struct device_node *bridge;
|
||||
struct device *parent;
|
||||
int ret;
|
||||
|
||||
bridge = of_parse_phandle(node, "arm,vexpress,config-bridge", 0);
|
||||
if (!bridge)
|
||||
if (WARN_ON(index >= func->num_templates))
|
||||
return -EINVAL;
|
||||
|
||||
parent = class_find_device(vexpress_config_class, NULL, bridge,
|
||||
vexpress_config_node_match);
|
||||
of_node_put(bridge);
|
||||
if (WARN_ON(!parent))
|
||||
return -ENODEV;
|
||||
command = readl(syscfg->base + SYS_CFGCTRL);
|
||||
if (WARN_ON(command & SYS_CFGCTRL_START))
|
||||
return -EBUSY;
|
||||
|
||||
ret = of_platform_populate(node, NULL, NULL, parent);
|
||||
command = func->template[index];
|
||||
command |= SYS_CFGCTRL_START;
|
||||
command |= write ? SYS_CFGCTRL_WRITE : 0;
|
||||
|
||||
put_device(parent);
|
||||
/* Use a canary for reads */
|
||||
if (!write)
|
||||
*data = 0xdeadbeef;
|
||||
|
||||
return ret;
|
||||
dev_dbg(syscfg->dev, "func %p, command %x, data %x\n",
|
||||
func, command, *data);
|
||||
writel(*data, syscfg->base + SYS_CFGDATA);
|
||||
writel(0, syscfg->base + SYS_CFGSTAT);
|
||||
writel(command, syscfg->base + SYS_CFGCTRL);
|
||||
mb();
|
||||
|
||||
/* The operation can take ages... Go to sleep, 100us initially */
|
||||
tries = 100;
|
||||
timeout = 100;
|
||||
do {
|
||||
if (!irqs_disabled()) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout(usecs_to_jiffies(timeout));
|
||||
if (signal_pending(current))
|
||||
return -EINTR;
|
||||
} else {
|
||||
udelay(timeout);
|
||||
}
|
||||
|
||||
status = readl(syscfg->base + SYS_CFGSTAT);
|
||||
if (status & SYS_CFGSTAT_ERR)
|
||||
return -EFAULT;
|
||||
|
||||
if (timeout > 20)
|
||||
timeout -= 20;
|
||||
} while (--tries && !(status & SYS_CFGSTAT_COMPLETE));
|
||||
if (WARN_ON_ONCE(!tries))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
if (!write) {
|
||||
*data = readl(syscfg->base + SYS_CFGDATA);
|
||||
dev_dbg(syscfg->dev, "func %p, read data %x\n", func, *data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init vexpress_config_init(void)
|
||||
static int vexpress_syscfg_read(void *context, unsigned int index,
|
||||
unsigned int *val)
|
||||
{
|
||||
int err = 0;
|
||||
struct device_node *node;
|
||||
struct vexpress_syscfg_func *func = context;
|
||||
|
||||
/* Need the config devices early, before the "normal" devices... */
|
||||
for_each_compatible_node(node, NULL, "arm,vexpress,config-bus") {
|
||||
err = vexpress_config_populate(node);
|
||||
if (err) {
|
||||
of_node_put(node);
|
||||
return vexpress_syscfg_exec(func, index, false, val);
|
||||
}
|
||||
|
||||
static int vexpress_syscfg_write(void *context, unsigned int index,
|
||||
unsigned int val)
|
||||
{
|
||||
struct vexpress_syscfg_func *func = context;
|
||||
|
||||
return vexpress_syscfg_exec(func, index, true, &val);
|
||||
}
|
||||
|
||||
static struct regmap_config vexpress_syscfg_regmap_config = {
|
||||
.lock = vexpress_config_lock,
|
||||
.unlock = vexpress_config_unlock,
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_read = vexpress_syscfg_read,
|
||||
.reg_write = vexpress_syscfg_write,
|
||||
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
.val_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
};
|
||||
|
||||
|
||||
static struct regmap *vexpress_syscfg_regmap_init(struct device *dev,
|
||||
void *context)
|
||||
{
|
||||
int err;
|
||||
struct vexpress_syscfg *syscfg = context;
|
||||
struct vexpress_syscfg_func *func;
|
||||
struct property *prop;
|
||||
const __be32 *val = NULL;
|
||||
__be32 energy_quirk[4];
|
||||
int num;
|
||||
u32 site, position, dcc;
|
||||
int i;
|
||||
|
||||
err = vexpress_config_get_topo(dev->of_node, &site,
|
||||
&position, &dcc);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
prop = of_find_property(dev->of_node,
|
||||
"arm,vexpress-sysreg,func", NULL);
|
||||
if (!prop)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
num = prop->length / sizeof(u32) / 2;
|
||||
val = prop->value;
|
||||
|
||||
/*
|
||||
* "arm,vexpress-energy" function used to be described
|
||||
* by its first device only, now it requires both
|
||||
*/
|
||||
if (num == 1 && of_device_is_compatible(dev->of_node,
|
||||
"arm,vexpress-energy")) {
|
||||
num = 2;
|
||||
energy_quirk[0] = *val;
|
||||
energy_quirk[2] = *val++;
|
||||
energy_quirk[1] = *val;
|
||||
energy_quirk[3] = cpu_to_be32(be32_to_cpup(val) + 1);
|
||||
val = energy_quirk;
|
||||
}
|
||||
|
||||
func = kzalloc(struct_size(func, template, num), GFP_KERNEL);
|
||||
if (!func)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
func->syscfg = syscfg;
|
||||
func->num_templates = num;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
u32 function, device;
|
||||
|
||||
function = be32_to_cpup(val++);
|
||||
device = be32_to_cpup(val++);
|
||||
|
||||
dev_dbg(dev, "func %p: %u/%u/%u/%u/%u\n",
|
||||
func, site, position, dcc,
|
||||
function, device);
|
||||
|
||||
func->template[i] = SYS_CFGCTRL_DCC(dcc);
|
||||
func->template[i] |= SYS_CFGCTRL_SITE(site);
|
||||
func->template[i] |= SYS_CFGCTRL_POSITION(position);
|
||||
func->template[i] |= SYS_CFGCTRL_FUNC(function);
|
||||
func->template[i] |= SYS_CFGCTRL_DEVICE(device);
|
||||
}
|
||||
|
||||
vexpress_syscfg_regmap_config.max_register = num - 1;
|
||||
|
||||
func->regmap = regmap_init(dev, NULL, func,
|
||||
&vexpress_syscfg_regmap_config);
|
||||
|
||||
if (IS_ERR(func->regmap)) {
|
||||
void *err = func->regmap;
|
||||
|
||||
kfree(func);
|
||||
return err;
|
||||
}
|
||||
|
||||
list_add(&func->list, &syscfg->funcs);
|
||||
|
||||
return func->regmap;
|
||||
}
|
||||
|
||||
static void vexpress_syscfg_regmap_exit(struct regmap *regmap, void *context)
|
||||
{
|
||||
struct vexpress_syscfg *syscfg = context;
|
||||
struct vexpress_syscfg_func *func, *tmp;
|
||||
|
||||
regmap_exit(regmap);
|
||||
|
||||
list_for_each_entry_safe(func, tmp, &syscfg->funcs, list) {
|
||||
if (func->regmap == regmap) {
|
||||
list_del(&syscfg->funcs);
|
||||
kfree(func);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
postcore_initcall(vexpress_config_init);
|
||||
|
||||
static struct vexpress_config_bridge_ops vexpress_syscfg_bridge_ops = {
|
||||
.regmap_init = vexpress_syscfg_regmap_init,
|
||||
.regmap_exit = vexpress_syscfg_regmap_exit,
|
||||
};
|
||||
|
||||
|
||||
static int vexpress_syscfg_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct vexpress_syscfg *syscfg;
|
||||
struct resource *res;
|
||||
struct vexpress_config_bridge *bridge;
|
||||
struct device_node *node;
|
||||
int master;
|
||||
u32 dt_hbi;
|
||||
|
||||
syscfg = devm_kzalloc(&pdev->dev, sizeof(*syscfg), GFP_KERNEL);
|
||||
if (!syscfg)
|
||||
return -ENOMEM;
|
||||
syscfg->dev = &pdev->dev;
|
||||
INIT_LIST_HEAD(&syscfg->funcs);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
syscfg->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(syscfg->base))
|
||||
return PTR_ERR(syscfg->base);
|
||||
|
||||
bridge = devm_kmalloc(&pdev->dev, sizeof(*bridge), GFP_KERNEL);
|
||||
if (!bridge)
|
||||
return -ENOMEM;
|
||||
|
||||
bridge->ops = &vexpress_syscfg_bridge_ops;
|
||||
bridge->context = syscfg;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, bridge);
|
||||
|
||||
master = readl(syscfg->base + SYS_MISC) & SYS_MISC_MASTERSITE ?
|
||||
VEXPRESS_SITE_DB2 : VEXPRESS_SITE_DB1;
|
||||
vexpress_config_set_master(master);
|
||||
|
||||
/* Confirm board type against DT property, if available */
|
||||
if (of_property_read_u32(of_root, "arm,hbi", &dt_hbi) == 0) {
|
||||
u32 id = readl(syscfg->base + (master == VEXPRESS_SITE_DB1 ?
|
||||
SYS_PROCID0 : SYS_PROCID1));
|
||||
u32 hbi = (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK;
|
||||
|
||||
if (WARN_ON(dt_hbi != hbi))
|
||||
dev_warn(&pdev->dev, "DT HBI (%x) is not matching hardware (%x)!\n",
|
||||
dt_hbi, hbi);
|
||||
}
|
||||
|
||||
for_each_compatible_node(node, NULL, "arm,vexpress,config-bus") {
|
||||
struct device_node *bridge_np;
|
||||
|
||||
bridge_np = of_parse_phandle(node, "arm,vexpress,config-bridge", 0);
|
||||
if (bridge_np != pdev->dev.parent->of_node)
|
||||
continue;
|
||||
|
||||
of_platform_populate(node, NULL, NULL, &pdev->dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id vexpress_syscfg_id_table[] = {
|
||||
{ "vexpress-syscfg", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, vexpress_syscfg_id_table);
|
||||
|
||||
static struct platform_driver vexpress_syscfg_driver = {
|
||||
.driver.name = "vexpress-syscfg",
|
||||
.id_table = vexpress_syscfg_id_table,
|
||||
.probe = vexpress_syscfg_probe,
|
||||
};
|
||||
module_platform_driver(vexpress_syscfg_driver);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -114,7 +114,7 @@ obj-$(CONFIG_ARCH_TEGRA) += tegra/
|
||||
obj-y += ti/
|
||||
obj-$(CONFIG_CLK_UNIPHIER) += uniphier/
|
||||
obj-$(CONFIG_ARCH_U8500) += ux500/
|
||||
obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/
|
||||
obj-y += versatile/
|
||||
ifeq ($(CONFIG_COMMON_CLK), y)
|
||||
obj-$(CONFIG_X86) += x86/
|
||||
endif
|
||||
|
@ -1,33 +1,35 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config ICST
|
||||
bool
|
||||
|
||||
config COMMON_CLK_VERSATILE
|
||||
bool "Clock driver for ARM Reference designs"
|
||||
depends on ARCH_INTEGRATOR || ARCH_REALVIEW || \
|
||||
ARCH_VERSATILE || ARCH_VEXPRESS || ARM64 || \
|
||||
COMPILE_TEST
|
||||
menuconfig COMMON_CLK_VERSATILE
|
||||
bool "Clock driver for ARM Reference designs" if COMPILE_TEST
|
||||
default y if ARCH_INTEGRATOR || ARCH_REALVIEW || \
|
||||
ARCH_VERSATILE || ARCH_VEXPRESS
|
||||
|
||||
if COMMON_CLK_VERSATILE
|
||||
|
||||
config ICST
|
||||
bool "Clock driver for ARM Reference designs ICST"
|
||||
select REGMAP_MMIO
|
||||
---help---
|
||||
Supports clocking on ARM Reference designs:
|
||||
- Integrator/AP and Integrator/CP
|
||||
- RealView PB1176, EB, PB11MP and PBX
|
||||
- Versatile Express
|
||||
|
||||
config CLK_SP810
|
||||
bool "Clock driver for ARM SP810 System Controller"
|
||||
depends on COMMON_CLK_VERSATILE
|
||||
default y if ARCH_VEXPRESS
|
||||
default y if (ARCH_VEXPRESS && ARM)
|
||||
---help---
|
||||
Supports clock muxing (REFCLK/TIMCLK to TIMERCLKEN0-3) capabilities
|
||||
of the ARM SP810 System Controller cell.
|
||||
|
||||
config CLK_VEXPRESS_OSC
|
||||
bool "Clock driver for Versatile Express OSC clock generators"
|
||||
depends on COMMON_CLK_VERSATILE
|
||||
tristate "Clock driver for Versatile Express OSC clock generators"
|
||||
depends on VEXPRESS_CONFIG
|
||||
select REGMAP_MMIO
|
||||
default y if ARCH_VEXPRESS
|
||||
---help---
|
||||
Simple regmap-based driver driving clock generators on Versatile
|
||||
Express platforms hidden behind its configuration infrastructure,
|
||||
commonly known as OSCs.
|
||||
|
||||
endif
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
@ -65,8 +66,8 @@ static int vexpress_osc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct clk_init_data init;
|
||||
struct vexpress_osc *osc;
|
||||
struct clk *clk;
|
||||
u32 range[2];
|
||||
int ret;
|
||||
|
||||
osc = devm_kzalloc(&pdev->dev, sizeof(*osc), GFP_KERNEL);
|
||||
if (!osc)
|
||||
@ -92,11 +93,11 @@ static int vexpress_osc_probe(struct platform_device *pdev)
|
||||
|
||||
osc->hw.init = &init;
|
||||
|
||||
clk = clk_register(NULL, &osc->hw);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
ret = devm_clk_hw_register(&pdev->dev, &osc->hw);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, clk);
|
||||
devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_simple_get, &osc->hw);
|
||||
clk_hw_set_rate_range(&osc->hw, osc->rate_min, osc->rate_max);
|
||||
|
||||
dev_dbg(&pdev->dev, "Registered clock '%s'\n", init.name);
|
||||
@ -108,6 +109,7 @@ static const struct of_device_id vexpress_osc_of_match[] = {
|
||||
{ .compatible = "arm,vexpress-osc", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, vexpress_osc_of_match);
|
||||
|
||||
static struct platform_driver vexpress_osc_driver = {
|
||||
.driver = {
|
||||
@ -116,9 +118,5 @@ static struct platform_driver vexpress_osc_driver = {
|
||||
},
|
||||
.probe = vexpress_osc_probe,
|
||||
};
|
||||
|
||||
static int __init vexpress_osc_init(void)
|
||||
{
|
||||
return platform_driver_register(&vexpress_osc_driver);
|
||||
}
|
||||
core_initcall(vexpress_osc_init);
|
||||
module_platform_driver(vexpress_osc_driver);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -2028,10 +2028,9 @@ config MCP_UCB1200_TS
|
||||
endmenu
|
||||
|
||||
config MFD_VEXPRESS_SYSREG
|
||||
bool "Versatile Express System Registers"
|
||||
depends on VEXPRESS_CONFIG && GPIOLIB && !ARCH_USES_GETTIMEOFFSET
|
||||
tristate "Versatile Express System Registers"
|
||||
depends on VEXPRESS_CONFIG && GPIOLIB
|
||||
default y
|
||||
select CLKSRC_MMIO
|
||||
select GPIO_GENERIC_PLATFORM
|
||||
select MFD_CORE
|
||||
select MFD_SYSCON
|
||||
|
@ -8,13 +8,12 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_data/syscon.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/vexpress.h>
|
||||
|
||||
#define SYS_ID 0x000
|
||||
#define SYS_SW 0x004
|
||||
@ -37,35 +36,8 @@
|
||||
#define SYS_CFGCTRL 0x0a4
|
||||
#define SYS_CFGSTAT 0x0a8
|
||||
|
||||
#define SYS_HBI_MASK 0xfff
|
||||
#define SYS_PROCIDx_HBI_SHIFT 0
|
||||
|
||||
#define SYS_MISC_MASTERSITE (1 << 14)
|
||||
|
||||
void vexpress_flags_set(u32 data)
|
||||
{
|
||||
static void __iomem *base;
|
||||
|
||||
if (!base) {
|
||||
struct device_node *node = of_find_compatible_node(NULL, NULL,
|
||||
"arm,vexpress-sysreg");
|
||||
|
||||
base = of_iomap(node, 0);
|
||||
}
|
||||
|
||||
if (WARN_ON(!base))
|
||||
return;
|
||||
|
||||
writel(~0, base + SYS_FLAGSCLR);
|
||||
writel(data, base + SYS_FLAGSSET);
|
||||
}
|
||||
|
||||
/* The sysreg block is just a random collection of various functions... */
|
||||
|
||||
static struct syscon_platform_data vexpress_sysreg_sys_id_pdata = {
|
||||
.label = "sys_id",
|
||||
};
|
||||
|
||||
static struct bgpio_pdata vexpress_sysreg_sys_led_pdata = {
|
||||
.label = "sys_led",
|
||||
.base = -1,
|
||||
@ -84,24 +56,8 @@ static struct bgpio_pdata vexpress_sysreg_sys_flash_pdata = {
|
||||
.ngpio = 1,
|
||||
};
|
||||
|
||||
static struct syscon_platform_data vexpress_sysreg_sys_misc_pdata = {
|
||||
.label = "sys_misc",
|
||||
};
|
||||
|
||||
static struct syscon_platform_data vexpress_sysreg_sys_procid_pdata = {
|
||||
.label = "sys_procid",
|
||||
};
|
||||
|
||||
static struct mfd_cell vexpress_sysreg_cells[] = {
|
||||
{
|
||||
.name = "syscon",
|
||||
.num_resources = 1,
|
||||
.resources = (struct resource []) {
|
||||
DEFINE_RES_MEM(SYS_ID, 0x4),
|
||||
},
|
||||
.platform_data = &vexpress_sysreg_sys_id_pdata,
|
||||
.pdata_size = sizeof(vexpress_sysreg_sys_id_pdata),
|
||||
}, {
|
||||
.name = "basic-mmio-gpio",
|
||||
.of_compatible = "arm,vexpress-sysreg,sys_led",
|
||||
.num_resources = 1,
|
||||
@ -128,27 +84,11 @@ static struct mfd_cell vexpress_sysreg_cells[] = {
|
||||
},
|
||||
.platform_data = &vexpress_sysreg_sys_flash_pdata,
|
||||
.pdata_size = sizeof(vexpress_sysreg_sys_flash_pdata),
|
||||
}, {
|
||||
.name = "syscon",
|
||||
.num_resources = 1,
|
||||
.resources = (struct resource []) {
|
||||
DEFINE_RES_MEM(SYS_MISC, 0x4),
|
||||
},
|
||||
.platform_data = &vexpress_sysreg_sys_misc_pdata,
|
||||
.pdata_size = sizeof(vexpress_sysreg_sys_misc_pdata),
|
||||
}, {
|
||||
.name = "syscon",
|
||||
.num_resources = 1,
|
||||
.resources = (struct resource []) {
|
||||
DEFINE_RES_MEM(SYS_PROCID0, 0x8),
|
||||
},
|
||||
.platform_data = &vexpress_sysreg_sys_procid_pdata,
|
||||
.pdata_size = sizeof(vexpress_sysreg_sys_procid_pdata),
|
||||
}, {
|
||||
.name = "vexpress-syscfg",
|
||||
.num_resources = 1,
|
||||
.resources = (struct resource []) {
|
||||
DEFINE_RES_MEM(SYS_CFGDATA, 0xc),
|
||||
DEFINE_RES_MEM(SYS_MISC, 0x4c),
|
||||
},
|
||||
}
|
||||
};
|
||||
@ -158,8 +98,6 @@ static int vexpress_sysreg_probe(struct platform_device *pdev)
|
||||
struct resource *mem;
|
||||
void __iomem *base;
|
||||
struct gpio_chip *mmc_gpio_chip;
|
||||
int master;
|
||||
u32 dt_hbi;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!mem)
|
||||
@ -169,21 +107,6 @@ static int vexpress_sysreg_probe(struct platform_device *pdev)
|
||||
if (!base)
|
||||
return -ENOMEM;
|
||||
|
||||
master = readl(base + SYS_MISC) & SYS_MISC_MASTERSITE ?
|
||||
VEXPRESS_SITE_DB2 : VEXPRESS_SITE_DB1;
|
||||
vexpress_config_set_master(master);
|
||||
|
||||
/* Confirm board type against DT property, if available */
|
||||
if (of_property_read_u32(of_root, "arm,hbi", &dt_hbi) == 0) {
|
||||
u32 id = readl(base + (master == VEXPRESS_SITE_DB1 ?
|
||||
SYS_PROCID0 : SYS_PROCID1));
|
||||
u32 hbi = (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK;
|
||||
|
||||
if (WARN_ON(dt_hbi != hbi))
|
||||
dev_warn(&pdev->dev, "DT HBI (%x) is not matching hardware (%x)!\n",
|
||||
dt_hbi, hbi);
|
||||
}
|
||||
|
||||
/*
|
||||
* Duplicated SYS_MCI pseudo-GPIO controller for compatibility with
|
||||
* older trees using sysreg node for MMC control lines.
|
||||
@ -195,9 +118,9 @@ static int vexpress_sysreg_probe(struct platform_device *pdev)
|
||||
bgpio_init(mmc_gpio_chip, &pdev->dev, 0x4, base + SYS_MCI,
|
||||
NULL, NULL, NULL, NULL, 0);
|
||||
mmc_gpio_chip->ngpio = 2;
|
||||
gpiochip_add_data(mmc_gpio_chip, NULL);
|
||||
devm_gpiochip_add_data(&pdev->dev, mmc_gpio_chip, NULL);
|
||||
|
||||
return mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO,
|
||||
return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO,
|
||||
vexpress_sysreg_cells,
|
||||
ARRAY_SIZE(vexpress_sysreg_cells), mem, 0, NULL);
|
||||
}
|
||||
@ -206,6 +129,7 @@ static const struct of_device_id vexpress_sysreg_match[] = {
|
||||
{ .compatible = "arm,vexpress-sysreg", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, vexpress_sysreg_match);
|
||||
|
||||
static struct platform_driver vexpress_sysreg_driver = {
|
||||
.driver = {
|
||||
@ -215,14 +139,5 @@ static struct platform_driver vexpress_sysreg_driver = {
|
||||
.probe = vexpress_sysreg_probe,
|
||||
};
|
||||
|
||||
static int __init vexpress_sysreg_init(void)
|
||||
{
|
||||
struct device_node *node;
|
||||
|
||||
/* Need the sysreg early, before any other device... */
|
||||
for_each_matching_node(node, vexpress_sysreg_match)
|
||||
of_platform_device_create(node, NULL, NULL);
|
||||
|
||||
return platform_driver_register(&vexpress_sysreg_driver);
|
||||
}
|
||||
core_initcall(vexpress_sysreg_init);
|
||||
module_platform_driver(vexpress_sysreg_driver);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -423,15 +423,6 @@ config SRAM
|
||||
config SRAM_EXEC
|
||||
bool
|
||||
|
||||
config VEXPRESS_SYSCFG
|
||||
bool "Versatile Express System Configuration driver"
|
||||
depends on VEXPRESS_CONFIG
|
||||
default y
|
||||
help
|
||||
ARM Ltd. Versatile Express uses specialised platform configuration
|
||||
bus. System Configuration interface is one of the possible means
|
||||
of generating transactions on this bus.
|
||||
|
||||
config PCI_ENDPOINT_TEST
|
||||
depends on PCI
|
||||
select CRC32
|
||||
|
@ -49,7 +49,6 @@ obj-$(CONFIG_SRAM_EXEC) += sram-exec.o
|
||||
obj-y += mic/
|
||||
obj-$(CONFIG_GENWQE) += genwqe/
|
||||
obj-$(CONFIG_ECHO) += echo/
|
||||
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
|
||||
obj-$(CONFIG_CXL_BASE) += cxl/
|
||||
obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o
|
||||
obj-$(CONFIG_OCXL) += ocxl/
|
||||
|
@ -1,280 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
*
|
||||
* Copyright (C) 2014 ARM Limited
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
#include <linux/vexpress.h>
|
||||
|
||||
|
||||
#define SYS_CFGDATA 0x0
|
||||
|
||||
#define SYS_CFGCTRL 0x4
|
||||
#define SYS_CFGCTRL_START (1 << 31)
|
||||
#define SYS_CFGCTRL_WRITE (1 << 30)
|
||||
#define SYS_CFGCTRL_DCC(n) (((n) & 0xf) << 26)
|
||||
#define SYS_CFGCTRL_FUNC(n) (((n) & 0x3f) << 20)
|
||||
#define SYS_CFGCTRL_SITE(n) (((n) & 0x3) << 16)
|
||||
#define SYS_CFGCTRL_POSITION(n) (((n) & 0xf) << 12)
|
||||
#define SYS_CFGCTRL_DEVICE(n) (((n) & 0xfff) << 0)
|
||||
|
||||
#define SYS_CFGSTAT 0x8
|
||||
#define SYS_CFGSTAT_ERR (1 << 1)
|
||||
#define SYS_CFGSTAT_COMPLETE (1 << 0)
|
||||
|
||||
|
||||
struct vexpress_syscfg {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct list_head funcs;
|
||||
};
|
||||
|
||||
struct vexpress_syscfg_func {
|
||||
struct list_head list;
|
||||
struct vexpress_syscfg *syscfg;
|
||||
struct regmap *regmap;
|
||||
int num_templates;
|
||||
u32 template[]; /* Keep it last! */
|
||||
};
|
||||
|
||||
|
||||
static int vexpress_syscfg_exec(struct vexpress_syscfg_func *func,
|
||||
int index, bool write, u32 *data)
|
||||
{
|
||||
struct vexpress_syscfg *syscfg = func->syscfg;
|
||||
u32 command, status;
|
||||
int tries;
|
||||
long timeout;
|
||||
|
||||
if (WARN_ON(index >= func->num_templates))
|
||||
return -EINVAL;
|
||||
|
||||
command = readl(syscfg->base + SYS_CFGCTRL);
|
||||
if (WARN_ON(command & SYS_CFGCTRL_START))
|
||||
return -EBUSY;
|
||||
|
||||
command = func->template[index];
|
||||
command |= SYS_CFGCTRL_START;
|
||||
command |= write ? SYS_CFGCTRL_WRITE : 0;
|
||||
|
||||
/* Use a canary for reads */
|
||||
if (!write)
|
||||
*data = 0xdeadbeef;
|
||||
|
||||
dev_dbg(syscfg->dev, "func %p, command %x, data %x\n",
|
||||
func, command, *data);
|
||||
writel(*data, syscfg->base + SYS_CFGDATA);
|
||||
writel(0, syscfg->base + SYS_CFGSTAT);
|
||||
writel(command, syscfg->base + SYS_CFGCTRL);
|
||||
mb();
|
||||
|
||||
/* The operation can take ages... Go to sleep, 100us initially */
|
||||
tries = 100;
|
||||
timeout = 100;
|
||||
do {
|
||||
if (!irqs_disabled()) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout(usecs_to_jiffies(timeout));
|
||||
if (signal_pending(current))
|
||||
return -EINTR;
|
||||
} else {
|
||||
udelay(timeout);
|
||||
}
|
||||
|
||||
status = readl(syscfg->base + SYS_CFGSTAT);
|
||||
if (status & SYS_CFGSTAT_ERR)
|
||||
return -EFAULT;
|
||||
|
||||
if (timeout > 20)
|
||||
timeout -= 20;
|
||||
} while (--tries && !(status & SYS_CFGSTAT_COMPLETE));
|
||||
if (WARN_ON_ONCE(!tries))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
if (!write) {
|
||||
*data = readl(syscfg->base + SYS_CFGDATA);
|
||||
dev_dbg(syscfg->dev, "func %p, read data %x\n", func, *data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vexpress_syscfg_read(void *context, unsigned int index,
|
||||
unsigned int *val)
|
||||
{
|
||||
struct vexpress_syscfg_func *func = context;
|
||||
|
||||
return vexpress_syscfg_exec(func, index, false, val);
|
||||
}
|
||||
|
||||
static int vexpress_syscfg_write(void *context, unsigned int index,
|
||||
unsigned int val)
|
||||
{
|
||||
struct vexpress_syscfg_func *func = context;
|
||||
|
||||
return vexpress_syscfg_exec(func, index, true, &val);
|
||||
}
|
||||
|
||||
static struct regmap_config vexpress_syscfg_regmap_config = {
|
||||
.lock = vexpress_config_lock,
|
||||
.unlock = vexpress_config_unlock,
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_read = vexpress_syscfg_read,
|
||||
.reg_write = vexpress_syscfg_write,
|
||||
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
.val_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
};
|
||||
|
||||
|
||||
static struct regmap *vexpress_syscfg_regmap_init(struct device *dev,
|
||||
void *context)
|
||||
{
|
||||
int err;
|
||||
struct vexpress_syscfg *syscfg = context;
|
||||
struct vexpress_syscfg_func *func;
|
||||
struct property *prop;
|
||||
const __be32 *val = NULL;
|
||||
__be32 energy_quirk[4];
|
||||
int num;
|
||||
u32 site, position, dcc;
|
||||
int i;
|
||||
|
||||
err = vexpress_config_get_topo(dev->of_node, &site,
|
||||
&position, &dcc);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
prop = of_find_property(dev->of_node,
|
||||
"arm,vexpress-sysreg,func", NULL);
|
||||
if (!prop)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
num = prop->length / sizeof(u32) / 2;
|
||||
val = prop->value;
|
||||
|
||||
/*
|
||||
* "arm,vexpress-energy" function used to be described
|
||||
* by its first device only, now it requires both
|
||||
*/
|
||||
if (num == 1 && of_device_is_compatible(dev->of_node,
|
||||
"arm,vexpress-energy")) {
|
||||
num = 2;
|
||||
energy_quirk[0] = *val;
|
||||
energy_quirk[2] = *val++;
|
||||
energy_quirk[1] = *val;
|
||||
energy_quirk[3] = cpu_to_be32(be32_to_cpup(val) + 1);
|
||||
val = energy_quirk;
|
||||
}
|
||||
|
||||
func = kzalloc(struct_size(func, template, num), GFP_KERNEL);
|
||||
if (!func)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
func->syscfg = syscfg;
|
||||
func->num_templates = num;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
u32 function, device;
|
||||
|
||||
function = be32_to_cpup(val++);
|
||||
device = be32_to_cpup(val++);
|
||||
|
||||
dev_dbg(dev, "func %p: %u/%u/%u/%u/%u\n",
|
||||
func, site, position, dcc,
|
||||
function, device);
|
||||
|
||||
func->template[i] = SYS_CFGCTRL_DCC(dcc);
|
||||
func->template[i] |= SYS_CFGCTRL_SITE(site);
|
||||
func->template[i] |= SYS_CFGCTRL_POSITION(position);
|
||||
func->template[i] |= SYS_CFGCTRL_FUNC(function);
|
||||
func->template[i] |= SYS_CFGCTRL_DEVICE(device);
|
||||
}
|
||||
|
||||
vexpress_syscfg_regmap_config.max_register = num - 1;
|
||||
|
||||
func->regmap = regmap_init(dev, NULL, func,
|
||||
&vexpress_syscfg_regmap_config);
|
||||
|
||||
if (IS_ERR(func->regmap)) {
|
||||
void *err = func->regmap;
|
||||
|
||||
kfree(func);
|
||||
return err;
|
||||
}
|
||||
|
||||
list_add(&func->list, &syscfg->funcs);
|
||||
|
||||
return func->regmap;
|
||||
}
|
||||
|
||||
static void vexpress_syscfg_regmap_exit(struct regmap *regmap, void *context)
|
||||
{
|
||||
struct vexpress_syscfg *syscfg = context;
|
||||
struct vexpress_syscfg_func *func, *tmp;
|
||||
|
||||
regmap_exit(regmap);
|
||||
|
||||
list_for_each_entry_safe(func, tmp, &syscfg->funcs, list) {
|
||||
if (func->regmap == regmap) {
|
||||
list_del(&syscfg->funcs);
|
||||
kfree(func);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct vexpress_config_bridge_ops vexpress_syscfg_bridge_ops = {
|
||||
.regmap_init = vexpress_syscfg_regmap_init,
|
||||
.regmap_exit = vexpress_syscfg_regmap_exit,
|
||||
};
|
||||
|
||||
|
||||
static int vexpress_syscfg_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct vexpress_syscfg *syscfg;
|
||||
struct resource *res;
|
||||
struct device *bridge;
|
||||
|
||||
syscfg = devm_kzalloc(&pdev->dev, sizeof(*syscfg), GFP_KERNEL);
|
||||
if (!syscfg)
|
||||
return -ENOMEM;
|
||||
syscfg->dev = &pdev->dev;
|
||||
INIT_LIST_HEAD(&syscfg->funcs);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
syscfg->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(syscfg->base))
|
||||
return PTR_ERR(syscfg->base);
|
||||
|
||||
/* Must use dev.parent (MFD), as that's where DT phandle points at... */
|
||||
bridge = vexpress_config_bridge_register(pdev->dev.parent,
|
||||
&vexpress_syscfg_bridge_ops, syscfg);
|
||||
|
||||
return PTR_ERR_OR_ZERO(bridge);
|
||||
}
|
||||
|
||||
static const struct platform_device_id vexpress_syscfg_id_table[] = {
|
||||
{ "vexpress-syscfg", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver vexpress_syscfg_driver = {
|
||||
.driver.name = "vexpress-syscfg",
|
||||
.id_table = vexpress_syscfg_id_table,
|
||||
.probe = vexpress_syscfg_probe,
|
||||
};
|
||||
|
||||
static int __init vexpress_syscfg_init(void)
|
||||
{
|
||||
return platform_driver_register(&vexpress_syscfg_driver);
|
||||
}
|
||||
core_initcall(vexpress_syscfg_init);
|
@ -10,38 +10,8 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define VEXPRESS_SITE_MB 0
|
||||
#define VEXPRESS_SITE_DB1 1
|
||||
#define VEXPRESS_SITE_DB2 2
|
||||
#define VEXPRESS_SITE_MASTER 0xf
|
||||
|
||||
/* Config infrastructure */
|
||||
|
||||
void vexpress_config_set_master(u32 site);
|
||||
u32 vexpress_config_get_master(void);
|
||||
|
||||
void vexpress_config_lock(void *arg);
|
||||
void vexpress_config_unlock(void *arg);
|
||||
|
||||
int vexpress_config_get_topo(struct device_node *node, u32 *site,
|
||||
u32 *position, u32 *dcc);
|
||||
|
||||
/* Config bridge API */
|
||||
|
||||
struct vexpress_config_bridge_ops {
|
||||
struct regmap * (*regmap_init)(struct device *dev, void *context);
|
||||
void (*regmap_exit)(struct regmap *regmap, void *context);
|
||||
};
|
||||
|
||||
struct device *vexpress_config_bridge_register(struct device *parent,
|
||||
struct vexpress_config_bridge_ops *ops, void *context);
|
||||
|
||||
/* Config regmap API */
|
||||
|
||||
struct regmap *devm_regmap_init_vexpress_config(struct device *dev);
|
||||
|
||||
/* Platform control */
|
||||
|
||||
void vexpress_flags_set(u32 data);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user