blob: ebfa259adc5e28fdcd4773d807f4e7983b423b8a [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <stdbool.h>
#include <stddef.h>
#include "os/mynewt.h"
#include "bsp/bsp.h"
#include "hal/hal_gpio.h"
#include "hal/hal_spi.h"
#include "mcu/mcu.h"
#include "mcu/mips_hal.h"
#include "mcu/p32mx470f512h.h"
#include "mcu/pps.h"
#define SPIxCON(P) (base_address[P][0x0 / 0x4])
#define SPIxCONCLR(P) (base_address[P][0x4 / 0x4])
#define SPIxCONSET(P) (base_address[P][0x8 / 0x4])
#define SPIxSTAT(P) (base_address[P][0x10 / 0x4])
#define SPIxSTATCLR(P) (base_address[P][0x14 / 0x4])
#define SPIxBUF(P) (base_address[P][0x20 / 0x4])
#define SPIxBRG(P) (base_address[P][0x30 / 0x4])
#define SPIxCON2(P) (base_address[P][0x40 / 0x4])
static volatile uint32_t * base_address[SPI_CNT] = {
(volatile uint32_t *)_SPI1_BASE_ADDRESS,
(volatile uint32_t *)_SPI2_BASE_ADDRESS,
};
struct hal_spi {
bool slave;
uint8_t *txbuf;
uint8_t *rxbuf;
int len;
int txcnt;
int rxcnt;
hal_spi_txrx_cb callback;
void *arg;
const struct mips_spi_cfg *pins;
uint32_t con;
uint32_t brg;
};
static struct hal_spi spis[SPI_CNT];
static void
hal_spi_power_up(int spi_num)
{
uint32_t mask = 0;
switch (spi_num) {
case 0:
mask = _PMD5_SPI1MD_MASK;
break;
case 1:
mask = _PMD5_SPI2MD_MASK;
break;
}
if (!(PMD5 & mask))
return;
PMD5CLR = mask;
/* It appeared that powering down the SPI module also clears SPIxBRG and SPIxCON */
SPIxBRG(spi_num) = spis[spi_num].brg;
SPIxCON(spi_num) = spis[spi_num].con;
}
static void
hal_spi_power_down(int spi_num)
{
/* It appeared that powering down the SPI module also clears SPIxBRG and SPIxCON */
spis[spi_num].brg = SPIxBRG(spi_num);
spis[spi_num].con = SPIxCON(spi_num);
switch (spi_num) {
case 0:
PMD5SET = _PMD5_SPI1MD_MASK;
break;
case 1:
PMD5SET = _PMD5_SPI2MD_MASK;
break;
}
}
static int
hal_spi_config_master(int spi_num, struct hal_spi_settings *psettings)
{
uint32_t pbclk, pbdiv;
/*
* Make sure that the SPI module is not powered down.
* If the module is powered down, one cannot write to registers.
*/
hal_spi_power_up(spi_num);
SPIxCON(spi_num) = 0;
SPIxCON2(spi_num) = 0;
/* Clear RX FIFO */
while (!(SPIxSTAT(spi_num) & _SPI1STAT_SPITBE_MASK)) {
volatile int rdata = SPIxBUF(spi_num);
(void)rdata;
}
/* The SPI module only supports MSB first */
if (psettings->data_order == HAL_SPI_LSB_FIRST) {
return -1;
}
/* Only 8-bit word size is supported */
if (psettings->word_size != HAL_SPI_WORD_SIZE_8BIT) {
return -1;
}
switch (psettings->data_mode) {
case HAL_SPI_MODE0:
SPIxCONCLR(spi_num) = _SPI1CON_CKP_MASK;
SPIxCONSET(spi_num) = _SPI1CON_CKE_MASK;
break;
case HAL_SPI_MODE1:
SPIxCONCLR(spi_num) = _SPI1CON_CKP_MASK | _SPI1CON_CKE_MASK;
break;
case HAL_SPI_MODE2:
SPIxCONSET(spi_num) = _SPI1CON_CKP_MASK | _SPI1CON_CKE_MASK;
break;
case HAL_SPI_MODE3:
SPIxCONCLR(spi_num) = _SPI1CON_CKE_MASK;
SPIxCONSET(spi_num) = _SPI1CON_CKP_MASK;
break;
default:
return -1;
}
/*
* From equation 23-1 of Section 23 of PIC32 FRM:
*
* Fpb2
* Fsck = -------------------
* 2 * (SPIxBRG + 1)
*/
pbdiv = (OSCCON & _OSCCON_PBDIV_MASK) >> _OSCCON_PBDIV_POSITION;
pbclk = MYNEWT_VAL(CLOCK_FREQ) / (pbdiv + 1);
SPIxBRG(spi_num) = (pbclk / (2 * psettings->baudrate)) - 1;
SPIxSTATCLR(spi_num) = _SPI1STAT_SPIROV_MASK;
SPIxCONSET(spi_num) = _SPI1CON_ENHBUF_MASK | _SPI1CON_MSTEN_MASK;
return 0;
}
static int
hal_spi_config_pins(int spi_num, uint8_t mode)
{
int ret = 0;
if (hal_gpio_init_out(spis[spi_num].pins->mosi, 0) ||
hal_gpio_init_out(spis[spi_num].pins->sck, 1) ||
hal_gpio_init_in(spis[spi_num].pins->miso, HAL_GPIO_PULL_NONE)) {
return -1;
}
/*
* To avoid any glitches when turning off and on module, the SCK pin must
* be set to the correct value depending on the mode.
*/
switch (mode) {
case HAL_SPI_MODE0:
case HAL_SPI_MODE1:
hal_gpio_write(spis[spi_num].pins->sck, 0);
break;
case HAL_SPI_MODE2:
case HAL_SPI_MODE3:
hal_gpio_write(spis[spi_num].pins->sck, 1);
break;
}
switch (spi_num) {
case 0:
ret += pps_configure_output(spis[spi_num].pins->mosi,
SDO1_OUT_FUNC);
ret += pps_configure_input(spis[spi_num].pins->miso,
SDI1_IN_FUNC);
break;
case 1:
ret += pps_configure_output(spis[spi_num].pins->mosi,
SDO2_OUT_FUNC);
ret += pps_configure_input(spis[spi_num].pins->miso,
SDI2_IN_FUNC);
break;
}
return ret;
}
static void
hal_spi_enable_int(int spi_num)
{
switch (spi_num) {
case 0:
IFS1CLR = _IFS1_SPI1TXIF_MASK;
IEC1SET = _IEC1_SPI1TXIE_MASK;
break;
case 1:
IFS1CLR = _IFS1_SPI2TXIF_MASK;
IEC1SET = _IEC1_SPI2TXIE_MASK;
break;
}
}
static void
hal_spi_disable_int(int spi_num)
{
switch (spi_num) {
case 0:
IFS1CLR = _IFS1_SPI1TXIF_MASK;
IEC1CLR = _IEC1_SPI1TXIE_MASK;
break;
case 1:
IFS1CLR = _IFS1_SPI2TXIF_MASK;
IEC1CLR = _IEC1_SPI2TXIE_MASK;
break;
}
}
static void
hal_spi_handle_isr(int spi_num)
{
/* Read everything in RX FIFO */
while (!(SPIxSTAT(spi_num) & _SPI1STAT_SPIRBE_MASK)) {
volatile uint32_t rxdata = SPIxBUF(spi_num);
if (spis[spi_num].rxbuf && spis[spi_num].rxcnt) {
*spis[spi_num].rxbuf++ = rxdata;
--spis[spi_num].rxcnt;
}
}
if (spis[spi_num].txcnt == 0 && spis[spi_num].rxcnt == 0) {
spis[spi_num].txbuf = NULL;
spis[spi_num].rxbuf = NULL;
if (spis[spi_num].callback) {
spis[spi_num].callback(spis[spi_num].arg, spis[spi_num].len);
}
hal_spi_disable_int(spi_num);
}
/* Fill TX FIFO */
while (spis[spi_num].txcnt &&
!(SPIxSTAT(spi_num) & _SPI1STAT_SPITBF_MASK)) {
SPIxBUF(spi_num) = *spis[spi_num].txbuf++;
--spis[spi_num].txcnt;
}
}
void
__attribute__((interrupt(IPL2AUTO), vector(_SPI_1_VECTOR)))
hal_spi1_isr(void)
{
hal_spi_handle_isr(0);
IFS1CLR = _IFS1_SPI1TXIF_MASK;
}
void
__attribute__((interrupt(IPL2AUTO), vector(_SPI_2_VECTOR)))
hal_spi2_isr(void)
{
hal_spi_handle_isr(1);
IFS1CLR = _IFS1_SPI2TXIF_MASK;
}
int
hal_spi_init(int spi_num, void *cfg, uint8_t spi_type)
{
if (spi_type != HAL_SPI_TYPE_MASTER &&
spi_type != HAL_SPI_TYPE_SLAVE) {
return -1;
}
spis[spi_num].slave = spi_type;
spis[spi_num].pins = cfg;
return 0;
}
int
hal_spi_config(int spi_num, struct hal_spi_settings *psettings)
{
/* Slave mode not supported */
if (spis[spi_num].slave) {
return -1;
}
/* Configure pins */
if (spis[spi_num].pins) {
if (hal_spi_config_pins(spi_num, psettings->data_mode)) {
return -1;
}
}
return hal_spi_config_master(spi_num, psettings);
}
int
hal_spi_set_txrx_cb(int spi_num, hal_spi_txrx_cb txrx_cb, void *arg)
{
if (SPIxCON(spi_num) & _SPI1CON_ON_MASK) {
return -1;
}
spis[spi_num].callback = txrx_cb;
spis[spi_num].arg = arg;
return 0;
}
int
hal_spi_enable(int spi_num)
{
hal_spi_power_up(spi_num);
SPIxCONSET(spi_num) = _SPI1CON_ON_MASK;
return 0;
}
int
hal_spi_disable(int spi_num)
{
/*
* Disabling SPI clears the FIFO, so this makes sure that everything was
* sent before disabling the module.
*/
while (!(SPIxSTAT(spi_num) & _SPI1STAT_SPITBE_MASK)) {
}
SPIxCONCLR(spi_num) = _SPI1CON_ON_MASK;
hal_spi_power_down(spi_num);
return 0;
}
uint16_t
hal_spi_tx_val(int spi_num, uint16_t val)
{
if (spis[spi_num].slave) {
return 0xFFFF;
}
/* Wait until there is some space in TX FIFO */
while (SPIxSTAT(spi_num) & _SPI1STAT_SPITBF_MASK) {
}
SPIxBUF(spi_num) = val;
/* Wait until RX FIFO is not empty */
while (SPIxSTAT(spi_num) & _SPI1STAT_SPIRBE_MASK) {
}
return SPIxBUF(spi_num);
}
int
hal_spi_txrx(int spi_num, void *txbuf, void *rxbuf, int cnt)
{
uint8_t *tx = (uint8_t *)txbuf;
uint8_t *rx = (uint8_t *)rxbuf;
/* Slave mode not supported */
if (spis[spi_num].slave) {
return -1;
}
while (cnt--) {
uint8_t rdata;
if (tx) {
/* Wait until there is some space in TX FIFO */
while (SPIxSTAT(spi_num) & _SPI1STAT_SPITBF_MASK) {
}
SPIxBUF(spi_num) = *tx++;
}
/* Wait until RX FIFO is not empty */
while (SPIxSTAT(spi_num) & _SPI1STAT_SPIRBE_MASK) {
}
/* Always read RX FIFO to avoid overrun */
rdata = SPIxBUF(spi_num);
if (rx) {
*rx++ = rdata;
}
}
return 0;
}
int
hal_spi_txrx_noblock(int spi_num, void *txbuf, void *rxbuf, int cnt)
{
uint32_t ctx;
/* Slave mode not supported */
if (spis[spi_num].slave) {
return -1;
}
if (txbuf == NULL) {
return -1;
}
/* Check if a transfer is pending */
if (spis[spi_num].rxbuf != NULL || spis[spi_num].txbuf != NULL) {
return -1;
}
spis[spi_num].txbuf = txbuf;
spis[spi_num].rxbuf = rxbuf;
spis[spi_num].txcnt = cnt;
spis[spi_num].rxcnt = cnt;
spis[spi_num].len = cnt;
/* Configure SPIxTXIF to trigger when TX FIFO is empty */
SPIxCONCLR(spi_num) = _SPI1CON_STXISEL_MASK;
SPIxCONSET(spi_num) = 0b01 << _SPI1CON_STXISEL_POSITION;
/* Set interrupt priority */
switch (spi_num) {
case 0:
IPC7CLR = _IPC7_SPI1IS_MASK | _IPC7_SPI1IP_MASK;
IPC7SET = 2 << _IPC7_SPI1IP_POSITION;
break;
case 1:
IPC8CLR = _IPC8_SPI2IS_MASK | _IPC8_SPI2IP_MASK;
IPC8SET = 2 << _IPC8_SPI2IP_POSITION;
break;
}
/* Enable interrupt */
hal_spi_enable_int(spi_num);
return 0;
}
int
hal_spi_slave_set_def_tx_val(int spi_num, uint16_t val)
{
/* Slave mode not supported */
return -1;
}
int
hal_spi_abort(int spi_num)
{
/* Cannot abort transfer if spi is not enabled */
if (!(SPIxCON(spi_num) & _SPI1CON_ON_MASK)) {
return -1;
}
hal_spi_disable_int(spi_num);
spis[spi_num].txbuf = NULL;
spis[spi_num].rxbuf = NULL;
spis[spi_num].txcnt = 0;
spis[spi_num].rxcnt = 0;
spis[spi_num].len = 0;
/* Make sure that we finished transmitting current byte before turning off module */
while (!(SPIxSTAT(spi_num) & _SPI1STAT_SRMT_MASK)) {
}
/* Clear TX and RX FIFO by turning off and on module */
SPIxCONCLR(spi_num) = _SPI1CON_ON_MASK;
asm volatile ("nop");
SPIxCONSET(spi_num) = _SPI1CON_ON_MASK;
return 0;
}