1 /**
2 * \file
3 *
4 * \brief SAM Two-Wire Interface
5 *
6 * Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries.
7 *
8 * \asf_license_start
9 *
10 * \page License
11 *
12 * Subject to your compliance with these terms, you may use Microchip
13 * software and any derivatives exclusively with Microchip products.
14 * It is your responsibility to comply with third party license terms applicable
15 * to your use of third party software (including open source software) that
16 * may accompany Microchip software.
17 *
18 * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
19 * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
20 * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
21 * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
22 * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
23 * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
24 * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
25 * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT
26 * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
27 * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
28 * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
29 *
30 * \asf_license_stop
31 *
32 */
33
34 #include <hpl_i2c_m_sync.h>
35 #include <hpl_twihs_config.h>
36 #include <utils.h>
37 #include <utils_assert.h>
38
39 /**
40 * \internal Set baudrate for TWIHS
41 *
42 * \param[in] hw TWIHS Base Address
43 * \param[in] clk TWIHS peripheral clock rate in HZ
44 * \param[in] baudrate TWIHS I2C baudrate in HZ
45 */
46 static int32_t _twihs_set_baudrate(void *const hw, uint32_t clk, uint32_t baudrate);
47
48 /**
49 * \internal Retrieve I2C Master Sync configuration instance
50 *
51 * \param[in] hw The pointer of TWIHS hardware instance
52 *
53 * \return The I2C Master Sync configuration instance
54 */
55 static const struct _i2cm_sync_cfg *_get_i2cm_sync_cfg(void *hw);
56
57 /**
58 * \internal I2C Master Sync read operation
59 *
60 * \param[in] dev The pointer of I2C Master instance
61 * \param[in] msg The pointer of I2C message struct
62 *
63 * \return The status of the operation
64 * \retval ERR_NONE Operation sucessfully
65 */
66 static inline int32_t _i2c_m_sync_read(struct _i2c_m_sync_device *const dev, struct _i2c_m_msg *msg);
67
68 /**
69 * \internal I2C Master Sync write operation
70 *
71 * \param[in] dev The pointer of I2C Master instance
72 * \param[in] msg The pointer of I2C message struct
73 *
74 * \return The status of the operation
75 * \retval ERR_NONE Operation sucessfully
76 */
77 static inline int32_t _i2c_m_sync_write(struct _i2c_m_sync_device *const dev, struct _i2c_m_msg *msg);
78
79 /**
80 * \brief TWIHS I2C Master Sync configuration Type
81 */
82 struct _i2cm_sync_cfg {
83 void * hw; /*!< instance of TWIHS */
84 hri_twihs_cr_reg_t ctrl;
85 hri_twihs_smbtr_reg_t smbtr;
86 hri_twihs_filtr_reg_t filtr;
87 hri_twihs_cwgr_reg_t cwgr;
88 uint32_t clkrate;
89 };
90
91 /**
92 * \brief Array of I2C Master Sync configurations
93 */
94 static const struct _i2cm_sync_cfg _i2cm_sync_cfgs[1] = {
95 {(void *)TWIHS0,
96 CONF_TWIHS0_CR_REG,
97 CONF_TWIHS0_SMBTR_REG,
98 CONF_TWIHS0_FILTR_REG,
99 CONF_TWIHS0_CWGR_REG,
100 CONF_TWIHS0_FREQUENCY / 1000},
101 };
102
103 /**
104 * \berif Retrieve I2C Master Sync configuration instance
105 */
_get_i2cm_sync_cfg(void * hw)106 static const struct _i2cm_sync_cfg *_get_i2cm_sync_cfg(void *hw)
107 {
108 uint8_t i;
109
110 for (i = 0; i < ARRAY_SIZE(_i2cm_sync_cfgs); i++) {
111 if (_i2cm_sync_cfgs[i].hw == hw) {
112 return &(_i2cm_sync_cfgs[i]);
113 }
114 }
115 return NULL;
116 }
117
_i2c_m_sync_init(struct _i2c_m_sync_device * const dev,void * const hw)118 int32_t _i2c_m_sync_init(struct _i2c_m_sync_device *const dev, void *const hw)
119 {
120 ASSERT(dev && hw);
121
122 const struct _i2cm_sync_cfg *cfg;
123
124 dev->hw = hw;
125 cfg = _get_i2cm_sync_cfg(dev->hw);
126
127 // hri_twihs_write_CR_reg(hw, TWIHS_CR_SWRST);
128 // hri_twihs_read_RHR_reg(hw);
129 hri_twihs_write_CR_reg(dev->hw, cfg->ctrl);
130 hri_twihs_write_SMBTR_reg(dev->hw, cfg->smbtr);
131 hri_twihs_write_FILTR_reg(dev->hw, cfg->filtr);
132 hri_twihs_write_CWGR_reg(dev->hw, cfg->cwgr);
133
134 return ERR_NONE;
135 }
136
_i2c_m_sync_deinit(struct _i2c_m_sync_device * const dev)137 int32_t _i2c_m_sync_deinit(struct _i2c_m_sync_device *const dev)
138 {
139 ASSERT(dev);
140
141 hri_twihs_write_CR_reg(dev->hw, TWIHS_CR_SWRST);
142 dev->hw = NULL;
143
144 return ERR_NONE;
145 }
146
_i2c_m_sync_enable(struct _i2c_m_sync_device * const dev)147 int32_t _i2c_m_sync_enable(struct _i2c_m_sync_device *const dev)
148 {
149 ASSERT(dev);
150
151 hri_twihs_write_CR_reg(dev->hw, TWIHS_CR_MSEN);
152
153 return ERR_NONE;
154 }
155
_i2c_m_sync_disable(struct _i2c_m_sync_device * const dev)156 int32_t _i2c_m_sync_disable(struct _i2c_m_sync_device *const dev)
157 {
158 ASSERT(dev);
159
160 hri_twihs_write_CR_reg(dev->hw, TWIHS_CR_MSDIS);
161
162 return ERR_NONE;
163 }
164
_i2c_m_sync_set_baudrate(struct _i2c_m_sync_device * const dev,uint32_t clkrate,uint32_t baudrate)165 int32_t _i2c_m_sync_set_baudrate(struct _i2c_m_sync_device *const dev, uint32_t clkrate, uint32_t baudrate)
166 {
167 ASSERT(dev && baudrate);
168 (void)clkrate;
169
170 const struct _i2cm_sync_cfg *cfg = _get_i2cm_sync_cfg(dev->hw);
171
172 return _twihs_set_baudrate(dev->hw, cfg->clkrate, baudrate);
173 }
174
_i2c_m_sync_send_stop(struct _i2c_m_sync_device * const dev)175 int32_t _i2c_m_sync_send_stop(struct _i2c_m_sync_device *const dev)
176 {
177 ASSERT(dev && dev->hw);
178
179 hri_twihs_write_CR_reg(dev->hw, TWIHS_CR_STOP);
180
181 return ERR_NONE;
182 }
183
_i2c_m_sync_transfer(struct _i2c_m_sync_device * const dev,struct _i2c_m_msg * msg)184 int32_t _i2c_m_sync_transfer(struct _i2c_m_sync_device *const dev, struct _i2c_m_msg *msg)
185 {
186 ASSERT(dev && msg);
187
188 if (dev->service.msg.flags & I2C_M_BUSY) {
189 return I2C_ERR_BUSY;
190 }
191
192 if (msg->flags & I2C_M_RD) {
193 return _i2c_m_sync_read(dev, msg);
194 } else {
195 return _i2c_m_sync_write(dev, msg);
196 }
197 }
198
_i2c_m_sync_write(struct _i2c_m_sync_device * const dev,struct _i2c_m_msg * msg)199 static inline int32_t _i2c_m_sync_write(struct _i2c_m_sync_device *const dev, struct _i2c_m_msg *msg)
200 {
201 uint32_t i;
202 uint32_t sr;
203 int ret = ERR_NONE;
204
205 msg->flags |= I2C_M_BUSY;
206
207 if (msg->addr & I2C_M_TEN) {
208 hri_twihs_write_MMR_reg(dev->hw, TWIHS_MMR_DADR(0x78 | (msg->addr >> 8)) | TWIHS_MMR_IADRSZ(1));
209 hri_twihs_write_IADR_reg(dev->hw, msg->addr & 0xff);
210 } else {
211 hri_twihs_write_MMR_reg(dev->hw, TWIHS_MMR_DADR(msg->addr));
212 }
213
214 for (i = 0; i < msg->len; i++) {
215 /* Wait for data is transferred from TWIHS_THR or if NACK is detected */
216 do {
217 sr = hri_twihs_read_SR_reg(dev->hw);
218 if (sr & TWIHS_SR_NACK) {
219 ret = I2C_NACK;
220 break;
221 }
222 } while (!(sr & TWIHS_SR_TXRDY));
223
224 if (ret != ERR_NONE)
225 break;
226 hri_twihs_write_THR_reg(dev->hw, msg->buffer[i]);
227 }
228
229 if (msg->flags & I2C_M_STOP) {
230 hri_twihs_write_CR_reg(dev->hw, TWIHS_CR_STOP);
231 while (!hri_twihs_get_SR_TXCOMP_bit(dev->hw)) {
232 };
233 }
234
235 dev->service.msg.flags &= ~I2C_M_BUSY;
236
237 return ret;
238 }
239
_i2c_m_sync_read(struct _i2c_m_sync_device * const dev,struct _i2c_m_msg * msg)240 static inline int32_t _i2c_m_sync_read(struct _i2c_m_sync_device *const dev, struct _i2c_m_msg *msg)
241 {
242 uint32_t i;
243
244 msg->flags |= I2C_M_BUSY;
245
246 if (msg->addr & I2C_M_TEN) {
247 hri_twihs_write_MMR_reg(dev->hw,
248 TWIHS_MMR_DADR(0x78 | (msg->addr >> 8)) | TWIHS_MMR_IADRSZ(1) | TWIHS_MMR_MREAD);
249 hri_twihs_write_IADR_reg(dev->hw, msg->addr & 0xff);
250 } else {
251 hri_twihs_write_MMR_reg(dev->hw, TWIHS_MMR_DADR(msg->addr) | TWIHS_MMR_MREAD);
252 }
253 /* In single data byte master read, the START and STOP must both be set */
254 hri_twihs_write_CR_reg(dev->hw, TWIHS_CR_START | ((msg->len == 1) ? TWIHS_CR_STOP : 0));
255
256 for (i = 0; i < msg->len; i++) {
257 /* Wait for a byte has been received in TWIHS_RHR since last read */
258 while (!hri_twihs_get_SR_RXRDY_bit(dev->hw)) {
259 /* Check whether slave acknowledge received after the address byte */
260 if (hri_twihs_get_SR_NACK_bit(dev->hw))
261 return I2C_NACK;
262 };
263
264 msg->buffer[i] = hri_twihs_read_RHR_reg(dev->hw);
265 /* In multiple data bytes master read, the STOP must be set after the
266 * last data received but one */
267 if (i == (msg->len - 2)) {
268 hri_twihs_write_CR_reg(dev->hw, TWIHS_CR_STOP);
269 }
270 }
271
272 while (!hri_twihs_get_SR_TXCOMP_bit(dev->hw)) {
273 };
274 dev->service.msg.flags &= ~I2C_M_BUSY;
275
276 return ERR_NONE;
277 }
278
_twihs_set_baudrate(void * const hw,uint32_t clk,uint32_t baudrate)279 static int32_t _twihs_set_baudrate(void *const hw, uint32_t clk, uint32_t baudrate)
280 {
281 uint8_t ckdiv = 0; /* CWGR_CKDIV */
282 uint32_t cldiv; /* CWGR_CLDIV */
283
284 cldiv = clk / (baudrate * 2);
285
286 /* cldiv(CWGR_CLDIV) must fit in 8 bits and
287 * ckdiv(CWGR_CKDIV) must fit in 3 bits
288 *
289 * cldiv may overflow 255 by ckdiv = 0 in previous step,
290 * So here will check cldiv, if cldiv > 255 then will loop ckdiv from 1 to
291 * 7 for find a valid cldiv value
292 */
293 while ((cldiv > 255) && (ckdiv < 7)) {
294 /* Increase clock divider */
295 ckdiv++;
296 /* Divide cldiv value */
297 cldiv = cldiv >> 1;
298 }
299
300 if (cldiv > 255) {
301 return ERR_INVALID_DATA;
302 }
303 /* set CWGR(Clock Waveform Generator Register) */
304 hri_twihs_write_CWGR_reg(hw, TWIHS_CWGR_CKDIV(ckdiv) | TWIHS_CWGR_CLDIV(cldiv) | TWIHS_CWGR_CHDIV(cldiv));
305
306 return ERR_NONE;
307 }
308