/*
* au1550 psc spi controller driver
* may work also with au1200, au1210, au1250
* will not work on au1000, au1100 and au1500 (no full spi controller there)
*
* Copyright (c) 2006 ATRON electronic GmbH
* Author: Jan Nikitenko <jan.nikitenko@gmail.com>
*
* 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 <linux/init.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/resource.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/dma-mapping.h>
#include <linux/completion.h>
#include <asm/mach-au1x00/au1000.h>
#include <asm/mach-au1x00/au1xxx_psc.h>
#include <asm/mach-au1x00/au1xxx_dbdma.h>
#include <asm/mach-au1x00/au1550_spi.h>
static unsigned usedma = 1;
module_param(usedma, uint, 0644);
/*
#define AU1550_SPI_DEBUG_LOOPBACK
*/
#define AU1550_SPI_DBDMA_DESCRIPTORS 1
#define AU1550_SPI_DMA_RXTMP_MINSIZE 2048U
struct au1550_spi {
struct spi_bitbang bitbang;
volatile psc_spi_t __iomem *regs;
int irq;
unsigned freq_max;
unsigned freq_min;
unsigned len;
unsigned tx_count;
unsigned rx_count;
const u8 *tx;
u8 *rx;
void (*rx_word)(struct au1550_spi *hw);
void (*tx_word)(struct au1550_spi *hw);
int (*txrx_bufs)(struct spi_device *spi, struct spi_transfer *t);
irqreturn_t (*irq_callback)(struct au1550_spi *hw);
struct completion master_done;
unsigned usedma;
u32 dma_tx_id;
u32 dma_rx_id;
u32 dma_tx_ch;
u32 dma_rx_ch;
u8 *dma_rx_tmpbuf;
unsigned dma_rx_tmpbuf_size;
u32 dma_rx_tmpbuf_addr;
struct spi_master *master;
struct device *dev;
struct au1550_spi_info *pdata;
struct resource *ioarea;
};
/* we use an 8-bit memory device for dma transfers to/from spi fifo */
static dbdev_tab_t au1550_spi_mem_dbdev =
{
.dev_id = DBDMA_MEM_CHAN,
.dev_flags = DEV_FLAGS_ANYUSE|DEV_FLAGS_SYNC,
.dev_tsize = 0,
.dev_devwidth = 8,
.dev_physaddr = 0x00000000,
.dev_intlevel = 0,
.dev_intpolarity = 0
};
static int ddma_memid; /* id to above mem dma device */
static void au1550_spi_bits_handlers_set(struct au1550_spi *hw, int bpw);
/*
* compute BRG and DIV bits to setup spi clock based on main input clock rate
* that was specified in platform data structure
* according to au1550 datasheet:
* psc_tempclk = psc_mainclk / (2 << DIV)
* spiclk = psc_tempclk / (2 * (BRG + 1))
* BRG valid range is 4..63
* DIV valid range is 0..3
*/
static u32 au1550_spi_baudcfg(struct au1550_spi *hw, unsigned speed_hz)
{
u32 mainclk_hz = hw->pdata->mainclk_hz;
u32 div, brg;
for (div = 0; div < 4; div++) {
brg = mainclk_hz / speed_hz / (4 << div);
/* now we have BRG+1 in brg, so count with that */
if (brg < (4 + 1)) {
brg = (4 + 1); /* speed_hz too big */
break; /* set lowest brg (div is == 0) */
}
if (brg <= (63 + 1))
break; /* we have valid brg and div */
}
if (div == 4) {
div = 3; /* speed_hz too small */
brg = (63 + 1); /* set highest brg and div */
}
brg--;
return PSC_SPICFG_SET_BAUD(brg) | PSC_SPICFG_SET_DIV(div);
}
static inline void au1550_spi_mask_ack_all(struct au1550_spi *hw)
{
hw->regs->psc_spimsk =
PSC_SPIMSK_MM | PSC_SPIMSK_RR | PSC_SPIMSK_RO
| PSC_SPIMSK_RU | PSC_SPIMSK_TR | PSC_SPIMSK_TO
| PSC_SPIMSK_TU | PSC_SPIMSK_SD | PSC_SPIMSK_MD;
au_sync();
hw->regs->psc_spievent =
PSC_SPIEVNT_MM | PSC_SPIEVNT_RR | PSC_SPIEVNT_RO
| PSC_SPIEVNT_RU | PSC_SPIEVNT_TR | PSC_SPIEVNT_TO
| PSC_SPIEVNT_TU | PSC_SPIEVNT_SD | PSC_SPIEVNT_MD;
au_sync();
}
static void au1550_spi_reset_fifos(struct au1550_spi *hw)
{
u32 pcr;
hw->regs->psc_spipcr = PSC_SPIPCR_RC | PSC_SPIPCR_TC;
au_sync();
do {