123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516 |
- /*******************************************************************
- *
- * 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_help.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 (getChipType() == 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 */
- enableGPIO(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;
- }
|