2 * Character device driver for Broadcom Secondary Memory Interface
4 * Written by Luke Wren <luke@raspberrypi.org>
5 * Copyright (c) 2015, Raspberry Pi (Trading) Ltd.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions, and the following disclaimer,
12 * without modification.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. The names of the above-listed copyright holders may not be used
17 * to endorse or promote products derived from this software without
18 * specific prior written permission.
20 * ALTERNATIVELY, this software may be distributed under the terms of the
21 * GNU General Public License ("GPL") version 2, as published by the Free
22 * Software Foundation.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
25 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
26 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 #include <linux/kernel.h>
38 #include <linux/module.h>
40 #include <linux/platform_device.h>
41 #include <linux/slab.h>
43 #include <linux/pagemap.h>
45 #include <linux/cdev.h>
48 #include <linux/broadcom/bcm2835_smi.h>
50 #define DEVICE_NAME "bcm2835-smi-dev"
51 #define DRIVER_NAME "smi-dev-bcm2835"
52 #define DEVICE_MINOR 0
54 static struct cdev bcm2835_smi_cdev;
55 static dev_t bcm2835_smi_devid;
56 static struct class *bcm2835_smi_class;
57 static struct device *bcm2835_smi_dev;
59 struct bcm2835_smi_dev_instance {
63 static struct bcm2835_smi_instance *smi_inst;
64 static struct bcm2835_smi_dev_instance *inst;
66 static const char *const ioctl_names[] = {
72 /****************************************************************************
74 * SMI chardev file ops
76 ***************************************************************************/
78 bcm2835_smi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
82 dev_info(inst->dev, "serving ioctl...");
85 case BCM2835_SMI_IOC_GET_SETTINGS:{
86 struct smi_settings *settings;
88 dev_info(inst->dev, "Reading SMI settings to user.");
89 settings = bcm2835_smi_get_settings_from_regs(smi_inst);
90 if (copy_to_user((void *)arg, settings,
91 sizeof(struct smi_settings)))
92 dev_err(inst->dev, "settings copy failed.");
95 case BCM2835_SMI_IOC_WRITE_SETTINGS:{
96 struct smi_settings *settings;
98 dev_info(inst->dev, "Setting user's SMI settings.");
99 settings = bcm2835_smi_get_settings_from_regs(smi_inst);
100 if (copy_from_user(settings, (void *)arg,
101 sizeof(struct smi_settings)))
102 dev_err(inst->dev, "settings copy failed.");
104 bcm2835_smi_set_regs_from_settings(smi_inst);
107 case BCM2835_SMI_IOC_ADDRESS:
108 dev_info(inst->dev, "SMI address set: 0x%02x", (int)arg);
109 bcm2835_smi_set_address(smi_inst, arg);
112 dev_err(inst->dev, "invalid ioctl cmd: %d", cmd);
120 static int bcm2835_smi_open(struct inode *inode, struct file *file)
122 int dev = iminor(inode);
124 dev_dbg(inst->dev, "SMI device opened.");
126 if (dev != DEVICE_MINOR) {
128 "bcm2835_smi_release: Unknown minor device: %d",
136 static int bcm2835_smi_release(struct inode *inode, struct file *file)
138 int dev = iminor(inode);
140 if (dev != DEVICE_MINOR) {
142 "bcm2835_smi_release: Unknown minor device %d", dev);
149 static ssize_t dma_bounce_user(
150 enum dma_transfer_direction dma_dir,
151 char __user *user_ptr,
153 struct bcm2835_smi_bounce_info *bounce)
157 int count_left = count;
163 /* Wait for current chunk to complete: */
164 if (down_timeout(&bounce->callback_sem,
165 msecs_to_jiffies(1000))) {
166 dev_err(inst->dev, "DMA bounce timed out");
167 count -= (count_left);
171 if (bounce->callback_sem.count >= DMA_BOUNCE_BUFFER_COUNT - 1)
172 dev_err(inst->dev, "WARNING: Ring buffer overflow");
173 chunk_size = count_left > DMA_BOUNCE_BUFFER_SIZE ?
174 DMA_BOUNCE_BUFFER_SIZE : count_left;
175 buf = bounce->buffer[chunk_no % DMA_BOUNCE_BUFFER_COUNT];
176 if (dma_dir == DMA_DEV_TO_MEM)
177 rv = copy_to_user(user_ptr, buf, chunk_size);
179 rv = copy_from_user(buf, user_ptr, chunk_size);
181 dev_err(inst->dev, "copy_*_user() failed!: %d", rv);
182 user_ptr += chunk_size;
183 count_left -= chunk_size;
190 bcm2835_read_file(struct file *f, char __user *user_ptr,
191 size_t count, loff_t *offs)
196 dev_dbg(inst->dev, "User reading %zu bytes from SMI.", count);
197 /* We don't want to DMA a number of bytes % 4 != 0 (32 bit FIFO) */
198 if (count > DMA_THRESHOLD_BYTES)
199 odd_bytes = count & 0x3;
205 struct bcm2835_smi_bounce_info *bounce;
207 count = bcm2835_smi_user_dma(smi_inst,
208 DMA_DEV_TO_MEM, user_ptr, count,
211 count = dma_bounce_user(DMA_DEV_TO_MEM, user_ptr,
214 if (odd_bytes && (count == count_check)) {
215 /* Read from FIFO directly if not using DMA */
216 uint8_t buf[DMA_THRESHOLD_BYTES];
217 unsigned long bytes_not_transferred;
219 bcm2835_smi_read_buf(smi_inst, buf, odd_bytes);
220 bytes_not_transferred = copy_to_user(user_ptr + count, buf, odd_bytes);
221 if (bytes_not_transferred)
222 dev_err(inst->dev, "copy_to_user() failed.");
223 count += odd_bytes - bytes_not_transferred;
229 bcm2835_write_file(struct file *f, const char __user *user_ptr,
230 size_t count, loff_t *offs)
235 dev_dbg(inst->dev, "User writing %zu bytes to SMI.", count);
236 if (count > DMA_THRESHOLD_BYTES)
237 odd_bytes = count & 0x3;
243 struct bcm2835_smi_bounce_info *bounce;
245 count = bcm2835_smi_user_dma(smi_inst,
246 DMA_MEM_TO_DEV, (char __user *)user_ptr, count,
249 count = dma_bounce_user(DMA_MEM_TO_DEV,
250 (char __user *)user_ptr,
253 if (odd_bytes && (count == count_check)) {
254 uint8_t buf[DMA_THRESHOLD_BYTES];
255 unsigned long bytes_not_transferred;
257 bytes_not_transferred = copy_from_user(buf, user_ptr + count, odd_bytes);
258 if (bytes_not_transferred)
259 dev_err(inst->dev, "copy_from_user() failed.");
261 bcm2835_smi_write_buf(smi_inst, buf, odd_bytes);
262 count += odd_bytes - bytes_not_transferred;
267 static const struct file_operations
269 .owner = THIS_MODULE,
270 .unlocked_ioctl = bcm2835_smi_ioctl,
271 .open = bcm2835_smi_open,
272 .release = bcm2835_smi_release,
273 .read = bcm2835_read_file,
274 .write = bcm2835_write_file,
278 /****************************************************************************
280 * bcm2835_smi_probe - called when the driver is loaded.
282 ***************************************************************************/
284 static int bcm2835_smi_dev_probe(struct platform_device *pdev)
288 struct device *dev = &pdev->dev;
289 struct device_node *node = dev->of_node, *smi_node;
292 dev_err(dev, "No device tree node supplied!");
296 smi_node = of_parse_phandle(node, "smi_handle", 0);
299 dev_err(dev, "No such property: smi_handle");
303 smi_inst = bcm2835_smi_get(smi_node);
306 return -EPROBE_DEFER;
308 /* Allocate buffers and instance data */
310 inst = devm_kzalloc(dev, sizeof(*inst), GFP_KERNEL);
317 /* Create character device entries */
319 err = alloc_chrdev_region(&bcm2835_smi_devid,
320 DEVICE_MINOR, 1, DEVICE_NAME);
322 dev_err(inst->dev, "unable to allocate device number");
325 cdev_init(&bcm2835_smi_cdev, &bcm2835_smi_fops);
326 bcm2835_smi_cdev.owner = THIS_MODULE;
327 err = cdev_add(&bcm2835_smi_cdev, bcm2835_smi_devid, 1);
329 dev_err(inst->dev, "unable to register device");
331 goto failed_cdev_add;
334 /* Create sysfs entries */
336 bcm2835_smi_class = class_create(THIS_MODULE, DEVICE_NAME);
337 ptr_err = bcm2835_smi_class;
339 goto failed_class_create;
341 bcm2835_smi_dev = device_create(bcm2835_smi_class, NULL,
342 bcm2835_smi_devid, NULL,
344 ptr_err = bcm2835_smi_dev;
346 goto failed_device_create;
348 dev_info(inst->dev, "initialised");
352 failed_device_create:
353 class_destroy(bcm2835_smi_class);
355 cdev_del(&bcm2835_smi_cdev);
356 err = PTR_ERR(ptr_err);
358 unregister_chrdev_region(bcm2835_smi_devid, 1);
359 dev_err(dev, "could not load bcm2835_smi_dev");
363 /****************************************************************************
365 * bcm2835_smi_remove - called when the driver is unloaded.
367 ***************************************************************************/
369 static int bcm2835_smi_dev_remove(struct platform_device *pdev)
371 device_destroy(bcm2835_smi_class, bcm2835_smi_devid);
372 class_destroy(bcm2835_smi_class);
373 cdev_del(&bcm2835_smi_cdev);
374 unregister_chrdev_region(bcm2835_smi_devid, 1);
376 dev_info(inst->dev, "SMI character dev removed - OK");
380 /****************************************************************************
382 * Register the driver with device tree
384 ***************************************************************************/
386 static const struct of_device_id bcm2835_smi_dev_of_match[] = {
387 {.compatible = "brcm,bcm2835-smi-dev",},
391 MODULE_DEVICE_TABLE(of, bcm2835_smi_dev_of_match);
393 static struct platform_driver bcm2835_smi_dev_driver = {
394 .probe = bcm2835_smi_dev_probe,
395 .remove = bcm2835_smi_dev_remove,
398 .owner = THIS_MODULE,
399 .of_match_table = bcm2835_smi_dev_of_match,
403 module_platform_driver(bcm2835_smi_dev_driver);
405 MODULE_ALIAS("platform:smi-dev-bcm2835");
406 MODULE_LICENSE("GPL");
408 "Character device driver for BCM2835's secondary memory interface");
409 MODULE_AUTHOR("Luke Wren <luke@raspberrypi.org>");