/**
 * ADS1292R - Linux Kernel Driver Module
 * Licensed under the GPL-2
 */

#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/spi/spi.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/of_irq.h>
#include <linux/of_device.h>
#include <linux/of_address.h>

#include "ads1292.h"

#define ADS_DEVICE_NAME		"ads1292_cdev"
#define SPI_BUFF_SIZE		64
#define SPI_BUS_SPEED_SLOW	100000
#define SPI_BUS_SPEED_FAST	15000000
#define ACQ_BUF_SIZE		9


struct ads1292_chip{
	struct spi_device *spi;
	char *rx_buff;
	char *tx_buff;
	struct spi_message msg;
	struct spi_transfer transfer;
};

struct ads1292_dev {
	struct cdev dev;
	dev_t dev_no;
	struct class *cl;
	struct ads1292_chip chip;

	struct mutex mutex;
	struct semaphore fop_sem;
	wait_queue_head_t wait_queue;

	int irq;
	int gpio_initialized;
	volatile int rx_count;
	volatile int sample_req;
};
static struct ads1292_dev ads_inst;

struct gpio ads1292_gpio_reset = { .label = "reset-gpio", .flags = GPIOF_OUT_INIT_HIGH };
struct gpio ads1292_gpio_start = { .label = "start-gpio", .flags = GPIOF_OUT_INIT_LOW };

static void ads1292_spi_xfer_complete(void *arg)
{
	struct ads1292_dev *ads = arg;
	pr_debug("%s", __func__);
	ads->rx_count = 1;
	wake_up_interruptible(&ads->wait_queue);
}

static irqreturn_t ads1292_irq_handler(int irq, void *id)
{
	pr_debug("%s", __func__);
	if (!ads_inst.sample_req)
		return IRQ_HANDLED;
	ads_inst.sample_req = 0;
	spi_async(ads_inst.chip.spi, &ads_inst.chip.msg);
	return IRQ_HANDLED;
}

static int ads1292_init_io_from_dt(struct device *dev, struct gpio *pgpio)
{
	int ret;

	/* node to get the information of a device tree node*/
	struct device_node *np = dev->of_node;
    
    	/* Get a GPIO number to use with GPIO API */
	pgpio->gpio = of_get_named_gpio(np, pgpio->label, 0);
	//printk(KERN_DEBUG "ads1292: %s: %d\n", pgpio->label, pgpio->gpio);

	/* check if gpio is valid on the system */
	if (!gpio_is_valid(pgpio->gpio)) {
		pr_err("ads1292: No %s gpio", pgpio->label);
		return -EINVAL;
	} else {
        	/* request a single GPIO with initial setup */
		ret = devm_gpio_request_one(dev, pgpio->gpio, pgpio->flags, pgpio->label);
		if (ret < 0) {
			pr_err("ads1292: Error requesting gpio %s:%d",
				pgpio->label, pgpio->gpio);
			return ret;
		}
	}

	return 0;
}

static int ads1292_init_gpio_pins(struct ads1292_dev *ads, struct device *dev, struct spi_device *spi)
{
	int ret;
    	
	if (ads->gpio_initialized == 0) {
		
		ret = ads1292_init_io_from_dt(dev, &ads1292_gpio_reset);
		if (ret < 0)
			goto error_exit;

		ret = ads1292_init_io_from_dt(dev, &ads1292_gpio_start);
		if (ret < 0)
			goto error_exit;

		if (spi->irq < 0) {
			pr_err("ads1292: IRQ resource missing");
			ret = -EINVAL;
			goto error_exit;
		} else {
 			pr_info("ads1292: IRQ: %d", spi->irq);
		}
	
		ret = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, 
				ads1292_irq_handler,
				IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
				"ads1292-drdy",
				ads);
		if (ret) {
			pr_err("ads1292: Cannot claim IRQ: %d", spi->irq);
			ret = -EINVAL;
			goto error_exit;
		}
		ads->gpio_initialized = 1;
	
	}
	return ads->gpio_initialized;

error_exit:
	ads->gpio_initialized = ret;
	return ret;
}


static int ads_send_byte(struct ads1292_chip *chip, int data)
{
	struct spi_message msg = { };
	struct spi_transfer transfer = { };
	//struct device *dev = &chip->spi->dev;
	int status;

    	/* zero each element in the structure and init transfer list */
	spi_message_init(&msg);
	chip->tx_buff[0] = data;
	transfer.tx_buf = chip->tx_buff;
	transfer.rx_buf = chip->rx_buff;
	transfer.len = 1;
	transfer.speed_hz = SPI_BUS_SPEED_SLOW;
    	/* enqueuing the transfer into the transfer list */
	spi_message_add_tail(&transfer, &msg);
	/* cann't be used in an atomic context as it can sleep */
    	status = spi_sync(chip->spi, &msg);
	//dev_info(dev, "%s (%#x) %d\n", __func__, data, status);
	return status;
}

static int ads_read_registers(struct ads1292_chip *chip, u8 reg, u8 size)
{
	struct spi_message msg = { };
	struct spi_transfer transfer = { };
	//struct device *dev = &chip->spi->dev;
	int ret;

	spi_message_init(&msg);
	chip->tx_buff[0] = ADS1292_RREG | reg;
	chip->tx_buff[1] = size - 1; 			// datasheet: no of regs - 1
	memset(chip->rx_buff + 2, 0, size);
	memset(chip->tx_buff + 2, 0, size);
	transfer.speed_hz = SPI_BUS_SPEED_SLOW;
	transfer.tx_buf = chip->tx_buff;
	transfer.rx_buf = chip->rx_buff;
	transfer.len = size + 2;			// 2 is for command tx
	transfer.delay_usecs = 40; 			// datasheet: 4 clocks after each message
	transfer.cs_change = 1; 			// Always toggle CS line after transfer
	spi_message_add_tail(&transfer, &msg);
	ret = spi_sync(chip->spi, &msg);
	//dev_info(dev, "%s(%#x,%d) %#x .. -> %d\n",
	//	__func__, reg, size, chip->rx_buff[2], ret);
	if (ret<0) {
		pr_err("%s - spi_sync failed!", __func__);
		return ret;
	}
	return ret; 
}

static int ads_send_RREG(struct ads1292_chip *chip, char __user * buf, u8 reg, u8 size)
{
	int status;
	status = ads_read_registers(chip, reg, size);
	if (unlikely(status))
		return status;
	status = copy_to_user(buf, chip->rx_buff + 2, size);
	return status;
}

static int ads_send_WREG(struct ads1292_chip *chip, char __user * buf, u8 reg, u8 size)
{
	struct spi_message msg = { };
	struct spi_transfer transfer = { };
	//struct device *dev = &chip->spi->dev;
	int ret;
	
	spi_message_init(&msg);
	chip->tx_buff[0] = ADS1292_WREG | reg;
	chip->tx_buff[1] = size - 1;
	ret = copy_from_user(chip->tx_buff + 2, buf, size);
	if (unlikely(ret))
		return ret;
	transfer.speed_hz = SPI_BUS_SPEED_SLOW;
	transfer.tx_buf = chip->tx_buff;
	transfer.rx_buf = chip->rx_buff;
	transfer.len = size + 2;
	transfer.delay_usecs = 50;
	transfer.cs_change = 1;
	spi_message_add_tail(&transfer, &msg);
	ret = spi_sync(chip->spi, &msg);
	//dev_info(dev, "%s(%#x,%d) %#x .. -> %d\n",
	//	__func__, reg, size, chip->tx_buff[2], ret);
	if (ret<0) {
		pr_err("%s - spi_sync failed!", __func__);
		return ret;
	}
	return ret;
}


static loff_t ads_cdev_llseek(struct file *filp, loff_t off, int whence)
{
	pr_debug("ads1292: %s", __func__);
	return 0;
}

static ssize_t ads_cdev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
	struct ads1292_dev *ads = filp->private_data;
	struct device *dev = &ads->chip.spi->dev;
	int status, bytes_send=0;

	if (*f_pos > 0)
		return 0;

	if (unlikely(down_interruptible(&ads->fop_sem)))
		return -ERESTARTSYS;

	dev_dbg(dev, "%s %ld", __func__, count);

	ads->chip.tx_buff[0] = ADS1292_RDATA;
	memset(ads->chip.tx_buff+1, 0, ACQ_BUF_SIZE);
	memset(ads->chip.rx_buff+1, 0, ACQ_BUF_SIZE);
	ads->chip.transfer.tx_buf = ads->chip.tx_buff;
	ads->chip.transfer.rx_buf = ads->chip.rx_buff;
	ads->chip.transfer.len = ACQ_BUF_SIZE + 1;
	ads->chip.transfer.speed_hz = SPI_BUS_SPEED_FAST;
	ads->chip.transfer.cs_change = 1;
	spi_message_init(&ads->chip.msg);
	spi_message_add_tail(&ads->chip.transfer, &ads->chip.msg);
	ads->chip.msg.context = ads;
	ads->chip.msg.complete = ads1292_spi_xfer_complete;

	dev_dbg(dev, "Setting chip in RDATAC");
	ads_send_byte(&ads->chip, ADS1292_RDATAC);
	gpio_set_value_cansleep(ads1292_gpio_start.gpio, 1);
	up(&ads->fop_sem);

	while (bytes_send <= (count - ACQ_BUF_SIZE)) 
	{
		ads->rx_count = 0;
		ads->sample_req = 1;
		status = wait_event_interruptible_timeout(ads->wait_queue, ads->rx_count == 1, HZ*1);
		ads->sample_req = 0;
		if (unlikely(status <= 0))
		{
			if (status == 0)
				status = -ETIMEDOUT;	// 1s without any data -> report timeout
			goto read_error;
		}
		if (unlikely(copy_to_user(&buf[bytes_send], ads->chip.rx_buff, ACQ_BUF_SIZE)))
		{
			status = -EFAULT;
			goto read_error;
		}
		bytes_send += ACQ_BUF_SIZE;
	}

	ads_send_byte(&ads->chip, ADS1292_SDATAC);
	dev_dbg(dev, "Setting chip in SDATAC");
	*f_pos += bytes_send;
	return bytes_send;

read_error:
	return status;
}

static ssize_t ads_cdev_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
{
	//pr_info("ads1292: %s\n", __func__);
	struct ads1292_dev *ads = filp->private_data;
	struct device *dev = &ads->chip.spi->dev;
	struct spi_message msg;
	struct spi_transfer transfer;
	int status = -ENODEV;

	dev_dbg(dev, "%s %ld", __func__, count);

	if (unlikely(down_interruptible(&ads->fop_sem)))
		return -ERESTARTSYS;

	if (count > SPI_BUFF_SIZE)
		count = SPI_BUFF_SIZE;

	if (unlikely(copy_from_user(ads->chip.tx_buff, buf, count)))
	{
		status = -EFAULT;
	}
	else
	{
		spi_message_init(&msg);
		transfer.tx_buf = ads->chip.tx_buff;
		transfer.rx_buf = ads->chip.rx_buff;
		transfer.len = count;
		transfer.speed_hz = SPI_BUS_SPEED_SLOW;
		spi_message_add_tail(&transfer, &msg);
		status = spi_sync(ads->chip.spi, &msg);
		if (status == 0)
		{
			status = count;
		}
	}

	up(&ads->fop_sem);
	return status;
}

static long ads_cdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	struct ads1292_dev *ads = filp->private_data;
	struct device *dev = &ads->chip.spi->dev;
	int status, cmd_nr, cmd_size;
	//dev_dbg(dev, "%s %#x\n", __func__, cmd);
	
    	if (_IOC_TYPE(cmd) != ADS1292_IOC_MAGIC)
	{
		dev_err(dev, "failed to decode ioctl!");
		return -ENOTTY;
	}

	if (dev == NULL)
	{
		dev_err(dev, "ioctl without device!");
		return -ENODEV;
	}

	if (unlikely(down_interruptible(&ads->fop_sem)))
		return -ERESTARTSYS;

	cmd_nr = _IOC_NR(cmd) & 0x7F;
	cmd_size = _IOC_SIZE(cmd) & 0x1F;
	status = -ENODEV;
	dev_dbg(dev, "%s %#x %d %#lx", __func__, cmd_nr, cmd_size, arg);

	if (cmd_nr < ADS1292_RREG)
	{
		if ((cmd_nr & ADS1292_CMD_MASK) == ADS1292_SDATAC) 
		{
			dev_info(dev, "%s gpio start=0", __func__);
			gpio_set_value_cansleep(ads1292_gpio_start.gpio, 0);
		}
		status = ads_send_byte(&ads->chip, cmd_nr);
		if ((cmd_nr & ADS1292_CMD_MASK) == ADS1292_RDATAC) 
		{
			dev_info(dev, "%s gpio start=1", __func__);
			gpio_set_value_cansleep(ads1292_gpio_start.gpio, 1);
		}
	}
	else
	{
		if (gpio_get_value(ads1292_gpio_start.gpio))
		{
			dev_warn(dev, "Cannot change registers during aquisition, send SDATAC first");
			status = -EBUSY;
		}
		else
		{
			switch (cmd_nr & (ADS1292_RREG | ADS1292_WREG))
			{
				case ADS1292_RREG:
					dev_dbg(dev, "in RREG");
					status = ads_send_RREG(&ads->chip, (char __user *)arg, cmd_nr, cmd_size);
					break;
				case ADS1292_WREG:
					dev_dbg(dev, "in WREG");
					status = ads_send_WREG(&ads->chip, (char __user *)arg, cmd_nr, cmd_size);		
					break;
				default:
					dev_dbg(dev, "in default");
					status = -ENOTTY;
					break;
			}
		}
	}

	up(&ads->fop_sem);
	return status;
}

static int ads_cdev_open(struct inode *inode, struct file *filp)
{
	struct ads1292_dev *ads = container_of(inode->i_cdev, struct ads1292_dev, dev);
	struct device *dev = &ads->chip.spi->dev;
	dev_dbg(dev, "%s", __func__);
	if (down_interruptible(&ads->fop_sem))
		return -ERESTARTSYS;
	filp->private_data = ads;
	/* stop data conversion */
	ads_send_byte(&ads->chip, ADS1292_SDATAC);
	up(&ads->fop_sem);	
	return 0;
}

static int ads_cdev_release(struct inode *inode, struct file *filp)
{
	struct ads1292_dev *ads = filp->private_data;
	struct device *dev = &ads->chip.spi->dev;
	dev_dbg(dev, "%s", __func__);
	if (down_interruptible(&ads->fop_sem))
		return -ERESTARTSYS;
	gpio_set_value_cansleep(ads1292_gpio_start.gpio, 0);
	up(&ads->fop_sem);
	return 0;
}

static const struct file_operations fops = {
	.owner = THIS_MODULE,
	.llseek = ads_cdev_llseek,
	.read = ads_cdev_read,
	.write = ads_cdev_write,
	.unlocked_ioctl = ads_cdev_ioctl,
	.open = ads_cdev_open,
	.release = ads_cdev_release,
};

static int ads1292_init_cdev(struct ads1292_dev *ads)
{
	int ret;

	/**
	 * Allocates a range of char device numbers. The major number will be chosen dynamically,
	 * and returned (along with the first minor number) in dev. Returns zero or a negative error code
	 */
	ret = alloc_chrdev_region( &ads->dev_no, 0, 1, ADS_DEVICE_NAME);
	if (ret < 0) {
		pr_err("ads1292: alloc_chrdev_region failed!");
		goto error_exit;
	}

    	/* creates a class directory under /sys/class/ */
	ads->cl = class_create(THIS_MODULE, ADS_DEVICE_NAME);
	if (ads->cl == NULL) {
		pr_err("ads1292: Cannot create class!");
		goto cleanup_region;
	}

    	/* creates a device and registers it with sysfs */
	if (device_create(ads->cl, NULL, ads->dev_no, NULL, ADS_DEVICE_NAME) == NULL) {
		pr_err("ads1292: Cannot create character device!");
		goto cleanup_class;
	}
    
    	/* Initializes cdev, remembering fops, making it ready to add to the system with cdev_add */
	cdev_init(&ads->dev, &fops );
	ret = cdev_add(&ads->dev, ads->dev_no, 1);
	if (ret < 0) {
		pr_err("ads1292: Cannot add character device!");
		goto cleanup_dev;
	}
	return 0;

cleanup_dev:
	device_destroy(ads->cl, ads->dev_no);
cleanup_class:
	class_destroy(ads->cl);
cleanup_region:
	unregister_chrdev_region(ads->dev_no, 1);
error_exit:
	return ret;
}

static int ads1292_probe(struct spi_device *spi)
{
	struct ads1292_chip *chip;
	int ret;
	char id;

	chip = &ads_inst.chip;
	chip->spi = spi;
	mutex_lock(&ads_inst.mutex);

	/**
	 * resource-managed kzalloc - memory allocated with this function is automatically 
	 * freed on driver detach. returns pointer to allocated memory on success, NULL on failure.
         */
	chip->rx_buff = devm_kzalloc(&spi->dev, SPI_BUFF_SIZE, GFP_KERNEL | GFP_DMA);
	if (chip->rx_buff == NULL) {
		ret = -ENOMEM;
		goto error_exit;
	}
	chip->tx_buff = devm_kzalloc(&spi->dev, SPI_BUFF_SIZE, GFP_KERNEL | GFP_DMA);
	if (chip->tx_buff == NULL) {
		ret = -ENOMEM;
		goto error_exit;
	}

	ret = ads1292_init_gpio_pins(&ads_inst, &spi->dev, spi);
	if (ret < 0)
		goto error_exit;

	/* read chip id read to check if chip present */
	if (gpio_is_valid(ads1292_gpio_reset.gpio)) {
		dev_info(&spi->dev, "sending reset pulse\n");
		gpio_set_value_cansleep(ads1292_gpio_reset.gpio, 1);
		gpio_set_value(ads1292_gpio_reset.gpio, 0);
		udelay(10);
		gpio_set_value(ads1292_gpio_reset.gpio, 1);
	} else {
		ads_send_byte(chip, ADS1292_RESET);
	}
	udelay(10);

	ads_send_byte(chip, ADS1292_SDATAC);
	ads_send_RREG(chip, &id, ADS1292_ID, sizeof(id));
	if (id != 0x73) {
		dev_err(&spi->dev, "probe failed! id = %#x", id);
		return -ENODEV;
	}

	dev_info(&spi->dev, "probe successful");
	mutex_unlock(&ads_inst.mutex);
	return 0;

error_exit:
	mutex_unlock(&ads_inst.mutex);
	return ret;
}

static int ads1292_remove(struct spi_device *spi)
{
	mutex_lock(&ads_inst.mutex);
	mutex_unlock(&ads_inst.mutex);
	pr_info("ads1292: remove successfully.");
	return 0;
}

/* to match devices in the DT */
static const struct of_device_id ads1292_of_match[] = {
	{ .compatible = "ti,ads1292" },
	{ }
};
MODULE_DEVICE_TABLE(of, ads1292_of_match);

/**
 * declare a device ids supported by this driver
 * in order to inform the SPI core about the device ID
 * we need to manage in the driver, and 
 * call the MODULE_DEVICE_TABLE macro on the driver structure
 */
static const struct spi_device_id ads1292_id[] = {
	{"ADS1292", 0},	/* {name, driver_data} */
	{ }
};
MODULE_DEVICE_TABLE(spi, ads1292_id);

/* represents the driver developed to manage our SPI device */
static struct spi_driver ads1292_driver = {
	.driver = {
		.name	= "ads1292",
		.of_match_table = ads1292_of_match,
		.owner	= THIS_MODULE,
	},
	.probe		= ads1292_probe,
	.remove		= ads1292_remove,
	.id_table	= ads1292_id,
};

static int __init ads1292_init(void)
{
	int ret;
	
	/* init mutex */
	mutex_init(&ads_inst.mutex);
	/* init semaphore(s) */
	sema_init(&ads_inst.fop_sem, 1);

	ads_inst.gpio_initialized = 0;
	ads_inst.chip.spi = NULL;
	ads_inst.sample_req = 0;

	ret = ads1292_init_cdev(&ads_inst);
	if (ret < 0) {
		pr_err("%s cdev init failed!", __func__);
		goto error_exit;
	}

	init_waitqueue_head(&ads_inst.wait_queue);

	ret = spi_register_driver(&ads1292_driver);
	if (ret < 0) {
		pr_err("ads1292: spi driver registration failed!");
		goto error_exit;
	}
	pr_info("%s successfully", __func__);
	return 0;

error_exit:
	return ret;
}
subsys_initcall(ads1292_init);

static void __exit ads1292_exit(void)
{
	devm_free_irq(&ads_inst.chip.spi->dev, ads_inst.irq, NULL);
	cdev_del(&ads_inst.dev);
	device_destroy(ads_inst.cl, ads_inst.dev_no);
	class_destroy(ads_inst.cl);
	unregister_chrdev_region(ads_inst.dev_no, 1);
	spi_unregister_driver(&ads1292_driver);
}
module_exit(ads1292_exit);

MODULE_AUTHOR("Vijay Prajapati <vijay.prajapati@teksun.com>");
MODULE_DESCRIPTION("TI ADS1292R - Low-Power, 2-Channel, 24-Bit AFE for Biopotential Measurements");
MODULE_LICENSE("GPL v2");
