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:
Ting Shen 2019-12-16 17:44:57 +08:00 committed by Commit Bot
parent 166c22d057
commit 45427c38df
8 changed files with 161 additions and 59 deletions

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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 */

View File

@ -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 */

56
include/i2c_private.h Normal file
View File

@ -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 */