diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-23 13:31:18 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-23 15:18:46 +0200 |
commit | 2197852371ea390092fcc6b5314a2e669a67e1a2 (patch) | |
tree | 6fa02d8a36808c40eb8357c6a0a50b50374ecabe /bsps/lm32 | |
parent | bsps: Move ATA drivers to bsps (diff) | |
download | rtems-2197852371ea390092fcc6b5314a2e669a67e1a2.tar.bz2 |
bsps/lm32: Move shared drivers to bsps
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps/lm32')
21 files changed, 3327 insertions, 0 deletions
diff --git a/bsps/lm32/shared/milkymist_ac97/ac97.c b/bsps/lm32/shared/milkymist_ac97/ac97.c new file mode 100644 index 0000000000..d06f4ced0a --- /dev/null +++ b/bsps/lm32/shared/milkymist_ac97/ac97.c @@ -0,0 +1,436 @@ +/* ac97.c + * + * Sound driver for Milkymist SoC + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * COPYRIGHT (c) 2010, 2011 Sebastien Bourdeauducq + */ + +#define RTEMS_STATUS_CHECKS_USE_PRINTK + +#include <rtems.h> +#include <bsp.h> +#include <bsp/irq-generic.h> +#include <rtems/libio.h> +#include <rtems/status-checks.h> +#include "../include/system_conf.h" +#include <bsp/milkymist_ac97.h> + +#define SND_DEVICE_NAME "/dev/snd" +#define MIXER_DEVICE_NAME "/dev/mixer" + +static rtems_id cr_write_sem; +static rtems_id cr_read_sem; + +static rtems_isr crrequest_handler(rtems_vector_number n) +{ + rtems_semaphore_release(cr_write_sem); + lm32_interrupt_ack(1 << MM_IRQ_AC97CRREQUEST); +} + +static rtems_isr crreply_handler(rtems_vector_number n) +{ + rtems_semaphore_release(cr_read_sem); + lm32_interrupt_ack(1 << MM_IRQ_AC97CRREPLY); +} + +/* queued playback buffers */ +#define PLAY_Q_SIZE 8 +#define PLAY_Q_MASK (PLAY_Q_SIZE-1) + +static struct snd_buffer *play_q[PLAY_Q_SIZE]; +static int play_produce; +static int play_consume; +static int play_level; + +/* buffers played, for application to collect */ +static rtems_id play_q_done; + +static void play_start(struct snd_buffer *buf) +{ + if (buf->nsamples > (AC97_MAX_DMASIZE/4)) + buf->nsamples = AC97_MAX_DMASIZE/4; + + MM_WRITE(MM_AC97_DADDRESS, (unsigned int)buf->samples); + MM_WRITE(MM_AC97_DREMAINING, buf->nsamples*4); + MM_WRITE(MM_AC97_DCTL, AC97_SCTL_EN); +} + +static rtems_isr pcmplay_handler(rtems_vector_number n) +{ + lm32_interrupt_ack(1 << MM_IRQ_AC97DMAR); + + rtems_message_queue_send(play_q_done, &play_q[play_consume], + sizeof(void *)); + + play_consume = (play_consume + 1) & PLAY_Q_MASK; + play_level--; + + if(play_level > 0) + play_start(play_q[play_consume]); + else + MM_WRITE(MM_AC97_DCTL, 0); +} + +/* queued record buffers */ +#define RECORD_Q_SIZE 8 +#define RECORD_Q_MASK (RECORD_Q_SIZE-1) + +static struct snd_buffer *record_q[RECORD_Q_SIZE]; +static int record_produce; +static int record_consume; +static int record_level; + +/* buffers recorded, for application to collect */ +static rtems_id record_q_done; + +static void record_start(struct snd_buffer *buf) +{ + if (buf->nsamples > (AC97_MAX_DMASIZE/4)) + buf->nsamples = AC97_MAX_DMASIZE/4; + + MM_WRITE(MM_AC97_UADDRESS, (unsigned int)buf->samples); + MM_WRITE(MM_AC97_UREMAINING, buf->nsamples*4); + MM_WRITE(MM_AC97_UCTL, AC97_SCTL_EN); +} + +static rtems_isr pcmrecord_handler(rtems_vector_number n) +{ + lm32_interrupt_ack(1 << MM_IRQ_AC97DMAW); + + __asm__ volatile( /* Invalidate Level-1 data cache */ + "wcsr DCC, r0\n" + "nop\n" + ); + + rtems_message_queue_send(record_q_done, &record_q[record_consume], + sizeof(void *)); + + record_consume = (record_consume + 1) & RECORD_Q_MASK; + record_level--; + + if(record_level > 0) + record_start(record_q[record_consume]); + else + MM_WRITE(MM_AC97_UCTL, 0); +} + +rtems_device_driver ac97_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code sc; + rtems_isr_entry dummy; + + sc = rtems_io_register_name(SND_DEVICE_NAME, major, 0); + RTEMS_CHECK_SC(sc, "create snd device"); + + sc = rtems_io_register_name(MIXER_DEVICE_NAME, major, 1); + RTEMS_CHECK_SC(sc, "create mixer device"); + + sc = rtems_semaphore_create( + rtems_build_name('C', 'R', 'W', 'S'), + 0, + RTEMS_SIMPLE_BINARY_SEMAPHORE, + 0, + &cr_write_sem + ); + RTEMS_CHECK_SC(sc, "create AC97 register write semaphore"); + + sc = rtems_semaphore_create( + rtems_build_name('C', 'R', 'R', 'S'), + 0, + RTEMS_SIMPLE_BINARY_SEMAPHORE, + 0, + &cr_read_sem + ); + RTEMS_CHECK_SC(sc, "create AC97 register read semaphore"); + + sc = rtems_message_queue_create( + rtems_build_name('P', 'L', 'Y', 'Q'), + PLAY_Q_SIZE*2, + sizeof(void *), + 0, + &play_q_done + ); + RTEMS_CHECK_SC(sc, "create playback done queue"); + + sc = rtems_message_queue_create( + rtems_build_name('R', 'E', 'C', 'Q'), + RECORD_Q_SIZE*2, + sizeof(void *), + 0, + &record_q_done + ); + RTEMS_CHECK_SC(sc, "create record done queue"); + + rtems_interrupt_catch(crrequest_handler, MM_IRQ_AC97CRREQUEST, &dummy); + rtems_interrupt_catch(crreply_handler, MM_IRQ_AC97CRREPLY, &dummy); + rtems_interrupt_catch(pcmplay_handler, MM_IRQ_AC97DMAR, &dummy); + rtems_interrupt_catch(pcmrecord_handler, MM_IRQ_AC97DMAW, &dummy); + bsp_interrupt_vector_enable(MM_IRQ_AC97CRREQUEST); + bsp_interrupt_vector_enable(MM_IRQ_AC97CRREPLY); + bsp_interrupt_vector_enable(MM_IRQ_AC97DMAR); + bsp_interrupt_vector_enable(MM_IRQ_AC97DMAW); + + play_produce = 0; + play_consume = 0; + play_level = 0; + + record_produce = 0; + record_consume = 0; + record_level = 0; + + return RTEMS_SUCCESSFUL; +} + +static rtems_status_code submit_play(struct snd_buffer *buf) +{ + bsp_interrupt_vector_disable(MM_IRQ_AC97DMAR); + if (play_level == PLAY_Q_SIZE) { + bsp_interrupt_vector_enable(MM_IRQ_AC97DMAR); + return RTEMS_UNSATISFIED; + } + play_q[play_produce] = buf; + play_produce = (play_produce + 1) & PLAY_Q_MASK; + play_level++; + + if (play_level == 1) + play_start(buf); + + bsp_interrupt_vector_enable(MM_IRQ_AC97DMAR); + return RTEMS_SUCCESSFUL; +} + +static rtems_status_code collect_play(struct snd_buffer **buf) +{ + size_t s; + + return rtems_message_queue_receive( + play_q_done, + buf, + &s, + RTEMS_WAIT, + RTEMS_NO_TIMEOUT + ); +} + +static rtems_status_code submit_record(struct snd_buffer *buf) +{ + bsp_interrupt_vector_disable(MM_IRQ_AC97DMAW); + if (record_level == RECORD_Q_SIZE) { + bsp_interrupt_vector_enable(MM_IRQ_AC97DMAW); + return RTEMS_UNSATISFIED; + } + record_q[record_produce] = buf; + record_produce = (record_produce + 1) & RECORD_Q_MASK; + record_level++; + + if (record_level == 1) + record_start(buf); + + bsp_interrupt_vector_enable(MM_IRQ_AC97DMAW); + return RTEMS_SUCCESSFUL; +} + +static rtems_status_code collect_record(struct snd_buffer **buf) +{ + size_t s; + + return rtems_message_queue_receive( + record_q_done, + buf, + &s, + RTEMS_WAIT, + RTEMS_NO_TIMEOUT + ); +} + +#define CR_TIMEOUT 10 + +static int read_cr(unsigned int adr) +{ + rtems_status_code sc; + + MM_WRITE(MM_AC97_CRADDR, adr); + MM_WRITE(MM_AC97_CRCTL, AC97_CRCTL_RQEN); + sc = rtems_semaphore_obtain(cr_write_sem, RTEMS_WAIT, CR_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + return -1; + sc = rtems_semaphore_obtain(cr_read_sem, RTEMS_WAIT, CR_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + return -1; + return MM_READ(MM_AC97_CRDATAIN); +} + +static int write_cr(unsigned int adr, unsigned int val) +{ + rtems_status_code sc; + + MM_WRITE(MM_AC97_CRADDR, adr); + MM_WRITE(MM_AC97_CRDATAOUT, val); + MM_WRITE(MM_AC97_CRCTL, AC97_CRCTL_RQEN|AC97_CRCTL_WRITE); + sc = rtems_semaphore_obtain(cr_write_sem, RTEMS_WAIT, CR_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + return 0; + return 1; +} + +rtems_device_driver ac97_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + int codec_id; + + if (minor == 0) { + /* snd */ + return RTEMS_SUCCESSFUL; + } else { + /* mixer */ + codec_id = read_cr(0x00); + if ((codec_id != 0x0d50) && (codec_id != 0x6150)) { + printk("AC97 codec detection failed\n"); + return RTEMS_UNSATISFIED; + } + write_cr(0x02, 0x0000); /* master volume */ + write_cr(0x04, 0x0f0f); /* headphones volume */ + write_cr(0x18, 0x0000); /* PCM out volume */ + write_cr(0x1c, 0x0f0f); /* record gain */ + + write_cr(0x1a, 0x0505); /* record select: stereo mix */ + + return RTEMS_SUCCESSFUL; + } +} + +static rtems_status_code ioctl_read_channel(void *buf, + unsigned int chan, int mono) +{ + unsigned int *val = (unsigned int *)buf; + int mic_boost; + int codec; + int left, right; + + codec = read_cr(chan); + if (codec < 0) + return RTEMS_UNSATISFIED; + if (codec & 0x8000) { + /* muted */ + *val = 0; + return RTEMS_SUCCESSFUL; + } + if (mono) { + left = 100-(((codec & 0x1f) + 1)*100)/32; + mic_boost = (codec & (1 << 6)) >> 6; + *val = left | mic_boost << 8; + } else { + right = 100-(((codec & 0x1f) + 1)*100)/32; + left = 100-((((codec & 0x1f00) >> 8) + 1)*100)/32; + *val = left | (right << 8); + } + return RTEMS_SUCCESSFUL; +} + +static rtems_status_code ioctl_write_channel(void *buf, + unsigned int chan, int mono) +{ + unsigned int *val = (unsigned int *)buf; + int mic_boost; + int left, right; + int codec; + rtems_status_code sc; + + left = *val & 0xff; + left = (left*32)/100 - 1; + if (left < 0) + left = 0; + + if (mono) { + mic_boost = *val >> 8; + right = 31; + } else { + right = (*val >> 8) & 0xff; + right = (right*32)/100 - 1; + if (right < 0) + right = 0; + } + + if ((left == 0) && (right == 0)) + /* mute */ + codec = 0x8000; + else + codec = (31-left) | ((31-right) << 8); + + if (mono) { + if (mic_boost) + codec |= (1 << 6); + else + codec &= ~(1 << 6); + } + + if (!write_cr(chan, codec)) + sc = RTEMS_UNSATISFIED; + else + sc = RTEMS_SUCCESSFUL; + return sc; +} + +rtems_device_driver ac97_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_ioctl_args_t *args = arg; + rtems_status_code sc; + + args->ioctl_return = -1; + if(minor == 0) { + /* dsp */ + switch (args->command) { + case SOUND_SND_SUBMIT_PLAY: + return submit_play((struct snd_buffer *)args->buffer); + case SOUND_SND_COLLECT_PLAY: + return collect_play((struct snd_buffer **)args->buffer); + case SOUND_SND_SUBMIT_RECORD: + return submit_record((struct snd_buffer *)args->buffer); + case SOUND_SND_COLLECT_RECORD: + return collect_record((struct snd_buffer **)args->buffer); + default: + return RTEMS_UNSATISFIED; + } + } else { + /* mixer */ + switch (args->command) { + case SOUND_MIXER_READ(SOUND_MIXER_MIC): + sc = ioctl_read_channel(args->buffer, 0x0e, 1); + if(sc == RTEMS_SUCCESSFUL) + args->ioctl_return = 0; + return sc; + case SOUND_MIXER_READ(SOUND_MIXER_LINE): + sc = ioctl_read_channel(args->buffer, 0x10, 0); + if(sc == RTEMS_SUCCESSFUL) + args->ioctl_return = 0; + return sc; + case SOUND_MIXER_WRITE(SOUND_MIXER_MIC): + sc = ioctl_write_channel(args->buffer, 0x0e, 1); + if(sc == RTEMS_SUCCESSFUL) + args->ioctl_return = 0; + return sc; + case SOUND_MIXER_WRITE(SOUND_MIXER_LINE): + sc = ioctl_write_channel(args->buffer, 0x10, 0); + if(sc == RTEMS_SUCCESSFUL) + args->ioctl_return = 0; + return sc; + default: + return RTEMS_UNSATISFIED; + } + } +} diff --git a/bsps/lm32/shared/milkymist_buttons/buttons.c b/bsps/lm32/shared/milkymist_buttons/buttons.c new file mode 100644 index 0000000000..6e5673b838 --- /dev/null +++ b/bsps/lm32/shared/milkymist_buttons/buttons.c @@ -0,0 +1,130 @@ +/* buttons.c + * + * Buttons driver for the Milkymist One board + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * COPYRIGHT (c) 2011 Sebastien Bourdeauducq + */ + +#define RTEMS_STATUS_CHECKS_USE_PRINTK + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <rtems.h> +#include <rtems/status-checks.h> +#include <bsp.h> +#include <bsp/irq-generic.h> +#include <rtems/libio.h> +#include "../include/system_conf.h" +#include <bsp/milkymist_buttons.h> + +#define DEVICE_NAME "/dev/buttons" + +static rtems_id event_q; + +static void send_byte(char b) +{ + rtems_message_queue_send(event_q, &b, 1); +} + +static rtems_isr interrupt_handler(rtems_vector_number n) +{ + static unsigned int previous_keys; + unsigned int keys, pushed_keys, released_keys; + + keys = MM_READ(MM_GPIO_IN) & (GPIO_BTN1|GPIO_BTN2|GPIO_BTN3); + pushed_keys = keys & ~previous_keys; + released_keys = previous_keys & ~keys; + previous_keys = keys; + + if(pushed_keys & GPIO_BTN1) + send_byte('A'); + if(pushed_keys & GPIO_BTN2) + send_byte('B'); + if(pushed_keys & GPIO_BTN3) + send_byte('C'); + + if(released_keys & GPIO_BTN1) + send_byte('a'); + if(released_keys & GPIO_BTN2) + send_byte('b'); + if(released_keys & GPIO_BTN3) + send_byte('c'); + + lm32_interrupt_ack(1 << MM_IRQ_GPIO); +} + +rtems_device_driver buttons_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code sc; + rtems_isr_entry dummy; + + sc = rtems_io_register_name(DEVICE_NAME, major, 0); + RTEMS_CHECK_SC(sc, "create buttons device"); + + sc = rtems_message_queue_create( + rtems_build_name('B', 'T', 'N', 'Q'), + 24, + 1, + 0, + &event_q + ); + RTEMS_CHECK_SC(sc, "create buttons event queue"); + + rtems_interrupt_catch(interrupt_handler, MM_IRQ_GPIO, &dummy); + bsp_interrupt_vector_enable(MM_IRQ_GPIO); + MM_WRITE(MM_GPIO_INTEN, GPIO_BTN1|GPIO_BTN2|GPIO_BTN3); + + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver buttons_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + uint32_t count; + + rtems_message_queue_flush(event_q, &count); + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver buttons_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg; + rtems_status_code sc; + + if(rw_args->count < 1) { + rw_args->bytes_moved = 0; + return RTEMS_UNSATISFIED; + } + + sc = rtems_message_queue_receive( + event_q, + rw_args->buffer, + (size_t *)&rw_args->bytes_moved, + RTEMS_WAIT, + RTEMS_NO_TIMEOUT + ); + + if(sc == RTEMS_SUCCESSFUL) + return RTEMS_SUCCESSFUL; + else { + rw_args->bytes_moved = 0; + return RTEMS_UNSATISFIED; + } +} diff --git a/bsps/lm32/shared/milkymist_clock/ckinit.c b/bsps/lm32/shared/milkymist_clock/ckinit.c new file mode 100644 index 0000000000..c138309982 --- /dev/null +++ b/bsps/lm32/shared/milkymist_clock/ckinit.c @@ -0,0 +1,52 @@ +/* + * Clock device driver for Lattice Mico32 (lm32). + */ + +/* + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * COPYRIGHT (c) Yann Sionneau <yann.sionneau@telecom-sudparis.eu> (GSoC 2010) + * Telecom SudParis + */ + +#include <bsp.h> +#include <bsp/irq-generic.h> +#include "../include/system_conf.h" +#include "clock.h" +#include "bspopts.h" + +#if ON_SIMULATOR +#define CLOCK_DRIVER_USE_FAST_IDLE 1 +#endif + +#define Clock_driver_support_at_tick() \ + do { \ + lm32_interrupt_ack(1 << MM_IRQ_TIMER0); \ + } while (0) + +#define Clock_driver_support_install_isr(_new ) \ + do { \ + rtems_isr_entry _old; \ + rtems_interrupt_catch(_new, MM_IRQ_TIMER0, &_old); \ + } while (0) + +static void Clock_driver_support_initialize_hardware(void) +{ + MM_WRITE(MM_TIMER0_COMPARE, + (MM_READ(MM_FREQUENCY)/(1000000/rtems_configuration_get_microseconds_per_tick()))); + MM_WRITE(MM_TIMER0_COUNTER, 0); + MM_WRITE(MM_TIMER0_CONTROL, TIMER_ENABLE | TIMER_AUTORESTART); + bsp_interrupt_vector_enable(MM_IRQ_TIMER0); +} + +#define Clock_driver_support_shutdown_hardware() \ + do { \ + bsp_interrupt_vector_disable(MM_IRQ_TIMER0); \ + MM_WRITE(MM_TIMER0_CONTROL, 0); \ + } while (0) + +#define CLOCK_DRIVER_USE_DUMMY_TIMECOUNTER + +#include "../../../shared/dev/clock/clockimpl.h" diff --git a/bsps/lm32/shared/milkymist_clock/clock.h b/bsps/lm32/shared/milkymist_clock/clock.h new file mode 100644 index 0000000000..96dc2efbf6 --- /dev/null +++ b/bsps/lm32/shared/milkymist_clock/clock.h @@ -0,0 +1,22 @@ +/** + * @file + * @ingroup lm32_clock lm32_milkymist_shared + * @brief LatticeMico32 Timer (Clock) definitions + */ + +/* + * This file contains definitions for LatticeMico32 Timer (Clock) + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * COPYRIGHT (c) Yann Sionneau <yann.sionneau@telecom-sudparis.eu> (GSoC 2010) + * Telecom SudParis + */ + +#ifndef _BSPCLOCK_H +#define _BSPCLOCK_H + + +#endif /* _BSPCLOCK_H */ diff --git a/bsps/lm32/shared/milkymist_console/console.c b/bsps/lm32/shared/milkymist_console/console.c new file mode 100644 index 0000000000..de87233299 --- /dev/null +++ b/bsps/lm32/shared/milkymist_console/console.c @@ -0,0 +1,216 @@ +/* + * Console driver for Milkymist + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * COPYRIGHT (c) 2010 Sebastien Bourdeauducq + */ + +#include <unistd.h> +#include <termios.h> + +#include <rtems.h> +#include <rtems/bspIo.h> +#include <rtems/libio.h> +#include <rtems/console.h> +#include <rtems/termiostypes.h> +#include <bsp/irq-generic.h> + +#include "../include/system_conf.h" +#include "uart.h" + +BSP_output_char_function_type BSP_output_char = BSP_uart_polled_write; +BSP_polling_getchar_function_type BSP_poll_char = BSP_uart_polled_read; + +static struct rtems_termios_tty *tty; + +static int mmconsole_first_open(int major, int minor, void *arg) +{ + tty = ((rtems_libio_open_close_args_t *) arg)->iop->data1; + return rtems_termios_set_initial_baud(tty, UART_BAUD_RATE); +} + +static int mmconsole_last_close(int major, int minor, void *arg) +{ + return 0; +} + +static int mmconsole_set_attributes(int minor, const struct termios *t) +{ + int baud; + + switch (t->c_ospeed) { + case B0: + baud = 0; + break; + case B50: + baud = 50; + break; + case B75: + baud = 75; + break; + case B110: + baud = 110; + break; + case B134: + baud = 134; + break; + case B150: + baud = 150; + break; + case B200: + baud = 200; + break; + case B300: + baud = 300; + break; + case B600: + baud = 600; + break; + case B1200: + baud = 1200; + break; + case B1800: + baud = 1800; + break; + case B2400: + baud = 2400; + break; + case B4800: + baud = 4800; + break; + case B9600: + baud = 9600; + break; + case B19200: + baud = 19200; + break; + case B38400: + baud = 38400; + break; + case B57600: + baud = 57600; + break; + case B115200: + baud = 115200; + break; + case B230400: + baud = 230400; + break; + case B460800: + baud = 460800; + break; + default: + baud = -1; + break; + } + + if (baud > 0) + MM_WRITE(MM_UART_DIV, MM_READ(MM_FREQUENCY)/baud/16); + + return 0; +} + +static ssize_t mmconsole_write(int minor, const char *buf, size_t n) +{ + if (n > 0) { + MM_WRITE(MM_UART_RXTX, *buf); + } + + return 0; +} + +static rtems_isr mmconsole_interrupt(rtems_vector_number n) +{ + char c; + while (MM_READ(MM_UART_STAT) & UART_STAT_RX_EVT) { + c = MM_READ(MM_UART_RXTX); + MM_WRITE(MM_UART_STAT, UART_STAT_RX_EVT); + rtems_termios_enqueue_raw_characters(tty, &c, 1); + } + if (MM_READ(MM_UART_STAT) & UART_STAT_TX_EVT) { + MM_WRITE(MM_UART_STAT, UART_STAT_TX_EVT); + rtems_termios_dequeue_characters(tty, 1); + } + lm32_interrupt_ack(1 << MM_IRQ_UART); +} + +static const rtems_termios_callbacks mmconsole_callbacks = { + .firstOpen = mmconsole_first_open, + .lastClose = mmconsole_last_close, + .pollRead = NULL, + .write = mmconsole_write, + .setAttributes = mmconsole_set_attributes, + .stopRemoteTx = NULL, + .startRemoteTx = NULL, + .outputUsesInterrupts = TERMIOS_IRQ_DRIVEN +}; + +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code status; + rtems_isr_entry dummy; + + rtems_termios_initialize(); + + status = rtems_io_register_name("/dev/console", major, 0); + if (status != RTEMS_SUCCESSFUL) + rtems_fatal_error_occurred(status); + + rtems_interrupt_catch(mmconsole_interrupt, MM_IRQ_UART, &dummy); + bsp_interrupt_vector_enable(MM_IRQ_UART); + MM_WRITE(MM_UART_CTRL, UART_CTRL_RX_INT|UART_CTRL_TX_INT); + + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver console_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + return rtems_termios_open(major, minor, arg, &mmconsole_callbacks); +} + +rtems_device_driver console_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + return rtems_termios_close(arg); +} + +rtems_device_driver console_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + return rtems_termios_read(arg); +} + +rtems_device_driver console_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + return rtems_termios_write(arg); +} + +rtems_device_driver console_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + return rtems_termios_ioctl(arg); +} diff --git a/bsps/lm32/shared/milkymist_console/uart.c b/bsps/lm32/shared/milkymist_console/uart.c new file mode 100644 index 0000000000..3ca1284954 --- /dev/null +++ b/bsps/lm32/shared/milkymist_console/uart.c @@ -0,0 +1,46 @@ +/* + * Driver for Milkymist UART + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * COPYRIGHT (c) 2010 Sebastien Bourdeauducq + * COPYRIGHT (c) Yann Sionneau <yann.sionneau@telecom-sudparis.eu> (GSoC 2010) + * Telecom SudParis + */ + +#include <rtems.h> +#include <rtems/libio.h> +#include "../include/system_conf.h" +#include "uart.h" + +void BSP_uart_init(int baud) +{ + MM_WRITE(MM_UART_DIV, MM_READ(MM_FREQUENCY)/baud/16); +} + +void BSP_uart_polled_write(char ch) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable(level); + while(!(MM_READ(MM_UART_STAT) & UART_STAT_THRE)); + MM_WRITE(MM_UART_RXTX, ch); + while(!(MM_READ(MM_UART_STAT) & UART_STAT_THRE)); + rtems_interrupt_enable(level); +} + +int BSP_uart_polled_read(void) +{ + char r; + rtems_interrupt_level level; + + rtems_interrupt_disable(level); + while(!(MM_READ(MM_UART_STAT) & UART_STAT_RX_EVT)); + r = MM_READ(MM_UART_RXTX); + MM_WRITE(MM_UART_STAT, UART_STAT_RX_EVT); + rtems_interrupt_enable(level); + + return r; +} diff --git a/bsps/lm32/shared/milkymist_console/uart.h b/bsps/lm32/shared/milkymist_console/uart.h new file mode 100644 index 0000000000..5dbb8cd859 --- /dev/null +++ b/bsps/lm32/shared/milkymist_console/uart.h @@ -0,0 +1,22 @@ +/** + * @file + * @ingroup lm32_uart lm32_milkymist_shared + * @brief Milkymist UART definitions + */ + +/* + * This file contains definitions for the Milkymist UART + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#ifndef _BSPUART_H +#define _BSPUART_H + +void BSP_uart_init(int baud); +void BSP_uart_polled_write(char ch); +int BSP_uart_polled_read(void); + +#endif /* _BSPUART_H */ diff --git a/bsps/lm32/shared/milkymist_dmx/dmx.c b/bsps/lm32/shared/milkymist_dmx/dmx.c new file mode 100644 index 0000000000..d2c25685ec --- /dev/null +++ b/bsps/lm32/shared/milkymist_dmx/dmx.c @@ -0,0 +1,128 @@ +/* dmx.c + * + * Milkymist DMX512 driver for RTEMS + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * COPYRIGHT (c) 2010, 2011 Sebastien Bourdeauducq + */ + +#define RTEMS_STATUS_CHECKS_USE_PRINTK + +#include <stdlib.h> +#include <sys/types.h> +#include <rtems.h> +#include <rtems/status-checks.h> +#include <bsp.h> +#include <rtems/libio.h> +#include "../include/system_conf.h" +#include <bsp/milkymist_dmx.h> + +#define IN_DEVICE_NAME "/dev/dmx_in" +#define OUT_DEVICE_NAME "/dev/dmx_out" + +rtems_device_driver dmx_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code sc; + + sc = rtems_io_register_name(IN_DEVICE_NAME, major, 0); + RTEMS_CHECK_SC(sc, "create DMX input device"); + + sc = rtems_io_register_name(OUT_DEVICE_NAME, major, 1); + RTEMS_CHECK_SC(sc, "create DMX output device"); + + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver dmx_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg; + int len; + unsigned int i; + unsigned char *values = (unsigned char *)rw_args->buffer; + + len = 512 - rw_args->offset; + if (len < 0) { + rw_args->bytes_moved = 0; + return RTEMS_SUCCESSFUL; + } + if (len > rw_args->count) + len = rw_args->count; + + if (minor == 0) { + for (i=0;i<len;i++) + values[i] = MM_READ(MM_DMX_RX((unsigned int)rw_args->offset+i)); + } else { + for (i=0;i<len;i++) + values[i] = MM_READ(MM_DMX_TX((unsigned int)rw_args->offset+i)); + } + + rw_args->bytes_moved = len; + + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver dmx_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg; + int len; + unsigned int i; + unsigned char *values = (unsigned char *)rw_args->buffer; + + if (minor == 0) { + rw_args->bytes_moved = 0; + return RTEMS_UNSATISFIED; + } + + len = 512 - rw_args->offset; + if (len < 0) { + rw_args->bytes_moved = 0; + return RTEMS_SUCCESSFUL; + } + if (len > rw_args->count) + len = rw_args->count; + + for (i=0;i<len;i++) + MM_WRITE(MM_DMX_TX((unsigned int)rw_args->offset+i), values[i]); + + rw_args->bytes_moved = len; + + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver dmx_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_ioctl_args_t *args = arg; + + switch (args->command) { + case DMX_SET_THRU: + args->ioctl_return = 0; + MM_WRITE(MM_DMX_THRU, (unsigned int)args->buffer); + return RTEMS_SUCCESSFUL; + case DMX_GET_THRU: + args->ioctl_return = 0; + *((unsigned int *)args->buffer) = MM_READ(MM_DMX_THRU); + return RTEMS_SUCCESSFUL; + default: + args->ioctl_return = -1; + return RTEMS_UNSATISFIED; + } +} diff --git a/bsps/lm32/shared/milkymist_flash/flash.c b/bsps/lm32/shared/milkymist_flash/flash.c new file mode 100644 index 0000000000..aeb0f6416d --- /dev/null +++ b/bsps/lm32/shared/milkymist_flash/flash.c @@ -0,0 +1,193 @@ +/* flash.c + * + * Milkymist flash driver for RTEMS + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * COPYRIGHT (c) 2010, 2011 Sebastien Bourdeauducq + */ + +#define RTEMS_STATUS_CHECKS_USE_PRINTK + +#include <rtems.h> +#include <stdio.h> +#include <bsp.h> +#include <string.h> +#include <rtems/libio.h> +#include <rtems/status-checks.h> +#include "../include/system_conf.h" +#include <bsp/milkymist_flash.h> + +static struct flash_partition partitions[FLASH_PARTITION_COUNT] + = FLASH_PARTITIONS; + +static rtems_id flash_lock; + +rtems_device_driver flash_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code sc; + int i; + char devname[16]; + + for (i=0;i<FLASH_PARTITION_COUNT;i++) { + sprintf(devname, "/dev/flash%d", i+1); + sc = rtems_io_register_name(devname, major, i); + RTEMS_CHECK_SC(sc, "Create flash device"); + } + + sc = rtems_semaphore_create( + rtems_build_name('F', 'L', 'S', 'H'), + 1, + RTEMS_SIMPLE_BINARY_SEMAPHORE, + 0, + &flash_lock + ); + RTEMS_CHECK_SC(sc, "create semaphore"); + + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver flash_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg; + void *startaddr; + int len; + + if (minor >= FLASH_PARTITION_COUNT) + return RTEMS_UNSATISFIED; + + startaddr = (void *)(partitions[minor].start_address + + (unsigned int)rw_args->offset); + len = partitions[minor].length - rw_args->offset; + if (len > rw_args->count) + len = rw_args->count; + if (len <= 0) { + rw_args->bytes_moved = 0; + return RTEMS_SUCCESSFUL; + } + + rtems_semaphore_obtain(flash_lock, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + memcpy(rw_args->buffer, startaddr, len); + rtems_semaphore_release(flash_lock); + + rw_args->bytes_moved = len; + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver flash_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg; + volatile unsigned short *startaddr; + unsigned short *srcdata; + int len; + int this_time; + int remaining; + int i; + + if (minor >= FLASH_PARTITION_COUNT) + return RTEMS_UNSATISFIED; + + startaddr = (unsigned short *)(partitions[minor].start_address + + (unsigned int)rw_args->offset); + len = partitions[minor].length - rw_args->offset; + if (len > rw_args->count) + len = rw_args->count; + if (len <= 2) { + rw_args->bytes_moved = 0; + return RTEMS_SUCCESSFUL; + } + len /= 2; + srcdata = (unsigned short *)rw_args->buffer; + remaining = len; + + rtems_semaphore_obtain(flash_lock, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + while (remaining > 0) { + this_time = remaining; + if (this_time > 256) + this_time = 256; + /* Issue "Buffered Programming Setup" command + * and wait for buffer available. + */ + do { + *startaddr = 0x00e8; + } while (!(*startaddr & 0x0080)); + /* Load word count */ + *startaddr = this_time-1; + /* Fill buffer */ + for(i=0;i<this_time;i++) + startaddr[i] = srcdata[i]; + /* Issue "Buffer Programming Confirm" command */ + *startaddr = 0x00d0; + while (!(*startaddr & 0x0080)); /* read status register, wait for ready */ + *startaddr = 0x0050; /* clear status register */ + /* update state */ + startaddr += this_time; + srcdata += this_time; + remaining -= this_time; + } + *startaddr = 0x00ff; /* back to read array mode */ + rtems_semaphore_release(flash_lock); + + rw_args->bytes_moved = 2*len; + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver flash_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_ioctl_args_t *args = arg; + unsigned int eraseaddr_i; + volatile unsigned short *eraseaddr; + + if (minor >= FLASH_PARTITION_COUNT) { + args->ioctl_return = -1; + return RTEMS_UNSATISFIED; + } + + switch (args->command) { + case FLASH_GET_SIZE: + *((unsigned int *)args->buffer) = partitions[minor].length; + break; + case FLASH_GET_BLOCKSIZE: + *((unsigned int *)args->buffer) = 128*1024; + break; + case FLASH_ERASE_BLOCK: + eraseaddr_i = (unsigned int)args->buffer; + if (eraseaddr_i >= partitions[minor].length) { + args->ioctl_return = -1; + return RTEMS_UNSATISFIED; + } + eraseaddr_i = eraseaddr_i + partitions[minor].start_address; + eraseaddr = (unsigned short *)eraseaddr_i; + rtems_semaphore_obtain(flash_lock, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + *eraseaddr = 0x0020; /* erase */ + *eraseaddr = 0x00d0; + while(!(*eraseaddr & 0x0080)); /* read status register, wait for ready */ + *eraseaddr = 0x0050; /* clear status register */ + *eraseaddr = 0x00ff; /* back to read array mode */ + rtems_semaphore_release(flash_lock); + break; + default: + args->ioctl_return = -1; + return RTEMS_UNSATISFIED; + } + args->ioctl_return = 0; + return RTEMS_SUCCESSFUL; +} diff --git a/bsps/lm32/shared/milkymist_framebuffer/framebuffer.c b/bsps/lm32/shared/milkymist_framebuffer/framebuffer.c new file mode 100644 index 0000000000..8bbd44ae0c --- /dev/null +++ b/bsps/lm32/shared/milkymist_framebuffer/framebuffer.c @@ -0,0 +1,251 @@ +/* framebuffer.c + * + * This file is the framebuffer driver for the Milkymist VGA IP-core + * This VGA Core is a part of Milkymist System-on-Chip + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * COPYRIGHT (c) Yann Sionneau <yann.sionneau@telecom-sudparis.eu> (GSoC 2010) + * Telecom SudParis + * Copyright (c) 2011 Sebastien Bourdeauducq + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <rtems.h> +#include <bsp.h> +#include "../include/system_conf.h" +#include <rtems/libio.h> +#include <rtems/fb.h> +#include <rtems/framebuffer.h> +#include <rtems/bspIo.h> + +static unsigned short int framebufferA[1024*768] + __attribute__((aligned(32))); +static unsigned short int framebufferB[1024*768] + __attribute__((aligned(32))); +static unsigned short int framebufferC[1024*768] + __attribute__((aligned(32))); + +static unsigned short int *frontbuffer; +static unsigned short int *backbuffer; +static unsigned short int *lastbuffer; + +static struct fb_var_screeninfo fb_var = { + .xres = 640, + .yres = 480, + .bits_per_pixel = 16 +}; + +static struct fb_fix_screeninfo fb_fix = { + .smem_len = 1024 * 768 * 2, + .type = FB_TYPE_VGA_PLANES, + .visual = FB_VISUAL_TRUECOLOR, + .line_length = 80 +}; + +static int get_fix_screen_info( struct fb_fix_screeninfo *info ) +{ + *info = fb_fix; + return 0; +} + +static int get_var_screen_info( struct fb_var_screeninfo *info ) +{ + *info = fb_var; + return 0; +} + +static void init_buffers(void) +{ + frontbuffer = framebufferA; + backbuffer = framebufferB; + lastbuffer = framebufferC; +} + +static void swap_buffers(void) +{ + unsigned short int *p; + + /* Make sure last buffer swap has been executed */ + while (MM_READ(MM_VGA_BASEADDRESS_ACT) != MM_READ(MM_VGA_BASEADDRESS)); + + p = frontbuffer; + frontbuffer = backbuffer; + backbuffer = lastbuffer; + lastbuffer = p; + + fb_fix.smem_start = (volatile char *)backbuffer; + MM_WRITE(MM_VGA_BASEADDRESS, (unsigned int)frontbuffer); +} + +static void set_video_mode(int mode) +{ + int hres, vres; + + MM_WRITE(MM_VGA_RESET, VGA_RESET); + hres = vres = 0; + switch(mode) { + case 0: // 640x480, pixel clock: 25MHz + hres = 640; + vres = 480; + MM_WRITE(MM_VGA_HSYNC_START, 656); + MM_WRITE(MM_VGA_HSYNC_END, 752); + MM_WRITE(MM_VGA_HSCAN, 799); + MM_WRITE(MM_VGA_VSYNC_START, 492); + MM_WRITE(MM_VGA_VSYNC_END, 494); + MM_WRITE(MM_VGA_VSCAN, 524); + MM_WRITE(MM_VGA_CLKSEL, 0); + break; + case 1: // 800x600, pixel clock: 50MHz + hres = 800; + vres = 600; + MM_WRITE(MM_VGA_HSYNC_START, 848); + MM_WRITE(MM_VGA_HSYNC_END, 976); + MM_WRITE(MM_VGA_HSCAN, 1040); + MM_WRITE(MM_VGA_VSYNC_START, 636); + MM_WRITE(MM_VGA_VSYNC_END, 642); + MM_WRITE(MM_VGA_VSCAN, 665); + MM_WRITE(MM_VGA_CLKSEL, 1); + break; + case 2: // 1024x768, pixel clock: 65MHz + hres = 1024; + vres = 768; + MM_WRITE(MM_VGA_HSYNC_START, 1048); + MM_WRITE(MM_VGA_HSYNC_END, 1184); + MM_WRITE(MM_VGA_HSCAN, 1344); + MM_WRITE(MM_VGA_VSYNC_START, 772); + MM_WRITE(MM_VGA_VSYNC_END, 778); + MM_WRITE(MM_VGA_VSCAN, 807); + MM_WRITE(MM_VGA_CLKSEL, 2); + break; + } + if((hres != 0) && (vres != 0)) { + MM_WRITE(MM_VGA_HRES, hres); + MM_WRITE(MM_VGA_VRES, vres); + fb_var.xres = hres; + fb_var.yres = vres; + memset(framebufferA, 0, hres*vres*2); + memset(framebufferB, 0, hres*vres*2); + memset(framebufferC, 0, hres*vres*2); + MM_WRITE(MM_VGA_BURST_COUNT, hres*vres/16); + MM_WRITE(MM_VGA_RESET, 0); + } /* otherwise, leave the VGA controller in reset */ +} + +rtems_device_driver frame_buffer_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code status; + + init_buffers(); + fb_fix.smem_start = (volatile char *)frontbuffer; + MM_WRITE(MM_VGA_BASEADDRESS, (unsigned int)frontbuffer); + + status = rtems_io_register_name(FRAMEBUFFER_DEVICE_0_NAME, major, 0); + if (status != RTEMS_SUCCESSFUL) { + printk("Error registering frame buffer device!\n"); + rtems_fatal_error_occurred( status ); + } + + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver frame_buffer_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver frame_buffer_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver frame_buffer_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg; + rw_args->bytes_moved = ((rw_args->offset + rw_args->count) > fb_fix.smem_len) + ? (fb_fix.smem_len - rw_args->offset) : rw_args->count; + memcpy(rw_args->buffer, (const void *)(fb_fix.smem_start + rw_args->offset), + rw_args->bytes_moved); + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver frame_buffer_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg; + rw_args->bytes_moved = ((rw_args->offset + rw_args->count) > fb_fix.smem_len) + ? (fb_fix.smem_len - rw_args->offset) : rw_args->count; + memcpy((void *)(fb_fix.smem_start + rw_args->offset), rw_args->buffer, + rw_args->bytes_moved); + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver frame_buffer_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_ioctl_args_t *args = arg; + + switch (args->command) { + case FBIOGET_FSCREENINFO: + args->ioctl_return = + get_fix_screen_info((struct fb_fix_screeninfo *)args->buffer); + return RTEMS_SUCCESSFUL; + case FBIOGET_VSCREENINFO: + args->ioctl_return = + get_var_screen_info((struct fb_var_screeninfo *)args->buffer); + return RTEMS_SUCCESSFUL; + case FBIOSWAPBUFFERS: + swap_buffers(); + args->ioctl_return = 0; + return RTEMS_SUCCESSFUL; + case FBIOSETBUFFERMODE: + args->ioctl_return = 0; + switch ((unsigned int)args->buffer) { + case FB_SINGLE_BUFFERED: + init_buffers(); + fb_fix.smem_start = (volatile char *)frontbuffer; + MM_WRITE(MM_VGA_BASEADDRESS, (unsigned int)frontbuffer); + return RTEMS_SUCCESSFUL; + case FB_TRIPLE_BUFFERED: + fb_fix.smem_start = (volatile char *)backbuffer; + return RTEMS_SUCCESSFUL; + default: + return RTEMS_UNSATISFIED; + } + case FBIOSETVIDEOMODE: + set_video_mode((int)args->buffer); + return RTEMS_SUCCESSFUL; + default: + args->ioctl_return = -1; + return RTEMS_UNSATISFIED; + } +} + diff --git a/bsps/lm32/shared/milkymist_gpio/gpio.c b/bsps/lm32/shared/milkymist_gpio/gpio.c new file mode 100644 index 0000000000..b559c9b4bc --- /dev/null +++ b/bsps/lm32/shared/milkymist_gpio/gpio.c @@ -0,0 +1,119 @@ +/* gpio.c + * + * GPIO driver for the Milkymist One board + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * COPYRIGHT (c) 2010, 2011 Sebastien Bourdeauducq + * COPYRIGHT (c) Yann Sionneau <yann.sionneau@telecom-sudparis.eu> (GSoC 2010) + * Telecom SudParis + */ + +#define RTEMS_STATUS_CHECKS_USE_PRINTK + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <rtems.h> +#include <rtems/status-checks.h> +#include <bsp.h> +#include <rtems/libio.h> +#include "../include/system_conf.h" +#include <bsp/milkymist_gpio.h> + +struct milkymist_gpio { + char *name; + unsigned int mask; + bool readonly; +}; + +static const struct milkymist_gpio gpio[] = { + { + .name = "/dev/led1", + .mask = GPIO_LED1, + .readonly = false + }, + { + .name = "/dev/led2", + .mask = GPIO_LED2, + .readonly = false + }, +}; + +rtems_device_driver gpio_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code sc; + int i; + + for (i=0;i<sizeof(gpio)/sizeof(struct milkymist_gpio);i++) { + sc = rtems_io_register_name(gpio[i].name, major, i); + RTEMS_CHECK_SC(sc, "create GPIO device"); + } + + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver gpio_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + unsigned int data; + + rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg; + + if (rw_args->offset > 0) { + rw_args->bytes_moved = 0; + return RTEMS_SUCCESSFUL; + } + + rw_args->bytes_moved = 1; + + if (gpio[minor].readonly) + data = MM_READ(MM_GPIO_IN); + else + data = MM_READ(MM_GPIO_OUT); + + if (data & gpio[minor].mask) + *(uint8_t *)rw_args->buffer = '1'; + else + *(uint8_t *)rw_args->buffer = '0'; + + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver gpio_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg; + + if (gpio[minor].readonly) { + rw_args->bytes_moved = 0; + return RTEMS_UNSATISFIED; + } + + if (rw_args->offset > 0) { + rw_args->bytes_moved = 0; + return RTEMS_SUCCESSFUL; + } + + rw_args->bytes_moved = 1; + + if (*(uint8_t *)rw_args->buffer == '1') + MM_WRITE(MM_GPIO_OUT, MM_READ(MM_GPIO_OUT)|gpio[minor].mask); + else + MM_WRITE(MM_GPIO_OUT, MM_READ(MM_GPIO_OUT) & ~gpio[minor].mask); + + return RTEMS_SUCCESSFUL; +} diff --git a/bsps/lm32/shared/milkymist_ir/ir.c b/bsps/lm32/shared/milkymist_ir/ir.c new file mode 100644 index 0000000000..5c5a195a39 --- /dev/null +++ b/bsps/lm32/shared/milkymist_ir/ir.c @@ -0,0 +1,104 @@ +/* ir.c + * + * Milkymist RC5 IR driver for RTEMS + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * COPYRIGHT (c) 2010, 2011 Sebastien Bourdeauducq + */ + +#define RTEMS_STATUS_CHECKS_USE_PRINTK + +#include <stdlib.h> +#include <sys/types.h> +#include <rtems.h> +#include <rtems/status-checks.h> +#include <bsp.h> +#include <bsp/irq-generic.h> +#include <rtems/libio.h> +#include "../include/system_conf.h" +#include <bsp/milkymist_ir.h> + +#define DEVICE_NAME "/dev/ir" + +static rtems_id ir_q; + +static rtems_isr interrupt_handler(rtems_vector_number n) +{ + unsigned short int msg; + + lm32_interrupt_ack(1 << MM_IRQ_IR); + msg = MM_READ(MM_IR_RX); + rtems_message_queue_send(ir_q, &msg, 2); +} + +rtems_device_driver ir_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code sc; + rtems_isr_entry dummy; + + sc = rtems_io_register_name(DEVICE_NAME, major, 0); + RTEMS_CHECK_SC(sc, "create IR input device"); + + sc = rtems_message_queue_create( + rtems_build_name('R', 'C', '5', 'Q'), + 64, + 2, + 0, + &ir_q + ); + RTEMS_CHECK_SC(sc, "create IR queue"); + + rtems_interrupt_catch(interrupt_handler, MM_IRQ_IR, &dummy); + bsp_interrupt_vector_enable(MM_IRQ_IR); + + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver ir_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + uint32_t count; + + rtems_message_queue_flush(ir_q, &count); + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver ir_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg; + rtems_status_code sc; + + if (rw_args->count < 2) { + rw_args->bytes_moved = 0; + return RTEMS_UNSATISFIED; + } + + sc = rtems_message_queue_receive( + ir_q, + rw_args->buffer, + (size_t *)&rw_args->bytes_moved, + RTEMS_WAIT, + RTEMS_NO_TIMEOUT + ); + + if(sc == RTEMS_SUCCESSFUL) + return RTEMS_SUCCESSFUL; + else { + rw_args->bytes_moved = 0; + return RTEMS_UNSATISFIED; + } +} diff --git a/bsps/lm32/shared/milkymist_memcard/memcard.c b/bsps/lm32/shared/milkymist_memcard/memcard.c new file mode 100644 index 0000000000..3e56303292 --- /dev/null +++ b/bsps/lm32/shared/milkymist_memcard/memcard.c @@ -0,0 +1,370 @@ +/* memcard.c + * + * Milkymist memory card driver for RTEMS + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * COPYRIGHT (c) 2010, 2011 Sebastien Bourdeauducq + */ + +#define RTEMS_STATUS_CHECKS_USE_PRINTK + +#include <rtems.h> +#include <rtems/libio.h> +#include <rtems/diskdevs.h> +#include <rtems/blkdev.h> +#include <rtems/status-checks.h> +#include <errno.h> +#include <bsp.h> +#include "../include/system_conf.h" +#include <bsp/milkymist_memcard.h> + +//#define MEMCARD_DEBUG + +#define BLOCK_SIZE 512 + +static void memcard_start_cmd_tx(void) +{ + MM_WRITE(MM_MEMCARD_ENABLE, MEMCARD_ENABLE_CMD_TX); +} + +static void memcard_start_cmd_rx(void) +{ + MM_WRITE(MM_MEMCARD_PENDING, MEMCARD_PENDING_CMD_RX); + MM_WRITE(MM_MEMCARD_START, MEMCARD_START_CMD_RX); + MM_WRITE(MM_MEMCARD_ENABLE, MEMCARD_ENABLE_CMD_RX); +} + +static void memcard_start_cmd_dat_rx(void) +{ + MM_WRITE(MM_MEMCARD_PENDING, MEMCARD_PENDING_CMD_RX|MEMCARD_PENDING_DAT_RX); + MM_WRITE(MM_MEMCARD_START, MEMCARD_START_CMD_RX|MEMCARD_START_DAT_RX); + MM_WRITE(MM_MEMCARD_ENABLE, MEMCARD_ENABLE_CMD_RX|MEMCARD_ENABLE_DAT_RX); +} + +static void memcard_send_command(unsigned char cmd, unsigned int arg) +{ + unsigned char packet[6]; + int a; + int i; + unsigned char data; + unsigned char crc; + + packet[0] = cmd | 0x40; + packet[1] = ((arg >> 24) & 0xff); + packet[2] = ((arg >> 16) & 0xff); + packet[3] = ((arg >> 8) & 0xff); + packet[4] = (arg & 0xff); + + crc = 0; + for(a=0;a<5;a++) { + data = packet[a]; + for(i=0;i<8;i++) { + crc <<= 1; + if((data & 0x80) ^ (crc & 0x80)) + crc ^= 0x09; + data <<= 1; + } + } + crc = (crc<<1) | 1; + + packet[5] = crc; + +#ifdef MEMCARD_DEBUG + printk(">> %02x %02x %02x %02x %02x %02x\n", + packet[0], packet[1], packet[2], packet[3], packet[4], packet[5]); +#endif + + for(i=0;i<6;i++) { + MM_WRITE(MM_MEMCARD_CMD, packet[i]); + while(MM_READ(MM_MEMCARD_PENDING) & MEMCARD_PENDING_CMD_TX); + } +} + +static void memcard_send_dummy(void) +{ + MM_WRITE(MM_MEMCARD_CMD, 0xff); + while(MM_READ(MM_MEMCARD_PENDING) & MEMCARD_PENDING_CMD_TX); +} + +static bool memcard_receive_command(unsigned char *buffer, int len) +{ + int i; + int timeout; + + for(i=0;i<len;i++) { + timeout = 2000000; + while(!(MM_READ(MM_MEMCARD_PENDING) & MEMCARD_PENDING_CMD_RX)) { + timeout--; + if(timeout == 0) { + #ifdef MEMCARD_DEBUG + printk("Command receive timeout\n"); + #endif + return false; + } + } + buffer[i] = MM_READ(MM_MEMCARD_CMD); + MM_WRITE(MM_MEMCARD_PENDING, MEMCARD_PENDING_CMD_RX); + } + + while(!(MM_READ(MM_MEMCARD_PENDING) & MEMCARD_PENDING_CMD_RX)); + + #ifdef MEMCARD_DEBUG + printk("<< "); + for(i=0;i<len;i++) + printk("%02x ", buffer[i]); + printk("\n"); + #endif + + return true; +} + +static bool memcard_receive_command_data(unsigned char *command, + unsigned int *data) +{ + int i, j; + int timeout; + + i = 0; + j = 0; + while(j < 128) { + timeout = 2000000; + while(!(MM_READ(MM_MEMCARD_PENDING) & + (MEMCARD_PENDING_CMD_RX|MEMCARD_PENDING_DAT_RX))) { + timeout--; + if(timeout == 0) { + #ifdef MEMCARD_DEBUG + printk("Command receive timeout\n"); + #endif + return false; + } + } + if(MM_READ(MM_MEMCARD_PENDING) & MEMCARD_PENDING_CMD_RX) { + command[i++] = MM_READ(MM_MEMCARD_CMD); + MM_WRITE(MM_MEMCARD_PENDING, MEMCARD_PENDING_CMD_RX); + if(i == 6) + /* disable command RX */ + MM_WRITE(MM_MEMCARD_ENABLE, MEMCARD_ENABLE_DAT_RX); + } + if(MM_READ(MM_MEMCARD_PENDING) & MEMCARD_PENDING_DAT_RX) { + data[j++] = MM_READ(MM_MEMCARD_DAT); + MM_WRITE(MM_MEMCARD_PENDING, MEMCARD_PENDING_DAT_RX); + } + } + + /* Get CRC (ignored) */ + for(i=0;i<2;i++) { + while(!(MM_READ(MM_MEMCARD_PENDING) & MEMCARD_PENDING_DAT_RX)); + #ifdef MEMCARD_DEBUG + printk("CRC: %08x\n", MM_READ(MM_MEMCARD_DAT)); + #endif + MM_WRITE(MM_MEMCARD_PENDING, MEMCARD_PENDING_DAT_RX); + } + + while(!(MM_READ(MM_MEMCARD_PENDING) & MEMCARD_PENDING_DAT_RX)); + + #ifdef MEMCARD_DEBUG + printk("<< %02x %02x %02x %02x %02x %02x\n", + command[0], command[1], command[2], command[3], command[4], command[5]); + #endif + + //for(i=0;i<128;i++) + // printk("%08x ", data[i]); + //printk("\n"); + + return true; +} + +static unsigned int block_count; + +static int memcard_disk_block_read(rtems_blkdev_request *r) +{ + unsigned char b[6]; + unsigned int i, nblocks; + unsigned int block; + + block = RTEMS_BLKDEV_START_BLOCK(r); + nblocks = r->bufnum; + + for(i=0;i<nblocks;i++) { + /* CMD17 - read block */ + memcard_start_cmd_tx(); + memcard_send_command(17, (block+i)*BLOCK_SIZE); + memcard_start_cmd_dat_rx(); + if(!memcard_receive_command_data(b, (unsigned int *)r->bufs[i].buffer)) + return -RTEMS_IO_ERROR; + } + + rtems_blkdev_request_done(r, RTEMS_SUCCESSFUL); + + return 0; +} + +static int memcard_disk_block_write(rtems_blkdev_request *r) +{ + rtems_blkdev_request_done(r, RTEMS_IO_ERROR); + + return 0; +} + +static rtems_status_code memcard_init(void) +{ + unsigned char b[17]; + unsigned int rca; + unsigned int block_shift; + unsigned int c_size; + unsigned int c_size_mult; + + MM_WRITE(MM_MEMCARD_CLK2XDIV, 250); + + /* CMD0 */ + memcard_start_cmd_tx(); + memcard_send_command(0, 0); + + memcard_send_dummy(); + + /* CMD8 */ + memcard_send_command(8, 0x1aa); + memcard_start_cmd_rx(); + if(!memcard_receive_command(b, 6)) return RTEMS_IO_ERROR; + + /* ACMD41 - initialize */ + while(1) { + memcard_start_cmd_tx(); + memcard_send_command(55, 0); + memcard_start_cmd_rx(); + if(!memcard_receive_command(b, 6)) return RTEMS_IO_ERROR; + memcard_start_cmd_tx(); + memcard_send_command(41, 0x00300000); + memcard_start_cmd_rx(); + if(!memcard_receive_command(b, 6)) return RTEMS_IO_ERROR; + if(b[1] & 0x80) break; + #ifdef MEMCARD_DEBUG + printk("Card is busy, retrying\n"); + #endif + } + + /* CMD2 - get CID */ + memcard_start_cmd_tx(); + memcard_send_command(2, 0); + memcard_start_cmd_rx(); + if(!memcard_receive_command(b, 17)) return RTEMS_IO_ERROR; + + /* CMD3 - get RCA */ + memcard_start_cmd_tx(); + memcard_send_command(3, 0); + memcard_start_cmd_rx(); + if(!memcard_receive_command(b, 6)) return RTEMS_IO_ERROR; + rca = (((unsigned int)b[1]) << 8)|((unsigned int)b[2]); + #ifdef MEMCARD_DEBUG + printk("RCA: %04x\n", rca); + #endif + + /* CMD9 - get CSD */ + memcard_start_cmd_tx(); + memcard_send_command(9, rca << 16); + memcard_start_cmd_rx(); + if(!memcard_receive_command(b, 17)) return RTEMS_IO_ERROR; + + if(((b)[0] >> 6) != 0) + return RTEMS_IO_ERROR; + + block_shift = ((unsigned int)(b)[5] & 0xf); + c_size = ((((unsigned int)(b)[6] & 0x3) << 10) + + (((unsigned int)(b)[7]) << 2) + + ((((unsigned int)(b)[8]) >> 6) & 0x3)); + c_size_mult = ((((b)[9] & 0x3) << 1) + (((b)[10] >> 7) & 0x1)); + block_count = (c_size + 1) * (1U << (c_size_mult + 2)); + + /* convert to 512-byte blocks for the sake of simplicity */ + if(block_shift < 9) + return RTEMS_IO_ERROR; + block_count <<= block_shift - 9; + + /* CMD7 - select card */ + memcard_start_cmd_tx(); + memcard_send_command(7, rca << 16); + memcard_start_cmd_rx(); + if(!memcard_receive_command(b, 6)) return RTEMS_IO_ERROR; + + /* ACMD6 - set bus width */ + memcard_start_cmd_tx(); + memcard_send_command(55, rca << 16); + memcard_start_cmd_rx(); + if(!memcard_receive_command(b, 6)) return RTEMS_IO_ERROR; + memcard_start_cmd_tx(); + memcard_send_command(6, 2); + memcard_start_cmd_rx(); + if(!memcard_receive_command(b, 6)) return RTEMS_IO_ERROR; + + MM_WRITE(MM_MEMCARD_CLK2XDIV, 3); + + return RTEMS_SUCCESSFUL; +} + +static int memcard_disk_ioctl(rtems_disk_device *dd, uint32_t req, void *arg) +{ + if (req == RTEMS_BLKIO_REQUEST) { + rtems_blkdev_request *r = (rtems_blkdev_request *)arg; + switch (r->req) { + case RTEMS_BLKDEV_REQ_READ: + return memcard_disk_block_read(r); + case RTEMS_BLKDEV_REQ_WRITE: + return memcard_disk_block_write(r); + default: + errno = EINVAL; + return -1; + } + } else if (req == RTEMS_BLKIO_CAPABILITIES) { + *(uint32_t *)arg = RTEMS_BLKDEV_CAP_MULTISECTOR_CONT; + return 0; + } else { + errno = EINVAL; + return -1; + } +} + +static rtems_status_code memcard_disk_init( + rtems_device_major_number major, rtems_device_minor_number minor, + void *arg) +{ + rtems_status_code sc; + dev_t dev; + + sc = rtems_disk_io_initialize(); + RTEMS_CHECK_SC(sc, "Initialize RTEMS disk IO"); + + dev = rtems_filesystem_make_dev_t(major, 0); + + sc = memcard_init(); + RTEMS_CHECK_SC(sc, "Initialize memory card"); + + sc = rtems_disk_create_phys(dev, BLOCK_SIZE, block_count, memcard_disk_ioctl, + NULL, "/dev/memcard"); + RTEMS_CHECK_SC(sc, "Create disk device"); + + return RTEMS_SUCCESSFUL; +} + + +static const rtems_driver_address_table memcard_disk_ops = { + .initialization_entry = memcard_disk_init, + .open_entry = rtems_blkdev_generic_open, + .close_entry = rtems_blkdev_generic_close, + .read_entry = rtems_blkdev_generic_read, + .write_entry = rtems_blkdev_generic_write, + .control_entry = rtems_blkdev_generic_ioctl +}; + +rtems_status_code memcard_register(void) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_device_major_number major = 0; + + sc = rtems_io_register_driver(0, &memcard_disk_ops, &major); + RTEMS_CHECK_SC(sc, "Register disk memory card driver"); + + return RTEMS_SUCCESSFUL; +} diff --git a/bsps/lm32/shared/milkymist_midi/midi.c b/bsps/lm32/shared/milkymist_midi/midi.c new file mode 100644 index 0000000000..956edbfaf0 --- /dev/null +++ b/bsps/lm32/shared/milkymist_midi/midi.c @@ -0,0 +1,122 @@ +/* midi.c + * + * Milkymist MIDI driver for RTEMS + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * COPYRIGHT (c) 2010, 2011 Sebastien Bourdeauducq + */ + +#define RTEMS_STATUS_CHECKS_USE_PRINTK + +#include <stdlib.h> +#include <sys/types.h> +#include <rtems.h> +#include <rtems/status-checks.h> +#include <bsp.h> +#include <bsp/irq-generic.h> +#include <rtems/libio.h> +#include "../include/system_conf.h" +#include <bsp/milkymist_midi.h> + +#define DEVICE_NAME "/dev/midi" + +static rtems_id midi_q; +static unsigned char *midi_p = NULL; +static unsigned char midi_msg[3]; + +static rtems_isr interrupt_handler(rtems_vector_number n) +{ + unsigned char msg; + + while (MM_READ(MM_MIDI_STAT) & MIDI_STAT_RX_EVT) { + msg = MM_READ(MM_MIDI_RXTX); + MM_WRITE(MM_MIDI_STAT, MIDI_STAT_RX_EVT); + + if ((msg & 0xf8) == 0xf8) + continue; /* ignore system real-time */ + + if (msg & 0x80) + midi_p = midi_msg; /* status byte */ + + if (!midi_p) + continue; /* ignore extra or unsynchronized data */ + + *midi_p++ = msg; + + if (midi_p == midi_msg+3) { + /* received a complete MIDI message */ + rtems_message_queue_send(midi_q, midi_msg, 3); + midi_p = NULL; + } + } + lm32_interrupt_ack(1 << MM_IRQ_MIDI); +} + +rtems_device_driver midi_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code sc; + rtems_isr_entry dummy; + + sc = rtems_io_register_name(DEVICE_NAME, major, 0); + RTEMS_CHECK_SC(sc, "create MIDI input device"); + + sc = rtems_message_queue_create( + rtems_build_name('M', 'I', 'D', 'I'), + 32, + 3, + 0, + &midi_q + ); + RTEMS_CHECK_SC(sc, "create MIDI queue"); + + rtems_interrupt_catch(interrupt_handler, MM_IRQ_MIDI, &dummy); + bsp_interrupt_vector_enable(MM_IRQ_MIDI); + /* Only MIDI THRU mode is supported atm */ + MM_WRITE(MM_MIDI_CTRL, MIDI_CTRL_RX_INT|MIDI_CTRL_THRU); + + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver midi_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + uint32_t count; + + rtems_message_queue_flush(midi_q, &count); + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver midi_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg; + rtems_status_code sc; + + sc = rtems_message_queue_receive( + midi_q, + rw_args->buffer, + (size_t *)&rw_args->bytes_moved, + RTEMS_WAIT, + RTEMS_NO_TIMEOUT + ); + + if(sc == RTEMS_SUCCESSFUL) + return RTEMS_SUCCESSFUL; + else { + rw_args->bytes_moved = 0; + return RTEMS_UNSATISFIED; + } +} diff --git a/bsps/lm32/shared/milkymist_pfpu/pfpu.c b/bsps/lm32/shared/milkymist_pfpu/pfpu.c new file mode 100644 index 0000000000..a9fb38f472 --- /dev/null +++ b/bsps/lm32/shared/milkymist_pfpu/pfpu.c @@ -0,0 +1,143 @@ +/* pfpu.c + * + * Milkymist PFPU driver for RTEMS + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * COPYRIGHT (c) 2010, 2011 Sebastien Bourdeauducq + */ + +#define RTEMS_STATUS_CHECKS_USE_PRINTK + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <rtems.h> +#include <bsp.h> +#include <bsp/irq-generic.h> +#include <rtems/libio.h> +#include <rtems/status-checks.h> +#include "../include/system_conf.h" +#include <bsp/milkymist_pfpu.h> + +#define DEVICE_NAME "/dev/pfpu" + +static rtems_id done_sem; + +static rtems_isr done_handler(rtems_vector_number n) +{ + rtems_semaphore_release(done_sem); + lm32_interrupt_ack(1 << MM_IRQ_PFPU); +} + +rtems_device_driver pfpu_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code sc; + rtems_isr_entry dummy; + + sc = rtems_io_register_name(DEVICE_NAME, major, 0); + RTEMS_CHECK_SC(sc, "create PFPU device"); + + sc = rtems_semaphore_create( + rtems_build_name('P', 'F', 'P', 'U'), + 0, + RTEMS_SIMPLE_BINARY_SEMAPHORE, + 0, + &done_sem + ); + RTEMS_CHECK_SC(sc, "create PFPU done semaphore"); + + rtems_interrupt_catch(done_handler, MM_IRQ_PFPU, &dummy); + bsp_interrupt_vector_enable(MM_IRQ_PFPU); + + return RTEMS_SUCCESSFUL; +} + +static void load_program(unsigned int *program, int size) +{ + int page; + int word; + volatile unsigned int *pfpu_prog = (unsigned int *)MM_PFPU_CODEBASE; + + for (page=0;page<(PFPU_PROGSIZE/PFPU_PAGESIZE);page++) { + MM_WRITE(MM_PFPU_CODEPAGE, page); + for (word=0;word<PFPU_PAGESIZE;word++) { + if (size == 0) return; + pfpu_prog[word] = *program; + program++; + size--; + } + } +} + +static void load_registers(float *registers) +{ + volatile float *pfpu_regs = (float *)MM_PFPU_DREGBASE; + int i; + + for (i=PFPU_SPREG_COUNT;i<PFPU_REG_COUNT;i++) + pfpu_regs[i] = registers[i]; +} + +static void update_registers(float *registers) +{ + volatile float *pfpu_regs = (float *)MM_PFPU_DREGBASE; + int i; + + for (i=PFPU_SPREG_COUNT;i<PFPU_REG_COUNT;i++) + registers[i] = pfpu_regs[i]; +} + +static rtems_status_code pfpu_execute(struct pfpu_td *td) +{ + rtems_status_code sc; + + load_program(td->program, td->progsize); + load_registers(td->registers); + MM_WRITE(MM_PFPU_MESHBASE, (unsigned int)td->output); + MM_WRITE(MM_PFPU_HMESHLAST, td->hmeshlast); + MM_WRITE(MM_PFPU_VMESHLAST, td->vmeshlast); + MM_WRITE(MM_PFPU_CTL, PFPU_CTL_START); + + sc = rtems_semaphore_obtain(done_sem, RTEMS_WAIT, 10); + if (sc != RTEMS_SUCCESSFUL) + return sc; + + if (td->update) { + update_registers(td->registers); + if (td->invalidate) { + __asm__ volatile( /* Invalidate Level-1 data cache */ + "wcsr DCC, r0\n" + "nop\n" + ); + } + } + + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver pfpu_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_ioctl_args_t *args = arg; + + args->ioctl_return = -1; + if (args->command != PFPU_EXECUTE) + return RTEMS_UNSATISFIED; + + if (pfpu_execute((struct pfpu_td *)args->buffer) != RTEMS_SUCCESSFUL) + return RTEMS_UNSATISFIED; + + args->ioctl_return = 0; + return RTEMS_SUCCESSFUL; +} diff --git a/bsps/lm32/shared/milkymist_timer/timer.c b/bsps/lm32/shared/milkymist_timer/timer.c new file mode 100644 index 0000000000..74d59956ec --- /dev/null +++ b/bsps/lm32/shared/milkymist_timer/timer.c @@ -0,0 +1,78 @@ +/* timer.c + * + * This file manages the benchmark timer used by the RTEMS Timing + * Test Suite. Each measured time period is demarcated by calls to + * benchmark_timer_initialize() and benchmark_timer_read(). + * benchmark_timer_read() usually returns the number of microseconds + * since benchmark_timer_initialize() exitted. + * + * NOTE: It is important that the timer start/stop overhead be + * determined when porting or modifying this code. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * COPYRIGHT (c) Yann Sionneau <yann.sionneau@telecom-sudparis.eu> (GSoC 2010) + * Telecom SudParis + */ + +#include <rtems.h> +#include <bsp.h> +#include <rtems/btimer.h> +#include <rtems/bspIo.h> +#include "../include/system_conf.h" +#include "../../shared/clock/clock.h" + +bool benchmark_timer_find_average_overhead; + +void benchmark_timer_initialize(void) +{ + MM_WRITE(MM_TIMER1_COMPARE, 0xffffffff); + MM_WRITE(MM_TIMER1_COUNTER, 0); + MM_WRITE(MM_TIMER1_CONTROL, TIMER_ENABLE); +} + +/* + * The following controls the behavior of benchmark_timer_read(). + * + * AVG_OVEREHAD is the overhead for starting and stopping the timer. It + * is usually deducted from the number returned. + * + * LEAST_VALID is the lowest number this routine should trust. Numbers + * below this are "noise" and zero is returned. + */ + +#define AVG_OVERHEAD 4 /* It typically takes X.X microseconds */ + /* (Y countdowns) to start/stop the timer. */ + /* This value is in microseconds. */ +#define LEAST_VALID 4 /* Don't trust a clicks value lower than this */ + +benchmark_timer_t benchmark_timer_read(void) +{ + uint32_t ticks; + uint32_t total; + + ticks = MM_READ(MM_TIMER1_COUNTER); + if (ticks == 0xffffffff) + printk("Timer overflow!\n"); + + total = ticks / (MM_READ(MM_FREQUENCY) / 1000000); + + if (benchmark_timer_find_average_overhead) + return total; + else + { + if (total < LEAST_VALID) + return 0; + + return (total - AVG_OVERHEAD); + } +} + +void benchmark_timer_disable_subtracting_average_overhead( + bool find_flag +) +{ + benchmark_timer_find_average_overhead = find_flag; +} diff --git a/bsps/lm32/shared/milkymist_tmu/tmu.c b/bsps/lm32/shared/milkymist_tmu/tmu.c new file mode 100644 index 0000000000..10b7b2660c --- /dev/null +++ b/bsps/lm32/shared/milkymist_tmu/tmu.c @@ -0,0 +1,161 @@ +/* tmu.c + * + * Milkymist TMU driver for RTEMS + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * COPYRIGHT (c) 2010, 2011 Sebastien Bourdeauducq + */ + +#define RTEMS_STATUS_CHECKS_USE_PRINTK + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <rtems.h> +#include <bsp.h> +#include <bsp/irq-generic.h> +#include <rtems/libio.h> +#include <rtems/status-checks.h> +#include "../include/system_conf.h" +#include <bsp/milkymist_tmu.h> + +#define DEVICE_NAME "/dev/tmu" + +static rtems_id done_sem; + +static rtems_isr done_handler(rtems_vector_number n) +{ + rtems_semaphore_release(done_sem); + lm32_interrupt_ack(1 << MM_IRQ_TMU); +} + +rtems_device_driver tmu_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code sc; + rtems_isr_entry dummy; + + sc = rtems_io_register_name(DEVICE_NAME, major, 0); + RTEMS_CHECK_SC(sc, "create TMU device"); + + sc = rtems_semaphore_create( + rtems_build_name('T', 'M', 'U', ' '), + 0, + RTEMS_SIMPLE_BINARY_SEMAPHORE, + 0, + &done_sem + ); + RTEMS_CHECK_SC(sc, "create TMU done semaphore"); + + rtems_interrupt_catch(done_handler, MM_IRQ_TMU, &dummy); + bsp_interrupt_vector_enable(MM_IRQ_TMU); + + return RTEMS_SUCCESSFUL; +} + +static void invalidate_l2(void) +{ + volatile char *flushbase = (char *)FMLBRG_FLUSH_BASE; + int i, offset; + + offset = 0; + for (i=0;i<FMLBRG_LINE_COUNT;i++) { + flushbase[offset] = 0; + offset += FMLBRG_LINE_LENGTH; + } +} + +static bool invalidate_after; + +static void tmu_start(struct tmu_td *td) +{ + if (td->invalidate_before) + invalidate_l2(); + + MM_WRITE(MM_TMU_HMESHLAST, td->hmeshlast); + MM_WRITE(MM_TMU_VMESHLAST, td->vmeshlast); + MM_WRITE(MM_TMU_BRIGHTNESS, td->brightness); + MM_WRITE(MM_TMU_CHROMAKEY, td->chromakey); + + MM_WRITE(MM_TMU_VERTICESADR, (unsigned int)td->vertices); + MM_WRITE(MM_TMU_TEXFBUF, (unsigned int)td->texfbuf); + MM_WRITE(MM_TMU_TEXHRES, td->texhres); + MM_WRITE(MM_TMU_TEXVRES, td->texvres); + MM_WRITE(MM_TMU_TEXHMASK, td->texhmask); + MM_WRITE(MM_TMU_TEXVMASK, td->texvmask); + + MM_WRITE(MM_TMU_DSTFBUF, (unsigned int)td->dstfbuf); + MM_WRITE(MM_TMU_DSTHRES, td->dsthres); + MM_WRITE(MM_TMU_DSTVRES, td->dstvres); + MM_WRITE(MM_TMU_DSTHOFFSET, td->dsthoffset); + MM_WRITE(MM_TMU_DSTVOFFSET, td->dstvoffset); + MM_WRITE(MM_TMU_DSTSQUAREW, td->dstsquarew); + MM_WRITE(MM_TMU_DSTSQUAREH, td->dstsquareh); + + MM_WRITE(MM_TMU_ALPHA, td->alpha); + + MM_WRITE(MM_TMU_CTL, td->flags|TMU_CTL_START); + + invalidate_after = td->invalidate_after; +} + +static rtems_status_code tmu_finalize(void) +{ + rtems_status_code sc; + + sc = rtems_semaphore_obtain(done_sem, RTEMS_WAIT, 100); + if (sc != RTEMS_SUCCESSFUL) + return sc; + + if (invalidate_after) { + invalidate_l2(); + __asm__ volatile( /* Invalidate Level-1 data cache */ + "wcsr DCC, r0\n" + "nop\n" + ); + } + + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver tmu_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_ioctl_args_t *args = arg; + struct tmu_td *td = (struct tmu_td *)args->buffer; + rtems_status_code sc; + + switch (args->command) { + case TMU_EXECUTE: + tmu_start(td); + sc = tmu_finalize(); + break; + case TMU_EXECUTE_NONBLOCK: + tmu_start(td); + sc = RTEMS_SUCCESSFUL; + break; + case TMU_EXECUTE_WAIT: + sc = tmu_finalize(); + break; + default: + sc = RTEMS_UNSATISFIED; + break; + } + + if (sc == RTEMS_SUCCESSFUL) + args->ioctl_return = 0; + else + args->ioctl_return = -1; + + return sc; +} diff --git a/bsps/lm32/shared/milkymist_usbinput/comloc.h b/bsps/lm32/shared/milkymist_usbinput/comloc.h new file mode 100644 index 0000000000..187792f348 --- /dev/null +++ b/bsps/lm32/shared/milkymist_usbinput/comloc.h @@ -0,0 +1,32 @@ +/** + * @file + * @ingroup lm32_milkymist_usbinput lm32_milkymist_shared + * @brief Milkymist USB input devices driver + */ + +/* comloc.h + * + * Milkymist USB input devices driver for RTEMS + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * COPYRIGHT (c) 2010 Sebastien Bourdeauducq + */ + +#ifndef __COMLOC_H_ +#define __COMLOC_H_ + +#define COMLOCV(x) (*(volatile unsigned char *)(x)) + +#define COMLOC_DEBUG_PRODUCE COMLOCV(MM_SOFTUSB_DMEM_BASE+0x1000) +#define COMLOC_DEBUG(offset) COMLOCV(MM_SOFTUSB_DMEM_BASE+0x1001+offset) +#define COMLOC_MEVT_PRODUCE COMLOCV(MM_SOFTUSB_DMEM_BASE+0x1101) +#define COMLOC_MEVT(offset) COMLOCV(MM_SOFTUSB_DMEM_BASE+0x1102+offset) +#define COMLOC_KEVT_PRODUCE COMLOCV(MM_SOFTUSB_DMEM_BASE+0x1142) +#define COMLOC_KEVT(offset) COMLOCV(MM_SOFTUSB_DMEM_BASE+0x1143+offset) +#define COMLOC_MIDI_PRODUCE COMLOCV(MM_SOFTUSB_DMEM_BASE+0x1183) +#define COMLOC_MIDI(offset) COMLOCV(MM_SOFTUSB_DMEM_BASE+0x1184+offset) + +#endif /* __COMLOC_H_ */ diff --git a/bsps/lm32/shared/milkymist_usbinput/usbinput.c b/bsps/lm32/shared/milkymist_usbinput/usbinput.c new file mode 100644 index 0000000000..de924b177f --- /dev/null +++ b/bsps/lm32/shared/milkymist_usbinput/usbinput.c @@ -0,0 +1,179 @@ +/* usbinput.c + * + * Milkymist USB input devices driver for RTEMS + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * COPYRIGHT (c) 2010, 2011, 2012 Sebastien Bourdeauducq + */ + +#define RTEMS_STATUS_CHECKS_USE_PRINTK + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <rtems.h> +#include <bsp.h> +#include <bsp/irq-generic.h> +#include <rtems/libio.h> +#include <rtems/status-checks.h> +#include "../include/system_conf.h" +#include <bsp/milkymist_usbinput.h> + +#include "comloc.h" + +#define DEVICE_NAME "/dev/usbinput" + +static int mouse_consume; +static int keyboard_consume; +static int midi_consume; + +static rtems_id event_q; + +static rtems_isr interrupt_handler(rtems_vector_number n) +{ + unsigned char msg[8]; + int i; + + lm32_interrupt_ack(1 << MM_IRQ_USB); + + while(mouse_consume != COMLOC_MEVT_PRODUCE) { + for(i=0;i<4;i++) + msg[i] = COMLOC_MEVT(4*mouse_consume+i); + rtems_message_queue_send(event_q, msg, 4); + mouse_consume = (mouse_consume + 1) & 0x0f; + } + + while(keyboard_consume != COMLOC_KEVT_PRODUCE) { + for(i=0;i<8;i++) + msg[i] = COMLOC_KEVT(8*keyboard_consume+i); + rtems_message_queue_send(event_q, msg, 8); + keyboard_consume = (keyboard_consume + 1) & 0x07; + } + + while(midi_consume != COMLOC_MIDI_PRODUCE) { + for(i=0;i<3;i++) + msg[i] = COMLOC_MIDI(4*midi_consume+i+1); + rtems_message_queue_send(event_q, msg, 3); + midi_consume = (midi_consume + 1) & 0x0f; + } + +} + +rtems_device_driver usbinput_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code sc; + rtems_isr_entry dummy; + + MM_WRITE(MM_SOFTUSB_CONTROL, SOFTUSB_CONTROL_RESET); + + mouse_consume = 0; + keyboard_consume = 0; + midi_consume = 0; + + sc = rtems_io_register_name(DEVICE_NAME, major, 0); + RTEMS_CHECK_SC(sc, "create USB input device"); + + sc = rtems_message_queue_create( + rtems_build_name('U', 'S', 'B', 'I'), + 64, + 8, + 0, + &event_q + ); + RTEMS_CHECK_SC(sc, "create USB event queue"); + + rtems_interrupt_catch(interrupt_handler, MM_IRQ_USB, &dummy); + bsp_interrupt_vector_enable(MM_IRQ_USB); + + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver usbinput_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + uint32_t count; + + rtems_message_queue_flush(event_q, &count); + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver usbinput_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg; + rtems_status_code sc; + + if(rw_args->count < 8) { + rw_args->bytes_moved = 0; + return RTEMS_UNSATISFIED; + } + + sc = rtems_message_queue_receive( + event_q, + rw_args->buffer, + (size_t *)&rw_args->bytes_moved, + RTEMS_WAIT, + RTEMS_NO_TIMEOUT + ); + + if(sc == RTEMS_SUCCESSFUL) + return RTEMS_SUCCESSFUL; + else { + rw_args->bytes_moved = 0; + return RTEMS_UNSATISFIED; + } +} + +static void load_firmware(const unsigned char *firmware, int length) +{ + int i, nwords; + volatile unsigned int *usb_dmem + = (volatile unsigned int *)MM_SOFTUSB_DMEM_BASE; + volatile unsigned int *usb_pmem + = (volatile unsigned int *)MM_SOFTUSB_PMEM_BASE; + + MM_WRITE(MM_SOFTUSB_CONTROL, SOFTUSB_CONTROL_RESET); + for(i=0;i<SOFTUSB_DMEM_SIZE/4;i++) + usb_dmem[i] = 0; + for(i=0;i<SOFTUSB_PMEM_SIZE/2;i++) + usb_pmem[i] = 0; + nwords = (length+1)/2; + for(i=0;i<nwords;i++) + usb_pmem[i] = ((unsigned int)(firmware[2*i])) + |((unsigned int)(firmware[2*i+1]) << 8); + MM_WRITE(MM_SOFTUSB_CONTROL, 0); +} + +rtems_device_driver usbinput_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_ioctl_args_t *args = arg; + struct usbinput_firmware_description *fd + = (struct usbinput_firmware_description *)args->buffer; + + if(args->command == USBINPUT_LOAD_FIRMWARE) { + load_firmware(fd->data, fd->length); + args->ioctl_return = 0; + return RTEMS_SUCCESSFUL; + } else { + args->ioctl_return = -1; + return RTEMS_UNSATISFIED; + } +} diff --git a/bsps/lm32/shared/milkymist_versions/versions.c b/bsps/lm32/shared/milkymist_versions/versions.c new file mode 100644 index 0000000000..84b2b5d2fb --- /dev/null +++ b/bsps/lm32/shared/milkymist_versions/versions.c @@ -0,0 +1,132 @@ +/* versions.c + * + * Milkymist versioning driver for RTEMS + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * COPYRIGHT (c) 2010, 2011 Sebastien Bourdeauducq + */ + +#define RTEMS_STATUS_CHECKS_USE_PRINTK + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <rtems.h> +#include <rtems/status-checks.h> +#include <bsp.h> +#include <rtems/libio.h> +#include "../include/system_conf.h" +#include <bsp/milkymist_versions.h> + +#define SOC_DEVICE_NAME "/dev/soc" +#define PCB_DEVICE_NAME "/dev/pcb" +#define PCBREV_DEVICE_NAME "/dev/pcb_rev" + +rtems_device_driver versions_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code sc; + + sc = rtems_io_register_name(SOC_DEVICE_NAME, major, 0); + RTEMS_CHECK_SC(sc, "create SoC version device"); + sc = rtems_io_register_name(PCB_DEVICE_NAME, major, 1); + RTEMS_CHECK_SC(sc, "create PCB type device"); + sc = rtems_io_register_name(PCBREV_DEVICE_NAME, major, 2); + RTEMS_CHECK_SC(sc, "create PCB revision device"); + + return RTEMS_SUCCESSFUL; +} + +static int get_soc_version(char *buffer) +{ + char fmt[13]; + char *version; + int len; + unsigned int id; + unsigned int major, minor, subminor, rc; + + id = MM_READ(MM_SYSTEM_ID); + major = (id & 0xf0000000) >> 28; + minor = (id & 0x0f000000) >> 24; + subminor = (id & 0x00f00000) >> 20; + rc = (id & 0x000f0000) >> 16; + + version = fmt; + version += sprintf(version, "%u.%u", major, minor); + if (subminor != 0) + version += sprintf(version, ".%u", subminor); + if (rc != 0) + version += sprintf(version, "RC%u", rc); + + len = version - fmt; + memcpy(buffer, fmt, len); + return len; +} + +static int get_pcb_type(char *buffer) +{ + unsigned int id; + + id = MM_READ(MM_SYSTEM_ID); + buffer[0] = (id & 0x0000ff00) >> 8; + buffer[1] = id & 0x000000ff; + return 2; +} + +static int get_pcb_revision(char *buffer) +{ + unsigned int v; + + v = MM_READ(MM_GPIO_IN); + v = (v & 0x78) >> 3; + buffer[0] = '0' + v; + return 1; +} + +rtems_device_driver versions_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *)arg; + + if(rw_args->offset != 0) { + rw_args->bytes_moved = 0; + return RTEMS_SUCCESSFUL; + } + + switch (minor) { + case 0: + if (rw_args->count < 12) { + rw_args->bytes_moved = 0; + return RTEMS_UNSATISFIED; + } + rw_args->bytes_moved = get_soc_version((char *)rw_args->buffer); + return RTEMS_SUCCESSFUL; + case 1: + if (rw_args->count < 2) { + rw_args->bytes_moved = 0; + return RTEMS_UNSATISFIED; + } + rw_args->bytes_moved = get_pcb_type((char *)rw_args->buffer); + return RTEMS_SUCCESSFUL; + case 2: + if (rw_args->count < 1) { + rw_args->bytes_moved = 0; + return RTEMS_UNSATISFIED; + } + rw_args->bytes_moved = get_pcb_revision((char *)rw_args->buffer); + return RTEMS_SUCCESSFUL; + } + + rw_args->bytes_moved = 0; + return RTEMS_UNSATISFIED; +} diff --git a/bsps/lm32/shared/milkymist_video/video.c b/bsps/lm32/shared/milkymist_video/video.c new file mode 100644 index 0000000000..bb6bf62928 --- /dev/null +++ b/bsps/lm32/shared/milkymist_video/video.c @@ -0,0 +1,391 @@ +/* video.c + * + * Milkymist video input driver for RTEMS + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * COPYRIGHT (c) 2010, 2011 Sebastien Bourdeauducq + */ + +#define RTEMS_STATUS_CHECKS_USE_PRINTK + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <rtems.h> +#include <bsp.h> +#include <bsp/irq-generic.h> +#include <rtems/libio.h> +#include <rtems/status-checks.h> +#include <rtems/bspIo.h> +#include "../include/system_conf.h" +#include <bsp/milkymist_video.h> + +#define DEVICE_NAME "/dev/video" +#define N_BUFFERS 3 +#define FRAME_W 720 +#define FRAME_H 288 + +static bool buffers_locked[N_BUFFERS]; +static void *buffers[N_BUFFERS]; +static int last_buffer; +static int current_buffer; + +static rtems_isr frame_handler(rtems_vector_number n) +{ + int remaining_attempts; + + lm32_interrupt_ack(1 << MM_IRQ_VIDEOIN); + + last_buffer = current_buffer; + + /* get a new buffer */ + remaining_attempts = N_BUFFERS; + do { + current_buffer++; + if(current_buffer == N_BUFFERS) + current_buffer = 0; + remaining_attempts--; + } while(buffers_locked[current_buffer] && (remaining_attempts > 0)); + + MM_WRITE(MM_BT656_BASE, (unsigned int)buffers[current_buffer]); + + if(buffers_locked[current_buffer]) + printk("Failed to find unlocked buffer\n"); +} + +static void i2c_delay(void) +{ + unsigned int i; + + for(i=0;i<1000;i++) __asm__("nop"); +} + +/* I2C bit-banging functions from http://en.wikipedia.org/wiki/I2c */ +static unsigned int i2c_read_bit(void) +{ + unsigned int bit; + + /* Let the slave drive data */ + MM_WRITE(MM_BT656_I2C, 0); + i2c_delay(); + MM_WRITE(MM_BT656_I2C, BT656_I2C_SDC); + i2c_delay(); + bit = MM_READ(MM_BT656_I2C) & BT656_I2C_SDAIN; + i2c_delay(); + MM_WRITE(MM_BT656_I2C, 0); + return bit; +} + +static void i2c_write_bit(unsigned int bit) +{ + if(bit) { + MM_WRITE(MM_BT656_I2C, BT656_I2C_SDAOE|BT656_I2C_SDAOUT); + } else { + MM_WRITE(MM_BT656_I2C, BT656_I2C_SDAOE); + } + i2c_delay(); + MM_WRITE(MM_BT656_I2C, MM_READ(MM_BT656_I2C) | BT656_I2C_SDC); + i2c_delay(); + MM_WRITE(MM_BT656_I2C, MM_READ(MM_BT656_I2C) & ~BT656_I2C_SDC); +} + +static int i2c_started; + +static void i2c_start_cond(void) +{ + if(i2c_started) { + /* set SDA to 1 */ + MM_WRITE(MM_BT656_I2C, BT656_I2C_SDAOE|BT656_I2C_SDAOUT); + i2c_delay(); + MM_WRITE(MM_BT656_I2C, MM_READ(MM_BT656_I2C) | BT656_I2C_SDC); + } + /* SCL is high, set SDA from 1 to 0 */ + MM_WRITE(MM_BT656_I2C, BT656_I2C_SDAOE|BT656_I2C_SDC); + i2c_delay(); + MM_WRITE(MM_BT656_I2C, BT656_I2C_SDAOE); + i2c_started = 1; +} + +static void i2c_stop_cond(void) +{ + /* set SDA to 0 */ + MM_WRITE(MM_BT656_I2C, BT656_I2C_SDAOE); + i2c_delay(); + /* Clock stretching */ + MM_WRITE(MM_BT656_I2C, BT656_I2C_SDAOE|BT656_I2C_SDC); + /* SCL is high, set SDA from 0 to 1 */ + MM_WRITE(MM_BT656_I2C, BT656_I2C_SDC); + i2c_delay(); + i2c_started = 0; +} + +static unsigned int i2c_write(unsigned char byte) +{ + unsigned int bit; + unsigned int ack; + + for(bit = 0; bit < 8; bit++) { + i2c_write_bit(byte & 0x80); + byte <<= 1; + } + ack = !i2c_read_bit(); + return ack; +} + +static unsigned char i2c_read(int ack) +{ + unsigned char byte = 0; + unsigned int bit; + + for(bit = 0; bit < 8; bit++) { + byte <<= 1; + byte |= i2c_read_bit(); + } + i2c_write_bit(!ack); + return byte; +} + +static unsigned char read_reg(unsigned char addr) +{ + unsigned char r; + + i2c_start_cond(); + i2c_write(0x40); + i2c_write(addr); + i2c_start_cond(); + i2c_write(0x41); + r = i2c_read(0); + i2c_stop_cond(); + + return r; +} + +static void write_reg(unsigned char addr, unsigned char val) +{ + i2c_start_cond(); + i2c_write(0x40); + i2c_write(addr); + i2c_write(val); + i2c_stop_cond(); +} + +static const char vreg_addr[] = { + 0x1d, 0xc3, 0xc4 +}; + +static const char vreg_dat[] = { + 0x40, 0x05, 0x80 +}; + +rtems_device_driver video_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code sc; + rtems_isr_entry dummy; + int i; + + MM_WRITE(MM_BT656_I2C, BT656_I2C_SDC); + + sc = rtems_io_register_name(DEVICE_NAME, major, 0); + RTEMS_CHECK_SC(sc, "create video input device"); + + rtems_interrupt_catch(frame_handler, MM_IRQ_VIDEOIN, &dummy); + bsp_interrupt_vector_enable(MM_IRQ_VIDEOIN); + + for(i=0;i<sizeof(vreg_addr);i++) + write_reg(vreg_addr[i], vreg_dat[i]); + + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver video_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + int i; + int status; + + for(i=0;i<N_BUFFERS;i++) { + status = posix_memalign(&buffers[i], 32, 2*FRAME_W*FRAME_H); + if(status != 0) { + i--; + while(i > 0) { + free(buffers[i]); + i--; + } + return RTEMS_UNSATISFIED; + } + } + + last_buffer = -1; + current_buffer = 0; + + MM_WRITE(MM_BT656_BASE, (unsigned int)buffers[current_buffer]); + MM_WRITE(MM_BT656_FILTERSTATUS, BT656_FILTER_FIELD1); + + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver video_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + int i; + + MM_WRITE(MM_BT656_FILTERSTATUS, 0); + while(MM_READ(MM_BT656_FILTERSTATUS) & BT656_FILTER_INFRAME); + for(i=0;i<N_BUFFERS;i++) + free(buffers[i]); + return RTEMS_SUCCESSFUL; +} + +static void invalidate_caches(void) +{ + volatile char *flushbase = (char *)FMLBRG_FLUSH_BASE; + int i, offset; + + offset = 0; + for (i=0;i<FMLBRG_LINE_COUNT;i++) { + flushbase[offset] = 0; + offset += FMLBRG_LINE_LENGTH; + } + __asm__ volatile( /* Invalidate Level-1 data cache */ + "wcsr DCC, r0\n" + "nop\n" + ); +} + +static void set_format(int format) +{ + switch(format) { + case VIDEO_FORMAT_CVBS6: + write_reg(0x00, 0x00); + write_reg(0xc3, 0x05); + write_reg(0xc4, 0x80); + break; + case VIDEO_FORMAT_CVBS5: + write_reg(0x00, 0x00); + write_reg(0xc3, 0x0d); + write_reg(0xc4, 0x80); + break; + case VIDEO_FORMAT_CVBS4: + write_reg(0x00, 0x00); + write_reg(0xc3, 0x04); + write_reg(0xc4, 0x80); + break; + case VIDEO_FORMAT_SVIDEO: + write_reg(0x00, 0x06); + write_reg(0xc3, 0xd5); + write_reg(0xc4, 0x80); + break; + case VIDEO_FORMAT_COMPONENT: + write_reg(0x00, 0x09); + write_reg(0xc3, 0x45); + write_reg(0xc4, 0x8d); + break; + } +} + +rtems_device_driver video_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_ioctl_args_t *args = arg; + unsigned int *a = (unsigned int *)args->buffer; + rtems_status_code sc; + + switch (args->command) { + case VIDEO_BUFFER_LOCK: + if (last_buffer == -1) { + *a = 0; + } else { + bsp_interrupt_vector_disable(MM_IRQ_VIDEOIN); + if(*a) invalidate_caches(); + *a = (unsigned int)buffers[last_buffer]; + buffers_locked[last_buffer] = true; + bsp_interrupt_vector_enable(MM_IRQ_VIDEOIN); + } + sc = RTEMS_SUCCESSFUL; + break; + case VIDEO_BUFFER_UNLOCK: { + int i; + for(i=0;i<N_BUFFERS;i++) { + if ((unsigned int)buffers[i] == (unsigned int)a) { + buffers_locked[i] = false; + break; + } + } + sc = RTEMS_SUCCESSFUL; + break; + } + + case VIDEO_SET_BRIGHTNESS: + write_reg(0x0a, (unsigned int)a); + sc = RTEMS_SUCCESSFUL; + break; + case VIDEO_GET_BRIGHTNESS: + *a = read_reg(0x0a); + sc = RTEMS_SUCCESSFUL; + break; + case VIDEO_SET_CONTRAST: + write_reg(0x08, (unsigned int)a); + sc = RTEMS_SUCCESSFUL; + break; + case VIDEO_GET_CONTRAST: + *a = read_reg(0x08); + sc = RTEMS_SUCCESSFUL; + break; + case VIDEO_SET_HUE: + write_reg(0x0b, (unsigned int)a); + sc = RTEMS_SUCCESSFUL; + break; + case VIDEO_GET_HUE: + *a = read_reg(0x0b); + sc = RTEMS_SUCCESSFUL; + break; + + case VIDEO_GET_SIGNAL: + *a = read_reg(0x10); + sc = RTEMS_SUCCESSFUL; + break; + + case VIDEO_SET_REGISTER: + write_reg(((unsigned int)a & 0xffff0000) >> 16, + (unsigned int)a & 0x0000ffff); + sc = RTEMS_SUCCESSFUL; + break; + case VIDEO_GET_REGISTER: + *a = read_reg(*a); + sc = RTEMS_SUCCESSFUL; + break; + + case VIDEO_SET_FORMAT: + set_format((int)a); + sc = RTEMS_SUCCESSFUL; + break; + + default: + sc = RTEMS_UNSATISFIED; + break; + } + + if (sc == RTEMS_SUCCESSFUL) + args->ioctl_return = 0; + else + args->ioctl_return = -1; + + return sc; +} |