1 /*
2  * Copyright (c) 2006-2021, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2017-12-04     Haley        the first version
9  */
10 
11 #include <rtthread.h>
12 #include <rtdevice.h>
13 #include "am_mcu_apollo.h"
14 
15 #ifdef RT_USING_SMBUS
16 
17 #define SMBUS_GPIO_SDA    5
18 #define SMBUS_GPIO_SCL    6
19 
20 #define mSDA_LOW()        am_hal_gpio_out_bit_clear(SMBUS_GPIO_SDA)    /* Clear SDA line */
21 #define mSDA_HIGH()       am_hal_gpio_out_bit_set(SMBUS_GPIO_SDA)      /* Set SDA line */
22 #define mSCL_LOW()        am_hal_gpio_out_bit_clear(SMBUS_GPIO_SCL)    /* Clear SCL line */
23 #define mSCL_HIGH()       am_hal_gpio_out_bit_set(SMBUS_GPIO_SCL)      /* Set SCL line */
24 
25 #define mSDA_READ()       am_hal_gpio_input_bit_read(SMBUS_GPIO_SDA)   /* Read SDA line */
26 
27 #define mSDA_IN()         am_hal_gpio_pin_config(SMBUS_GPIO_SDA, AM_HAL_GPIO_INPUT | AM_HAL_GPIO_PULL6K)  /* Set SDA as Input */
28 #define mSDA_OUT()        am_hal_gpio_pin_config(SMBUS_GPIO_SDA, AM_HAL_GPIO_OUTPUT)                      /* Set SDA as Output */
29 #define mSCL_OUT()        am_hal_gpio_pin_config(SMBUS_GPIO_SCL, AM_HAL_GPIO_OUTPUT)                      /* Set SCL as Output */
30 
31 #define ACK       0
32 #define NACK      1
33 
34 /* SCL keep time */
keep_delay(void)35 static void keep_delay(void)
36 {
37     int i;
38     for(i = 0; i < 30; i++)
39        __nop();
40 }
41 
few_delay(void)42 static void few_delay(void)
43 {
44     __nop();
45     __nop();
46 }
47 
am_smbus_send_bit(rt_uint8_t send_bit)48 static rt_uint8_t am_smbus_send_bit(rt_uint8_t send_bit)
49 {
50     mSDA_OUT();
51     few_delay();
52 
53     if(send_bit)        /* Send a bit */
54         mSDA_HIGH();
55     else
56         mSDA_LOW();
57 
58     mSCL_HIGH();        /* High Level of Clock Pulse */
59     keep_delay();
60 
61     mSCL_LOW();
62     keep_delay();
63 
64     return 0;
65 }
66 
am_smbus_read_bit(void)67 static rt_uint8_t am_smbus_read_bit(void)
68 {
69     rt_uint8_t read_bit;
70 
71     mSDA_IN();
72     few_delay();
73 
74     mSCL_HIGH();             /* High Level of Clock Pulse */
75     keep_delay();
76 
77     read_bit = mSDA_READ();  /* Read a bit, save it in Read_bit */
78 
79     mSCL_LOW();
80     keep_delay();
81 
82     return read_bit;
83 }
84 
am_smbus_start_bit(void)85 static void am_smbus_start_bit(void)
86 {
87     mSDA_OUT();
88     mSDA_HIGH();     /* Generate bus free time between Stop */
89     keep_delay();
90     mSCL_HIGH();
91     keep_delay();
92 
93     mSDA_LOW();      /* Hold time after (Repeated) Start */
94     keep_delay();
95 
96     mSCL_LOW();
97     keep_delay();
98 }
99 
am_smbus_stop_bit(void)100 static void am_smbus_stop_bit(void)
101 {
102     mSDA_OUT();
103     mSDA_HIGH();     /* Generate bus free time between Stop */
104     keep_delay();
105     mSCL_LOW();
106     keep_delay();
107 
108     mSDA_LOW();      /* Hold time after Stop */
109     keep_delay();
110 
111     mSCL_HIGH();     /* For sleep mode(SCL needs to be high during Sleep.) */
112     keep_delay();
113 }
114 
am_smbus_tx_byte(rt_uint8_t tx_byte)115 static rt_uint8_t am_smbus_tx_byte(rt_uint8_t tx_byte)
116 {
117     int i;
118     rt_uint8_t ack_bit;
119     rt_uint8_t bit_out;
120 
121     for(i = 0; i < 8; i++)
122     {
123         if(tx_byte&0x80)
124             bit_out = 1;             /* If the current bit of Tx_buffer is 1 set bit_out */
125         else
126             bit_out = 0;             /* else clear bit_out */
127 
128         am_smbus_send_bit(bit_out);  /* Send the current bit on SDA */
129         tx_byte <<= 1;               /* Get next bit for checking */
130     }
131 
132     ack_bit = am_smbus_read_bit();   /* Get acknowledgment bit */
133 
134     return ack_bit;
135 }
136 
am_smbus_rx_byte(rt_uint8_t ack_nack)137 static rt_uint8_t am_smbus_rx_byte(rt_uint8_t ack_nack)
138 {
139     int i;
140     rt_uint8_t rx_byte;
141 
142     for(i = 0; i < 8; i++)
143     {
144         if(am_smbus_read_bit())   /* Get a bit from the SDA line */
145         {
146             rx_byte <<= 1;        /* If the bit is HIGH save 1  in RX_buffer */
147             rx_byte |=0x01;
148         }
149         else
150         {
151             rx_byte <<= 1;        /* If the bit is LOW save 0 in RX_buffer */
152             rx_byte &=0xfe;
153         }
154     }
155     am_smbus_send_bit(ack_nack);  /* Sends acknowledgment bit */
156 
157     return rx_byte;
158 }
159 
am_smbus_tx_then_tx(rt_uint8_t SlaveAddress,rt_uint8_t command,rt_uint8_t * pBuffer,rt_uint16_t bytesNumber)160 rt_uint8_t am_smbus_tx_then_tx(rt_uint8_t SlaveAddress, rt_uint8_t command, rt_uint8_t* pBuffer, rt_uint16_t bytesNumber)
161 {
162     int i;
163 
164     am_smbus_start_bit();                      /* Start condition */
165 
166     if(am_smbus_tx_byte(SlaveAddress))         /* Send SlaveAddress and write */
167         return 1;
168 
169     if(am_smbus_tx_byte(command))              /* Send command */
170         return 1;
171 
172     for(i = 0; i < bytesNumber; i++)
173     {
174         am_smbus_tx_byte(pBuffer[i]);          /* Write data, slave must send ACK */
175     }
176 
177     am_smbus_stop_bit();                       /* Stop condition */
178 
179     return 0;
180 }
181 
am_smbus_tx_then_rx(rt_uint8_t SlaveAddress,rt_uint8_t command,rt_uint8_t * pBuffer,rt_uint16_t bytesNumber)182 rt_uint8_t am_smbus_tx_then_rx(rt_uint8_t SlaveAddress, rt_uint8_t command, rt_uint8_t* pBuffer, rt_uint16_t bytesNumber)
183 {
184     int i;
185 
186     am_smbus_start_bit();                      /* Start condition */
187 
188     if(am_smbus_tx_byte(SlaveAddress))         /* Send SlaveAddress and write */
189         return 1;
190 
191     if(am_smbus_tx_byte(command))              /* Send command */
192         return 1;
193 
194     am_smbus_start_bit();                      /* Repeated Start condition */
195 
196     if(am_smbus_tx_byte(SlaveAddress | 0x01))  /* Send SlaveAddress and read */
197         return 1;
198 
199     for(i = 0; i < bytesNumber; i++)
200     {
201         pBuffer[i] = am_smbus_rx_byte(ACK);    /* Read data, master must send ACK */
202     }
203 
204     am_smbus_stop_bit();                       /* Stop condition */
205 
206     return 0;
207 }
208 
am_smbus_scl_high(void)209 void am_smbus_scl_high(void)
210 {
211     mSCL_HIGH();     /* For sleep mode(SCL needs to be high during Sleep.) */
212     keep_delay();
213 }
214 
am_smbus_scl_low(void)215 void am_smbus_scl_low(void)
216 {
217     mSCL_LOW();     /* For sleep mode(SCL needs to be high during Sleep.) */
218     keep_delay();
219 }
220 
rt_hw_smbus_init(void)221 int rt_hw_smbus_init(void)
222 {
223     mSDA_OUT();
224     mSCL_OUT();
225     mSDA_HIGH();   /* bus free */
226     mSCL_HIGH();
227 
228     return 0;
229 }
230 #ifdef RT_USING_COMPONENTS_INIT
231 INIT_BOARD_EXPORT(rt_hw_smbus_init);
232 #endif
233 
234 #endif
235 
236 /*@}*/
237