aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorMike Frysinger <vapier@gentoo.org>2009-09-23 18:57:16 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-09-24 10:21:03 -0400
commit156dd635e26ab2b356be139ec4b5651afd3805e2 (patch)
tree5bf47aea1c4eb9129e044402bd12be9b0ad1cb9a /drivers
parentfbd8ae106850b6a0215c2776e70a75a1b93cafc2 (diff)
bfin-otp: add writing support
The on-chip OTP may be written at runtime, so enable support for it in the driver. However, since writing should really be done only on development systems, don't bend over backwards to make sure the simple software lock is per-fd -- per-device is OK. Signed-off-by: Mike Frysinger <vapier@gentoo.org> Signed-off-by: Bryan Wu <cooloney@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/char/bfin-otp.c173
1 files changed, 129 insertions, 44 deletions
diff --git a/drivers/char/bfin-otp.c b/drivers/char/bfin-otp.c
index 0a01329451e4..e3dd24bff514 100644
--- a/drivers/char/bfin-otp.c
+++ b/drivers/char/bfin-otp.c
@@ -1,8 +1,7 @@
1/* 1/*
2 * Blackfin On-Chip OTP Memory Interface 2 * Blackfin On-Chip OTP Memory Interface
3 * Supports BF52x/BF54x
4 * 3 *
5 * Copyright 2007-2008 Analog Devices Inc. 4 * Copyright 2007-2009 Analog Devices Inc.
6 * 5 *
7 * Enter bugs at http://blackfin.uclinux.org/ 6 * Enter bugs at http://blackfin.uclinux.org/
8 * 7 *
@@ -17,8 +16,10 @@
17#include <linux/module.h> 16#include <linux/module.h>
18#include <linux/mutex.h> 17#include <linux/mutex.h>
19#include <linux/types.h> 18#include <linux/types.h>
19#include <mtd/mtd-abi.h>
20 20
21#include <asm/blackfin.h> 21#include <asm/blackfin.h>
22#include <asm/bfrom.h>
22#include <asm/uaccess.h> 23#include <asm/uaccess.h>
23 24
24#define stamp(fmt, args...) pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args) 25#define stamp(fmt, args...) pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args)
@@ -30,39 +31,6 @@
30 31
31static DEFINE_MUTEX(bfin_otp_lock); 32static DEFINE_MUTEX(bfin_otp_lock);
32 33
33/* OTP Boot ROM functions */
34#define _BOOTROM_OTP_COMMAND 0xEF000018
35#define _BOOTROM_OTP_READ 0xEF00001A
36#define _BOOTROM_OTP_WRITE 0xEF00001C
37
38static u32 (* const otp_command)(u32 command, u32 value) = (void *)_BOOTROM_OTP_COMMAND;
39static u32 (* const otp_read)(u32 page, u32 flags, u64 *page_content) = (void *)_BOOTROM_OTP_READ;
40static u32 (* const otp_write)(u32 page, u32 flags, u64 *page_content) = (void *)_BOOTROM_OTP_WRITE;
41
42/* otp_command(): defines for "command" */
43#define OTP_INIT 0x00000001
44#define OTP_CLOSE 0x00000002
45
46/* otp_{read,write}(): defines for "flags" */
47#define OTP_LOWER_HALF 0x00000000 /* select upper/lower 64-bit half (bit 0) */
48#define OTP_UPPER_HALF 0x00000001
49#define OTP_NO_ECC 0x00000010 /* do not use ECC */
50#define OTP_LOCK 0x00000020 /* sets page protection bit for page */
51#define OTP_ACCESS_READ 0x00001000
52#define OTP_ACCESS_READWRITE 0x00002000
53
54/* Return values for all functions */
55#define OTP_SUCCESS 0x00000000
56#define OTP_MASTER_ERROR 0x001
57#define OTP_WRITE_ERROR 0x003
58#define OTP_READ_ERROR 0x005
59#define OTP_ACC_VIO_ERROR 0x009
60#define OTP_DATA_MULT_ERROR 0x011
61#define OTP_ECC_MULT_ERROR 0x021
62#define OTP_PREV_WR_ERROR 0x041
63#define OTP_DATA_SB_WARN 0x100
64#define OTP_ECC_SB_WARN 0x200
65
66/** 34/**
67 * bfin_otp_read - Read OTP pages 35 * bfin_otp_read - Read OTP pages
68 * 36 *
@@ -86,9 +54,11 @@ static ssize_t bfin_otp_read(struct file *file, char __user *buff, size_t count,
86 page = *pos / (sizeof(u64) * 2); 54 page = *pos / (sizeof(u64) * 2);
87 while (bytes_done < count) { 55 while (bytes_done < count) {
88 flags = (*pos % (sizeof(u64) * 2) ? OTP_UPPER_HALF : OTP_LOWER_HALF); 56 flags = (*pos % (sizeof(u64) * 2) ? OTP_UPPER_HALF : OTP_LOWER_HALF);
89 stamp("processing page %i (%s)", page, (flags == OTP_UPPER_HALF ? "upper" : "lower")); 57 stamp("processing page %i (0x%x:%s)", page, flags,
90 ret = otp_read(page, flags, &content); 58 (flags & OTP_UPPER_HALF ? "upper" : "lower"));
59 ret = bfrom_OtpRead(page, flags, &content);
91 if (ret & OTP_MASTER_ERROR) { 60 if (ret & OTP_MASTER_ERROR) {
61 stamp("error from otp: 0x%x", ret);
92 bytes_done = -EIO; 62 bytes_done = -EIO;
93 break; 63 break;
94 } 64 }
@@ -96,7 +66,7 @@ static ssize_t bfin_otp_read(struct file *file, char __user *buff, size_t count,
96 bytes_done = -EFAULT; 66 bytes_done = -EFAULT;
97 break; 67 break;
98 } 68 }
99 if (flags == OTP_UPPER_HALF) 69 if (flags & OTP_UPPER_HALF)
100 ++page; 70 ++page;
101 bytes_done += sizeof(content); 71 bytes_done += sizeof(content);
102 *pos += sizeof(content); 72 *pos += sizeof(content);
@@ -108,14 +78,53 @@ static ssize_t bfin_otp_read(struct file *file, char __user *buff, size_t count,
108} 78}
109 79
110#ifdef CONFIG_BFIN_OTP_WRITE_ENABLE 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
111/** 115/**
112 * bfin_otp_write - Write OTP pages 116 * bfin_otp_write - write OTP pages
113 * 117 *
114 * All writes must be in half page chunks (half page == 64 bits). 118 * All writes must be in half page chunks (half page == 64 bits).
115 */ 119 */
116static ssize_t bfin_otp_write(struct file *filp, const char __user *buff, size_t count, loff_t *pos) 120static ssize_t bfin_otp_write(struct file *filp, const char __user *buff, size_t count, loff_t *pos)
117{ 121{
118 stampit(); 122 ssize_t bytes_done;
123 u32 timing, page, base_flags, flags, ret;
124 u64 content;
125
126 if (!allow_writes)
127 return -EACCES;
119 128
120 if (count % sizeof(u64)) 129 if (count % sizeof(u64))
121 return -EMSGSIZE; 130 return -EMSGSIZE;
@@ -123,20 +132,96 @@ static ssize_t bfin_otp_write(struct file *filp, const char __user *buff, size_t
123 if (mutex_lock_interruptible(&bfin_otp_lock)) 132 if (mutex_lock_interruptible(&bfin_otp_lock))
124 return -ERESTARTSYS; 133 return -ERESTARTSYS;
125 134
126 /* need otp_init() documentation before this can be implemented */ 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);
127 168
128 mutex_unlock(&bfin_otp_lock); 169 mutex_unlock(&bfin_otp_lock);
129 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
130 return -EINVAL; 213 return -EINVAL;
131} 214}
132#else 215#else
133# define bfin_otp_write NULL 216# define bfin_otp_write NULL
217# define bfin_otp_ioctl NULL
134#endif 218#endif
135 219
136static struct file_operations bfin_otp_fops = { 220static struct file_operations bfin_otp_fops = {
137 .owner = THIS_MODULE, 221 .owner = THIS_MODULE,
138 .read = bfin_otp_read, 222 .unlocked_ioctl = bfin_otp_ioctl,
139 .write = bfin_otp_write, 223 .read = bfin_otp_read,
224 .write = bfin_otp_write,
140}; 225};
141 226
142static struct miscdevice bfin_otp_misc_device = { 227static struct miscdevice bfin_otp_misc_device = {