This commit is contained in:
Mark Qvist 2014-04-03 22:21:37 +02:00
commit c898b090dd
1049 changed files with 288572 additions and 0 deletions

View file

@ -0,0 +1,50 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Low-level ADC module for ARM (interface).
*
* \author Daniele Basile <asterix@develer.com>
*
*/
#include <cpu/detect.h>
#if CPU_CM3_LM3S
#include "adc_lm3s.h"
#elif CPU_CM3_STM32
#include "adc_stm32.h"
#elif CPU_CM3_SAM3X
#include "adc_sam3.h"
/*#elif Add other ARM families here */
#else
#error Unknown CPU
#endif

View file

@ -0,0 +1,199 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief ADC hardware-specific implementation
*
* This ADC module should be use both whit kernel or none.
* If you are using a kernel, the adc drive does not wait the finish of
* conversion but use a singal every time a required conversion are
* ended. This signal wake up a process that return a result of
* conversion. Otherwise, if you not use a kernl, this module wait
* whit a loop the finishing of conversion.
*
*
* \author Daniele Basile <asterix@develer.com>
*/
#include "adc_lm3s.h"
#include <cpu/irq.h>
#include "cfg/cfg_adc.h"
#include "cfg/cfg_proc.h"
#include "cfg/cfg_signal.h"
#include <cfg/macros.h>
#include <cfg/compiler.h>
#include <cfg/debug.h>
// Define log settings for cfg/log.h.
#define LOG_LEVEL ADC_LOG_LEVEL
#define LOG_FORMAT ADC_LOG_FORMAT
#include <cfg/log.h>
#include <drv/adc.h>
#include <drv/timer.h>
#include <drv/clock_lm3s.h>
#include <io/lm3s.h>
/* Select witch ADC use */
#if CPU_CM3_LM3S1968 || CPU_CM3_LM3S8962
#define ADC_BASE ADC0_BASE
#define SYSCTL_RCGC_R SYSCTL_RCGC0_R
#define SYSCTL_RCGC_ADC SYSCTL_RCGC0_ADC0
#else
#error Unknow ADC register for select cpu core
#endif
#if CONFIG_KERN
#include <cfg/module.h>
#include <kern/proc.h>
#include <kern/signal.h>
#include <drv/irq_cm3.h>
#if !CONFIG_KERN_SIGNALS
#error Signals must be active to use ADC with kernel
#endif
/* Signal adc convertion end */
#define SIG_ADC_COMPLETE SIG_USER0
/* ADC waiting process */
static struct Process *adc_process;
/**
* ADC ISR.
* Simply signal the adc process that convertion is complete.
*/
static DECLARE_ISR(adc_conversion_end_irq)
{
sig_post(adc_process, SIG_ADC_COMPLETE);
/* Clear the status bit */
HWREG(ADC_BASE + ADC_O_ISC) |= ADC_ISC_IN3;
}
static void adc_enable_irq(void)
{
/* Clear all pending irq */
HWREG(ADC_BASE + ADC_O_ISC) = 0;
/* Register the IRQ handler */
sysirq_setHandler(INT_ADC3, adc_conversion_end_irq);
/* Enable IRQ */
HWREG(ADC_BASE + ADC_O_SSCTL3) |= ADC_SSCTL3_IE0;
HWREG(ADC_BASE + ADC_O_IM) |= ADC_IM_MASK3;
}
#endif /* CONFIG_KERN */
/**
* Select mux channel \a ch.
* Generally the stm32 cpu family allow us to program the order
* of adc channel that we want to read.
* In this driver implementation we put as fist channel to read the
* select ones.
*/
void adc_hw_select_ch(uint8_t ch)
{
/* Select channel that we want read */
HWREG(ADC_BASE + ADC_O_SSMUX3) = ch;
/* Make single acquisition */
HWREG(ADC_BASE + ADC_O_SSCTL3) |= ADC_SSCTL3_END0;
/* Enable sequence S03 (single sample on select channel) */
HWREG(ADC_BASE + ADC_O_ACTSS) |= ADC_ACTSS_ASEN3;
}
/**
* Start an ADC convertion.
* If a kernel is present, preempt until convertion is complete, otherwise
* a busy wait on ADC_DRDY bit is done.
*/
uint16_t adc_hw_read(void)
{
#if CONFIG_KERN
/* Ensure ADC is not already in use by another process */
ASSERT(adc_process == NULL);
adc_process = proc_current();
#endif
/* Start convertion */
HWREG(ADC0_BASE + ADC_O_PSSI) |= ADC_PSSI_SS3;
#if CONFIG_KERN
/* Ensure IRQs enabled. */
IRQ_ASSERT_ENABLED();
sig_wait(SIG_ADC_COMPLETE);
/* Prevent race condition in case of preemptive kernel */
uint16_t ret = (uint16_t)HWREG(ADC_BASE + ADC_O_SSFIFO3);
MEMORY_BARRIER;
adc_process = NULL;
return ret;
#else
/* Wait in polling until conversion is done */
while (!(HWREG(ADC_BASE + ADC_O_SSFSTAT3) & ADC_SSFSTAT3_FULL));
/* Return the last converted data */
return (uint16_t)HWREG(ADC_BASE + ADC_O_SSFIFO3);
#endif
}
/**
* Init ADC hardware.
*/
void adc_hw_init(void)
{
/* Enable ADC0 clock */
SYSCTL_RCGC_R |= SYSCTL_RCGC_ADC;
/*
* We wait some time because the clock is istable
* and that could cause system hardfault
*/
lm3s_busyWait(10);
/* Disable all sequence */
HWREG(ADC_BASE + ADC_O_ACTSS) = 0;
/* Set trigger event to programmed (for all sequence) */
HWREG(ADC_BASE + ADC_O_EMUX) = 0;
#if CONFIG_KERN
adc_enable_irq();
#endif
}

View file

@ -0,0 +1,68 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief ADC hardware-specific definition
*
* \author Daniele Basile <asterix@develer.com>
*/
#ifndef DRV_ADC_LM3S_H
#define DRV_ADC_LM3S_H
#include "cfg/cfg_adc.h"
#include <cfg/compiler.h>
/**
* ADC config define.
*/
#if CPU_CM3_LM3S1968
#define ADC_MUX_MAXCH 8 //Max number of channel for ADC.
#define ADC_BITS 10 //Bit resolution for ADC converter.
#elif CPU_CM3_LM3S8962
#define ADC_MUX_MAXCH 4 //Max number of channel for ADC.
#define ADC_BITS 10 //Bit resolution for ADC convert
#endif
/**
* Init the ADC pins.
* Implement it if necessary.
*/
#define ADC_INIT_PINS() \
do { \
} while (0)
void adc_hw_select_ch(uint8_t ch);
uint16_t adc_hw_read(void);
void adc_hw_init(void);
#endif /* DRV_ADC_LM3S_H */

View file

@ -0,0 +1,157 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2011 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief ADC hardware-specific implementation
*
* \author Daniele Basile <asterix@develer.com>
*/
#include "adc_sam3.h"
#include "cfg/cfg_adc.h"
#include <cfg/macros.h>
#include <cfg/compiler.h>
// Define log settings for cfg/log.h.
#define LOG_LEVEL ADC_LOG_LEVEL
#define LOG_FORMAT ADC_LOG_FORMAT
#include <cfg/log.h>
#include <drv/adc.h>
#include <drv/irq_cm3.h>
#include <cpu/irq.h>
#include <mware/event.h>
#include <io/cm3.h>
/* We use event to signal the end of conversion */
static Event data_ready;
/* The last converted data */
static uint32_t data;
/**
* ADC ISR.
*
* The interrupt is connected to ready data, so when the
* adc ends the conversion we generate an event and then
* we return the converted value.
*
* \note to clear the Ready data bit and End of conversion
* bit we should read the Last Converted Data register, otherwise
* the ready data interrupt loop on this call.
*/
static DECLARE_ISR(adc_conversion_end_irq)
{
data = 0;
if (ADC_ISR & BV(ADC_DRDY))
{
data = ADC_LDATA;
event_do(&data_ready);
}
}
/**
* Select mux channel \a ch.
*/
void adc_hw_select_ch(uint8_t ch)
{
/* Disable all channels */
ADC_CHDR = ADC_CH_MASK;
/* Enable select channel */
ADC_CHER = BV(ch);
}
/**
* Start an ADC convertion.
*/
uint16_t adc_hw_read(void)
{
ADC_CR = BV(ADC_START);
event_wait(&data_ready);
return(data);
}
/**
* Init ADC hardware.
*/
void adc_hw_init(void)
{
/* Make sure that interrupt are enabled */
IRQ_ASSERT_ENABLED();
/* Initialize the dataready event */
event_initGeneric(&data_ready);
/* Clock ADC peripheral */
pmc_periphEnable(ADC_ID);
/* Reset adc controller */
ADC_CR = ADC_SWRST;
/*
* Set adc mode register:
* - Disable hardware trigger and enable software trigger.
* - Select normal mode.
*/
ADC_MR = 0;
/* Set ADC_BITS bit convertion resolution. */
#if ADC_BITS == 12
ADC_MR &= ~BV(ADC_LOWRES);
#elif ADC_BITS == 10
ADC_MR |= BV(ADC_LOWRES);
#else
#error No select bit resolution is supported to this CPU
#endif
/* Setup ADC */
LOG_INFO("Computed ADC_CLOCK %ld\n", ADC_CLOCK);
ADC_MR |= ((ADC_PRESCALER << ADC_PRESCALER_SHIFT) & ADC_PRESCALER_MASK);
LOG_INFO("prescaler[%ld]\n", ADC_PRESCALER);
ADC_MR |= ((CONFIG_ADC_SUT << ADC_STARTUP_SHIFT) & ADC_STARTUP_MASK);
LOG_INFO("starup[%d]\n", CONFIG_ADC_SUT);
ADC_MR |= ((CONFIG_ADC_STTLING << ADC_SETTLING_SHIFT) & ADC_SETTLING_MASK);
LOG_INFO("sttime[%d]\n", CONFIG_ADC_STTLING);
ADC_MR |= ((CONFIG_ADC_TRACKTIM << ADC_TRACKTIM_SHIFT) & ADC_TRACKTIM_MASK);
LOG_INFO("tracking[%d]\n", CONFIG_ADC_TRACKTIM);
ADC_MR |= ((CONFIG_ADC_TRANSFER << ADC_TRANSFER_SHIFT) & ADC_TRANSFER_MASK);
LOG_INFO("tranfer[%d]\n", CONFIG_ADC_TRANSFER);
/* Register and enable irq for adc. */
sysirq_setHandler(INT_ADC, adc_conversion_end_irq);
ADC_IER = BV(ADC_DRDY);
}

View file

@ -0,0 +1,64 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2008 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief ADC hardware-specific definition
*
* \author Daniele Basile <asterix@develer.com>
*/
#ifndef DRV_ADC_SAM3_H
#define DRV_ADC_SAM3_H
#include <hw/hw_cpufreq.h>
#include "cfg/cfg_adc.h"
#include <cfg/compiler.h>
/**
* ADC config define.
*/
#define ADC_MUX_MAXCH 16 //Max number of channel for ADC.
#define ADC_BITS 12 //Bit resolution for ADC converter.
/**
* Macro for computing correct value to write into ADC
* register.
*/
#define ADC_PRESCALER (DIV_ROUNDUP(CPU_FREQ, 2 * CONFIG_ADC_CLOCK) - 1)
#define ADC_CLOCK (CPU_FREQ / ((ADC_PRESCALER + 1) * 2))
void adc_hw_select_ch(uint8_t ch);
uint16_t adc_hw_read(void);
void adc_hw_init(void);
#endif /* DRV_ADC_SAM3_H */

View file

@ -0,0 +1,215 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief ADC hardware-specific implementation
*
* This ADC module should be use both whit kernel or none.
* If you are using a kernel, the adc drive does not wait the finish of
* conversion but use a singal every time a required conversion are
* ended. This signal wake up a process that return a result of
* conversion. Otherwise, if you not use a kernl, this module wait
* whit a loop the finishing of conversion.
*
*
* \author Daniele Basile <asterix@develer.com>
*/
#include "adc_stm32.h"
#include <cpu/irq.h>
#include "cfg/cfg_adc.h"
#include "cfg/cfg_proc.h"
#include "cfg/cfg_signal.h"
#include <cfg/macros.h>
#include <cfg/compiler.h>
#include <cfg/debug.h>
// Define log settings for cfg/log.h.
#define LOG_LEVEL ADC_LOG_LEVEL
#define LOG_FORMAT ADC_LOG_FORMAT
#include <cfg/log.h>
#include <drv/adc.h>
#include <drv/clock_stm32.h>
#include <drv/gpio_stm32.h>
#include <io/stm32.h>
struct stm32_adc *adc = (struct stm32_adc *)ADC1_BASE;
#if CONFIG_KERN
#include <cfg/module.h>
#include <kern/proc.h>
#include <kern/signal.h>
#include <drv/irq_cm3.h>
#if !CONFIG_KERN_SIGNALS
#error Signals must be active to use ADC with kernel
#endif
/* Signal adc convertion end */
#define SIG_ADC_COMPLETE SIG_USER0
/* ADC waiting process */
static struct Process *adc_process;
/**
* ADC ISR.
* Simply signal the adc process that convertion is complete.
*/
static DECLARE_ISR(adc_conversion_end_irq)
{
sig_post(adc_process, SIG_ADC_COMPLETE);
/* Clear the status bit */
adc->SR &= ~BV(SR_EOC);
}
static void adc_enable_irq(void)
{
/* Register the IRQ handler */
sysirq_setHandler(ADC_IRQHANDLER, adc_conversion_end_irq);
adc->CR1 |= BV(CR1_EOCIE);
}
#endif /* CONFIG_KERN */
/**
* Select mux channel \a ch.
* Generally the stm32 cpu family allow us to program the order
* of adc channel that we want to read.
* In this driver implementation we put as fist channel to read the
* select ones.
*/
void adc_hw_select_ch(uint8_t ch)
{
/* We sample only from one channel */
adc->SQR1 |= BV(SQR1_SQ_LEN_SHIFT);
adc->SQR3 = (ch & SQR3_SQ_MASK);
}
/**
* Start an ADC convertion.
* If a kernel is present, preempt until convertion is complete, otherwise
* a busy wait on ADC_DRDY bit is done.
*/
uint16_t adc_hw_read(void)
{
#if CONFIG_KERN
/* Ensure ADC is not already in use by another process */
ASSERT(adc_process == NULL);
adc_process = proc_current();
#endif
/* Start convertion */
adc->CR2 |= CR2_EXTTRIG_SWSTRT_SET;
#if CONFIG_KERN
/* Ensure IRQs enabled. */
IRQ_ASSERT_ENABLED();
sig_wait(SIG_ADC_COMPLETE);
/* Prevent race condition in case of preemptive kernel */
uint16_t ret = adc->DR;
MEMORY_BARRIER;
adc_process = NULL;
return ret;
#else
/* Wait in polling until conversion is done */
while (!(adc->SR & BV(SR_EOC)));
/* Return the last converted data */
return (adc->DR);
#endif
}
/**
* Init ADC hardware.
*/
void adc_hw_init(void)
{
RCC->APB2ENR |= (RCC_APB2_GPIOA | RCC_APB2_GPIOB | RCC_APB2_GPIOC);
RCC->APB2ENR |= RCC_APB2_ADC1;
/* Reset registry */
adc->CR1 = 0;
adc->CR2 = 0;
adc->SQR1 = 0;
adc->SQR2 = 0;
adc->SQR3 = 0;
/* Calibrate ADC */
adc->CR2 |= BV(CR2_RTSCAL);
adc->CR2 |= BV(CR2_CAL);
/* Wait in polling until calibration is done */
while (adc->CR2 & BV(CR2_CAL));
/*
* Configure ADC
* - Regular mode
* - Wake up adc
* - Wake up temperature and Vrefint
*/
adc->CR2 |= (BV(CR2_ADON) | ADC_EXTERNALTRIGCONV_NONE | BV(CR2_TSVREFE));
/* Set 17.1usec sampling time*/
adc->SMPR1 |= ((ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH17) |
(ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH16) |
(ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH15) |
(ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH14) |
(ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH13) |
(ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH12) |
(ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH11) |
(ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH10));
adc->SMPR2 |= ((ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH9) |
(ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH8) |
(ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH7) |
(ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH6) |
(ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH5) |
(ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH4) |
(ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH3) |
(ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH2) |
(ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH1) |
(ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH0));
#if CONFIG_KERN
adc_enable_irq();
#endif
}

View file

@ -0,0 +1,65 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief ADC hardware-specific definition
*
* \author Daniele Basile <asterix@develer.com>
*/
#ifndef DRV_ADC_STM32_H
#define DRV_ADC_STM32_H
#include <hw/hw_cpufreq.h>
#include "cfg/cfg_adc.h"
#include <cfg/compiler.h>
/**
* ADC config define.
*/
#define ADC_MUX_MAXCH 17 //Max number of channel for ADC.
#define ADC_BITS 12 //Bit resolution for ADC converter.
/**
* Init the ADC pins.
* Implement it if necessary.
*/
#define ADC_INIT_PINS() \
do { \
} while (0)
void adc_hw_select_ch(uint8_t ch);
uint16_t adc_hw_read(void);
void adc_hw_init(void);
#endif /* DRV_ADC_STM32_H */

View file

@ -0,0 +1,51 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Low-level Clock module for ARM Cortex-m3 (interface).
*
* \author Daniele Basile <asterix@develer.com>
*
*/
#include <cpu/detect.h>
#if CPU_CM3_LM3S
#include "clock_lm3s.h"
#elif CPU_CM3_STM32
#include "clock_stm32.h"
#elif CPU_CM3_SAM3
#include "clock_sam3.h"
/*#elif Add other Cortex-M3 CPUs here */
#else
#error Unknown CPU
#endif

View file

@ -0,0 +1,199 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief LM3S1968 Clocking driver.
*
* \author Andrea Righi <arighi@develer.com>
*/
#include "clock_lm3s.h"
#include <cfg/compiler.h>
#include <cfg/debug.h>
#include <io/lm3s.h>
/* The PLL VCO frequency is 400 MHz */
#define PLL_VCO 400000000UL
/* Extract the system clock divisor from the RCC register */
#define RCC_TO_DIV(rcc) \
(((rcc & SYSCTL_RCC_SYSDIV_MASK) >> \
SYSCTL_RCC_SYSDIV_SHIFT) + 1)
/*
* Very small delay: each loop takes 3 cycles.
*/
void NAKED lm3s_busyWait(unsigned long iterations)
{
register uint32_t __n asm("r0") = iterations;
asm volatile (
"1: subs r0, #1\n\t"
"bne 1b\n\t"
"bx lr\n\t"
: : "r"(__n) : "memory", "cc");
}
INLINE unsigned long clock_get_rate(void)
{
reg32_t rcc = HWREG(SYSCTL_RCC);
return rcc & SYSCTL_RCC_USESYSDIV ?
PLL_VCO / 2 / RCC_TO_DIV(rcc) : PLL_VCO;
}
/*
* Try to evaluate the correct SYSDIV value depending on the desired CPU
* frequency.
*/
INLINE int evaluate_sysdiv(unsigned long freq)
{
int i;
/*
* NOTE: with BYPASS=0, SYSDIV < 3 are reserved values (see LM3S1968
* Microcontroller DATASHEET, p.78).
*/
for (i = 3; i < 16; i++)
if (freq >= (PLL_VCO / 2 / (i + 1)))
break;
return i;
}
void clock_init(void)
{
reg32_t rcc, rcc2;
unsigned long clk;
int i;
/*
* PLL may not function properly at default LDO setting.
*
* Description:
*
* In designs that enable and use the PLL module, unstable device
* behavior may occur with the LDO set at its default of 2.5 volts or
* below (minimum of 2.25 volts). Designs that do not use the PLL
* module are not affected.
*
* Workaround: Prior to enabling the PLL module, it is recommended that
* the default LDO voltage setting of 2.5 V be adjusted to 2.75 V using
* the LDO Power Control (LDOPCTL) register.
*
* Silicon Revision Affected: A1, A2
*
* See also: Stellaris LM3S1968 A2 Errata documentation.
*/
if (REVISION_IS_A1 | REVISION_IS_A2)
HWREG(SYSCTL_LDOPCTL) = SYSCTL_LDOPCTL_2_75V;
rcc = HWREG(SYSCTL_RCC);
rcc2 = HWREG(SYSCTL_RCC2);
/*
* Step #1: bypass the PLL and system clock divider by setting the
* BYPASS bit and clearing the USESYS bit in the RCC register. This
* configures the system to run off a raw clock source (using the
* main oscillator or internal oscillator) and allows for the new PLL
* configuration to be validated before switching the system clock to
* the PLL.
*/
rcc |= SYSCTL_RCC_BYPASS;
rcc &= ~SYSCTL_RCC_USESYSDIV;
rcc2 |= SYSCTL_RCC2_BYPASS2;
/* Write back RCC/RCC2 registers */
HWREG(SYSCTL_RCC) = rcc;
HWREG(SYSCTL_RCC) = rcc2;
lm3s_busyWait(16);
/*
* Step #2: select the crystal value (XTAL) and oscillator source
* (OSCSRC), and clear the PWRDN bit in RCC/RCC2. Setting the XTAL
* field automatically pulls valid PLL configuration data for the
* appropriate crystal, and clearing the PWRDN bit powers and enables
* the PLL and its output.
*/
/* Enable the main oscillator first. */
rcc &= ~(SYSCTL_RCC_IOSCDIS | SYSCTL_RCC_MOSCDIS);
rcc |= SYSCTL_RCC_IOSCDIS;
/* Do not override RCC register fields */
rcc2 &= ~SYSCTL_RCC2_USERCC2;
rcc &= ~(SYSCTL_RCC_XTAL_M | SYSCTL_RCC_OSCSRC_M | SYSCTL_RCC_PWRDN);
rcc |= XTAL_FREQ | SYSCTL_RCC_OSCSRC_MAIN;
/* Clear the PLL lock interrupt. */
HWREG(SYSCTL_MISC) = SYSCTL_INT_PLL_LOCK;
HWREG(SYSCTL_RCC) = rcc;
HWREG(SYSCTL_RCC) = rcc2;
lm3s_busyWait(16);
/*
* Step #3: select the desired system divider (SYSDIV) in RCC/RCC2 and
* set the USESYS bit in RCC. The SYSDIV field determines the system
* frequency for the microcontroller.
*/
rcc &= ~(SYSCTL_RCC_SYSDIV_M | SYSCTL_RCC_USESYSDIV);
clk = PLL_VCO / 2;
for (i = 3; i < 16; i++)
if (CPU_FREQ >= (clk / (i + 1)))
break;
rcc |= SYSCTL_RCC_USESYSDIV |
(evaluate_sysdiv(CPU_FREQ) << SYSCTL_RCC_SYSDIV_SHIFT);
/*
* Step #4: wait for the PLL to lock by polling the PLLLRIS bit in the
* Raw Interrupt Status (RIS) register.
*/
for (i = 0; i < 32768; i++)
if (HWREG(SYSCTL_RIS) & SYSCTL_INT_PLL_LOCK)
break;
/*
* Step #5: enable use of the PLL by clearing the BYPASS bit in
* RCC/RCC2.
*/
rcc &= ~SYSCTL_RCC_BYPASS;
HWREG(SYSCTL_RCC) = rcc;
lm3s_busyWait(16);
}

View file

@ -0,0 +1,48 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Low-level clocking driver for LM3S1968.
*
* \author Andrea Righi <arighi@develer.com>
*/
#ifndef DRV_LM3S1968_CLOCK_H
#define DRV_LM3S1968_CLOCK_H
/* Crystal frequency attached to the main oscillator. */
#define XTAL_FREQ SYSCTL_RCC_XTAL_8MHZ
extern void lm3s_busyWait(unsigned long iterations);
void clock_init(void);
#endif /* DRV_LM3S1968_CLOCK_H */

View file

@ -0,0 +1,146 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Atmel SAM3 clock setup.
*
* \author Stefano Fedrigo <aleph@develer.com>
*/
#include "clock_sam3.h"
#include <cfg/compiler.h>
#include <cfg/macros.h>
#include <io/sam3.h>
/* Frequency of board main oscillator */
#define BOARDOSC_FREQ 12000000
/* Timer countdown timeout for clock initialization operations */
#define CLOCK_TIMEOUT 0xFFFFFFFF
#if CPU_FREQ == 84000000 || CPU_FREQ == 48000000
INLINE uint32_t evaluate_pll(void)
{
return CKGR_PLLR_MUL(CPU_FREQ / BOARDOSC_FREQ * 2 - 1) | CKGR_PLLR_DIV(2);
}
#else
#warning CPU clock frequency non-standard setting: multiplier and divider values \
will be computed at runtime: effective computed frequency could be different \
from expected.
/*
* Try to evaluate the correct divider and multiplier value depending
* on the desired CPU frequency.
*
* We try all combinations in a certain range of divider and multiplier
* values. Start with higher multipliers and divisors, generally better.
*/
INLINE uint32_t evaluate_pll(void)
{
int mul, div, best_mul, best_div;
int best_delta = CPU_FREQ;
int freq = 0;
for (mul = 13; mul > 0; mul--)
{
for (div = 24; div > 0; div--)
{
freq = BOARDOSC_FREQ / div * (1 + mul);
if (ABS((int)CPU_FREQ - freq) < best_delta) {
best_delta = ABS((int)CPU_FREQ - freq);
best_mul = mul;
best_div = div;
}
}
}
return CKGR_PLLR_DIV(best_div) | CKGR_PLLR_MUL(best_mul);
}
#endif /* CPU_FREQ */
void clock_init(void)
{
uint32_t timeout;
/* Disable watchdog */
WDT_MR = BV(WDT_WDDIS);
/* Set wait states for flash access, needed for higher CPU clock rates */
EEFC0_FMR = EEFC_FMR_FWS(3);
#ifdef EEFC1_FMR
EEFC1_FMR = EEFC_FMR_FWS(3);
#endif
// Initialize main oscillator
if (!(CKGR_MOR & BV(CKGR_MOR_MOSCSEL)))
{
CKGR_MOR = CKGR_MOR_KEY(0x37) | CKGR_MOR_MOSCXTST(0x8)
| BV(CKGR_MOR_MOSCRCEN) | BV(CKGR_MOR_MOSCXTEN);
timeout = CLOCK_TIMEOUT;
while (!(PMC_SR & BV(PMC_SR_MOSCXTS)) && --timeout);
}
// Switch to external oscillator
CKGR_MOR = CKGR_MOR_KEY(0x37) | CKGR_MOR_MOSCXTST(0x8)
| BV(CKGR_MOR_MOSCRCEN) | BV(CKGR_MOR_MOSCXTEN) | BV(CKGR_MOR_MOSCSEL);
timeout = CLOCK_TIMEOUT;
while (!(PMC_SR & BV(PMC_SR_MOSCXTS)) && --timeout);
// Initialize and enable PLL clock
CKGR_PLLR = evaluate_pll() | BV(CKGR_PLLR_STUCKTO1) | CKGR_PLLR_PLLCOUNT(0x2);
timeout = CLOCK_TIMEOUT;
while (!(PMC_SR & BV(PMC_SR_LOCK)) && --timeout);
PMC_MCKR = PMC_MCKR_CSS_MAIN_CLK;
timeout = CLOCK_TIMEOUT;
while (!(PMC_SR & BV(PMC_SR_MCKRDY)) && --timeout);
PMC_MCKR = PMC_MCKR_CSS_PLL_CLK;
timeout = CLOCK_TIMEOUT;
while (!(PMC_SR & BV(PMC_SR_MCKRDY)) && --timeout);
/* Enable clock on PIO for inputs */
// TODO: move this in gpio_init() for better power management?
pmc_periphEnable(PIOA_ID);
pmc_periphEnable(PIOB_ID);
pmc_periphEnable(PIOC_ID);
#ifdef PIOF_ID
pmc_periphEnable(PIOD_ID);
pmc_periphEnable(PIOE_ID);
pmc_periphEnable(PIOF_ID);
#endif
}

View file

@ -0,0 +1,43 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Low-level clocking driver for SAM3.
*
* \author Stefano Fedrigo <aleph@develer.com>
*/
#ifndef DRV_SAM3_CLOCK_H
#define DRV_SAM3_CLOCK_H
void clock_init(void);
#endif /* DRV_SAM3_CLOCK_H */

View file

@ -0,0 +1,151 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief STM32 Clocking driver.
*
* \author Andrea Righi <arighi@develer.com>
*/
#include "clock_stm32.h"
#include <cfg/compiler.h>
#include <cfg/debug.h>
#include <io/stm32.h>
struct RCC *RCC;
INLINE int rcc_get_flag_status(uint32_t flag)
{
uint32_t id;
reg32_t reg;
/* Get the RCC register index */
id = flag >> 5;
/* The flag to check is in CR register */
if (id == 1)
reg = RCC->CR;
/* The flag to check is in BDCR register */
else if (id == 2)
reg = RCC->BDCR;
/* The flag to check is in CSR register */
else
reg = RCC->CSR;
/* Get the flag position */
id = flag & FLAG_MASK;
return reg & (1 << id);
}
INLINE uint16_t pll_clock(void)
{
unsigned int div, mul;
/* Hopefully this is evaluate at compile time... */
for (div = 2; div; div--)
for (mul = 2; mul <= 16; mul++)
if (CPU_FREQ <= (PLL_VCO / div * mul))
break;
return mul << 8 | div;
}
INLINE void rcc_pll_config(void)
{
reg32_t reg = RCC->CFGR & CFGR_PLL_MASK;
/* Evaluate clock parameters */
uint16_t clock = pll_clock();
uint32_t pll_mul = ((clock >> 8) - 2) << 18;
uint32_t pll_div = ((clock & 0xff) << 1 | 1) << 16;
/* Set the PLL configuration bits */
reg |= pll_div | pll_mul;
/* Store the new value */
RCC->CFGR = reg;
/* Enable PLL */
*CR_PLLON_BB = 1;
}
INLINE void rcc_set_clock_source(uint32_t source)
{
reg32_t reg;
reg = RCC->CFGR & CFGR_SW_MASK;
reg |= source;
RCC->CFGR = reg;
}
void clock_init(void)
{
/* Initialize global RCC structure */
RCC = (struct RCC *)RCC_BASE;
/* Enable the internal oscillator */
*CR_HSION_BB = 1;
while (!rcc_get_flag_status(RCC_FLAG_HSIRDY));
/* Clock the system from internal HSI RC (8 MHz) */
rcc_set_clock_source(RCC_SYSCLK_HSI);
/* Enable external oscillator */
RCC->CR &= CR_HSEON_RESET;
RCC->CR &= CR_HSEBYP_RESET;
RCC->CR |= CR_HSEON_SET;
while (!rcc_get_flag_status(RCC_FLAG_HSERDY));
/* Initialize PLL according to CPU_FREQ */
rcc_pll_config();
while(!rcc_get_flag_status(RCC_FLAG_PLLRDY));
/* Configure USB clock (48MHz) */
*CFGR_USBPRE_BB = RCC_USBCLK_PLLCLK_1DIV5;
/* Configure ADC clock: PCLK2 (9MHz) */
RCC->CFGR &= CFGR_ADCPRE_RESET_MASK;
RCC->CFGR |= RCC_PCLK2_DIV8;
/* Configure system clock dividers: PCLK2 (72MHz) */
RCC->CFGR &= CFGR_PPRE2_RESET_MASK;
RCC->CFGR |= RCC_HCLK_DIV1 << 3;
/* Configure system clock dividers: PCLK1 (36MHz) */
RCC->CFGR &= CFGR_PPRE1_RESET_MASK;
RCC->CFGR |= RCC_HCLK_DIV2;
/* Configure system clock dividers: HCLK */
RCC->CFGR &= CFGR_HPRE_RESET_MASK;
RCC->CFGR |= RCC_SYSCLK_DIV1;
/* Set 1 wait state for the flash memory */
*(reg32_t *)FLASH_BASE = 0x12;
/* Clock the system from the PLL */
rcc_set_clock_source(RCC_SYSCLK_PLLCLK);
}

View file

@ -0,0 +1,276 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Low-level clocking driver for Cortex-M3 STM32.
*
* \author Andrea Righi <arighi@develer.com>
*/
#ifndef CLOCK_STM32_H
#define CLOCK_STM32_H
#include <cfg/compiler.h>
/* RCC registers bit address */
#define RCC_OFFSET (RCC_BASE - PERIPH_BASE)
/**
* CR Register
*/
/*\{*/
/* Alias word address of HSION bit */
#define CR_OFFSET (RCC_OFFSET + 0x00)
#define HSION_BITNUMBER 0x00
#define CR_HSION_BB ((reg32_t *)(PERIPH_BB_BASE + (CR_OFFSET * 32) + (HSION_BITNUMBER * 4)))
/* Alias word address of PLLON bit */
#define PLLON_BITNUMBER 0x18
#define CR_PLLON_BB ((reg32_t *)(PERIPH_BB_BASE + (CR_OFFSET * 32) + (PLLON_BITNUMBER * 4)))
/* Alias word address of CSSON bit */
#define CSSON_BITNUMBER 0x13
#define CR_CSSON_BB ((reg32_t *)(PERIPH_BB_BASE + (CR_OFFSET * 32) + (CSSON_BITNUMBER * 4)))
/*\}*/
/**
* CFGR Register
*/
/*\{*/
/* Alias word address of USBPRE bit */
#define CFGR_OFFSET (RCC_OFFSET + 0x04)
#define USBPRE_BITNUMBER 0x16
#define CFGR_USBPRE_BB ((reg32_t *)(PERIPH_BB_BASE + (CFGR_OFFSET * 32) + (USBPRE_BITNUMBER * 4)))
/*\}*/
/**
* BDCR Register
*/
/*\{*/
/* Alias word address of RTCEN bit */
#define BDCR_OFFSET (RCC_OFFSET + 0x20)
#define RTCEN_BITNUMBER 0x0F
#define BDCR_RTCEN_BB ((reg32_t *)(PERIPH_BB_BASE + (BDCR_OFFSET * 32) + (RTCEN_BITNUMBER * 4)))
/* Alias word address of BDRST bit */
#define BDRST_BITNUMBER 0x10
#define BDCR_BDRST_BB ((reg32_t *)(PERIPH_BB_BASE + (BDCR_OFFSET * 32) + (BDRST_BITNUMBER * 4)))
/*\}*/
/**
* CSR Register
*/
/*\{*/
/* Alias word address of LSION bit */
#define CSR_OFFSET (RCC_OFFSET + 0x24)
#define LSION_BITNUMBER 0x00
#define CSR_LSION_BB ((reg32_t *)(PERIPH_BB_BASE + (CSR_OFFSET * 32) + (LSION_BITNUMBER * 4)))
/*\}*/
/**
* RCC registers bit mask
*/
/*\{*/
/* CR register bit mask */
#define CR_HSEBYP_RESET (0xFFFBFFFF)
#define CR_HSEBYP_SET (0x00040000)
#define CR_HSEON_RESET (0xFFFEFFFF)
#define CR_HSEON_SET (0x00010000)
#define CR_HSITRIM_MASK (0xFFFFFF07)
/* CFGR register bit mask */
#define CFGR_PLL_MASK (0xFFC0FFFF)
#define CFGR_PLLMull_MASK (0x003C0000)
#define CFGR_PLLSRC_MASK (0x00010000)
#define CFGR_PLLXTPRE_MASK (0x00020000)
#define CFGR_SWS_MASK (0x0000000C)
#define CFGR_SW_MASK (0xFFFFFFFC)
#define CFGR_HPRE_RESET_MASK (0xFFFFFF0F)
#define CFGR_HPRE_SET_MASK (0x000000F0)
#define CFGR_PPRE1_RESET_MASK (0xFFFFF8FF)
#define CFGR_PPRE1_SET_MASK (0x00000700)
#define CFGR_PPRE2_RESET_MASK (0xFFFFC7FF)
#define CFGR_PPRE2_SET_MASK (0x00003800)
#define CFGR_ADCPRE_RESET_MASK (0xFFFF3FFF)
#define CFGR_ADCPRE_SET_MASK (0x0000C000)
/* CSR register bit mask */
#define CSR_RVMF_SET (0x01000000)
/* RCC Flag MASK */
#define FLAG_MASK (0x1F)
/* Typical VALUE of the HSI in Hz */
#define HSI_VALUE (8000000)
/* BDCR register base address */
#define BDCR_BASE (PERIPH_BASE + BDCR_OFFSET)
/* RCC Flag */
#define RCC_FLAG_HSIRDY (0x20)
#define RCC_FLAG_HSERDY (0x31)
#define RCC_FLAG_PLLRDY (0x39)
#define RCC_FLAG_LSERDY (0x41)
#define RCC_FLAG_LSIRDY (0x61)
#define RCC_FLAG_PINRST (0x7A)
#define RCC_FLAG_PORRST (0x7B)
#define RCC_FLAG_SFTRST (0x7C)
#define RCC_FLAG_IWDGRST (0x7D)
#define RCC_FLAG_WWDGRST (0x7E)
#define RCC_FLAG_LPWRRST (0x7F)
/* System clock source */
#define RCC_SYSCLK_HSI (0x00000000)
#define RCC_SYSCLK_HSE (0x00000001)
#define RCC_SYSCLK_PLLCLK (0x00000002)
/* PLL entry clock source */
#define RCC_PLL_HSI_DIV2 (0x00000000)
#define RCC_PLL_HSE_DIV1 (0x00010000)
#define RCC_PLL_HSE_DIV2 (0x00030000)
/* PLL multiplication factor */
#define RCC_PLLMUL_2 (0x00000000)
#define RCC_PLLMUL_3 (0x00040000)
#define RCC_PLLMUL_4 (0x00080000)
#define RCC_PLLMUL_5 (0x000C0000)
#define RCC_PLLMUL_6 (0x00100000)
#define RCC_PLLMUL_7 (0x00140000)
#define RCC_PLLMUL_8 (0x00180000)
#define RCC_PLLMUL_9 (0x001C0000)
#define RCC_PLLMUL_10 (0x00200000)
#define RCC_PLLMUL_11 (0x00240000)
#define RCC_PLLMUL_12 (0x00280000)
#define RCC_PLLMUL_13 (0x002C0000)
#define RCC_PLLMUL_14 (0x00300000)
#define RCC_PLLMUL_15 (0x00340000)
#define RCC_PLLMUL_16 (0x00380000)
/* APB1/APB2 clock source */
#define RCC_HCLK_DIV1 (0x00000000)
#define RCC_HCLK_DIV2 (0x00000400)
#define RCC_HCLK_DIV4 (0x00000500)
#define RCC_HCLK_DIV8 (0x00000600)
#define RCC_HCLK_DIV16 (0x00000700)
/* USB clock source */
#define RCC_USBCLK_PLLCLK_1DIV5 (0x00)
#define RCC_USBCLK_PLLCLK_DIV1 (0x01)
/* ADC clock source */
#define RCC_PCLK2_DIV2 (0x00000000)
#define RCC_PCLK2_DIV4 (0x00004000)
#define RCC_PCLK2_DIV6 (0x00008000)
#define RCC_PCLK2_DIV8 (0x0000C000)
/* AHB clock source */
#define RCC_SYSCLK_DIV1 (0x00000000)
#define RCC_SYSCLK_DIV2 (0x00000080)
#define RCC_SYSCLK_DIV4 (0x00000090)
#define RCC_SYSCLK_DIV8 (0x000000A0)
#define RCC_SYSCLK_DIV16 (0x000000B0)
#define RCC_SYSCLK_DIV64 (0x000000C0)
#define RCC_SYSCLK_DIV128 (0x000000D0)
#define RCC_SYSCLK_DIV256 (0x000000E0)
#define RCC_SYSCLK_DIV512 (0x000000F0)
/*\}*/
/**
* RCC register: APB1 peripheral
*/
/*\{*/
#define RCC_APB1_TIM2 (0x00000001)
#define RCC_APB1_TIM3 (0x00000002)
#define RCC_APB1_TIM4 (0x00000004)
#define RCC_APB1_WWDG (0x00000800)
#define RCC_APB1_SPI2 (0x00004000)
#define RCC_APB1_USART2 (0x00020000)
#define RCC_APB1_USART3 (0x00040000)
#define RCC_APB1_I2C1 (0x00200000)
#define RCC_APB1_I2C2 (0x00400000)
#define RCC_APB1_USB (0x00800000)
#define RCC_APB1_CAN (0x02000000)
#define RCC_APB1_BKP (0x08000000)
#define RCC_APB1_PWR (0x10000000)
#define RCC_APB1_ALL (0x1AE64807)
/*\}*/
/**
* RCC register: APB2 peripheral
*/
/*\{*/
#define RCC_APB2_AFIO (0x00000001)
#define RCC_APB2_GPIOA (0x00000004)
#define RCC_APB2_GPIOB (0x00000008)
#define RCC_APB2_GPIOC (0x00000010)
#define RCC_APB2_GPIOD (0x00000020)
#define RCC_APB2_GPIOE (0x00000040)
#define RCC_APB2_ADC1 (0x00000200)
#define RCC_APB2_ADC2 (0x00000400)
#define RCC_APB2_TIM1 (0x00000800)
#define RCC_APB2_SPI1 (0x00001000)
#define RCC_APB2_USART1 (0x00004000)
#define RCC_APB2_ALL (0x00005E7D)
/**
* RCC register: BCDR
*/
#define RCC_BDCR_LSEON (0x00000001)
#define RCC_BDCR_LSERDY (0x00000002)
#define RCC_BDCR_RTCSEL (0x00000300)
#define RCC_BDCR_RTCEN (0x00008000)
/*\}*/
/* Crystal frequency of the main oscillator (8MHz) */
#define PLL_VCO 8000000
/* Reset and Clock Controller registers */
struct RCC
{
reg32_t CR;
reg32_t CFGR;
reg32_t CIR;
reg32_t APB2RSTR;
reg32_t APB1RSTR;
reg32_t AHBENR;
reg32_t APB2ENR;
reg32_t APB1ENR;
reg32_t BDCR;
reg32_t CSR;
};
/* RCC registers base */
extern struct RCC *RCC;
void clock_init(void);
#endif /* CLOCK_STM32_h */

View file

@ -0,0 +1,46 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2011 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Low-level DAC module for Cortex-m3.
*
* \author Daniele Basile <asterix@develer.com>
*
*/
#include <cpu/detect.h>
#if CPU_CM3_SAM3X
#include "adc_sam3.h"
/*#elif Add other ARM families here */
#else
#error Unknown CPU
#endif

View file

@ -0,0 +1,334 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2011 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief DAC hardware-specific implementation
*
* \author Daniele Basile <asterix@develer.com>
*/
#include "dac_sam3.h"
#include "cfg/cfg_dac.h"
#include <cfg/macros.h>
#include <cfg/compiler.h>
// Define log settings for cfg/log.h.
#define LOG_LEVEL DAC_LOG_LEVEL
#define LOG_FORMAT DAC_LOG_FORMAT
#include <cfg/log.h>
#include <drv/dac.h>
#include <cpu/irq.h>
#include <drv/irq_cm3.h>
#include <cpu/types.h>
#include <mware/event.h>
#include <io/cm3.h>
#include <string.h>
struct DacHardware
{
uint16_t channels;
uint32_t rate;
bool end;
};
struct DacHardware dac_hw;
/* We use event to signal the end of conversion */
static Event buff_emtpy;
#if CONFIG_DAC_TIMER == DACC_TRGSEL_TIO_CH0 /* Select Timer counter TIO Channel 0 */
#define DAC_TC_ID TC0_ID
#define DAC_TC_CCR TC0_CCR0
#define DAC_TC_IDR TC0_IDR0
#define DAC_TC_CMR TC0_CMR0
#define DAC_TC_SR TC0_SR0
#define DAC_TC_RA TC0_RA0
#define DAC_TC_RC TC0_RC0
#elif CONFIG_DAC_TIMER == DACC_TRGSEL_TIO_CH1 /* Select Timer counter TIO Channel 1 */
#define DAC_TC_ID TC1_ID
#define DAC_TC_CCR TC0_CCR1
#define DAC_TC_IDR TC0_IDR1
#define DAC_TC_CMR TC0_CMR1
#define DAC_TC_SR TC0_SR1
#define DAC_TC_RA TC0_RA1
#define DAC_TC_RC TC0_RC1
#elif CONFIG_DAC_TIMER == DACC_TRGSEL_TIO_CH2 /* Select Timer counter TIO Channel 2 */
#define DAC_TC_ID TC2_ID
#define DAC_TC_CCR TC0_CCR2
#define DAC_TC_IDR TC0_IDR2
#define DAC_TC_CMR TC0_CMR2
#define DAC_TC_SR TC0_SR2
#define DAC_TC_RA TC0_RA2
#define DAC_TC_RC TC0_RC2
#elif CONFIG_DAC_TIMER == DACC_TRGSEL_PWM0 || CONFIG_DAC_TIMER == DACC_TRGSEL_PWM1
#error unimplemented pwm triger select.
#endif
INLINE void tc_init(void)
{
pmc_periphEnable(DAC_TC_ID);
/* Disable TC clock */
DAC_TC_CCR = BV(TC_CCR_CLKDIS);
/* Disable interrupts */
DAC_TC_IDR = 0xFFFFFFFF;
/* Clear status register */
volatile uint32_t dummy = DAC_TC_SR;
(void)dummy;
/*
* Setup the timer counter:
* - select clock TCLK1 (MCK/2)
* - enable wave form mode
* - RA compare effect SET
* - RC compare effect CLEAR
* - UP mode with automatic trigger on RC Compare
*/
DAC_TC_CMR = TC_TIMER_CLOCK1 | BV(TC_CMR_WAVE) | TC_CMR_ACPA_SET | TC_CMR_ACPC_CLEAR | BV(TC_CMR_CPCTRG);
/* Setup the pio: TODO: fix for more generic */
PIOB_PDR = BV(25);
PIO_PERIPH_SEL(PIOB_BASE, BV(25), PIO_PERIPH_B);
}
INLINE void tc_setup(uint32_t freq, size_t n_sample)
{
/*
* Compute the sample frequency
* the RC counter will update every MCK/2 (see above)
* so to convert one sample at the user freq we generate
* the trigger every TC_CLK / (numer_of_sample * user_freq)
* where TC_CLK = MCK / 2.
*/
uint32_t rc = DIV_ROUND((CPU_FREQ / 2), n_sample * freq);
DAC_TC_RC = rc;
/* generate the square wave with duty = 50% */
DAC_TC_RA = DIV_ROUND(50 * rc, 100);
}
INLINE void tc_start(void)
{
DAC_TC_CCR = BV(TC_CCR_CLKEN)| BV(TC_CCR_SWTRG);
}
INLINE void tc_stop(void)
{
DAC_TC_CCR = BV(TC_CCR_CLKDIS);
}
static int sam3x_dac_write(struct Dac *dac, unsigned channel, uint16_t sample)
{
(void)dac;
ASSERT(channel <= DAC_MAXCH);
DACC_MR |= (channel << DACC_USER_SEL_SHIFT) & DACC_USER_SEL_MASK;
DACC_CHER |= BV(channel);
DACC_CDR = sample ;
return 0;
}
static void sam3x_dac_setCh(struct Dac *dac, uint32_t mask)
{
/* we have only the ch0 and ch1 */
ASSERT(mask < BV(3));
dac->hw->channels = mask;
if (mask & BV(DACC_CH0))
DACC_MR |= (DACC_CH0 << DACC_USER_SEL_SHIFT) & DACC_USER_SEL_MASK;
if (mask & BV(DACC_CH1))
DACC_MR |= (DACC_CH1 << DACC_USER_SEL_SHIFT) & DACC_USER_SEL_MASK;
DACC_CHER |= mask;
}
static void sam3x_dac_setSampleRate(struct Dac *dac, uint32_t rate)
{
/* Eneble hw trigger */
DACC_MR |= BV(DACC_TRGEN) | (CONFIG_DAC_TIMER << DACC_TRGSEL_SHIFT);
dac->hw->rate = rate;
}
static void sam3x_dac_conversion(struct Dac *dac, void *buf, size_t len)
{
/* setup timer and start it */
tc_setup(dac->hw->rate, len);
tc_start();
/* Setup dma and start it */
DACC_TPR = (uint32_t)buf;
DACC_TCR = len;
DACC_PTCR |= BV(DACC_PTCR_TXTEN);
}
static uint16_t *sample_buff;
static size_t next_idx = 0;
static size_t chunk_size = 0;
static size_t remaing_size = 0;
static DECLARE_ISR(irq_dac)
{
if (DACC_ISR & BV(DACC_ENDTX))
{
if (remaing_size > 0)
{
DACC_TNPR = (uint32_t)&sample_buff[next_idx];
DACC_TNCR = chunk_size;
remaing_size -= chunk_size;
next_idx += chunk_size;
}
else
/* Clear the pending irq when the dma ends the conversion */
DACC_TCR = 1;
}
event_do(&buff_emtpy);
}
static bool sam3x_dac_isFinished(struct Dac *dac)
{
return dac->hw->end;
}
static void sam3x_dac_start(struct Dac *dac, void *_buf, size_t len, size_t slice_len)
{
ASSERT(dac);
ASSERT(len >= slice_len);
/* Reset the previous status. */
dac->hw->end = false;
sample_buff = (uint16_t *)_buf;
next_idx = 0;
chunk_size = slice_len;
remaing_size = len;
/* Program the dma with the first and second chunk of samples and update counter */
dac->ctx.callback(dac, &sample_buff[0], chunk_size);
DACC_TPR = (uint32_t)&sample_buff[0];
DACC_TCR = chunk_size;
remaing_size -= chunk_size;
next_idx += chunk_size;
if (chunk_size <= remaing_size)
{
dac->ctx.callback(dac, &sample_buff[next_idx], chunk_size);
DACC_TNPR = (uint32_t)&sample_buff[next_idx];
DACC_TNCR = chunk_size;
remaing_size -= chunk_size;
next_idx += chunk_size;
}
DACC_PTCR |= BV(DACC_PTCR_TXTEN);
DACC_IER = BV(DACC_ENDTX);
/* Set up timer and trig the conversions */
tc_setup(dac->hw->rate, len);
tc_start();
while (1)
{
event_wait(&buff_emtpy);
if (remaing_size <= 0)
{
DAC_TC_CCR = BV(TC_CCR_CLKDIS);
dac->hw->end = true;
next_idx = 0;
chunk_size = 0;
remaing_size = 0;
break;
}
dac->ctx.callback(dac, &sample_buff[next_idx], chunk_size);
}
}
static void sam3x_dac_stop(struct Dac *dac)
{
dac->hw->end = false;
/* Disable the irq, timer and channel */
DACC_IDR = BV(DACC_ENDTX);
DACC_PTCR |= BV(DACC_PTCR_TXTDIS);
DAC_TC_CCR = BV(TC_CCR_CLKDIS);
}
void dac_init(struct Dac *dac)
{
/* Initialize the dataready event */
event_initGeneric(&buff_emtpy);
/* Fill the virtual table */
dac->ctx.write = sam3x_dac_write;
dac->ctx.setCh = sam3x_dac_setCh;
dac->ctx.setSampleRate = sam3x_dac_setSampleRate;
dac->ctx.conversion = sam3x_dac_conversion;
dac->ctx.isFinished = sam3x_dac_isFinished;
dac->ctx.start = sam3x_dac_start;
dac->ctx.stop = sam3x_dac_stop;
DB(dac->ctx._type = DAC_SAM3X;)
dac->hw = &dac_hw;
/* Clock DAC peripheral */
pmc_periphEnable(DACC_ID);
/* Reset hw */
DACC_CR |= BV(DACC_SWRST);
DACC_MR = 0;
/* Configure the dac */
DACC_MR |= (CONFIG_DAC_REFRESH << DACC_REFRESH_SHIFT) & DACC_REFRESH_MASK;
DACC_MR |= (CONFIG_DAC_STARTUP << DACC_STARTUP_SHIFT) & DACC_STARTUP_MASK;
DACC_IDR = 0xFFFFFFFF;
sysirq_setHandler(INT_DACC, irq_dac);
tc_init();
}

View file

@ -0,0 +1,51 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2011 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief DAC hardware-specific definition
*
* \author Daniele Basile <asterix@develer.com>
*/
#ifndef DRV_DAC_SAM3_H
#define DRV_DAC_SAM3_H
#include <drv/dac.h>
/**
* DAC config define.
*/
#define DAC_MAXCH 2 //Max number of channel for ADC.
#define DAC_BITS 12 //Bit resolution for ADC converter.
#define DAC_SAM3X MAKE_ID('S', 'D', '3', 'X')
#endif /* DRV_DAC_SAM3_H */

View file

@ -0,0 +1,533 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010,2011 Develer S.r.l. (http://www.develer.com/)
* All Rights Reserved.
* -->
*
* \brief EMAC driver for AT91SAM family with Davicom 9161A phy.
*
* \author Daniele Basile <asterix@develer.com>
* \author Andrea Righi <arighi@develer.com>
* \author Stefano Fedrigo <aleph@develer.com>
*/
#include "cfg/cfg_eth.h"
#define LOG_LEVEL ETH_LOG_LEVEL
#define LOG_FORMAT ETH_LOG_FORMAT
#include <cfg/log.h>
#include <cfg/debug.h>
#include <cfg/log.h>
#include <cfg/macros.h>
#include <cfg/compiler.h>
// TODO: unify includes
//#include <io/at91sam7.h>
//#include <io/arm.h>
//#include <io/include.h>
#include <io/sam3.h>
#include <drv/irq_cm3.h>
#include <cpu/power.h>
#include <cpu/types.h>
#include <cpu/irq.h>
#include <drv/timer.h>
#include <drv/eth.h>
#include <mware/event.h>
#include <string.h>
#include "eth_sam3.h"
#define EMAC_RX_INTS (BV(EMAC_RCOMP) | BV(EMAC_ROVR) | BV(EMAC_RXUBR))
#define EMAC_TX_INTS (BV(EMAC_TCOMP) | BV(EMAC_TXUBR) | BV(EMAC_RLEX))
/* Silent Doxygen bug... */
#ifndef __doxygen__
/*
* NOTE: this buffer should be declared as 'volatile' because it is read by the
* hardware. However, this is accessed only via memcpy() that should guarantee
* coherency when copying from/to buffers.
*/
static uint8_t tx_buf[EMAC_TX_BUFFERS * EMAC_TX_BUFSIZ] ALIGNED(8);
static volatile BufDescriptor tx_buf_tab[EMAC_TX_DESCRIPTORS] ALIGNED(8);
/*
* NOTE: this buffer should be declared as 'volatile' because it is wrote by
* the hardware. However, this is accessed only via memcpy() that should
* guarantee coherency when copying from/to buffers.
*/
static uint8_t rx_buf[EMAC_RX_BUFFERS * EMAC_RX_BUFSIZ] ALIGNED(8);
static volatile BufDescriptor rx_buf_tab[EMAC_RX_DESCRIPTORS] ALIGNED(8);
#endif
static int tx_buf_idx;
static int tx_buf_offset;
static int rx_buf_idx;
static Event recv_wait, send_wait;
static DECLARE_ISR(emac_irqHandler)
{
/* Read interrupt status and disable interrupts. */
uint32_t isr = EMAC_ISR;
/* Receiver interrupt */
if ((isr & EMAC_RX_INTS))
{
if (isr & BV(EMAC_RCOMP))
event_do(&recv_wait);
EMAC_RSR = EMAC_RX_INTS;
}
/* Transmitter interrupt */
if (isr & EMAC_TX_INTS)
{
if (isr & BV(EMAC_TCOMP))
event_do(&send_wait);
EMAC_TSR = EMAC_TX_INTS;
}
//AIC_EOICR = 0;
}
/*
* \brief Read contents of PHY register.
*
* \param reg PHY register number.
*
* \return Contents of the specified register.
*/
static uint16_t phy_hw_read(uint8_t phy_addr, reg8_t reg)
{
// PHY read command.
EMAC_MAN = EMAC_SOF | EMAC_RW_READ
| ((phy_addr << EMAC_PHYA_SHIFT) & EMAC_PHYA)
| ((reg << EMAC_REGA_SHIFT) & EMAC_REGA)
| EMAC_CODE;
// Wait until PHY logic completed.
while (!(EMAC_NSR & BV(EMAC_IDLE)))
cpu_relax();
// Get data from PHY maintenance register.
return (uint16_t)(EMAC_MAN & EMAC_DATA);
}
#if 0
/*
* \brief Write value to PHY register.
*
* \param reg PHY register number.
* \param val Value to write.
*/
static void phy_hw_write(uint8_t phy_addr, reg8_t reg, uint16_t val)
{
// PHY write command.
EMAC_MAN = EMAC_SOF | EMAC_RW_WRITE
| ((phy_addr << EMAC_PHYA_SHIFT) & EMAC_PHYA)
| ((reg << EMAC_REGA_SHIFT) & EMAC_REGA)
| EMAC_CODE | val;
// Wait until PHY logic completed.
while (!(EMAC_NSR & BV(EMAC_IDLE)))
cpu_relax();
}
#endif
/*
* Check link speed and duplex as negotiated by the PHY
* and configure CPU EMAC accordingly.
* Requires active PHY maintenance mode.
*/
static void emac_autoNegotiation(void)
{
uint16_t reg;
time_t start;
// Wait for auto-negotation to complete
start = timer_clock();
do {
reg = phy_hw_read(NIC_PHY_ADDR, NIC_PHY_BMSR);
if (timer_clock() - start > 2000)
{
kprintf("eth error: auto-negotiation timeout\n");
return;
}
}
while (!(reg & NIC_PHY_BMSR_ANCOMPL));
reg = phy_hw_read(NIC_PHY_ADDR, NIC_PHY_ANLPAR);
if ((reg & NIC_PHY_ANLPAR_TX_FDX) || (reg & NIC_PHY_ANLPAR_TX_HDX))
{
LOG_INFO("eth: 100BASE-TX\n");
EMAC_NCFGR |= BV(EMAC_SPD);
}
else
{
LOG_INFO("eth: 10BASE-T\n");
EMAC_NCFGR &= ~BV(EMAC_SPD);
}
if ((reg & NIC_PHY_ANLPAR_TX_FDX) || (reg & NIC_PHY_ANLPAR_10_FDX))
{
LOG_INFO("eth: full duplex\n");
EMAC_NCFGR |= BV(EMAC_FD);
}
else
{
LOG_INFO("eth: half duplex\n");
EMAC_NCFGR &= ~BV(EMAC_FD);
}
}
static int emac_reset(void)
{
#if CPU_ARM_AT91
// Enable devices
PMC_PCER = BV(PIOA_ID);
PMC_PCER = BV(PIOB_ID);
PMC_PCER = BV(EMAC_ID);
// Disable TESTMODE and RMII
PIOB_PUDR = BV(PHY_RXDV_TESTMODE_BIT);
PIOB_PUDR = BV(PHY_COL_RMII_BIT);
// Disable PHY power down.
PIOB_PER = BV(PHY_PWRDN_BIT);
PIOB_OER = BV(PHY_PWRDN_BIT);
PIOB_CODR = BV(PHY_PWRDN_BIT);
#else
pmc_periphEnable(PIOA_ID);
pmc_periphEnable(PIOB_ID);
pmc_periphEnable(PIOC_ID);
pmc_periphEnable(PIOD_ID);
pmc_periphEnable(EMAC_ID);
// Disable TESTMODE
PIOB_PUDR = BV(PHY_RXDV_TESTMODE_BIT);
#endif
// Toggle external hardware reset pin.
RSTC_MR = RSTC_KEY | (1 << RSTC_ERSTL_SHIFT) | BV(RSTC_URSTEN);
RSTC_CR = RSTC_KEY | BV(RSTC_EXTRST);
while ((RSTC_SR & BV(RSTC_NRSTL)) == 0)
cpu_relax();
// Configure MII ports.
#if CPU_ARM_AT91
PIOB_ASR = PHY_MII_PINS;
PIOB_BSR = 0;
PIOB_PDR = PHY_MII_PINS;
// Enable receive and transmit clocks.
EMAC_USRIO = BV(EMAC_CLKEN);
#else
PIO_PERIPH_SEL(PIOB_BASE, PHY_MII_PINS_PORTB, PIO_PERIPH_A);
PIOB_PDR = PHY_MII_PINS_PORTB;
// Enable receive, transmit clocks and RMII mode.
EMAC_USRIO = BV(EMAC_CLKEN) | BV(EMAC_RMII);
#endif
// Enable management port.
EMAC_NCR |= BV(EMAC_MPE);
EMAC_NCFGR |= EMAC_CLK_HCLK_64;
// Set local MAC address.
EMAC_SA1L = (mac_addr[3] << 24) | (mac_addr[2] << 16) |
(mac_addr[1] << 8) | mac_addr[0];
EMAC_SA1H = (mac_addr[5] << 8) | mac_addr[4];
emac_autoNegotiation();
// Disable management port.
EMAC_NCR &= ~BV(EMAC_MPE);
return 0;
}
static int emac_start(void)
{
uint32_t addr;
int i;
for (i = 0; i < EMAC_RX_DESCRIPTORS; i++)
{
addr = (uint32_t)(rx_buf + (i * EMAC_RX_BUFSIZ));
rx_buf_tab[i].addr = addr & BUF_ADDRMASK;
}
rx_buf_tab[EMAC_RX_DESCRIPTORS - 1].addr |= RXBUF_WRAP;
for (i = 0; i < EMAC_TX_DESCRIPTORS; i++)
{
addr = (uint32_t)(tx_buf + (i * EMAC_TX_BUFSIZ));
tx_buf_tab[i].addr = addr & BUF_ADDRMASK;
tx_buf_tab[i].stat = TXS_USED;
}
tx_buf_tab[EMAC_TX_DESCRIPTORS - 1].stat = TXS_USED | TXS_WRAP;
/* Tell the EMAC where to find the descriptors. */
EMAC_RBQP = (uint32_t)rx_buf_tab;
EMAC_TBQP = (uint32_t)tx_buf_tab;
/* Clear receiver status. */
EMAC_RSR = BV(EMAC_OVR) | BV(EMAC_REC) | BV(EMAC_BNA);
/* Copy all frames and discard FCS. */
EMAC_NCFGR |= BV(EMAC_CAF) | BV(EMAC_DRFCS);
/* Enable receiver, transmitter and statistics. */
EMAC_NCR |= BV(EMAC_TE) | BV(EMAC_RE) | BV(EMAC_WESTAT);
return 0;
}
ssize_t eth_putFrame(const uint8_t *buf, size_t len)
{
size_t wr_len;
if (UNLIKELY(!len))
return -1;
ASSERT(len <= sizeof(tx_buf));
/* Check if the transmit buffer is available */
while (!(tx_buf_tab[tx_buf_idx].stat & TXS_USED))
event_wait(&send_wait);
/* Copy the data into the buffer and prepare descriptor */
wr_len = MIN(len, (size_t)EMAC_TX_BUFSIZ - tx_buf_offset);
memcpy((uint8_t *)tx_buf_tab[tx_buf_idx].addr + tx_buf_offset,
buf, wr_len);
tx_buf_offset += wr_len;
return wr_len;
}
void eth_sendFrame(void)
{
tx_buf_tab[tx_buf_idx].stat = (tx_buf_offset & TXS_LENGTH_FRAME) |
TXS_LAST_BUFF |
((tx_buf_idx == EMAC_TX_DESCRIPTORS - 1) ? TXS_WRAP : 0);
EMAC_NCR |= BV(EMAC_TSTART);
tx_buf_offset = 0;
if (++tx_buf_idx >= EMAC_TX_DESCRIPTORS)
tx_buf_idx = 0;
}
ssize_t eth_send(const uint8_t *buf, size_t len)
{
if (UNLIKELY(!len))
return -1;
len = eth_putFrame(buf, len);
eth_sendFrame();
return len;
}
static void eth_buf_realign(int idx)
{
/* Empty buffer found. Realign. */
do {
rx_buf_tab[rx_buf_idx].addr &= ~RXBUF_OWNERSHIP;
if (++rx_buf_idx >= EMAC_RX_BUFFERS)
rx_buf_idx = 0;
} while (idx != rx_buf_idx);
}
static size_t __eth_getFrameLen(void)
{
int idx, n = EMAC_RX_BUFFERS;
skip:
/* Skip empty buffers */
while ((n > 0) && !(rx_buf_tab[rx_buf_idx].addr & RXBUF_OWNERSHIP))
{
if (++rx_buf_idx >= EMAC_RX_BUFFERS)
rx_buf_idx = 0;
n--;
}
if (UNLIKELY(!n))
{
LOG_INFO("no frame found\n");
return 0;
}
/* Search the start of frame and cleanup fragments */
while ((n > 0) && (rx_buf_tab[rx_buf_idx].addr & RXBUF_OWNERSHIP) &&
!(rx_buf_tab[rx_buf_idx].stat & RXS_SOF))
{
rx_buf_tab[rx_buf_idx].addr &= ~RXBUF_OWNERSHIP;
if (++rx_buf_idx >= EMAC_RX_BUFFERS)
rx_buf_idx = 0;
n--;
}
if (UNLIKELY(!n))
{
LOG_INFO("no SOF found\n");
return 0;
}
/* Search end of frame to evaluate the total frame size */
idx = rx_buf_idx;
restart:
while (n > 0)
{
if (UNLIKELY(!(rx_buf_tab[idx].addr & RXBUF_OWNERSHIP)))
{
/* Empty buffer found. Realign. */
eth_buf_realign(idx);
goto skip;
}
if (rx_buf_tab[idx].stat & RXS_EOF)
return rx_buf_tab[idx].stat & RXS_LENGTH_FRAME;
if (UNLIKELY((idx != rx_buf_idx) &&
(rx_buf_tab[idx].stat & RXS_SOF)))
{
/* Another start of frame found. Realign. */
eth_buf_realign(idx);
goto restart;
}
if (++idx >= EMAC_RX_BUFFERS)
idx = 0;
n--;
}
LOG_INFO("no EOF found\n");
return 0;
}
size_t eth_getFrameLen(void)
{
size_t len;
/* Check if there is at least one available frame in the buffer */
while (1)
{
len = __eth_getFrameLen();
if (LIKELY(len))
break;
/* Wait for RX interrupt */
event_wait(&recv_wait);
}
return len;
}
ssize_t eth_getFrame(uint8_t *buf, size_t len)
{
uint8_t *addr;
size_t rd_len = 0;
if (UNLIKELY(!len))
return -1;
ASSERT(len <= sizeof(rx_buf));
/* Copy data from the RX buffer */
addr = (uint8_t *)(rx_buf_tab[rx_buf_idx].addr & BUF_ADDRMASK);
if (addr + len > &rx_buf[countof(rx_buf)])
{
size_t count = &rx_buf[countof(rx_buf)] - addr;
memcpy(buf, addr, count);
memcpy(buf + count, rx_buf, len - count);
}
else
{
memcpy(buf, addr, len);
}
/* Update descriptors */
while (rd_len < len)
{
if (len - rd_len >= EMAC_RX_BUFSIZ)
rd_len += EMAC_RX_BUFSIZ;
else
rd_len += len - rd_len;
if (UNLIKELY(!(rx_buf_tab[rx_buf_idx].addr & RXBUF_OWNERSHIP)))
{
LOG_INFO("bad frame found\n");
return 0;
}
rx_buf_tab[rx_buf_idx].addr &= ~RXBUF_OWNERSHIP;
if (++rx_buf_idx >= EMAC_RX_DESCRIPTORS)
rx_buf_idx = 0;
}
return rd_len;
}
ssize_t eth_recv(uint8_t *buf, size_t len)
{
if (UNLIKELY(!len))
return -1;
len = MIN(len, eth_getFrameLen());
return len ? eth_getFrame(buf, len) : 0;
}
int eth_init()
{
cpu_flags_t flags;
emac_reset();
emac_start();
event_initGeneric(&recv_wait);
event_initGeneric(&send_wait);
// Register interrupt vector
IRQ_SAVE_DISABLE(flags);
/* Disable all emac interrupts */
EMAC_IDR = 0xFFFFFFFF;
#if CPU_ARM_AT91
// TODO: define sysirq_set...
/* Set the vector. */
AIC_SVR(EMAC_ID) = emac_irqHandler;
/* Initialize to edge triggered with defined priority. */
AIC_SMR(EMAC_ID) = AIC_SRCTYPE_INT_EDGE_TRIGGERED;
/* Clear pending interrupt */
AIC_ICCR = BV(EMAC_ID);
/* Enable the system IRQ */
AIC_IECR = BV(EMAC_ID);
#else
sysirq_setHandler(INT_EMAC, emac_irqHandler);
#endif
/* Enable interrupts */
EMAC_IER = EMAC_RX_INTS | EMAC_TX_INTS;
IRQ_RESTORE(flags);
return 0;
}

View file

@ -0,0 +1,210 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2009,2010,2011 Develer S.r.l. (http://www.develer.com/)
* All Rights Reserved.
* -->
*
* \brief EMAC driver for AT91SAM family with Davicom 9161A phy, interface.
*
* \author Daniele Basile <asterix@develer.com>
* \author Andrea Righi <arighi@develer.com>
* \author Stefano Fedrigo <aleph@develer.com>
*/
#ifndef ETH_SAM3_H
#define ETH_SAM3_H
// Settings and definition for DAVICOM 9161A
// \{
#define NIC_PHY_ADDR 0
// Register bits definition
#define NIC_PHY_BMCR 0x00 // Basic mode control register.
#define NIC_PHY_BMCR_COLTEST 0x0080 // Collision test.
#define NIC_PHY_BMCR_FDUPLEX 0x0100 // Full duplex mode.
#define NIC_PHY_BMCR_ANEGSTART 0x0200 // Restart auto negotiation.
#define NIC_PHY_BMCR_ISOLATE 0x0400 // Isolate from MII.
#define NIC_PHY_BMCR_PWRDN 0x0800 // Power-down.
#define NIC_PHY_BMCR_ANEGENA 0x1000 // Enable auto negotiation.
#define NIC_PHY_BMCR_100MBPS 0x2000 // Select 100 Mbps.
#define NIC_PHY_BMCR_LOOPBACK 0x4000 // Enable loopback mode.
#define NIC_PHY_BMCR_RESET 0x8000 // Software reset.
#define NIC_PHY_BMSR 0x01 // Basic mode status register.
#define NIC_PHY_BMSR_ANCOMPL 0x0020 // Auto negotiation complete.
#define NIC_PHY_BMSR_ANEGCAPABLE 0x0008 // Able to do auto-negotiation
#define NIC_PHY_BMSR_LINKSTAT 0x0004 // Link status.
#define NIC_PHY_ANLPAR_10_HDX BV(5) // 10BASE-T half duplex
#define NIC_PHY_ANLPAR_10_FDX BV(6) // 10BASE-T full duplex
#define NIC_PHY_ANLPAR_TX_HDX BV(7) // 100BASE-TX half duplex
#define NIC_PHY_ANLPAR_TX_FDX BV(8) // 100BASE-TX full duplex
#define NIC_PHY_ID1 0x02 // PHY identifier register 1.
#define NIC_PHY_ID2 0x03 // PHY identifier register 2.
#define NIC_PHY_ANAR 0x04 // Auto negotiation advertisement register.
#define NIC_PHY_ANLPAR 0x05 // Auto negotiation link partner availability register.
#define NIC_PHY_ANER 0x06 // Auto negotiation expansion register.
#if CPU_ARM_AT91
/*
* Pin definition for DAVICOM 9161A.
* See schematics for AT91SAM7X-EK evalution board.
*/
// All pins in port B
#define PHY_REFCLK_XT2_BIT 0
#define PHY_TXEN_BIT 1
#define PHY_TXD0_BIT 2
#define PHY_TXD1_BIT 3
#define PHY_CRS_AD4_BIT 4
#define PHY_RXD0_AD0_BIT 5
#define PHY_RXD1_AD1_BIT 6
#define PHY_RXER_RXD4_RPTR_BIT 7
#define PHY_MDC_BIT 8
#define PHY_MDIO_BIT 9
#define PHY_TXD2_BIT 10
#define PHY_TXD3_BIT 11
#define PHY_TXER_TXD4_BIT 12
#define PHY_RXD2_AD2_BIT 13
#define PHY_RXD3_AD3_BIT 14
#define PHY_RXDV_TESTMODE_BIT 15
#define PHY_COL_RMII_BIT 16
#define PHY_RXCLK_10BTSER_BIT 17
#define PHY_PWRDN_BIT 18
#define PHY_MDINTR_BIT 26
#define PHY_MII_PINS \
BV(PHY_REFCLK_XT2_BIT) \
| BV(PHY_TXEN_BIT) \
| BV(PHY_TXD0_BIT) \
| BV(PHY_TXD1_BIT) \
| BV(PHY_CRS_AD4_BIT) \
| BV(PHY_RXD0_AD0_BIT) \
| BV(PHY_RXD1_AD1_BIT) \
| BV(PHY_RXER_RXD4_RPTR_BIT) \
| BV(PHY_MDC_BIT) \
| BV(PHY_MDIO_BIT) \
| BV(PHY_TXD2_BIT) \
| BV(PHY_TXD3_BIT) \
| BV(PHY_TXER_TXD4_BIT) \
| BV(PHY_RXD2_AD2_BIT) \
| BV(PHY_RXD3_AD3_BIT) \
| BV(PHY_RXDV_TESTMODE_BIT) \
| BV(PHY_COL_RMII_BIT) \
| BV(PHY_RXCLK_10BTSER_BIT)
#else
/*
* Pin definition for DAVICOM 9161A.
* See schematics for SAM3X-EK evalution board.
*/
// Port B
#define PHY_REFCLK_XT2_BIT 0
#define PHY_TXEN_BIT 1
#define PHY_TXD0_BIT 2
#define PHY_TXD1_BIT 3
#define PHY_RXDV_TESTMODE_BIT 4
#define PHY_RXD0_AD0_BIT 5
#define PHY_RXD1_AD1_BIT 6
#define PHY_RXER_RXD4_RPTR_BIT 7
#define PHY_MDC_BIT 8
#define PHY_MDIO_BIT 9
// Port A
#define PHY_MDINTR_BIT 5
#define PHY_MII_PINS_PORTB \
BV(PHY_REFCLK_XT2_BIT) \
| BV(PHY_TXEN_BIT) \
| BV(PHY_TXD0_BIT) \
| BV(PHY_TXD1_BIT) \
| BV(PHY_RXD0_AD0_BIT) \
| BV(PHY_RXD1_AD1_BIT) \
| BV(PHY_RXER_RXD4_RPTR_BIT) \
| BV(PHY_MDC_BIT) \
| BV(PHY_MDIO_BIT)
#endif /* CPU_ARM_AT91 */
// \}
#define EMAC_TX_BUFSIZ 1518 //!!! Don't change this
#define EMAC_TX_BUFFERS 1 //!!! Don't change this
#define EMAC_TX_DESCRIPTORS EMAC_TX_BUFFERS
#define EMAC_RX_BUFFERS 32 //!!! Don't change this
#define EMAC_RX_BUFSIZ 128 //!!! Don't change this
#define EMAC_RX_DESCRIPTORS EMAC_RX_BUFFERS
// Flag to manage local tx buffer
#define TXS_USED 0x80000000 //Used buffer.
#define TXS_WRAP 0x40000000 //Last descriptor.
#define TXS_ERROR 0x20000000 //Retry limit exceeded.
#define TXS_UNDERRUN 0x10000000 //Transmit underrun.
#define TXS_NO_BUFFER 0x08000000 //Buffer exhausted.
#define TXS_NO_CRC 0x00010000 //CRC not appended.
#define TXS_LAST_BUFF 0x00008000 //Last buffer of frame.
#define TXS_LENGTH_FRAME 0x000007FF // Length of frame including FCS.
// Flag to manage local rx buffer
#define RXBUF_OWNERSHIP 0x00000001
#define RXBUF_WRAP 0x00000002
#define BUF_ADDRMASK 0xFFFFFFFC
#define RXS_BROADCAST_ADDR 0x80000000 // Broadcast address detected.
#define RXS_MULTICAST_HASH 0x40000000 // Multicast hash match.
#define RXS_UNICAST_HASH 0x20000000 // Unicast hash match.
#define RXS_EXTERNAL_ADDR 0x10000000 // External address match.
#define RXS_SA1_ADDR 0x04000000 // Specific address register 1 match.
#define RXS_SA2_ADDR 0x02000000 // Specific address register 2 match.
#define RXS_SA3_ADDR 0x01000000 // Specific address register 3 match.
#define RXS_SA4_ADDR 0x00800000 // Specific address register 4 match.
#define RXS_TYPE_ID 0x00400000 // Type ID match.
#define RXS_VLAN_TAG 0x00200000 // VLAN tag detected.
#define RXS_PRIORITY_TAG 0x00100000 // Priority tag detected.
#define RXS_VLAN_PRIORITY 0x000E0000 // VLAN priority.
#define RXS_CFI_IND 0x00010000 // Concatenation format indicator.
#define RXS_EOF 0x00008000 // End of frame.
#define RXS_SOF 0x00004000 // Start of frame.
#define RXS_RBF_OFFSET 0x00003000 // Receive buffer offset mask.
#define RXS_LENGTH_FRAME 0x000007FF // Length of frame including FCS.
#define EMAC_RSR_BITS (BV(EMAC_BNA) | BV(EMAC_REC) | BV(EMAC_OVR))
#define EMAC_TSR_BITS (BV(EMAC_UBR) | BV(EMAC_COL) | BV(EMAC_RLES) | \
BV(EMAC_BEX) | BV(EMAC_COMP) | BV(EMAC_UND))
typedef struct BufDescriptor
{
volatile uint32_t addr;
volatile uint32_t stat;
} BufDescriptor;
#endif /* ETH_SAM3_H */

View file

@ -0,0 +1,47 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Low-level flash module for Cortex-M3 (interface).
*
* \author Daniele Basile <asterix@develer.com>
*/
#include <cpu/detect.h>
#if CPU_CM3_LM3S
#include "flash_lm3s.h"
#elif CPU_CM3_STM32
#include "flash_stm32.h"
/*#elif Add other Cortex-M3 CPUs here */
#else
#error Unknown CPU
#endif

View file

@ -0,0 +1,223 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief LM3S1968 internal flash memory driver.
*
* \author Andrea Righi <arighi@develer.com>
*/
#include "flash_lm3s.h"
#include "cfg/cfg_emb_flash.h"
// Define log settings for cfg/log.h
#define LOG_LEVEL CONFIG_FLASH_EMB_LOG_LEVEL
#define LOG_FORMAT CONFIG_FLASH_EMB_LOG_FORMAT
#include <cfg/log.h>
#include <cfg/macros.h>
#include <io/kblock.h>
#include <drv/timer.h>
#include <drv/flash.h>
#include <cpu/power.h> /* cpu_relax() */
#include <cpu/types.h>
#include <string.h> /* memcpy() */
struct FlashHardware
{
int status;
};
static bool flash_wait(struct KBlock *blk, uint32_t event)
{
Flash *fls = FLASH_CAST(blk);
ticks_t start = timer_clock();
while (true)
{
if (!(FLASH_FMC_R & event))
break;
if (FLASH_FCRIS_R & FLASH_FCRIS_ARIS)
{
fls->hw->status |= FLASH_WR_PROTECT;
LOG_ERR("wr protect..\n");
return false;
}
if (timer_clock() - start > ms_to_ticks(CONFIG_FLASH_WR_TIMEOUT))
{
fls->hw->status |= FLASH_WR_TIMEOUT;
LOG_ERR("Timeout..\n");
return false;
}
cpu_relax();
}
return true;
}
static int lm3s_erasePage(struct KBlock *blk, uint32_t addr)
{
FLASH_FCMISC_R = FLASH_FCMISC_AMISC;
FLASH_FMA_R = (volatile uint32_t)addr;
FLASH_FMC_R = FLASH_FMC_WRKEY | FLASH_FMC_ERASE;
return flash_wait(blk, FLASH_FMC_ERASE);
}
static int lm3s_writeWord(struct KBlock *blk, uint32_t addr, uint32_t data)
{
FLASH_FCMISC_R = FLASH_FCMISC_AMISC;
FLASH_FMA_R = (volatile uint32_t)addr;
FLASH_FMD_R = (volatile uint32_t)data;
FLASH_FMC_R = FLASH_FMC_WRKEY | FLASH_FMC_WRITE;
return flash_wait(blk, FLASH_FMC_WRITE);
}
static size_t lm3s_flash_readDirect(struct KBlock *blk, block_idx_t idx, void *buf, size_t offset, size_t size)
{
memcpy(buf, (void *)(idx * blk->blk_size + offset), size);
return size;
}
static size_t lm3s_flash_writeDirect(struct KBlock *blk, block_idx_t idx, const void *_buf, size_t offset, size_t size)
{
(void)offset;
ASSERT(offset == 0);
ASSERT(size == blk->blk_size);
if (!lm3s_erasePage(blk, (idx * blk->blk_size)))
return 0;
uint32_t addr = idx * blk->blk_size;
const uint8_t *buf = (const uint8_t *)_buf;
while (size)
{
uint32_t data = (*(buf + 3) << 24) |
(*(buf + 2) << 16) |
(*(buf + 1) << 8) |
*buf;
if (!lm3s_writeWord(blk, addr, data))
return 0;
size -= 4;
buf += 4;
addr += 4;
}
return blk->blk_size;
}
static int lm3s_flash_error(struct KBlock *blk)
{
Flash *fls = FLASH_CAST(blk);
return fls->hw->status;
}
static void lm3s_flash_clearerror(struct KBlock *blk)
{
Flash *fls = FLASH_CAST(blk);
fls->hw->status = 0;
}
static const KBlockVTable flash_lm3s_buffered_vt =
{
.readDirect = lm3s_flash_readDirect,
.writeDirect = lm3s_flash_writeDirect,
.readBuf = kblock_swReadBuf,
.writeBuf = kblock_swWriteBuf,
.load = kblock_swLoad,
.store = kblock_swStore,
.close = kblock_swClose,
.error = lm3s_flash_error,
.clearerr = lm3s_flash_clearerror,
};
static const KBlockVTable flash_lm3s_unbuffered_vt =
{
.readDirect = lm3s_flash_readDirect,
.writeDirect = lm3s_flash_writeDirect,
.close = kblock_swClose,
.error = lm3s_flash_error,
.clearerr = lm3s_flash_clearerror,
};
static struct FlashHardware flash_lm3s_hw;
static uint8_t flash_buf[FLASH_PAGE_SIZE];
static void common_init(Flash *fls)
{
memset(fls, 0, sizeof(*fls));
DB(fls->blk.priv.type = KBT_FLASH);
FLASH_USECRL_R = CPU_FREQ / 1000000 - 1;
fls->hw = &flash_lm3s_hw;
fls->blk.blk_size = FLASH_PAGE_SIZE;
fls->blk.blk_cnt = FLASH_SIZE / FLASH_PAGE_SIZE;
}
void flash_hw_init(Flash *fls, UNUSED_ARG(int, flags))
{
common_init(fls);
fls->blk.priv.vt = &flash_lm3s_buffered_vt;
fls->blk.priv.flags |= KB_BUFFERED | KB_PARTIAL_WRITE;
fls->blk.priv.buf = flash_buf;
/* Load the first block in the cache */
void *flash_start = 0x0;
memcpy(fls->blk.priv.buf, flash_start, fls->blk.blk_size);
}
void flash_hw_initUnbuffered(Flash *fls, UNUSED_ARG(int, flags))
{
common_init(fls);
fls->blk.priv.vt = &flash_lm3s_unbuffered_vt;
}

View file

@ -0,0 +1,41 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief LM3S1968 internal flash memory driver.
*
* \author Andrea Righi <arighi@develer.com>
*/
#ifndef FLASH_LM3S_H
#define FLASH_LM3S_H
#endif /* FLASH_LM3S_H */

View file

@ -0,0 +1,252 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief STM32F103xx internal flash memory driver.
*
* \author Daniele Basile <asterix@develer.com>
*/
#include "flash_stm32.h"
#include "cfg/cfg_emb_flash.h"
// Define log settings for cfg/log.h
#define LOG_LEVEL CONFIG_FLASH_EMB_LOG_LEVEL
#define LOG_FORMAT CONFIG_FLASH_EMB_LOG_FORMAT
#include <cfg/log.h>
#include <drv/timer.h>
#include <drv/flash.h>
#include <cpu/power.h>
#include <cpu/detect.h>
#include <io/stm32.h>
#include <string.h>
#define EMB_FLASH ((struct stm32_flash*)FLASH_R_BASE)
struct FlashHardware
{
uint8_t status;
};
static bool flash_wait(struct KBlock *blk)
{
Flash *fls = FLASH_CAST(blk);
ticks_t start = timer_clock();
while (true)
{
cpu_relax();
if (!(EMB_FLASH->SR & FLASH_FLAG_BSY))
break;
if (EMB_FLASH->SR & FLASH_FLAG_PGERR)
{
fls->hw->status |= FLASH_NOT_ERASED;
LOG_ERR("flash not erased..\n");
return false;
}
if (EMB_FLASH->SR & FLASH_FLAG_WRPRTERR)
{
fls->hw->status |= FLASH_WR_PROTECT;
LOG_ERR("wr protect..\n");
return false;
}
if (timer_clock() - start > ms_to_ticks(CONFIG_FLASH_WR_TIMEOUT))
{
fls->hw->status |= FLASH_WR_TIMEOUT;
LOG_ERR("Timeout..\n");
return false;
}
}
return true;
}
static bool stm32_erasePage(struct KBlock *blk, uint32_t page_add)
{
EMB_FLASH->CR |= CR_PER_SET;
EMB_FLASH->AR = page_add;
EMB_FLASH->CR |= CR_STRT_SET;
if (!flash_wait(blk))
return false;
EMB_FLASH->CR &= CR_PER_RESET;
return true;
}
#if 0
// not used for now
static bool stm32_eraseAll(struct KBlock *blk)
{
EMB_FLASH->CR |= CR_MER_SET;
EMB_FLASH->CR |= CR_STRT_SET;
if (!flash_wait(blk))
return false;
EMB_FLASH->CR &= CR_MER_RESET;
return true;
}
#endif
static int stm32_flash_error(struct KBlock *blk)
{
Flash *fls = FLASH_CAST(blk);
return fls->hw->status;
}
static void stm32_flash_clearerror(struct KBlock *blk)
{
Flash *fls = FLASH_CAST(blk);
fls->hw->status = 0;
}
static size_t stm32_flash_readDirect(struct KBlock *blk, block_idx_t idx, void *buf, size_t offset, size_t size)
{
memcpy(buf, (void *)(idx * blk->blk_size + offset), size);
return size;
}
INLINE bool stm32_writeWord(struct KBlock *blk, uint32_t addr, uint16_t data)
{
ASSERT(!(addr % 2));
EMB_FLASH->CR |= CR_PG_SET;
*(reg16_t *)addr = data;
if (!flash_wait(blk))
return false;
EMB_FLASH->CR &= CR_PG_RESET;
return true;
}
static size_t stm32_flash_writeDirect(struct KBlock *blk, block_idx_t idx, const void *_buf, size_t offset, size_t size)
{
ASSERT(offset == 0);
ASSERT(size == blk->blk_size);
if (!stm32_erasePage(blk, (idx * blk->blk_size)))
return 0;
uint32_t addr = idx * blk->blk_size;
const uint8_t *buf = (const uint8_t *)_buf;
while (size)
{
uint16_t data = (*(buf + 1) << 8) | *buf;
if (!stm32_writeWord(blk, addr, data))
return 0;
buf += 2;
size -= 2;
addr += 2;
}
return blk->blk_size;
}
static const KBlockVTable flash_stm32_buffered_vt =
{
.readDirect = stm32_flash_readDirect,
.writeDirect = stm32_flash_writeDirect,
.readBuf = kblock_swReadBuf,
.writeBuf = kblock_swWriteBuf,
.load = kblock_swLoad,
.store = kblock_swStore,
.close = kblock_swClose,
.error = stm32_flash_error,
.clearerr = stm32_flash_clearerror,
};
static const KBlockVTable flash_stm32_unbuffered_vt =
{
.readDirect = stm32_flash_readDirect,
.writeDirect = stm32_flash_writeDirect,
.close = kblock_swClose,
.error = stm32_flash_error,
.clearerr = stm32_flash_clearerror,
};
static struct FlashHardware flash_stm32_hw;
static uint8_t flash_buf[FLASH_PAGE_SIZE];
static void common_init(Flash *fls)
{
memset(fls, 0, sizeof(*fls));
DB(fls->blk.priv.type = KBT_FLASH);
fls->hw = &flash_stm32_hw;
fls->blk.blk_size = FLASH_PAGE_SIZE;
fls->blk.blk_cnt = (F_SIZE * 1024) / FLASH_PAGE_SIZE;
/* Unlock flash memory for the FPEC Access */
EMB_FLASH->KEYR = FLASH_KEY1;
EMB_FLASH->KEYR = FLASH_KEY2;
}
void flash_hw_init(Flash *fls, UNUSED_ARG(int, flags))
{
common_init(fls);
fls->blk.priv.vt = &flash_stm32_buffered_vt;
fls->blk.priv.flags |= KB_BUFFERED | KB_PARTIAL_WRITE;
fls->blk.priv.buf = flash_buf;
/* Load the first block in the cache */
void *flash_start = 0x0;
memcpy(fls->blk.priv.buf, flash_start, fls->blk.blk_size);
}
void flash_hw_initUnbuffered(Flash *fls, UNUSED_ARG(int, flags))
{
common_init(fls);
fls->blk.priv.vt = &flash_stm32_unbuffered_vt;
}

View file

@ -0,0 +1,41 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief STM32F103xx internal flash memory driver.
*
* \author Daniele Basile <asterix@develer.com>
*/
#ifndef FLASH_STM32_H
#define FLASH_STM32_H
#endif /* FLASH_STM32_H */

View file

@ -0,0 +1,206 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief LM3S1968 GPIO control interface.
*
* \author Andrea Righi <arighi@develer.com>
*/
#include "gpio_lm3s.h"
#include <cfg/compiler.h>
#include <cfg/debug.h>
#include <io/lm3s.h>
/* Set the pin(s) direction and mode */
INLINE int lm3s_gpioPinConfigMode(uint32_t port, uint8_t pins, uint32_t mode)
{
if (mode == GPIO_DIR_MODE_IN)
{
HWREG(port + GPIO_O_DIR) &= ~pins;
HWREG(port + GPIO_O_AFSEL) &= ~pins;
}
else if (mode == GPIO_DIR_MODE_OUT)
{
HWREG(port + GPIO_O_DIR) |= pins;
HWREG(port + GPIO_O_AFSEL) &= ~pins;
}
else if (mode == GPIO_DIR_MODE_HW)
{
HWREG(port + GPIO_O_DIR) &= ~pins;
HWREG(port + GPIO_O_AFSEL) |= pins;
}
else
{
ASSERT(0);
return -1;
}
return 0;
}
/* Set the pin(s) output strength */
INLINE int
lm3s_gpioPinConfigStrength(uint32_t port, uint8_t pins, uint32_t strength)
{
if (strength == GPIO_STRENGTH_2MA)
{
HWREG(port + GPIO_O_DR2R) |= pins;
HWREG(port + GPIO_O_DR4R) &= ~pins;
HWREG(port + GPIO_O_DR8R) &= ~pins;
HWREG(port + GPIO_O_SLR) &= ~pins;
}
else if (strength == GPIO_STRENGTH_4MA)
{
HWREG(port + GPIO_O_DR2R) &= ~pins;
HWREG(port + GPIO_O_DR4R) |= pins;
HWREG(port + GPIO_O_DR8R) &= ~pins;
HWREG(port + GPIO_O_SLR) &= ~pins;
}
else if (strength == GPIO_STRENGTH_8MA)
{
HWREG(port + GPIO_O_DR2R) &= ~pins;
HWREG(port + GPIO_O_DR4R) &= ~pins;
HWREG(port + GPIO_O_DR8R) |= pins;
HWREG(port + GPIO_O_SLR) &= ~pins;
}
else if (strength == GPIO_STRENGTH_8MA_SC)
{
HWREG(port + GPIO_O_DR2R) &= ~pins;
HWREG(port + GPIO_O_DR4R) &= ~pins;
HWREG(port + GPIO_O_DR8R) |= pins;
HWREG(port + GPIO_O_SLR) |= pins;
}
else
{
ASSERT(0);
return -1;
}
return 0;
}
/* Set the pin(s) type */
INLINE int lm3s_gpioPinConfigType(uint32_t port, uint8_t pins, uint32_t type)
{
if (type == GPIO_PIN_TYPE_STD)
{
HWREG(port + GPIO_O_ODR) &= ~pins;
HWREG(port + GPIO_O_PUR) &= ~pins;
HWREG(port + GPIO_O_PDR) &= ~pins;
HWREG(port + GPIO_O_DEN) |= pins;
HWREG(port + GPIO_O_AMSEL) &= ~pins;
}
else if (type == GPIO_PIN_TYPE_STD_WPU)
{
HWREG(port + GPIO_O_ODR) &= ~pins;
HWREG(port + GPIO_O_PUR) |= pins;
HWREG(port + GPIO_O_PDR) &= ~pins;
HWREG(port + GPIO_O_DEN) |= pins;
HWREG(port + GPIO_O_AMSEL) &= ~pins;
}
else if (type == GPIO_PIN_TYPE_STD_WPD)
{
HWREG(port + GPIO_O_ODR) &= ~pins;
HWREG(port + GPIO_O_PUR) &= ~pins;
HWREG(port + GPIO_O_PDR) |= pins;
HWREG(port + GPIO_O_DEN) |= pins;
HWREG(port + GPIO_O_AMSEL) &= ~pins;
}
else if (type == GPIO_PIN_TYPE_OD)
{
HWREG(port + GPIO_O_ODR) |= pins;
HWREG(port + GPIO_O_PUR) &= ~pins;
HWREG(port + GPIO_O_PDR) &= ~pins;
HWREG(port + GPIO_O_DEN) |= pins;
HWREG(port + GPIO_O_AMSEL) &= ~pins;
}
else if (type == GPIO_PIN_TYPE_OD_WPU)
{
HWREG(port + GPIO_O_ODR) |= pins;
HWREG(port + GPIO_O_PUR) |= pins;
HWREG(port + GPIO_O_PDR) &= ~pins;
HWREG(port + GPIO_O_DEN) |= pins;
HWREG(port + GPIO_O_AMSEL) &= ~pins;
}
else if (type == GPIO_PIN_TYPE_OD_WPD)
{
HWREG(port + GPIO_O_ODR) |= pins;
HWREG(port + GPIO_O_PUR) &= pins;
HWREG(port + GPIO_O_PDR) |= pins;
HWREG(port + GPIO_O_DEN) |= pins;
HWREG(port + GPIO_O_AMSEL) &= ~pins;
}
else if (type == GPIO_PIN_TYPE_ANALOG)
{
HWREG(port + GPIO_O_ODR) &= ~pins;
HWREG(port + GPIO_O_PUR) &= ~pins;
HWREG(port + GPIO_O_PDR) &= ~pins;
HWREG(port + GPIO_O_DEN) &= ~pins;
HWREG(port + GPIO_O_AMSEL) |= pins;
}
else
{
ASSERT(0);
return -1;
}
return 0;
}
/**
* Configure a GPIO pin
*
* \param port Base address of the GPIO port
* \param pins Bit-packed representation of the pin(s)
* \param mode Pin(s) configuration mode
* \param strength Output drive strength
* \param type Pin(s) type
*
* Return 0 on success, otherwise a negative value.
*/
int lm3s_gpioPinConfig(uint32_t port, uint8_t pins,
uint32_t mode, uint32_t strength, uint32_t type)
{
int ret;
ret = lm3s_gpioPinConfigMode(port, pins, mode);
if (UNLIKELY(ret < 0))
return ret;
ret = lm3s_gpioPinConfigStrength(port, pins, strength);
if (UNLIKELY(ret < 0))
return ret;
ret = lm3s_gpioPinConfigType(port, pins, type);
if (UNLIKELY(ret < 0))
return ret;
return 0;
}

View file

@ -0,0 +1,97 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief LM3S1968 GPIO control interface.
*/
#ifndef GPIO_LM3S_H
#define GPIO_LM3S_H
#include <io/lm3s.h>
/**
* GPIO mode
*/
/*\{*/
enum
{
GPIO_DIR_MODE_IN = 0, //< Pin is a GPIO input
GPIO_DIR_MODE_OUT, //< Pin is a GPIO output
GPIO_DIR_MODE_HW, //< Pin is a peripheral function
};
/*\}*/
/**
* GPIO strenght
*/
/*\{*/
enum
{
GPIO_STRENGTH_2MA = 0, //< 2mA drive strength
GPIO_STRENGTH_4MA, //< 4mA drive strength
GPIO_STRENGTH_8MA, //< 8mA drive strength
GPIO_STRENGTH_8MA_SC, //< 8mA drive with slew rate control
};
/*\}*/
/**
* GPIO type
*/
/*\{*/
enum
{
GPIO_PIN_TYPE_ANALOG = 0, //< Analog comparator
GPIO_PIN_TYPE_STD, //< Push-pull
GPIO_PIN_TYPE_STD_WPU, //< Push-pull with weak pull-up
GPIO_PIN_TYPE_STD_WPD, //< Push-pull with weak pull-down
GPIO_PIN_TYPE_OD, //< Open-drain
GPIO_PIN_TYPE_OD_WPU, //< Open-drain with weak pull-up
GPIO_PIN_TYPE_OD_WPD, //< Open-drain with weak pull-down
};
/*\}*/
/* Write a value to the specified pin(s) */
INLINE void lm3s_gpioPinWrite(uint32_t port, uint8_t pins, uint8_t val)
{
HWREG(port + GPIO_O_DATA + (pins << 2)) = val;
}
/* Read a value from the specified pin(s) */
INLINE uint32_t lm3s_gpioPinRead(uint32_t port, uint8_t pins)
{
return HWREG(port + GPIO_O_DATA + (pins << 2));
}
int lm3s_gpioPinConfig(uint32_t port, uint8_t pins,
uint32_t mode, uint32_t strength, uint32_t type);
#endif /* GPIO_LM3S_H */

View file

@ -0,0 +1,110 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief STM32 GPIO control interface.
*
* \author Andrea Righi <arighi@develer.com>
*/
#include "gpio_stm32.h"
#include <cfg/compiler.h>
#include <cfg/debug.h>
#include <io/stm32.h>
/**
* Configure a GPIO pin
*
* \param base Base address of the GPIO port
* \param pins Bit-packed representation of the pin(s)
* \param mode Pin(s) configuration mode
* \param speed Output drive speed
*
* Return 0 on success, otherwise a negative value.
*/
int stm32_gpioPinConfig(struct stm32_gpio *base,
uint16_t pins, uint8_t mode, uint8_t speed)
{
uint32_t reg_mode = mode & 0x0f;
int i;
if (mode & 0x10)
reg_mode |= speed;
if (pins & 0xff)
{
uint32_t reg = base->CRL;
for (i = 0; i < 8; i++)
{
uint32_t pos = 1 << i;
if (pins & pos)
{
pos = i << 2;
reg &= ~(0x0f << pos);
reg |= reg_mode << pos;
if (mode == GPIO_MODE_IPD)
base->BRR = 0x01 << i;
if (mode == GPIO_MODE_IPU)
base->BSRR = 0x01 << i;
}
}
base->CRL = reg;
}
if (pins > 0xff)
{
uint32_t reg = base->CRH;
for (i = 0; i < 8; i++)
{
uint32_t pos = 1 << (i + 8);
if (pins & pos)
{
pos = i << 2;
reg &= ~(0x0f << pos);
reg |= reg_mode << pos;
if (mode == GPIO_MODE_IPD)
base->BRR = 0x01 << (i + 8);
if (mode == GPIO_MODE_IPU)
base->BSRR = 0x01 << (i + 8);
}
}
base->CRH = reg;
}
return 0;
}

View file

@ -0,0 +1,106 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief STM32 GPIO control interface.
*/
#ifndef GPIO_STM32_H
#define GPIO_STM32_H
#include <io/stm32.h>
/**
* GPIO mode
* \{
*/
enum
{
GPIO_MODE_AIN = 0x0,
GPIO_MODE_IN_FLOATING = 0x04,
GPIO_MODE_IPD = 0x28,
GPIO_MODE_IPU = 0x48,
GPIO_MODE_OUT_OD = 0x14,
GPIO_MODE_OUT_PP = 0x10,
GPIO_MODE_AF_OD = 0x1C,
GPIO_MODE_AF_PP = 0x18,
};
/*\}*/
/**
* GPIO speed
*\{
*/
enum
{
GPIO_SPEED_10MHZ = 1,
GPIO_SPEED_2MHZ,
GPIO_SPEED_50MHZ,
};
/*\}*/
/**
* Write a value to the specified pin(s)
*
* \param base gpio register address
* \param pins mask of pins that we want set or clear
* \param val true to set selected pins of false to clear they.
*/
INLINE void stm32_gpioPinWrite(struct stm32_gpio *base, uint16_t pins, bool val)
{
if (val)
base->BSRR |= pins;
else
base->BRR |= pins;
}
/**
* Read a value from the specified pin(s)
*
* \param base gpio register address
* \param pins mask of pins that we want read
*/
INLINE uint16_t stm32_gpioPinRead(struct stm32_gpio *base, uint16_t pins)
{
return (base->IDR & pins);
}
/**
* Initialize a GPIO peripheral configuration
*
* \param base gpio register address
* \param pins mask of pins that we want to configure
* \param mode select the behaviour of selected pins
* \param speed clock frequency for selected gpio ports
*/
int stm32_gpioPinConfig(struct stm32_gpio *base, uint16_t pins, uint8_t mode, uint8_t speed);
#endif /* GPIO_STM32_H */

View file

@ -0,0 +1,51 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Low-level I2C module for ARM Cortex-m3 (interface).
*
* \author Daniele Basile <asterix@develer.com>
*
*/
#include <cpu/detect.h>
#if CPU_CM3_LM3S
#include "i2c_lm3s.h"
#elif CPU_CM3_STM32
#include "i2c_stm32.h"
#elif CPU_CM3_SAM3
#include "i2c_sam3.h"
/*#elif Add other Cortex-M3 CPUs here */
#else
#error Unknown CPU
#endif

View file

@ -0,0 +1,256 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Driver for the LM3S I2C (implementation)
*
* \author Daniele Basile <asterix@develer.com>
*
*/
#include "cfg/cfg_i2c.h"
#define LOG_LEVEL I2C_LOG_LEVEL
#define LOG_FORMAT I2C_LOG_FORMAT
#include <cfg/log.h>
#include <cfg/debug.h>
#include <cfg/macros.h> // BV()
#include <cpu/detect.h>
#include <cpu/irq.h>
#include <cpu/types.h>
#include <cpu/power.h>
#include <io/lm3s.h>
#include <drv/timer.h>
#include <drv/i2c.h>
#include <drv/gpio_lm3s.h>
#include <drv/clock_lm3s.h>
struct I2cHardware
{
uint32_t base;
uint32_t sys_cntl;
uint32_t sys_gpio;
uint32_t pin_mask;
uint32_t gpio_base;
bool first_xtranf;
};
#define WAIT_BUSY(base) \
do { \
while (HWREG(base + I2C_O_MCS) & I2C_MCS_BUSY ) \
cpu_relax(); \
} while (0);
/*
* The start is not performed when we call the start function
* because the hardware should know the first data byte to send.
* Generally to perform a byte send we should write the slave address
* in slave address register and the first byte to send in data registry.
* After then we can perform the start write procedure, and send really
* the our data. To use common bertos i2c api the really start will be
* performed when the user "put" or "send" its data. These tricks are hide
* from the driver implementation.
*/
static void i2c_lm3s_start(struct I2c *i2c, uint16_t slave_addr)
{
i2c->hw->first_xtranf = true;
if (I2C_TEST_START(i2c->flags) == I2C_START_W)
HWREG(i2c->hw->base + I2C_O_MSA) = slave_addr & ~BV(0);
else /* (I2C_TEST_START(i2c->flags) == I2C_START_R) */
HWREG(i2c->hw->base + I2C_O_MSA) = slave_addr | BV(0);
}
INLINE bool wait_addrAck(I2c *i2c, uint32_t mode_mask)
{
ticks_t start = timer_clock();
while (1)
{
uint32_t status = HWREG(i2c->hw->base + I2C_O_MCS);
if (timer_clock() - start > ms_to_ticks(CONFIG_I2C_START_TIMEOUT))
return false;
if(status & I2C_MCS_ADRACK)
{
HWREG(i2c->hw->base + I2C_O_MCS) = mode_mask;
WAIT_BUSY(i2c->hw->base);
}
else
break;
cpu_relax();
}
return true;
}
static void i2c_lm3s_putc(I2c *i2c, const uint8_t data)
{
HWREG(i2c->hw->base + I2C_O_MDR) = data;
if (i2c->hw->first_xtranf)
{
HWREG(i2c->hw->base + I2C_O_MCS) = I2C_MCS_RUN | I2C_MCS_START;
while( HWREG(i2c->hw->base + I2C_O_MCS) & I2C_MCS_BUSY );
if (!wait_addrAck(i2c, I2C_MCS_RUN | I2C_MCS_START))
{
LOG_ERR("Start timeout\n");
i2c->errors |= I2C_START_TIMEOUT;
HWREG(i2c->hw->base + I2C_O_MCS) = I2C_MCS_STOP;
WAIT_BUSY(i2c->hw->base);
return;
}
i2c->hw->first_xtranf = false;
}
else
{
HWREG(i2c->hw->base + I2C_O_MCS) = I2C_MCS_RUN;
WAIT_BUSY(i2c->hw->base);
}
if ((i2c->xfer_size == 1) && (I2C_TEST_STOP(i2c->flags) == I2C_STOP))
{
HWREG(i2c->hw->base + I2C_O_MCS) = I2C_MCS_STOP;
WAIT_BUSY(i2c->hw->base);
}
}
static uint8_t i2c_lm3s_getc(I2c *i2c)
{
uint8_t data;
if (i2c->hw->first_xtranf)
{
uint32_t start_mode;
if (i2c->xfer_size == 1)
start_mode = I2C_MCS_RUN | I2C_MCS_START;
else
start_mode = I2C_MCS_ACK | I2C_MCS_RUN | I2C_MCS_START;
HWREG(i2c->hw->base + I2C_O_MCS) = start_mode;
WAIT_BUSY(i2c->hw->base);
if (!wait_addrAck(i2c, start_mode))
{
LOG_ERR("Start timeout\n");
i2c->errors |= I2C_START_TIMEOUT;
HWREG(i2c->hw->base + I2C_O_MCS) = I2C_MCS_STOP;
WAIT_BUSY(i2c->hw->base);
return 0xFF;
}
data = HWREG(i2c->hw->base + I2C_O_MDR);
i2c->hw->first_xtranf = false;
}
else
{
if (i2c->xfer_size > 1)
HWREG(i2c->hw->base + I2C_O_MCS) = I2C_MCS_ACK | I2C_MCS_RUN;
else
HWREG(i2c->hw->base + I2C_O_MCS) = I2C_MCS_RUN;
WAIT_BUSY(i2c->hw->base);
data = HWREG(i2c->hw->base + I2C_O_MDR);
}
if ((i2c->xfer_size == 1) && (I2C_TEST_STOP(i2c->flags) == I2C_STOP))
{
HWREG(i2c->hw->base + I2C_O_MCS) = I2C_MCS_STOP;
WAIT_BUSY(i2c->hw->base);
}
return data;
}
static const I2cVT i2c_lm3s_vt =
{
.start = i2c_lm3s_start,
.getc = i2c_lm3s_getc,
.putc = i2c_lm3s_putc,
.write = i2c_genericWrite,
.read = i2c_genericRead,
};
static struct I2cHardware i2c_lm3s_hw[] =
{
{ /* I2C0 */
.base = I2C0_MASTER_BASE,
.sys_cntl = SYSCTL_RCGC1_I2C0,
.sys_gpio = SYSCTL_RCGC2_GPIOB,
.pin_mask = (GPIO_I2C0_SCL_PIN | GPIO_I2C0_SDA_PIN),
.gpio_base = GPIO_PORTB_BASE,
},
{ /* I2C1 */
.base = I2C1_MASTER_BASE,
.sys_cntl = SYSCTL_RCGC1_I2C1,
.sys_gpio = SYSCTL_RCGC2_GPIOA,
.pin_mask = (GPIO_I2C1_SCL_PIN | GPIO_I2C1_SDA_PIN),
.gpio_base = GPIO_PORTA_BASE,
},
};
/**
* Initialize I2C module.
*/
void i2c_hw_init(I2c *i2c, int dev, uint32_t clock)
{
i2c->hw = &i2c_lm3s_hw[dev];
i2c->vt = &i2c_lm3s_vt;
/* Enable the peripheral clock */
SYSCTL_RCGC1_R |= i2c->hw->sys_cntl;
SYSCTL_RCGC2_R |= i2c->hw->sys_gpio;
/* Configure GPIO pins to work as I2C pins */
lm3s_gpioPinConfig(i2c->hw->gpio_base, i2c->hw->pin_mask,
GPIO_DIR_MODE_HW, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_OD_WPU);
/*
* Note: to set correctly the i2c speed we shold before enable the i2c
* device and then set in master time period the correct value
*/
/* Enable i2c device */
HWREG(i2c->hw->base + I2C_O_MCR) |= I2C_MCR_MFE;
/*
* Compute the clock divider that achieves the fastest speed less than or
* equal to the desired speed. The numerator is biased to favor a larger
* clock divider so that the resulting clock is always less than or equal
* to the desired clock, never greater.
*/
HWREG(i2c->hw->base + I2C_O_MTPR) = ((CPU_FREQ + (2 * 10 * clock) - 1) / (2 * 10 * clock)) - 1;
}

View file

@ -0,0 +1,57 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Driver for the LM3S I2C (interface)
*
* \author Daniele Basile <asterix@develer.com>
*/
#ifndef I2C_LM3S_H
#define I2C_LM3S_H
#include <drv/i2c.h>
/**
* \name I2C devices enum
*/
enum
{
#if CPU_CM3_LM3S1968
I2C0,
I2C1,
#elif CPU_CM3_LM3S8962
I2C0,
#enif
I2C_CNT /**< Number of serial ports */
};
#endif /* I2C_LM3S_H */

View file

@ -0,0 +1,253 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2011 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief TWI driver for SAM3 (implementation)
*
* Only master mode is supported.
*
* \author Stefano Fedrigo <aleph@develer.com>
*/
#include "cfg/cfg_i2c.h"
#define LOG_LEVEL I2C_LOG_LEVEL
#define LOG_FORMAT I2C_LOG_FORMAT
#include <cfg/log.h>
#include <hw/hw_cpufreq.h> // CPU_FREQ
#include <cfg/debug.h>
#include <cfg/macros.h> // BV()
#include <cfg/module.h>
#include <cpu/detect.h>
#include <cpu/irq.h>
#include <cpu/power.h>
#include <drv/timer.h>
#include <drv/i2c.h>
#include <io/sam3.h>
struct I2cHardware
{
uint32_t base;
bool first_xtranf;
};
INLINE bool waitTxRdy(I2c *i2c, time_t ms_timeout)
{
ticks_t start = timer_clock();
while (!(HWREG(i2c->hw->base + TWI_SR_OFF) & TWI_SR_TXRDY))
{
if (timer_clock() - start > ms_to_ticks(ms_timeout))
return false;
cpu_relax();
}
return true;
}
INLINE bool waitRxRdy(I2c *i2c, time_t ms_timeout)
{
ticks_t start = timer_clock();
while (!(HWREG(i2c->hw->base + TWI_SR_OFF) & TWI_SR_RXRDY))
{
if (timer_clock() - start > ms_to_ticks(ms_timeout))
return false;
cpu_relax();
}
return true;
}
INLINE void waitXferComplete(I2c *i2c)
{
while (!(HWREG(i2c->hw->base + TWI_SR_OFF) & TWI_SR_TXCOMP))
cpu_relax();
}
/*
* The start is not performed when we call the start function
* because the hardware should know the first data byte to send.
* Generally to perform a byte send we should write the slave address
* in slave address register and the first byte to send in data registry.
* After then we can perform the start write procedure, and send really
* the our data. To use common bertos i2c api the really start will be
* performed when the user "put" or "send" its data. These tricks are hide
* from the driver implementation.
*/
static void i2c_sam3_start(struct I2c *i2c, uint16_t slave_addr)
{
i2c->hw->first_xtranf = true;
if (I2C_TEST_START(i2c->flags) == I2C_START_R)
HWREG(i2c->hw->base + TWI_MMR_OFF) = TWI_MMR_DADR(slave_addr >> 1) | TWI_MMR_MREAD;
else
HWREG(i2c->hw->base + TWI_MMR_OFF) = TWI_MMR_DADR(slave_addr >> 1);
}
static void i2c_sam3_putc(I2c *i2c, const uint8_t data)
{
if (!waitTxRdy(i2c, CONFIG_I2C_START_TIMEOUT))
{
LOG_ERR("i2c: txready timeout\n");
i2c->errors |= I2C_START_TIMEOUT;
return;
}
HWREG(i2c->hw->base + TWI_THR_OFF) = data;
if ((i2c->xfer_size == 1) && (I2C_TEST_STOP(i2c->flags) == I2C_STOP))
HWREG(i2c->hw->base + TWI_CR_OFF) = TWI_CR_STOP;
// On first byte sent wait for start timeout
if (i2c->hw->first_xtranf && !waitTxRdy(i2c, CONFIG_I2C_START_TIMEOUT))
{
LOG_ERR("i2c: write start timeout\n");
i2c->errors |= I2C_START_TIMEOUT;
HWREG(i2c->hw->base + TWI_CR_OFF) = TWI_CR_STOP;
waitXferComplete(i2c);
return;
}
i2c->hw->first_xtranf = false;
if ((i2c->xfer_size == 1) && (I2C_TEST_STOP(i2c->flags) == I2C_STOP))
waitXferComplete(i2c);
}
static uint8_t i2c_sam3_getc(I2c *i2c)
{
uint8_t data;
uint32_t cr = 0;
if (i2c->hw->first_xtranf)
{
cr |= TWI_CR_START;
i2c->hw->first_xtranf = false;
}
if ((i2c->xfer_size == 1) && (I2C_TEST_STOP(i2c->flags) == I2C_STOP))
cr |= TWI_CR_STOP;
HWREG(i2c->hw->base + TWI_CR_OFF) = cr;
if (!waitRxRdy(i2c, CONFIG_I2C_START_TIMEOUT))
{
LOG_ERR("i2c: read start timeout\n");
i2c->errors |= I2C_START_TIMEOUT;
return 0xFF;
}
data = HWREG(i2c->hw->base + TWI_RHR_OFF);
if ((i2c->xfer_size == 1) && (I2C_TEST_STOP(i2c->flags) == I2C_STOP))
waitXferComplete(i2c);
return data;
}
static void i2c_setClock(I2c *i2c, int clock)
{
uint32_t ck_div = 0;
uint32_t cl_div;
for (;;)
{
cl_div = ((CPU_FREQ / (2 * clock)) - 4) / (1 << ck_div);
if (cl_div <= 255)
break;
ck_div++;
}
ASSERT(ck_div < 8);
LOG_INFO("i2c: using CKDIV = %lu and CLDIV/CHDIV = %lu\n\n", ck_div, cl_div);
HWREG(i2c->hw->base + TWI_CWGR_OFF) = 0;
HWREG(i2c->hw->base + TWI_CWGR_OFF) = (ck_div << 16) | (cl_div << 8) | cl_div;
}
static const I2cVT i2c_sam3_vt =
{
.start = i2c_sam3_start,
.getc = i2c_sam3_getc,
.putc = i2c_sam3_putc,
.write = i2c_genericWrite,
.read = i2c_genericRead,
};
struct I2cHardware i2c_sam3_hw[I2C_CNT];
/**
* Initialize I2C module.
*/
void i2c_hw_init(I2c *i2c, int dev, uint32_t clock)
{
ASSERT(dev < I2C_CNT);
i2c->hw = &i2c_sam3_hw[dev];
i2c->vt = &i2c_sam3_vt;
pmc_periphEnable(PIOA_ID);
switch (dev)
{
case I2C0:
i2c->hw->base = TWI0_BASE;
PIO_PERIPH_SEL(TWI0_PORT, BV(TWI0_TWD) | BV(TWI0_TWCK), TWI0_PERIPH);
HWREG(TWI0_PORT + PIO_PDR_OFF) = BV(TWI0_TWD) | BV(TWI0_TWCK);
pmc_periphEnable(TWI0_ID);
break;
case I2C1:
i2c->hw->base = TWI1_BASE;
PIO_PERIPH_SEL(TWI1_PORT, BV(TWI1_TWD) | BV(TWI1_TWCK), TWI1_PERIPH);
HWREG(TWI1_PORT + PIO_PDR_OFF) = BV(TWI1_TWD) | BV(TWI1_TWCK);
pmc_periphEnable(TWI1_ID);
break;
default:
ASSERT(!"i2c: invalid dev number");
return;
}
// Reset and set master mode
HWREG(i2c->hw->base + TWI_CR_OFF) = TWI_CR_SWRST;
HWREG(i2c->hw->base + TWI_CR_OFF) = TWI_CR_MSEN | TWI_CR_SVDIS;
i2c_setClock(i2c, clock);
}

View file

@ -0,0 +1,53 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2011 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief TWI driver for SAM3 (interface)
*
* \author Stefano Fedrigo <aleph@develer.com>
*/
#ifndef DRV_I2C_SAM3_H
#define DRV_I2C_SAM3_H
/**
* \name I2C devices enum
*/
enum
{
I2C0,
I2C1,
I2C_CNT /**< Number of ports */
};
#endif /* DRV_I2C_SAM3_H */

View file

@ -0,0 +1,353 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief STM32F103xx I2C driver.
*
* \author Daniele Basile <asterix@develer.com>
*/
#include "cfg/cfg_i2c.h"
#define LOG_LEVEL I2C_LOG_LEVEL
#define LOG_FORMAT I2C_LOG_FORMAT
#include <cfg/log.h>
#include <cfg/debug.h>
#include <cfg/macros.h> // BV()
#include <cfg/module.h>
#include <cpu/power.h>
#include <drv/gpio_stm32.h>
#include <drv/irq_cm3.h>
#include <drv/clock_stm32.h>
#include <drv/i2c.h>
#include <drv/timer.h>
#include <io/stm32.h>
struct I2cHardware
{
struct stm32_i2c *base;
uint32_t clk_i2c_en;
uint32_t pin_mask;
uint8_t cache[2];
bool cached;
};
#define WAIT_BTF(base) \
do { \
while (!(base->SR1 & BV(SR1_BTF))) \
cpu_relax(); \
} while (0)
#define WAIT_RXNE(base) \
do { \
while (!(base->SR1 & BV(SR1_RXNE))) \
cpu_relax(); \
} while (0)
INLINE uint32_t get_status(struct stm32_i2c *base)
{
return ((base->SR1 | (base->SR2 << 16)) & 0x00FFFFFF);
}
/*
* This fuction read the status registers of the i2c device
* and waint until the selec event happen. If occur one error
* the funtions return false.
*/
INLINE bool wait_event(I2c *i2c, uint32_t event)
{
while (true)
{
uint32_t stat = get_status(i2c->hw->base);
if (stat == event)
break;
if (stat & SR1_ERR_MASK)
{
i2c->hw->base->SR1 &= ~SR1_ERR_MASK;
return false;
}
cpu_relax();
}
return true;
}
INLINE void start_w(struct I2c *i2c, uint16_t slave_addr)
{
/*
* Loop on the select write sequence: when the eeprom is busy
* writing previously sent data it will reply to the SLA_W
* control byte with a NACK. In this case, we must
* keep trying until the eeprom responds with an ACK.
*/
ticks_t start = timer_clock();
while (true)
{
i2c->hw->base->CR1 |= CR1_ACK_SET | CR1_START_SET;
if(!wait_event(i2c, I2C_EVENT_MASTER_MODE_SELECT))
{
LOG_ERR("ARBIT lost\n");
i2c->errors |= I2C_ARB_LOST;
break;
}
i2c->hw->base->DR = slave_addr & OAR1_ADD0_RESET;
if(wait_event(i2c, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
break;
if (timer_clock() - start > ms_to_ticks(CONFIG_I2C_START_TIMEOUT))
{
LOG_ERR("Timeout on I2C START\n");
i2c->errors |= I2C_START_TIMEOUT;
i2c->hw->base->CR1 |= CR1_STOP_SET;
break;
}
}
}
INLINE bool start_and_addr(struct I2c *i2c, uint16_t slave_addr)
{
i2c->hw->base->CR1 |= CR1_START_SET;
if(!wait_event(i2c, I2C_EVENT_MASTER_MODE_SELECT))
{
LOG_ERR("ARBIT lost\n");
i2c->errors |= I2C_ARB_LOST;
i2c->hw->base->CR1 |= CR1_STOP_SET;
return false;
}
i2c->hw->base->DR = (slave_addr | OAR1_ADD0_SET);
if (i2c->xfer_size == 2)
i2c->hw->base->CR1 |= CR1_ACK_SET | CR1_POS_SET;
if(!wait_event(i2c, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
{
LOG_ERR("SLAR NACK:%08lx\n", get_status(i2c->hw->base));
i2c->errors |= I2C_NO_ACK;
i2c->hw->base->CR1 |= CR1_STOP_SET;
return false;
}
return true;
}
INLINE void start_r(struct I2c *i2c, uint16_t slave_addr)
{
if (!start_and_addr(i2c, slave_addr))
return;
/*
* Due to the hardware receive bytes from slave in automatically mode
* we should manage contextually all cases that we want to read one, two or more
* than two bytes. To comply this behaviour to our api we shoul bufferd some byte
* to hide all special case that needs to use this device.
*/
if (i2c->xfer_size == 1)
{
i2c->hw->base->CR1 &= CR1_ACK_RESET;
cpu_flags_t irq;
IRQ_SAVE_DISABLE(irq);
(void)i2c->hw->base->SR2;
if (I2C_TEST_STOP(i2c->flags) == I2C_STOP)
i2c->hw->base->CR1 |= CR1_STOP_SET;
IRQ_RESTORE(irq);
WAIT_RXNE(i2c->hw->base);
i2c->hw->cache[0] = i2c->hw->base->DR;
i2c->hw->cached = true;
if (I2C_TEST_STOP(i2c->flags) == I2C_STOP)
while (i2c->hw->base->CR1 & CR1_STOP_SET);
i2c->hw->base->CR1 |= CR1_ACK_SET;
}
else if (i2c->xfer_size == 2)
{
cpu_flags_t irq;
IRQ_SAVE_DISABLE(irq);
(void)i2c->hw->base->SR2;
i2c->hw->base->CR1 &= CR1_ACK_RESET;
IRQ_RESTORE(irq);
WAIT_BTF(i2c->hw->base);
IRQ_SAVE_DISABLE(irq);
if (I2C_TEST_STOP(i2c->flags) == I2C_STOP)
i2c->hw->base->CR1 |= CR1_STOP_SET;
/*
* We store read bytes like a fifo..
*/
i2c->hw->cache[1] = i2c->hw->base->DR;
i2c->hw->cache[0] = i2c->hw->base->DR;
i2c->hw->cached = true;
IRQ_RESTORE(irq);
i2c->hw->base->CR1 &= CR1_POS_RESET;
i2c->hw->base->CR1 |= CR1_ACK_SET;
}
}
static void i2c_stm32_start(struct I2c *i2c, uint16_t slave_addr)
{
i2c->hw->cached = false;
if (I2C_TEST_START(i2c->flags) == I2C_START_W)
start_w(i2c, slave_addr);
else /* (I2C_TEST_START(i2c->flags) == I2C_START_R) */
start_r(i2c, slave_addr);
}
static void i2c_stm32_putc(I2c *i2c, const uint8_t data)
{
i2c->hw->base->DR = data;
WAIT_BTF(i2c->hw->base);
/* Generate the stop if we finish to send all programmed bytes */
if ((i2c->xfer_size == 1) && (I2C_TEST_STOP(i2c->flags) == I2C_STOP))
{
wait_event(i2c, I2C_EVENT_MASTER_BYTE_TRANSMITTED);
i2c->hw->base->CR1 |= CR1_STOP_SET;
}
}
static uint8_t i2c_stm32_getc(I2c *i2c)
{
if (i2c->hw->cached)
{
ASSERT(i2c->xfer_size <= 2);
return i2c->hw->cache[i2c->xfer_size - 1];
}
else
{
WAIT_BTF(i2c->hw->base);
if (i2c->xfer_size == 3)
{
i2c->hw->base->CR1 &= CR1_ACK_RESET;
cpu_flags_t irq;
IRQ_SAVE_DISABLE(irq);
uint8_t data = i2c->hw->base->DR;
if (I2C_TEST_STOP(i2c->flags) == I2C_STOP)
i2c->hw->base->CR1 |= CR1_STOP_SET;
i2c->hw->cache[1] = i2c->hw->base->DR;
IRQ_RESTORE(irq);
WAIT_RXNE(i2c->hw->base);
i2c->hw->cache[0] = i2c->hw->base->DR;
i2c->hw->cached = true;
if (I2C_TEST_STOP(i2c->flags) == I2C_STOP)
while (i2c->hw->base->CR1 & CR1_STOP_SET);
return data;
}
else
return i2c->hw->base->DR;
}
}
static const I2cVT i2c_stm32_vt =
{
.start = i2c_stm32_start,
.getc = i2c_stm32_getc,
.putc = i2c_stm32_putc,
.write = i2c_genericWrite,
.read = i2c_genericRead,
};
static struct I2cHardware i2c_stm32_hw[] =
{
{ /* I2C1 */
.base = (struct stm32_i2c *)I2C1_BASE,
.clk_i2c_en = RCC_APB1_I2C1,
.pin_mask = (GPIO_I2C1_SCL_PIN | GPIO_I2C1_SDA_PIN),
},
{ /* I2C2 */
.base = (struct stm32_i2c *)I2C2_BASE,
.clk_i2c_en = RCC_APB1_I2C2,
.pin_mask = (GPIO_I2C2_SCL_PIN | GPIO_I2C2_SDA_PIN),
},
};
/**
* Initialize I2C module.
*/
void i2c_hw_init(I2c *i2c, int dev, uint32_t clock)
{
i2c->hw = &i2c_stm32_hw[dev];
i2c->vt = &i2c_stm32_vt;
RCC->APB2ENR |= RCC_APB2_GPIOB;
RCC->APB1ENR |= i2c->hw->clk_i2c_en;
/* Set gpio to use I2C driver */
stm32_gpioPinConfig((struct stm32_gpio *)GPIOB_BASE, i2c->hw->pin_mask,
GPIO_MODE_AF_OD, GPIO_SPEED_50MHZ);
/* Clear all needed registers */
i2c->hw->base->CR1 = 0;
i2c->hw->base->CR2 = 0;
i2c->hw->base->CCR = 0;
i2c->hw->base->TRISE = 0;
i2c->hw->base->OAR1 = 0;
/* Set PCLK1 frequency accornding to the master clock settings. See stm32_clock.c */
i2c->hw->base->CR2 |= CR2_FREQ_36MHZ;
/* Configure spi in standard mode */
ASSERT2(clock >= 100000, "fast mode not supported");
i2c->hw->base->CCR |= (uint16_t)((CR2_FREQ_36MHZ * 1000000) / (clock << 1));
i2c->hw->base->TRISE |= (CR2_FREQ_36MHZ + 1);
i2c->hw->base->CR1 |= CR1_PE_SET;
}

View file

@ -0,0 +1,53 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Driver for the STM32F103xx I2C (interface)
*
*/
#ifndef I2C_STM32_H
#define I2C_STM32_H
#include <drv/i2c.h>
/**
* \name I2C devices enum
*/
enum
{
I2C1,
I2C2,
I2C_CNT /**< Number of serial ports */
};
#endif /* I2C_STM32_H */

View file

@ -0,0 +1,138 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Cortex-M3 IRQ management.
*
* \author Andrea Righi <arighi@develer.com>
*/
#include "irq_cm3.h"
#include <cfg/debug.h> /* ASSERT() */
#include <cfg/log.h> /* LOG_ERR() */
#include <cpu/irq.h>
#ifdef __IAR_SYSTEMS_ICC__
#pragma data_alignment=0x400
static void (*irq_table[NUM_INTERRUPTS])(void);
#else
static void (*irq_table[NUM_INTERRUPTS])(void)
__attribute__((section("vtable")));
#endif
/* Priority register / IRQ number table */
static const uint32_t nvic_prio_reg[] =
{
/* System exception registers */
0, NVIC_SYS_PRI1, NVIC_SYS_PRI2, NVIC_SYS_PRI3,
/* External interrupts registers */
NVIC_PRI0, NVIC_PRI1, NVIC_PRI2, NVIC_PRI3,
NVIC_PRI4, NVIC_PRI5, NVIC_PRI6, NVIC_PRI7,
NVIC_PRI8, NVIC_PRI9, NVIC_PRI10, NVIC_PRI11,
NVIC_PRI12, NVIC_PRI13
};
/* Unhandled IRQ */
static NAKED NORETURN void unhandled_isr(void)
{
register uint32_t reg;
#ifdef __IAR_SYSTEMS_ICC__
reg = CPU_READ_IPSR();
#else
asm volatile ("mrs %0, ipsr" : "=r"(reg));
#endif
LOG_ERR("unhandled IRQ %lu\n", reg);
while (1)
PAUSE;
}
void sysirq_setPriority(sysirq_t irq, int prio)
{
uint32_t pos = (irq & 3) * 8;
reg32_t reg = nvic_prio_reg[irq >> 2];
uint32_t val;
val = HWREG(reg);
val &= ~(0xff << pos);
val |= prio << pos;
HWREG(reg) = val;
}
static void sysirq_enable(sysirq_t irq)
{
/* Enable the IRQ line (only for generic IRQs) */
if (irq >= 16 && irq < 48)
NVIC_EN0_R = 1 << (irq - 16);
else if (irq >= 48)
NVIC_EN1_R = 1 << (irq - 48);
}
void sysirq_setHandler(sysirq_t irq, sysirq_handler_t handler)
{
cpu_flags_t flags;
ASSERT(irq < NUM_INTERRUPTS);
IRQ_SAVE_DISABLE(flags);
irq_table[irq] = handler;
sysirq_setPriority(irq, IRQ_PRIO);
sysirq_enable(irq);
IRQ_RESTORE(flags);
}
void sysirq_freeHandler(sysirq_t irq)
{
cpu_flags_t flags;
ASSERT(irq < NUM_INTERRUPTS);
IRQ_SAVE_DISABLE(flags);
irq_table[irq] = unhandled_isr;
IRQ_RESTORE(flags);
}
void sysirq_init(void)
{
cpu_flags_t flags;
int i;
IRQ_SAVE_DISABLE(flags);
for (i = 0; i < NUM_INTERRUPTS; i++)
irq_table[i] = unhandled_isr;
/* Update NVIC to point to the new vector table */
NVIC_VTABLE_R = (size_t)irq_table;
IRQ_RESTORE(flags);
}

View file

@ -0,0 +1,63 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief IRQ management for the Cortex-M3 processor.
*
* \author Andrea Righi <arighi@develer.com>
*/
#ifndef DRV_CORTEX_M3_SYSIRQ_H
#define DRV_CORTEX_M3_SYSIRQ_H
#include <cfg/compiler.h>
#if CPU_CM3_LM3S
#include <io/lm3s.h>
#elif CPU_CM3_STM32
#include <io/stm32.h>
#elif CPU_CM3_SAM3
#include <io/sam3.h>
/*#elif Add other families here */
#else
#error Unknown CPU
#endif
typedef void (*sysirq_handler_t)(void);
typedef unsigned int sysirq_t;
void sysirq_setHandler(sysirq_t irq, sysirq_handler_t handler);
void sysirq_setPriority(sysirq_t irq, int prio);
void sysirq_freeHandler(sysirq_t irq);
void sysirq_init(void);
#endif /* DRV_CORTEX_M3_SYSIRQ_H */

View file

@ -0,0 +1,49 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \author Francesco Sacchi <batt@develer.com>
*
* \brief Low-level kdebug module for Cortex-M3 (inplementation).
*/
#include <cpu/detect.h>
#if CPU_CM3_LM3S
#include "kdebug_lm3s.c"
#elif CPU_CM3_STM32
#include "kdebug_stm32.c"
#elif CPU_CM3_SAM3
#include "kdebug_sam3.c"
/*#elif Add other families here */
#else
#error Unknown CPU
#endif

View file

@ -0,0 +1,118 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief LM3S debug support (implementation).
*
* \author Andrea Righi <arighi@develer.com>
*/
#include <cfg/cfg_debug.h>
#include <cfg/macros.h> /* for BV() */
#include <drv/clock_lm3s.h> /* lm3s_busyWait() */
#include <drv/gpio_lm3s.h>
#include <drv/ser_lm3s.h>
#include "kdebug_lm3s.h"
#if CONFIG_KDEBUG_PORT == 0
#define UART_BASE UART0_BASE
#define UART_GPIO_BASE GPIO_PORTA_BASE
#define UART_PINS (BV(1) | BV(0))
#define UART_REG_SYSCTL SYSCTL_RCGC2_GPIOA
#elif CONFIG_KDEBUG_PORT == 1
#define UART_BASE UART1_BASE
#define UART_GPIO_BASE GPIO_PORTD_BASE
#define UART_PINS (BV(3) | BV(2))
#define UART_REG_SYSCTL SYSCTL_RCGC2_GPIOD
#elif CONFIG_KDEBUG_PORT == 2
#define UART_BASE UART2_BASE
#define UART_GPIO_BASE GPIO_PORTG_BASE
#define UART_PINS (BV(1) | BV(0))
#define UART_REG_SYSCTL SYSCTL_RCGC2_GPIOG
#else
#error "UART port not supported in this board"
#endif
#define KDBG_WAIT_READY() while (!lm3s_uartReady(UART_BASE)) {}
#define KDBG_WAIT_TXDONE() while (!lm3s_uartTxDone(UART_BASE)) {}
#define KDBG_WRITE_CHAR(c) do { lm3s_uartPutCharNonBlocking(UART_BASE, c); } while(0)
/* Debug unit is used only for debug purposes so does not generate interrupts. */
#define KDBG_MASK_IRQ(old) do { (void)old; } while(0)
/* Debug unit is used only for debug purposes so does not generate interrupts. */
#define KDBG_RESTORE_IRQ(old) do { (void)old; } while(0)
typedef uint32_t kdbg_irqsave_t;
INLINE void uart_hw_config(void)
{
unsigned long div, baud = CONFIG_KDEBUG_BAUDRATE;
bool hi_speed = false;
if (baud * 16 > CPU_FREQ)
{
hi_speed = true;
baud /= 2;
}
div = (CPU_FREQ * 8 / baud + 1) / 2;
lm3s_uartDisable(UART_BASE);
if (hi_speed)
HWREG(UART_BASE + UART_O_CTL) |= UART_CTL_HSE;
else
HWREG(UART_BASE + UART_O_CTL) &= ~UART_CTL_HSE;
/* Set the baud rate */
HWREG(UART_BASE + UART_O_IBRD) = div / 64;
HWREG(UART_BASE + UART_O_FBRD) = div % 64;
/* Set word lenght and parity */
HWREG(UART_BASE + UART_O_LCRH) = UART_LCRH_WLEN_8;
lm3s_uartClear(UART_BASE);
lm3s_uartEnable(UART_BASE);
}
INLINE void kdbg_hw_init(void)
{
uint32_t reg_clock = 1 << CONFIG_KDEBUG_PORT;
/* Enable the peripheral clock */
SYSCTL_RCGC1_R |= reg_clock;
SYSCTL_RCGC2_R |= UART_REG_SYSCTL;
lm3s_busyWait(512);
/* Configure GPIO pins to work as UART pins */
lm3s_gpioPinConfig(UART_GPIO_BASE, UART_PINS,
GPIO_DIR_MODE_HW, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD);
/* Low-level UART configuration */
uart_hw_config();
}

View file

@ -0,0 +1,44 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief LM3S debug support (interface).
*
* \author Andrea Righi <arighi@develer.com>
*/
#ifndef DRV_KDEBUG_LM3S_H
#define DRV_KDEBUG_LM3S_H
#define KDEBUG_PORT_DEFAULT 0 ///< Default debug port.
/* \} */
#endif /* DRV_KDEBUG_LM3S_H */

View file

@ -0,0 +1,100 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief SAM3 debug support (implementation).
*
* \author Stefano Fedrigo <aleph@develer.com>
*/
#include <cfg/cfg_debug.h>
#include <cfg/macros.h> /* for BV() */
#include <cpu/types.h>
#include <io/sam3.h>
#if (CONFIG_KDEBUG_PORT == 0)
#define UART_BASE UART0_BASE
#define UART_ID UART0_ID
#define UART_PIO_BASE UART0_PORT
#define UART_PERIPH UART0_PERIPH
#define UART_PINS (BV(URXD0) | BV(UTXD0))
#elif (CONFIG_KDEBUG_PORT == 1) && UART_PORTS > 1
#define UART_BASE UART1_BASE
#define UART_ID UART1_ID
#define UART_PIO_BASE UART1_PORT
#define UART_PERIPH UART1_PERIPH
#define UART_PINS (BV(URXD1) | BV(UTXD1))
#else
#error "UART port not supported in this board"
#endif
// TODO: refactor serial simple functions and use them, see lm3s kdebug
#define KDBG_WAIT_READY() while (!(HWREG(UART_BASE + UART_SR_OFF) & BV(UART_SR_TXRDY))) {}
#define KDBG_WAIT_TXDONE() while (!(HWREG(UART_BASE + UART_SR_OFF) & BV(UART_SR_TXEMPTY))) {}
#define KDBG_WRITE_CHAR(c) do { HWREG(UART_BASE + UART_THR_OFF) = (c); } while(0)
/* Debug unit is used only for debug purposes so does not generate interrupts. */
#define KDBG_MASK_IRQ(old) do { (void)old; } while(0)
/* Debug unit is used only for debug purposes so does not generate interrupts. */
#define KDBG_RESTORE_IRQ(old) do { (void)old; } while(0)
typedef uint32_t kdbg_irqsave_t;
INLINE void kdbg_hw_init(void)
{
/*
* Disable PIO mode and set appropriate UART pins peripheral mode.
* SAM3X,A,N,S,U: all of them has all UARTs on peripheral A.
*/
HWREG(UART_PIO_BASE + PIO_PDR_OFF) = UART_PINS;
PIO_PERIPH_SEL(UART_PIO_BASE, UART_PINS, UART_PERIPH);
/* Enable the peripheral clock */
pmc_periphEnable(UART_ID);
/* Reset and disable receiver & transmitter */
HWREG(UART_BASE + UART_CR_OFF) = BV(UART_CR_RSTRX) | BV(UART_CR_RSTTX) | BV(UART_CR_RXDIS) | BV(UART_CR_TXDIS);
/* Set mode: normal, no parity */
HWREG(UART_BASE + UART_MR_OFF) = UART_MR_PAR_NO;
/* Set baud rate */
HWREG(UART_BASE + UART_BRGR_OFF) = CPU_FREQ / CONFIG_KDEBUG_BAUDRATE / 16;
/* Enable receiver & transmitter */
HWREG(UART_BASE + UART_CR_OFF) = BV(UART_CR_RXEN) | BV(UART_CR_TXEN);
}

View file

@ -0,0 +1,103 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief STM32 debug support (implementation).
*
* \author Andrea Righi <arighi@develer.com>
*/
#include "kdebug_stm32.h"
#include <cfg/cfg_debug.h>
#include <drv/gpio_stm32.h>
#include <drv/clock_stm32.h> /* RCC */
#include <io/stm32.h>
#if CONFIG_KDEBUG_PORT == 0
#define UART_BASE ((struct stm32_usart *)USART1_BASE)
#elif CONFIG_KDEBUG_PORT == 1
#define UART_BASE ((struct stm32_usart *)USART2_BASE)
#elif CONFIG_KDEBUG_PORT == 2
#define UART_BASE ((struct stm32_usart *)USART3_BASE)
#else
#error "UART port not supported in this board"
#endif
#define KDBG_WAIT_READY() while (!(UART_BASE->SR & USART_FLAG_TXE))
#define KDBG_WAIT_TXDONE() while (!(UART_BASE->SR & USART_FLAG_TC))
#define KDBG_WRITE_CHAR(c) do { UART_BASE->DR = (c) & 0x1ff; } while(0)
/* Debug unit is used only for debug purposes so does not generate interrupts. */
#define KDBG_MASK_IRQ(old) do { (void)old; } while(0)
/* Debug unit is used only for debug purposes so does not generate interrupts. */
#define KDBG_RESTORE_IRQ(old) do { (void)old; } while(0)
typedef uint32_t kdbg_irqsave_t;
/* Initialize UART debug port */
INLINE void kdbg_hw_init(void)
{
/* Enable clocking on AFIO */
RCC->APB2ENR |= RCC_APB2_AFIO;
/* Configure USART pins */
#if CONFIG_KDEBUG_PORT == 0
RCC->APB2ENR |= RCC_APB2_GPIOA;
RCC->APB2ENR |= RCC_APB2_USART1;
stm32_gpioPinConfig((struct stm32_gpio *)GPIOA_BASE, GPIO_USART1_TX_PIN,
GPIO_MODE_AF_PP, GPIO_SPEED_50MHZ);
#elif CONFIG_KDEBUG_PORT == 1
RCC->APB2ENR |= RCC_APB2_GPIOA;
RCC->APB1ENR |= RCC_APB1_USART2;
stm32_gpioPinConfig((struct stm32_gpio *)GPIOA_BASE, GPIO_USART2_TX_PIN,
GPIO_MODE_AF_PP, GPIO_SPEED_50MHZ);
#elif CONFIG_KDEBUG_PORT == 2
RCC->APB2ENR |= RCC_APB2_GPIOB;
RCC->APB1ENR |= RCC_APB1_USART3;
stm32_gpioPinConfig((struct stm32_gpio *)GPIOB_BASE, GPIO_USART3_TX_PIN,
GPIO_MODE_AF_PP, GPIO_SPEED_50MHZ);
#else
#error "UART port not supported in this board"
#endif
/* Enable the USART by writing the UE bit */
UART_BASE->CR1 |= CR1_RUN_SET;
/* Configure the desired baud rate */
UART_BASE->BRR = (uint16_t)evaluate_brr(UART_BASE, CPU_FREQ, CONFIG_KDEBUG_BAUDRATE);
/* Set the Transmitter Enable bit in CR1 */
UART_BASE->CR1 |= USART_MODE_TX;
}

View file

@ -0,0 +1,44 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief STM32 debug support (interface).
*
* \author Andrea Righi <arighi@develer.com>
*/
#ifndef DRV_KDEBUG_STM32_H
#define DRV_KDEBUG_STM32_H
#define KDEBUG_PORT_DEFAULT 1 ///< Default debug port.
/* \} */
#endif /* DRV_KDEBUG_STM32_H */

View file

@ -0,0 +1,272 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2011 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief NAND driver hardware implementation for SAM3's static memory controller.
*
* \author Stefano Fedrigo <aleph@develer.com>
*/
#include <drv/nand.h>
#include <cfg/log.h>
#include <io/sam3.h>
#include <drv/timer.h>
#include <cpu/power.h> // cpu_relax()
/*
* PIO definitions.
*/
#define NAND_PIN_CE BV(6)
#define NAND_PIN_RB BV(2)
#define NAND_PINS_PORTA (NAND_PIN_CE | NAND_PIN_RB)
#define NAND_PERIPH_PORTA PIO_PERIPH_B
#define NAND_PIN_OE BV(19)
#define NAND_PIN_WE BV(20)
#define NAND_PIN_IO 0x0000FFFF
#define NAND_PINS_PORTC (NAND_PIN_OE | NAND_PIN_WE | NAND_PIN_IO)
#define NAND_PERIPH_PORTC PIO_PERIPH_A
#define NAND_PIN_CLE BV(9)
#define NAND_PIN_ALE BV(8)
#define NAND_PINS_PORTD (NAND_PIN_CLE | NAND_PIN_ALE)
#define NAND_PERIPH_PORTD PIO_PERIPH_A
/*
* Wait for edge transition of READY/BUSY NAND
* signal.
* Return true for edge detection, false in case of timeout.
*/
bool nand_waitReadyBusy(UNUSED_ARG(Nand *, chip), time_t timeout)
{
time_t start = timer_clock();
while (!(SMC_SR & SMC_SR_RB_EDGE0))
{
cpu_relax();
if (timer_clock() - start > timeout)
{
LOG_INFO("nand: R/B timeout\n");
return false;
}
}
return true;
}
/*
* Wait for transfer to complete until timeout.
* If transfer completes return true, false in case of timeout.
*/
bool nand_waitTransferComplete(UNUSED_ARG(Nand *, chip), time_t timeout)
{
time_t start = timer_clock();
while (!(SMC_SR & SMC_SR_XFRDONE))
{
cpu_relax();
if (timer_clock() - start > timeout)
{
LOG_INFO("nand: xfer complete timeout\n");
return false;
}
}
return true;
}
/*
* Send command to NAND and wait for completion.
*/
void nand_sendCommand(Nand *chip,
uint32_t cmd1, uint32_t cmd2,
int num_cycles, uint32_t cycle0, uint32_t cycle1234)
{
reg32_t *cmd_addr;
uint32_t cmd_val;
while (HWREG(NFC_CMD_BASE_ADDR + NFC_CMD_NFCCMD) & 0x8000000);
if (num_cycles == 5)
SMC_ADDR = cycle0;
cmd_val = NFC_CMD_NFCCMD
| ((chip->chip_select << NFC_CMD_CSID_SHIFT) & NFC_CMD_CSID_MASK)
| ((num_cycles << NFC_CMD_ACYCLE_SHIFT) & NFC_CMD_ACYCLE_MASK)
| cmd1 << 2
| cmd2 << 10;
// Check for commands transferring data
if (cmd1 == NAND_CMD_WRITE_1 || cmd1 == NAND_CMD_READ_1 || cmd1 == NAND_CMD_READID)
cmd_val |= NFC_CMD_NFCEN;
// Check for commands writing data
if (cmd1 == NAND_CMD_WRITE_1)
cmd_val |= NFC_CMD_NFCWR;
// Check for two command cycles
if (cmd2)
cmd_val |= NFC_CMD_VCMD2;
cmd_addr = (reg32_t *)(NFC_CMD_BASE_ADDR + cmd_val);
*cmd_addr = cycle1234;
while (!(SMC_SR & SMC_SR_CMDDONE));
}
/*
* Get NAND chip status register.
*
* NOTE: this is global between different chip selects, so returns
* the status register of the last used NAND chip.
*/
uint8_t nand_getChipStatus(UNUSED_ARG(Nand *, chip))
{
return (uint8_t)HWREG(NFC_CMD_BASE_ADDR);
}
/*
* Return pointer to buffer where data are read to or written from
* by nand_sendCommand().
*/
void *nand_dataBuffer(UNUSED_ARG(Nand *, chip))
{
return (void *)NFC_SRAM_BASE_ADDR;
}
/*
* Extract ECC data from ECC_PRx registers.
*/
bool nand_checkEcc(UNUSED_ARG(Nand *, chip))
{
uint32_t sr1 = SMC_ECC_SR1;
if (sr1)
{
LOG_INFO("ECC error, ECC_SR1=0x%lx\n", sr1);
return false;
}
else
return true;
}
/*
* Compute ECC on data in a buffer.
*
* \param chip nand context
* \param buf buffer containing data
* \param size size of data buffer
* \param ecc pointer to buffer where computed ECC is stored
* \param ecc_size max size for ecc buffer
*/
void nand_computeEcc(UNUSED_ARG(Nand *, chip),
UNUSED_ARG(const void *, buf), UNUSED_ARG(size_t, size), uint32_t *ecc, size_t ecc_size)
{
size_t i;
for (i = 0; i < ecc_size; i++)
ecc[i] = *((reg32_t *)(SMC_BASE + SMC_ECC_PR0_OFF) + i);
}
/*
* Low-level hardware driver initialization.
*/
void nand_hwInit(UNUSED_ARG(Nand *, chip))
{
// FIXME: Parameters specific for MT29F8G08AAD
// PIO init
pmc_periphEnable(PIOA_ID);
pmc_periphEnable(PIOC_ID);
pmc_periphEnable(PIOD_ID);
PIO_PERIPH_SEL(PIOA_BASE, NAND_PINS_PORTA, NAND_PERIPH_PORTA);
PIOA_PDR = NAND_PINS_PORTA;
PIOA_PUER = NAND_PINS_PORTA;
PIO_PERIPH_SEL(PIOC_BASE, NAND_PINS_PORTC, NAND_PERIPH_PORTC);
PIOC_PDR = NAND_PINS_PORTC;
PIOC_PUER = NAND_PINS_PORTC;
PIO_PERIPH_SEL(PIOD_BASE, NAND_PINS_PORTD, NAND_PERIPH_PORTD);
PIOD_PDR = NAND_PINS_PORTD;
PIOD_PUER = NAND_PINS_PORTD;
pmc_periphEnable(SMC_SDRAMC_ID);
// SMC init
SMC_SETUP0 = SMC_SETUP_NWE_SETUP(0)
| SMC_SETUP_NCS_WR_SETUP(0)
| SMC_SETUP_NRD_SETUP(0)
| SMC_SETUP_NCS_RD_SETUP(0);
SMC_PULSE0 = SMC_PULSE_NWE_PULSE(2)
| SMC_PULSE_NCS_WR_PULSE(3)
| SMC_PULSE_NRD_PULSE(2)
| SMC_PULSE_NCS_RD_PULSE(3);
SMC_CYCLE0 = SMC_CYCLE_NWE_CYCLE(3)
| SMC_CYCLE_NRD_CYCLE(3);
SMC_TIMINGS0 = SMC_TIMINGS_TCLR(1)
| SMC_TIMINGS_TADL(6)
| SMC_TIMINGS_TAR(4)
| SMC_TIMINGS_TRR(2)
| SMC_TIMINGS_TWB(9)
| SMC_TIMINGS_RBNSEL(7)
| SMC_TIMINGS_NFSEL;
SMC_MODE0 = SMC_MODE_READ_MODE
| SMC_MODE_WRITE_MODE;
SMC_CFG = SMC_CFG_PAGESIZE_PS2048_64
| SMC_CFG_EDGECTRL
| SMC_CFG_DTOMUL_X1048576
| SMC_CFG_DTOCYC(0xF)
| SMC_CFG_WSPARE
| SMC_CFG_RSPARE;
// Disable SMC interrupts, reset and enable NFC controller
SMC_IDR = ~0;
SMC_CTRL = 0;
SMC_CTRL = SMC_CTRL_NFCEN;
// Enable ECC, 1 ECC per 256 bytes
SMC_ECC_CTRL = SMC_ECC_CTRL_SWRST;
SMC_ECC_MD = SMC_ECC_MD_ECC_PAGESIZE_PS2048_64 | SMC_ECC_MD_TYPCORREC_C256B;
}

View file

@ -0,0 +1,93 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief LM3 backend implementation entropy pulling.
* \author Giovanni Bajo <rasky@develer.com>
*/
#include "random_p.h"
#include <cpu/power.h>
#include <io/cm3.h>
#include <drv/clock_cm3.h>
/*
* Return the cpu core temperature in raw format
*/
INLINE uint16_t hw_readRawTemp(void)
{
/* Trig the temperature sampling */
HWREG(ADC0_BASE + ADC_O_PSSI) |= ADC_PSSI_SS3;
while (!(HWREG(ADC0_BASE + ADC_O_SSFSTAT3) & ADC_SSFSTAT3_FULL))
cpu_relax();
return (uint16_t)HWREG(ADC0_BASE + ADC_O_SSFIFO3);
}
INLINE void hw_initIntTemp(void)
{
SYSCTL_RCGC0_R |= SYSCTL_RCGC0_ADC0;
lm3s_busyWait(10);
/* Disable all sequence */
HWREG(ADC0_BASE + ADC_O_ACTSS) = 0;
/* Set trigger event to programmed (for all sequence) */
HWREG(ADC0_BASE + ADC_O_EMUX) = 0;
/* Enalbe read of temperature sensor */
HWREG(ADC0_BASE + ADC_O_SSCTL3) |= ADC_SSCTL3_TS0;
/* Enable sequence S03 (single sample on select channel) */
HWREG(ADC0_BASE + ADC_O_ACTSS) |= ADC_ACTSS_ASEN3;
}
void random_pull_entropy(uint8_t *entropy, size_t len)
{
// We use the internal temperature sensor of LM3S as a source of entropy.
// The last bit of the acquisition is very variable and with a decent distribution
// to consider it "entropic". It does not really matter because it will
// go through a randomness extractor anyway.
hw_initIntTemp();
for (size_t j=0; j<len; j++)
{
uint8_t accum = 0;
for (int b=0; b<8; ++b)
if (hw_readRawTemp() & 1)
accum |= 1<<b;
*entropy++ = accum;
}
}

View file

@ -0,0 +1,109 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief STM32 backend implementation entropy pulling.
* \author Daniele Basile <asterix@develer.com>
*
*/
#include <sec/random_p.h>
#include <cpu/power.h>
#include <drv/clock_cm3.h>
#include <io/cm3.h>
struct stm32_adc *adc = (struct stm32_adc *)ADC1_BASE;
/*
* Return the cpu core temperature in raw format
*/
INLINE uint16_t hw_readRawTemp(void)
{
/* We sample only from one channel */
adc->SQR1 |= BV(SQR1_SQ_LEN_SHIFT);
adc->SQR3 = (ADC_TEMP_CH & SQR3_SQ_MASK);
/* Start convertion */
adc->CR2 |= CR2_EXTTRIG_SWSTRT_SET;
/* Wait in polling until conversion is done */
while (!(adc->SR & BV(SR_EOC)))
cpu_relax();
/* Return the last converted data */
return (uint16_t)adc->DR;
}
INLINE void hw_initIntTemp(void)
{
RCC->APB2ENR |= RCC_APB2_ADC1;
/* Reset registry */
adc->CR1 = 0;
adc->CR2 = 0;
adc->SQR1 = 0;
adc->SQR2 = 0;
adc->SQR3 = 0;
/*
* Configure ADC
* - Regular mode
* - Wake up adc
* - Wake up temperature and Vrefint
*/
adc->CR2 |= BV(CR2_ADON) | ADC_EXTERNALTRIGCONV_NONE | BV(CR2_TSVREFE);
/* Set 17.1usec sampling time*/
adc->SMPR1 |= ((ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH17) | (ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH16));
}
void random_pull_entropy(uint8_t *entropy, size_t len)
{
// We use the internal temperature sensor of LM3S as a source of entropy.
// The last bit of the acquisition is very variable and with a decent distribution
// to consider it "entropic". It does not really matter because it will
// go through a randomness extractor anyway.
hw_initIntTemp();
for (size_t j=0; j<len; j++)
{
uint8_t accum = 0;
for (int b=0; b<8; ++b)
if (hw_readRawTemp() & 1)
accum |= 1<<b;
*entropy++ = accum;
}
}

View file

@ -0,0 +1,152 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2011 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief STM32 RTC driver.
*
* \author Andrea Righi <arighi@develer.com>
*/
#include "clock_stm32.h"
#include <cfg/compiler.h>
#include <cfg/module.h>
#include <cfg/debug.h>
#include <io/stm32.h>
#include <io/stm32_pwr.h>
#include <cpu/power.h> // cpu_relax()
#include <drv/rtc.h>
/* PWR registers base */
static struct PWR *PWR = (struct PWR *)PWR_BASE;
/* RTC clock source: LSE */
#define RTC_CLKSRC 0x00000100
/* RTC clock: 32768 Hz */
#define RTC_CLOCK 32768
/* RTC clock period (in ms) */
#define RTC_PERIOD 1000
/* RTC control register */
#define RTC_CRH (*(reg16_t *)(RTC_BASE + 0x00))
#define RTC_CRL (*(reg16_t *)(RTC_BASE + 0x04))
#define RTC_CRL_SECIE BV(0)
#define RTC_CRL_ALRIE BV(1)
#define RTC_CRL_OWIE BV(2)
#define RTC_CRL_SECF BV(0)
#define RTC_CRL_ALRF BV(1)
#define RTC_CRL_OWF BV(2)
#define RTC_CRL_RSF BV(3)
#define RTC_CRL_CNF BV(4)
#define RTC_CRL_RTOFF BV(5)
/* RTC prescaler load register */
#define RTC_PRLH (*(reg16_t *)(RTC_BASE + 0x08))
#define RTC_PRLL (*(reg16_t *)(RTC_BASE + 0x0c))
/* RTC prescaler divider register */
#define RTC_DIVH (*(reg16_t *)(RTC_BASE + 0x10))
#define RTC_DIVL (*(reg16_t *)(RTC_BASE + 0x14))
/* RTC counter register */
#define RTC_CNTH (*(reg16_t *)(RTC_BASE + 0x18))
#define RTC_CNTL (*(reg16_t *)(RTC_BASE + 0x1c))
/* RTC alarm register */
#define RTC_ALRH (*(reg16_t *)(RTC_BASE + 0x20))
#define RTC_ALRL (*(reg16_t *)(RTC_BASE + 0x24))
static void rtc_enterConfig(void)
{
/* Enter configuration mode */
RTC_CRL |= RTC_CRL_CNF;
}
static void rtc_exitConfig(void)
{
/* Exit from configuration mode */
RTC_CRL &= ~RTC_CRL_CNF;
while (!(RTC_CRL & RTC_CRL_RTOFF))
cpu_relax();
}
uint32_t rtc_time(void)
{
return (RTC_CNTH << 16) | RTC_CNTL;
}
void rtc_setTime(uint32_t val)
{
rtc_enterConfig();
RTC_CNTH = (val >> 16) & 0xffff;
RTC_CNTL = val & 0xffff;
rtc_exitConfig();
}
/* Initialize the RTC clock */
int rtc_init(void)
{
#if CONFIG_KERN
MOD_CHECK(proc);
#endif
/* Enable clock for Power interface */
RCC->APB1ENR |= RCC_APB1_PWR;
/* Enable access to RTC registers */
PWR->CR |= PWR_CR_DBP;
/* Enable LSE */
RCC->BDCR |= RCC_BDCR_LSEON;
/* Wait for LSE ready */
while (!(RCC->BDCR & RCC_BDCR_LSERDY))
cpu_relax();
/* Set clock source and enable RTC peripheral */
RCC->BDCR |= RTC_CLKSRC | RCC_BDCR_RTCEN;
rtc_enterConfig();
/* Set prescaler */
RTC_PRLH = ((RTC_PERIOD * RTC_CLOCK / 1000 - 1) >> 16) & 0xff;
RTC_PRLL = ((RTC_PERIOD * RTC_CLOCK / 1000 - 1)) & 0xffff;
rtc_exitConfig();
/* Disable access to the RTC registers */
PWR->CR &= ~PWR_CR_DBP;
return 0;
}

View file

@ -0,0 +1,49 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Low-level serial module for Cortex-M3 (interface).
*
* \author Andrea Righi <arighi@develer.com>
*/
#include <cpu/detect.h>
#if CPU_CM3_LM3S
#include "ser_lm3s.h"
#elif CPU_CM3_STM32
#include "ser_stm32.h"
#elif CPU_CM3_SAM3
#include "ser_sam3.h"
/*#elif Add other Cortex-M3 CPUs here */
#else
#error Unknown CPU
#endif

View file

@ -0,0 +1,369 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief LM3S1968 UART interface driver.
*
* \author Andrea Righi <arighi@develer.com>
*/
#include <cfg/macros.h> /* for BV() */
#include <drv/gpio_lm3s.h>
#include <drv/ser_p.h>
#include <drv/ser.h>
#include <drv/irq_cm3.h>
#include "cfg/cfg_ser.h"
#include "ser_lm3s.h"
/* From the high-level serial driver */
extern struct Serial *ser_handles[SER_CNT];
struct CM3Serial
{
struct SerialHardware hw;
bool sending;
uint32_t base;
sysirq_t irq;
};
/* Forward declaration */
static struct CM3Serial UARTDesc[SER_CNT];
/* GPIO descriptor for UART pins */
struct gpio_uart_info
{
/* Sysctl */
uint32_t sysctl;
/* GPIO base address register */
uint32_t base;
/* Pin(s) bitmask */
uint8_t pins;
};
/* Table to retrieve GPIO pins configuration to work as UART pins */
static const struct gpio_uart_info gpio_uart[SER_CNT] =
{
/* UART0 */
{
.base = GPIO_PORTA_BASE,
.pins = BV(1) | BV(0),
.sysctl = SYSCTL_RCGC2_GPIOA,
},
/* UART1 */
{
.base = GPIO_PORTD_BASE,
.pins = BV(3) | BV(2),
.sysctl = SYSCTL_RCGC2_GPIOD,
},
/* UART2 */
{
.base = GPIO_PORTG_BASE,
.pins = BV(1) | BV(0),
.sysctl = SYSCTL_RCGC2_GPIOG,
},
};
void lm3s_uartSetBaudRate(uint32_t base, unsigned long baud)
{
unsigned long div;
bool hi_speed;
if (baud * 16 > CPU_FREQ)
{
hi_speed = true;
baud /= 2;
}
div = (CPU_FREQ * 8 / baud + 1) / 2;
lm3s_uartDisable(base);
if (hi_speed)
HWREG(base + UART_O_CTL) |= UART_CTL_HSE;
else
HWREG(base + UART_O_CTL) &= ~UART_CTL_HSE;
/* Set the baud rate */
HWREG(base + UART_O_IBRD) = div / 64;
HWREG(base + UART_O_FBRD) = div % 64;
lm3s_uartClear(base);
lm3s_uartEnable(base);
}
void lm3s_uartSetParity(uint32_t base, int parity)
{
/* Set 8-bit word, one stop bit by default */
uint32_t config = UART_LCRH_WLEN_8;
switch(parity)
{
case SER_PARITY_NONE:
break;
case SER_PARITY_ODD:
config |= UART_LCRH_PEN;
break;
case SER_PARITY_EVEN:
config |= UART_LCRH_EPS | UART_LCRH_PEN;
break;
default:
ASSERT(0);
return;
}
lm3s_uartDisable(base);
HWREG(base + UART_O_LCRH) = config;
lm3s_uartClear(base);
lm3s_uartEnable(base);
}
void lm3s_uartInit(int port)
{
uint32_t reg_clock, base;
ASSERT(port >= 0 && port < SER_CNT);
base = UARTDesc[port].base;
reg_clock = 1 << port;
/* Enable the peripheral clock */
SYSCTL_RCGC1_R |= reg_clock;
SYSCTL_RCGC2_R |= gpio_uart[port].sysctl;
lm3s_busyWait(512);
/* Configure GPIO pins to work as UART pins */
lm3s_gpioPinConfig(gpio_uart[port].base, gpio_uart[port].pins,
GPIO_DIR_MODE_HW, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD);
/* Set serial param: 115.200 bps, no parity */
lm3s_uartSetBaudRate(base, 115200);
lm3s_uartSetParity(base, SER_PARITY_NONE);
}
static bool tx_sending(struct SerialHardware *_hw)
{
struct CM3Serial *hw = (struct CM3Serial *)_hw;
return hw->sending;
}
static void uart_irq_rx(int port)
{
struct FIFOBuffer *rxfifo = &ser_handles[port]->rxfifo;
uint32_t base = UARTDesc[port].base;
char c;
while (lm3s_uartRxReady(base))
{
c = HWREG(base + UART_O_DR);
if (fifo_isfull(rxfifo))
ser_handles[port]->status |= SERRF_RXFIFOOVERRUN;
else
fifo_push(rxfifo, c);
}
}
static void uart_irq_tx(int port)
{
struct FIFOBuffer *txfifo = &ser_handles[port]->txfifo;
uint32_t base = UARTDesc[port].base;
while (lm3s_uartTxReady(base))
{
if (fifo_isempty(txfifo))
{
/*
* Disable TX empty interrupts if there're no more
* characters to transmit.
*/
HWREG(base + UART_O_IM) &= ~UART_IM_TXIM;
UARTDesc[port].sending = false;
break;
}
HWREG(base + UART_O_DR) = fifo_pop(txfifo);
}
}
static void uart_common_irq_handler(int port)
{
uint32_t base = UARTDesc[port].base;
uint32_t status;
/* Read and clear the IRQ status */
status = HWREG(base + UART_O_RIS);
/* Process the IRQ */
if (status & (UART_RIS_RXRIS | UART_RIS_RTRIS))
uart_irq_rx(port);
if (status & UART_RIS_TXRIS)
uart_irq_tx(port);
}
static void lm3s_uartIRQEnable(int port, sysirq_handler_t handler)
{
uint32_t base = UARTDesc[port].base;
sysirq_t irq = UARTDesc[port].irq;
/* Register the IRQ handler */
sysirq_setHandler(irq, handler);
/* Enable RX interrupt in the UART interrupt mask register */
HWREG(base + UART_O_IM) |= UART_IM_RXIM | UART_IM_RTIM;
}
static void lm3s_uartIRQDisable(int port)
{
uint32_t base = UARTDesc[port].base;
HWREG(base + UART_O_IM) &=
~(UART_IM_TXIM | UART_IM_RXIM | UART_IM_RTIM);
}
/* UART class definition */
#define UART_PORT(port) \
/* UART TX and RX buffers */ \
static unsigned char \
uart ## port ## _txbuffer[CONFIG_UART ## port ## _TXBUFSIZE]; \
static unsigned char \
uart ## port ## _rxbuffer[CONFIG_UART ## port ## _RXBUFSIZE]; \
\
/* UART interrupt handler */ \
static DECLARE_ISR(uart ## port ## _irq_handler) \
{ \
uart_common_irq_handler(port); \
} \
\
/* UART public methods */ \
static void \
uart ## port ## _txStart(struct SerialHardware *_hw) \
{ \
struct FIFOBuffer *txfifo = &ser_handles[port]->txfifo; \
struct CM3Serial *hw = (struct CM3Serial *)_hw; \
\
if (hw->sending) \
return; \
lm3s_uartPutChar(UART ## port ## _BASE, fifo_pop(txfifo)); \
if (!fifo_isempty(txfifo)) \
{ \
HWREG(UART ## port ## _BASE + UART_O_IM) |= \
UART_IM_TXIM; \
hw->sending = true; \
} \
} \
\
static void \
uart ## port ## _setbaudrate(UNUSED_ARG(struct SerialHardware *, hw), \
unsigned long baud) \
{ \
lm3s_uartSetBaudRate(UART ## port ## _BASE, baud); \
} \
\
static void \
uart ## port ## _setparity(UNUSED_ARG(struct SerialHardware *, hw), \
int parity) \
{ \
lm3s_uartSetParity(UART ## port ## _BASE, parity); \
} \
\
static void \
uart ## port ## _cleanup(struct SerialHardware *_hw) \
{ \
struct CM3Serial *hw = (struct CM3Serial *)_hw; \
\
hw->sending = false; \
lm3s_uartIRQDisable(port); \
lm3s_uartClear(UART ## port ## _BASE); \
lm3s_uartDisable(UART ## port ## _BASE); \
} \
\
static void \
uart ## port ## _init(UNUSED_ARG(struct SerialHardware *, hw), \
UNUSED_ARG(struct Serial *, ser)) \
{ \
lm3s_uartInit(port); \
lm3s_uartEnable(UART ## port ## _BASE); \
lm3s_uartIRQEnable(port, uart ## port ## _irq_handler); \
} \
\
/* UART operations */ \
static const struct SerialHardwareVT UART ## port ## _VT = \
{ \
.init = uart ## port ## _init, \
.cleanup = uart ## port ## _cleanup, \
.setBaudrate = uart ## port ## _setbaudrate, \
.setParity = uart ## port ## _setparity, \
.txStart = uart ## port ## _txStart, \
.txSending = tx_sending, \
};
/* UART port instances */
UART_PORT(0)
UART_PORT(1)
UART_PORT(2)
static struct CM3Serial UARTDesc[SER_CNT] =
{
{
.hw = {
.table = &UART0_VT,
.txbuffer = uart0_txbuffer,
.rxbuffer = uart0_rxbuffer,
.txbuffer_size = sizeof(uart0_txbuffer),
.rxbuffer_size = sizeof(uart0_rxbuffer),
},
.sending = false,
.base = UART0_BASE,
.irq = INT_UART0,
},
{
.hw = {
.table = &UART1_VT,
.txbuffer = uart1_txbuffer,
.rxbuffer = uart1_rxbuffer,
.txbuffer_size = sizeof(uart1_txbuffer),
.rxbuffer_size = sizeof(uart1_rxbuffer),
},
.sending = false,
.base = UART1_BASE,
.irq = INT_UART1,
},
{
.hw = {
.table = &UART2_VT,
.txbuffer = uart2_txbuffer,
.rxbuffer = uart2_rxbuffer,
.txbuffer_size = sizeof(uart2_txbuffer),
.rxbuffer_size = sizeof(uart2_rxbuffer),
},
.sending = false,
.base = UART2_BASE,
.irq = INT_UART2,
},
};
struct SerialHardware *ser_hw_getdesc(int port)
{
ASSERT(port >= 0 && port < SER_CNT);
return &UARTDesc[port].hw;
}

View file

@ -0,0 +1,154 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief LM3S1968 UART interface driver.
*
* \author Andrea Righi <arighi@develer.com>
*/
#ifndef SER_LM3S_H
#define SER_LM3S_H
#include <cfg/cfg_debug.h>
#include <cpu/power.h> /* cpu_relax() */
#include <drv/clock_lm3s.h> /* lm3s_busyWait() */
#include <io/lm3s.h>
/* Serial hardware numbers */
enum
{
SER_UART0,
SER_UART1,
SER_UART2,
SER_CNT //< Number of serial ports
};
/* Software errors */
#define SERRF_RXFIFOOVERRUN BV(0) //< Rx FIFO buffer overrun
#define SERRF_RXTIMEOUT BV(1) //< Receive timeout
#define SERRF_TXTIMEOUT BV(2) //< Transmit timeout
/*
* Hardware errors.
*/
#define SERRF_RXSROVERRUN 0 //< Input overrun
#define SERRF_FRAMEERROR 0 //< Stop bit missing
#define SERRF_PARITYERROR 0 //< Parity error
#define SERRF_NOISEERROR 0 //< Noise error
/* Serial error/status flags */
typedef uint32_t serstatus_t;
INLINE void lm3s_uartDisable(uint32_t base)
{
/* Disable the hardware FIFO */
HWREG(base + UART_O_LCRH) &= ~UART_LCRH_FEN;
/* Disable the UART */
HWREG(base + UART_O_CTL) &=
~(UART_CTL_UARTEN | UART_CTL_TXE | UART_CTL_RXE);
lm3s_busyWait(512);
}
INLINE void lm3s_uartEnable(uint32_t base)
{
/* Enable the hardware FIFO */
HWREG(base + UART_O_LCRH) |= UART_LCRH_FEN;
/* Enable RX, TX, and the UART */
HWREG(base + UART_O_CTL) |=
UART_CTL_UARTEN | UART_CTL_TXE | UART_CTL_RXE;
lm3s_busyWait(512);
}
/* Clear the flags register */
INLINE void lm3s_uartClear(uint32_t base)
{
HWREG(base + UART_O_FR) = 0;
}
INLINE bool lm3s_uartTxDone(uint32_t base)
{
return HWREG(base + UART_O_FR) & UART_FR_TXFE ? true : false;
}
INLINE bool lm3s_uartTxReady(uint32_t base)
{
return HWREG(base + UART_O_FR) & UART_FR_TXFF ? false : true;
}
INLINE bool lm3s_uartRxReady(uint32_t base)
{
return HWREG(base + UART_O_FR) & UART_FR_RXFE ? false : true;
}
INLINE bool lm3s_uartReady(uint32_t base)
{
return HWREG(base + UART_O_FR) & UART_FR_BUSY ? false : true;
}
INLINE int lm3s_uartPutCharNonBlocking(uint32_t base, unsigned char c)
{
if (!lm3s_uartTxReady(base))
return EOF;
HWREG(base + UART_O_DR) = c;
return c;
}
INLINE int lm3s_uartPutChar(uint32_t base, unsigned char c)
{
while (!lm3s_uartTxReady(base))
cpu_relax();
HWREG(base + UART_O_DR) = c;
return c;
}
INLINE int lm3s_uartGetCharNonBlocking(uint32_t base)
{
if (!lm3s_uartRxReady(base))
return EOF;
return HWREG(base + UART_O_DR);
}
INLINE int lm3s_uartGetChar(uint32_t base)
{
while (!lm3s_uartRxReady(base))
cpu_relax();
return HWREG(base + UART_O_DR);
}
void lm3s_uartSetBaudRate(uint32_t base, unsigned long baud);
void lm3s_uartSetParity(uint32_t base, int parity);
void lm3s_uartInit(int port);
#endif /* SER_LM3S_H */

View file

@ -0,0 +1,996 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2003, 2004 Develer S.r.l. (http://www.develer.com/)
* Copyright 2000 Bernie Innocenti <bernie@codewiz.org>
*
* -->
*
* \brief ARM UART and SPI I/O driver
*
*
* \author Daniele Basile <asterix@develer.com>
* \author Stefano Fedrigo <aleph@develer.com>
*/
#include "hw/hw_ser.h" /* Required for bus macros overrides */
#include <hw/hw_cpufreq.h> /* CPU_FREQ */
#include "cfg/cfg_ser.h"
#include <cfg/debug.h>
//#include <io/arm.h>
#include <io/cm3.h>
#include <drv/irq_cm3.h>
#include <cpu/attr.h>
#include <drv/ser.h>
#include <drv/ser_p.h>
#include <struct/fifobuf.h>
#define SERIRQ_PRIORITY 4 ///< default priority for serial irqs.
/**
* \name Overridable serial bus hooks
*
* These can be redefined in hw.h to implement
* special bus policies such as half-duplex, 485, etc.
*
*
* \code
* TXBEGIN TXCHAR TXEND TXOFF
* | __________|__________ | |
* | | | | | | | | |
* v v v v v v v v v
* ______ __ __ __ __ __ __ ________________
* \/ \/ \/ \/ \/ \/ \/
* ______/\__/\__/\__/\__/\__/\__/
*
* \endcode
*
* \{
*/
#ifndef SER_UART0_BUS_TXINIT
/**
* Default TXINIT macro - invoked in uart0_init()
*
* - Disable GPIO on USART0 tx/rx pins
*/
#if CPU_ARM_AT91 && !CPU_ARM_SAM7S_LARGE && !CPU_ARM_SAM7X
#warning Check USART0 pins!
#endif
#define SER_UART0_BUS_TXINIT do { \
PIOA_PDR = BV(RXD0) | BV(TXD0); \
PIO_PERIPH_SEL(PIOA_BASE, BV(RXD0) | BV(TXD0), USART0_PERIPH); \
} while (0)
#endif
#ifndef SER_UART0_BUS_TXBEGIN
/**
* Invoked before starting a transmission
*/
#define SER_UART0_BUS_TXBEGIN
#endif
#ifndef SER_UART0_BUS_TXCHAR
/**
* Invoked to send one character.
*/
#define SER_UART0_BUS_TXCHAR(c) do { \
US0_THR = (c); \
} while (0)
#endif
#ifndef SER_UART0_BUS_TXEND
/**
* Invoked as soon as the txfifo becomes empty
*/
#define SER_UART0_BUS_TXEND
#endif
/* End USART0 macros */
#if USART_PORTS > 1
#ifndef SER_UART1_BUS_TXINIT
/**
* Default TXINIT macro - invoked in uart1_init()
*
* - Disable GPIO on USART1 tx/rx pins
*/
#if CPU_ARM_AT91 && !CPU_ARM_SAM7S_LARGE && !CPU_ARM_SAM7X
#warning Check USART1 pins!
#endif
#define SER_UART1_BUS_TXINIT do { \
PIOA_PDR = BV(RXD1) | BV(TXD1); \
PIO_PERIPH_SEL(PIOA_BASE, BV(RXD1) | BV(TXD1), USART1_PERIPH); \
} while (0)
#endif
#ifndef SER_UART1_BUS_TXBEGIN
/**
* Invoked before starting a transmission
*/
#define SER_UART1_BUS_TXBEGIN
#endif
#ifndef SER_UART1_BUS_TXCHAR
/**
* Invoked to send one character.
*/
#define SER_UART1_BUS_TXCHAR(c) do { \
US1_THR = (c); \
} while (0)
#endif
#ifndef SER_UART1_BUS_TXEND
/**
* Invoked as soon as the txfifo becomes empty
*/
#define SER_UART1_BUS_TXEND
#endif
#endif
/**
* \name Overridable SPI hooks
*
* These can be redefined in hw.h to implement
* special bus policies such as slave select pin handling, etc.
*
* \{
*/
#ifndef SER_SPI0_BUS_TXINIT
/**
* Default TXINIT macro - invoked in spi_init()
* The default is no action.
*/
#if CPU_CM3_SAM3
#define SER_SPI0_BUS_TXINIT do { \
/* Disable PIO on SPI pins */ \
PIOA_PDR = BV(SPI0_SPCK) | BV(SPI0_MOSI) | BV(SPI0_MISO); \
/* SPI is peripheral A on SAM3X,A,N,S,U */ \
PIO_PERIPH_SEL(PIOA_BASE, BV(SPI0_SPCK) | BV(SPI0_MOSI) | BV(SPI0_MISO), PIO_PERIPH_A); \
} while (0)
#else
#define SER_SPI0_BUS_TXINIT do { \
/* Disable PIO on SPI pins */ \
PIOA_PDR = BV(SPI0_SPCK) | BV(SPI0_MOSI) | BV(SPI0_MISO); \
} while (0)
#endif
#endif
#ifndef SER_SPI0_BUS_TXCLOSE
/**
* Invoked after the last character has been transmitted.
* The default is no action.
*/
#define SER_SPI0_BUS_TXCLOSE do { \
/* Enable PIO on SPI pins */ \
PIOA_PER = BV(SPI0_SPCK) | BV(SPI0_MOSI) | BV(SPI0_MISO); \
} while (0)
#endif
#if CPU_ARM_SAM7X
#ifndef SER_SPI1_BUS_TXINIT
/**
* Default TXINIT macro - invoked in spi_init()
* The default is no action.
*/
#define SER_SPI1_BUS_TXINIT do { \
/* Disable PIO on SPI pins */ \
PIOA_PDR = BV(SPI1_SPCK) | BV(SPI1_MOSI) | BV(SPI1_MISO); \
/* SPI1 pins are on B peripheral function! */ \
PIOA_BSR = BV(SPI1_SPCK) | BV(SPI1_MOSI) | BV(SPI1_MISO); \
} while (0)
#endif
#ifndef SER_SPI1_BUS_TXCLOSE
/**
* Invoked after the last character has been transmitted.
* The default is no action.
*/
#define SER_SPI1_BUS_TXCLOSE do { \
/* Enable PIO on SPI pins */ \
PIOA_PER = BV(SPI1_SPCK) | BV(SPI1_MOSI) | BV(SPI1_MISO); \
} while (0)
#endif
#endif
/*\}*/
/**
* \name Core dependent interrupt handling macros
*
* Atmel serial hardware is used on different CPU cores,
* i.e. SAM3 and SAM7. The user interface of the serial
* subsystem is identical but core interrupt controllers
* are different.
*
* \{
*/
#if CPU_ARM_AT91
INLINE void sysirq_setHandler(sysirq_t irq, sysirq_handler_t handler)
{
/* Set the vector. */
AIC_SVR(irq) = uart0_irq_dispatcher;
/* Initialize to level/edge sensitive with defined priority. */
#if CPU_ARM_SAM7X
if (irq == SPI0_ID || irq == SPI1_ID)
#else
if (irq == SPI0_ID)
#endif
AIC_SMR(irq) = (AIC_SMR(irq) & ~AIC_SRCTYPE_MASK) | AIC_SRCTYPE_INT_EDGE_TRIGGERED;
else // USART/UART
AIC_SMR(irq) = (AIC_SMR(irq) & ~AIC_SRCTYPE_MASK) | AIC_SRCTYPE_INT_LEVEL_SENSITIVE;
/* Enable IRQ */
AIC_IECR = BV(irq);
}
INLINE void sysirq_setPriority(sysirq_t irq, int prio)
{
AIC_SMR(irq) = (AIC_SMR(irq) & ~AIC_PRIOR_MASK) | SERIRQ_PRIORITY;
}
/** Inform hw that we have served the IRQ */
#define SER_INT_ACK do { \
AIC_EOICR = 0; \
} while (0)
#elif CPU_CM3_SAM3
/** Inform hw that we have served the IRQ */
#define SER_INT_ACK do { /* nop */ } while (0)
#else
#error No interrupt handling macros defined for current architecture
#endif
/*\}*/
/* From the high-level serial driver */
extern struct Serial *ser_handles[SER_CNT];
/* TX and RX buffers */
static unsigned char uart0_txbuffer[CONFIG_UART0_TXBUFSIZE];
static unsigned char uart0_rxbuffer[CONFIG_UART0_RXBUFSIZE];
#if USART_PORTS > 1
static unsigned char uart1_txbuffer[CONFIG_UART1_TXBUFSIZE];
static unsigned char uart1_rxbuffer[CONFIG_UART1_RXBUFSIZE];
#endif
static unsigned char spi0_txbuffer[CONFIG_SPI0_TXBUFSIZE];
static unsigned char spi0_rxbuffer[CONFIG_SPI0_RXBUFSIZE];
#if CPU_ARM_SAM7X
static unsigned char spi1_txbuffer[CONFIG_SPI1_TXBUFSIZE];
static unsigned char spi1_rxbuffer[CONFIG_SPI1_RXBUFSIZE];
#endif
/**
* Internal hardware state structure
*
* The \a sending variable is true while the transmission
* interrupt is retriggering itself.
*
* For the USARTs the \a sending flag is useful for taking specific
* actions before sending a burst of data, at the start of a trasmission
* but not before every char sent.
*
* For the SPI, this flag is necessary because the SPI sends and receives
* bytes at the same time and the SPI IRQ is unique for send/receive.
* The only way to start transmission is to write data in SPDR (this
* is done by spi_starttx()). We do this *only* if a transfer is
* not already started.
*/
struct ArmSerial
{
struct SerialHardware hw;
volatile bool sending;
};
static ISR_PROTO(uart0_irq_dispatcher);
#if USART_PORTS > 1
static ISR_PROTO(uart1_irq_dispatcher);
#endif
static ISR_PROTO(spi0_irq_handler);
#if CPU_ARM_SAM7X
static ISR_PROTO(spi1_irq_handler);
#endif
/*
* Callbacks for USART0
*/
static void uart0_init(
UNUSED_ARG(struct SerialHardware *, _hw),
UNUSED_ARG(struct Serial *, ser))
{
US0_IDR = 0xFFFFFFFF;
pmc_periphEnable(US0_ID);
/*
* - Reset USART0
* - Set serial param: mode Normal, 8bit data, 1bit stop, parity none
* - Enable both the receiver and the transmitter
* - Enable only the RX complete interrupt
*/
US0_CR = BV(US_RSTRX) | BV(US_RSTTX);
US0_MR = US_CHMODE_NORMAL | US_CHRL_8 | US_NBSTOP_1 | US_PAR_NO;
US0_CR = BV(US_RXEN) | BV(US_TXEN);
US0_IER = BV(US_RXRDY);
SER_UART0_BUS_TXINIT;
sysirq_setPriority(INT_US0, SERIRQ_PRIORITY);
sysirq_setHandler(INT_US0, uart0_irq_dispatcher);
SER_STROBE_INIT;
}
static void uart0_cleanup(UNUSED_ARG(struct SerialHardware *, _hw))
{
US0_CR = BV(US_RSTRX) | BV(US_RSTTX) | BV(US_RXDIS) | BV(US_TXDIS) | BV(US_RSTSTA);
}
static void uart0_enabletxirq(struct SerialHardware *_hw)
{
struct ArmSerial *hw = (struct ArmSerial *)_hw;
/*
* WARNING: racy code here! The tx interrupt sets hw->sending to false
* when it runs with an empty fifo. The order of statements in the
* if-block matters.
*/
if (!hw->sending)
{
hw->sending = true;
/*
* - Enable the transmitter
* - Enable TX empty interrupt
*/
SER_UART0_BUS_TXBEGIN;
US0_IER = BV(US_TXEMPTY);
}
}
static void uart0_setbaudrate(UNUSED_ARG(struct SerialHardware *, _hw), unsigned long rate)
{
/* Compute baud-rate period */
US0_BRGR = CPU_FREQ / (16 * rate);
//DB(kprintf("uart0_setbaudrate(rate=%lu): period=%d\n", rate, period);)
}
static void uart0_setparity(UNUSED_ARG(struct SerialHardware *, _hw), int parity)
{
US0_MR &= ~US_PAR_MASK;
/* Set UART parity */
switch(parity)
{
case SER_PARITY_NONE:
{
/* Parity none. */
US0_MR |= US_PAR_NO;
break;
}
case SER_PARITY_EVEN:
{
/* Even parity. */
US0_MR |= US_PAR_EVEN;
break;
}
case SER_PARITY_ODD:
{
/* Odd parity. */
US0_MR |= US_PAR_ODD;
break;
}
default:
ASSERT(0);
}
}
#if USART_PORTS > 1
/*
* Callbacks for USART1
*/
static void uart1_init(
UNUSED_ARG(struct SerialHardware *, _hw),
UNUSED_ARG(struct Serial *, ser))
{
US1_IDR = 0xFFFFFFFF;
pmc_periphEnable(US1_ID);
/*
* - Reset USART1
* - Set serial param: mode Normal, 8bit data, 1bit stop, parity none
* - Enable both the receiver and the transmitter
* - Enable only the RX complete interrupt
*/
US1_CR = BV(US_RSTRX) | BV(US_RSTTX);
US1_MR = US_CHMODE_NORMAL | US_CHRL_8 | US_NBSTOP_1 | US_PAR_NO;
US1_CR = BV(US_RXEN) | BV(US_TXEN);
US1_IER = BV(US_RXRDY);
SER_UART1_BUS_TXINIT;
sysirq_setPriority(INT_US1, SERIRQ_PRIORITY);
sysirq_setHandler(INT_US1, uart1_irq_dispatcher);
SER_STROBE_INIT;
}
static void uart1_cleanup(UNUSED_ARG(struct SerialHardware *, _hw))
{
US1_CR = BV(US_RSTRX) | BV(US_RSTTX) | BV(US_RXDIS) | BV(US_TXDIS) | BV(US_RSTSTA);
}
static void uart1_enabletxirq(struct SerialHardware *_hw)
{
struct ArmSerial *hw = (struct ArmSerial *)_hw;
/*
* WARNING: racy code here! The tx interrupt sets hw->sending to false
* when it runs with an empty fifo. The order of statements in the
* if-block matters.
*/
if (!hw->sending)
{
hw->sending = true;
/*
* - Enable the transmitter
* - Enable TX empty interrupt
*/
SER_UART1_BUS_TXBEGIN;
US1_IER = BV(US_TXEMPTY);
}
}
static void uart1_setbaudrate(UNUSED_ARG(struct SerialHardware *, _hw), unsigned long rate)
{
/* Compute baud-rate period */
US1_BRGR = CPU_FREQ / (16 * rate);
//DB(kprintf("uart0_setbaudrate(rate=%lu): period=%d\n", rate, period);)
}
static void uart1_setparity(UNUSED_ARG(struct SerialHardware *, _hw), int parity)
{
US1_MR &= ~US_PAR_MASK;
/* Set UART parity */
switch(parity)
{
case SER_PARITY_NONE:
{
/* Parity none. */
US1_MR |= US_PAR_NO;
break;
}
case SER_PARITY_EVEN:
{
/* Even parity. */
US1_MR |= US_PAR_EVEN;
break;
}
case SER_PARITY_ODD:
{
/* Odd parity. */
US1_MR |= US_PAR_ODD;
break;
}
default:
ASSERT(0);
}
}
#endif /* USART_PORTS > 1 */
/* SPI driver */
static void spi0_init(UNUSED_ARG(struct SerialHardware *, _hw), UNUSED_ARG(struct Serial *, ser))
{
SER_SPI0_BUS_TXINIT;
/* Reset device */
SPI0_CR = BV(SPI_SWRST);
/*
* Set SPI to master mode, fixed peripheral select, chip select directly connected to a peripheral device,
* SPI clock set to MCK, mode fault detection disabled, loopback disable, NPCS0 active, Delay between CS = 0
*/
SPI0_MR = BV(SPI_MSTR) | BV(SPI_MODFDIS);
/*
* Set SPI mode.
* At reset clock division factor is set to 0, that is
* *forbidden*. Set SPI clock to minimum to keep it valid.
* Set all possible chip select registers in case user manually
* change CPS field in SPI_MR.
*/
SPI0_CSR0 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT);
SPI0_CSR1 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT);
SPI0_CSR2 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT);
SPI0_CSR3 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT);
/* Disable all irqs */
SPI0_IDR = 0xFFFFFFFF;
//sysirq_setPriority(INT_SPI0, SERIRQ_PRIORITY);
sysirq_setHandler(INT_SPI0, spi0_irq_handler);
pmc_periphEnable(SPI0_ID);
/* Enable SPI */
SPI0_CR = BV(SPI_SPIEN);
SER_STROBE_INIT;
}
static void spi0_cleanup(UNUSED_ARG(struct SerialHardware *, _hw))
{
/* Disable SPI */
SPI0_CR = BV(SPI_SPIDIS);
/* Disable all irqs */
SPI0_IDR = 0xFFFFFFFF;
SER_SPI0_BUS_TXCLOSE;
}
static void spi0_starttx(struct SerialHardware *_hw)
{
struct ArmSerial *hw = (struct ArmSerial *)_hw;
cpu_flags_t flags;
IRQ_SAVE_DISABLE(flags);
/* Send data only if the SPI is not already transmitting */
if (!hw->sending && !fifo_isempty(&ser_handles[SER_SPI0]->txfifo))
{
hw->sending = true;
SPI0_TDR = fifo_pop(&ser_handles[SER_SPI0]->txfifo);
/* Enable interrupt on tx buffer empty */
SPI0_IER = BV(SPI_TXEMPTY);
}
IRQ_RESTORE(flags);
}
static void spi0_setbaudrate(UNUSED_ARG(struct SerialHardware *, _hw), unsigned long rate)
{
SPI0_CSR0 &= ~SPI_SCBR;
ASSERT((uint8_t)DIV_ROUND(CPU_FREQ, rate));
SPI0_CSR0 |= DIV_ROUND(CPU_FREQ, rate) << SPI_SCBR_SHIFT;
}
#if CPU_ARM_SAM7X
/* SPI driver */
static void spi1_init(UNUSED_ARG(struct SerialHardware *, _hw), UNUSED_ARG(struct Serial *, ser))
{
SER_SPI1_BUS_TXINIT;
/* Reset device */
SPI1_CR = BV(SPI_SWRST);
/*
* Set SPI to master mode, fixed peripheral select, chip select directly connected to a peripheral device,
* SPI clock set to MCK, mode fault detection disabled, loopback disable, NPCS0 active, Delay between CS = 0
*/
SPI1_MR = BV(SPI_MSTR) | BV(SPI_MODFDIS);
/*
* Set SPI mode.
* At reset clock division factor is set to 0, that is
* *forbidden*. Set SPI clock to minimum to keep it valid.
* Set all possible chip select registers in case user manually
* change chip select.
*/
SPI1_CSR0 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT);
SPI1_CSR1 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT);
SPI1_CSR2 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT);
SPI1_CSR3 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT);
/* Disable all SPI irqs */
SPI1_IDR = 0xFFFFFFFF;
sysirq_setPriority(INT_SPI1, SERIRQ_PRIORITY);
sysirq_setHandler(INT_SPI1, spi1_irq_dispatcher);
pmc_periphEnable(SPI1_ID);
/* Enable SPI */
SPI1_CR = BV(SPI_SPIEN);
SER_STROBE_INIT;
}
static void spi1_cleanup(UNUSED_ARG(struct SerialHardware *, _hw))
{
/* Disable SPI */
SPI1_CR = BV(SPI_SPIDIS);
/* Disable all irqs */
SPI1_IDR = 0xFFFFFFFF;
SER_SPI1_BUS_TXCLOSE;
}
static void spi1_starttx(struct SerialHardware *_hw)
{
struct ArmSerial *hw = (struct ArmSerial *)_hw;
cpu_flags_t flags;
IRQ_SAVE_DISABLE(flags);
/* Send data only if the SPI is not already transmitting */
if (!hw->sending && !fifo_isempty(&ser_handles[SER_SPI1]->txfifo))
{
hw->sending = true;
SPI1_TDR = fifo_pop(&ser_handles[SER_SPI1]->txfifo);
/* Enable interrupt on tx buffer empty */
SPI1_IER = BV(SPI_TXEMPTY);
}
IRQ_RESTORE(flags);
}
static void spi1_setbaudrate(UNUSED_ARG(struct SerialHardware *, _hw), unsigned long rate)
{
SPI1_CSR0 &= ~SPI_SCBR;
ASSERT((uint8_t)DIV_ROUND(CPU_FREQ, rate));
SPI1_CSR0 |= DIV_ROUND(CPU_FREQ, rate) << SPI_SCBR_SHIFT;
}
#endif
static void spi_setparity(UNUSED_ARG(struct SerialHardware *, _hw), UNUSED_ARG(int, parity))
{
// nop
}
static bool tx_sending(struct SerialHardware* _hw)
{
struct ArmSerial *hw = (struct ArmSerial *)_hw;
return hw->sending;
}
// FIXME: move into compiler.h? Ditch?
#if COMPILER_C99
#define C99INIT(name,val) .name = val
#elif defined(__GNUC__)
#define C99INIT(name,val) name: val
#else
#warning No designated initializers, double check your code
#define C99INIT(name,val) (val)
#endif
/*
* High-level interface data structures
*/
static const struct SerialHardwareVT UART0_VT =
{
C99INIT(init, uart0_init),
C99INIT(cleanup, uart0_cleanup),
C99INIT(setBaudrate, uart0_setbaudrate),
C99INIT(setParity, uart0_setparity),
C99INIT(txStart, uart0_enabletxirq),
C99INIT(txSending, tx_sending),
};
#if USART_PORTS > 1
static const struct SerialHardwareVT UART1_VT =
{
C99INIT(init, uart1_init),
C99INIT(cleanup, uart1_cleanup),
C99INIT(setBaudrate, uart1_setbaudrate),
C99INIT(setParity, uart1_setparity),
C99INIT(txStart, uart1_enabletxirq),
C99INIT(txSending, tx_sending),
};
#endif /* USART_PORTS > 1 */
static const struct SerialHardwareVT SPI0_VT =
{
C99INIT(init, spi0_init),
C99INIT(cleanup, spi0_cleanup),
C99INIT(setBaudrate, spi0_setbaudrate),
C99INIT(setParity, spi_setparity),
C99INIT(txStart, spi0_starttx),
C99INIT(txSending, tx_sending),
};
#if CPU_ARM_SAM7X
static const struct SerialHardwareVT SPI1_VT =
{
C99INIT(init, spi1_init),
C99INIT(cleanup, spi1_cleanup),
C99INIT(setBaudrate, spi1_setbaudrate),
C99INIT(setParity, spi_setparity),
C99INIT(txStart, spi1_starttx),
C99INIT(txSending, tx_sending),
};
#endif
static struct ArmSerial UARTDescs[SER_CNT] =
{
{
C99INIT(hw, /**/) {
C99INIT(table, &UART0_VT),
C99INIT(txbuffer, uart0_txbuffer),
C99INIT(rxbuffer, uart0_rxbuffer),
C99INIT(txbuffer_size, sizeof(uart0_txbuffer)),
C99INIT(rxbuffer_size, sizeof(uart0_rxbuffer)),
},
C99INIT(sending, false),
},
#if USART_PORTS > 1
{
C99INIT(hw, /**/) {
C99INIT(table, &UART1_VT),
C99INIT(txbuffer, uart1_txbuffer),
C99INIT(rxbuffer, uart1_rxbuffer),
C99INIT(txbuffer_size, sizeof(uart1_txbuffer)),
C99INIT(rxbuffer_size, sizeof(uart1_rxbuffer)),
},
C99INIT(sending, false),
},
#endif
{
C99INIT(hw, /**/) {
C99INIT(table, &SPI0_VT),
C99INIT(txbuffer, spi0_txbuffer),
C99INIT(rxbuffer, spi0_rxbuffer),
C99INIT(txbuffer_size, sizeof(spi0_txbuffer)),
C99INIT(rxbuffer_size, sizeof(spi0_rxbuffer)),
},
C99INIT(sending, false),
},
#if CPU_ARM_SAM7X
{
C99INIT(hw, /**/) {
C99INIT(table, &SPI1_VT),
C99INIT(txbuffer, spi1_txbuffer),
C99INIT(rxbuffer, spi1_rxbuffer),
C99INIT(txbuffer_size, sizeof(spi1_txbuffer)),
C99INIT(rxbuffer_size, sizeof(spi1_rxbuffer)),
},
C99INIT(sending, false),
}
#endif
};
struct SerialHardware *ser_hw_getdesc(int unit)
{
ASSERT(unit < SER_CNT);
return &UARTDescs[unit].hw;
}
/**
* Serial 0 TX interrupt handler
*/
INLINE void uart0_irq_tx(void)
{
SER_STROBE_ON;
struct FIFOBuffer * const txfifo = &ser_handles[SER_UART0]->txfifo;
if (fifo_isempty(txfifo))
{
/*
* - Disable the TX empty interrupts
*/
US0_IDR = BV(US_TXEMPTY);
SER_UART0_BUS_TXEND;
UARTDescs[SER_UART0].sending = false;
}
else
{
char c = fifo_pop(txfifo);
SER_UART0_BUS_TXCHAR(c);
}
SER_STROBE_OFF;
}
/**
* Serial 0 RX complete interrupt handler.
*/
INLINE void uart0_irq_rx(void)
{
SER_STROBE_ON;
/* Should be read before US_CRS */
ser_handles[SER_UART0]->status |= US0_CSR & (SERRF_RXSROVERRUN | SERRF_FRAMEERROR);
US0_CR = BV(US_RSTSTA);
char c = US0_RHR;
struct FIFOBuffer * const rxfifo = &ser_handles[SER_UART0]->rxfifo;
if (fifo_isfull(rxfifo))
ser_handles[SER_UART0]->status |= SERRF_RXFIFOOVERRUN;
else
fifo_push(rxfifo, c);
SER_STROBE_OFF;
}
/**
* Serial IRQ dispatcher for USART0.
*/
static DECLARE_ISR(uart0_irq_dispatcher)
{
if (US0_CSR & BV(US_RXRDY))
uart0_irq_rx();
if (US0_CSR & BV(US_TXEMPTY))
uart0_irq_tx();
SER_INT_ACK;
}
#if USART_PORTS > 1
/**
* Serial 1 TX interrupt handler
*/
INLINE void uart1_irq_tx(void)
{
SER_STROBE_ON;
struct FIFOBuffer * const txfifo = &ser_handles[SER_UART1]->txfifo;
if (fifo_isempty(txfifo))
{
/*
* - Disable the TX empty interrupts
*/
US1_IDR = BV(US_TXEMPTY);
SER_UART1_BUS_TXEND;
UARTDescs[SER_UART1].sending = false;
}
else
{
char c = fifo_pop(txfifo);
SER_UART1_BUS_TXCHAR(c);
}
SER_STROBE_OFF;
}
/**
* Serial 1 RX complete interrupt handler.
*/
INLINE void uart1_irq_rx(void)
{
SER_STROBE_ON;
/* Should be read before US_CRS */
ser_handles[SER_UART1]->status |= US1_CSR & (SERRF_RXSROVERRUN | SERRF_FRAMEERROR);
US1_CR = BV(US_RSTSTA);
char c = US1_RHR;
struct FIFOBuffer * const rxfifo = &ser_handles[SER_UART1]->rxfifo;
if (fifo_isfull(rxfifo))
ser_handles[SER_UART1]->status |= SERRF_RXFIFOOVERRUN;
else
fifo_push(rxfifo, c);
SER_STROBE_OFF;
}
/**
* Serial IRQ dispatcher for USART1.
*/
static DECLARE_ISR(uart1_irq_dispatcher)
{
if (US1_CSR & BV(US_RXRDY))
uart1_irq_rx();
if (US1_CSR & BV(US_TXEMPTY))
uart1_irq_tx();
SER_INT_ACK;
}
#endif /* USART_PORTS > 1 */
/**
* SPI0 interrupt handler
*/
static DECLARE_ISR(spi0_irq_handler)
{
SER_STROBE_ON;
char c = SPI0_RDR;
/* Read incoming byte. */
if (!fifo_isfull(&ser_handles[SER_SPI0]->rxfifo))
fifo_push(&ser_handles[SER_SPI0]->rxfifo, c);
/*
* FIXME
else
ser_handles[SER_SPI0]->status |= SERRF_RXFIFOOVERRUN;
*/
/* Send */
if (!fifo_isempty(&ser_handles[SER_SPI0]->txfifo))
SPI0_TDR = fifo_pop(&ser_handles[SER_SPI0]->txfifo);
else
{
UARTDescs[SER_SPI0].sending = false;
/* Disable interrupt on tx buffer empty */
SPI0_IDR = BV(SPI_TXEMPTY);
}
SER_INT_ACK;
SER_STROBE_OFF;
}
#if CPU_ARM_SAM7X
/**
* SPI1 interrupt handler
*/
static DECLARE_ISR(spi1_irq_handler)
{
SER_STROBE_ON;
char c = SPI1_RDR;
/* Read incoming byte. */
if (!fifo_isfull(&ser_handles[SER_SPI1]->rxfifo))
fifo_push(&ser_handles[SER_SPI1]->rxfifo, c);
/*
* FIXME
else
ser_handles[SER_SPI1]->status |= SERRF_RXFIFOOVERRUN;
*/
/* Send */
if (!fifo_isempty(&ser_handles[SER_SPI1]->txfifo))
SPI1_TDR = fifo_pop(&ser_handles[SER_SPI1]->txfifo);
else
{
UARTDescs[SER_SPI1].sending = false;
/* Disable interrupt on tx buffer empty */
SPI1_IDR = BV(SPI_TXEMPTY);
}
SER_INT_ACK;
SER_STROBE_OFF;
}
#endif

View file

@ -0,0 +1,81 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2003,2004 Develer S.r.l. (http://www.develer.com/)
* Copyright 2000 Bernie Innocenti <bernie@codewiz.org>
*
* -->
*
* \brief High level serial I/O API
*
* \author Daniele Basile <asterix@develer.com>
*/
#ifndef SER_SAM3_H
#define SER_SAM3_H
#include <cfg/macros.h> /* BV() */
#include <cfg/compiler.h> /* uint32_t */
#include <cpu/detect.h> /* CPU_* */
/** \name Serial Error/status flags. */
/*\{*/
typedef uint32_t serstatus_t;
/* Software errors */
#define SERRF_RXFIFOOVERRUN BV(0) /**< Rx FIFO buffer overrun */
#define SERRF_RXTIMEOUT BV(1) /**< Receive timeout */
#define SERRF_TXTIMEOUT BV(2) /**< Transmit timeout */
/*
* Hardware errors.
* These flags map directly to the ARM USART Channel Status Register (US_CSR).
*/
#define SERRF_RXSROVERRUN BV(5) /**< Rx shift register overrun */
#define SERRF_FRAMEERROR BV(6) /**< Stop bit missing */
#define SERRF_PARITYERROR BV(7) /**< Parity error */
#define SERRF_NOISEERROR 0 /**< Unsupported */
/*\}*/
/**
* \name Serial hw numbers
*
* \{
*/
enum
{
SER_UART0,
#if !CPU_CM3_SAM3U
SER_UART1,
#endif
SER_SPI0,
SER_CNT /**< Number of serial ports */
};
/*\}*/
#endif /* SER_SAM3_H */

View file

@ -0,0 +1,392 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief STM32 UART interface driver.
*
* \author Daniele Basile <asterix@develer.com>
*/
#include "ser_stm32.h"
#include "cfg/cfg_ser.h"
#include <cfg/macros.h> /* for BV() */
#include <cfg/debug.h>
#include <drv/gpio_stm32.h>
#include <drv/irq_cm3.h>
#include <drv/clock_stm32.h>
#include <drv/ser_p.h>
#include <drv/ser.h>
/* From the high-level serial driver */
extern struct Serial *ser_handles[SER_CNT];
struct CM3Serial
{
struct SerialHardware hw;
volatile bool sending;
uint32_t base;
sysirq_t irq;
};
/* Forward declaration */
static struct CM3Serial UARTDesc[SER_CNT];
/* GPIO descriptor for UART pins */
struct gpio_uart_info
{
/* GPIO base address register */
uint32_t base;
/* Pin(s) bitmask */
uint32_t rx_pin;
uint32_t tx_pin;
/* Sysctl */
uint32_t sysctl_gpio;
uint32_t sysctl_usart;
};
/* Table to retrieve GPIO pins configuration to work as UART pins */
static const struct gpio_uart_info gpio_uart[SER_CNT] =
{
/* UART1 */
{
.base = GPIOA_BASE,
.rx_pin = GPIO_USART1_RX_PIN,
.tx_pin = GPIO_USART1_TX_PIN,
.sysctl_gpio = RCC_APB2_GPIOA,
.sysctl_usart = RCC_APB2_USART1,
},
/* UART2 */
{
.base = GPIOA_BASE,
.rx_pin = GPIO_USART2_RX_PIN,
.tx_pin = GPIO_USART2_TX_PIN,
.sysctl_gpio = RCC_APB2_GPIOA,
.sysctl_usart = RCC_APB1_USART2,
},
#if CPU_CM3_STM32F103RB || CPU_CM3_STM32F103RE
/* UART3 */
{
.base = GPIOB_BASE,
.rx_pin = GPIO_USART3_RX_PIN,
.tx_pin = GPIO_USART3_TX_PIN,
.sysctl_gpio = RCC_APB2_GPIOB,
.sysctl_usart = RCC_APB1_USART3,
},
#endif
};
#define USART1_PORT 0
#define USART2_PORT 1
#define USART3_PORT 2
void stm32_uartSetBaudRate(uint32_t base, unsigned long baud)
{
struct stm32_usart *_base = (struct stm32_usart *)base;
_base->BRR = evaluate_brr(_base, CPU_FREQ, baud);
}
void stm32_uartSetParity(uint32_t base, int parity)
{
struct stm32_usart *_base = (struct stm32_usart *)base;
/* USART_WORD_LEN_8B */
_base->CR1 &= ~BV(CR1_M);
switch(parity)
{
case SER_PARITY_NONE:
_base->CR1 &= ~BV(CR1_PCE);
break;
case SER_PARITY_ODD:
_base->CR1 |= (BV(CR1_PCE) | BV(CR1_PS));
break;
case SER_PARITY_EVEN:
_base->CR1 |= BV(CR1_PCE);
_base->CR1 &= ~BV(CR1_PS);
break;
default:
ASSERT(0);
return;
}
}
void stm32_uartInit(int port)
{
struct stm32_usart *base = (struct stm32_usart *)UARTDesc[port].base;
ASSERT(port >= 0 && port < SER_CNT);
/* Enable clocking on AFIO */
RCC->APB2ENR |= RCC_APB2_AFIO;
RCC->APB2ENR |= gpio_uart[port].sysctl_gpio;
/* Configure USART pins */
if (port == USART1_PORT)
{
RCC->APB2ENR |= gpio_uart[port].sysctl_usart;
}
else
{
RCC->APB1ENR |= gpio_uart[port].sysctl_usart;
}
stm32_gpioPinConfig((struct stm32_gpio *)gpio_uart[port].base, gpio_uart[port].tx_pin,
GPIO_MODE_AF_PP, GPIO_SPEED_50MHZ);
stm32_gpioPinConfig((struct stm32_gpio *)gpio_uart[port].base, gpio_uart[port].rx_pin,
GPIO_MODE_IN_FLOATING, GPIO_SPEED_50MHZ);
/* Clear control registry */
base->CR2 = 0;
base->CR1 = 0;
base->CR3 = 0;
base->SR = 0;
/* Set serial param: 115.200 bps, no parity */
stm32_uartSetBaudRate(UARTDesc[port].base, 115200);
stm32_uartSetParity(UARTDesc[port].base, SER_PARITY_NONE);
/* Enable trasmision and receiver */
base->CR1 |= (BV(CR1_TE) | BV(CR1_RE));
}
static bool tx_sending(struct SerialHardware *_hw)
{
struct CM3Serial *hw = (struct CM3Serial *)_hw;
return hw->sending;
}
static void uart_irq_rx(int port)
{
struct FIFOBuffer *rxfifo = &ser_handles[port]->rxfifo;
struct stm32_usart *base = (struct stm32_usart *)UARTDesc[port].base;
char c;
while (stm32_uartRxReady(UARTDesc[port].base))
{
c = base->DR;
if (fifo_isfull(rxfifo))
ser_handles[port]->status |= SERRF_RXFIFOOVERRUN;
else
fifo_push(rxfifo, c);
}
}
static void uart_irq_tx(int port)
{
struct FIFOBuffer *txfifo = &ser_handles[port]->txfifo;
struct stm32_usart *base = (struct stm32_usart *)UARTDesc[port].base;
if (fifo_isempty(txfifo))
{
/*
* Disable TX empty interrupts if there're no more
* characters to transmit.
*/
base->CR1 &= ~BV(CR1_TXEIE);
UARTDesc[port].sending = false;
}
else
{
base->DR = fifo_pop(txfifo);
}
}
static void uart_common_irq_handler(int port)
{
struct stm32_usart *base = (struct stm32_usart *)UARTDesc[port].base;
uint32_t status;
/* Read and clear the IRQ status */
status = base->SR;
/* Check hw errors */
ser_handles[port]->status = status &
(BV(SR_ORE) | BV(SR_FE) | BV(SR_PE) | BV(SR_NE));
/* Process the IRQ */
if (status & BV(CR1_RXNEIE))
{
uart_irq_rx(port);
}
if (status & (BV(CR1_TXEIE) | BV(CR1_TCIE)))
{
uart_irq_tx(port);
}
}
static void stm32_uartIRQEnable(int port, sysirq_handler_t handler)
{
struct stm32_usart *base = (struct stm32_usart *)UARTDesc[port].base;
/* Register the IRQ handler */
sysirq_setHandler(UARTDesc[port].irq, handler);
base->CR1 |= BV(CR1_RXNEIE);
}
static void stm32_uartIRQDisable(int port)
{
struct stm32_usart *base = (struct stm32_usart *)UARTDesc[port].base;
base->CR1 &= ~(BV(CR1_RXNEIE) | USART_FLAG_TXE);
}
/* UART class definition */
#define UART_PORT(port) \
/* UART TX and RX buffers */ \
static unsigned char uart ## port ## _txbuffer[CONFIG_UART ## port ## _TXBUFSIZE]; \
static unsigned char uart ## port ## _rxbuffer[CONFIG_UART ## port ## _RXBUFSIZE]; \
\
/* UART interrupt handler */ \
static DECLARE_ISR(uart ## port ## _irq_handler) \
{ \
uart_common_irq_handler(USART ## port ## _PORT); \
} \
\
/* UART public methods */ \
static void uart ## port ## _txStart(struct SerialHardware *_hw) \
{ \
struct FIFOBuffer *txfifo = &ser_handles[USART ## port ## _PORT]->txfifo; \
struct CM3Serial *hw = (struct CM3Serial *)_hw; \
struct stm32_usart *base = (struct stm32_usart *)USART## port ## _BASE; \
if (hw->sending) \
return; \
stm32_uartPutChar(USART ## port ## _BASE, fifo_pop(txfifo)); \
if (!fifo_isempty(txfifo)) \
{ \
hw->sending = true; \
base->CR1 |= BV(CR1_TXEIE); \
} \
} \
\
static void uart ## port ## _setbaudrate(UNUSED_ARG(struct SerialHardware *, hw), \
unsigned long baud) \
{ \
stm32_uartSetBaudRate(USART## port ## _BASE, baud); \
} \
\
static void uart ## port ## _setparity(UNUSED_ARG(struct SerialHardware *, hw), \
int parity) \
{ \
stm32_uartSetParity(USART## port ## _BASE, parity); \
} \
\
static void uart ## port ## _cleanup(struct SerialHardware *_hw) \
{ \
struct CM3Serial *hw = (struct CM3Serial *)_hw; \
hw->sending = false; \
stm32_uartIRQDisable(USART ## port ## _PORT); \
stm32_uartClear(USART## port ## _BASE); \
stm32_uartDisable(USART## port ## _BASE); \
} \
\
static void uart ## port ## _init(UNUSED_ARG(struct SerialHardware *, hw), \
UNUSED_ARG(struct Serial *, ser)) \
{ \
stm32_uartInit(USART ## port ## _PORT); \
stm32_uartEnable(USART## port ## _BASE); \
stm32_uartIRQEnable(USART ## port ## _PORT, uart ## port ## _irq_handler); \
} \
\
/* UART operations */ \
static const struct SerialHardwareVT USART ## port ## _VT = \
{ \
.init = uart ## port ## _init, \
.cleanup = uart ## port ## _cleanup, \
.setBaudrate = uart ## port ## _setbaudrate, \
.setParity = uart ## port ## _setparity, \
.txStart = uart ## port ## _txStart, \
.txSending = tx_sending, \
};
/* UART port instances */
UART_PORT(1)
UART_PORT(2)
#if CPU_CM3_STM32F103RB || CPU_CM3_STM32F103RE
UART_PORT(3)
#endif
static struct CM3Serial UARTDesc[SER_CNT] =
{
{
.hw = {
.table = &USART1_VT,
.txbuffer = uart1_txbuffer,
.rxbuffer = uart1_rxbuffer,
.txbuffer_size = sizeof(uart1_txbuffer),
.rxbuffer_size = sizeof(uart1_rxbuffer),
},
.sending = false,
.base = USART1_BASE,
.irq = USART1_IRQHANDLER,
},
{
.hw = {
.table = &USART2_VT,
.txbuffer = uart2_txbuffer,
.rxbuffer = uart2_rxbuffer,
.txbuffer_size = sizeof(uart2_txbuffer),
.rxbuffer_size = sizeof(uart2_rxbuffer),
},
.sending = false,
.base = USART2_BASE,
.irq = USART2_IRQHANDLER,
},
#if CPU_CM3_STM32F103RB || CPU_CM3_STM32F103RE
{
.hw = {
.table = &USART3_VT,
.txbuffer = uart3_txbuffer,
.rxbuffer = uart3_rxbuffer,
.txbuffer_size = sizeof(uart3_txbuffer),
.rxbuffer_size = sizeof(uart3_rxbuffer),
},
.sending = false,
.base = USART3_BASE,
.irq = USART3_IRQHANDLER,
},
#endif
};
struct SerialHardware *ser_hw_getdesc(int port)
{
ASSERT(port >= 0 && port < SER_CNT);
return &UARTDesc[port].hw;
}

View file

@ -0,0 +1,130 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief STM32F10xx UART interface driver.
*
* \author Daniele Basile <asterix@develer.com>
*/
#ifndef SER_STM32_H
#define SER_STM32_H
#include <cfg/debug.h>
#include <cpu/power.h> /* cpu_relax() */
#include <io/stm32.h>
/* Serial hardware numbers */
enum
{
SER_UART1 = 0,
SER_UART2,
#if CPU_CM3_STM32F103RB || CPU_CM3_STM32F103RE
SER_UART3,
#endif
SER_CNT //< Number of serial ports
};
/* Software errors */
#define SERRF_RXFIFOOVERRUN BV(6) //< Rx FIFO buffer overrun
#define SERRF_RXTIMEOUT BV(5) //< Receive timeout
#define SERRF_TXTIMEOUT BV(4) //< Transmit timeout
/*
* Hardware errors.
*/
#define SERRF_RXSROVERRUN SR_ORE //< Input overrun
#define SERRF_FRAMEERROR SR_FE //< Stop bit missing
#define SERRF_PARITYERROR SR_PE //< Parity error
#define SERRF_NOISEERROR SR_NE //< Noise error
/* Serial error/status flags */
typedef uint32_t serstatus_t;
INLINE void stm32_uartDisable(uint32_t base)
{
struct stm32_usart *_base = (struct stm32_usart *)base;
_base->CR1 &= ~CR1_RUN_RESET;
}
INLINE void stm32_uartEnable(uint32_t base)
{
struct stm32_usart *_base = (struct stm32_usart *)base;
_base->CR1 |= CR1_RUN_SET;
}
/* Clear the flags register */
INLINE void stm32_uartClear(uint32_t base)
{
struct stm32_usart *_base = (struct stm32_usart *)base;
_base->SR &= ~USART_FLAG_MASK;
}
INLINE bool stm32_uartTxDone(uint32_t base)
{
struct stm32_usart *_base = (struct stm32_usart *)base;
return (_base->SR & USART_FLAG_TC);
}
INLINE bool stm32_uartTxReady(uint32_t base)
{
struct stm32_usart *_base = (struct stm32_usart *)base;
return (_base->SR & (BV(CR1_TXEIE) | BV(CR1_TCIE)));
}
INLINE bool stm32_uartRxReady(uint32_t base)
{
struct stm32_usart *_base = (struct stm32_usart *)base;
return (_base->SR & BV(CR1_RXNEIE));
}
INLINE int stm32_uartPutChar(uint32_t base, unsigned char c)
{
struct stm32_usart *_base = (struct stm32_usart *)base;
while (!stm32_uartTxReady(base))
cpu_relax();
_base->DR = c;
return c;
}
INLINE int stm32_uartGetChar(uint32_t base)
{
struct stm32_usart * _base = (struct stm32_usart *)base;
return _base->DR;
}
void stm32_uartSetBaudRate(uint32_t base, unsigned long baud);
void stm32_uartSetParity(uint32_t base, int parity);
void stm32_uartInit(int port);
#endif /* SER_STM32_H */

View file

@ -0,0 +1,170 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2008 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief SPI driver with DMA.
*
* \author Francesco Sacchi <batt@develer.com>
* \author Luca Ottaviano <lottaviano@develer.com>
*/
#include "cfg/cfg_spi_dma.h"
#include "hw/hw_spi_dma.h"
#include <io/cm3.h>
#include <io/kfile.h>
#include <struct/fifobuf.h>
#include <struct/kfile_fifo.h>
#include <drv/timer.h>
#include <drv/spi_dma.h>
#include <cpu/attr.h>
#include <cpu/power.h>
#include <string.h> /* memset */
void spi_dma_setclock(uint32_t rate)
{
SPI0_CSR0 &= ~SPI_SCBR;
ASSERT((uint8_t)DIV_ROUND(CPU_FREQ, rate));
SPI0_CSR0 |= DIV_ROUND(CPU_FREQ, rate) << SPI_SCBR_SHIFT;
}
static int spi_dma_flush(UNUSED_ARG(struct KFile *, fd))
{
/* Wait for DMA to finish */
while (!(SPI0_SR & BV(SPI_TXBUFE)))
cpu_relax();
/* Wait until last bit has been shifted out */
while (!(SPI0_SR & BV(SPI_TXEMPTY)))
cpu_relax();
return 0;
}
static size_t spi_dma_write(struct KFile *fd, const void *_buf, size_t size)
{
SPI0_PTCR = BV(PDC_PTCR_TXTDIS);
SPI0_TPR = (reg32_t)_buf;
SPI0_TCR = size;
SPI0_PTCR = BV(PDC_PTSR_TXTEN);
spi_dma_flush(fd);
return size;
}
/*
* Dummy buffer used to transmit 0xff chars while receiving data.
* This buffer is completetly constant and the compiler should allocate it
* in flash memory.
*/
static const uint8_t tx_dummy_buf[CONFIG_SPI_DMA_MAX_RX] = { [0 ... (CONFIG_SPI_DMA_MAX_RX - 1)] = 0xFF };
static size_t spi_dma_read(UNUSED_ARG(struct KFile *, fd), void *_buf, size_t size)
{
size_t count, total_rx = 0;
uint8_t *buf = (uint8_t *)_buf;
while (size)
{
count = MIN(size, (size_t)CONFIG_SPI_DMA_MAX_RX);
SPI0_PTCR = BV(PDC_PTCR_TXTDIS) | BV(PDC_PTCR_RXTDIS);
SPI0_RPR = (reg32_t)buf;
SPI0_RCR = count;
SPI0_TPR = (reg32_t)tx_dummy_buf;
SPI0_TCR = count;
/* Avoid reading the previous sent char */
*buf = SPI0_RDR;
/* Start transfer */
SPI0_PTCR = BV(PDC_PTCR_RXTEN) | BV(PDC_PTCR_TXTEN);
/* wait for transfer to finish */
while (!(SPI0_SR & BV(SPI_ENDRX)))
cpu_relax();
size -= count;
total_rx += count;
buf += count;
}
SPI0_PTCR = BV(PDC_PTCR_RXTDIS) | BV(PDC_PTCR_TXTDIS);
return total_rx;
}
#define SPI_DMA_IRQ_PRIORITY 4
void spi_dma_init(SpiDma *spi)
{
/* Disable PIO on SPI pins */
PIOA_PDR = BV(SPI0_SPCK) | BV(SPI0_MOSI) | BV(SPI0_MISO);
/* Reset device */
SPI0_CR = BV(SPI_SWRST);
/*
* Set SPI to master mode, fixed peripheral select, chip select directly connected to a peripheral device,
* SPI clock set to MCK, mode fault detection disabled, loopback disable, NPCS0 active, Delay between CS = 0
*/
SPI0_MR = BV(SPI_MSTR) | BV(SPI_MODFDIS);
/*
* Set SPI mode.
* At reset clock division factor is set to 0, that is
* *forbidden*. Set SPI clock to minimum to keep it valid.
*/
SPI0_CSR0 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT);
/* Disable all irqs */
SPI0_IDR = 0xFFFFFFFF;
/* Enable SPI clock. */
PMC_PCER = BV(SPI0_ID);
/* Enable SPI */
SPI0_CR = BV(SPI_SPIEN);
DB(spi->fd._type = KFT_SPIDMA);
spi->fd.write = spi_dma_write;
spi->fd.read = spi_dma_read;
spi->fd.flush = spi_dma_flush;
SPI_DMA_STROBE_INIT();
}

View file

@ -0,0 +1,208 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief LM3S1968 Synchronous Serial Interface (SSI) driver.
*
* \author Andrea Righi <arighi@develer.com>
*/
#include "ssi_lm3s.h"
#include <cfg/compiler.h>
#include <cfg/debug.h>
#include <string.h> /* memset() */
/* SSI clocking informations (CPSDVSR + SCR) */
struct SSIClock
{
unsigned int cpsdvsr;
unsigned int scr;
};
/*
* Evaluate the SSI clock prescale (SSICPSR) and SSI serial clock rate (SCR).
*/
INLINE struct SSIClock
lm3s_ssiPrescale(unsigned int bitrate)
{
struct SSIClock ret;
for (ret.cpsdvsr = 2, ret.scr = CPU_FREQ / bitrate / ret.cpsdvsr - 1;
ret.scr > 255; ret.cpsdvsr += 2);
ASSERT(ret.cpsdvsr < 255);
return ret;
}
/*
* Initialize the SSI interface.
*
* Return 0 in case of success, a negative value otherwise.
*/
int lm3s_ssiOpen(uint32_t addr, uint32_t frame, int mode,
int bitrate, uint32_t data_width)
{
struct SSIClock ssi_clock;
ASSERT(addr == SSI0_BASE || addr == SSI1_BASE);
/* Configure the SSI operating mode */
switch (mode)
{
/* SSI Slave Mode Output Disable */
case SSI_MODE_SLAVE_OD:
HWREG(addr + SSI_O_CR1) = SSI_CR1_SOD;
break;
/* SSI Slave */
case SSI_MODE_SLAVE:
HWREG(addr + SSI_O_CR1) = SSI_CR1_MS;
break;
/* SSI Master */
case SSI_MODE_MASTER:
HWREG(addr + SSI_O_CR1) = 0;
break;
default:
ASSERT(0);
return -1;
}
/* Configure the peripheral clock and frame format */
ssi_clock = lm3s_ssiPrescale(bitrate);
HWREG(addr + SSI_O_CPSR) = ssi_clock.cpsdvsr;
HWREG(addr + SSI_O_CR0) =
(ssi_clock.scr << 8) |
((frame & 3) << 6) |
(frame & SSI_CR0_FRF_M) |
(data_width - 1);
/* Enable the SSI interface */
HWREG(addr + SSI_O_CR1) |= SSI_CR1_SSE;
return 0;
}
/*
* Write data to the SSI bus.
*
* Return the number of bytes written to the bus.
*/
static size_t lm3s_ssiWrite(struct KFile *fd, const void *buf, size_t size)
{
LM3SSSI *fds = LM3SSSI_CAST(fd);
const char *p = (const char *)buf;
uint32_t frame;
size_t count = 0;
while (count < size)
{
frame = p[count];
if (fds->flags & LM3S_SSI_NONBLOCK)
{
if (!lm3s_ssiWriteFrameNonBlocking(fds->addr,
frame))
break;
}
else
lm3s_ssiWriteFrame(fds->addr, frame);
count++;
}
return count;
}
/*
* Read data from the SSI bus.
*
* Return the number of bytes read from the bus.
*/
static size_t lm3s_ssiRead(struct KFile *fd, void *buf, size_t size)
{
LM3SSSI *fds = LM3SSSI_CAST(fd);
uint8_t *p = (uint8_t *)buf;
uint32_t frame;
size_t count = 0;
while (count < size)
{
if (fds->flags & LM3S_SSI_NONBLOCK)
{
if (!lm3s_ssiReadFrameNonBlocking(fds->addr, &frame))
break;
}
else
lm3s_ssiReadFrame(fds->addr, &frame);
*p++ = (uint8_t)frame;
count++;
}
return count;
}
/* Wait for data in the TX FIFO being actually transmitted */
static int lm3s_ssiFlush(struct KFile *fd)
{
LM3SSSI *fds = LM3SSSI_CAST(fd);
while (!lm3s_ssiTxDone(fds->addr))
cpu_relax();
return 0;
}
/* Disable the SSI interface */
static int lm3s_ssiClose(struct KFile *fd)
{
LM3SSSI *fds = LM3SSSI_CAST(fd);
lm3s_ssiFlush(fd);
HWREG(fds->addr + SSI_O_CR1) &= ~SSI_CR1_SSE;
return 0;
}
/**
* Initialize a LM3S SSI driver.
*/
void lm3s_ssiInit(struct LM3SSSI *fds, uint32_t addr, uint32_t frame, int mode,
int bitrate, uint32_t data_width)
{
memset(fds, 0, sizeof(*fds));
DB(fds->fd._type = KFT_LM3SSSI);
/* TODO: only 8-bit frame size is supported */
ASSERT(data_width == 8);
fds->fd.write = lm3s_ssiWrite;
fds->fd.read = lm3s_ssiRead;
fds->fd.close = lm3s_ssiClose;
fds->fd.flush = lm3s_ssiFlush;
fds->addr = addr;
lm3s_ssiOpen(addr, frame, mode, bitrate, data_width);
}

View file

@ -0,0 +1,191 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief LM3S1968 Synchronous Serial Interface (SSI) driver.
*
*/
#ifndef SSI_LM3S_H
#define SSI_LM3S_H
#include <cpu/power.h> /* cpu_relax() */
#include <io/kfile.h> /* KFile */
#include <io/lm3s.h>
/**
* LM3S1968 SSI frame format
*/
/*\{*/
#define SSI_FRF_MOTO_MODE_0 0x00000000 //< Moto fmt, polarity 0, phase 0
#define SSI_FRF_MOTO_MODE_1 0x00000002 //< Moto fmt, polarity 0, phase 1
#define SSI_FRF_MOTO_MODE_2 0x00000001 //< Moto fmt, polarity 1, phase 0
#define SSI_FRF_MOTO_MODE_3 0x00000003 //< Moto fmt, polarity 1, phase 1
#define SSI_FRF_TI 0x00000010 //< TI frame format
#define SSI_FRF_NMW 0x00000020 //< National MicroWire frame format
/*\}*/
/**
* LM3S1968 SSI operational mode
*/
/*\{*/
#define SSI_MODE_MASTER 0x00000000 //< SSI master
#define SSI_MODE_SLAVE 0x00000001 //< SSI slave
#define SSI_MODE_SLAVE_OD 0x00000002 //< SSI slave with output disabled
/*\}*/
/* LM3S SSI handle properties */
enum
{
/* Non-blocking I/O */
LM3S_SSI_NONBLOCK = 1,
};
/** LM3S1968 SSI handle structure */
typedef struct LM3SSSI
{
/* SSI Kfile structure */
KFile fd;
/* Handle properties */
uint32_t flags;
/* SSI port address */
uint32_t addr;
} LM3SSSI;
/**
* ID for LM3S SSI.
*/
#define KFT_LM3SSSI MAKE_ID('L', 'S', 'S', 'I')
INLINE LM3SSSI *LM3SSSI_CAST(KFile *fd)
{
ASSERT(fd->_type == KFT_LM3SSSI);
return (LM3SSSI *)fd;
}
/* KFile interface to LM3S SSI */
void lm3s_ssiInit(struct LM3SSSI *fds, uint32_t addr, uint32_t frame, int mode,
int bitrate, uint32_t data_width);
/* Raw interface to LM3S SSI */
int lm3s_ssiOpen(uint32_t addr, uint32_t frame, int mode,
int bitrate, uint32_t data_width);
/*
* Check if the SSI transmitter is busy or not
*
* This allows to determine whether the TX FIFO have been cleared by the
* hardware, so the transmission can be safely considered completed.
*/
INLINE bool lm3s_ssiTxDone(uint32_t base)
{
return (HWREG(base + SSI_O_SR) & SSI_SR_BSY) ? true : false;
}
/*
* Check if the SSI TX FIFO is full
*/
INLINE bool lm3s_ssiTxReady(uint32_t base)
{
return (HWREG(base + SSI_O_SR) & SSI_SR_TNF) ? true : false;
}
/*
* Check for data available in the RX FIFO
*/
INLINE bool lm3s_ssiRxReady(uint32_t base)
{
return (HWREG(base + SSI_O_SR) & SSI_SR_RNE) ? true : false;
}
/*
* Get a frame into the SSI receive FIFO without blocking.
*
* Return the number of frames read from the RX FIFO.
*/
INLINE int lm3s_ssiReadFrameNonBlocking(uint32_t base, uint32_t *val)
{
/* Check for data available in the RX FIFO */
if (!lm3s_ssiRxReady(base))
return 0;
/* Read data from SSI RX FIFO */
*val = HWREG(base + SSI_O_DR);
return 1;
}
/*
* Get a frame from the SSI receive FIFO.
*/
INLINE void lm3s_ssiReadFrame(uint32_t base, uint32_t *val)
{
/* Wait for data available in the RX FIFO */
while (!lm3s_ssiRxReady(base))
cpu_relax();
/* Read data from SSI RX FIFO */
*val = HWREG(base + SSI_O_DR);
}
/*
* Put a frame into the SSI transmit FIFO without blocking.
*
* NOTE: the upper bits of the frame will be automatically discarded by the
* hardware according to the frame data width.
*
* Return the number of frames written to the TX FIFO.
*/
INLINE int lm3s_ssiWriteFrameNonBlocking(uint32_t base, uint32_t val)
{
/* Check for available space in the TX FIFO */
if (!lm3s_ssiTxReady(base))
return 0;
/* Enqueue data to the TX FIFO */
HWREG(base + SSI_O_DR) = val;
return 1;
}
/*
* Put a frame into the SSI transmit FIFO.
*
* NOTE: the upper bits of the frame will be automatically discarded by the
* hardware according to the frame data width.
*/
INLINE void lm3s_ssiWriteFrame(uint32_t base, uint32_t val)
{
/* Wait for available space in the TX FIFO */
while (!lm3s_ssiTxReady(base))
cpu_relax();
/* Enqueue data to the TX FIFO */
HWREG(base + SSI_O_DR) = val;
}
#endif /* SSI_LM3S_H */

View file

@ -0,0 +1,74 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Low-level timer driver (SysTick) for LM3S1968.
*
* \author Andrea Righi <arighi@develer.com>
*/
#include "timer_cm3.h"
#include <cfg/debug.h>
#include <cpu/irq.h>
#include <drv/irq_cm3.h>
INLINE void timer_hw_setPeriod(unsigned long period)
{
ASSERT(period < (1 << 24));
NVIC_ST_RELOAD_R = period - 1;
}
static void timer_hw_enable(void)
{
NVIC_ST_CTRL_R |=
NVIC_ST_CTRL_CLK_SRC | NVIC_ST_CTRL_ENABLE | NVIC_ST_CTRL_INTEN;
}
static void timer_hw_disable(void)
{
NVIC_ST_CTRL_R &= ~(NVIC_ST_CTRL_ENABLE | NVIC_ST_CTRL_INTEN);
}
void timer_hw_init(void)
{
timer_hw_setPeriod(CPU_FREQ / TIMER_TICKS_PER_SEC);
sysirq_setHandler(FAULT_SYSTICK, timer_handler);
timer_hw_enable();
}
void timer_hw_exit(void)
{
timer_hw_disable();
sysirq_freeHandler(FAULT_SYSTICK);
}

View file

@ -0,0 +1,109 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \author Andrea Righi <arighi@develer.com>
*
* \brief Low-level timer driver (SysTick) for Cortex-M3.
*/
#ifndef TIMER_CM3_H
#define TIMER_CM3_H
#include "cfg/cfg_timer.h" /* CONFIG_TIMER */
#include <cpu/detect.h>
#include <cpu/irq.h>
#if CPU_CM3_LM3S
#include <io/lm3s.h>
#elif CPU_CM3_STM32
#include <io/stm32.h>
#elif CPU_CM3_SAM3
#include <io/sam3.h>
/*#elif Add other families here */
#else
#error Unknown CPU
#endif
/**
* \name Values for CONFIG_TIMER.
*
* Select which hardware timer interrupt to use for system clock and softtimers.
*
* $WIZ$ timer_select = "TIMER_DEFAULT", "TIMER_ON_GPTM"
*/
#define TIMER_ON_GPTM 1
#define TIMER_DEFAULT TIMER_ON_GPTM ///< Default system timer
#if (CONFIG_TIMER == TIMER_ON_GPTM)
/* Ticks frequency (HZ) */
#define TIMER_TICKS_PER_SEC 1000
/* Frequency of the hardware high-precision timer. */
#define TIMER_HW_HPTICKS_PER_SEC (CPU_FREQ)
/* Maximum value of the high-precision hardware counter register */
#define TIMER_HW_CNT (CPU_FREQ / TIMER_TICKS_PER_SEC)
/** Type of time expressed in ticks of the hardware high-precision timer */
typedef uint32_t hptime_t;
#define SIZEOF_HPTIME_T 4
/* Timer ISR prototype */
ISR_PROTO_CONTEXT_SWITCH(timer_handler);
#define DEFINE_TIMER_ISR DECLARE_ISR_CONTEXT_SWITCH(timer_handler)
INLINE void timer_hw_irq(void)
{
}
INLINE bool timer_hw_triggered(void)
{
return true;
}
INLINE hptime_t timer_hw_hpread(void)
{
return (TIMER_HW_CNT - NVIC_ST_CURRENT_R);
}
#else
#error Unimplemented value for CONFIG_TIMER
#endif /* CONFIG_TIMER */
void timer_hw_init(void);
void timer_hw_exit(void);
#endif /* TIMER_CM3_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,233 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \author Andrea Righi <arighi@develer.com>
*
* \brief STM32: USB full-speed device driver
*
* Low-level USB device driver for the STM32 architecture.
*/
#ifndef USB_STM32_H
#define USB_STM32_H
#include <cfg/compiler.h>
#include <drv/usb.h>
#include <drv/usb_endpoint.h>
#define USB_BASE_ADDR 0x40005C00
#define USB_DM_PIN (1 << 11)
#define USB_DP_PIN (1 << 12)
#define USB_DISC_PIN (1 << 11)
#define USB_EP0_MAX_SIZE CONFIG_EP0_MAX_SIZE
#define USB_XFER_MAX_SIZE 64
#define EP_MAX_SLOTS USB_EP_MAX
#define EP_MAX_NUM (EP_MAX_SLOTS << 1)
/* USB packet memory organization */
#define USB_PACKET_MEMORY_BASE 0x40006000
#define USB_PACKET_MEMORY_SIZE 512
/* Offset of the buffer descriptor table inside the packet memory */
#define USB_BDT_OFFSET \
((USB_PACKET_MEMORY_SIZE - (sizeof(stm32_UsbBd) * EP_MAX_NUM)) & ~7)
#define USB_MEM_ADDR(offset) \
(USB_PACKET_MEMORY_BASE + ((offset << 1) & ~3) + (offset & 1))
#define EP_DTB_READ(slot, offset) \
(*((uint16_t *)(USB_MEM_ADDR((USB_BDT_OFFSET + \
(slot) * sizeof(stm32_UsbBd) + \
(offset))))))
#define EP_DTB_WRITE(slot, offset, data) (EP_DTB_READ(slot, offset) = data)
#define ADDR_TX_OFFSET offsetof(stm32_UsbBd, AddrTx)
#define COUNT_TX_OFFSET offsetof(stm32_UsbBd, CountTx)
#define ADDR_RX_OFFSET offsetof(stm32_UsbBd, AddrRx)
#define COUNT_RX_OFFSET offsetof(stm32_UsbBd, CountRx)
#define USB_CTRL_RW_MASK 0x070F
#define USB_CTRL_CLEAR_ONLY_MASK 0x8080
#define USB_CTRL_TOGGLE_MASK 0x7070
/* CNTR register flags */
#define bmCTRM 0x8000
#define bmPMAOVRM 0x4000
#define bmERRM 0x2000
#define bmWKUPM 0x1000
#define bmSUSPM 0x0800
#define bmRESETM 0x0400
#define bmSOFM 0x0200
#define bmESOFM 0x0100
#define bmRESUME 0x0010
#define bmFSUSP 0x0008
#define bmLPMODE 0x0004
#define bmPDWN 0x0002
#define bmFRES 0x0001
/* USB error codes */
enum stm32_usb_error
{
USB_OK = 0,
USB_INTR_ERROR,
USB_INVAL_ERROR,
USB_NODEV_ERROR,
USB_MEMORY_FULL,
USB_BUF_OVERFLOW,
USB_EP_STALLED,
USB_FATAL_ERROR,
};
/* STM32 USB endpoint types */
enum stm32_UsbEpype
{
EP_BULK = 0,
EP_CTRL,
EP_ISO,
EP_INTERRUPT,
EP_TYPE_MAX
};
/* STM32 USB interrupt status register bits */
typedef union
{
uint32_t status;
struct {
uint8_t EP_ID : 4;
uint8_t DIR : 1;
uint8_t : 2;
uint8_t SZDPR : 1;
uint8_t ESOF : 1;
uint8_t SOF : 1;
uint8_t RESET : 1;
uint8_t SUSP : 1;
uint8_t WKUP : 1;
uint8_t ERR : 1;
uint8_t PMAOVR : 1;
uint8_t CTR : 1;
};
} PACKED stm32_usb_irq_status_t;
/* Endpoint state */
typedef enum
{
EP_DISABLED = 0,
EP_STALL,
EP_NAK,
EP_VALID
} stm32_UsbEpState;
/* STM32 USB supported endpoints */
typedef enum stm32_UsbEP
{
CTRL_ENP_OUT = 0, CTRL_ENP_IN,
ENP1_OUT, ENP1_IN,
ENP2_OUT, ENP2_IN,
ENP3_OUT, ENP3_IN,
ENP4_OUT, ENP4_IN,
ENP5_OUT, ENP5_IN,
ENP6_OUT, ENP6_IN,
ENP7_OUT, ENP7_IN,
ENP8_OUT, ENP8_IN,
ENP9_OUT, ENP9_IN,
ENP10_OUT, ENP10_IN,
ENP11_OUT, ENP11_IN,
ENP12_OUT, ENP12_IN,
ENP13_OUT, ENP13_IN,
ENP14_OUT, ENP14_IN,
ENP15_OUT, ENP15_IN,
EP_MAX_HW_NUM
} stm32_UsbEP;
/* STM32 USB packet memory slot */
typedef struct stm32_UsbMemSlot
{
stm32_UsbEP ep_addr;
uint16_t Start;
uint16_t Size;
struct stm32_UsbMemSlot *next;
} stm32_UsbMemSlot;
/* STM32 USB buffer descriptor (packet memory) */
typedef struct stm32_UsbBd
{
uint16_t AddrTx;
uint16_t CountTx;
uint16_t AddrRx;
uint16_t CountRx;
} PACKED stm32_UsbBd;
/* STM32 USB endpoint I/O status */
typedef enum stm32_UsbIoStatus
{
NOT_READY = 0,
NO_SERVICED,
BEGIN_SERVICED,
COMPLETE,
BUFFER_UNDERRUN,
BUFFER_OVERRUN,
SETUP_OVERWRITE,
STALLED,
} stm32_UsbIoStatus;
/* STM32 USB hardware endpoint descriptor */
typedef struct stm32_UsbEp
{
reg32_t *hw;
uint8_t type;
void (*complete)(int);
ssize_t max_size;
ssize_t offset;
ssize_t size;
stm32_UsbIoStatus status;
union
{
uint8_t *read_buffer;
const uint8_t *write_buffer;
};
int32_t avail_data;
uint8_t flags;
} stm32_UsbEp;
/* STM32 USB hardware endpoint flags */
#define STM32_USB_EP_AVAIL_DATA BV(0)
#define STM32_USB_EP_ZERO_PACKET BV(1)
#define STM32_USB_EP_ZERO_POSSIBLE BV(2)
#endif /* USB_STM32_H */