diff mbox series

lib: utils/i2c: update i2c interface

Message ID 20211229173909.282858-1-wxjstz@126.com
State Superseded
Headers show
Series lib: utils/i2c: update i2c interface | expand

Commit Message

Xiang W Dec. 29, 2021, 5:39 p.m. UTC
1. Remove i2c register related operations
2. Simplify the low-level interface
3. Add 10bit address support
4. Add combination operation
5. Update fdt_i2c_sifive.c
6. Update sifive_fu740.c

Signed-off-by: Xiang W <wxjstz@126.com>
---
 include/sbi_utils/i2c/i2c.h     |  80 ++++++++++----------
 lib/utils/i2c/fdt_i2c_sifive.c  | 130 +++++++++-----------------------
 lib/utils/i2c/i2c.c             | 123 ++++++++++++++++++++++++++----
 platform/generic/sifive_fu740.c |  31 ++++++--
 4 files changed, 208 insertions(+), 156 deletions(-)

Comments

Nikita Shubin Dec. 30, 2021, 9:29 a.m. UTC | #1
Hello Xiang!

Thank you for your patch.

On Thu, 30 Dec 2021 01:39:09 +0800
Xiang W <wxjstz@126.com> wrote:

> 1. Remove i2c register related operations
> 2. Simplify the low-level interface
> 3. Add 10bit address support
> 4. Add combination operation
> 5. Update fdt_i2c_sifive.c
> 6. Update sifive_fu740.c
> 

First of all my Unmatched board no longer reboots with your patch
applied:

[   53.068656] reboot: Restarting system
da9063_system_reset: chip is not da9063 PMIC

What kind of hardware are you using currently ?

In general i think raw read/write possibility is really needed, but
this kind of functionality can be achieved with flags without exposing
all this stuff in i2c.c, 

I was thinking on something like "struct i2c_msg" Linux have:

https://elixir.bootlin.com/linux/v5.16-rc7/source/include/uapi/linux/i2c.h#L73

We can still read/write a byte with this, ask for nostop and nack -
can't we ?

Please see my comments below:

> Signed-off-by: Xiang W <wxjstz@126.com>
> ---
>  include/sbi_utils/i2c/i2c.h     |  80 ++++++++++----------
>  lib/utils/i2c/fdt_i2c_sifive.c  | 130
> +++++++++----------------------- lib/utils/i2c/i2c.c             |
> 123 ++++++++++++++++++++++++++---- platform/generic/sifive_fu740.c |
> 31 ++++++-- 4 files changed, 208 insertions(+), 156 deletions(-)
> 
> diff --git a/include/sbi_utils/i2c/i2c.h b/include/sbi_utils/i2c/i2c.h
> index 5a70364..53e76e3 100644
> --- a/include/sbi_utils/i2c/i2c.h
> +++ b/include/sbi_utils/i2c/i2c.h
> @@ -13,6 +13,11 @@
>  #include <sbi/sbi_types.h>
>  #include <sbi/sbi_list.h>
>  
> +enum i2c_op {
> +	I2C_OP_W = 0,
> +	I2C_OP_R = 1
> +};
> +
>  /** Representation of a I2C adapter */
>  struct i2c_adapter {
>  	/** Pointer to I2C driver owning this I2C adapter */
> @@ -21,21 +26,11 @@ struct i2c_adapter {
>  	/** Unique ID of the I2C adapter assigned by the driver */
>  	int id;
>  
> -	/**
> -	 * Send buffer to given address, register
> -	 *
> -	 * @return 0 on success and negative error code on failure
> -	 */
> -	int (*write)(struct i2c_adapter *ia, uint8_t addr, uint8_t
> reg,
> -		    uint8_t *buffer, int len);
> -
> -	/**
> -	 * Read buffer from given address, register
> -	 *
> -	 * @return 0 on success and negative error code on failure
> -	 */
> -	int (*read)(struct i2c_adapter *ia, uint8_t addr, uint8_t
> reg,
> -		    uint8_t *buffer, int len);
> +	int (*write_byte)(struct i2c_adapter *ia, uint8_t byte,
> +			bool send_start, bool send_stop);
> +
> +	int (*read_byte)(struct i2c_adapter *ia, uint8_t *byte,
> +			bool nack, bool send_stop);
>  

Can't we use flags for bool nack, bool send_stop ?


>  	/** List */
>  	struct sbi_dlist node;
> @@ -46,6 +41,13 @@ static inline struct i2c_adapter
> *to_i2c_adapter(struct sbi_dlist *node) return container_of(node,
> struct i2c_adapter, node); }
>  
> +/**
> + * This function is used to pack a 10bit address into a 15-bit
> address.
> + * The 10bit address during read and write operations needs to be
> processed by
> + * this function.
> + * */
> +unsigned i2c_10bit_address(unsigned addr);
> +
>  /** Find a registered I2C adapter */
>  struct i2c_adapter *i2c_adapter_find(int id);
>  
> @@ -55,31 +57,27 @@ int i2c_adapter_add(struct i2c_adapter *ia);
>  /** Un-register I2C adapter */
>  void i2c_adapter_remove(struct i2c_adapter *ia);
>  
> -/** Send to device on I2C adapter bus */
> -int i2c_adapter_write(struct i2c_adapter *ia, uint8_t addr, uint8_t
> reg,
> -		      uint8_t *buffer, int len);
> -
> -/** Read from device on I2C adapter bus */
> -int i2c_adapter_read(struct i2c_adapter *ia, uint8_t addr, uint8_t
> reg,
> -		     uint8_t *buffer, int len);
> -
> -static inline int i2c_adapter_reg_write(struct i2c_adapter *ia,
> uint8_t addr,
> -				 uint8_t reg, uint8_t val)
> -{
> -	return i2c_adapter_write(ia, addr, reg, &val, 1);
> -}
> -
> -static inline int i2c_adapter_reg_read(struct i2c_adapter *ia,
> uint8_t addr,
> -				       uint8_t reg, uint8_t *val)
> -{
> -	uint8_t buf;
> -	int ret = i2c_adapter_read(ia, addr, reg, &buf, 1);
> -
> -	if (ret)
> -		return ret;
> -
> -	*val = buf;
> -	return 0;
> -}
> +/**
> + * Read data from the device with address addr to buff
> + * For 10bit address operation, addr = i2c_10bit_address (orig_addr)
> +*/
> +int i2c_adapter_read(struct i2c_adapter *ia, unsigned addr,
> +		uint8_t *buff, unsigned len);
> +
> +/**
> + * Write buff data to the device with address addr
> + * For 10bit address operation, addr = i2c_10bit_address (orig_addr)
> +*/
> +int i2c_adapter_write(struct i2c_adapter *ia, unsigned addr,
> +		uint8_t *buff, unsigned len);
> +
> +/**
> + * Combined operation, there is no stop signal between two iic
> operations
> + * For 10bit address operation, addr = i2c_10bit_address (orig_addr)
> + * op0 and op1 can only be I2C_OP_R (read operate) or I2C_OP_W
> (write operate)
> + */
> +int i2c_adapter_comb(struct i2c_adapter *ia, unsigned addr,
> +		enum i2c_op op0, uint8_t *buff0, unsigned len0,
> +		enum i2c_op op1, uint8_t *buff1, unsigned len1);
>  

Why should we need this, if you already exposed bool nack, bool
send_stop in read_byte, write_byte ?


>  #endif
> diff --git a/lib/utils/i2c/fdt_i2c_sifive.c
> b/lib/utils/i2c/fdt_i2c_sifive.c index 871241a..bb0456f 100644
> --- a/lib/utils/i2c/fdt_i2c_sifive.c
> +++ b/lib/utils/i2c/fdt_i2c_sifive.c
> @@ -113,127 +113,71 @@ static int sifive_i2c_adapter_start(struct
> sifive_i2c_adapter *adap, return sifive_i2c_adapter_poll_tip(adap);
>  }
>  
> -static int sifive_i2c_adapter_write(struct i2c_adapter *ia, uint8_t
> addr,
> -				    uint8_t reg, uint8_t *buffer,
> int len) +static int sifive_i2c_adapter_write_byte(struct i2c_adapter
> *ia,
> +				uint8_t byte, bool send_start, bool
> send_stop) {
>  	struct sifive_i2c_adapter *adap =
>  		container_of(ia, struct sifive_i2c_adapter, adapter);
> -	int rc = sifive_i2c_adapter_start(adap, addr,
> SIFIVE_I2C_WRITE_BIT); -
> -	if (rc)
> -		return rc;
> -
> -	rc = sifive_i2c_adapter_rxack(adap);
> -	if (rc)
> -		return rc;
> -
> -	/* set register address */
> -	sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, reg);
> -	sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR |
> -				SIFIVE_I2C_CMD_IACK);
> -	rc = sifive_i2c_adapter_poll_tip(adap);
> -	if (rc)
> -		return rc;
> -
> -	rc = sifive_i2c_adapter_rxack(adap);
> -	if (rc)
> -		return rc;
> -
> -	/* set value */
> -	while (len) {
> -		sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, *buffer);
> +	int rc;
> +	if (send_start) {
> +		rc = sifive_i2c_adapter_start(adap, byte >> 1, byte
> & 1);
> +		if (rc)
> +			return rc;
> +	} else {
> +		sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, byte);
>  		sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> SIFIVE_I2C_CMD_WR | SIFIVE_I2C_CMD_IACK);
>  
>  		rc = sifive_i2c_adapter_poll_tip(adap);
>  		if (rc)
>  			return rc;
> -
> -		rc = sifive_i2c_adapter_rxack(adap);
> -		if (rc)
> -			return rc;
> -
> -		buffer++;
> -		len--;
>  	}
> -
> -	sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_STO |
> -					       SIFIVE_I2C_CMD_IACK);
> -
> -	/* poll BUSY instead of ACK*/
> -	rc = sifive_i2c_adapter_poll_busy(adap);
> +	rc = sifive_i2c_adapter_rxack(adap);
>  	if (rc)
>  		return rc;
>  
> -	sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_IACK);
> +	if (send_stop) {
> +		sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> SIFIVE_I2C_CMD_STO |
> +					       SIFIVE_I2C_CMD_IACK);
> +		rc = sifive_i2c_adapter_poll_busy(adap);
> +		if (rc)
> +			return rc;
>  
> +		sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> SIFIVE_I2C_CMD_IACK);
> +	}
>  	return 0;
>  }
>  
> -static int sifive_i2c_adapter_read(struct i2c_adapter *ia, uint8_t
> addr,
> -				   uint8_t reg, uint8_t *buffer, int
> len) +static int sifive_i2c_adapter_read_byte(struct i2c_adapter *ia,
> +				uint8_t *byte, bool nack, bool
> send_stop) {
>  	struct sifive_i2c_adapter *adap =
>  		container_of(ia, struct sifive_i2c_adapter, adapter);
>  	int rc;
> -
> -	rc = sifive_i2c_adapter_start(adap, addr,
> SIFIVE_I2C_WRITE_BIT);
> -	if (rc)
> -		return rc;
> -
> -	rc = sifive_i2c_adapter_rxack(adap);
> -	if (rc)
> -		return rc;
> -
> -	sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, reg);
> -	sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR |
> -					       SIFIVE_I2C_CMD_IACK);
> -
> +	if (nack)
> +		sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> +					SIFIVE_I2C_CMD_ACK |
> +					SIFIVE_I2C_CMD_RD |
> +					SIFIVE_I2C_CMD_IACK);
> +	else
> +		sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> +					SIFIVE_I2C_CMD_RD |
> +					SIFIVE_I2C_CMD_IACK);
>  	rc = sifive_i2c_adapter_poll_tip(adap);
>  	if (rc)
>  		return rc;
>  
> -	rc = sifive_i2c_adapter_rxack(adap);
> -	if (rc)
> -		return rc;
> -
> -	/* setting addr with high 0 bit */
> -	rc = sifive_i2c_adapter_start(adap, addr,
> SIFIVE_I2C_READ_BIT);
> -	if (rc)
> -		return rc;
> -
> -	rc = sifive_i2c_adapter_rxack(adap);
> -	if (rc)
> -		return rc;
> +	*byte = sifive_i2c_getreg(adap, SIFIVE_I2C_RXR);
>  
> -	while (len) {
> -		if (len == 1)
> -			sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> -					  SIFIVE_I2C_CMD_ACK |
> -					  SIFIVE_I2C_CMD_RD |
> -					  SIFIVE_I2C_CMD_IACK);
> -		else
> -			sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> -					  SIFIVE_I2C_CMD_RD |
> -					  SIFIVE_I2C_CMD_IACK);
> -
> -		rc = sifive_i2c_adapter_poll_tip(adap);
> +	if (send_stop) {
> +		sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> SIFIVE_I2C_CMD_STO |
> +					       SIFIVE_I2C_CMD_IACK);
> +		rc = sifive_i2c_adapter_poll_busy(adap);
>  		if (rc)
>  			return rc;
>  
> -		*buffer = sifive_i2c_getreg(adap, SIFIVE_I2C_RXR);
> -		buffer++;
> -		len--;
> +		sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> SIFIVE_I2C_CMD_IACK); }
> -
> -	sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_STO |
> -					       SIFIVE_I2C_CMD_IACK);
> -	rc = sifive_i2c_adapter_poll_busy(adap);
> -	if (rc)
> -		return rc;
> -
> -	sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_IACK);
> -
>  	return 0;
>  }
>  
> @@ -256,8 +200,8 @@ static int sifive_i2c_init(void *fdt, int nodeoff,
>  	adapter->addr = addr;
>  	adapter->adapter.driver = &fdt_i2c_adapter_sifive;
>  	adapter->adapter.id = nodeoff;
> -	adapter->adapter.write = sifive_i2c_adapter_write;
> -	adapter->adapter.read = sifive_i2c_adapter_read;
> +	adapter->adapter.write_byte = sifive_i2c_adapter_write_byte;
> +	adapter->adapter.read_byte = sifive_i2c_adapter_read_byte;
>  	rc = i2c_adapter_add(&adapter->adapter);
>  	if (rc)
>  		return rc;
> diff --git a/lib/utils/i2c/i2c.c b/lib/utils/i2c/i2c.c
> index 6be4e24..972c7bb 100644
> --- a/lib/utils/i2c/i2c.c
> +++ b/lib/utils/i2c/i2c.c
> @@ -12,8 +12,12 @@
>   */
>  
>  #include <sbi/sbi_error.h>
> +#include <sbi/sbi_string.h>
>  #include <sbi_utils/i2c/i2c.h>
>  
> +#define I2C_10BIT_MASK	0x7c
> +#define I2C_10BIT_VAL	0x78
> +
>  static SBI_LIST_HEAD(i2c_adapters_list);
>  
>  struct i2c_adapter *i2c_adapter_find(int id)
> @@ -51,25 +55,116 @@ void i2c_adapter_remove(struct i2c_adapter *ia)
>  	sbi_list_del(&(ia->node));
>  }
>  
> -int i2c_adapter_write(struct i2c_adapter *ia, uint8_t addr, uint8_t
> reg,
> -		     uint8_t *buffer, int len)
> +unsigned i2c_10bit_address(unsigned addr)
>  {
> -	if (!ia)
> -		return SBI_EINVAL;
> -	if (!ia->write)
> -		return SBI_ENOSYS;
> +	return (addr & 0x3ff) | (I2C_10BIT_VAL << 8);
> +}
>  
> -	return ia->write(ia, addr, reg, buffer, len);
> +static inline int i2c_is_10bit_address(unsigned addr)
> +{
> +	return ((addr >> 8) & I2C_10BIT_MASK) == I2C_10BIT_VAL;
>  }
>  
> +static int i2c_adapter_read_helper(struct i2c_adapter *ia,
> +		unsigned addr, uint8_t *buff, unsigned len, bool
> send_stop) +{
> +	int ret;
> +	if (!i2c_is_10bit_address(addr)) {
> +		ret = ia->write_byte(ia, (addr << 1) | I2C_OP_R,
> true, false);

Shoudn't there also be a hardware support for 10bit addresses ?

I think it's up to driver to decide how to deal with 10bit addresses

> +		if (ret)
> +			return ret;
> +	} else {
> +		unsigned addr_h = (addr >> 8) & 0x7f;
> +		unsigned addr_l = (addr >> 0) & 0xff;
> +		ret = ia->write_byte(ia, (addr_h << 1) | I2C_OP_W,
> true, false);
> +		if (ret)
> +			return ret;
> +		ret = ia->write_byte(ia, addr_l, false, false);
> +		if (ret)
> +			return ret;
> +		ret = ia->write_byte(ia, (addr_h << 1) | I2C_OP_R,
> true, false);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	while (len--) {
> +		ret = ia->read_byte(ia, buff, len == 0, send_stop &&
> (len == 0));
> +		if (ret)
> +			return ret;
> +		buff++;
> +	}
> +	return ret;
> +}

I don't think sending should exposed to upper abstraction level, it's
up to driver to decide how to read.

>  
> -int i2c_adapter_read(struct i2c_adapter *ia, uint8_t addr, uint8_t
> reg,
> -		     uint8_t *buffer, int len)
> +static int i2c_adapter_write_helper(struct i2c_adapter *ia,
> +		unsigned addr, uint8_t *buff, unsigned len, bool
> send_stop) {
> -	if (!ia)
> -		return SBI_EINVAL;
> -	if (!ia->read)
> -		return SBI_ENOSYS;
> +	int ret;
> +	if (!i2c_is_10bit_address(addr)) {
> +		ret = ia->write_byte(ia, (addr << 1) | I2C_OP_W,
> true, false);
> +		if (ret)
> +			return ret;
> +	} else {
> +		unsigned addr_h = (addr >> 8) & 0x7f;
> +		unsigned addr_l = (addr >> 0) & 0xff;
> +		ret = ia->write_byte(ia, (addr_h << 1) | I2C_OP_W,
> true, false);
> +		if (ret)
> +			return ret;
> +		ret = ia->write_byte(ia, addr_l, false, false);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	while (len--) {
> +		ret = ia->write_byte(ia, *buff, false,  send_stop &&
> (len == 0));
> +		if (ret)
> +			return ret;
> +		buff++;
> +	}
> +	return ret;
> +}

Same here.

> +
> +int i2c_adapter_read(struct i2c_adapter *ia,
> +		unsigned addr, uint8_t *buff, unsigned len)
> +{
> +	return i2c_adapter_read_helper(ia, addr, buff, len, true);
> +}
>  
> -	return ia->read(ia, addr, reg, buffer, len);
> +int i2c_adapter_write(struct i2c_adapter *ia,
> +		unsigned addr, uint8_t *buff, unsigned len)
> +{
> +	return i2c_adapter_write_helper(ia, addr, buff, len, true);
>  }
> +
> +int i2c_adapter_comb(struct i2c_adapter *ia, unsigned addr,
> +		enum i2c_op op0, uint8_t *buff0, unsigned len0,
> +		enum i2c_op op1, uint8_t *buff1, unsigned len1)
> +{
> +	int ret;
> +	switch (op0) {
> +		case I2C_OP_W:
> +			ret = i2c_adapter_write_helper(ia, addr,
> buff0, len0, false);
> +			break;
> +		case I2C_OP_R:
> +			ret = i2c_adapter_read_helper(ia, addr,
> buff0, len0, false);
> +			break;
> +		default:
> +			return SBI_EINVAL;
> +	}
> +	if (ret)
> +		return ret;
> +
> +	if (i2c_is_10bit_address(addr))
> +		addr = (addr >> 8) & 0x7f;
> +	switch (op0) {
> +		case I2C_OP_W:
> +			ret = i2c_adapter_write_helper(ia, addr,
> buff0, len0, true);
> +			break;
> +		case I2C_OP_R:
> +			ret = i2c_adapter_read_helper(ia, addr,
> buff0, len0, true);
> +			break;
> +		default:
> +			return SBI_EINVAL;
> +	}
> +	return ret;
> +}

i2c_adapter_comb looks really cumbersome and counter intuitive to me.
What are we trying to achieve with this ?


> \ No newline at end of file
> diff --git a/platform/generic/sifive_fu740.c
> b/platform/generic/sifive_fu740.c index 333b3fb..8e42a5d 100644
> --- a/platform/generic/sifive_fu740.c
> +++ b/platform/generic/sifive_fu740.c
> @@ -34,6 +34,21 @@
>  
>  #define PMIC_CHIP_ID_DA9063		0x61
>  
> +static inline int da9063_read_reg(struct i2c_adapter *adap, uint8_t
> addr,
> +	uint8_t reg, uint8_t *val)
> +{
> +	return i2c_adapter_comb(adap, addr, I2C_OP_W, &reg, 1,
> I2C_OP_R, val, 1); +}
> +
> +static inline int da9063_write_reg(struct i2c_adapter *adap, uint8_t
> addr,
> +	uint8_t reg, uint8_t val)
> +{
> +	uint8_t buff[2];
> +	buff[0] = reg;
> +	buff[1] = val;
> +	return i2c_adapter_write(adap, addr, buff, 2);
> +}
> +
>  static struct {
>  	struct i2c_adapter *adapter;
>  	uint32_t reg;
> @@ -55,13 +70,13 @@ static int da9063_system_reset_check(u32 type,
> u32 reason) static inline int da9063_sanity_check(struct i2c_adapter
> *adap, uint32_t reg) {
>  	uint8_t val;
> -	int rc = i2c_adapter_reg_write(adap, reg,
> DA9063_REG_PAGE_CON, 0x02);
> +	int rc = da9063_write_reg(adap, reg, DA9063_REG_PAGE_CON,
> 0x02); 
>  	if (rc)
>  		return rc;
>  
>  	/* check set page*/
> -	rc = i2c_adapter_reg_read(adap, reg, 0x0, &val);
> +	rc = da9063_read_reg(adap, reg, 0x0, &val);
>  	if (rc)
>  		return rc;
>  
> @@ -69,7 +84,7 @@ static inline int da9063_sanity_check(struct
> i2c_adapter *adap, uint32_t reg) return SBI_ENODEV;
>  
>  	/* read and check device id */
> -	rc = i2c_adapter_reg_read(adap, reg, DA9063_REG_DEVICE_ID,
> &val);
> +	rc = da9063_read_reg(adap, reg, DA9063_REG_DEVICE_ID, &val);
>  	if (rc)
>  		return rc;
>  
> @@ -81,32 +96,32 @@ static inline int da9063_sanity_check(struct
> i2c_adapter *adap, uint32_t reg) 
>  static inline int da9063_shutdown(struct i2c_adapter *adap, uint32_t
> reg) {
> -	int rc = i2c_adapter_reg_write(adap, da9063.reg,
> +	int rc = da9063_write_reg(adap, da9063.reg,
>  					DA9063_REG_PAGE_CON, 0x00);
>  
>  	if (rc)
>  		return rc;
>  
> -	return i2c_adapter_reg_write(adap, da9063.reg,
> +	return da9063_write_reg(adap, da9063.reg,
>  				     DA9063_REG_CONTROL_F,
>  				     DA9063_CONTROL_F_SHUTDOWN);
>  }
>  
>  static inline int da9063_reset(struct i2c_adapter *adap, uint32_t
> reg) {
> -	int rc = i2c_adapter_reg_write(adap, da9063.reg,
> +	int rc = da9063_write_reg(adap, da9063.reg,
>  					DA9063_REG_PAGE_CON, 0x00);
>  
>  	if (rc)
>  		return rc;
>  
> -	rc = i2c_adapter_reg_write(adap, da9063.reg,
> +	rc = da9063_write_reg(adap, da9063.reg,
>  				   DA9063_REG_CONTROL_F,
>  				   DA9063_CONTROL_F_WAKEUP);
>  	if (rc)
>  		return rc;
>  
> -	return i2c_adapter_reg_write(adap, da9063.reg,
> +	return da9063_write_reg(adap, da9063.reg,
>  				DA9063_REG_CONTROL_A,
>  				DA9063_CONTROL_A_M_POWER1_EN |
>  				DA9063_CONTROL_A_M_POWER_EN |
Xiang W Dec. 30, 2021, 12:47 p.m. UTC | #2
在 2021-12-30星期四的 12:29 +0300,Nikita Shubin写道:
> Hello Xiang!
> 
> Thank you for your patch.
> 
> On Thu, 30 Dec 2021 01:39:09 +0800
> Xiang W <wxjstz@126.com> wrote:
> 
> > 1. Remove i2c register related operations
> > 2. Simplify the low-level interface
> > 3. Add 10bit address support
> > 4. Add combination operation
> > 5. Update fdt_i2c_sifive.c
> > 6. Update sifive_fu740.c
> > 
> 
> First of all my Unmatched board no longer reboots with your patch
> applied:
> 
> [   53.068656] reboot: Restarting system
> da9063_system_reset: chip is not da9063 PMIC
> 
> What kind of hardware are you using currently ?

I have a piece of Unmatched, which cannot be restarted, and opensbi v1.0
also cannot be restarted. I even commented
da9063_read_reg/da9063_write_reg and there is no change.

> 
> In general i think raw read/write possibility is really needed, but
> this kind of functionality can be achieved with flags without exposing
> all this stuff in i2c.c, 
> 
> I was thinking on something like "struct i2c_msg" Linux have:
> 
>  
> https://elixir.bootlin.com/linux/v5.16-rc7/source/include/uapi/linux/i2c.h#L73
> 
> We can still read/write a byte with this, ask for nostop and nack -
> can't we ?
> 
> Please see my comments below:
> 
> > Signed-off-by: Xiang W <wxjstz@126.com>
> > ---
> >  include/sbi_utils/i2c/i2c.h     |  80 ++++++++++----------
> >  lib/utils/i2c/fdt_i2c_sifive.c  | 130
> > +++++++++----------------------- lib/utils/i2c/i2c.c             |
> > 123 ++++++++++++++++++++++++++---- platform/generic/sifive_fu740.c |
> > 31 ++++++-- 4 files changed, 208 insertions(+), 156 deletions(-)
> > 
> > diff --git a/include/sbi_utils/i2c/i2c.h
> > b/include/sbi_utils/i2c/i2c.h
> > index 5a70364..53e76e3 100644
> > --- a/include/sbi_utils/i2c/i2c.h
> > +++ b/include/sbi_utils/i2c/i2c.h
> > @@ -13,6 +13,11 @@
> >  #include <sbi/sbi_types.h>
> >  #include <sbi/sbi_list.h>
> >  
> > +enum i2c_op {
> > +       I2C_OP_W = 0,
> > +       I2C_OP_R = 1
> > +};
> > +
> >  /** Representation of a I2C adapter */
> >  struct i2c_adapter {
> >         /** Pointer to I2C driver owning this I2C adapter */
> > @@ -21,21 +26,11 @@ struct i2c_adapter {
> >         /** Unique ID of the I2C adapter assigned by the driver */
> >         int id;
> >  
> > -       /**
> > -        * Send buffer to given address, register
> > -        *
> > -        * @return 0 on success and negative error code on failure
> > -        */
> > -       int (*write)(struct i2c_adapter *ia, uint8_t addr, uint8_t
> > reg,
> > -                   uint8_t *buffer, int len);
> > -
> > -       /**
> > -        * Read buffer from given address, register
> > -        *
> > -        * @return 0 on success and negative error code on failure
> > -        */
> > -       int (*read)(struct i2c_adapter *ia, uint8_t addr, uint8_t
> > reg,
> > -                   uint8_t *buffer, int len);
> > +       int (*write_byte)(struct i2c_adapter *ia, uint8_t byte,
> > +                       bool send_start, bool send_stop);
> > +
> > +       int (*read_byte)(struct i2c_adapter *ia, uint8_t *byte,
> > +                       bool nack, bool send_stop);
> >  
> 
> Can't we use flags for bool nack, bool send_stop ?

You mean to modify the interface to:
int (*write_byte)(struct i2c_adapter *ia, uint8_t byte, int flags);
int (*read_byte)(struct i2c_adapter *ia, uint8_t *byte, int flags);
???

> 
> 
> >         /** List */
> >         struct sbi_dlist node;
> > @@ -46,6 +41,13 @@ static inline struct i2c_adapter
> > *to_i2c_adapter(struct sbi_dlist *node) return container_of(node,
> > struct i2c_adapter, node); }
> >  
> > +/**
> > + * This function is used to pack a 10bit address into a 15-bit
> > address.
> > + * The 10bit address during read and write operations needs to be
> > processed by
> > + * this function.
> > + * */
> > +unsigned i2c_10bit_address(unsigned addr);
> > +
> >  /** Find a registered I2C adapter */
> >  struct i2c_adapter *i2c_adapter_find(int id);
> >  
> > @@ -55,31 +57,27 @@ int i2c_adapter_add(struct i2c_adapter *ia);
> >  /** Un-register I2C adapter */
> >  void i2c_adapter_remove(struct i2c_adapter *ia);
> >  
> > -/** Send to device on I2C adapter bus */
> > -int i2c_adapter_write(struct i2c_adapter *ia, uint8_t addr, uint8_t
> > reg,
> > -                     uint8_t *buffer, int len);
> > -
> > -/** Read from device on I2C adapter bus */
> > -int i2c_adapter_read(struct i2c_adapter *ia, uint8_t addr, uint8_t
> > reg,
> > -                    uint8_t *buffer, int len);
> > -
> > -static inline int i2c_adapter_reg_write(struct i2c_adapter *ia,
> > uint8_t addr,
> > -                                uint8_t reg, uint8_t val)
> > -{
> > -       return i2c_adapter_write(ia, addr, reg, &val, 1);
> > -}
> > -
> > -static inline int i2c_adapter_reg_read(struct i2c_adapter *ia,
> > uint8_t addr,
> > -                                      uint8_t reg, uint8_t *val)
> > -{
> > -       uint8_t buf;
> > -       int ret = i2c_adapter_read(ia, addr, reg, &buf, 1);
> > -
> > -       if (ret)
> > -               return ret;
> > -
> > -       *val = buf;
> > -       return 0;
> > -}
> > +/**
> > + * Read data from the device with address addr to buff
> > + * For 10bit address operation, addr = i2c_10bit_address
> > (orig_addr)
> > +*/
> > +int i2c_adapter_read(struct i2c_adapter *ia, unsigned addr,
> > +               uint8_t *buff, unsigned len);
> > +
> > +/**
> > + * Write buff data to the device with address addr
> > + * For 10bit address operation, addr = i2c_10bit_address
> > (orig_addr)
> > +*/
> > +int i2c_adapter_write(struct i2c_adapter *ia, unsigned addr,
> > +               uint8_t *buff, unsigned len);
> > +
> > +/**
> > + * Combined operation, there is no stop signal between two iic
> > operations
> > + * For 10bit address operation, addr = i2c_10bit_address
> > (orig_addr)
> > + * op0 and op1 can only be I2C_OP_R (read operate) or I2C_OP_W
> > (write operate)
> > + */
> > +int i2c_adapter_comb(struct i2c_adapter *ia, unsigned addr,
> > +               enum i2c_op op0, uint8_t *buff0, unsigned len0,
> > +               enum i2c_op op1, uint8_t *buff1, unsigned len1);
> >  
> 
> Why should we need this, if you already exposed bool nack, bool
> send_stop in read_byte, write_byte ?
Please refer https://www.nxp.com/docs/en/user-guide/UM10204.pdf
Figure 13

The previous interface i2c_adapter_reg_read is a combined format.

Regards,
Xiang W
> 
> >  #endif
> > diff --git a/lib/utils/i2c/fdt_i2c_sifive.c
> > b/lib/utils/i2c/fdt_i2c_sifive.c index 871241a..bb0456f 100644
> > --- a/lib/utils/i2c/fdt_i2c_sifive.c
> > +++ b/lib/utils/i2c/fdt_i2c_sifive.c
> > @@ -113,127 +113,71 @@ static int sifive_i2c_adapter_start(struct
> > sifive_i2c_adapter *adap, return sifive_i2c_adapter_poll_tip(adap);
> >  }
> >  
> > -static int sifive_i2c_adapter_write(struct i2c_adapter *ia, uint8_t
> > addr,
> > -                                   uint8_t reg, uint8_t *buffer,
> > int len) +static int sifive_i2c_adapter_write_byte(struct
> > i2c_adapter
> > *ia,
> > +                               uint8_t byte, bool send_start, bool
> > send_stop) {
> >         struct sifive_i2c_adapter *adap =
> >                 container_of(ia, struct sifive_i2c_adapter,
> > adapter);
> > -       int rc = sifive_i2c_adapter_start(adap, addr,
> > SIFIVE_I2C_WRITE_BIT); -
> > -       if (rc)
> > -               return rc;
> > -
> > -       rc = sifive_i2c_adapter_rxack(adap);
> > -       if (rc)
> > -               return rc;
> > -
> > -       /* set register address */
> > -       sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, reg);
> > -       sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR |
> > -                               SIFIVE_I2C_CMD_IACK);
> > -       rc = sifive_i2c_adapter_poll_tip(adap);
> > -       if (rc)
> > -               return rc;
> > -
> > -       rc = sifive_i2c_adapter_rxack(adap);
> > -       if (rc)
> > -               return rc;
> > -
> > -       /* set value */
> > -       while (len) {
> > -               sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, *buffer);
> > +       int rc;
> > +       if (send_start) {
> > +               rc = sifive_i2c_adapter_start(adap, byte >> 1, byte
> > & 1);
> > +               if (rc)
> > +                       return rc;
> > +       } else {
> > +               sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, byte);
> >                 sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > SIFIVE_I2C_CMD_WR | SIFIVE_I2C_CMD_IACK);
> >  
> >                 rc = sifive_i2c_adapter_poll_tip(adap);
> >                 if (rc)
> >                         return rc;
> > -
> > -               rc = sifive_i2c_adapter_rxack(adap);
> > -               if (rc)
> > -                       return rc;
> > -
> > -               buffer++;
> > -               len--;
> >         }
> > -
> > -       sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_STO |
> > -                                              SIFIVE_I2C_CMD_IACK);
> > -
> > -       /* poll BUSY instead of ACK*/
> > -       rc = sifive_i2c_adapter_poll_busy(adap);
> > +       rc = sifive_i2c_adapter_rxack(adap);
> >         if (rc)
> >                 return rc;
> >  
> > -       sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_IACK);
> > +       if (send_stop) {
> > +               sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > SIFIVE_I2C_CMD_STO |
> > +                                              SIFIVE_I2C_CMD_IACK);
> > +               rc = sifive_i2c_adapter_poll_busy(adap);
> > +               if (rc)
> > +                       return rc;
> >  
> > +               sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > SIFIVE_I2C_CMD_IACK);
> > +       }
> >         return 0;
> >  }
> >  
> > -static int sifive_i2c_adapter_read(struct i2c_adapter *ia, uint8_t
> > addr,
> > -                                  uint8_t reg, uint8_t *buffer, int
> > len) +static int sifive_i2c_adapter_read_byte(struct i2c_adapter
> > *ia,
> > +                               uint8_t *byte, bool nack, bool
> > send_stop) {
> >         struct sifive_i2c_adapter *adap =
> >                 container_of(ia, struct sifive_i2c_adapter,
> > adapter);
> >         int rc;
> > -
> > -       rc = sifive_i2c_adapter_start(adap, addr,
> > SIFIVE_I2C_WRITE_BIT);
> > -       if (rc)
> > -               return rc;
> > -
> > -       rc = sifive_i2c_adapter_rxack(adap);
> > -       if (rc)
> > -               return rc;
> > -
> > -       sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, reg);
> > -       sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR |
> > -                                              SIFIVE_I2C_CMD_IACK);
> > -
> > +       if (nack)
> > +               sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > +                                       SIFIVE_I2C_CMD_ACK |
> > +                                       SIFIVE_I2C_CMD_RD |
> > +                                       SIFIVE_I2C_CMD_IACK);
> > +       else
> > +               sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > +                                       SIFIVE_I2C_CMD_RD |
> > +                                       SIFIVE_I2C_CMD_IACK);
> >         rc = sifive_i2c_adapter_poll_tip(adap);
> >         if (rc)
> >                 return rc;
> >  
> > -       rc = sifive_i2c_adapter_rxack(adap);
> > -       if (rc)
> > -               return rc;
> > -
> > -       /* setting addr with high 0 bit */
> > -       rc = sifive_i2c_adapter_start(adap, addr,
> > SIFIVE_I2C_READ_BIT);
> > -       if (rc)
> > -               return rc;
> > -
> > -       rc = sifive_i2c_adapter_rxack(adap);
> > -       if (rc)
> > -               return rc;
> > +       *byte = sifive_i2c_getreg(adap, SIFIVE_I2C_RXR);
> >  
> > -       while (len) {
> > -               if (len == 1)
> > -                       sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > -                                         SIFIVE_I2C_CMD_ACK |
> > -                                         SIFIVE_I2C_CMD_RD |
> > -                                         SIFIVE_I2C_CMD_IACK);
> > -               else
> > -                       sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > -                                         SIFIVE_I2C_CMD_RD |
> > -                                         SIFIVE_I2C_CMD_IACK);
> > -
> > -               rc = sifive_i2c_adapter_poll_tip(adap);
> > +       if (send_stop) {
> > +               sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > SIFIVE_I2C_CMD_STO |
> > +                                              SIFIVE_I2C_CMD_IACK);
> > +               rc = sifive_i2c_adapter_poll_busy(adap);
> >                 if (rc)
> >                         return rc;
> >  
> > -               *buffer = sifive_i2c_getreg(adap, SIFIVE_I2C_RXR);
> > -               buffer++;
> > -               len--;
> > +               sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > SIFIVE_I2C_CMD_IACK); }
> > -
> > -       sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_STO |
> > -                                              SIFIVE_I2C_CMD_IACK);
> > -       rc = sifive_i2c_adapter_poll_busy(adap);
> > -       if (rc)
> > -               return rc;
> > -
> > -       sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_IACK);
> > -
> >         return 0;
> >  }
> >  
> > @@ -256,8 +200,8 @@ static int sifive_i2c_init(void *fdt, int
> > nodeoff,
> >         adapter->addr = addr;
> >         adapter->adapter.driver = &fdt_i2c_adapter_sifive;
> >         adapter->adapter.id = nodeoff;
> > -       adapter->adapter.write = sifive_i2c_adapter_write;
> > -       adapter->adapter.read = sifive_i2c_adapter_read;
> > +       adapter->adapter.write_byte = sifive_i2c_adapter_write_byte;
> > +       adapter->adapter.read_byte = sifive_i2c_adapter_read_byte;
> >         rc = i2c_adapter_add(&adapter->adapter);
> >         if (rc)
> >                 return rc;
> > diff --git a/lib/utils/i2c/i2c.c b/lib/utils/i2c/i2c.c
> > index 6be4e24..972c7bb 100644
> > --- a/lib/utils/i2c/i2c.c
> > +++ b/lib/utils/i2c/i2c.c
> > @@ -12,8 +12,12 @@
> >   */
> >  
> >  #include <sbi/sbi_error.h>
> > +#include <sbi/sbi_string.h>
> >  #include <sbi_utils/i2c/i2c.h>
> >  
> > +#define I2C_10BIT_MASK 0x7c
> > +#define I2C_10BIT_VAL  0x78
> > +
> >  static SBI_LIST_HEAD(i2c_adapters_list);
> >  
> >  struct i2c_adapter *i2c_adapter_find(int id)
> > @@ -51,25 +55,116 @@ void i2c_adapter_remove(struct i2c_adapter *ia)
> >         sbi_list_del(&(ia->node));
> >  }
> >  
> > -int i2c_adapter_write(struct i2c_adapter *ia, uint8_t addr, uint8_t
> > reg,
> > -                    uint8_t *buffer, int len)
> > +unsigned i2c_10bit_address(unsigned addr)
> >  {
> > -       if (!ia)
> > -               return SBI_EINVAL;
> > -       if (!ia->write)
> > -               return SBI_ENOSYS;
> > +       return (addr & 0x3ff) | (I2C_10BIT_VAL << 8);
> > +}
> >  
> > -       return ia->write(ia, addr, reg, buffer, len);
> > +static inline int i2c_is_10bit_address(unsigned addr)
> > +{
> > +       return ((addr >> 8) & I2C_10BIT_MASK) == I2C_10BIT_VAL;
> >  }
> >  
> > +static int i2c_adapter_read_helper(struct i2c_adapter *ia,
> > +               unsigned addr, uint8_t *buff, unsigned len, bool
> > send_stop) +{
> > +       int ret;
> > +       if (!i2c_is_10bit_address(addr)) {
> > +               ret = ia->write_byte(ia, (addr << 1) | I2C_OP_R,
> > true, false);
> 
> Shoudn't there also be a hardware support for 10bit addresses ?
> 
> I think it's up to driver to decide how to deal with 10bit addresses
> 
> > +               if (ret)
> > +                       return ret;
> > +       } else {
> > +               unsigned addr_h = (addr >> 8) & 0x7f;
> > +               unsigned addr_l = (addr >> 0) & 0xff;
> > +               ret = ia->write_byte(ia, (addr_h << 1) | I2C_OP_W,
> > true, false);
> > +               if (ret)
> > +                       return ret;
> > +               ret = ia->write_byte(ia, addr_l, false, false);
> > +               if (ret)
> > +                       return ret;
> > +               ret = ia->write_byte(ia, (addr_h << 1) | I2C_OP_R,
> > true, false);
> > +               if (ret)
> > +                       return ret;
> > +       }
> > +
> > +       while (len--) {
> > +               ret = ia->read_byte(ia, buff, len == 0, send_stop &&
> > (len == 0));
> > +               if (ret)
> > +                       return ret;
> > +               buff++;
> > +       }
> > +       return ret;
> > +}
> 
> I don't think sending should exposed to upper abstraction level, it's
> up to driver to decide how to read.
> 
> >  
> > -int i2c_adapter_read(struct i2c_adapter *ia, uint8_t addr, uint8_t
> > reg,
> > -                    uint8_t *buffer, int len)
> > +static int i2c_adapter_write_helper(struct i2c_adapter *ia,
> > +               unsigned addr, uint8_t *buff, unsigned len, bool
> > send_stop) {
> > -       if (!ia)
> > -               return SBI_EINVAL;
> > -       if (!ia->read)
> > -               return SBI_ENOSYS;
> > +       int ret;
> > +       if (!i2c_is_10bit_address(addr)) {
> > +               ret = ia->write_byte(ia, (addr << 1) | I2C_OP_W,
> > true, false);
> > +               if (ret)
> > +                       return ret;
> > +       } else {
> > +               unsigned addr_h = (addr >> 8) & 0x7f;
> > +               unsigned addr_l = (addr >> 0) & 0xff;
> > +               ret = ia->write_byte(ia, (addr_h << 1) | I2C_OP_W,
> > true, false);
> > +               if (ret)
> > +                       return ret;
> > +               ret = ia->write_byte(ia, addr_l, false, false);
> > +               if (ret)
> > +                       return ret;
> > +       }
> > +
> > +       while (len--) {
> > +               ret = ia->write_byte(ia, *buff, false,  send_stop &&
> > (len == 0));
> > +               if (ret)
> > +                       return ret;
> > +               buff++;
> > +       }
> > +       return ret;
> > +}
> 
> Same here.
> 
> > +
> > +int i2c_adapter_read(struct i2c_adapter *ia,
> > +               unsigned addr, uint8_t *buff, unsigned len)
> > +{
> > +       return i2c_adapter_read_helper(ia, addr, buff, len, true);
> > +}
> >  
> > -       return ia->read(ia, addr, reg, buffer, len);
> > +int i2c_adapter_write(struct i2c_adapter *ia,
> > +               unsigned addr, uint8_t *buff, unsigned len)
> > +{
> > +       return i2c_adapter_write_helper(ia, addr, buff, len, true);
> >  }
> > +
> > +int i2c_adapter_comb(struct i2c_adapter *ia, unsigned addr,
> > +               enum i2c_op op0, uint8_t *buff0, unsigned len0,
> > +               enum i2c_op op1, uint8_t *buff1, unsigned len1)
> > +{
> > +       int ret;
> > +       switch (op0) {
> > +               case I2C_OP_W:
> > +                       ret = i2c_adapter_write_helper(ia, addr,
> > buff0, len0, false);
> > +                       break;
> > +               case I2C_OP_R:
> > +                       ret = i2c_adapter_read_helper(ia, addr,
> > buff0, len0, false);
> > +                       break;
> > +               default:
> > +                       return SBI_EINVAL;
> > +       }
> > +       if (ret)
> > +               return ret;
> > +
> > +       if (i2c_is_10bit_address(addr))
> > +               addr = (addr >> 8) & 0x7f;
> > +       switch (op0) {
> > +               case I2C_OP_W:
> > +                       ret = i2c_adapter_write_helper(ia, addr,
> > buff0, len0, true);
> > +                       break;
> > +               case I2C_OP_R:
> > +                       ret = i2c_adapter_read_helper(ia, addr,
> > buff0, len0, true);
> > +                       break;
> > +               default:
> > +                       return SBI_EINVAL;
> > +       }
> > +       return ret;
> > +}
> 
> i2c_adapter_comb looks really cumbersome and counter intuitive to me.
> What are we trying to achieve with this ?
> 
> 
> > \ No newline at end of file
> > diff --git a/platform/generic/sifive_fu740.c
> > b/platform/generic/sifive_fu740.c index 333b3fb..8e42a5d 100644
> > --- a/platform/generic/sifive_fu740.c
> > +++ b/platform/generic/sifive_fu740.c
> > @@ -34,6 +34,21 @@
> >  
> >  #define PMIC_CHIP_ID_DA9063            0x61
> >  
> > +static inline int da9063_read_reg(struct i2c_adapter *adap, uint8_t
> > addr,
> > +       uint8_t reg, uint8_t *val)
> > +{
> > +       return i2c_adapter_comb(adap, addr, I2C_OP_W, &reg, 1,
> > I2C_OP_R, val, 1); +}
> > +
> > +static inline int da9063_write_reg(struct i2c_adapter *adap,
> > uint8_t
> > addr,
> > +       uint8_t reg, uint8_t val)
> > +{
> > +       uint8_t buff[2];
> > +       buff[0] = reg;
> > +       buff[1] = val;
> > +       return i2c_adapter_write(adap, addr, buff, 2);
> > +}
> > +
> >  static struct {
> >         struct i2c_adapter *adapter;
> >         uint32_t reg;
> > @@ -55,13 +70,13 @@ static int da9063_system_reset_check(u32 type,
> > u32 reason) static inline int da9063_sanity_check(struct i2c_adapter
> > *adap, uint32_t reg) {
> >         uint8_t val;
> > -       int rc = i2c_adapter_reg_write(adap, reg,
> > DA9063_REG_PAGE_CON, 0x02);
> > +       int rc = da9063_write_reg(adap, reg, DA9063_REG_PAGE_CON,
> > 0x02); 
> >         if (rc)
> >                 return rc;
> >  
> >         /* check set page*/
> > -       rc = i2c_adapter_reg_read(adap, reg, 0x0, &val);
> > +       rc = da9063_read_reg(adap, reg, 0x0, &val);
> >         if (rc)
> >                 return rc;
> >  
> > @@ -69,7 +84,7 @@ static inline int da9063_sanity_check(struct
> > i2c_adapter *adap, uint32_t reg) return SBI_ENODEV;
> >  
> >         /* read and check device id */
> > -       rc = i2c_adapter_reg_read(adap, reg, DA9063_REG_DEVICE_ID,
> > &val);
> > +       rc = da9063_read_reg(adap, reg, DA9063_REG_DEVICE_ID, &val);
> >         if (rc)
> >                 return rc;
> >  
> > @@ -81,32 +96,32 @@ static inline int da9063_sanity_check(struct
> > i2c_adapter *adap, uint32_t reg) 
> >  static inline int da9063_shutdown(struct i2c_adapter *adap,
> > uint32_t
> > reg) {
> > -       int rc = i2c_adapter_reg_write(adap, da9063.reg,
> > +       int rc = da9063_write_reg(adap, da9063.reg,
> >                                         DA9063_REG_PAGE_CON, 0x00);
> >  
> >         if (rc)
> >                 return rc;
> >  
> > -       return i2c_adapter_reg_write(adap, da9063.reg,
> > +       return da9063_write_reg(adap, da9063.reg,
> >                                      DA9063_REG_CONTROL_F,
> >                                      DA9063_CONTROL_F_SHUTDOWN);
> >  }
> >  
> >  static inline int da9063_reset(struct i2c_adapter *adap, uint32_t
> > reg) {
> > -       int rc = i2c_adapter_reg_write(adap, da9063.reg,
> > +       int rc = da9063_write_reg(adap, da9063.reg,
> >                                         DA9063_REG_PAGE_CON, 0x00);
> >  
> >         if (rc)
> >                 return rc;
> >  
> > -       rc = i2c_adapter_reg_write(adap, da9063.reg,
> > +       rc = da9063_write_reg(adap, da9063.reg,
> >                                    DA9063_REG_CONTROL_F,
> >                                    DA9063_CONTROL_F_WAKEUP);
> >         if (rc)
> >                 return rc;
> >  
> > -       return i2c_adapter_reg_write(adap, da9063.reg,
> > +       return da9063_write_reg(adap, da9063.reg,
> >                                 DA9063_REG_CONTROL_A,
> >                                 DA9063_CONTROL_A_M_POWER1_EN |
> >                                 DA9063_CONTROL_A_M_POWER_EN |
Anup Patel Dec. 30, 2021, 12:55 p.m. UTC | #3
On Thu, Dec 30, 2021 at 3:00 PM Nikita Shubin <nikita.shubin@maquefel.me> wrote:
>
> Hello Xiang!
>
> Thank you for your patch.
>
> On Thu, 30 Dec 2021 01:39:09 +0800
> Xiang W <wxjstz@126.com> wrote:
>
> > 1. Remove i2c register related operations
> > 2. Simplify the low-level interface
> > 3. Add 10bit address support
> > 4. Add combination operation
> > 5. Update fdt_i2c_sifive.c
> > 6. Update sifive_fu740.c
> >
>
> First of all my Unmatched board no longer reboots with your patch
> applied:
>
> [   53.068656] reboot: Restarting system
> da9063_system_reset: chip is not da9063 PMIC
>
> What kind of hardware are you using currently ?
>
> In general i think raw read/write possibility is really needed, but
> this kind of functionality can be achieved with flags without exposing
> all this stuff in i2c.c,
>
> I was thinking on something like "struct i2c_msg" Linux have:
>
> https://elixir.bootlin.com/linux/v5.16-rc7/source/include/uapi/linux/i2c.h#L73

I am also inclined towards using "struct i2c_msg". It's simple and
extensible.

Regards,
Anup

>
> We can still read/write a byte with this, ask for nostop and nack -
> can't we ?
>
> Please see my comments below:
>
> > Signed-off-by: Xiang W <wxjstz@126.com>
> > ---
> >  include/sbi_utils/i2c/i2c.h     |  80 ++++++++++----------
> >  lib/utils/i2c/fdt_i2c_sifive.c  | 130
> > +++++++++----------------------- lib/utils/i2c/i2c.c             |
> > 123 ++++++++++++++++++++++++++---- platform/generic/sifive_fu740.c |
> > 31 ++++++-- 4 files changed, 208 insertions(+), 156 deletions(-)
> >
> > diff --git a/include/sbi_utils/i2c/i2c.h b/include/sbi_utils/i2c/i2c.h
> > index 5a70364..53e76e3 100644
> > --- a/include/sbi_utils/i2c/i2c.h
> > +++ b/include/sbi_utils/i2c/i2c.h
> > @@ -13,6 +13,11 @@
> >  #include <sbi/sbi_types.h>
> >  #include <sbi/sbi_list.h>
> >
> > +enum i2c_op {
> > +     I2C_OP_W = 0,
> > +     I2C_OP_R = 1
> > +};
> > +
> >  /** Representation of a I2C adapter */
> >  struct i2c_adapter {
> >       /** Pointer to I2C driver owning this I2C adapter */
> > @@ -21,21 +26,11 @@ struct i2c_adapter {
> >       /** Unique ID of the I2C adapter assigned by the driver */
> >       int id;
> >
> > -     /**
> > -      * Send buffer to given address, register
> > -      *
> > -      * @return 0 on success and negative error code on failure
> > -      */
> > -     int (*write)(struct i2c_adapter *ia, uint8_t addr, uint8_t
> > reg,
> > -                 uint8_t *buffer, int len);
> > -
> > -     /**
> > -      * Read buffer from given address, register
> > -      *
> > -      * @return 0 on success and negative error code on failure
> > -      */
> > -     int (*read)(struct i2c_adapter *ia, uint8_t addr, uint8_t
> > reg,
> > -                 uint8_t *buffer, int len);
> > +     int (*write_byte)(struct i2c_adapter *ia, uint8_t byte,
> > +                     bool send_start, bool send_stop);
> > +
> > +     int (*read_byte)(struct i2c_adapter *ia, uint8_t *byte,
> > +                     bool nack, bool send_stop);
> >
>
> Can't we use flags for bool nack, bool send_stop ?
>
>
> >       /** List */
> >       struct sbi_dlist node;
> > @@ -46,6 +41,13 @@ static inline struct i2c_adapter
> > *to_i2c_adapter(struct sbi_dlist *node) return container_of(node,
> > struct i2c_adapter, node); }
> >
> > +/**
> > + * This function is used to pack a 10bit address into a 15-bit
> > address.
> > + * The 10bit address during read and write operations needs to be
> > processed by
> > + * this function.
> > + * */
> > +unsigned i2c_10bit_address(unsigned addr);
> > +
> >  /** Find a registered I2C adapter */
> >  struct i2c_adapter *i2c_adapter_find(int id);
> >
> > @@ -55,31 +57,27 @@ int i2c_adapter_add(struct i2c_adapter *ia);
> >  /** Un-register I2C adapter */
> >  void i2c_adapter_remove(struct i2c_adapter *ia);
> >
> > -/** Send to device on I2C adapter bus */
> > -int i2c_adapter_write(struct i2c_adapter *ia, uint8_t addr, uint8_t
> > reg,
> > -                   uint8_t *buffer, int len);
> > -
> > -/** Read from device on I2C adapter bus */
> > -int i2c_adapter_read(struct i2c_adapter *ia, uint8_t addr, uint8_t
> > reg,
> > -                  uint8_t *buffer, int len);
> > -
> > -static inline int i2c_adapter_reg_write(struct i2c_adapter *ia,
> > uint8_t addr,
> > -                              uint8_t reg, uint8_t val)
> > -{
> > -     return i2c_adapter_write(ia, addr, reg, &val, 1);
> > -}
> > -
> > -static inline int i2c_adapter_reg_read(struct i2c_adapter *ia,
> > uint8_t addr,
> > -                                    uint8_t reg, uint8_t *val)
> > -{
> > -     uint8_t buf;
> > -     int ret = i2c_adapter_read(ia, addr, reg, &buf, 1);
> > -
> > -     if (ret)
> > -             return ret;
> > -
> > -     *val = buf;
> > -     return 0;
> > -}
> > +/**
> > + * Read data from the device with address addr to buff
> > + * For 10bit address operation, addr = i2c_10bit_address (orig_addr)
> > +*/
> > +int i2c_adapter_read(struct i2c_adapter *ia, unsigned addr,
> > +             uint8_t *buff, unsigned len);
> > +
> > +/**
> > + * Write buff data to the device with address addr
> > + * For 10bit address operation, addr = i2c_10bit_address (orig_addr)
> > +*/
> > +int i2c_adapter_write(struct i2c_adapter *ia, unsigned addr,
> > +             uint8_t *buff, unsigned len);
> > +
> > +/**
> > + * Combined operation, there is no stop signal between two iic
> > operations
> > + * For 10bit address operation, addr = i2c_10bit_address (orig_addr)
> > + * op0 and op1 can only be I2C_OP_R (read operate) or I2C_OP_W
> > (write operate)
> > + */
> > +int i2c_adapter_comb(struct i2c_adapter *ia, unsigned addr,
> > +             enum i2c_op op0, uint8_t *buff0, unsigned len0,
> > +             enum i2c_op op1, uint8_t *buff1, unsigned len1);
> >
>
> Why should we need this, if you already exposed bool nack, bool
> send_stop in read_byte, write_byte ?
>
>
> >  #endif
> > diff --git a/lib/utils/i2c/fdt_i2c_sifive.c
> > b/lib/utils/i2c/fdt_i2c_sifive.c index 871241a..bb0456f 100644
> > --- a/lib/utils/i2c/fdt_i2c_sifive.c
> > +++ b/lib/utils/i2c/fdt_i2c_sifive.c
> > @@ -113,127 +113,71 @@ static int sifive_i2c_adapter_start(struct
> > sifive_i2c_adapter *adap, return sifive_i2c_adapter_poll_tip(adap);
> >  }
> >
> > -static int sifive_i2c_adapter_write(struct i2c_adapter *ia, uint8_t
> > addr,
> > -                                 uint8_t reg, uint8_t *buffer,
> > int len) +static int sifive_i2c_adapter_write_byte(struct i2c_adapter
> > *ia,
> > +                             uint8_t byte, bool send_start, bool
> > send_stop) {
> >       struct sifive_i2c_adapter *adap =
> >               container_of(ia, struct sifive_i2c_adapter, adapter);
> > -     int rc = sifive_i2c_adapter_start(adap, addr,
> > SIFIVE_I2C_WRITE_BIT); -
> > -     if (rc)
> > -             return rc;
> > -
> > -     rc = sifive_i2c_adapter_rxack(adap);
> > -     if (rc)
> > -             return rc;
> > -
> > -     /* set register address */
> > -     sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, reg);
> > -     sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR |
> > -                             SIFIVE_I2C_CMD_IACK);
> > -     rc = sifive_i2c_adapter_poll_tip(adap);
> > -     if (rc)
> > -             return rc;
> > -
> > -     rc = sifive_i2c_adapter_rxack(adap);
> > -     if (rc)
> > -             return rc;
> > -
> > -     /* set value */
> > -     while (len) {
> > -             sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, *buffer);
> > +     int rc;
> > +     if (send_start) {
> > +             rc = sifive_i2c_adapter_start(adap, byte >> 1, byte
> > & 1);
> > +             if (rc)
> > +                     return rc;
> > +     } else {
> > +             sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, byte);
> >               sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > SIFIVE_I2C_CMD_WR | SIFIVE_I2C_CMD_IACK);
> >
> >               rc = sifive_i2c_adapter_poll_tip(adap);
> >               if (rc)
> >                       return rc;
> > -
> > -             rc = sifive_i2c_adapter_rxack(adap);
> > -             if (rc)
> > -                     return rc;
> > -
> > -             buffer++;
> > -             len--;
> >       }
> > -
> > -     sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_STO |
> > -                                            SIFIVE_I2C_CMD_IACK);
> > -
> > -     /* poll BUSY instead of ACK*/
> > -     rc = sifive_i2c_adapter_poll_busy(adap);
> > +     rc = sifive_i2c_adapter_rxack(adap);
> >       if (rc)
> >               return rc;
> >
> > -     sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_IACK);
> > +     if (send_stop) {
> > +             sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > SIFIVE_I2C_CMD_STO |
> > +                                            SIFIVE_I2C_CMD_IACK);
> > +             rc = sifive_i2c_adapter_poll_busy(adap);
> > +             if (rc)
> > +                     return rc;
> >
> > +             sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > SIFIVE_I2C_CMD_IACK);
> > +     }
> >       return 0;
> >  }
> >
> > -static int sifive_i2c_adapter_read(struct i2c_adapter *ia, uint8_t
> > addr,
> > -                                uint8_t reg, uint8_t *buffer, int
> > len) +static int sifive_i2c_adapter_read_byte(struct i2c_adapter *ia,
> > +                             uint8_t *byte, bool nack, bool
> > send_stop) {
> >       struct sifive_i2c_adapter *adap =
> >               container_of(ia, struct sifive_i2c_adapter, adapter);
> >       int rc;
> > -
> > -     rc = sifive_i2c_adapter_start(adap, addr,
> > SIFIVE_I2C_WRITE_BIT);
> > -     if (rc)
> > -             return rc;
> > -
> > -     rc = sifive_i2c_adapter_rxack(adap);
> > -     if (rc)
> > -             return rc;
> > -
> > -     sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, reg);
> > -     sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR |
> > -                                            SIFIVE_I2C_CMD_IACK);
> > -
> > +     if (nack)
> > +             sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > +                                     SIFIVE_I2C_CMD_ACK |
> > +                                     SIFIVE_I2C_CMD_RD |
> > +                                     SIFIVE_I2C_CMD_IACK);
> > +     else
> > +             sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > +                                     SIFIVE_I2C_CMD_RD |
> > +                                     SIFIVE_I2C_CMD_IACK);
> >       rc = sifive_i2c_adapter_poll_tip(adap);
> >       if (rc)
> >               return rc;
> >
> > -     rc = sifive_i2c_adapter_rxack(adap);
> > -     if (rc)
> > -             return rc;
> > -
> > -     /* setting addr with high 0 bit */
> > -     rc = sifive_i2c_adapter_start(adap, addr,
> > SIFIVE_I2C_READ_BIT);
> > -     if (rc)
> > -             return rc;
> > -
> > -     rc = sifive_i2c_adapter_rxack(adap);
> > -     if (rc)
> > -             return rc;
> > +     *byte = sifive_i2c_getreg(adap, SIFIVE_I2C_RXR);
> >
> > -     while (len) {
> > -             if (len == 1)
> > -                     sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > -                                       SIFIVE_I2C_CMD_ACK |
> > -                                       SIFIVE_I2C_CMD_RD |
> > -                                       SIFIVE_I2C_CMD_IACK);
> > -             else
> > -                     sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > -                                       SIFIVE_I2C_CMD_RD |
> > -                                       SIFIVE_I2C_CMD_IACK);
> > -
> > -             rc = sifive_i2c_adapter_poll_tip(adap);
> > +     if (send_stop) {
> > +             sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > SIFIVE_I2C_CMD_STO |
> > +                                            SIFIVE_I2C_CMD_IACK);
> > +             rc = sifive_i2c_adapter_poll_busy(adap);
> >               if (rc)
> >                       return rc;
> >
> > -             *buffer = sifive_i2c_getreg(adap, SIFIVE_I2C_RXR);
> > -             buffer++;
> > -             len--;
> > +             sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > SIFIVE_I2C_CMD_IACK); }
> > -
> > -     sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_STO |
> > -                                            SIFIVE_I2C_CMD_IACK);
> > -     rc = sifive_i2c_adapter_poll_busy(adap);
> > -     if (rc)
> > -             return rc;
> > -
> > -     sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_IACK);
> > -
> >       return 0;
> >  }
> >
> > @@ -256,8 +200,8 @@ static int sifive_i2c_init(void *fdt, int nodeoff,
> >       adapter->addr = addr;
> >       adapter->adapter.driver = &fdt_i2c_adapter_sifive;
> >       adapter->adapter.id = nodeoff;
> > -     adapter->adapter.write = sifive_i2c_adapter_write;
> > -     adapter->adapter.read = sifive_i2c_adapter_read;
> > +     adapter->adapter.write_byte = sifive_i2c_adapter_write_byte;
> > +     adapter->adapter.read_byte = sifive_i2c_adapter_read_byte;
> >       rc = i2c_adapter_add(&adapter->adapter);
> >       if (rc)
> >               return rc;
> > diff --git a/lib/utils/i2c/i2c.c b/lib/utils/i2c/i2c.c
> > index 6be4e24..972c7bb 100644
> > --- a/lib/utils/i2c/i2c.c
> > +++ b/lib/utils/i2c/i2c.c
> > @@ -12,8 +12,12 @@
> >   */
> >
> >  #include <sbi/sbi_error.h>
> > +#include <sbi/sbi_string.h>
> >  #include <sbi_utils/i2c/i2c.h>
> >
> > +#define I2C_10BIT_MASK       0x7c
> > +#define I2C_10BIT_VAL        0x78
> > +
> >  static SBI_LIST_HEAD(i2c_adapters_list);
> >
> >  struct i2c_adapter *i2c_adapter_find(int id)
> > @@ -51,25 +55,116 @@ void i2c_adapter_remove(struct i2c_adapter *ia)
> >       sbi_list_del(&(ia->node));
> >  }
> >
> > -int i2c_adapter_write(struct i2c_adapter *ia, uint8_t addr, uint8_t
> > reg,
> > -                  uint8_t *buffer, int len)
> > +unsigned i2c_10bit_address(unsigned addr)
> >  {
> > -     if (!ia)
> > -             return SBI_EINVAL;
> > -     if (!ia->write)
> > -             return SBI_ENOSYS;
> > +     return (addr & 0x3ff) | (I2C_10BIT_VAL << 8);
> > +}
> >
> > -     return ia->write(ia, addr, reg, buffer, len);
> > +static inline int i2c_is_10bit_address(unsigned addr)
> > +{
> > +     return ((addr >> 8) & I2C_10BIT_MASK) == I2C_10BIT_VAL;
> >  }
> >
> > +static int i2c_adapter_read_helper(struct i2c_adapter *ia,
> > +             unsigned addr, uint8_t *buff, unsigned len, bool
> > send_stop) +{
> > +     int ret;
> > +     if (!i2c_is_10bit_address(addr)) {
> > +             ret = ia->write_byte(ia, (addr << 1) | I2C_OP_R,
> > true, false);
>
> Shoudn't there also be a hardware support for 10bit addresses ?
>
> I think it's up to driver to decide how to deal with 10bit addresses
>
> > +             if (ret)
> > +                     return ret;
> > +     } else {
> > +             unsigned addr_h = (addr >> 8) & 0x7f;
> > +             unsigned addr_l = (addr >> 0) & 0xff;
> > +             ret = ia->write_byte(ia, (addr_h << 1) | I2C_OP_W,
> > true, false);
> > +             if (ret)
> > +                     return ret;
> > +             ret = ia->write_byte(ia, addr_l, false, false);
> > +             if (ret)
> > +                     return ret;
> > +             ret = ia->write_byte(ia, (addr_h << 1) | I2C_OP_R,
> > true, false);
> > +             if (ret)
> > +                     return ret;
> > +     }
> > +
> > +     while (len--) {
> > +             ret = ia->read_byte(ia, buff, len == 0, send_stop &&
> > (len == 0));
> > +             if (ret)
> > +                     return ret;
> > +             buff++;
> > +     }
> > +     return ret;
> > +}
>
> I don't think sending should exposed to upper abstraction level, it's
> up to driver to decide how to read.
>
> >
> > -int i2c_adapter_read(struct i2c_adapter *ia, uint8_t addr, uint8_t
> > reg,
> > -                  uint8_t *buffer, int len)
> > +static int i2c_adapter_write_helper(struct i2c_adapter *ia,
> > +             unsigned addr, uint8_t *buff, unsigned len, bool
> > send_stop) {
> > -     if (!ia)
> > -             return SBI_EINVAL;
> > -     if (!ia->read)
> > -             return SBI_ENOSYS;
> > +     int ret;
> > +     if (!i2c_is_10bit_address(addr)) {
> > +             ret = ia->write_byte(ia, (addr << 1) | I2C_OP_W,
> > true, false);
> > +             if (ret)
> > +                     return ret;
> > +     } else {
> > +             unsigned addr_h = (addr >> 8) & 0x7f;
> > +             unsigned addr_l = (addr >> 0) & 0xff;
> > +             ret = ia->write_byte(ia, (addr_h << 1) | I2C_OP_W,
> > true, false);
> > +             if (ret)
> > +                     return ret;
> > +             ret = ia->write_byte(ia, addr_l, false, false);
> > +             if (ret)
> > +                     return ret;
> > +     }
> > +
> > +     while (len--) {
> > +             ret = ia->write_byte(ia, *buff, false,  send_stop &&
> > (len == 0));
> > +             if (ret)
> > +                     return ret;
> > +             buff++;
> > +     }
> > +     return ret;
> > +}
>
> Same here.
>
> > +
> > +int i2c_adapter_read(struct i2c_adapter *ia,
> > +             unsigned addr, uint8_t *buff, unsigned len)
> > +{
> > +     return i2c_adapter_read_helper(ia, addr, buff, len, true);
> > +}
> >
> > -     return ia->read(ia, addr, reg, buffer, len);
> > +int i2c_adapter_write(struct i2c_adapter *ia,
> > +             unsigned addr, uint8_t *buff, unsigned len)
> > +{
> > +     return i2c_adapter_write_helper(ia, addr, buff, len, true);
> >  }
> > +
> > +int i2c_adapter_comb(struct i2c_adapter *ia, unsigned addr,
> > +             enum i2c_op op0, uint8_t *buff0, unsigned len0,
> > +             enum i2c_op op1, uint8_t *buff1, unsigned len1)
> > +{
> > +     int ret;
> > +     switch (op0) {
> > +             case I2C_OP_W:
> > +                     ret = i2c_adapter_write_helper(ia, addr,
> > buff0, len0, false);
> > +                     break;
> > +             case I2C_OP_R:
> > +                     ret = i2c_adapter_read_helper(ia, addr,
> > buff0, len0, false);
> > +                     break;
> > +             default:
> > +                     return SBI_EINVAL;
> > +     }
> > +     if (ret)
> > +             return ret;
> > +
> > +     if (i2c_is_10bit_address(addr))
> > +             addr = (addr >> 8) & 0x7f;
> > +     switch (op0) {
> > +             case I2C_OP_W:
> > +                     ret = i2c_adapter_write_helper(ia, addr,
> > buff0, len0, true);
> > +                     break;
> > +             case I2C_OP_R:
> > +                     ret = i2c_adapter_read_helper(ia, addr,
> > buff0, len0, true);
> > +                     break;
> > +             default:
> > +                     return SBI_EINVAL;
> > +     }
> > +     return ret;
> > +}
>
> i2c_adapter_comb looks really cumbersome and counter intuitive to me.
> What are we trying to achieve with this ?
>
>
> > \ No newline at end of file
> > diff --git a/platform/generic/sifive_fu740.c
> > b/platform/generic/sifive_fu740.c index 333b3fb..8e42a5d 100644
> > --- a/platform/generic/sifive_fu740.c
> > +++ b/platform/generic/sifive_fu740.c
> > @@ -34,6 +34,21 @@
> >
> >  #define PMIC_CHIP_ID_DA9063          0x61
> >
> > +static inline int da9063_read_reg(struct i2c_adapter *adap, uint8_t
> > addr,
> > +     uint8_t reg, uint8_t *val)
> > +{
> > +     return i2c_adapter_comb(adap, addr, I2C_OP_W, &reg, 1,
> > I2C_OP_R, val, 1); +}
> > +
> > +static inline int da9063_write_reg(struct i2c_adapter *adap, uint8_t
> > addr,
> > +     uint8_t reg, uint8_t val)
> > +{
> > +     uint8_t buff[2];
> > +     buff[0] = reg;
> > +     buff[1] = val;
> > +     return i2c_adapter_write(adap, addr, buff, 2);
> > +}
> > +
> >  static struct {
> >       struct i2c_adapter *adapter;
> >       uint32_t reg;
> > @@ -55,13 +70,13 @@ static int da9063_system_reset_check(u32 type,
> > u32 reason) static inline int da9063_sanity_check(struct i2c_adapter
> > *adap, uint32_t reg) {
> >       uint8_t val;
> > -     int rc = i2c_adapter_reg_write(adap, reg,
> > DA9063_REG_PAGE_CON, 0x02);
> > +     int rc = da9063_write_reg(adap, reg, DA9063_REG_PAGE_CON,
> > 0x02);
> >       if (rc)
> >               return rc;
> >
> >       /* check set page*/
> > -     rc = i2c_adapter_reg_read(adap, reg, 0x0, &val);
> > +     rc = da9063_read_reg(adap, reg, 0x0, &val);
> >       if (rc)
> >               return rc;
> >
> > @@ -69,7 +84,7 @@ static inline int da9063_sanity_check(struct
> > i2c_adapter *adap, uint32_t reg) return SBI_ENODEV;
> >
> >       /* read and check device id */
> > -     rc = i2c_adapter_reg_read(adap, reg, DA9063_REG_DEVICE_ID,
> > &val);
> > +     rc = da9063_read_reg(adap, reg, DA9063_REG_DEVICE_ID, &val);
> >       if (rc)
> >               return rc;
> >
> > @@ -81,32 +96,32 @@ static inline int da9063_sanity_check(struct
> > i2c_adapter *adap, uint32_t reg)
> >  static inline int da9063_shutdown(struct i2c_adapter *adap, uint32_t
> > reg) {
> > -     int rc = i2c_adapter_reg_write(adap, da9063.reg,
> > +     int rc = da9063_write_reg(adap, da9063.reg,
> >                                       DA9063_REG_PAGE_CON, 0x00);
> >
> >       if (rc)
> >               return rc;
> >
> > -     return i2c_adapter_reg_write(adap, da9063.reg,
> > +     return da9063_write_reg(adap, da9063.reg,
> >                                    DA9063_REG_CONTROL_F,
> >                                    DA9063_CONTROL_F_SHUTDOWN);
> >  }
> >
> >  static inline int da9063_reset(struct i2c_adapter *adap, uint32_t
> > reg) {
> > -     int rc = i2c_adapter_reg_write(adap, da9063.reg,
> > +     int rc = da9063_write_reg(adap, da9063.reg,
> >                                       DA9063_REG_PAGE_CON, 0x00);
> >
> >       if (rc)
> >               return rc;
> >
> > -     rc = i2c_adapter_reg_write(adap, da9063.reg,
> > +     rc = da9063_write_reg(adap, da9063.reg,
> >                                  DA9063_REG_CONTROL_F,
> >                                  DA9063_CONTROL_F_WAKEUP);
> >       if (rc)
> >               return rc;
> >
> > -     return i2c_adapter_reg_write(adap, da9063.reg,
> > +     return da9063_write_reg(adap, da9063.reg,
> >                               DA9063_REG_CONTROL_A,
> >                               DA9063_CONTROL_A_M_POWER1_EN |
> >                               DA9063_CONTROL_A_M_POWER_EN |
>
>
> --
> opensbi mailing list
> opensbi@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi
Xiang W Dec. 30, 2021, 1 p.m. UTC | #4
在 2021-12-30星期四的 18:25 +0530,Anup Patel写道:
> On Thu, Dec 30, 2021 at 3:00 PM Nikita Shubin <
> nikita.shubin@maquefel.me> wrote:
> > 
> > Hello Xiang!
> > 
> > Thank you for your patch.
> > 
> > On Thu, 30 Dec 2021 01:39:09 +0800
> > Xiang W <wxjstz@126.com> wrote:
> > 
> > > 1. Remove i2c register related operations
> > > 2. Simplify the low-level interface
> > > 3. Add 10bit address support
> > > 4. Add combination operation
> > > 5. Update fdt_i2c_sifive.c
> > > 6. Update sifive_fu740.c
> > > 
> > 
> > First of all my Unmatched board no longer reboots with your patch
> > applied:
> > 
> > [   53.068656] reboot: Restarting system
> > da9063_system_reset: chip is not da9063 PMIC
> > 
> > What kind of hardware are you using currently ?
> > 
> > In general i think raw read/write possibility is really needed, but
> > this kind of functionality can be achieved with flags without
> > exposing
> > all this stuff in i2c.c,
> > 
> > I was thinking on something like "struct i2c_msg" Linux have:
> > 
> > https://elixir.bootlin.com/linux/v5.16-rc7/source/include/uapi/linux/i2c.h#L73
> 
> I am also inclined towards using "struct i2c_msg". It's simple and
> extensible.
> 
> Regards,
> Anup

These two interfaces only send one byte, which is to simplify the low-
level implementation.

We can use struct i2c_msg on the upper interface.

I will update soon.

Regards,
Xiang W

> 
> > 
> > We can still read/write a byte with this, ask for nostop and nack -
> > can't we ?
> > 
> > Please see my comments below:
> > 
> > > Signed-off-by: Xiang W <wxjstz@126.com>
> > > ---
> > >  include/sbi_utils/i2c/i2c.h     |  80 ++++++++++----------
> > >  lib/utils/i2c/fdt_i2c_sifive.c  | 130
> > > +++++++++----------------------- lib/utils/i2c/i2c.c             |
> > > 123 ++++++++++++++++++++++++++---- platform/generic/sifive_fu740.c
> > > |
> > > 31 ++++++-- 4 files changed, 208 insertions(+), 156 deletions(-)
> > > 
> > > diff --git a/include/sbi_utils/i2c/i2c.h
> > > b/include/sbi_utils/i2c/i2c.h
> > > index 5a70364..53e76e3 100644
> > > --- a/include/sbi_utils/i2c/i2c.h
> > > +++ b/include/sbi_utils/i2c/i2c.h
> > > @@ -13,6 +13,11 @@
> > >  #include <sbi/sbi_types.h>
> > >  #include <sbi/sbi_list.h>
> > > 
> > > +enum i2c_op {
> > > +     I2C_OP_W = 0,
> > > +     I2C_OP_R = 1
> > > +};
> > > +
> > >  /** Representation of a I2C adapter */
> > >  struct i2c_adapter {
> > >       /** Pointer to I2C driver owning this I2C adapter */
> > > @@ -21,21 +26,11 @@ struct i2c_adapter {
> > >       /** Unique ID of the I2C adapter assigned by the driver */
> > >       int id;
> > > 
> > > -     /**
> > > -      * Send buffer to given address, register
> > > -      *
> > > -      * @return 0 on success and negative error code on failure
> > > -      */
> > > -     int (*write)(struct i2c_adapter *ia, uint8_t addr, uint8_t
> > > reg,
> > > -                 uint8_t *buffer, int len);
> > > -
> > > -     /**
> > > -      * Read buffer from given address, register
> > > -      *
> > > -      * @return 0 on success and negative error code on failure
> > > -      */
> > > -     int (*read)(struct i2c_adapter *ia, uint8_t addr, uint8_t
> > > reg,
> > > -                 uint8_t *buffer, int len);
> > > +     int (*write_byte)(struct i2c_adapter *ia, uint8_t byte,
> > > +                     bool send_start, bool send_stop);
> > > +
> > > +     int (*read_byte)(struct i2c_adapter *ia, uint8_t *byte,
> > > +                     bool nack, bool send_stop);
> > > 
> > 
> > Can't we use flags for bool nack, bool send_stop ?
> > 
> > 
> > >       /** List */
> > >       struct sbi_dlist node;
> > > @@ -46,6 +41,13 @@ static inline struct i2c_adapter
> > > *to_i2c_adapter(struct sbi_dlist *node) return container_of(node,
> > > struct i2c_adapter, node); }
> > > 
> > > +/**
> > > + * This function is used to pack a 10bit address into a 15-bit
> > > address.
> > > + * The 10bit address during read and write operations needs to be
> > > processed by
> > > + * this function.
> > > + * */
> > > +unsigned i2c_10bit_address(unsigned addr);
> > > +
> > >  /** Find a registered I2C adapter */
> > >  struct i2c_adapter *i2c_adapter_find(int id);
> > > 
> > > @@ -55,31 +57,27 @@ int i2c_adapter_add(struct i2c_adapter *ia);
> > >  /** Un-register I2C adapter */
> > >  void i2c_adapter_remove(struct i2c_adapter *ia);
> > > 
> > > -/** Send to device on I2C adapter bus */
> > > -int i2c_adapter_write(struct i2c_adapter *ia, uint8_t addr,
> > > uint8_t
> > > reg,
> > > -                   uint8_t *buffer, int len);
> > > -
> > > -/** Read from device on I2C adapter bus */
> > > -int i2c_adapter_read(struct i2c_adapter *ia, uint8_t addr,
> > > uint8_t
> > > reg,
> > > -                  uint8_t *buffer, int len);
> > > -
> > > -static inline int i2c_adapter_reg_write(struct i2c_adapter *ia,
> > > uint8_t addr,
> > > -                              uint8_t reg, uint8_t val)
> > > -{
> > > -     return i2c_adapter_write(ia, addr, reg, &val, 1);
> > > -}
> > > -
> > > -static inline int i2c_adapter_reg_read(struct i2c_adapter *ia,
> > > uint8_t addr,
> > > -                                    uint8_t reg, uint8_t *val)
> > > -{
> > > -     uint8_t buf;
> > > -     int ret = i2c_adapter_read(ia, addr, reg, &buf, 1);
> > > -
> > > -     if (ret)
> > > -             return ret;
> > > -
> > > -     *val = buf;
> > > -     return 0;
> > > -}
> > > +/**
> > > + * Read data from the device with address addr to buff
> > > + * For 10bit address operation, addr = i2c_10bit_address
> > > (orig_addr)
> > > +*/
> > > +int i2c_adapter_read(struct i2c_adapter *ia, unsigned addr,
> > > +             uint8_t *buff, unsigned len);
> > > +
> > > +/**
> > > + * Write buff data to the device with address addr
> > > + * For 10bit address operation, addr = i2c_10bit_address
> > > (orig_addr)
> > > +*/
> > > +int i2c_adapter_write(struct i2c_adapter *ia, unsigned addr,
> > > +             uint8_t *buff, unsigned len);
> > > +
> > > +/**
> > > + * Combined operation, there is no stop signal between two iic
> > > operations
> > > + * For 10bit address operation, addr = i2c_10bit_address
> > > (orig_addr)
> > > + * op0 and op1 can only be I2C_OP_R (read operate) or I2C_OP_W
> > > (write operate)
> > > + */
> > > +int i2c_adapter_comb(struct i2c_adapter *ia, unsigned addr,
> > > +             enum i2c_op op0, uint8_t *buff0, unsigned len0,
> > > +             enum i2c_op op1, uint8_t *buff1, unsigned len1);
> > > 
> > 
> > Why should we need this, if you already exposed bool nack, bool
> > send_stop in read_byte, write_byte ?
> > 
> > 
> > >  #endif
> > > diff --git a/lib/utils/i2c/fdt_i2c_sifive.c
> > > b/lib/utils/i2c/fdt_i2c_sifive.c index 871241a..bb0456f 100644
> > > --- a/lib/utils/i2c/fdt_i2c_sifive.c
> > > +++ b/lib/utils/i2c/fdt_i2c_sifive.c
> > > @@ -113,127 +113,71 @@ static int sifive_i2c_adapter_start(struct
> > > sifive_i2c_adapter *adap, return
> > > sifive_i2c_adapter_poll_tip(adap);
> > >  }
> > > 
> > > -static int sifive_i2c_adapter_write(struct i2c_adapter *ia,
> > > uint8_t
> > > addr,
> > > -                                 uint8_t reg, uint8_t *buffer,
> > > int len) +static int sifive_i2c_adapter_write_byte(struct
> > > i2c_adapter
> > > *ia,
> > > +                             uint8_t byte, bool send_start, bool
> > > send_stop) {
> > >       struct sifive_i2c_adapter *adap =
> > >               container_of(ia, struct sifive_i2c_adapter,
> > > adapter);
> > > -     int rc = sifive_i2c_adapter_start(adap, addr,
> > > SIFIVE_I2C_WRITE_BIT); -
> > > -     if (rc)
> > > -             return rc;
> > > -
> > > -     rc = sifive_i2c_adapter_rxack(adap);
> > > -     if (rc)
> > > -             return rc;
> > > -
> > > -     /* set register address */
> > > -     sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, reg);
> > > -     sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR |
> > > -                             SIFIVE_I2C_CMD_IACK);
> > > -     rc = sifive_i2c_adapter_poll_tip(adap);
> > > -     if (rc)
> > > -             return rc;
> > > -
> > > -     rc = sifive_i2c_adapter_rxack(adap);
> > > -     if (rc)
> > > -             return rc;
> > > -
> > > -     /* set value */
> > > -     while (len) {
> > > -             sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, *buffer);
> > > +     int rc;
> > > +     if (send_start) {
> > > +             rc = sifive_i2c_adapter_start(adap, byte >> 1, byte
> > > & 1);
> > > +             if (rc)
> > > +                     return rc;
> > > +     } else {
> > > +             sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, byte);
> > >               sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > > SIFIVE_I2C_CMD_WR | SIFIVE_I2C_CMD_IACK);
> > > 
> > >               rc = sifive_i2c_adapter_poll_tip(adap);
> > >               if (rc)
> > >                       return rc;
> > > -
> > > -             rc = sifive_i2c_adapter_rxack(adap);
> > > -             if (rc)
> > > -                     return rc;
> > > -
> > > -             buffer++;
> > > -             len--;
> > >       }
> > > -
> > > -     sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_STO |
> > > -                                            SIFIVE_I2C_CMD_IACK);
> > > -
> > > -     /* poll BUSY instead of ACK*/
> > > -     rc = sifive_i2c_adapter_poll_busy(adap);
> > > +     rc = sifive_i2c_adapter_rxack(adap);
> > >       if (rc)
> > >               return rc;
> > > 
> > > -     sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_IACK);
> > > +     if (send_stop) {
> > > +             sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > > SIFIVE_I2C_CMD_STO |
> > > +                                            SIFIVE_I2C_CMD_IACK);
> > > +             rc = sifive_i2c_adapter_poll_busy(adap);
> > > +             if (rc)
> > > +                     return rc;
> > > 
> > > +             sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > > SIFIVE_I2C_CMD_IACK);
> > > +     }
> > >       return 0;
> > >  }
> > > 
> > > -static int sifive_i2c_adapter_read(struct i2c_adapter *ia,
> > > uint8_t
> > > addr,
> > > -                                uint8_t reg, uint8_t *buffer, int
> > > len) +static int sifive_i2c_adapter_read_byte(struct i2c_adapter
> > > *ia,
> > > +                             uint8_t *byte, bool nack, bool
> > > send_stop) {
> > >       struct sifive_i2c_adapter *adap =
> > >               container_of(ia, struct sifive_i2c_adapter,
> > > adapter);
> > >       int rc;
> > > -
> > > -     rc = sifive_i2c_adapter_start(adap, addr,
> > > SIFIVE_I2C_WRITE_BIT);
> > > -     if (rc)
> > > -             return rc;
> > > -
> > > -     rc = sifive_i2c_adapter_rxack(adap);
> > > -     if (rc)
> > > -             return rc;
> > > -
> > > -     sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, reg);
> > > -     sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR |
> > > -                                            SIFIVE_I2C_CMD_IACK);
> > > -
> > > +     if (nack)
> > > +             sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > > +                                     SIFIVE_I2C_CMD_ACK |
> > > +                                     SIFIVE_I2C_CMD_RD |
> > > +                                     SIFIVE_I2C_CMD_IACK);
> > > +     else
> > > +             sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > > +                                     SIFIVE_I2C_CMD_RD |
> > > +                                     SIFIVE_I2C_CMD_IACK);
> > >       rc = sifive_i2c_adapter_poll_tip(adap);
> > >       if (rc)
> > >               return rc;
> > > 
> > > -     rc = sifive_i2c_adapter_rxack(adap);
> > > -     if (rc)
> > > -             return rc;
> > > -
> > > -     /* setting addr with high 0 bit */
> > > -     rc = sifive_i2c_adapter_start(adap, addr,
> > > SIFIVE_I2C_READ_BIT);
> > > -     if (rc)
> > > -             return rc;
> > > -
> > > -     rc = sifive_i2c_adapter_rxack(adap);
> > > -     if (rc)
> > > -             return rc;
> > > +     *byte = sifive_i2c_getreg(adap, SIFIVE_I2C_RXR);
> > > 
> > > -     while (len) {
> > > -             if (len == 1)
> > > -                     sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > > -                                       SIFIVE_I2C_CMD_ACK |
> > > -                                       SIFIVE_I2C_CMD_RD |
> > > -                                       SIFIVE_I2C_CMD_IACK);
> > > -             else
> > > -                     sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > > -                                       SIFIVE_I2C_CMD_RD |
> > > -                                       SIFIVE_I2C_CMD_IACK);
> > > -
> > > -             rc = sifive_i2c_adapter_poll_tip(adap);
> > > +     if (send_stop) {
> > > +             sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > > SIFIVE_I2C_CMD_STO |
> > > +                                            SIFIVE_I2C_CMD_IACK);
> > > +             rc = sifive_i2c_adapter_poll_busy(adap);
> > >               if (rc)
> > >                       return rc;
> > > 
> > > -             *buffer = sifive_i2c_getreg(adap, SIFIVE_I2C_RXR);
> > > -             buffer++;
> > > -             len--;
> > > +             sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
> > > SIFIVE_I2C_CMD_IACK); }
> > > -
> > > -     sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_STO |
> > > -                                            SIFIVE_I2C_CMD_IACK);
> > > -     rc = sifive_i2c_adapter_poll_busy(adap);
> > > -     if (rc)
> > > -             return rc;
> > > -
> > > -     sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_IACK);
> > > -
> > >       return 0;
> > >  }
> > > 
> > > @@ -256,8 +200,8 @@ static int sifive_i2c_init(void *fdt, int
> > > nodeoff,
> > >       adapter->addr = addr;
> > >       adapter->adapter.driver = &fdt_i2c_adapter_sifive;
> > >       adapter->adapter.id = nodeoff;
> > > -     adapter->adapter.write = sifive_i2c_adapter_write;
> > > -     adapter->adapter.read = sifive_i2c_adapter_read;
> > > +     adapter->adapter.write_byte = sifive_i2c_adapter_write_byte;
> > > +     adapter->adapter.read_byte = sifive_i2c_adapter_read_byte;
> > >       rc = i2c_adapter_add(&adapter->adapter);
> > >       if (rc)
> > >               return rc;
> > > diff --git a/lib/utils/i2c/i2c.c b/lib/utils/i2c/i2c.c
> > > index 6be4e24..972c7bb 100644
> > > --- a/lib/utils/i2c/i2c.c
> > > +++ b/lib/utils/i2c/i2c.c
> > > @@ -12,8 +12,12 @@
> > >   */
> > > 
> > >  #include <sbi/sbi_error.h>
> > > +#include <sbi/sbi_string.h>
> > >  #include <sbi_utils/i2c/i2c.h>
> > > 
> > > +#define I2C_10BIT_MASK       0x7c
> > > +#define I2C_10BIT_VAL        0x78
> > > +
> > >  static SBI_LIST_HEAD(i2c_adapters_list);
> > > 
> > >  struct i2c_adapter *i2c_adapter_find(int id)
> > > @@ -51,25 +55,116 @@ void i2c_adapter_remove(struct i2c_adapter
> > > *ia)
> > >       sbi_list_del(&(ia->node));
> > >  }
> > > 
> > > -int i2c_adapter_write(struct i2c_adapter *ia, uint8_t addr,
> > > uint8_t
> > > reg,
> > > -                  uint8_t *buffer, int len)
> > > +unsigned i2c_10bit_address(unsigned addr)
> > >  {
> > > -     if (!ia)
> > > -             return SBI_EINVAL;
> > > -     if (!ia->write)
> > > -             return SBI_ENOSYS;
> > > +     return (addr & 0x3ff) | (I2C_10BIT_VAL << 8);
> > > +}
> > > 
> > > -     return ia->write(ia, addr, reg, buffer, len);
> > > +static inline int i2c_is_10bit_address(unsigned addr)
> > > +{
> > > +     return ((addr >> 8) & I2C_10BIT_MASK) == I2C_10BIT_VAL;
> > >  }
> > > 
> > > +static int i2c_adapter_read_helper(struct i2c_adapter *ia,
> > > +             unsigned addr, uint8_t *buff, unsigned len, bool
> > > send_stop) +{
> > > +     int ret;
> > > +     if (!i2c_is_10bit_address(addr)) {
> > > +             ret = ia->write_byte(ia, (addr << 1) | I2C_OP_R,
> > > true, false);
> > 
> > Shoudn't there also be a hardware support for 10bit addresses ?
> > 
> > I think it's up to driver to decide how to deal with 10bit addresses
> > 
> > > +             if (ret)
> > > +                     return ret;
> > > +     } else {
> > > +             unsigned addr_h = (addr >> 8) & 0x7f;
> > > +             unsigned addr_l = (addr >> 0) & 0xff;
> > > +             ret = ia->write_byte(ia, (addr_h << 1) | I2C_OP_W,
> > > true, false);
> > > +             if (ret)
> > > +                     return ret;
> > > +             ret = ia->write_byte(ia, addr_l, false, false);
> > > +             if (ret)
> > > +                     return ret;
> > > +             ret = ia->write_byte(ia, (addr_h << 1) | I2C_OP_R,
> > > true, false);
> > > +             if (ret)
> > > +                     return ret;
> > > +     }
> > > +
> > > +     while (len--) {
> > > +             ret = ia->read_byte(ia, buff, len == 0, send_stop &&
> > > (len == 0));
> > > +             if (ret)
> > > +                     return ret;
> > > +             buff++;
> > > +     }
> > > +     return ret;
> > > +}
> > 
> > I don't think sending should exposed to upper abstraction level,
> > it's
> > up to driver to decide how to read.
> > 
> > > 
> > > -int i2c_adapter_read(struct i2c_adapter *ia, uint8_t addr,
> > > uint8_t
> > > reg,
> > > -                  uint8_t *buffer, int len)
> > > +static int i2c_adapter_write_helper(struct i2c_adapter *ia,
> > > +             unsigned addr, uint8_t *buff, unsigned len, bool
> > > send_stop) {
> > > -     if (!ia)
> > > -             return SBI_EINVAL;
> > > -     if (!ia->read)
> > > -             return SBI_ENOSYS;
> > > +     int ret;
> > > +     if (!i2c_is_10bit_address(addr)) {
> > > +             ret = ia->write_byte(ia, (addr << 1) | I2C_OP_W,
> > > true, false);
> > > +             if (ret)
> > > +                     return ret;
> > > +     } else {
> > > +             unsigned addr_h = (addr >> 8) & 0x7f;
> > > +             unsigned addr_l = (addr >> 0) & 0xff;
> > > +             ret = ia->write_byte(ia, (addr_h << 1) | I2C_OP_W,
> > > true, false);
> > > +             if (ret)
> > > +                     return ret;
> > > +             ret = ia->write_byte(ia, addr_l, false, false);
> > > +             if (ret)
> > > +                     return ret;
> > > +     }
> > > +
> > > +     while (len--) {
> > > +             ret = ia->write_byte(ia, *buff, false,  send_stop &&
> > > (len == 0));
> > > +             if (ret)
> > > +                     return ret;
> > > +             buff++;
> > > +     }
> > > +     return ret;
> > > +}
> > 
> > Same here.
> > 
> > > +
> > > +int i2c_adapter_read(struct i2c_adapter *ia,
> > > +             unsigned addr, uint8_t *buff, unsigned len)
> > > +{
> > > +     return i2c_adapter_read_helper(ia, addr, buff, len, true);
> > > +}
> > > 
> > > -     return ia->read(ia, addr, reg, buffer, len);
> > > +int i2c_adapter_write(struct i2c_adapter *ia,
> > > +             unsigned addr, uint8_t *buff, unsigned len)
> > > +{
> > > +     return i2c_adapter_write_helper(ia, addr, buff, len, true);
> > >  }
> > > +
> > > +int i2c_adapter_comb(struct i2c_adapter *ia, unsigned addr,
> > > +             enum i2c_op op0, uint8_t *buff0, unsigned len0,
> > > +             enum i2c_op op1, uint8_t *buff1, unsigned len1)
> > > +{
> > > +     int ret;
> > > +     switch (op0) {
> > > +             case I2C_OP_W:
> > > +                     ret = i2c_adapter_write_helper(ia, addr,
> > > buff0, len0, false);
> > > +                     break;
> > > +             case I2C_OP_R:
> > > +                     ret = i2c_adapter_read_helper(ia, addr,
> > > buff0, len0, false);
> > > +                     break;
> > > +             default:
> > > +                     return SBI_EINVAL;
> > > +     }
> > > +     if (ret)
> > > +             return ret;
> > > +
> > > +     if (i2c_is_10bit_address(addr))
> > > +             addr = (addr >> 8) & 0x7f;
> > > +     switch (op0) {
> > > +             case I2C_OP_W:
> > > +                     ret = i2c_adapter_write_helper(ia, addr,
> > > buff0, len0, true);
> > > +                     break;
> > > +             case I2C_OP_R:
> > > +                     ret = i2c_adapter_read_helper(ia, addr,
> > > buff0, len0, true);
> > > +                     break;
> > > +             default:
> > > +                     return SBI_EINVAL;
> > > +     }
> > > +     return ret;
> > > +}
> > 
> > i2c_adapter_comb looks really cumbersome and counter intuitive to
> > me.
> > What are we trying to achieve with this ?
> > 
> > 
> > > \ No newline at end of file
> > > diff --git a/platform/generic/sifive_fu740.c
> > > b/platform/generic/sifive_fu740.c index 333b3fb..8e42a5d 100644
> > > --- a/platform/generic/sifive_fu740.c
> > > +++ b/platform/generic/sifive_fu740.c
> > > @@ -34,6 +34,21 @@
> > > 
> > >  #define PMIC_CHIP_ID_DA9063          0x61
> > > 
> > > +static inline int da9063_read_reg(struct i2c_adapter *adap,
> > > uint8_t
> > > addr,
> > > +     uint8_t reg, uint8_t *val)
> > > +{
> > > +     return i2c_adapter_comb(adap, addr, I2C_OP_W, &reg, 1,
> > > I2C_OP_R, val, 1); +}
> > > +
> > > +static inline int da9063_write_reg(struct i2c_adapter *adap,
> > > uint8_t
> > > addr,
> > > +     uint8_t reg, uint8_t val)
> > > +{
> > > +     uint8_t buff[2];
> > > +     buff[0] = reg;
> > > +     buff[1] = val;
> > > +     return i2c_adapter_write(adap, addr, buff, 2);
> > > +}
> > > +
> > >  static struct {
> > >       struct i2c_adapter *adapter;
> > >       uint32_t reg;
> > > @@ -55,13 +70,13 @@ static int da9063_system_reset_check(u32 type,
> > > u32 reason) static inline int da9063_sanity_check(struct
> > > i2c_adapter
> > > *adap, uint32_t reg) {
> > >       uint8_t val;
> > > -     int rc = i2c_adapter_reg_write(adap, reg,
> > > DA9063_REG_PAGE_CON, 0x02);
> > > +     int rc = da9063_write_reg(adap, reg, DA9063_REG_PAGE_CON,
> > > 0x02);
> > >       if (rc)
> > >               return rc;
> > > 
> > >       /* check set page*/
> > > -     rc = i2c_adapter_reg_read(adap, reg, 0x0, &val);
> > > +     rc = da9063_read_reg(adap, reg, 0x0, &val);
> > >       if (rc)
> > >               return rc;
> > > 
> > > @@ -69,7 +84,7 @@ static inline int da9063_sanity_check(struct
> > > i2c_adapter *adap, uint32_t reg) return SBI_ENODEV;
> > > 
> > >       /* read and check device id */
> > > -     rc = i2c_adapter_reg_read(adap, reg, DA9063_REG_DEVICE_ID,
> > > &val);
> > > +     rc = da9063_read_reg(adap, reg, DA9063_REG_DEVICE_ID, &val);
> > >       if (rc)
> > >               return rc;
> > > 
> > > @@ -81,32 +96,32 @@ static inline int da9063_sanity_check(struct
> > > i2c_adapter *adap, uint32_t reg)
> > >  static inline int da9063_shutdown(struct i2c_adapter *adap,
> > > uint32_t
> > > reg) {
> > > -     int rc = i2c_adapter_reg_write(adap, da9063.reg,
> > > +     int rc = da9063_write_reg(adap, da9063.reg,
> > >                                       DA9063_REG_PAGE_CON, 0x00);
> > > 
> > >       if (rc)
> > >               return rc;
> > > 
> > > -     return i2c_adapter_reg_write(adap, da9063.reg,
> > > +     return da9063_write_reg(adap, da9063.reg,
> > >                                    DA9063_REG_CONTROL_F,
> > >                                    DA9063_CONTROL_F_SHUTDOWN);
> > >  }
> > > 
> > >  static inline int da9063_reset(struct i2c_adapter *adap, uint32_t
> > > reg) {
> > > -     int rc = i2c_adapter_reg_write(adap, da9063.reg,
> > > +     int rc = da9063_write_reg(adap, da9063.reg,
> > >                                       DA9063_REG_PAGE_CON, 0x00);
> > > 
> > >       if (rc)
> > >               return rc;
> > > 
> > > -     rc = i2c_adapter_reg_write(adap, da9063.reg,
> > > +     rc = da9063_write_reg(adap, da9063.reg,
> > >                                  DA9063_REG_CONTROL_F,
> > >                                  DA9063_CONTROL_F_WAKEUP);
> > >       if (rc)
> > >               return rc;
> > > 
> > > -     return i2c_adapter_reg_write(adap, da9063.reg,
> > > +     return da9063_write_reg(adap, da9063.reg,
> > >                               DA9063_REG_CONTROL_A,
> > >                               DA9063_CONTROL_A_M_POWER1_EN |
> > >                               DA9063_CONTROL_A_M_POWER_EN |
> > 
> > 
> > --
> > opensbi mailing list
> > opensbi@lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/opensbi
diff mbox series

Patch

diff --git a/include/sbi_utils/i2c/i2c.h b/include/sbi_utils/i2c/i2c.h
index 5a70364..53e76e3 100644
--- a/include/sbi_utils/i2c/i2c.h
+++ b/include/sbi_utils/i2c/i2c.h
@@ -13,6 +13,11 @@ 
 #include <sbi/sbi_types.h>
 #include <sbi/sbi_list.h>
 
+enum i2c_op {
+	I2C_OP_W = 0,
+	I2C_OP_R = 1
+};
+
 /** Representation of a I2C adapter */
 struct i2c_adapter {
 	/** Pointer to I2C driver owning this I2C adapter */
@@ -21,21 +26,11 @@  struct i2c_adapter {
 	/** Unique ID of the I2C adapter assigned by the driver */
 	int id;
 
-	/**
-	 * Send buffer to given address, register
-	 *
-	 * @return 0 on success and negative error code on failure
-	 */
-	int (*write)(struct i2c_adapter *ia, uint8_t addr, uint8_t reg,
-		    uint8_t *buffer, int len);
-
-	/**
-	 * Read buffer from given address, register
-	 *
-	 * @return 0 on success and negative error code on failure
-	 */
-	int (*read)(struct i2c_adapter *ia, uint8_t addr, uint8_t reg,
-		    uint8_t *buffer, int len);
+	int (*write_byte)(struct i2c_adapter *ia, uint8_t byte,
+			bool send_start, bool send_stop);
+
+	int (*read_byte)(struct i2c_adapter *ia, uint8_t *byte,
+			bool nack, bool send_stop);
 
 	/** List */
 	struct sbi_dlist node;
@@ -46,6 +41,13 @@  static inline struct i2c_adapter *to_i2c_adapter(struct sbi_dlist *node)
 	return container_of(node, struct i2c_adapter, node);
 }
 
+/**
+ * This function is used to pack a 10bit address into a 15-bit address.
+ * The 10bit address during read and write operations needs to be processed by
+ * this function.
+ * */
+unsigned i2c_10bit_address(unsigned addr);
+
 /** Find a registered I2C adapter */
 struct i2c_adapter *i2c_adapter_find(int id);
 
@@ -55,31 +57,27 @@  int i2c_adapter_add(struct i2c_adapter *ia);
 /** Un-register I2C adapter */
 void i2c_adapter_remove(struct i2c_adapter *ia);
 
-/** Send to device on I2C adapter bus */
-int i2c_adapter_write(struct i2c_adapter *ia, uint8_t addr, uint8_t reg,
-		      uint8_t *buffer, int len);
-
-/** Read from device on I2C adapter bus */
-int i2c_adapter_read(struct i2c_adapter *ia, uint8_t addr, uint8_t reg,
-		     uint8_t *buffer, int len);
-
-static inline int i2c_adapter_reg_write(struct i2c_adapter *ia, uint8_t addr,
-				 uint8_t reg, uint8_t val)
-{
-	return i2c_adapter_write(ia, addr, reg, &val, 1);
-}
-
-static inline int i2c_adapter_reg_read(struct i2c_adapter *ia, uint8_t addr,
-				       uint8_t reg, uint8_t *val)
-{
-	uint8_t buf;
-	int ret = i2c_adapter_read(ia, addr, reg, &buf, 1);
-
-	if (ret)
-		return ret;
-
-	*val = buf;
-	return 0;
-}
+/**
+ * Read data from the device with address addr to buff
+ * For 10bit address operation, addr = i2c_10bit_address (orig_addr)
+*/
+int i2c_adapter_read(struct i2c_adapter *ia, unsigned addr,
+		uint8_t *buff, unsigned len);
+
+/**
+ * Write buff data to the device with address addr
+ * For 10bit address operation, addr = i2c_10bit_address (orig_addr)
+*/
+int i2c_adapter_write(struct i2c_adapter *ia, unsigned addr,
+		uint8_t *buff, unsigned len);
+
+/**
+ * Combined operation, there is no stop signal between two iic operations
+ * For 10bit address operation, addr = i2c_10bit_address (orig_addr)
+ * op0 and op1 can only be I2C_OP_R (read operate) or I2C_OP_W (write operate)
+ */
+int i2c_adapter_comb(struct i2c_adapter *ia, unsigned addr,
+		enum i2c_op op0, uint8_t *buff0, unsigned len0,
+		enum i2c_op op1, uint8_t *buff1, unsigned len1);
 
 #endif
diff --git a/lib/utils/i2c/fdt_i2c_sifive.c b/lib/utils/i2c/fdt_i2c_sifive.c
index 871241a..bb0456f 100644
--- a/lib/utils/i2c/fdt_i2c_sifive.c
+++ b/lib/utils/i2c/fdt_i2c_sifive.c
@@ -113,127 +113,71 @@  static int sifive_i2c_adapter_start(struct sifive_i2c_adapter *adap,
 	return sifive_i2c_adapter_poll_tip(adap);
 }
 
-static int sifive_i2c_adapter_write(struct i2c_adapter *ia, uint8_t addr,
-				    uint8_t reg, uint8_t *buffer, int len)
+static int sifive_i2c_adapter_write_byte(struct i2c_adapter *ia,
+				uint8_t byte, bool send_start, bool send_stop)
 {
 	struct sifive_i2c_adapter *adap =
 		container_of(ia, struct sifive_i2c_adapter, adapter);
-	int rc = sifive_i2c_adapter_start(adap, addr, SIFIVE_I2C_WRITE_BIT);
-
-	if (rc)
-		return rc;
-
-	rc = sifive_i2c_adapter_rxack(adap);
-	if (rc)
-		return rc;
-
-	/* set register address */
-	sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, reg);
-	sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR |
-				SIFIVE_I2C_CMD_IACK);
-	rc = sifive_i2c_adapter_poll_tip(adap);
-	if (rc)
-		return rc;
-
-	rc = sifive_i2c_adapter_rxack(adap);
-	if (rc)
-		return rc;
-
-	/* set value */
-	while (len) {
-		sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, *buffer);
+	int rc;
+	if (send_start) {
+		rc = sifive_i2c_adapter_start(adap, byte >> 1, byte & 1);
+		if (rc)
+			return rc;
+	} else {
+		sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, byte);
 		sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR |
 						       SIFIVE_I2C_CMD_IACK);
 
 		rc = sifive_i2c_adapter_poll_tip(adap);
 		if (rc)
 			return rc;
-
-		rc = sifive_i2c_adapter_rxack(adap);
-		if (rc)
-			return rc;
-
-		buffer++;
-		len--;
 	}
-
-	sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_STO |
-					       SIFIVE_I2C_CMD_IACK);
-
-	/* poll BUSY instead of ACK*/
-	rc = sifive_i2c_adapter_poll_busy(adap);
+	rc = sifive_i2c_adapter_rxack(adap);
 	if (rc)
 		return rc;
 
-	sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_IACK);
+	if (send_stop) {
+		sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_STO |
+					       SIFIVE_I2C_CMD_IACK);
+		rc = sifive_i2c_adapter_poll_busy(adap);
+		if (rc)
+			return rc;
 
+		sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_IACK);
+	}
 	return 0;
 }
 
-static int sifive_i2c_adapter_read(struct i2c_adapter *ia, uint8_t addr,
-				   uint8_t reg, uint8_t *buffer, int len)
+static int sifive_i2c_adapter_read_byte(struct i2c_adapter *ia,
+				uint8_t *byte, bool nack, bool send_stop)
 {
 	struct sifive_i2c_adapter *adap =
 		container_of(ia, struct sifive_i2c_adapter, adapter);
 	int rc;
-
-	rc = sifive_i2c_adapter_start(adap, addr, SIFIVE_I2C_WRITE_BIT);
-	if (rc)
-		return rc;
-
-	rc = sifive_i2c_adapter_rxack(adap);
-	if (rc)
-		return rc;
-
-	sifive_i2c_setreg(adap, SIFIVE_I2C_TXR, reg);
-	sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_WR |
-					       SIFIVE_I2C_CMD_IACK);
-
+	if (nack)
+		sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
+					SIFIVE_I2C_CMD_ACK |
+					SIFIVE_I2C_CMD_RD |
+					SIFIVE_I2C_CMD_IACK);
+	else
+		sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
+					SIFIVE_I2C_CMD_RD |
+					SIFIVE_I2C_CMD_IACK);
 	rc = sifive_i2c_adapter_poll_tip(adap);
 	if (rc)
 		return rc;
 
-	rc = sifive_i2c_adapter_rxack(adap);
-	if (rc)
-		return rc;
-
-	/* setting addr with high 0 bit */
-	rc = sifive_i2c_adapter_start(adap, addr, SIFIVE_I2C_READ_BIT);
-	if (rc)
-		return rc;
-
-	rc = sifive_i2c_adapter_rxack(adap);
-	if (rc)
-		return rc;
+	*byte = sifive_i2c_getreg(adap, SIFIVE_I2C_RXR);
 
-	while (len) {
-		if (len == 1)
-			sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
-					  SIFIVE_I2C_CMD_ACK |
-					  SIFIVE_I2C_CMD_RD |
-					  SIFIVE_I2C_CMD_IACK);
-		else
-			sifive_i2c_setreg(adap, SIFIVE_I2C_CR,
-					  SIFIVE_I2C_CMD_RD |
-					  SIFIVE_I2C_CMD_IACK);
-
-		rc = sifive_i2c_adapter_poll_tip(adap);
+	if (send_stop) {
+		sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_STO |
+					       SIFIVE_I2C_CMD_IACK);
+		rc = sifive_i2c_adapter_poll_busy(adap);
 		if (rc)
 			return rc;
 
-		*buffer = sifive_i2c_getreg(adap, SIFIVE_I2C_RXR);
-		buffer++;
-		len--;
+		sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_IACK);
 	}
-
-	sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_STO |
-					       SIFIVE_I2C_CMD_IACK);
-	rc = sifive_i2c_adapter_poll_busy(adap);
-	if (rc)
-		return rc;
-
-	sifive_i2c_setreg(adap, SIFIVE_I2C_CR, SIFIVE_I2C_CMD_IACK);
-
 	return 0;
 }
 
@@ -256,8 +200,8 @@  static int sifive_i2c_init(void *fdt, int nodeoff,
 	adapter->addr = addr;
 	adapter->adapter.driver = &fdt_i2c_adapter_sifive;
 	adapter->adapter.id = nodeoff;
-	adapter->adapter.write = sifive_i2c_adapter_write;
-	adapter->adapter.read = sifive_i2c_adapter_read;
+	adapter->adapter.write_byte = sifive_i2c_adapter_write_byte;
+	adapter->adapter.read_byte = sifive_i2c_adapter_read_byte;
 	rc = i2c_adapter_add(&adapter->adapter);
 	if (rc)
 		return rc;
diff --git a/lib/utils/i2c/i2c.c b/lib/utils/i2c/i2c.c
index 6be4e24..972c7bb 100644
--- a/lib/utils/i2c/i2c.c
+++ b/lib/utils/i2c/i2c.c
@@ -12,8 +12,12 @@ 
  */
 
 #include <sbi/sbi_error.h>
+#include <sbi/sbi_string.h>
 #include <sbi_utils/i2c/i2c.h>
 
+#define I2C_10BIT_MASK	0x7c
+#define I2C_10BIT_VAL	0x78
+
 static SBI_LIST_HEAD(i2c_adapters_list);
 
 struct i2c_adapter *i2c_adapter_find(int id)
@@ -51,25 +55,116 @@  void i2c_adapter_remove(struct i2c_adapter *ia)
 	sbi_list_del(&(ia->node));
 }
 
-int i2c_adapter_write(struct i2c_adapter *ia, uint8_t addr, uint8_t reg,
-		     uint8_t *buffer, int len)
+unsigned i2c_10bit_address(unsigned addr)
 {
-	if (!ia)
-		return SBI_EINVAL;
-	if (!ia->write)
-		return SBI_ENOSYS;
+	return (addr & 0x3ff) | (I2C_10BIT_VAL << 8);
+}
 
-	return ia->write(ia, addr, reg, buffer, len);
+static inline int i2c_is_10bit_address(unsigned addr)
+{
+	return ((addr >> 8) & I2C_10BIT_MASK) == I2C_10BIT_VAL;
 }
 
+static int i2c_adapter_read_helper(struct i2c_adapter *ia,
+		unsigned addr, uint8_t *buff, unsigned len, bool send_stop)
+{
+	int ret;
+	if (!i2c_is_10bit_address(addr)) {
+		ret = ia->write_byte(ia, (addr << 1) | I2C_OP_R, true, false);
+		if (ret)
+			return ret;
+	} else {
+		unsigned addr_h = (addr >> 8) & 0x7f;
+		unsigned addr_l = (addr >> 0) & 0xff;
+		ret = ia->write_byte(ia, (addr_h << 1) | I2C_OP_W, true, false);
+		if (ret)
+			return ret;
+		ret = ia->write_byte(ia, addr_l, false, false);
+		if (ret)
+			return ret;
+		ret = ia->write_byte(ia, (addr_h << 1) | I2C_OP_R, true, false);
+		if (ret)
+			return ret;
+	}
+
+	while (len--) {
+		ret = ia->read_byte(ia, buff, len == 0, send_stop && (len == 0));
+		if (ret)
+			return ret;
+		buff++;
+	}
+	return ret;
+}
 
-int i2c_adapter_read(struct i2c_adapter *ia, uint8_t addr, uint8_t reg,
-		     uint8_t *buffer, int len)
+static int i2c_adapter_write_helper(struct i2c_adapter *ia,
+		unsigned addr, uint8_t *buff, unsigned len, bool send_stop)
 {
-	if (!ia)
-		return SBI_EINVAL;
-	if (!ia->read)
-		return SBI_ENOSYS;
+	int ret;
+	if (!i2c_is_10bit_address(addr)) {
+		ret = ia->write_byte(ia, (addr << 1) | I2C_OP_W, true, false);
+		if (ret)
+			return ret;
+	} else {
+		unsigned addr_h = (addr >> 8) & 0x7f;
+		unsigned addr_l = (addr >> 0) & 0xff;
+		ret = ia->write_byte(ia, (addr_h << 1) | I2C_OP_W, true, false);
+		if (ret)
+			return ret;
+		ret = ia->write_byte(ia, addr_l, false, false);
+		if (ret)
+			return ret;
+	}
+
+	while (len--) {
+		ret = ia->write_byte(ia, *buff, false,  send_stop && (len == 0));
+		if (ret)
+			return ret;
+		buff++;
+	}
+	return ret;
+}
+
+int i2c_adapter_read(struct i2c_adapter *ia,
+		unsigned addr, uint8_t *buff, unsigned len)
+{
+	return i2c_adapter_read_helper(ia, addr, buff, len, true);
+}
 
-	return ia->read(ia, addr, reg, buffer, len);
+int i2c_adapter_write(struct i2c_adapter *ia,
+		unsigned addr, uint8_t *buff, unsigned len)
+{
+	return i2c_adapter_write_helper(ia, addr, buff, len, true);
 }
+
+int i2c_adapter_comb(struct i2c_adapter *ia, unsigned addr,
+		enum i2c_op op0, uint8_t *buff0, unsigned len0,
+		enum i2c_op op1, uint8_t *buff1, unsigned len1)
+{
+	int ret;
+	switch (op0) {
+		case I2C_OP_W:
+			ret = i2c_adapter_write_helper(ia, addr, buff0, len0, false);
+			break;
+		case I2C_OP_R:
+			ret = i2c_adapter_read_helper(ia, addr, buff0, len0, false);
+			break;
+		default:
+			return SBI_EINVAL;
+	}
+	if (ret)
+		return ret;
+
+	if (i2c_is_10bit_address(addr))
+		addr = (addr >> 8) & 0x7f;
+	switch (op0) {
+		case I2C_OP_W:
+			ret = i2c_adapter_write_helper(ia, addr, buff0, len0, true);
+			break;
+		case I2C_OP_R:
+			ret = i2c_adapter_read_helper(ia, addr, buff0, len0, true);
+			break;
+		default:
+			return SBI_EINVAL;
+	}
+	return ret;
+}
\ No newline at end of file
diff --git a/platform/generic/sifive_fu740.c b/platform/generic/sifive_fu740.c
index 333b3fb..8e42a5d 100644
--- a/platform/generic/sifive_fu740.c
+++ b/platform/generic/sifive_fu740.c
@@ -34,6 +34,21 @@ 
 
 #define PMIC_CHIP_ID_DA9063		0x61
 
+static inline int da9063_read_reg(struct i2c_adapter *adap, uint8_t addr,
+	uint8_t reg, uint8_t *val)
+{
+	return i2c_adapter_comb(adap, addr, I2C_OP_W, &reg, 1, I2C_OP_R, val, 1);
+}
+
+static inline int da9063_write_reg(struct i2c_adapter *adap, uint8_t addr,
+	uint8_t reg, uint8_t val)
+{
+	uint8_t buff[2];
+	buff[0] = reg;
+	buff[1] = val;
+	return i2c_adapter_write(adap, addr, buff, 2);
+}
+
 static struct {
 	struct i2c_adapter *adapter;
 	uint32_t reg;
@@ -55,13 +70,13 @@  static int da9063_system_reset_check(u32 type, u32 reason)
 static inline int da9063_sanity_check(struct i2c_adapter *adap, uint32_t reg)
 {
 	uint8_t val;
-	int rc = i2c_adapter_reg_write(adap, reg, DA9063_REG_PAGE_CON, 0x02);
+	int rc = da9063_write_reg(adap, reg, DA9063_REG_PAGE_CON, 0x02);
 
 	if (rc)
 		return rc;
 
 	/* check set page*/
-	rc = i2c_adapter_reg_read(adap, reg, 0x0, &val);
+	rc = da9063_read_reg(adap, reg, 0x0, &val);
 	if (rc)
 		return rc;
 
@@ -69,7 +84,7 @@  static inline int da9063_sanity_check(struct i2c_adapter *adap, uint32_t reg)
 		return SBI_ENODEV;
 
 	/* read and check device id */
-	rc = i2c_adapter_reg_read(adap, reg, DA9063_REG_DEVICE_ID, &val);
+	rc = da9063_read_reg(adap, reg, DA9063_REG_DEVICE_ID, &val);
 	if (rc)
 		return rc;
 
@@ -81,32 +96,32 @@  static inline int da9063_sanity_check(struct i2c_adapter *adap, uint32_t reg)
 
 static inline int da9063_shutdown(struct i2c_adapter *adap, uint32_t reg)
 {
-	int rc = i2c_adapter_reg_write(adap, da9063.reg,
+	int rc = da9063_write_reg(adap, da9063.reg,
 					DA9063_REG_PAGE_CON, 0x00);
 
 	if (rc)
 		return rc;
 
-	return i2c_adapter_reg_write(adap, da9063.reg,
+	return da9063_write_reg(adap, da9063.reg,
 				     DA9063_REG_CONTROL_F,
 				     DA9063_CONTROL_F_SHUTDOWN);
 }
 
 static inline int da9063_reset(struct i2c_adapter *adap, uint32_t reg)
 {
-	int rc = i2c_adapter_reg_write(adap, da9063.reg,
+	int rc = da9063_write_reg(adap, da9063.reg,
 					DA9063_REG_PAGE_CON, 0x00);
 
 	if (rc)
 		return rc;
 
-	rc = i2c_adapter_reg_write(adap, da9063.reg,
+	rc = da9063_write_reg(adap, da9063.reg,
 				   DA9063_REG_CONTROL_F,
 				   DA9063_CONTROL_F_WAKEUP);
 	if (rc)
 		return rc;
 
-	return i2c_adapter_reg_write(adap, da9063.reg,
+	return da9063_write_reg(adap, da9063.reg,
 				DA9063_REG_CONTROL_A,
 				DA9063_CONTROL_A_M_POWER1_EN |
 				DA9063_CONTROL_A_M_POWER_EN |