From 1cb8e980c41e86760fa93de63f4e4cf643bef9d9 Mon Sep 17 00:00:00 2001 From: wdenk Date: Thu, 6 Mar 2003 21:55:29 +0000 Subject: =?UTF-8?q?*=20Patches=20by=20David=20M=FCller,=2031=20Jan=202003:?= =?UTF-8?q?=20=20=20-=20minimal=20setup=20for=20CardBus=20bridges=20=20=20?= =?UTF-8?q?-=20add=20EEPROM=20read/write=20support=20in=20the=20CS8900=20d?= =?UTF-8?q?river=20=20=20-=20add=20support=20for=20the=20builtin=20I2C=20c?= =?UTF-8?q?ontroller=20in=20the=20Samsung=20s3c24x0=20chips=20=20=20-=20ad?= =?UTF-8?q?d=20support=20for=20=20MPL's=20VCMA9=20(Samsung=20s3c2410=20bas?= =?UTF-8?q?ed)=20board?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Patch by Steven Scholz, 04 Feb 2003: add support for RTC DS1307 * Patch by Reinhard Meyer, 5 Feb 2003: fix PLPRCR/SCCR init sequence on 8xx to allow for changes of EBDF by software * Patch by Vladimir Gurevich, 07 Feb 2003: "API-compatibility patch" for 4xx I2C driver --- drivers/Makefile | 2 +- drivers/cs8900.c | 38 +++++ drivers/cs8900.h | 4 +- drivers/inca-ip_sw.c | 0 drivers/pci_auto.c | 10 ++ drivers/s3c24x0_i2c.c | 409 ++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 461 insertions(+), 2 deletions(-) create mode 100644 drivers/inca-ip_sw.c create mode 100644 drivers/s3c24x0_i2c.c (limited to 'drivers') diff --git a/drivers/Makefile b/drivers/Makefile index 65de8b5..3278cec 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -32,7 +32,7 @@ OBJS = 3c589.o 5701rls.o bcm570x.o bcm570x_autoneg.o \ eepro100.o i8042.o inca-ip_sw.o \ natsemi.o ns16550.o ns8382x.o ns87308.o \ pci.o pci_auto.o pci_indirect.o \ - pcnet.o sed13806.o serial.o \ + pcnet.o s3c24x0_i2c.o sed13806.o serial.o \ smc91111.o smiLynxEM.o sym53c8xx.o \ tigon3.o w83c553f.o diff --git a/drivers/cs8900.c b/drivers/cs8900.c index 7165d60..224ab95 100644 --- a/drivers/cs8900.c +++ b/drivers/cs8900.c @@ -272,6 +272,44 @@ retry: return 0; } +static void cs8900_e2prom_ready(void) +{ + while(get_reg(PP_SelfST) & SI_BUSY); +} + +/***********************************************************/ +/* read a 16-bit word out of the EEPROM */ +/***********************************************************/ + +int cs8900_e2prom_read(unsigned char addr, unsigned short *value) +{ + cs8900_e2prom_ready(); + put_reg(PP_EECMD, EEPROM_READ_CMD | addr); + cs8900_e2prom_ready(); + *value = get_reg(PP_EEData); + + return 0; +} + + +/***********************************************************/ +/* write a 16-bit word into the EEPROM */ +/***********************************************************/ + +void cs8900_e2prom_write(unsigned char addr, unsigned short value) +{ + cs8900_e2prom_ready(); + put_reg(PP_EECMD, EEPROM_WRITE_EN); + cs8900_e2prom_ready(); + put_reg(PP_EEData, value); + put_reg(PP_EECMD, EEPROM_WRITE_CMD | addr); + cs8900_e2prom_ready(); + put_reg(PP_EECMD, EEPROM_WRITE_DIS); + cs8900_e2prom_ready(); + + return 0; +} + #endif /* COMMANDS & CFG_NET */ #endif /* CONFIG_DRIVER_CS8900 */ diff --git a/drivers/cs8900.h b/drivers/cs8900.h index 0a35ba6..dbfda3c 100644 --- a/drivers/cs8900.h +++ b/drivers/cs8900.h @@ -250,7 +250,9 @@ #define EEPROM_WRITE_DIS 0x0000 #define EEPROM_WRITE_CMD 0x0100 #define EEPROM_READ_CMD 0x0200 +#define EEPROM_ERASE_CMD 0x0300 - +extern int cs8900_e2prom_read(uchar, ushort *); +extern void cs8900_e2prom_write(uchar, ushort); #endif /* CONFIG_DRIVER_CS8900 */ diff --git a/drivers/inca-ip_sw.c b/drivers/inca-ip_sw.c new file mode 100644 index 0000000..e69de29 diff --git a/drivers/pci_auto.c b/drivers/pci_auto.c index 00177cc..38b59ec 100644 --- a/drivers/pci_auto.c +++ b/drivers/pci_auto.c @@ -314,6 +314,16 @@ int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev) pciauto_setup_device(hose, dev, 6, hose->pci_mem, hose->pci_io); break; + case PCI_CLASS_BRIDGE_CARDBUS: + /* just do a minimal setup of the bridge, let the OS take care of the rest */ + pciauto_setup_device(hose, dev, 0, hose->pci_mem, hose->pci_io); + + DEBUGF("PCI Autoconfig: Found P2CardBus bridge, device %d\n", + PCI_DEV(dev)); + + hose->current_busno++; + break; + default: pciauto_setup_device(hose, dev, 6, hose->pci_mem, hose->pci_io); break; diff --git a/drivers/s3c24x0_i2c.c b/drivers/s3c24x0_i2c.c new file mode 100644 index 0000000..bf435c9 --- /dev/null +++ b/drivers/s3c24x0_i2c.c @@ -0,0 +1,409 @@ +/* + * (C) Copyright 2002 + * David Mueller, ELSOFT AG, d.mueller@elsoft.ch + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* This code should work for both the S3C2400 and the S3C2410 + * as they seem to have the same I2C controller inside. + * The different address mapping is handled by the s3c24xx.h files below. + */ + +#include + +#ifdef CONFIG_DRIVER_S3C24X0_I2C + +#if defined(CONFIG_S3C2400) +#include +#elif defined(CONFIG_S3C2410) +#include +#endif +#include + +#ifdef CONFIG_HARD_I2C + +#define IIC_WRITE 0 +#define IIC_READ 1 + +#define IIC_OK 0 +#define IIC_NOK 1 +#define IIC_NACK 2 +#define IIC_NOK_LA 3 /* Lost arbitration */ +#define IIC_NOK_TOUT 4 /* time out */ + +#define IICSTAT_BSY 0x20 /* Busy bit */ +#define IICSTAT_NACK 0x01 /* Nack bit */ +#define IICCON_IRPND 0x10 /* Interrupt pending bit */ +#define IIC_MODE_MT 0xC0 /* Master Transmit Mode */ +#define IIC_MODE_MR 0x80 /* Master Receive Mode */ +#define IIC_START_STOP 0x20 /* START / STOP */ +#define IIC_TXRX_ENA 0x10 /* I2C Tx/Rx enable */ + +#define IIC_TIMEOUT 1 /* 1 seconde */ + + +static int GetIICSDA(void) +{ + return (rGPEDAT & 0x8000) >> 15; +} + +static void SetIICSDA(int x) +{ + rGPEDAT = (rGPEDAT & ~0x8000) | (x&1) << 15; +} + +static void SetIICSCL(int x) +{ + rGPEDAT = (rGPEDAT & ~0x4000) | (x&1) << 14; +} + + +static int WaitForXfer(void) +{ + int i, status; + + i = IIC_TIMEOUT * 1000; + status = rIICCON; + while ((i > 0) && !(status & IICCON_IRPND)) { + udelay(1000); + status = rIICCON; + i--; + } + + return(status & IICCON_IRPND) ? IIC_OK : IIC_NOK_TOUT; +} + +static int IsACK(void) +{ + return(!(rIICSTAT & IICSTAT_NACK)); +} + +static void ReadWriteByte(void) +{ + rIICCON &= ~IICCON_IRPND; +} + +void i2c_init (int speed, int slaveadd) +{ + ulong freq, pres = 16, div; + int i, status; + + /* wait for some time to give previous transfer a chance to finish */ + + i = IIC_TIMEOUT * 1000; + status = rIICSTAT; + while ((i > 0) && (status & IICSTAT_BSY)) { + udelay(1000); + status = rIICSTAT; + i--; + } + + if ((status & IICSTAT_BSY) || GetIICSDA() == 0) { + ulong old_gpecon = rGPECON; + /* bus still busy probably by (most) previously interrupted transfer */ + + /* set IICSDA and IICSCL (GPE15, GPE14) to GPIO */ + rGPECON = (rGPECON & ~0xF0000000) | 0x10000000; + + /* toggle IICSCL until bus idle */ + SetIICSCL(0); udelay(1000); + i = 10; + while ((i > 0) && (GetIICSDA() != 1)) { + SetIICSCL(1); udelay(1000); + SetIICSCL(0); udelay(1000); + i--; + } + SetIICSCL(1); udelay(1000); + + /* restore pin functions */ + rGPECON = old_gpecon; + } + + /* calculate prescaler and divisor values */ + freq = get_PCLK(); + if ((freq / pres / (16+1)) > speed) + /* set prescaler to 512 */ + pres = 512; + + div = 0; + while ((freq / pres / (div+1)) > speed) + div++; + + /* set prescaler, divisor according to freq, also set + ACKGEN, IRQ */ + rIICCON = (div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0); + + /* init to SLAVE REVEIVE and set slaveaddr */ + rIICSTAT = 0; + rIICADD = slaveadd; + /* program Master Transmit (and implicit STOP) */ + rIICSTAT = IIC_MODE_MT | IIC_TXRX_ENA; + +} + +/* + cmd_type is 0 for write 1 for read. + + addr_len can take any value from 0-255, it is only limited + by the char, we could make it larger if needed. If it is + 0 we skip the address write cycle. + +*/ +static +int i2c_transfer(unsigned char cmd_type, + unsigned char chip, + unsigned char addr[], + unsigned char addr_len, + unsigned char data[], + unsigned short data_len) +{ + int i, status, result; + + if (data == 0 || data_len == 0) { + /*Don't support data transfer of no length or to address 0*/ + printf( "i2c_transfer: bad call\n" ); + return IIC_NOK; + } + + //CheckDelay(); + + /* Check I2C bus idle */ + i = IIC_TIMEOUT * 1000; + status = rIICSTAT; + while ((i > 0) && (status & IICSTAT_BSY)) { + udelay(1000); + status = rIICSTAT; + i--; + } + + + if (status & IICSTAT_BSY) { + result = IIC_NOK_TOUT; + return(result); + } + + rIICCON |= 0x80; + + result = IIC_OK; + + switch (cmd_type) { + case IIC_WRITE: + if (addr && addr_len) { + rIICDS = chip; + /* send START */ + rIICSTAT = IIC_MODE_MT | IIC_TXRX_ENA | IIC_START_STOP; + i = 0; + while ((i < addr_len) && (result == IIC_OK)) { + result = WaitForXfer(); + rIICDS = addr[i]; + ReadWriteByte(); + i++; + } + i = 0; + while ((i < data_len) && (result == IIC_OK)) { + result = WaitForXfer(); + rIICDS = data[i]; + ReadWriteByte(); + i++; + } + } else { + rIICDS = chip; + /* send START */ + rIICSTAT = IIC_MODE_MT | IIC_TXRX_ENA | IIC_START_STOP; + i = 0; + while ((i < data_len) && (result = IIC_OK)) { + result = WaitForXfer(); + rIICDS = data[i]; + ReadWriteByte(); + i++; + } + } + + if (result == IIC_OK) + result = WaitForXfer(); + + /* send STOP */ + rIICSTAT = IIC_MODE_MR | IIC_TXRX_ENA; + ReadWriteByte(); + break; + + case IIC_READ: + if (addr && addr_len) { + rIICSTAT = IIC_MODE_MT | IIC_TXRX_ENA; + rIICDS = chip; + /* send START */ + rIICSTAT |= IIC_START_STOP; + result = WaitForXfer(); + if (IsACK()) { + i = 0; + while ((i < addr_len) && (result == IIC_OK)) { + rIICDS = addr[i]; + ReadWriteByte(); + result = WaitForXfer(); + i++; + } + + rIICDS = chip; + /* resend START */ + rIICSTAT = IIC_MODE_MR | IIC_TXRX_ENA | IIC_START_STOP; + ReadWriteByte(); + result = WaitForXfer(); + i = 0; + while ((i < data_len) && (result == IIC_OK)) { + /* disable ACK for final READ */ + if (i == data_len - 1) + rIICCON &= ~0x80; + ReadWriteByte(); + result = WaitForXfer(); + data[i] = rIICDS; + i++; + } + } else { + result = IIC_NACK; + } + + } else { + rIICSTAT = IIC_MODE_MR | IIC_TXRX_ENA; + rIICDS = chip; + /* send START */ + rIICSTAT |= IIC_START_STOP; + result = WaitForXfer(); + + if (IsACK()) { + i = 0; + while ((i < data_len) && (result == IIC_OK)) { + /* disable ACK for final READ */ + if (i == data_len - 1) + rIICCON &= ~0x80; + ReadWriteByte(); + result = WaitForXfer(); + data[i] = rIICDS; + i++; + } + } else { + result = IIC_NACK; + } + } + + /* send STOP */ + rIICSTAT = IIC_MODE_MR | IIC_TXRX_ENA; + ReadWriteByte(); + break; + + default: + printf( "i2c_transfer: bad call\n" ); + result = IIC_NOK; + break; + } + + return (result); +} + +int i2c_probe (uchar chip) +{ + uchar buf[1]; + + buf[0] = 0; + + /* + * What is needed is to send the chip address and verify that the + * address was ed (i.e. there was a chip at that address which + * drove the data line low). + */ + return(i2c_transfer (IIC_READ, chip << 1, 0, 0, buf, 1) != IIC_OK); +} + +int i2c_read (uchar chip, uint addr, int alen, uchar * buffer, int len) +{ + uchar xaddr[4]; + int ret; + + if ( alen > 4 ) { + printf ("I2C read: addr len %d not supported\n", alen); + return 1; + } + + if ( alen > 0 ) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } + + +#ifdef CFG_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of + * address and the extra bits end up in the "chip address" + * bit slots. This makes a 24WC08 (1Kbyte) chip look like + * four 256 byte chips. + * + * Note that we consider the length of the address field to + * still be one byte because the extra address bits are + * hidden in the chip address. + */ + if( alen > 0 ) + chip |= ((addr >> (alen * 8)) & CFG_I2C_EEPROM_ADDR_OVERFLOW); +#endif + if( (ret = i2c_transfer(IIC_READ, chip<<1, &xaddr[4-alen], alen, buffer, len )) != 0) { + printf( "I2c read: failed %d\n", ret); + return 1; + } + return 0; +} + +int i2c_write (uchar chip, uint addr, int alen, uchar * buffer, int len) +{ + uchar xaddr[4]; + + if ( alen > 4 ) { + printf ("I2C write: addr len %d not supported\n", alen); + return 1; + } + + if ( alen > 0 ) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } + +#ifdef CFG_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of + * address and the extra bits end up in the "chip address" + * bit slots. This makes a 24WC08 (1Kbyte) chip look like + * four 256 byte chips. + * + * Note that we consider the length of the address field to + * still be one byte because the extra address bits are + * hidden in the chip address. + */ + if( alen > 0 ) + chip |= ((addr >> (alen * 8)) & CFG_I2C_EEPROM_ADDR_OVERFLOW); +#endif + return (i2c_transfer(IIC_WRITE, chip<<1, &xaddr[4-alen], alen, buffer, len ) != 0); +} + +#endif /* CONFIG_HARD_I2C */ + +#endif /* CONFIG_DRIVER_S3C24X0_I2C */ -- cgit v1.1