This adds a driver for the SPI controller found on davinci based SoCs from Texas Instruments. Signed-off-by: Sekhar Nori <nsekhar@ti.com> Signed-off-by: Sudhakar Rajashekhara <sudhakar.raj@ti.com> Signed-off-by: Sandeep Paulraj <s-paulraj@ti.com>master
parent
44d8025622
commit
8ed5885693
@ -0,0 +1,223 @@ |
||||
/*
|
||||
* Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* |
||||
* Driver for SPI controller on DaVinci. Based on atmel_spi.c |
||||
* by Atmel Corporation |
||||
* |
||||
* Copyright (C) 2007 Atmel Corporation |
||||
* |
||||
* See file CREDITS for list of people who contributed to this |
||||
* project. |
||||
* |
||||
* This program 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 of |
||||
* the License, or (at your option) any later version. |
||||
* |
||||
* This program 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 this program; if not, write to the Free Software |
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
||||
* MA 02111-1307 USA |
||||
*/ |
||||
#include <common.h> |
||||
#include <spi.h> |
||||
#include <malloc.h> |
||||
#include <asm/io.h> |
||||
#include <asm/arch/hardware.h> |
||||
#include "davinci_spi.h" |
||||
|
||||
void spi_init() |
||||
{ |
||||
/* do nothing */ |
||||
} |
||||
|
||||
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, |
||||
unsigned int max_hz, unsigned int mode) |
||||
{ |
||||
struct davinci_spi_slave *ds; |
||||
|
||||
if (!spi_cs_is_valid(bus, cs)) |
||||
return NULL; |
||||
|
||||
ds = malloc(sizeof(*ds)); |
||||
if (!ds) |
||||
return NULL; |
||||
|
||||
ds->slave.bus = bus; |
||||
ds->slave.cs = cs; |
||||
ds->regs = (struct davinci_spi_regs *)CONFIG_SYS_SPI_BASE; |
||||
ds->freq = max_hz; |
||||
|
||||
return &ds->slave; |
||||
} |
||||
|
||||
void spi_free_slave(struct spi_slave *slave) |
||||
{ |
||||
struct davinci_spi_slave *ds = to_davinci_spi(slave); |
||||
|
||||
free(ds); |
||||
} |
||||
|
||||
int spi_claim_bus(struct spi_slave *slave) |
||||
{ |
||||
struct davinci_spi_slave *ds = to_davinci_spi(slave); |
||||
unsigned int scalar, data1_reg_val = 0; |
||||
|
||||
/* Enable the SPI hardware */ |
||||
writel(SPIGCR0_SPIRST_MASK, &ds->regs->gcr0); |
||||
udelay(1000); |
||||
writel(SPIGCR0_SPIENA_MASK, &ds->regs->gcr0); |
||||
|
||||
/* Set master mode, powered up and not activated */ |
||||
writel(SPIGCR1_MASTER_MASK | SPIGCR1_CLKMOD_MASK, &ds->regs->gcr1); |
||||
|
||||
/* CS, CLK, SIMO and SOMI are functional pins */ |
||||
writel((SPIPC0_EN0FUN_MASK | SPIPC0_CLKFUN_MASK | |
||||
SPIPC0_DOFUN_MASK | SPIPC0_DIFUN_MASK), &ds->regs->pc0); |
||||
|
||||
/* setup format */ |
||||
scalar = ((CONFIG_SYS_SPI_CLK / ds->freq) - 1) & 0xFF; |
||||
|
||||
/*
|
||||
* Use following format: |
||||
* character length = 8, |
||||
* clock signal delayed by half clk cycle, |
||||
* clock low in idle state - Mode 0, |
||||
* MSB shifted out first |
||||
*/ |
||||
writel(8 | (scalar << SPIFMT_PRESCALE_SHIFT) | |
||||
(1 << SPIFMT_PHASE_SHIFT), &ds->regs->fmt0); |
||||
|
||||
/* hold cs active at end of transfer until explicitly de-asserted */ |
||||
data1_reg_val = (1 << SPIDAT1_CSHOLD_SHIFT) | |
||||
(slave->cs << SPIDAT1_CSNR_SHIFT); |
||||
writel(data1_reg_val, &ds->regs->dat1); |
||||
|
||||
/*
|
||||
* Including a minor delay. No science here. Should be good even with |
||||
* no delay |
||||
*/ |
||||
writel((50 << SPI_C2TDELAY_SHIFT) | |
||||
(50 << SPI_T2CDELAY_SHIFT), &ds->regs->delay); |
||||
|
||||
/* default chip select register */ |
||||
writel(SPIDEF_CSDEF0_MASK, &ds->regs->def); |
||||
|
||||
/* no interrupts */ |
||||
writel(0, &ds->regs->int0); |
||||
writel(0, &ds->regs->lvl); |
||||
|
||||
/* enable SPI */ |
||||
writel((readl(&ds->regs->gcr1) | SPIGCR1_SPIENA_MASK), &ds->regs->gcr1); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
void spi_release_bus(struct spi_slave *slave) |
||||
{ |
||||
struct davinci_spi_slave *ds = to_davinci_spi(slave); |
||||
|
||||
/* Disable the SPI hardware */ |
||||
writel(SPIGCR0_SPIRST_MASK, &ds->regs->gcr0); |
||||
} |
||||
|
||||
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, |
||||
const void *dout, void *din, unsigned long flags) |
||||
{ |
||||
struct davinci_spi_slave *ds = to_davinci_spi(slave); |
||||
unsigned int len, data1_reg_val = readl(&ds->regs->dat1); |
||||
int ret, i; |
||||
const u8 *txp = dout; /* dout can be NULL for read operation */ |
||||
u8 *rxp = din; /* din can be NULL for write operation */ |
||||
|
||||
ret = 0; |
||||
|
||||
if (bitlen == 0) |
||||
/* Finish any previously submitted transfers */ |
||||
goto out; |
||||
|
||||
/*
|
||||
* It's not clear how non-8-bit-aligned transfers are supposed to be |
||||
* represented as a stream of bytes...this is a limitation of |
||||
* the current SPI interface - here we terminate on receiving such a |
||||
* transfer request. |
||||
*/ |
||||
if (bitlen % 8) { |
||||
/* Errors always terminate an ongoing transfer */ |
||||
flags |= SPI_XFER_END; |
||||
goto out; |
||||
} |
||||
|
||||
len = bitlen / 8; |
||||
|
||||
/* do an empty read to clear the current contents */ |
||||
readl(&ds->regs->buf); |
||||
|
||||
/* keep writing and reading 1 byte until done */ |
||||
for (i = 0; i < len; i++) { |
||||
/* wait till TXFULL is asserted */ |
||||
while (readl(&ds->regs->buf) & SPIBUF_TXFULL_MASK); |
||||
|
||||
/* write the data */ |
||||
data1_reg_val &= ~0xFFFF; |
||||
if (txp) { |
||||
data1_reg_val |= *txp; |
||||
txp++; |
||||
} |
||||
|
||||
/*
|
||||
* Write to DAT1 is required to keep the serial transfer going. |
||||
* We just terminate when we reach the end. |
||||
*/ |
||||
if ((i == (len - 1)) && (flags & SPI_XFER_END)) { |
||||
/* clear CS hold */ |
||||
writel(data1_reg_val & |
||||
~(1 << SPIDAT1_CSHOLD_SHIFT), &ds->regs->dat1); |
||||
} else { |
||||
/* enable CS hold */ |
||||
data1_reg_val |= ((1 << SPIDAT1_CSHOLD_SHIFT) | |
||||
(slave->cs << SPIDAT1_CSNR_SHIFT)); |
||||
writel(data1_reg_val, &ds->regs->dat1); |
||||
} |
||||
|
||||
/* read the data - wait for data availability */ |
||||
while (readl(&ds->regs->buf) & SPIBUF_RXEMPTY_MASK); |
||||
|
||||
if (rxp) { |
||||
*rxp = readl(&ds->regs->buf) & 0xFF; |
||||
rxp++; |
||||
} else { |
||||
/* simply drop the read character */ |
||||
readl(&ds->regs->buf); |
||||
} |
||||
} |
||||
return 0; |
||||
|
||||
out: |
||||
if (flags & SPI_XFER_END) { |
||||
writel(data1_reg_val & |
||||
~(1 << SPIDAT1_CSHOLD_SHIFT), &ds->regs->dat1); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
int spi_cs_is_valid(unsigned int bus, unsigned int cs) |
||||
{ |
||||
return bus == 0 && cs == 0; |
||||
} |
||||
|
||||
void spi_cs_activate(struct spi_slave *slave) |
||||
{ |
||||
/* do nothing */ |
||||
} |
||||
|
||||
void spi_cs_deactivate(struct spi_slave *slave) |
||||
{ |
||||
/* do nothing */ |
||||
} |
||||
|
@ -0,0 +1,101 @@ |
||||
/*
|
||||
* Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* |
||||
* Register definitions for the DaVinci SPI Controller |
||||
* |
||||
* This program 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 of |
||||
* the License, or (at your option) any later version. |
||||
* |
||||
* This program 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 this program; if not, write to the Free Software |
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
||||
* MA 02111-1307 USA |
||||
*/ |
||||
|
||||
#ifndef _DAVINCI_SPI_H_ |
||||
#define _DAVINCI_SPI_H_ |
||||
|
||||
struct davinci_spi_regs { |
||||
dv_reg gcr0; /* 0x00 */ |
||||
dv_reg gcr1; /* 0x04 */ |
||||
dv_reg int0; /* 0x08 */ |
||||
dv_reg lvl; /* 0x0c */ |
||||
dv_reg flg; /* 0x10 */ |
||||
dv_reg pc0; /* 0x14 */ |
||||
dv_reg pc1; /* 0x18 */ |
||||
dv_reg pc2; /* 0x1c */ |
||||
dv_reg pc3; /* 0x20 */ |
||||
dv_reg pc4; /* 0x24 */ |
||||
dv_reg pc5; /* 0x28 */ |
||||
dv_reg rsvd[3]; |
||||
dv_reg dat0; /* 0x38 */ |
||||
dv_reg dat1; /* 0x3c */ |
||||
dv_reg buf; /* 0x40 */ |
||||
dv_reg emu; /* 0x44 */ |
||||
dv_reg delay; /* 0x48 */ |
||||
dv_reg def; /* 0x4c */ |
||||
dv_reg fmt0; /* 0x50 */ |
||||
dv_reg fmt1; /* 0x54 */ |
||||
dv_reg fmt2; /* 0x58 */ |
||||
dv_reg fmt3; /* 0x5c */ |
||||
dv_reg intvec0; /* 0x60 */ |
||||
dv_reg intvec1; /* 0x64 */ |
||||
}; |
||||
|
||||
#define BIT(x) (1 << (x)) |
||||
|
||||
/* SPIGCR0 */ |
||||
#define SPIGCR0_SPIENA_MASK 0x1 |
||||
#define SPIGCR0_SPIRST_MASK 0x0 |
||||
|
||||
/* SPIGCR0 */ |
||||
#define SPIGCR1_CLKMOD_MASK BIT(1) |
||||
#define SPIGCR1_MASTER_MASK BIT(0) |
||||
#define SPIGCR1_SPIENA_MASK BIT(24) |
||||
|
||||
/* SPIPC0 */ |
||||
#define SPIPC0_DIFUN_MASK BIT(11) /* SIMO */ |
||||
#define SPIPC0_DOFUN_MASK BIT(10) /* SOMI */ |
||||
#define SPIPC0_CLKFUN_MASK BIT(9) /* CLK */ |
||||
#define SPIPC0_EN0FUN_MASK BIT(0) |
||||
|
||||
/* SPIFMT0 */ |
||||
#define SPIFMT_SHIFTDIR_SHIFT 20 |
||||
#define SPIFMT_POLARITY_SHIFT 17 |
||||
#define SPIFMT_PHASE_SHIFT 16 |
||||
#define SPIFMT_PRESCALE_SHIFT 8 |
||||
|
||||
/* SPIDAT1 */ |
||||
#define SPIDAT1_CSHOLD_SHIFT 28 |
||||
#define SPIDAT1_CSNR_SHIFT 16 |
||||
|
||||
/* SPIDELAY */ |
||||
#define SPI_C2TDELAY_SHIFT 24 |
||||
#define SPI_T2CDELAY_SHIFT 16 |
||||
|
||||
/* SPIBUF */ |
||||
#define SPIBUF_RXEMPTY_MASK BIT(31) |
||||
#define SPIBUF_TXFULL_MASK BIT(29) |
||||
|
||||
/* SPIDEF */ |
||||
#define SPIDEF_CSDEF0_MASK BIT(0) |
||||
|
||||
struct davinci_spi_slave { |
||||
struct spi_slave slave; |
||||
struct davinci_spi_regs *regs; |
||||
unsigned int freq; |
||||
}; |
||||
|
||||
static inline struct davinci_spi_slave *to_davinci_spi(struct spi_slave *slave) |
||||
{ |
||||
return container_of(slave, struct davinci_spi_slave, slave); |
||||
} |
||||
|
||||
#endif /* _DAVINCI_SPI_H_ */ |
Loading…
Reference in new issue