965 lines
27 KiB
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);
|