Re: [RFC][PATCH 4/4] RTC: SMBus support for the M41T80, etc. driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Added CC to authers of the driver.

On Wed, 7 May 2008 01:40:41 +0100 (BST), "Maciej W. Rozycki" <macro@xxxxxxxxxxxxxx> wrote:
>  The BCM1250A SOC which is used on the SWARM board utilising an M41T81
> chip only supports pure I2C in the raw bit-banged mode.  Nobody sane
> really wants to use it unless absolutely necessary and the M41T80, etc.  
> chips work just fine with an SMBus controller which is what the standard
> mode of operation of the BCM1250A.  The only drawback of byte accesses
> with the M41T80 is the chip only latches clock data registers for the
> duration of an I2C transaction which works fine with a block transfers,
> but not byte-wise accesses.
> 
>  The driver currently requires an I2C controller providing both SMBus and
> pure I2C access.  This is a set of changes to make it work with either,
> with a preference to pure I2C.  The problem of unlatched clock data if
> SMBus transactions are used is resolved in the standard way.
> 
>  Whether a choice between SMBus and pure I2C access is possible is 
> obviously device dependent.  If the code proved useful for other I2C 
> devices, then it could be moved to a more generic place.  It is a code
> simplification regardless -- similar I2C packet definitions are not 
> scattered throughout the driver anymore.
> 
> Signed-off-by: Maciej W. Rozycki <macro@xxxxxxxxxxxxxx>
> ---
>  Please note I have no means to test these changes with a pure I2C 
> controller -- anybody interested, please help.

I tested this with i2c-gpio.  It needs some fix.  Detailed below.

> -static int m41t80_get_datetime(struct i2c_client *client,
> -			       struct rtc_time *tm)
> +
> +static int m41t80_i2c_transfer(struct i2c_client *client, int write,
> +			       u8 reg, u8 num, u8 *buf)
>  {
> -	u8 buf[M41T80_DATETIME_REG_SIZE], dt_addr[1] = { M41T80_REG_SEC };
>  	struct i2c_msg msgs[] = {
>  		{
>  			.addr	= client->addr,
>  			.flags	= 0,
>  			.len	= 1,
> -			.buf	= dt_addr,
> +			.buf	= &reg,
>  		},
>  		{
>  			.addr	= client->addr,
> -			.flags	= I2C_M_RD,
> -			.len	= M41T80_DATETIME_REG_SIZE - M41T80_REG_SEC,
> -			.buf	= buf + M41T80_REG_SEC,
> +			.flags	= write ? 0 : I2C_M_RD,
> +			.len	= num,
> +			.buf	= buf,
>  		},
>  	};
>  
> -	if (i2c_transfer(client->adapter, msgs, 2) < 0) {
> -		dev_err(&client->dev, "read error\n");
> -		return -EIO;
> +	return i2c_transfer(client->adapter, msgs, 2);
> +}

For write transfer, you must use only one i2c_msg.  If two i2c_msg
were used, the slave address is inserted between register number and
first data.  This chip cannot accept such sequence.

> +static int m41t80_transfer(struct i2c_client *client, int write,
> +			   u8 reg, u8 num, u8 *buf)
> +{
> +	int i, rc;
> +
> +	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
> +		return m41t80_i2c_transfer(client, write, reg, num, buf);

On success, m41t80_i2c_transfer() returns positive value, but
m41t80_transfer() should return 0.


Here is a proposed fix on top of your patch.

--- drivers/rtc/rtc-m41t80.c.orig	2008-05-08 11:08:57.000000000 +0900
+++ drivers/rtc/rtc-m41t80.c	2008-05-08 12:30:49.000000000 +0900
@@ -83,8 +83,8 @@ struct m41t80_data {
 };
 
 
-static int m41t80_i2c_transfer(struct i2c_client *client, int write,
-			       u8 reg, u8 num, u8 *buf)
+static int m41t80_i2c_read_transfer(struct i2c_client *client,
+				    u8 reg, u8 num, u8 *buf)
 {
 	struct i2c_msg msgs[] = {
 		{
@@ -95,7 +95,7 @@ static int m41t80_i2c_transfer(struct i2
 		},
 		{
 			.addr	= client->addr,
-			.flags	= write ? 0 : I2C_M_RD,
+			.flags	= I2C_M_RD,
 			.len	= num,
 			.buf	= buf,
 		},
@@ -104,13 +104,29 @@ static int m41t80_i2c_transfer(struct i2
 	return i2c_transfer(client->adapter, msgs, 2);
 }
 
+static int m41t80_i2c_write_transfer(struct i2c_client *client,
+				     u8 reg, u8 num, u8 *buf)
+{
+	u8 wbuf[M41T80_DATETIME_REG_SIZE + 1];
+	struct i2c_msg msg = {
+		.addr	= client->addr,
+		.flags	= 0,
+		.len	= num + 1,
+		.buf	= wbuf,
+	};
+
+	wbuf[0] = reg;
+	memcpy(wbuf + 1, buf, num);
+	return i2c_transfer(client->adapter, &msg, 1);
+}
+
 static int m41t80_read_byte_data(struct i2c_client *client, u8 reg)
 {
 	int rc;
 	u8 val;
 
 	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
-		rc = m41t80_i2c_transfer(client, 0, reg, 1, &val);
+		rc = m41t80_i2c_read_transfer(client, reg, 1, &val);
 		if (rc < 0)
 			return rc;
 		else
@@ -122,7 +138,7 @@ static int m41t80_read_byte_data(struct 
 static int m41t80_write_byte_data(struct i2c_client *client, u8 reg, u8 val)
 {
 	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
-		return m41t80_i2c_transfer(client, 1, reg, 1, &val);
+		return m41t80_i2c_write_transfer(client, reg, 1, &val);
 	else
 		return i2c_smbus_write_byte_data(client, reg, val);
 }
@@ -132,16 +148,20 @@ static int m41t80_transfer(struct i2c_cl
 {
 	int i, rc;
 
-	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
-		return m41t80_i2c_transfer(client, write, reg, num, buf);
-	else if (write) {
+	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		if (write)
+			rc = m41t80_i2c_write_transfer(client, reg, num, buf);
+		else
+			rc = m41t80_i2c_read_transfer(client, reg, num, buf);
+		if (rc < 0)
+			return rc;
+	} else if (write) {
 		for (i = 0; i < num; i++) {
 			rc = i2c_smbus_write_byte_data(client, reg + i,
 						       buf[i]);
 			if (rc < 0)
 				return rc;
 		}
-		return 0;
 	} else {
 		for (i = 0; i < num; i++) {
 			rc = i2c_smbus_read_byte_data(client, reg + i);
@@ -149,8 +169,8 @@ static int m41t80_transfer(struct i2c_cl
 				return rc;
 			buf[i] = rc;
 		}
-		return 0;
 	}
+	return 0;
 }
 
 static int m41t80_get_datetime(struct i2c_client *client, struct rtc_time *tm)


[Index of Archives]     [Linux MIPS Home]     [LKML Archive]     [Linux ARM Kernel]     [Linux ARM]     [Linux]     [Git]     [Yosemite News]     [Linux SCSI]     [Linux Hams]

  Powered by Linux