Драйвер spi-baikal-t1.so для ЗОСРВ Нейтрино редакции 2020

This commit is contained in:
commit e9a13d9216
16 changed files with 1113 additions and 0 deletions

3
Makefile Normal file
View File

@ -0,0 +1,3 @@
LIST=hardware
EARLY_DIRS=i2c
include recurse.mk

55
README.md Normal file
View File

@ -0,0 +1,55 @@
## Общая структура SPI драйвера
```
┌───────────────────────────┐
│ │
│ SPI шина │
│ │
└─────────────▴─────────────┘
┌─────────────┴─────────────┐ ┌───────────────────────────┐
│ │ │ │
│ SPI-драйвер (spi-*) ◂────────┤ Менеджер spi-master │
│ │ │ │
└───────────────────────────┘ └───────────────────────────┘
┌─────────────┴─────────────┐
│ │
│ Клиентское приложение │
│ │
└───────────────────────────┘
```
## Дерево исходных кодов
```
|- spi/
| `- baikal-t1/ - Исходный код SPI-драйвера для Baikal-T1
| |- Makefile - Правила сборки дерева исходников
| `- common.mk - Параметры сборки драйверов
|
`- Makefile - Правила сборки дерева исходников
```
## Сборка драйвера
- Установить и настроить [комплект разработчика](https://help.kpda.ru/help/topic/ru.kpda.doc.dev_tools_ru/html/devkit/devkit.html) для [ЗОСРВ "Нейтрино" редакции 2020](https://help.kpda.ru/help/index.jsp).
- Выполнить команду:
```
make
```
## Запуск драйвера
```
spi-master -u1 -d baikal-t1 base=0x1f04e000,irq=0x128,gpiobase=0x1f044000,gpiocs=24 &
```

2
spi/Makefile Normal file
View File

@ -0,0 +1,2 @@
LIST=BOARD
include recurse.mk

8
spi/baikal-t1/Makefile Normal file
View File

@ -0,0 +1,8 @@
LIST=CPU
ifndef QRECURSE
QRECURSE=recurse.mk
ifdef QCONFIG
QRDIR=$(dir $(QCONFIG))
endif
endif
include $(QRDIR)$(QRECURSE)

View File

@ -0,0 +1,264 @@
/*
* (c) 2019, SWD Embedded Systems Limited, http://www.kpda.ru
*
* (c) 2014 Baikal Electronics. Based on U-Boot code from Baikal Electronics.
*/
/*
* Board: Baikal-T1 CPC313
* Interface: SPI
* Contain control structures, wrapped i/o functions,
* register's offsets and bitmasks
*/
#ifndef _BAIKALSPI_H_INCLUDED
#define _BAIKALSPI_H_INCLUDED
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/slog.h>
#include <hw/inout.h>
#include <hw/spi-master.h>
#include <mips/baikalt1_intr.h>
#include <errno.h>
#define BAIKAL_SPI_PRIORITY 21
#define BAIKAL_SPI_EVENT 1
#define BAIKAL_SPI_BUS_FREQ 50000000 /* 50 MHz system clock */
#define BAIKAL_SPI_SIZE 0x100
#define BAIKAL_SPI_FIFO_LEN 64
#define BAIKAL_SPI1_BASE 0x1F04E000
#define BAIKAL_SPI1_IRQ BT1_INTR_SPI0 /* 0x128 */
#define BAIKAL_SPI2_BASE 0x1F04F000
#define BAIKAL_SPI2_IRQ BT1_INTR_SPI1 /* 0x129 */
#define BAIKAL_GPIO_BASE 0x1F044000
#define BAIKAL_GPIO_CS_PIN 24
#define BAIKAL_GPIO_SIZE 0x78 /* Last 32bit reg. at offset 0x74 */
#define GPIO_DR 0x0
#define GPIO_DR_HIGH 0x1
#define GPIO_DR_LOW 0x0
#define GPIO_DDR 0x4
#define GPIO_DDR_INPUT 0x0
#define GPIO_DDR_OUTPUT 0x1
/* REGISTER ADDRESSES */
#define SPI_CTRLR0 0x00 /* Control Register 0 */
#define SPI_CTRLR1 0x04 /* Control Register 1 */
#define SPI_SSIENR 0x08 /* SSI Enable Register */
#define SPI_MWCR 0x0c /* Microwire Control Register. */
#define SPI_SER 0x10 /* Slave Enable Register. */
#define SPI_BAUDR 0x14 /* Baud Rate Select. */
#define SPI_TXFTLR 0x18 /* Transmit FIFO Threshold Level. */
#define SPI_RXFTLR 0x1c /* Receive FIFO Threshold level. */
#define SPI_TXFLR 0x20 /* Transmit FIFO Level Register */
#define SPI_RXFLR 0x24 /* Receive FIFO Level Register */
#define SPI_SR 0x28 /* Status Register */
#define SPI_IMR 0x2c /* Interrupt Mask Register */
#define SPI_ISR 0x30 /* Interrupt Status Register */
#define SPI_RISR 0x34 /* Raw Interrupt Status Register */
#define SPI_TXOICR 0x38 /* Transmit FIFO Overflow Interrupt Clear Register */
#define SPI_RXOICR 0x3c /* Receive FIFO Overflow Interrupt Clear Register */
#define SPI_RXUICR 0x40 /* Receive FIFO Underflow Interrupt Clear Register */
#define SPI_MSTICR 0x44 /* Multi-Master Interrupt Clear Register */
#define SPI_ICR 0x48 /* Interrupt Clear Register */
#define SPI_DMACR 0x4c /* DMA Control Register. */
#define SPI_DMATDLR 0x50 /* DMA Transmit Data Level. */
#define SPI_DMARDLR 0x54 /* DMA Receive Data Level. */
#define SPI_IDR 0x58 /* Identification Register. */
#define SPI_SSI_VERSION_ID 0x5c /* coreKit Version ID Register */
#define SPI_DR0 0x60 /* A 16-bit read/write buffer for the transmit/receive FIFOs. */
#define SPI_DR35 0xec /* A 16-bit read/write buffer for the transmit/receive FIFOs. */
#define SPI_RX_SAMPLE_DLY 0xf0 /* RX Sample Delay. */
#define SPI_RSVD_0 0xf4 /* RSVD_0 - Reserved address location */
#define SPI_RSVD_1 0xf8 /* RSVD_1 - Reserved address location */
#define SPI_RSVD_2 0xfc /* RSVD_2 - Reserved address location */
/* Bit fields in ISR, RISR, IMR */
#define SPI_INT_TXEI (1 << 0)
#define SPI_INT_TXOI (1 << 1)
#define SPI_INT_RXUI (1 << 2)
#define SPI_INT_RXOI (1 << 3)
#define SPI_INT_RXFI (1 << 4)
#define SPI_INT_MSTI (1 << 5)
#define SPI_INT_MASK 0x3f /* Full Mask */
#define SPI_DISABLE 0x0
#define SPI_ENABLE 0x1
#define BIT( x ) (1 << (x))
/* Bit fields in CTRLR0 */
#define SPI_DFS_OFFSET 0
#define SPI_DFS_8BIT 0x8
#define SPI_DFS( x ) (((x) - 1) << SPI_DFS_OFFSET)
/* Frame Format */
#define SPI_FRF_OFFSET 4
#define SPI_FRF_SPI 0x0
#define SPI_FRF_SSP 0x1
#define SPI_FRF_MICROWIRE 0x2
#define SPI_FRF_RESV 0x3
#define SPI_FRF( x ) ((x) << SPI_FRF_OFFSET)
/* Serial Clock Phase. */
#define SPI_SCPH_OFFSET 6
#define SPI_SCPH_MIDDLE 0
#define SPI_SCPH_START 1
#define SPI_SCPH( x ) ((x) << SPI_SCPH_OFFSET)
/* Serial Clock Polarity. */
#define SPI_SCPOL_OFFSET 7
#define SPI_SCPOL_LOW 0
#define SPI_SCPOL_HIGH 1
#define SPI_SCPOL( x ) ((x) << SPI_SCPOL_OFFSET)
/* SPI Clock Mode */
#define SPI_CPHA 0x01 /* clock phase */
#define SPI_CPOL 0x02 /* clock polarity */
#define SPI_MODE_OFFSET SPI_SCPH_OFFSET
#define SPI_MODE_0 ( 0 | 0 )
#define SPI_MODE_1 ( 0 | SPI_CPHA )
#define SPI_MODE_2 ( SPI_CPOL | 0 )
#define SPI_MODE_3 ( SPI_CPOL | SPI_CPHA)
#define SPI_MODE( x ) ((x) << SPI_MODE_OFFSET)
/* Transfer mode bits */
#define SPI_TMOD_OFFSET 8
#define SPI_TMOD_MASK (0x3 << SPI_TMOD_OFFSET)
#define SPI_TMOD_TR 0x00 /* transmit and receive */
#define SPI_TMOD_TO 0x01 /* transmit only */
#define SPI_TMOD_RO 0x02 /* receive only */
#define SPI_TMOD_EPROMREAD 0x03 /* EEPROM read */
#define SPI_TMOD( x ) ((x) << SPI_TMOD_OFFSET)
/* Slave output enable. */
//#define SPI_SLV_OE_OFFSET 10
//#define SPI_SLV_OE 1
//#define SPI_SLV( x ) ((x) << SPI_SLV_OE_OFFSET)
#define SPI_SRL_OFFSET 11
#define SPI_SRL( x ) ((x) << SPI_SRL_OFFSET)
//#define SPI_CFS_OFFSET 12
/* End CTRLR0 */
/* Bit fields in SR */
#define SPI_SR_DCOL BIT( 6 )
#define SPI_SR_TXE BIT( 5 )
#define SPI_SR_RFF BIT( 4 )
#define SPI_SR_RFNE BIT( 3 )
#define SPI_SR_TFE BIT( 2 )
#define SPI_SR_TFNF BIT( 1 )
#define SPI_SR_BUSY BIT( 0 )
/* xfer errcode in interrupt mode */
#define SPI_XFER_OK 0
#define SPI_XFER_TIMEO 1
#define SPI_XFER_FIFO 2
typedef struct {
uint64_t pbase; /* the physical base address of GPIO port used as chip select */
uintptr_t vbase; /* the virtual base address of GPIO port used as chip select */
#define GPIOCS_EN (1 << 30) /* GPIO based chip selection is enabled */
#define GPIOCS_CSHOLD (1 << 29) /* SPI_MODE_CSHOLD_HIGH is set by spi_setcfg */
#define MSK_GPIO 0x0000001f /* get only the GPIO number used for chip selection */
uint32_t gpio; /* combination of the GPIO number & flags mentioned previously */
} gpio_cs_t;
typedef struct {
SPIDEV spi; /* This has to be the first element */
uint64_t pbase;
uintptr_t vbase;
uint32_t irq; /* interrupt vector to use in InterruptAttach() calls */
int iid; /* 'id' returned from InterruptAttach() */
int chid; /* 'id' returned from ChannelCreate() */
int coid; /* 'id' returned from ConnectAttach() */
uint32_t clock;
volatile uint8_t *pbuf;
int xlen, tlen, rlen;
int dlen;
int dtime; /* usec per data, for time out use */
uint32_t loopback; /* loopback mode */
uint32_t poll; /* poll mode */
gpio_cs_t gpiocs;
struct sigevent spievent;
} baikal_spi_dev_t;
static inline uint32_t baikal_readl( baikal_spi_dev_t * dev, volatile uint32_t offset )
{
return inle32( dev->vbase + offset );
}
static inline void baikal_writel( baikal_spi_dev_t * dev, uint32_t offset, uint32_t value )
{
outle32( dev->vbase + offset, value );
}
/* Disable IRQ bits */
static inline void spi_mask_intr( baikal_spi_dev_t * dev, uint32_t mask )
{
uint32_t new_mask;
new_mask = baikal_readl( dev, SPI_IMR ) & ~mask;
baikal_writel( dev, SPI_IMR, new_mask );
}
/* Enable IRQ bits */
static inline void spi_umask_intr( baikal_spi_dev_t * dev, uint32_t mask )
{
uint32_t new_mask;
new_mask = baikal_readl( dev, SPI_IMR ) | mask;
baikal_writel( dev, SPI_IMR, new_mask );
}
static inline void baikal_spi_enable( baikal_spi_dev_t * dev, int enable )
{
baikal_writel( dev, SPI_SSIENR, (enable ? SPI_ENABLE : SPI_DISABLE));
}
/*
* This does disable the SPI controller, interrupts, and re-enable the
* controller back. Transmit and receive FIFO buffers are cleared when the
* device is disabled.
*/
static inline void baikal_spi_reset( baikal_spi_dev_t * dev )
{
baikal_spi_enable( dev, SPI_DISABLE );
spi_mask_intr( dev, SPI_INT_MASK );
baikal_spi_enable( dev, SPI_ENABLE );
}
extern void *baikal_spi_init( void *hdl, char *options );
extern void baikal_spi_dinit( void *hdl );
extern int baikal_spi_setcfg( void *hdl, uint16_t device, spi_cfg_t *cfg );
extern void *baikal_spi_xfer( void *hdl, uint32_t device, uint8_t *buf, int *len );
extern int baikal_spi_drvinfo( void *hdl, spi_drvinfo_t *info );
extern int baikal_spi_devinfo( void *hdl, uint32_t device, spi_devinfo_t *info );
extern int baikal_spi_attach_intr( baikal_spi_dev_t *dev );
extern int baikal_wait( baikal_spi_dev_t *dev, int len );
extern uint32_t baikal_spi_cfg( void *hdl, spi_cfg_t *cfg );
#endif

440
spi/baikal-t1/baikal-t1.c Normal file
View File

@ -0,0 +1,440 @@
/*
* (c) 2019, SWD Embedded Systems Limited, http://www.kpda.ru
*
* (c) 2014 Baikal Electronics. Based on U-Boot code from Baikal Electronics.
*/
/*
* Board: Baikal-T1 CPC313
* Interface: SPI
* Initialize, configure and transfer
*/
#include "baikal-t1-spi.h"
enum opt_index { BASE, IRQ, CLOCK, LOOPBACK, GPIOCSBASE, GPIOCS, POLL, END };
static char *baikal_opts[] = {
[BASE] = "base", /* Base address for this SPI controller */
[IRQ] = "irq", /* IRQ for this SPI intereface */
[CLOCK] = "clock", /* SPI clock */
[LOOPBACK] = "loopback", /* loopback interface for test purposes*/
[GPIOCSBASE] = "gpiobase", /* gpio base address of bank of gpios used as
* chipselects.
* if SPI_MODE_CSHOLD_HIGH is to be used the corresponding
* chipselect should be configured as gpio
*/
[GPIOCS] = "gpiocs", /* gpio pin number to be used as chipselect */
[POLL] = "poll",
[END] = NULL
};
spi_funcs_t spi_drv_entry = {
sizeof( spi_funcs_t ),
baikal_spi_init, /* init() */
baikal_spi_dinit, /* fini() */
baikal_spi_drvinfo, /* drvinfo() */
baikal_spi_devinfo, /* devinfo() */
baikal_spi_setcfg, /* setcfg() */
baikal_spi_xfer, /* xfer() */
NULL /* dma_xfer() */
};
static spi_devinfo_t devlist[1] = {
{
0x00, /* Device ID */
"SPI-DEV0", /* Description */
{
SPI_MODE_CKPHASE_HALF |
SPI_MODE_CSPOL_HIGH |
8, /* data length 8 bit */
1000000 /* Clock rate */
},
},
};
static int baikal_spi_options( baikal_spi_dev_t *dev, char *optstring )
{
int opt, rc = 0;
char *options, *freeptr, *c, *value;
if ( optstring == NULL ) {
return 0;
}
freeptr = options = strdup( optstring );
while ( options && *options != '\0' ) {
c = options;
if ( (opt = getsubopt( &options, baikal_opts, &value )) == -1 ) {
goto error;
}
switch ( opt ) {
case BASE:
dev->pbase = strtoull( value, 0, 0 );
continue;
case IRQ:
dev->irq = strtoul( value, 0, 0 );
continue;
case CLOCK:
dev->clock = strtoul( value, 0, 0 );
continue;
case LOOPBACK:
dev->loopback = value ? strtoul( value, 0, 0 ) : 1;
continue;
case GPIOCSBASE:
dev->gpiocs.pbase = strtoul( value, 0, 0 );
continue;
case GPIOCS:
dev->gpiocs.gpio = strtoull( value, 0, 0 ) | GPIOCS_EN;
continue;
case POLL:
dev->poll = strtoul( value, 0, 0 );
continue;
}
error:
fprintf( stderr, "%s: unknown option %s", __FUNCTION__, c );
rc = -1;
}
free( freeptr );
return rc;
}
/*
* baikal_spi_set_fifo_depth function set it to 0x3f again.
* NOTE: fifo threshold after reset 0x3f.
*/
void baikal_spi_set_fifo_depth( baikal_spi_dev_t * dev ) {
uint32_t fifo;
baikal_spi_enable( dev, SPI_DISABLE );
for ( fifo = 1; fifo <= BAIKAL_SPI_FIFO_LEN; fifo++ ) {
baikal_writel( dev, SPI_TXFTLR, fifo );
if ( fifo != baikal_readl( dev, SPI_TXFTLR ) ) {
break;
}
}
baikal_spi_enable( dev, SPI_ENABLE );
fifo = baikal_readl( dev, SPI_TXFTLR );
}
void baikal_gpio_cs_reset( baikal_spi_dev_t *dev )
{
uint32_t gpio_bit = BIT( dev->gpiocs.gpio & MSK_GPIO );
uint32_t gpio = inle32( dev->gpiocs.vbase + GPIO_DR );
gpio |= gpio_bit;
outle32( dev->gpiocs.vbase + GPIO_DR, gpio );
outle32( dev->gpiocs.vbase + GPIO_DDR, gpio );
gpio &= ~gpio_bit;
outle32( dev->gpiocs.vbase + GPIO_DR, gpio );
outle32( dev->gpiocs.vbase + GPIO_DDR, gpio );
}
void baikal_gpio_chip_select( baikal_spi_dev_t *dev, uint8_t dir, uint8_t level )
{
uint32_t gpio_bit = BIT( dev->gpiocs.gpio & MSK_GPIO );
uint32_t gpio_dr = inle32( dev->gpiocs.vbase + GPIO_DR ); /* Data Register */
uint32_t gpio_ddr = inle32( dev->gpiocs.vbase + GPIO_DDR ); /* Data Directrion Register */
if ( dir )
gpio_ddr |= gpio_bit; /* Output Directrion */
else
gpio_ddr &= ~gpio_bit; /* Input Directrion */
/* GPIO Chip Select logic is inverted */
if ( !level )
gpio_dr |= gpio_bit;
else
gpio_dr &= ~gpio_bit;
outle32( dev->gpiocs.vbase + GPIO_DR, gpio_dr );
outle32( dev->gpiocs.vbase + GPIO_DDR, gpio_ddr );
}
void *baikal_spi_init( void *hdl, char *options )
{
baikal_spi_dev_t *dev;
uintptr_t base;
dev = calloc( 1, sizeof( baikal_spi_dev_t ) );
if ( dev == NULL )
return ( NULL );
dev->pbase = BAIKAL_SPI1_BASE;
dev->irq = BAIKAL_SPI1_IRQ;
dev->clock = BAIKAL_SPI_BUS_FREQ; /* 50 MHz */
dev->poll = 0;
/* GPIO Base Address and Pin Number set in baikal_spi_options() */
// dev->gpiocs.pbase = BAIKAL_GPIO_BASE;
// dev->gpiocs.gpio = 24 | GPIOCS_EN;
baikal_spi_options( dev, options );
if ( (base = mmap_device_io( BAIKAL_SPI_SIZE, dev->pbase )) == (uintptr_t)MAP_FAILED ) {
/* error */
goto fail0;
}
dev->vbase = base;
if ( dev->gpiocs.gpio & GPIOCS_EN ) {
if ( (base = mmap_device_io( BAIKAL_GPIO_SIZE, dev->gpiocs.pbase )) == ( uintptr_t ) MAP_FAILED ) {
/* error */
goto fail1;
}
dev->gpiocs.vbase = base;
}
baikal_spi_reset( dev );
// baikal_spi_set_fifo_depth( dev );
/* Set default data format */
baikal_spi_setcfg( dev, 0, &devlist[0].cfg );
if ( dev->poll == 0 ) {
/* Attach SPI interrupt */
if ( baikal_spi_attach_intr( dev ) ) {
goto fail2;
}
}
dev->spi.hdl = hdl;
return ( dev );
fail2:
if ( dev->gpiocs.vbase ) {
munmap_device_io( dev->gpiocs.vbase, BAIKAL_GPIO_SIZE );
}
fail1:
munmap_device_io( dev->vbase, BAIKAL_SPI_SIZE );
fail0:
free( dev );
return ( NULL );
}
void baikal_spi_dinit( void *hdl )
{
baikal_spi_dev_t *dev = hdl;
/* Detach the interrupt */
InterruptDetach( dev->iid );
ConnectDetach( dev->coid );
ChannelDestroy( dev->chid );
/* Disable SPI */
baikal_spi_enable( dev, SPI_DISABLE );
if ( dev->gpiocs.vbase != (uintptr_t)MAP_FAILED )
munmap_device_io( dev->gpiocs.vbase, BAIKAL_GPIO_SIZE );
/* Unmap the register */
munmap_device_io( dev->vbase, BAIKAL_SPI_SIZE );
free( hdl );
}
int baikal_spi_drvinfo(void *hdl, spi_drvinfo_t *info)
{
info->version = ( SPI_VERSION_MAJOR << SPI_VERMAJOR_SHIFT ) |
( SPI_VERSION_MINOR << SPI_VERMINOR_SHIFT ) |
( SPI_REVISION << SPI_VERREV_SHIFT );
strcpy( info->name, "Baikal-T1 SPI" );
info->feature = 0;
return ( EOK );
}
int baikal_spi_setcfg( void *hdl, uint16_t device, spi_cfg_t *cfg )
{
baikal_spi_dev_t *dev = hdl;
uint32_t ctrl0 = 0;
/* We have only 4 Chip Select lines */
if ( device > 0x4 )
return ( EINVAL );
devlist[0].device = device;
memcpy( &devlist[0].cfg, cfg, sizeof( spi_cfg_t ) );
if ( (ctrl0 = baikal_spi_cfg( hdl, &devlist[0].cfg )) == 0 )
return ( EINVAL );
baikal_spi_enable( dev, SPI_DISABLE ); /* Disable controller */
baikal_writel( dev, SPI_CTRLR0, ctrl0 ); /* Write cfg */
baikal_spi_enable( dev, SPI_ENABLE ); /* Enable controller */
/* NOTE: slave select (SS) polarity, 0 = active low, 1 = active high
* NOTE: SCLK and SS polarity bit settings are different */
if ( cfg->mode & SPI_MODE_CSHOLD_HIGH ) {
baikal_writel( dev, SPI_SER, BIT( device ) );
}
else baikal_writel( dev, SPI_SER, 0x0 );
/* GPIO CS */
if ( dev->gpiocs.vbase && ( dev->gpiocs.gpio & GPIOCS_EN ) )
{
if ( cfg->mode & SPI_MODE_CSHOLD_HIGH ) /* always keep the SSx assert */
{
dev->gpiocs.gpio |= GPIOCS_CSHOLD;
if ( cfg->mode & SPI_MODE_CSPOL_HIGH )
{
dev->gpiocs.gpio |= SPI_MODE_CSPOL_HIGH;
baikal_gpio_chip_select( dev, GPIO_DDR_OUTPUT, GPIO_DR_HIGH ); /* level high */
}
else {
dev->gpiocs.gpio &= ~SPI_MODE_CSPOL_HIGH;
baikal_gpio_chip_select( dev, GPIO_DDR_OUTPUT, GPIO_DR_LOW ); /* level low */
}
}
else {
dev->gpiocs.gpio &= ~GPIOCS_CSHOLD;
if ( cfg->mode & SPI_MODE_CSPOL_HIGH )
{
dev->gpiocs.gpio |= SPI_MODE_CSPOL_HIGH;
// baikal_gpio_chip_select( dev, GPIO_DDR_INPUT, GPIO_DR_LOW ); /* level low */
}
else {
dev->gpiocs.gpio &= ~SPI_MODE_CSPOL_HIGH;
// baikal_gpio_chip_select( dev, GPIO_DDR_INPUT, GPIO_DR_HIGH ); /* level high */
}
}
}
return ( EOK );
}
int baikal_spi_devinfo( void *hdl, uint32_t device, spi_devinfo_t *info )
{
memcpy( info, &devlist[0], sizeof( spi_devinfo_t ) );
return ( EOK );
}
void *baikal_spi_xfer( void *hdl, uint32_t device, uint8_t *buf, int *len )
{
baikal_spi_dev_t *dev = hdl;
uint32_t id;
uint32_t clk_div;
uint8_t level; /* chip select level high/low */
id = device & SPI_DEV_ID_MASK;
if ( id > 4 ) {
*len = -1;
return buf;
}
dev->tlen = 0;
dev->rlen = 0;
dev->xlen = *len;
dev->pbuf = buf;
dev->dlen = ((devlist[0].cfg.mode & 0xF) + 7) >> 3;
dev->dtime = 1 + dev->dlen * 8 * 1000 * 1000 / devlist[0].cfg.clock_rate;
if ( dev->xlen < dev->dlen ) {
*len = -1;
fprintf( stderr, "%s: Unexpected exchange data length %d (word length is %d)\n", __FUNCTION__, dev->xlen, dev->dlen );
}
baikal_spi_enable( dev, SPI_DISABLE );
/* Configure SPI Clock rate */
clk_div = (dev->clock / devlist[0].cfg.clock_rate) + 1;
clk_div &= 0xFFFE;
if ( clk_div != baikal_readl( dev, SPI_BAUDR ) )
baikal_writel( dev, SPI_BAUDR, clk_div );
/* Enable Controller */
baikal_spi_enable( dev, SPI_ENABLE );
/* CHIP SELECT */
level = (dev->gpiocs.gpio & SPI_MODE_CSPOL_HIGH) ? GPIO_DR_HIGH : GPIO_DR_LOW;
// if ( Chip Select Hold = Off )
if ( 0 == (dev->gpiocs.gpio & GPIOCS_CSHOLD) ) {
if ( dev->gpiocs.vbase && (dev->gpiocs.gpio & GPIOCS_EN) )
baikal_gpio_chip_select( dev, GPIO_DDR_OUTPUT, level );
}
baikal_writel( dev, SPI_SER, BIT( device ) );
if ( dev->poll ) {
/* poll mode */
uint32_t data;
while ( dev->rlen < dev->xlen ) {
if ( dev->tlen < dev->xlen ) {
switch ( dev->dlen ) {
case 1:
data = dev->pbuf[dev->tlen];
break;
case 2:
data = *(uint16_t *)(&dev->pbuf[dev->tlen]);
break;
default:
fprintf( stderr, "%s: Unsupported word length\n", __FUNCTION__ );
*len = -1;
goto fail;
break;
}
baikal_writel( dev, SPI_DR0, data );
dev->tlen += dev->dlen;
}
if ( baikal_readl( dev, SPI_SR ) & SPI_SR_RFNE ) {
data = baikal_readl( dev, SPI_DR0 );
switch ( dev->dlen ) {
case 1:
dev->pbuf[dev->rlen] = data;
break;
case 2:
*(uint16_t *)(&dev->pbuf[dev->rlen]) = data;
break;
default:
fprintf( stderr, "%s: Unsupported word length\n", __FUNCTION__ );
*len = -1;
goto fail;
break;
}
dev->rlen += dev->dlen;
}
} /* while */
} else {
/* interrupt mode */
spi_umask_intr( dev, SPI_INT_MASK );
switch ( baikal_wait( dev, dev->xlen ) ) {
case SPI_XFER_OK:
break;
case SPI_XFER_FIFO:
baikal_spi_reset( dev );
fprintf( stderr, "%s: fifo overrun/underrun\n", __FUNCTION__ );
dev->rlen = -1;
break;
case SPI_XFER_TIMEO:
fprintf( stderr, "%s: timeout\n", __FUNCTION__ );
dev->rlen = -1;
break;
default:
fprintf( stderr, "%s: unknown error\n", __FUNCTION__ );
dev->rlen = -1;
break;
}
}
*len = dev->rlen;
if ( 0 == ( dev->gpiocs.gpio & GPIOCS_CSHOLD ) ) {
if ( dev->gpiocs.vbase && (dev->gpiocs.gpio & GPIOCS_EN) )
baikal_gpio_chip_select( dev, GPIO_DDR_OUTPUT, !level );
}
baikal_writel( dev, SPI_SER, 0x0 );
fail:
return buf;
}

24
spi/baikal-t1/common.mk Normal file
View File

@ -0,0 +1,24 @@
ifndef QCONFIG
QCONFIG=qconfig.mk
endif
include $(QCONFIG)
include $(MKFILES_ROOT)/qmacros.mk
NAME := spi-$(NAME)
USEFILE=$(PROJECT_ROOT)/$(NAME).use
EXTRA_SILENT_VARIANTS+=$(SECTION)
define PINFO
PINFO DESCRIPTION=
endef
include $(PROJECT_ROOT)/pinfo.mk
LDVFLAG_dll= -L.
#####AUTO-GENERATED by packaging script... do not checkin#####
INSTALL_ROOT_nto = $(PROJECT_ROOT)/../../../../install
USE_INSTALL_ROOT=1
##############################################################
include $(MKFILES_ROOT)/qtargets.mk
-include $(PROJECT_ROOT)/roots.mk

52
spi/baikal-t1/config.c Normal file
View File

@ -0,0 +1,52 @@
/*
* (c) 2019, SWD Embedded Systems Limited, http://www.kpda.ru
*/
/*
* Board: Baikal-T1 CPC313
* Interface: SPI
* Parse spi_cfg_t for interface configuring
*/
#include "baikal-t1-spi.h"
uint32_t baikal_spi_cfg( void *hdl, spi_cfg_t *cfg )
{
baikal_spi_dev_t *dev = hdl;
uint32_t spi_ctrl = 0;
uint8_t len;
if ( cfg == NULL || dev == NULL ) {
return 0;
}
len = cfg->mode & SPI_MODE_CHAR_LEN_MASK;
/* NOTE: len valid values [4..16] */
if ( len < 4 || len > 16 )
return 0;
/* if SPI_MODE_CSHOLD_HIGH is to be used the corresponding
* chipselect should be configured as gpio */
if ( (cfg->mode & SPI_MODE_CSHOLD_HIGH) && !dev->gpiocs.vbase )
return 0;
if ( dev->loopback )
spi_ctrl |= SPI_SRL( 1 ); /* Enable Test Mode */
spi_ctrl |= SPI_DFS( len );
spi_ctrl |= SPI_FRF( SPI_FRF_SPI ); /* Frame Format: Motorolla SPI */
/* set SCLK polarity, CPOL: assume SPI_MODE_CKPOL_HIGH same as CPOL */
if ( cfg->mode & SPI_MODE_CKPOL_HIGH )
spi_ctrl |= SPI_SCPOL( SPI_SCPOL_HIGH ); /* set CPOL=1 */
else
spi_ctrl |= SPI_SCPOL( SPI_SCPOL_LOW ); /* set CPOL=0 */
/* set SCLK phase, CPHA: assume SPI_MODE_CKPHASE_HALF same as CPHA */
if ( cfg->mode & SPI_MODE_CKPHASE_HALF )
spi_ctrl |= SPI_SCPH( SPI_SCPH_MIDDLE ); /* set CPHA=0 */
else
spi_ctrl |= SPI_SCPH( SPI_SCPH_START ); /* set CPHA=1 */
return spi_ctrl;
}

107
spi/baikal-t1/intr.c Normal file
View File

@ -0,0 +1,107 @@
/*
* (c) 2019, SWD Embedded Systems Limited, http://www.kpda.ru
*/
/*
* Board: Baikal-T1 CPC313
* Interface: SPI
* Attach Interrupt and Interrupt handler functions
*/
#include "baikal-t1-spi.h"
/*
* We use the same buffer for transmit and receive
* For exchange, that's exactly what we wanted
* For Read, it doesn't matter what we write to SPI, so we are OK.
* For transmit, the receive data is put at the buffer we just transmitted, we are still OK.
*/
static const struct sigevent *spi_intr( void *area, int id )
{
baikal_spi_dev_t *dev = area;
uintptr_t base = dev->vbase;
volatile uint32_t data;
volatile uint32_t isr;
isr = inle32( base + SPI_ISR );
if ( isr & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI) ) {
/* Disable Interrupts */
outle32( base + SPI_IMR, 0x0 );
/* Clear Interrupts */
inle32( base + SPI_ICR );
dev->spievent.sigev_value.sival_int = SPI_XFER_FIFO;
return (&dev->spievent);
}
if ( isr & SPI_INT_RXFI ) {
data = inle32( base + SPI_DR0 );
if ( dev->rlen <= dev->xlen ) {
switch ( dev->dlen ) {
case 1:
dev->pbuf[dev->rlen] = data;
break;
case 2:
*(uint16_t *)(&dev->pbuf[dev->rlen]) = data;
break;
}
dev->rlen += dev->dlen;
}
}
if ( isr & SPI_INT_TXEI ) {
/* More to transmit? */
if ( dev->tlen < dev->xlen ) {
switch ( dev->dlen ) {
case 1:
data = dev->pbuf[dev->tlen];
break;
case 2:
data = *(uint16_t *)(&dev->pbuf[dev->tlen]);
break;
}
outle32( base + SPI_DR0, data );
dev->tlen += dev->dlen;
}
}
if ( dev->rlen >= dev->xlen ) {
/* Disable interrupt and return spievent */
outle32( base + SPI_IMR, 0x0 );
dev->spievent.sigev_value.sival_int = SPI_XFER_OK;
return (&dev->spievent);
}
return NULL;
}
int baikal_spi_attach_intr( baikal_spi_dev_t *dev )
{
if ( (dev->chid = ChannelCreate( _NTO_CHF_DISCONNECT | _NTO_CHF_UNBLOCK )) == -1 )
return (-1);
if ( (dev->coid = ConnectAttach( 0, 0, dev->chid, _NTO_SIDE_CHANNEL, 0 )) == -1 )
goto fail0;
dev->spievent.sigev_notify = SIGEV_PULSE;
dev->spievent.sigev_coid = dev->coid;
dev->spievent.sigev_code = BAIKAL_SPI_EVENT;
dev->spievent.sigev_priority = BAIKAL_SPI_PRIORITY;
/* Attach SPI interrupt */
dev->iid = InterruptAttach( dev->irq, spi_intr, dev, 0, _NTO_INTR_FLAGS_TRK_MSK );
if ( dev->iid != -1 )
return (0);
ConnectDetach( dev->coid );
fail0:
ChannelDestroy( dev->chid );
return (-1);
}

View File

@ -0,0 +1,8 @@
LIST=VARIANT
ifndef QRECURSE
QRECURSE=recurse.mk
ifdef QCONFIG
QRDIR=$(dir $(QCONFIG))
endif
endif
include $(QRDIR)$(QRECURSE)

View File

@ -0,0 +1,52 @@
/*
* (c) 2019, SWD Embedded Systems Limited, http://www.kpda.ru
*/
/*
* Baikal T1 interrupt number definitions
*/
#ifndef __MIPS_BAIKALT1_INTR_H_INCLUDED
#define __MIPS_BAIKALT1_INTR_H_INCLUDED
#include <mips/intr.h>
#define MIPS_BT1_INTR_CLASS (_NTO_INTR_CLASS_EXTERNAL + 0x100)
#define MIPS_BT1_MSI_CLASS (_NTO_INTR_CLASS_EXTERNAL + 100)
#define BT1_INTR_IPI0 (MIPS_BT1_INTR_CLASS + 0)
#define BT1_INTR_IPI15 (MIPS_BT1_INTR_CLASS + 15)
#define BT1_INTR_APB (MIPS_BT1_INTR_CLASS + 16)
#define BT1_INTR_WDT (MIPS_BT1_INTR_CLASS + 17)
#define BT1_INTR_GPIO (MIPS_BT1_INTR_CLASS + 19)
#define BT1_INTR_TMR0 (MIPS_BT1_INTR_CLASS + 24)
#define BT1_INTR_TMR1 (MIPS_BT1_INTR_CLASS + 25)
#define BT1_INTR_TMR2 (MIPS_BT1_INTR_CLASS + 26)
#define BT1_INTR_PVT (MIPS_BT1_INTR_CLASS + 31)
#define BT1_INTR_I2C0 (MIPS_BT1_INTR_CLASS + 32)
#define BT1_INTR_I2C1 (MIPS_BT1_INTR_CLASS + 33)
#define BT1_INTR_I2C2 (MIPS_BT1_INTR_CLASS + 34)
#define BT1_INTR_SPI0 (MIPS_BT1_INTR_CLASS + 40)
#define BT1_INTR_SPI1 (MIPS_BT1_INTR_CLASS + 41)
#define BT1_INTR_UART0 (MIPS_BT1_INTR_CLASS + 48)
#define BT1_INTR_UART1 (MIPS_BT1_INTR_CLASS + 49)
#define BT1_INTR_DMAC (MIPS_BT1_INTR_CLASS + 56)
#define BT1_INTR_SATA (MIPS_BT1_INTR_CLASS + 64)
#define BT1_INTR_USB (MIPS_BT1_INTR_CLASS + 68)
#define BT1_INTR_ETH_GMAC0 (MIPS_BT1_INTR_CLASS + 72)
#define BT1_INTR_ETH_GMAC1 (MIPS_BT1_INTR_CLASS + 73)
#define BT1_INTR_XGMAC (MIPS_BT1_INTR_CLASS + 74)
#define BT1_INTR_XGMAC_TX0 (MIPS_BT1_INTR_CLASS + 75)
#define BT1_INTR_XGMAC_TX1 (MIPS_BT1_INTR_CLASS + 76)
#define BT1_INTR_XGMAC_RX0 (MIPS_BT1_INTR_CLASS + 77)
#define BT1_INTR_XGMAC_RX1 (MIPS_BT1_INTR_CLASS + 78)
#define BT1_INTR_XPCS (MIPS_BT1_INTR_CLASS + 79)
#define BT1_INTR_PCIE_EDMA0 (MIPS_BT1_INTR_CLASS + 80)
#define BT1_INTR_PCIE_MSI (MIPS_BT1_INTR_CLASS + 88)
#define BT1_INTR_PCIE_AER (MIPS_BT1_INTR_CLASS + 89)
#define BT1_INTR_PCIE_PME (MIPS_BT1_INTR_CLASS + 90)
#define BT1_INTR_PCIE_HP (MIPS_BT1_INTR_CLASS + 91)
#define BT1_INTR_PCIE_BW (MIPS_BT1_INTR_CLASS + 92)
#define BT1_INTR_PCIE_L_REQ (MIPS_BT1_INTR_CLASS + 93)
#endif

View File

@ -0,0 +1 @@
include ../../common.mk

41
spi/baikal-t1/module.tmpl Normal file
View File

@ -0,0 +1,41 @@
<?xml version="1.0"?>
<module name="spi-baikal-t1">
<type>Element</type>
<classification>Driver</classification>
<description>
<short>Baikal-T1 SPI Driver</short>
<abstract>
<![CDATA[The Serial Protocol Interface on the Baikal-T1 is protocol compatible
with the standard Serial Protocol Interface (SPI). The Baikal-T1
support for the SPI (spi-baikal) supports Full Duplex communication in
Master mode. The SPI provides serial communications between the Baikal-T1
CPU core and peripheral devices.]]>
</abstract>
</description>
<supports>
<availability>
<cpu isa="mips">
<byteOrder>le</byteOrder>
</cpu>
</availability>
</supports>
<source available="false">
<location type="">.</location>
</source>
<GroupOwner>hw</GroupOwner>
<contents>
<component id="spi-baikal-t1" generated="true">
<location basedir="{cpu}/dll{.:endian}"
runtime="true">spi-baikal-t1.so</location>
</component>
</contents>
<requires>
<part build="true" location="lib/spi"/>
<part build="false" location="hardware/spi"/>
</requires>
</module>

3
spi/baikal-t1/pinfo.mk Normal file
View File

@ -0,0 +1,3 @@
define PINFO
PINFO DESCRIPTION=Baikal-T1 SPI driver
endef

View File

@ -0,0 +1,19 @@
%C Driver for Baikal-T1 SPI controller
Syntax:
spi-master -d baikal-t1 [option[,option ...]] ... &
Options (to override autodetected defaults):
base=address Base address of SPI controller, default 0x1F04E000
irq=num IRQ of the interface, default 0x128
clock=num SPI clock, default 50MHz
loopback=num Set internal loopback for test, default 0
(loopback disabled)
poll=num Enable poll mode, default 0 ( interrupt mode )
gpiobase=address GPIO Base address for GPIO ChipSelect pin
gpiocs=num GPIO pin number for CS pin
Example:
# Start SPI driver with base address, IRQ and GPIO ChipSelect pin 24
spi-master -d baikal-t1 base=0x1F04E000,irq=0x128,gpiobase=0x1F044000,gpiocs=24

34
spi/baikal-t1/wait.c Normal file
View File

@ -0,0 +1,34 @@
/*
* (c) 2019, SWD Embedded Systems Limited, http://www.kpda.ru
*/
/*
* Board: Baikal-T1 CPC313
* Interface: SPI
* Wait event by Interrupt handler
*/
#include "baikal-t1-spi.h"
int baikal_wait( baikal_spi_dev_t *dev, int len )
{
struct _pulse pulse;
while ( 1 ) {
if ( len ) {
uint64_t to = dev->dtime;
to *= len * 1000 * 50; /* 50 times for time out */
TimerTimeout( CLOCK_REALTIME, _NTO_TIMEOUT_RECEIVE, NULL, &to, NULL );
}
if ( MsgReceivePulse( dev->chid, &pulse, sizeof( pulse ), NULL ) == -1 )
return (SPI_XFER_TIMEO);
if ( pulse.code == BAIKAL_SPI_EVENT ){
// fprintf( stderr, "pulse.value = %x\n", pulse.value );
return (pulse.value.sival_int);
}
}
return (0);
}