处理器只支持3个i2c通道,常常会不够用,最近写了一个gpio模拟i2c的driver,把模拟的i2c通道加入了i2c-core中,作为第 4 通道,调用接口与标准i2c一致,代码如下:
#define DELAY 2 #define SCL_GPIO GPIO_I2C_SCL #define SDA_GPIO GPIO_I2C_SDA static inline void i2c_delay(uint16_t delay) { udelay(delay); } static inline void set_scl_low(void) { gpio_direction_output(SCL_GPIO, 0); } static inline void set_scl_high(void) { gpio_direction_output(SCL_GPIO, 1); } static inline void set_sda_low(void) { gpio_direction_output(SDA_GPIO, 0); } static inline void set_sda_high(void) { gpio_direction_output(SDA_GPIO, 1); } static inline void set_sda_in(void) { gpio_direction_input(SDA_GPIO); } static inline uint8_t get_sda_bit(void) { return __gpio_get_value(SDA_GPIO); } int i2c_gpio_init(void) { int err; err = gpio_request(SCL_GPIO, NULL); if (err != 0) return err; err = gpio_request(SDA_GPIO, NULL); set_sda_high(); set_scl_high(); return err; } void i2c_gpio_free(void) { gpio_free(SDA_GPIO); gpio_free(SCL_GPIO); } static inline void i2c_start(void) { set_sda_high(); i2c_delay(DELAY); set_scl_high(); i2c_delay(DELAY); set_sda_low(); i2c_delay(DELAY); set_scl_low(); i2c_delay(DELAY); } static inline void i2c_stop(void) { set_sda_low(); i2c_delay(DELAY); set_scl_high(); i2c_delay(4*DELAY); set_sda_high(); i2c_delay(4*DELAY); } /* * return value: * 0 --- 收到ACK * 1 --- 没收到ACK */ uint8_t i2c_send_byte(uint8_t send_byte) { uint8_t rc = 0; uint8_t out_mask = 0x80; uint8_t count = 8; uint8_t value; while(count > 0) { set_scl_low(); i2c_delay(DELAY); value = ((send_byte & out_mask) ? 1 : 0); if(value == 1) { set_sda_high(); } else { set_sda_low(); } send_byte <<= 1; i2c_delay(DELAY); set_scl_high(); i2c_delay(DELAY); count--; } set_scl_low(); set_sda_in(); i2c_delay(4*DELAY); set_scl_high(); i2c_delay(DELAY); rc = get_sda_bit(); i2c_delay(DELAY); set_scl_low(); return rc; } /* * ack = 0 发送ACK * ack = 1 发送非ACK停止读取 */ void i2c_read_byte(uint8_t *buffer, uint8_t ack) { uint8_t count = 0x08; uint8_t data = 0x00; uint8_t temp = 0; while(count > 0) { set_scl_low(); i2c_delay(2*DELAY); if(count == 8) set_sda_in(); i2c_delay(DELAY); set_scl_high(); i2c_delay(2*DELAY); temp = get_sda_bit(); data <<= 1; if(temp) data |= 0x01; i2c_delay(DELAY); count--; } set_scl_low(); i2c_delay(2*DELAY); if(ack) { set_sda_high(); } else { set_sda_low(); } i2c_delay(DELAY); set_scl_high(); i2c_delay(2*DELAY); *buffer = data; set_scl_low(); } struct atxx_i2c_gpio { struct i2c_adapter adap; struct device *dev; struct clk *clk; struct i2c_msg *msg; spinlock_t lock; }; static int send_i2c(struct atxx_i2c_gpio *i2c) { int i; uint8_t ack; spin_lock_irq(&i2c->lock); i2c_start(); ack = i2c_send_byte((i2c->msg->addr << 1) | 0x00); if(ack){ goto out; } for(i = 0; i < i2c->msg->len; i++) { ack = i2c_send_byte(i2c->msg->buf[i]); if(ack){ goto out; } } out: i2c_stop(); spin_unlock_irq(&i2c->lock); return ack; } static int recv_i2c(struct atxx_i2c_gpio *i2c) { int i; uint8_t ack; spin_lock_irq(&i2c->lock); i2c_start(); ack = i2c_send_byte((i2c->msg->addr << 1) | 0x01); if(ack){ goto out; } for(i = 0; i < i2c->msg->len - 1; i++) { i2c_read_byte(&i2c->msg->buf[i], 0); } i2c_read_byte(&i2c->msg->buf[i2c->msg->len - 1], 1); out: i2c_stop(); spin_unlock_irq(&i2c->lock); return ack; } static int i2c_atxx_gpio_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { int i, ret; struct atxx_i2c_gpio *i2c = i2c_get_adapdata(adap); for (i = 0; i < num; i++) { i2c->msg = &msgs[i]; i2c->msg->flags = msgs[i].flags; if (i2c->msg->flags & I2C_M_RD) { ret = recv_i2c(i2c); if (ret) { printk("recv_i2c failed. dev_name(%s).addr = 0x%02x\n", dev_name(i2c->dev), i2c->msg[0].addr); return -EAGAIN; } } else { ret = send_i2c(i2c); if (ret) { printk("send_i2c failed. dev_name(%s).addr = 0x%02x\n", dev_name(i2c->dev), i2c->msg[0].addr); return -EAGAIN; } } } return num; } static uint32_t i2c_atxx_gpio_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } static struct i2c_algorithm i2c_atxx_gpio_algo = { .master_xfer = i2c_atxx_gpio_xfer, .functionality = i2c_atxx_gpio_func, }; static int i2c_atxx_gpio_probe(struct platform_device *pdev) { int ret; struct atxx_i2c_gpio *i2c; ret = i2c_gpio_init(); if(ret) { dev_err(&pdev->dev, "couldn't request gpio\n"); return ret; } i2c = kzalloc(sizeof(*i2c), GFP_KERNEL); if (!i2c) { dev_err(&pdev->dev, "couldn't allocate memory\n");; ret = -ENOMEM; goto err_mem; } spin_lock_init(&i2c->lock); i2c->dev = &pdev->dev; i2c->adap.owner = THIS_MODULE; i2c->adap.algo = &i2c_atxx_gpio_algo; i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; i2c->adap.timeout = I2C_ATXX_TIMEOUT; i2c->adap.retries = I2C_ATXX_RETRIES; i2c->adap.algo_data = i2c; i2c->adap.dev.parent = &pdev->dev; i2c->adap.nr = pdev->id != -1 ? pdev->id : 0; sprintf(i2c->adap.name, "ATXX i2c gpio adapter"); platform_set_drvdata(pdev, i2c); i2c_set_adapdata(&i2c->adap, i2c); ret = i2c_add_numbered_adapter(&i2c->adap); if (ret) { dev_err(i2c->dev, "failure adding adapter\n"); goto err_adp; } return 0; err_adp: kfree(i2c); err_mem: i2c_gpio_free(); return ret; } static int i2c_atxx_gpio_remove(struct platform_device *pdev) { struct atxx_i2c_gpio *i2c = platform_get_drvdata(pdev); platform_set_drvdata(pdev, NULL); i2c_del_adapter(&i2c->adap); kfree(i2c); i2c_gpio_free(); return 0; } static int i2c_atxx_gpio_suspend(struct device *dev) { return 0; } static int i2c_atxx_gpio_resume(struct device *dev) { return 0; } static void i2c_atxx_gpio_shutdown(struct platform_device *pdev) { /* TODO: */ } const struct dev_pm_ops i2c_atxx_gpio_pm_ops = { .suspend = i2c_atxx_gpio_suspend, .resume = i2c_atxx_gpio_resume, }; static struct platform_driver i2c_atxx_gpio_driver = { .driver = { .name = "i2c-gpio", .owner = THIS_MODULE, .pm = &i2c_atxx_gpio_pm_ops, }, .probe = i2c_atxx_gpio_probe, .remove = i2c_atxx_gpio_remove, .shutdown = i2c_atxx_gpio_shutdown, }; static int __init i2c_adap_atxx_gpio_init(void) { return platform_driver_register(&i2c_atxx_gpio_driver); } static void __exit i2c_adap_atxx_gpio_exit(void) { platform_driver_unregister(&i2c_atxx_gpio_driver); } arch_initcall(i2c_adap_atxx_gpio_init);