Add support for reading and writing Micrel ks8995m managed switch register set from user space over the SPI bus.
Signed-off-by: Grant Likely <grant.likely at gdcanada.com> diff -ruNp linux-spi-mpc5200/drivers/spi/Kconfig linux-spi-mpc5200-ks8995m/drivers/spi/Kconfig --- linux-spi-mpc5200/drivers/spi/Kconfig 2005-07-22 17:16:13.000000000 -0400 +++ linux-spi-mpc5200-ks8995m/drivers/spi/Kconfig 2005-07-23 02:59:02.000000000 -0400 @@ -30,5 +30,12 @@ config SPI_BUS_MPC52XX_PSC comment "SPI slaves" depends on SPI +config KS8995M + tristate "Micrel/Kendin KS8995M Ethernet Switch SPI interface driver" + depends on SPI + help + Say Y here if you need to control a Kendin/Micrel KS8995M managed + Ethernet switch via the SPI interface. + endmenu diff -ruNp linux-spi-mpc5200/drivers/spi/Makefile linux-spi-mpc5200-ks8995m/drivers/spi/Makefile --- linux-spi-mpc5200/drivers/spi/Makefile 2005-07-22 17:16:13.000000000 -0400 +++ linux-spi-mpc5200-ks8995m/drivers/spi/Makefile 2005-07-23 02:59:02.000000000 -0400 @@ -4,3 +4,4 @@ obj-$(CONFIG_SPI) += spi-core.o obj-$(CONFIG_SPI_BUS_MPC52XX_PSC) += spi-mpc52xx-psc.o +obj-$(CONFIG_KS8995M) += ks8995m.o diff -ruNp linux-spi-mpc5200/drivers/spi/ks8995m.c linux-spi-mpc5200-ks8995m/drivers/spi/ks8995m.c --- linux-spi-mpc5200/drivers/spi/ks8995m.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-spi-mpc5200-ks8995m/drivers/spi/ks8995m.c 2005-07-23 03:10:37.000000000 -0400 @@ -0,0 +1,237 @@ +/* + * drivers/spi/ks8995m-spi.c + * + * Micrel/Kendin KS8995M managed switch SPI interface driver + * + * Maintainer : Grant Likely <glikely at gmail.com> + * + * This file is in the public domain + */ +#include <linux/device.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/cdev.h> +#include <linux/interrupt.h> +#include <linux/spi.h> + +#include <asm/io.h> +#include <asm/uaccess.h> + + +#define KS8995M_NUM_REGS (128) + +struct ks8995m_data { + struct cdev cdev; + struct spi_device *spi_dev; + uint8_t in_buff[KS8995M_NUM_REGS+2]; + uint8_t out_buff[KS8995M_NUM_REGS+2]; +}; + +static int ks8995m_major; +static int ks8995m_minor; +static int ks8995m_minor_offset = 0; + +/* Forward method declarations for cdev */ +static int ks8995m_open(struct inode *inode, struct file *filp); +static ssize_t ks8995m_read(struct file *filp, char __user *buff, + size_t count, loff_t *offp); +static ssize_t ks8995m_write(struct file *filp, const char __user *buff, + size_t count, loff_t *offp); +static int ks8995m_release(struct inode *inode, struct file *filp); + +static struct file_operations ks8995m_fops = { + .owner = THIS_MODULE, + .open = ks8995m_open, + .read = ks8995m_read, + .write = ks8995m_write, + .release = ks8995m_release, +}; + +static int ks8995m_open(struct inode *inode, struct file *filp) +{ + struct ks8995m_data *data = container_of(inode->i_cdev, struct ks8995m_data, cdev); + + /* Private data is always set to the ks8995m_data structure */ + filp->private_data = data; + return 0; +} + +static ssize_t ks8995m_check_size(int start, int count) +{ + if ((count < 1) || (start > KS8995M_NUM_REGS)) + return 0; + + if (start + count > KS8995M_NUM_REGS) + count = KS8995M_NUM_REGS - start; + + return count; +} + +static ssize_t ks8995m_read(struct file *filp, char __user *buff, + size_t count, loff_t *offp) +{ + struct ks8995m_data *data = filp->private_data; + struct spi_bus *bus; + int ret; + + count = ks8995m_check_size(*offp, count); + if (!count) + return 0; + + data->out_buff[0] = 0x03; /* read command */ + data->out_buff[1] = *offp; /* address */ + memset(&data->out_buff[2], 0, count); + + bus = to_spi_bus(data->spi_dev->dev.parent); + + ret = bus->ops->transfer(bus, data->spi_dev->id, SPI_CLKEDGE_RISING, + data->in_buff, data->out_buff, count+2); + if (ret) + return ret; + + copy_to_user(buff, &data->in_buff[2], count); + *offp += count; + + return count; +} + +static ssize_t ks8995m_write(struct file *filp, const char __user *buff, + size_t count, loff_t *offp) +{ + struct ks8995m_data *data = filp->private_data; + struct spi_bus *bus; + int ret; + + count = ks8995m_check_size(*offp, count); + if (!count) + return 0; + + data->out_buff[0] = 0x02; /* write command */ + data->out_buff[1] = *offp; /* address */ + copy_from_user(&data->out_buff[2], buff, count); + + bus = to_spi_bus(data->spi_dev->dev.parent); + + ret = bus->ops->transfer(bus, data->spi_dev->id, SPI_CLKEDGE_RISING, + data->in_buff, data->out_buff, count+2); + if (ret) + return ret; + + *offp += count; + + return count; +} + +static int ks8995m_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +static int ks8995m_probe (struct device *dev) +{ + struct ks8995m_data *data; + struct spi_device *spi_dev; + int devno, err; + + spi_dev = to_spi_dev(dev); + printk("ks8995m_probe(dev->bus_id=%s) (name=%s)\n", + dev->bus_id, spi_dev->name); + + data = kmalloc(sizeof(struct ks8995m_data), GFP_KERNEL); + if (!data) + { + printk(KERN_ALERT "Could not allocate driver memory\n"); + return -ENOMEM; + } + memset(data, 0, sizeof(struct ks8995m_data)); + + dev->driver_data = data; + + data->spi_dev = spi_dev; + cdev_init(&data->cdev, &ks8995m_fops); + data->cdev.owner = THIS_MODULE; + data->cdev.ops = &ks8995m_fops; + + devno = MKDEV(ks8995m_major, ks8995m_minor + ks8995m_minor_offset++); + err = cdev_add(&data->cdev, devno, 1); + if (err) + { + printk(KERN_INFO "ks8995m: could not register char dev\n"); + return -1; + } + + return 0; +} + +static int ks8995m_remove (struct device *dev) +{ + struct ks8995m_data *data = dev->driver_data; + + printk("ks8995_spi_remove(dev->bus_id=%s)\n", dev->bus_id); + + cdev_del(&data->cdev); + kfree(data); + dev->driver_data = NULL; + + return 0; +} + +static struct spi_driver ks8995m_driver = { + .name = "ks8995m", + .module = THIS_MODULE, + .driver = { + .probe = ks8995m_probe, + .remove = ks8995m_remove, + }, +}; + +static int __init ks8995m_init(void) +{ + dev_t dev; + int ret; + + printk(KERN_ALERT "ks8995m: initializing\n"); + + if (alloc_chrdev_region(&dev, 0, 128, "ks8995m")) + { + printk(KERN_ALERT "ks8995m: could not allocate chrdev\n"); + goto fail_alloc_chrdev; + } + ks8995m_major = MAJOR(dev); + ks8995m_minor = MINOR(dev); + + printk(KERN_ALERT "ks8995m: allocated chrdev region (%i:%i)!\n", + ks8995m_major, ks8995m_minor); + + ret = spi_driver_register(&ks8995m_driver); + if (ret) + { + printk(KERN_ALERT "ks8995m: could not register driver\n"); + goto fail_register; + } + + + return 0; + +fail_register: + +fail_alloc_chrdev: + return -1; +} + +static void __exit ks8995m_exit(void) +{ + int devno = MKDEV(ks8995m_major, ks8995m_minor); + + spi_driver_unregister(&ks8995m_driver); + unregister_chrdev_region(devno, 128); + + printk(KERN_ALERT "ks8995m: exiting\n"); +} + +module_init(ks8995m_init); +module_exit(ks8995m_exit); + +MODULE_AUTHOR("Grant Likely <glikely at gmail.com>"); +MODULE_DESCRIPTION("SPI register interface for ks8995m managed switch"); +MODULE_LICENSE("Dual BSD/GPL");