Skip to content

Commit

Permalink
regmap: add ARM SMCCC support
Browse files Browse the repository at this point in the history
Accessing secure controllers can be done by using SMC calls. This regmap
allows to forward register read request to the secure monitor.

Signed-off-by: Clément Léger <clement.leger@bootlin.com>
  • Loading branch information
clementleger committed Jul 6, 2021
1 parent 2293329 commit 614ec77
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 1 deletion.
7 changes: 6 additions & 1 deletion drivers/base/regmap/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# subsystems should select the appropriate symbols.

config REGMAP
default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ || REGMAP_SOUNDWIRE || REGMAP_SOUNDWIRE_MBQ || REGMAP_SCCB || REGMAP_I3C || REGMAP_SPI_AVMM)
default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ || REGMAP_SOUNDWIRE || REGMAP_SOUNDWIRE_MBQ || REGMAP_SCCB || REGMAP_I3C || REGMAP_SPI_AVMM || REGMAP_SMCCC)
select IRQ_DOMAIN if REGMAP_IRQ
bool

Expand Down Expand Up @@ -61,3 +61,8 @@ config REGMAP_I3C
config REGMAP_SPI_AVMM
tristate
depends on SPI

config REGMAP_SMCCC
default y if HAVE_ARM_SMCCC_DISCOVERY
tristate
depends on HAVE_ARM_SMCCC_DISCOVERY
1 change: 1 addition & 0 deletions drivers/base/regmap/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ obj-$(CONFIG_REGMAP_SOUNDWIRE_MBQ) += regmap-sdw-mbq.o
obj-$(CONFIG_REGMAP_SCCB) += regmap-sccb.o
obj-$(CONFIG_REGMAP_I3C) += regmap-i3c.o
obj-$(CONFIG_REGMAP_SPI_AVMM) += regmap-spi-avmm.o
obj-$(CONFIG_REGMAP_SMCCC) += regmap-smccc.o
133 changes: 133 additions & 0 deletions drivers/base/regmap/regmap-smccc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.

#include <linux/arm-smccc.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/types.h>

#define REGMAP_SMC_READ 0
#define REGMAP_SMC_WRITE 1

struct regmap_smccc_ctx {
u32 regmap_smc_id;
u8 val_bytes;
};

static int regmap_smccc_reg_write(void *context, unsigned int reg,
unsigned int val)
{
struct regmap_smccc_ctx *ctx = context;
struct arm_smccc_res res;

arm_smccc_1_1_invoke(ctx->regmap_smc_id, REGMAP_SMC_WRITE, reg, val,
&res);

if (res.a0)
return -EACCES;

return 0;
}

static int regmap_smccc_reg_read(void *context, unsigned int reg,
unsigned int *val)
{
struct regmap_smccc_ctx *ctx = context;
struct arm_smccc_res res;

arm_smccc_1_1_invoke(ctx->regmap_smc_id, REGMAP_SMC_READ, reg, &res);

if (res.a0)
return -EACCES;

*val = res.a1;

return 0;
}

static struct regmap_bus regmap_smccc = {
.reg_write = regmap_smccc_reg_write,
.reg_read = regmap_smccc_reg_read,
};

static int regmap_smccc_bits_is_supported(int val_bits)
{
switch (val_bits) {
case 8:
case 16:
case 32:
return 0;
case 64:
/*
* SMCs are using registers to pass informations so if architecture is
* not using 64 bits registers, we won't be able to pass information
* transparently.
*/
#if !defined(CONFIG_64BIT)
return -EINVAL;
#else
return 0;
#endif
default:
return -EINVAL;
}
}

static struct regmap_smccc_ctx *smccc_regmap_init_ctx(
const struct regmap_config *config,
u32 regmap_smc_id)
{
int ret;
struct regmap_smccc_ctx *ctx;

ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return ERR_PTR(-ENOMEM);

ret = regmap_smccc_bits_is_supported(config->val_bits);
if (ret)
return ERR_PTR(ret);

ctx->regmap_smc_id = regmap_smc_id;
ctx->val_bytes = config->val_bits / 8;

return ctx;
}

struct regmap *__regmap_init_smccc(struct device *dev, u32 regmap_smc_id,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
struct regmap_smccc_ctx *ctx;

ctx = smccc_regmap_init_ctx(config, regmap_smc_id);
if (IS_ERR(ctx))
return ERR_CAST(ctx);

return __regmap_init(dev, &regmap_smccc, ctx, config, lock_key,
lock_name);

}
EXPORT_SYMBOL_GPL(__regmap_init_smccc);

struct regmap *__devm_regmap_init_smccc(struct device *dev, u32 regmap_smc_id,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
struct regmap_smccc_ctx *ctx;

ctx = smccc_regmap_init_ctx(config, regmap_smc_id);
if (IS_ERR(ctx))
return ERR_CAST(ctx);

return __devm_regmap_init(dev, &regmap_smccc, ctx, config, lock_key,
lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_smccc);

MODULE_AUTHOR("Clément Léger <clement.leger@bootlin.com>");
MODULE_DESCRIPTION("Regmap SMCCC Module");
MODULE_LICENSE("GPL v2");
38 changes: 38 additions & 0 deletions include/linux/regmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,11 @@ struct regmap *__regmap_init_spi_avmm(struct spi_device *spi,
struct lock_class_key *lock_key,
const char *lock_name);

struct regmap *__regmap_init_smccc(struct device *dev, u32 regmap_smc_id,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name);

struct regmap *__devm_regmap_init(struct device *dev,
const struct regmap_bus *bus,
void *bus_context,
Expand Down Expand Up @@ -644,6 +649,10 @@ struct regmap *__devm_regmap_init_spi_avmm(struct spi_device *spi,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name);
struct regmap *__devm_regmap_init_smccc(struct device *dev, u32 regmap_smc_id,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name);
/*
* Wrapper for regmap_init macros to include a unique lockdep key and name
* for each call. No-op if CONFIG_LOCKDEP is not set.
Expand Down Expand Up @@ -857,6 +866,20 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
__regmap_lockdep_wrapper(__regmap_init_spi_avmm, #config, \
spi, config)

/**
* regmap_init_smccc() - Initialize register map for ARM SMCCC
*
* @dev: Device that will be interacted with
* @smc_id: SMC id to used for calls
* @config: Configuration for register map
*
* The return value will be an ERR_PTR() on error or a valid pointer
* to a struct regmap.
*/
#define regmap_init_smccc(dev, smc_id, config) \
__regmap_lockdep_wrapper(__regmap_init_smccc, #config, \
dev, smc_id, config)

/**
* devm_regmap_init() - Initialise managed register map
*
Expand Down Expand Up @@ -1072,6 +1095,21 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
__regmap_lockdep_wrapper(__devm_regmap_init_spi_avmm, #config, \
spi, config)

/**
* devm_regmap_init_smccc() - Initialize register map for ARM SMCCC
*
* @dev: Device that will be interacted with
* @smc_id: SMC id to used for calls
* @config: Configuration for register map
*
* The return value will be an ERR_PTR() on error or a valid pointer
* to a struct regmap. The map will be automatically freed by the
* device management code.
*/
#define devm_regmap_init_smccc(dev, smc_id, config) \
__regmap_lockdep_wrapper(__devm_regmap_init_smccc, #config, \
dev, smc_id, config)

int regmap_mmio_attach_clk(struct regmap *map, struct clk *clk);
void regmap_mmio_detach_clk(struct regmap *map);
void regmap_exit(struct regmap *map);
Expand Down

0 comments on commit 614ec77

Please sign in to comment.