mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
43f07fa261
This implements `pm_set_lowest()` for the MSP430. Unlike most other platforms, it intentionally does not use pm_layered. It is pretty similar to `pm_layered` in that is does use reference counters, but it uses them for two independent clock sources. The main difference is that the low frequency clock domain can be disabled even when the high frequency clock is still active. With the layers, disabling layer n-1 while layer n is still blocked would not work.
192 lines
5.2 KiB
C
192 lines
5.2 KiB
C
#include "macros/math.h"
|
|
#include "mutex.h"
|
|
#include "periph_conf.h"
|
|
#include "periph_cpu.h"
|
|
|
|
const msp430_usart_uart_params_t usart0_as_uart = {
|
|
.usart_params = {
|
|
.num = 0,
|
|
.dev = &USART_0,
|
|
.sfr = &USART_0_SFR,
|
|
.tx_irq_mask = UTXE0,
|
|
.rx_irq_mask = URXE0,
|
|
},
|
|
.tx_enable_mask = UTXE0,
|
|
.rxtx_enable_mask = URXE0 | UTXE0,
|
|
.txd = GPIO_PIN(P3, 4),
|
|
.rxd = GPIO_PIN(P3, 5),
|
|
};
|
|
|
|
const msp430_usart_uart_params_t usart1_as_uart = {
|
|
.usart_params = {
|
|
.num = 1,
|
|
.dev = &USART_1,
|
|
.sfr = &USART_1_SFR,
|
|
.tx_irq_mask = UTXE1,
|
|
.rx_irq_mask = URXE1,
|
|
},
|
|
.tx_enable_mask = UTXE1,
|
|
.rxtx_enable_mask = URXE1 | UTXE1,
|
|
.txd = GPIO_PIN(P3, 6),
|
|
.rxd = GPIO_PIN(P3, 7),
|
|
};
|
|
|
|
const msp430_usart_spi_params_t usart0_as_spi = {
|
|
.usart_params = {
|
|
.num = 0,
|
|
.dev = &USART_0,
|
|
.sfr = &USART_0_SFR,
|
|
.tx_irq_mask = UTXE0,
|
|
.rx_irq_mask = URXE0,
|
|
},
|
|
.enable_mask = USPIE0,
|
|
.mosi = GPIO_PIN(P3, 1),
|
|
.miso = GPIO_PIN(P3, 2),
|
|
.sck = GPIO_PIN(P3, 3),
|
|
};
|
|
|
|
const msp430_usart_spi_params_t usart1_as_spi = {
|
|
.usart_params = {
|
|
.num = 1,
|
|
.dev = &USART_1,
|
|
.sfr = &USART_1_SFR,
|
|
.tx_irq_mask = UTXE1,
|
|
.rx_irq_mask = URXE1,
|
|
},
|
|
.enable_mask = USPIE1,
|
|
.mosi = GPIO_PIN(P5, 1),
|
|
.miso = GPIO_PIN(P5, 2),
|
|
.sck = GPIO_PIN(P5, 3),
|
|
};
|
|
|
|
static mutex_t usart_locks[USART_NUMOF] = {
|
|
MUTEX_INIT,
|
|
MUTEX_INIT,
|
|
};
|
|
|
|
/* store the clock acquired by each USART, so it can be release again */
|
|
static msp430_usart_clk_t _clocks_acquired[USART_NUMOF];
|
|
|
|
void msp430_usart_acquire(const msp430_usart_params_t *params,
|
|
const msp430_usart_conf_t *conf,
|
|
uint8_t enable_mask)
|
|
{
|
|
assume(params->num < USART_NUMOF);
|
|
|
|
mutex_lock(&usart_locks[params->num]);
|
|
msp430_usart_t *dev = params->dev;
|
|
msp430_usart_sfr_t *sfr = params->sfr;
|
|
|
|
_clocks_acquired[params->num] = conf->prescaler.clk_source;
|
|
switch (_clocks_acquired[params->num]) {
|
|
case USART_CLK_SUBMAIN:
|
|
msp430_clock_acquire(MSP430_CLOCK_SUBMAIN);
|
|
break;
|
|
case USART_CLK_AUX:
|
|
msp430_clock_acquire(MSP430_CLOCK_AUXILIARY);
|
|
break;
|
|
default:
|
|
/* external clock from GPIO, safe to disable internal clocks */
|
|
break;
|
|
}
|
|
|
|
/* first, make sure USART is off before reconfiguring it */
|
|
sfr->ME = 0;
|
|
/* reset USART */
|
|
dev->CTL = SWRST;
|
|
|
|
/* apply given configuration */
|
|
dev->CTL = conf->ctl | SWRST;
|
|
dev->MCTL = conf->prescaler.mctl;
|
|
dev->TCTL = conf->prescaler.clk_source;
|
|
dev->BR0 = conf->prescaler.br0;
|
|
dev->BR1 = conf->prescaler.br1;
|
|
|
|
/* disable USART IRQs and clear any spurious IRQ flags */
|
|
sfr->IE = 0;
|
|
sfr->IFG = 0;
|
|
/* enable USART as specified */
|
|
sfr->ME = enable_mask;
|
|
}
|
|
|
|
void msp430_usart_release(const msp430_usart_params_t *params)
|
|
{
|
|
assume(params->num < USART_NUMOF);
|
|
|
|
msp430_usart_sfr_t *sfr = params->sfr;
|
|
|
|
/* Disable USART */
|
|
sfr->ME = 0;
|
|
/* disable USART IRQs and clear any spurious IRQ flags */
|
|
sfr->IE = 0;
|
|
sfr->IFG = 0;
|
|
|
|
switch (_clocks_acquired[params->num]) {
|
|
case USART_CLK_SUBMAIN:
|
|
msp430_clock_release(MSP430_CLOCK_SUBMAIN);
|
|
break;
|
|
case USART_CLK_AUX:
|
|
msp430_clock_release(MSP430_CLOCK_AUXILIARY);
|
|
break;
|
|
default:
|
|
/* external clock from GPIO, not managed here */
|
|
break;
|
|
}
|
|
|
|
/* Release mutex */
|
|
mutex_unlock(&usart_locks[params->num]);
|
|
}
|
|
|
|
msp430_usart_prescaler_t msp430_usart_prescale(uint32_t clock, uint16_t min_br)
|
|
{
|
|
msp430_usart_prescaler_t result = { .mctl = 0 };
|
|
uint32_t clk_hz;
|
|
|
|
/* If a watch crystal is used for the auxiliary clock, allow using the
|
|
* auxiliary clock to be used as clock source for well-known
|
|
* symbol rates, so that enabling low power modes is possible while
|
|
* UART RX is active */
|
|
if ((clock_params.lfxt1_frequency == 32768)
|
|
&& (clock_params.auxiliary_clock_divier == AUXILIARY_CLOCK_DIVIDE_BY_1)) {
|
|
clk_hz = msp430_auxiliary_clock_freq();
|
|
assume(clk_hz == 32768);
|
|
result.clk_source = USART_CLK_AUX;
|
|
/* Rather than calculating the correct modulation control register
|
|
* values, just hard-code it for four well-known symbol rates. If the
|
|
* symbol rate is something else, we go for the high frequency
|
|
* subsystem main clock, where bit timings are easier to hit even
|
|
* without fine-tuning it via the modulation control register */
|
|
switch (clock) {
|
|
case 9600:
|
|
result.mctl = 0x4a;
|
|
break;
|
|
case 4800:
|
|
result.mctl = 0x6f;
|
|
break;
|
|
case 2400:
|
|
result.mctl = 0x6b;
|
|
break;
|
|
case 1200:
|
|
result.mctl = 0x03;
|
|
break;
|
|
default:
|
|
clk_hz = msp430_submain_clock_freq();
|
|
result.clk_source = USART_CLK_SUBMAIN;
|
|
}
|
|
}
|
|
else {
|
|
clk_hz = msp430_submain_clock_freq();
|
|
result.clk_source = USART_CLK_SUBMAIN;
|
|
}
|
|
|
|
uint16_t br = DIV_ROUND(clk_hz, clock);
|
|
|
|
if (br < min_br) {
|
|
br = min_br;
|
|
}
|
|
|
|
result.br0 = (uint8_t)br;
|
|
result.br1 = (uint8_t)(br >> 8);
|
|
return result;
|
|
}
|