linux/drivers/char/bfin-otp.c
<<
>>
Prefs
   1/*
   2 * Blackfin On-Chip OTP Memory Interface
   3 *
   4 * Copyright 2007-2009 Analog Devices Inc.
   5 *
   6 * Enter bugs at http://blackfin.uclinux.org/
   7 *
   8 * Licensed under the GPL-2 or later.
   9 */
  10
  11#include <linux/device.h>
  12#include <linux/errno.h>
  13#include <linux/fs.h>
  14#include <linux/init.h>
  15#include <linux/miscdevice.h>
  16#include <linux/module.h>
  17#include <linux/mutex.h>
  18#include <linux/types.h>
  19#include <mtd/mtd-abi.h>
  20
  21#include <asm/blackfin.h>
  22#include <asm/bfrom.h>
  23#include <asm/uaccess.h>
  24
  25#define stamp(fmt, args...) pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args)
  26#define stampit() stamp("here i am")
  27#define pr_init(fmt, args...) ({ static const __initconst char __fmt[] = fmt; printk(__fmt, ## args); })
  28
  29#define DRIVER_NAME "bfin-otp"
  30#define PFX DRIVER_NAME ": "
  31
  32static DEFINE_MUTEX(bfin_otp_lock);
  33
  34/**
  35 *      bfin_otp_read - Read OTP pages
  36 *
  37 *      All reads must be in half page chunks (half page == 64 bits).
  38 */
  39static ssize_t bfin_otp_read(struct file *file, char __user *buff, size_t count, loff_t *pos)
  40{
  41        ssize_t bytes_done;
  42        u32 page, flags, ret;
  43        u64 content;
  44
  45        stampit();
  46
  47        if (count % sizeof(u64))
  48                return -EMSGSIZE;
  49
  50        if (mutex_lock_interruptible(&bfin_otp_lock))
  51                return -ERESTARTSYS;
  52
  53        bytes_done = 0;
  54        page = *pos / (sizeof(u64) * 2);
  55        while (bytes_done < count) {
  56                flags = (*pos % (sizeof(u64) * 2) ? OTP_UPPER_HALF : OTP_LOWER_HALF);
  57                stamp("processing page %i (0x%x:%s)", page, flags,
  58                        (flags & OTP_UPPER_HALF ? "upper" : "lower"));
  59                ret = bfrom_OtpRead(page, flags, &content);
  60                if (ret & OTP_MASTER_ERROR) {
  61                        stamp("error from otp: 0x%x", ret);
  62                        bytes_done = -EIO;
  63                        break;
  64                }
  65                if (copy_to_user(buff + bytes_done, &content, sizeof(content))) {
  66                        bytes_done = -EFAULT;
  67                        break;
  68                }
  69                if (flags & OTP_UPPER_HALF)
  70                        ++page;
  71                bytes_done += sizeof(content);
  72                *pos += sizeof(content);
  73        }
  74
  75        mutex_unlock(&bfin_otp_lock);
  76
  77        return bytes_done;
  78}
  79
  80#ifdef CONFIG_BFIN_OTP_WRITE_ENABLE
  81static bool allow_writes;
  82
  83/**
  84 *      bfin_otp_init_timing - setup OTP timing parameters
  85 *
  86 *      Required before doing any write operation.  Algorithms from HRM.
  87 */
  88static u32 bfin_otp_init_timing(void)
  89{
  90        u32 tp1, tp2, tp3, timing;
  91
  92        tp1 = get_sclk() / 1000000;
  93        tp2 = (2 * get_sclk() / 10000000) << 8;
  94        tp3 = (0x1401) << 15;
  95        timing = tp1 | tp2 | tp3;
  96        if (bfrom_OtpCommand(OTP_INIT, timing))
  97                return 0;
  98
  99        return timing;
 100}
 101
 102/**
 103 *      bfin_otp_deinit_timing - set timings to only allow reads
 104 *
 105 *      Should be called after all writes are done.
 106 */
 107static void bfin_otp_deinit_timing(u32 timing)
 108{
 109        /* mask bits [31:15] so that any attempts to write fail */
 110        bfrom_OtpCommand(OTP_CLOSE, 0);
 111        bfrom_OtpCommand(OTP_INIT, timing & ~(-1 << 15));
 112        bfrom_OtpCommand(OTP_CLOSE, 0);
 113}
 114
 115/**
 116 *      bfin_otp_write - write OTP pages
 117 *
 118 *      All writes must be in half page chunks (half page == 64 bits).
 119 */
 120static ssize_t bfin_otp_write(struct file *filp, const char __user *buff, size_t count, loff_t *pos)
 121{
 122        ssize_t bytes_done;
 123        u32 timing, page, base_flags, flags, ret;
 124        u64 content;
 125
 126        if (!allow_writes)
 127                return -EACCES;
 128
 129        if (count % sizeof(u64))
 130                return -EMSGSIZE;
 131
 132        if (mutex_lock_interruptible(&bfin_otp_lock))
 133                return -ERESTARTSYS;
 134
 135        stampit();
 136
 137        timing = bfin_otp_init_timing();
 138        if (timing == 0) {
 139                mutex_unlock(&bfin_otp_lock);
 140                return -EIO;
 141        }
 142
 143        base_flags = OTP_CHECK_FOR_PREV_WRITE;
 144
 145        bytes_done = 0;
 146        page = *pos / (sizeof(u64) * 2);
 147        while (bytes_done < count) {
 148                flags = base_flags | (*pos % (sizeof(u64) * 2) ? OTP_UPPER_HALF : OTP_LOWER_HALF);
 149                stamp("processing page %i (0x%x:%s) from %p", page, flags,
 150                        (flags & OTP_UPPER_HALF ? "upper" : "lower"), buff + bytes_done);
 151                if (copy_from_user(&content, buff + bytes_done, sizeof(content))) {
 152                        bytes_done = -EFAULT;
 153                        break;
 154                }
 155                ret = bfrom_OtpWrite(page, flags, &content);
 156                if (ret & OTP_MASTER_ERROR) {
 157                        stamp("error from otp: 0x%x", ret);
 158                        bytes_done = -EIO;
 159                        break;
 160                }
 161                if (flags & OTP_UPPER_HALF)
 162                        ++page;
 163                bytes_done += sizeof(content);
 164                *pos += sizeof(content);
 165        }
 166
 167        bfin_otp_deinit_timing(timing);
 168
 169        mutex_unlock(&bfin_otp_lock);
 170
 171        return bytes_done;
 172}
 173
 174static long bfin_otp_ioctl(struct file *filp, unsigned cmd, unsigned long arg)
 175{
 176        stampit();
 177
 178        switch (cmd) {
 179        case OTPLOCK: {
 180                u32 timing;
 181                int ret = -EIO;
 182
 183                if (!allow_writes)
 184                        return -EACCES;
 185
 186                if (mutex_lock_interruptible(&bfin_otp_lock))
 187                        return -ERESTARTSYS;
 188
 189                timing = bfin_otp_init_timing();
 190                if (timing) {
 191                        u32 otp_result = bfrom_OtpWrite(arg, OTP_LOCK, NULL);
 192                        stamp("locking page %lu resulted in 0x%x", arg, otp_result);
 193                        if (!(otp_result & OTP_MASTER_ERROR))
 194                                ret = 0;
 195
 196                        bfin_otp_deinit_timing(timing);
 197                }
 198
 199                mutex_unlock(&bfin_otp_lock);
 200
 201                return ret;
 202        }
 203
 204        case MEMLOCK:
 205                allow_writes = false;
 206                return 0;
 207
 208        case MEMUNLOCK:
 209                allow_writes = true;
 210                return 0;
 211        }
 212
 213        return -EINVAL;
 214}
 215#else
 216# define bfin_otp_write NULL
 217# define bfin_otp_ioctl NULL
 218#endif
 219
 220static const struct file_operations bfin_otp_fops = {
 221        .owner          = THIS_MODULE,
 222        .unlocked_ioctl = bfin_otp_ioctl,
 223        .read           = bfin_otp_read,
 224        .write          = bfin_otp_write,
 225        .llseek         = default_llseek,
 226};
 227
 228static struct miscdevice bfin_otp_misc_device = {
 229        .minor    = MISC_DYNAMIC_MINOR,
 230        .name     = DRIVER_NAME,
 231        .fops     = &bfin_otp_fops,
 232};
 233
 234/**
 235 *      bfin_otp_init - Initialize module
 236 *
 237 *      Registers the device and notifier handler. Actual device
 238 *      initialization is handled by bfin_otp_open().
 239 */
 240static int __init bfin_otp_init(void)
 241{
 242        int ret;
 243
 244        stampit();
 245
 246        ret = misc_register(&bfin_otp_misc_device);
 247        if (ret) {
 248                pr_init(KERN_ERR PFX "unable to register a misc device\n");
 249                return ret;
 250        }
 251
 252        pr_init(KERN_INFO PFX "initialized\n");
 253
 254        return 0;
 255}
 256
 257/**
 258 *      bfin_otp_exit - Deinitialize module
 259 *
 260 *      Unregisters the device and notifier handler. Actual device
 261 *      deinitialization is handled by bfin_otp_close().
 262 */
 263static void __exit bfin_otp_exit(void)
 264{
 265        stampit();
 266
 267        misc_deregister(&bfin_otp_misc_device);
 268}
 269
 270module_init(bfin_otp_init);
 271module_exit(bfin_otp_exit);
 272
 273MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>");
 274MODULE_DESCRIPTION("Blackfin OTP Memory Interface");
 275MODULE_LICENSE("GPL");
 276
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.