/* * $Id$ * * HWDD NVRAM module : TWI related routines * Copyright (c) 2011 Network Appliance, Inc. * */ #include #include #include #include #include "hwdd_nvram.h" #define DIP0_TWI_ADDR (0x9c >> 1) #define DIP1_TWI_ADDR (0x9e >> 1) /* TWI register offsets */ #define TWI_ELEMENT 0x0 #define TWI_FEATURE 0x4 #define TWI_INTR_MASK 0x08 #define TWI_INTR_STAT 0x0c #define TWI_CTRL 0x10 #define TWI_STAT 0x14 #define TWI_DATA_BUF_START 0x40 #define TWI_DATA_BUF_END 0x80 #define TWI_ANAK 12 #define TWI_DNAK 11 #define TWI_LOOP 1000 /* * TWI control register bit settings are as follows, detailed description * of these bits are availabe in the TWI spec. * bits [6:0] - slave address * bit [7] - GO bit, setting of which initiates TWI transaction * bits [9:8] - CMD len * bit [12] - R/W * bit [13] - interrupt * bit [14] - SCL16 * bit [15] - Reset * bits [21-16] - Data Length * Below Macros are for setting some of these bits */ #define TWI_SET_ADDR(ctrl, addr) \ ctrl &= ~((u32)0x7f); \ ctrl |= (addr & ((u32)0x7f)); #define TWI_SET_DATALEN(ctrl, data_len) \ ctrl &= ~((u32)(0x3f << 16)); \ ctrl |= (data_len << 16); #define TWI_SET_CMDLEN(ctrl, cmd_len) \ ctrl &= ~((u32)(0x3 << 8)); \ ctrl |= (cmd_len << 8); #define TWI_SET_READ(ctrl) \ ctrl |= (1 << 12); #define TWI_SET_WRITE(ctrl) \ ctrl &= ~(1 << 12); #define TWI_SET_GO(ctrl) \ ctrl |= (1 << 7); #ifdef TWI_REGDUMP static void twi_regdump(void); #endif static int twi_configure(int offset, u8 addr, int len, int cmd_len, u32 *ctrl); static int twi_read(int offset, u8 addr, void *ptr, int len, int cmd_len); static int twi_read_data_buf(int offset, void *buf, int len); static int dip0_offset= -1, dip1_offset = -1; /* * dip_access: * PCA9559, DIP switch(es) access routine * There is only one register to access hence only 1 byte is read * returns -1 on error, else the value read */ int dip_access(u8 dip_id) { u8 buf; switch (dip_id) { case DIP0: if (dip0_offset < 0) return -1; if (twi_read(dip0_offset, DIP0_TWI_ADDR, &buf, 1, 0 )) { printk(KERN_ERR "TWI read on DIP0 failed\n"); return -1; } break; case DIP1: if (dip1_offset < 0) return -1; if (twi_read(dip1_offset, DIP1_TWI_ADDR, &buf, 1, 0 )) { printk(KERN_ERR "TWI read on DIP1 failed\n"); return -1; } break; default: printk(KERN_ERR "invalid dip id:%d \n", dip_id); return -1; } return (int)buf ; } /* * This routine is used to read the TWI hardware data buffers * these buffers will hold the value read from the TWI slave device. * @offset: TWI slave start offset * buf : buffer to hold read data * @len: number of bytes to read * TODO, should return the number of bytes actually read */ static int twi_read_data_buf(int offset, void *buf, int len) { int ret, dbuf_offset = TWI_DATA_BUF_START; int cnt = 0 ; u32 data; u32 *tmp_buf = buf; while ( (dbuf_offset + cnt ) < TWI_DATA_BUF_END) { ret = read_reg(offset + dbuf_offset + cnt, &data); if (ret) { printk(KERN_ERR "read_reg (dbuf) failed \n"); return ret; } cnt += 4; if (len <= cnt) { int store, i; /* ok, so we have enough data, should we discard any bytes ? */ store = cnt - len; for (i=0; i< (4-store); i++) { *(((u8*)tmp_buf) + i) = (u8)(data & 0xff); data >>= 8; } break; } else { *tmp_buf = data; tmp_buf++ ; } } return 0; } /* * twi_configure: prparee the value to be written to TWI control register * @offset : offset of the TWI controller * @addr : TWI slave device * @len : Total number of bytes to write, max of 64 bytes (this is command + data) * @cmd_len: Number of command bytes in the buffer (max 3) * returns 0 on success */ static int twi_configure(int offset, u8 addr, int len, int cmd_len, u32 *ctrl) { int ret; if (len > 64 || cmd_len > 3) return -1; ret = read_reg(offset + TWI_CTRL, ctrl); if (ret) { printk(KERN_ERR "TWI Control Registar, read access failure !\n" ); return ret; } /* set the address bits 6:0 */ TWI_SET_ADDR(*ctrl, addr); /* set the data length bits, 21:16 */ TWI_SET_DATALEN(*ctrl, len); /* set the command length bits, 9:8 */ TWI_SET_CMDLEN(*ctrl, cmd_len); return 0; } /* * Routine to initiate a TWI read transaction * @offset : offset of the TWI controller * @addr: TWI slave device address * @ptr : buffer to hold the read data (also commands to write, if any) * @len : Number of bytes to read * @cmd_len : Number of command bytes, if any (these commands should be the first 'cmd_len' bytes of buffer * pointed to by ptr */ static int twi_read(int offset, u8 addr, void *ptr, int len, int cmd_len) { u32 ctrl=0xdeadbeef; int ret = 0; int cnt = 0; u32 stat; ret = twi_configure(offset, addr, len, cmd_len, &ctrl); if (ret) { printk(KERN_ERR "twi_configure() failed \n"); return ret; } /* set the read bit */ TWI_SET_READ(ctrl); write_reg(offset + TWI_CTRL, ctrl); /* start the data transfer (setting this separately as I am not sure of the impact if done in unison*/ TWI_SET_GO(ctrl); write_reg(offset + TWI_CTRL, ctrl); /* wait till the data xfer is completed or we time out */ while (1) { ret = read_reg(offset + TWI_CTRL, &ctrl); if (ret) { printk(KERN_ERR "read_reg (ctrl) failed \n"); return ret; } ret = read_reg(offset + TWI_STAT, &stat); if (ret) { printk(KERN_ERR "read_reg (stat) failed \n"); return ret; } if (ctrl & (1 << 7)) { if (cnt < TWI_LOOP) schedule(); else { printk(KERN_CRIT "Error, timedout waiting for completion, ctrl:%x stat:%x\n", ctrl, stat); return -1; } } else break; } /* Once the data transfer is successful, bit 12 (Addr NACK) should be clear */ if (stat & (1 << TWI_ANAK)) { printk(KERN_ERR "TWI transaction terminated prematurely with address NACK \n"); return -1; } /* * check if Data NACK is set, as per TWI spec, if this bit is set, it could either mean a success or a error * condition, if the NACK occured, prior to all the data bytes getting transferred. This premature NACK is * indicated when the ACTUAL field does not match DATALEN field */ if (stat & (1 << TWI_DNAK)) { int actual = ((stat >> 16) & 0x3f) ; if (actual != len) { printk(KERN_ERR "TWI data NACK, expected :%d actual :%d\n", len, actual); return -1; } } /* if we are here then the read was a success, get the data read from the data buffers */ ret = twi_read_data_buf(offset, ptr, (len - cmd_len)); return ret; } /* * Detect the presence of DIP0 and DIP1 related TWI registers and get * the offsets */ void twi_init(void) { dip0_offset = get_mri_base(MRI_TWI_ELEID, DIP0); if (dip0_offset < 0) { printk(KERN_ERR "No TWI DIP 0 element found \n"); return; } dip1_offset = get_mri_base(MRI_TWI_ELEID, DIP1); if (dip1_offset < 0) { printk(KERN_ERR "No TWI DIP 1 element found \n"); return; } #ifdef TWI_REGDUMP twi_regdump(); #endif } #ifdef TWI_REGDUMP /* * This rouine is not compiled by default, and is there for my debugging * will be removing this at a later point of time. */ static void twi_regdump(void) { u32 ctrl=0xdead, stat=0xbeef, ele=0xaa; int ret; printk(KERN_CRIT "DIP0, offsets, TWI ctrl:%x TWI stat:%x\n", dip0_offset + TWI_CTRL, dip0_offset + TWI_STAT); ret = read_reg(dip0_offset + TWI_ELEMENT, &ele); if (!ret) { printk(KERN_CRIT " DIP0, TWI ELE:%x\n", ele); } else printk(KERN_CRIT "read_reg failed ele:\n"); ret = read_reg(dip0_offset + TWI_FEATURE, &ele); if (!ret) { printk(KERN_CRIT " DIP0, TWI FEA:%x\n", ele); } else printk(KERN_CRIT "read_reg failed fea:\n"); ret = read_reg(dip0_offset + TWI_INTR_MASK, &ele); if (!ret) { printk(KERN_CRIT " DIP0, TWI INTR MASK:%x\n", ele); } else printk(KERN_CRIT "read_reg failed intr_mask:\n"); ret = read_reg(dip0_offset + TWI_INTR_STAT, &ele); if (!ret) { printk(KERN_CRIT " DIP0, TWI INTR STAT:%x\n", ele); } else printk(KERN_CRIT "read_reg failed intr_stat:\n"); ret = read_reg(dip0_offset + TWI_STAT, &stat); if (!ret) { printk(KERN_CRIT " DIP0, TWI STAT:%x\n", stat); } else printk(KERN_CRIT "read_reg failed 2:\n"); ret = read_reg(dip0_offset + TWI_CTRL, &ctrl); if (!ret) { printk(KERN_CRIT " DIP0, TWI ctrl:%x\n", ctrl); } else printk(KERN_CRIT "read_reg failed 1:\n"); printk(KERN_CRIT "Writing to TWI ctrl reg\n"); write_reg(dip0_offset + TWI_CTRL, 0x5); ret = read_reg(dip0_offset + TWI_CTRL, &ctrl); if (!ret) { printk(KERN_CRIT " DIP0, TWI ctrl:%x\n", ctrl); } else printk(KERN_CRIT "read_reg failed 1:\n"); printk(KERN_CRIT "DIP1, offsets, TWI ctrl:%x TWI stat:%x\n", dip1_offset + TWI_CTRL, dip1_offset + TWI_STAT); ret = read_reg(dip1_offset + TWI_CTRL, &ctrl); if (!ret) { printk(KERN_CRIT " DIP1, TWI ctrl:%x\n", ctrl); } else printk(KERN_CRIT "read_reg failed 3:\n"); ret = read_reg(dip1_offset + TWI_STAT, &stat); if (!ret) { printk(KERN_CRIT " DIP1, TWI STAT:%x\n", stat); } else printk(KERN_CRIT "read_reg failed 4:\n"); } #endif