BeRTOS
ser_dsp56k.c
Go to the documentation of this file.
00001 
00041 #include <drv/ser.h>
00042 #include <drv/ser_p.h>
00043 #include <drv/irq.h>
00044 #include <cfg/debug.h>
00045 #include <hw.h>
00046 #include <DSP56F807.h>
00047 
00048 // GPIO E is shared with SPI (in DSP56807). Pins 0&1 are TXD0 and RXD0. To use
00049 //  the serial, we need to disable the GPIO functions on them.
00050 #define REG_GPIO_SERIAL_0       REG_GPIO_E
00051 #define REG_GPIO_SERIAL_MASK_0  0x03
00052 
00053 #define REG_GPIO_SERIAL_1       REG_GPIO_D
00054 #define REG_GPIO_SERIAL_MASK_1  0xC0
00055 
00056 
00057 // Check flag consistency
00058 #if (SERRF_PARITYERROR != REG_SCI_SR_PF) || \
00059     (SERRF_RXSROVERRUN != REG_SCI_SR_OR) || \
00060     (SERRF_FRAMEERROR  != REG_SCI_SR_FE) || \
00061     (SERRF_NOISEERROR  != REG_SCI_SR_NF)
00062     #error error flags do not match with register bits
00063 #endif
00064 
00065 static unsigned char ser0_fifo_rx[CONFIG_SER0_FIFOSIZE_RX];
00066 static unsigned char ser0_fifo_tx[CONFIG_SER0_FIFOSIZE_TX];
00067 static unsigned char ser1_fifo_rx[CONFIG_SER1_FIFOSIZE_RX];
00068 static unsigned char ser1_fifo_tx[CONFIG_SER1_FIFOSIZE_TX];
00069 
00070 #if CONFIG_SER_MULTI
00071     #include <kern/sem.h>
00072 
00073     #define MAX_MULTI_GROUPS     1
00074 
00075     struct Semaphore multi_sems[MAX_MULTI_GROUPS];
00076 #endif
00077 
00078 
00079 struct SCI
00080 {
00081     struct SerialHardware hw;
00082     struct Serial* serial;
00083     volatile struct REG_SCI_STRUCT* regs;
00084     IRQ_VECTOR irq_tx;
00085     IRQ_VECTOR irq_rx;
00086     int num_group;
00087     int id;
00088 };
00089 
00090 static inline void enable_tx_irq_bare(volatile struct REG_SCI_STRUCT* regs)
00091 {
00092     regs->CR |= REG_SCI_CR_TEIE | REG_SCI_CR_TIIE;
00093 }
00094 
00095 static inline void enable_rx_irq_bare(volatile struct REG_SCI_STRUCT* regs)
00096 {
00097     regs->CR |= REG_SCI_CR_RIE;
00098 }
00099 
00100 static inline void disable_tx_irq_bare(volatile struct REG_SCI_STRUCT* regs)
00101 {
00102     regs->CR &= ~(REG_SCI_CR_TEIE | REG_SCI_CR_TIIE);
00103 }
00104 
00105 static inline void disable_rx_irq_bare(volatile struct REG_SCI_STRUCT* regs)
00106 {
00107     regs->CR &= ~(REG_SCI_CR_RIE | REG_SCI_CR_REIE);
00108 }
00109 
00110 static inline void disable_tx_irq(struct SerialHardware* _hw)
00111 {
00112     struct SCI* hw = (struct SCI*)_hw;
00113 
00114     disable_tx_irq_bare(hw->regs);
00115 }
00116 
00117 static inline void disable_rx_irq(struct SerialHardware* _hw)
00118 {
00119     struct SCI* hw = (struct SCI*)_hw;
00120 
00121     disable_rx_irq_bare(hw->regs);
00122 }
00123 
00124 static inline void enable_tx_irq(struct SerialHardware* _hw)
00125 {
00126     struct SCI* hw = (struct SCI*)_hw;
00127 
00128     enable_tx_irq_bare(hw->regs);
00129 }
00130 
00131 static inline void enable_rx_irq(struct SerialHardware* _hw)
00132 {
00133     struct SCI* hw = (struct SCI*)_hw;
00134 
00135     enable_rx_irq_bare(hw->regs);
00136 }
00137 
00138 static inline bool tx_irq_enabled(struct SerialHardware* _hw)
00139 {
00140     struct SCI* hw = (struct SCI*)_hw;
00141 
00142     return (hw->regs->CR & REG_SCI_CR_TEIE);
00143 }
00144 
00145 static void tx_isr(const struct SCI *hw)
00146 {
00147 #pragma interrupt warn
00148     volatile struct REG_SCI_STRUCT* regs = hw->regs;
00149 
00150     if (fifo_isempty(&hw->serial->txfifo))
00151         disable_tx_irq_bare(regs);
00152     else
00153     {
00154         // Clear transmitter flags before sending data
00155         (void)regs->SR;
00156         regs->DR = fifo_pop(&hw->serial->txfifo);
00157     }
00158 }
00159 
00160 static void rx_isr(const struct SCI *hw)
00161 {
00162 #pragma interrupt warn
00163     volatile struct REG_SCI_STRUCT* regs = hw->regs;
00164 
00165     // Propagate errors
00166     hw->serial->status |= regs->SR & (SERRF_PARITYERROR |
00167                                       SERRF_RXSROVERRUN |
00168                                       SERRF_FRAMEERROR |
00169                                       SERRF_NOISEERROR);
00170 
00171     /*
00172      * Serial IRQ can happen for two reason: data ready (RDRF) or overrun (OR)
00173      * If the data is ready, we need to fetch it from the data register or
00174      * the interrupt will retrigger immediatly. In case of overrun, instead,
00175      * the value of the data register is meaningless.
00176      */
00177     if (regs->SR & REG_SCI_SR_RDRF)
00178     {
00179         unsigned char data = regs->DR;
00180 
00181         if (fifo_isfull(&hw->serial->rxfifo))
00182             hw->serial->status |= SERRF_RXFIFOOVERRUN;
00183         else
00184             fifo_push(&hw->serial->rxfifo, data);
00185     }
00186 
00187     // Writing anything to the status register clear the error bits.
00188     regs->SR = 0;
00189 }
00190 
00191 static void init(struct SerialHardware* _hw, struct Serial* ser)
00192 {
00193     struct SCI* hw = (struct SCI*)_hw;
00194     volatile struct REG_SCI_STRUCT* regs = hw->regs;
00195 
00196     // Clear status register (IRQ/status flags)
00197     (void)regs->SR;
00198     regs->SR = 0;
00199 
00200     // Clear data register
00201     (void)regs->DR;
00202 
00203     // Install the handlers and set priorities for both IRQs
00204     irq_install(hw->irq_tx, (isr_t)tx_isr, hw);
00205     irq_install(hw->irq_rx, (isr_t)rx_isr, hw);
00206     irq_setpriority(hw->irq_tx, IRQ_PRIORITY_SCI_TX);
00207     irq_setpriority(hw->irq_rx, IRQ_PRIORITY_SCI_RX);
00208 
00209     // Activate the RX error interrupts, and RX/TX transmissions
00210     regs->CR = REG_SCI_CR_TE | REG_SCI_CR_RE;
00211     enable_rx_irq_bare(regs);
00212 
00213     // Disable GPIO pins for TX and RX lines
00214     // \todo this should be divided into serial 0 and 1
00215     REG_GPIO_SERIAL_0->PER |= REG_GPIO_SERIAL_MASK_0;
00216     REG_GPIO_SERIAL_1->PER |= REG_GPIO_SERIAL_MASK_1;
00217 
00218     hw->serial = ser;
00219 }
00220 
00221 static void cleanup(struct SerialHardware* _hw)
00222 {
00223     struct SCI* hw = (struct SCI*)_hw;
00224 
00225     // Uninstall the ISRs
00226     disable_rx_irq(_hw);
00227     disable_tx_irq(_hw);
00228     irq_uninstall(hw->irq_tx);
00229     irq_uninstall(hw->irq_rx);
00230 }
00231 
00232 static void setbaudrate(struct SerialHardware* _hw, unsigned long rate)
00233 {
00234     struct SCI* hw = (struct SCI*)_hw;
00235 
00236     // SCI has an internal 16x divider on the input clock, which comes
00237     //  from the IPbus (see the scheme in user manual, 12.7.3). We apply
00238     //  it to calculate the period to store in the register.
00239     hw->regs->BR = (IPBUS_FREQ + rate * 8ul) / (rate * 16ul);
00240 }
00241 
00242 static void setparity(struct SerialHardware* _hw, int parity)
00243 {
00244     // ???
00245     ASSERT(0);
00246 }
00247 
00248 
00249 #if CONFIG_SER_MULTI
00250 
00251 static void multi_init(void)
00252 {
00253     static bool flag = false;
00254     int i;
00255 
00256     if (flag)
00257         return;
00258 
00259     for (i = 0; i < MAX_MULTI_GROUPS; ++i)
00260         sem_init(&multi_sems[i]);
00261     flag = true;
00262 }
00263 
00264 static void init_lock(struct SerialHardware* _hw, struct Serial *ser)
00265 {
00266     struct SCI* hw = (struct SCI*)_hw;
00267 
00268     // Initialize the multi engine (if needed)
00269     multi_init();
00270 
00271     // Acquire the lock of the semaphore for this group
00272     ASSERT(hw->num_group >= 0);
00273     ASSERT(hw->num_group < MAX_MULTI_GROUPS);
00274     sem_obtain(&multi_sems[hw->num_group]);
00275 
00276     // Do a hardware switch to the given serial
00277     ser_hw_switch(hw->num_group, hw->id);
00278 
00279     init(_hw, ser);
00280 }
00281 
00282 static void cleanup_unlock(struct SerialHardware* _hw)
00283 {
00284     struct SCI* hw = (struct SCI*)_hw;
00285 
00286     cleanup(_hw);
00287 
00288     sem_release(&multi_sems[hw->num_group]);
00289 }
00290 
00291 #endif /* CONFIG_SER_MULTI */
00292 
00293 
00294 static const struct SerialHardwareVT SCI_VT =
00295 {
00296     .init = init,
00297     .cleanup = cleanup,
00298     .setBaudrate = setbaudrate,
00299     .setParity = setparity,
00300     .txStart = enable_tx_irq,
00301     .txSending = tx_irq_enabled,
00302 };
00303 
00304 #if CONFIG_SER_MULTI
00305 static const struct SerialHardwareVT SCI_MULTI_VT =
00306 {
00307     .init = init_lock,
00308     .cleanup = cleanup_unlock,
00309     .setBaudrate = setbaudrate,
00310     .setParity = setparity,
00311     .txStart = enable_tx_irq,
00312     .txSending = tx_irq_enabled,
00313 };
00314 #endif /* CONFIG_SER_MULTI */
00315 
00316 #define SCI_DESC_NORMAL(hwch) \
00317     { \
00318         .hw = \
00319         { \
00320             .table = &SCI_VT, \
00321             .rxbuffer = ser ## hwch ## _fifo_rx, \
00322             .txbuffer = ser ## hwch ## _fifo_tx, \
00323             .rxbuffer_size = countof(ser ## hwch ## _fifo_rx), \
00324             .txbuffer_size = countof(ser ## hwch ## _fifo_tx), \
00325         }, \
00326         .regs = &REG_SCI[hwch], \
00327         .irq_rx = IRQ_SCI ## hwch ## _RECEIVER_FULL, \
00328         .irq_tx = IRQ_SCI ## hwch ## _TRANSMITTER_READY, \
00329         .num_group = -1, \
00330         .id = -1, \
00331     } \
00332     
00333 
00334 #if CONFIG_SER_MULTI
00335 #define SCI_DESC_MULTI(hwch, group_, id_) \
00336     { \
00337         .hw = \
00338         { \
00339             .table = &SCI_MULTI_VT, \
00340             .rxbuffer = ser ## hwch ## _fifo_rx, \
00341             .txbuffer = ser ## hwch ## _fifo_tx, \
00342             .rxbuffer_size = countof(ser ## hwch ## _fifo_rx), \
00343             .txbuffer_size = countof(ser ## hwch ## _fifo_tx), \
00344         }, \
00345         .regs = &REG_SCI[hwch], \
00346         .irq_rx = IRQ_SCI ## hwch ## _RECEIVER_FULL, \
00347         .irq_tx = IRQ_SCI ## hwch ## _TRANSMITTER_READY, \
00348         .num_group = group_, \
00349         .id = id_, \
00350     } \
00351     
00352 #endif /* CONFIG_SER_MULTI */
00353 
00354 // \todo Move this into hw.h, with a little preprocessor magic
00355 static struct SCI SCIDescs[] =
00356 {
00357     SCI_DESC_NORMAL(0),
00358     SCI_DESC_MULTI(1, 0, 0),
00359     SCI_DESC_MULTI(1, 0, 1),
00360 };
00361 
00362 struct SerialHardware* ser_hw_getdesc(int unit)
00363 {
00364     ASSERT(unit < countof(SCIDescs));
00365     return &SCIDescs[unit].hw;
00366 }