| /* |
| * Copyright (c) 2007 by Silicon Motion, Inc. (SMI) |
| * |
| * All rights are reserved. Reproduction or in part is prohibited |
| * without the written consent of the copyright owner. |
| * |
| * swi2c.c --- SM750/SM718 DDK |
| * This file contains the source code for I2C using software |
| * implementation. |
| */ |
| |
| #include "ddk750_chip.h" |
| #include "ddk750_reg.h" |
| #include "ddk750_swi2c.h" |
| #include "ddk750_power.h" |
| |
| /* |
| * I2C Software Master Driver: |
| * =========================== |
| * Each i2c cycle is split into 4 sections. Each of these section marks |
| * a point in time where the SCL or SDA may be changed. |
| * |
| * 1 Cycle == | Section I. | Section 2. | Section 3. | Section 4. | |
| * +-------------+-------------+-------------+-------------+ |
| * | SCL set LOW |SCL no change| SCL set HIGH|SCL no change| |
| * |
| * ____________ _____________ |
| * SCL == XXXX _____________ ____________ / |
| * |
| * I.e. the SCL may only be changed in section 1. and section 3. while |
| * the SDA may only be changed in section 2. and section 4. The table |
| * below gives the changes for these 2 lines in the varios sections. |
| * |
| * Section changes Table: |
| * ====================== |
| * blank = no change, L = set bit LOW, H = set bit HIGH |
| * |
| * | 1.| 2.| 3.| 4.| |
| * ---------------+---+---+---+---+ |
| * Tx Start SDA | | H | | L | |
| * SCL | L | | H | | |
| * ---------------+---+---+---+---+ |
| * Tx Stop SDA | | L | | H | |
| * SCL | L | | H | | |
| * ---------------+---+---+---+---+ |
| * Tx bit H SDA | | H | | | |
| * SCL | L | | H | | |
| * ---------------+---+---+---+---+ |
| * Tx bit L SDA | | L | | | |
| * SCL | L | | H | | |
| * ---------------+---+---+---+---+ |
| * |
| */ |
| |
| /* GPIO pins used for this I2C. It ranges from 0 to 63. */ |
| static unsigned char sw_i2c_clk_gpio = DEFAULT_I2C_SCL; |
| static unsigned char sw_i2c_data_gpio = DEFAULT_I2C_SDA; |
| |
| /* |
| * Below is the variable declaration for the GPIO pin register usage |
| * for the i2c Clock and i2c Data. |
| * |
| * Note: |
| * Notice that the GPIO usage for the i2c clock and i2c Data are |
| * separated. This is to make this code flexible enough when |
| * two separate GPIO pins for the clock and data are located |
| * in two different GPIO register set (worst case). |
| */ |
| |
| /* i2c Clock GPIO Register usage */ |
| static unsigned long sw_i2c_clk_gpio_mux_reg = GPIO_MUX; |
| static unsigned long sw_i2c_clk_gpio_data_reg = GPIO_DATA; |
| static unsigned long sw_i2c_clk_gpio_data_dir_reg = GPIO_DATA_DIRECTION; |
| |
| /* i2c Data GPIO Register usage */ |
| static unsigned long sw_i2c_data_gpio_mux_reg = GPIO_MUX; |
| static unsigned long sw_i2c_data_gpio_data_reg = GPIO_DATA; |
| static unsigned long sw_i2c_data_gpio_data_dir_reg = GPIO_DATA_DIRECTION; |
| |
| /* |
| * This function puts a delay between command |
| */ |
| static void sw_i2c_wait(void) |
| { |
| /* find a bug: |
| * peekIO method works well before suspend/resume |
| * but after suspend, peekIO(0x3ce,0x61) & 0x10 |
| * always be non-zero,which makes the while loop |
| * never finish. |
| * use non-ultimate for loop below is safe |
| */ |
| |
| /* Change wait algorithm to use PCI bus clock, |
| * it's more reliable than counter loop .. |
| * write 0x61 to 0x3ce and read from 0x3cf |
| */ |
| int i, tmp; |
| |
| for (i = 0; i < 600; i++) { |
| tmp = i; |
| tmp += i; |
| } |
| } |
| |
| /* |
| * This function set/reset the SCL GPIO pin |
| * |
| * Parameters: |
| * value - Bit value to set to the SCL or SDA (0 = low, 1 = high) |
| * |
| * Notes: |
| * When setting SCL to high, just set the GPIO as input where the pull up |
| * resistor will pull the signal up. Do not use software to pull up the |
| * signal because the i2c will fail when other device try to drive the |
| * signal due to SM50x will drive the signal to always high. |
| */ |
| static void sw_i2c_scl(unsigned char value) |
| { |
| unsigned long gpio_data; |
| unsigned long gpio_dir; |
| |
| gpio_dir = peek32(sw_i2c_clk_gpio_data_dir_reg); |
| if (value) { /* High */ |
| /* |
| * Set direction as input. This will automatically |
| * pull the signal up. |
| */ |
| gpio_dir &= ~(1 << sw_i2c_clk_gpio); |
| poke32(sw_i2c_clk_gpio_data_dir_reg, gpio_dir); |
| } else { /* Low */ |
| /* Set the signal down */ |
| gpio_data = peek32(sw_i2c_clk_gpio_data_reg); |
| gpio_data &= ~(1 << sw_i2c_clk_gpio); |
| poke32(sw_i2c_clk_gpio_data_reg, gpio_data); |
| |
| /* Set direction as output */ |
| gpio_dir |= (1 << sw_i2c_clk_gpio); |
| poke32(sw_i2c_clk_gpio_data_dir_reg, gpio_dir); |
| } |
| } |
| |
| /* |
| * This function set/reset the SDA GPIO pin |
| * |
| * Parameters: |
| * value - Bit value to set to the SCL or SDA (0 = low, 1 = high) |
| * |
| * Notes: |
| * When setting SCL to high, just set the GPIO as input where the pull up |
| * resistor will pull the signal up. Do not use software to pull up the |
| * signal because the i2c will fail when other device try to drive the |
| * signal due to SM50x will drive the signal to always high. |
| */ |
| static void sw_i2c_sda(unsigned char value) |
| { |
| unsigned long gpio_data; |
| unsigned long gpio_dir; |
| |
| gpio_dir = peek32(sw_i2c_data_gpio_data_dir_reg); |
| if (value) { /* High */ |
| /* |
| * Set direction as input. This will automatically |
| * pull the signal up. |
| */ |
| gpio_dir &= ~(1 << sw_i2c_data_gpio); |
| poke32(sw_i2c_data_gpio_data_dir_reg, gpio_dir); |
| } else { /* Low */ |
| /* Set the signal down */ |
| gpio_data = peek32(sw_i2c_data_gpio_data_reg); |
| gpio_data &= ~(1 << sw_i2c_data_gpio); |
| poke32(sw_i2c_data_gpio_data_reg, gpio_data); |
| |
| /* Set direction as output */ |
| gpio_dir |= (1 << sw_i2c_data_gpio); |
| poke32(sw_i2c_data_gpio_data_dir_reg, gpio_dir); |
| } |
| } |
| |
| /* |
| * This function read the data from the SDA GPIO pin |
| * |
| * Return Value: |
| * The SDA data bit sent by the Slave |
| */ |
| static unsigned char sw_i2c_read_sda(void) |
| { |
| unsigned long gpio_dir; |
| unsigned long gpio_data; |
| unsigned long dir_mask = 1 << sw_i2c_data_gpio; |
| |
| /* Make sure that the direction is input (High) */ |
| gpio_dir = peek32(sw_i2c_data_gpio_data_dir_reg); |
| if ((gpio_dir & dir_mask) != ~dir_mask) { |
| gpio_dir &= ~(1 << sw_i2c_data_gpio); |
| poke32(sw_i2c_data_gpio_data_dir_reg, gpio_dir); |
| } |
| |
| /* Now read the SDA line */ |
| gpio_data = peek32(sw_i2c_data_gpio_data_reg); |
| if (gpio_data & (1 << sw_i2c_data_gpio)) |
| return 1; |
| else |
| return 0; |
| } |
| |
| /* |
| * This function sends ACK signal |
| */ |
| static void sw_i2c_ack(void) |
| { |
| return; /* Single byte read is ok without it. */ |
| } |
| |
| /* |
| * This function sends the start command to the slave device |
| */ |
| static void sw_i2c_start(void) |
| { |
| /* Start I2C */ |
| sw_i2c_sda(1); |
| sw_i2c_scl(1); |
| sw_i2c_sda(0); |
| } |
| |
| /* |
| * This function sends the stop command to the slave device |
| */ |
| static void sw_i2c_stop(void) |
| { |
| /* Stop the I2C */ |
| sw_i2c_scl(1); |
| sw_i2c_sda(0); |
| sw_i2c_sda(1); |
| } |
| |
| /* |
| * This function writes one byte to the slave device |
| * |
| * Parameters: |
| * data - Data to be write to the slave device |
| * |
| * Return Value: |
| * 0 - Success |
| * -1 - Fail to write byte |
| */ |
| static long sw_i2c_write_byte(unsigned char data) |
| { |
| unsigned char value = data; |
| int i; |
| |
| /* Sending the data bit by bit */ |
| for (i = 0; i < 8; i++) { |
| /* Set SCL to low */ |
| sw_i2c_scl(0); |
| |
| /* Send data bit */ |
| if ((value & 0x80) != 0) |
| sw_i2c_sda(1); |
| else |
| sw_i2c_sda(0); |
| |
| sw_i2c_wait(); |
| |
| /* Toggle clk line to one */ |
| sw_i2c_scl(1); |
| sw_i2c_wait(); |
| |
| /* Shift byte to be sent */ |
| value = value << 1; |
| } |
| |
| /* Set the SCL Low and SDA High (prepare to get input) */ |
| sw_i2c_scl(0); |
| sw_i2c_sda(1); |
| |
| /* Set the SCL High for ack */ |
| sw_i2c_wait(); |
| sw_i2c_scl(1); |
| sw_i2c_wait(); |
| |
| /* Read SDA, until SDA==0 */ |
| for (i = 0; i < 0xff; i++) { |
| if (!sw_i2c_read_sda()) |
| break; |
| |
| sw_i2c_scl(0); |
| sw_i2c_wait(); |
| sw_i2c_scl(1); |
| sw_i2c_wait(); |
| } |
| |
| /* Set the SCL Low and SDA High */ |
| sw_i2c_scl(0); |
| sw_i2c_sda(1); |
| |
| if (i < 0xff) |
| return 0; |
| else |
| return -1; |
| } |
| |
| /* |
| * This function reads one byte from the slave device |
| * |
| * Parameters: |
| * ack - Flag to indicate either to send the acknowledge |
| * message to the slave device or not |
| * |
| * Return Value: |
| * One byte data read from the Slave device |
| */ |
| static unsigned char sw_i2c_read_byte(unsigned char ack) |
| { |
| int i; |
| unsigned char data = 0; |
| |
| for (i = 7; i >= 0; i--) { |
| /* Set the SCL to Low and SDA to High (Input) */ |
| sw_i2c_scl(0); |
| sw_i2c_sda(1); |
| sw_i2c_wait(); |
| |
| /* Set the SCL High */ |
| sw_i2c_scl(1); |
| sw_i2c_wait(); |
| |
| /* Read data bits from SDA */ |
| data |= (sw_i2c_read_sda() << i); |
| } |
| |
| if (ack) |
| sw_i2c_ack(); |
| |
| /* Set the SCL Low and SDA High */ |
| sw_i2c_scl(0); |
| sw_i2c_sda(1); |
| |
| return data; |
| } |
| |
| /* |
| * This function initializes GPIO port for SW I2C communication. |
| * |
| * Parameters: |
| * clk_gpio - The GPIO pin to be used as i2c SCL |
| * data_gpio - The GPIO pin to be used as i2c SDA |
| * |
| * Return Value: |
| * -1 - Fail to initialize the i2c |
| * 0 - Success |
| */ |
| static long sm750le_i2c_init(unsigned char clk_gpio, unsigned char data_gpio) |
| { |
| int i; |
| |
| /* Initialize the GPIO pin for the i2c Clock Register */ |
| sw_i2c_clk_gpio_data_reg = GPIO_DATA_SM750LE; |
| sw_i2c_clk_gpio_data_dir_reg = GPIO_DATA_DIRECTION_SM750LE; |
| |
| /* Initialize the Clock GPIO Offset */ |
| sw_i2c_clk_gpio = clk_gpio; |
| |
| /* Initialize the GPIO pin for the i2c Data Register */ |
| sw_i2c_data_gpio_data_reg = GPIO_DATA_SM750LE; |
| sw_i2c_data_gpio_data_dir_reg = GPIO_DATA_DIRECTION_SM750LE; |
| |
| /* Initialize the Data GPIO Offset */ |
| sw_i2c_data_gpio = data_gpio; |
| |
| /* Note that SM750LE don't have GPIO MUX and power is always on */ |
| |
| /* Clear the i2c lines. */ |
| for (i = 0; i < 9; i++) |
| sw_i2c_stop(); |
| |
| return 0; |
| } |
| |
| /* |
| * This function initializes the i2c attributes and bus |
| * |
| * Parameters: |
| * clk_gpio - The GPIO pin to be used as i2c SCL |
| * data_gpio - The GPIO pin to be used as i2c SDA |
| * |
| * Return Value: |
| * -1 - Fail to initialize the i2c |
| * 0 - Success |
| */ |
| long sm750_sw_i2c_init(unsigned char clk_gpio, unsigned char data_gpio) |
| { |
| int i; |
| |
| /* |
| * Return 0 if the GPIO pins to be used is out of range. The |
| * range is only from [0..63] |
| */ |
| if ((clk_gpio > 31) || (data_gpio > 31)) |
| return -1; |
| |
| if (sm750_get_chip_type() == SM750LE) |
| return sm750le_i2c_init(clk_gpio, data_gpio); |
| |
| /* Initialize the GPIO pin for the i2c Clock Register */ |
| sw_i2c_clk_gpio_mux_reg = GPIO_MUX; |
| sw_i2c_clk_gpio_data_reg = GPIO_DATA; |
| sw_i2c_clk_gpio_data_dir_reg = GPIO_DATA_DIRECTION; |
| |
| /* Initialize the Clock GPIO Offset */ |
| sw_i2c_clk_gpio = clk_gpio; |
| |
| /* Initialize the GPIO pin for the i2c Data Register */ |
| sw_i2c_data_gpio_mux_reg = GPIO_MUX; |
| sw_i2c_data_gpio_data_reg = GPIO_DATA; |
| sw_i2c_data_gpio_data_dir_reg = GPIO_DATA_DIRECTION; |
| |
| /* Initialize the Data GPIO Offset */ |
| sw_i2c_data_gpio = data_gpio; |
| |
| /* Enable the GPIO pins for the i2c Clock and Data (GPIO MUX) */ |
| poke32(sw_i2c_clk_gpio_mux_reg, |
| peek32(sw_i2c_clk_gpio_mux_reg) & ~(1 << sw_i2c_clk_gpio)); |
| poke32(sw_i2c_data_gpio_mux_reg, |
| peek32(sw_i2c_data_gpio_mux_reg) & ~(1 << sw_i2c_data_gpio)); |
| |
| /* Enable GPIO power */ |
| sm750_enable_gpio(1); |
| |
| /* Clear the i2c lines. */ |
| for (i = 0; i < 9; i++) |
| sw_i2c_stop(); |
| |
| return 0; |
| } |
| |
| /* |
| * This function reads the slave device's register |
| * |
| * Parameters: |
| * addr - i2c Slave device address which register |
| * to be read from |
| * reg - Slave device's register to be read |
| * |
| * Return Value: |
| * Register value |
| */ |
| unsigned char sm750_sw_i2c_read_reg(unsigned char addr, unsigned char reg) |
| { |
| unsigned char data; |
| |
| /* Send the Start signal */ |
| sw_i2c_start(); |
| |
| /* Send the device address */ |
| sw_i2c_write_byte(addr); |
| |
| /* Send the register index */ |
| sw_i2c_write_byte(reg); |
| |
| /* Get the bus again and get the data from the device read address */ |
| sw_i2c_start(); |
| sw_i2c_write_byte(addr + 1); |
| data = sw_i2c_read_byte(1); |
| |
| /* Stop swI2C and release the bus */ |
| sw_i2c_stop(); |
| |
| return data; |
| } |
| |
| /* |
| * This function writes a value to the slave device's register |
| * |
| * Parameters: |
| * addr - i2c Slave device address which register |
| * to be written |
| * reg - Slave device's register to be written |
| * data - Data to be written to the register |
| * |
| * Result: |
| * 0 - Success |
| * -1 - Fail |
| */ |
| long sm750_sw_i2c_write_reg(unsigned char addr, |
| unsigned char reg, |
| unsigned char data) |
| { |
| long ret = 0; |
| |
| /* Send the Start signal */ |
| sw_i2c_start(); |
| |
| /* Send the device address and read the data. All should return success |
| * in order for the writing processed to be successful |
| */ |
| if ((sw_i2c_write_byte(addr) != 0) || |
| (sw_i2c_write_byte(reg) != 0) || |
| (sw_i2c_write_byte(data) != 0)) { |
| ret = -1; |
| } |
| |
| /* Stop i2c and release the bus */ |
| sw_i2c_stop(); |
| |
| return ret; |
| } |