diff options
author | RGIB <gibellini.roberto@gmail.com> | 2016-05-27 15:41:13 +0200 |
---|---|---|
committer | Roberto Gibellini <gibellini.roberto@gmail.com> | 2016-05-27 06:45:25 -0700 |
commit | 462bab3a059ebeeb45dc3ebfef4ee8b39a4757e3 (patch) | |
tree | 7f5dc0a29e59087c4e83fe88173d3ce64a93d4e1 /drivers/misc/modem_if/modem_boot_device_spi.c | |
parent | 206f43e8ae09166ba608f520c60bb208661769ca (diff) | |
download | kernel_samsung_smdk4412-462bab3a059ebeeb45dc3ebfef4ee8b39a4757e3.zip kernel_samsung_smdk4412-462bab3a059ebeeb45dc3ebfef4ee8b39a4757e3.tar.gz kernel_samsung_smdk4412-462bab3a059ebeeb45dc3ebfef4ee8b39a4757e3.tar.bz2 |
smdk4412 : modem_if KK driver from N5100ZTCNL4
Change-Id: I7d1e6412700b5db293448aca99c53ac4a52fc1a8
Diffstat (limited to 'drivers/misc/modem_if/modem_boot_device_spi.c')
-rw-r--r-- | drivers/misc/modem_if/modem_boot_device_spi.c | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/drivers/misc/modem_if/modem_boot_device_spi.c b/drivers/misc/modem_if/modem_boot_device_spi.c new file mode 100644 index 0000000..369e473 --- /dev/null +++ b/drivers/misc/modem_if/modem_boot_device_spi.c @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2011 Samsung Electronics. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/miscdevice.h> + +#include <linux/uaccess.h> +#include <linux/fs.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/wakelock.h> +#include <linux/delay.h> +#include <linux/spi/spi.h> + +#include "modem.h" +#include "modem_prj.h" + +#define SPI_XMIT_DELEY 100 + +static int check_cp_status(unsigned int gpio_cp_status, unsigned int count) +{ + int ret = 0; + int cnt = 0; + + while (1) { + if (gpio_get_value(gpio_cp_status) != 0) { + ret = 0; + break; + } + + cnt++; + if (cnt >= count) { + mif_err("ERR! gpio_cp_status == 0 (cnt %d)\n", cnt); + ret = -EFAULT; + break; + } + + msleep(20); + } + + return ret; +} + +static inline int spi_send(struct modem_boot_spi *loader, const char val) +{ + char buff[1]; + int ret; + struct spi_message msg; + struct spi_transfer xfer = { + .len = 1, + .tx_buf = buff, + }; + + buff[0] = val; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + ret = spi_sync(loader->spi_dev, &msg); + if (ret < 0) + mif_err("ERR! spi_sync fail (err %d)\n", ret); + + return ret; +} + +static int spi_boot_write(struct modem_boot_spi *loader, const char *addr, + const long len) +{ + int i; + int ret = 0; + char *buff = NULL; + mif_err("+++\n"); + + buff = kzalloc(len, GFP_KERNEL); + if (!buff) { + mif_err("ERR! kzalloc(%ld) fail\n", len); + ret = -ENOMEM; + goto exit; + } + + ret = copy_from_user(buff, (const void __user *)addr, len); + if (ret) { + mif_err("ERR! copy_from_user fail (err %d)\n", ret); + ret = -EFAULT; + goto exit; + } + + for (i = 0 ; i < len ; i++) { + ret = spi_send(loader, buff[i]); + if (ret < 0) { + mif_err("ERR! spi_send fail (err %d)\n", ret); + goto exit; + } + } + +exit: + if (buff) + kfree(buff); + + mif_err("---\n"); + return ret; +} + +static int spi_boot_open(struct inode *inode, struct file *filp) +{ + struct modem_boot_spi *loader = to_modem_boot_spi(filp->private_data); + filp->private_data = loader; + return 0; +} + +static long spi_boot_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + struct modem_firmware img; + struct modem_boot_spi *loader = filp->private_data; + + mutex_lock(&loader->lock); + switch (cmd) { + case IOCTL_MODEM_XMIT_BOOT: + ret = copy_from_user(&img, (const void __user *)arg, + sizeof(struct modem_firmware)); + if (ret) { + mif_err("ERR! copy_from_user fail (err %d)\n", ret); + ret = -EFAULT; + goto exit_err; + } + mif_info("IOCTL_MODEM_XMIT_BOOT (size %d)\n", img.size); + + ret = spi_boot_write(loader, img.binary, img.size); + if (ret < 0) { + mif_err("ERR! spi_boot_write fail (err %d)\n", ret); + break; + } + + if (!loader->gpio_cp_status) + break; + + ret = check_cp_status(loader->gpio_cp_status, 100); + if (ret < 0) + mif_err("ERR! check_cp_status fail (err %d)\n", ret); + + break; + + default: + mif_err("ioctl cmd error\n"); + ret = -ENOIOCTLCMD; + + break; + } + mutex_unlock(&loader->lock); + +exit_err: + return ret; +} + +static const struct file_operations modem_spi_boot_fops = { + .owner = THIS_MODULE, + .open = spi_boot_open, + .unlocked_ioctl = spi_boot_ioctl, +}; + +static int __devinit modem_spi_boot_probe(struct spi_device *spi) +{ + int ret; + struct modem_boot_spi *loader; + struct modem_boot_spi_platform_data *pdata; + mif_debug("+++\n"); + + loader = kzalloc(sizeof(*loader), GFP_KERNEL); + if (!loader) { + mif_err("failed to allocate for modem_boot_spi\n"); + ret = -ENOMEM; + goto err_alloc; + } + mutex_init(&loader->lock); + + spi->bits_per_word = 8; + + if (spi_setup(spi)) { + mif_err("ERR! spi_setup fail\n"); + ret = -EINVAL; + goto err_setup; + } + loader->spi_dev = spi; + + if (!spi->dev.platform_data) { + mif_err("ERR! no platform_data\n"); + ret = -EINVAL; + goto err_setup; + } + pdata = (struct modem_boot_spi_platform_data *)spi->dev.platform_data; + + loader->gpio_cp_status = pdata->gpio_cp_status; + + spi_set_drvdata(spi, loader); + + loader->dev.minor = MISC_DYNAMIC_MINOR; + loader->dev.name = MODEM_BOOT_DEV_SPI; + loader->dev.fops = &modem_spi_boot_fops; + ret = misc_register(&loader->dev); + if (ret) { + mif_err("ERR! misc_register fail (err %d)\n", ret); + goto err_setup; + } + + mif_debug("---\n"); + return 0; + +err_setup: + mutex_destroy(&loader->lock); + kfree(loader); + +err_alloc: + mif_err("xxx\n"); + return ret; +} + +static int __devexit modem_spi_boot_remove(struct spi_device *spi) +{ + struct modem_boot_spi *loader = spi_get_drvdata(spi); + + misc_deregister(&loader->dev); + mutex_destroy(&loader->lock); + kfree(loader); + + return 0; +} + +static struct spi_driver modem_boot_device_spi_driver = { + .driver = { + .name = MODEM_BOOT_DEV_SPI, + .owner = THIS_MODULE, + }, + .probe = modem_spi_boot_probe, + .remove = __devexit_p(modem_spi_boot_remove), +}; + +static int __init modem_boot_device_spi_init(void) +{ + int err; + + err = spi_register_driver(&modem_boot_device_spi_driver); + if (err) { + mif_err("spi_register_driver fail (err %d)\n", err); + return err; + } + + return 0; +} + +static void __exit modem_boot_device_spi_exit(void) +{ + spi_unregister_driver(&modem_boot_device_spi_driver); +} + +module_init(modem_boot_device_spi_init); +module_exit(modem_boot_device_spi_exit); + +MODULE_DESCRIPTION("SPI Driver for Downloading Modem Bootloader"); +MODULE_LICENSE("GPL"); + |