124 lines
3.3 KiB
C
124 lines
3.3 KiB
C
/* Driver for I2C Master Controller inside TI Calypso */
|
|
|
|
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
|
|
*
|
|
* All Rights Reserved
|
|
*
|
|
* 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.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
|
|
#include <debug.h>
|
|
#include <memory.h>
|
|
#include <i2c.h>
|
|
|
|
#define BASE_ADDR_I2C 0xfffe2800
|
|
#define I2C_REG(x) (BASE_ADDR_I2C+(x))
|
|
|
|
enum i2c_reg {
|
|
DEVICE_REG = 0,
|
|
ADDRESS_REG,
|
|
DATA_WR_REG,
|
|
DATA_RD_REG,
|
|
CMD_REG,
|
|
CONF_FIFO_REG,
|
|
CONF_CLK_REG,
|
|
CONF_CLK_FUNC_REF,
|
|
STATUS_FIFO_REG,
|
|
STATUS_ACTIVITY_REG,
|
|
};
|
|
|
|
#define I2C_CMD_SOFT_RESET (1 << 0)
|
|
#define I2C_CMD_EN_CLK (1 << 1)
|
|
#define I2C_CMD_START (1 << 2)
|
|
#define I2C_CMD_RW_READ (1 << 3)
|
|
#define I2C_CMD_COMP_READ (1 << 4)
|
|
#define I2C_CMD_IRQ_ENABLE (1 << 5)
|
|
|
|
#define I2C_STATUS_ERROR_DATA (1 << 0)
|
|
#define I2C_STATUS_ERROR_DEV (1 << 1)
|
|
#define I2C_STATUS_IDLE (1 << 2)
|
|
#define I2C_STATUS_INTERRUPT (1 << 3)
|
|
|
|
int i2c_write(uint8_t chip, uint32_t addr, int alen, const uint8_t *buffer, int len)
|
|
{
|
|
uint8_t cmd;
|
|
|
|
/* Calypso I2C controller doesn't support fancy addressing */
|
|
if (alen > 1)
|
|
return -1;
|
|
|
|
/* FIXME: implement writes longer than fifo size */
|
|
if (len > 16)
|
|
return -1;
|
|
|
|
printd("i2c_write(chip=0x%02u, addr=0x%02u): ", chip, addr)
|
|
|
|
writeb(chip & 0x3f, I2C_REG(DEVICE_REG));
|
|
writeb(addr & 0xff, I2C_REG(ADDRESS_REG));
|
|
|
|
/* we have to tell the controler how many bits we'll put into the fifo ?!? */
|
|
writeb(len-1, I2C_REG(CONF_FIFO_REG));
|
|
|
|
/* fill the FIFO */
|
|
while (len--) {
|
|
uint8_t byte = *buffer++;
|
|
writeb(byte, I2C_REG(DATA_WR_REG));
|
|
printd("%02X ", byte);
|
|
}
|
|
dputchar('\n');
|
|
|
|
/* start the transfer */
|
|
cmd = readb(I2C_REG(CMD_REG));
|
|
cmd |= I2C_CMD_START;
|
|
writeb(cmd, I2C_REG(CMD_REG));
|
|
|
|
/* wait until transfer completes */
|
|
while (1) {
|
|
uint8_t reg = readb(I2C_REG(STATUS_ACTIVITY_REG));
|
|
printd("I2C Status: 0x%02x\n", rerg & 0xf);
|
|
if (reg & I2C_STATUS_IDLE)
|
|
break;
|
|
}
|
|
dputs("I2C transfer completed\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
void i2c_init(int speed, int slaveadd)
|
|
{
|
|
/* scl_out = clk_func_ref / 3,
|
|
clk_func_ref = master_clock_freq / (divisor_2 + 1)
|
|
master_clock_freq = ext_clock_freq / divisor_1 */
|
|
/* clk_func_ref = scl_out * 3,
|
|
divisor_2 = (master_clock_freq / clk_func_ref) - 1
|
|
divisor_1 = ext_clock_freq / master_clock_freq */
|
|
/* for a target freq of 200kHz:
|
|
ext_clock_freq = 13MHz
|
|
clk_func_ref = 3 * 300kHZ = 600kHz
|
|
divisor_1 = 1 => master_clock_freq = ext_clock_freq = 13MHz
|
|
divisor_2 = 21 => clk_func_ref = 13MHz / (21+2) = 590.91 kHz
|
|
scl_out = clk_func_ref / 3 = 509.91 kHz / 3 = 196.97kHz */
|
|
writeb(I2C_CMD_SOFT_RESET, I2C_REG(CMD_REG));
|
|
|
|
writeb(0x00, I2C_REG(CONF_CLK_REG));
|
|
writeb(21, I2C_REG(CONF_CLK_FUNC_REF));
|
|
|
|
writeb(I2C_CMD_EN_CLK, I2C_REG(CMD_REG));
|
|
}
|