i2c updates for v2024.01-rc2

- nuvoton: support standard/fast/fast plus mode
- bootcount: remove legacy i2c driver and implement
  DM based version

Bugfixes:
- designware_i2c: adjust timing calculation
  SPL probing failed on the StarFive VisionFive 2 board
  Heinrich fixed this, by syncing timing calculation with
  linux implementation.
This commit is contained in:
Tom Rini 2023-11-02 10:12:33 -04:00
commit 07fe79c93c
8 changed files with 218 additions and 74 deletions

View file

@ -79,14 +79,6 @@ config BOOTCOUNT_RAM
Store the bootcount in DRAM protected against bit errors Store the bootcount in DRAM protected against bit errors
due to short power loss or holding a system in RESET. due to short power loss or holding a system in RESET.
config BOOTCOUNT_I2C
bool "Boot counter on I2C device"
help
Enable support for the bootcounter on an i2c (like RTC) device.
CFG_SYS_I2C_RTC_ADDR = i2c chip address
CONFIG_SYS_BOOTCOUNT_ADDR = i2c addr which is used for
the bootcounter.
config BOOTCOUNT_AT91 config BOOTCOUNT_AT91
bool "Boot counter for Atmel AT91SAM9XE" bool "Boot counter for Atmel AT91SAM9XE"
depends on AT91SAM9XE depends on AT91SAM9XE
@ -117,6 +109,16 @@ config DM_BOOTCOUNT_RTC
Accesses to the backing store are performed using the write16 Accesses to the backing store are performed using the write16
and read16 ops of DM RTC devices. and read16 ops of DM RTC devices.
config DM_BOOTCOUNT_I2C
bool "Driver Model boot counter on I2C device"
depends on DM_I2C
help
Enable support for the bootcounter on a generic i2c device, like a RTC
or PMIC. The bootcounter is configured in the device tree using the
"u-boot,bootcount-i2c" compatible string. It requires a phandle
'i2cbcdev' for the i2c device and an 'offset' property used within the
device.
config DM_BOOTCOUNT_I2C_EEPROM config DM_BOOTCOUNT_I2C_EEPROM
bool "Support i2c eeprom devices as a backing store for bootcount" bool "Support i2c eeprom devices as a backing store for bootcount"
depends on I2C_EEPROM depends on I2C_EEPROM
@ -175,14 +177,6 @@ config BOOTCOUNT_BOOTLIMIT
counter being cleared. counter being cleared.
If set to 0, do not set a boot limit in the environment. If set to 0, do not set a boot limit in the environment.
config BOOTCOUNT_ALEN
int "I2C address length"
default 1
depends on BOOTCOUNT_I2C
help
Length of the the I2C address at SYS_BOOTCOUNT_ADDR for storing
the boot counter.
config SYS_BOOTCOUNT_SINGLEWORD config SYS_BOOTCOUNT_SINGLEWORD
bool "Use single word to pack boot count and magic value" bool "Use single word to pack boot count and magic value"
depends on BOOTCOUNT_GENERIC depends on BOOTCOUNT_GENERIC
@ -218,7 +212,7 @@ config SYS_BOOTCOUNT_ADDR
default 0x44E3E000 if BOOTCOUNT_AM33XX || BOOTCOUNT_AM33XX_NVMEM default 0x44E3E000 if BOOTCOUNT_AM33XX || BOOTCOUNT_AM33XX_NVMEM
default 0xE0115FF8 if ARCH_LS1043A || ARCH_LS1021A default 0xE0115FF8 if ARCH_LS1043A || ARCH_LS1021A
depends on BOOTCOUNT_AM33XX || BOOTCOUNT_GENERIC || BOOTCOUNT_EXT || \ depends on BOOTCOUNT_AM33XX || BOOTCOUNT_GENERIC || BOOTCOUNT_EXT || \
BOOTCOUNT_I2C || BOOTCOUNT_AM33XX_NVMEM BOOTCOUNT_AM33XX_NVMEM
help help
Set the address used for reading and writing the boot counter. Set the address used for reading and writing the boot counter.
@ -226,13 +220,11 @@ config SYS_BOOTCOUNT_MAGIC
hex "Magic value for the boot counter" hex "Magic value for the boot counter"
default 0xB001C041 if BOOTCOUNT_GENERIC || BOOTCOUNT_EXT || \ default 0xB001C041 if BOOTCOUNT_GENERIC || BOOTCOUNT_EXT || \
BOOTCOUNT_AM33XX || BOOTCOUNT_ENV || \ BOOTCOUNT_AM33XX || BOOTCOUNT_ENV || \
BOOTCOUNT_RAM || BOOTCOUNT_I2C || \ BOOTCOUNT_RAM || BOOTCOUNT_AT91 || DM_BOOTCOUNT
BOOTCOUNT_AT91 || DM_BOOTCOUNT
default 0xB0 if BOOTCOUNT_AM33XX_NVMEM default 0xB0 if BOOTCOUNT_AM33XX_NVMEM
depends on BOOTCOUNT_GENERIC || BOOTCOUNT_EXT || \ depends on BOOTCOUNT_GENERIC || BOOTCOUNT_EXT || \
BOOTCOUNT_AM33XX || BOOTCOUNT_ENV || \ BOOTCOUNT_AM33XX || BOOTCOUNT_ENV || \
BOOTCOUNT_RAM || BOOTCOUNT_I2C || \ BOOTCOUNT_RAM || BOOTCOUNT_AT91 || DM_BOOTCOUNT || \
BOOTCOUNT_AT91 || DM_BOOTCOUNT || \
BOOTCOUNT_AM33XX_NVMEM BOOTCOUNT_AM33XX_NVMEM
help help
Set the magic value used for the boot counter. Set the magic value used for the boot counter.

View file

@ -6,7 +6,6 @@ obj-$(CONFIG_BOOTCOUNT_AT91) += bootcount_at91.o
obj-$(CONFIG_BOOTCOUNT_AM33XX) += bootcount_davinci.o obj-$(CONFIG_BOOTCOUNT_AM33XX) += bootcount_davinci.o
obj-$(CONFIG_BOOTCOUNT_RAM) += bootcount_ram.o obj-$(CONFIG_BOOTCOUNT_RAM) += bootcount_ram.o
obj-$(CONFIG_BOOTCOUNT_ENV) += bootcount_env.o obj-$(CONFIG_BOOTCOUNT_ENV) += bootcount_env.o
obj-$(CONFIG_BOOTCOUNT_I2C) += bootcount_i2c.o
obj-$(CONFIG_BOOTCOUNT_EXT) += bootcount_ext.o obj-$(CONFIG_BOOTCOUNT_EXT) += bootcount_ext.o
obj-$(CONFIG_BOOTCOUNT_AM33XX_NVMEM) += bootcount_nvmem.o obj-$(CONFIG_BOOTCOUNT_AM33XX_NVMEM) += bootcount_nvmem.o
@ -14,5 +13,6 @@ obj-$(CONFIG_DM_BOOTCOUNT) += bootcount-uclass.o
obj-$(CONFIG_DM_BOOTCOUNT_PMIC_PFUZE100) += pmic_pfuze100.o obj-$(CONFIG_DM_BOOTCOUNT_PMIC_PFUZE100) += pmic_pfuze100.o
obj-$(CONFIG_DM_BOOTCOUNT_RTC) += rtc.o obj-$(CONFIG_DM_BOOTCOUNT_RTC) += rtc.o
obj-$(CONFIG_DM_BOOTCOUNT_I2C_EEPROM) += i2c-eeprom.o obj-$(CONFIG_DM_BOOTCOUNT_I2C_EEPROM) += i2c-eeprom.o
obj-$(CONFIG_DM_BOOTCOUNT_I2C) += bootcount_dm_i2c.o
obj-$(CONFIG_DM_BOOTCOUNT_SPI_FLASH) += spi-flash.o obj-$(CONFIG_DM_BOOTCOUNT_SPI_FLASH) += spi-flash.o
obj-$(CONFIG_DM_BOOTCOUNT_SYSCON) += bootcount_syscon.o obj-$(CONFIG_DM_BOOTCOUNT_SYSCON) += bootcount_syscon.o

View file

@ -0,0 +1,102 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2023
* Philip Richard Oberfichtner <pro@denx.de>
*
* Based on previous work from Heiko Schocher (legacy bootcount_i2c.c driver)
*/
#include <bootcount.h>
#include <dm.h>
#include <i2c.h>
#define BC_MAGIC 0x55
struct bootcount_i2c_priv {
struct udevice *bcdev;
unsigned int offset;
};
static int bootcount_i2c_set(struct udevice *dev, const u32 val)
{
int ret;
struct bootcount_i2c_priv *priv = dev_get_priv(dev);
ret = dm_i2c_reg_write(priv->bcdev, priv->offset, BC_MAGIC);
if (ret < 0)
goto err_exit;
ret = dm_i2c_reg_write(priv->bcdev, priv->offset + 1, val & 0xff);
if (ret < 0)
goto err_exit;
return 0;
err_exit:
log_debug("%s: Error writing to I2C device (%d)\n", __func__, ret);
return ret;
}
static int bootcount_i2c_get(struct udevice *dev, u32 *val)
{
int ret;
struct bootcount_i2c_priv *priv = dev_get_priv(dev);
ret = dm_i2c_reg_read(priv->bcdev, priv->offset);
if (ret < 0)
goto err_exit;
if ((ret & 0xff) != BC_MAGIC) {
log_debug("%s: Invalid Magic, reset bootcounter.\n", __func__);
*val = 0;
return bootcount_i2c_set(dev, 0);
}
ret = dm_i2c_reg_read(priv->bcdev, priv->offset + 1);
if (ret < 0)
goto err_exit;
*val = ret;
return 0;
err_exit:
log_debug("%s: Error reading from I2C device (%d)\n", __func__, ret);
return ret;
}
static int bootcount_i2c_probe(struct udevice *dev)
{
struct bootcount_i2c_priv *priv = dev_get_priv(dev);
int ret;
ret = dev_read_u32(dev, "offset", &priv->offset);
if (ret)
goto exit;
ret = i2c_get_chip_by_phandle(dev, "i2cbcdev", &priv->bcdev);
exit:
if (ret)
log_debug("%s failed, ret = %d\n", __func__, ret);
return ret;
}
static const struct bootcount_ops bootcount_i2c_ops = {
.get = bootcount_i2c_get,
.set = bootcount_i2c_set,
};
static const struct udevice_id bootcount_i2c_ids[] = {
{ .compatible = "u-boot,bootcount-i2c" },
{ }
};
U_BOOT_DRIVER(bootcount_i2c) = {
.name = "bootcount-i2c",
.id = UCLASS_BOOTCOUNT,
.priv_auto = sizeof(struct bootcount_i2c_priv),
.probe = bootcount_i2c_probe,
.of_match = bootcount_i2c_ids,
.ops = &bootcount_i2c_ops,
};

View file

@ -1,43 +0,0 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2013
* Heiko Schocher, DENX Software Engineering, hs@denx.de.
*/
#include <bootcount.h>
#include <linux/compiler.h>
#include <i2c.h>
#define BC_MAGIC 0xbc
void bootcount_store(ulong a)
{
unsigned char buf[3];
int ret;
buf[0] = BC_MAGIC;
buf[1] = (a & 0xff);
ret = i2c_write(CFG_SYS_I2C_RTC_ADDR, CONFIG_SYS_BOOTCOUNT_ADDR,
CONFIG_BOOTCOUNT_ALEN, buf, 2);
if (ret != 0)
puts("Error writing bootcount\n");
}
ulong bootcount_load(void)
{
unsigned char buf[3];
int ret;
ret = i2c_read(CFG_SYS_I2C_RTC_ADDR, CONFIG_SYS_BOOTCOUNT_ADDR,
CONFIG_BOOTCOUNT_ALEN, buf, 2);
if (ret != 0) {
puts("Error loading bootcount\n");
return 0;
}
if (buf[0] == BC_MAGIC)
return buf[1];
bootcount_store(0);
return 0;
}

View file

@ -24,6 +24,17 @@
*/ */
#define DW_I2C_COMP_TYPE 0x44570140 #define DW_I2C_COMP_TYPE 0x44570140
/*
* This constant is used to calculate when during the clock high phase the data
* bit shall be read. The value was copied from the Linux v6.5 function
* i2c_dw_scl_hcnt() which provides the following explanation:
*
* "This is just an experimental rule: the tHD;STA period turned out to be
* proportinal to (_HCNT + 3). With this setting, we could meet both tHIGH and
* tHD;STA timing specs."
*/
#define T_HD_STA_OFFSET 3
static int dw_i2c_enable(struct i2c_regs *i2c_base, bool enable) static int dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
{ {
u32 ena = enable ? IC_ENABLE_0B : 0; u32 ena = enable ? IC_ENABLE_0B : 0;
@ -155,10 +166,10 @@ static int dw_i2c_calc_timing(struct dw_i2c *priv, enum i2c_speed_mode mode,
/* /*
* Back-solve for hcnt and lcnt according to the following equations: * Back-solve for hcnt and lcnt according to the following equations:
* SCL_High_time = [(HCNT + IC_*_SPKLEN + 7) * ic_clk] + SCL_Fall_time * SCL_High_time = [(HCNT + IC_*_SPKLEN + T_HD_STA_OFFSET) * ic_clk] + SCL_Fall_time
* SCL_Low_time = [(LCNT + 1) * ic_clk] - SCL_Fall_time + SCL_Rise_time * SCL_Low_time = [(LCNT + 1) * ic_clk] - SCL_Fall_time + SCL_Rise_time
*/ */
hcnt = min_thigh_cnt - fall_cnt - 7 - spk_cnt; hcnt = min_thigh_cnt - fall_cnt - T_HD_STA_OFFSET - spk_cnt;
lcnt = min_tlow_cnt - rise_cnt + fall_cnt - 1; lcnt = min_tlow_cnt - rise_cnt + fall_cnt - 1;
if (hcnt < 0 || lcnt < 0) { if (hcnt < 0 || lcnt < 0) {
@ -170,13 +181,13 @@ static int dw_i2c_calc_timing(struct dw_i2c *priv, enum i2c_speed_mode mode,
* Now add things back up to ensure the period is hit. If it is off, * Now add things back up to ensure the period is hit. If it is off,
* split the difference and bias to lcnt for remainder * split the difference and bias to lcnt for remainder
*/ */
tot = hcnt + lcnt + 7 + spk_cnt + rise_cnt + 1; tot = hcnt + lcnt + T_HD_STA_OFFSET + spk_cnt + rise_cnt + 1;
if (tot < period_cnt) { if (tot < period_cnt) {
diff = (period_cnt - tot) / 2; diff = (period_cnt - tot) / 2;
hcnt += diff; hcnt += diff;
lcnt += diff; lcnt += diff;
tot = hcnt + lcnt + 7 + spk_cnt + rise_cnt + 1; tot = hcnt + lcnt + T_HD_STA_OFFSET + spk_cnt + rise_cnt + 1;
lcnt += period_cnt - tot; lcnt += period_cnt - tot;
} }

View file

@ -388,6 +388,81 @@ int i2c_get_chip_for_busnum(int busnum, int chip_addr, uint offset_len,
return 0; return 0;
} }
/* Find and probe I2C bus based on a chip attached to it */
static int i2c_get_parent_bus(ofnode chip, struct udevice **devp)
{
ofnode node;
struct udevice *dev;
int ret;
node = ofnode_get_parent(chip);
if (!ofnode_valid(node))
return -ENODEV;
ret = uclass_get_device_by_ofnode(UCLASS_I2C, node, &dev);
if (ret) {
*devp = NULL;
return ret;
}
*devp = dev;
return 0;
}
int i2c_get_chip_by_phandle(const struct udevice *parent, const char *prop_name,
struct udevice **devp)
{
ofnode node;
uint phandle;
struct udevice *bus, *chip;
char *dev_name;
int ret;
debug("%s: Searching I2C chip for phandle \"%s\"\n",
__func__, prop_name);
dev_name = strdup(prop_name);
if (!dev_name) {
ret = -ENOMEM;
goto err_exit;
}
ret = dev_read_u32(parent, "i2cbcdev", &phandle);
if (ret)
goto err_exit;
node = ofnode_get_by_phandle(phandle);
if (!ofnode_valid(node)) {
ret = -ENODEV;
goto err_exit;
}
ret = i2c_get_parent_bus(node, &bus);
if (ret)
goto err_exit;
ret = device_bind_driver_to_node(bus, "i2c_generic_chip_drv",
dev_name, node, &chip);
if (ret)
goto err_exit;
ret = device_probe(chip);
if (ret) {
device_unbind(chip);
goto err_exit;
}
debug("%s succeeded\n", __func__);
*devp = chip;
return 0;
err_exit:
free(dev_name);
debug("%s failed, ret = %d\n", __func__, ret);
*devp = NULL;
return ret;
}
int dm_i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags, int dm_i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags,
struct udevice **devp) struct udevice **devp)
{ {

View file

@ -517,11 +517,6 @@ static int npcm_i2c_init_clk(struct npcm_i2c_bus *bus, u32 bus_freq)
u32 sclfrq; u32 sclfrq;
u8 hldt, val; u8 hldt, val;
if (bus_freq > I2C_FREQ_100K) {
printf("Support standard mode only\n");
return -EINVAL;
}
/* SCLFRQ = T(SCL)/4/T(CLK) = FREQ(CLK)/4/FREQ(SCL) */ /* SCLFRQ = T(SCL)/4/T(CLK) = FREQ(CLK)/4/FREQ(SCL) */
sclfrq = freq / (bus_freq * 4); sclfrq = freq / (bus_freq * 4);
if (sclfrq < SCLFRQ_MIN || sclfrq > SCLFRQ_MAX) if (sclfrq < SCLFRQ_MIN || sclfrq > SCLFRQ_MAX)

View file

@ -537,6 +537,18 @@ int i2c_get_chip(struct udevice *bus, uint chip_addr, uint offset_len,
int i2c_get_chip_for_busnum(int busnum, int chip_addr, uint offset_len, int i2c_get_chip_for_busnum(int busnum, int chip_addr, uint offset_len,
struct udevice **devp); struct udevice **devp);
/**
* i2c_get_chip_by_phandle() - get a device to use to access a chip
* based on a phandle property pointing to it
*
* @parent: Parent device containing the phandle pointer
* @name: Name of phandle property in the parent device node
* @devp: Returns pointer to new device or NULL if not found
* Return: 0 on success, -ve on failure
*/
int i2c_get_chip_by_phandle(const struct udevice *parent, const char *prop_name,
struct udevice **devp);
/** /**
* i2c_chip_of_to_plat() - Decode standard I2C platform data * i2c_chip_of_to_plat() - Decode standard I2C platform data
* *