source: rtems/bsps/arm/atsam/i2c/atsam_i2c_bus.c @ a2dad96

Last change on this file since a2dad96 was a2dad96, checked in by Sebastian Huber <sebastian.huber@…>, on Apr 23, 2018 at 7:45:28 AM

bsps: Move I2C drivers to bsps

This patch is a part of the BSP source reorganization.

Update #3285.

  • Property mode set to 100644
File size: 9.2 KB
Line 
1/*
2 * Copyright (c) 2016 embedded brains GmbH.  All rights reserved.
3 *
4 *  embedded brains GmbH
5 *  Dornierstr. 4
6 *  82178 Puchheim
7 *  Germany
8 *  <info@embedded-brains.de>
9 *
10 * The license and distribution terms for this file may be
11 * found in the file LICENSE in this distribution or at
12 * http://www.rtems.org/license/LICENSE.
13 */
14
15#include <bsp/atsam-clock-config.h>
16#include <bsp/atsam-i2c.h>
17
18#include <rtems/irq-extension.h>
19
20#define ATSAMV_I2C_IRQ_ERROR \
21        (TWIHS_IDR_ARBLST \
22        | TWIHS_IDR_TOUT \
23        | TWIHS_IDR_OVRE \
24        | TWIHS_IDR_UNRE \
25        | TWIHS_IDR_NACK)
26
27#define TEN_BIT_MASK 0xFC00
28#define SEVEN_BIT_MASK 0xFF80
29#define TEN_BIT_START_ADDR_MASK 0x78
30#define LAST_TWO_BITS_MASK 0x03
31#define LAST_BYTE_MASK 0x00FF
32
33static void
34atsam_i2c_disable_interrupts(Twihs *regs)
35{
36        regs->TWIHS_IDR = 0xFFFFFFFF;
37}
38
39static void
40atsam_i2c_set_transfer_status(transfer_desc *trans_desc, transfer_state state)
41{
42        trans_desc->trans_state = state;
43}
44
45static void
46atsam_i2c_continue_read(Twihs *regs, transfer_desc *trans_desc)
47{
48        trans_desc->data[trans_desc->already_transferred] = TWI_ReadByte(regs);
49        trans_desc->already_transferred++;
50
51        /* check for transfer finish */
52        if (trans_desc->already_transferred == trans_desc->data_size) {
53                if (trans_desc->stop_request){
54                        TWI_DisableIt(regs, TWIHS_IDR_RXRDY);
55                        TWI_EnableIt(regs, TWIHS_IER_TXCOMP);
56                        atsam_i2c_set_transfer_status(trans_desc, TX_RX_STOP_SENT);
57                } else {
58                        atsam_i2c_set_transfer_status(trans_desc, RX_CONT_MESSAGE_NEEDED);
59                }
60        }
61        /* Last byte? */
62        else if ((trans_desc->already_transferred == (trans_desc->data_size - 1))
63            && (trans_desc->stop_request)){
64                TWI_Stop(regs);
65        }
66}
67
68static bool
69atsam_i2c_is_state(transfer_desc *trans_desc, transfer_state state)
70{
71        return (trans_desc->trans_state == state);
72}
73
74static void
75atsam_i2c_continue_write(Twihs *regs, transfer_desc *trans_desc)
76{
77        /* Transfer finished ? */
78        if (trans_desc->already_transferred == trans_desc->data_size) {
79                TWI_DisableIt(regs, TWIHS_IDR_TXRDY);
80                if (trans_desc->stop_request){
81                        TWI_EnableIt(regs, TWIHS_IER_TXCOMP);
82                        TWI_SendSTOPCondition(regs);
83                        atsam_i2c_set_transfer_status(trans_desc, TX_RX_STOP_SENT);
84                } else {
85                        atsam_i2c_set_transfer_status(trans_desc, TX_CONT_MESSAGE_NEEDED);
86                }
87        }
88        /* Bytes remaining */
89        else {
90                TWI_WriteByte(regs,
91                    trans_desc->data[trans_desc->already_transferred]);
92                trans_desc->already_transferred++;
93        }
94}
95
96static void
97atsam_i2c_finish_write_transfer(Twihs *regs, transfer_desc *trans_desc)
98{
99                TWI_ReadByte(regs);
100                TWI_DisableIt(regs, TWIHS_IDR_TXCOMP);
101                trans_desc->status = 0;
102}
103
104static void
105atsam_i2c_next_packet(atsam_i2c_bus *bus)
106{
107        i2c_msg *msg;
108
109        ++bus->msgs;
110        --bus->msg_todo;
111
112        msg = &bus->msgs[0];
113
114        bus->current_msg_todo = msg->len;
115        bus->current_msg_byte = msg->buf;
116}
117
118static void
119atsam_i2c_set_td(atsam_i2c_bus *bus, uint32_t already_transferred,
120    bool stop_needed)
121{
122        transfer_desc *trans_desc = &bus->trans_desc;
123
124        trans_desc->status = ASYNC_STATUS_PENDING;
125        trans_desc->data = bus->current_msg_byte;
126        trans_desc->data_size = bus->current_msg_todo;
127        trans_desc->already_transferred = already_transferred;
128        trans_desc->stop_request = stop_needed;
129}
130
131static bool
132atsam_i2c_set_address_size(const i2c_msg *msg)
133{
134        bool rv = ((msg->flags & I2C_M_TEN) == 0) ? false : true;
135        return rv;
136}
137
138static void
139atsam_i2c_set_address_regs(Twihs *regs, uint16_t address, bool ten_bit_addr,
140    bool read_transfer)
141{
142        /* No internal addresses used */
143        uint32_t mmr_temp = 0;
144        uint32_t iadr_temp = 0;
145
146        assert(regs != NULL);
147        if (ten_bit_addr){
148                uint8_t addr_temp = TEN_BIT_START_ADDR_MASK;
149                assert(address & TEN_BIT_MASK);
150                mmr_temp = (1u << 8) | ((addr_temp & LAST_TWO_BITS_MASK) << 16);
151                iadr_temp = (addr_temp & LAST_BYTE_MASK);
152        } else {
153                assert((address & SEVEN_BIT_MASK) == 0);
154                mmr_temp = (address << 16);
155        }
156
157        if (read_transfer){
158                mmr_temp |= TWIHS_MMR_MREAD;
159        }
160
161        /* Set slave and number of internal address bytes */
162        regs->TWIHS_MMR = 0;
163        regs->TWIHS_MMR = mmr_temp;
164
165        /* Set internal address bytes */
166        regs->TWIHS_IADR = 0;
167        regs->TWIHS_IADR = iadr_temp;
168}
169
170static void
171atsam_i2c_setup_read_transfer(Twihs *regs, bool ctrl, uint16_t slave_addr,
172    bool send_stop)
173{
174        TWI_EnableIt(regs, TWIHS_IER_RXRDY);
175        atsam_i2c_set_address_regs(regs, slave_addr, ctrl, true);
176        if (send_stop){
177                regs->TWIHS_CR = TWIHS_CR_START | TWIHS_CR_STOP;
178        } else {
179                regs->TWIHS_CR = TWIHS_CR_START;
180        }
181}
182
183static void
184atsam_i2c_setup_write_transfer(atsam_i2c_bus *bus, Twihs *regs, bool ctrl,
185    uint16_t slave_addr)
186{
187        atsam_i2c_set_address_regs(regs, slave_addr, ctrl, false);
188        TWI_WriteByte(regs, *bus->current_msg_byte);
189        TWI_EnableIt(regs, TWIHS_IER_TXRDY);
190}
191
192static void
193atsam_i2c_setup_transfer(atsam_i2c_bus *bus, Twihs *regs)
194{
195        const i2c_msg *msgs = bus->msgs;
196        bool send_stop = false;
197        uint32_t msg_todo = bus->msg_todo;
198        uint16_t slave_addr;
199        bool ten_bit_addr = false;
200        uint32_t already_transferred;
201        bool stop_needed = true;
202
203        ten_bit_addr = atsam_i2c_set_address_size(msgs);
204
205        if ((msg_todo > 1) && ((msgs[1].flags & I2C_M_NOSTART) != 0)){
206                stop_needed = false;
207        }
208
209        bus->read = (msgs->flags & I2C_M_RD) != 0;
210        slave_addr = msgs->addr;
211        already_transferred = (bus->read == true) ? 0 : 1;
212        atsam_i2c_set_td(bus, already_transferred, stop_needed);
213
214        transfer_desc *trans_desc = &bus->trans_desc;
215
216        if (bus->read){
217                if (bus->current_msg_todo == 1){
218                        send_stop = true;
219                }
220                atsam_i2c_set_transfer_status(trans_desc, RX_SEND_DATA);
221                atsam_i2c_setup_read_transfer(regs, ten_bit_addr,
222                    slave_addr, send_stop);
223        } else {
224                atsam_i2c_set_transfer_status(trans_desc, TX_SEND_DATA);
225                atsam_i2c_setup_write_transfer(bus, regs, ten_bit_addr,
226                    slave_addr);
227        }
228}
229
230static void
231atsam_i2c_interrupt(void *arg)
232{
233        atsam_i2c_bus *bus = arg;
234        uint32_t irqstatus;
235        bool done = false;
236        transfer_desc *trans_desc;
237
238        Twihs *regs = bus->regs;
239
240        /* read interrupts */
241        irqstatus = regs->TWIHS_SR;
242
243        if((irqstatus & (TWIHS_SR_ARBLST | TWIHS_SR_NACK)) != 0) {
244                done = true;
245        }
246
247        trans_desc = &bus->trans_desc;
248
249        if (((irqstatus & TWIHS_SR_RXRDY) != 0) &&
250            (atsam_i2c_is_state(trans_desc, RX_SEND_DATA))){
251                /* carry on read transfer */
252                atsam_i2c_continue_read(regs, trans_desc);
253        } else if (((irqstatus & TWIHS_SR_TXCOMP) != 0) &&
254            (atsam_i2c_is_state(trans_desc, TX_RX_STOP_SENT))){
255                atsam_i2c_finish_write_transfer(regs, trans_desc);
256                done = true;
257        } else if (((irqstatus & TWIHS_SR_TXRDY) != 0) &&
258            (atsam_i2c_is_state(trans_desc, TX_SEND_DATA))){
259                atsam_i2c_continue_write(regs, trans_desc);
260                if (trans_desc->trans_state == TX_CONT_MESSAGE_NEEDED){
261                        done = true;
262                }
263        }
264
265        if(done){
266                uint32_t err = irqstatus & ATSAMV_I2C_IRQ_ERROR;
267
268                atsam_i2c_next_packet(bus);
269                if (bus->msg_todo == 0 || err != 0) {
270                        rtems_status_code sc;
271
272                        atsam_i2c_disable_interrupts(regs);
273                        sc = rtems_event_transient_send(bus->task_id);
274                        assert(sc == RTEMS_SUCCESSFUL);
275                } else {
276                        atsam_i2c_setup_transfer(bus, regs);
277                }
278        }
279}
280
281static int
282atsam_i2c_transfer(i2c_bus *base, i2c_msg *msgs, uint32_t msg_count)
283{
284        rtems_status_code sc;
285        atsam_i2c_bus *bus = (atsam_i2c_bus *)base;
286        Twihs *regs;
287        uint32_t i;
288
289        rtems_task_wake_after(1);
290
291        if (msg_count < 1){
292                return 1;
293        }
294
295        for (i = 0; i < msg_count; ++i) {
296                if ((msgs[i].flags & I2C_M_RECV_LEN) != 0) {
297                        return -EINVAL;
298                }
299        }
300
301        bus->msgs = &msgs[0];
302        bus->msg_todo = msg_count;
303        bus->current_msg_todo = msgs[0].len;
304        bus->current_msg_byte = msgs[0].buf;
305        bus->task_id = rtems_task_self();
306
307        regs = bus->regs;
308
309        atsam_i2c_setup_transfer(bus, regs);
310
311        regs->TWIHS_IER = ATSAMV_I2C_IRQ_ERROR;
312
313        sc = rtems_event_transient_receive(RTEMS_WAIT, bus->base.timeout);
314        if (sc != RTEMS_SUCCESSFUL){
315                rtems_event_transient_clear();
316                return -ETIMEDOUT;
317        }
318
319        return 0;
320}
321
322static int
323atsam_i2c_set_clock(i2c_bus *base, unsigned long clock)
324{
325        atsam_i2c_bus *bus = (atsam_i2c_bus *)base;
326        Twihs *regs = bus->regs;
327        TWI_ConfigureMaster(regs, clock, BOARD_MCK);
328        return 0;
329}
330
331static void
332atsam_i2c_destroy(i2c_bus *base)
333{
334        atsam_i2c_bus *bus = (atsam_i2c_bus *)base;
335        rtems_status_code sc;
336
337        sc = rtems_interrupt_handler_remove(bus->irq, atsam_i2c_interrupt, bus);
338        assert(sc == RTEMS_SUCCESSFUL);
339
340        i2c_bus_destroy_and_free(&bus->base);
341}
342
343static void
344atsam_i2c_init(atsam_i2c_bus *bus, uint32_t input_clock, Twihs *board_base,
345    uint32_t board_id, const Pin *pins)
346{
347
348        /* Initialize the TWI */
349        PIO_Configure(pins, TWI_AMOUNT_PINS);
350
351        /* Enable the TWI clock */
352        ENABLE_PERIPHERAL(board_id);
353
354        TWI_ConfigureMaster(board_base, input_clock, BOARD_MCK);
355}
356
357int
358i2c_bus_register_atsam(
359    const char *bus_path,
360    Twihs *register_base,
361    rtems_vector_number irq,
362    const Pin pins[TWI_AMOUNT_PINS]
363)
364{
365        atsam_i2c_bus *bus;
366        rtems_status_code sc;
367        uint32_t board_id = (uint32_t) irq;
368
369        bus = (atsam_i2c_bus *) i2c_bus_alloc_and_init(sizeof(*bus));
370        if (bus == NULL){
371                return -1;
372        }
373
374        bus->regs = register_base;
375        bus->irq = irq;
376
377        atsam_i2c_init(bus, I2C_BUS_CLOCK_DEFAULT, bus->regs,
378            board_id, pins);
379
380        sc = rtems_interrupt_handler_install(
381            irq,
382            "Atsamv_I2C",
383            RTEMS_INTERRUPT_UNIQUE,
384            atsam_i2c_interrupt,
385            bus
386        );
387        if(sc != RTEMS_SUCCESSFUL){
388                (*bus->base.destroy)(&bus->base);
389
390                rtems_set_errno_and_return_minus_one(EIO);
391        }
392
393        bus->base.transfer = atsam_i2c_transfer;
394        bus->base.set_clock = atsam_i2c_set_clock;
395        bus->base.destroy = atsam_i2c_destroy;
396
397        return i2c_bus_register(&bus->base, bus_path);
398}
Note: See TracBrowser for help on using the repository browser.