@ -4,16 +4,30 @@
* SPDX - License - Identifier : GPL - 2.0 +
*/
# include <common.h>
# include <clk.h>
# include <dm.h>
# include <fdtdec.h>
# include <spi.h>
# include <malloc.h>
# include <wait_bit.h>
# include <asm/io.h>
# include <asm/arch/clk.h>
# include <asm/arch/hardware.h>
# ifdef CONFIG_DM_SPI
# include <asm/arch/at91_spi.h>
# endif
# ifdef CONFIG_DM_GPIO
# include <asm/gpio.h>
# endif
# include "atmel_spi.h"
DECLARE_GLOBAL_DATA_PTR ;
# ifndef CONFIG_DM_SPI
static int spi_has_wdrbt ( struct atmel_spi_slave * slave )
{
unsigned int ver ;
@ -209,3 +223,277 @@ out:
return 0 ;
}
# else
# define MAX_CS_COUNT 4
struct atmel_spi_platdata {
struct at91_spi * regs ;
} ;
struct atmel_spi_priv {
unsigned int freq ; /* Default frequency */
unsigned int mode ;
ulong bus_clk_rate ;
struct gpio_desc cs_gpios [ MAX_CS_COUNT ] ;
} ;
static int atmel_spi_claim_bus ( struct udevice * dev )
{
struct udevice * bus = dev_get_parent ( dev ) ;
struct atmel_spi_platdata * bus_plat = dev_get_platdata ( bus ) ;
struct atmel_spi_priv * priv = dev_get_priv ( bus ) ;
struct dm_spi_slave_platdata * slave_plat = dev_get_parent_platdata ( dev ) ;
struct at91_spi * reg_base = bus_plat - > regs ;
u32 cs = slave_plat - > cs ;
u32 freq = priv - > freq ;
u32 scbr , csrx , mode ;
scbr = ( priv - > bus_clk_rate + freq - 1 ) / freq ;
if ( scbr > ATMEL_SPI_CSRx_SCBR_MAX )
return - EINVAL ;
if ( scbr < 1 )
scbr = 1 ;
csrx = ATMEL_SPI_CSRx_SCBR ( scbr ) ;
csrx | = ATMEL_SPI_CSRx_BITS ( ATMEL_SPI_BITS_8 ) ;
if ( ! ( priv - > mode & SPI_CPHA ) )
csrx | = ATMEL_SPI_CSRx_NCPHA ;
if ( priv - > mode & SPI_CPOL )
csrx | = ATMEL_SPI_CSRx_CPOL ;
writel ( csrx , & reg_base - > csr [ cs ] ) ;
mode = ATMEL_SPI_MR_MSTR |
ATMEL_SPI_MR_MODFDIS |
ATMEL_SPI_MR_WDRBT |
ATMEL_SPI_MR_PCS ( ~ ( 1 < < cs ) ) ;
writel ( mode , & reg_base - > mr ) ;
writel ( ATMEL_SPI_CR_SPIEN , & reg_base - > cr ) ;
return 0 ;
}
static int atmel_spi_release_bus ( struct udevice * dev )
{
struct udevice * bus = dev_get_parent ( dev ) ;
struct atmel_spi_platdata * bus_plat = dev_get_platdata ( bus ) ;
writel ( ATMEL_SPI_CR_SPIDIS , & bus_plat - > regs - > cr ) ;
return 0 ;
}
static void atmel_spi_cs_activate ( struct udevice * dev )
{
struct udevice * bus = dev_get_parent ( dev ) ;
struct atmel_spi_priv * priv = dev_get_priv ( bus ) ;
struct dm_spi_slave_platdata * slave_plat = dev_get_parent_platdata ( dev ) ;
u32 cs = slave_plat - > cs ;
dm_gpio_set_value ( & priv - > cs_gpios [ cs ] , 0 ) ;
}
static void atmel_spi_cs_deactivate ( struct udevice * dev )
{
struct udevice * bus = dev_get_parent ( dev ) ;
struct atmel_spi_priv * priv = dev_get_priv ( bus ) ;
struct dm_spi_slave_platdata * slave_plat = dev_get_parent_platdata ( dev ) ;
u32 cs = slave_plat - > cs ;
dm_gpio_set_value ( & priv - > cs_gpios [ cs ] , 1 ) ;
}
static int atmel_spi_xfer ( struct udevice * dev , unsigned int bitlen ,
const void * dout , void * din , unsigned long flags )
{
struct udevice * bus = dev_get_parent ( dev ) ;
struct atmel_spi_platdata * bus_plat = dev_get_platdata ( bus ) ;
struct at91_spi * reg_base = bus_plat - > regs ;
u32 len_tx , len_rx , len ;
u32 status ;
const u8 * txp = dout ;
u8 * rxp = din ;
u8 value ;
if ( bitlen = = 0 )
goto out ;
/*
* The controller can do non - multiple - of - 8 bit
* transfers , but this driver currently doesn ' t support it .
*
* It ' s also not clear how such transfers are supposed to be
* represented as a stream of bytes . . . this is a limitation of
* the current SPI interface .
*/
if ( bitlen % 8 ) {
/* Errors always terminate an ongoing transfer */
flags | = SPI_XFER_END ;
goto out ;
}
len = bitlen / 8 ;
/*
* The controller can do automatic CS control , but it is
* somewhat quirky , and it doesn ' t really buy us much anyway
* in the context of U - Boot .
*/
if ( flags & SPI_XFER_BEGIN ) {
atmel_spi_cs_activate ( dev ) ;
/*
* sometimes the RDR is not empty when we get here ,
* in theory that should not happen , but it DOES happen .
* Read it here to be on the safe side .
* That also clears the OVRES flag . Required if the
* following loop exits due to OVRES !
*/
readl ( & reg_base - > rdr ) ;
}
for ( len_tx = 0 , len_rx = 0 ; len_rx < len ; ) {
status = readl ( & reg_base - > sr ) ;
if ( status & ATMEL_SPI_SR_OVRES )
return - 1 ;
if ( ( len_tx < len ) & & ( status & ATMEL_SPI_SR_TDRE ) ) {
if ( txp )
value = * txp + + ;
else
value = 0 ;
writel ( value , & reg_base - > tdr ) ;
len_tx + + ;
}
if ( status & ATMEL_SPI_SR_RDRF ) {
value = readl ( & reg_base - > rdr ) ;
if ( rxp )
* rxp + + = value ;
len_rx + + ;
}
}
out :
if ( flags & SPI_XFER_END ) {
/*
* Wait until the transfer is completely done before
* we deactivate CS .
*/
wait_for_bit ( __func__ , & reg_base - > sr ,
ATMEL_SPI_SR_TXEMPTY , true , 1000 , false ) ;
atmel_spi_cs_deactivate ( dev ) ;
}
return 0 ;
}
static int atmel_spi_set_speed ( struct udevice * bus , uint speed )
{
struct atmel_spi_priv * priv = dev_get_priv ( bus ) ;
priv - > freq = speed ;
return 0 ;
}
static int atmel_spi_set_mode ( struct udevice * bus , uint mode )
{
struct atmel_spi_priv * priv = dev_get_priv ( bus ) ;
priv - > mode = mode ;
return 0 ;
}
static const struct dm_spi_ops atmel_spi_ops = {
. claim_bus = atmel_spi_claim_bus ,
. release_bus = atmel_spi_release_bus ,
. xfer = atmel_spi_xfer ,
. set_speed = atmel_spi_set_speed ,
. set_mode = atmel_spi_set_mode ,
/*
* cs_info is not needed , since we require all chip selects to be
* in the device tree explicitly
*/
} ;
static int atmel_spi_enable_clk ( struct udevice * bus )
{
struct atmel_spi_priv * priv = dev_get_priv ( bus ) ;
struct clk clk ;
ulong clk_rate ;
int ret ;
ret = clk_get_by_index ( bus , 0 , & clk ) ;
if ( ret )
return - EINVAL ;
ret = clk_enable ( & clk ) ;
if ( ret )
return ret ;
clk_rate = clk_get_rate ( & clk ) ;
if ( ! clk_rate )
return - EINVAL ;
priv - > bus_clk_rate = clk_rate ;
clk_free ( & clk ) ;
return 0 ;
}
static int atmel_spi_probe ( struct udevice * bus )
{
struct atmel_spi_platdata * bus_plat = dev_get_platdata ( bus ) ;
struct atmel_spi_priv * priv = dev_get_priv ( bus ) ;
int i , ret ;
ret = atmel_spi_enable_clk ( bus ) ;
if ( ret )
return ret ;
bus_plat - > regs = ( struct at91_spi * ) dev_get_addr ( bus ) ;
ret = gpio_request_list_by_name ( bus , " cs-gpios " , priv - > cs_gpios ,
ARRAY_SIZE ( priv - > cs_gpios ) , 0 ) ;
if ( ret < 0 ) {
error ( " Can't get %s gpios! Error: %d " , bus - > name , ret ) ;
return ret ;
}
for ( i = 0 ; i < ARRAY_SIZE ( priv - > cs_gpios ) ; i + + ) {
dm_gpio_set_dir_flags ( & priv - > cs_gpios [ i ] ,
GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE ) ;
}
writel ( ATMEL_SPI_CR_SWRST , & bus_plat - > regs - > cr ) ;
return 0 ;
}
static const struct udevice_id atmel_spi_ids [ ] = {
{ . compatible = " atmel,at91rm9200-spi " } ,
{ }
} ;
U_BOOT_DRIVER ( atmel_spi ) = {
. name = " atmel_spi " ,
. id = UCLASS_SPI ,
. of_match = atmel_spi_ids ,
. ops = & atmel_spi_ops ,
. platdata_auto_alloc_size = sizeof ( struct atmel_spi_platdata ) ,
. priv_auto_alloc_size = sizeof ( struct atmel_spi_priv ) ,
. probe = atmel_spi_probe ,
} ;
# endif