444 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			444 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|  | /*
 | ||
|  |  * Copyright (c) 2006-2018, RT-Thread Development Team | ||
|  |  * | ||
|  |  * SPDX-License-Identifier: Apache-2.0 | ||
|  |  * | ||
|  |  * Change Logs: | ||
|  |  * Date           Author        Notes | ||
|  |  * 2012-04-25     weety         first version | ||
|  |  */ | ||
|  | 
 | ||
|  | #include <rtdevice.h>
 | ||
|  | 
 | ||
|  | #ifdef RT_I2C_BIT_DEBUG
 | ||
|  | #define bit_dbg(fmt, ...)   rt_kprintf(fmt, ##__VA_ARGS__)
 | ||
|  | #else
 | ||
|  | #define bit_dbg(fmt, ...)
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | #define SET_SDA(ops, val)   ops->set_sda(ops->data, val)
 | ||
|  | #define SET_SCL(ops, val)   ops->set_scl(ops->data, val)
 | ||
|  | #define GET_SDA(ops)        ops->get_sda(ops->data)
 | ||
|  | #define GET_SCL(ops)        ops->get_scl(ops->data)
 | ||
|  | 
 | ||
|  | rt_inline void i2c_delay(struct rt_i2c_bit_ops *ops) | ||
|  | { | ||
|  |     ops->udelay((ops->delay_us + 1) >> 1); | ||
|  | } | ||
|  | 
 | ||
|  | rt_inline void i2c_delay2(struct rt_i2c_bit_ops *ops) | ||
|  | { | ||
|  |     ops->udelay(ops->delay_us); | ||
|  | } | ||
|  | 
 | ||
|  | #define SDA_L(ops)          SET_SDA(ops, 0)
 | ||
|  | #define SDA_H(ops)          SET_SDA(ops, 1)
 | ||
|  | #define SCL_L(ops)          SET_SCL(ops, 0)
 | ||
|  | 
 | ||
|  | /**
 | ||
|  |  * release scl line, and wait scl line to high. | ||
|  |  */ | ||
|  | static rt_err_t SCL_H(struct rt_i2c_bit_ops *ops) | ||
|  | { | ||
|  |     rt_tick_t start; | ||
|  | 
 | ||
|  |     SET_SCL(ops, 1); | ||
|  | 
 | ||
|  |     if (!ops->get_scl) | ||
|  |         goto done; | ||
|  | 
 | ||
|  |     start = rt_tick_get(); | ||
|  |     while (!GET_SCL(ops)) | ||
|  |     { | ||
|  |         if ((rt_tick_get() - start) > ops->timeout) | ||
|  |             return -RT_ETIMEOUT; | ||
|  |         rt_thread_delay((ops->timeout + 1) >> 1); | ||
|  |     } | ||
|  | #ifdef RT_I2C_BIT_DEBUG
 | ||
|  |     if (rt_tick_get() != start) | ||
|  |     { | ||
|  |         bit_dbg("wait %ld tick for SCL line to go high\n", | ||
|  |                 rt_tick_get() - start); | ||
|  |     } | ||
|  | #endif
 | ||
|  | 
 | ||
|  | done: | ||
|  |     i2c_delay(ops); | ||
|  | 
 | ||
|  |     return RT_EOK; | ||
|  | } | ||
|  | 
 | ||
|  | static void i2c_start(struct rt_i2c_bit_ops *ops) | ||
|  | { | ||
|  | #ifdef RT_I2C_BIT_DEBUG
 | ||
|  |     if (ops->get_scl && !GET_SCL(ops)) | ||
|  |     { | ||
|  |         bit_dbg("I2C bus error, SCL line low\n"); | ||
|  |     } | ||
|  |     if (ops->get_sda && !GET_SDA(ops)) | ||
|  |     { | ||
|  |         bit_dbg("I2C bus error, SDA line low\n"); | ||
|  |     } | ||
|  | #endif
 | ||
|  |     SDA_L(ops); | ||
|  |     i2c_delay(ops); | ||
|  |     SCL_L(ops); | ||
|  | } | ||
|  | 
 | ||
|  | static void i2c_restart(struct rt_i2c_bit_ops *ops) | ||
|  | { | ||
|  |     SDA_H(ops); | ||
|  |     SCL_H(ops); | ||
|  |     i2c_delay(ops); | ||
|  |     SDA_L(ops); | ||
|  |     i2c_delay(ops); | ||
|  |     SCL_L(ops); | ||
|  | } | ||
|  | 
 | ||
|  | static void i2c_stop(struct rt_i2c_bit_ops *ops) | ||
|  | { | ||
|  |     SDA_L(ops); | ||
|  |     i2c_delay(ops); | ||
|  |     SCL_H(ops); | ||
|  |     i2c_delay(ops); | ||
|  |     SDA_H(ops); | ||
|  |     i2c_delay2(ops); | ||
|  | } | ||
|  | 
 | ||
|  | rt_inline rt_bool_t i2c_waitack(struct rt_i2c_bit_ops *ops) | ||
|  | { | ||
|  |     rt_bool_t ack; | ||
|  | 
 | ||
|  |     SDA_H(ops); | ||
|  |     i2c_delay(ops); | ||
|  | 
 | ||
|  |     if (SCL_H(ops) < 0) | ||
|  |     { | ||
|  |         bit_dbg("wait ack timeout\n"); | ||
|  | 
 | ||
|  |         return -RT_ETIMEOUT; | ||
|  |     } | ||
|  | 
 | ||
|  |     ack = !GET_SDA(ops);    /* ACK : SDA pin is pulled low */ | ||
|  |     bit_dbg("%s\n", ack ? "ACK" : "NACK"); | ||
|  | 
 | ||
|  |     SCL_L(ops); | ||
|  | 
 | ||
|  |     return ack; | ||
|  | } | ||
|  | 
 | ||
|  | static rt_int32_t i2c_writeb(struct rt_i2c_bus_device *bus, rt_uint8_t data) | ||
|  | { | ||
|  |     rt_int32_t i; | ||
|  |     rt_uint8_t bit; | ||
|  | 
 | ||
|  |     struct rt_i2c_bit_ops *ops = bus->priv; | ||
|  | 
 | ||
|  |     for (i = 7; i >= 0; i--) | ||
|  |     { | ||
|  |         SCL_L(ops); | ||
|  |         bit = (data >> i) & 1; | ||
|  |         SET_SDA(ops, bit); | ||
|  |         i2c_delay(ops); | ||
|  |         if (SCL_H(ops) < 0) | ||
|  |         { | ||
|  |             bit_dbg("i2c_writeb: 0x%02x, " | ||
|  |                     "wait scl pin high timeout at bit %d\n", | ||
|  |                     data, i); | ||
|  | 
 | ||
|  |             return -RT_ETIMEOUT; | ||
|  |         } | ||
|  |     } | ||
|  |     SCL_L(ops); | ||
|  |     i2c_delay(ops); | ||
|  | 
 | ||
|  |     return i2c_waitack(ops); | ||
|  | } | ||
|  | 
 | ||
|  | static rt_int32_t i2c_readb(struct rt_i2c_bus_device *bus) | ||
|  | { | ||
|  |     rt_uint8_t i; | ||
|  |     rt_uint8_t data = 0; | ||
|  |     struct rt_i2c_bit_ops *ops = bus->priv; | ||
|  | 
 | ||
|  |     SDA_H(ops); | ||
|  |     i2c_delay(ops); | ||
|  |     for (i = 0; i < 8; i++) | ||
|  |     { | ||
|  |         data <<= 1; | ||
|  | 
 | ||
|  |         if (SCL_H(ops) < 0) | ||
|  |         { | ||
|  |             bit_dbg("i2c_readb: wait scl pin high " | ||
|  |                     "timeout at bit %d\n", 7 - i); | ||
|  | 
 | ||
|  |             return -RT_ETIMEOUT; | ||
|  |         } | ||
|  | 
 | ||
|  |         if (GET_SDA(ops)) | ||
|  |             data |= 1; | ||
|  |         SCL_L(ops); | ||
|  |         i2c_delay2(ops); | ||
|  |     } | ||
|  | 
 | ||
|  |     return data; | ||
|  | } | ||
|  | 
 | ||
|  | static rt_size_t i2c_send_bytes(struct rt_i2c_bus_device *bus, | ||
|  |                                 struct rt_i2c_msg        *msg) | ||
|  | { | ||
|  |     rt_int32_t ret; | ||
|  |     rt_size_t bytes = 0; | ||
|  |     const rt_uint8_t *ptr = msg->buf; | ||
|  |     rt_int32_t count = msg->len; | ||
|  |     rt_uint16_t ignore_nack = msg->flags & RT_I2C_IGNORE_NACK; | ||
|  | 
 | ||
|  |     while (count > 0) | ||
|  |     { | ||
|  |         ret = i2c_writeb(bus, *ptr); | ||
|  | 
 | ||
|  |         if ((ret > 0) || (ignore_nack && (ret == 0))) | ||
|  |         { | ||
|  |             count --; | ||
|  |             ptr ++; | ||
|  |             bytes ++; | ||
|  |         } | ||
|  |         else if (ret == 0) | ||
|  |         { | ||
|  |             i2c_dbg("send bytes: NACK.\n"); | ||
|  | 
 | ||
|  |             return 0; | ||
|  |         } | ||
|  |         else | ||
|  |         { | ||
|  |             i2c_dbg("send bytes: error %d\n", ret); | ||
|  | 
 | ||
|  |             return ret; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     return bytes; | ||
|  | } | ||
|  | 
 | ||
|  | static rt_err_t i2c_send_ack_or_nack(struct rt_i2c_bus_device *bus, int ack) | ||
|  | { | ||
|  |     struct rt_i2c_bit_ops *ops = bus->priv; | ||
|  | 
 | ||
|  |     if (ack) | ||
|  |         SET_SDA(ops, 0); | ||
|  |     i2c_delay(ops); | ||
|  |     if (SCL_H(ops) < 0) | ||
|  |     { | ||
|  |         bit_dbg("ACK or NACK timeout\n"); | ||
|  | 
 | ||
|  |         return -RT_ETIMEOUT; | ||
|  |     } | ||
|  |     SCL_L(ops); | ||
|  | 
 | ||
|  |     return RT_EOK; | ||
|  | } | ||
|  | 
 | ||
|  | static rt_size_t i2c_recv_bytes(struct rt_i2c_bus_device *bus, | ||
|  |                                 struct rt_i2c_msg        *msg) | ||
|  | { | ||
|  |     rt_int32_t val; | ||
|  |     rt_int32_t bytes = 0;   /* actual bytes */ | ||
|  |     rt_uint8_t *ptr = msg->buf; | ||
|  |     rt_int32_t count = msg->len; | ||
|  |     const rt_uint32_t flags = msg->flags; | ||
|  | 
 | ||
|  |     while (count > 0) | ||
|  |     { | ||
|  |         val = i2c_readb(bus); | ||
|  |         if (val >= 0) | ||
|  |         { | ||
|  |             *ptr = val; | ||
|  |             bytes ++; | ||
|  |         } | ||
|  |         else | ||
|  |         { | ||
|  |             break; | ||
|  |         } | ||
|  | 
 | ||
|  |         ptr ++; | ||
|  |         count --; | ||
|  | 
 | ||
|  |         bit_dbg("recieve bytes: 0x%02x, %s\n", | ||
|  |                 val, (flags & RT_I2C_NO_READ_ACK) ? | ||
|  |                 "(No ACK/NACK)" : (count ? "ACK" : "NACK")); | ||
|  | 
 | ||
|  |         if (!(flags & RT_I2C_NO_READ_ACK)) | ||
|  |         { | ||
|  |             val = i2c_send_ack_or_nack(bus, count); | ||
|  |             if (val < 0) | ||
|  |                 return val; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     return bytes; | ||
|  | } | ||
|  | 
 | ||
|  | static rt_int32_t i2c_send_address(struct rt_i2c_bus_device *bus, | ||
|  |                                    rt_uint8_t                addr, | ||
|  |                                    rt_int32_t                retries) | ||
|  | { | ||
|  |     struct rt_i2c_bit_ops *ops = bus->priv; | ||
|  |     rt_int32_t i; | ||
|  |     rt_err_t ret = 0; | ||
|  | 
 | ||
|  |     for (i = 0; i <= retries; i++) | ||
|  |     { | ||
|  |         ret = i2c_writeb(bus, addr); | ||
|  |         if (ret == 1 || i == retries) | ||
|  |             break; | ||
|  |         bit_dbg("send stop condition\n"); | ||
|  |         i2c_stop(ops); | ||
|  |         i2c_delay2(ops); | ||
|  |         bit_dbg("send start condition\n"); | ||
|  |         i2c_start(ops); | ||
|  |     } | ||
|  | 
 | ||
|  |     return ret; | ||
|  | } | ||
|  | 
 | ||
|  | static rt_err_t i2c_bit_send_address(struct rt_i2c_bus_device *bus, | ||
|  |                                      struct rt_i2c_msg        *msg) | ||
|  | { | ||
|  |     rt_uint16_t flags = msg->flags; | ||
|  |     rt_uint16_t ignore_nack = msg->flags & RT_I2C_IGNORE_NACK; | ||
|  |     struct rt_i2c_bit_ops *ops = bus->priv; | ||
|  | 
 | ||
|  |     rt_uint8_t addr1, addr2; | ||
|  |     rt_int32_t retries; | ||
|  |     rt_err_t ret; | ||
|  | 
 | ||
|  |     retries = ignore_nack ? 0 : bus->retries; | ||
|  | 
 | ||
|  |     if (flags & RT_I2C_ADDR_10BIT) | ||
|  |     { | ||
|  |         addr1 = 0xf0 | ((msg->addr >> 7) & 0x06); | ||
|  |         addr2 = msg->addr & 0xff; | ||
|  | 
 | ||
|  |         bit_dbg("addr1: %d, addr2: %d\n", addr1, addr2); | ||
|  | 
 | ||
|  |         ret = i2c_send_address(bus, addr1, retries); | ||
|  |         if ((ret != 1) && !ignore_nack) | ||
|  |         { | ||
|  |             bit_dbg("NACK: sending first addr\n"); | ||
|  | 
 | ||
|  |             return -RT_EIO; | ||
|  |         } | ||
|  | 
 | ||
|  |         ret = i2c_writeb(bus, addr2); | ||
|  |         if ((ret != 1) && !ignore_nack) | ||
|  |         { | ||
|  |             bit_dbg("NACK: sending second addr\n"); | ||
|  | 
 | ||
|  |             return -RT_EIO; | ||
|  |         } | ||
|  |         if (flags & RT_I2C_RD) | ||
|  |         { | ||
|  |             bit_dbg("send repeated start condition\n"); | ||
|  |             i2c_restart(ops); | ||
|  |             addr1 |= 0x01; | ||
|  |             ret = i2c_send_address(bus, addr1, retries); | ||
|  |             if ((ret != 1) && !ignore_nack) | ||
|  |             { | ||
|  |                 bit_dbg("NACK: sending repeated addr\n"); | ||
|  | 
 | ||
|  |                 return -RT_EIO; | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  |     else | ||
|  |     { | ||
|  |         /* 7-bit addr */ | ||
|  |         addr1 = msg->addr << 1; | ||
|  |         if (flags & RT_I2C_RD) | ||
|  |             addr1 |= 1; | ||
|  |         ret = i2c_send_address(bus, addr1, retries); | ||
|  |         if ((ret != 1) && !ignore_nack) | ||
|  |             return -RT_EIO; | ||
|  |     } | ||
|  | 
 | ||
|  |     return RT_EOK; | ||
|  | } | ||
|  | 
 | ||
|  | static rt_size_t i2c_bit_xfer(struct rt_i2c_bus_device *bus, | ||
|  |                               struct rt_i2c_msg         msgs[], | ||
|  |                               rt_uint32_t               num) | ||
|  | { | ||
|  |     struct rt_i2c_msg *msg; | ||
|  |     struct rt_i2c_bit_ops *ops = bus->priv; | ||
|  |     rt_int32_t i, ret; | ||
|  |     rt_uint16_t ignore_nack; | ||
|  | 
 | ||
|  |     bit_dbg("send start condition\n"); | ||
|  |     i2c_start(ops); | ||
|  |     for (i = 0; i < num; i++) | ||
|  |     { | ||
|  |         msg = &msgs[i]; | ||
|  |         ignore_nack = msg->flags & RT_I2C_IGNORE_NACK; | ||
|  |         if (!(msg->flags & RT_I2C_NO_START)) | ||
|  |         { | ||
|  |             if (i) | ||
|  |             { | ||
|  |                 i2c_restart(ops); | ||
|  |             } | ||
|  |             ret = i2c_bit_send_address(bus, msg); | ||
|  |             if ((ret != RT_EOK) && !ignore_nack) | ||
|  |             { | ||
|  |                 bit_dbg("receive NACK from device addr 0x%02x msg %d\n", | ||
|  |                         msgs[i].addr, i); | ||
|  |                 goto out; | ||
|  |             } | ||
|  |         } | ||
|  |         if (msg->flags & RT_I2C_RD) | ||
|  |         { | ||
|  |             ret = i2c_recv_bytes(bus, msg); | ||
|  |             if (ret >= 1) | ||
|  |                 bit_dbg("read %d byte%s\n", ret, ret == 1 ? "" : "s"); | ||
|  |             if (ret < msg->len) | ||
|  |             { | ||
|  |                 if (ret >= 0) | ||
|  |                     ret = -RT_EIO; | ||
|  |                 goto out; | ||
|  |             } | ||
|  |         } | ||
|  |         else | ||
|  |         { | ||
|  |             ret = i2c_send_bytes(bus, msg); | ||
|  |             if (ret >= 1) | ||
|  |                 bit_dbg("write %d byte%s\n", ret, ret == 1 ? "" : "s"); | ||
|  |             if (ret < msg->len) | ||
|  |             { | ||
|  |                 if (ret >= 0) | ||
|  |                     ret = -RT_ERROR; | ||
|  |                 goto out; | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  |     ret = i; | ||
|  | 
 | ||
|  | out: | ||
|  |     bit_dbg("send stop condition\n"); | ||
|  |     i2c_stop(ops); | ||
|  | 
 | ||
|  |     return ret; | ||
|  | } | ||
|  | 
 | ||
|  | static const struct rt_i2c_bus_device_ops i2c_bit_bus_ops = | ||
|  | { | ||
|  |     i2c_bit_xfer, | ||
|  |     RT_NULL, | ||
|  |     RT_NULL | ||
|  | }; | ||
|  | 
 | ||
|  | rt_err_t rt_i2c_bit_add_bus(struct rt_i2c_bus_device *bus, | ||
|  |                             const char               *bus_name) | ||
|  | { | ||
|  |     bus->ops = &i2c_bit_bus_ops; | ||
|  | 
 | ||
|  |     return rt_i2c_bus_device_register(bus, bus_name); | ||
|  | } |