spi_at91.c
字号:
//==========================================================================
//
// spi_at91.c
//
// Atmel AT91 (ARM) SPI driver
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): Savin Zlobec <savin@elatec.si>
// Date: 2004-08-25
//
//####DESCRIPTIONEND####
//
//==========================================================================
#include <pkgconf/hal.h>
#include <pkgconf/io_spi.h>
#include <pkgconf/devs_spi_arm_at91.h>
#include <cyg/infra/cyg_type.h>
#include <cyg/infra/cyg_ass.h>
#include <cyg/hal/hal_io.h>
#include <cyg/hal/hal_if.h>
#include <cyg/hal/hal_intr.h>
#include <cyg/hal/drv_api.h>
#include <cyg/io/spi.h>
#include <cyg/io/spi_at91.h>
#include <cyg/error/codes.h>
// -------------------------------------------------------------------------
static void spi_at91_init_bus(cyg_spi_at91_bus_t * bus);
static cyg_uint32 spi_at91_ISR(cyg_vector_t vector, cyg_addrword_t data);
static void spi_at91_DSR(cyg_vector_t vector,
cyg_ucount32 count,
cyg_addrword_t data);
static void spi_at91_transaction_begin(cyg_spi_device *dev);
static void spi_at91_transaction_transfer(cyg_spi_device *dev,
cyg_bool polled,
cyg_uint32 count,
const cyg_uint8 *tx_data,
cyg_uint8 *rx_data,
cyg_bool drop_cs);
static void spi_at91_transaction_tick(cyg_spi_device *dev,
cyg_bool polled,
cyg_uint32 count);
static void spi_at91_transaction_end(cyg_spi_device* dev);
static int spi_at91_get_config(cyg_spi_device *dev,
cyg_uint32 key,
void *buf,
cyg_uint32 *len);
static int spi_at91_set_config(cyg_spi_device *dev,
cyg_uint32 key,
const void *buf,
cyg_uint32 *len);
// -------------------------------------------------------------------------
// AT91 SPI BUS
#ifdef CYGHWR_DEVS_SPI_ARM_AT91_BUS0
cyg_spi_at91_bus_t cyg_spi_at91_bus0 = {
.spi_bus.spi_transaction_begin = spi_at91_transaction_begin,
.spi_bus.spi_transaction_transfer = spi_at91_transaction_transfer,
.spi_bus.spi_transaction_tick = spi_at91_transaction_tick,
.spi_bus.spi_transaction_end = spi_at91_transaction_end,
.spi_bus.spi_get_config = spi_at91_get_config,
.spi_bus.spi_set_config = spi_at91_set_config,
.interrupt_number = CYGNUM_HAL_INTERRUPT_SPI,
.base = AT91_SPI,
#ifndef CYGDAT_DEVS_SPI_ARM_AT91_BUS0_NPCS0_NONE
.cs_en[0] = true,
.cs_gpio[0] = CYGDAT_DEVS_SPI_ARM_AT91_BUS0_NPCS0,
#else
.cs_en[0] = false,
#endif
#ifndef CYGDAT_DEVS_SPI_ARM_AT91_BUS0_NPCS1_NONE
.cs_en[1] = true,
.cs_gpio[1] = CYGDAT_DEVS_SPI_ARM_AT91_BUS0_NPCS1,
#else
.cs_en[1] = false,
#endif
#ifndef CYGDAT_DEVS_SPI_ARM_AT91_BUS0_NPCS2_NONE
.cs_en[2] = true,
.cs_gpio[2] = CYGDAT_DEVS_SPI_ARM_AT91_BUS0_NPCS2,
#else
.cs_en[2] = false,
#endif
#ifndef CYGDAT_DEVS_SPI_ARM_AT91_BUS0_NPCS3_NONE
.cs_en[3] = true,
.cs_gpio[3] = CYGDAT_DEVS_SPI_ARM_AT91_BUS0_NPCS3,
#else
.cs_en[3] = false,
#endif
};
CYG_SPI_DEFINE_BUS_TABLE(cyg_spi_at91_device_t, 0);
#endif
#ifdef CYGHWR_DEVS_SPI_ARM_AT91_BUS1
cyg_spi_at91_bus_t cyg_spi_at91_bus1 = {
.spi_bus.spi_transaction_begin = spi_at91_transaction_begin,
.spi_bus.spi_transaction_transfer = spi_at91_transaction_transfer,
.spi_bus.spi_transaction_tick = spi_at91_transaction_tick,
.spi_bus.spi_transaction_end = spi_at91_transaction_end,
.spi_bus.spi_get_config = spi_at91_get_config,
.spi_bus.spi_set_config = spi_at91_set_config,
.interrupt_number = CYGNUM_HAL_INTERRUPT_SPI1,
.base = AT91_SPI1,
#ifndef CYGDAT_DEVS_SPI_ARM_AT91_BUS1_NPCS0_NONE
.cs_en[0] = true,
.cs_gpio[0] = CYGDAT_DEVS_SPI_ARM_AT91_BUS1_NPCS0,
#else
.cs_en[0] = false,
#endif
#ifndef CYGDAT_DEVS_SPI_ARM_AT91_BUS1_NPCS1_NONE
.cs_en[1] = true,
.cs_gpio[1] = CYGDAT_DEVS_SPI_ARM_AT91_BUS1_NPCS1,
#else
.cs_en[1] = false,
#endif
#ifndef CYGDAT_DEVS_SPI_ARM_AT91_BUS1_NPCS2_NONE
.cs_en[2] = true,
.cs_gpio[2] = CYGDAT_DEVS_SPI_ARM_AT91_BUS1_NPCS2,
#else
.cs_en[2] = false,
#endif
#ifndef CYGDAT_DEVS_SPI_ARM_AT91_BUS1_NPCS3_NONE
.cs_en[3] = true,
.cs_gpio[3] = CYGDAT_DEVS_SPI_ARM_AT91_BUS1_NPCS3,
#else
.cs_en[3] = false,
#endif
};
CYG_SPI_DEFINE_BUS_TABLE(cyg_spi_at91_device_t, 1);
#endif
// -------------------------------------------------------------------------
void
cyg_spi_at91_bus_init(void)
{
#ifdef CYGHWR_DEVS_SPI_ARM_AT91_BUS0
// NOTE: here we let the SPI controller control
// the data in, out and clock signals, but
// we need to handle the chip selects manually
// in order to achieve better chip select control
// in between transactions.
// Put SPI MISO, MOIS and SPCK pins into peripheral mode
HAL_ARM_AT91_PIO_CFG(AT91_SPI_SPCK);
HAL_ARM_AT91_PIO_CFG(AT91_SPI_MISO);
HAL_ARM_AT91_PIO_CFG(AT91_SPI_MOIS);
spi_at91_init_bus(&cyg_spi_at91_bus0);
#endif
#ifdef CYGHWR_DEVS_SPI_ARM_AT91_BUS1
// NOTE: here we let the SPI controller control
// the data in, out and clock signals, but
// we need to handle the chip selects manually
// in order to achieve better chip select control
// in between transactions.
// Put SPI MISO, MOIS and SPCK pins into peripheral mode
HAL_ARM_AT91_PIO_CFG(AT91_SPI1_SPCK);
HAL_ARM_AT91_PIO_CFG(AT91_SPI1_MISO);
HAL_ARM_AT91_PIO_CFG(AT91_SPI1_MOSI);
spi_at91_init_bus(&cyg_spi_at91_bus1);
#endif
}
// -------------------------------------------------------------------------
static void spi_at91_init_bus(cyg_spi_at91_bus_t * spi_bus)
{
cyg_uint32 ctr;
// Create and attach SPI interrupt object
cyg_drv_interrupt_create(spi_bus->interrupt_number,
4,
(cyg_addrword_t)spi_bus,
spi_at91_ISR,
spi_at91_DSR,
&spi_bus->spi_interrupt_handle,
&spi_bus->spi_interrupt);
cyg_drv_interrupt_attach(spi_bus->spi_interrupt_handle);
// Init transfer mutex and condition
cyg_drv_mutex_init(&spi_bus->transfer_mx);
cyg_drv_cond_init(&spi_bus->transfer_cond,
&spi_bus->transfer_mx);
// Init flags
spi_bus->transfer_end = true;
spi_bus->cs_up = false;
// Soft reset the SPI controller
HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_CR, AT91_SPI_CR_SWRST);
// Configure SPI pins
// Put SPI chip select pins in IO output mode
for(ctr = 0;ctr<4;ctr++)
{
if(spi_bus->cs_en[ctr])
{
HAL_ARM_AT91_GPIO_CFG_DIRECTION(spi_bus->cs_gpio[ctr],AT91_PIN_OUT);
}
}
// Call upper layer bus init
CYG_SPI_BUS_COMMON_INIT(&spi_bus->spi_bus);
}
static cyg_uint32
spi_at91_ISR(cyg_vector_t vector, cyg_addrword_t data)
{
cyg_uint32 stat;
cyg_spi_at91_bus_t * spi_bus = (cyg_spi_at91_bus_t *)data;
// Read the status register and disable
// the SPI int events that have occoured
HAL_READ_UINT32(spi_bus->base+AT91_SPI_SR, stat);
HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_IDR, stat);
cyg_drv_interrupt_mask(vector);
cyg_drv_interrupt_acknowledge(vector);
return CYG_ISR_CALL_DSR;
}
static void
spi_at91_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
cyg_spi_at91_bus_t *spi_bus = (cyg_spi_at91_bus_t *) data;
cyg_uint32 stat;
// Read the status register and
// check for transfer completition
HAL_READ_UINT32(spi_bus->base+AT91_SPI_SR, stat);
if((stat & AT91_SPI_SR_ENDRX) && (stat & AT91_SPI_SR_ENDTX))
{
// Transfer ended
spi_bus->transfer_end = true;
cyg_drv_cond_signal(&spi_bus->transfer_cond);
}
else
{
// Transfer still in progress - unmask the SPI
// int so we can get more SPI int events
cyg_drv_interrupt_unmask(vector);
}
}
static cyg_bool
spi_at91_calc_scbr(cyg_spi_at91_device_t *dev)
{
cyg_uint32 scbr;
cyg_bool res = true;
// Calculate SCBR from baud rate
scbr = CYGNUM_HAL_ARM_AT91_CLOCK_SPEED / (2*dev->cl_brate);
if (scbr < 2)
{
dev->cl_scbr = 2;
dev->cl_div32 = 0;
res = false;
}
else if (scbr > 255)
{
dev->cl_div32 = 1;
scbr = CYGNUM_HAL_ARM_AT91_CLOCK_SPEED / (64*dev->cl_brate);
if (scbr < 2)
{
dev->cl_scbr = 2;
res = false;
}
else if (scbr > 255)
{
dev->cl_scbr = 255;
res = false;
}
else
dev->cl_scbr = (cyg_uint8)scbr;
}
else
{
dev->cl_scbr = (cyg_uint8)scbr;
dev->cl_div32 = 0;
}
return res;
}
static void
spi_at91_set_npcs(cyg_spi_at91_bus_t *spi_bus,int val)
{
cyg_uint32 ctr;
for(ctr=0;ctr<4;ctr++)
{
if(spi_bus->cs_en[ctr])
{
HAL_ARM_AT91_GPIO_PUT(spi_bus->cs_gpio[ctr], (val & (1<<ctr)));
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -