Merge tag 'v5.15.57' into rpi-5.15.y
[platform/kernel/linux-rpi.git] / drivers / char / broadcom / bcm2835_smi_dev.c
1 /**
2  * Character device driver for Broadcom Secondary Memory Interface
3  *
4  * Written by Luke Wren <luke@raspberrypi.org>
5  * Copyright (c) 2015, Raspberry Pi (Trading) Ltd.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
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.
19  *
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.
23  *
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.
35  */
36
37 #include <linux/kernel.h>
38 #include <linux/module.h>
39 #include <linux/of.h>
40 #include <linux/platform_device.h>
41 #include <linux/slab.h>
42 #include <linux/mm.h>
43 #include <linux/pagemap.h>
44 #include <linux/fs.h>
45 #include <linux/cdev.h>
46 #include <linux/fs.h>
47
48 #include <linux/broadcom/bcm2835_smi.h>
49
50 #define DEVICE_NAME "bcm2835-smi-dev"
51 #define DRIVER_NAME "smi-dev-bcm2835"
52 #define DEVICE_MINOR 0
53
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;
58
59 struct bcm2835_smi_dev_instance {
60         struct device *dev;
61 };
62
63 static struct bcm2835_smi_instance *smi_inst;
64 static struct bcm2835_smi_dev_instance *inst;
65
66 static const char *const ioctl_names[] = {
67         "READ_SETTINGS",
68         "WRITE_SETTINGS",
69         "ADDRESS"
70 };
71
72 /****************************************************************************
73 *
74 *   SMI chardev file ops
75 *
76 ***************************************************************************/
77 static long
78 bcm2835_smi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
79 {
80         long ret = 0;
81
82         dev_info(inst->dev, "serving ioctl...");
83
84         switch (cmd) {
85         case BCM2835_SMI_IOC_GET_SETTINGS:{
86                 struct smi_settings *settings;
87
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.");
93                 break;
94         }
95         case BCM2835_SMI_IOC_WRITE_SETTINGS:{
96                 struct smi_settings *settings;
97
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.");
103                 else
104                         bcm2835_smi_set_regs_from_settings(smi_inst);
105                 break;
106         }
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);
110                 break;
111         default:
112                 dev_err(inst->dev, "invalid ioctl cmd: %d", cmd);
113                 ret = -ENOTTY;
114                 break;
115         }
116
117         return ret;
118 }
119
120 static int bcm2835_smi_open(struct inode *inode, struct file *file)
121 {
122         int dev = iminor(inode);
123
124         dev_dbg(inst->dev, "SMI device opened.");
125
126         if (dev != DEVICE_MINOR) {
127                 dev_err(inst->dev,
128                         "bcm2835_smi_release: Unknown minor device: %d",
129                         dev);
130                 return -ENXIO;
131         }
132
133         return 0;
134 }
135
136 static int bcm2835_smi_release(struct inode *inode, struct file *file)
137 {
138         int dev = iminor(inode);
139
140         if (dev != DEVICE_MINOR) {
141                 dev_err(inst->dev,
142                         "bcm2835_smi_release: Unknown minor device %d", dev);
143                 return -ENXIO;
144         }
145
146         return 0;
147 }
148
149 static ssize_t dma_bounce_user(
150         enum dma_transfer_direction dma_dir,
151         char __user *user_ptr,
152         size_t count,
153         struct bcm2835_smi_bounce_info *bounce)
154 {
155         int chunk_size;
156         int chunk_no = 0;
157         int count_left = count;
158
159         while (count_left) {
160                 int rv;
161                 void *buf;
162
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);
168                         break;
169                 }
170
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);
178                 else
179                         rv = copy_from_user(buf, user_ptr, chunk_size);
180                 if (rv)
181                         dev_err(inst->dev, "copy_*_user() failed!: %d", rv);
182                 user_ptr += chunk_size;
183                 count_left -= chunk_size;
184                 chunk_no++;
185         }
186         return count;
187 }
188
189 static ssize_t
190 bcm2835_read_file(struct file *f, char __user *user_ptr,
191                   size_t count, loff_t *offs)
192 {
193         int odd_bytes;
194         size_t count_check;
195
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;
200         else
201                 odd_bytes = count;
202         count -= odd_bytes;
203         count_check = count;
204         if (count) {
205                 struct bcm2835_smi_bounce_info *bounce;
206
207                 count = bcm2835_smi_user_dma(smi_inst,
208                         DMA_DEV_TO_MEM, user_ptr, count,
209                         &bounce);
210                 if (count)
211                         count = dma_bounce_user(DMA_DEV_TO_MEM, user_ptr,
212                                 count, bounce);
213         }
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;
218
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;
224         }
225         return count;
226 }
227
228 static ssize_t
229 bcm2835_write_file(struct file *f, const char __user *user_ptr,
230                    size_t count, loff_t *offs)
231 {
232         int odd_bytes;
233         size_t count_check;
234
235         dev_dbg(inst->dev, "User writing %zu bytes to SMI.", count);
236         if (count > DMA_THRESHOLD_BYTES)
237                 odd_bytes = count & 0x3;
238         else
239                 odd_bytes = count;
240         count -= odd_bytes;
241         count_check = count;
242         if (count) {
243                 struct bcm2835_smi_bounce_info *bounce;
244
245                 count = bcm2835_smi_user_dma(smi_inst,
246                         DMA_MEM_TO_DEV, (char __user *)user_ptr, count,
247                         &bounce);
248                 if (count)
249                         count = dma_bounce_user(DMA_MEM_TO_DEV,
250                                 (char __user *)user_ptr,
251                                 count, bounce);
252         }
253         if (odd_bytes && (count == count_check)) {
254                 uint8_t buf[DMA_THRESHOLD_BYTES];
255                 unsigned long bytes_not_transferred;
256
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.");
260                 else
261                         bcm2835_smi_write_buf(smi_inst, buf, odd_bytes);
262                 count += odd_bytes - bytes_not_transferred;
263         }
264         return count;
265 }
266
267 static const struct file_operations
268 bcm2835_smi_fops = {
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,
275 };
276
277
278 /****************************************************************************
279 *
280 *   bcm2835_smi_probe - called when the driver is loaded.
281 *
282 ***************************************************************************/
283
284 static int bcm2835_smi_dev_probe(struct platform_device *pdev)
285 {
286         int err;
287         void *ptr_err;
288         struct device *dev = &pdev->dev;
289         struct device_node *node = dev->of_node, *smi_node;
290
291         if (!node) {
292                 dev_err(dev, "No device tree node supplied!");
293                 return -EINVAL;
294         }
295
296         smi_node = of_parse_phandle(node, "smi_handle", 0);
297
298         if (!smi_node) {
299                 dev_err(dev, "No such property: smi_handle");
300                 return -ENXIO;
301         }
302
303         smi_inst = bcm2835_smi_get(smi_node);
304
305         if (!smi_inst)
306                 return -EPROBE_DEFER;
307
308         /* Allocate buffers and instance data */
309
310         inst = devm_kzalloc(dev, sizeof(*inst), GFP_KERNEL);
311
312         if (!inst)
313                 return -ENOMEM;
314
315         inst->dev = dev;
316
317         /* Create character device entries */
318
319         err = alloc_chrdev_region(&bcm2835_smi_devid,
320                                   DEVICE_MINOR, 1, DEVICE_NAME);
321         if (err != 0) {
322                 dev_err(inst->dev, "unable to allocate device number");
323                 return -ENOMEM;
324         }
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);
328         if (err != 0) {
329                 dev_err(inst->dev, "unable to register device");
330                 err = -ENOMEM;
331                 goto failed_cdev_add;
332         }
333
334         /* Create sysfs entries */
335
336         bcm2835_smi_class = class_create(THIS_MODULE, DEVICE_NAME);
337         ptr_err = bcm2835_smi_class;
338         if (IS_ERR(ptr_err))
339                 goto failed_class_create;
340
341         bcm2835_smi_dev = device_create(bcm2835_smi_class, NULL,
342                                         bcm2835_smi_devid, NULL,
343                                         "smi");
344         ptr_err = bcm2835_smi_dev;
345         if (IS_ERR(ptr_err))
346                 goto failed_device_create;
347
348         dev_info(inst->dev, "initialised");
349
350         return 0;
351
352 failed_device_create:
353         class_destroy(bcm2835_smi_class);
354 failed_class_create:
355         cdev_del(&bcm2835_smi_cdev);
356         err = PTR_ERR(ptr_err);
357 failed_cdev_add:
358         unregister_chrdev_region(bcm2835_smi_devid, 1);
359         dev_err(dev, "could not load bcm2835_smi_dev");
360         return err;
361 }
362
363 /****************************************************************************
364 *
365 *   bcm2835_smi_remove - called when the driver is unloaded.
366 *
367 ***************************************************************************/
368
369 static int bcm2835_smi_dev_remove(struct platform_device *pdev)
370 {
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);
375
376         dev_info(inst->dev, "SMI character dev removed - OK");
377         return 0;
378 }
379
380 /****************************************************************************
381 *
382 *   Register the driver with device tree
383 *
384 ***************************************************************************/
385
386 static const struct of_device_id bcm2835_smi_dev_of_match[] = {
387         {.compatible = "brcm,bcm2835-smi-dev",},
388         { /* sentinel */ },
389 };
390
391 MODULE_DEVICE_TABLE(of, bcm2835_smi_dev_of_match);
392
393 static struct platform_driver bcm2835_smi_dev_driver = {
394         .probe = bcm2835_smi_dev_probe,
395         .remove = bcm2835_smi_dev_remove,
396         .driver = {
397                    .name = DRIVER_NAME,
398                    .owner = THIS_MODULE,
399                    .of_match_table = bcm2835_smi_dev_of_match,
400                    },
401 };
402
403 module_platform_driver(bcm2835_smi_dev_driver);
404
405 MODULE_ALIAS("platform:smi-dev-bcm2835");
406 MODULE_LICENSE("GPL");
407 MODULE_DESCRIPTION(
408         "Character device driver for BCM2835's secondary memory interface");
409 MODULE_AUTHOR("Luke Wren <luke@raspberrypi.org>");