处理器只支持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);