chrome-ec/chip/mchp/i2c.c

965 lines
27 KiB
C

/* Copyright 2017 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 port module for MCHP MEC
* TODO handle chip variants
*/
#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"
#include "tfdp_chip.h"
#define CPUTS(outstr) cputs(CC_I2C, outstr)
#define CPRINTF(format, args...) cprintf(CC_I2C, format, ## args)
#define CPRINTS(format, args...) cprints(CC_I2C, format, ## args)
/*
* MCHP I2C BAUD clock source is 16 MHz.
*/
#define I2C_CLOCK 16000000 /* 16 MHz */
/* SMBus Timing values for 1MHz Speed */
#define SPEED_1MHZ_BUS_CLOCK 0x0509
#define SPEED_1MHZ_DATA_TIMING 0x06060601
#define SPEED_1MHZ_DATA_TIMING_2 0x06
#define SPEED_1MHZ_IDLE_SCALING 0x01000050
#define SPEED_1MHZ_TIMEOUT_SCALING 0x149CC2C7
/* SMBus Timing values for 400kHz speed */
#define SPEED_400KHZ_BUS_CLOCK 0x0F17
#define SPEED_400KHZ_DATA_TIMING 0x040A0F01
#define SPEED_400KHZ_DATA_TIMING_2 0x0A
#define SPEED_400KHZ_IDLE_SCALING 0x01000050
#define SPEED_400KHZ_TIMEOUT_SCALING 0x149CC2C7
/* SMBus Timing values for 100kHz speed */
#define SPEED_100KHZ_BUS_CLOCK 0x4F4Ful
#define SPEED_100KHZ_DATA_TIMING 0x0C4D4306ul
#define SPEED_100KHZ_DATA_TIMING_2 0x4Dul
#define SPEED_100KHZ_IDLE_SCALING 0x01FC01EDul
#define SPEED_100KHZ_TIMEOUT_SCALING 0x4B9CC2C7ul
/* Status */
#define STS_NBB BIT(0) /* Bus busy */
#define STS_LAB BIT(1) /* Arbitration lost */
#define STS_LRB BIT(3) /* Last received bit */
#define STS_BER BIT(4) /* Bus error */
#define STS_PIN BIT(7) /* Pending interrupt */
/* Control */
#define CTRL_ACK BIT(0) /* Acknowledge */
#define CTRL_STO BIT(1) /* STOP */
#define CTRL_STA BIT(2) /* START */
#define CTRL_ENI BIT(3) /* Enable interrupt */
#define CTRL_ESO BIT(6) /* Enable serial output */
#define CTRL_PIN BIT(7) /* Pending interrupt not */
/* Completion */
#define COMP_DTEN BIT(2) /* enable device timeouts */
#define COMP_MCEN BIT(3) /* enable master cumulative timeouts */
#define COMP_SCEN BIT(4) /* enable slave cumulative timeouts */
#define COMP_BIDEN BIT(5) /* enable Bus idle timeouts */
#define COMP_IDLE BIT(29) /* i2c bus is idle */
#define COMP_RW_BITS_MASK 0x3C /* R/W bits mask */
/* Configuration */
#define CFG_PORT_MASK (0x0F) /* port selection field */
#define CFG_TCEN BIT(4) /* Enable HW bus timeouts */
#define CFG_FEN BIT(8) /* enable input filtering */
#define CFG_RESET BIT(9) /* reset controller */
#define CFG_ENABLE BIT(10) /* enable controller */
#define CFG_GC_DIS BIT(14) /* disable general call address */
#define CFG_ENIDI BIT(29) /* Enable I2C idle interrupt */
/* Enable network layer master done interrupt */
#define CFG_ENMI BIT(30)
/* Enable network layer slave done interrupt */
#define CFG_ENSI BIT(31)
/* Master Command */
#define MCMD_MRUN BIT(0)
#define MCMD_MPROCEED BIT(1)
#define MCMD_START0 BIT(8)
#define MCMD_STARTN BIT(9)
#define MCMD_STOP BIT(10)
#define MCMD_READM BIT(12)
#define MCMD_WCNT_BITPOS (16)
#define MCMD_WCNT_MASK0 (0xFF)
#define MCMD_WCNT_MASK (0xFF << 16)
#define MCMD_RCNT_BITPOS (24)
#define MCMD_RCNT_MASK0 (0xFF)
#define MCMD_RCNT_MASK (0xFF << 24)
/* Maximum transfer of a SMBUS block transfer */
#define SMBUS_MAX_BLOCK_SIZE 32
/*
* Amount of time to blocking wait for i2c bus to finish. After this
* blocking timeout, if the bus is still not finished, then allow other
* tasks to run.
* Note: this is just long enough for a 400kHz bus to finish transmitting
* one byte assuming the bus isn't being held.
*/
#define I2C_WAIT_BLOCKING_TIMEOUT_US 25
enum i2c_transaction_state {
/* Stop condition was sent in previous transaction */
I2C_TRANSACTION_STOPPED,
/* Stop condition was not sent in previous transaction */
I2C_TRANSACTION_OPEN,
};
/* I2C controller state data
* NOTE: I2C_CONTROLLER_COUNT is defined at board level.
*/
static struct {
/* Transaction timeout, or 0 to use default. */
uint32_t timeout_us;
/* Task waiting on port, or TASK_ID_INVALID if none. */
/*
* MCHP Remove volatile.
* ISR only reads.
* Non-ISR only writes when interrupt is disabled.
*/
task_id_t task_waiting;
enum i2c_transaction_state transaction_state;
/* transaction context */
int out_size;
const uint8_t *outp;
int in_size;
uint8_t *inp;
int xflags;
uint32_t i2c_complete; /* ISR write */
uint32_t flags;
uint8_t port;
uint8_t slv_addr_8bit;
uint8_t ctrl;
uint8_t hwsts;
uint8_t hwsts2;
uint8_t hwsts3; /* ISR write */
uint8_t hwsts4;
uint8_t lines;
} cdata[I2C_CONTROLLER_COUNT];
static const uint16_t i2c_controller_pcr[MCHP_I2C_CTRL_MAX] = {
MCHP_PCR_I2C0,
MCHP_PCR_I2C1,
MCHP_PCR_I2C2,
MCHP_PCR_I2C3
};
static void i2c_ctrl_slp_en(int controller, int sleep_en)
{
if ((controller < 0) || (controller > MCHP_I2C_CTRL_MAX))
return;
if (sleep_en)
MCHP_PCR_SLP_EN_DEV(i2c_controller_pcr[controller]);
else
MCHP_PCR_SLP_DIS_DEV(i2c_controller_pcr[controller]);
}
static int chip_i2c_is_controller_valid(int controller)
{
if ((controller < 0) || (controller >= MCHP_I2C_CTRL_MAX))
return 0;
return 1;
}
uint32_t chip_i2c_get_ctx_flags(int port)
{
int controller = i2c_port_to_controller(port);
if (!chip_i2c_is_controller_valid(controller))
return 0;
return cdata[controller].flags;
}
/*
* MCHP I2C controller tuned bus clock values.
* MCHP I2C_SMB_Controller_3.6.pdf Table 6-3
*/
struct i2c_bus_clk {
int freq_khz;
int bus_clk;
};
const struct i2c_bus_clk i2c_freq_tbl[] = {
{ 40, 0xC7C7 },
{ 80, 0x6363 },
{ 100, 0x4F4F },
{ 333, 0x0F1F },
{ 400, 0x0F17 },
{ 1000, 0x0509 },
};
static int get_closest(int lesser, int greater, int target)
{
if (target - i2c_freq_tbl[lesser].freq_khz >=
i2c_freq_tbl[greater].freq_khz - target)
return greater;
else
return lesser;
}
/*
* Return index in i2c_freq_tbl of supported frequeny
* closest to requested frequency.
*/
static const struct i2c_bus_clk *get_supported_speed_idx(int req_kbps)
{
int i, limit, m, imax;
ASSERT(ARRAY_SIZE(i2c_freq_tbl) != 0);
if (req_kbps <= i2c_freq_tbl[0].freq_khz)
return &i2c_freq_tbl[0];
imax = ARRAY_SIZE(i2c_freq_tbl);
if (req_kbps >= i2c_freq_tbl[imax-1].freq_khz)
return &i2c_freq_tbl[imax-1];
/* we only get here if ARRAY_SIZE(...) > 1
* and req_kbps is in range.
*/
i = 0;
limit = imax;
while (i < limit) {
m = (i + limit) / 2;
if (i2c_freq_tbl[m].freq_khz == req_kbps)
break;
if (req_kbps < i2c_freq_tbl[m].freq_khz) {
if (m > 0 && req_kbps > i2c_freq_tbl[m-1].freq_khz) {
m = get_closest(m-1, m, req_kbps);
break;
}
limit = m;
} else {
if (m < imax-1 &&
req_kbps < i2c_freq_tbl[m+1].freq_khz) {
m = get_closest(m, m+1, req_kbps);
break;
}
i = m + 1;
}
}
return &i2c_freq_tbl[m];
}
/*
* Refer to NXP UM10204 for minimum timing requirement of T_Low and T_High.
* http://www.nxp.com/documents/user_manual/UM10204.pdf
* I2C spec. timing value are used in recommended registers values
* in MCHP I2C_SMB_Controller_3.6.pdf
* Restrict frequencies to those in the above MCHP spec.
* 40, 80, 100, 333, 400, and 1000 kHz.
*/
static void configure_controller_speed(int controller, int kbps)
{
const struct i2c_bus_clk *p;
p = get_supported_speed_idx(kbps);
MCHP_I2C_BUS_CLK(controller) = p->bus_clk;
if (p->freq_khz > 400) { /* Fast mode plus */
MCHP_I2C_DATA_TIM(controller) = SPEED_1MHZ_DATA_TIMING;
MCHP_I2C_DATA_TIM_2(controller) = SPEED_1MHZ_DATA_TIMING_2;
MCHP_I2C_IDLE_SCALE(controller) = SPEED_1MHZ_IDLE_SCALING;
MCHP_I2C_TOUT_SCALE(controller) = SPEED_1MHZ_TIMEOUT_SCALING;
} else if (p->freq_khz > 100) { /* Fast mode */
MCHP_I2C_DATA_TIM(controller) = SPEED_400KHZ_DATA_TIMING;
MCHP_I2C_DATA_TIM_2(controller) = SPEED_400KHZ_DATA_TIMING_2;
MCHP_I2C_IDLE_SCALE(controller) = SPEED_400KHZ_IDLE_SCALING;
MCHP_I2C_TOUT_SCALE(controller) = SPEED_400KHZ_TIMEOUT_SCALING;
} else { /* Standard mode */
MCHP_I2C_DATA_TIM(controller) = SPEED_100KHZ_DATA_TIMING;
MCHP_I2C_DATA_TIM_2(controller) = SPEED_100KHZ_DATA_TIMING_2;
MCHP_I2C_IDLE_SCALE(controller) = SPEED_100KHZ_IDLE_SCALING;
MCHP_I2C_TOUT_SCALE(controller) = SPEED_100KHZ_TIMEOUT_SCALING;
}
}
/*
* NOTE: direct mode interrupts do not need GIRQn bit
* set in aggregator block enable register.
*/
static void enable_controller_irq(int controller)
{
MCHP_INT_ENABLE(MCHP_I2C_GIRQ) =
MCHP_I2C_GIRQ_BIT(controller);
task_enable_irq(MCHP_IRQ_I2C_0 + controller);
}
static void disable_controller_irq(int controller)
{
MCHP_INT_DISABLE(MCHP_I2C_GIRQ) =
MCHP_I2C_GIRQ_BIT(controller);
/* read back into read-only reg. to insure disable takes effect */
MCHP_INT_BLK_IRQ = MCHP_INT_DISABLE(MCHP_I2C_GIRQ);
task_disable_irq(MCHP_IRQ_I2C_0 + controller);
task_clear_pending_irq(MCHP_IRQ_I2C_0 + controller);
}
/*
* Do NOT enable controller's IDLE interrupt in the configuration
* register. IDLE is meant for mult-master and controller as slave.
*/
static void configure_controller(int controller, int port, int kbps)
{
if (!chip_i2c_is_controller_valid(controller))
return;
disable_controller_irq(controller);
MCHP_INT_SOURCE(MCHP_I2C_GIRQ) =
MCHP_I2C_GIRQ_BIT(controller);
/* set to default except for port select field b[3:0] */
MCHP_I2C_CONFIG(controller) = (uint32_t)(port & 0xf);
MCHP_I2C_CTRL(controller) = CTRL_PIN;
/* Set both controller slave addresses to 0 the
* general call address. We disable general call
* below.
*/
MCHP_I2C_OWN_ADDR(controller) = 0;
configure_controller_speed(controller, kbps);
/* Controller timings done, clear RO status, enable
* output, and ACK generation.
*/
MCHP_I2C_CTRL(controller) = CTRL_PIN | CTRL_ESO | CTRL_ACK;
/* filter enable, disable General Call */
MCHP_I2C_CONFIG(controller) |= CFG_FEN + CFG_GC_DIS;
/* enable controller */
MCHP_I2C_CONFIG(controller) |= CFG_ENABLE;
}
static void reset_controller(int controller)
{
int i;
/* Reset asserted for at least one AHB clock */
MCHP_I2C_CONFIG(controller) |= BIT(9);
MCHP_EC_ID_RO = 0;
MCHP_I2C_CONFIG(controller) &= ~BIT(9);
for (i = 0; i < i2c_ports_used; ++i)
if (controller == i2c_port_to_controller(i2c_ports[i].port)) {
configure_controller(controller, i2c_ports[i].port,
i2c_ports[i].kbps);
cdata[controller].transaction_state =
I2C_TRANSACTION_STOPPED;
break;
}
}
/*
* !!! WARNING !!!
* We have observed task_wait_event_mask() returning 0 if the I2C
* controller IDLE interrupt is enabled. We believe it is due to the ISR
* post multiple events too quickly but don't have absolute proof.
*/
static int wait_for_interrupt(int controller, int timeout)
{
int event;
if (timeout <= 0)
return EC_ERROR_TIMEOUT;
cdata[controller].task_waiting = task_get_current();
enable_controller_irq(controller);
/* Wait until I2C interrupt or timeout. */
event = task_wait_event_mask(TASK_EVENT_I2C_IDLE, timeout);
disable_controller_irq(controller);
cdata[controller].task_waiting = TASK_ID_INVALID;
return (event & TASK_EVENT_TIMER) ? EC_ERROR_TIMEOUT : EC_SUCCESS;
}
static int wait_idle(int controller)
{
uint8_t sts = MCHP_I2C_STATUS(controller);
uint64_t block_timeout = get_time().val + I2C_WAIT_BLOCKING_TIMEOUT_US;
uint64_t task_timeout = block_timeout + cdata[controller].timeout_us;
int rv = 0;
while (!(sts & STS_NBB)) {
if (rv)
return rv;
if (get_time().val > block_timeout)
rv = wait_for_interrupt(controller,
task_timeout - get_time().val);
sts = MCHP_I2C_STATUS(controller);
}
if (sts & (STS_BER | STS_LAB))
return EC_ERROR_UNKNOWN;
return EC_SUCCESS;
}
/*
* Return EC_SUCCESS on ACK of byte else EC_ERROR_UNKNOWN.
* Record I2C.Status in cdata[controller] structure.
* Byte transmit finished with no I2C bus error or lost arbitration.
* PIN -> 0. LRB bit contains slave ACK/NACK bit.
* Slave ACK: I2C.Status == 0x00
* Slave NACK: I2C.Status == 0x08
* Byte transmit finished with I2C bus errors or lost arbitration.
* PIN -> 0 and BER and/or LAB set.
*
* Byte receive finished with no I2C bus errors or lost arbitration.
* PIN -> 0. LRB=0/1 based on ACK bit in I2C.Control.
* Master receiver must NACK last byte it wants to receive.
* How do we handle this if we don't know direction of transfer?
* I2C.Control is write-only so we can't see Master's ACK control bit.
*
*/
static int wait_byte_done(int controller, uint8_t mask, uint8_t expected)
{
uint64_t block_timeout;
uint64_t task_timeout;
int rv;
uint8_t sts;
rv = 0;
block_timeout = get_time().val + I2C_WAIT_BLOCKING_TIMEOUT_US;
task_timeout = block_timeout + cdata[controller].timeout_us;
sts = MCHP_I2C_STATUS(controller);
cdata[controller].hwsts = sts;
while (sts & STS_PIN) {
if (rv)
return rv;
if (get_time().val > block_timeout) {
rv = wait_for_interrupt(controller,
task_timeout - get_time().val);
}
sts = MCHP_I2C_STATUS(controller);
cdata[controller].hwsts = sts;
}
rv = EC_SUCCESS;
if ((sts & mask) != expected)
rv = EC_ERROR_UNKNOWN;
return rv;
}
/*
* Select port on controller. If controller configured
* for port do nothing.
* Switch port by reset and reconfigure to handle cases where
* the slave on current port is driving line(s) low.
* NOTE: I2C hardware reset only requires one AHB clock, back to back
* writes is OK but we added a dummy write as insurance.
*/
static void select_port(int port, int controller)
{
uint32_t port_sel;
port_sel = (uint32_t)(port & 0x0f);
if ((MCHP_I2C_CONFIG(controller) & 0x0f) == port_sel)
return;
MCHP_I2C_CONFIG(controller) |= BIT(9);
MCHP_EC_ID_RO = 0; /* dummy write to read-only as delay */
MCHP_I2C_CONFIG(controller) &= ~BIT(9);
configure_controller(controller, port_sel, i2c_ports[port].kbps);
}
/*
* Use safe method (reading GPIO.Control PAD input bit)
* to obtain SCL line state in bit[0] and SDA line state in bit[1].
* NOTE: I2C controller bit-bang register is not safe. Using
* bit-bang requires timeouts be disabled and the controller in an
* idle state. Switching controller to bit-bang mode when the controller
* is not idle will cause problems.
*/
static uint32_t get_line_level(int port)
{
uint32_t lines;
lines = i2c_raw_get_scl(port) & 0x01;
lines |= (i2c_raw_get_sda(port) & 0x01) << 1;
return lines;
}
/*
* Check if I2C port connected to controller has bus error or
* other signalling issues such as stuck clock/data lines.
*/
static int i2c_check_recover(int port, int controller)
{
uint32_t lines;
uint8_t reg;
lines = get_line_level(port);
reg = MCHP_I2C_STATUS(controller);
if ((((reg & (STS_BER | STS_LAB)) || !(reg & STS_NBB)) ||
(lines != I2C_LINE_IDLE))) {
cdata[controller].flags |= (1ul << 16);
CPRINTS("I2C%d port%d recov status 0x%02x, SDA:SCL=0x%0x",
controller, port, reg, lines);
/* Attempt to unwedge the port. */
if (lines != I2C_LINE_IDLE)
if (i2c_unwedge(port))
return EC_ERROR_UNKNOWN;
/* Bus error, bus busy, or arbitration lost. Try reset. */
reset_controller(controller);
select_port(port, controller);
/*
* We don't know what edges the slave saw, so sleep long enough
* that the slave will see the new start condition below.
*/
usleep(1000);
reg = MCHP_I2C_STATUS(controller);
lines = get_line_level(port);
if ((reg & (STS_BER | STS_LAB)) || !(reg & STS_NBB) ||
(lines != I2C_LINE_IDLE))
return EC_ERROR_UNKNOWN;
}
return EC_SUCCESS;
}
static inline void push_in_buf(uint8_t **in, uint8_t val, int skip)
{
if (!skip) {
**in = val;
(*in)++;
}
}
/*
* I2C Master transmit
* Caller has filled in cdata[ctrl] parameters
*/
static int i2c_mtx(int ctrl)
{
int i, rv;
rv = EC_SUCCESS;
cdata[ctrl].flags |= (1ul << 1);
if (cdata[ctrl].xflags & I2C_XFER_START) {
cdata[ctrl].flags |= (1ul << 2);
MCHP_I2C_DATA(ctrl) = cdata[ctrl].slv_addr_8bit;
/* Clock out the slave address, sending START bit */
MCHP_I2C_CTRL(ctrl) = CTRL_PIN | CTRL_ESO | CTRL_ENI |
CTRL_ACK | CTRL_STA;
cdata[ctrl].transaction_state = I2C_TRANSACTION_OPEN;
}
for (i = 0; i < cdata[ctrl].out_size; ++i) {
rv = wait_byte_done(ctrl, 0xff, 0x00);
if (rv) {
cdata[ctrl].flags |= (1ul << 17);
MCHP_I2C_CTRL(ctrl) = CTRL_PIN | CTRL_ESO |
CTRL_ENI | CTRL_STO | CTRL_ACK;
return rv;
}
cdata[ctrl].flags |= (1ul << 15);
MCHP_I2C_DATA(ctrl) = cdata[ctrl].outp[i];
}
rv = wait_byte_done(ctrl, 0xff, 0x00);
if (rv) {
cdata[ctrl].flags |= (1ul << 18);
MCHP_I2C_CTRL(ctrl) = CTRL_PIN | CTRL_ESO | CTRL_ENI |
CTRL_STO | CTRL_ACK;
return rv;
}
/*
* Send STOP bit if the stop flag is on, and caller
* doesn't expect to receive data.
*/
if ((cdata[ctrl].xflags & I2C_XFER_STOP) &&
(cdata[ctrl].in_size == 0)) {
cdata[ctrl].flags |= (1ul << 3);
MCHP_I2C_CTRL(ctrl) = CTRL_PIN | CTRL_ESO |
CTRL_STO | CTRL_ACK;
cdata[ctrl].transaction_state = I2C_TRANSACTION_STOPPED;
}
return rv;
}
/*
* I2C Master-Receive helper routine for sending START or
* Repeated-START.
* This routine should only be called if a (Repeated-)START
* is required.
* If I2C controller is Idle or Stopped
* Send START by:
* Write read address to I2C.Data
* Write PIN=ESO=STA=ACK=1, STO=0 to I2C.Ctrl. This
* will trigger controller to output 8-bits of data.
* Else if I2C controller is Open (previous START sent)
* Send Repeated-START by:
* Write ESO=STA=ACK=1, PIN=STO=0 to I2C.Ctrl. Controller
* will generate START but not transmit data.
* Write read address to I2C.Data. Controller will transmit
* 8-bits of data
* Endif
* NOTE: Controller clocks in address on SDA as its transmitting.
* Therefore 1-byte RX-FIFO will contain address plus R/nW bit.
* Controller will wait for slave to release SCL before transmitting
* 9th clock and latching (N)ACK on SDA.
* Spin on I2C.Status PIN -> 0. Enable I2C interrupt if spin time
* exceeds threshold. If a timeout occurs generate STOP and return
* an error.
*
* Because I2C generates clocks for next byte when reading I2C.Data
* register we must prepare control logic.
* If the caller requests STOP and read length is 1 then set
* clear ACK bit in I2C.Ctrl. Set ESO=ENI=1, PIN=STA=STO=ACK=0
* in I2C.Ctrl. Master must NACK last byte.
*/
static int i2c_mrx_start(int ctrl)
{
uint8_t u8;
int rv;
cdata[ctrl].flags |= (1ul << 4);
u8 = CTRL_ESO | CTRL_ENI | CTRL_STA | CTRL_ACK;
if (cdata[ctrl].transaction_state == I2C_TRANSACTION_OPEN) {
cdata[ctrl].flags |= (1ul << 5);
/* Repeated-START then address */
MCHP_I2C_CTRL(ctrl) = u8;
}
MCHP_I2C_DATA(ctrl) = cdata[ctrl].slv_addr_8bit | 0x01;
if (cdata[ctrl].transaction_state == I2C_TRANSACTION_STOPPED) {
cdata[ctrl].flags |= (1ul << 6);
/* address then START */
MCHP_I2C_CTRL(ctrl) = u8 | CTRL_PIN;
}
cdata[ctrl].transaction_state = I2C_TRANSACTION_OPEN;
/* Controller generates START, transmits data(address) capturing
* 9-bits from SDA (8-bit address + (N)Ack bit).
* We leave captured address in I2C.Data register.
* Master Receive data read routine assumes data is pending
* in I2C.Data
*/
cdata[ctrl].flags |= (1ul << 7);
rv = wait_byte_done(ctrl, 0xff, 0x00);
if (rv) {
cdata[ctrl].flags |= (1ul << 19);
MCHP_I2C_CTRL(ctrl) = CTRL_PIN | CTRL_ESO |
CTRL_STO | CTRL_ACK;
return rv;
}
/* if STOP requested and last 1 or 2 bytes prepare controller
* to NACK last byte. Do this before read of dummy data so
* controller is setup to NACK last byte.
*/
cdata[ctrl].flags |= (1ul << 8);
if (cdata[ctrl].xflags & I2C_XFER_STOP &&
(cdata[ctrl].in_size < 2)) {
cdata[ctrl].flags |= (1ul << 9);
MCHP_I2C_CTRL(ctrl) = CTRL_ESO | CTRL_ENI;
}
/*
* Read & discard slave address.
* Generates clocks for next data
*/
cdata[ctrl].flags |= (1ul << 10);
u8 = MCHP_I2C_DATA(ctrl);
return rv;
}
/*
* I2C Master-Receive data read helper.
* Assumes I2C is in use, (Rpt-)START was previously sent.
* Reading I2C.Data generates clocks for the next byte. If caller
* requests STOP then we must clear I2C.Ctrl ACK before reading
* second to last byte from RX-FIFO data register. Before reading
* the last byte we must set I2C.Ctrl to generate a stop after
* the read from RX-FIFO register.
* NOTE: I2C.Status.LRB only records the (N)ACK bit in master
* transmit mode, not in master receive mode.
* NOTE2: Do not set ENI bit in I2C.Ctrl for STOP generation.
*/
static int i2c_mrx_data(int ctrl)
{
uint32_t nrx = (uint32_t)cdata[ctrl].in_size;
uint32_t stop = (uint32_t)cdata[ctrl].xflags & I2C_XFER_STOP;
uint8_t *pdest = cdata[ctrl].inp;
int rv;
cdata[ctrl].flags |= (1ul << 11);
while (nrx) {
rv = wait_byte_done(ctrl, 0xff, 0x00);
if (rv) {
cdata[ctrl].flags |= (1ul << 20);
MCHP_I2C_CTRL(ctrl) = CTRL_PIN | CTRL_ESO |
CTRL_STO | CTRL_ACK;
return rv;
}
if (stop) {
if (nrx == 2) {
cdata[ctrl].flags |= (1ul << 12);
MCHP_I2C_CTRL(ctrl) = CTRL_ESO | CTRL_ENI;
} else if (nrx == 1) {
cdata[ctrl].flags |= (1ul << 13);
MCHP_I2C_CTRL(ctrl) = CTRL_PIN | CTRL_ESO |
CTRL_STO | CTRL_ACK;
}
}
*pdest++ = MCHP_I2C_DATA(ctrl);
nrx--;
}
cdata[ctrl].flags |= (1ul << 14);
return EC_SUCCESS;
}
/*
* Called from common/i2c_master
*/
int chip_i2c_xfer(int port, uint16_t slave_addr_flags,
const uint8_t *out, int out_size,
uint8_t *in, int in_size, int flags)
{
int ctrl;
int ret_done;
if (out_size == 0 && in_size == 0)
return EC_SUCCESS;
ctrl = i2c_port_to_controller(port);
if (ctrl < 0)
return EC_ERROR_INVAL;
cdata[ctrl].flags = (1ul << 0);
disable_controller_irq(ctrl);
select_port(port, ctrl);
/* store transfer context */
cdata[ctrl].i2c_complete = 0;
cdata[ctrl].hwsts = 0;
cdata[ctrl].hwsts2 = 0;
cdata[ctrl].hwsts3 = 0;
cdata[ctrl].hwsts4 = 0;
cdata[ctrl].port = port & 0xff;
cdata[ctrl].slv_addr_8bit = I2C_GET_ADDR(slave_addr_flags) << 1;
cdata[ctrl].out_size = out_size;
cdata[ctrl].outp = out;
cdata[ctrl].in_size = in_size;
cdata[ctrl].inp = in;
cdata[ctrl].xflags = flags;
if ((flags & I2C_XFER_START) &&
cdata[ctrl].transaction_state == I2C_TRANSACTION_STOPPED) {
wait_idle(ctrl);
ret_done = i2c_check_recover(port, ctrl);
if (ret_done)
goto err_chip_i2c_xfer;
}
ret_done = EC_SUCCESS;
if (out_size) {
ret_done = i2c_mtx(ctrl);
if (ret_done)
goto err_chip_i2c_xfer;
}
if (in_size) {
if (cdata[ctrl].xflags & I2C_XFER_START) {
ret_done = i2c_mrx_start(ctrl);
if (ret_done)
goto err_chip_i2c_xfer;
}
ret_done = i2c_mrx_data(ctrl);
if (ret_done)
goto err_chip_i2c_xfer;
}
cdata[ctrl].flags |= (1ul << 15);
/* MCHP wait for STOP to complete */
if (cdata[ctrl].xflags & I2C_XFER_STOP)
wait_idle(ctrl);
/* Check for error conditions */
if (MCHP_I2C_STATUS(ctrl) & (STS_LAB | STS_BER)) {
cdata[ctrl].flags |= (1ul << 21);
goto err_chip_i2c_xfer;
}
cdata[ctrl].flags |= (1ul << 14);
return EC_SUCCESS;
err_chip_i2c_xfer:
cdata[ctrl].flags |= (1ul << 22);
cdata[ctrl].hwsts2 = MCHP_I2C_STATUS(ctrl); /* record status */
/* NOTE: writing I2C.Ctrl.PIN=1 will clear all bits
* except NBB in I2C.Status
*/
MCHP_I2C_CTRL(ctrl) = CTRL_PIN | CTRL_ESO |
CTRL_STO | CTRL_ACK;
cdata[ctrl].transaction_state = I2C_TRANSACTION_STOPPED;
/* record status after STOP */
cdata[ctrl].hwsts4 = MCHP_I2C_STATUS(ctrl);
/* record line levels.
* Note line levels may reflect STOP condition
*/
cdata[ctrl].lines = (uint8_t)get_line_level(cdata[ctrl].port);
if (cdata[ctrl].hwsts2 & STS_BER) {
cdata[ctrl].flags |= (1ul << 23);
reset_controller(ctrl);
}
return EC_ERROR_UNKNOWN;
}
/*
* A safe method of reading port's SCL pin level.
*/
int i2c_raw_get_scl(int port)
{
enum gpio_signal g;
/* If no SCL pin defined for this port,
* then return 1 to appear idle.
*/
if (get_scl_from_i2c_port(port, &g) != EC_SUCCESS)
return 1;
return gpio_get_level(g);
}
/*
* A safe method of reading port's SDA pin level.
*/
int i2c_raw_get_sda(int port)
{
enum gpio_signal g;
/* If no SDA pin defined for this port,
* then return 1 to appear idle.
*/
if (get_sda_from_i2c_port(port, &g) != EC_SUCCESS)
return 1;
return gpio_get_level(g);
}
/*
* Caller is responsible for locking the port.
*/
int i2c_get_line_levels(int port)
{
int rv, controller;
controller = i2c_port_to_controller(port);
if (controller < 0)
return 0x03; /* No controller, return high line levels */
select_port(port, controller);
rv = get_line_level(port);
return rv;
}
/*
* I2C port must be a zero based number.
* MCHP I2C can map any port to any of the 4 controllers.
* Call board level function as board designs may choose
* to wire up and group ports differently.
*/
int i2c_port_to_controller(int port)
{
return board_i2c_p2c(port);
}
void i2c_set_timeout(int port, uint32_t timeout)
{
/* Param is port, but timeout is stored by-controller. */
cdata[i2c_port_to_controller(port)].timeout_us =
timeout ? timeout : I2C_TIMEOUT_DEFAULT_US;
}
/*
* Initialize I2C controllers specified by the board configuration.
* If multiple ports are mapped to the same controller choose the
* lowest speed.
*/
static void i2c_init(void)
{
int i, controller, kbps;
int controller_kbps[MCHP_I2C_CTRL_MAX];
const struct i2c_bus_clk *pbc;
for (i = 0; i < MCHP_I2C_CTRL_MAX; i++)
controller_kbps[i] = 0;
/* Configure GPIOs */
gpio_config_module(MODULE_I2C, 1);
memset(cdata, 0, sizeof(cdata));
for (i = 0; i < i2c_ports_used; ++i) {
controller = i2c_port_to_controller(i2c_ports[i].port);
kbps = i2c_ports[i].kbps;
/* Clear PCR sleep enable for controller */
i2c_ctrl_slp_en(controller, 0);
if (controller_kbps[controller] &&
(controller_kbps[controller] != kbps)) {
CPRINTF("I2C[%d] init speed conflict: %d != %d\n",
controller, kbps,
controller_kbps[controller]);
kbps = MIN(kbps, controller_kbps[controller]);
}
/* controller speed hardware limits */
pbc = get_supported_speed_idx(kbps);
if (pbc->freq_khz != kbps)
CPRINTF("I2C[%d] init requested speed %d"
" using closest supported speed %d\n",
controller, kbps, pbc->freq_khz);
controller_kbps[controller] = pbc->freq_khz;
configure_controller(controller, i2c_ports[i].port,
controller_kbps[controller]);
cdata[controller].task_waiting = TASK_ID_INVALID;
cdata[controller].transaction_state = I2C_TRANSACTION_STOPPED;
/* Use default timeout. */
i2c_set_timeout(i2c_ports[i].port, 0);
}
}
DECLARE_HOOK(HOOK_INIT, i2c_init, HOOK_PRIO_INIT_I2C);
/*
* Handle I2C interrupts.
* I2C controller is configured to fire interrupts on
* anything causing PIN 1->0 and I2C IDLE (NBB -> 1).
* NVIC interrupt disable must clear NVIC pending bit.
*/
static void handle_interrupt(int controller)
{
uint32_t r;
int id = cdata[controller].task_waiting;
/*
* Write to control register interferes with I2C transaction.
* Instead, let's disable IRQ from the core until the next time
* we want to wait for STS_PIN/STS_NBB.
*/
disable_controller_irq(controller);
cdata[controller].hwsts3 = MCHP_I2C_STATUS(controller);
/* Clear all interrupt status */
r = MCHP_I2C_COMPLETE(controller);
MCHP_I2C_COMPLETE(controller) = r;
cdata[controller].i2c_complete = r;
MCHP_INT_SOURCE(MCHP_I2C_GIRQ) = MCHP_I2C_GIRQ_BIT(controller);
/* Wake up the task which was waiting on the I2C interrupt, if any. */
if (id != TASK_ID_INVALID)
task_set_event(id, TASK_EVENT_I2C_IDLE, 0);
}
void i2c0_interrupt(void) { handle_interrupt(0); }
void i2c1_interrupt(void) { handle_interrupt(1); }
void i2c2_interrupt(void) { handle_interrupt(2); }
void i2c3_interrupt(void) { handle_interrupt(3); }
DECLARE_IRQ(MCHP_IRQ_I2C_0, i2c0_interrupt, 2);
DECLARE_IRQ(MCHP_IRQ_I2C_1, i2c1_interrupt, 2);
DECLARE_IRQ(MCHP_IRQ_I2C_2, i2c2_interrupt, 2);
DECLARE_IRQ(MCHP_IRQ_I2C_3, i2c3_interrupt, 2);