OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | office | 1 | From 8b3f935c48ff65dfb8985ce3e6c631530cd1471a Mon Sep 17 00:00:00 2001 |
2 | From: Luke Wren <wren6991@gmail.com> |
||
3 | Date: Sat, 5 Sep 2015 01:14:45 +0100 |
||
4 | Subject: [PATCH] Add SMI driver |
||
5 | |||
6 | Signed-off-by: Luke Wren <wren6991@gmail.com> |
||
7 | --- |
||
8 | .../bindings/misc/brcm,bcm2835-smi-dev.txt | 17 + |
||
9 | .../devicetree/bindings/misc/brcm,bcm2835-smi.txt | 48 + |
||
10 | drivers/char/broadcom/Kconfig | 8 + |
||
11 | drivers/char/broadcom/Makefile | 2 +- |
||
12 | drivers/char/broadcom/bcm2835_smi_dev.c | 402 +++++++++ |
||
13 | drivers/misc/Kconfig | 8 + |
||
14 | drivers/misc/Makefile | 1 + |
||
15 | drivers/misc/bcm2835_smi.c | 985 +++++++++++++++++++++ |
||
16 | include/linux/broadcom/bcm2835_smi.h | 391 ++++++++ |
||
17 | 9 files changed, 1861 insertions(+), 1 deletion(-) |
||
18 | create mode 100644 Documentation/devicetree/bindings/misc/brcm,bcm2835-smi-dev.txt |
||
19 | create mode 100644 Documentation/devicetree/bindings/misc/brcm,bcm2835-smi.txt |
||
20 | create mode 100644 drivers/char/broadcom/bcm2835_smi_dev.c |
||
21 | create mode 100644 drivers/misc/bcm2835_smi.c |
||
22 | create mode 100644 include/linux/broadcom/bcm2835_smi.h |
||
23 | |||
24 | --- /dev/null |
||
25 | +++ b/Documentation/devicetree/bindings/misc/brcm,bcm2835-smi-dev.txt |
||
26 | @@ -0,0 +1,17 @@ |
||
27 | +* Broadcom BCM2835 SMI character device driver. |
||
28 | + |
||
29 | +SMI or secondary memory interface is a peripheral specific to certain Broadcom |
||
30 | +SOCs, and is helpful for talking to things like parallel-interface displays |
||
31 | +and NAND flashes (in fact, most things with a parallel register interface). |
||
32 | + |
||
33 | +This driver adds a character device which provides a user-space interface to |
||
34 | +an instance of the SMI driver. |
||
35 | + |
||
36 | +Required properties: |
||
37 | +- compatible: "brcm,bcm2835-smi-dev" |
||
38 | +- smi_handle: a phandle to the smi node. |
||
39 | + |
||
40 | +Optional properties: |
||
41 | +- None. |
||
42 | + |
||
43 | + |
||
44 | --- /dev/null |
||
45 | +++ b/Documentation/devicetree/bindings/misc/brcm,bcm2835-smi.txt |
||
46 | @@ -0,0 +1,48 @@ |
||
47 | +* Broadcom BCM2835 SMI driver. |
||
48 | + |
||
49 | +SMI or secondary memory interface is a peripheral specific to certain Broadcom |
||
50 | +SOCs, and is helpful for talking to things like parallel-interface displays |
||
51 | +and NAND flashes (in fact, most things with a parallel register interface). |
||
52 | + |
||
53 | +Required properties: |
||
54 | +- compatible: "brcm,bcm2835-smi" |
||
55 | +- reg: Should contain location and length of SMI registers and SMI clkman regs |
||
56 | +- interrupts: *the* SMI interrupt. |
||
57 | +- pinctrl-names: should be "default". |
||
58 | +- pinctrl-0: the phandle of the gpio pin node. |
||
59 | +- brcm,smi-clock-source: the clock source for clkman |
||
60 | +- brcm,smi-clock-divisor: the integer clock divisor for clkman |
||
61 | +- dmas: the dma controller phandle and the DREQ number (4 on a 2835) |
||
62 | +- dma-names: the name used by the driver to request its channel. |
||
63 | + Should be "rx-tx". |
||
64 | + |
||
65 | +Optional properties: |
||
66 | +- None. |
||
67 | + |
||
68 | +Examples: |
||
69 | + |
||
70 | +8 data pin configuration: |
||
71 | + |
||
72 | +smi: smi@7e600000 { |
||
73 | + compatible = "brcm,bcm2835-smi"; |
||
74 | + reg = <0x7e600000 0x44>, <0x7e1010b0 0x8>; |
||
75 | + interrupts = <2 16>; |
||
76 | + pinctrl-names = "default"; |
||
77 | + pinctrl-0 = <&smi_pins>; |
||
78 | + brcm,smi-clock-source = <6>; |
||
79 | + brcm,smi-clock-divisor = <4>; |
||
80 | + dmas = <&dma 4>; |
||
81 | + dma-names = "rx-tx"; |
||
82 | + |
||
83 | + status = "okay"; |
||
84 | +}; |
||
85 | + |
||
86 | +smi_pins: smi_pins { |
||
87 | + brcm,pins = <2 3 4 5 6 7 8 9 10 11 12 13 14 15>; |
||
88 | + /* Alt 1: SMI */ |
||
89 | + brcm,function = <5 5 5 5 5 5 5 5 5 5 5 5 5 5>; |
||
90 | + /* /CS, /WE and /OE are pulled high, as they are |
||
91 | + generally active low signals */ |
||
92 | + brcm,pull = <2 2 2 2 2 2 0 0 0 0 0 0 0 0>; |
||
93 | +}; |
||
94 | + |
||
95 | --- a/drivers/char/broadcom/Kconfig |
||
96 | +++ b/drivers/char/broadcom/Kconfig |
||
97 | @@ -41,3 +41,11 @@ config BCM2835_DEVGPIOMEM |
||
98 | on the 2835. Calling mmap(/dev/gpiomem) will map the GPIO |
||
99 | register page to the user's pointer. |
||
100 | |||
101 | +config BCM2835_SMI_DEV |
||
102 | + tristate "Character device driver for BCM2835 Secondary Memory Interface" |
||
103 | + depends on BCM2835_SMI |
||
104 | + default m |
||
105 | + help |
||
106 | + This driver provides a character device interface (ioctl + read/write) to |
||
107 | + Broadcom's Secondary Memory interface. The low-level functionality is provided |
||
108 | + by the SMI driver itself. |
||
109 | --- a/drivers/char/broadcom/Makefile |
||
110 | +++ b/drivers/char/broadcom/Makefile |
||
111 | @@ -3,4 +3,4 @@ obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o |
||
112 | obj-$(CONFIG_BCM_VC_SM) += vc_sm/ |
||
113 | |||
114 | obj-$(CONFIG_BCM2835_DEVGPIOMEM)+= bcm2835-gpiomem.o |
||
115 | - |
||
116 | +obj-$(CONFIG_BCM2835_SMI_DEV) += bcm2835_smi_dev.o |
||
117 | --- /dev/null |
||
118 | +++ b/drivers/char/broadcom/bcm2835_smi_dev.c |
||
119 | @@ -0,0 +1,402 @@ |
||
120 | +/** |
||
121 | + * Character device driver for Broadcom Secondary Memory Interface |
||
122 | + * |
||
123 | + * Written by Luke Wren <luke@raspberrypi.org> |
||
124 | + * Copyright (c) 2015, Raspberry Pi (Trading) Ltd. |
||
125 | + * |
||
126 | + * Redistribution and use in source and binary forms, with or without |
||
127 | + * modification, are permitted provided that the following conditions |
||
128 | + * are met: |
||
129 | + * 1. Redistributions of source code must retain the above copyright |
||
130 | + * notice, this list of conditions, and the following disclaimer, |
||
131 | + * without modification. |
||
132 | + * 2. Redistributions in binary form must reproduce the above copyright |
||
133 | + * notice, this list of conditions and the following disclaimer in the |
||
134 | + * documentation and/or other materials provided with the distribution. |
||
135 | + * 3. The names of the above-listed copyright holders may not be used |
||
136 | + * to endorse or promote products derived from this software without |
||
137 | + * specific prior written permission. |
||
138 | + * |
||
139 | + * ALTERNATIVELY, this software may be distributed under the terms of the |
||
140 | + * GNU General Public License ("GPL") version 2, as published by the Free |
||
141 | + * Software Foundation. |
||
142 | + * |
||
143 | + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
||
144 | + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
||
145 | + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||
146 | + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||
147 | + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||
148 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||
149 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||
150 | + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||
151 | + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||
152 | + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||
153 | + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
154 | + */ |
||
155 | + |
||
156 | +#include <linux/kernel.h> |
||
157 | +#include <linux/module.h> |
||
158 | +#include <linux/of.h> |
||
159 | +#include <linux/platform_device.h> |
||
160 | +#include <linux/slab.h> |
||
161 | +#include <linux/mm.h> |
||
162 | +#include <linux/pagemap.h> |
||
163 | +#include <linux/fs.h> |
||
164 | +#include <linux/cdev.h> |
||
165 | +#include <linux/fs.h> |
||
166 | + |
||
167 | +#include <linux/broadcom/bcm2835_smi.h> |
||
168 | + |
||
169 | +#define DEVICE_NAME "bcm2835-smi-dev" |
||
170 | +#define DRIVER_NAME "smi-dev-bcm2835" |
||
171 | +#define DEVICE_MINOR 0 |
||
172 | + |
||
173 | +static struct cdev bcm2835_smi_cdev; |
||
174 | +static dev_t bcm2835_smi_devid; |
||
175 | +static struct class *bcm2835_smi_class; |
||
176 | +static struct device *bcm2835_smi_dev; |
||
177 | + |
||
178 | +struct bcm2835_smi_dev_instance { |
||
179 | + struct device *dev; |
||
180 | +}; |
||
181 | + |
||
182 | +static struct bcm2835_smi_instance *smi_inst; |
||
183 | +static struct bcm2835_smi_dev_instance *inst; |
||
184 | + |
||
185 | +static const char *const ioctl_names[] = { |
||
186 | + "READ_SETTINGS", |
||
187 | + "WRITE_SETTINGS", |
||
188 | + "ADDRESS" |
||
189 | +}; |
||
190 | + |
||
191 | +/**************************************************************************** |
||
192 | +* |
||
193 | +* SMI chardev file ops |
||
194 | +* |
||
195 | +***************************************************************************/ |
||
196 | +static long |
||
197 | +bcm2835_smi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
||
198 | +{ |
||
199 | + long ret = 0; |
||
200 | + |
||
201 | + dev_info(inst->dev, "serving ioctl..."); |
||
202 | + |
||
203 | + switch (cmd) { |
||
204 | + case BCM2835_SMI_IOC_GET_SETTINGS:{ |
||
205 | + struct smi_settings *settings; |
||
206 | + |
||
207 | + dev_info(inst->dev, "Reading SMI settings to user."); |
||
208 | + settings = bcm2835_smi_get_settings_from_regs(smi_inst); |
||
209 | + if (copy_to_user((void *)arg, settings, |
||
210 | + sizeof(struct smi_settings))) |
||
211 | + dev_err(inst->dev, "settings copy failed."); |
||
212 | + break; |
||
213 | + } |
||
214 | + case BCM2835_SMI_IOC_WRITE_SETTINGS:{ |
||
215 | + struct smi_settings *settings; |
||
216 | + |
||
217 | + dev_info(inst->dev, "Setting user's SMI settings."); |
||
218 | + settings = bcm2835_smi_get_settings_from_regs(smi_inst); |
||
219 | + if (copy_from_user(settings, (void *)arg, |
||
220 | + sizeof(struct smi_settings))) |
||
221 | + dev_err(inst->dev, "settings copy failed."); |
||
222 | + else |
||
223 | + bcm2835_smi_set_regs_from_settings(smi_inst); |
||
224 | + break; |
||
225 | + } |
||
226 | + case BCM2835_SMI_IOC_ADDRESS: |
||
227 | + dev_info(inst->dev, "SMI address set: 0x%02x", (int)arg); |
||
228 | + bcm2835_smi_set_address(smi_inst, arg); |
||
229 | + break; |
||
230 | + default: |
||
231 | + dev_err(inst->dev, "invalid ioctl cmd: %d", cmd); |
||
232 | + ret = -ENOTTY; |
||
233 | + break; |
||
234 | + } |
||
235 | + |
||
236 | + return ret; |
||
237 | +} |
||
238 | + |
||
239 | +static int bcm2835_smi_open(struct inode *inode, struct file *file) |
||
240 | +{ |
||
241 | + int dev = iminor(inode); |
||
242 | + |
||
243 | + dev_dbg(inst->dev, "SMI device opened."); |
||
244 | + |
||
245 | + if (dev != DEVICE_MINOR) { |
||
246 | + dev_err(inst->dev, |
||
247 | + "bcm2835_smi_release: Unknown minor device: %d", |
||
248 | + dev); |
||
249 | + return -ENXIO; |
||
250 | + } |
||
251 | + |
||
252 | + return 0; |
||
253 | +} |
||
254 | + |
||
255 | +static int bcm2835_smi_release(struct inode *inode, struct file *file) |
||
256 | +{ |
||
257 | + int dev = iminor(inode); |
||
258 | + |
||
259 | + if (dev != DEVICE_MINOR) { |
||
260 | + dev_err(inst->dev, |
||
261 | + "bcm2835_smi_release: Unknown minor device %d", dev); |
||
262 | + return -ENXIO; |
||
263 | + } |
||
264 | + |
||
265 | + return 0; |
||
266 | +} |
||
267 | + |
||
268 | +static ssize_t dma_bounce_user( |
||
269 | + enum dma_transfer_direction dma_dir, |
||
270 | + char __user *user_ptr, |
||
271 | + size_t count, |
||
272 | + struct bcm2835_smi_bounce_info *bounce) |
||
273 | +{ |
||
274 | + int chunk_size; |
||
275 | + int chunk_no = 0; |
||
276 | + int count_left = count; |
||
277 | + |
||
278 | + while (count_left) { |
||
279 | + int rv; |
||
280 | + void *buf; |
||
281 | + |
||
282 | + /* Wait for current chunk to complete: */ |
||
283 | + if (down_timeout(&bounce->callback_sem, |
||
284 | + msecs_to_jiffies(1000))) { |
||
285 | + dev_err(inst->dev, "DMA bounce timed out"); |
||
286 | + count -= (count_left); |
||
287 | + break; |
||
288 | + } |
||
289 | + |
||
290 | + if (bounce->callback_sem.count >= DMA_BOUNCE_BUFFER_COUNT - 1) |
||
291 | + dev_err(inst->dev, "WARNING: Ring buffer overflow"); |
||
292 | + chunk_size = count_left > DMA_BOUNCE_BUFFER_SIZE ? |
||
293 | + DMA_BOUNCE_BUFFER_SIZE : count_left; |
||
294 | + buf = bounce->buffer[chunk_no % DMA_BOUNCE_BUFFER_COUNT]; |
||
295 | + if (dma_dir == DMA_DEV_TO_MEM) |
||
296 | + rv = copy_to_user(user_ptr, buf, chunk_size); |
||
297 | + else |
||
298 | + rv = copy_from_user(buf, user_ptr, chunk_size); |
||
299 | + if (rv) |
||
300 | + dev_err(inst->dev, "copy_*_user() failed!: %d", rv); |
||
301 | + user_ptr += chunk_size; |
||
302 | + count_left -= chunk_size; |
||
303 | + chunk_no++; |
||
304 | + } |
||
305 | + return count; |
||
306 | +} |
||
307 | + |
||
308 | +static ssize_t |
||
309 | +bcm2835_read_file(struct file *f, char __user *user_ptr, |
||
310 | + size_t count, loff_t *offs) |
||
311 | +{ |
||
312 | + int odd_bytes; |
||
313 | + |
||
314 | + dev_dbg(inst->dev, "User reading %d bytes from SMI.", count); |
||
315 | + /* We don't want to DMA a number of bytes % 4 != 0 (32 bit FIFO) */ |
||
316 | + if (count > DMA_THRESHOLD_BYTES) |
||
317 | + odd_bytes = count & 0x3; |
||
318 | + else |
||
319 | + odd_bytes = count; |
||
320 | + count -= odd_bytes; |
||
321 | + if (count) { |
||
322 | + struct bcm2835_smi_bounce_info *bounce; |
||
323 | + |
||
324 | + count = bcm2835_smi_user_dma(smi_inst, |
||
325 | + DMA_DEV_TO_MEM, user_ptr, count, |
||
326 | + &bounce); |
||
327 | + if (count) |
||
328 | + count = dma_bounce_user(DMA_DEV_TO_MEM, user_ptr, |
||
329 | + count, bounce); |
||
330 | + } |
||
331 | + if (odd_bytes) { |
||
332 | + /* Read from FIFO directly if not using DMA */ |
||
333 | + uint8_t buf[DMA_THRESHOLD_BYTES]; |
||
334 | + |
||
335 | + bcm2835_smi_read_buf(smi_inst, buf, odd_bytes); |
||
336 | + if (copy_to_user(user_ptr, buf, odd_bytes)) |
||
337 | + dev_err(inst->dev, "copy_to_user() failed."); |
||
338 | + count += odd_bytes; |
||
339 | + |
||
340 | + } |
||
341 | + return count; |
||
342 | +} |
||
343 | + |
||
344 | +static ssize_t |
||
345 | +bcm2835_write_file(struct file *f, const char __user *user_ptr, |
||
346 | + size_t count, loff_t *offs) |
||
347 | +{ |
||
348 | + int odd_bytes; |
||
349 | + |
||
350 | + dev_dbg(inst->dev, "User writing %d bytes to SMI.", count); |
||
351 | + if (count > DMA_THRESHOLD_BYTES) |
||
352 | + odd_bytes = count & 0x3; |
||
353 | + else |
||
354 | + odd_bytes = count; |
||
355 | + count -= odd_bytes; |
||
356 | + if (count) { |
||
357 | + struct bcm2835_smi_bounce_info *bounce; |
||
358 | + |
||
359 | + count = bcm2835_smi_user_dma(smi_inst, |
||
360 | + DMA_MEM_TO_DEV, (char __user *)user_ptr, count, |
||
361 | + &bounce); |
||
362 | + if (count) |
||
363 | + count = dma_bounce_user(DMA_MEM_TO_DEV, |
||
364 | + (char __user *)user_ptr, |
||
365 | + count, bounce); |
||
366 | + } |
||
367 | + if (odd_bytes) { |
||
368 | + uint8_t buf[DMA_THRESHOLD_BYTES]; |
||
369 | + |
||
370 | + if (copy_from_user(buf, user_ptr, odd_bytes)) |
||
371 | + dev_err(inst->dev, "copy_from_user() failed."); |
||
372 | + else |
||
373 | + bcm2835_smi_write_buf(smi_inst, buf, odd_bytes); |
||
374 | + count += odd_bytes; |
||
375 | + } |
||
376 | + return count; |
||
377 | +} |
||
378 | + |
||
379 | +static const struct file_operations |
||
380 | +bcm2835_smi_fops = { |
||
381 | + .owner = THIS_MODULE, |
||
382 | + .unlocked_ioctl = bcm2835_smi_ioctl, |
||
383 | + .open = bcm2835_smi_open, |
||
384 | + .release = bcm2835_smi_release, |
||
385 | + .read = bcm2835_read_file, |
||
386 | + .write = bcm2835_write_file, |
||
387 | +}; |
||
388 | + |
||
389 | + |
||
390 | +/**************************************************************************** |
||
391 | +* |
||
392 | +* bcm2835_smi_probe - called when the driver is loaded. |
||
393 | +* |
||
394 | +***************************************************************************/ |
||
395 | + |
||
396 | +static int bcm2835_smi_dev_probe(struct platform_device *pdev) |
||
397 | +{ |
||
398 | + int err; |
||
399 | + void *ptr_err; |
||
400 | + struct device *dev = &pdev->dev; |
||
401 | + struct device_node *node = dev->of_node, *smi_node; |
||
402 | + |
||
403 | + if (!node) { |
||
404 | + dev_err(dev, "No device tree node supplied!"); |
||
405 | + return -EINVAL; |
||
406 | + } |
||
407 | + |
||
408 | + smi_node = of_parse_phandle(node, "smi_handle", 0); |
||
409 | + |
||
410 | + if (!smi_node) { |
||
411 | + dev_err(dev, "No such property: smi_handle"); |
||
412 | + return -ENXIO; |
||
413 | + } |
||
414 | + |
||
415 | + smi_inst = bcm2835_smi_get(smi_node); |
||
416 | + |
||
417 | + if (!smi_inst) |
||
418 | + return -EPROBE_DEFER; |
||
419 | + |
||
420 | + /* Allocate buffers and instance data */ |
||
421 | + |
||
422 | + inst = devm_kzalloc(dev, sizeof(*inst), GFP_KERNEL); |
||
423 | + |
||
424 | + if (!inst) |
||
425 | + return -ENOMEM; |
||
426 | + |
||
427 | + inst->dev = dev; |
||
428 | + |
||
429 | + /* Create character device entries */ |
||
430 | + |
||
431 | + err = alloc_chrdev_region(&bcm2835_smi_devid, |
||
432 | + DEVICE_MINOR, 1, DEVICE_NAME); |
||
433 | + if (err != 0) { |
||
434 | + dev_err(inst->dev, "unable to allocate device number"); |
||
435 | + return -ENOMEM; |
||
436 | + } |
||
437 | + cdev_init(&bcm2835_smi_cdev, &bcm2835_smi_fops); |
||
438 | + bcm2835_smi_cdev.owner = THIS_MODULE; |
||
439 | + err = cdev_add(&bcm2835_smi_cdev, bcm2835_smi_devid, 1); |
||
440 | + if (err != 0) { |
||
441 | + dev_err(inst->dev, "unable to register device"); |
||
442 | + err = -ENOMEM; |
||
443 | + goto failed_cdev_add; |
||
444 | + } |
||
445 | + |
||
446 | + /* Create sysfs entries */ |
||
447 | + |
||
448 | + bcm2835_smi_class = class_create(THIS_MODULE, DEVICE_NAME); |
||
449 | + ptr_err = bcm2835_smi_class; |
||
450 | + if (IS_ERR(ptr_err)) |
||
451 | + goto failed_class_create; |
||
452 | + |
||
453 | + bcm2835_smi_dev = device_create(bcm2835_smi_class, NULL, |
||
454 | + bcm2835_smi_devid, NULL, |
||
455 | + "smi"); |
||
456 | + ptr_err = bcm2835_smi_dev; |
||
457 | + if (IS_ERR(ptr_err)) |
||
458 | + goto failed_device_create; |
||
459 | + |
||
460 | + dev_info(inst->dev, "initialised"); |
||
461 | + |
||
462 | + return 0; |
||
463 | + |
||
464 | +failed_device_create: |
||
465 | + class_destroy(bcm2835_smi_class); |
||
466 | +failed_class_create: |
||
467 | + cdev_del(&bcm2835_smi_cdev); |
||
468 | + err = PTR_ERR(ptr_err); |
||
469 | +failed_cdev_add: |
||
470 | + unregister_chrdev_region(bcm2835_smi_devid, 1); |
||
471 | + dev_err(dev, "could not load bcm2835_smi_dev"); |
||
472 | + return err; |
||
473 | +} |
||
474 | + |
||
475 | +/**************************************************************************** |
||
476 | +* |
||
477 | +* bcm2835_smi_remove - called when the driver is unloaded. |
||
478 | +* |
||
479 | +***************************************************************************/ |
||
480 | + |
||
481 | +static int bcm2835_smi_dev_remove(struct platform_device *pdev) |
||
482 | +{ |
||
483 | + device_destroy(bcm2835_smi_class, bcm2835_smi_devid); |
||
484 | + class_destroy(bcm2835_smi_class); |
||
485 | + cdev_del(&bcm2835_smi_cdev); |
||
486 | + unregister_chrdev_region(bcm2835_smi_devid, 1); |
||
487 | + |
||
488 | + dev_info(inst->dev, "SMI character dev removed - OK"); |
||
489 | + return 0; |
||
490 | +} |
||
491 | + |
||
492 | +/**************************************************************************** |
||
493 | +* |
||
494 | +* Register the driver with device tree |
||
495 | +* |
||
496 | +***************************************************************************/ |
||
497 | + |
||
498 | +static const struct of_device_id bcm2835_smi_dev_of_match[] = { |
||
499 | + {.compatible = "brcm,bcm2835-smi-dev",}, |
||
500 | + { /* sentinel */ }, |
||
501 | +}; |
||
502 | + |
||
503 | +MODULE_DEVICE_TABLE(of, bcm2835_smi_dev_of_match); |
||
504 | + |
||
505 | +static struct platform_driver bcm2835_smi_dev_driver = { |
||
506 | + .probe = bcm2835_smi_dev_probe, |
||
507 | + .remove = bcm2835_smi_dev_remove, |
||
508 | + .driver = { |
||
509 | + .name = DRIVER_NAME, |
||
510 | + .owner = THIS_MODULE, |
||
511 | + .of_match_table = bcm2835_smi_dev_of_match, |
||
512 | + }, |
||
513 | +}; |
||
514 | + |
||
515 | +module_platform_driver(bcm2835_smi_dev_driver); |
||
516 | + |
||
517 | +MODULE_ALIAS("platform:smi-dev-bcm2835"); |
||
518 | +MODULE_LICENSE("GPL"); |
||
519 | +MODULE_DESCRIPTION( |
||
520 | + "Character device driver for BCM2835's secondary memory interface"); |
||
521 | +MODULE_AUTHOR("Luke Wren <luke@raspberrypi.org>"); |
||
522 | --- a/drivers/misc/Kconfig |
||
523 | +++ b/drivers/misc/Kconfig |
||
524 | @@ -10,6 +10,14 @@ config SENSORS_LIS3LV02D |
||
525 | select INPUT_POLLDEV |
||
526 | default n |
||
527 | |||
528 | +config BCM2835_SMI |
||
529 | + tristate "Broadcom 283x Secondary Memory Interface driver" |
||
530 | + depends on ARCH_BCM2835 |
||
531 | + default m |
||
532 | + help |
||
533 | + Driver for enabling and using Broadcom's Secondary/Slow Memory Interface. |
||
534 | + Appears as /dev/bcm2835_smi. For ioctl interface see drivers/misc/bcm2835_smi.h |
||
535 | + |
||
536 | config AD525X_DPOT |
||
537 | tristate "Analog Devices Digital Potentiometers" |
||
538 | depends on (I2C || SPI) && SYSFS |
||
539 | --- a/drivers/misc/Makefile |
||
540 | +++ b/drivers/misc/Makefile |
||
541 | @@ -9,6 +9,7 @@ obj-$(CONFIG_AD525X_DPOT_SPI) += ad525x_ |
||
542 | obj-$(CONFIG_INTEL_MID_PTI) += pti.o |
||
543 | obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o |
||
544 | obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o |
||
545 | +obj-$(CONFIG_BCM2835_SMI) += bcm2835_smi.o |
||
546 | obj-$(CONFIG_DUMMY_IRQ) += dummy-irq.o |
||
547 | obj-$(CONFIG_ICS932S401) += ics932s401.o |
||
548 | obj-$(CONFIG_LKDTM) += lkdtm.o |
||
549 | --- /dev/null |
||
550 | +++ b/drivers/misc/bcm2835_smi.c |
||
551 | @@ -0,0 +1,985 @@ |
||
552 | +/** |
||
553 | + * Broadcom Secondary Memory Interface driver |
||
554 | + * |
||
555 | + * Written by Luke Wren <luke@raspberrypi.org> |
||
556 | + * Copyright (c) 2015, Raspberry Pi (Trading) Ltd. |
||
557 | + * |
||
558 | + * Redistribution and use in source and binary forms, with or without |
||
559 | + * modification, are permitted provided that the following conditions |
||
560 | + * are met: |
||
561 | + * 1. Redistributions of source code must retain the above copyright |
||
562 | + * notice, this list of conditions, and the following disclaimer, |
||
563 | + * without modification. |
||
564 | + * 2. Redistributions in binary form must reproduce the above copyright |
||
565 | + * notice, this list of conditions and the following disclaimer in the |
||
566 | + * documentation and/or other materials provided with the distribution. |
||
567 | + * 3. The names of the above-listed copyright holders may not be used |
||
568 | + * to endorse or promote products derived from this software without |
||
569 | + * specific prior written permission. |
||
570 | + * |
||
571 | + * ALTERNATIVELY, this software may be distributed under the terms of the |
||
572 | + * GNU General Public License ("GPL") version 2, as published by the Free |
||
573 | + * Software Foundation. |
||
574 | + * |
||
575 | + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
||
576 | + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
||
577 | + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||
578 | + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||
579 | + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||
580 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||
581 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||
582 | + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||
583 | + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||
584 | + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||
585 | + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
586 | + */ |
||
587 | + |
||
588 | +#include <linux/kernel.h> |
||
589 | +#include <linux/module.h> |
||
590 | +#include <linux/of.h> |
||
591 | +#include <linux/platform_device.h> |
||
592 | +#include <linux/of_address.h> |
||
593 | +#include <linux/of_platform.h> |
||
594 | +#include <linux/mm.h> |
||
595 | +#include <linux/slab.h> |
||
596 | +#include <linux/pagemap.h> |
||
597 | +#include <linux/dma-mapping.h> |
||
598 | +#include <linux/dmaengine.h> |
||
599 | +#include <linux/semaphore.h> |
||
600 | +#include <linux/spinlock.h> |
||
601 | +#include <linux/io.h> |
||
602 | + |
||
603 | +#define BCM2835_SMI_IMPLEMENTATION |
||
604 | +#include <linux/broadcom/bcm2835_smi.h> |
||
605 | + |
||
606 | +#define DRIVER_NAME "smi-bcm2835" |
||
607 | + |
||
608 | +#define N_PAGES_FROM_BYTES(n) ((n + PAGE_SIZE-1) / PAGE_SIZE) |
||
609 | + |
||
610 | +#define DMA_WRITE_TO_MEM true |
||
611 | +#define DMA_READ_FROM_MEM false |
||
612 | + |
||
613 | +struct bcm2835_smi_instance { |
||
614 | + struct device *dev; |
||
615 | + struct smi_settings settings; |
||
616 | + __iomem void *smi_regs_ptr, *cm_smi_regs_ptr; |
||
617 | + dma_addr_t smi_regs_busaddr; |
||
618 | + |
||
619 | + struct dma_chan *dma_chan; |
||
620 | + struct dma_slave_config dma_config; |
||
621 | + |
||
622 | + struct bcm2835_smi_bounce_info bounce; |
||
623 | + |
||
624 | + struct scatterlist buffer_sgl; |
||
625 | + |
||
626 | + int clock_source; |
||
627 | + int clock_divisor; |
||
628 | + |
||
629 | + /* Sometimes we are called into in an atomic context (e.g. by |
||
630 | + JFFS2 + MTD) so we can't use a mutex */ |
||
631 | + spinlock_t transaction_lock; |
||
632 | +}; |
||
633 | + |
||
634 | +/**************************************************************************** |
||
635 | +* |
||
636 | +* SMI clock manager setup |
||
637 | +* |
||
638 | +***************************************************************************/ |
||
639 | + |
||
640 | +static inline void write_smi_cm_reg(struct bcm2835_smi_instance *inst, |
||
641 | + u32 val, unsigned reg) |
||
642 | +{ |
||
643 | + writel(CM_PWD | val, inst->cm_smi_regs_ptr + reg); |
||
644 | +} |
||
645 | + |
||
646 | +static inline u32 read_smi_cm_reg(struct bcm2835_smi_instance *inst, |
||
647 | + unsigned reg) |
||
648 | +{ |
||
649 | + return readl(inst->cm_smi_regs_ptr + reg); |
||
650 | +} |
||
651 | + |
||
652 | +static void smi_setup_clock(struct bcm2835_smi_instance *inst) |
||
653 | +{ |
||
654 | + dev_dbg(inst->dev, "Setting up clock..."); |
||
655 | + /* Disable SMI clock and wait for it to stop. */ |
||
656 | + write_smi_cm_reg(inst, 0, CM_SMI_CTL); |
||
657 | + while (read_smi_cm_reg(inst, CM_SMI_CTL) & CM_SMI_CTL_BUSY) |
||
658 | + ; |
||
659 | + |
||
660 | + write_smi_cm_reg(inst, (inst->clock_divisor << CM_SMI_DIV_DIVI_OFFS), |
||
661 | + CM_SMI_DIV); |
||
662 | + write_smi_cm_reg(inst, (inst->clock_source << CM_SMI_CTL_SRC_OFFS), |
||
663 | + CM_SMI_CTL); |
||
664 | + |
||
665 | + /* Enable the clock */ |
||
666 | + write_smi_cm_reg(inst, (inst->clock_source << CM_SMI_CTL_SRC_OFFS) | |
||
667 | + CM_SMI_CTL_ENAB, CM_SMI_CTL); |
||
668 | +} |
||
669 | + |
||
670 | +/**************************************************************************** |
||
671 | +* |
||
672 | +* SMI peripheral setup |
||
673 | +* |
||
674 | +***************************************************************************/ |
||
675 | + |
||
676 | +static inline void write_smi_reg(struct bcm2835_smi_instance *inst, |
||
677 | + u32 val, unsigned reg) |
||
678 | +{ |
||
679 | + writel(val, inst->smi_regs_ptr + reg); |
||
680 | +} |
||
681 | + |
||
682 | +static inline u32 read_smi_reg(struct bcm2835_smi_instance *inst, unsigned reg) |
||
683 | +{ |
||
684 | + return readl(inst->smi_regs_ptr + reg); |
||
685 | +} |
||
686 | + |
||
687 | +/* Token-paste macro for e.g SMIDSR_RSTROBE -> value of SMIDSR_RSTROBE_MASK */ |
||
688 | +#define _CONCAT(x, y) x##y |
||
689 | +#define CONCAT(x, y) _CONCAT(x, y) |
||
690 | + |
||
691 | +#define SET_BIT_FIELD(dest, field, bits) ((dest) = \ |
||
692 | + ((dest) & ~CONCAT(field, _MASK)) | (((bits) << CONCAT(field, _OFFS))& \ |
||
693 | + CONCAT(field, _MASK))) |
||
694 | +#define GET_BIT_FIELD(src, field) (((src) & \ |
||
695 | + CONCAT(field, _MASK)) >> CONCAT(field, _OFFS)) |
||
696 | + |
||
697 | +static void smi_dump_context_labelled(struct bcm2835_smi_instance *inst, |
||
698 | + const char *label) |
||
699 | +{ |
||
700 | + dev_err(inst->dev, "SMI context dump: %s", label); |
||
701 | + dev_err(inst->dev, "SMICS: 0x%08x", read_smi_reg(inst, SMICS)); |
||
702 | + dev_err(inst->dev, "SMIL: 0x%08x", read_smi_reg(inst, SMIL)); |
||
703 | + dev_err(inst->dev, "SMIDSR: 0x%08x", read_smi_reg(inst, SMIDSR0)); |
||
704 | + dev_err(inst->dev, "SMIDSW: 0x%08x", read_smi_reg(inst, SMIDSW0)); |
||
705 | + dev_err(inst->dev, "SMIDC: 0x%08x", read_smi_reg(inst, SMIDC)); |
||
706 | + dev_err(inst->dev, "SMIFD: 0x%08x", read_smi_reg(inst, SMIFD)); |
||
707 | + dev_err(inst->dev, " "); |
||
708 | +} |
||
709 | + |
||
710 | +static inline void smi_dump_context(struct bcm2835_smi_instance *inst) |
||
711 | +{ |
||
712 | + smi_dump_context_labelled(inst, ""); |
||
713 | +} |
||
714 | + |
||
715 | +static void smi_get_default_settings(struct bcm2835_smi_instance *inst) |
||
716 | +{ |
||
717 | + struct smi_settings *settings = &inst->settings; |
||
718 | + |
||
719 | + settings->data_width = SMI_WIDTH_16BIT; |
||
720 | + settings->pack_data = true; |
||
721 | + |
||
722 | + settings->read_setup_time = 1; |
||
723 | + settings->read_hold_time = 1; |
||
724 | + settings->read_pace_time = 1; |
||
725 | + settings->read_strobe_time = 3; |
||
726 | + |
||
727 | + settings->write_setup_time = settings->read_setup_time; |
||
728 | + settings->write_hold_time = settings->read_hold_time; |
||
729 | + settings->write_pace_time = settings->read_pace_time; |
||
730 | + settings->write_strobe_time = settings->read_strobe_time; |
||
731 | + |
||
732 | + settings->dma_enable = true; |
||
733 | + settings->dma_passthrough_enable = false; |
||
734 | + settings->dma_read_thresh = 0x01; |
||
735 | + settings->dma_write_thresh = 0x3f; |
||
736 | + settings->dma_panic_read_thresh = 0x20; |
||
737 | + settings->dma_panic_write_thresh = 0x20; |
||
738 | +} |
||
739 | + |
||
740 | +void bcm2835_smi_set_regs_from_settings(struct bcm2835_smi_instance *inst) |
||
741 | +{ |
||
742 | + struct smi_settings *settings = &inst->settings; |
||
743 | + int smidsr_temp = 0, smidsw_temp = 0, smics_temp, |
||
744 | + smidcs_temp, smidc_temp = 0; |
||
745 | + |
||
746 | + spin_lock(&inst->transaction_lock); |
||
747 | + |
||
748 | + /* temporarily disable the peripheral: */ |
||
749 | + smics_temp = read_smi_reg(inst, SMICS); |
||
750 | + write_smi_reg(inst, 0, SMICS); |
||
751 | + smidcs_temp = read_smi_reg(inst, SMIDCS); |
||
752 | + write_smi_reg(inst, 0, SMIDCS); |
||
753 | + |
||
754 | + if (settings->pack_data) |
||
755 | + smics_temp |= SMICS_PXLDAT; |
||
756 | + else |
||
757 | + smics_temp &= ~SMICS_PXLDAT; |
||
758 | + |
||
759 | + SET_BIT_FIELD(smidsr_temp, SMIDSR_RWIDTH, settings->data_width); |
||
760 | + SET_BIT_FIELD(smidsr_temp, SMIDSR_RSETUP, settings->read_setup_time); |
||
761 | + SET_BIT_FIELD(smidsr_temp, SMIDSR_RHOLD, settings->read_hold_time); |
||
762 | + SET_BIT_FIELD(smidsr_temp, SMIDSR_RPACE, settings->read_pace_time); |
||
763 | + SET_BIT_FIELD(smidsr_temp, SMIDSR_RSTROBE, settings->read_strobe_time); |
||
764 | + write_smi_reg(inst, smidsr_temp, SMIDSR0); |
||
765 | + |
||
766 | + SET_BIT_FIELD(smidsw_temp, SMIDSW_WWIDTH, settings->data_width); |
||
767 | + if (settings->data_width == SMI_WIDTH_8BIT) |
||
768 | + smidsw_temp |= SMIDSW_WSWAP; |
||
769 | + else |
||
770 | + smidsw_temp &= ~SMIDSW_WSWAP; |
||
771 | + SET_BIT_FIELD(smidsw_temp, SMIDSW_WSETUP, settings->write_setup_time); |
||
772 | + SET_BIT_FIELD(smidsw_temp, SMIDSW_WHOLD, settings->write_hold_time); |
||
773 | + SET_BIT_FIELD(smidsw_temp, SMIDSW_WPACE, settings->write_pace_time); |
||
774 | + SET_BIT_FIELD(smidsw_temp, SMIDSW_WSTROBE, |
||
775 | + settings->write_strobe_time); |
||
776 | + write_smi_reg(inst, smidsw_temp, SMIDSW0); |
||
777 | + |
||
778 | + SET_BIT_FIELD(smidc_temp, SMIDC_REQR, settings->dma_read_thresh); |
||
779 | + SET_BIT_FIELD(smidc_temp, SMIDC_REQW, settings->dma_write_thresh); |
||
780 | + SET_BIT_FIELD(smidc_temp, SMIDC_PANICR, |
||
781 | + settings->dma_panic_read_thresh); |
||
782 | + SET_BIT_FIELD(smidc_temp, SMIDC_PANICW, |
||
783 | + settings->dma_panic_write_thresh); |
||
784 | + if (settings->dma_passthrough_enable) { |
||
785 | + smidc_temp |= SMIDC_DMAP; |
||
786 | + smidsr_temp |= SMIDSR_RDREQ; |
||
787 | + write_smi_reg(inst, smidsr_temp, SMIDSR0); |
||
788 | + smidsw_temp |= SMIDSW_WDREQ; |
||
789 | + write_smi_reg(inst, smidsw_temp, SMIDSW0); |
||
790 | + } else |
||
791 | + smidc_temp &= ~SMIDC_DMAP; |
||
792 | + if (settings->dma_enable) |
||
793 | + smidc_temp |= SMIDC_DMAEN; |
||
794 | + else |
||
795 | + smidc_temp &= ~SMIDC_DMAEN; |
||
796 | + |
||
797 | + write_smi_reg(inst, smidc_temp, SMIDC); |
||
798 | + |
||
799 | + /* re-enable (if was previously enabled) */ |
||
800 | + write_smi_reg(inst, smics_temp, SMICS); |
||
801 | + write_smi_reg(inst, smidcs_temp, SMIDCS); |
||
802 | + |
||
803 | + spin_unlock(&inst->transaction_lock); |
||
804 | +} |
||
805 | +EXPORT_SYMBOL(bcm2835_smi_set_regs_from_settings); |
||
806 | + |
||
807 | +struct smi_settings *bcm2835_smi_get_settings_from_regs |
||
808 | + (struct bcm2835_smi_instance *inst) |
||
809 | +{ |
||
810 | + struct smi_settings *settings = &inst->settings; |
||
811 | + int smidsr, smidsw, smidc; |
||
812 | + |
||
813 | + spin_lock(&inst->transaction_lock); |
||
814 | + |
||
815 | + smidsr = read_smi_reg(inst, SMIDSR0); |
||
816 | + smidsw = read_smi_reg(inst, SMIDSW0); |
||
817 | + smidc = read_smi_reg(inst, SMIDC); |
||
818 | + |
||
819 | + settings->pack_data = (read_smi_reg(inst, SMICS) & SMICS_PXLDAT) ? |
||
820 | + true : false; |
||
821 | + |
||
822 | + settings->data_width = GET_BIT_FIELD(smidsr, SMIDSR_RWIDTH); |
||
823 | + settings->read_setup_time = GET_BIT_FIELD(smidsr, SMIDSR_RSETUP); |
||
824 | + settings->read_hold_time = GET_BIT_FIELD(smidsr, SMIDSR_RHOLD); |
||
825 | + settings->read_pace_time = GET_BIT_FIELD(smidsr, SMIDSR_RPACE); |
||
826 | + settings->read_strobe_time = GET_BIT_FIELD(smidsr, SMIDSR_RSTROBE); |
||
827 | + |
||
828 | + settings->write_setup_time = GET_BIT_FIELD(smidsw, SMIDSW_WSETUP); |
||
829 | + settings->write_hold_time = GET_BIT_FIELD(smidsw, SMIDSW_WHOLD); |
||
830 | + settings->write_pace_time = GET_BIT_FIELD(smidsw, SMIDSW_WPACE); |
||
831 | + settings->write_strobe_time = GET_BIT_FIELD(smidsw, SMIDSW_WSTROBE); |
||
832 | + |
||
833 | + settings->dma_read_thresh = GET_BIT_FIELD(smidc, SMIDC_REQR); |
||
834 | + settings->dma_write_thresh = GET_BIT_FIELD(smidc, SMIDC_REQW); |
||
835 | + settings->dma_panic_read_thresh = GET_BIT_FIELD(smidc, SMIDC_PANICR); |
||
836 | + settings->dma_panic_write_thresh = GET_BIT_FIELD(smidc, SMIDC_PANICW); |
||
837 | + settings->dma_passthrough_enable = (smidc & SMIDC_DMAP) ? true : false; |
||
838 | + settings->dma_enable = (smidc & SMIDC_DMAEN) ? true : false; |
||
839 | + |
||
840 | + spin_unlock(&inst->transaction_lock); |
||
841 | + |
||
842 | + return settings; |
||
843 | +} |
||
844 | +EXPORT_SYMBOL(bcm2835_smi_get_settings_from_regs); |
||
845 | + |
||
846 | +static inline void smi_set_address(struct bcm2835_smi_instance *inst, |
||
847 | + unsigned int address) |
||
848 | +{ |
||
849 | + int smia_temp = 0, smida_temp = 0; |
||
850 | + |
||
851 | + SET_BIT_FIELD(smia_temp, SMIA_ADDR, address); |
||
852 | + SET_BIT_FIELD(smida_temp, SMIDA_ADDR, address); |
||
853 | + |
||
854 | + /* Write to both address registers - user doesn't care whether we're |
||
855 | + doing programmed or direct transfers. */ |
||
856 | + write_smi_reg(inst, smia_temp, SMIA); |
||
857 | + write_smi_reg(inst, smida_temp, SMIDA); |
||
858 | +} |
||
859 | + |
||
860 | +static void smi_setup_regs(struct bcm2835_smi_instance *inst) |
||
861 | +{ |
||
862 | + |
||
863 | + dev_dbg(inst->dev, "Initialising SMI registers..."); |
||
864 | + /* Disable the peripheral if already enabled */ |
||
865 | + write_smi_reg(inst, 0, SMICS); |
||
866 | + write_smi_reg(inst, 0, SMIDCS); |
||
867 | + |
||
868 | + smi_get_default_settings(inst); |
||
869 | + bcm2835_smi_set_regs_from_settings(inst); |
||
870 | + smi_set_address(inst, 0); |
||
871 | + |
||
872 | + write_smi_reg(inst, read_smi_reg(inst, SMICS) | SMICS_ENABLE, SMICS); |
||
873 | + write_smi_reg(inst, read_smi_reg(inst, SMIDCS) | SMIDCS_ENABLE, |
||
874 | + SMIDCS); |
||
875 | +} |
||
876 | + |
||
877 | +/**************************************************************************** |
||
878 | +* |
||
879 | +* Low-level SMI access functions |
||
880 | +* Other modules should use the exported higher-level functions e.g. |
||
881 | +* bcm2835_smi_write_buf() unless they have a good reason to use these |
||
882 | +* |
||
883 | +***************************************************************************/ |
||
884 | + |
||
885 | +static inline uint32_t smi_read_single_word(struct bcm2835_smi_instance *inst) |
||
886 | +{ |
||
887 | + int timeout = 0; |
||
888 | + |
||
889 | + write_smi_reg(inst, SMIDCS_ENABLE, SMIDCS); |
||
890 | + write_smi_reg(inst, SMIDCS_ENABLE | SMIDCS_START, SMIDCS); |
||
891 | + /* Make sure things happen in the right order...*/ |
||
892 | + mb(); |
||
893 | + while (!(read_smi_reg(inst, SMIDCS) & SMIDCS_DONE) && |
||
894 | + ++timeout < 10000) |
||
895 | + ; |
||
896 | + if (timeout < 10000) |
||
897 | + return read_smi_reg(inst, SMIDD); |
||
898 | + |
||
899 | + dev_err(inst->dev, |
||
900 | + "SMI direct read timed out (is the clock set up correctly?)"); |
||
901 | + return 0; |
||
902 | +} |
||
903 | + |
||
904 | +static inline void smi_write_single_word(struct bcm2835_smi_instance *inst, |
||
905 | + uint32_t data) |
||
906 | +{ |
||
907 | + int timeout = 0; |
||
908 | + |
||
909 | + write_smi_reg(inst, SMIDCS_ENABLE | SMIDCS_WRITE, SMIDCS); |
||
910 | + write_smi_reg(inst, data, SMIDD); |
||
911 | + write_smi_reg(inst, SMIDCS_ENABLE | SMIDCS_WRITE | SMIDCS_START, |
||
912 | + SMIDCS); |
||
913 | + |
||
914 | + while (!(read_smi_reg(inst, SMIDCS) & SMIDCS_DONE) && |
||
915 | + ++timeout < 10000) |
||
916 | + ; |
||
917 | + if (timeout >= 10000) |
||
918 | + dev_err(inst->dev, |
||
919 | + "SMI direct write timed out (is the clock set up correctly?)"); |
||
920 | +} |
||
921 | + |
||
922 | +/* Initiates a programmed read into the read FIFO. It is up to the caller to |
||
923 | + * read data from the FIFO - either via paced DMA transfer, |
||
924 | + * or polling SMICS_RXD to check whether data is available. |
||
925 | + * SMICS_ACTIVE will go low upon completion. */ |
||
926 | +static void smi_init_programmed_read(struct bcm2835_smi_instance *inst, |
||
927 | + int num_transfers) |
||
928 | +{ |
||
929 | + int smics_temp; |
||
930 | + |
||
931 | + /* Disable the peripheral: */ |
||
932 | + smics_temp = read_smi_reg(inst, SMICS) & ~(SMICS_ENABLE | SMICS_WRITE); |
||
933 | + write_smi_reg(inst, smics_temp, SMICS); |
||
934 | + while (read_smi_reg(inst, SMICS) & SMICS_ENABLE) |
||
935 | + ; |
||
936 | + |
||
937 | + /* Program the transfer count: */ |
||
938 | + write_smi_reg(inst, num_transfers, SMIL); |
||
939 | + |
||
940 | + /* re-enable and start: */ |
||
941 | + smics_temp |= SMICS_ENABLE; |
||
942 | + write_smi_reg(inst, smics_temp, SMICS); |
||
943 | + smics_temp |= SMICS_CLEAR; |
||
944 | + /* Just to be certain: */ |
||
945 | + mb(); |
||
946 | + while (read_smi_reg(inst, SMICS) & SMICS_ACTIVE) |
||
947 | + ; |
||
948 | + write_smi_reg(inst, smics_temp, SMICS); |
||
949 | + smics_temp |= SMICS_START; |
||
950 | + write_smi_reg(inst, smics_temp, SMICS); |
||
951 | +} |
||
952 | + |
||
953 | +/* Initiates a programmed write sequence, using data from the write FIFO. |
||
954 | + * It is up to the caller to initiate a DMA transfer before calling, |
||
955 | + * or use another method to keep the write FIFO topped up. |
||
956 | + * SMICS_ACTIVE will go low upon completion. |
||
957 | + */ |
||
958 | +static void smi_init_programmed_write(struct bcm2835_smi_instance *inst, |
||
959 | + int num_transfers) |
||
960 | +{ |
||
961 | + int smics_temp; |
||
962 | + |
||
963 | + /* Disable the peripheral: */ |
||
964 | + smics_temp = read_smi_reg(inst, SMICS) & ~SMICS_ENABLE; |
||
965 | + write_smi_reg(inst, smics_temp, SMICS); |
||
966 | + while (read_smi_reg(inst, SMICS) & SMICS_ENABLE) |
||
967 | + ; |
||
968 | + |
||
969 | + /* Program the transfer count: */ |
||
970 | + write_smi_reg(inst, num_transfers, SMIL); |
||
971 | + |
||
972 | + /* setup, re-enable and start: */ |
||
973 | + smics_temp |= SMICS_WRITE | SMICS_ENABLE; |
||
974 | + write_smi_reg(inst, smics_temp, SMICS); |
||
975 | + smics_temp |= SMICS_START; |
||
976 | + write_smi_reg(inst, smics_temp, SMICS); |
||
977 | +} |
||
978 | + |
||
979 | +/* Initiate a read and then poll FIFO for data, reading out as it appears. */ |
||
980 | +static void smi_read_fifo(struct bcm2835_smi_instance *inst, |
||
981 | + uint32_t *dest, int n_bytes) |
||
982 | +{ |
||
983 | + if (read_smi_reg(inst, SMICS) & SMICS_RXD) { |
||
984 | + smi_dump_context_labelled(inst, |
||
985 | + "WARNING: read FIFO not empty at start of read call."); |
||
986 | + while (read_smi_reg(inst, SMICS)) |
||
987 | + ; |
||
988 | + } |
||
989 | + |
||
990 | + /* Dispatch the read: */ |
||
991 | + if (inst->settings.data_width == SMI_WIDTH_8BIT) |
||
992 | + smi_init_programmed_read(inst, n_bytes); |
||
993 | + else if (inst->settings.data_width == SMI_WIDTH_16BIT) |
||
994 | + smi_init_programmed_read(inst, n_bytes / 2); |
||
995 | + else { |
||
996 | + dev_err(inst->dev, "Unsupported data width for read."); |
||
997 | + return; |
||
998 | + } |
||
999 | + |
||
1000 | + /* Poll FIFO to keep it empty */ |
||
1001 | + while (!(read_smi_reg(inst, SMICS) & SMICS_DONE)) |
||
1002 | + if (read_smi_reg(inst, SMICS) & SMICS_RXD) |
||
1003 | + *dest++ = read_smi_reg(inst, SMID); |
||
1004 | + |
||
1005 | + /* Ensure that the FIFO is emptied */ |
||
1006 | + if (read_smi_reg(inst, SMICS) & SMICS_RXD) { |
||
1007 | + int fifo_count; |
||
1008 | + |
||
1009 | + fifo_count = GET_BIT_FIELD(read_smi_reg(inst, SMIFD), |
||
1010 | + SMIFD_FCNT); |
||
1011 | + while (fifo_count--) |
||
1012 | + *dest++ = read_smi_reg(inst, SMID); |
||
1013 | + } |
||
1014 | + |
||
1015 | + if (!(read_smi_reg(inst, SMICS) & SMICS_DONE)) |
||
1016 | + smi_dump_context_labelled(inst, |
||
1017 | + "WARNING: transaction finished but done bit not set."); |
||
1018 | + |
||
1019 | + if (read_smi_reg(inst, SMICS) & SMICS_RXD) |
||
1020 | + smi_dump_context_labelled(inst, |
||
1021 | + "WARNING: read FIFO not empty at end of read call."); |
||
1022 | + |
||
1023 | +} |
||
1024 | + |
||
1025 | +/* Initiate a write, and then keep the FIFO topped up. */ |
||
1026 | +static void smi_write_fifo(struct bcm2835_smi_instance *inst, |
||
1027 | + uint32_t *src, int n_bytes) |
||
1028 | +{ |
||
1029 | + int i, timeout = 0; |
||
1030 | + |
||
1031 | + /* Empty FIFOs if not already so */ |
||
1032 | + if (!(read_smi_reg(inst, SMICS) & SMICS_TXE)) { |
||
1033 | + smi_dump_context_labelled(inst, |
||
1034 | + "WARNING: write fifo not empty at start of write call."); |
||
1035 | + write_smi_reg(inst, read_smi_reg(inst, SMICS) | SMICS_CLEAR, |
||
1036 | + SMICS); |
||
1037 | + } |
||
1038 | + |
||
1039 | + /* Initiate the transfer */ |
||
1040 | + if (inst->settings.data_width == SMI_WIDTH_8BIT) |
||
1041 | + smi_init_programmed_write(inst, n_bytes); |
||
1042 | + else if (inst->settings.data_width == SMI_WIDTH_16BIT) |
||
1043 | + smi_init_programmed_write(inst, n_bytes / 2); |
||
1044 | + else { |
||
1045 | + dev_err(inst->dev, "Unsupported data width for write."); |
||
1046 | + return; |
||
1047 | + } |
||
1048 | + /* Fill the FIFO: */ |
||
1049 | + for (i = 0; i < (n_bytes - 1) / 4 + 1; ++i) { |
||
1050 | + while (!(read_smi_reg(inst, SMICS) & SMICS_TXD)) |
||
1051 | + ; |
||
1052 | + write_smi_reg(inst, *src++, SMID); |
||
1053 | + } |
||
1054 | + /* Busy wait... */ |
||
1055 | + while (!(read_smi_reg(inst, SMICS) & SMICS_DONE) && ++timeout < |
||
1056 | + 1000000) |
||
1057 | + ; |
||
1058 | + if (timeout >= 1000000) |
||
1059 | + smi_dump_context_labelled(inst, |
||
1060 | + "Timed out on write operation!"); |
||
1061 | + if (!(read_smi_reg(inst, SMICS) & SMICS_TXE)) |
||
1062 | + smi_dump_context_labelled(inst, |
||
1063 | + "WARNING: FIFO not empty at end of write operation."); |
||
1064 | +} |
||
1065 | + |
||
1066 | +/**************************************************************************** |
||
1067 | +* |
||
1068 | +* SMI DMA operations |
||
1069 | +* |
||
1070 | +***************************************************************************/ |
||
1071 | + |
||
1072 | +/* Disable SMI and put it into the correct direction before doing DMA setup. |
||
1073 | + Stops spurious DREQs during setup. Peripheral is re-enabled by init_*() */ |
||
1074 | +static void smi_disable(struct bcm2835_smi_instance *inst, |
||
1075 | + enum dma_transfer_direction direction) |
||
1076 | +{ |
||
1077 | + int smics_temp = read_smi_reg(inst, SMICS) & ~SMICS_ENABLE; |
||
1078 | + |
||
1079 | + if (direction == DMA_DEV_TO_MEM) |
||
1080 | + smics_temp &= ~SMICS_WRITE; |
||
1081 | + else |
||
1082 | + smics_temp |= SMICS_WRITE; |
||
1083 | + write_smi_reg(inst, smics_temp, SMICS); |
||
1084 | + while (read_smi_reg(inst, SMICS) & SMICS_ACTIVE) |
||
1085 | + ; |
||
1086 | +} |
||
1087 | + |
||
1088 | +static struct scatterlist *smi_scatterlist_from_buffer( |
||
1089 | + struct bcm2835_smi_instance *inst, |
||
1090 | + dma_addr_t buf, |
||
1091 | + size_t len, |
||
1092 | + struct scatterlist *sg) |
||
1093 | +{ |
||
1094 | + sg_init_table(sg, 1); |
||
1095 | + sg_dma_address(sg) = buf; |
||
1096 | + sg_dma_len(sg) = len; |
||
1097 | + return sg; |
||
1098 | +} |
||
1099 | + |
||
1100 | +static void smi_dma_callback_user_copy(void *param) |
||
1101 | +{ |
||
1102 | + /* Notify the bottom half that a chunk is ready for user copy */ |
||
1103 | + struct bcm2835_smi_instance *inst = |
||
1104 | + (struct bcm2835_smi_instance *)param; |
||
1105 | + |
||
1106 | + up(&inst->bounce.callback_sem); |
||
1107 | +} |
||
1108 | + |
||
1109 | +/* Creates a descriptor, assigns the given callback, and submits the |
||
1110 | + descriptor to dmaengine. Does not block - can queue up multiple |
||
1111 | + descriptors and then wait for them all to complete. |
||
1112 | + sg_len is the number of control blocks, NOT the number of bytes. |
||
1113 | + dir can be DMA_MEM_TO_DEV or DMA_DEV_TO_MEM. |
||
1114 | + callback can be NULL - in this case it is not called. */ |
||
1115 | +static inline struct dma_async_tx_descriptor *smi_dma_submit_sgl( |
||
1116 | + struct bcm2835_smi_instance *inst, |
||
1117 | + struct scatterlist *sgl, |
||
1118 | + size_t sg_len, |
||
1119 | + enum dma_transfer_direction dir, |
||
1120 | + dma_async_tx_callback callback) |
||
1121 | +{ |
||
1122 | + struct dma_async_tx_descriptor *desc; |
||
1123 | + |
||
1124 | + desc = dmaengine_prep_slave_sg(inst->dma_chan, |
||
1125 | + sgl, |
||
1126 | + sg_len, |
||
1127 | + dir, |
||
1128 | + DMA_PREP_INTERRUPT | DMA_CTRL_ACK | |
||
1129 | + DMA_PREP_FENCE); |
||
1130 | + if (!desc) { |
||
1131 | + dev_err(inst->dev, "read_sgl: dma slave preparation failed!"); |
||
1132 | + write_smi_reg(inst, read_smi_reg(inst, SMICS) & ~SMICS_ACTIVE, |
||
1133 | + SMICS); |
||
1134 | + while (read_smi_reg(inst, SMICS) & SMICS_ACTIVE) |
||
1135 | + cpu_relax(); |
||
1136 | + write_smi_reg(inst, read_smi_reg(inst, SMICS) | SMICS_ACTIVE, |
||
1137 | + SMICS); |
||
1138 | + return NULL; |
||
1139 | + } |
||
1140 | + desc->callback = callback; |
||
1141 | + desc->callback_param = inst; |
||
1142 | + if (dmaengine_submit(desc) < 0) |
||
1143 | + return NULL; |
||
1144 | + return desc; |
||
1145 | +} |
||
1146 | + |
||
1147 | +/* NB this function blocks until the transfer is complete */ |
||
1148 | +static void |
||
1149 | +smi_dma_read_sgl(struct bcm2835_smi_instance *inst, |
||
1150 | + struct scatterlist *sgl, size_t sg_len, size_t n_bytes) |
||
1151 | +{ |
||
1152 | + struct dma_async_tx_descriptor *desc; |
||
1153 | + |
||
1154 | + /* Disable SMI and set to read before dispatching DMA - if SMI is in |
||
1155 | + * write mode and TX fifo is empty, it will generate a DREQ which may |
||
1156 | + * cause the read DMA to complete before the SMI read command is even |
||
1157 | + * dispatched! We want to dispatch DMA before SMI read so that reading |
||
1158 | + * is gapless, for logic analyser. |
||
1159 | + */ |
||
1160 | + |
||
1161 | + smi_disable(inst, DMA_DEV_TO_MEM); |
||
1162 | + |
||
1163 | + desc = smi_dma_submit_sgl(inst, sgl, sg_len, DMA_DEV_TO_MEM, NULL); |
||
1164 | + dma_async_issue_pending(inst->dma_chan); |
||
1165 | + |
||
1166 | + if (inst->settings.data_width == SMI_WIDTH_8BIT) |
||
1167 | + smi_init_programmed_read(inst, n_bytes); |
||
1168 | + else |
||
1169 | + smi_init_programmed_read(inst, n_bytes / 2); |
||
1170 | + |
||
1171 | + if (dma_wait_for_async_tx(desc) == DMA_ERROR) |
||
1172 | + smi_dump_context_labelled(inst, "DMA timeout!"); |
||
1173 | +} |
||
1174 | + |
||
1175 | +static void |
||
1176 | +smi_dma_write_sgl(struct bcm2835_smi_instance *inst, |
||
1177 | + struct scatterlist *sgl, size_t sg_len, size_t n_bytes) |
||
1178 | +{ |
||
1179 | + struct dma_async_tx_descriptor *desc; |
||
1180 | + |
||
1181 | + if (inst->settings.data_width == SMI_WIDTH_8BIT) |
||
1182 | + smi_init_programmed_write(inst, n_bytes); |
||
1183 | + else |
||
1184 | + smi_init_programmed_write(inst, n_bytes / 2); |
||
1185 | + |
||
1186 | + desc = smi_dma_submit_sgl(inst, sgl, sg_len, DMA_MEM_TO_DEV, NULL); |
||
1187 | + dma_async_issue_pending(inst->dma_chan); |
||
1188 | + |
||
1189 | + if (dma_wait_for_async_tx(desc) == DMA_ERROR) |
||
1190 | + smi_dump_context_labelled(inst, "DMA timeout!"); |
||
1191 | + else |
||
1192 | + /* Wait for SMI to finish our writes */ |
||
1193 | + while (!(read_smi_reg(inst, SMICS) & SMICS_DONE)) |
||
1194 | + cpu_relax(); |
||
1195 | +} |
||
1196 | + |
||
1197 | +ssize_t bcm2835_smi_user_dma( |
||
1198 | + struct bcm2835_smi_instance *inst, |
||
1199 | + enum dma_transfer_direction dma_dir, |
||
1200 | + char __user *user_ptr, size_t count, |
||
1201 | + struct bcm2835_smi_bounce_info **bounce) |
||
1202 | +{ |
||
1203 | + int chunk_no = 0, chunk_size, count_left = count; |
||
1204 | + struct scatterlist *sgl; |
||
1205 | + void (*init_trans_func)(struct bcm2835_smi_instance *, int); |
||
1206 | + |
||
1207 | + spin_lock(&inst->transaction_lock); |
||
1208 | + |
||
1209 | + if (dma_dir == DMA_DEV_TO_MEM) |
||
1210 | + init_trans_func = smi_init_programmed_read; |
||
1211 | + else |
||
1212 | + init_trans_func = smi_init_programmed_write; |
||
1213 | + |
||
1214 | + smi_disable(inst, dma_dir); |
||
1215 | + |
||
1216 | + sema_init(&inst->bounce.callback_sem, 0); |
||
1217 | + if (bounce) |
||
1218 | + *bounce = &inst->bounce; |
||
1219 | + while (count_left) { |
||
1220 | + chunk_size = count_left > DMA_BOUNCE_BUFFER_SIZE ? |
||
1221 | + DMA_BOUNCE_BUFFER_SIZE : count_left; |
||
1222 | + if (chunk_size == DMA_BOUNCE_BUFFER_SIZE) { |
||
1223 | + sgl = |
||
1224 | + &inst->bounce.sgl[chunk_no % DMA_BOUNCE_BUFFER_COUNT]; |
||
1225 | + } else { |
||
1226 | + sgl = smi_scatterlist_from_buffer( |
||
1227 | + inst, |
||
1228 | + inst->bounce.phys[ |
||
1229 | + chunk_no % DMA_BOUNCE_BUFFER_COUNT], |
||
1230 | + chunk_size, |
||
1231 | + &inst->buffer_sgl); |
||
1232 | + } |
||
1233 | + |
||
1234 | + if (!smi_dma_submit_sgl(inst, sgl, 1, dma_dir, |
||
1235 | + smi_dma_callback_user_copy |
||
1236 | + )) { |
||
1237 | + dev_err(inst->dev, "sgl submit failed"); |
||
1238 | + count = 0; |
||
1239 | + goto out; |
||
1240 | + } |
||
1241 | + count_left -= chunk_size; |
||
1242 | + chunk_no++; |
||
1243 | + } |
||
1244 | + dma_async_issue_pending(inst->dma_chan); |
||
1245 | + |
||
1246 | + if (inst->settings.data_width == SMI_WIDTH_8BIT) |
||
1247 | + init_trans_func(inst, count); |
||
1248 | + else if (inst->settings.data_width == SMI_WIDTH_16BIT) |
||
1249 | + init_trans_func(inst, count / 2); |
||
1250 | +out: |
||
1251 | + spin_unlock(&inst->transaction_lock); |
||
1252 | + return count; |
||
1253 | +} |
||
1254 | +EXPORT_SYMBOL(bcm2835_smi_user_dma); |
||
1255 | + |
||
1256 | + |
||
1257 | +/**************************************************************************** |
||
1258 | +* |
||
1259 | +* High level buffer transfer functions - for use by other drivers |
||
1260 | +* |
||
1261 | +***************************************************************************/ |
||
1262 | + |
||
1263 | +/* Buffer must be physically contiguous - i.e. kmalloc, not vmalloc! */ |
||
1264 | +void bcm2835_smi_write_buf( |
||
1265 | + struct bcm2835_smi_instance *inst, |
||
1266 | + const void *buf, size_t n_bytes) |
||
1267 | +{ |
||
1268 | + int odd_bytes = n_bytes & 0x3; |
||
1269 | + |
||
1270 | + n_bytes -= odd_bytes; |
||
1271 | + |
||
1272 | + spin_lock(&inst->transaction_lock); |
||
1273 | + |
||
1274 | + if (n_bytes > DMA_THRESHOLD_BYTES) { |
||
1275 | + dma_addr_t phy_addr = dma_map_single( |
||
1276 | + inst->dev, |
||
1277 | + (void *)buf, |
||
1278 | + n_bytes, |
||
1279 | + DMA_MEM_TO_DEV); |
||
1280 | + struct scatterlist *sgl = |
||
1281 | + smi_scatterlist_from_buffer(inst, phy_addr, n_bytes, |
||
1282 | + &inst->buffer_sgl); |
||
1283 | + |
||
1284 | + if (!sgl) { |
||
1285 | + smi_dump_context_labelled(inst, |
||
1286 | + "Error: could not create scatterlist for write!"); |
||
1287 | + goto out; |
||
1288 | + } |
||
1289 | + smi_dma_write_sgl(inst, sgl, 1, n_bytes); |
||
1290 | + |
||
1291 | + dma_unmap_single |
||
1292 | + (inst->dev, phy_addr, n_bytes, DMA_MEM_TO_DEV); |
||
1293 | + } else if (n_bytes) { |
||
1294 | + smi_write_fifo(inst, (uint32_t *) buf, n_bytes); |
||
1295 | + } |
||
1296 | + buf += n_bytes; |
||
1297 | + |
||
1298 | + if (inst->settings.data_width == SMI_WIDTH_8BIT) { |
||
1299 | + while (odd_bytes--) |
||
1300 | + smi_write_single_word(inst, *(uint8_t *) (buf++)); |
||
1301 | + } else { |
||
1302 | + while (odd_bytes >= 2) { |
||
1303 | + smi_write_single_word(inst, *(uint16_t *)buf); |
||
1304 | + buf += 2; |
||
1305 | + odd_bytes -= 2; |
||
1306 | + } |
||
1307 | + if (odd_bytes) { |
||
1308 | + /* Reading an odd number of bytes on a 16 bit bus is |
||
1309 | + a user bug. It's kinder to fail early and tell them |
||
1310 | + than to e.g. transparently give them the bottom byte |
||
1311 | + of a 16 bit transfer. */ |
||
1312 | + dev_err(inst->dev, |
||
1313 | + "WARNING: odd number of bytes specified for wide transfer."); |
||
1314 | + dev_err(inst->dev, |
||
1315 | + "At least one byte dropped as a result."); |
||
1316 | + dump_stack(); |
||
1317 | + } |
||
1318 | + } |
||
1319 | +out: |
||
1320 | + spin_unlock(&inst->transaction_lock); |
||
1321 | +} |
||
1322 | +EXPORT_SYMBOL(bcm2835_smi_write_buf); |
||
1323 | + |
||
1324 | +void bcm2835_smi_read_buf(struct bcm2835_smi_instance *inst, |
||
1325 | + void *buf, size_t n_bytes) |
||
1326 | +{ |
||
1327 | + |
||
1328 | + /* SMI is inherently 32-bit, which causes surprising amounts of mess |
||
1329 | + for bytes % 4 != 0. Easiest to avoid this mess altogether |
||
1330 | + by handling remainder separately. */ |
||
1331 | + int odd_bytes = n_bytes & 0x3; |
||
1332 | + |
||
1333 | + spin_lock(&inst->transaction_lock); |
||
1334 | + n_bytes -= odd_bytes; |
||
1335 | + if (n_bytes > DMA_THRESHOLD_BYTES) { |
||
1336 | + dma_addr_t phy_addr = dma_map_single(inst->dev, |
||
1337 | + buf, n_bytes, |
||
1338 | + DMA_DEV_TO_MEM); |
||
1339 | + struct scatterlist *sgl = smi_scatterlist_from_buffer( |
||
1340 | + inst, phy_addr, n_bytes, |
||
1341 | + &inst->buffer_sgl); |
||
1342 | + if (!sgl) { |
||
1343 | + smi_dump_context_labelled(inst, |
||
1344 | + "Error: could not create scatterlist for read!"); |
||
1345 | + goto out; |
||
1346 | + } |
||
1347 | + smi_dma_read_sgl(inst, sgl, 1, n_bytes); |
||
1348 | + dma_unmap_single(inst->dev, phy_addr, n_bytes, DMA_DEV_TO_MEM); |
||
1349 | + } else if (n_bytes) { |
||
1350 | + smi_read_fifo(inst, (uint32_t *)buf, n_bytes); |
||
1351 | + } |
||
1352 | + buf += n_bytes; |
||
1353 | + |
||
1354 | + if (inst->settings.data_width == SMI_WIDTH_8BIT) { |
||
1355 | + while (odd_bytes--) |
||
1356 | + *((uint8_t *) (buf++)) = smi_read_single_word(inst); |
||
1357 | + } else { |
||
1358 | + while (odd_bytes >= 2) { |
||
1359 | + *(uint16_t *) buf = smi_read_single_word(inst); |
||
1360 | + buf += 2; |
||
1361 | + odd_bytes -= 2; |
||
1362 | + } |
||
1363 | + if (odd_bytes) { |
||
1364 | + dev_err(inst->dev, |
||
1365 | + "WARNING: odd number of bytes specified for wide transfer."); |
||
1366 | + dev_err(inst->dev, |
||
1367 | + "At least one byte dropped as a result."); |
||
1368 | + dump_stack(); |
||
1369 | + } |
||
1370 | + } |
||
1371 | +out: |
||
1372 | + spin_unlock(&inst->transaction_lock); |
||
1373 | +} |
||
1374 | +EXPORT_SYMBOL(bcm2835_smi_read_buf); |
||
1375 | + |
||
1376 | +void bcm2835_smi_set_address(struct bcm2835_smi_instance *inst, |
||
1377 | + unsigned int address) |
||
1378 | +{ |
||
1379 | + spin_lock(&inst->transaction_lock); |
||
1380 | + smi_set_address(inst, address); |
||
1381 | + spin_unlock(&inst->transaction_lock); |
||
1382 | +} |
||
1383 | +EXPORT_SYMBOL(bcm2835_smi_set_address); |
||
1384 | + |
||
1385 | +struct bcm2835_smi_instance *bcm2835_smi_get(struct device_node *node) |
||
1386 | +{ |
||
1387 | + struct platform_device *pdev; |
||
1388 | + |
||
1389 | + if (!node) |
||
1390 | + return NULL; |
||
1391 | + |
||
1392 | + pdev = of_find_device_by_node(node); |
||
1393 | + if (!pdev) |
||
1394 | + return NULL; |
||
1395 | + |
||
1396 | + return platform_get_drvdata(pdev); |
||
1397 | +} |
||
1398 | +EXPORT_SYMBOL(bcm2835_smi_get); |
||
1399 | + |
||
1400 | +/**************************************************************************** |
||
1401 | +* |
||
1402 | +* bcm2835_smi_probe - called when the driver is loaded. |
||
1403 | +* |
||
1404 | +***************************************************************************/ |
||
1405 | + |
||
1406 | +static int bcm2835_smi_dma_setup(struct bcm2835_smi_instance *inst) |
||
1407 | +{ |
||
1408 | + int i, rv = 0; |
||
1409 | + |
||
1410 | + inst->dma_chan = dma_request_slave_channel(inst->dev, "rx-tx"); |
||
1411 | + |
||
1412 | + inst->dma_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; |
||
1413 | + inst->dma_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; |
||
1414 | + inst->dma_config.src_addr = inst->smi_regs_busaddr + SMID; |
||
1415 | + inst->dma_config.dst_addr = inst->dma_config.src_addr; |
||
1416 | + /* Direction unimportant - always overridden by prep_slave_sg */ |
||
1417 | + inst->dma_config.direction = DMA_DEV_TO_MEM; |
||
1418 | + dmaengine_slave_config(inst->dma_chan, &inst->dma_config); |
||
1419 | + /* Alloc and map bounce buffers */ |
||
1420 | + for (i = 0; i < DMA_BOUNCE_BUFFER_COUNT; ++i) { |
||
1421 | + inst->bounce.buffer[i] = |
||
1422 | + dmam_alloc_coherent(inst->dev, DMA_BOUNCE_BUFFER_SIZE, |
||
1423 | + &inst->bounce.phys[i], |
||
1424 | + GFP_KERNEL); |
||
1425 | + if (!inst->bounce.buffer[i]) { |
||
1426 | + dev_err(inst->dev, "Could not allocate buffer!"); |
||
1427 | + rv = -ENOMEM; |
||
1428 | + break; |
||
1429 | + } |
||
1430 | + smi_scatterlist_from_buffer( |
||
1431 | + inst, |
||
1432 | + inst->bounce.phys[i], |
||
1433 | + DMA_BOUNCE_BUFFER_SIZE, |
||
1434 | + &inst->bounce.sgl[i] |
||
1435 | + ); |
||
1436 | + } |
||
1437 | + |
||
1438 | + return rv; |
||
1439 | +} |
||
1440 | + |
||
1441 | +static int bcm2835_smi_probe(struct platform_device *pdev) |
||
1442 | +{ |
||
1443 | + int err; |
||
1444 | + struct device *dev = &pdev->dev; |
||
1445 | + struct device_node *node = dev->of_node; |
||
1446 | + struct resource *ioresource; |
||
1447 | + struct bcm2835_smi_instance *inst; |
||
1448 | + |
||
1449 | + /* Allocate buffers and instance data */ |
||
1450 | + |
||
1451 | + inst = devm_kzalloc(dev, sizeof(struct bcm2835_smi_instance), |
||
1452 | + GFP_KERNEL); |
||
1453 | + |
||
1454 | + if (!inst) |
||
1455 | + return -ENOMEM; |
||
1456 | + |
||
1457 | + inst->dev = dev; |
||
1458 | + spin_lock_init(&inst->transaction_lock); |
||
1459 | + |
||
1460 | + /* We require device tree support */ |
||
1461 | + if (!node) |
||
1462 | + return -EINVAL; |
||
1463 | + |
||
1464 | + ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
||
1465 | + inst->smi_regs_ptr = devm_ioremap_resource(dev, ioresource); |
||
1466 | + ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
||
1467 | + inst->cm_smi_regs_ptr = devm_ioremap_resource(dev, ioresource); |
||
1468 | + inst->smi_regs_busaddr = be32_to_cpu( |
||
1469 | + *of_get_address(node, 0, NULL, NULL)); |
||
1470 | + of_property_read_u32(node, |
||
1471 | + "brcm,smi-clock-source", |
||
1472 | + &inst->clock_source); |
||
1473 | + of_property_read_u32(node, |
||
1474 | + "brcm,smi-clock-divisor", |
||
1475 | + &inst->clock_divisor); |
||
1476 | + |
||
1477 | + err = bcm2835_smi_dma_setup(inst); |
||
1478 | + if (err) |
||
1479 | + return err; |
||
1480 | + |
||
1481 | + /* Finally, do peripheral setup */ |
||
1482 | + |
||
1483 | + smi_setup_clock(inst); |
||
1484 | + smi_setup_regs(inst); |
||
1485 | + |
||
1486 | + platform_set_drvdata(pdev, inst); |
||
1487 | + |
||
1488 | + dev_info(inst->dev, "initialised"); |
||
1489 | + |
||
1490 | + return 0; |
||
1491 | +} |
||
1492 | + |
||
1493 | +/**************************************************************************** |
||
1494 | +* |
||
1495 | +* bcm2835_smi_remove - called when the driver is unloaded. |
||
1496 | +* |
||
1497 | +***************************************************************************/ |
||
1498 | + |
||
1499 | +static int bcm2835_smi_remove(struct platform_device *pdev) |
||
1500 | +{ |
||
1501 | + struct bcm2835_smi_instance *inst = platform_get_drvdata(pdev); |
||
1502 | + struct device *dev = inst->dev; |
||
1503 | + |
||
1504 | + dev_info(dev, "SMI device removed - OK"); |
||
1505 | + return 0; |
||
1506 | +} |
||
1507 | + |
||
1508 | +/**************************************************************************** |
||
1509 | +* |
||
1510 | +* Register the driver with device tree |
||
1511 | +* |
||
1512 | +***************************************************************************/ |
||
1513 | + |
||
1514 | +static const struct of_device_id bcm2835_smi_of_match[] = { |
||
1515 | + {.compatible = "brcm,bcm2835-smi",}, |
||
1516 | + { /* sentinel */ }, |
||
1517 | +}; |
||
1518 | + |
||
1519 | +MODULE_DEVICE_TABLE(of, bcm2835_smi_of_match); |
||
1520 | + |
||
1521 | +static struct platform_driver bcm2835_smi_driver = { |
||
1522 | + .probe = bcm2835_smi_probe, |
||
1523 | + .remove = bcm2835_smi_remove, |
||
1524 | + .driver = { |
||
1525 | + .name = DRIVER_NAME, |
||
1526 | + .owner = THIS_MODULE, |
||
1527 | + .of_match_table = bcm2835_smi_of_match, |
||
1528 | + }, |
||
1529 | +}; |
||
1530 | + |
||
1531 | +module_platform_driver(bcm2835_smi_driver); |
||
1532 | + |
||
1533 | +MODULE_ALIAS("platform:smi-bcm2835"); |
||
1534 | +MODULE_LICENSE("GPL"); |
||
1535 | +MODULE_DESCRIPTION("Device driver for BCM2835's secondary memory interface"); |
||
1536 | +MODULE_AUTHOR("Luke Wren <luke@raspberrypi.org>"); |
||
1537 | --- /dev/null |
||
1538 | +++ b/include/linux/broadcom/bcm2835_smi.h |
||
1539 | @@ -0,0 +1,391 @@ |
||
1540 | +/** |
||
1541 | + * Declarations and definitions for Broadcom's Secondary Memory Interface |
||
1542 | + * |
||
1543 | + * Written by Luke Wren <luke@raspberrypi.org> |
||
1544 | + * Copyright (c) 2015, Raspberry Pi (Trading) Ltd. |
||
1545 | + * Copyright (c) 2010-2012 Broadcom. All rights reserved. |
||
1546 | + * |
||
1547 | + * Redistribution and use in source and binary forms, with or without |
||
1548 | + * modification, are permitted provided that the following conditions |
||
1549 | + * are met: |
||
1550 | + * 1. Redistributions of source code must retain the above copyright |
||
1551 | + * notice, this list of conditions, and the following disclaimer, |
||
1552 | + * without modification. |
||
1553 | + * 2. Redistributions in binary form must reproduce the above copyright |
||
1554 | + * notice, this list of conditions and the following disclaimer in the |
||
1555 | + * documentation and/or other materials provided with the distribution. |
||
1556 | + * 3. The names of the above-listed copyright holders may not be used |
||
1557 | + * to endorse or promote products derived from this software without |
||
1558 | + * specific prior written permission. |
||
1559 | + * |
||
1560 | + * ALTERNATIVELY, this software may be distributed under the terms of the |
||
1561 | + * GNU General Public License ("GPL") version 2, as published by the Free |
||
1562 | + * Software Foundation. |
||
1563 | + * |
||
1564 | + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
||
1565 | + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
||
1566 | + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||
1567 | + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||
1568 | + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||
1569 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||
1570 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||
1571 | + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||
1572 | + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||
1573 | + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||
1574 | + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
1575 | + */ |
||
1576 | + |
||
1577 | +#ifndef BCM2835_SMI_H |
||
1578 | +#define BCM2835_SMI_H |
||
1579 | + |
||
1580 | +#include <linux/ioctl.h> |
||
1581 | + |
||
1582 | +#ifndef __KERNEL__ |
||
1583 | +#include <stdint.h> |
||
1584 | +#include <stdbool.h> |
||
1585 | +#endif |
||
1586 | + |
||
1587 | +#define BCM2835_SMI_IOC_MAGIC 0x1 |
||
1588 | +#define BCM2835_SMI_INVALID_HANDLE (~0) |
||
1589 | + |
||
1590 | +/* IOCTLs 0x100...0x1ff are not device-specific - we can use them */ |
||
1591 | +#define BCM2835_SMI_IOC_GET_SETTINGS _IO(BCM2835_SMI_IOC_MAGIC, 0) |
||
1592 | +#define BCM2835_SMI_IOC_WRITE_SETTINGS _IO(BCM2835_SMI_IOC_MAGIC, 1) |
||
1593 | +#define BCM2835_SMI_IOC_ADDRESS _IO(BCM2835_SMI_IOC_MAGIC, 2) |
||
1594 | +#define BCM2835_SMI_IOC_MAX 2 |
||
1595 | + |
||
1596 | +#define SMI_WIDTH_8BIT 0 |
||
1597 | +#define SMI_WIDTH_16BIT 1 |
||
1598 | +#define SMI_WIDTH_9BIT 2 |
||
1599 | +#define SMI_WIDTH_18BIT 3 |
||
1600 | + |
||
1601 | +/* max number of bytes where DMA will not be used */ |
||
1602 | +#define DMA_THRESHOLD_BYTES 128 |
||
1603 | +#define DMA_BOUNCE_BUFFER_SIZE (1024 * 1024 / 2) |
||
1604 | +#define DMA_BOUNCE_BUFFER_COUNT 3 |
||
1605 | + |
||
1606 | + |
||
1607 | +struct smi_settings { |
||
1608 | + int data_width; |
||
1609 | + /* Whether or not to pack multiple SMI transfers into a |
||
1610 | + single 32 bit FIFO word */ |
||
1611 | + bool pack_data; |
||
1612 | + |
||
1613 | + /* Timing for reads (writes the same but for WE) |
||
1614 | + * |
||
1615 | + * OE ----------+ +-------------------- |
||
1616 | + * | | |
||
1617 | + * +----------+ |
||
1618 | + * SD -<==============================>----------- |
||
1619 | + * SA -<=========================================>- |
||
1620 | + * <-setup-> <-strobe -> <-hold -> <- pace -> |
||
1621 | + */ |
||
1622 | + |
||
1623 | + int read_setup_time; |
||
1624 | + int read_hold_time; |
||
1625 | + int read_pace_time; |
||
1626 | + int read_strobe_time; |
||
1627 | + |
||
1628 | + int write_setup_time; |
||
1629 | + int write_hold_time; |
||
1630 | + int write_pace_time; |
||
1631 | + int write_strobe_time; |
||
1632 | + |
||
1633 | + bool dma_enable; /* DREQs */ |
||
1634 | + bool dma_passthrough_enable; /* External DREQs */ |
||
1635 | + int dma_read_thresh; |
||
1636 | + int dma_write_thresh; |
||
1637 | + int dma_panic_read_thresh; |
||
1638 | + int dma_panic_write_thresh; |
||
1639 | +}; |
||
1640 | + |
||
1641 | +/**************************************************************************** |
||
1642 | +* |
||
1643 | +* Declare exported SMI functions |
||
1644 | +* |
||
1645 | +***************************************************************************/ |
||
1646 | + |
||
1647 | +#ifdef __KERNEL__ |
||
1648 | + |
||
1649 | +#include <linux/dmaengine.h> /* for enum dma_transfer_direction */ |
||
1650 | +#include <linux/of.h> |
||
1651 | +#include <linux/semaphore.h> |
||
1652 | + |
||
1653 | +struct bcm2835_smi_instance; |
||
1654 | + |
||
1655 | +struct bcm2835_smi_bounce_info { |
||
1656 | + struct semaphore callback_sem; |
||
1657 | + void *buffer[DMA_BOUNCE_BUFFER_COUNT]; |
||
1658 | + dma_addr_t phys[DMA_BOUNCE_BUFFER_COUNT]; |
||
1659 | + struct scatterlist sgl[DMA_BOUNCE_BUFFER_COUNT]; |
||
1660 | +}; |
||
1661 | + |
||
1662 | + |
||
1663 | +void bcm2835_smi_set_regs_from_settings(struct bcm2835_smi_instance *); |
||
1664 | + |
||
1665 | +struct smi_settings *bcm2835_smi_get_settings_from_regs( |
||
1666 | + struct bcm2835_smi_instance *inst); |
||
1667 | + |
||
1668 | +void bcm2835_smi_write_buf( |
||
1669 | + struct bcm2835_smi_instance *inst, |
||
1670 | + const void *buf, |
||
1671 | + size_t n_bytes); |
||
1672 | + |
||
1673 | +void bcm2835_smi_read_buf( |
||
1674 | + struct bcm2835_smi_instance *inst, |
||
1675 | + void *buf, |
||
1676 | + size_t n_bytes); |
||
1677 | + |
||
1678 | +void bcm2835_smi_set_address(struct bcm2835_smi_instance *inst, |
||
1679 | + unsigned int address); |
||
1680 | + |
||
1681 | +ssize_t bcm2835_smi_user_dma( |
||
1682 | + struct bcm2835_smi_instance *inst, |
||
1683 | + enum dma_transfer_direction dma_dir, |
||
1684 | + char __user *user_ptr, |
||
1685 | + size_t count, |
||
1686 | + struct bcm2835_smi_bounce_info **bounce); |
||
1687 | + |
||
1688 | +struct bcm2835_smi_instance *bcm2835_smi_get(struct device_node *node); |
||
1689 | + |
||
1690 | +#endif /* __KERNEL__ */ |
||
1691 | + |
||
1692 | +/**************************************************************** |
||
1693 | +* |
||
1694 | +* Implementation-only declarations |
||
1695 | +* |
||
1696 | +****************************************************************/ |
||
1697 | + |
||
1698 | +#ifdef BCM2835_SMI_IMPLEMENTATION |
||
1699 | + |
||
1700 | +/* Clock manager registers for SMI clock: */ |
||
1701 | +#define CM_SMI_BASE_ADDRESS ((BCM2708_PERI_BASE) + 0x1010b0) |
||
1702 | +/* Clock manager "password" to protect registers from spurious writes */ |
||
1703 | +#define CM_PWD (0x5a << 24) |
||
1704 | + |
||
1705 | +#define CM_SMI_CTL 0x00 |
||
1706 | +#define CM_SMI_DIV 0x04 |
||
1707 | + |
||
1708 | +#define CM_SMI_CTL_FLIP (1 << 8) |
||
1709 | +#define CM_SMI_CTL_BUSY (1 << 7) |
||
1710 | +#define CM_SMI_CTL_KILL (1 << 5) |
||
1711 | +#define CM_SMI_CTL_ENAB (1 << 4) |
||
1712 | +#define CM_SMI_CTL_SRC_MASK (0xf) |
||
1713 | +#define CM_SMI_CTL_SRC_OFFS (0) |
||
1714 | + |
||
1715 | +#define CM_SMI_DIV_DIVI_MASK (0xf << 12) |
||
1716 | +#define CM_SMI_DIV_DIVI_OFFS (12) |
||
1717 | +#define CM_SMI_DIV_DIVF_MASK (0xff << 4) |
||
1718 | +#define CM_SMI_DIV_DIVF_OFFS (4) |
||
1719 | + |
||
1720 | +/* SMI register mapping:*/ |
||
1721 | +#define SMI_BASE_ADDRESS ((BCM2708_PERI_BASE) + 0x600000) |
||
1722 | + |
||
1723 | +#define SMICS 0x00 /* control + status register */ |
||
1724 | +#define SMIL 0x04 /* length/count (n external txfers) */ |
||
1725 | +#define SMIA 0x08 /* address register */ |
||
1726 | +#define SMID 0x0c /* data register */ |
||
1727 | +#define SMIDSR0 0x10 /* device 0 read settings */ |
||
1728 | +#define SMIDSW0 0x14 /* device 0 write settings */ |
||
1729 | +#define SMIDSR1 0x18 /* device 1 read settings */ |
||
1730 | +#define SMIDSW1 0x1c /* device 1 write settings */ |
||
1731 | +#define SMIDSR2 0x20 /* device 2 read settings */ |
||
1732 | +#define SMIDSW2 0x24 /* device 2 write settings */ |
||
1733 | +#define SMIDSR3 0x28 /* device 3 read settings */ |
||
1734 | +#define SMIDSW3 0x2c /* device 3 write settings */ |
||
1735 | +#define SMIDC 0x30 /* DMA control registers */ |
||
1736 | +#define SMIDCS 0x34 /* direct control/status register */ |
||
1737 | +#define SMIDA 0x38 /* direct address register */ |
||
1738 | +#define SMIDD 0x3c /* direct data registers */ |
||
1739 | +#define SMIFD 0x40 /* FIFO debug register */ |
||
1740 | + |
||
1741 | + |
||
1742 | + |
||
1743 | +/* Control and Status register bits: |
||
1744 | + * SMICS_RXF : RX fifo full: 1 when RX fifo is full |
||
1745 | + * SMICS_TXE : TX fifo empty: 1 when empty. |
||
1746 | + * SMICS_RXD : RX fifo contains data: 1 when there is data. |
||
1747 | + * SMICS_TXD : TX fifo can accept data: 1 when true. |
||
1748 | + * SMICS_RXR : RX fifo needs reading: 1 when fifo more than 3/4 full, or |
||
1749 | + * when "DONE" and fifo not emptied. |
||
1750 | + * SMICS_TXW : TX fifo needs writing: 1 when less than 1/4 full. |
||
1751 | + * SMICS_AFERR : AXI FIFO error: 1 when fifo read when empty or written |
||
1752 | + * when full. Write 1 to clear. |
||
1753 | + * SMICS_EDREQ : 1 when external DREQ received. |
||
1754 | + * SMICS_PXLDAT : Pixel data: write 1 to enable pixel transfer modes. |
||
1755 | + * SMICS_SETERR : 1 if there was an error writing to setup regs (e.g. |
||
1756 | + * tx was in progress). Write 1 to clear. |
||
1757 | + * SMICS_PVMODE : Set to 1 to enable pixel valve mode. |
||
1758 | + * SMICS_INTR : Set to 1 to enable interrupt on RX. |
||
1759 | + * SMICS_INTT : Set to 1 to enable interrupt on TX. |
||
1760 | + * SMICS_INTD : Set to 1 to enable interrupt on DONE condition. |
||
1761 | + * SMICS_TEEN : Tear effect mode enabled: Programmed transfers will wait |
||
1762 | + * for a TE trigger before writing. |
||
1763 | + * SMICS_PAD1 : Padding settings for external transfers. For writes: the |
||
1764 | + * number of bytes initially written to the TX fifo that |
||
1765 | + * SMICS_PAD0 : should be ignored. For reads: the number of bytes that will |
||
1766 | + * be read before the data, and should be dropped. |
||
1767 | + * SMICS_WRITE : Transfer direction: 1 = write to external device, 0 = read |
||
1768 | + * SMICS_CLEAR : Write 1 to clear the FIFOs. |
||
1769 | + * SMICS_START : Write 1 to start the programmed transfer. |
||
1770 | + * SMICS_ACTIVE : Reads as 1 when a programmed transfer is underway. |
||
1771 | + * SMICS_DONE : Reads as 1 when transfer finished. For RX, not set until |
||
1772 | + * FIFO emptied. |
||
1773 | + * SMICS_ENABLE : Set to 1 to enable the SMI peripheral, 0 to disable. |
||
1774 | + */ |
||
1775 | + |
||
1776 | +#define SMICS_RXF (1 << 31) |
||
1777 | +#define SMICS_TXE (1 << 30) |
||
1778 | +#define SMICS_RXD (1 << 29) |
||
1779 | +#define SMICS_TXD (1 << 28) |
||
1780 | +#define SMICS_RXR (1 << 27) |
||
1781 | +#define SMICS_TXW (1 << 26) |
||
1782 | +#define SMICS_AFERR (1 << 25) |
||
1783 | +#define SMICS_EDREQ (1 << 15) |
||
1784 | +#define SMICS_PXLDAT (1 << 14) |
||
1785 | +#define SMICS_SETERR (1 << 13) |
||
1786 | +#define SMICS_PVMODE (1 << 12) |
||
1787 | +#define SMICS_INTR (1 << 11) |
||
1788 | +#define SMICS_INTT (1 << 10) |
||
1789 | +#define SMICS_INTD (1 << 9) |
||
1790 | +#define SMICS_TEEN (1 << 8) |
||
1791 | +#define SMICS_PAD1 (1 << 7) |
||
1792 | +#define SMICS_PAD0 (1 << 6) |
||
1793 | +#define SMICS_WRITE (1 << 5) |
||
1794 | +#define SMICS_CLEAR (1 << 4) |
||
1795 | +#define SMICS_START (1 << 3) |
||
1796 | +#define SMICS_ACTIVE (1 << 2) |
||
1797 | +#define SMICS_DONE (1 << 1) |
||
1798 | +#define SMICS_ENABLE (1 << 0) |
||
1799 | + |
||
1800 | +/* Address register bits: */ |
||
1801 | + |
||
1802 | +#define SMIA_DEVICE_MASK ((1 << 9) | (1 << 8)) |
||
1803 | +#define SMIA_DEVICE_OFFS (8) |
||
1804 | +#define SMIA_ADDR_MASK (0x3f) /* bits 5 -> 0 */ |
||
1805 | +#define SMIA_ADDR_OFFS (0) |
||
1806 | + |
||
1807 | +/* DMA control register bits: |
||
1808 | + * SMIDC_DMAEN : DMA enable: set 1: DMA requests will be issued. |
||
1809 | + * SMIDC_DMAP : DMA passthrough: when set to 0, top two data pins are used by |
||
1810 | + * SMI as usual. When set to 1, the top two pins are used for |
||
1811 | + * external DREQs: pin 16 read request, 17 write. |
||
1812 | + * SMIDC_PANIC* : Threshold at which DMA will panic during read/write. |
||
1813 | + * SMIDC_REQ* : Threshold at which DMA will generate a DREQ. |
||
1814 | + */ |
||
1815 | + |
||
1816 | +#define SMIDC_DMAEN (1 << 28) |
||
1817 | +#define SMIDC_DMAP (1 << 24) |
||
1818 | +#define SMIDC_PANICR_MASK (0x3f << 18) |
||
1819 | +#define SMIDC_PANICR_OFFS (18) |
||
1820 | +#define SMIDC_PANICW_MASK (0x3f << 12) |
||
1821 | +#define SMIDC_PANICW_OFFS (12) |
||
1822 | +#define SMIDC_REQR_MASK (0x3f << 6) |
||
1823 | +#define SMIDC_REQR_OFFS (6) |
||
1824 | +#define SMIDC_REQW_MASK (0x3f) |
||
1825 | +#define SMIDC_REQW_OFFS (0) |
||
1826 | + |
||
1827 | +/* Device settings register bits: same for all 4 (or 3?) device register sets. |
||
1828 | + * Device read settings: |
||
1829 | + * SMIDSR_RWIDTH : Read transfer width. 00 = 8bit, 01 = 16bit, |
||
1830 | + * 10 = 18bit, 11 = 9bit. |
||
1831 | + * SMIDSR_RSETUP : Read setup time: number of core cycles between chip |
||
1832 | + * select/address and read strobe. Min 1, max 64. |
||
1833 | + * SMIDSR_MODE68 : 1 for System 68 mode (i.e. enable + direction pins, |
||
1834 | + * rather than OE + WE pin) |
||
1835 | + * SMIDSR_FSETUP : If set to 1, setup time only applies to first |
||
1836 | + * transfer after address change. |
||
1837 | + * SMIDSR_RHOLD : Number of core cycles between read strobe going |
||
1838 | + * inactive and CS/address going inactive. Min 1, max 64 |
||
1839 | + * SMIDSR_RPACEALL : When set to 1, this device's RPACE value will always |
||
1840 | + * be used for the next transaction, even if it is not |
||
1841 | + * to this device. |
||
1842 | + * SMIDSR_RPACE : Number of core cycles spent waiting between CS |
||
1843 | + * deassert and start of next transfer. Min 1, max 128 |
||
1844 | + * SMIDSR_RDREQ : 1 = use external DMA request on SD16 to pace reads |
||
1845 | + * from device. Must also set DMAP in SMICS. |
||
1846 | + * SMIDSR_RSTROBE : Number of cycles to assert the read strobe. |
||
1847 | + * min 1, max 128. |
||
1848 | + */ |
||
1849 | +#define SMIDSR_RWIDTH_MASK ((1<<31)|(1<<30)) |
||
1850 | +#define SMIDSR_RWIDTH_OFFS (30) |
||
1851 | +#define SMIDSR_RSETUP_MASK (0x3f << 24) |
||
1852 | +#define SMIDSR_RSETUP_OFFS (24) |
||
1853 | +#define SMIDSR_MODE68 (1 << 23) |
||
1854 | +#define SMIDSR_FSETUP (1 << 22) |
||
1855 | +#define SMIDSR_RHOLD_MASK (0x3f << 16) |
||
1856 | +#define SMIDSR_RHOLD_OFFS (16) |
||
1857 | +#define SMIDSR_RPACEALL (1 << 15) |
||
1858 | +#define SMIDSR_RPACE_MASK (0x7f << 8) |
||
1859 | +#define SMIDSR_RPACE_OFFS (8) |
||
1860 | +#define SMIDSR_RDREQ (1 << 7) |
||
1861 | +#define SMIDSR_RSTROBE_MASK (0x7f) |
||
1862 | +#define SMIDSR_RSTROBE_OFFS (0) |
||
1863 | + |
||
1864 | +/* Device write settings: |
||
1865 | + * SMIDSW_WWIDTH : Write transfer width. 00 = 8bit, 01 = 16bit, |
||
1866 | + * 10= 18bit, 11 = 9bit. |
||
1867 | + * SMIDSW_WSETUP : Number of cycles between CS assert and write strobe. |
||
1868 | + * Min 1, max 64. |
||
1869 | + * SMIDSW_WFORMAT : Pixel format of input. 0 = 16bit RGB 565, |
||
1870 | + * 1 = 32bit RGBA 8888 |
||
1871 | + * SMIDSW_WSWAP : 1 = swap pixel data bits. (Use with SMICS_PXLDAT) |
||
1872 | + * SMIDSW_WHOLD : Time between WE deassert and CS deassert. 1 to 64 |
||
1873 | + * SMIDSW_WPACEALL : 1: this device's WPACE will be used for the next |
||
1874 | + * transfer, regardless of that transfer's device. |
||
1875 | + * SMIDSW_WPACE : Cycles between CS deassert and next CS assert. |
||
1876 | + * Min 1, max 128 |
||
1877 | + * SMIDSW_WDREQ : Use external DREQ on pin 17 to pace writes. DMAP must |
||
1878 | + * be set in SMICS. |
||
1879 | + * SMIDSW_WSTROBE : Number of cycles to assert the write strobe. |
||
1880 | + * Min 1, max 128 |
||
1881 | + */ |
||
1882 | +#define SMIDSW_WWIDTH_MASK ((1<<31)|(1<<30)) |
||
1883 | +#define SMIDSW_WWIDTH_OFFS (30) |
||
1884 | +#define SMIDSW_WSETUP_MASK (0x3f << 24) |
||
1885 | +#define SMIDSW_WSETUP_OFFS (24) |
||
1886 | +#define SMIDSW_WFORMAT (1 << 23) |
||
1887 | +#define SMIDSW_WSWAP (1 << 22) |
||
1888 | +#define SMIDSW_WHOLD_MASK (0x3f << 16) |
||
1889 | +#define SMIDSW_WHOLD_OFFS (16) |
||
1890 | +#define SMIDSW_WPACEALL (1 << 15) |
||
1891 | +#define SMIDSW_WPACE_MASK (0x7f << 8) |
||
1892 | +#define SMIDSW_WPACE_OFFS (8) |
||
1893 | +#define SMIDSW_WDREQ (1 << 7) |
||
1894 | +#define SMIDSW_WSTROBE_MASK (0x7f) |
||
1895 | +#define SMIDSW_WSTROBE_OFFS (0) |
||
1896 | + |
||
1897 | +/* Direct transfer control + status register |
||
1898 | + * SMIDCS_WRITE : Direction of transfer: 1 -> write, 0 -> read |
||
1899 | + * SMIDCS_DONE : 1 when a transfer has finished. Write 1 to clear. |
||
1900 | + * SMIDCS_START : Write 1 to start a transfer, if one is not already underway. |
||
1901 | + * SMIDCE_ENABLE: Write 1 to enable SMI in direct mode. |
||
1902 | + */ |
||
1903 | + |
||
1904 | +#define SMIDCS_WRITE (1 << 3) |
||
1905 | +#define SMIDCS_DONE (1 << 2) |
||
1906 | +#define SMIDCS_START (1 << 1) |
||
1907 | +#define SMIDCS_ENABLE (1 << 0) |
||
1908 | + |
||
1909 | +/* Direct transfer address register |
||
1910 | + * SMIDA_DEVICE : Indicates which of the device settings banks should be used. |
||
1911 | + * SMIDA_ADDR : The value to be asserted on the address pins. |
||
1912 | + */ |
||
1913 | + |
||
1914 | +#define SMIDA_DEVICE_MASK ((1<<9)|(1<<8)) |
||
1915 | +#define SMIDA_DEVICE_OFFS (8) |
||
1916 | +#define SMIDA_ADDR_MASK (0x3f) |
||
1917 | +#define SMIDA_ADDR_OFFS (0) |
||
1918 | + |
||
1919 | +/* FIFO debug register |
||
1920 | + * SMIFD_FLVL : The high-tide mark of FIFO count during the most recent txfer |
||
1921 | + * SMIFD_FCNT : The current FIFO count. |
||
1922 | + */ |
||
1923 | +#define SMIFD_FLVL_MASK (0x3f << 8) |
||
1924 | +#define SMIFD_FLVL_OFFS (8) |
||
1925 | +#define SMIFD_FCNT_MASK (0x3f) |
||
1926 | +#define SMIFD_FCNT_OFFS (0) |
||
1927 | + |
||
1928 | +#endif /* BCM2835_SMI_IMPLEMENTATION */ |
||
1929 | + |
||
1930 | +#endif /* BCM2835_SMI_H */ |