/********************************************************************
*
* Description: spi.c
*
*       Driver, implementing the SPI and MicroWire bus on a parallel
*       port. Used to interface to a lot of chips, like ADCs, DACs
*       and so on. Chips are available from Motorola, National
*       Semiconductor, Maxim, Harris and others.
*
* Author: Ulrich Paul <upaul@paul.de>
* Created on: Monday, 29 Oct 2001, 14:00:00 MET DST
* License: GPL Ver. 2
*    
* Copyright (c) 2001 Ulrich Paul <upaul@paul.de> All rights reserved.
*
* Modified by Paul Corner <paul_c@users.sourceforge.net> on
*       Friday, 30 March 2007.
*
*   Only 2.6.x series kernels are now supported !!
*
*   Extensive additions to utilise the parport subsystem allowing use of
*   PCI cards complete with port address checks. If an invalid address is
*   used, the module will reject it and fail to load gracefully.
*
*   Switched to the hotplug subsystem for creation/destruction of /dev/spi.
*   This requires a rule to be added to /etc/udev if non-root users are to
*   access the device.
*
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
********************************************************************/
/* SPI driver
   Version 2.02 (Bug fixed in cleanup_module) 29.Oct.2001
*/

/* SPI driver
   Version 3.00 Ported to 2.6 series kernels. 30.March.2007
*/

#include <linux/module.h>
#include <linux/version.h>
#include <linux/fs.h>

#include <linux/slab.h>
#include <linux/fcntl.h>
#include <asm/uaccess.h>

#include <linux/miscdevice.h>
#include <linux/parport.h>
#include <asm/io.h>

#include "spi.h"

#define SPI_BASE base
#define SPI_DATA base
#define SPI_STAT base+1
#define SPI_CNTL base+2

#define SPI_IDLE 3		/* idle state of MOSI and SCK is low */
#define SPI_NOCE idle_ce
#define SPI_CLK 2		/* bit 1 of command reg (AUTO_LF) */
#define SPI_DIN_LINE 0x80	/* bit 7 of status reg (BUSY) */

static int (*spi_bit_driver) (int);
static int openflag;
static unsigned char spi_ctl_bck;
static int base = 0x378;	/* port to use per default */
static int idle_ce = 0xff;	/* set all CEs high per default */

static unsigned char *spi_buf = NULL;	/* pointer to the buffer in use */
static int spi_buf_len = 0;	/* len of allocated buffer */

module_param(base, int, S_IRUGO);
MODULE_PARM_DESC(base, " Base address for the parallel port.");

module_param(idle_ce, int, S_IRUGO);
MODULE_PARM_DESC(base, " status of the CE-lines when the bus is not in use");

MODULE_AUTHOR("Ulrich Paul <upaul@paul.de>, Paul Corner <paul_c@users.sourceforge.net>");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("3.00");
MODULE_DESCRIPTION("SPI and MicroWire bus on a parallel port");


/* Some support routines first */

static int spi_transfer_byte(int databyte)
/* spi_transfer_byte sends one byte over the SPI bus and concurrently
   receives one (as specified in the SPI specs). The MSB is 
   transferred first.
   It expects the byte in the low byte of the int and returns the
   result in the low byte. The reason, that ints are used is to
   simplify the code.
*/
{
    int i;			/* the counter */
    int inval = 0;		/* temp. var for the byte to read in */
    int outval;			/* same for byte to send */
    for (i = 0; i < 8; i++) {	/* loop 8 times */
	inval = inval << 1;	/* shift new bit up */
	databyte = databyte << 1;	/* shift MSB of lower byte into HB */
	outval = (databyte & 0x100) >> 8;	/* mask bit, shift to LSB */
	inval = inval | (spi_bit_driver(outval) == 0 ? 1 : 0);	/* transfer bit in MSB */
    }				/*end-for *//* one byte is transferred */

    return inval;
}


static int pha0_driver(int bit)

/* this is the driver for any microwire device.
It sends a one if "bit" is not zero and returns a non-zero value
if it read in a one. The bit set in the return value is not defined! If a zero
value is returned, a zero was read in and if a non-zero value is returned a one
was read in. 
*/
{
    int inxx;			/* we must keep the bit for a while */

    outb_p(spi_ctl_bck ^ bit, SPI_CNTL);	/* put out bit, deactivate SCK */
    inxx = inb_p(SPI_STAT) & SPI_DIN_LINE;	/* read bit before clock pulse */
    outb_p((spi_ctl_bck ^ bit) ^ SPI_CLK, SPI_CNTL);	/* activate SCK */
    return inxx;
}


static int pha1_driver(int bit)

/* this is the driver for any SPI device.
It sends a one if "bit" is not zero and returns a non-zero value
if it read in a one. The bit set in the return value is not defined! If a zero
value is returned, a zero was read in and if a non-zero value is returned a one
was read in. 
*/
{
    int tmpo;
    tmpo = spi_ctl_bck ^ bit;	/* we need it more than once */
    outb_p(tmpo, SPI_CNTL);	/* out bit, deactivate SCK */
    outb_p(tmpo ^ SPI_CLK, SPI_CNTL);	/* out bit, activate SCK */
    outb_p(tmpo, SPI_CNTL);	/* put out bit, deactivate SCK */
    outb_p(tmpo, SPI_CNTL);	/* again, to let line settle */
    return inb_p(SPI_STAT) & SPI_DIN_LINE;	/* read bit after clock pulse */
}


static int select_proto(int proto)
{
    int retval = 0;

    switch (proto) {
    case SEL_SPI_CPOL_1:
	spi_bit_driver = pha1_driver;	/* set the correct bit driver */
	spi_ctl_bck = spi_ctl_bck & ~SPI_CLK;	/* set clock high */
	outb_p(spi_ctl_bck, SPI_CNTL);
	break;
    case SEL_SPI_CPOL_0:
	spi_bit_driver = pha1_driver;	/* set the correct bit driver */
	spi_ctl_bck = spi_ctl_bck | SPI_CLK;	/* set clock low */
	outb_p(spi_ctl_bck, SPI_CNTL);
	break;
    case SEL_UWIRE_CPOL_0:
	spi_bit_driver = pha0_driver;	/* set the correct bit driver */
	spi_ctl_bck = spi_ctl_bck | SPI_CLK;	/* set clock low */
	outb_p(spi_ctl_bck, SPI_CNTL);
	break;
    case SEL_UWIRE_CPOL_1:
	spi_bit_driver = pha0_driver;	/* set the correct bit driver */
	spi_ctl_bck = spi_ctl_bck & ~SPI_CLK;	/* set clock high */
	outb_p(spi_ctl_bck, SPI_CNTL);
	break;
    default:
	retval = -EINVAL;
    }
    return retval;
}


/* here come the driver functions now */

static int spi_reset(void)
{
    outb_p(SPI_NOCE, SPI_DATA);	/* set all CEs incative */
    outb_p(SPI_IDLE, SPI_CNTL);
    spi_ctl_bck = SPI_IDLE;	/* remember the default idle state */
    return (inb_p(SPI_STAT));	/* return status, just for fun */
}


/* write data in buffer to spi bus and concurrently read in new.
The buffer must have the following structure in it: (1 byte per entry)
___________________________________________
| protocol specifier in the least low bits | 
|------------------------------------------|
|     CE settings to use at the start      |
|------------------------------------------|
|             data byte 1                  |
|------------------------------------------|
|             data byte 2                  |
|------------------------------------------|
|                 ...                      |
|------------------------------------------|
|            last data byte                |
|------------------------------------------|
|  CE settings, that are to left behind    |
--------------------------------------------
	   
As there must be at least one data byte the minimum count is 4
(3 bytes overhead and at least one data byte)
The starting CE settings are applied just before the first byte
is transferred. The ending CE settings are applied after the last
data byte is transferred. In a one byte message the starting
setting adresses a chip and the ending deadresses it. When talking
to chips that need multibyte transfers with a pause inbetween, 
however, the start settings of the whole action adresses the chip
as usual, but the ending settings of any intermediate transfer
leaves the chip adressed.
	   
Each byte received supersedes the corresponding byte sent in the
buffer. Thus the buffer contains the received data bytes on
return from this function. As there are always exactly as many
bytes received as sent, there cannot be an overflow of the buffer.
*/

static ssize_t spi_write(struct file *file, const char __user * buf,
			 size_t count, loff_t * ppos)
{
    int tmp;
    int i, err;
    unsigned char *bufptr;

    if (count < 4)
	return -EINVAL;		/* must be at least 4 bytes in buf */
    if (spi_buf != NULL) {	/* buffer already allocated? */
	if (spi_buf_len < count) {	/* yes, new write bigger than last? */
	    kfree(spi_buf);	/* yes, free the old buffer */
	    spi_buf_len = count;	/* save requested len for the read */
	    spi_buf = kmalloc(count, GFP_KERNEL);	/* allocate new buffer */
	}			/* old buffer is big enough for write */
    } else {			/* no buffer allocated */
	spi_buf_len = count;	/* save requested len for the read */
	spi_buf = kmalloc(count, GFP_KERNEL);	/* allocate buffer */
    }
    err = copy_from_user(spi_buf, buf, count);	/* copy data from user space */
    if (err) {
	kfree(spi_buf);
	return -EFAULT;
    }

    bufptr = spi_buf;		/* point working pointer to our buf */
    tmp = select_proto(*bufptr++);	/* set the protocol */
    if (tmp == -EINVAL) {	/* wrong protocol specified */
	kfree(spi_buf);		/* free buf */
	spi_buf = NULL;		/* invalidate pointer to buf */
	spi_buf_len = 0;	/* set len of buf to zero */
	return -EINVAL;
    }
    outb_p(*bufptr++, SPI_DATA);	/* set the CEs */

    for (i = count - 3; i > 0; i--) {
	tmp = spi_transfer_byte(*bufptr);	/* transfer a byte over the bus */
	*bufptr++ = tmp;;	/* write returned  byte to buffer */
    }				/*end-for *//* all data bytes transferred now */

    outb_p(spi_ctl_bck, SPI_CNTL);	/* deactivate SCK */
    outb_p(*bufptr, SPI_DATA);	/* the ending CE settings */
    return count;		/* we can always send all data */
}


static ssize_t spi_read(struct file *file, char __user * buf, size_t count,
			loff_t * ppos)
{
    int len, err;

    if (spi_buf == NULL)
	return 0;		/* no bytes read when buf invalid */

    if (count > spi_buf_len)
	len = spi_buf_len;
    else
	len = count;		/* prevent reading beyond buf */

    err = copy_to_user(buf, spi_buf, len);	/* send the complete buf to user */
    kfree(spi_buf);		/* dealloc buffer */
    if (err)
	return -EFAULT;
    spi_buf = NULL;		/* invalidate pointer to buffer */
    spi_buf_len = 0;		/* set len of buf to zero */
    return len;
}


static int spi_open(struct inode *inode, struct file *file)
{
    if (openflag)
	return -EBUSY;		/* only one access at a time */
    openflag = 1;		/* mark it already opened */
    return 0;
}

static int spi_release(struct inode *inode, struct file *file)
{
    openflag = 0;
    return 0;
}

static void dummy_func(struct parport *ppt)
{
    return;
}

/* static struct file_operations spi_fops  */
static struct file_operations spi_fops = {
    .owner = THIS_MODULE,
    .read = spi_read,
    .write = spi_write,
    .open = spi_open,
    .release = spi_release,
};

static struct miscdevice spi_dev = {
    .name = "spi",
    .minor = MISC_DYNAMIC_MINOR,
    .fops = &spi_fops,
};

static struct pardevice *spi_device = NULL;

static struct parport_driver spi_ppt = {
    .name = THIS_MODULE->name,
    .attach = dummy_func,
    .detach = dummy_func,
};

static void spi_cleanup(void)
{
    parport_release(spi_device);
    parport_unregister_device(spi_device);
    parport_unregister_driver(&spi_ppt);

    if (spi_buf != NULL)
	kfree(spi_buf);
}

static int __init spi_claim_port(void)
{
    struct parport *ppt;

    /* reserve our port, but check first if free */

    /* Register with the parport subsystem */
    if (parport_register_driver(&spi_ppt)) {
	printk(KERN_ERR "SPI: unable to register with parport\n");
	return -EIO;
    }

    ppt = parport_find_base(SPI_BASE);

    if (ppt == NULL) {
	printk(KERN_ERR "SPI: no parport found at 0x%x\n", SPI_BASE);
	parport_unregister_driver(&spi_ppt);
	return -ENODEV;
    }

    spi_device = parport_register_device(ppt, THIS_MODULE->name, NULL, NULL, NULL, 0, NULL);
    parport_put_port(ppt);

    if (!spi_device) {
	parport_unregister_driver(&spi_ppt);
	return -ENODEV;
    }

    if (parport_claim(spi_device)) {
	printk(KERN_ERR "SPI: %s busy. Can not claim.\n", spi_device->port->name);
	parport_unregister_device(spi_device);
	parport_unregister_driver(&spi_ppt);
	return -EBUSY;
    }
    return 0;
}

static void __exit spi_module_exit(void)
{
    misc_deregister(&spi_dev);
    spi_cleanup();
}


static int __init spi_module_init(void)
{
    int result = spi_claim_port();

    if (result)
        return result; 

    /* write to port & read back to check */
    outb_p(0, SPI_DATA);
    if (inb_p(SPI_DATA)) {
	spi_cleanup();
	printk("SPI: no chips at 0x%04x \n", SPI_BASE);
	return -EINVAL;
    }

	spi_ctl_bck = SPI_IDLE;
	spi_reset();
	printk("SPI bus at 0x%04x \n", SPI_BASE);

    result = misc_register(&spi_dev);
    if (result) {
	printk("SPI: unable to register /dev/spi for SPI bus\n");
	spi_cleanup();
    }
    return result;
}

module_init(spi_module_init);
module_exit(spi_module_exit);

