| /* |
| * Copyright (c) 2014 MediaTek Inc. |
| * Author: Ranrn.Lu <ranran.lu@mediatek.com> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/init.h> |
| #include <linux/i2c.h> |
| #include <linux/platform_device.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/slab.h> |
| #include <linux/errno.h> |
| #include <linux/err.h> |
| #include <linux/delay.h> |
| #include "i2c-mtk.h" |
| /* filer out error messages */ |
| |
| static char data_buffer[256 * 4]; |
| unsigned long long t3, t4, t5, t6; |
| |
| static inline void i2c_writew_d(struct mt_i2c *i2c, u8 offset, u16 value) |
| { |
| writew(value, (void *)((i2c->base) + (offset))); |
| } |
| |
| static inline u16 i2c_readw_d(struct mt_i2c *i2c, u8 offset) |
| { |
| return readw((void const *)((i2c->base) + (offset))); |
| } |
| |
| int mt_i2c_test(int id, int addr) |
| { |
| int ret = 0; |
| /* ret = i2c_trans_data(id, addr, buffer,,buffer, 1, 1, 0); */ |
| return ret; |
| } |
| EXPORT_SYMBOL(mt_i2c_test); |
| |
| int mt_i2c_test_multi_wr(int id, int addr) |
| { |
| int ret; |
| struct i2c_msg msg[12]; |
| struct i2c_adapter *adap; |
| char buf0[3] = {0x55, 0x00, 0x01}; |
| char buf1[3] = {0x55, 0x01, 0x02}; |
| char buf2[3] = {0x55, 0x02, 0x03}; |
| char buf3[3] = {0x55, 0x03, 0x04}; |
| char buf4[2] = {0x55, 0x00}; |
| char buf5[2] = {0xff, 0xff}; |
| char buf6[2] = {0x55, 0x01}; |
| char buf7[2] = {0xff, 0xff}; |
| char buf8[2] = {0x55, 0x02}; |
| char buf9[2] = {0xff, 0xff}; |
| char buf10[2] = {0x55, 0x03}; |
| char buf11[2] = {0xff, 0xff}; |
| |
| adap = i2c_get_adapter(id); |
| if (!adap) |
| return -1; |
| |
| msg[0].addr = addr; |
| msg[0].flags = 0; |
| msg[0].len = 3; |
| msg[0].buf = buf0; |
| msg[1].addr = addr; |
| msg[1].flags = 0; |
| msg[1].len = 3; |
| msg[1].buf = buf1; |
| msg[2].addr = addr; |
| msg[2].flags = 0; |
| msg[2].len = 3; |
| msg[2].buf = buf2; |
| msg[3].addr = addr; |
| msg[3].flags = 0; |
| msg[3].len = 3; |
| msg[3].buf = buf3; |
| msg[4].addr = addr; |
| msg[4].flags = 0; |
| msg[4].len = 2; |
| msg[4].buf = buf4; |
| msg[5].addr = addr; |
| msg[5].flags = I2C_M_RD; |
| msg[5].len = 1; |
| msg[5].buf = buf5; |
| msg[6].addr = addr; |
| msg[6].flags = 0; |
| msg[6].len = 2; |
| msg[6].buf = buf6; |
| msg[7].addr = addr; |
| msg[7].flags = I2C_M_RD; |
| msg[7].len = 1; |
| msg[7].buf = buf7; |
| msg[8].addr = addr; |
| msg[8].flags = 0; |
| msg[8].len = 2; |
| msg[8].buf = buf8; |
| msg[9].addr = addr; |
| msg[9].flags = I2C_M_RD; |
| msg[9].len = 1; |
| msg[9].buf = buf9; |
| msg[10].addr = addr; |
| msg[10].flags = 0; |
| msg[10].len = 2; |
| msg[10].buf = buf10; |
| msg[11].addr = addr; |
| msg[11].flags = I2C_M_RD; |
| msg[11].len = 1; |
| msg[11].buf = buf11; |
| hw_trig_i2c_enable(adap); |
| ret = hw_trig_i2c_transfer(adap, msg, 4); |
| hw_trig_i2c_disable(adap); |
| pr_info("camera 0x5500 : %x 0x5501 : %x 0x5502 : %x 0x5503 : %x .\n", |
| buf5[0], buf7[0], buf9[0], buf11[0]); |
| return ret; |
| } |
| |
| int mt_i2c_test_wrrd(int id, int addr, int wr_len, int rd_len, |
| char *wr_buf, char *rd_buf, unsigned int speedhz) |
| { |
| |
| int ret; |
| struct i2c_msg msg[2]; |
| struct i2c_adapter *adap; |
| struct mt_i2c *i2c; |
| unsigned int origin_speed = 0; |
| |
| adap = i2c_get_adapter(id); |
| if (!adap) |
| return -1; |
| |
| i2c = container_of(adap, struct mt_i2c, adap); |
| if (speedhz) { |
| origin_speed = i2c->speed_hz; |
| i2c->speed_hz = speedhz; |
| } |
| |
| msg[0].addr = addr; |
| msg[0].flags = 0; |
| msg[0].len = wr_len; |
| msg[0].buf = wr_buf; |
| /* |
| * for(i = 0; i < wr_len; i++) { |
| * printk("cxd wr_len = %d i2c_trans_data-%d = 0x%x\n", |
| * wr_len, i, wr_buf[i]); |
| * } |
| */ |
| msg[1].addr = addr; |
| msg[1].flags = I2C_M_RD; |
| msg[1].len = rd_len; |
| msg[1].buf = rd_buf; |
| |
| ret = i2c_transfer(adap, &msg[0], 2); |
| /* |
| * printk("cxd i2c_trans_ret = %d\n", ret); |
| * for(ii = 0; ii<rd_len; ii++) |
| * printk("cxd i2c_trans_data-%d = 0x%x\n", ii, rd_buf[ii]); |
| */ |
| if (origin_speed) |
| i2c->speed_hz = origin_speed; |
| |
| return ret; |
| |
| } |
| |
| static ssize_t show_config(struct device *dev, struct device_attribute *attr, |
| char *buff) |
| { |
| int len = strlen(data_buffer); |
| |
| pr_info("Usage echo \"[bus_id] [address] [operation] [w_count]\n" |
| "[r_count] [data]\" > ut\n"); |
| memcpy(buff, data_buffer, len); |
| pr_info("Return Value:%s\n\n", data_buffer); |
| return len; |
| } |
| |
| static int pows(int x, int y) |
| { |
| int result = 1; |
| |
| while (y--) |
| result *= x; |
| return result; |
| } |
| |
| int string2hex(const char *buffer, int cnt) |
| { |
| int c = 0; |
| char t = 0; |
| int count = cnt; |
| |
| while (count--) { |
| t = *(buffer + cnt - count - 1); |
| if (t >= 'A' && t <= 'F') |
| c += ((t - 'A') + 10) * pows(16, count); |
| else if (t >= '0' && t <= '9') |
| c += (t - '0') * pows(16, count); |
| else |
| c = -1; |
| } |
| return c; |
| } |
| |
| char *get_hexbuffer(char *data_buffer, char *hex_buffer) |
| { |
| char *ptr = data_buffer; |
| int index = 0; |
| |
| while (*ptr && *++ptr) { |
| *(hex_buffer + index++) = string2hex(ptr - 1, 2); |
| ptr++; |
| } |
| *(hex_buffer + index) = 0; |
| return hex_buffer; |
| } |
| |
| int i2c_trans_data(int bus_id, int address, char *buf_wr, char *buf_rd, |
| int operation, int len_wr, int len_rd, unsigned int speedhz) |
| { |
| int ret; |
| |
| struct i2c_msg msg; |
| struct i2c_adapter *adap; |
| struct mt_i2c *i2c; |
| unsigned int origin_speed = 0; |
| |
| adap = i2c_get_adapter(bus_id); |
| if (!adap) |
| return -1; |
| |
| i2c = container_of(adap, struct mt_i2c, adap); |
| if (speedhz) { |
| origin_speed = i2c->speed_hz; |
| i2c->speed_hz = speedhz; |
| } |
| |
| msg.addr = address; |
| if (operation == 2) { |
| msg.flags = I2C_M_RD; |
| msg.len = len_rd; |
| msg.buf = (char *)buf_rd; |
| } else { |
| msg.flags = 0; |
| msg.len = len_wr; |
| msg.buf = (char *)buf_wr; |
| } |
| ret = i2c_transfer(adap, &msg, 1); |
| /* |
| *if(ret > 0) { |
| * for(i = 0; i<msg.len; i++) |
| * printk("cxd i2c_trans_data-%d = 0x%x\n", |
| * i, msg.buf[i]); |
| * |
| *} |
| */ |
| if (origin_speed) |
| i2c->speed_hz = origin_speed; |
| |
| i2c_put_adapter(adap); |
| return (ret == 1) ? msg.len : ret; |
| } |
| |
| /* extern mt_i2c ; */ |
| static int i2c_test_reg(int bus_id, int val) |
| { |
| int ret = 0; |
| struct i2c_adapter *adap; |
| struct mt_i2c *i2c; |
| |
| adap = i2c_get_adapter(bus_id); |
| if (!adap) |
| return -1; |
| i2c = container_of(adap, struct mt_i2c, adap); |
| /* printk("I2C%d base address %8x\n", bus_id, |
| * (unsigned int)(i2c->base)); |
| */ |
| /* write i2c writable register with 0 */ |
| i2c_writew_d(i2c, OFFSET_SLAVE_ADDR, val); |
| i2c_writew_d(i2c, OFFSET_INTR_MASK, val); |
| i2c_writew_d(i2c, OFFSET_INTR_STAT, val); |
| i2c_writew_d(i2c, OFFSET_CONTROL, val); |
| i2c_writew_d(i2c, OFFSET_TRANSFER_LEN, val); |
| i2c_writew_d(i2c, OFFSET_TRANSAC_LEN, val); |
| i2c_writew_d(i2c, OFFSET_DELAY_LEN, val); |
| i2c_writew_d(i2c, OFFSET_TIMING, val); |
| i2c_writew_d(i2c, OFFSET_EXT_CONF, val); |
| i2c_writew_d(i2c, OFFSET_IO_CONFIG, val); |
| i2c_writew_d(i2c, OFFSET_HS, val); |
| /* If everything went ok (i.e. 1 msg transmitted), return #bytes |
| * transmitted, else error code. |
| */ |
| i2c_put_adapter(adap); |
| return ret; |
| } |
| |
| static int i2c_soft_reset(int bus_id) |
| { |
| int ret = 0; |
| struct i2c_adapter *adap; |
| struct mt_i2c *i2c; |
| |
| adap = i2c_get_adapter(bus_id); |
| if (!adap) |
| return -1; |
| i2c = container_of(adap, struct mt_i2c, adap); |
| /* printk("I2C%d base address %8x\n", bus_id, |
| * (unsigned int)(i2c->base)); |
| */ |
| /* write i2c writable register with 0 */ |
| i2c_writew_d(i2c, OFFSET_SOFTRESET, 1); |
| /* If everything went ok (i.e. 1 msg transmitted), return #bytes |
| * transmitted, else error code. |
| */ |
| i2c_put_adapter(adap); |
| return ret; |
| } |
| |
| static int i2c_ext_conf_test(int bus_id, int val) |
| { |
| int ret = 0; |
| struct i2c_adapter *adap; |
| struct mt_i2c *i2c; |
| |
| adap = i2c_get_adapter(bus_id); |
| if (!adap) |
| return -1; |
| i2c = container_of(adap, struct mt_i2c, adap); |
| /* printk("I2C%d base address %8x\n", bus_id, |
| * (unsigned int)(i2c->base)); |
| */ |
| /* write i2c writable register with 0 */ |
| i2c_writew_d(i2c, OFFSET_EXT_CONF, val); |
| /* printk("EXT_CONF 0x%x", i2c_readw_d(i2c, OFFSET_EXT_CONF)); */ |
| /* If everything went ok (i.e. 1 msg transmitted), return #bytes |
| * transmitted, else error code. |
| */ |
| i2c_put_adapter(adap); |
| return ret; |
| } |
| |
| static void hex2string(unsigned char *in, unsigned char *out, int length) |
| { |
| unsigned char *ptr = in; |
| unsigned char *ptrout = out; |
| unsigned char t; |
| |
| while (length--) { |
| t = (*ptr & 0xF0) >> 4; |
| if (t < 10) |
| *ptrout = t + '0'; |
| else |
| *ptrout = t + 'A' - 10; |
| |
| ptrout++; |
| |
| t = (*ptr & 0x0F); |
| if (t < 10) |
| *ptrout = t + '0'; |
| else |
| *ptrout = t + 'A' - 10; |
| |
| ptr++; |
| ptrout++; |
| } |
| *ptrout = 0; |
| } |
| |
| unsigned long long fail_time; |
| static ssize_t set_config(struct device *dev, struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| int bus_id; |
| int address; |
| int operation; |
| int wr_number = 0; |
| int rd_number = 0; |
| unsigned int speedhz = 0; |
| char *buf_test; |
| |
| int length = 0; |
| void *vir_addr_wr = NULL; |
| void *vir_addr_rd = NULL; |
| /* int status; */ |
| int ret = 0; |
| int scanf_ret = 0; |
| unsigned char tmpbuffer[128]; |
| |
| pr_info("%s\n", buf); |
| scanf_ret = sscanf(buf, "%d %x %d %d %d %d %1023s", |
| &bus_id, &address, &operation, |
| &wr_number, &rd_number, &speedhz, |
| data_buffer); |
| if (scanf_ret) { |
| pr_info("bus_id:%d,address:%x,operation:0x%x, speed:%d\n", |
| bus_id, address, operation, speedhz); |
| if ((address != 0) && (operation <= 2)) { |
| length = strlen(data_buffer); |
| |
| if (operation == 1) { |
| if ((length >> 1) != wr_number) |
| pr_info("Error length of data number = %d,length = %d\n", |
| wr_number, length >> 1); |
| vir_addr_wr = kzalloc(wr_number, GFP_KERNEL); |
| if (vir_addr_wr == NULL) { |
| |
| pr_info("alloc virtual memory failed\n"); |
| goto err; |
| } |
| get_hexbuffer(data_buffer, vir_addr_wr); |
| pr_info("data_buffer:%s\n", data_buffer); |
| |
| |
| } |
| if (operation == 2) { |
| vir_addr_rd = kzalloc(rd_number, GFP_KERNEL); |
| if (vir_addr_rd == NULL) { |
| |
| pr_info("alloc virtual memory failed\n"); |
| goto err; |
| } |
| } |
| if (operation == 0) { |
| vir_addr_wr = kzalloc(wr_number, GFP_KERNEL); |
| if (vir_addr_wr == NULL) { |
| |
| pr_info("alloc virtual memory failed\n"); |
| goto err; |
| } |
| vir_addr_rd = kzalloc(rd_number, GFP_KERNEL); |
| if (vir_addr_rd == NULL) { |
| kfree(vir_addr_wr); |
| pr_info("alloc virtual memory failed\n"); |
| goto err; |
| } |
| get_hexbuffer(data_buffer, vir_addr_wr); |
| pr_info("data_buffer:%s\n", data_buffer); |
| } |
| |
| if (operation == 0) { /* 0:WRRD 1:WR 2:RD */ |
| t3 = sched_clock(); |
| /* while (1) { */ |
| /* msleep(500);*/ |
| ret = mt_i2c_test_wrrd(bus_id, address, |
| wr_number, |
| rd_number, |
| vir_addr_wr, |
| vir_addr_rd, speedhz); |
| /* buf_test = (char *)vir_addr_rd; |
| * if (buf_test[0] != 0x75) { |
| * fail_time++; |
| * pr_info("I2C press fail_time=%llu\n", |
| * fail_time); |
| * } |
| * } |
| */ |
| t4 = sched_clock(); |
| /* pr_info("test i2c wr-rd:hw time=%llu, |
| * total=%llu\n", t2-t1, t4-t3); |
| */ |
| } else { |
| t5 = sched_clock(); |
| ret = i2c_trans_data(bus_id, address, |
| vir_addr_wr, vir_addr_rd, |
| operation, wr_number, |
| rd_number, speedhz); |
| buf_test = (char *)vir_addr_rd; |
| |
| t6 = sched_clock(); |
| /* pr_info("test i2c :hw time=%llu,total=%llu\n", |
| * t2-t1, t6-t5); |
| */ |
| } |
| /* dealing */ |
| |
| if (ret >= 0) { |
| if (operation == 2) { |
| hex2string(vir_addr_rd, tmpbuffer, |
| rd_number); |
| snprintf(data_buffer, |
| sizeof(data_buffer), |
| "1 %s", tmpbuffer); |
| pr_info("Actual return Value:%d %s\n", |
| ret, data_buffer); |
| } else if (operation == 0) { |
| hex2string(vir_addr_rd, tmpbuffer, |
| rd_number); |
| snprintf(data_buffer, |
| sizeof(data_buffer), |
| "1 %s", tmpbuffer); |
| pr_info("Actual return Value:%d %s\n", |
| ret, data_buffer); |
| if (strncmp(tmpbuffer, "68", 1)) { |
| fail_time++; |
| pr_info("data_buffer != 68, fail time=%llu\n", |
| fail_time); |
| } |
| } else { |
| snprintf(data_buffer, |
| sizeof(data_buffer), |
| "1 %s", "00"); |
| pr_info("Actual return Value:%d %s\n", |
| ret, data_buffer); |
| } |
| |
| } else if (ret < 0) { |
| if (ret == -EINVAL) |
| snprintf(data_buffer, |
| sizeof(data_buffer), |
| "0 %s", "Invalid Parameter"); |
| else if (ret == -ETIMEDOUT) |
| snprintf(data_buffer, |
| sizeof(data_buffer), |
| "0 %s", "Transfer Timeout"); |
| else if (ret == -EREMOTEIO) |
| snprintf(data_buffer, |
| sizeof(data_buffer), |
| "0 %s", "Ack Error"); |
| else |
| snprintf(data_buffer, |
| sizeof(data_buffer), |
| "0 %s", "unknown error"); |
| pr_info("Actual return Value:%d %p\n", ret, |
| data_buffer); |
| } |
| kfree(vir_addr_rd); |
| kfree(vir_addr_wr); |
| |
| } else { |
| struct i2c_adapter *adap = i2c_get_adapter(bus_id); |
| |
| if (adap) { |
| struct mt_i2c *i2c = i2c_get_adapdata(adap); |
| |
| if (operation == 3) { |
| i2c_dump_info(i2c); |
| } else if (operation == 4) { |
| i2c_test_reg(bus_id, 0); |
| i2c_dump_info(i2c); |
| i2c_test_reg(bus_id, 0xFFFFFFFF); |
| i2c_dump_info(i2c); |
| } else if (operation == 5) { |
| i2c_ext_conf_test(bus_id, address); |
| } else if (operation == 9) { |
| i2c_soft_reset(bus_id); |
| i2c_dump_info(i2c); |
| } else if (operation == 6) { |
| mt_i2c_test_multi_wr(bus_id, address); |
| if (bus_id == 0) { |
| /* I2C0 PINMUX2 power on */ |
| /* hwPowerOn(MT65XX_POWER_LDO_VMC1, |
| * VOL_DEFAULT, |
| * "i2c_pinmux"); |
| */ |
| /* hwPowerOn(MT65XX_POWER_LDO_VMCH1, |
| * VOL_DEFAULT, |
| * "i2c_pinmux"); |
| */ |
| } |
| |
| } else if (operation == 7) { |
| mt_i2c_test(1, 0x50); |
| } else { |
| pr_info("i2c debug system: Parameter invalid!\n"); |
| } |
| } else { |
| /*adap invalid */ |
| pr_info("i2c debug system: get adap fail!\n"); |
| } |
| } |
| } else { |
| /*parameter invalid */ |
| pr_info("i2c debug system: Parameter invalid!\n"); |
| } |
| |
| return count; |
| err: |
| pr_info("analyze failed\n"); |
| return -1; |
| } |
| |
| static DEVICE_ATTR(ut, 0660, show_config, set_config); |
| |
| static int i2c_common_probe(struct i2c_client *client, |
| const struct i2c_device_id *id) |
| { |
| int ret = 0; |
| /* your code here\A3\ACyour should save client in your own way */ |
| pr_info("i2c_common device probe\n"); |
| ret = device_create_file(&client->dev, &dev_attr_ut); |
| pr_info("i2c_common device probe ret = %d\n", ret); |
| return ret; |
| } |
| |
| static int i2c_common_remove(struct i2c_client *client) |
| { |
| int ret = 0; |
| /* your code here */ |
| device_remove_file(&client->dev, &dev_attr_ut); |
| return ret; |
| } |
| |
| static const struct i2c_device_id i2c_common_test_id[] = { |
| {"mtk-iicd", 0}, |
| {}, |
| }; |
| |
| static const struct of_device_id i2c_common_of_match[] = { |
| {.compatible = "mediatek,mtk-iicd"}, |
| {}, |
| }; |
| |
| static struct i2c_driver i2c_common_driver = { |
| .driver = { |
| .name = "mtk-iicd", |
| .of_match_table = i2c_common_of_match, |
| }, |
| .probe = i2c_common_probe, |
| .remove = i2c_common_remove, |
| .id_table = i2c_common_test_id, |
| }; |
| module_i2c_driver(i2c_common_driver); |
| |
| MODULE_LICENSE("GPL"); |
| MODULE_DESCRIPTION("MediaTek I2C Bus Driver Test Driver"); |
| MODULE_AUTHOR("Ranran Lu"); |