Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/mfd/ab3100-otp.c
15111 views
1
/*
2
* drivers/mfd/ab3100_otp.c
3
*
4
* Copyright (C) 2007-2009 ST-Ericsson AB
5
* License terms: GNU General Public License (GPL) version 2
6
* Driver to read out OTP from the AB3100 Mixed-signal circuit
7
* Author: Linus Walleij <[email protected]>
8
*/
9
10
#include <linux/module.h>
11
#include <linux/kernel.h>
12
#include <linux/slab.h>
13
#include <linux/init.h>
14
#include <linux/platform_device.h>
15
#include <linux/mfd/abx500.h>
16
#include <linux/debugfs.h>
17
#include <linux/seq_file.h>
18
19
/* The OTP registers */
20
#define AB3100_OTP0 0xb0
21
#define AB3100_OTP1 0xb1
22
#define AB3100_OTP2 0xb2
23
#define AB3100_OTP3 0xb3
24
#define AB3100_OTP4 0xb4
25
#define AB3100_OTP5 0xb5
26
#define AB3100_OTP6 0xb6
27
#define AB3100_OTP7 0xb7
28
#define AB3100_OTPP 0xbf
29
30
/**
31
* struct ab3100_otp
32
* @dev containing device
33
* @locked whether the OTP is locked, after locking, no more bits
34
* can be changed but before locking it is still possible
35
* to change bits from 1->0.
36
* @freq clocking frequency for the OTP, this frequency is either
37
* 32768Hz or 1MHz/30
38
* @paf product activation flag, indicates whether this is a real
39
* product (paf true) or a lab board etc (paf false)
40
* @imeich if this is set it is possible to override the
41
* IMEI number found in the tac, fac and svn fields with
42
* (secured) software
43
* @cid customer ID
44
* @tac type allocation code of the IMEI
45
* @fac final assembly code of the IMEI
46
* @svn software version number of the IMEI
47
* @debugfs a debugfs file used when dumping to file
48
*/
49
struct ab3100_otp {
50
struct device *dev;
51
bool locked;
52
u32 freq;
53
bool paf;
54
bool imeich;
55
u16 cid:14;
56
u32 tac:20;
57
u8 fac;
58
u32 svn:20;
59
struct dentry *debugfs;
60
};
61
62
static int __init ab3100_otp_read(struct ab3100_otp *otp)
63
{
64
u8 otpval[8];
65
u8 otpp;
66
int err;
67
68
err = abx500_get_register_interruptible(otp->dev, 0,
69
AB3100_OTPP, &otpp);
70
if (err) {
71
dev_err(otp->dev, "unable to read OTPP register\n");
72
return err;
73
}
74
75
err = abx500_get_register_page_interruptible(otp->dev, 0,
76
AB3100_OTP0, otpval, 8);
77
if (err) {
78
dev_err(otp->dev, "unable to read OTP register page\n");
79
return err;
80
}
81
82
/* Cache OTP properties, they never change by nature */
83
otp->locked = (otpp & 0x80);
84
otp->freq = (otpp & 0x40) ? 32768 : 34100;
85
otp->paf = (otpval[1] & 0x80);
86
otp->imeich = (otpval[1] & 0x40);
87
otp->cid = ((otpval[1] << 8) | otpval[0]) & 0x3fff;
88
otp->tac = ((otpval[4] & 0x0f) << 16) | (otpval[3] << 8) | otpval[2];
89
otp->fac = ((otpval[5] & 0x0f) << 4) | (otpval[4] >> 4);
90
otp->svn = (otpval[7] << 12) | (otpval[6] << 4) | (otpval[5] >> 4);
91
return 0;
92
}
93
94
/*
95
* This is a simple debugfs human-readable file that dumps out
96
* the contents of the OTP.
97
*/
98
#ifdef CONFIG_DEBUG_FS
99
static int ab3100_show_otp(struct seq_file *s, void *v)
100
{
101
struct ab3100_otp *otp = s->private;
102
103
seq_printf(s, "OTP is %s\n", otp->locked ? "LOCKED" : "UNLOCKED");
104
seq_printf(s, "OTP clock switch startup is %uHz\n", otp->freq);
105
seq_printf(s, "PAF is %s\n", otp->paf ? "SET" : "NOT SET");
106
seq_printf(s, "IMEI is %s\n", otp->imeich ?
107
"CHANGEABLE" : "NOT CHANGEABLE");
108
seq_printf(s, "CID: 0x%04x (decimal: %d)\n", otp->cid, otp->cid);
109
seq_printf(s, "IMEI: %u-%u-%u\n", otp->tac, otp->fac, otp->svn);
110
return 0;
111
}
112
113
static int ab3100_otp_open(struct inode *inode, struct file *file)
114
{
115
return single_open(file, ab3100_show_otp, inode->i_private);
116
}
117
118
static const struct file_operations ab3100_otp_operations = {
119
.open = ab3100_otp_open,
120
.read = seq_read,
121
.llseek = seq_lseek,
122
.release = single_release,
123
};
124
125
static int __init ab3100_otp_init_debugfs(struct device *dev,
126
struct ab3100_otp *otp)
127
{
128
otp->debugfs = debugfs_create_file("ab3100_otp", S_IFREG | S_IRUGO,
129
NULL, otp,
130
&ab3100_otp_operations);
131
if (!otp->debugfs) {
132
dev_err(dev, "AB3100 debugfs OTP file registration failed!\n");
133
return -ENOENT;
134
}
135
return 0;
136
}
137
138
static void __exit ab3100_otp_exit_debugfs(struct ab3100_otp *otp)
139
{
140
debugfs_remove(otp->debugfs);
141
}
142
#else
143
/* Compile this out if debugfs not selected */
144
static inline int __init ab3100_otp_init_debugfs(struct device *dev,
145
struct ab3100_otp *otp)
146
{
147
return 0;
148
}
149
150
static inline void __exit ab3100_otp_exit_debugfs(struct ab3100_otp *otp)
151
{
152
}
153
#endif
154
155
#define SHOW_AB3100_ATTR(name) \
156
static ssize_t ab3100_otp_##name##_show(struct device *dev, \
157
struct device_attribute *attr, \
158
char *buf) \
159
{\
160
struct ab3100_otp *otp = dev_get_drvdata(dev); \
161
return sprintf(buf, "%u\n", otp->name); \
162
}
163
164
SHOW_AB3100_ATTR(locked)
165
SHOW_AB3100_ATTR(freq)
166
SHOW_AB3100_ATTR(paf)
167
SHOW_AB3100_ATTR(imeich)
168
SHOW_AB3100_ATTR(cid)
169
SHOW_AB3100_ATTR(fac)
170
SHOW_AB3100_ATTR(tac)
171
SHOW_AB3100_ATTR(svn)
172
173
static struct device_attribute ab3100_otp_attrs[] = {
174
__ATTR(locked, S_IRUGO, ab3100_otp_locked_show, NULL),
175
__ATTR(freq, S_IRUGO, ab3100_otp_freq_show, NULL),
176
__ATTR(paf, S_IRUGO, ab3100_otp_paf_show, NULL),
177
__ATTR(imeich, S_IRUGO, ab3100_otp_imeich_show, NULL),
178
__ATTR(cid, S_IRUGO, ab3100_otp_cid_show, NULL),
179
__ATTR(fac, S_IRUGO, ab3100_otp_fac_show, NULL),
180
__ATTR(tac, S_IRUGO, ab3100_otp_tac_show, NULL),
181
__ATTR(svn, S_IRUGO, ab3100_otp_svn_show, NULL),
182
};
183
184
static int __init ab3100_otp_probe(struct platform_device *pdev)
185
{
186
struct ab3100_otp *otp;
187
int err = 0;
188
int i;
189
190
otp = kzalloc(sizeof(struct ab3100_otp), GFP_KERNEL);
191
if (!otp) {
192
dev_err(&pdev->dev, "could not allocate AB3100 OTP device\n");
193
return -ENOMEM;
194
}
195
otp->dev = &pdev->dev;
196
197
/* Replace platform data coming in with a local struct */
198
platform_set_drvdata(pdev, otp);
199
200
err = ab3100_otp_read(otp);
201
if (err)
202
goto err_otp_read;
203
204
dev_info(&pdev->dev, "AB3100 OTP readout registered\n");
205
206
/* sysfs entries */
207
for (i = 0; i < ARRAY_SIZE(ab3100_otp_attrs); i++) {
208
err = device_create_file(&pdev->dev,
209
&ab3100_otp_attrs[i]);
210
if (err)
211
goto err_create_file;
212
}
213
214
/* debugfs entries */
215
err = ab3100_otp_init_debugfs(&pdev->dev, otp);
216
if (err)
217
goto err_init_debugfs;
218
219
return 0;
220
221
err_init_debugfs:
222
err_create_file:
223
while (--i >= 0)
224
device_remove_file(&pdev->dev, &ab3100_otp_attrs[i]);
225
err_otp_read:
226
kfree(otp);
227
return err;
228
}
229
230
static int __exit ab3100_otp_remove(struct platform_device *pdev)
231
{
232
struct ab3100_otp *otp = platform_get_drvdata(pdev);
233
int i;
234
235
for (i = 0; i < ARRAY_SIZE(ab3100_otp_attrs); i++)
236
device_remove_file(&pdev->dev,
237
&ab3100_otp_attrs[i]);
238
ab3100_otp_exit_debugfs(otp);
239
kfree(otp);
240
return 0;
241
}
242
243
static struct platform_driver ab3100_otp_driver = {
244
.driver = {
245
.name = "ab3100-otp",
246
.owner = THIS_MODULE,
247
},
248
.remove = __exit_p(ab3100_otp_remove),
249
};
250
251
static int __init ab3100_otp_init(void)
252
{
253
return platform_driver_probe(&ab3100_otp_driver,
254
ab3100_otp_probe);
255
}
256
257
static void __exit ab3100_otp_exit(void)
258
{
259
platform_driver_unregister(&ab3100_otp_driver);
260
}
261
262
module_init(ab3100_otp_init);
263
module_exit(ab3100_otp_exit);
264
265
MODULE_AUTHOR("Linus Walleij <[email protected]>");
266
MODULE_DESCRIPTION("AB3100 OTP Readout Driver");
267
MODULE_LICENSE("GPL");
268
269