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