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