1*b7846af2SChristian Marangi // SPDX-License-Identifier: GPL-2.0 2*b7846af2SChristian Marangi /* 3*b7846af2SChristian Marangi * Author: Christian Marangi <ansuelsmth@gmail.com> 4*b7846af2SChristian Marangi */ 5*b7846af2SChristian Marangi 6*b7846af2SChristian Marangi #include <linux/arm-smccc.h> 7*b7846af2SChristian Marangi #include <linux/mod_devicetable.h> 8*b7846af2SChristian Marangi #include <linux/module.h> 9*b7846af2SChristian Marangi #include <linux/nvmem-provider.h> 10*b7846af2SChristian Marangi #include <linux/platform_device.h> 11*b7846af2SChristian Marangi #include <linux/of.h> 12*b7846af2SChristian Marangi #include <linux/regmap.h> 13*b7846af2SChristian Marangi 14*b7846af2SChristian Marangi #define AIROHA_SMC_EFUSE_FID 0x82000001 15*b7846af2SChristian Marangi #define AIROHA_SMC_EFUSE_SUB_ID_READ 0x44414552 16*b7846af2SChristian Marangi 17*b7846af2SChristian Marangi #define AIROHA_EFUSE_CELLS 64 18*b7846af2SChristian Marangi 19*b7846af2SChristian Marangi struct airoha_efuse_bank_priv { 20*b7846af2SChristian Marangi u32 bank_index; 21*b7846af2SChristian Marangi }; 22*b7846af2SChristian Marangi 23*b7846af2SChristian Marangi static int airoha_efuse_read(void *context, unsigned int offset, 24*b7846af2SChristian Marangi void *val, size_t bytes) 25*b7846af2SChristian Marangi { 26*b7846af2SChristian Marangi struct regmap *regmap = context; 27*b7846af2SChristian Marangi 28*b7846af2SChristian Marangi return regmap_bulk_read(regmap, offset, 29*b7846af2SChristian Marangi val, bytes / sizeof(u32)); 30*b7846af2SChristian Marangi } 31*b7846af2SChristian Marangi 32*b7846af2SChristian Marangi static int airoha_efuse_reg_read(void *context, unsigned int offset, 33*b7846af2SChristian Marangi unsigned int *val) 34*b7846af2SChristian Marangi { 35*b7846af2SChristian Marangi struct airoha_efuse_bank_priv *priv = context; 36*b7846af2SChristian Marangi struct arm_smccc_res res; 37*b7846af2SChristian Marangi 38*b7846af2SChristian Marangi arm_smccc_1_1_invoke(AIROHA_SMC_EFUSE_FID, 39*b7846af2SChristian Marangi AIROHA_SMC_EFUSE_SUB_ID_READ, 40*b7846af2SChristian Marangi priv->bank_index, offset, 0, 0, 0, 0, &res); 41*b7846af2SChristian Marangi 42*b7846af2SChristian Marangi /* check if SMC reported an error */ 43*b7846af2SChristian Marangi if (res.a0) 44*b7846af2SChristian Marangi return -EIO; 45*b7846af2SChristian Marangi 46*b7846af2SChristian Marangi *val = res.a1; 47*b7846af2SChristian Marangi return 0; 48*b7846af2SChristian Marangi } 49*b7846af2SChristian Marangi 50*b7846af2SChristian Marangi static int airoha_efuse_probe(struct platform_device *pdev) 51*b7846af2SChristian Marangi { 52*b7846af2SChristian Marangi struct device *dev = &pdev->dev; 53*b7846af2SChristian Marangi int ret; 54*b7846af2SChristian Marangi 55*b7846af2SChristian Marangi for_each_child_of_node_scoped(dev->of_node, child) { 56*b7846af2SChristian Marangi struct nvmem_config nvmem_config = { 57*b7846af2SChristian Marangi .size = AIROHA_EFUSE_CELLS * sizeof(u32), 58*b7846af2SChristian Marangi .stride = sizeof(u32), 59*b7846af2SChristian Marangi .word_size = sizeof(u32), 60*b7846af2SChristian Marangi .reg_read = airoha_efuse_read, 61*b7846af2SChristian Marangi }; 62*b7846af2SChristian Marangi struct regmap_config regmap_config = { 63*b7846af2SChristian Marangi .reg_read = airoha_efuse_reg_read, 64*b7846af2SChristian Marangi .reg_bits = 32, 65*b7846af2SChristian Marangi .val_bits = 32, 66*b7846af2SChristian Marangi .reg_stride = 4, 67*b7846af2SChristian Marangi }; 68*b7846af2SChristian Marangi struct airoha_efuse_bank_priv *priv; 69*b7846af2SChristian Marangi struct nvmem_device *nvmem; 70*b7846af2SChristian Marangi struct regmap *regmap; 71*b7846af2SChristian Marangi const char *name; 72*b7846af2SChristian Marangi u32 bank; 73*b7846af2SChristian Marangi 74*b7846af2SChristian Marangi ret = of_property_read_u32(child, "reg", &bank); 75*b7846af2SChristian Marangi if (ret) 76*b7846af2SChristian Marangi return ret; 77*b7846af2SChristian Marangi 78*b7846af2SChristian Marangi priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 79*b7846af2SChristian Marangi if (!priv) 80*b7846af2SChristian Marangi return -ENOMEM; 81*b7846af2SChristian Marangi 82*b7846af2SChristian Marangi name = devm_kasprintf(dev, GFP_KERNEL, "airoha-efuse-%u", 83*b7846af2SChristian Marangi bank); 84*b7846af2SChristian Marangi if (!name) 85*b7846af2SChristian Marangi return -ENOMEM; 86*b7846af2SChristian Marangi 87*b7846af2SChristian Marangi priv->bank_index = bank; 88*b7846af2SChristian Marangi 89*b7846af2SChristian Marangi regmap_config.name = name; 90*b7846af2SChristian Marangi regmap = devm_regmap_init(dev, NULL, priv, 91*b7846af2SChristian Marangi ®map_config); 92*b7846af2SChristian Marangi if (IS_ERR(regmap)) 93*b7846af2SChristian Marangi return PTR_ERR(regmap); 94*b7846af2SChristian Marangi 95*b7846af2SChristian Marangi nvmem_config.name = name; 96*b7846af2SChristian Marangi nvmem_config.priv = regmap; 97*b7846af2SChristian Marangi nvmem_config.dev = dev; 98*b7846af2SChristian Marangi nvmem_config.id = bank; 99*b7846af2SChristian Marangi nvmem_config.of_node = child; 100*b7846af2SChristian Marangi nvmem = devm_nvmem_register(dev, &nvmem_config); 101*b7846af2SChristian Marangi if (IS_ERR(nvmem)) 102*b7846af2SChristian Marangi return PTR_ERR(nvmem); 103*b7846af2SChristian Marangi } 104*b7846af2SChristian Marangi 105*b7846af2SChristian Marangi return 0; 106*b7846af2SChristian Marangi } 107*b7846af2SChristian Marangi 108*b7846af2SChristian Marangi static const struct of_device_id airoha_efuse_of_match[] = { 109*b7846af2SChristian Marangi { .compatible = "airoha,an7581-efuses", }, 110*b7846af2SChristian Marangi { /* sentinel */ } 111*b7846af2SChristian Marangi }; 112*b7846af2SChristian Marangi MODULE_DEVICE_TABLE(of, airoha_efuse_of_match); 113*b7846af2SChristian Marangi 114*b7846af2SChristian Marangi static struct platform_driver airoha_efuse_driver = { 115*b7846af2SChristian Marangi .probe = airoha_efuse_probe, 116*b7846af2SChristian Marangi .driver = { 117*b7846af2SChristian Marangi .name = "airoha-efuse", 118*b7846af2SChristian Marangi .of_match_table = airoha_efuse_of_match, 119*b7846af2SChristian Marangi }, 120*b7846af2SChristian Marangi }; 121*b7846af2SChristian Marangi module_platform_driver(airoha_efuse_driver); 122*b7846af2SChristian Marangi 123*b7846af2SChristian Marangi MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>"); 124*b7846af2SChristian Marangi MODULE_DESCRIPTION("Driver for Airoha SMC eFUSEs"); 125*b7846af2SChristian Marangi MODULE_LICENSE("GPL"); 126