/* SPDX-License-Identifier: BSD-2-Clause */ /* * RTEMS generic MPC5200 BSP * * This file contains the MSCAN driver. */ /* * Copyright (c) 2005 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include "../mscan/mscan_int.h" /* #define MSCAN_LOOPBACK */ struct mpc5200_rx_cntrl mpc5200_mscan_rx_cntrl[MPC5200_CAN_NO]; static struct mscan_channel_info chan_info[MPC5200_CAN_NO]; /* * MPC5x00 MSCAN tx ring buffer function to get a can message buffer from the head of the tx ring buffer */ static struct can_message *get_tx_buffer(struct mscan_channel_info *chan) { /* define a temp. mess ptr. */ struct can_message *tmp_mess_ptr = NULL, *temp_head_ptr; /* set temp. head pointer */ temp_head_ptr = chan->tx_ring_buf.head_ptr; /* check buffer empty condition */ if (temp_head_ptr != chan->tx_ring_buf.tail_ptr) { /* current buffer head. ptr. */ tmp_mess_ptr = temp_head_ptr; /* increment the head pointer */ temp_head_ptr++; /* check for wrap around condition */ if (temp_head_ptr > chan->tx_ring_buf.buf_ptr + NO_OF_MSCAN_TX_BUFF) { /* set head ptr. to the begin of the ring buffer */ temp_head_ptr = chan->tx_ring_buf.buf_ptr; } /* end of crtical section restore head ptr. */ chan->tx_ring_buf.head_ptr = temp_head_ptr; } /* return the current head pointer */ return tmp_mess_ptr; } /* * MPC5x00 MSCAN tx ring buffer function to write a can message buffer to the tail of the tx ring buffer */ static struct can_message *fill_tx_buffer(struct mscan_channel_info *chan, struct can_message *mess_ptr) { /* define a temp. mess ptr. to the entry which follows the current tail entry */ struct can_message *tmp_mess_ptr = chan->tx_ring_buf.tail_ptr + 1; /* check for the wrap around condition */ if (tmp_mess_ptr > chan->tx_ring_buf.buf_ptr + NO_OF_MSCAN_TX_BUFF) { /* set temp. mess. ptr to the begin of the ring buffer */ tmp_mess_ptr = chan->tx_ring_buf.buf_ptr; } /* check buffer full condition */ if (tmp_mess_ptr == chan->tx_ring_buf.head_ptr) { /* return NULL in case buffer is full */ return NULL; } else { /* copy the can mess. to the tail of the buffer */ memcpy((void *) chan->tx_ring_buf.tail_ptr, (void *) mess_ptr, sizeof (struct can_message)); /* set new tail equal to temp. mess. ptr. */ chan->tx_ring_buf.tail_ptr = tmp_mess_ptr; } /* return the actual tail ptr. (next free entry) */ return chan->tx_ring_buf.tail_ptr; } /* * MPC5x00 MSCAN interrupt handler */ static void mpc5200_mscan_interrupt_handler(rtems_irq_hdl_param handle) { rtems_status_code status; mscan_handle *mscan_hdl = (mscan_handle *) handle; struct mscan_channel_info *chan = &chan_info[mscan_hdl->mscan_channel]; struct can_message rx_mess, *rx_mess_ptr, *tx_mess_ptr; mscan *m = chan->regs; register uint8_t idx; /* handle tx ring buffer */ /* loop over all 3 tx buffers */ for (idx = TFLG_TXE0; idx <= TFLG_TXE2; idx = idx << 1) { /* check for tx buffer vacation */ if ((m->tflg) & idx) { /* try to get a message */ tx_mess_ptr = get_tx_buffer(chan); /* check for new tx message */ if (tx_mess_ptr != NULL) { /* select the tx buffer */ m->bsel = idx; /* check for toucan interface */ if ((mscan_hdl->toucan_callback) == NULL) { /* set tx id */ m->txidr0 = SET_IDR0(tx_mess_ptr->mess_id); m->txidr1 = SET_IDR1(tx_mess_ptr->mess_id); m->txidr2 = 0; m->txidr3 = 0; } /* fill in tx data if TOUCAN is activ an TOUCAN index have a match with the tx buffer or TOUCAN is disabled */ if (((mscan_hdl->toucan_callback) == NULL) || (((mscan_hdl->toucan_callback) != NULL) && ((tx_mess_ptr->toucan_tx_idx) == idx))) { /* insert dlc into m register */ m->txdlr = (uint8_t) ((tx_mess_ptr->mess_len) & 0x000F); /* skip data copy in case of RTR */ if (!(MSCAN_MESS_ID_HAS_RTR(tx_mess_ptr->mess_id))) { /* copy tx data to MSCAN registers */ switch (m->txdlr) { case 8: m->txdsr7 = tx_mess_ptr->mess_data[7]; case 7: m->txdsr6 = tx_mess_ptr->mess_data[6]; case 6: m->txdsr5 = tx_mess_ptr->mess_data[5]; case 5: m->txdsr4 = tx_mess_ptr->mess_data[4]; case 4: m->txdsr3 = tx_mess_ptr->mess_data[3]; case 3: m->txdsr2 = tx_mess_ptr->mess_data[2]; case 2: m->txdsr1 = tx_mess_ptr->mess_data[1]; case 1: m->txdsr0 = tx_mess_ptr->mess_data[0]; break; default: break; } } /* enable message buffer specific interrupt */ m->tier |= m->bsel; /* start transfer */ m->tflg = m->bsel; /* release counting semaphore of tx ring buffer */ rtems_semaphore_release((rtems_id) (chan->tx_rb_sid)); } else { /* refill the tx ring buffer with the message */ fill_tx_buffer(chan, tx_mess_ptr); } } else { /* reset interrupt enable bit */ m->tier &= ~(idx); } } } /* handle rx interrupts */ /* check for rx interrupt source */ if (m->rier & RIER_RXFIE) { /* can messages received ? */ while (m->rflg & RFLG_RXF) { if (mscan_hdl->toucan_callback == NULL) { /* select temporary rx buffer */ rx_mess_ptr = &rx_mess; } else { /* check the rx fliter-match indicators (16-bit filter mode) */ /* in case of more than one hit, lower hit has priority */ idx = (m->idac) & 0x7; switch (idx) { case 0: case 1: case 2: case 3: rx_mess_ptr = (struct can_message *) &(mpc5200_mscan_rx_cntrl[mscan_hdl->mscan_channel]. can_rx_message[idx]); break; /* this case should never happen */ default: /* reset the rx indication flag */ m->rflg |= RFLG_RXF; return; break; } } /* get rx ID */ rx_mess_ptr->mess_id = GET_IDR0(m->rxidr0) | GET_IDR1(m->rxidr1); /* get rx len */ rx_mess_ptr->mess_len = ((m->rxdlr) & 0x0F); /* get time stamp */ rx_mess_ptr->mess_time_stamp = ((m->rxtimh << 8) | (m->rxtiml)); /* skip data copy in case of RTR */ if (!(MSCAN_MESS_ID_HAS_RTR(rx_mess_ptr->mess_id))) { /* get the data */ switch (rx_mess_ptr->mess_len) { case 8: rx_mess_ptr->mess_data[7] = m->rxdsr7; case 7: rx_mess_ptr->mess_data[6] = m->rxdsr6; case 6: rx_mess_ptr->mess_data[5] = m->rxdsr5; case 5: rx_mess_ptr->mess_data[4] = m->rxdsr4; case 4: rx_mess_ptr->mess_data[3] = m->rxdsr3; case 3: rx_mess_ptr->mess_data[2] = m->rxdsr2; case 2: rx_mess_ptr->mess_data[1] = m->rxdsr1; case 1: rx_mess_ptr->mess_data[0] = m->rxdsr0; case 0: default: break; } } if (mscan_hdl->toucan_callback == NULL) { if ((status = rtems_message_queue_send(chan->rx_qid, (void *) rx_mess_ptr, sizeof (struct can_message))) != RTEMS_SUCCESSFUL) { chan->int_rx_err++; } } else { mscan_hdl->toucan_callback((int16_t) (((m->idac) & 0x7) + 3)); } /* reset the rx indication flag */ m->rflg |= RFLG_RXF; } /* end of while(m->rflg & RFLG_RXF) */ } /* status change detected */ if (m->rflg & RFLG_CSCIF) { m->rflg |= RFLG_CSCIF; if (mscan_hdl->toucan_callback != NULL) { mscan_hdl->toucan_callback((int16_t) (-1)); } } } /** * @brief Enables some interrupts for the MSCAN module @a m. * * Enabled interrupts: * - Receiver or transmitter enters or leaves bus off state * - Receiver buffer full */ static void mscan_interrupts_enable(mscan *m) { /* RX Interrupt Enable on MSCAN_A/_B ----------------------------- */ /* [07]:WUPIE 0 : WakeUp interrupt disabled */ /* [06]:CSCIE 1 : Status Change interrupt enabled */ /* [05]:RSTATE1 0 : Recv. Status Change int. ,Bit 1 */ /* [04]:RSTATE0 1 : Recv. Status Change int. ,Bit 0 */ /* -> 01 BusOff status changes enabled */ /* [03]:TSTAT1 0 : Transmit. Status Change int. , Bit 1 */ /* [02]:TSTAT0 1 : Transmit. Status Change int. , Bit 0 */ /* -> 01 BusOff status changes enabled */ /* [01]:OVRIE 0 : Overrun Interrupt is disabled */ /* [00]:RXFIE 1 : Recv. Full interrupt is enabled */ m->rier |= (RIER_CSCIE | RIER_RXFIE | RIER_RSTAT(1) | RIER_TSTAT(1)); return; } /* * Unmask MPC5x00 MSCAN_A interrupts */ static void mpc5200_mscan_a_on(const rtems_irq_connect_data * ptr) { mscan *m = (&chan_info[MSCAN_A])->regs; mscan_interrupts_enable(m); return; } /* * Mask MPC5x00 MSCAN_A interrupts */ static void mpc5200_mscan_a_off(const rtems_irq_connect_data * ptr) { mscan *m = (&chan_info[MSCAN_A])->regs; mscan_interrupts_disable(m); return; } /* * Get MSCAN_A interrupt mask setting */ static int mpc5200_mscan_a_isOn(const rtems_irq_connect_data * ptr) { mscan *m = (&chan_info[MSCAN_A])->regs; if ((m->rier & RIER_CSCIE) && (m->rier & RIER_RXFIE)) return RTEMS_SUCCESSFUL; else return RTEMS_UNSATISFIED; return RTEMS_SUCCESSFUL; } /* * Unmask MPC5x00 MSCAN_B interrupts */ static void mpc5200_mscan_b_on(const rtems_irq_connect_data * ptr) { mscan *m = (&chan_info[MSCAN_B])->regs; mscan_interrupts_enable(m); return; } /* * Mask MPC5x00 MSCAN_B interrupts */ static void mpc5200_mscan_b_off(const rtems_irq_connect_data * ptr) { mscan *m = (&chan_info[MSCAN_B])->regs; mscan_interrupts_disable(m); return; } /* * Get MSCAN_B interrupt mask setting */ static int mpc5200_mscan_b_isOn(const rtems_irq_connect_data * ptr) { mscan *m = (&chan_info[MSCAN_B])->regs; if ((m->rier & RIER_CSCIE) && (m->rier & RIER_RXFIE)) return RTEMS_SUCCESSFUL; else return RTEMS_UNSATISFIED; return RTEMS_SUCCESSFUL; } static mscan_handle mscan_a_handle = { MSCAN_A, NULL }; static mscan_handle mscan_b_handle = { MSCAN_B, NULL }; /* * MPC5x00 MSCAN_A/_B irq data */ static rtems_irq_connect_data mpc5200_mscan_irq_data[MPC5200_CAN_NO] = { { BSP_SIU_IRQ_MSCAN1, (rtems_irq_hdl) mpc5200_mscan_interrupt_handler, (rtems_irq_hdl_param) & mscan_a_handle, (rtems_irq_enable) mpc5200_mscan_a_on, (rtems_irq_disable) mpc5200_mscan_a_off, (rtems_irq_is_enabled) mpc5200_mscan_a_isOn }, { BSP_SIU_IRQ_MSCAN2, (rtems_irq_hdl) mpc5200_mscan_interrupt_handler, (rtems_irq_hdl_param) & mscan_b_handle, (rtems_irq_enable) mpc5200_mscan_b_on, (rtems_irq_disable) mpc5200_mscan_b_off, (rtems_irq_is_enabled) mpc5200_mscan_b_isOn } }; /* * MPC5x00 MSCAN wait for sync. with CAN bus */ void mpc5200_mscan_wait_sync(mscan *m) { /* Control Register 0 -------------------------------------------- */ /* [07]:RXFRM 0 : Recv. Frame, Flag Bit (rd.&clear only) */ /* [06]:RXACT 0 : Recv. Active, Status Bit (rd. only) */ /* [05]:CSWAI 0 : CAN Stops in Wait Mode */ /* [04]:SYNCH 0->1 : Synchronized, Status Bit (rd. only) */ /* [03]:TIME 1 : Generate Timestamps */ /* [02]:WUPE 0 : WakeUp Disabled */ /* [01]:SLPRQ 0 : No Sleep Mode Request */ /* [00]:INITRQ 0 : No init. Mode Request */ /* wait for MSCAN A_/_B bus synch. */ #if 0 /* we don't have a need to wait for sync. */ while (!((m->ctl0) & CTL0_SYNCH)); #endif return; } /* * MPC5x00 MSCAN perform settings in init mode */ static void mpc5200_mscan_perform_initialization_mode_settings(mscan *m) { mscan_context context; /* perform all can bit time settings */ (void) mscan_set_bit_rate(m, MSCAN_BIT_RATE_DEFAULT); /* Enter initialization mode */ mscan_initialization_mode_enter( m, &context); /* Control Register 1 -------------------------------------------- */ /* [07]:CANE 0 : MSCAN Module is disabled */ /* [06]:CLKSRC 0 : Clock Source -> IPB_CLOCK (bsp.h) */ /* [05]:LOOPB 0 : No Loopback */ /* [04]:LISTEN 0 : Normal Operation */ /* [03]:res 0 : reserved */ /* [02]:WUPM 0 : No protect. from spurious WakeUp */ /* [01]:SLPAK 1->0 : Sleep Mode Acknowledge (rd. only) */ /* [00]:INITAK 0 : Init. Mode Acknowledge (rd. only) */ /* Set CLK source, disable loopback & listen-only mode */ #ifndef MSCAN_LOOPBACK m->ctl1 &= ~(CTL1_LISTEN | CTL1_LOOPB | CTL1_CLKSRC); #else m->ctl1 &= ~(CTL1_LISTEN | CTL1_CLKSRC); m->ctl1 |= (CTL1_LOOPB); #endif /* IPB clock -> IPB_CLOCK */ /* bitrate -> CAN_BIT_RATE */ /* Max. no of Tq -> CAN_MAX_NO_OF_TQ */ /* Prescaler value -> prescale_val = ROUND_UP(IPB_CLOCK/(CAN_BIT_RATE * CAN_MAX_NO_OF_TQ)) */ /* SYNC_SEG -> 1 tq */ /* time segment 1 -> 16 tq (PROP_SEG+PHASE_SEG), CAN_MAX_NO_OF_TQ_TSEG1 = 15 */ /* time segment 2 -> 8 tq (PHASE_SEG2) , CAN_MAX_NO_OF_TQ_TSEG2 = 7 */ /* SJW -> 3 (fixed 0...3) , CAN_MAX_NO_OF_TQ_SJW = 2 */ /* ID Acceptance Control MSCAN_A/_B ------------------------------ */ /* [07]:res. 0 : reserved */ /* [06]:res. 0 : reserved */ /* [05]:IDAM1 0 : ID acceptance control, Bit1 */ /* [04]:IDAM0 1 : ID acceptance control, Bit0 */ /* -> filter 16 bit mode */ /* [03]:res. 0 : reserved */ /* [02]:IDHIT2 0 : ID acceptance hit indication, Bit 2 */ /* [01]:IDHIT1 0 : ID acceptance hit indication, Bit 1 */ /* [00]:IDHIT0 0 : ID acceptance hit indication, Bit 0 */ m->idac &= ~(IDAC_IDAM1); m->idac |= (IDAC_IDAM0); /* initialize rx filter masks (16 bit), don't care including rtr */ m->idmr0 = SET_IDMR0(0x7FF); m->idmr1 = SET_IDMR1(0x7FF); m->idmr2 = SET_IDMR2(0x7FF); m->idmr3 = SET_IDMR3(0x7FF); m->idmr4 = SET_IDMR4(0x7FF); m->idmr5 = SET_IDMR5(0x7FF); m->idmr6 = SET_IDMR6(0x7FF); m->idmr7 = SET_IDMR7(0x7FF); /* Control Register 1 -------------------------------------------- */ /* [07]:CANE 0->1 : MSCAN Module is enabled */ /* [06]:CLKSRC 1 : Clock Source -> IPB_CLOCK (bsp.h) */ /* [05]:LOOPB 0 : No Loopback */ /* [04]:LISTEN 0 : Normal Operation */ /* [03]:res 0 : reserved */ /* [02]:WUPM 0 : No protect. from spurious WakeUp */ /* [01]:SLPAK 0 : Sleep Mode Acknowledge (rd. only) */ /* [00]:INITAK 0 : Init. Mode Acknowledge (rd. only) */ /* enable MSCAN A_/_B */ m->ctl1 |= (CTL1_CANE); /* Leave initialization mode */ mscan_initialization_mode_leave( m, &context); return; } /* * MPC5x00 MSCAN perform settings in normal mode */ void mpc5200_mscan_perform_normal_mode_settings(mscan *m) { /* Control Register 0 -------------------------------------------- */ /* [07]:RXFRM 0 : Recv. Frame, Flag Bit (rd.&clear only) */ /* [06]:RXACT 0 : Recv. Active, Status Bit (rd. only) */ /* [05]:CSWAI 0 : CAN Stops in Wait Mode */ /* [04]:SYNCH 0 : Synchronized, Status Bit (rd. only) */ /* [03]:TIME 1 : Generate Timestamps */ /* [02]:WUPE 0 : WakeUp Disabled */ /* [01]:SLPRQ 0 : No Sleep Mode Request */ /* [00]:INITRQ 0 : No init. Mode Request */ /* Disable wait mode, enable timestamps */ m->ctl0 &= ~(CTL0_CSWAI); m->ctl0 |= (CTL0_TIME); return; } rtems_status_code mpc5200_mscan_set_mode(rtems_device_minor_number minor, uint8_t mode) { struct mscan_channel_info *chan = NULL; mscan *m = NULL; switch (minor) { case MSCAN_A: case MSCAN_B: chan = &chan_info[minor]; m = chan->regs; break; default: return RTEMS_UNSATISFIED; break; } if (chan->mode == mode) return RTEMS_SUCCESSFUL; switch (mode) { case MSCAN_INIT_NORMAL_MODE: /* perform initialization which has to be done in init mode */ mpc5200_mscan_perform_initialization_mode_settings(m); break; case MSCAN_NORMAL_MODE: if ((chan->mode) == MSCAN_INITIALIZED_MODE) { /* perform initialization which has to be done in init mode */ mpc5200_mscan_perform_initialization_mode_settings(m); } if ((chan->mode) == MSCAN_SLEEP_MODE) { /* exit sleep mode */ mscan_sleep_mode_leave(m); } /* enable ints. */ mscan_interrupts_enable(m); /* wait for bus sync. */ mpc5200_mscan_wait_sync(m); break; case MSCAN_SLEEP_MODE: /* disable ints. */ mscan_interrupts_disable(m); /* knter sleep mode */ mscan_sleep_mode_enter(m); break; default: return RTEMS_UNSATISFIED; break; } /* set new channel mode */ chan->mode = mode; return RTEMS_SUCCESSFUL; } /* * initialization of channel info. */ rtems_status_code mscan_channel_initialize(rtems_device_major_number major, rtems_device_minor_number minor) { rtems_status_code status; struct mscan_channel_info *chan = &chan_info[minor]; /* set registers according to MSCAN channel information */ switch (minor) { case MSCAN_A: chan->rx_qname = rtems_build_name('C', 'N', 'A', 'Q'); chan->tx_rb_sname = rtems_build_name('C', 'N', 'A', 'S'); /* register RTEMS device names for MSCAN A */ if ((status = rtems_io_register_name(MSCAN_A_DEV_NAME, major, MSCAN_A)) != RTEMS_SUCCESSFUL) return status; /* register RTEMS device names for MSCAN 0 */ if ((status = rtems_io_register_name(MSCAN_0_DEV_NAME, major, MSCAN_A)) != RTEMS_SUCCESSFUL) return status; /* allocate the space for MSCAN A tx ring buffer */ if (((chan->tx_ring_buf.buf_ptr) = malloc(sizeof (struct can_message) * (NO_OF_MSCAN_TX_BUFF + 1))) != NULL) { chan->tx_ring_buf.head_ptr = chan->tx_ring_buf.tail_ptr = chan->tx_ring_buf.buf_ptr; } else { return RTEMS_UNSATISFIED; } break; case MSCAN_B: chan->rx_qname = rtems_build_name('C', 'N', 'B', 'Q'); chan->tx_rb_sname = rtems_build_name('C', 'N', 'B', 'S'); /* register RTEMS device names for MSCAN B */ if ((status = rtems_io_register_name(MSCAN_B_DEV_NAME, major, MSCAN_B)) != RTEMS_SUCCESSFUL) return status; /* register RTEMS device names for MSCAN 1 */ if ((status = rtems_io_register_name(MSCAN_1_DEV_NAME, major, MSCAN_B)) != RTEMS_SUCCESSFUL) return status; /* allocate the space for MSCAN B tx ring buffer */ if (((chan->tx_ring_buf.buf_ptr) = malloc(sizeof (struct can_message) * (NO_OF_MSCAN_TX_BUFF + 1))) != NULL) { chan->tx_ring_buf.head_ptr = chan->tx_ring_buf.tail_ptr = chan->tx_ring_buf.buf_ptr; } else { return RTEMS_UNSATISFIED; } break; default: return RTEMS_UNSATISFIED; break; } /* create RTEMS rx message queue */ status = rtems_message_queue_create(chan->rx_qname, (uint32_t) NO_OF_MSCAN_RX_BUFF, (uint32_t) MSCAN_MESSAGE_SIZE(sizeof (struct can_message)), (rtems_attribute) RTEMS_LOCAL | RTEMS_FIFO, (rtems_id *) & (chan->rx_qid)); /* create counting RTEMS tx ring buffer semaphore */ status = rtems_semaphore_create(chan->tx_rb_sname, (uint32_t) (NO_OF_MSCAN_TX_BUFF), RTEMS_COUNTING_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, (rtems_task_priority) 0, (rtems_id *) & (chan->tx_rb_sid)); /* Set up interrupts */ if (!BSP_install_rtems_irq_handler(&(mpc5200_mscan_irq_data[minor]))) rtems_panic("Can't attach MPC5x00 MSCAN interrupt handler %ld\n", minor); /* basic setup for channel info. struct. */ chan->regs = (mscan *) &(mpc5200.mscan[minor]); chan->int_rx_err = 0; chan->id_extended = FALSE; chan->mode = MSCAN_INITIALIZED_MODE; chan->tx_buf_no = NO_OF_MSCAN_TX_BUFF; return status; } /* * MPC5x00 MSCAN device initialization */ rtems_device_driver mscan_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { rtems_status_code status; /* Initialization requested via RTEMS */ if ((status = mscan_channel_initialize(major, MSCAN_A)) != RTEMS_SUCCESSFUL) bsp_fatal(MPC5200_FATAL_MSCAN_A_INIT); if ((status = mscan_channel_initialize(major, MSCAN_B)) != RTEMS_SUCCESSFUL) bsp_fatal(MPC5200_FATAL_MSCAN_B_INIT); if ((status = mpc5200_mscan_set_mode(MSCAN_A, MSCAN_INIT_NORMAL_MODE)) != RTEMS_SUCCESSFUL) bsp_fatal(MPC5200_FATAL_MSCAN_A_SET_MODE); if ((status = mpc5200_mscan_set_mode(MSCAN_B, MSCAN_INIT_NORMAL_MODE)) != RTEMS_SUCCESSFUL) bsp_fatal(MPC5200_FATAL_MSCAN_B_SET_MODE); return status; } /* * MPC5x00 MSCAN device open */ rtems_device_driver mscan_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { rtems_status_code status = RTEMS_SUCCESSFUL; struct mscan_channel_info *chan = NULL; switch (minor) { case MSCAN_A: case MSCAN_B: chan = &chan_info[minor]; break; default: return RTEMS_UNSATISFIED; break; } /* check mode */ if ((chan->mode) == MSCAN_SLEEP_MODE) { /* if not already set enter init mode */ status = mpc5200_mscan_set_mode(minor, MSCAN_NORMAL_MODE); } return status; } /* * MPC5x00 MSCAN device close */ rtems_device_driver mscan_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { rtems_status_code status; switch (minor) { case MSCAN_A: case MSCAN_B: break; default: return RTEMS_UNSATISFIED; break; } /* enter deep sleep mode */ status = mpc5200_mscan_set_mode(minor, MSCAN_SLEEP_MODE); return status; } /* * MPC5x00 MSCAN device read */ rtems_device_driver mscan_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { rtems_status_code status; size_t message_size = 0; rtems_libio_rw_args_t *parms = (rtems_libio_rw_args_t *) arg; struct mscan_rx_parms *rx_parms = (struct mscan_rx_parms *) (parms->buffer); struct can_message *rx_mess = (struct can_message *) (rx_parms->rx_mess); struct mscan_channel_info *chan = NULL; switch (minor) { case MSCAN_A: case MSCAN_B: chan = &chan_info[minor]; break; default: return RTEMS_UNSATISFIED; break; } /* end init mode if it is first read */ if ((chan->mode) == MSCAN_INIT_NORMAL_MODE) { /* if not already set enter init mode */ mpc5200_mscan_set_mode(minor, MSCAN_NORMAL_MODE); } if ((status = rtems_message_queue_receive(chan->rx_qid, (void *) (rx_mess), &message_size, (uint32_t) (rx_parms->rx_flags), (rtems_interval) (rx_parms->rx_timeout))) != RTEMS_SUCCESSFUL) { parms->bytes_moved = 0; } else { parms->bytes_moved = sizeof (struct can_message); } return status; } /* * MPC5x00 MSCAN device write */ rtems_device_driver mscan_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { rtems_status_code status; rtems_libio_rw_args_t *parms = (rtems_libio_rw_args_t *) arg; struct mscan_tx_parms *tx_parms = (struct mscan_tx_parms *) (parms->buffer); struct can_message *tx_mess = (struct can_message *) (tx_parms->tx_mess); struct mscan_channel_info *chan = NULL; mscan *m = NULL; switch (minor) { case MSCAN_A: case MSCAN_B: chan = &chan_info[minor]; m = chan->regs; break; default: return RTEMS_UNSATISFIED; break; } /* end init mode if it is first write */ if ((chan->mode) == MSCAN_INIT_NORMAL_MODE) { /* if not already set enter init mode */ mpc5200_mscan_set_mode(minor, MSCAN_NORMAL_MODE); } /* preset moved bytes */ parms->bytes_moved = 0; /* obtain counting semaphore of tx ring buffer */ if ((status = rtems_semaphore_obtain((rtems_id) (chan->tx_rb_sid), RTEMS_NO_WAIT, (rtems_interval) 0)) == RTEMS_SUCCESSFUL) { /* append the TOUCAN tx_id to the mess. due to interrupt handling */ tx_mess->toucan_tx_idx = tx_parms->tx_idx; /* fill the tx ring buffer with the message */ fill_tx_buffer(chan, tx_mess); /* enable message buffer specific interrupt */ m->tier |= (TIER_TXEI0 | TIER_TXEI1 | TIER_TXEI2); /* calculate moved bytes */ parms->bytes_moved = (tx_mess->mess_len) & 0x000F; } return status; } /* * MPC5x00 MSCAN device control */ rtems_device_driver mscan_control(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { rtems_status_code status; uint16_t tx_id; rtems_libio_ioctl_args_t *parms = (rtems_libio_ioctl_args_t *) arg; struct mscan_ctrl_parms *ctrl_parms = (struct mscan_ctrl_parms *) (parms->buffer); struct mscan_channel_info *chan = NULL; mscan_handle *mscan_hdl = NULL; mscan *m = NULL; mscan_context context; uint8_t tx_buf_count = 0; switch (minor) { case MSCAN_A: case MSCAN_B: chan = &chan_info[minor]; mscan_hdl = mpc5200_mscan_irq_data[minor].handle; m = chan->regs; break; default: return RTEMS_UNSATISFIED; break; } switch (parms->command) { /* TOUCAN callback initialization for MSCAN */ case TOUCAN_MSCAN_INIT: mscan_hdl->toucan_callback = ctrl_parms->toucan_cb_fnc; break; /* set rx buffer ID */ case MSCAN_SET_RX_ID: /* enter init mode */ mscan_initialization_mode_enter(m, &context); switch (ctrl_parms->ctrl_reg_no) { case RX_BUFFER_0: m->idar0 = SET_IDR0(ctrl_parms->ctrl_id); m->idar1 = SET_IDR1(ctrl_parms->ctrl_id); break; case RX_BUFFER_1: m->idar2 = SET_IDR2(ctrl_parms->ctrl_id); m->idar3 = SET_IDR3(ctrl_parms->ctrl_id); break; case RX_BUFFER_2: m->idar4 = SET_IDR4(ctrl_parms->ctrl_id); m->idar5 = SET_IDR5(ctrl_parms->ctrl_id); break; case RX_BUFFER_3: m->idar6 = SET_IDR6(ctrl_parms->ctrl_id); m->idar7 = SET_IDR7(ctrl_parms->ctrl_id); break; default: break; } /* exit init mode and perform further initialization which is required in the normal mode */ mscan_initialization_mode_leave(m, &context); /* enable ints. */ mscan_interrupts_enable(m); /* wait for bus sync. */ mpc5200_mscan_wait_sync(m); return RTEMS_SUCCESSFUL; break; /* get rx buffer ID */ case MSCAN_GET_RX_ID: switch (ctrl_parms->ctrl_reg_no) { case RX_BUFFER_0: ctrl_parms->ctrl_id = GET_IDR0(m->idar0) | GET_IDR1(m->idar1); break; case RX_BUFFER_1: ctrl_parms->ctrl_id = GET_IDR2(m->idar2) | GET_IDR3(m->idar3); break; case RX_BUFFER_2: ctrl_parms->ctrl_id = GET_IDR4(m->idar4) | GET_IDR5(m->idar5); break; case RX_BUFFER_3: ctrl_parms->ctrl_id = GET_IDR6(m->idar6) | GET_IDR7(m->idar7); break; default: break; } break; /* set rx buffer ID mask */ case MSCAN_SET_RX_ID_MASK: /* enter init mode */ mscan_initialization_mode_enter(m, &context); switch (ctrl_parms->ctrl_reg_no) { case RX_BUFFER_0: m->idmr0 = SET_IDMR0(ctrl_parms->ctrl_id_mask); m->idmr1 = SET_IDMR1(ctrl_parms->ctrl_id_mask); break; case RX_BUFFER_1: m->idmr2 = SET_IDMR2(ctrl_parms->ctrl_id_mask); m->idmr3 = SET_IDMR3(ctrl_parms->ctrl_id_mask); break; case RX_BUFFER_2: m->idmr4 = SET_IDMR4(ctrl_parms->ctrl_id_mask); m->idmr5 = SET_IDMR5(ctrl_parms->ctrl_id_mask); break; case RX_BUFFER_3: m->idmr6 = SET_IDMR6(ctrl_parms->ctrl_id_mask); m->idmr7 = SET_IDMR7(ctrl_parms->ctrl_id_mask); break; default: break; } /* exit init mode and perform further initialization which is required in the normal mode */ mscan_initialization_mode_leave(m, &context); /* enable ints. */ mscan_interrupts_enable(m); /* wait for bus sync. */ mpc5200_mscan_wait_sync(m); break; /* get rx buffer ID mask */ case MSCAN_GET_RX_ID_MASK: switch (ctrl_parms->ctrl_reg_no) { case RX_BUFFER_0: ctrl_parms->ctrl_id_mask = (GET_IDMR0(m->idmr0) | GET_IDMR1(m->idmr1)); break; case RX_BUFFER_1: ctrl_parms->ctrl_id_mask = (GET_IDMR2(m->idmr2) | GET_IDMR3(m->idmr3)); break; case RX_BUFFER_2: ctrl_parms->ctrl_id_mask = (GET_IDMR4(m->idmr4) | GET_IDMR5(m->idmr5)); break; case RX_BUFFER_3: ctrl_parms->ctrl_id_mask = (GET_IDMR6(m->idmr6) | GET_IDMR7(m->idmr7)); break; default: break; } /* set tx buffer ID */ case MSCAN_SET_TX_ID: /* check for availability of tx buffer */ if (!((m->tflg) & (uint8_t) (ctrl_parms->ctrl_reg_no))) { /* do abort tx buf. request */ m->tarq = (uint8_t) (ctrl_parms->ctrl_reg_no); /* wait for abort tx buf. ack. */ while ((m->taak) & (uint8_t) (ctrl_parms->ctrl_reg_no)); } /* select tx buf. */ m->bsel = (uint8_t) (ctrl_parms->ctrl_reg_no); /* set the tx id of selected buf. */ tx_id = ctrl_parms->ctrl_id; m->txidr0 = SET_IDR0(tx_id); m->txidr1 = SET_IDR1(tx_id); m->txidr2 = 0; m->txidr3 = 0; break; /* get tx buffer ID */ case MSCAN_GET_TX_ID: /* select tx buf. */ m->bsel = (uint8_t) (ctrl_parms->ctrl_reg_no); /* get tx id. of selected buf. */ ctrl_parms->ctrl_id = GET_IDR0(m->txidr0) | GET_IDR1(m->txidr1); break; /* set can bitrate */ case MSCAN_SET_BAUDRATE: /* perform all can bit time settings */ if (!mscan_set_bit_rate(m, ctrl_parms->ctrl_can_bitrate)) { return RTEMS_UNSATISFIED; } /* enable ints. */ mscan_interrupts_enable(m); /* wait for bus sync. */ mpc5200_mscan_wait_sync(m); break; case SET_TX_BUF_NO: /* check for different settings of tx ring buffer */ if ((tx_buf_count = chan->tx_buf_no) != (uint8_t) (ctrl_parms->ctrl_tx_buf_no)) { /* preset the channel specific no of messages in the tx ring buffer */ tx_buf_count = chan->tx_buf_no; /* try to obtain all of the tx ring buffers */ while (tx_buf_count > 0) { /* obtain semaphore of all tx ring buffers */ if ((status = rtems_semaphore_obtain((rtems_id) (chan->tx_rb_sid), RTEMS_WAIT, (rtems_interval) 10)) == RTEMS_SUCCESSFUL) { tx_buf_count--; } } /* free the former tx ring buffer */ free((void *) chan->tx_ring_buf.buf_ptr); /* allocate the tx ring buffer with new size */ if (((chan->tx_ring_buf.buf_ptr) = malloc(sizeof (struct can_message) * ((uint8_t) (ctrl_parms->ctrl_tx_buf_no) + 1))) != NULL) { chan->tx_ring_buf.head_ptr = chan->tx_ring_buf.tail_ptr = chan->tx_ring_buf.buf_ptr; } else { return RTEMS_UNSATISFIED; } /* set the new amount of tx buffers */ chan->tx_buf_no = (uint8_t) (ctrl_parms->ctrl_tx_buf_no); /* release the semaphore of all tx ring buffers */ while (tx_buf_count < chan->tx_buf_no) { /* obtain semaphore of all tx ring buffers */ rtems_semaphore_release((rtems_id) (chan->tx_rb_sid)); tx_buf_count++; } } else { return RTEMS_SUCCESSFUL; } break; default: break; } return RTEMS_SUCCESSFUL; }