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