diff options
Diffstat (limited to 'drivers/scp')
-rwxr-xr-x | drivers/scp/Kconfig | 12 | ||||
-rwxr-xr-x | drivers/scp/Makefile | 5 | ||||
-rwxr-xr-x | drivers/scp/scp-dev.c | 654 | ||||
-rwxr-xr-x | drivers/scp/scp-dev.h | 196 | ||||
-rwxr-xr-x | drivers/scp/spi_eeprom.c | 350 |
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]); + } +} |