2 * drivers/mfd/ab3100_otp.c
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 <linus.walleij@stericsson.com>
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>
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
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
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
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
59 struct dentry
*debugfs
;
62 static int __init
ab3100_otp_read(struct ab3100_otp
*otp
)
68 err
= abx500_get_register_interruptible(otp
->dev
, 0,
71 dev_err(otp
->dev
, "unable to read OTPP register\n");
75 err
= abx500_get_register_page_interruptible(otp
->dev
, 0,
76 AB3100_OTP0
, otpval
, 8);
78 dev_err(otp
->dev
, "unable to read OTP register page\n");
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);
95 * This is a simple debugfs human-readable file that dumps out
96 * the contents of the OTP.
98 #ifdef CONFIG_DEBUG_FS
99 static int ab3100_show_otp(struct seq_file
*s
, void *v
)
101 struct ab3100_otp
*otp
= s
->private;
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
);
113 static int ab3100_otp_open(struct inode
*inode
, struct file
*file
)
115 return single_open(file
, ab3100_show_otp
, inode
->i_private
);
118 static const struct file_operations ab3100_otp_operations
= {
119 .open
= ab3100_otp_open
,
122 .release
= single_release
,
125 static int __init
ab3100_otp_init_debugfs(struct device
*dev
,
126 struct ab3100_otp
*otp
)
128 otp
->debugfs
= debugfs_create_file("ab3100_otp", S_IFREG
| S_IRUGO
,
130 &ab3100_otp_operations
);
132 dev_err(dev
, "AB3100 debugfs OTP file registration failed!\n");
138 static void __exit
ab3100_otp_exit_debugfs(struct ab3100_otp
*otp
)
140 debugfs_remove(otp
->debugfs
);
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
)
150 static inline void __exit
ab3100_otp_exit_debugfs(struct ab3100_otp
*otp
)
155 #define SHOW_AB3100_ATTR(name) \
156 static ssize_t ab3100_otp_##name##_show(struct device *dev, \
157 struct device_attribute *attr, \
160 struct ab3100_otp *otp = dev_get_drvdata(dev); \
161 return sprintf(buf, "%u\n", otp->name); \
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
)
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
),
184 static int __init
ab3100_otp_probe(struct platform_device
*pdev
)
186 struct ab3100_otp
*otp
;
190 otp
= devm_kzalloc(&pdev
->dev
, sizeof(struct ab3100_otp
), GFP_KERNEL
);
194 otp
->dev
= &pdev
->dev
;
196 /* Replace platform data coming in with a local struct */
197 platform_set_drvdata(pdev
, otp
);
199 err
= ab3100_otp_read(otp
);
203 dev_info(&pdev
->dev
, "AB3100 OTP readout registered\n");
206 for (i
= 0; i
< ARRAY_SIZE(ab3100_otp_attrs
); i
++) {
207 err
= device_create_file(&pdev
->dev
,
208 &ab3100_otp_attrs
[i
]);
213 /* debugfs entries */
214 err
= ab3100_otp_init_debugfs(&pdev
->dev
, otp
);
222 device_remove_file(&pdev
->dev
, &ab3100_otp_attrs
[i
]);
226 static int __exit
ab3100_otp_remove(struct platform_device
*pdev
)
228 struct ab3100_otp
*otp
= platform_get_drvdata(pdev
);
231 for (i
= 0; i
< ARRAY_SIZE(ab3100_otp_attrs
); i
++)
232 device_remove_file(&pdev
->dev
,
233 &ab3100_otp_attrs
[i
]);
234 ab3100_otp_exit_debugfs(otp
);
238 static struct platform_driver ab3100_otp_driver
= {
240 .name
= "ab3100-otp",
242 .remove
= __exit_p(ab3100_otp_remove
),
245 module_platform_driver_probe(ab3100_otp_driver
, ab3100_otp_probe
);
247 MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
248 MODULE_DESCRIPTION("AB3100 OTP Readout Driver");
249 MODULE_LICENSE("GPL");