/* Copyright (c) 2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ /* This driver implements a simple SPI read/write interface to access * an external device over SPI. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define CI_MAX_BUFFER_SIZE (64 * 1024) struct ci_bridge { dev_t ci_bridge_dev; struct cdev cdev; struct class *bridge_class; struct device *bridge_dev; char *write_buffer; char *read_buffer; struct mutex lock; struct spi_device *spi; unsigned int gpio_reset_pin; unsigned int gpio_interrupt_pin; int num_opened; }; static struct ci_bridge ci; static int __devinit ci_bridge_spi_probe(struct spi_device *spi) { int ret; struct ci_bridge_platform_data *pdata; if (spi->dev.platform_data == NULL) { pr_err("%s: platform data is missing\n", __func__); return -EINVAL; } ci.spi = spi; ci.num_opened = 0; mutex_init(&ci.lock); spi_set_drvdata(spi, &ci); pdata = spi->dev.platform_data; ci.gpio_reset_pin = pdata->reset_pin; ci.gpio_interrupt_pin = pdata->interrupt_pin; ret = gpio_request(ci.gpio_reset_pin, "ci_bridge_spi"); if (ret) { pr_err("%s: GPIO request for pin number %u failed\n", __func__, ci.gpio_reset_pin); return ret; } ret = gpio_direction_output(ci.gpio_reset_pin, 1); if (ret) { pr_err("%s: unable to set GPIO direction, err=%d\n", __func__, ret); goto err_free_reset_pin; } ret = gpio_request(ci.gpio_interrupt_pin, "ci_bridge_spi"); if (ret) { pr_err("%s: GPIO request for pin number %u failed\n", __func__, ci.gpio_interrupt_pin); goto err_free_reset_pin; } ret = gpio_direction_input(ci.gpio_interrupt_pin); if (ret) { pr_err("%s: unable to set GPIO direction, err=%d\n", __func__, ret); goto err_free_int_pin; } return 0; err_free_int_pin: gpio_free(ci.gpio_interrupt_pin); err_free_reset_pin: gpio_free(ci.gpio_reset_pin); return ret; } static int __devexit ci_bridge_spi_remove(struct spi_device *spi) { struct ci_bridge *bridge = spi_get_drvdata(spi); spi_set_drvdata(bridge->spi, NULL); bridge->spi = NULL; mutex_destroy(&ci.lock); gpio_free(ci.gpio_reset_pin); gpio_free(ci.gpio_interrupt_pin); return 0; } static struct spi_driver ci_bridge_driver = { .driver = { .name = "ci_bridge_spi", .owner = THIS_MODULE, }, .probe = ci_bridge_spi_probe, .remove = __devexit_p(ci_bridge_spi_remove), }; static void ci_bridge_spi_completion_cb(void *arg) { complete(arg); } static ssize_t ci_bridge_spi_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { int ret = 0; unsigned long not_copied = 0; struct spi_transfer spi_transfer; struct spi_message spi_message; DECLARE_COMPLETION_ONSTACK(context); struct ci_bridge *bridge = filp->private_data; if ((bridge == NULL) || (bridge->spi == NULL)) return -ENODEV; if (count > CI_MAX_BUFFER_SIZE) return -EMSGSIZE; memset(&spi_transfer, 0, sizeof(struct spi_transfer)); memset(&spi_message, 0, sizeof(struct spi_message)); mutex_lock(&bridge->lock); spi_transfer.rx_buf = bridge->read_buffer; spi_transfer.len = count; spi_message_init(&spi_message); spi_message_add_tail(&spi_transfer, &spi_message); spi_message.complete = ci_bridge_spi_completion_cb; spi_message.context = &context; /* must use spi_async in a context that may sleep */ ret = spi_async(bridge->spi, &spi_message); if (ret == 0) { wait_for_completion(&context); if (spi_message.status == 0) { /* spi_message.actual_length should contain the number * of bytes actually read and should update ret to be * the actual length, but since our driver doesn't * support this, assume all count bytes were read. */ ret = count; } if (ret > 0) { not_copied = copy_to_user(buf, bridge->read_buffer, ret); if (not_copied == ret) ret = -EFAULT; else ret -= not_copied; } } else { pr_err("%s: Error calling spi_async, ret = %d\n", __func__, ret); } mutex_unlock(&bridge->lock); return ret; } static ssize_t ci_bridge_spi_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { int ret = 0; unsigned long not_copied = 0; struct spi_transfer spi_transfer; struct spi_message spi_message; DECLARE_COMPLETION_ONSTACK(context); struct ci_bridge *bridge = filp->private_data; if ((bridge == NULL) || (bridge->spi == NULL)) return -ENODEV; if (count > CI_MAX_BUFFER_SIZE) return -EMSGSIZE; memset(&spi_transfer, 0, sizeof(struct spi_transfer)); memset(&spi_message, 0, sizeof(struct spi_message)); mutex_lock(&bridge->lock); /* copy user data to our SPI Tx buffer */ not_copied = copy_from_user(bridge->write_buffer, buf, count); if (not_copied != 0) { ret = -EFAULT; } else { spi_transfer.tx_buf = bridge->write_buffer; spi_transfer.len = count; spi_message_init(&spi_message); spi_message_add_tail(&spi_transfer, &spi_message); spi_message.complete = ci_bridge_spi_completion_cb; spi_message.context = &context; /* must use spi_async in a context that may sleep */ ret = spi_async(bridge->spi, &spi_message); if (ret == 0) { wait_for_completion(&context); /* update ret to contain * the number of bytes actually written */ if (spi_message.status == 0) ret = spi_transfer.len; else pr_err("%s: SPI transfer error, spi_message.status = %d\n", __func__, spi_message.status); } else { pr_err("%s: Error calling spi_async, ret = %d\n", __func__, ret); } } mutex_unlock(&bridge->lock); return ret; } static int ci_bridge_spi_open(struct inode *inode, struct file *filp) { /* forbid opening more then one instance at a time, parallel execution can still be problematic */ if (ci.num_opened != 0) return -EBUSY; /* allocate write buffer */ ci.write_buffer = kzalloc((CI_MAX_BUFFER_SIZE * sizeof(char)), GFP_KERNEL); if (ci.write_buffer == NULL) { pr_err("%s: Error allocating memory for write buffer\n", __func__); return -ENOMEM; } /* allocate read buffer */ ci.read_buffer = kzalloc((CI_MAX_BUFFER_SIZE * sizeof(char)), GFP_KERNEL); if (ci.read_buffer == NULL) { pr_err("%s: Error allocating memory for read buffer\n", __func__); kfree(ci.write_buffer); return -ENOMEM; } /* device is non-seekable */ nonseekable_open(inode, filp); filp->private_data = &ci; ci.num_opened = 1; return 0; } static int ci_bridge_ioctl_get_int(void *arg) { int state; if (arg == NULL) return -EINVAL; state = gpio_get_value_cansleep(ci.gpio_interrupt_pin); if (copy_to_user(arg, &state, sizeof(state))) return -EFAULT; return 0; } static int ci_bridge_ioctl_reset(unsigned long arg) { if ((arg != 0) && (arg != 1)) return -EINVAL; gpio_set_value_cansleep(ci.gpio_reset_pin, arg); return 0; } static long ci_bridge_spi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret; switch (cmd) { case CI_BRIDGE_IOCTL_RESET: ret = ci_bridge_ioctl_reset(arg); break; case CI_BRIDGE_IOCTL_GET_INT_STATE: ret = ci_bridge_ioctl_get_int((void *) arg); break; default: ret = -EINVAL; break; } return ret; } static int ci_bridge_spi_release(struct inode *inode, struct file *filp) { struct ci_bridge *bridge = filp->private_data; if ((bridge == NULL) || (bridge->spi == NULL)) return -ENODEV; kfree(bridge->write_buffer); kfree(bridge->read_buffer); filp->private_data = NULL; ci.num_opened = 0; return 0; } static const struct file_operations ci_bridge_spi_fops = { .owner = THIS_MODULE, .read = ci_bridge_spi_read, .write = ci_bridge_spi_write, .open = ci_bridge_spi_open, .unlocked_ioctl = ci_bridge_spi_ioctl, .release = ci_bridge_spi_release, .llseek = no_llseek, }; static int __init ci_bridge_init(void) { int ret = 0; ret = alloc_chrdev_region(&ci.ci_bridge_dev, 0, 1, "ci_bridge_spi"); if (ret != 0) return ret; ci.bridge_class = class_create(THIS_MODULE, "ci_bridge_spi"); if (IS_ERR(ci.bridge_class)) { ret = PTR_ERR(ci.bridge_class); pr_err("Error creating ci.bridge_class: %d\n", ret); goto free_region; } cdev_init(&ci.cdev, &ci_bridge_spi_fops); ci.cdev.owner = THIS_MODULE; ret = cdev_add(&ci.cdev, ci.ci_bridge_dev, 1); if (ret != 0) { pr_err("Error calling cdev_add: %d\n", ret); goto class_destroy; } ci.bridge_dev = device_create(ci.bridge_class, NULL, ci.cdev.dev, &ci, "ci_bridge_spi0"); if (IS_ERR(ci.bridge_dev)) { ret = PTR_ERR(ci.bridge_dev); pr_err("device_create failed: %d\n", ret); goto del_cdev; } ret = spi_register_driver(&ci_bridge_driver); if (ret != 0) { pr_err("Error registering spi driver: %d\n", ret); goto device_destroy; } /* successful return */ return 0; device_destroy: device_destroy(ci.bridge_class, ci.ci_bridge_dev); del_cdev: cdev_del(&ci.cdev); class_destroy: class_destroy(ci.bridge_class); free_region: unregister_chrdev_region(ci.ci_bridge_dev, 1); return ret; } static void __exit ci_bridge_exit(void) { spi_unregister_driver(&ci_bridge_driver); device_destroy(ci.bridge_class, ci.ci_bridge_dev); cdev_del(&ci.cdev); class_destroy(ci.bridge_class); unregister_chrdev_region(ci.ci_bridge_dev, 1); } module_init(ci_bridge_init); module_exit(ci_bridge_exit); MODULE_DESCRIPTION("CI Bridge SPI Driver"); MODULE_LICENSE("GPL v2");