Files
system76-coreboot/src/drivers/i2c/designware/dw_i2c.c
Werner Zeh 1e02ad3f5a drivers/i2c/designware: Report I2C timings for additional bus speeds
Since the OS provides its own driver for the I2C controller it can
choose to use a bus speed other than the one used at coreboot runtime.
In this case it would be good to provide a way how the needed bus
timings are communicated to the OS, since these are very board-specific
and there is no way that the OS can know them other than read the
appropriate ACPI reported timings.
This patch adds some code to report additional bus speed timings if
there are some defined in the devicetree.

Change-Id: If921e0613864660dc1bb8d7c1b30fb9db8ac655d
Signed-off-by: Werner Zeh <werner.zeh@siemens.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/55088
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
Reviewed-by: Mario Scheithauer <mario.scheithauer@siemens.com>
Reviewed-by: Paul Menzel <paulepanter@mailbox.org>
2021-06-04 12:39:06 +00:00

851 lines
21 KiB
C

/* SPDX-License-Identifier: GPL-2.0-only */
#include <acpi/acpigen.h>
#include <device/mmio.h>
#include <console/console.h>
#include <device/device.h>
#include <device/i2c_bus.h>
#include <device/i2c_simple.h>
#include <string.h>
#include <timer.h>
#include "dw_i2c.h"
/* Use a ~10ms timeout for various operations */
#define DW_I2C_TIMEOUT_US 10000
/* High and low times in different speed modes (in ns) */
enum {
/* SDA Hold Time */
DEFAULT_SDA_HOLD_TIME = 300,
/* Standard Speed */
MIN_SS_SCL_HIGHTIME = 4000,
MIN_SS_SCL_LOWTIME = 4700,
/* Fast Speed */
MIN_FS_SCL_HIGHTIME = 600,
MIN_FS_SCL_LOWTIME = 1300,
/* Fast Plus Speed */
MIN_FP_SCL_HIGHTIME = 260,
MIN_FP_SCL_LOWTIME = 500,
/* High Speed */
MIN_HS_SCL_HIGHTIME = 60,
MIN_HS_SCL_LOWTIME = 160,
};
/* Frequency represented as ticks per ns. Can also be used to calculate
* the number of ticks to meet a time target or the period. */
struct freq {
uint32_t ticks;
uint32_t ns;
};
/* Control register definitions */
enum {
CONTROL_MASTER_MODE = (1 << 0),
CONTROL_SPEED_SS = (1 << 1),
CONTROL_SPEED_FS = (1 << 2),
CONTROL_SPEED_HS = (3 << 1),
CONTROL_SPEED_MASK = (3 << 1),
CONTROL_10BIT_SLAVE = (1 << 3),
CONTROL_10BIT_MASTER = (1 << 4),
CONTROL_RESTART_ENABLE = (1 << 5),
CONTROL_SLAVE_DISABLE = (1 << 6),
};
/* Command/Data register definitions */
enum {
CMD_DATA_CMD = (1 << 8),
CMD_DATA_STOP = (1 << 9),
};
/* Status register definitions */
enum {
STATUS_ACTIVITY = (1 << 0),
STATUS_TX_FIFO_NOT_FULL = (1 << 1),
STATUS_TX_FIFO_EMPTY = (1 << 2),
STATUS_RX_FIFO_NOT_EMPTY = (1 << 3),
STATUS_RX_FIFO_FULL = (1 << 4),
STATUS_MASTER_ACTIVITY = (1 << 5),
STATUS_SLAVE_ACTIVITY = (1 << 6),
};
/* Enable register definitions */
enum {
ENABLE_CONTROLLER = (1 << 0),
};
/* Interrupt status register definitions */
enum {
INTR_STAT_RX_UNDER = (1 << 0),
INTR_STAT_RX_OVER = (1 << 1),
INTR_STAT_RX_FULL = (1 << 2),
INTR_STAT_TX_OVER = (1 << 3),
INTR_STAT_TX_EMPTY = (1 << 4),
INTR_STAT_RD_REQ = (1 << 5),
INTR_STAT_TX_ABORT = (1 << 6),
INTR_STAT_RX_DONE = (1 << 7),
INTR_STAT_ACTIVITY = (1 << 8),
INTR_STAT_STOP_DET = (1 << 9),
INTR_STAT_START_DET = (1 << 10),
INTR_STAT_GEN_CALL = (1 << 11),
};
/* I2C Controller MMIO register space */
struct dw_i2c_regs {
uint32_t control; /* 0x0 */
uint32_t target_addr; /* 0x4 */
uint32_t slave_addr; /* 0x8 */
uint32_t master_addr; /* 0xc */
uint32_t cmd_data; /* 0x10 */
uint32_t ss_scl_hcnt; /* 0x14 */
uint32_t ss_scl_lcnt; /* 0x18 */
uint32_t fs_scl_hcnt; /* 0x1c */
uint32_t fs_scl_lcnt; /* 0x20 */
uint32_t hs_scl_hcnt; /* 0x24 */
uint32_t hs_scl_lcnt; /* 0x28 */
uint32_t intr_stat; /* 0x2c */
uint32_t intr_mask; /* 0x30 */
uint32_t raw_intr_stat; /* 0x34 */
uint32_t rx_thresh; /* 0x38 */
uint32_t tx_thresh; /* 0x3c */
uint32_t clear_intr; /* 0x40 */
uint32_t clear_rx_under_intr; /* 0x44 */
uint32_t clear_rx_over_intr; /* 0x48 */
uint32_t clear_tx_over_intr; /* 0x4c */
uint32_t clear_rd_req_intr; /* 0x50 */
uint32_t clear_tx_abrt_intr; /* 0x54 */
uint32_t clear_rx_done_intr; /* 0x58 */
uint32_t clear_activity_intr; /* 0x5c */
uint32_t clear_stop_det_intr; /* 0x60 */
uint32_t clear_start_det_intr; /* 0x64 */
uint32_t clear_gen_call_intr; /* 0x68 */
uint32_t enable; /* 0x6c */
uint32_t status; /* 0x70 */
uint32_t tx_level; /* 0x74 */
uint32_t rx_level; /* 0x78 */
uint32_t sda_hold; /* 0x7c */
uint32_t tx_abort_source; /* 0x80 */
uint32_t slv_data_nak_only; /* 0x84 */
uint32_t dma_cr; /* 0x88 */
uint32_t dma_tdlr; /* 0x8c */
uint32_t dma_rdlr; /* 0x90 */
uint32_t sda_setup; /* 0x94 */
uint32_t ack_general_call; /* 0x98 */
uint32_t enable_status; /* 0x9c */
uint32_t fs_spklen; /* 0xa0 */
uint32_t hs_spklen; /* 0xa4 */
uint32_t clr_restart_det; /* 0xa8 */
uint32_t reserved[18]; /* 0xac - 0xf0 */
uint32_t comp_param1; /* 0xf4 */
uint32_t comp_version; /* 0xf8 */
uint32_t comp_type; /* 0xfc */
} __packed;
/* Constant value defined in the DesignWare DW_apb_i2c Databook. */
#define DW_I2C_COMP_TYPE 0x44570140
static const struct i2c_descriptor {
enum i2c_speed speed;
struct freq freq;
int min_thigh_ns;
int min_tlow_ns;
} speed_descriptors[] = {
{
.speed = I2C_SPEED_STANDARD,
.freq = {
.ticks = 100,
.ns = 1000*1000,
},
.min_thigh_ns = MIN_SS_SCL_HIGHTIME,
.min_tlow_ns = MIN_SS_SCL_LOWTIME,
},
{
.speed = I2C_SPEED_FAST,
.freq = {
.ticks = 400,
.ns = 1000*1000,
},
.min_thigh_ns = MIN_FS_SCL_HIGHTIME,
.min_tlow_ns = MIN_FS_SCL_LOWTIME,
},
{
.speed = I2C_SPEED_FAST_PLUS,
.freq = {
.ticks = 1,
.ns = 1000,
},
.min_thigh_ns = MIN_FP_SCL_HIGHTIME,
.min_tlow_ns = MIN_FP_SCL_LOWTIME,
},
{
/* 100pF max capacitance */
.speed = I2C_SPEED_HIGH,
.freq = {
.ticks = 3400,
.ns = 1000*1000,
},
.min_thigh_ns = MIN_HS_SCL_HIGHTIME,
.min_tlow_ns = MIN_HS_SCL_LOWTIME,
},
};
static const struct soc_clock {
int clk_speed_mhz;
struct freq freq;
} soc_clocks[] = {
{
.clk_speed_mhz = 120,
.freq = {
.ticks = 120,
.ns = 1000,
},
},
{
.clk_speed_mhz = 133,
.freq = {
.ticks = 400,
.ns = 3000,
},
},
{
.clk_speed_mhz = 150,
.freq = {
.ticks = 600,
.ns = 4000,
},
},
{
.clk_speed_mhz = 216,
.freq = {
.ticks = 1080,
.ns = 5000,
},
},
};
static const struct i2c_descriptor *get_bus_descriptor(enum i2c_speed speed)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(speed_descriptors); i++)
if (speed == speed_descriptors[i].speed)
return &speed_descriptors[i];
return NULL;
}
static const struct soc_clock *get_soc_descriptor(int ic_clk)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(soc_clocks); i++)
if (ic_clk == soc_clocks[i].clk_speed_mhz)
return &soc_clocks[i];
return NULL;
}
static int counts_from_time(const struct freq *f, int ns)
{
return DIV_ROUND_UP(f->ticks * ns, f->ns);
}
static int counts_from_freq(const struct freq *fast, const struct freq *slow)
{
return DIV_ROUND_UP(fast->ticks * slow->ns, fast->ns * slow->ticks);
}
/* Enable this I2C controller */
static void dw_i2c_enable(struct dw_i2c_regs *regs)
{
uint32_t enable = read32(&regs->enable);
if (!(enable & ENABLE_CONTROLLER))
write32(&regs->enable, enable | ENABLE_CONTROLLER);
}
/* Disable this I2C controller */
static int dw_i2c_disable(struct dw_i2c_regs *regs)
{
uint32_t enable = read32(&regs->enable);
if (enable & ENABLE_CONTROLLER) {
struct stopwatch sw;
write32(&regs->enable, enable & ~ENABLE_CONTROLLER);
/* Wait for enable bit to clear */
stopwatch_init_usecs_expire(&sw, DW_I2C_TIMEOUT_US);
while (read32(&regs->enable_status) & ENABLE_CONTROLLER)
if (stopwatch_expired(&sw))
return -1;
}
return 0;
}
/* Wait for this I2C controller to go idle for transmit */
static int dw_i2c_wait_for_bus_idle(struct dw_i2c_regs *regs)
{
struct stopwatch sw;
/* Start timeout for up to 16 bytes in FIFO */
stopwatch_init_usecs_expire(&sw, 16 * DW_I2C_TIMEOUT_US);
while (!stopwatch_expired(&sw)) {
uint32_t status = read32(&regs->status);
/* Check for master activity and keep waiting */
if (status & STATUS_MASTER_ACTIVITY)
continue;
/* Check for TX FIFO empty to indicate TX idle */
if (status & STATUS_TX_FIFO_EMPTY)
return 0;
}
/* Timed out while waiting for bus to go idle */
return -1;
}
/* Transfer one byte of one segment, sending stop bit if requested */
static int dw_i2c_transfer_byte(struct dw_i2c_regs *regs,
const struct i2c_msg *segment,
size_t byte, int send_stop)
{
struct stopwatch sw;
uint32_t cmd = CMD_DATA_CMD; /* Read op */
stopwatch_init_usecs_expire(&sw, DW_I2C_TIMEOUT_US);
if (!(segment->flags & I2C_M_RD)) {
/* Write op only: Wait for FIFO not full */
while (!(read32(&regs->status) & STATUS_TX_FIFO_NOT_FULL)) {
if (stopwatch_expired(&sw)) {
printk(BIOS_ERR, "I2C transmit timeout\n");
return -1;
}
}
cmd = segment->buf[byte];
}
/* Send stop on last byte, if desired */
if (send_stop && byte == segment->len - 1)
cmd |= CMD_DATA_STOP;
write32(&regs->cmd_data, cmd);
if (segment->flags & I2C_M_RD) {
/* Read op only: Wait for FIFO data and store it */
while (!(read32(&regs->status) & STATUS_RX_FIFO_NOT_EMPTY)) {
if (stopwatch_expired(&sw)) {
printk(BIOS_ERR, "I2C receive timeout\n");
return -1;
}
}
segment->buf[byte] = read32(&regs->cmd_data);
}
return 0;
}
static int _dw_i2c_transfer(unsigned int bus, const struct i2c_msg *segments,
size_t count)
{
struct stopwatch sw;
struct dw_i2c_regs *regs;
size_t byte;
int ret = -1;
regs = (struct dw_i2c_regs *)dw_i2c_base_address(bus);
if (!regs) {
printk(BIOS_ERR, "I2C bus %u base address not found\n", bus);
return -1;
}
/* The assumption is that the host controller is disabled -- either
after running this function or from performing the initialization
sequence in dw_i2c_init(). */
/* Set target slave address */
write32(&regs->target_addr, segments->slave);
dw_i2c_enable(regs);
/* Process each segment */
while (count--) {
if (CONFIG(DRIVERS_I2C_DESIGNWARE_DEBUG)) {
printk(BIOS_DEBUG, "i2c %u:%02x %s %d bytes : ",
bus, segments->slave,
(segments->flags & I2C_M_RD) ? "R" : "W",
segments->len);
}
/* Read or write each byte in segment */
for (byte = 0; byte < segments->len; byte++) {
/*
* Set stop condition on final segment only.
* Repeated start will be automatically generated
* by the controller on R->W or W->R switch.
*/
if (dw_i2c_transfer_byte(regs, segments, byte,
count == 0) < 0) {
printk(BIOS_ERR, "I2C %s failed: bus %u "
"addr 0x%02x\n",
(segments->flags & I2C_M_RD) ?
"read" : "write", bus, segments->slave);
goto out;
}
}
if (CONFIG(DRIVERS_I2C_DESIGNWARE_DEBUG)) {
int j;
for (j = 0; j < segments->len; j++)
printk(BIOS_DEBUG, "%02x ", segments->buf[j]);
printk(BIOS_DEBUG, "\n");
}
segments++;
}
/* Wait for interrupt status to indicate transfer is complete */
stopwatch_init_usecs_expire(&sw, DW_I2C_TIMEOUT_US);
while (!(read32(&regs->raw_intr_stat) & INTR_STAT_STOP_DET)) {
if (stopwatch_expired(&sw)) {
printk(BIOS_ERR, "I2C stop bit not received\n");
goto out;
}
}
/* Read to clear INTR_STAT_STOP_DET */
read32(&regs->clear_stop_det_intr);
/* Check TX abort */
if (read32(&regs->raw_intr_stat) & INTR_STAT_TX_ABORT) {
printk(BIOS_ERR, "I2C TX abort detected (%08x)\n",
read32(&regs->tx_abort_source));
/* clear INTR_STAT_TX_ABORT */
read32(&regs->clear_tx_abrt_intr);
goto out;
}
/* Wait for the bus to go idle */
if (dw_i2c_wait_for_bus_idle(regs)) {
printk(BIOS_ERR, "I2C timeout waiting for bus %u idle\n", bus);
goto out;
}
/* Flush the RX FIFO in case it is not empty */
stopwatch_init_usecs_expire(&sw, 16 * DW_I2C_TIMEOUT_US);
while (read32(&regs->status) & STATUS_RX_FIFO_NOT_EMPTY) {
if (stopwatch_expired(&sw)) {
printk(BIOS_ERR, "I2C timeout flushing RX FIFO\n");
goto out;
}
read32(&regs->cmd_data);
}
ret = 0;
out:
read32(&regs->clear_intr);
dw_i2c_disable(regs);
return ret;
}
int dw_i2c_transfer(unsigned int bus, const struct i2c_msg *msg, size_t count)
{
const struct i2c_msg *orig_msg = msg;
size_t i;
size_t start;
uint16_t addr;
if (count == 0 || !msg)
return -1;
/* Break up the transfers at the differing slave address boundary. */
addr = orig_msg->slave;
for (i = 0, start = 0; i < count; i++, msg++) {
if (addr != msg->slave) {
if (_dw_i2c_transfer(bus, &orig_msg[start], i - start))
return -1;
start = i;
addr = msg->slave;
}
}
return _dw_i2c_transfer(bus, &orig_msg[start], count - start);
}
/* Global I2C bus handler, defined in include/device/i2c_simple.h */
int platform_i2c_transfer(unsigned int bus, struct i2c_msg *msg, int count)
{
return dw_i2c_transfer(bus, msg, count < 0 ? 0 : count);
}
static int dw_i2c_set_speed_config(unsigned int bus,
const struct dw_i2c_speed_config *config)
{
struct dw_i2c_regs *regs;
void *hcnt_reg, *lcnt_reg;
regs = (struct dw_i2c_regs *)dw_i2c_base_address(bus);
if (!regs || !config)
return -1;
/* Nothing to do if no values are set */
if (!config->scl_lcnt && !config->scl_hcnt && !config->sda_hold)
return 0;
if (config->speed >= I2C_SPEED_HIGH) {
/* High and Fast Ultra speed */
hcnt_reg = &regs->hs_scl_hcnt;
lcnt_reg = &regs->hs_scl_lcnt;
} else if (config->speed >= I2C_SPEED_FAST) {
/* Fast and Fast-Plus speed */
hcnt_reg = &regs->fs_scl_hcnt;
lcnt_reg = &regs->fs_scl_lcnt;
} else {
/* Standard speed */
hcnt_reg = &regs->ss_scl_hcnt;
lcnt_reg = &regs->ss_scl_lcnt;
}
/* SCL count must be set after the speed is selected */
if (config->scl_hcnt)
write32(hcnt_reg, config->scl_hcnt);
if (config->scl_lcnt)
write32(lcnt_reg, config->scl_lcnt);
/* Set SDA Hold Time register */
if (config->sda_hold)
write32(&regs->sda_hold, config->sda_hold);
return 0;
}
static int dw_i2c_gen_config_rise_fall_time(struct dw_i2c_regs *regs,
enum i2c_speed speed,
const struct dw_i2c_bus_config *bcfg,
int ic_clk,
struct dw_i2c_speed_config *config)
{
const struct i2c_descriptor *bus;
const struct soc_clock *soc;
int fall_cnt, rise_cnt, min_tlow_cnt, min_thigh_cnt, spk_cnt;
int hcnt, lcnt, period_cnt, diff, tot;
int data_hold_time_ns;
bus = get_bus_descriptor(speed);
soc = get_soc_descriptor(ic_clk);
if (bus == NULL) {
printk(BIOS_ERR, "dw_i2c: invalid bus speed %d\n", speed);
return -1;
}
if (soc == NULL) {
printk(BIOS_ERR, "dw_i2c: invalid SoC clock speed %d MHz\n",
ic_clk);
return -1;
}
/* Get the proper spike suppression count based on target speed. */
if (speed >= I2C_SPEED_HIGH)
spk_cnt = read32(&regs->hs_spklen);
else
spk_cnt = read32(&regs->fs_spklen);
/* Find the period, rise, fall, min tlow, and min thigh in terms of
* counts of SoC clock. */
period_cnt = counts_from_freq(&soc->freq, &bus->freq);
rise_cnt = counts_from_time(&soc->freq, bcfg->rise_time_ns);
fall_cnt = counts_from_time(&soc->freq, bcfg->fall_time_ns);
min_tlow_cnt = counts_from_time(&soc->freq, bus->min_tlow_ns);
min_thigh_cnt = counts_from_time(&soc->freq, bus->min_thigh_ns);
printk(DW_I2C_DEBUG, "dw_i2c: SoC %d/%d ns Bus: %d/%d ns\n",
soc->freq.ticks, soc->freq.ns, bus->freq.ticks, bus->freq.ns);
printk(DW_I2C_DEBUG,
" dw_i2c: period %d rise %d fall %d tlow %d thigh %d spk %d\n",
period_cnt, rise_cnt, fall_cnt, min_tlow_cnt, min_thigh_cnt,
spk_cnt);
/*
* Back solve for hcnt and lcnt according to the following equations.
* SCL_High_time = [(HCNT + IC_*_SPKLEN + 7) * ic_clk] + SCL_Fall_time
* SCL_Low_time = [(LCNT + 1) * ic_clk] - SCL_Fall_time + SCL_Rise_time
*/
hcnt = min_thigh_cnt - fall_cnt - 7 - spk_cnt;
lcnt = min_tlow_cnt - rise_cnt + fall_cnt - 1;
if (hcnt < 0 || lcnt < 0) {
printk(BIOS_ERR, "dw_i2c: bad counts. hcnt = %d lcnt = %d\n",
hcnt, lcnt);
return -1;
}
/* Now add things back up to ensure the period is hit. If off,
* split the difference and bias to lcnt for remainder. */
tot = hcnt + lcnt + 7 + spk_cnt + rise_cnt + 1;
if (tot < period_cnt) {
diff = (period_cnt - tot) / 2;
hcnt += diff;
lcnt += diff;
tot = hcnt + lcnt + 7 + spk_cnt + rise_cnt + 1;
lcnt += period_cnt - tot;
}
config->speed = speed;
config->scl_lcnt = lcnt;
config->scl_hcnt = hcnt;
/* Use internal default unless other value is specified. */
data_hold_time_ns = DEFAULT_SDA_HOLD_TIME;
if (bcfg->data_hold_time_ns)
data_hold_time_ns = bcfg->data_hold_time_ns;
config->sda_hold = counts_from_time(&soc->freq, data_hold_time_ns);
printk(DW_I2C_DEBUG, "dw_i2c: hcnt = %d lcnt = %d sda hold = %d\n",
hcnt, lcnt, config->sda_hold);
return 0;
}
int dw_i2c_gen_speed_config(uintptr_t dw_i2c_addr,
enum i2c_speed speed,
const struct dw_i2c_bus_config *bcfg,
struct dw_i2c_speed_config *config)
{
const int ic_clk = CONFIG_DRIVERS_I2C_DESIGNWARE_CLOCK_MHZ;
struct dw_i2c_regs *regs;
int i;
regs = (struct dw_i2c_regs *)dw_i2c_addr;
_Static_assert(CONFIG_DRIVERS_I2C_DESIGNWARE_CLOCK_MHZ != 0,
"DRIVERS_I2C_DESIGNWARE_CLOCK_MHZ can't be zero!");
/* Apply board specific override for this speed if found */
for (i = 0; i < DW_I2C_SPEED_CONFIG_COUNT; i++) {
if (bcfg->speed_config[i].speed != speed)
continue;
memcpy(config, &bcfg->speed_config[i], sizeof(*config));
return 0;
}
/* Use the time calculation. */
return dw_i2c_gen_config_rise_fall_time(regs, speed, bcfg, ic_clk, config);
}
static int dw_i2c_set_speed(unsigned int bus, enum i2c_speed speed,
const struct dw_i2c_bus_config *bcfg)
{
struct dw_i2c_regs *regs;
struct dw_i2c_speed_config config;
uint32_t control;
/* Clock must be provided by Kconfig */
regs = (struct dw_i2c_regs *)dw_i2c_base_address(bus);
if (!regs || !speed)
return -1;
control = read32(&regs->control);
control &= ~CONTROL_SPEED_MASK;
if (speed >= I2C_SPEED_HIGH) {
/* High and Fast-Ultra speed share config registers */
control |= CONTROL_SPEED_HS;
} else if (speed >= I2C_SPEED_FAST) {
/* Fast speed and Fast-Plus */
control |= CONTROL_SPEED_FS;
} else {
/* Standard speed */
control |= CONTROL_SPEED_SS;
}
/* Generate speed config based on clock */
if (dw_i2c_gen_speed_config((uintptr_t)regs, speed, bcfg, &config) < 0)
return -1;
/* Select this speed in the control register */
write32(&regs->control, control);
/* Write the speed config that was generated earlier */
dw_i2c_set_speed_config(bus, &config);
return 0;
}
/*
* Initialize this bus controller and set the speed.
*
* The bus speed can be passed in Hz or using values from device/i2c.h and
* will default to I2C_SPEED_FAST if it is not provided.
*/
int dw_i2c_init(unsigned int bus, const struct dw_i2c_bus_config *bcfg)
{
struct dw_i2c_regs *regs;
enum i2c_speed speed;
if (!bcfg)
return -1;
speed = bcfg->speed ? : I2C_SPEED_FAST;
regs = (struct dw_i2c_regs *)dw_i2c_base_address(bus);
if (!regs) {
printk(BIOS_ERR, "I2C bus %u base address not found\n", bus);
return -1;
}
if (read32(&regs->comp_type) != DW_I2C_COMP_TYPE) {
printk(BIOS_ERR, "I2C bus %u has unknown type 0x%x.\n", bus,
read32(&regs->comp_type));
return -1;
}
printk(BIOS_DEBUG, "I2C bus %u version 0x%x\n", bus, read32(&regs->comp_version));
if (dw_i2c_disable(regs) < 0) {
printk(BIOS_ERR, "I2C timeout disabling bus %u\n", bus);
return -1;
}
/* Put controller in master mode with restart enabled */
write32(&regs->control, CONTROL_MASTER_MODE | CONTROL_SLAVE_DISABLE |
CONTROL_RESTART_ENABLE);
/* Set bus speed to FAST by default */
if (dw_i2c_set_speed(bus, speed, bcfg) < 0) {
printk(BIOS_ERR, "I2C failed to set speed for bus %u\n", bus);
return -1;
}
/* Set RX/TX thresholds to smallest values */
write32(&regs->rx_thresh, 0);
write32(&regs->tx_thresh, 0);
/* Enable stop detection and TX abort interrupt */
write32(&regs->intr_mask, INTR_STAT_STOP_DET | INTR_STAT_TX_ABORT);
printk(BIOS_INFO, "DW I2C bus %u at %p (%u KHz)\n",
bus, regs, speed / KHz);
return 0;
}
/*
* Write ACPI object to describe speed configuration.
*
* ACPI Object: Name ("xxxx", Package () { scl_lcnt, scl_hcnt, sda_hold }
*
* SSCN: I2C_SPEED_STANDARD
* FMCN: I2C_SPEED_FAST
* FPCN: I2C_SPEED_FAST_PLUS
* HSCN: I2C_SPEED_HIGH
*/
static void dw_i2c_acpi_write_speed_config(
const struct dw_i2c_speed_config *config)
{
if (!config)
return;
if (!config->scl_lcnt && !config->scl_hcnt && !config->sda_hold)
return;
if (config->speed >= I2C_SPEED_HIGH)
acpigen_write_name("HSCN");
else if (config->speed >= I2C_SPEED_FAST_PLUS)
acpigen_write_name("FPCN");
else if (config->speed >= I2C_SPEED_FAST)
acpigen_write_name("FMCN");
else
acpigen_write_name("SSCN");
/* Package () { scl_lcnt, scl_hcnt, sda_hold } */
acpigen_write_package(3);
acpigen_write_word(config->scl_hcnt);
acpigen_write_word(config->scl_lcnt);
acpigen_write_dword(config->sda_hold);
acpigen_pop_len();
}
/*
* The device should already be enabled and out of reset,
* either from early init in coreboot or SiliconInit in FSP.
*/
void dw_i2c_dev_init(struct device *dev)
{
const struct dw_i2c_bus_config *config;
int bus = dw_i2c_soc_dev_to_bus(dev);
if (bus < 0)
return;
config = dw_i2c_get_soc_cfg(bus);
if (!config)
return;
dw_i2c_init(bus, config);
}
/*
* Generate I2C timing information into the SSDT for the OS driver to consume,
* optionally applying override values provided by the caller.
*/
void dw_i2c_acpi_fill_ssdt(const struct device *dev)
{
const struct dw_i2c_bus_config *bcfg;
uintptr_t dw_i2c_addr;
struct dw_i2c_speed_config sgen;
int bus;
const char *path;
unsigned int speed, i;
bus = dw_i2c_soc_dev_to_bus(dev);
if (bus < 0)
return;
bcfg = dw_i2c_get_soc_cfg(bus);
if (!bcfg)
return;
dw_i2c_addr = dw_i2c_base_address(bus);
if (!dw_i2c_addr)
return;
path = acpi_device_path(dev);
if (!path)
return;
/* Ensure a default speed is available */
speed = (bcfg->speed == 0) ? I2C_SPEED_FAST : bcfg->speed;
/* Report currently used timing values for the OS driver */
acpigen_write_scope(path);
if (dw_i2c_gen_speed_config(dw_i2c_addr, speed, bcfg, &sgen) >= 0) {
dw_i2c_acpi_write_speed_config(&sgen);
}
/* Now check if there are more speed settings available and report them as well. */
for (i = 0; i < DW_I2C_SPEED_CONFIG_COUNT; i++) {
if (bcfg->speed_config[i].speed && speed != bcfg->speed_config[i].speed)
dw_i2c_acpi_write_speed_config(&bcfg->speed_config[i]);
}
acpigen_write_scope_end();
}
static int dw_i2c_dev_transfer(struct device *dev,
const struct i2c_msg *msg, size_t count)
{
return dw_i2c_transfer(dw_i2c_soc_dev_to_bus(dev), msg, count);
}
const struct i2c_bus_operations dw_i2c_bus_ops = {
.transfer = dw_i2c_dev_transfer,
};