/* Copyright 2015 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ /* I2C module for Chrome EC */ #include "clock.h" #include "common.h" #include "console.h" #include "gpio.h" #include "hooks.h" #include "i2c.h" #include "registers.h" #include "task.h" #include "timer.h" #include "util.h" #define CPRINTS(format, args...) cprints(CC_I2C, format, ## args) /* * The count number of the counter for 25 ms register. * The 25 ms register is calculated by (count number *1.024 kHz). */ #define I2C_CLK_LOW_TIMEOUT 255 /* ~=249 ms */ /* Default maximum time we allow for an I2C transfer */ #define I2C_TIMEOUT_DEFAULT_US (100 * MSEC) enum enhanced_i2c_transfer_direct { TX_DIRECT, RX_DIRECT, }; enum i2c_host_status { /* Host busy */ HOSTA_HOBY = 0x01, /* Finish Interrupt */ HOSTA_FINTR = 0x02, /* Device error */ HOSTA_DVER = 0x04, /* Bus error */ HOSTA_BSER = 0x08, /* Fail */ HOSTA_FAIL = 0x10, /* Not response ACK */ HOSTA_NACK = 0x20, /* Time-out error */ HOSTA_TMOE = 0x40, /* Byte done status */ HOSTA_BDS = 0x80, /* Error bit is set */ HOSTA_ANY_ERROR = (HOSTA_DVER | HOSTA_BSER | HOSTA_FAIL | HOSTA_NACK | HOSTA_TMOE), /* W/C for next byte */ HOSTA_NEXT_BYTE = HOSTA_BDS, /* W/C host status register */ HOSTA_ALL_WC_BIT = (HOSTA_FINTR | HOSTA_ANY_ERROR | HOSTA_BDS), }; enum enhanced_i2c_host_status { /* ACK receive */ E_HOSTA_ACK = 0x01, /* Interrupt pending */ E_HOSTA_INTP = 0x02, /* Read/Write */ E_HOSTA_RW = 0x04, /* Time out error */ E_HOSTA_TMOE = 0x08, /* Arbitration lost */ E_HOSTA_ARB = 0x10, /* Bus busy */ E_HOSTA_BB = 0x20, /* Address match */ E_HOSTA_AM = 0x40, /* Byte done status */ E_HOSTA_BDS = 0x80, /* time out or lost arbitration */ E_HOSTA_ANY_ERROR = (E_HOSTA_TMOE | E_HOSTA_ARB), /* Byte transfer done and ACK receive */ E_HOSTA_BDS_AND_ACK = (E_HOSTA_BDS | E_HOSTA_ACK), }; enum enhanced_i2c_ctl { /* Hardware reset */ E_HW_RST = 0x01, /* Stop */ E_STOP = 0x02, /* Start & Repeat start */ E_START = 0x04, /* Acknowledge */ E_ACK = 0x08, /* State reset */ E_STS_RST = 0x10, /* Mode select */ E_MODE_SEL = 0x20, /* I2C interrupt enable */ E_INT_EN = 0x40, /* 0 : Standard mode , 1 : Receive mode */ E_RX_MODE = 0x80, /* State reset and hardware reset */ E_STS_AND_HW_RST = (E_STS_RST | E_HW_RST), /* Generate start condition and transmit slave address */ E_START_ID = (E_INT_EN | E_MODE_SEL | E_ACK | E_START | E_HW_RST), /* Generate stop condition */ E_FINISH = (E_INT_EN | E_MODE_SEL | E_ACK | E_STOP | E_HW_RST), }; enum i2c_reset_cause { I2C_RC_NO_IDLE_FOR_START = 1, I2C_RC_TIMEOUT, }; struct i2c_ch_freq { int kpbs; uint8_t freq_set; }; static const struct i2c_ch_freq i2c_freq_select[] = { { 50, 1}, { 100, 2}, { 400, 3}, { 1000, 4}, }; struct i2c_pin { volatile uint8_t *pin_clk; volatile uint8_t *pin_data; volatile uint8_t *pin_clk_ctrl; volatile uint8_t *pin_data_ctrl; volatile uint8_t *mirror_clk; volatile uint8_t *mirror_data; uint8_t clk_mask; uint8_t data_mask; }; static const struct i2c_pin i2c_pin_regs[] = { { &IT83XX_GPIO_GPCRB3, &IT83XX_GPIO_GPCRB4, &IT83XX_GPIO_GPDRB, &IT83XX_GPIO_GPDRB, &IT83XX_GPIO_GPDMRB, &IT83XX_GPIO_GPDMRB, 0x08, 0x10}, { &IT83XX_GPIO_GPCRC1, &IT83XX_GPIO_GPCRC2, &IT83XX_GPIO_GPDRC, &IT83XX_GPIO_GPDRC, &IT83XX_GPIO_GPDMRC, &IT83XX_GPIO_GPDMRC, 0x02, 0x04}, #ifdef CONFIG_IT83XX_SMCLK2_ON_GPC7 { &IT83XX_GPIO_GPCRC7, &IT83XX_GPIO_GPCRF7, &IT83XX_GPIO_GPDRC, &IT83XX_GPIO_GPDRF, &IT83XX_GPIO_GPDMRC, &IT83XX_GPIO_GPDMRF, 0x80, 0x80}, #else { &IT83XX_GPIO_GPCRF6, &IT83XX_GPIO_GPCRF7, &IT83XX_GPIO_GPDRF, &IT83XX_GPIO_GPDRF, &IT83XX_GPIO_GPDMRF, &IT83XX_GPIO_GPDMRF, 0x40, 0x80}, #endif { &IT83XX_GPIO_GPCRH1, &IT83XX_GPIO_GPCRH2, &IT83XX_GPIO_GPDRH, &IT83XX_GPIO_GPDRH, &IT83XX_GPIO_GPDMRH, &IT83XX_GPIO_GPDMRH, 0x02, 0x04}, { &IT83XX_GPIO_GPCRE0, &IT83XX_GPIO_GPCRE7, &IT83XX_GPIO_GPDRE, &IT83XX_GPIO_GPDRE, &IT83XX_GPIO_GPDMRE, &IT83XX_GPIO_GPDMRE, 0x01, 0x80}, { &IT83XX_GPIO_GPCRA4, &IT83XX_GPIO_GPCRA5, &IT83XX_GPIO_GPDRA, &IT83XX_GPIO_GPDRA, &IT83XX_GPIO_GPDMRA, &IT83XX_GPIO_GPDMRA, 0x10, 0x20}, }; struct i2c_ctrl_t { uint8_t irq; enum clock_gate_offsets clock_gate; int reg_shift; }; const struct i2c_ctrl_t i2c_ctrl_regs[] = { {IT83XX_IRQ_SMB_A, CGC_OFFSET_SMBA, -1}, {IT83XX_IRQ_SMB_B, CGC_OFFSET_SMBB, -1}, {IT83XX_IRQ_SMB_C, CGC_OFFSET_SMBC, -1}, {IT83XX_IRQ_SMB_D, CGC_OFFSET_SMBD, 3}, {IT83XX_IRQ_SMB_E, CGC_OFFSET_SMBE, 0}, {IT83XX_IRQ_SMB_F, CGC_OFFSET_SMBF, 1}, }; enum i2c_ch_status { I2C_CH_NORMAL = 0, I2C_CH_REPEAT_START, I2C_CH_WAIT_READ, I2C_CH_WAIT_NEXT_XFER, }; /* I2C port state data */ struct i2c_port_data { const uint8_t *out; /* Output data pointer */ int out_size; /* Output data to transfer, in bytes */ uint8_t *in; /* Input data pointer */ int in_size; /* Input data to transfer, in bytes */ int flags; /* Flags (I2C_XFER_*) */ int widx; /* Index into output data */ int ridx; /* Index into input data */ int err; /* Error code, if any */ uint8_t addr__8b; /* address of device */ uint32_t timeout_us; /* Transaction timeout, or 0 to use default */ uint8_t freq; /* Frequency setting */ enum i2c_ch_status i2ccs; /* Task waiting on port, or TASK_ID_INVALID if none. */ volatile int task_waiting; }; static struct i2c_port_data pdata[I2C_PORT_COUNT]; static int i2c_ch_reg_shift(int p) { /* * only enhanced port needs to be changed the parameter of registers */ ASSERT(p >= I2C_STANDARD_PORT_COUNT && p < I2C_PORT_COUNT); /* * The registers of i2c enhanced ports are not sequential. * This routine transfers the i2c port number to related * parameter of registers. * * IT83xx chip : i2c enhanced ports - channel D,E,F * channel D registers : 0x3680 ~ 0x36FF * channel E registers : 0x3500 ~ 0x357F * channel F registers : 0x3580 ~ 0x35FF */ return i2c_ctrl_regs[p].reg_shift; } static void i2c_reset(int p, int cause) { int p_ch; if (p < I2C_STANDARD_PORT_COUNT) { /* bit1, kill current transaction. */ IT83XX_SMB_HOCTL(p) = 0x2; IT83XX_SMB_HOCTL(p) = 0; /* W/C host status register */ IT83XX_SMB_HOSTA(p) = HOSTA_ALL_WC_BIT; } else { /* Shift register */ p_ch = i2c_ch_reg_shift(p); /* State reset and hardware reset */ IT83XX_I2C_CTR(p_ch) = E_STS_AND_HW_RST; } CPRINTS("I2C ch%d reset cause %d", p, cause); } static void i2c_r_last_byte(int p) { struct i2c_port_data *pd = pdata + p; /* * bit5, The firmware shall write 1 to this bit * when the next byte will be the last byte for i2c read. */ if ((pd->flags & I2C_XFER_STOP) && (pd->ridx == pd->in_size - 1)) IT83XX_SMB_HOCTL(p) |= 0x20; } static void i2c_w2r_change_direction(int p) { /* I2C switch direction */ if (IT83XX_SMB_HOCTL2(p) & 0x08) { i2c_r_last_byte(p); IT83XX_SMB_HOSTA(p) = HOSTA_NEXT_BYTE; } else { /* * bit2, I2C switch direction wait. * bit3, I2C switch direction enable. */ IT83XX_SMB_HOCTL2(p) |= 0x0C; IT83XX_SMB_HOSTA(p) = HOSTA_NEXT_BYTE; i2c_r_last_byte(p); IT83XX_SMB_HOCTL2(p) &= ~0x04; } } static void i2c_pio_trans_data(int p, enum enhanced_i2c_transfer_direct direct, uint8_t data, int first_byte) { struct i2c_port_data *pd = pdata + p; int p_ch; int nack = 0; /* Shift register */ p_ch = i2c_ch_reg_shift(p); if (first_byte) { /* First byte must be slave address. */ IT83XX_I2C_DTR(p_ch) = data | (direct == RX_DIRECT ? (1 << 0) : 0); /* start or repeat start signal. */ IT83XX_I2C_CTR(p_ch) = E_START_ID; } else { if (direct == TX_DIRECT) /* Transmit data */ IT83XX_I2C_DTR(p_ch) = data; else { /* * Receive data. * Last byte should be NACK in the end of read cycle */ if (((pd->ridx + 1) == pd->in_size) && (pd->flags & I2C_XFER_STOP)) nack = 1; } /* Set hardware reset to start next transmission */ IT83XX_I2C_CTR(p_ch) = E_INT_EN | E_MODE_SEL | E_HW_RST | (nack ? 0 : E_ACK); } } static int i2c_tran_write(int p) { struct i2c_port_data *pd = pdata + p; if (pd->flags & I2C_XFER_START) { /* i2c enable */ IT83XX_SMB_HOCTL2(p) = 0x13; /* * bit0, Direction of the host transfer. * bit[1:7}, Address of the targeted slave. */ IT83XX_SMB_TRASLA(p) = pd->addr__8b; /* Send first byte */ IT83XX_SMB_HOBDB(p) = *(pd->out++); pd->widx++; /* clear start flag */ pd->flags &= ~I2C_XFER_START; /* * bit0, Host interrupt enable. * bit[2:4}, Extend command. * bit6, start. */ IT83XX_SMB_HOCTL(p) = 0x5D; } else { /* Host has completed the transmission of a byte */ if (IT83XX_SMB_HOSTA(p) & HOSTA_BDS) { if (pd->widx < pd->out_size) { /* Send next byte */ IT83XX_SMB_HOBDB(p) = *(pd->out++); pd->widx++; /* W/C byte done for next byte */ IT83XX_SMB_HOSTA(p) = HOSTA_NEXT_BYTE; if (pd->i2ccs == I2C_CH_REPEAT_START) { pd->i2ccs = I2C_CH_NORMAL; task_enable_irq(i2c_ctrl_regs[p].irq); } } else { /* done */ pd->out_size = 0; if (pd->in_size > 0) { /* write to read */ i2c_w2r_change_direction(p); } else { if (pd->flags & I2C_XFER_STOP) { /* set I2C_EN = 0 */ IT83XX_SMB_HOCTL2(p) = 0x11; /* W/C byte done for finish */ IT83XX_SMB_HOSTA(p) = HOSTA_NEXT_BYTE; } else { pd->i2ccs = I2C_CH_REPEAT_START; return 0; } } } } } return 1; } static int i2c_tran_read(int p) { struct i2c_port_data *pd = pdata + p; if (pd->flags & I2C_XFER_START) { /* i2c enable */ IT83XX_SMB_HOCTL2(p) = 0x13; /* * bit0, Direction of the host transfer. * bit[1:7}, Address of the targeted slave. */ IT83XX_SMB_TRASLA(p) = pd->addr__8b | 0x01; /* clear start flag */ pd->flags &= ~I2C_XFER_START; /* * bit0, Host interrupt enable. * bit[2:4}, Extend command. * bit5, The firmware shall write 1 to this bit * when the next byte will be the last byte. * bit6, start. */ if ((1 == pd->in_size) && (pd->flags & I2C_XFER_STOP)) IT83XX_SMB_HOCTL(p) = 0x7D; else IT83XX_SMB_HOCTL(p) = 0x5D; } else { if ((pd->i2ccs == I2C_CH_REPEAT_START) || (pd->i2ccs == I2C_CH_WAIT_READ)) { if (pd->i2ccs == I2C_CH_REPEAT_START) { /* write to read */ i2c_w2r_change_direction(p); } else { /* For last byte */ i2c_r_last_byte(p); /* W/C for next byte */ IT83XX_SMB_HOSTA(p) = HOSTA_NEXT_BYTE; } pd->i2ccs = I2C_CH_NORMAL; task_enable_irq(i2c_ctrl_regs[p].irq); } else if (IT83XX_SMB_HOSTA(p) & HOSTA_BDS) { if (pd->ridx < pd->in_size) { /* To get received data. */ *(pd->in++) = IT83XX_SMB_HOBDB(p); pd->ridx++; /* For last byte */ i2c_r_last_byte(p); /* done */ if (pd->ridx == pd->in_size) { pd->in_size = 0; if (pd->flags & I2C_XFER_STOP) { /* W/C for finish */ IT83XX_SMB_HOSTA(p) = HOSTA_NEXT_BYTE; } else { pd->i2ccs = I2C_CH_WAIT_READ; return 0; } } else { /* W/C for next byte */ IT83XX_SMB_HOSTA(p) = HOSTA_NEXT_BYTE; } } } } return 1; } static void enhanced_i2c_start(int p) { /* Shift register */ int p_ch = i2c_ch_reg_shift(p); /* State reset and hardware reset */ IT83XX_I2C_CTR(p_ch) = E_STS_AND_HW_RST; /* Set i2c frequency */ IT83XX_I2C_PSR(p_ch) = pdata[p].freq; IT83XX_I2C_HSPR(p_ch) = pdata[p].freq; /* * Set time out register. * I2C D/E/F clock/data low timeout. */ IT83XX_I2C_TOR(p_ch) = I2C_CLK_LOW_TIMEOUT; /* bit1: Enable enhanced i2c module */ IT83XX_I2C_CTR1(p_ch) = (1 << 1); } static int enhanced_i2c_tran_write(int p) { struct i2c_port_data *pd = pdata + p; uint8_t out_data; int p_ch; /* Shift register */ p_ch = i2c_ch_reg_shift(p); if (pd->flags & I2C_XFER_START) { /* Clear start bit */ pd->flags &= ~I2C_XFER_START; enhanced_i2c_start(p); /* Send ID */ i2c_pio_trans_data(p, TX_DIRECT, pd->addr__8b, 1); } else { /* Host has completed the transmission of a byte */ if (pd->widx < pd->out_size) { out_data = *(pd->out++); pd->widx++; /* Send Byte */ i2c_pio_trans_data(p, TX_DIRECT, out_data, 0); if (pd->i2ccs == I2C_CH_WAIT_NEXT_XFER) { pd->i2ccs = I2C_CH_NORMAL; task_enable_irq(i2c_ctrl_regs[p].irq); } } else { /* done */ pd->out_size = 0; if (pd->in_size > 0) { /* Write to read protocol */ pd->i2ccs = I2C_CH_REPEAT_START; /* Repeat Start */ i2c_pio_trans_data(p, RX_DIRECT, pd->addr__8b, 1); } else { if (pd->flags & I2C_XFER_STOP) { IT83XX_I2C_CTR(p_ch) = E_FINISH; /* wait for stop bit interrupt*/ return 1; } /* Direct write with direct read */ pd->i2ccs = I2C_CH_WAIT_NEXT_XFER; return 0; } } } return 1; } static int enhanced_i2c_tran_read(int p) { struct i2c_port_data *pd = pdata + p; uint8_t in_data = 0; int p_ch; /* Shift register */ p_ch = i2c_ch_reg_shift(p); if (pd->flags & I2C_XFER_START) { /* clear start flag */ pd->flags &= ~I2C_XFER_START; enhanced_i2c_start(p); /* Direct read */ pd->i2ccs = I2C_CH_WAIT_READ; /* Send ID */ i2c_pio_trans_data(p, RX_DIRECT, pd->addr__8b, 1); } else { if (pd->i2ccs) { if (pd->i2ccs == I2C_CH_REPEAT_START) { pd->i2ccs = I2C_CH_NORMAL; /* Receive data */ i2c_pio_trans_data(p, RX_DIRECT, in_data, 0); } else if (pd->i2ccs == I2C_CH_WAIT_READ) { pd->i2ccs = I2C_CH_NORMAL; /* Receive data */ i2c_pio_trans_data(p, RX_DIRECT, in_data, 0); /* Turn on irq before next direct read */ task_enable_irq(i2c_ctrl_regs[p].irq); } else { /* Write to read */ pd->i2ccs = I2C_CH_WAIT_READ; /* Send ID */ i2c_pio_trans_data(p, RX_DIRECT, pd->addr__8b, 1); task_enable_irq(i2c_ctrl_regs[p].irq); } } else { if (pd->ridx < pd->in_size) { /* read data */ *(pd->in++) = IT83XX_I2C_DRR(p_ch); pd->ridx++; /* done */ if (pd->ridx == pd->in_size) { pd->in_size = 0; if (pd->flags & I2C_XFER_STOP) { pd->i2ccs = I2C_CH_NORMAL; IT83XX_I2C_CTR(p_ch) = E_FINISH; /* wait for stop bit interrupt*/ return 1; } /* End the transaction */ pd->i2ccs = I2C_CH_WAIT_READ; return 0; } /* read next byte */ i2c_pio_trans_data(p, RX_DIRECT, in_data, 0); } } } return 1; } static int enhanced_i2c_error(int p) { struct i2c_port_data *pd = pdata + p; /* Shift register */ int p_ch = i2c_ch_reg_shift(p); int i2c_str = IT83XX_I2C_STR(p_ch); if (i2c_str & E_HOSTA_ANY_ERROR) { pd->err = i2c_str & E_HOSTA_ANY_ERROR; /* device does not respond ACK */ } else if ((i2c_str & E_HOSTA_BDS_AND_ACK) == E_HOSTA_BDS) { if (IT83XX_I2C_CTR(p_ch) & E_ACK) pd->err = E_HOSTA_ACK; } return pd->err; } static int i2c_transaction(int p) { struct i2c_port_data *pd = pdata + p; int p_ch; if (p < I2C_STANDARD_PORT_COUNT) { /* any error */ if (IT83XX_SMB_HOSTA(p) & HOSTA_ANY_ERROR) { pd->err = (IT83XX_SMB_HOSTA(p) & HOSTA_ANY_ERROR); } else { /* i2c write */ if (pd->out_size) return i2c_tran_write(p); /* i2c read */ else if (pd->in_size) return i2c_tran_read(p); /* wait finish */ if (!(IT83XX_SMB_HOSTA(p) & HOSTA_FINTR)) return 1; } /* W/C */ IT83XX_SMB_HOSTA(p) = HOSTA_ALL_WC_BIT; /* disable the SMBus host interface */ IT83XX_SMB_HOCTL2(p) = 0x00; } else { /* no error */ if (!(enhanced_i2c_error(p))) { /* i2c write */ if (pd->out_size) return enhanced_i2c_tran_write(p); /* i2c read */ else if (pd->in_size) return enhanced_i2c_tran_read(p); } p_ch = i2c_ch_reg_shift(p); IT83XX_I2C_CTR(p_ch) = E_STS_AND_HW_RST; IT83XX_I2C_CTR1(p_ch) = 0; } /* done doing work */ return 0; } int i2c_is_busy(int port) { int p_ch; if (port < I2C_STANDARD_PORT_COUNT) return (IT83XX_SMB_HOSTA(port) & (HOSTA_HOBY | HOSTA_ALL_WC_BIT)); p_ch = i2c_ch_reg_shift(port); return (IT83XX_I2C_STR(p_ch) & E_HOSTA_BB); } int chip_i2c_xfer__7bf(int port, uint16_t slave_addr__7bf, const uint8_t *out, int out_size, uint8_t *in, int in_size, int flags) { struct i2c_port_data *pd = pdata + port; uint32_t events = 0; if (out_size == 0 && in_size == 0) return EC_SUCCESS; if (pd->i2ccs) { if ((flags & I2C_XFER_SINGLE) == I2C_XFER_SINGLE) flags &= ~I2C_XFER_START; } /* Copy data to port struct */ pd->out = out; pd->out_size = out_size; pd->in = in; pd->in_size = in_size; pd->flags = flags; pd->widx = 0; pd->ridx = 0; pd->err = 0; pd->addr__8b = I2C_GET_ADDR__7b(slave_addr__7bf) << 1; /* Make sure we're in a good state to start */ if ((flags & I2C_XFER_START) && (i2c_is_busy(port) || (i2c_get_line_levels(port) != I2C_LINE_IDLE))) { /* Attempt to unwedge the port. */ i2c_unwedge(port); /* reset i2c port */ i2c_reset(port, I2C_RC_NO_IDLE_FOR_START); } pd->task_waiting = task_get_current(); if (pd->flags & I2C_XFER_START) { pd->i2ccs = I2C_CH_NORMAL; /* enable i2c interrupt */ task_clear_pending_irq(i2c_ctrl_regs[port].irq); task_enable_irq(i2c_ctrl_regs[port].irq); } /* Start transaction */ i2c_transaction(port); /* Wait for transfer complete or timeout */ events = task_wait_event_mask(TASK_EVENT_I2C_IDLE, pd->timeout_us); /* disable i2c interrupt */ task_disable_irq(i2c_ctrl_regs[port].irq); pd->task_waiting = TASK_ID_INVALID; /* Handle timeout */ if (!(events & TASK_EVENT_I2C_IDLE)) { pd->err = EC_ERROR_TIMEOUT; /* reset i2c port */ i2c_reset(port, I2C_RC_TIMEOUT); } /* reset i2c channel status */ if (pd->err) pd->i2ccs = I2C_CH_NORMAL; return pd->err; } int i2c_raw_get_scl(int port) { enum gpio_signal g; if (get_scl_from_i2c_port(port, &g) == EC_SUCCESS) return !!(*i2c_pin_regs[port].mirror_clk & i2c_pin_regs[port].clk_mask); /* If no SCL pin defined for this port, then return 1 to appear idle */ return 1; } int i2c_raw_get_sda(int port) { enum gpio_signal g; if (get_sda_from_i2c_port(port, &g) == EC_SUCCESS) return !!(*i2c_pin_regs[port].mirror_data & i2c_pin_regs[port].data_mask); /* If no SDA pin defined for this port, then return 1 to appear idle */ return 1; } int i2c_get_line_levels(int port) { int pin_sts = 0; if (port < I2C_STANDARD_PORT_COUNT) return IT83XX_SMB_SMBPCTL(port) & 0x03; if (*i2c_pin_regs[port].mirror_clk & i2c_pin_regs[port].clk_mask) pin_sts |= I2C_LINE_SCL_HIGH; if (*i2c_pin_regs[port].mirror_data & i2c_pin_regs[port].data_mask) pin_sts |= I2C_LINE_SDA_HIGH; return pin_sts; } void i2c_set_timeout(int port, uint32_t timeout) { pdata[port].timeout_us = timeout ? timeout : I2C_TIMEOUT_DEFAULT_US; } void i2c_interrupt(int port) { int id = pdata[port].task_waiting; /* Clear the interrupt status */ task_clear_pending_irq(i2c_ctrl_regs[port].irq); /* If no task is waiting, just return */ if (id == TASK_ID_INVALID) return; /* If done doing work, wake up the task waiting for the transfer */ if (!i2c_transaction(port)) { task_disable_irq(i2c_ctrl_regs[port].irq); task_set_event(id, TASK_EVENT_I2C_IDLE, 0); } } static void i2c_freq_changed(void) { int i, f, clk_div, psr, freq; int p_ch; /* * Standard I2C Channels */ for (i = 0; i < i2c_ports_used; i++) { freq = i2c_ports[i].kbps; if (i2c_ports[i].port < I2C_STANDARD_PORT_COUNT) { for (f = ARRAY_SIZE(i2c_freq_select) - 1; f >= 0; f--) { if (freq >= i2c_freq_select[f].kpbs) { IT83XX_SMB_SCLKTS(i2c_ports[i].port) = i2c_freq_select[f].freq_set; break; } } } else { p_ch = i2c_ch_reg_shift(i2c_ports[i].port); /* * Let psr(Prescale) = IT83XX_I2C_PSR(p_ch) * Then, 1 SCL cycle = 2 x (psr + 2) x SMBus clock cycle * SMBus clock = PLL_CLOCK / clk_div * SMBus clock cycle = 1 / SMBus clock * 1 SCL cycle = 1 / (1000 x freq) * 1 / (1000 x freq) = * 2 x (psr + 2) x (1 / (PLL_CLOCK / clk_div)) * psr = ((PLL_CLOCK / clk_div) x * (1 / (1000 x freq)) x (1 / 2)) - 2 */ if (freq) { /* Get SMBus clock divide value */ clk_div = (IT83XX_ECPM_SCDCR2 & 0x0F) + 1; /* Calculate PSR value */ psr = (PLL_CLOCK / (clk_div * (2 * 1000 * freq))) - 2; /* Set psr value under 0xFD */ if (psr > 0xFD) psr = 0xFD; /* Set I2C Speed */ IT83XX_I2C_PSR(p_ch) = (psr & 0xFF); IT83XX_I2C_HSPR(p_ch) = (psr & 0xFF); /* Backup */ pdata[i2c_ports[i].port].freq = (psr & 0xFF); } } } /* This field defines the SMCLK0/1/2 clock/data low timeout. */ IT83XX_SMB_25MS = I2C_CLK_LOW_TIMEOUT; } DECLARE_HOOK(HOOK_FREQ_CHANGE, i2c_freq_changed, HOOK_PRIO_DEFAULT); static void i2c_init(void) { int i, p, p_ch; /* Configure GPIOs */ gpio_config_module(MODULE_I2C, 1); #ifdef CONFIG_IT83XX_SMCLK2_ON_GPC7 /* bit7, 0: SMCLK2 is located on GPF6, 1: SMCLK2 is located on GPC7 */ IT83XX_GPIO_GRC7 |= 0x80; #endif /* Enable I2C function. */ for (i = 0; i < i2c_ports_used; i++) { /* I2c port mapping. */ p = i2c_ports[i].port; clock_enable_peripheral(i2c_ctrl_regs[p].clock_gate, 0, 0); if (p < I2C_STANDARD_PORT_COUNT) { /* * bit0, The SMBus host interface is enabled. * bit1, Enable to communicate with I2C device * and support I2C-compatible cycles. * bit4, This bit controls the reset mechanism * of SMBus master to handle the SMDAT * line low if 25ms reg timeout. */ IT83XX_SMB_HOCTL2(p) = 0x11; /* * bit1, Kill SMBus host transaction. * bit0, Enable the interrupt for the master interface. */ IT83XX_SMB_HOCTL(p) = 0x03; IT83XX_SMB_HOCTL(p) = 0x01; /* W/C host status register */ IT83XX_SMB_HOSTA(p) = HOSTA_ALL_WC_BIT; IT83XX_SMB_HOCTL2(p) = 0x00; } else { /* Shift register */ p_ch = i2c_ch_reg_shift(p); switch (p) { case IT83XX_I2C_CH_D: #ifndef CONFIG_UART_HOST /* Enable SMBus D channel */ IT83XX_GPIO_GRC2 |= 0x20; #endif break; case IT83XX_I2C_CH_E: /* Enable SMBus E channel */ IT83XX_GCTRL_PMER1 |= 0x01; break; case IT83XX_I2C_CH_F: /* Enable SMBus F channel */ IT83XX_GCTRL_PMER1 |= 0x02; break; } /* Software reset */ IT83XX_I2C_DHTR(p_ch) |= 0x80; IT83XX_I2C_DHTR(p_ch) &= 0x7F; /* State reset and hardware reset */ IT83XX_I2C_CTR(p_ch) = E_STS_AND_HW_RST; /* bit1, Module enable */ IT83XX_I2C_CTR1(p_ch) = 0; } pdata[i].task_waiting = TASK_ID_INVALID; } i2c_freq_changed(); for (i = 0; i < I2C_PORT_COUNT; i++) { /* Use default timeout */ i2c_set_timeout(i, 0); } } DECLARE_HOOK(HOOK_INIT, i2c_init, HOOK_PRIO_INIT_I2C);