File indexing completed on 2025-05-11 08:22:45
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
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
0054
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
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
0075 else if (bus->current_msg_todo == 1 && bus->stop_request) {
0076 TWI_Stop(regs);
0077 }
0078
0079 return done;
0080 }
0081
0082
0083
0084
0085
0086 static bool
0087 atsam_i2c_continue_write(Twihs *regs, atsam_i2c_bus *bus)
0088 {
0089 bool done = false;
0090
0091
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
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
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
0155 regs->TWIHS_MMR = 0;
0156 regs->TWIHS_MMR = mmr_temp;
0157
0158
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
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
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
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
0335 PIO_Configure(pins, TWI_AMOUNT_PINS);
0336
0337
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 }