Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:22:45

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /*
0004  * Copyright (c) 2016 embedded brains GmbH & Co. KG
0005  *
0006  * Redistribution and use in source and binary forms, with or without
0007  * modification, are permitted provided that the following conditions
0008  * are met:
0009  * 1. Redistributions of source code must retain the above copyright
0010  *    notice, this list of conditions and the following disclaimer.
0011  * 2. Redistributions in binary form must reproduce the above copyright
0012  *    notice, this list of conditions and the following disclaimer in the
0013  *    documentation and/or other materials provided with the distribution.
0014  *
0015  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0016  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0017  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0018  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0019  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0020  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0021  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0022  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0023  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0024  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0025  * POSSIBILITY OF SUCH DAMAGE.
0026  */
0027 
0028 #include <bsp/atsam-clock-config.h>
0029 #include <bsp/atsam-i2c.h>
0030 
0031 #include <rtems/irq-extension.h>
0032 
0033 #define ATSAMV_I2C_IRQ_ERROR \
0034     (TWIHS_IDR_ARBLST \
0035     | TWIHS_IDR_TOUT \
0036     | TWIHS_IDR_OVRE \
0037     | TWIHS_IDR_UNRE \
0038     | TWIHS_IDR_NACK)
0039 
0040 #define TEN_BIT_MASK 0xFC00
0041 #define SEVEN_BIT_MASK 0xFF80
0042 #define TEN_BIT_START_ADDR_MASK 0x78
0043 #define LAST_TWO_BITS_MASK 0x03
0044 #define LAST_BYTE_MASK 0x00FF
0045 
0046 static void
0047 atsam_i2c_disable_interrupts(Twihs *regs)
0048 {
0049     TWI_DisableIt(regs, 0xFFFFFFFF);
0050 }
0051 
0052 /*
0053  * Return true if the message is done right after this. That is the case if all
0054  * bytes are received but no stop is requested.
0055  */
0056 static bool
0057 atsam_i2c_continue_read(Twihs *regs, atsam_i2c_bus *bus)
0058 {
0059     bool done = false;
0060 
0061     *bus->current_msg_byte = TWI_ReadByte(regs);
0062     ++bus->current_msg_byte;
0063     --bus->current_msg_todo;
0064 
0065     /* check for transfer finish */
0066     if (bus->current_msg_todo == 0) {
0067         if (bus->stop_request){
0068             TWI_DisableIt(regs, TWIHS_IDR_RXRDY);
0069             TWI_EnableIt(regs, TWIHS_IER_TXCOMP);
0070         } else {
0071             done = true;
0072         }
0073     }
0074     /* Last byte? */
0075     else if (bus->current_msg_todo == 1 && bus->stop_request) {
0076         TWI_Stop(regs);
0077     }
0078 
0079     return done;
0080 }
0081 
0082 /*
0083  * Return true if the message is done right after this. That is the case if all
0084  * bytes are sent but no stop is requested.
0085  */
0086 static bool
0087 atsam_i2c_continue_write(Twihs *regs, atsam_i2c_bus *bus)
0088 {
0089     bool done = false;
0090 
0091     /* Transfer finished ? */
0092     if (bus->current_msg_todo == 0) {
0093         TWI_DisableIt(regs, TWIHS_IDR_TXRDY);
0094         if (bus->stop_request){
0095             TWI_EnableIt(regs, TWIHS_IER_TXCOMP);
0096             TWI_SendSTOPCondition(regs);
0097         } else {
0098             done = true;
0099         }
0100     }
0101     /* Bytes remaining */
0102     else {
0103         TWI_WriteByte(regs, *bus->current_msg_byte);
0104         ++bus->current_msg_byte;
0105         --bus->current_msg_todo;
0106     }
0107     return done;
0108 }
0109 
0110 static void
0111 atsam_i2c_next_packet(atsam_i2c_bus *bus)
0112 {
0113     i2c_msg *msg;
0114 
0115     ++bus->msgs;
0116     --bus->msg_todo;
0117 
0118     msg = &bus->msgs[0];
0119 
0120     bus->current_msg_todo = msg->len;
0121     bus->current_msg_byte = msg->buf;
0122 }
0123 
0124 static bool
0125 atsam_i2c_set_address_size(const i2c_msg *msg)
0126 {
0127     bool rv = ((msg->flags & I2C_M_TEN) == 0) ? false : true;
0128     return rv;
0129 }
0130 
0131 static void
0132 atsam_i2c_set_address_regs(Twihs *regs, uint16_t address, bool ten_bit_addr,
0133     bool read_transfer)
0134 {
0135     /* No internal addresses used */
0136     uint32_t mmr_temp = 0;
0137     uint32_t iadr_temp = 0;
0138 
0139     assert(regs != NULL);
0140     if (ten_bit_addr){
0141         uint8_t addr_temp = TEN_BIT_START_ADDR_MASK;
0142         assert(address & TEN_BIT_MASK);
0143         mmr_temp = (1u << 8) | ((addr_temp & LAST_TWO_BITS_MASK) << 16);
0144         iadr_temp = (addr_temp & LAST_BYTE_MASK);
0145     } else {
0146         assert((address & SEVEN_BIT_MASK) == 0);
0147         mmr_temp = (address << 16);
0148     }
0149 
0150     if (read_transfer){
0151         mmr_temp |= TWIHS_MMR_MREAD;
0152     }
0153 
0154     /* Set slave and number of internal address bytes */
0155     regs->TWIHS_MMR = 0;
0156     regs->TWIHS_MMR = mmr_temp;
0157 
0158     /* Set internal address bytes */
0159     regs->TWIHS_IADR = 0;
0160     regs->TWIHS_IADR = iadr_temp;
0161 }
0162 
0163 static void
0164 atsam_i2c_setup_read_transfer(Twihs *regs, bool ctrl, uint16_t slave_addr,
0165     bool send_stop)
0166 {
0167     TWI_EnableIt(regs, TWIHS_IER_RXRDY);
0168     atsam_i2c_set_address_regs(regs, slave_addr, ctrl, true);
0169     if (send_stop){
0170         regs->TWIHS_CR = TWIHS_CR_START | TWIHS_CR_STOP;
0171     } else {
0172         regs->TWIHS_CR = TWIHS_CR_START;
0173     }
0174 }
0175 
0176 static void
0177 atsam_i2c_setup_write_transfer(atsam_i2c_bus *bus, Twihs *regs, bool ctrl,
0178     uint16_t slave_addr)
0179 {
0180     atsam_i2c_set_address_regs(regs, slave_addr, ctrl, false);
0181     TWI_WriteByte(regs, *bus->current_msg_byte);
0182     ++bus->current_msg_byte;
0183     --bus->current_msg_todo;
0184     TWI_EnableIt(regs, TWIHS_IER_TXRDY);
0185 }
0186 
0187 static void
0188 atsam_i2c_setup_transfer(atsam_i2c_bus *bus, Twihs *regs)
0189 {
0190     const i2c_msg *msgs = bus->msgs;
0191     bool send_stop = false;
0192     uint32_t msg_todo = bus->msg_todo;
0193     uint16_t slave_addr;
0194     bool ten_bit_addr = false;
0195     bool stop_needed = true;
0196     bool read;
0197 
0198     ten_bit_addr = atsam_i2c_set_address_size(msgs);
0199 
0200     if ((msg_todo > 1) && ((msgs[1].flags & I2C_M_NOSTART) != 0)){
0201         stop_needed = false;
0202     }
0203 
0204     read = (msgs->flags & I2C_M_RD) != 0;
0205     slave_addr = msgs->addr;
0206     bus->stop_request = stop_needed;
0207 
0208     if (read){
0209         if (bus->current_msg_todo == 1){
0210             send_stop = true;
0211         }
0212         atsam_i2c_setup_read_transfer(regs, ten_bit_addr,
0213             slave_addr, send_stop);
0214     } else {
0215         atsam_i2c_setup_write_transfer(bus, regs, ten_bit_addr,
0216             slave_addr);
0217     }
0218 }
0219 
0220 static void
0221 atsam_i2c_interrupt(void *arg)
0222 {
0223     atsam_i2c_bus *bus = arg;
0224     uint32_t irqstatus;
0225     bool done = false;
0226 
0227     Twihs *regs = bus->regs;
0228 
0229     /* read interrupts */
0230     irqstatus = TWI_GetMaskedStatus(regs);
0231 
0232     if((irqstatus & ATSAMV_I2C_IRQ_ERROR) != 0) {
0233         done = true;
0234     } else if ((irqstatus & TWIHS_SR_RXRDY) != 0) {
0235         done = atsam_i2c_continue_read(regs, bus);
0236     } else if ((irqstatus & TWIHS_SR_TXCOMP) != 0) {
0237         TWI_DisableIt(regs, TWIHS_IDR_TXCOMP);
0238         done = true;
0239     } else if ((irqstatus & TWIHS_SR_TXRDY) != 0) {
0240         done = atsam_i2c_continue_write(regs, bus);
0241     }
0242 
0243     if(done){
0244         bus->err = irqstatus & ATSAMV_I2C_IRQ_ERROR;
0245 
0246         atsam_i2c_next_packet(bus);
0247         if (bus->msg_todo == 0 || bus->err != 0) {
0248             atsam_i2c_disable_interrupts(regs);
0249             rtems_binary_semaphore_post(&bus->sem);
0250         } else {
0251             atsam_i2c_setup_transfer(bus, regs);
0252         }
0253     }
0254 }
0255 
0256 static int
0257 atsam_i2c_transfer(i2c_bus *base, i2c_msg *msgs, uint32_t msg_count)
0258 {
0259     atsam_i2c_bus *bus = (atsam_i2c_bus *)base;
0260     Twihs *regs;
0261     uint32_t i;
0262     int eno;
0263 
0264     if (msg_count < 1){
0265         return 0;
0266     }
0267 
0268     for (i = 0; i < msg_count; ++i) {
0269         if ((msgs[i].flags & I2C_M_RECV_LEN) != 0) {
0270             return -EINVAL;
0271         }
0272         if (msgs[i].len == 0) {
0273             /* Hardware doesn't support zero length messages */
0274             return -EINVAL;
0275         }
0276     }
0277 
0278     bus->msgs = &msgs[0];
0279     bus->msg_todo = msg_count;
0280     bus->current_msg_todo = msgs[0].len;
0281     bus->current_msg_byte = msgs[0].buf;
0282     bus->err = 0;
0283 
0284     regs = bus->regs;
0285 
0286     /* Start with a clean start. Enable error interrupts. */
0287     TWI_ConfigureMaster(bus->regs, bus->output_clock, BOARD_MCK);
0288     TWI_EnableIt(regs, ATSAMV_I2C_IRQ_ERROR);
0289 
0290     atsam_i2c_setup_transfer(bus, regs);
0291 
0292     eno = rtems_binary_semaphore_wait_timed_ticks(
0293         &bus->sem,
0294         bus->base.timeout
0295     );
0296     if (eno != 0 || bus->err != 0) {
0297         TWI_ConfigureMaster(bus->regs, bus->output_clock, BOARD_MCK);
0298         rtems_binary_semaphore_try_wait(&bus->sem);
0299         if (bus->err != 0) {
0300             return -EIO;
0301         } else {
0302             return -ETIMEDOUT;
0303         }
0304     }
0305 
0306     return 0;
0307 }
0308 
0309 static int
0310 atsam_i2c_set_clock(i2c_bus *base, unsigned long clock)
0311 {
0312     atsam_i2c_bus *bus = (atsam_i2c_bus *)base;
0313     bus->output_clock = clock;
0314     TWI_ConfigureMaster(bus->regs, clock, BOARD_MCK);
0315     return 0;
0316 }
0317 
0318 static void
0319 atsam_i2c_destroy(i2c_bus *base)
0320 {
0321     atsam_i2c_bus *bus = (atsam_i2c_bus *)base;
0322     rtems_status_code sc;
0323 
0324     sc = rtems_interrupt_handler_remove(bus->irq, atsam_i2c_interrupt, bus);
0325     assert(sc == RTEMS_SUCCESSFUL);
0326 
0327     i2c_bus_destroy_and_free(&bus->base);
0328 }
0329 
0330 static void
0331 atsam_i2c_init(atsam_i2c_bus *bus, uint32_t board_id, const Pin *pins)
0332 {
0333 
0334     /* Initialize the TWI */
0335     PIO_Configure(pins, TWI_AMOUNT_PINS);
0336 
0337     /* Enable the TWI clock */
0338     ENABLE_PERIPHERAL(board_id);
0339 
0340     TWI_ConfigureMaster(bus->regs, bus->output_clock, BOARD_MCK);
0341 }
0342 
0343 int
0344 i2c_bus_register_atsam(
0345     const char *bus_path,
0346     Twihs *register_base,
0347     rtems_vector_number irq,
0348     const Pin pins[TWI_AMOUNT_PINS]
0349 )
0350 {
0351     atsam_i2c_bus *bus;
0352     rtems_status_code sc;
0353 
0354     bus = (atsam_i2c_bus *) i2c_bus_alloc_and_init(sizeof(*bus));
0355     if (bus == NULL){
0356         return -1;
0357     }
0358 
0359     rtems_binary_semaphore_init(&bus->sem, "ATSAM I2C");
0360     bus->regs = register_base;
0361     bus->irq = irq;
0362     bus->output_clock = I2C_BUS_CLOCK_DEFAULT;
0363 
0364     atsam_i2c_init(bus, irq, pins);
0365 
0366     sc = rtems_interrupt_handler_install(
0367         irq,
0368         "ATSAM I2C",
0369         RTEMS_INTERRUPT_UNIQUE,
0370         atsam_i2c_interrupt,
0371         bus
0372     );
0373     if(sc != RTEMS_SUCCESSFUL){
0374         (*bus->base.destroy)(&bus->base);
0375 
0376         rtems_set_errno_and_return_minus_one(EIO);
0377     }
0378 
0379     bus->base.transfer = atsam_i2c_transfer;
0380     bus->base.set_clock = atsam_i2c_set_clock;
0381     bus->base.destroy = atsam_i2c_destroy;
0382 
0383     return i2c_bus_register(&bus->base, bus_path);
0384 }