aboutsummaryrefslogtreecommitdiff
path: root/drivers/scp
diff options
context:
space:
mode:
authorDavid Barksdale <amatus@amatus.name>2014-08-13 16:14:13 -0500
committerDavid Barksdale <amatus@amatus.name>2014-08-13 16:14:13 -0500
commitace6c6d243016e272050787c14e27a83ecd94a25 (patch)
treec837edb1ca98b2552fbc7edba47aeb63f98ca1f0 /drivers/scp
parent1b6e1688bd215cd7c9cb75650fa815a1ec6567e1 (diff)
Diffstat (limited to 'drivers/scp')
-rwxr-xr-xdrivers/scp/Kconfig12
-rwxr-xr-xdrivers/scp/Makefile5
-rwxr-xr-xdrivers/scp/scp-dev.c654
-rwxr-xr-xdrivers/scp/scp-dev.h196
-rwxr-xr-xdrivers/scp/spi_eeprom.c350
5 files changed, 1217 insertions, 0 deletions
diff --git a/drivers/scp/Kconfig b/drivers/scp/Kconfig
new file mode 100755
index 00000000000..b44c6d556cb
--- /dev/null
+++ b/drivers/scp/Kconfig
@@ -0,0 +1,12 @@
+#
+# Character device configuration
+#
+
+menu "SCP support"
+config SCP
+ tristate "SCP device interface"
+ depends on (440EPX || 440GRX || 405EX || 405EXr || APM82181)
+ default n
+ help
+ When selected the SCP device driver is provided.
+endmenu
diff --git a/drivers/scp/Makefile b/drivers/scp/Makefile
new file mode 100755
index 00000000000..8fa43d08906
--- /dev/null
+++ b/drivers/scp/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the kernel spi bus driver.
+#
+amcc_scp-objs := scp-dev.o spi_eeprom.o
+obj-$(CONFIG_SCP) += amcc_scp.o
diff --git a/drivers/scp/scp-dev.c b/drivers/scp/scp-dev.c
new file mode 100755
index 00000000000..b8f286c0342
--- /dev/null
+++ b/drivers/scp/scp-dev.c
@@ -0,0 +1,654 @@
+/*
+ **************************************************************************
+ * drivers/spi/scp-dev.c -- Serial Communications Port
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *
+ * (C) Copyright IBM Corp. 2004 Christophe Lombard <christophe_lombard@fr.ibm.com>
+ *
+ *
+ * Usage:
+ * Transmit data to scp
+ * echo -n "..." > /proc/driver/scp_dev
+ * Example: Write and read the value 0x19 at the address 0x1EE
+
+ echo "Status Read "
+
+ echo -n "CSENA" > /proc/driver/scp_dev # Enable FRAM
+ echo -n "RDSR" > /proc/driver/scp_dev # RDSR: 0x05 to read FRAM status
+ echo -n "0" > /proc/driver/scp_dev
+ echo -n "RXDATA" > /proc/driver/scp_dev # read data
+ echo -n "CSDIS" > /proc/driver/scp_dev # Disable FRAM
+
+
+ #echo "write data "
+
+ echo -n "CSENA" > /proc/driver/scp_dev # Enable FRAM
+ echo -n "WREN" > /proc/driver/scp_dev # WREN: 0x06
+ echo -n "CSDIS" > /proc/driver/scp_dev # Disable FRAM
+
+ echo -n "CSENA" > /proc/driver/scp_dev # Enable FRAM
+ echo -n "WRITE" > /proc/driver/scp_dev # WRITE: 0x02
+ echo -n "01" > /proc/driver/scp_dev # write address on 10 bits. Ex: 0x17EE (max : 3FF)
+ echo -n "EE" > /proc/driver/scp_dev
+ echo -n "19" > /proc/driver/scp_dev # write data on 8 bits. Ex: 19
+ echo -n "CSDIS" > /proc/driver/scp_dev # Disable FRAM
+
+
+ #echo "read data "
+
+ echo -n "CSENA" > /proc/driver/scp_dev # Enable FRAM
+ echo -n "READ" > /proc/driver/scp_dev # READ: 0x03
+ echo -n "1" > /proc/driver/scp_dev # read address on 10 bits. Ex: 0x1EE
+ echo -n "EE" > /proc/driver/scp_dev
+ echo -n "0" > /proc/driver/scp_dev # write dummy data: 0 (Mandatory)
+ echo -n "RXDATA" > /proc/driver/scp_dev # read data
+ echo -n "CSDIS" > /proc/driver/scp_dev # Disable FRAM
+ *
+ * It is impotant to know the example to know how the SCP works.
+ * On Beech board the SPI EEPROM is AT25080A. This affects the max address.
+ * ************************************************************************/
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+#include <linux/platform_device.h>
+#include <asm/dcr-native.h>
+#include <linux/of_platform.h>
+
+#undef SCP_DEBUG
+#define SCP_DEBUG
+#include "scp-dev.h"
+
+/*
+ * If you want debugging uncomment:
+ */
+#ifdef SCP_DEBUG
+int scp_verbose = 1;
+#endif
+
+#define DRIVER_VERSION "0.1"
+
+MODULE_DESCRIPTION("AMCC scp driver v" DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+static int major; /* default is dynamic major device number */
+module_param(major, int, 0);
+MODULE_PARM_DESC(major, "Major device number");
+
+#define MASK_SEL_SCP 0xFFFDFFFF
+#define CS_LOW 0xFFFDFFFF
+#define CS_HIGH 0x00020000
+#define GPIO_MASK 0xFFFFFFF3
+
+static int
+scp_ioctl(struct inode *inode, struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ return 0;
+}
+
+static struct file_operations scp_device_fops = {
+ owner:THIS_MODULE,
+ ioctl:scp_ioctl,
+};
+
+static int scpdev_initialized;
+static void *virtual_scp_add; /* Virtual address to access to the scp core */
+static void *gpio_base;
+
+
+#define scp_readb(addr) in_8((volatile u8 *)(virtual_scp_add + (addr)))
+#define scp_writeb(addr,b) out_8((volatile u8 *)(virtual_scp_add + (addr)), (b))
+
+//static DECLARE_WAIT_QUEUE_HEAD(scp_wait);
+
+/* ***********************************************************************
+ * FUNCTIONS
+ *
+ */
+
+/*
+ **************************************************************************
+ * scp_set_configuration
+ *
+ * Change the configuration of the SCP.
+ * Enable the serial port and the serial clock Phase
+ *
+ * Return nothing.
+ * ************************************************************************/
+void scp_set_configuration(void)
+{
+ /*
+ * SCP Clock Divisor Modulus register
+ */
+ scp_writeb(SCPD_CDM, 0x18);
+
+ /*
+ * SCP Mode register => Serial port Enabled + loopback (for test)
+ */
+ /*
+ * scp_writeb(SCPD_SPMODE, SCPD_EN | SCPD_LOOP | SCPD_SCP);
+ */
+
+ /*
+ * SCP Mode register => Serial port Enabled + Serial Clock Phase
+ */
+ scp_writeb(SCPD_SPMODE, SCPD_EN | SCPD_SCP);
+}
+
+
+/*
+ **************************************************************************
+ + * scp_rx_data
+ + *
+ + * Read the Receive Data Register. Read Only register.
+ + *
+ + * Return unsigned char read
+ + * **********************************************************************/
+unsigned char scp_rx_data(void)
+{
+ unsigned char buffer = 0x00;
+
+ /*
+ * In case of loopback the data are inverted
+ */
+ /*
+ * buffer = ~scp_readb(SCPD_RXDATA);
+ */
+ /*
+ * start the xfer
+ */
+ //scp_writeb(SCPD_CR, SCPD_STR); /* fscz */
+ buffer = (unsigned char) scp_readb(SCPD_RXDATA);
+#ifdef SCP_DEBUG
+ if (scp_verbose)
+ printk("scp_rx_data: receive data: %#X \n", buffer);
+#endif
+
+ return buffer;
+}
+
+
+/*
+ **************************************************************************
+ * scp_tx_data
+ *
+ * Write data from command on the Transmit Data Register.
+ *
+ * Return nothing.
+ * ************************************************************************/
+void scp_tx_data(unsigned char buffer)
+{
+ /*
+ * write data to txdata
+ */
+#ifdef SCP_DEBUG
+ if (scp_verbose)
+ printk("scp_tx_data: Transmit data: %#X \n", buffer);
+#endif
+ scp_writeb(SCPD_TXDATA, buffer);
+
+ /*
+ * start the xfer
+ */
+ scp_writeb(SCPD_CR, SCPD_STR);
+}
+
+/*
+ **************************************************************************
+ * scp_cs
+ *
+ * Enable/Disable Chip Select in the EPLD register (Selection_2_reg).
+ *
+ * Return nothing.
+ * ************************************************************************/
+void scp_cs(int chipid, int high)
+{
+ ulong val;
+#if 1
+ val = in_be32((gpio_base+(GPIO0_OR-GPIO_BASE)));
+ if (!high) {
+ out_be32((gpio_base+(GPIO0_OR-GPIO_BASE)), val && CS_LOW);
+ }
+ else {
+ out_be32((gpio_base+(GPIO0_OR-GPIO_BASE)), val | CS_HIGH );
+ }
+#endif
+/* CPU 32bit not 64bit
+ u64 val;
+
+ val = in_be64((gpio_base+(GPIO0_OR-GPIO_BASE)));
+ if (!high) {
+ out_be64((gpio_base+(GPIO0_OR-GPIO_BASE)), val && CS_LOW);
+ }
+ else {
+ out_be64((gpio_base+(GPIO0_OR-GPIO_BASE)), val | CS_HIGH );
+ }
+*/
+
+}
+
+/*
+ **************************************************************************
+ * scp_write_proc: proc interface
+ *
+ * This function is called when the user writes in the interface /proc/driver/scp_dev
+ *
+ * Commands: works on chip 0
+ * CSENA: Enable Chip Select in the EPLD
+ * CSDIS: Disable Chip Select in the EPLD
+ * RXDATA: Read Receive Data register
+ *
+ * WREN: Set Write Enable Latch 0x06
+ * WRDI: Write Disable 0x04
+ * RDSR: Read Status 0x05
+ * WRSR: Write Status 0x01
+ * WRITE: Write Memory Data 0x02
+ * READ: Read Memory Data 0x03
+ *
+ * Return the number of data
+ *
+ * ************************************************************************/
+
+static int
+scp_write_proc(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ char str[10];
+ int val;
+#ifdef SCP_DEBUG
+ scp_verbose = 1;
+#endif
+
+ memset(str, 0, sizeof(str));
+ if (copy_from_user(str, buffer, min_t(unsigned long, count, sizeof(str))))
+ return -EFAULT;
+
+ /*
+ * Enable Chip Select in the EPLD
+ */
+ if (!strncmp(str, "CSENA", 5))
+ scp_cs(0, 0);
+
+ /*
+ * Disable Chip Select in the EPLD
+ */
+ else if (!strncmp(str, "CSDIS", 5))
+ scp_cs(0, 1);
+
+ /*
+ * Read RX DATA
+ */
+ else if (!strncmp(str, "RXDATA", 6)) {
+ scp_rx_data();
+ }
+ /*
+ * Set Write Enable Latch: 0x06
+ */
+ else if (!strncmp(str, "WREN", 4))
+ scp_tx_data(0x06);
+
+ /*
+ * Write Disable: 0x04
+ */
+ else if (!strncmp(str, "WRDI", 4))
+ scp_tx_data(0x04);
+
+ /*
+ * Read Status Command: 0x05
+ */
+ else if (!strncmp(str, "RDSR", 4))
+ scp_tx_data(0x05);
+ /*
+ * Write Status Command: 0x01
+ */
+ else if (!strncmp(str, "WRSR", 4))
+ scp_tx_data(0x01);
+
+ /*
+ * Write Memory Data: 0x02
+ */
+ else if (!strncmp(str, "WRITE", 5))
+ scp_tx_data(0x02);
+
+ /*
+ * Read Memory Data: 0x03
+ */
+ else if (!strncmp(str, "READ", 4))
+ scp_tx_data(0x03);
+
+ else {
+ val = simple_strtol(str, NULL, 16);
+ scp_tx_data((unsigned char) val);
+ }
+ return count;
+}
+
+/*
+ **************************************************************************
+ * scp_int_handler: Handle general SCP interrupts.
+ *
+ *
+ * ************************************************************************/
+static irqreturn_t scp_int_handler(int irq, void *dev_id)
+{
+ unsigned char status_register;
+
+ status_register = scp_readb(SCPD_SR);
+ //wake_up(&scp_wait);
+ // Serial Data receive is complete and RxD is available
+ if (status_register & SCPD_RBR) {
+#ifdef SCP_DEBUG
+ if (scp_verbose)
+ printk("Int-> ");
+#endif
+ scp_rx_data();
+ }
+
+ return IRQ_HANDLED;
+}
+
+int scp_io(int chipid, struct spi_dev_desc *desc,
+ const unsigned char **inbufs, unsigned int *incounts,
+ unsigned char **outbufs, unsigned int *outcounts, int cansleep)
+{
+ unsigned int incount, outcount, tries;
+ const unsigned char *inp;
+ unsigned char *outp;
+ int extra_clock=0;
+
+ /* CS 'L' */
+ scp_cs(chipid,0);
+ udelay(desc->tcss);
+#ifdef SCP_DEBUG
+ scp_verbose = 0;
+#endif
+ /* do scatter IO */
+ inp = inbufs ? *inbufs : NULL;
+ extra_clock = (*inp == ATMEL_RDSR)? 1:0;
+ incount = *incounts;
+ while (incount && inp) {
+ tries = 0;
+ while (scp_readb(SCPD_SR) & SCPD_SR_BUSY) {
+ udelay(desc->tcss);
+ if (tries++ > 20) {
+ scp_cs(chipid,1);
+ return -1;
+ }
+ }
+ scp_tx_data(*inp++);
+ incount --;
+ if (!incount && *(++inbufs)) {
+ incount = *(++incounts);
+ inp = *(inbufs);
+ }
+ }
+#ifdef SCP_DEBUG
+ scp_verbose = 0;
+#endif
+ if (extra_clock) {
+ scp_tx_data(0);
+ scp_tx_data(0);
+ }
+ outp = outbufs ? *outbufs : NULL;
+ outcount = *outcounts;
+ while (outcount && outp) {
+ scp_tx_data(0); /* dummy, for clock */
+ //wait_event(scp_wait, scp_readb(SCPD_SR) & SCPD_SR_RBR);
+ tries = 0;
+ while (!(scp_readb(SCPD_SR) & SCPD_SR_RBR)) {
+ udelay(desc->tcss);
+ if (tries++ > 5) {
+ return -1;
+ }
+ }
+ *outp++ = scp_rx_data();
+ outcount --;
+ }
+ udelay(desc->tcss);
+ /* CS 'H' */
+ scp_cs(chipid,1);
+
+ return 0;
+}
+
+static int __devinit scp_request_irq(struct of_device *ofdev,
+ struct platform_device *dev)
+{
+ struct device_node *np = ofdev->node;
+ int irq;
+
+ irq = irq_of_parse_and_map(np, 0);
+ if (irq == NO_IRQ) {
+ dev_err(&ofdev->dev, "irq_of_parse_and_map failed\n");
+ return NO_IRQ;
+ }
+
+ /* request interrupt */
+ if (request_irq(irq, scp_int_handler , 0, SCP_NAME, dev)) {
+ dev_err(&ofdev->dev, "request_irq %d failed\n", irq);
+ return NO_IRQ;
+ }
+
+ return irq;
+}
+
+/*
+ **************************************************************************
+ * scpdev_cleanup: Clean the module
+ *
+ *
+ * Return nothing.
+ * ************************************************************************/
+static int __devexit scp_remove(struct of_device *ofdev)
+{
+ if (scpdev_initialized > 0) {
+ free_irq(SCP_IRQ, NULL);
+ iounmap(virtual_scp_add);
+ virtual_scp_add = NULL;
+ iounmap(gpio_base);
+ gpio_base = NULL;
+ release_mem_region(SCP_PHYS_ADD, 24);
+ remove_proc_entry("AMCC-SCP", NULL);
+ unregister_chrdev(major, "AMCC-SCP");
+ scpdev_initialized--;
+ }
+ return 0;
+}
+
+static int __init scp_probe(struct of_device *ofdev,
+ const struct of_device_id *match)
+{
+ SPI_DEV *spi_dev[NUM_SPI_SLAVES];
+ struct proc_dir_entry *pentry;
+ struct platform_device *scp_dev;
+ int retval = 0, i;
+ ulong val;
+ if (scpdev_initialized) {
+ return -1;
+ }
+
+ scp_dev = kzalloc(sizeof(*scp_dev), GFP_KERNEL);
+ if (!scp_dev) {
+ dev_err(&ofdev->dev, "failed to allocate device data\n");
+ return -ENOMEM;
+ }
+ dev_set_drvdata(&ofdev->dev, scp_dev);
+
+ if (request_mem_region(SCP_PHYS_ADD, 24, "AMCC-SCP") == NULL) {
+ retval = -EBUSY;
+ goto err0;
+ }
+ if ((virtual_scp_add = ioremap(SCP_PHYS_ADD, 24)) == NULL) {
+ retval = -ENOMEM;
+ goto err1;
+ }
+ //printk("virtual_scp_add=0x%p\n", virtual_scp_add);
+ if ((gpio_base = ioremap(GPIO_BASE, 0x100)) == NULL) {
+ retval = -ENOMEM;
+ goto err2;
+ }
+#if 1
+ /* select SCP */
+ val = SDR_READ(DCRN_SDR_PFC1);
+ SDR_WRITE(DCRN_SDR_PFC1, val & MASK_SEL_SCP);
+
+ /* enable GPIO output for pin 9, 10, 11 and 14*/
+ val = SDR_READ(DCRN_SDR_PFC0);
+ SDR_WRITE(DCRN_SDR_PFC0, (val & 0xFF8FFFFF) | 0x00700000);
+
+ val = SDR_READ(DCRN_SDR_PFC0);
+ SDR_WRITE(DCRN_SDR_PFC0, (val & 0xFFFDFFFF) | 0x00020000);
+
+ /* configure GPIO 9 and 11 as SPIClkOut and SPIDO */
+ val = in_be32((gpio_base+(GPIO0_OSRL-GPIO_BASE)));
+ out_be32((gpio_base+(GPIO0_OSRL-GPIO_BASE)), (val & 0xFFFFCCFF) | 0x00001100);
+
+ val = in_be32((gpio_base+(GPIO0_TSRL-GPIO_BASE)));
+ out_be32((gpio_base+(GPIO0_TSRL-GPIO_BASE)), (val & 0xFFFFCCFF) | 0x00001100);
+
+ val = in_be32((gpio_base+(GPIO0_ISR1L-GPIO_BASE)));
+ out_be32((gpio_base+(GPIO0_ISR1L-GPIO_BASE)), (val & 0xFFFFF3FF) | 0x00000400);
+
+ /* configure GPIO14 as chip select */
+ val = in_be32((gpio_base+(GPIO0_TCR-GPIO_BASE)));
+ out_be32((gpio_base+(GPIO0_TCR-GPIO_BASE)), (val & 0xFFFDFFFF) | 0x00020000);
+
+ val = in_be32((gpio_base+(GPIO0_TSRL-GPIO_BASE)));
+ out_be32((gpio_base+(GPIO0_TSRL-GPIO_BASE)), (val & 0xFFFFFFF3) | 0x00000000);
+
+ val = in_be32((gpio_base+(GPIO0_OSRL-GPIO_BASE)));
+ out_be32((gpio_base+(GPIO0_OSRL-GPIO_BASE)), (val & 0xFFFFFFF3) | 0x00000000);
+
+ val = in_be32((gpio_base+(GPIO0_ODR-GPIO_BASE)));
+ out_be32((gpio_base+(GPIO0_ODR-GPIO_BASE)), (val & 0xFFFDFFFF) | 0x00000000);
+
+ val = in_be32((gpio_base+(GPIO0_ISR1L-GPIO_BASE)));
+ out_be32((gpio_base+(GPIO0_ISR1L-GPIO_BASE)), val & GPIO_MASK);
+
+ val = in_be32((gpio_base+(GPIO0_ISR2L-GPIO_BASE)));
+ out_be32((gpio_base+(GPIO0_ISR2L-GPIO_BASE)), val & GPIO_MASK);
+
+ val = in_be32((gpio_base+(GPIO0_ISR3L-GPIO_BASE)));
+ out_be32((gpio_base+(GPIO0_ISR3L-GPIO_BASE)), val & GPIO_MASK);
+
+ /* CS high, b14 <= '1' */
+ val = in_be32((gpio_base+(GPIO0_OR-GPIO_BASE)));
+ out_be32((gpio_base+(GPIO0_OR-GPIO_BASE)), val | CS_HIGH );
+#endif
+
+ if ((retval = register_chrdev(major, "AMCC-SCP", &scp_device_fops)) < 0) {
+ retval = -ENODEV;
+ goto err3;
+ }
+ /* Init the SCP Core */
+ scp_set_configuration();
+ /* Create proc entry */
+ if ((pentry = create_proc_entry(SCP_PROC_NAME, 0200, NULL)) == NULL) {
+ retval = -ENODEV;
+ goto err4;
+ }
+ pentry->write_proc = scp_write_proc;
+ /* SCP interrupt registration to Linux. */
+ printk(KERN_INFO "SCP: Requesting SCP irq %d ...\n", SCP_IRQ);
+ /* Request SCP interrupt */
+ if ((scp_request_irq(ofdev, scp_dev)) == NO_IRQ)
+ {
+ printk(KERN_INFO "SCP: Requesting SCP irq %d ...fail\n", SCP_IRQ);
+ retval = -EINVAL;
+ goto err5;
+ }
+
+ for (i = 0; i < NUM_SPI_SLAVES; i++) {
+ spi_dev[i] = NULL;
+ /* Allocate SPI Flash device */
+ if ( (spi_dev[i] = (SPI_DEV *)kmalloc(sizeof(SPI_DEV), GFP_KERNEL))
+ == NULL) {
+ retval = -ENOMEM;
+ goto err6;
+ }
+ spi_dev[i]->slaveid = i;
+ if ((retval = beech_scp_init(spi_dev[i])) != 0) {
+ printk(KERN_ERR
+ "beech_scp_init(): %s beech_scp_init err 0x%08x\n",
+ DEVICE_NAME, retval);
+ goto err7;
+ }
+ }
+ scpdev_initialized++;
+
+ return 0;
+err7:
+err6:
+ for (i = 0; i < NUM_SPI_SLAVES; i++) {
+ if (spi_dev[i]) {
+ kfree(spi_dev[i]);
+ }
+ }
+ free_irq(SCP_IRQ, NULL);
+err5:
+ remove_proc_entry("AMCC-SCP", NULL);
+ unregister_chrdev(major, "AMCC-SCP");
+err4:
+err3:
+ iounmap(gpio_base);
+ gpio_base = NULL;
+err2:
+ iounmap(virtual_scp_add);
+ virtual_scp_add = NULL;
+
+err1:
+ release_mem_region(SCP_PHYS_ADD, 24);
+err0:
+ return retval;
+}
+
+static const struct of_device_id amcc_scp_match[] = {
+ { .compatible = "amcc,scp-405ex", },
+ {}
+};
+
+static struct of_platform_driver amcc_scp_driver = {
+ .name = "AMCC-SCP",
+ .match_table = amcc_scp_match,
+ .probe = scp_probe,
+ .remove = __devexit_p(scp_remove),
+};
+
+static int __init scp_init(void)
+{
+ return of_register_platform_driver(&amcc_scp_driver);
+}
+
+static void __exit scp_exit(void)
+{
+ of_unregister_platform_driver(&amcc_scp_driver);
+}
+
+module_init(scp_init);
+module_exit(scp_exit);
+
+MODULE_AUTHOR("christophe Lombard <christophe_lombard@fr.ibm.com>");
+MODULE_DESCRIPTION("SCP /dev entries driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scp/scp-dev.h b/drivers/scp/scp-dev.h
new file mode 100755
index 00000000000..4e7c385a596
--- /dev/null
+++ b/drivers/scp/scp-dev.h
@@ -0,0 +1,196 @@
+/* **************************************************************************
+ * drivers/spi/scp-dev.c -- Serial Communications Port
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *
+ * (C) Copyright IBM Corp. 2004 Christophe Lombard <christophe_lombard@fr.ibm.com>
+ *
+ *
+ * Usage:
+ * Transmit data to scp
+ * echo "ad" > /proc/driver/scp_dev
+ *
+ * **************************************************************************/
+#include <linux/mtd/mtd.h> /* required for mtd support */
+
+/* SDR read/write helper macros */
+#define DCRN_SDR_CONFIG_ADDR 0x000E
+#define DCRN_SDR_CONFIG_DATA 0x000F
+
+#define SDR_READ(offset) ({ \
+ mtdcr(DCRN_SDR_CONFIG_ADDR, offset); \
+ mfdcr(DCRN_SDR_CONFIG_DATA);})
+#define SDR_WRITE(offset, data) ({ \
+ mtdcr(DCRN_SDR_CONFIG_ADDR, offset); \
+ mtdcr(DCRN_SDR_CONFIG_DATA, data);})
+
+// Physical address
+#define SCP_PHYS_ADD 0x4EF600900ULL
+/* GPIO address */
+#define GPIO_BASE (0x4EF600B00ULL)
+#define GPIO0_OR (GPIO_BASE+0x0)
+#define GPIO0_TCR (GPIO_BASE+0x4)
+#define GPIO0_OSRL (GPIO_BASE+0x8)
+#define GPIO0_OSRH (GPIO_BASE+0xC)
+#define GPIO0_TSRL (GPIO_BASE+0x10)
+#define GPIO0_TSRH (GPIO_BASE+0x14)
+#define GPIO0_ODR (GPIO_BASE+0x18)
+#define GPIO0_ISR1L (GPIO_BASE+0x30)
+#define GPIO0_ISR1H (GPIO_BASE+0x34)
+#define GPIO0_ISR2L (GPIO_BASE+0x38)
+#define GPIO0_ISR2H (GPIO_BASE+0x3C)
+#define GPIO0_ISR3L (GPIO_BASE+0x40)
+#define GPIO0_ISR3H (GPIO_BASE+0x44)
+/* Function Selection register */
+#define DCRN_SDR_PFC0 0x4100
+#define DCRN_SDR_PFC1 0x4101
+
+#define SCP_IRQ 34
+#define SCP_NAME "Serial Communications Port"
+#define SCP_PROC_NAME "driver/scp_dev"
+
+/*-------------------------------------------------------------------------*/
+/* Device Driver Control Flags (combinable flags parameters) */
+/*-------------------------------------------------------------------------*/
+/* Flags used in SCPD_Set_Configuration API */
+#define SCPD_SCP 0x10 /* Serial Clock Phase */
+#define SCPD_EN 0x08 /* Serial Port Enabled */
+#define SCPD_RD 0x04 /* Data bit 7 transfer first */
+#define SCPD_CI 0x02 /* Clock inverse */
+#define SCPD_LOOP 0x01 /* Internal Loopback */
+
+/*-------------------------------------------------------------------------*/
+/* Device Driver Control values */
+/*-------------------------------------------------------------------------*/
+/* SCPD_Tx_Char values */
+#define SCPD_STR 0x01 /* start of xfer */
+
+/* SCPD_RBR values */
+#define SCPD_RBR 0x01 /* Rx Byte Ready */
+
+/*----------------------------------------------------------------------*/
+/* Core Register Address Map */
+/*----------------------------------------------------------------------*/
+#define SCPD_SPMODE 0x0000
+#define SCPD_RXDATA 0x0001
+#define SCPD_TXDATA 0x0002
+#define SCPD_CR 0x0003
+#define SCPD_SR 0x0004
+#define SCPD_CDM 0x0006
+
+#define SCPD_SR_BUSY 0x2
+#define SCPD_SR_RBR 0x1
+
+#define MAF_NAME_STR_LEN 256
+#define MEM_TYPE_STR_LEN 256
+
+#define DEVICE_NAME "scp"
+#define DEVICE_REGS "ssi_regs"
+#define NUM_SPI_SLAVES 1
+
+/* ATMEL 250x0 instructions */
+#define ATMEL_WREN 0x06
+#define ATMEL_WRDI 0x04
+#define ATMEL_RDSR 0x05
+#define ATMEL_WRSR 0x01
+#define ATMEL_READ 0x03
+#define ATMEL_WRITE 0x02
+#define ATMEL_WRITE_SIZE 32
+
+#define ATMEL_SR_BSY 0x01
+#define ATMEL_SR_WEN 0x02
+#define ATMEL_SR_BP0 0x04
+#define ATMEL_SR_BP1 0x08
+
+
+
+/*******************************************************************************
+ * struct spi_chip
+ * Data structure for SPI Flash chip
+ @read_buf : read data from the chip into the buffer
+ @write_buf : write data from the buffer to the chip
+ @select_chip : select chip number
+ @hwcontrol : hw specific function for accessing control-lines
+ @dev_ready : hw specific function for device ready/busy ?
+ @chip_lock : lock to protect access
+ @wq : wait queue to sleep if SPI operation in progress
+ @state : current state of SPI device
+ @options : various chip options
+ @numchips : number of physical chips
+ @maf_name : Manufacturer name, obtained from READ_ID call
+ @mem_type : Memory Type, obtained from READ_ID call
+ @chipsize : Memory Capacity, obtained from READ_ID call
+ @numsectors : number of sectors on flash chip
+ @sectorsize : size of each sector
+ @numpages : number of pages on flash chip
+ @pagesize : size of each page
+ @slaveid : slave/flash chip number
+******************************************************************************/
+typedef struct spi_chip
+{
+ /* SPI devices list */
+ struct list_head list;
+
+ void (*select_chip)(int chip, int on_off);
+
+ int (*single_command)(struct mtd_info *mtd,
+ const int command);
+
+ int (*read_buf)(struct mtd_info *mtd, ulong addr,
+ u_char *data, int data_len);
+
+ int (*write_buf)(struct mtd_info *mtd, ulong addr,
+ const u_char *data, int data_len);
+
+ spinlock_t chip_lock;
+ wait_queue_head_t wq;
+ uint options;
+ int manuf_id;
+ int id_mem_type;
+ int chipsize;
+ int numsectors;
+ int sectorsize;
+ int numpages;
+ int chip_delay;
+ int slaveid;
+ void *priv;
+} SPI_DEV;
+
+/* SPI */
+struct spi_dev_desc {
+ unsigned int baud;
+ unsigned int tcss, tcsh, tcsr; /* CS setup/hold/recovery time */
+ unsigned int byteorder:1; /* 0:LSB-First, 1:MSB-First */
+ unsigned int polarity:1; /* 0:High-Active */
+ unsigned int phase:1; /* 0:Sample-Then-Shift */
+};
+
+
+extern unsigned char scp_rx_data(void);
+extern void scp_tx_data(unsigned char buffer);
+extern int scp_io(int chipid, struct spi_dev_desc *desc,
+ const unsigned char **inbufs, unsigned int *incounts,
+ unsigned char **outbufs, unsigned int *outcounts,
+ int cansleep);
+extern int beech_scp_init(SPI_DEV * s);
+extern void scp_cs(int chipid, int on);
+
+#ifdef SCP_DEBUG
+extern int scp_verbose;
+#endif
+
diff --git a/drivers/scp/spi_eeprom.c b/drivers/scp/spi_eeprom.c
new file mode 100755
index 00000000000..47c83a4679d
--- /dev/null
+++ b/drivers/scp/spi_eeprom.c
@@ -0,0 +1,350 @@
+/*
+ * spi_eeprom.c
+ *
+ * 2003-2005 (c) MontaVista Software, Inc. This file is licensed under the
+ * terms of the GNU General Public License version 2. This program is
+ * licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ */
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/mtd/partitions.h> /* for mtd partitions */
+
+//#undef SCP_DEBUG
+#define SCP_DEBUG
+#include "scp-dev.h"
+DEFINE_SPINLOCK(spi_eeprom_lock);
+
+static struct spi_dev_desc eeprom_dev_desc = {
+ .baud = 1500000, /* 1.5Mbps */
+ .tcss = 10,
+ .tcsh = 10,
+ .tcsr = 1,
+ .byteorder = 1, /* MSB-First */
+ .polarity = 0, /* High-Active */
+ .phase = 0, /* Sample-Then-Shift */
+
+};
+
+static struct mtd_info beech_spi_mtd[NUM_SPI_SLAVES];
+
+static struct mtd_partition spi_partition_allmem[] = {
+ {
+ .name = "scp",
+ .offset = 0x0,
+ .size = 0x400,
+ },
+};
+
+static int
+spi_eeprom_io(int chipid,
+ const unsigned char **inbufs, unsigned int *incounts,
+ unsigned char **outbufs, unsigned int *outcounts)
+{
+ return scp_io(chipid, &eeprom_dev_desc,
+ inbufs, incounts, outbufs, outcounts, 0);
+}
+
+int spi_eeprom_write_enable(int chipid, int enable)
+{
+ unsigned char inbuf[1];
+ const unsigned char *inbufs[2];
+ unsigned int incounts[2], outcounts[1];
+ unsigned long flags;
+ int stat;
+
+ inbuf[0] = enable ? ATMEL_WREN : ATMEL_WRDI;
+ inbufs[0] = inbuf;
+ incounts[0] = sizeof(inbuf);
+ inbufs[1] = NULL;
+ incounts[1] = 0;
+ outcounts[0] = 0;
+ spin_lock_irqsave(&spi_eeprom_lock, flags);
+ stat = spi_eeprom_io(chipid, inbufs, incounts, NULL, outcounts);
+ spin_unlock_irqrestore(&spi_eeprom_lock, flags);
+ return stat;
+}
+
+static int spi_eeprom_read_status_nolock(int chipid)
+{
+ unsigned char inbuf[1], outbuf[1];
+ const unsigned char *inbufs[2];
+ unsigned char *outbufs[1];
+ unsigned int incounts[2], outcounts[1];
+ int stat;
+
+ inbuf[0] = ATMEL_RDSR;
+ inbufs[0] = inbuf;
+ incounts[0] = sizeof(inbuf);
+ incounts[1] = 0;
+ outbufs[0] = outbuf;
+ outcounts[0] = sizeof(outbuf);
+ stat = spi_eeprom_io(chipid, inbufs, incounts, outbufs, outcounts);
+ if (stat < 0) {
+ printk("KERNEL_DEBUG %s bail\n", __FUNCTION__);
+ return stat;
+ }
+ return outbuf[0];
+}
+
+int spi_eeprom_read_status(int chipid)
+{
+ unsigned long flags;
+ int stat;
+
+ spin_lock_irqsave(&spi_eeprom_lock, flags);
+ stat = spi_eeprom_read_status_nolock(chipid);
+ spin_unlock_irqrestore(&spi_eeprom_lock, flags);
+ return stat;
+}
+
+
+int spi_eeprom_read(struct mtd_info *mtd, ulong address, u_char * buf,
+ int len)
+{
+ unsigned char inbuf[3];
+ const unsigned char *inbufs[2];
+ unsigned char *outbufs[1];
+ unsigned int incounts[2], outcounts[1];
+ unsigned long flags;
+ int stat;
+
+ SPI_DEV *spi_dev = mtd->priv;
+
+ address &= 0xffff;
+ inbuf[0] = ATMEL_READ;
+ inbuf[1] = address >> 8;
+ inbuf[2] = address & 0xff;
+
+ inbufs[0] = inbuf;
+ incounts[0] = sizeof(inbuf);
+ inbufs[1] = NULL;
+ incounts[1] = 0;
+ outbufs[0] = buf;
+ outcounts[0] = len;
+ spin_lock_irqsave(&spi_eeprom_lock, flags);
+ stat = spi_eeprom_io(spi_dev->slaveid, inbufs, incounts,
+ outbufs, outcounts);
+ spin_unlock_irqrestore(&spi_eeprom_lock, flags);
+ return stat;
+}
+
+int spi_eeprom_write(struct mtd_info *mtd, ulong address,
+ const u_char * buf, int len)
+{
+ unsigned char inbuf[3];
+ const unsigned char *inbufs[3];
+ unsigned int incounts[3], outcounts[1];
+ unsigned long flags;
+ int i, stat, w_len, remain;
+
+ SPI_DEV *spi_dev = mtd->priv;
+
+ remain = len;
+ while (remain > 0) {
+ stat = spi_eeprom_write_enable(spi_dev->slaveid, 1);
+ if (stat < 0)
+ return stat;
+ stat = spi_eeprom_read_status(spi_dev->slaveid);
+ if (stat < 0)
+ return stat;
+ if (!(stat & ATMEL_SR_WEN))
+ return -EPERM;
+
+ inbuf[0] = ATMEL_WRITE;
+ address &= 0xffff;
+ inbuf[1] = address >> 8;
+ inbuf[2] = address & 0xff;
+
+ inbufs[0] = inbuf;
+ inbufs[1] = buf;
+ inbufs[2] = NULL;
+
+ incounts[0] = sizeof(inbuf);
+ if (address & (ATMEL_WRITE_SIZE-1)) {
+ w_len = ATMEL_WRITE_SIZE - (address & (ATMEL_WRITE_SIZE-1));
+ if (remain <= w_len) {
+ w_len = remain;
+ }
+ }
+ else {
+ w_len = (remain>= ATMEL_WRITE_SIZE) ? ATMEL_WRITE_SIZE:remain;
+ }
+ remain -= w_len;
+ address += w_len;
+ buf += w_len;
+
+ incounts[1] = w_len;
+ incounts[2] = 0;
+
+ outcounts[0] = 0;
+ spin_lock_irqsave(&spi_eeprom_lock, flags);
+ stat = spi_eeprom_io(spi_dev->slaveid, inbufs, incounts,
+ NULL, outcounts);
+ if (stat < 0)
+ goto unlock_return;
+ /* write start. max 10ms */
+ for (i = 5; i > 0; i--) {
+ mdelay(2);
+ stat = spi_eeprom_read_status_nolock(spi_dev->slaveid);
+#ifdef SCP_DEBUG
+ printk("w%d 0x%x\n",i, stat);
+#endif
+ if (stat < 0)
+ goto unlock_return;
+ if (!(stat & ATMEL_SR_BSY)) {
+ /* Should bail out here */
+ //printk("bail ATMEL_SR_BSY\n");
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&spi_eeprom_lock, flags);
+ if (i == 0) {
+ printk(KERN_DEBUG "bail -EIO\n");
+ break;
+ //return -EIO;
+ }
+ }
+ return (len-remain);
+unlock_return:
+ printk("bail unlock_return\n");
+ spin_unlock_irqrestore(&spi_eeprom_lock, flags);
+ return stat;
+}
+
+
+/*******************************************************************************
+* Function spi_read
+* This function provides the mtd interface to read the buffer from the SPI flash
+* at the offset
+*******************************************************************************/
+int
+spi_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t * retlen, u_char * buf)
+{
+ int err = 0;
+ SPI_DEV *this = mtd->priv;
+#ifdef SCP_DEBUG
+ printk(KERN_DEBUG "spi_read: from= 0x%08x, len= %d, buf= %p\n",
+ (uint) from, len, buf);
+#endif
+ err = this->read_buf(mtd, from, buf, len);
+ if (err) {
+ printk(KERN_DEBUG "spi_read: read_data failed with err %d\n", err);
+ goto out;
+ }
+
+ /* Tell the MTD device how many bytes have been read */
+ *retlen = len;
+
+out:
+ return err;
+}
+
+/*******************************************************************************
+* Function spi_write
+* This function provides the mtd interface to write the buffer to the SPI flash
+* at the offset
+*******************************************************************************/
+int
+spi_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t * retlen, const u_char * buf)
+{
+ int err = 0;
+ SPI_DEV *this = mtd->priv;
+
+ printk(KERN_DEBUG "spi_write: to= 0x%08x, len= %d, buf= %p\n",
+ (uint) to, len, buf);
+ err = this->write_buf(mtd, to, buf, len);
+#ifdef SCP_DEBUG
+ printk(KERN_DEBUG "spi_write: write_buf returned %d\n", err);
+#endif
+ return err;
+}
+
+
+/*******************************************************************************
+* Function spi_scan
+* This function scans for the existence of the slave SPI Flash device
+* by reading the Manufacturer ID, Memory Type and Chip Size.
+*******************************************************************************/
+int spi_scan(struct mtd_info *mtd)
+{
+#ifdef SCP_DEBUG
+ printk(KERN_DEBUG "spi_scan()\n");
+#endif
+ /* Fill in MTD driver data */
+ mtd->type = MTD_DATAFLASH;
+ mtd->read = spi_read;
+ mtd->write = spi_write;
+ mtd->lock = NULL;
+ mtd->unlock = NULL;
+ mtd->writesize=0x00100000;
+ mtd->size = 0x00100000;
+ mtd->name = "AMCC-SCP";
+ mtd->flags |= MTD_WRITEABLE;
+ mtd->owner = THIS_MODULE;
+ mtd->erasesize = 32;
+ return 0;
+}
+
+
+/*******************************************************************************
+ * Function: pine_scp_init
+ * This function is called by the LiveOak SSI device layer
+ * to initialize the device.
+*******************************************************************************/
+int beech_scp_init(SPI_DEV * this)
+{
+ int id = this->slaveid;
+ int err = 0;
+
+#ifdef SCP_DEBUG
+ printk(KERN_DEBUG "beech_spi_init() : %s slave id : %d\n",
+ DEVICE_NAME, id);
+#endif
+ memset(&(beech_spi_mtd[id]), 0, sizeof(beech_spi_mtd[id]));
+
+ /* Link the private data with the MTD structure */
+ beech_spi_mtd[id].priv = (void *) this;
+
+ this->chip_delay = 100;
+
+ this->read_buf = spi_eeprom_read;
+ this->write_buf = spi_eeprom_write;
+
+ /* Init wait queue */
+ //init_waitqueue_head(&this->wq);
+
+ /* Scan to find existence of SPI Flash device */
+ if ( (err = spi_scan(&beech_spi_mtd[id])) != 0) {
+ goto err;
+ }
+
+ /* Adr MTD partition onto SPI device */
+ add_mtd_partitions(&beech_spi_mtd[id], spi_partition_allmem,
+ sizeof(spi_partition_allmem) /
+ sizeof(spi_partition_allmem[0]));
+err:
+ return err;
+}
+
+/****************************************************************************
+ * Function: pine_spi_cleanup
+ * This function is called by the Pine SCP device layer
+ * on exit.
+*****************************************************************************/
+void beech_spi_cleanup(void)
+{
+ int i;
+#ifdef SCP_DEBUG
+ printk(KERN_DEBUG "beech_spi_cleanup()\n");
+#endif
+ for(i = 0; i < NUM_SPI_SLAVES; i++) {
+ /* Unregister partitions */
+ del_mtd_device(&beech_spi_mtd[i]);
+ }
+}