sc7180: Add I2C driver
Add I2C functionality in coreboot. Change-Id: I61221ffff8afe5c7ede5abb9e194e242ab0274d8 Signed-off-by: Roja Rani Yarubandi <rojay@codeaurora.org> Reviewed-on: https://review.coreboot.org/c/coreboot/+/36830 Reviewed-by: Julius Werner <jwerner@chromium.org> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
parent
47a0832f82
commit
52353d09fc
|
@ -8,6 +8,7 @@ bootblock-y += timer.c
|
||||||
bootblock-y += spi.c
|
bootblock-y += spi.c
|
||||||
bootblock-y += qupv3_spi.c
|
bootblock-y += qupv3_spi.c
|
||||||
bootblock-y += gpio.c
|
bootblock-y += gpio.c
|
||||||
|
bootblock-y += qupv3_i2c.c
|
||||||
bootblock-$(CONFIG_DRIVERS_UART) += uart_bitbang.c
|
bootblock-$(CONFIG_DRIVERS_UART) += uart_bitbang.c
|
||||||
bootblock-y += clock.c
|
bootblock-y += clock.c
|
||||||
bootblock-$(CONFIG_SC7180_QSPI) += qspi.c
|
bootblock-$(CONFIG_SC7180_QSPI) += qspi.c
|
||||||
|
@ -19,6 +20,7 @@ verstage-y += timer.c
|
||||||
verstage-y += spi.c
|
verstage-y += spi.c
|
||||||
verstage-y += qupv3_spi.c
|
verstage-y += qupv3_spi.c
|
||||||
verstage-y += gpio.c
|
verstage-y += gpio.c
|
||||||
|
verstage-y += qupv3_i2c.c
|
||||||
verstage-y += clock.c
|
verstage-y += clock.c
|
||||||
verstage-$(CONFIG_SC7180_QSPI) += qspi.c
|
verstage-$(CONFIG_SC7180_QSPI) += qspi.c
|
||||||
verstage-y += qcom_qup_se.c
|
verstage-y += qcom_qup_se.c
|
||||||
|
@ -36,6 +38,7 @@ romstage-y += usb.c
|
||||||
romstage-y += spi.c
|
romstage-y += spi.c
|
||||||
romstage-y += qupv3_spi.c
|
romstage-y += qupv3_spi.c
|
||||||
romstage-y += gpio.c
|
romstage-y += gpio.c
|
||||||
|
romstage-y += qupv3_i2c.c
|
||||||
romstage-y += clock.c
|
romstage-y += clock.c
|
||||||
romstage-$(CONFIG_SC7180_QSPI) += qspi.c
|
romstage-$(CONFIG_SC7180_QSPI) += qspi.c
|
||||||
romstage-y += qcom_qup_se.c
|
romstage-y += qcom_qup_se.c
|
||||||
|
@ -48,6 +51,7 @@ ramstage-y += timer.c
|
||||||
ramstage-y += spi.c
|
ramstage-y += spi.c
|
||||||
ramstage-y += qupv3_spi.c
|
ramstage-y += qupv3_spi.c
|
||||||
ramstage-y += gpio.c
|
ramstage-y += gpio.c
|
||||||
|
ramstage-y += qupv3_i2c.c
|
||||||
ramstage-y += clock.c
|
ramstage-y += clock.c
|
||||||
ramstage-$(CONFIG_SC7180_QSPI) += qspi.c
|
ramstage-$(CONFIG_SC7180_QSPI) += qspi.c
|
||||||
ramstage-y += aop_load_reset.c
|
ramstage-y += aop_load_reset.c
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the depthcharge project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018-2019, The Linux Foundation. All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 and
|
||||||
|
* only version 2 as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __I2C_QCOM_HEADER___
|
||||||
|
#define __I2C_QCOM_HEADER___
|
||||||
|
|
||||||
|
#include <device/i2c.h>
|
||||||
|
|
||||||
|
void i2c_init(unsigned int bus, enum i2c_speed speed);
|
||||||
|
|
||||||
|
#endif /* __I2C_QCOM_HEADER */
|
|
@ -0,0 +1,165 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the depthcharge project.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018-2020, The Linux Foundation. All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 and
|
||||||
|
* only version 2 as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <device/i2c_simple.h>
|
||||||
|
#include <gpio.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <lib.h>
|
||||||
|
#include <soc/clock.h>
|
||||||
|
#include <soc/qcom_qup_se.h>
|
||||||
|
#include <soc/qupv3_config.h>
|
||||||
|
#include <soc/qupv3_i2c.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static void i2c_clk_configure(unsigned int bus, enum i2c_speed speed)
|
||||||
|
{
|
||||||
|
int clk_div = 0, t_high = 0, t_low = 0, t_cycle = 0;
|
||||||
|
struct qup_regs *regs = qup[bus].regs;
|
||||||
|
|
||||||
|
switch (speed) {
|
||||||
|
case I2C_SPEED_STANDARD:
|
||||||
|
clk_div = 7;
|
||||||
|
t_high = 10;
|
||||||
|
t_low = 11;
|
||||||
|
t_cycle = 26;
|
||||||
|
break;
|
||||||
|
case I2C_SPEED_FAST:
|
||||||
|
clk_div = 2;
|
||||||
|
t_high = 5;
|
||||||
|
t_low = 12;
|
||||||
|
t_cycle = 24;
|
||||||
|
break;
|
||||||
|
case I2C_SPEED_FAST_PLUS:
|
||||||
|
clk_div = 1;
|
||||||
|
t_high = 3;
|
||||||
|
t_low = 9;
|
||||||
|
t_cycle = 18;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
die("Unsupported I2C speed");
|
||||||
|
}
|
||||||
|
|
||||||
|
write32(®s->geni_ser_m_clk_cfg, (clk_div << 4) | 1);
|
||||||
|
/* Serial clock frequency is 19.2 MHz */
|
||||||
|
write32(®s->i2c_scl_counters, ((t_high << 20) | (t_low << 10)
|
||||||
|
| t_cycle));
|
||||||
|
}
|
||||||
|
|
||||||
|
void i2c_init(unsigned int bus, enum i2c_speed speed)
|
||||||
|
{
|
||||||
|
uint32_t proto;
|
||||||
|
struct qup_regs *regs = qup[bus].regs;
|
||||||
|
|
||||||
|
qupv3_se_fw_load_and_init(bus, SE_PROTOCOL_I2C, MIXED);
|
||||||
|
clock_enable_qup(bus);
|
||||||
|
i2c_clk_configure(bus, speed);
|
||||||
|
|
||||||
|
proto = ((read32(®s->geni_fw_revision_ro) &
|
||||||
|
GENI_FW_REVISION_RO_PROTOCOL_MASK) >>
|
||||||
|
GENI_FW_REVISION_RO_PROTOCOL_SHIFT);
|
||||||
|
|
||||||
|
assert(proto == 3);
|
||||||
|
|
||||||
|
/* Serial engine IO initialization */
|
||||||
|
write32(®s->geni_cgc_ctrl, DEFAULT_CGC_EN);
|
||||||
|
write32(®s->dma_general_cfg,
|
||||||
|
(AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CFG_ON
|
||||||
|
| DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON));
|
||||||
|
write32(®s->geni_output_ctrl,
|
||||||
|
DEFAULT_IO_OUTPUT_CTRL_MSK);
|
||||||
|
write32(®s->geni_force_default_reg, FORCE_DEFAULT);
|
||||||
|
|
||||||
|
/* Serial engine IO set mode */
|
||||||
|
write32(®s->se_irq_en, (GENI_M_IRQ_EN |
|
||||||
|
GENI_S_IRQ_EN | DMA_TX_IRQ_EN | DMA_RX_IRQ_EN));
|
||||||
|
write32(®s->se_gsi_event_en, 0);
|
||||||
|
|
||||||
|
/* Set RX and RFR watermark */
|
||||||
|
write32(®s->geni_rx_watermark_reg, 0);
|
||||||
|
write32(®s->geni_rx_rfr_watermark_reg, FIFO_DEPTH - 2);
|
||||||
|
|
||||||
|
/* FIFO PACKING CONFIGURATION */
|
||||||
|
write32(®s->geni_tx_packing_cfg0, PACK_VECTOR0
|
||||||
|
| (PACK_VECTOR1 << 10));
|
||||||
|
write32(®s->geni_tx_packing_cfg1, PACK_VECTOR2
|
||||||
|
| (PACK_VECTOR3 << 10));
|
||||||
|
write32(®s->geni_rx_packing_cfg0, PACK_VECTOR0
|
||||||
|
| (PACK_VECTOR1 << 10));
|
||||||
|
write32(®s->geni_rx_packing_cfg1, PACK_VECTOR2
|
||||||
|
| (PACK_VECTOR3 << 10));
|
||||||
|
write32(®s->geni_byte_granularity, (log2(BITS_PER_WORD) - 3));
|
||||||
|
|
||||||
|
/* GPIO Configuration */
|
||||||
|
gpio_configure(qup[bus].pin[0], qup[bus].func[0], GPIO_PULL_UP,
|
||||||
|
GPIO_2MA, GPIO_OUTPUT_ENABLE);
|
||||||
|
gpio_configure(qup[bus].pin[1], qup[bus].func[1], GPIO_PULL_UP,
|
||||||
|
GPIO_2MA, GPIO_OUTPUT_ENABLE);
|
||||||
|
|
||||||
|
/* Select and setup FIFO mode */
|
||||||
|
write32(®s->geni_m_irq_clear, 0xFFFFFFFF);
|
||||||
|
write32(®s->geni_s_irq_clear, 0xFFFFFFFF);
|
||||||
|
write32(®s->dma_tx_irq_clr, 0xFFFFFFFF);
|
||||||
|
write32(®s->dma_rx_irq_clr, 0xFFFFFFFF);
|
||||||
|
write32(®s->geni_m_irq_enable, (M_COMMON_GENI_M_IRQ_EN |
|
||||||
|
M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN |
|
||||||
|
M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN));
|
||||||
|
write32(®s->geni_s_irq_enable, (S_COMMON_GENI_S_IRQ_EN
|
||||||
|
| S_CMD_DONE_EN));
|
||||||
|
clrbits32(®s->geni_dma_mode_en, GENI_DMA_MODE_EN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2c_do_xfer(unsigned int bus, struct i2c_msg segment,
|
||||||
|
unsigned int prams)
|
||||||
|
{
|
||||||
|
unsigned int cmd = (segment.flags & I2C_M_RD) ? 2 : 1;
|
||||||
|
unsigned int master_cmd_reg_val = (cmd << M_OPCODE_SHFT);
|
||||||
|
struct qup_regs *regs = qup[bus].regs;
|
||||||
|
void *dout = NULL, *din = NULL;
|
||||||
|
|
||||||
|
if (!(segment.flags & I2C_M_RD)) {
|
||||||
|
write32(®s->i2c_tx_trans_len, segment.len);
|
||||||
|
write32(®s->geni_tx_watermark_reg, TX_WATERMARK);
|
||||||
|
dout = segment.buf;
|
||||||
|
} else {
|
||||||
|
write32(®s->i2c_rx_trans_len, segment.len);
|
||||||
|
din = segment.buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
master_cmd_reg_val |= (prams & M_PARAMS_MSK);
|
||||||
|
write32(®s->geni_m_cmd0, master_cmd_reg_val);
|
||||||
|
|
||||||
|
return qup_handle_transfer(bus, dout, din, segment.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int platform_i2c_transfer(unsigned int bus, struct i2c_msg *segments,
|
||||||
|
int seg_count)
|
||||||
|
{
|
||||||
|
struct i2c_msg *seg = segments;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
while (!ret && seg_count--) {
|
||||||
|
/* Stretch means end with repeated start, not stop */
|
||||||
|
u32 stretch = (seg_count ? 1 : 0);
|
||||||
|
u32 m_param = 0;
|
||||||
|
|
||||||
|
m_param |= (stretch << 2);
|
||||||
|
m_param |= ((seg->slave & 0x7F) << 9);
|
||||||
|
ret = i2c_do_xfer(bus, *seg, m_param);
|
||||||
|
seg++;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
Loading…
Reference in New Issue