1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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");
|