aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJean Delvare <khali@linux-fr.org>2005-10-26 15:28:55 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2005-10-28 17:02:14 -0400
commit421ef47be20c5454b12ae0ec918d5073a9d2b938 (patch)
treea7225bd055acb418693d0e6ed4f8d886dc68ec55
parentb8095544bc27044a7aa79e1e073b781a249910c3 (diff)
[PATCH] i2c: SMBus PEC support rewrite, 2 of 3
This is my rewrite of the SMBus PEC support. The original implementation was known to have bugs (credits go to Hideki Iwamoto for reporting many of them recently), and was incomplete due to a conceptual limitation. The rewrite affects only software PEC. Hardware PEC needs very little code and is mostly untouched. Technically, both implementations differ in that the original one was emulating PEC in software by modifying the contents of an i2c_smbus_data union (changing the transaction to a different type), while the new one works one level lower, on i2c_msg structures (working on message contents). Due to the definition of the i2c_smbus_data union, not all SMBus transactions could be handled (at least not without changing the definition of this union, which would break user-space compatibility), and those which could had to be implemented individually. At the opposite, adding PEC to an i2c_msg structure can be done on any SMBus transaction with common code. Advantages of the new implementation: * It's about twice as small (from ~136 lines before to ~70 now, only counting i2c-core, including blank and comment lines). The memory used by i2c-core is down by ~640 bytes (~3.5%). * Easier to validate, less tricky code. The code being common to all transactions by design, the risk that a bug can stay uncovered is lower. * All SMBus transactions have PEC support in I2C emulation mode (providing the non-PEC transaction is also implemented). Transactions which have no emulation code right now will get PEC support for free when they finally get implemented. * Allows for code simplifications in header files and bus drivers (patch follows). Drawbacks (I guess there had to be at least one): * PEC emulation for non-PEC capable non-I2C SMBus masters was dropped. It was based on SMBus tricks and doesn't quite fit in the new design. I don't think it's really a problem, as the benefit was certainly not worth the additional complexity, but it's only fair that I at least mention it. Lastly, let's note that the new implementation does slightly affect compatibility (both in kernel and user-space), but doesn't actually break it. Some defines will be dropped, but the code can always be changed in a way that will work with both the old and the new implementations. It shouldn't be a problem as there doesn't seem to be many users of SMBus PEC to date anyway. Signed-off-by: Jean Delvare <khali@linux-fr.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/i2c/i2c-core.c162
-rw-r--r--include/linux/i2c.h2
2 files changed, 49 insertions, 115 deletions
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 0040981b6698..02e335a04f09 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -19,7 +19,8 @@
19 19
20/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi>. 20/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi>.
21 All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl> 21 All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl>
22 SMBus 2.0 support by Mark Studebaker <mdsxyz123@yahoo.com> */ 22 SMBus 2.0 support by Mark Studebaker <mdsxyz123@yahoo.com> and
23 Jean Delvare <khali@linux-fr.org> */
23 24
24#include <linux/module.h> 25#include <linux/module.h>
25#include <linux/kernel.h> 26#include <linux/kernel.h>
@@ -830,101 +831,44 @@ crc8(u16 data)
830 return (u8)(data >> 8); 831 return (u8)(data >> 8);
831} 832}
832 833
833/* CRC over count bytes in the first array plus the bytes in the rest 834/* Incremental CRC8 over count bytes in the array pointed to by p */
834 array if it is non-null. rest[0] is the (length of rest) - 1 835static u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count)
835 and is included. */
836static u8 i2c_smbus_partial_pec(u8 crc, int count, u8 *first, u8 *rest)
837{ 836{
838 int i; 837 int i;
839 838
840 for(i = 0; i < count; i++) 839 for(i = 0; i < count; i++)
841 crc = crc8((crc ^ first[i]) << 8); 840 crc = crc8((crc ^ p[i]) << 8);
842 if(rest != NULL)
843 for(i = 0; i <= rest[0]; i++)
844 crc = crc8((crc ^ rest[i]) << 8);
845 return crc; 841 return crc;
846} 842}
847 843
848static u8 i2c_smbus_pec(int count, u8 *first, u8 *rest) 844/* Assume a 7-bit address, which is reasonable for SMBus */
845static u8 i2c_smbus_msg_pec(u8 pec, struct i2c_msg *msg)
849{ 846{
850 return i2c_smbus_partial_pec(0, count, first, rest); 847 /* The address will be sent first */
848 u8 addr = (msg->addr << 1) | !!(msg->flags & I2C_M_RD);
849 pec = i2c_smbus_pec(pec, &addr, 1);
850
851 /* The data buffer follows */
852 return i2c_smbus_pec(pec, msg->buf, msg->len);
851} 853}
852 854
853/* Returns new "size" (transaction type) 855/* Used for write only transactions */
854 Note that we convert byte to byte_data and byte_data to word_data 856static inline void i2c_smbus_add_pec(struct i2c_msg *msg)
855 rather than invent new xxx_PEC transactions. */
856static int i2c_smbus_add_pec(u16 addr, u8 command, int size,
857 union i2c_smbus_data *data)
858{ 857{
859 u8 buf[3]; 858 msg->buf[msg->len] = i2c_smbus_msg_pec(0, msg);
860 859 msg->len++;
861 buf[0] = addr << 1;
862 buf[1] = command;
863 switch(size) {
864 case I2C_SMBUS_BYTE:
865 data->byte = i2c_smbus_pec(2, buf, NULL);
866 size = I2C_SMBUS_BYTE_DATA;
867 break;
868 case I2C_SMBUS_BYTE_DATA:
869 buf[2] = data->byte;
870 data->word = buf[2] |
871 (i2c_smbus_pec(3, buf, NULL) << 8);
872 size = I2C_SMBUS_WORD_DATA;
873 break;
874 case I2C_SMBUS_WORD_DATA:
875 /* unsupported */
876 break;
877 case I2C_SMBUS_BLOCK_DATA:
878 data->block[data->block[0] + 1] =
879 i2c_smbus_pec(2, buf, data->block);
880 size = I2C_SMBUS_BLOCK_DATA_PEC;
881 break;
882 }
883 return size;
884} 860}
885 861
886static int i2c_smbus_check_pec(u16 addr, u8 command, int size, u8 partial, 862/* Return <0 on CRC error
887 union i2c_smbus_data *data) 863 If there was a write before this read (most cases) we need to take the
864 partial CRC from the write part into account.
865 Note that this function does modify the message (we need to decrease the
866 message length to hide the CRC byte from the caller). */
867static int i2c_smbus_check_pec(u8 cpec, struct i2c_msg *msg)
888{ 868{
889 u8 buf[3], rpec, cpec; 869 u8 rpec = msg->buf[--msg->len];
870 cpec = i2c_smbus_msg_pec(cpec, msg);
890 871
891 buf[1] = command;
892 switch(size) {
893 case I2C_SMBUS_BYTE_DATA:
894 buf[0] = (addr << 1) | 1;
895 cpec = i2c_smbus_pec(2, buf, NULL);
896 rpec = data->byte;
897 break;
898 case I2C_SMBUS_WORD_DATA:
899 buf[0] = (addr << 1) | 1;
900 buf[2] = data->word & 0xff;
901 cpec = i2c_smbus_pec(3, buf, NULL);
902 rpec = data->word >> 8;
903 break;
904 case I2C_SMBUS_WORD_DATA_PEC:
905 /* unsupported */
906 cpec = rpec = 0;
907 break;
908 case I2C_SMBUS_PROC_CALL_PEC:
909 /* unsupported */
910 cpec = rpec = 0;
911 break;
912 case I2C_SMBUS_BLOCK_DATA_PEC:
913 buf[0] = (addr << 1);
914 buf[2] = (addr << 1) | 1;
915 cpec = i2c_smbus_pec(3, buf, data->block);
916 rpec = data->block[data->block[0] + 1];
917 break;
918 case I2C_SMBUS_BLOCK_PROC_CALL_PEC:
919 buf[0] = (addr << 1) | 1;
920 rpec = i2c_smbus_partial_pec(partial, 1,
921 buf, data->block);
922 cpec = data->block[data->block[0] + 1];
923 break;
924 default:
925 cpec = rpec = 0;
926 break;
927 }
928 if (rpec != cpec) { 872 if (rpec != cpec) {
929 pr_debug("i2c-core: Bad PEC 0x%02x vs. 0x%02x\n", 873 pr_debug("i2c-core: Bad PEC 0x%02x vs. 0x%02x\n",
930 rpec, cpec); 874 rpec, cpec);
@@ -951,9 +895,8 @@ s32 i2c_smbus_read_byte(struct i2c_client *client)
951 895
952s32 i2c_smbus_write_byte(struct i2c_client *client, u8 value) 896s32 i2c_smbus_write_byte(struct i2c_client *client, u8 value)
953{ 897{
954 union i2c_smbus_data data; /* only for PEC */
955 return i2c_smbus_xfer(client->adapter,client->addr,client->flags, 898 return i2c_smbus_xfer(client->adapter,client->addr,client->flags,
956 I2C_SMBUS_WRITE,value, I2C_SMBUS_BYTE,&data); 899 I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, NULL);
957} 900}
958 901
959s32 i2c_smbus_read_byte_data(struct i2c_client *client, u8 command) 902s32 i2c_smbus_read_byte_data(struct i2c_client *client, u8 command)
@@ -1043,6 +986,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
1043 { addr, flags | I2C_M_RD, 0, msgbuf1 } 986 { addr, flags | I2C_M_RD, 0, msgbuf1 }
1044 }; 987 };
1045 int i; 988 int i;
989 u8 partial_pec = 0;
1046 990
1047 msgbuf0[0] = command; 991 msgbuf0[0] = command;
1048 switch(size) { 992 switch(size) {
@@ -1085,7 +1029,6 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
1085 msgbuf0[2] = (data->word >> 8) & 0xff; 1029 msgbuf0[2] = (data->word >> 8) & 0xff;
1086 break; 1030 break;
1087 case I2C_SMBUS_BLOCK_DATA: 1031 case I2C_SMBUS_BLOCK_DATA:
1088 case I2C_SMBUS_BLOCK_DATA_PEC:
1089 if (read_write == I2C_SMBUS_READ) { 1032 if (read_write == I2C_SMBUS_READ) {
1090 dev_err(&adapter->dev, "Block read not supported " 1033 dev_err(&adapter->dev, "Block read not supported "
1091 "under I2C emulation!\n"); 1034 "under I2C emulation!\n");
@@ -1098,14 +1041,11 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
1098 data->block[0]); 1041 data->block[0]);
1099 return -1; 1042 return -1;
1100 } 1043 }
1101 if(size == I2C_SMBUS_BLOCK_DATA_PEC)
1102 (msg[0].len)++;
1103 for (i = 1; i < msg[0].len; i++) 1044 for (i = 1; i < msg[0].len; i++)
1104 msgbuf0[i] = data->block[i-1]; 1045 msgbuf0[i] = data->block[i-1];
1105 } 1046 }
1106 break; 1047 break;
1107 case I2C_SMBUS_BLOCK_PROC_CALL: 1048 case I2C_SMBUS_BLOCK_PROC_CALL:
1108 case I2C_SMBUS_BLOCK_PROC_CALL_PEC:
1109 dev_dbg(&adapter->dev, "Block process call not supported " 1049 dev_dbg(&adapter->dev, "Block process call not supported "
1110 "under I2C emulation!\n"); 1050 "under I2C emulation!\n");
1111 return -1; 1051 return -1;
@@ -1130,9 +1070,30 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
1130 return -1; 1070 return -1;
1131 } 1071 }
1132 1072
1073 i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK
1074 && size != I2C_SMBUS_I2C_BLOCK_DATA);
1075 if (i) {
1076 /* Compute PEC if first message is a write */
1077 if (!(msg[0].flags & I2C_M_RD)) {
1078 if (num == 1) /* Write only */
1079 i2c_smbus_add_pec(&msg[0]);
1080 else /* Write followed by read */
1081 partial_pec = i2c_smbus_msg_pec(0, &msg[0]);
1082 }
1083 /* Ask for PEC if last message is a read */
1084 if (msg[num-1].flags & I2C_M_RD)
1085 msg[num-1].len++;
1086 }
1087
1133 if (i2c_transfer(adapter, msg, num) < 0) 1088 if (i2c_transfer(adapter, msg, num) < 0)
1134 return -1; 1089 return -1;
1135 1090
1091 /* Check PEC if last message is a read */
1092 if (i && (msg[num-1].flags & I2C_M_RD)) {
1093 if (i2c_smbus_check_pec(partial_pec, &msg[num-1]) < 0)
1094 return -1;
1095 }
1096
1136 if (read_write == I2C_SMBUS_READ) 1097 if (read_write == I2C_SMBUS_READ)
1137 switch(size) { 1098 switch(size) {
1138 case I2C_SMBUS_BYTE: 1099 case I2C_SMBUS_BYTE:
@@ -1161,28 +1122,8 @@ s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,
1161 union i2c_smbus_data * data) 1122 union i2c_smbus_data * data)
1162{ 1123{
1163 s32 res; 1124 s32 res;
1164 int swpec = 0;
1165 u8 partial = 0;
1166 1125
1167 flags &= I2C_M_TEN | I2C_CLIENT_PEC; 1126 flags &= I2C_M_TEN | I2C_CLIENT_PEC;
1168 if((flags & I2C_CLIENT_PEC) &&
1169 !(i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HWPEC_CALC))) {
1170 swpec = 1;
1171 if(read_write == I2C_SMBUS_READ &&
1172 size == I2C_SMBUS_BLOCK_DATA)
1173 size = I2C_SMBUS_BLOCK_DATA_PEC;
1174 else if(size == I2C_SMBUS_PROC_CALL)
1175 size = I2C_SMBUS_PROC_CALL_PEC;
1176 else if(size == I2C_SMBUS_BLOCK_PROC_CALL) {
1177 i2c_smbus_add_pec(addr, command,
1178 I2C_SMBUS_BLOCK_DATA, data);
1179 partial = data->block[data->block[0] + 1];
1180 size = I2C_SMBUS_BLOCK_PROC_CALL_PEC;
1181 } else if(read_write == I2C_SMBUS_WRITE &&
1182 size != I2C_SMBUS_QUICK &&
1183 size != I2C_SMBUS_I2C_BLOCK_DATA)
1184 size = i2c_smbus_add_pec(addr, command, size, data);
1185 }
1186 1127
1187 if (adapter->algo->smbus_xfer) { 1128 if (adapter->algo->smbus_xfer) {
1188 down(&adapter->bus_lock); 1129 down(&adapter->bus_lock);
@@ -1193,13 +1134,6 @@ s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,
1193 res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write, 1134 res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,
1194 command,size,data); 1135 command,size,data);
1195 1136
1196 if(res >= 0 && swpec &&
1197 size != I2C_SMBUS_QUICK && size != I2C_SMBUS_I2C_BLOCK_DATA &&
1198 (read_write == I2C_SMBUS_READ || size == I2C_SMBUS_PROC_CALL_PEC ||
1199 size == I2C_SMBUS_BLOCK_PROC_CALL_PEC)) {
1200 if(i2c_smbus_check_pec(addr, command, size, partial, data))
1201 return -1;
1202 }
1203 return res; 1137 return res;
1204} 1138}
1205 1139
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index bcd4bb1b450c..78c64f7b3105 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -434,7 +434,7 @@ union i2c_smbus_data {
434 __u8 byte; 434 __u8 byte;
435 __u16 word; 435 __u16 word;
436 __u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */ 436 __u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */
437 /* and one more for PEC */ 437 /* and one more for user-space compatibility */
438}; 438};
439 439
440/* smbus_access read or write markers */ 440/* smbus_access read or write markers */