diff options
Diffstat (limited to 'drivers/char/bfin-otp.c')
-rw-r--r-- | drivers/char/bfin-otp.c | 173 |
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 | ||
31 | static DEFINE_MUTEX(bfin_otp_lock); | 32 | static 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 | |||
38 | static u32 (* const otp_command)(u32 command, u32 value) = (void *)_BOOTROM_OTP_COMMAND; | ||
39 | static u32 (* const otp_read)(u32 page, u32 flags, u64 *page_content) = (void *)_BOOTROM_OTP_READ; | ||
40 | static 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 |
81 | static 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 | */ | ||
88 | static 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 | */ | ||
107 | static 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 | */ |
116 | static ssize_t bfin_otp_write(struct file *filp, const char __user *buff, size_t count, loff_t *pos) | 120 | static 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 | |||
174 | static 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 | ||
136 | static struct file_operations bfin_otp_fops = { | 220 | static 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 | ||
142 | static struct miscdevice bfin_otp_misc_device = { | 227 | static struct miscdevice bfin_otp_misc_device = { |