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
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
bool "Boot counter for Atmel AT91SAM9XE"
depends on AT91SAM9XE
@ -117,6 +109,16 @@ config DM_BOOTCOUNT_RTC
Accesses to the backing store are performed using the write16
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
bool "Support i2c eeprom devices as a backing store for bootcount"
depends on I2C_EEPROM
@ -175,14 +177,6 @@ config BOOTCOUNT_BOOTLIMIT
counter being cleared.
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
bool "Use single word to pack boot count and magic value"
depends on BOOTCOUNT_GENERIC
@ -218,7 +212,7 @@ config SYS_BOOTCOUNT_ADDR
default 0x44E3E000 if BOOTCOUNT_AM33XX || BOOTCOUNT_AM33XX_NVMEM
default 0xE0115FF8 if ARCH_LS1043A || ARCH_LS1021A
depends on BOOTCOUNT_AM33XX || BOOTCOUNT_GENERIC || BOOTCOUNT_EXT || \
BOOTCOUNT_I2C || BOOTCOUNT_AM33XX_NVMEM
BOOTCOUNT_AM33XX_NVMEM
help
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"
default 0xB001C041 if BOOTCOUNT_GENERIC || BOOTCOUNT_EXT || \
BOOTCOUNT_AM33XX || BOOTCOUNT_ENV || \
BOOTCOUNT_RAM || BOOTCOUNT_I2C || \
BOOTCOUNT_AT91 || DM_BOOTCOUNT
BOOTCOUNT_RAM || BOOTCOUNT_AT91 || DM_BOOTCOUNT
default 0xB0 if BOOTCOUNT_AM33XX_NVMEM
depends on BOOTCOUNT_GENERIC || BOOTCOUNT_EXT || \
BOOTCOUNT_AM33XX || BOOTCOUNT_ENV || \
BOOTCOUNT_RAM || BOOTCOUNT_I2C || \
BOOTCOUNT_AT91 || DM_BOOTCOUNT || \
BOOTCOUNT_RAM || BOOTCOUNT_AT91 || DM_BOOTCOUNT || \
BOOTCOUNT_AM33XX_NVMEM
help
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_RAM) += bootcount_ram.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_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_RTC) += rtc.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_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
/*
* 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)
{
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:
* 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
*/
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;
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,
* 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) {
diff = (period_cnt - tot) / 2;
hcnt += 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;
}

View file

@ -388,6 +388,81 @@ int i2c_get_chip_for_busnum(int busnum, int chip_addr, uint offset_len,
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,
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;
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 = freq / (bus_freq * 4);
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,
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
*