mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-11 07:34:31 +00:00
Merge tag 'i2cfixes-for-v2024-01-rc2' of https://source.denx.de/u-boot/custodians/u-boot-i2c
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:
commit
07fe79c93c
8 changed files with 218 additions and 74 deletions
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
102
drivers/bootcount/bootcount_dm_i2c.c
Normal file
102
drivers/bootcount/bootcount_dm_i2c.c
Normal 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,
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue