]>
Commit | Line | Data |
---|---|---|
1394f032 BW |
1 | /* |
2 | * File: arch/blackfin/mach-bf561/coreb.c | |
3 | * Based on: | |
4 | * Author: | |
5 | * | |
6 | * Created: | |
7 | * Description: Handle CoreB on a BF561 | |
8 | * | |
9 | * Modified: | |
10 | * Copyright 2004-2006 Analog Devices Inc. | |
11 | * | |
12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | |
13 | * | |
14 | * This program is free software; you can redistribute it and/or modify | |
15 | * it under the terms of the GNU General Public License as published by | |
16 | * the Free Software Foundation; either version 2 of the License, or | |
17 | * (at your option) any later version. | |
18 | * | |
19 | * This program is distributed in the hope that it will be useful, | |
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | * GNU General Public License for more details. | |
23 | * | |
24 | * You should have received a copy of the GNU General Public License | |
25 | * along with this program; if not, see the file COPYING, or write | |
26 | * to the Free Software Foundation, Inc., | |
27 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
28 | */ | |
29 | ||
30 | #include <linux/mm.h> | |
31 | #include <linux/miscdevice.h> | |
32 | #include <linux/device.h> | |
33 | #include <linux/ioport.h> | |
34 | #include <linux/module.h> | |
1f83b8f1 | 35 | #include <linux/uaccess.h> |
76a7f404 | 36 | #include <linux/fs.h> |
1394f032 | 37 | #include <asm/dma.h> |
76a7f404 | 38 | #include <asm/cacheflush.h> |
1394f032 BW |
39 | |
40 | #define MODULE_VER "v0.1" | |
41 | ||
42 | static spinlock_t coreb_lock; | |
43 | static wait_queue_head_t coreb_dma_wait; | |
44 | ||
45 | #define COREB_IS_OPEN 0x00000001 | |
46 | #define COREB_IS_RUNNING 0x00000010 | |
47 | ||
48 | #define CMD_COREB_INDEX 1 | |
49 | #define CMD_COREB_START 2 | |
50 | #define CMD_COREB_STOP 3 | |
51 | #define CMD_COREB_RESET 4 | |
52 | ||
53 | #define COREB_MINOR 229 | |
54 | ||
55 | static unsigned long coreb_status = 0; | |
56 | static unsigned long coreb_base = 0xff600000; | |
57 | static unsigned long coreb_size = 0x4000; | |
58 | int coreb_dma_done; | |
59 | ||
60 | static loff_t coreb_lseek(struct file *file, loff_t offset, int origin); | |
61 | static ssize_t coreb_read(struct file *file, char *buf, size_t count, | |
62 | loff_t * ppos); | |
63 | static ssize_t coreb_write(struct file *file, const char *buf, size_t count, | |
64 | loff_t * ppos); | |
65 | static int coreb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, | |
66 | unsigned long arg); | |
67 | static int coreb_open(struct inode *inode, struct file *file); | |
68 | static int coreb_release(struct inode *inode, struct file *file); | |
69 | ||
70 | static irqreturn_t coreb_dma_interrupt(int irq, void *dev_id) | |
71 | { | |
72 | clear_dma_irqstat(CH_MEM_STREAM2_DEST); | |
73 | coreb_dma_done = 1; | |
74 | wake_up_interruptible(&coreb_dma_wait); | |
75 | return IRQ_HANDLED; | |
76 | } | |
77 | ||
78 | static ssize_t coreb_write(struct file *file, const char *buf, size_t count, | |
79 | loff_t * ppos) | |
80 | { | |
81 | unsigned long p = *ppos; | |
82 | ssize_t wrote = 0; | |
83 | ||
84 | if (p + count > coreb_size) | |
85 | return -EFAULT; | |
86 | ||
87 | while (count > 0) { | |
88 | int len = count; | |
89 | ||
90 | if (len > PAGE_SIZE) | |
91 | len = PAGE_SIZE; | |
92 | ||
93 | coreb_dma_done = 0; | |
94 | ||
76a7f404 | 95 | flush_dcache_range((unsigned long)buf, (unsigned long)(buf+len)); |
1394f032 BW |
96 | /* Source Channel */ |
97 | set_dma_start_addr(CH_MEM_STREAM2_SRC, (unsigned long)buf); | |
98 | set_dma_x_count(CH_MEM_STREAM2_SRC, len); | |
99 | set_dma_x_modify(CH_MEM_STREAM2_SRC, sizeof(char)); | |
76a7f404 | 100 | set_dma_config(CH_MEM_STREAM2_SRC, 0); |
1394f032 BW |
101 | /* Destination Channel */ |
102 | set_dma_start_addr(CH_MEM_STREAM2_DEST, coreb_base + p); | |
103 | set_dma_x_count(CH_MEM_STREAM2_DEST, len); | |
104 | set_dma_x_modify(CH_MEM_STREAM2_DEST, sizeof(char)); | |
105 | set_dma_config(CH_MEM_STREAM2_DEST, WNR | RESTART | DI_EN); | |
106 | ||
107 | enable_dma(CH_MEM_STREAM2_SRC); | |
108 | enable_dma(CH_MEM_STREAM2_DEST); | |
109 | ||
110 | wait_event_interruptible(coreb_dma_wait, coreb_dma_done); | |
111 | ||
112 | disable_dma(CH_MEM_STREAM2_SRC); | |
113 | disable_dma(CH_MEM_STREAM2_DEST); | |
114 | ||
115 | count -= len; | |
116 | wrote += len; | |
117 | buf += len; | |
118 | p += len; | |
119 | } | |
120 | *ppos = p; | |
121 | return wrote; | |
122 | } | |
123 | ||
124 | static ssize_t coreb_read(struct file *file, char *buf, size_t count, | |
125 | loff_t * ppos) | |
126 | { | |
127 | unsigned long p = *ppos; | |
128 | ssize_t read = 0; | |
129 | ||
130 | if ((p + count) > coreb_size) | |
131 | return -EFAULT; | |
132 | ||
133 | while (count > 0) { | |
134 | int len = count; | |
135 | ||
136 | if (len > PAGE_SIZE) | |
137 | len = PAGE_SIZE; | |
138 | ||
139 | coreb_dma_done = 0; | |
140 | ||
76a7f404 | 141 | invalidate_dcache_range((unsigned long)buf, (unsigned long)(buf+len)); |
1394f032 BW |
142 | /* Source Channel */ |
143 | set_dma_start_addr(CH_MEM_STREAM2_SRC, coreb_base + p); | |
144 | set_dma_x_count(CH_MEM_STREAM2_SRC, len); | |
145 | set_dma_x_modify(CH_MEM_STREAM2_SRC, sizeof(char)); | |
76a7f404 | 146 | set_dma_config(CH_MEM_STREAM2_SRC, 0); |
1394f032 BW |
147 | /* Destination Channel */ |
148 | set_dma_start_addr(CH_MEM_STREAM2_DEST, (unsigned long)buf); | |
149 | set_dma_x_count(CH_MEM_STREAM2_DEST, len); | |
150 | set_dma_x_modify(CH_MEM_STREAM2_DEST, sizeof(char)); | |
151 | set_dma_config(CH_MEM_STREAM2_DEST, WNR | RESTART | DI_EN); | |
152 | ||
153 | enable_dma(CH_MEM_STREAM2_SRC); | |
154 | enable_dma(CH_MEM_STREAM2_DEST); | |
155 | ||
156 | wait_event_interruptible(coreb_dma_wait, coreb_dma_done); | |
157 | ||
158 | disable_dma(CH_MEM_STREAM2_SRC); | |
159 | disable_dma(CH_MEM_STREAM2_DEST); | |
160 | ||
161 | count -= len; | |
162 | read += len; | |
163 | buf += len; | |
164 | p += len; | |
165 | } | |
166 | ||
167 | return read; | |
168 | } | |
169 | ||
170 | static loff_t coreb_lseek(struct file *file, loff_t offset, int origin) | |
171 | { | |
172 | loff_t ret; | |
173 | ||
174 | mutex_lock(&file->f_dentry->d_inode->i_mutex); | |
175 | ||
176 | switch (origin) { | |
177 | case 0 /* SEEK_SET */ : | |
178 | if (offset < coreb_size) { | |
179 | file->f_pos = offset; | |
180 | ret = file->f_pos; | |
181 | } else | |
182 | ret = -EINVAL; | |
183 | break; | |
184 | case 1 /* SEEK_CUR */ : | |
185 | if ((offset + file->f_pos) < coreb_size) { | |
186 | file->f_pos += offset; | |
187 | ret = file->f_pos; | |
188 | } else | |
189 | ret = -EINVAL; | |
190 | default: | |
191 | ret = -EINVAL; | |
192 | } | |
193 | mutex_unlock(&file->f_dentry->d_inode->i_mutex); | |
194 | return ret; | |
195 | } | |
196 | ||
57debdda | 197 | /* No BKL needed here */ |
1394f032 BW |
198 | static int coreb_open(struct inode *inode, struct file *file) |
199 | { | |
200 | spin_lock_irq(&coreb_lock); | |
201 | ||
202 | if (coreb_status & COREB_IS_OPEN) | |
203 | goto out_busy; | |
204 | ||
205 | coreb_status |= COREB_IS_OPEN; | |
206 | ||
207 | spin_unlock_irq(&coreb_lock); | |
208 | return 0; | |
209 | ||
1f83b8f1 | 210 | out_busy: |
1394f032 BW |
211 | spin_unlock_irq(&coreb_lock); |
212 | return -EBUSY; | |
213 | } | |
214 | ||
215 | static int coreb_release(struct inode *inode, struct file *file) | |
216 | { | |
217 | spin_lock_irq(&coreb_lock); | |
218 | coreb_status &= ~COREB_IS_OPEN; | |
219 | spin_unlock_irq(&coreb_lock); | |
220 | return 0; | |
221 | } | |
222 | ||
223 | static int coreb_ioctl(struct inode *inode, struct file *file, | |
224 | unsigned int cmd, unsigned long arg) | |
225 | { | |
226 | int retval = 0; | |
227 | int coreb_index = 0; | |
228 | ||
229 | switch (cmd) { | |
230 | case CMD_COREB_INDEX: | |
231 | if (copy_from_user(&coreb_index, (int *)arg, sizeof(int))) { | |
232 | retval = -EFAULT; | |
233 | break; | |
234 | } | |
235 | ||
236 | spin_lock_irq(&coreb_lock); | |
237 | switch (coreb_index) { | |
238 | case 0: | |
239 | coreb_base = 0xff600000; | |
240 | coreb_size = 0x4000; | |
241 | break; | |
242 | case 1: | |
243 | coreb_base = 0xff610000; | |
244 | coreb_size = 0x4000; | |
245 | break; | |
246 | case 2: | |
247 | coreb_base = 0xff500000; | |
248 | coreb_size = 0x8000; | |
249 | break; | |
250 | case 3: | |
251 | coreb_base = 0xff400000; | |
252 | coreb_size = 0x8000; | |
253 | break; | |
254 | default: | |
255 | retval = -EINVAL; | |
256 | break; | |
257 | } | |
258 | spin_unlock_irq(&coreb_lock); | |
259 | ||
260 | mutex_lock(&file->f_dentry->d_inode->i_mutex); | |
261 | file->f_pos = 0; | |
262 | mutex_unlock(&file->f_dentry->d_inode->i_mutex); | |
263 | break; | |
264 | case CMD_COREB_START: | |
265 | spin_lock_irq(&coreb_lock); | |
266 | if (coreb_status & COREB_IS_RUNNING) { | |
267 | retval = -EBUSY; | |
268 | break; | |
269 | } | |
270 | printk(KERN_INFO "Starting Core B\n"); | |
271 | coreb_status |= COREB_IS_RUNNING; | |
272 | bfin_write_SICA_SYSCR(bfin_read_SICA_SYSCR() & ~0x0020); | |
273 | SSYNC(); | |
76a7f404 | 274 | spin_unlock_irq(&coreb_lock); |
1394f032 BW |
275 | break; |
276 | #if defined(CONFIG_BF561_COREB_RESET) | |
277 | case CMD_COREB_STOP: | |
278 | spin_lock_irq(&coreb_lock); | |
279 | printk(KERN_INFO "Stopping Core B\n"); | |
280 | bfin_write_SICA_SYSCR(bfin_read_SICA_SYSCR() | 0x0020); | |
281 | bfin_write_SICB_SYSCR(bfin_read_SICB_SYSCR() | 0x0080); | |
282 | coreb_status &= ~COREB_IS_RUNNING; | |
76a7f404 | 283 | spin_unlock_irq(&coreb_lock); |
1394f032 BW |
284 | break; |
285 | case CMD_COREB_RESET: | |
286 | printk(KERN_INFO "Resetting Core B\n"); | |
287 | bfin_write_SICB_SYSCR(bfin_read_SICB_SYSCR() | 0x0080); | |
288 | break; | |
289 | #endif | |
290 | } | |
291 | ||
292 | return retval; | |
293 | } | |
294 | ||
295 | static struct file_operations coreb_fops = { | |
296 | .owner = THIS_MODULE, | |
297 | .llseek = coreb_lseek, | |
298 | .read = coreb_read, | |
299 | .write = coreb_write, | |
300 | .ioctl = coreb_ioctl, | |
301 | .open = coreb_open, | |
302 | .release = coreb_release | |
303 | }; | |
304 | ||
305 | static struct miscdevice coreb_dev = { | |
306 | COREB_MINOR, | |
307 | "coreb", | |
308 | &coreb_fops | |
309 | }; | |
310 | ||
311 | static ssize_t coreb_show_status(struct device *dev, struct device_attribute *attr, char *buf) | |
312 | { | |
313 | return sprintf(buf, | |
314 | "Base Address:\t0x%08lx\n" | |
315 | "Core B is %s\n" | |
316 | "SICA_SYSCR:\t%04x\n" | |
317 | "SICB_SYSCR:\t%04x\n" | |
318 | "\n" | |
319 | "IRQ Status:\tCore A\t\tCore B\n" | |
320 | "ISR0:\t\t%08x\t\t%08x\n" | |
321 | "ISR1:\t\t%08x\t\t%08x\n" | |
322 | "IMASK0:\t\t%08x\t\t%08x\n" | |
323 | "IMASK1:\t\t%08x\t\t%08x\n", | |
324 | coreb_base, | |
325 | coreb_status & COREB_IS_RUNNING ? "running" : "stalled", | |
326 | bfin_read_SICA_SYSCR(), bfin_read_SICB_SYSCR(), | |
327 | bfin_read_SICA_ISR0(), bfin_read_SICB_ISR0(), | |
328 | bfin_read_SICA_ISR1(), bfin_read_SICB_ISR0(), | |
329 | bfin_read_SICA_IMASK0(), bfin_read_SICB_IMASK0(), | |
330 | bfin_read_SICA_IMASK1(), bfin_read_SICB_IMASK1()); | |
331 | } | |
332 | ||
333 | static DEVICE_ATTR(coreb_status, S_IRUGO, coreb_show_status, NULL); | |
334 | ||
335 | int __init bf561_coreb_init(void) | |
336 | { | |
337 | init_waitqueue_head(&coreb_dma_wait); | |
338 | ||
339 | spin_lock_init(&coreb_lock); | |
340 | /* Request the core memory regions for Core B */ | |
341 | if (request_mem_region(0xff600000, 0x4000, | |
342 | "Core B - Instruction SRAM") == NULL) | |
343 | goto exit; | |
344 | ||
345 | if (request_mem_region(0xFF610000, 0x4000, | |
346 | "Core B - Instruction SRAM") == NULL) | |
347 | goto release_instruction_a_sram; | |
348 | ||
349 | if (request_mem_region(0xFF500000, 0x8000, | |
350 | "Core B - Data Bank B SRAM") == NULL) | |
351 | goto release_instruction_b_sram; | |
352 | ||
353 | if (request_mem_region(0xff400000, 0x8000, | |
354 | "Core B - Data Bank A SRAM") == NULL) | |
355 | goto release_data_b_sram; | |
356 | ||
357 | if (request_dma(CH_MEM_STREAM2_DEST, "Core B - DMA Destination") < 0) | |
358 | goto release_data_a_sram; | |
359 | ||
360 | if (request_dma(CH_MEM_STREAM2_SRC, "Core B - DMA Source") < 0) | |
361 | goto release_dma_dest; | |
362 | ||
363 | set_dma_callback(CH_MEM_STREAM2_DEST, coreb_dma_interrupt, NULL); | |
364 | ||
365 | misc_register(&coreb_dev); | |
366 | ||
367 | if (device_create_file(coreb_dev.this_device, &dev_attr_coreb_status)) | |
368 | goto release_dma_src; | |
369 | ||
370 | printk(KERN_INFO "BF561 Core B driver %s initialized.\n", MODULE_VER); | |
371 | return 0; | |
372 | ||
1f83b8f1 | 373 | release_dma_src: |
1394f032 | 374 | free_dma(CH_MEM_STREAM2_SRC); |
1f83b8f1 | 375 | release_dma_dest: |
1394f032 | 376 | free_dma(CH_MEM_STREAM2_DEST); |
1f83b8f1 | 377 | release_data_a_sram: |
1394f032 | 378 | release_mem_region(0xff400000, 0x8000); |
1f83b8f1 | 379 | release_data_b_sram: |
1394f032 | 380 | release_mem_region(0xff500000, 0x8000); |
1f83b8f1 | 381 | release_instruction_b_sram: |
1394f032 | 382 | release_mem_region(0xff610000, 0x4000); |
1f83b8f1 | 383 | release_instruction_a_sram: |
1394f032 | 384 | release_mem_region(0xff600000, 0x4000); |
1f83b8f1 | 385 | exit: |
1394f032 BW |
386 | return -ENOMEM; |
387 | } | |
388 | ||
389 | void __exit bf561_coreb_exit(void) | |
390 | { | |
391 | device_remove_file(coreb_dev.this_device, &dev_attr_coreb_status); | |
392 | misc_deregister(&coreb_dev); | |
393 | ||
394 | release_mem_region(0xff610000, 0x4000); | |
395 | release_mem_region(0xff600000, 0x4000); | |
396 | release_mem_region(0xff500000, 0x8000); | |
397 | release_mem_region(0xff400000, 0x8000); | |
398 | ||
399 | free_dma(CH_MEM_STREAM2_DEST); | |
400 | free_dma(CH_MEM_STREAM2_SRC); | |
401 | } | |
402 | ||
403 | module_init(bf561_coreb_init); | |
404 | module_exit(bf561_coreb_exit); | |
405 | ||
406 | MODULE_AUTHOR("Bas Vermeulen <bvermeul@blackstar.xs4all.nl>"); | |
407 | MODULE_DESCRIPTION("BF561 Core B Support"); |