mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-31 02:15:45 +01: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:
		| @@ -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 | ||||
|  * | ||||
|   | ||||
		Reference in New Issue
	
	Block a user