Root/
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 | |
32 | static 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 | */ |
39 | static 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 |
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 | |
115 | /** |
116 | * bfin_otp_write - write OTP pages |
117 | * |
118 | * All writes must be in half page chunks (half page == 64 bits). |
119 | */ |
120 | static 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 | |
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 | |
213 | return -EINVAL; |
214 | } |
215 | #else |
216 | # define bfin_otp_write NULL |
217 | # define bfin_otp_ioctl NULL |
218 | #endif |
219 | |
220 | static 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 | |
228 | static 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 | */ |
240 | static 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 | */ |
263 | static void __exit bfin_otp_exit(void) |
264 | { |
265 | stampit(); |
266 | |
267 | misc_deregister(&bfin_otp_misc_device); |
268 | } |
269 | |
270 | module_init(bfin_otp_init); |
271 | module_exit(bfin_otp_exit); |
272 | |
273 | MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>"); |
274 | MODULE_DESCRIPTION("Blackfin OTP Memory Interface"); |
275 | MODULE_LICENSE("GPL"); |
276 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9