i2c: Support changing I2C bus speed at runtime
Add a i2c_set_freq function and let chip drivers add their underlying implementation. Also implemented on stm32f0. BUG=b:143677811,b:78189419 TEST=1) make 2) On kodama, call i2c_set_freq(1, 100) during init. verify the bus is configured to 100kbps in kodama rev 1 BRANCH=kukui Change-Id: Iebb5baacf098b3e5649a4bd8ca14acf097d39693 Signed-off-by: Ting Shen <phoenixshen@google.com> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1969245 Reviewed-by: Matthew Blecker <matthewb@chromium.org> Reviewed-by: Jett Rink <jettrink@chromium.org> Commit-Queue: Ting Shen <phoenixshen@chromium.org> Tested-by: Ting Shen <phoenixshen@chromium.org>
This commit is contained in:
parent
166c22d057
commit
45427c38df
|
@ -23,7 +23,6 @@
|
|||
#include "gpio.h"
|
||||
#include "hooks.h"
|
||||
#include "host_command.h"
|
||||
#include "i2c-stm32f0.h"
|
||||
#include "i2c.h"
|
||||
#include "lid_switch.h"
|
||||
#include "power.h"
|
||||
|
@ -64,7 +63,8 @@ BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT);
|
|||
/* I2C ports */
|
||||
const struct i2c_port_t i2c_ports[] = {
|
||||
{"typec", 0, 400, GPIO_I2C1_SCL, GPIO_I2C1_SDA},
|
||||
{"other", 1, 400, GPIO_I2C2_SCL, GPIO_I2C2_SDA},
|
||||
{"other", 1, 400, GPIO_I2C2_SCL, GPIO_I2C2_SDA,
|
||||
.flags = I2C_PORT_FLAG_DYNAMIC_SPEED},
|
||||
};
|
||||
const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports);
|
||||
|
||||
|
@ -271,12 +271,8 @@ DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT);
|
|||
*/
|
||||
static void board_i2c_init(void)
|
||||
{
|
||||
if (board_get_version() < 2) {
|
||||
const struct i2c_port_t i2c_port = {
|
||||
"other", 1, 100, GPIO_I2C2_SCL, GPIO_I2C2_SDA,
|
||||
};
|
||||
stm32f0_i2c_init_port(&i2c_port);
|
||||
}
|
||||
if (board_get_version() < 2)
|
||||
i2c_set_freq(1, I2C_FREQ_100KHZ);
|
||||
}
|
||||
DECLARE_HOOK(HOOK_INIT, board_i2c_init, HOOK_PRIO_INIT_I2C);
|
||||
|
||||
|
|
|
@ -8,8 +8,9 @@
|
|||
|
||||
CORE:=host
|
||||
|
||||
chip-y=system.o gpio.o uart.o persistence.o flash.o lpc.o reboot.o i2c.o \
|
||||
chip-y=system.o gpio.o uart.o persistence.o flash.o lpc.o reboot.o \
|
||||
clock.o spi_master.o trng.o
|
||||
|
||||
ifndef CONFIG_KEYBOARD_NOT_RAW
|
||||
chip-$(HAS_TASK_KEYSCAN)+=keyboard_raw.o
|
||||
endif
|
||||
|
@ -21,6 +22,8 @@ dirs-y += chip/g/dcrypto
|
|||
endif
|
||||
dirs-y += chip/host/dcrypto
|
||||
|
||||
chip-$(CONFIG_I2C)+= i2c.o
|
||||
|
||||
chip-$(CONFIG_DCRYPTO)+= dcrypto/aes.o
|
||||
chip-$(CONFIG_DCRYPTO)+= dcrypto/app_cipher.o
|
||||
chip-$(CONFIG_DCRYPTO)+= dcrypto/app_key.o
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "hooks.h"
|
||||
#include "i2c.h"
|
||||
#include "i2c_private.h"
|
||||
#include "link_defs.h"
|
||||
#include "test_util.h"
|
||||
|
||||
|
@ -95,6 +96,26 @@ int chip_i2c_xfer(const int port, const uint16_t slave_addr_flags,
|
|||
return EC_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
int chip_i2c_set_freq(int port, enum i2c_freq freq)
|
||||
{
|
||||
return EC_ERROR_UNIMPLEMENTED;
|
||||
}
|
||||
|
||||
enum i2c_freq chip_i2c_get_freq(int port)
|
||||
{
|
||||
switch (i2c_ports[port].kbps) {
|
||||
case 1000:
|
||||
return I2C_FREQ_1000KHZ;
|
||||
case 400:
|
||||
return I2C_FREQ_400KHZ;
|
||||
case 100:
|
||||
return I2C_FREQ_100KHZ;
|
||||
}
|
||||
|
||||
/* fallback to 100k */
|
||||
return I2C_FREQ_100KHZ;
|
||||
}
|
||||
|
||||
int i2c_raw_get_scl(int port)
|
||||
{
|
||||
return 1;
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include "host_command.h"
|
||||
#include "hwtimer.h"
|
||||
#include "i2c.h"
|
||||
#include "i2c-stm32f0.h"
|
||||
#include "i2c_private.h"
|
||||
#include "registers.h"
|
||||
#include "system.h"
|
||||
#include "task.h"
|
||||
|
@ -113,22 +113,39 @@ static const uint32_t timingr_regs[I2C_CLK_SRC_COUNT][I2C_FREQ_COUNT] = {
|
|||
},
|
||||
};
|
||||
|
||||
static void i2c_set_freq_port(const struct i2c_port_t *p,
|
||||
enum stm32_i2c_clk_src src,
|
||||
enum i2c_freq freq)
|
||||
int chip_i2c_set_freq(int port, enum i2c_freq freq)
|
||||
{
|
||||
int port = p->port;
|
||||
const uint32_t *regs = timingr_regs[src];
|
||||
enum stm32_i2c_clk_src src = I2C_CLK_SRC_48MHZ;
|
||||
|
||||
#if defined(CONFIG_HOSTCMD_I2C_SLAVE_ADDR_FLAGS) && \
|
||||
defined(CONFIG_LOW_POWER_IDLE) && \
|
||||
(I2C_PORT_EC == STM32_I2C1_PORT)
|
||||
if (port == STM32_I2C1_PORT) {
|
||||
/*
|
||||
* Use HSI (8MHz) for i2c clock. This allows smooth wakeup
|
||||
* from STOP mode since HSI is only clock running immediately
|
||||
* upon exit from STOP mode.
|
||||
*/
|
||||
src = I2C_CLK_SRC_8MHZ;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Disable port */
|
||||
STM32_I2C_CR1(port) = 0;
|
||||
STM32_I2C_CR2(port) = 0;
|
||||
/* Set clock frequency */
|
||||
STM32_I2C_TIMINGR(port) = regs[freq];
|
||||
STM32_I2C_TIMINGR(port) = timingr_regs[src][freq];
|
||||
/* Enable port */
|
||||
STM32_I2C_CR1(port) = STM32_I2C_CR1_PE;
|
||||
|
||||
pdata[port].freq = freq;
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
enum i2c_freq chip_i2c_get_freq(int port)
|
||||
{
|
||||
return pdata[port].freq;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -136,10 +153,10 @@ static void i2c_set_freq_port(const struct i2c_port_t *p,
|
|||
*
|
||||
* @param p the I2c port
|
||||
*/
|
||||
void stm32f0_i2c_init_port(const struct i2c_port_t *p)
|
||||
static int i2c_init_port(const struct i2c_port_t *p)
|
||||
{
|
||||
int port = p->port;
|
||||
enum stm32_i2c_clk_src src = I2C_CLK_SRC_48MHZ;
|
||||
int ret = EC_SUCCESS;
|
||||
enum i2c_freq freq;
|
||||
|
||||
/* Enable clocks to I2C modules if necessary */
|
||||
|
@ -148,15 +165,14 @@ void stm32f0_i2c_init_port(const struct i2c_port_t *p)
|
|||
|
||||
if (port == STM32_I2C1_PORT) {
|
||||
#if defined(CONFIG_HOSTCMD_I2C_SLAVE_ADDR_FLAGS) && \
|
||||
defined(CONFIG_LOW_POWER_IDLE) && \
|
||||
(I2C_PORT_EC == STM32_I2C1_PORT)
|
||||
defined(CONFIG_LOW_POWER_IDLE) && \
|
||||
(I2C_PORT_EC == STM32_I2C1_PORT)
|
||||
/*
|
||||
* Use HSI (8MHz) for i2c clock. This allows smooth wakeup
|
||||
* from STOP mode since HSI is only clock running immediately
|
||||
* upon exit from STOP mode.
|
||||
*/
|
||||
STM32_RCC_CFGR3 &= ~0x10;
|
||||
src = I2C_CLK_SRC_8MHZ;
|
||||
#else
|
||||
/* Use SYSCLK for i2c clock. */
|
||||
STM32_RCC_CFGR3 |= 0x10;
|
||||
|
@ -180,13 +196,16 @@ defined(CONFIG_LOW_POWER_IDLE) && \
|
|||
default: /* unknown speed, defaults to 100kBps */
|
||||
CPRINTS("I2C bad speed %d kBps", p->kbps);
|
||||
freq = I2C_FREQ_100KHZ;
|
||||
ret = EC_ERROR_INVAL;
|
||||
}
|
||||
|
||||
/* Set up initial bus frequencies */
|
||||
i2c_set_freq_port(p, src, freq);
|
||||
chip_i2c_set_freq(p->port, freq);
|
||||
|
||||
/* Set up default timeout */
|
||||
i2c_set_timeout(port, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@ -604,7 +623,7 @@ void i2c_init(void)
|
|||
int i;
|
||||
|
||||
for (i = 0; i < i2c_ports_used; i++, p++)
|
||||
stm32f0_i2c_init_port(p);
|
||||
i2c_init_port(p);
|
||||
|
||||
#ifdef CONFIG_HOSTCMD_I2C_SLAVE_ADDR_FLAGS
|
||||
STM32_I2C_CR1(I2C_PORT_EC) |= STM32_I2C_CR1_RXIE | STM32_I2C_CR1_ERRIE
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
/* Copyright 2019 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.
|
||||
*/
|
||||
#ifndef __CROS_EC_I2C_STM32F0_H
|
||||
#define __CROS_EC_I2C_STM32F0_H
|
||||
|
||||
/**
|
||||
* Initialize on the specified I2C port.
|
||||
*
|
||||
* @param p the I2c port
|
||||
*/
|
||||
void stm32f0_i2c_init_port(const struct i2c_port_t *p);
|
||||
|
||||
#endif
|
|
@ -14,6 +14,7 @@
|
|||
#include "gpio.h"
|
||||
#include "i2c.h"
|
||||
#include "i2c_bitbang.h"
|
||||
#include "i2c_private.h"
|
||||
#include "system.h"
|
||||
#include "task.h"
|
||||
#include "usb_pd.h"
|
||||
|
@ -973,6 +974,24 @@ unwedge_done:
|
|||
return ret;
|
||||
}
|
||||
|
||||
int i2c_set_freq(int port, enum i2c_freq freq)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!(get_i2c_port(port)->flags & I2C_PORT_FLAG_DYNAMIC_SPEED))
|
||||
return EC_ERROR_INVAL;
|
||||
|
||||
i2c_lock(port, 1);
|
||||
ret = chip_i2c_set_freq(port, freq);
|
||||
i2c_lock(port, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum i2c_freq i2c_get_freq(int port)
|
||||
{
|
||||
return chip_i2c_get_freq(port);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Host commands */
|
||||
|
||||
|
|
|
@ -66,6 +66,9 @@
|
|||
/* The size of the header for a version 3 response packet sent over I2C. */
|
||||
#define I2C_RESPONSE_HEADER_SIZE 2
|
||||
|
||||
/* This port allows changing speed at runtime */
|
||||
#define I2C_PORT_FLAG_DYNAMIC_SPEED BIT(0)
|
||||
|
||||
/*
|
||||
* Supported I2C CLK frequencies.
|
||||
* TODO(crbug.com/549286): Use this enum in i2c_port_t.
|
||||
|
@ -113,6 +116,7 @@ struct i2c_port_t {
|
|||
int (*passthru_allowed)(const struct i2c_port_t *port,
|
||||
uint16_t addr_flags);
|
||||
const struct i2c_drv *drv;
|
||||
uint16_t flags; /* I2C_PORT_FLAG_* flags */
|
||||
};
|
||||
|
||||
extern const struct i2c_port_t i2c_ports[];
|
||||
|
@ -195,27 +199,6 @@ int i2c_xfer_unlocked(const int port,
|
|||
#define I2C_LINE_SDA_HIGH BIT(1)
|
||||
#define I2C_LINE_IDLE (I2C_LINE_SCL_HIGH | I2C_LINE_SDA_HIGH)
|
||||
|
||||
/**
|
||||
* Chip-level function to transmit one block of raw data, then receive one
|
||||
* block of raw data.
|
||||
*
|
||||
* This is a low-level chip-dependent function and should only be called by
|
||||
* i2c_xfer().
|
||||
*
|
||||
* @param port Port to access
|
||||
* @param slave_addr Slave device address
|
||||
* @param out Data to send
|
||||
* @param out_size Number of bytes to send
|
||||
* @param in Destination buffer for received data
|
||||
* @param in_size Number of bytes to receive
|
||||
* @param flags Flags (see I2C_XFER_* above)
|
||||
* @return EC_SUCCESS, or non-zero if error.
|
||||
*/
|
||||
int chip_i2c_xfer(const int port,
|
||||
const uint16_t slave_addr_flags,
|
||||
const uint8_t *out, int out_size,
|
||||
uint8_t *in, int in_size, int flags);
|
||||
|
||||
/**
|
||||
* Return raw I/O line levels (I2C_LINE_*) for a port when port is in alternate
|
||||
* function mode.
|
||||
|
@ -580,4 +563,24 @@ void i2c_end_xfer_notify(const int port,
|
|||
void i2c_trace_notify(int port, uint16_t slave_addr_flags,
|
||||
int direction, const uint8_t *data, size_t size);
|
||||
|
||||
/**
|
||||
* Set bus speed. Only support for ports with I2C_PORT_FLAG_DYNAMIC_SPEED
|
||||
* flag.
|
||||
*
|
||||
* @param port: Port to access
|
||||
* @param freq: Bus speed.
|
||||
*
|
||||
* @return EC_SUCCESS, or non-zero if error.
|
||||
*/
|
||||
int i2c_set_freq(int port, enum i2c_freq freq);
|
||||
|
||||
/**
|
||||
* Chip level function to get bus speed.
|
||||
*
|
||||
* @param port: Port to access
|
||||
*
|
||||
* @return Bus speed
|
||||
*/
|
||||
enum i2c_freq i2c_get_freq(int port);
|
||||
|
||||
#endif /* __CROS_EC_I2C_H */
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Private chipset-specific implementations that only accessible by
|
||||
* i2c_master.c. Don't include this directly unless you are implementing
|
||||
* these functions.
|
||||
*/
|
||||
#ifndef __CROS_EC_I2C_PRIVATE_H
|
||||
#define __CROS_EC_I2C_PRIVATE_H
|
||||
|
||||
#include "i2c.h"
|
||||
|
||||
/**
|
||||
* Chip-level function to transmit one block of raw data, then receive one
|
||||
* block of raw data.
|
||||
*
|
||||
* This is a low-level chip-dependent function and should only be called by
|
||||
* i2c_xfer().
|
||||
*
|
||||
* @param port Port to access
|
||||
* @param slave_addr Slave device address
|
||||
* @param out Data to send
|
||||
* @param out_size Number of bytes to send
|
||||
* @param in Destination buffer for received data
|
||||
* @param in_size Number of bytes to receive
|
||||
* @param flags Flags (see I2C_XFER_* above)
|
||||
* @return EC_SUCCESS, or non-zero if error.
|
||||
*/
|
||||
int chip_i2c_xfer(const int port,
|
||||
const uint16_t slave_addr_flags,
|
||||
const uint8_t *out, int out_size,
|
||||
uint8_t *in, int in_size, int flags);
|
||||
|
||||
/**
|
||||
* Chip level function to set bus speed.
|
||||
*
|
||||
* @param port: Port to access
|
||||
* @param kbps: Bus speed in kbps.
|
||||
*
|
||||
* @return EC_SUCCESS, or non-zero if error.
|
||||
*/
|
||||
int chip_i2c_set_freq(int port, enum i2c_freq freq);
|
||||
|
||||
/**
|
||||
* Chip level function to get bus speed.
|
||||
*
|
||||
* @param port: Port to access
|
||||
*
|
||||
* @return Bus speed
|
||||
*/
|
||||
enum i2c_freq chip_i2c_get_freq(int port);
|
||||
|
||||
#endif /* __CROS_EC_I2C_PRIVATE_H */
|
Loading…
Reference in New Issue