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