diff options
Diffstat (limited to 'drivers/nvmem/airoha-smc-efuses.c')
| -rw-r--r-- | drivers/nvmem/airoha-smc-efuses.c | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/drivers/nvmem/airoha-smc-efuses.c b/drivers/nvmem/airoha-smc-efuses.c new file mode 100644 index 000000000..e56a99f4a --- /dev/null +++ b/drivers/nvmem/airoha-smc-efuses.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Author: Christian Marangi <ansuelsmth@gmail.com> + */ + +#include <linux/arm-smccc.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/nvmem-provider.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/regmap.h> + +#define AIROHA_SMC_EFUSE_FID 0x82000001 +#define AIROHA_SMC_EFUSE_SUB_ID_READ 0x44414552 + +#define AIROHA_EFUSE_CELLS 64 + +struct airoha_efuse_bank_priv { + u32 bank_index; +}; + +static int airoha_efuse_read(void *context, unsigned int offset, + void *val, size_t bytes) +{ + struct regmap *regmap = context; + + return regmap_bulk_read(regmap, offset, + val, bytes / sizeof(u32)); +} + +static int airoha_efuse_reg_read(void *context, unsigned int offset, + unsigned int *val) +{ + struct airoha_efuse_bank_priv *priv = context; + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(AIROHA_SMC_EFUSE_FID, + AIROHA_SMC_EFUSE_SUB_ID_READ, + priv->bank_index, offset, 0, 0, 0, 0, &res); + + /* check if SMC reported an error */ + if (res.a0) + return -EIO; + + *val = res.a1; + return 0; +} + +static int airoha_efuse_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int ret; + + for_each_child_of_node_scoped(dev->of_node, child) { + struct nvmem_config nvmem_config = { + .size = AIROHA_EFUSE_CELLS * sizeof(u32), + .stride = sizeof(u32), + .word_size = sizeof(u32), + .reg_read = airoha_efuse_read, + }; + struct regmap_config regmap_config = { + .reg_read = airoha_efuse_reg_read, + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + }; + struct airoha_efuse_bank_priv *priv; + struct nvmem_device *nvmem; + struct regmap *regmap; + const char *name; + u32 bank; + + ret = of_property_read_u32(child, "reg", &bank); + if (ret) + return ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + name = devm_kasprintf(dev, GFP_KERNEL, "airoha-efuse-%u", + bank); + if (!name) + return -ENOMEM; + + priv->bank_index = bank; + + regmap_config.name = name; + regmap = devm_regmap_init(dev, NULL, priv, + ®map_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + nvmem_config.name = name; + nvmem_config.priv = regmap; + nvmem_config.dev = dev; + nvmem_config.id = bank; + nvmem_config.of_node = child; + nvmem = devm_nvmem_register(dev, &nvmem_config); + if (IS_ERR(nvmem)) + return PTR_ERR(nvmem); + } + + return 0; +} + +static const struct of_device_id airoha_efuse_of_match[] = { + { .compatible = "airoha,an7581-efuses", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, airoha_efuse_of_match); + +static struct platform_driver airoha_efuse_driver = { + .probe = airoha_efuse_probe, + .driver = { + .name = "airoha-efuse", + .of_match_table = airoha_efuse_of_match, + }, +}; +module_platform_driver(airoha_efuse_driver); + +MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>"); +MODULE_DESCRIPTION("Driver for Airoha SMC eFUSEs"); +MODULE_LICENSE("GPL"); |
