1d3b63577SJean-Christophe PLAGNIOL-VILLARD /* 2d3b63577SJean-Christophe PLAGNIOL-VILLARD * (C) Copyright 2002 3d3b63577SJean-Christophe PLAGNIOL-VILLARD * David Mueller, ELSOFT AG, d.mueller@elsoft.ch 4d3b63577SJean-Christophe PLAGNIOL-VILLARD * 5d3b63577SJean-Christophe PLAGNIOL-VILLARD * See file CREDITS for list of people who contributed to this 6d3b63577SJean-Christophe PLAGNIOL-VILLARD * project. 7d3b63577SJean-Christophe PLAGNIOL-VILLARD * 8d3b63577SJean-Christophe PLAGNIOL-VILLARD * This program is free software; you can redistribute it and/or 9d3b63577SJean-Christophe PLAGNIOL-VILLARD * modify it under the terms of the GNU General Public License as 10d3b63577SJean-Christophe PLAGNIOL-VILLARD * published by the Free Software Foundation; either version 2 of 11d3b63577SJean-Christophe PLAGNIOL-VILLARD * the License, or (at your option) any later version. 12d3b63577SJean-Christophe PLAGNIOL-VILLARD * 13d3b63577SJean-Christophe PLAGNIOL-VILLARD * This program is distributed in the hope that it will be useful, 14d3b63577SJean-Christophe PLAGNIOL-VILLARD * but WITHOUT ANY WARRANTY; without even the implied warranty of 15d3b63577SJean-Christophe PLAGNIOL-VILLARD * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16d3b63577SJean-Christophe PLAGNIOL-VILLARD * GNU General Public License for more details. 17d3b63577SJean-Christophe PLAGNIOL-VILLARD * 18d3b63577SJean-Christophe PLAGNIOL-VILLARD * You should have received a copy of the GNU General Public License 19d3b63577SJean-Christophe PLAGNIOL-VILLARD * along with this program; if not, write to the Free Software 20d3b63577SJean-Christophe PLAGNIOL-VILLARD * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 21d3b63577SJean-Christophe PLAGNIOL-VILLARD * MA 02111-1307 USA 22d3b63577SJean-Christophe PLAGNIOL-VILLARD */ 23d3b63577SJean-Christophe PLAGNIOL-VILLARD 24d3b63577SJean-Christophe PLAGNIOL-VILLARD /* This code should work for both the S3C2400 and the S3C2410 25d3b63577SJean-Christophe PLAGNIOL-VILLARD * as they seem to have the same I2C controller inside. 26d3b63577SJean-Christophe PLAGNIOL-VILLARD * The different address mapping is handled by the s3c24xx.h files below. 27d3b63577SJean-Christophe PLAGNIOL-VILLARD */ 28d3b63577SJean-Christophe PLAGNIOL-VILLARD 29d3b63577SJean-Christophe PLAGNIOL-VILLARD #include <common.h> 30ac67804fSkevin.morfitt@fearnside-systems.co.uk #include <asm/arch/s3c24x0_cpu.h> 31eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk 32eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk #include <asm/io.h> 33d3b63577SJean-Christophe PLAGNIOL-VILLARD #include <i2c.h> 34d3b63577SJean-Christophe PLAGNIOL-VILLARD 35d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_HARD_I2C 36d3b63577SJean-Christophe PLAGNIOL-VILLARD 37d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_WRITE 0 38d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_READ 1 39d3b63577SJean-Christophe PLAGNIOL-VILLARD 40d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_OK 0 41d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_NOK 1 42d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_NACK 2 43d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_NOK_LA 3 /* Lost arbitration */ 44d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_NOK_TOUT 4 /* time out */ 45d3b63577SJean-Christophe PLAGNIOL-VILLARD 46d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2CSTAT_BSY 0x20 /* Busy bit */ 47d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2CSTAT_NACK 0x01 /* Nack bit */ 48d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2CCON_IRPND 0x10 /* Interrupt pending bit */ 49d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_MODE_MT 0xC0 /* Master Transmit Mode */ 50d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_MODE_MR 0x80 /* Master Receive Mode */ 51d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_START_STOP 0x20 /* START / STOP */ 52d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_TXRX_ENA 0x10 /* I2C Tx/Rx enable */ 53d3b63577SJean-Christophe PLAGNIOL-VILLARD 54d3b63577SJean-Christophe PLAGNIOL-VILLARD #define I2C_TIMEOUT 1 /* 1 second */ 55d3b63577SJean-Christophe PLAGNIOL-VILLARD 56d3b63577SJean-Christophe PLAGNIOL-VILLARD static int GetI2CSDA(void) 57d3b63577SJean-Christophe PLAGNIOL-VILLARD { 58eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); 59d3b63577SJean-Christophe PLAGNIOL-VILLARD 60d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2410 61*d9abba82SC Nauman return (readl(&gpio->gpedat) & 0x8000) >> 15; 62d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif 63d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2400 64*d9abba82SC Nauman return (readl(&gpio->pgdat) & 0x0020) >> 5; 65d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif 66d3b63577SJean-Christophe PLAGNIOL-VILLARD } 67d3b63577SJean-Christophe PLAGNIOL-VILLARD 68d3b63577SJean-Christophe PLAGNIOL-VILLARD #if 0 69d3b63577SJean-Christophe PLAGNIOL-VILLARD static void SetI2CSDA(int x) 70d3b63577SJean-Christophe PLAGNIOL-VILLARD { 71d3b63577SJean-Christophe PLAGNIOL-VILLARD rGPEDAT = (rGPEDAT & ~0x8000) | (x & 1) << 15; 72d3b63577SJean-Christophe PLAGNIOL-VILLARD } 73d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif 74d3b63577SJean-Christophe PLAGNIOL-VILLARD 75d3b63577SJean-Christophe PLAGNIOL-VILLARD static void SetI2CSCL(int x) 76d3b63577SJean-Christophe PLAGNIOL-VILLARD { 77eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); 78d3b63577SJean-Christophe PLAGNIOL-VILLARD 79d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2410 80*d9abba82SC Nauman writel((readl(&gpio->gpedat) & ~0x4000) | (x & 1) << 14, &gpio->gpedat); 81d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif 82d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2400 83*d9abba82SC Nauman writel((readl(&gpio->pgdat) & ~0x0040) | (x & 1) << 6, &gpio->pgdat); 84d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif 85d3b63577SJean-Christophe PLAGNIOL-VILLARD } 86d3b63577SJean-Christophe PLAGNIOL-VILLARD 87d3b63577SJean-Christophe PLAGNIOL-VILLARD static int WaitForXfer(void) 88d3b63577SJean-Christophe PLAGNIOL-VILLARD { 89eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c(); 90eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk int i; 91d3b63577SJean-Christophe PLAGNIOL-VILLARD 92d3b63577SJean-Christophe PLAGNIOL-VILLARD i = I2C_TIMEOUT * 10000; 93*d9abba82SC Nauman while (!(readl(&i2c->iiccon) & I2CCON_IRPND) && (i > 0)) { 94d3b63577SJean-Christophe PLAGNIOL-VILLARD udelay(100); 95d3b63577SJean-Christophe PLAGNIOL-VILLARD i--; 96d3b63577SJean-Christophe PLAGNIOL-VILLARD } 97d3b63577SJean-Christophe PLAGNIOL-VILLARD 98*d9abba82SC Nauman return (readl(&i2c->iiccon) & I2CCON_IRPND) ? I2C_OK : I2C_NOK_TOUT; 99d3b63577SJean-Christophe PLAGNIOL-VILLARD } 100d3b63577SJean-Christophe PLAGNIOL-VILLARD 101d3b63577SJean-Christophe PLAGNIOL-VILLARD static int IsACK(void) 102d3b63577SJean-Christophe PLAGNIOL-VILLARD { 103eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c(); 104d3b63577SJean-Christophe PLAGNIOL-VILLARD 105*d9abba82SC Nauman return !(readl(&i2c->iicstat) & I2CSTAT_NACK); 106d3b63577SJean-Christophe PLAGNIOL-VILLARD } 107d3b63577SJean-Christophe PLAGNIOL-VILLARD 108d3b63577SJean-Christophe PLAGNIOL-VILLARD static void ReadWriteByte(void) 109d3b63577SJean-Christophe PLAGNIOL-VILLARD { 110eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c(); 111d3b63577SJean-Christophe PLAGNIOL-VILLARD 112*d9abba82SC Nauman writel(readl(&i2c->iiccon) & ~I2CCON_IRPND, &i2c->iiccon); 113d3b63577SJean-Christophe PLAGNIOL-VILLARD } 114d3b63577SJean-Christophe PLAGNIOL-VILLARD 115d3b63577SJean-Christophe PLAGNIOL-VILLARD void i2c_init(int speed, int slaveadd) 116d3b63577SJean-Christophe PLAGNIOL-VILLARD { 117eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c(); 118eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); 119d3b63577SJean-Christophe PLAGNIOL-VILLARD ulong freq, pres = 16, div; 120eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk int i; 121d3b63577SJean-Christophe PLAGNIOL-VILLARD 122d3b63577SJean-Christophe PLAGNIOL-VILLARD /* wait for some time to give previous transfer a chance to finish */ 123d3b63577SJean-Christophe PLAGNIOL-VILLARD 124d3b63577SJean-Christophe PLAGNIOL-VILLARD i = I2C_TIMEOUT * 1000; 125*d9abba82SC Nauman while ((readl(&i2c->iicstat) && I2CSTAT_BSY) && (i > 0)) { 126d3b63577SJean-Christophe PLAGNIOL-VILLARD udelay(1000); 127d3b63577SJean-Christophe PLAGNIOL-VILLARD i--; 128d3b63577SJean-Christophe PLAGNIOL-VILLARD } 129d3b63577SJean-Christophe PLAGNIOL-VILLARD 130*d9abba82SC Nauman if ((readl(&i2c->iicstat) & I2CSTAT_BSY) || GetI2CSDA() == 0) { 131d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2410 132*d9abba82SC Nauman ulong old_gpecon = readl(&gpio->gpecon); 133d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif 134d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2400 135*d9abba82SC Nauman ulong old_gpecon = readl(&gpio->pgcon); 136d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif 137eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk /* bus still busy probably by (most) previously interrupted 138eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk transfer */ 139d3b63577SJean-Christophe PLAGNIOL-VILLARD 140d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2410 141d3b63577SJean-Christophe PLAGNIOL-VILLARD /* set I2CSDA and I2CSCL (GPE15, GPE14) to GPIO */ 142*d9abba82SC Nauman writel((readl(&gpio->gpecon) & ~0xF0000000) | 0x10000000, 143*d9abba82SC Nauman &gpio->gpecon); 144d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif 145d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2400 146d3b63577SJean-Christophe PLAGNIOL-VILLARD /* set I2CSDA and I2CSCL (PG5, PG6) to GPIO */ 147*d9abba82SC Nauman writel((readl(&gpio->pgcon) & ~0x00003c00) | 0x00001000, 148*d9abba82SC Nauman &gpio->pgcon); 149d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif 150d3b63577SJean-Christophe PLAGNIOL-VILLARD 151d3b63577SJean-Christophe PLAGNIOL-VILLARD /* toggle I2CSCL until bus idle */ 152d3b63577SJean-Christophe PLAGNIOL-VILLARD SetI2CSCL(0); 153d3b63577SJean-Christophe PLAGNIOL-VILLARD udelay(1000); 154d3b63577SJean-Christophe PLAGNIOL-VILLARD i = 10; 155d3b63577SJean-Christophe PLAGNIOL-VILLARD while ((i > 0) && (GetI2CSDA() != 1)) { 156d3b63577SJean-Christophe PLAGNIOL-VILLARD SetI2CSCL(1); 157d3b63577SJean-Christophe PLAGNIOL-VILLARD udelay(1000); 158d3b63577SJean-Christophe PLAGNIOL-VILLARD SetI2CSCL(0); 159d3b63577SJean-Christophe PLAGNIOL-VILLARD udelay(1000); 160d3b63577SJean-Christophe PLAGNIOL-VILLARD i--; 161d3b63577SJean-Christophe PLAGNIOL-VILLARD } 162d3b63577SJean-Christophe PLAGNIOL-VILLARD SetI2CSCL(1); 163d3b63577SJean-Christophe PLAGNIOL-VILLARD udelay(1000); 164d3b63577SJean-Christophe PLAGNIOL-VILLARD 165d3b63577SJean-Christophe PLAGNIOL-VILLARD /* restore pin functions */ 166d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2410 167*d9abba82SC Nauman writel(old_gpecon, &gpio->gpecon); 168d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif 169d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_S3C2400 170*d9abba82SC Nauman writel(old_gpecon, &gpio->pgcon); 171d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif 172d3b63577SJean-Christophe PLAGNIOL-VILLARD } 173d3b63577SJean-Christophe PLAGNIOL-VILLARD 174d3b63577SJean-Christophe PLAGNIOL-VILLARD /* calculate prescaler and divisor values */ 175d3b63577SJean-Christophe PLAGNIOL-VILLARD freq = get_PCLK(); 176d3b63577SJean-Christophe PLAGNIOL-VILLARD if ((freq / pres / (16 + 1)) > speed) 177d3b63577SJean-Christophe PLAGNIOL-VILLARD /* set prescaler to 512 */ 178d3b63577SJean-Christophe PLAGNIOL-VILLARD pres = 512; 179d3b63577SJean-Christophe PLAGNIOL-VILLARD 180d3b63577SJean-Christophe PLAGNIOL-VILLARD div = 0; 181d3b63577SJean-Christophe PLAGNIOL-VILLARD while ((freq / pres / (div + 1)) > speed) 182d3b63577SJean-Christophe PLAGNIOL-VILLARD div++; 183d3b63577SJean-Christophe PLAGNIOL-VILLARD 184d3b63577SJean-Christophe PLAGNIOL-VILLARD /* set prescaler, divisor according to freq, also set 185d3b63577SJean-Christophe PLAGNIOL-VILLARD * ACKGEN, IRQ */ 186*d9abba82SC Nauman writel((div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0), &i2c->iiccon); 187d3b63577SJean-Christophe PLAGNIOL-VILLARD 188d3b63577SJean-Christophe PLAGNIOL-VILLARD /* init to SLAVE REVEIVE and set slaveaddr */ 189*d9abba82SC Nauman writel(0, &i2c->iicstat); 190*d9abba82SC Nauman writel(slaveadd, &i2c->iicadd); 191d3b63577SJean-Christophe PLAGNIOL-VILLARD /* program Master Transmit (and implicit STOP) */ 192*d9abba82SC Nauman writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat); 193d3b63577SJean-Christophe PLAGNIOL-VILLARD 194d3b63577SJean-Christophe PLAGNIOL-VILLARD } 195d3b63577SJean-Christophe PLAGNIOL-VILLARD 196d3b63577SJean-Christophe PLAGNIOL-VILLARD /* 197d3b63577SJean-Christophe PLAGNIOL-VILLARD * cmd_type is 0 for write, 1 for read. 198d3b63577SJean-Christophe PLAGNIOL-VILLARD * 199d3b63577SJean-Christophe PLAGNIOL-VILLARD * addr_len can take any value from 0-255, it is only limited 200d3b63577SJean-Christophe PLAGNIOL-VILLARD * by the char, we could make it larger if needed. If it is 201d3b63577SJean-Christophe PLAGNIOL-VILLARD * 0 we skip the address write cycle. 202d3b63577SJean-Christophe PLAGNIOL-VILLARD */ 203d3b63577SJean-Christophe PLAGNIOL-VILLARD static 204d3b63577SJean-Christophe PLAGNIOL-VILLARD int i2c_transfer(unsigned char cmd_type, 205d3b63577SJean-Christophe PLAGNIOL-VILLARD unsigned char chip, 206d3b63577SJean-Christophe PLAGNIOL-VILLARD unsigned char addr[], 207d3b63577SJean-Christophe PLAGNIOL-VILLARD unsigned char addr_len, 208d3b63577SJean-Christophe PLAGNIOL-VILLARD unsigned char data[], unsigned short data_len) 209d3b63577SJean-Christophe PLAGNIOL-VILLARD { 210eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c(); 211eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk int i, result; 212d3b63577SJean-Christophe PLAGNIOL-VILLARD 213d3b63577SJean-Christophe PLAGNIOL-VILLARD if (data == 0 || data_len == 0) { 214d3b63577SJean-Christophe PLAGNIOL-VILLARD /*Don't support data transfer of no length or to address 0 */ 215d3b63577SJean-Christophe PLAGNIOL-VILLARD printf("i2c_transfer: bad call\n"); 216d3b63577SJean-Christophe PLAGNIOL-VILLARD return I2C_NOK; 217d3b63577SJean-Christophe PLAGNIOL-VILLARD } 218d3b63577SJean-Christophe PLAGNIOL-VILLARD 219d3b63577SJean-Christophe PLAGNIOL-VILLARD /* Check I2C bus idle */ 220d3b63577SJean-Christophe PLAGNIOL-VILLARD i = I2C_TIMEOUT * 1000; 221*d9abba82SC Nauman while ((readl(&i2c->iicstat) & I2CSTAT_BSY) && (i > 0)) { 222d3b63577SJean-Christophe PLAGNIOL-VILLARD udelay(1000); 223d3b63577SJean-Christophe PLAGNIOL-VILLARD i--; 224d3b63577SJean-Christophe PLAGNIOL-VILLARD } 225d3b63577SJean-Christophe PLAGNIOL-VILLARD 226*d9abba82SC Nauman if (readl(&i2c->iicstat) & I2CSTAT_BSY) 227d3b63577SJean-Christophe PLAGNIOL-VILLARD return I2C_NOK_TOUT; 228d3b63577SJean-Christophe PLAGNIOL-VILLARD 229*d9abba82SC Nauman writel(readl(&i2c->iiccon) | 0x80, &i2c->iiccon); 230d3b63577SJean-Christophe PLAGNIOL-VILLARD result = I2C_OK; 231d3b63577SJean-Christophe PLAGNIOL-VILLARD 232d3b63577SJean-Christophe PLAGNIOL-VILLARD switch (cmd_type) { 233d3b63577SJean-Christophe PLAGNIOL-VILLARD case I2C_WRITE: 234d3b63577SJean-Christophe PLAGNIOL-VILLARD if (addr && addr_len) { 235*d9abba82SC Nauman writel(chip, &i2c->iicds); 236d3b63577SJean-Christophe PLAGNIOL-VILLARD /* send START */ 237eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP, 238*d9abba82SC Nauman &i2c->iicstat); 239d3b63577SJean-Christophe PLAGNIOL-VILLARD i = 0; 240d3b63577SJean-Christophe PLAGNIOL-VILLARD while ((i < addr_len) && (result == I2C_OK)) { 241d3b63577SJean-Christophe PLAGNIOL-VILLARD result = WaitForXfer(); 242*d9abba82SC Nauman writel(addr[i], &i2c->iicds); 243d3b63577SJean-Christophe PLAGNIOL-VILLARD ReadWriteByte(); 244d3b63577SJean-Christophe PLAGNIOL-VILLARD i++; 245d3b63577SJean-Christophe PLAGNIOL-VILLARD } 246d3b63577SJean-Christophe PLAGNIOL-VILLARD i = 0; 247d3b63577SJean-Christophe PLAGNIOL-VILLARD while ((i < data_len) && (result == I2C_OK)) { 248d3b63577SJean-Christophe PLAGNIOL-VILLARD result = WaitForXfer(); 249*d9abba82SC Nauman writel(data[i], &i2c->iicds); 250d3b63577SJean-Christophe PLAGNIOL-VILLARD ReadWriteByte(); 251d3b63577SJean-Christophe PLAGNIOL-VILLARD i++; 252d3b63577SJean-Christophe PLAGNIOL-VILLARD } 253d3b63577SJean-Christophe PLAGNIOL-VILLARD } else { 254*d9abba82SC Nauman writel(chip, &i2c->iicds); 255d3b63577SJean-Christophe PLAGNIOL-VILLARD /* send START */ 256eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP, 257*d9abba82SC Nauman &i2c->iicstat); 258d3b63577SJean-Christophe PLAGNIOL-VILLARD i = 0; 259d3b63577SJean-Christophe PLAGNIOL-VILLARD while ((i < data_len) && (result = I2C_OK)) { 260d3b63577SJean-Christophe PLAGNIOL-VILLARD result = WaitForXfer(); 261*d9abba82SC Nauman writel(data[i], &i2c->iicds); 262d3b63577SJean-Christophe PLAGNIOL-VILLARD ReadWriteByte(); 263d3b63577SJean-Christophe PLAGNIOL-VILLARD i++; 264d3b63577SJean-Christophe PLAGNIOL-VILLARD } 265d3b63577SJean-Christophe PLAGNIOL-VILLARD } 266d3b63577SJean-Christophe PLAGNIOL-VILLARD 267d3b63577SJean-Christophe PLAGNIOL-VILLARD if (result == I2C_OK) 268d3b63577SJean-Christophe PLAGNIOL-VILLARD result = WaitForXfer(); 269d3b63577SJean-Christophe PLAGNIOL-VILLARD 270d3b63577SJean-Christophe PLAGNIOL-VILLARD /* send STOP */ 271*d9abba82SC Nauman writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat); 272d3b63577SJean-Christophe PLAGNIOL-VILLARD ReadWriteByte(); 273d3b63577SJean-Christophe PLAGNIOL-VILLARD break; 274d3b63577SJean-Christophe PLAGNIOL-VILLARD 275d3b63577SJean-Christophe PLAGNIOL-VILLARD case I2C_READ: 276d3b63577SJean-Christophe PLAGNIOL-VILLARD if (addr && addr_len) { 277*d9abba82SC Nauman writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat); 278*d9abba82SC Nauman writel(chip, &i2c->iicds); 279d3b63577SJean-Christophe PLAGNIOL-VILLARD /* send START */ 280*d9abba82SC Nauman writel(readl(&i2c->iicstat) | I2C_START_STOP, 281*d9abba82SC Nauman &i2c->iicstat); 282d3b63577SJean-Christophe PLAGNIOL-VILLARD result = WaitForXfer(); 283d3b63577SJean-Christophe PLAGNIOL-VILLARD if (IsACK()) { 284d3b63577SJean-Christophe PLAGNIOL-VILLARD i = 0; 285d3b63577SJean-Christophe PLAGNIOL-VILLARD while ((i < addr_len) && (result == I2C_OK)) { 286*d9abba82SC Nauman writel(addr[i], &i2c->iicds); 287d3b63577SJean-Christophe PLAGNIOL-VILLARD ReadWriteByte(); 288d3b63577SJean-Christophe PLAGNIOL-VILLARD result = WaitForXfer(); 289d3b63577SJean-Christophe PLAGNIOL-VILLARD i++; 290d3b63577SJean-Christophe PLAGNIOL-VILLARD } 291d3b63577SJean-Christophe PLAGNIOL-VILLARD 292*d9abba82SC Nauman writel(chip, &i2c->iicds); 293d3b63577SJean-Christophe PLAGNIOL-VILLARD /* resend START */ 294eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk writel(I2C_MODE_MR | I2C_TXRX_ENA | 295*d9abba82SC Nauman I2C_START_STOP, &i2c->iicstat); 296d3b63577SJean-Christophe PLAGNIOL-VILLARD ReadWriteByte(); 297d3b63577SJean-Christophe PLAGNIOL-VILLARD result = WaitForXfer(); 298d3b63577SJean-Christophe PLAGNIOL-VILLARD i = 0; 299d3b63577SJean-Christophe PLAGNIOL-VILLARD while ((i < data_len) && (result == I2C_OK)) { 300d3b63577SJean-Christophe PLAGNIOL-VILLARD /* disable ACK for final READ */ 301d3b63577SJean-Christophe PLAGNIOL-VILLARD if (i == data_len - 1) 302*d9abba82SC Nauman writel(readl(&i2c->iiccon) 303*d9abba82SC Nauman & ~0x80, &i2c->iiccon); 304d3b63577SJean-Christophe PLAGNIOL-VILLARD ReadWriteByte(); 305d3b63577SJean-Christophe PLAGNIOL-VILLARD result = WaitForXfer(); 306*d9abba82SC Nauman data[i] = readl(&i2c->iicds); 307d3b63577SJean-Christophe PLAGNIOL-VILLARD i++; 308d3b63577SJean-Christophe PLAGNIOL-VILLARD } 309d3b63577SJean-Christophe PLAGNIOL-VILLARD } else { 310d3b63577SJean-Christophe PLAGNIOL-VILLARD result = I2C_NACK; 311d3b63577SJean-Christophe PLAGNIOL-VILLARD } 312d3b63577SJean-Christophe PLAGNIOL-VILLARD 313d3b63577SJean-Christophe PLAGNIOL-VILLARD } else { 314*d9abba82SC Nauman writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat); 315*d9abba82SC Nauman writel(chip, &i2c->iicds); 316d3b63577SJean-Christophe PLAGNIOL-VILLARD /* send START */ 317*d9abba82SC Nauman writel(readl(&i2c->iicstat) | I2C_START_STOP, 318*d9abba82SC Nauman &i2c->iicstat); 319d3b63577SJean-Christophe PLAGNIOL-VILLARD result = WaitForXfer(); 320d3b63577SJean-Christophe PLAGNIOL-VILLARD 321d3b63577SJean-Christophe PLAGNIOL-VILLARD if (IsACK()) { 322d3b63577SJean-Christophe PLAGNIOL-VILLARD i = 0; 323d3b63577SJean-Christophe PLAGNIOL-VILLARD while ((i < data_len) && (result == I2C_OK)) { 324d3b63577SJean-Christophe PLAGNIOL-VILLARD /* disable ACK for final READ */ 325d3b63577SJean-Christophe PLAGNIOL-VILLARD if (i == data_len - 1) 326*d9abba82SC Nauman writel(readl(&i2c->iiccon) & 327*d9abba82SC Nauman ~0x80, &i2c->iiccon); 328d3b63577SJean-Christophe PLAGNIOL-VILLARD ReadWriteByte(); 329d3b63577SJean-Christophe PLAGNIOL-VILLARD result = WaitForXfer(); 330*d9abba82SC Nauman data[i] = readl(&i2c->iicds); 331d3b63577SJean-Christophe PLAGNIOL-VILLARD i++; 332d3b63577SJean-Christophe PLAGNIOL-VILLARD } 333d3b63577SJean-Christophe PLAGNIOL-VILLARD } else { 334d3b63577SJean-Christophe PLAGNIOL-VILLARD result = I2C_NACK; 335d3b63577SJean-Christophe PLAGNIOL-VILLARD } 336d3b63577SJean-Christophe PLAGNIOL-VILLARD } 337d3b63577SJean-Christophe PLAGNIOL-VILLARD 338d3b63577SJean-Christophe PLAGNIOL-VILLARD /* send STOP */ 339*d9abba82SC Nauman writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat); 340d3b63577SJean-Christophe PLAGNIOL-VILLARD ReadWriteByte(); 341d3b63577SJean-Christophe PLAGNIOL-VILLARD break; 342d3b63577SJean-Christophe PLAGNIOL-VILLARD 343d3b63577SJean-Christophe PLAGNIOL-VILLARD default: 344d3b63577SJean-Christophe PLAGNIOL-VILLARD printf("i2c_transfer: bad call\n"); 345d3b63577SJean-Christophe PLAGNIOL-VILLARD result = I2C_NOK; 346d3b63577SJean-Christophe PLAGNIOL-VILLARD break; 347d3b63577SJean-Christophe PLAGNIOL-VILLARD } 348d3b63577SJean-Christophe PLAGNIOL-VILLARD 349d3b63577SJean-Christophe PLAGNIOL-VILLARD return (result); 350d3b63577SJean-Christophe PLAGNIOL-VILLARD } 351d3b63577SJean-Christophe PLAGNIOL-VILLARD 352d3b63577SJean-Christophe PLAGNIOL-VILLARD int i2c_probe(uchar chip) 353d3b63577SJean-Christophe PLAGNIOL-VILLARD { 354d3b63577SJean-Christophe PLAGNIOL-VILLARD uchar buf[1]; 355d3b63577SJean-Christophe PLAGNIOL-VILLARD 356d3b63577SJean-Christophe PLAGNIOL-VILLARD buf[0] = 0; 357d3b63577SJean-Christophe PLAGNIOL-VILLARD 358d3b63577SJean-Christophe PLAGNIOL-VILLARD /* 359d3b63577SJean-Christophe PLAGNIOL-VILLARD * What is needed is to send the chip address and verify that the 360d3b63577SJean-Christophe PLAGNIOL-VILLARD * address was <ACK>ed (i.e. there was a chip at that address which 361d3b63577SJean-Christophe PLAGNIOL-VILLARD * drove the data line low). 362d3b63577SJean-Christophe PLAGNIOL-VILLARD */ 363eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk return i2c_transfer(I2C_READ, chip << 1, 0, 0, buf, 1) != I2C_OK; 364d3b63577SJean-Christophe PLAGNIOL-VILLARD } 365d3b63577SJean-Christophe PLAGNIOL-VILLARD 366d3b63577SJean-Christophe PLAGNIOL-VILLARD int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) 367d3b63577SJean-Christophe PLAGNIOL-VILLARD { 368d3b63577SJean-Christophe PLAGNIOL-VILLARD uchar xaddr[4]; 369d3b63577SJean-Christophe PLAGNIOL-VILLARD int ret; 370d3b63577SJean-Christophe PLAGNIOL-VILLARD 371d3b63577SJean-Christophe PLAGNIOL-VILLARD if (alen > 4) { 372d3b63577SJean-Christophe PLAGNIOL-VILLARD printf("I2C read: addr len %d not supported\n", alen); 373d3b63577SJean-Christophe PLAGNIOL-VILLARD return 1; 374d3b63577SJean-Christophe PLAGNIOL-VILLARD } 375d3b63577SJean-Christophe PLAGNIOL-VILLARD 376d3b63577SJean-Christophe PLAGNIOL-VILLARD if (alen > 0) { 377d3b63577SJean-Christophe PLAGNIOL-VILLARD xaddr[0] = (addr >> 24) & 0xFF; 378d3b63577SJean-Christophe PLAGNIOL-VILLARD xaddr[1] = (addr >> 16) & 0xFF; 379d3b63577SJean-Christophe PLAGNIOL-VILLARD xaddr[2] = (addr >> 8) & 0xFF; 380d3b63577SJean-Christophe PLAGNIOL-VILLARD xaddr[3] = addr & 0xFF; 381d3b63577SJean-Christophe PLAGNIOL-VILLARD } 382d3b63577SJean-Christophe PLAGNIOL-VILLARD 383d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW 384d3b63577SJean-Christophe PLAGNIOL-VILLARD /* 385d3b63577SJean-Christophe PLAGNIOL-VILLARD * EEPROM chips that implement "address overflow" are ones 386d3b63577SJean-Christophe PLAGNIOL-VILLARD * like Catalyst 24WC04/08/16 which has 9/10/11 bits of 387d3b63577SJean-Christophe PLAGNIOL-VILLARD * address and the extra bits end up in the "chip address" 388d3b63577SJean-Christophe PLAGNIOL-VILLARD * bit slots. This makes a 24WC08 (1Kbyte) chip look like 389d3b63577SJean-Christophe PLAGNIOL-VILLARD * four 256 byte chips. 390d3b63577SJean-Christophe PLAGNIOL-VILLARD * 391d3b63577SJean-Christophe PLAGNIOL-VILLARD * Note that we consider the length of the address field to 392d3b63577SJean-Christophe PLAGNIOL-VILLARD * still be one byte because the extra address bits are 393d3b63577SJean-Christophe PLAGNIOL-VILLARD * hidden in the chip address. 394d3b63577SJean-Christophe PLAGNIOL-VILLARD */ 395d3b63577SJean-Christophe PLAGNIOL-VILLARD if (alen > 0) 396eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk chip |= ((addr >> (alen * 8)) & 397eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); 398d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif 399d3b63577SJean-Christophe PLAGNIOL-VILLARD if ((ret = 400d3b63577SJean-Christophe PLAGNIOL-VILLARD i2c_transfer(I2C_READ, chip << 1, &xaddr[4 - alen], alen, 401d3b63577SJean-Christophe PLAGNIOL-VILLARD buffer, len)) != 0) { 402d3b63577SJean-Christophe PLAGNIOL-VILLARD printf("I2c read: failed %d\n", ret); 403d3b63577SJean-Christophe PLAGNIOL-VILLARD return 1; 404d3b63577SJean-Christophe PLAGNIOL-VILLARD } 405d3b63577SJean-Christophe PLAGNIOL-VILLARD return 0; 406d3b63577SJean-Christophe PLAGNIOL-VILLARD } 407d3b63577SJean-Christophe PLAGNIOL-VILLARD 408d3b63577SJean-Christophe PLAGNIOL-VILLARD int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) 409d3b63577SJean-Christophe PLAGNIOL-VILLARD { 410d3b63577SJean-Christophe PLAGNIOL-VILLARD uchar xaddr[4]; 411d3b63577SJean-Christophe PLAGNIOL-VILLARD 412d3b63577SJean-Christophe PLAGNIOL-VILLARD if (alen > 4) { 413d3b63577SJean-Christophe PLAGNIOL-VILLARD printf("I2C write: addr len %d not supported\n", alen); 414d3b63577SJean-Christophe PLAGNIOL-VILLARD return 1; 415d3b63577SJean-Christophe PLAGNIOL-VILLARD } 416d3b63577SJean-Christophe PLAGNIOL-VILLARD 417d3b63577SJean-Christophe PLAGNIOL-VILLARD if (alen > 0) { 418d3b63577SJean-Christophe PLAGNIOL-VILLARD xaddr[0] = (addr >> 24) & 0xFF; 419d3b63577SJean-Christophe PLAGNIOL-VILLARD xaddr[1] = (addr >> 16) & 0xFF; 420d3b63577SJean-Christophe PLAGNIOL-VILLARD xaddr[2] = (addr >> 8) & 0xFF; 421d3b63577SJean-Christophe PLAGNIOL-VILLARD xaddr[3] = addr & 0xFF; 422d3b63577SJean-Christophe PLAGNIOL-VILLARD } 423d3b63577SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW 424d3b63577SJean-Christophe PLAGNIOL-VILLARD /* 425d3b63577SJean-Christophe PLAGNIOL-VILLARD * EEPROM chips that implement "address overflow" are ones 426d3b63577SJean-Christophe PLAGNIOL-VILLARD * like Catalyst 24WC04/08/16 which has 9/10/11 bits of 427d3b63577SJean-Christophe PLAGNIOL-VILLARD * address and the extra bits end up in the "chip address" 428d3b63577SJean-Christophe PLAGNIOL-VILLARD * bit slots. This makes a 24WC08 (1Kbyte) chip look like 429d3b63577SJean-Christophe PLAGNIOL-VILLARD * four 256 byte chips. 430d3b63577SJean-Christophe PLAGNIOL-VILLARD * 431d3b63577SJean-Christophe PLAGNIOL-VILLARD * Note that we consider the length of the address field to 432d3b63577SJean-Christophe PLAGNIOL-VILLARD * still be one byte because the extra address bits are 433d3b63577SJean-Christophe PLAGNIOL-VILLARD * hidden in the chip address. 434d3b63577SJean-Christophe PLAGNIOL-VILLARD */ 435d3b63577SJean-Christophe PLAGNIOL-VILLARD if (alen > 0) 436eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk chip |= ((addr >> (alen * 8)) & 437eb0ae7f5Skevin.morfitt@fearnside-systems.co.uk CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); 438d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif 439d3b63577SJean-Christophe PLAGNIOL-VILLARD return (i2c_transfer 440d3b63577SJean-Christophe PLAGNIOL-VILLARD (I2C_WRITE, chip << 1, &xaddr[4 - alen], alen, buffer, 441d3b63577SJean-Christophe PLAGNIOL-VILLARD len) != 0); 442d3b63577SJean-Christophe PLAGNIOL-VILLARD } 443d3b63577SJean-Christophe PLAGNIOL-VILLARD #endif /* CONFIG_HARD_I2C */ 444