Blame view

drivers/char/bfin-otp.c 5.93 KB
2dc63a84b   Mike Frysinger   Blackfin char dri...
1
2
  /*
   * Blackfin On-Chip OTP Memory Interface
2dc63a84b   Mike Frysinger   Blackfin char dri...
3
   *
156dd635e   Mike Frysinger   bfin-otp: add wri...
4
   * Copyright 2007-2009 Analog Devices Inc.
2dc63a84b   Mike Frysinger   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   Mike Frysinger   bfin-otp: add wri...
19
  #include <mtd/mtd-abi.h>
2dc63a84b   Mike Frysinger   Blackfin char dri...
20
21
  
  #include <asm/blackfin.h>
156dd635e   Mike Frysinger   bfin-otp: add wri...
22
  #include <asm/bfrom.h>
2dc63a84b   Mike Frysinger   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   Mike Frysinger   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   Mike Frysinger   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   Mike Frysinger   Blackfin char dri...
60
  		if (ret & OTP_MASTER_ERROR) {
156dd635e   Mike Frysinger   bfin-otp: add wri...
61
  			stamp("error from otp: 0x%x", ret);
2dc63a84b   Mike Frysinger   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   Mike Frysinger   bfin-otp: add wri...
69
  		if (flags & OTP_UPPER_HALF)
2dc63a84b   Mike Frysinger   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   Mike Frysinger   bfin-otp: add wri...
81
  static bool allow_writes;
2dc63a84b   Mike Frysinger   Blackfin char dri...
82
  /**
156dd635e   Mike Frysinger   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   Mike Frysinger   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   Mike Frysinger   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   Mike Frysinger   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   Mike Frysinger   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   Mike Frysinger   Blackfin char dri...
166
167
  
  	mutex_unlock(&bfin_otp_lock);
156dd635e   Mike Frysinger   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   Mike Frysinger   Blackfin char dri...
209
210
211
212
  	return -EINVAL;
  }
  #else
  # define bfin_otp_write NULL
156dd635e   Mike Frysinger   bfin-otp: add wri...
213
  # define bfin_otp_ioctl NULL
2dc63a84b   Mike Frysinger   Blackfin char dri...
214
  #endif
828c09509   Alexey Dobriyan   const: constify r...
215
  static const struct file_operations bfin_otp_fops = {
156dd635e   Mike Frysinger   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   Arnd Bergmann   llseek: automatic...
220
  	.llseek		= default_llseek,
2dc63a84b   Mike Frysinger   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");