]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/CORTEX_A5_SAMA5D2x_Xplained_IAR/AtmelFiles/drivers/peripherals/twid.c
Add SAMA5D2 Xplained IAR demo.
[freertos] / FreeRTOS / Demo / CORTEX_A5_SAMA5D2x_Xplained_IAR / AtmelFiles / drivers / peripherals / twid.c
1 /* ----------------------------------------------------------------------------\r
2  *         SAM Software Package License\r
3  * ----------------------------------------------------------------------------\r
4  * Copyright (c) 2015, Atmel Corporation\r
5  *\r
6  * All rights reserved.\r
7  *\r
8  * Redistribution and use in source and binary forms, with or without\r
9  * modification, are permitted provided that the following conditions are met:\r
10  *\r
11  * - Redistributions of source code must retain the above copyright notice,\r
12  * this list of conditions and the disclaimer below.\r
13  *\r
14  * Atmel's name may not be used to endorse or promote products derived from\r
15  * this software without specific prior written permission.\r
16  *\r
17  * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR\r
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE\r
20  * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,\r
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,\r
23  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\r
24  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
25  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\r
26  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
27  * ----------------------------------------------------------------------------\r
28  */\r
29 \r
30 \r
31 #ifdef CONFIG_HAVE_FLEXCOM\r
32 #include "peripherals/flexcom.h"\r
33 #endif\r
34 #include "peripherals/pmc.h"\r
35 #include "peripherals/twid.h"\r
36 #include "peripherals/twi.h"\r
37 #include "peripherals/xdmad.h"\r
38 #include "peripherals/l2cc.h"\r
39 \r
40 #include "cortex-a/cp15.h"\r
41 \r
42 #include "trace.h"\r
43 #include "io.h"\r
44 #include "timer.h"\r
45 \r
46 #include <assert.h>\r
47 #include <string.h>\r
48 \r
49 #define TWID_DMA_THRESHOLD      16\r
50 #define TWID_TIMEOUT            100\r
51 \r
52 static uint32_t _twid_wait_twi_transfer(struct _twi_desc* desc)\r
53 {\r
54         struct _timeout timeout;\r
55         timer_start_timeout(&timeout, TWID_TIMEOUT);\r
56         while(!twi_is_transfer_complete(desc->addr)){\r
57                 if (timer_timeout_reached(&timeout)) {\r
58                         trace_error("twid: Unable to complete transfert!\r\n");\r
59                         twid_configure(desc);\r
60                         return TWID_ERROR_TRANSFER;\r
61                 }\r
62         }\r
63         return TWID_SUCCESS;\r
64 }\r
65 \r
66 static void _twid_xdmad_callback_wrapper(struct _xdmad_channel* channel,\r
67                                            void* args)\r
68 {\r
69         trace_debug("TWID DMA Transfert Finished\r\n");\r
70         struct _twi_desc* twid = (struct _twi_desc*) args;\r
71 \r
72         xdmad_free_channel(channel);\r
73 \r
74         if (twid->region_start && twid->region_end) {\r
75                 l2cc_invalidate_region(twid->region_start, twid->region_end);\r
76         }\r
77 \r
78         if (twid && twid->callback)\r
79                 twid->callback(twid, twid->cb_args);\r
80 \r
81 }\r
82 \r
83 static void _twid_init_dma_read_channel(const struct _twi_desc* desc,\r
84                                           struct _xdmad_channel** channel,\r
85                                           struct _xdmad_cfg* cfg)\r
86 {\r
87         assert(cfg);\r
88         assert(channel);\r
89 \r
90         uint32_t id = get_twi_id_from_addr(desc->addr);\r
91         assert(id < ID_PERIPH_COUNT);\r
92 \r
93         memset(cfg, 0x0, sizeof(*cfg));\r
94 \r
95         *channel =\r
96                 xdmad_allocate_channel(id, XDMAD_PERIPH_MEMORY);\r
97         assert(*channel);\r
98 \r
99         xdmad_prepare_channel(*channel);\r
100         cfg->cfg.uint32_value = XDMAC_CC_TYPE_PER_TRAN\r
101                 | XDMAC_CC_DSYNC_PER2MEM\r
102                 | XDMAC_CC_MEMSET_NORMAL_MODE\r
103                 | XDMAC_CC_CSIZE_CHK_1\r
104                 | XDMAC_CC_DWIDTH_BYTE\r
105                 | XDMAC_CC_DIF_AHB_IF0\r
106                 | XDMAC_CC_SIF_AHB_IF1\r
107                 | XDMAC_CC_SAM_FIXED_AM;\r
108 \r
109         cfg->src_addr = (void*)&desc->addr->TWI_RHR;\r
110 }\r
111 \r
112 static void _twid_dma_read(const struct _twi_desc* desc,\r
113                            struct _buffer* buffer)\r
114 {\r
115         struct _xdmad_channel* channel = NULL;\r
116         struct _xdmad_cfg cfg;\r
117 \r
118         _twid_init_dma_read_channel(desc, &channel, &cfg);\r
119 \r
120         cfg.cfg.bitfield.dam = XDMAC_CC_DAM_INCREMENTED_AM\r
121                 >> XDMAC_CC_DAM_Pos;\r
122         cfg.dest_addr = buffer->data;\r
123         cfg.ublock_size = buffer->size;\r
124         cfg.block_size = 0;\r
125         xdmad_configure_transfer(channel, &cfg, 0, 0);\r
126         xdmad_set_callback(channel, _twid_xdmad_callback_wrapper,\r
127                            (void*)desc);\r
128 \r
129         l2cc_clean_region(desc->region_start, desc->region_end);\r
130 \r
131         xdmad_start_transfer(channel);\r
132 }\r
133 \r
134 static void _twid_init_dma_write_channel(struct _twi_desc* desc,\r
135                                          struct _xdmad_channel** channel,\r
136                                          struct _xdmad_cfg* cfg)\r
137 {\r
138         assert(cfg);\r
139         assert(channel);\r
140 \r
141         uint32_t id = get_twi_id_from_addr(desc->addr);\r
142         assert(id < ID_PERIPH_COUNT);\r
143         memset(cfg, 0x0, sizeof(*cfg));\r
144 \r
145         *channel =\r
146                 xdmad_allocate_channel(XDMAD_PERIPH_MEMORY, id);\r
147         assert(*channel);\r
148 \r
149         xdmad_prepare_channel(*channel);\r
150         cfg->cfg.uint32_value = XDMAC_CC_TYPE_PER_TRAN\r
151                 | XDMAC_CC_DSYNC_MEM2PER\r
152                 | XDMAC_CC_MEMSET_NORMAL_MODE\r
153                 | XDMAC_CC_CSIZE_CHK_1\r
154                 | XDMAC_CC_DWIDTH_BYTE\r
155                 | XDMAC_CC_DIF_AHB_IF1\r
156                 | XDMAC_CC_SIF_AHB_IF0\r
157                 | XDMAC_CC_DAM_FIXED_AM;\r
158 \r
159         cfg->dest_addr = (void*)&desc->addr->TWI_THR;\r
160 }\r
161 \r
162 static void _twid_dma_write(struct _twi_desc* desc,\r
163                             struct _buffer* buffer)\r
164 {\r
165         struct _xdmad_channel* channel = NULL;\r
166         struct _xdmad_cfg cfg;\r
167 \r
168         _twid_init_dma_write_channel(desc, &channel, &cfg);\r
169 \r
170         cfg.cfg.bitfield.sam = XDMAC_CC_SAM_INCREMENTED_AM\r
171                 >> XDMAC_CC_SAM_Pos;\r
172         cfg.src_addr = buffer->data;\r
173         cfg.ublock_size = buffer->size;\r
174         cfg.block_size = 0;\r
175         xdmad_configure_transfer(channel, &cfg, 0, 0);\r
176         xdmad_set_callback(channel, _twid_xdmad_callback_wrapper,\r
177                            (void*)desc);\r
178 \r
179         l2cc_clean_region(desc->region_start, desc->region_end);\r
180 \r
181         xdmad_start_transfer(channel);\r
182 }\r
183 \r
184 void twid_configure(struct _twi_desc* desc)\r
185 {\r
186         uint32_t id = get_twi_id_from_addr(desc->addr);\r
187         assert(id < ID_PERIPH_COUNT);\r
188 \r
189 #ifdef CONFIG_HAVE_FLEXCOM\r
190         Flexcom* flexcom = get_flexcom_addr_from_id(get_twi_id_from_addr(desc->addr));\r
191         if (flexcom) {\r
192                 flexcom_select(flexcom, FLEX_MR_OPMODE_TWI);\r
193         }\r
194 #endif\r
195 \r
196         pmc_enable_peripheral(id);\r
197         twi_configure_master(desc->addr, desc->freq);\r
198 \r
199 #ifdef CONFIG_HAVE_TWI_FIFO\r
200         if (desc->transfert_mode == TWID_MODE_FIFO) {\r
201                 uint32_t fifo_depth = get_peripheral_fifo_depth(desc->addr);\r
202                 twi_fifo_configure(desc->addr, fifo_depth/2, fifo_depth/2,\r
203                                    TWI_FMR_RXRDYM_ONE_DATA | TWI_FMR_TXRDYM_ONE_DATA);\r
204         }\r
205 #endif\r
206 }\r
207 \r
208 static uint32_t _twid_poll_write(struct _twi_desc* desc, struct _buffer* buffer)\r
209 {\r
210         int i = 0;\r
211         struct _timeout timeout;\r
212         twi_init_write_transfert(desc->addr,\r
213                                  desc->slave_addr,\r
214                                  desc->iaddr,\r
215                                  desc->isize,\r
216                                  buffer->size);\r
217         if (twi_get_status(desc->addr) & TWI_SR_NACK) {\r
218                 trace_error("twid: command NACK!\r\n");\r
219                 return TWID_ERROR_ACK;\r
220         }\r
221         for (i = 0; i < buffer->size; ++i) {\r
222                 timer_start_timeout(&timeout, TWID_TIMEOUT);\r
223                 while(!twi_byte_sent(desc->addr)) {\r
224                         if (timer_timeout_reached(&timeout)) {\r
225                                 trace_error("twid: Device doesn't answer, "\r
226                                             "(TX TIMEOUT)\r\n");\r
227                                 break;\r
228                         }\r
229                 }\r
230                 twi_write_byte(desc->addr, buffer->data[i]);\r
231                 if(twi_get_status(desc->addr) & TWI_SR_NACK) {\r
232                         trace_error("twid: command NACK!\r\n");\r
233                         return TWID_ERROR_ACK;\r
234                 }\r
235         }\r
236         /* wait transfert to be finished */\r
237         return _twid_wait_twi_transfer(desc);\r
238 }\r
239 \r
240 static uint32_t _twid_poll_read(struct _twi_desc* desc, struct _buffer* buffer)\r
241 {\r
242         int i = 0;\r
243         struct _timeout timeout;\r
244         twi_init_read_transfert(desc->addr,\r
245                                 desc->slave_addr,\r
246                                 desc->iaddr,\r
247                                 desc->isize,\r
248                                 buffer->size);\r
249         if (twi_get_status(desc->addr) & TWI_SR_NACK) {\r
250                 trace_error("twid: command NACK!\r\n");\r
251                 return TWID_ERROR_ACK;\r
252         }\r
253         for (i = 0; i < buffer->size; ++i) {\r
254                 timer_start_timeout(&timeout, TWID_TIMEOUT);\r
255                 while(!twi_is_byte_received(desc->addr)) {\r
256                         if (timer_timeout_reached(&timeout)) {\r
257                                 trace_error("twid: Device doesn't answer, "\r
258                                             "(RX TIMEOUT)\r\n");\r
259                                 break;\r
260                         }\r
261                 }\r
262                 buffer->data[i] = twi_read_byte(desc->addr);\r
263                 if(twi_get_status(desc->addr) & TWI_SR_NACK) {\r
264                         trace_error("twid: command NACK\r\n");\r
265                 return TWID_ERROR_ACK;\r
266                 }\r
267         }\r
268         /* wait transfert to be finished */\r
269         return _twid_wait_twi_transfer(desc);\r
270 }\r
271 \r
272 uint32_t twid_transfert(struct _twi_desc* desc, struct _buffer* rx,\r
273                         struct _buffer* tx, twid_callback_t cb,\r
274                         void* user_args)\r
275 {\r
276         uint32_t status = TWID_SUCCESS;\r
277 \r
278         desc->callback = cb;\r
279         desc->cb_args = user_args;\r
280 \r
281         if (mutex_try_lock(&desc->mutex)) {\r
282                 return TWID_ERROR_LOCK;\r
283         }\r
284 \r
285         switch (desc->transfert_mode) {\r
286         case TWID_MODE_POLLING:\r
287                 if (tx) {\r
288                         status = _twid_poll_write(desc, tx);\r
289                         if (status) break;\r
290                 }\r
291                 if (rx) {\r
292                         status = _twid_poll_read(desc, rx);\r
293                         if (status) break;\r
294                 }\r
295                 if (cb)\r
296                         cb(desc, user_args);\r
297                 mutex_free(&desc->mutex);\r
298                 break;\r
299 \r
300         case TWID_MODE_DMA:\r
301                 if (!(rx || tx)) {\r
302                         status = TWID_ERROR_DUPLEX;\r
303                         break;\r
304                 }\r
305                 if (tx) {\r
306                         if (tx->size < TWID_DMA_THRESHOLD) {\r
307                                 status = _twid_poll_write(desc, tx);\r
308                                 if (status) break;\r
309                                 if (cb)\r
310                                         cb(desc, user_args);\r
311                                 mutex_free(&desc->mutex);\r
312                         } else {\r
313                                 twi_init_write_transfert(desc->addr,\r
314                                                          desc->slave_addr,\r
315                                                          desc->iaddr,\r
316                                                          desc->isize,\r
317                                                          tx->size);\r
318                                 desc->region_start = (uint32_t)tx->data;\r
319                                 desc->region_end = desc->region_start\r
320                                         + tx->size;\r
321                                 _twid_dma_write(desc, tx);\r
322                         }\r
323                 }\r
324                 if (rx) {\r
325                         if (rx->size < TWID_DMA_THRESHOLD) {\r
326                                 status = _twid_poll_read(desc, rx);\r
327                                 if (status) break;\r
328                                 if (cb)\r
329                                         cb(desc, user_args);\r
330                                 mutex_free(&desc->mutex);\r
331                         } else {\r
332                                 twi_init_read_transfert(desc->addr,\r
333                                                         desc->slave_addr,\r
334                                                         desc->iaddr,\r
335                                                         desc->isize,\r
336                                                         rx->size);\r
337                                 desc->region_start = (uint32_t)rx->data;\r
338                                 desc->region_end = desc->region_start\r
339                                         + rx->size;\r
340                                 if(twi_get_status(desc->addr) & TWI_SR_NACK) {\r
341                                         trace_error("twid: Acknolegment "\r
342                                                     "Error\r\n");\r
343                                         status = TWID_ERROR_ACK;\r
344                                         break;\r
345                                 }\r
346                                 _twid_dma_read(desc, rx);\r
347                         }\r
348                 }\r
349                 break;\r
350 \r
351 #ifdef CONFIG_HAVE_TWI_FIFO\r
352         case TWID_MODE_FIFO:\r
353                 if (tx) {\r
354                         status = twi_write_stream(desc->addr, desc->slave_addr,\r
355                                                   desc->iaddr, desc->isize,\r
356                                                   tx->data, tx->size);\r
357                         status = status ? TWID_SUCCESS : TWID_ERROR_ACK;\r
358                         if (status)\r
359                                 break;\r
360                         status = _twid_wait_twi_transfer(desc);\r
361                         if (status)\r
362                                 break;\r
363                 }\r
364                 if (rx) {\r
365                         status = twi_read_stream(desc->addr, desc->slave_addr,\r
366                                                  desc->iaddr, desc->isize,\r
367                                                  rx->data, rx->size);\r
368                         status = status ? TWID_SUCCESS : TWID_ERROR_ACK;\r
369                         if (status)\r
370                                 break;\r
371                         status = _twid_wait_twi_transfer(desc);\r
372                         if (status)\r
373                                 break;\r
374                 }\r
375                 if (cb)\r
376                         cb(desc, user_args);\r
377                 mutex_free(&desc->mutex);\r
378                 break;\r
379 #endif\r
380         default:\r
381                 trace_debug("Unkown mode");\r
382         }\r
383 \r
384         if (status)\r
385                 mutex_free(&desc->mutex);\r
386 \r
387         return status;\r
388 }\r
389 \r
390 void twid_finish_transfert_callback(struct _twi_desc* desc, void* user_args)\r
391 {\r
392         (void)user_args;\r
393         twid_finish_transfert(desc);\r
394 }\r
395 \r
396 void twid_finish_transfert(struct _twi_desc* desc)\r
397 {\r
398         mutex_free(&desc->mutex);\r
399 }\r
400 \r
401 uint32_t twid_is_busy(const struct _twi_desc* desc)\r
402 {\r
403         return mutex_is_locked(&desc->mutex);\r
404 }\r
405 \r
406 void twid_wait_transfert(const struct _twi_desc* desc)\r
407 {\r
408         while (mutex_is_locked(&desc->mutex));\r
409 }\r