mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2025-04-21 12:27:27 +03:00
[lantiq] cleanup patches
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@32953 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
483
target/linux/lantiq/files/drivers/spi/spi-falcon.c
Normal file
483
target/linux/lantiq/files/drivers/spi/spi-falcon.c
Normal file
@@ -0,0 +1,483 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* Copyright (C) 2010 Thomas Langer <thomas.langer@lantiq.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <lantiq_soc.h>
|
||||
|
||||
#define DRV_NAME "falcon_spi"
|
||||
|
||||
#define FALCON_SPI_XFER_BEGIN (1 << 0)
|
||||
#define FALCON_SPI_XFER_END (1 << 1)
|
||||
|
||||
/* Bus Read Configuration Register0 */
|
||||
#define LTQ_BUSRCON0 0x00000010
|
||||
/* Bus Write Configuration Register0 */
|
||||
#define LTQ_BUSWCON0 0x00000018
|
||||
/* Serial Flash Configuration Register */
|
||||
#define LTQ_SFCON 0x00000080
|
||||
/* Serial Flash Time Register */
|
||||
#define LTQ_SFTIME 0x00000084
|
||||
/* Serial Flash Status Register */
|
||||
#define LTQ_SFSTAT 0x00000088
|
||||
/* Serial Flash Command Register */
|
||||
#define LTQ_SFCMD 0x0000008C
|
||||
/* Serial Flash Address Register */
|
||||
#define LTQ_SFADDR 0x00000090
|
||||
/* Serial Flash Data Register */
|
||||
#define LTQ_SFDATA 0x00000094
|
||||
/* Serial Flash I/O Control Register */
|
||||
#define LTQ_SFIO 0x00000098
|
||||
/* EBU Clock Control Register */
|
||||
#define LTQ_EBUCC 0x000000C4
|
||||
|
||||
/* Dummy Phase Length */
|
||||
#define SFCMD_DUMLEN_OFFSET 16
|
||||
#define SFCMD_DUMLEN_MASK 0x000F0000
|
||||
/* Chip Select */
|
||||
#define SFCMD_CS_OFFSET 24
|
||||
#define SFCMD_CS_MASK 0x07000000
|
||||
/* field offset */
|
||||
#define SFCMD_ALEN_OFFSET 20
|
||||
#define SFCMD_ALEN_MASK 0x00700000
|
||||
/* SCK Rise-edge Position */
|
||||
#define SFTIME_SCKR_POS_OFFSET 8
|
||||
#define SFTIME_SCKR_POS_MASK 0x00000F00
|
||||
/* SCK Period */
|
||||
#define SFTIME_SCK_PER_OFFSET 0
|
||||
#define SFTIME_SCK_PER_MASK 0x0000000F
|
||||
/* SCK Fall-edge Position */
|
||||
#define SFTIME_SCKF_POS_OFFSET 12
|
||||
#define SFTIME_SCKF_POS_MASK 0x0000F000
|
||||
/* Device Size */
|
||||
#define SFCON_DEV_SIZE_A23_0 0x03000000
|
||||
#define SFCON_DEV_SIZE_MASK 0x0F000000
|
||||
/* Read Data Position */
|
||||
#define SFTIME_RD_POS_MASK 0x000F0000
|
||||
/* Data Output */
|
||||
#define SFIO_UNUSED_WD_MASK 0x0000000F
|
||||
/* Command Opcode mask */
|
||||
#define SFCMD_OPC_MASK 0x000000FF
|
||||
/* dlen bytes of data to write */
|
||||
#define SFCMD_DIR_WRITE 0x00000100
|
||||
/* Data Length offset */
|
||||
#define SFCMD_DLEN_OFFSET 9
|
||||
/* Command Error */
|
||||
#define SFSTAT_CMD_ERR 0x20000000
|
||||
/* Access Command Pending */
|
||||
#define SFSTAT_CMD_PEND 0x00400000
|
||||
/* Frequency set to 100MHz. */
|
||||
#define EBUCC_EBUDIV_SELF100 0x00000001
|
||||
/* Serial Flash */
|
||||
#define BUSRCON0_AGEN_SERIAL_FLASH 0xF0000000
|
||||
/* 8-bit multiplexed */
|
||||
#define BUSRCON0_PORTW_8_BIT_MUX 0x00000000
|
||||
/* Serial Flash */
|
||||
#define BUSWCON0_AGEN_SERIAL_FLASH 0xF0000000
|
||||
/* Chip Select after opcode */
|
||||
#define SFCMD_KEEP_CS_KEEP_SELECTED 0x00008000
|
||||
|
||||
struct falcon_spi {
|
||||
u32 sfcmd; /* for caching of opcode, direction, ... */
|
||||
struct spi_master *master;
|
||||
};
|
||||
|
||||
int
|
||||
falcon_spi_xfer(struct spi_device *spi,
|
||||
struct spi_transfer *t,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct device *dev = &spi->dev;
|
||||
struct falcon_spi *priv = spi_master_get_devdata(spi->master);
|
||||
const u8 *txp = t->tx_buf;
|
||||
u8 *rxp = t->rx_buf;
|
||||
unsigned int bytelen = ((8 * t->len + 7) / 8);
|
||||
unsigned int len, alen, dumlen;
|
||||
u32 val;
|
||||
enum {
|
||||
state_init,
|
||||
state_command_prepare,
|
||||
state_write,
|
||||
state_read,
|
||||
state_disable_cs,
|
||||
state_end
|
||||
} state = state_init;
|
||||
|
||||
do {
|
||||
switch (state) {
|
||||
case state_init: /* detect phase of upper layer sequence */
|
||||
{
|
||||
/* initial write ? */
|
||||
if (flags & FALCON_SPI_XFER_BEGIN) {
|
||||
if (!txp) {
|
||||
dev_err(dev,
|
||||
"BEGIN without tx data!\n");
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* Prepare the parts of the sfcmd register,
|
||||
* which should not
|
||||
* change during a sequence!
|
||||
* Only exception are the length fields,
|
||||
* especially alen and dumlen.
|
||||
*/
|
||||
|
||||
priv->sfcmd = ((spi->chip_select
|
||||
<< SFCMD_CS_OFFSET)
|
||||
& SFCMD_CS_MASK);
|
||||
priv->sfcmd |= SFCMD_KEEP_CS_KEEP_SELECTED;
|
||||
priv->sfcmd |= *txp;
|
||||
txp++;
|
||||
bytelen--;
|
||||
if (bytelen) {
|
||||
/*
|
||||
* more data:
|
||||
* maybe address and/or dummy
|
||||
*/
|
||||
state = state_command_prepare;
|
||||
break;
|
||||
} else {
|
||||
dev_dbg(dev, "write cmd %02X\n",
|
||||
priv->sfcmd & SFCMD_OPC_MASK);
|
||||
}
|
||||
}
|
||||
/* continued write ? */
|
||||
if (txp && bytelen) {
|
||||
state = state_write;
|
||||
break;
|
||||
}
|
||||
/* read data? */
|
||||
if (rxp && bytelen) {
|
||||
state = state_read;
|
||||
break;
|
||||
}
|
||||
/* end of sequence? */
|
||||
if (flags & FALCON_SPI_XFER_END)
|
||||
state = state_disable_cs;
|
||||
else
|
||||
state = state_end;
|
||||
break;
|
||||
}
|
||||
/* collect tx data for address and dummy phase */
|
||||
case state_command_prepare:
|
||||
{
|
||||
/* txp is valid, already checked */
|
||||
val = 0;
|
||||
alen = 0;
|
||||
dumlen = 0;
|
||||
while (bytelen > 0) {
|
||||
if (alen < 3) {
|
||||
val = (val<<8)|(*txp++);
|
||||
alen++;
|
||||
} else if ((dumlen < 15) && (*txp == 0)) {
|
||||
/*
|
||||
* assume dummy bytes are set to 0
|
||||
* from upper layer
|
||||
*/
|
||||
dumlen++;
|
||||
txp++;
|
||||
} else
|
||||
break;
|
||||
bytelen--;
|
||||
}
|
||||
priv->sfcmd &= ~(SFCMD_ALEN_MASK | SFCMD_DUMLEN_MASK);
|
||||
priv->sfcmd |= (alen << SFCMD_ALEN_OFFSET) |
|
||||
(dumlen << SFCMD_DUMLEN_OFFSET);
|
||||
if (alen > 0)
|
||||
ltq_ebu_w32(val, LTQ_SFADDR);
|
||||
|
||||
dev_dbg(dev, "write cmd %02X, alen=%d "
|
||||
"(addr=%06X) dumlen=%d\n",
|
||||
priv->sfcmd & SFCMD_OPC_MASK,
|
||||
alen, val, dumlen);
|
||||
|
||||
if (bytelen > 0) {
|
||||
/* continue with write */
|
||||
state = state_write;
|
||||
} else if (flags & FALCON_SPI_XFER_END) {
|
||||
/* end of sequence? */
|
||||
state = state_disable_cs;
|
||||
} else {
|
||||
/*
|
||||
* go to end and expect another
|
||||
* call (read or write)
|
||||
*/
|
||||
state = state_end;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case state_write:
|
||||
{
|
||||
/* txp still valid */
|
||||
priv->sfcmd |= SFCMD_DIR_WRITE;
|
||||
len = 0;
|
||||
val = 0;
|
||||
do {
|
||||
if (bytelen--)
|
||||
val |= (*txp++) << (8 * len++);
|
||||
if ((flags & FALCON_SPI_XFER_END)
|
||||
&& (bytelen == 0)) {
|
||||
priv->sfcmd &=
|
||||
~SFCMD_KEEP_CS_KEEP_SELECTED;
|
||||
}
|
||||
if ((len == 4) || (bytelen == 0)) {
|
||||
ltq_ebu_w32(val, LTQ_SFDATA);
|
||||
ltq_ebu_w32(priv->sfcmd
|
||||
| (len<<SFCMD_DLEN_OFFSET),
|
||||
LTQ_SFCMD);
|
||||
len = 0;
|
||||
val = 0;
|
||||
priv->sfcmd &= ~(SFCMD_ALEN_MASK
|
||||
| SFCMD_DUMLEN_MASK);
|
||||
}
|
||||
} while (bytelen);
|
||||
state = state_end;
|
||||
break;
|
||||
}
|
||||
case state_read:
|
||||
{
|
||||
/* read data */
|
||||
priv->sfcmd &= ~SFCMD_DIR_WRITE;
|
||||
do {
|
||||
if ((flags & FALCON_SPI_XFER_END)
|
||||
&& (bytelen <= 4)) {
|
||||
priv->sfcmd &=
|
||||
~SFCMD_KEEP_CS_KEEP_SELECTED;
|
||||
}
|
||||
len = (bytelen > 4) ? 4 : bytelen;
|
||||
bytelen -= len;
|
||||
ltq_ebu_w32(priv->sfcmd
|
||||
|(len<<SFCMD_DLEN_OFFSET), LTQ_SFCMD);
|
||||
priv->sfcmd &= ~(SFCMD_ALEN_MASK
|
||||
| SFCMD_DUMLEN_MASK);
|
||||
do {
|
||||
val = ltq_ebu_r32(LTQ_SFSTAT);
|
||||
if (val & SFSTAT_CMD_ERR) {
|
||||
/* reset error status */
|
||||
dev_err(dev, "SFSTAT: CMD_ERR "
|
||||
"(%x)\n", val);
|
||||
ltq_ebu_w32(SFSTAT_CMD_ERR,
|
||||
LTQ_SFSTAT);
|
||||
return -1;
|
||||
}
|
||||
} while (val & SFSTAT_CMD_PEND);
|
||||
val = ltq_ebu_r32(LTQ_SFDATA);
|
||||
do {
|
||||
*rxp = (val & 0xFF);
|
||||
rxp++;
|
||||
val >>= 8;
|
||||
len--;
|
||||
} while (len);
|
||||
} while (bytelen);
|
||||
state = state_end;
|
||||
break;
|
||||
}
|
||||
case state_disable_cs:
|
||||
{
|
||||
priv->sfcmd &= ~SFCMD_KEEP_CS_KEEP_SELECTED;
|
||||
ltq_ebu_w32(priv->sfcmd | (0 << SFCMD_DLEN_OFFSET),
|
||||
LTQ_SFCMD);
|
||||
val = ltq_ebu_r32(LTQ_SFSTAT);
|
||||
if (val & SFSTAT_CMD_ERR) {
|
||||
/* reset error status */
|
||||
dev_err(dev, "SFSTAT: CMD_ERR (%x)\n", val);
|
||||
ltq_ebu_w32(SFSTAT_CMD_ERR, LTQ_SFSTAT);
|
||||
return -1;
|
||||
}
|
||||
state = state_end;
|
||||
break;
|
||||
}
|
||||
case state_end:
|
||||
break;
|
||||
}
|
||||
} while (state != state_end);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
falcon_spi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct device *dev = &spi->dev;
|
||||
const u32 ebuclk = 100000000;
|
||||
unsigned int i;
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(dev, "setup\n");
|
||||
|
||||
if (spi->master->bus_num > 0 || spi->chip_select > 0)
|
||||
return -ENODEV;
|
||||
|
||||
spin_lock_irqsave(&ebu_lock, flags);
|
||||
|
||||
if (ebuclk < spi->max_speed_hz) {
|
||||
/* set EBU clock to 100 MHz */
|
||||
ltq_sys1_w32_mask(0, EBUCC_EBUDIV_SELF100, LTQ_EBUCC);
|
||||
i = 1; /* divider */
|
||||
} else {
|
||||
/* set EBU clock to 50 MHz */
|
||||
ltq_sys1_w32_mask(EBUCC_EBUDIV_SELF100, 0, LTQ_EBUCC);
|
||||
|
||||
/* search for suitable divider */
|
||||
for (i = 1; i < 7; i++) {
|
||||
if (ebuclk / i <= spi->max_speed_hz)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* setup period of serial clock */
|
||||
ltq_ebu_w32_mask(SFTIME_SCKF_POS_MASK
|
||||
| SFTIME_SCKR_POS_MASK
|
||||
| SFTIME_SCK_PER_MASK,
|
||||
(i << SFTIME_SCKR_POS_OFFSET)
|
||||
| (i << (SFTIME_SCK_PER_OFFSET + 1)),
|
||||
LTQ_SFTIME);
|
||||
|
||||
/*
|
||||
* set some bits of unused_wd, to not trigger HOLD/WP
|
||||
* signals on non QUAD flashes
|
||||
*/
|
||||
ltq_ebu_w32((SFIO_UNUSED_WD_MASK & (0x8 | 0x4)), LTQ_SFIO);
|
||||
|
||||
ltq_ebu_w32(BUSRCON0_AGEN_SERIAL_FLASH | BUSRCON0_PORTW_8_BIT_MUX,
|
||||
LTQ_BUSRCON0);
|
||||
ltq_ebu_w32(BUSWCON0_AGEN_SERIAL_FLASH, LTQ_BUSWCON0);
|
||||
/* set address wrap around to maximum for 24-bit addresses */
|
||||
ltq_ebu_w32_mask(SFCON_DEV_SIZE_MASK, SFCON_DEV_SIZE_A23_0, LTQ_SFCON);
|
||||
|
||||
spin_unlock_irqrestore(&ebu_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
falcon_spi_transfer(struct spi_device *spi, struct spi_message *m)
|
||||
{
|
||||
struct falcon_spi *priv = spi_master_get_devdata(spi->master);
|
||||
struct spi_transfer *t;
|
||||
unsigned long spi_flags;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
priv->sfcmd = 0;
|
||||
m->actual_length = 0;
|
||||
|
||||
spi_flags = FALCON_SPI_XFER_BEGIN;
|
||||
list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
if (list_is_last(&t->transfer_list, &m->transfers))
|
||||
spi_flags |= FALCON_SPI_XFER_END;
|
||||
|
||||
spin_lock_irqsave(&ebu_lock, flags);
|
||||
ret = falcon_spi_xfer(spi, t, spi_flags);
|
||||
spin_unlock_irqrestore(&ebu_lock, flags);
|
||||
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
m->actual_length += t->len;
|
||||
|
||||
if (t->delay_usecs || t->cs_change)
|
||||
BUG();
|
||||
|
||||
spi_flags = 0;
|
||||
}
|
||||
|
||||
m->status = ret;
|
||||
m->complete(m->context);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
falcon_spi_cleanup(struct spi_device *spi)
|
||||
{
|
||||
struct device *dev = &spi->dev;
|
||||
|
||||
dev_dbg(dev, "cleanup\n");
|
||||
}
|
||||
|
||||
static int __devinit
|
||||
falcon_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct falcon_spi *priv;
|
||||
struct spi_master *master;
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "probing\n");
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*priv));
|
||||
if (!master) {
|
||||
dev_err(dev, "no memory for spi_master\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv = spi_master_get_devdata(master);
|
||||
priv->master = master;
|
||||
|
||||
master->mode_bits = SPI_MODE_3;
|
||||
master->num_chipselect = 1;
|
||||
master->bus_num = 0;
|
||||
|
||||
master->setup = falcon_spi_setup;
|
||||
master->transfer = falcon_spi_transfer;
|
||||
master->cleanup = falcon_spi_cleanup;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
ret = spi_register_master(master);
|
||||
if (ret)
|
||||
spi_master_put(master);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit
|
||||
falcon_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct falcon_spi *priv = platform_get_drvdata(pdev);
|
||||
|
||||
dev_dbg(dev, "removed\n");
|
||||
|
||||
spi_unregister_master(priv->master);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver falcon_spi_driver = {
|
||||
.probe = falcon_spi_probe,
|
||||
.remove = __devexit_p(falcon_spi_remove),
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE
|
||||
}
|
||||
};
|
||||
|
||||
static int __init
|
||||
falcon_spi_init(void)
|
||||
{
|
||||
return platform_driver_register(&falcon_spi_driver);
|
||||
}
|
||||
|
||||
static void __exit
|
||||
falcon_spi_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&falcon_spi_driver);
|
||||
}
|
||||
|
||||
module_init(falcon_spi_init);
|
||||
module_exit(falcon_spi_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Lantiq Falcon SPI controller driver");
|
||||
1070
target/linux/lantiq/files/drivers/spi/spi-xway.c
Normal file
1070
target/linux/lantiq/files/drivers/spi/spi-xway.c
Normal file
File diff suppressed because it is too large
Load Diff
955
target/linux/lantiq/files/drivers/spi/spi_svip.c
Normal file
955
target/linux/lantiq/files/drivers/spi/spi_svip.c
Normal file
@@ -0,0 +1,955 @@
|
||||
/************************************************************************
|
||||
*
|
||||
* Copyright (c) 2008
|
||||
* Infineon Technologies AG
|
||||
* St. Martin Strasse 53; 81669 Muenchen; Germany
|
||||
*
|
||||
* Inspired by Atmel AT32/AT91 SPI Controller driver
|
||||
* Copyright (c) 2006 Atmel Corporation
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
************************************************************************/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <status_reg.h>
|
||||
#include <base_reg.h>
|
||||
#include <ssc_reg.h>
|
||||
#include <sys0_reg.h>
|
||||
#include <sys1_reg.h>
|
||||
|
||||
#define SFRAME_SIZE 512 /* bytes */
|
||||
#define FIFO_HEADROOM 2 /* words */
|
||||
|
||||
#define SVIP_SSC_RFIFO_WORDS 8
|
||||
|
||||
enum svip_ssc_dir {
|
||||
SSC_RXTX,
|
||||
SSC_RX,
|
||||
SSC_TX,
|
||||
SSC_UNDEF
|
||||
};
|
||||
|
||||
/*
|
||||
* The core SPI transfer engine just talks to a register bank to set up
|
||||
* DMA transfers; transfer queue progress is driven by IRQs. The clock
|
||||
* framework provides the base clock, subdivided for each spi_device.
|
||||
*/
|
||||
struct svip_ssc_device {
|
||||
struct svip_reg_ssc *regs;
|
||||
enum svip_ssc_dir bus_dir;
|
||||
struct spi_device *stay;
|
||||
|
||||
u8 stopping;
|
||||
struct list_head queue;
|
||||
struct spi_transfer *current_transfer;
|
||||
int remaining_bytes;
|
||||
int rx_bytes;
|
||||
int tx_bytes;
|
||||
|
||||
char intname[4][16];
|
||||
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static int svip_ssc_setup(struct spi_device *spi);
|
||||
|
||||
extern unsigned int ltq_get_fbs0_hz(void);
|
||||
|
||||
static void cs_activate(struct svip_ssc_device *ssc_dev, struct spi_device *spi)
|
||||
{
|
||||
ssc_dev->regs->whbgpostat = 0x0001 << spi->chip_select; /* activate the chip select */
|
||||
}
|
||||
|
||||
static void cs_deactivate(struct svip_ssc_device *ssc_dev, struct spi_device *spi)
|
||||
{
|
||||
ssc_dev->regs->whbgpostat = 0x0100 << spi->chip_select; /* deactivate the chip select */
|
||||
}
|
||||
|
||||
/*
|
||||
* "Normally" returns Byte Valid = 4.
|
||||
* If the unaligned remainder of the packet is 3 bytes, these have to be
|
||||
* transferred as a combination of a 16-bit and a 8-bit FPI transfer. For
|
||||
* 2 or 1 remaining bytes a single 16-bit or 8-bit transfer will do.
|
||||
*/
|
||||
static int inline _estimate_bv(int byte_pos, int bytelen)
|
||||
{
|
||||
int remainder = bytelen % 4;
|
||||
|
||||
if (byte_pos < (bytelen - remainder))
|
||||
return 4;
|
||||
|
||||
if (remainder == 3)
|
||||
{
|
||||
if (byte_pos == (bytelen - remainder))
|
||||
return 2;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
return remainder;
|
||||
}
|
||||
|
||||
/*
|
||||
* Submit next transfer.
|
||||
* lock is held, spi irq is blocked
|
||||
*/
|
||||
static void svip_ssc_next_xfer(struct spi_master *master,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
struct svip_ssc_device *ssc_dev = spi_master_get_devdata(master);
|
||||
struct spi_transfer *xfer;
|
||||
unsigned char *buf_ptr;
|
||||
|
||||
xfer = ssc_dev->current_transfer;
|
||||
if (!xfer || ssc_dev->remaining_bytes == 0) {
|
||||
if (xfer)
|
||||
xfer = list_entry(xfer->transfer_list.next,
|
||||
struct spi_transfer, transfer_list);
|
||||
else
|
||||
xfer = list_entry(msg->transfers.next,
|
||||
struct spi_transfer, transfer_list);
|
||||
ssc_dev->remaining_bytes = xfer->len;
|
||||
ssc_dev->rx_bytes = 0;
|
||||
ssc_dev->tx_bytes = 0;
|
||||
ssc_dev->current_transfer = xfer;
|
||||
ssc_dev->regs->sfcon = 0; /* reset Serial Framing */
|
||||
|
||||
/* enable and flush RX/TX FIFO */
|
||||
ssc_dev->regs->rxfcon =
|
||||
SSC_RXFCON_RXFITL_VAL(SVIP_SSC_RFIFO_WORDS-FIFO_HEADROOM) |
|
||||
SSC_RXFCON_RXFLU | /* Receive FIFO Flush */
|
||||
SSC_RXFCON_RXFEN; /* Receive FIFO Enable */
|
||||
|
||||
ssc_dev->regs->txfcon =
|
||||
SSC_TXFCON_TXFITL_VAL(FIFO_HEADROOM) |
|
||||
SSC_TXFCON_TXFLU | /* Transmit FIFO Flush */
|
||||
SSC_TXFCON_TXFEN; /* Transmit FIFO Enable */
|
||||
|
||||
asm("sync");
|
||||
|
||||
/* select mode RXTX, RX or TX */
|
||||
if (xfer->rx_buf && xfer->tx_buf) /* RX and TX */
|
||||
{
|
||||
if (ssc_dev->bus_dir != SSC_RXTX)
|
||||
{
|
||||
ssc_dev->regs->mcon &= ~(SSC_MCON_RXOFF | SSC_MCON_TXOFF);
|
||||
ssc_dev->bus_dir = SSC_RXTX;
|
||||
ssc_dev->regs->irnen = SSC_IRNEN_T | SSC_IRNEN_F | SSC_IRNEN_E;
|
||||
}
|
||||
ssc_dev->regs->sfcon =
|
||||
SSC_SFCON_PLEN_VAL(0) |
|
||||
SSC_SFCON_DLEN_VAL(((xfer->len-1)%SFRAME_SIZE)*8+7) |
|
||||
SSC_SFCON_STOP |
|
||||
SSC_SFCON_ICLK_VAL(2) |
|
||||
SSC_SFCON_IDAT_VAL(2) |
|
||||
SSC_SFCON_IAEN |
|
||||
SSC_SFCON_SFEN;
|
||||
|
||||
}
|
||||
else if (xfer->rx_buf) /* RX only */
|
||||
{
|
||||
if (ssc_dev->bus_dir != SSC_RX)
|
||||
{
|
||||
ssc_dev->regs->mcon =
|
||||
(ssc_dev->regs->mcon | SSC_MCON_TXOFF) & ~SSC_MCON_RXOFF;
|
||||
|
||||
ssc_dev->bus_dir = SSC_RX;
|
||||
|
||||
ssc_dev->regs->irnen = SSC_IRNEN_R | SSC_IRNEN_E;
|
||||
}
|
||||
/* Initiate clock generation for Rx-Only Transfer. In case of RX-only transfer,
|
||||
* rx_bytes represents the number of already requested bytes.
|
||||
*/
|
||||
ssc_dev->rx_bytes = min(xfer->len, (unsigned)(SVIP_SSC_RFIFO_WORDS*4));
|
||||
ssc_dev->regs->rxreq = ssc_dev->rx_bytes;
|
||||
}
|
||||
else /* TX only */
|
||||
{
|
||||
if (ssc_dev->bus_dir != SSC_TX)
|
||||
{
|
||||
ssc_dev->regs->mcon =
|
||||
(ssc_dev->regs->mcon | SSC_MCON_RXOFF) & ~SSC_MCON_TXOFF;
|
||||
|
||||
ssc_dev->bus_dir = SSC_TX;
|
||||
|
||||
ssc_dev->regs->irnen =
|
||||
SSC_IRNEN_T | SSC_IRNEN_F | SSC_IRNEN_E;
|
||||
}
|
||||
ssc_dev->regs->sfcon =
|
||||
SSC_SFCON_PLEN_VAL(0) |
|
||||
SSC_SFCON_DLEN_VAL(((xfer->len-1)%SFRAME_SIZE)*8+7) |
|
||||
SSC_SFCON_STOP |
|
||||
SSC_SFCON_ICLK_VAL(2) |
|
||||
SSC_SFCON_IDAT_VAL(2) |
|
||||
SSC_SFCON_IAEN |
|
||||
SSC_SFCON_SFEN;
|
||||
}
|
||||
}
|
||||
|
||||
if (xfer->tx_buf)
|
||||
{
|
||||
int outstanding;
|
||||
int i;
|
||||
int fstat = ssc_dev->regs->fstat;
|
||||
int txffl = SSC_FSTAT_TXFFL_GET(fstat);
|
||||
int rxffl = SSC_FSTAT_RXFFL_GET(fstat);
|
||||
|
||||
outstanding = txffl;
|
||||
|
||||
if (xfer->rx_buf)
|
||||
{
|
||||
outstanding += rxffl;
|
||||
if (SSC_STATE_BSY_GET(ssc_dev->regs->state))
|
||||
outstanding++;
|
||||
|
||||
while (rxffl) /* is 0 in TX-Only mode */
|
||||
{
|
||||
unsigned int rb;
|
||||
int rxbv = _estimate_bv(ssc_dev->rx_bytes, xfer->len);
|
||||
rb = ssc_dev->regs->rb;
|
||||
for (i=0; i<rxbv; i++)
|
||||
{
|
||||
((unsigned char*)xfer->rx_buf)[ssc_dev->rx_bytes] =
|
||||
(rb >> ((rxbv-i-1)*8)) & 0xFF;
|
||||
|
||||
ssc_dev->rx_bytes++;
|
||||
}
|
||||
rxffl--;
|
||||
outstanding--;
|
||||
}
|
||||
ssc_dev->remaining_bytes = xfer->len - ssc_dev->rx_bytes;
|
||||
}
|
||||
|
||||
/* for last Tx cycle set TxFifo threshold to 0 */
|
||||
if ((xfer->len - ssc_dev->tx_bytes) <=
|
||||
(4*(SVIP_SSC_RFIFO_WORDS-1-outstanding)))
|
||||
{
|
||||
ssc_dev->regs->txfcon = SSC_TXFCON_TXFITL_VAL(0) |
|
||||
SSC_TXFCON_TXFEN;
|
||||
}
|
||||
|
||||
while ((ssc_dev->tx_bytes < xfer->len) &&
|
||||
(outstanding < (SVIP_SSC_RFIFO_WORDS-1)))
|
||||
{
|
||||
unsigned int tb = 0;
|
||||
int txbv = _estimate_bv(ssc_dev->tx_bytes, xfer->len);
|
||||
|
||||
for (i=0; i<txbv; i++)
|
||||
{
|
||||
tb |= ((unsigned char*)xfer->tx_buf)[ssc_dev->tx_bytes] <<
|
||||
((txbv-i-1)*8);
|
||||
|
||||
ssc_dev->tx_bytes++;
|
||||
}
|
||||
switch(txbv)
|
||||
{
|
||||
#ifdef __BIG_ENDIAN
|
||||
case 1:
|
||||
*((unsigned char *)(&(ssc_dev->regs->tb))+3) = tb & 0xFF;
|
||||
break;
|
||||
case 2:
|
||||
*((unsigned short *)(&(ssc_dev->regs->tb))+1) = tb & 0xFFFF;
|
||||
break;
|
||||
#else /* __LITTLE_ENDIAN */
|
||||
case 1:
|
||||
*((unsigned char *)(&(ssc_dev->regs->tb))) = tb & 0xFF;
|
||||
break;
|
||||
case 2:
|
||||
*((unsigned short *)(&(ssc_dev->regs->tb))) = tb & 0xFFFF;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ssc_dev->regs->tb = tb;
|
||||
}
|
||||
outstanding++;
|
||||
}
|
||||
}
|
||||
else /* xfer->tx_buf == NULL -> RX only! */
|
||||
{
|
||||
int j;
|
||||
int rxffl = SSC_FSTAT_RXFFL_GET(ssc_dev->regs->fstat);
|
||||
int rxbv = 0;
|
||||
unsigned int rbuf;
|
||||
|
||||
buf_ptr = (unsigned char*)xfer->rx_buf +
|
||||
(xfer->len - ssc_dev->remaining_bytes);
|
||||
|
||||
for (j = 0; j < rxffl; j++)
|
||||
{
|
||||
rxbv = SSC_STATE_RXBV_GET(ssc_dev->regs->state);
|
||||
rbuf = ssc_dev->regs->rb;
|
||||
|
||||
if (rxbv == 4)
|
||||
{
|
||||
*((unsigned int*)buf_ptr+j) = ntohl(rbuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
int b;
|
||||
#ifdef __BIG_ENDIAN
|
||||
for (b = 0; b < rxbv; b++)
|
||||
{
|
||||
buf_ptr[4*j+b] = ((unsigned char*)(&rbuf))[4-rxbv+b];
|
||||
}
|
||||
#else /* __LITTLE_ENDIAN */
|
||||
for (b = 0; b < rxbv; b++)
|
||||
{
|
||||
buf_ptr[4*j+b] = ((unsigned char*)(&rbuf))[rxbv-1-b];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
ssc_dev->remaining_bytes -= rxbv;
|
||||
}
|
||||
if ((ssc_dev->rx_bytes < xfer->len) &&
|
||||
!SSC_STATE_BSY_GET(ssc_dev->regs->state))
|
||||
{
|
||||
int rxreq = min(xfer->len - ssc_dev->rx_bytes,
|
||||
(unsigned)(SVIP_SSC_RFIFO_WORDS*4));
|
||||
|
||||
ssc_dev->rx_bytes += rxreq;
|
||||
ssc_dev->regs->rxreq = rxreq;
|
||||
}
|
||||
|
||||
if (ssc_dev->remaining_bytes < 0)
|
||||
{
|
||||
printk("ssc_dev->remaining_bytes = %d! xfer->len = %d, "
|
||||
"rxffl=%d, rxbv=%d\n", ssc_dev->remaining_bytes, xfer->len,
|
||||
rxffl, rxbv);
|
||||
|
||||
ssc_dev->remaining_bytes = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Submit next message.
|
||||
* lock is held
|
||||
*/
|
||||
static void svip_ssc_next_message(struct spi_master *master)
|
||||
{
|
||||
struct svip_ssc_device *ssc_dev = spi_master_get_devdata(master);
|
||||
struct spi_message *msg;
|
||||
struct spi_device *spi;
|
||||
|
||||
BUG_ON(ssc_dev->current_transfer);
|
||||
|
||||
msg = list_entry(ssc_dev->queue.next, struct spi_message, queue);
|
||||
spi = msg->spi;
|
||||
|
||||
dev_dbg(master->dev.parent, "start message %p on %p\n", msg, spi);
|
||||
|
||||
/* select chip if it's not still active */
|
||||
if (ssc_dev->stay) {
|
||||
if (ssc_dev->stay != spi) {
|
||||
cs_deactivate(ssc_dev, ssc_dev->stay);
|
||||
svip_ssc_setup(spi);
|
||||
cs_activate(ssc_dev, spi);
|
||||
}
|
||||
ssc_dev->stay = NULL;
|
||||
}
|
||||
else {
|
||||
svip_ssc_setup(spi);
|
||||
cs_activate(ssc_dev, spi);
|
||||
}
|
||||
|
||||
svip_ssc_next_xfer(master, msg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Report message completion.
|
||||
* lock is held
|
||||
*/
|
||||
static void
|
||||
svip_ssc_msg_done(struct spi_master *master, struct svip_ssc_device *ssc_dev,
|
||||
struct spi_message *msg, int status, int stay)
|
||||
{
|
||||
if (!stay || status < 0)
|
||||
cs_deactivate(ssc_dev, msg->spi);
|
||||
else
|
||||
ssc_dev->stay = msg->spi;
|
||||
|
||||
list_del(&msg->queue);
|
||||
msg->status = status;
|
||||
|
||||
dev_dbg(master->dev.parent,
|
||||
"xfer complete: %u bytes transferred\n",
|
||||
msg->actual_length);
|
||||
|
||||
spin_unlock(&ssc_dev->lock);
|
||||
msg->complete(msg->context);
|
||||
spin_lock(&ssc_dev->lock);
|
||||
|
||||
ssc_dev->current_transfer = NULL;
|
||||
|
||||
/* continue if needed */
|
||||
if (list_empty(&ssc_dev->queue) || ssc_dev->stopping)
|
||||
; /* TODO: disable hardware */
|
||||
else
|
||||
svip_ssc_next_message(master);
|
||||
}
|
||||
|
||||
static irqreturn_t svip_ssc_eir_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct platform_device *pdev = (struct platform_device*)dev_id;
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct svip_ssc_device *ssc_dev = spi_master_get_devdata(master);
|
||||
|
||||
dev_err (&pdev->dev, "ERROR: errirq. STATE = 0x%0lx\n",
|
||||
ssc_dev->regs->state);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t svip_ssc_rir_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct platform_device *pdev = (struct platform_device*)dev_id;
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct svip_ssc_device *ssc_dev = spi_master_get_devdata(master);
|
||||
struct spi_message *msg;
|
||||
struct spi_transfer *xfer;
|
||||
|
||||
xfer = ssc_dev->current_transfer;
|
||||
msg = list_entry(ssc_dev->queue.next, struct spi_message, queue);
|
||||
|
||||
/* Tx and Rx Interrupts are fairly unpredictable. Just leave interrupt
|
||||
* handler for spurious Interrupts!
|
||||
*/
|
||||
if (!xfer) {
|
||||
dev_dbg(master->dev.parent,
|
||||
"%s(%d): xfer = NULL\n", __FUNCTION__, irq);
|
||||
goto out;
|
||||
}
|
||||
if ( !(xfer->rx_buf) ) {
|
||||
dev_dbg(master->dev.parent,
|
||||
"%s(%d): xfer->rx_buf = NULL\n", __FUNCTION__, irq);
|
||||
goto out;
|
||||
}
|
||||
if (ssc_dev->remaining_bytes > 0)
|
||||
{
|
||||
/*
|
||||
* Keep going, we still have data to send in
|
||||
* the current transfer.
|
||||
*/
|
||||
svip_ssc_next_xfer(master, msg);
|
||||
}
|
||||
|
||||
if (ssc_dev->remaining_bytes == 0)
|
||||
{
|
||||
msg->actual_length += xfer->len;
|
||||
|
||||
if (msg->transfers.prev == &xfer->transfer_list) {
|
||||
/* report completed message */
|
||||
svip_ssc_msg_done(master, ssc_dev, msg, 0,
|
||||
xfer->cs_change);
|
||||
}
|
||||
else {
|
||||
if (xfer->cs_change) {
|
||||
cs_deactivate(ssc_dev, msg->spi);
|
||||
udelay(1); /* not nice in interrupt context */
|
||||
cs_activate(ssc_dev, msg->spi);
|
||||
}
|
||||
|
||||
/* Not done yet. Submit the next transfer. */
|
||||
svip_ssc_next_xfer(master, msg);
|
||||
}
|
||||
}
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t svip_ssc_tir_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct platform_device *pdev = (struct platform_device*)dev_id;
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct svip_ssc_device *ssc_dev = spi_master_get_devdata(master);
|
||||
struct spi_message *msg;
|
||||
struct spi_transfer *xfer;
|
||||
int tx_remain;
|
||||
|
||||
xfer = ssc_dev->current_transfer;
|
||||
msg = list_entry(ssc_dev->queue.next, struct spi_message, queue);
|
||||
|
||||
/* Tx and Rx Interrupts are fairly unpredictable. Just leave interrupt
|
||||
* handler for spurious Interrupts!
|
||||
*/
|
||||
if (!xfer) {
|
||||
dev_dbg(master->dev.parent,
|
||||
"%s(%d): xfer = NULL\n", __FUNCTION__, irq);
|
||||
goto out;
|
||||
}
|
||||
if ( !(xfer->tx_buf) ) {
|
||||
dev_dbg(master->dev.parent,
|
||||
"%s(%d): xfer->tx_buf = NULL\n", __FUNCTION__, irq);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ssc_dev->remaining_bytes > 0)
|
||||
{
|
||||
tx_remain = xfer->len - ssc_dev->tx_bytes;
|
||||
if ( tx_remain == 0 )
|
||||
{
|
||||
dev_dbg(master->dev.parent,
|
||||
"%s(%d): tx_remain = 0\n", __FUNCTION__, irq);
|
||||
}
|
||||
else
|
||||
/*
|
||||
* Keep going, we still have data to send in
|
||||
* the current transfer.
|
||||
*/
|
||||
svip_ssc_next_xfer(master, msg);
|
||||
}
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t svip_ssc_fir_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct platform_device *pdev = (struct platform_device*)dev_id;
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct svip_ssc_device *ssc_dev = spi_master_get_devdata(master);
|
||||
struct spi_message *msg;
|
||||
struct spi_transfer *xfer;
|
||||
|
||||
xfer = ssc_dev->current_transfer;
|
||||
msg = list_entry(ssc_dev->queue.next, struct spi_message, queue);
|
||||
|
||||
/* Tx and Rx Interrupts are fairly unpredictable. Just leave interrupt
|
||||
* handler for spurious Interrupts!
|
||||
*/
|
||||
if (!xfer) {
|
||||
dev_dbg(master->dev.parent,
|
||||
"%s(%d): xfer = NULL\n", __FUNCTION__, irq);
|
||||
goto out;
|
||||
}
|
||||
if ( !(xfer->tx_buf) ) {
|
||||
dev_dbg(master->dev.parent,
|
||||
"%s(%d): xfer->tx_buf = NULL\n", __FUNCTION__, irq);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ssc_dev->remaining_bytes > 0)
|
||||
{
|
||||
int tx_remain = xfer->len - ssc_dev->tx_bytes;
|
||||
|
||||
if (tx_remain == 0)
|
||||
{
|
||||
/* Frame interrupt gets raised _before_ last Rx interrupt */
|
||||
if (xfer->rx_buf)
|
||||
{
|
||||
svip_ssc_next_xfer(master, msg);
|
||||
if (ssc_dev->remaining_bytes)
|
||||
printk("expected RXTX transfer to be complete!\n");
|
||||
}
|
||||
ssc_dev->remaining_bytes = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ssc_dev->regs->sfcon = SSC_SFCON_PLEN_VAL(0) |
|
||||
SSC_SFCON_DLEN_VAL(SFRAME_SIZE*8-1) |
|
||||
SSC_SFCON_STOP |
|
||||
SSC_SFCON_ICLK_VAL(2) |
|
||||
SSC_SFCON_IDAT_VAL(2) |
|
||||
SSC_SFCON_IAEN |
|
||||
SSC_SFCON_SFEN;
|
||||
}
|
||||
}
|
||||
|
||||
if (ssc_dev->remaining_bytes == 0)
|
||||
{
|
||||
msg->actual_length += xfer->len;
|
||||
|
||||
if (msg->transfers.prev == &xfer->transfer_list) {
|
||||
/* report completed message */
|
||||
svip_ssc_msg_done(master, ssc_dev, msg, 0,
|
||||
xfer->cs_change);
|
||||
}
|
||||
else {
|
||||
if (xfer->cs_change) {
|
||||
cs_deactivate(ssc_dev, msg->spi);
|
||||
udelay(1); /* not nice in interrupt context */
|
||||
cs_activate(ssc_dev, msg->spi);
|
||||
}
|
||||
|
||||
/* Not done yet. Submit the next transfer. */
|
||||
svip_ssc_next_xfer(master, msg);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* the spi->mode bits understood by this driver: */
|
||||
#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_LOOP)
|
||||
|
||||
static int svip_ssc_setup(struct spi_device *spi)
|
||||
{
|
||||
struct spi_master *master = spi->master;
|
||||
struct svip_ssc_device *ssc_dev = spi_master_get_devdata(master);
|
||||
unsigned int bits = spi->bits_per_word;
|
||||
unsigned int br, sck_hz = spi->max_speed_hz;
|
||||
unsigned long flags;
|
||||
|
||||
if (ssc_dev->stopping)
|
||||
return -ESHUTDOWN;
|
||||
|
||||
if (spi->chip_select >= master->num_chipselect) {
|
||||
dev_dbg(&spi->dev,
|
||||
"setup: invalid chipselect %u (%u defined)\n",
|
||||
spi->chip_select, master->num_chipselect);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (bits == 0)
|
||||
bits = 8;
|
||||
if (bits != 8) {
|
||||
dev_dbg(&spi->dev,
|
||||
"setup: invalid bits_per_word %u (expect 8)\n",
|
||||
bits);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (spi->mode & ~MODEBITS) {
|
||||
dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n",
|
||||
spi->mode & ~MODEBITS);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Disable SSC */
|
||||
ssc_dev->regs->whbstate = SSC_WHBSTATE_CLREN;
|
||||
|
||||
if (sck_hz == 0)
|
||||
sck_hz = 10000;
|
||||
|
||||
br = ltq_get_fbs0_hz()/(2 *sck_hz);
|
||||
if (ltq_get_fbs0_hz()%(2 *sck_hz) == 0)
|
||||
br = br -1;
|
||||
ssc_dev->regs->br = br;
|
||||
|
||||
/* set Control Register */
|
||||
ssc_dev->regs->mcon = SSC_MCON_ENBV |
|
||||
SSC_MCON_RUEN |
|
||||
SSC_MCON_TUEN |
|
||||
SSC_MCON_AEN |
|
||||
SSC_MCON_REN |
|
||||
SSC_MCON_TEN |
|
||||
(spi->mode & SPI_CPOL ? SSC_MCON_PO : 0) | /* Clock Polarity */
|
||||
(spi->mode & SPI_CPHA ? 0 : SSC_MCON_PH) | /* Tx on trailing edge */
|
||||
(spi->mode & SPI_LOOP ? SSC_MCON_LB : 0) | /* Loopback */
|
||||
(spi->mode & SPI_LSB_FIRST ? 0 : SSC_MCON_HB); /* MSB first */
|
||||
ssc_dev->bus_dir = SSC_UNDEF;
|
||||
|
||||
/* Enable SSC */
|
||||
ssc_dev->regs->whbstate = SSC_WHBSTATE_SETEN;
|
||||
asm("sync");
|
||||
|
||||
spin_lock_irqsave(&ssc_dev->lock, flags);
|
||||
if (ssc_dev->stay == spi)
|
||||
ssc_dev->stay = NULL;
|
||||
cs_deactivate(ssc_dev, spi);
|
||||
spin_unlock_irqrestore(&ssc_dev->lock, flags);
|
||||
|
||||
dev_dbg(&spi->dev,
|
||||
"setup: %u Hz bpw %u mode 0x%02x cs %u\n",
|
||||
sck_hz, bits, spi->mode, spi->chip_select);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int svip_ssc_transfer(struct spi_device *spi, struct spi_message *msg)
|
||||
{
|
||||
struct spi_master *master = spi->master;
|
||||
struct svip_ssc_device *ssc_dev = spi_master_get_devdata(master);
|
||||
struct spi_transfer *xfer;
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(&spi->dev, "new message %p submitted\n", msg);
|
||||
|
||||
if (unlikely(list_empty(&msg->transfers)
|
||||
|| !spi->max_speed_hz)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ssc_dev->stopping)
|
||||
return -ESHUTDOWN;
|
||||
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
if (!(xfer->tx_buf || xfer->rx_buf) || (xfer->len == 0)) {
|
||||
dev_dbg(&spi->dev, "missing rx or tx buf\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* FIXME implement these protocol options!! */
|
||||
if (xfer->bits_per_word || xfer->speed_hz) {
|
||||
dev_dbg(&spi->dev, "no protocol options yet\n");
|
||||
return -ENOPROTOOPT;
|
||||
}
|
||||
|
||||
#ifdef VERBOSE
|
||||
dev_dbg(spi->dev,
|
||||
" xfer %p: len %u tx %p/%08x rx %p/%08x\n",
|
||||
xfer, xfer->len,
|
||||
xfer->tx_buf, xfer->tx_dma,
|
||||
xfer->rx_buf, xfer->rx_dma);
|
||||
#endif
|
||||
}
|
||||
|
||||
msg->status = -EINPROGRESS;
|
||||
msg->actual_length = 0;
|
||||
|
||||
spin_lock_irqsave(&ssc_dev->lock, flags);
|
||||
list_add_tail(&msg->queue, &ssc_dev->queue);
|
||||
if (!ssc_dev->current_transfer)
|
||||
{
|
||||
/* start transmission machine, if not started yet */
|
||||
svip_ssc_next_message(master);
|
||||
}
|
||||
spin_unlock_irqrestore(&ssc_dev->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void svip_ssc_cleanup(struct spi_device *spi)
|
||||
{
|
||||
struct svip_ssc_device *ssc_dev = spi_master_get_devdata(spi->master);
|
||||
unsigned long flags;
|
||||
|
||||
if (!spi->controller_state)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&ssc_dev->lock, flags);
|
||||
if (ssc_dev->stay == spi) {
|
||||
ssc_dev->stay = NULL;
|
||||
cs_deactivate(ssc_dev, spi);
|
||||
}
|
||||
spin_unlock_irqrestore(&ssc_dev->lock, flags);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init svip_ssc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct spi_master *master;
|
||||
struct svip_ssc_device *ssc_dev;
|
||||
struct resource *res_regs;
|
||||
int irq;
|
||||
|
||||
ret = -ENOMEM;
|
||||
|
||||
/* setup spi core then atmel-specific driver state */
|
||||
master = spi_alloc_master(&pdev->dev, sizeof (*ssc_dev));
|
||||
if (!master)
|
||||
{
|
||||
dev_err (&pdev->dev, "ERROR: no memory for master spi\n");
|
||||
goto errout;
|
||||
}
|
||||
|
||||
ssc_dev = spi_master_get_devdata(master);
|
||||
platform_set_drvdata(pdev, master);
|
||||
|
||||
master->bus_num = pdev->id;
|
||||
master->num_chipselect = 8;
|
||||
master->mode_bits = MODEBITS;
|
||||
master->setup = svip_ssc_setup;
|
||||
master->transfer = svip_ssc_transfer;
|
||||
master->cleanup = svip_ssc_cleanup;
|
||||
|
||||
spin_lock_init(&ssc_dev->lock);
|
||||
INIT_LIST_HEAD(&ssc_dev->queue);
|
||||
|
||||
/* retrive register configration */
|
||||
res_regs = platform_get_resource_byname (pdev, IORESOURCE_MEM, "regs");
|
||||
if (NULL == res_regs)
|
||||
{
|
||||
dev_err (&pdev->dev, "ERROR: missed 'regs' resource\n");
|
||||
goto spierr;
|
||||
}
|
||||
|
||||
ssc_dev->regs = (struct svip_reg_ssc*)KSEG1ADDR(res_regs->start);
|
||||
|
||||
irq = platform_get_irq_byname (pdev, "tx");
|
||||
if (irq < 0)
|
||||
goto irqerr;
|
||||
sprintf(ssc_dev->intname[0], "%s_tx", pdev->name);
|
||||
ret = devm_request_irq(&pdev->dev, irq, svip_ssc_tir_handler,
|
||||
IRQF_DISABLED, ssc_dev->intname[0], pdev);
|
||||
if (ret != 0)
|
||||
goto irqerr;
|
||||
|
||||
irq = platform_get_irq_byname (pdev, "rx");
|
||||
if (irq < 0)
|
||||
goto irqerr;
|
||||
sprintf(ssc_dev->intname[1], "%s_rx", pdev->name);
|
||||
ret = devm_request_irq(&pdev->dev, irq, svip_ssc_rir_handler,
|
||||
IRQF_DISABLED, ssc_dev->intname[1], pdev);
|
||||
if (ret != 0)
|
||||
goto irqerr;
|
||||
|
||||
irq = platform_get_irq_byname (pdev, "err");
|
||||
if (irq < 0)
|
||||
goto irqerr;
|
||||
sprintf(ssc_dev->intname[2], "%s_err", pdev->name);
|
||||
ret = devm_request_irq(&pdev->dev, irq, svip_ssc_eir_handler,
|
||||
IRQF_DISABLED, ssc_dev->intname[2], pdev);
|
||||
if (ret != 0)
|
||||
goto irqerr;
|
||||
|
||||
irq = platform_get_irq_byname (pdev, "frm");
|
||||
if (irq < 0)
|
||||
goto irqerr;
|
||||
sprintf(ssc_dev->intname[3], "%s_frm", pdev->name);
|
||||
ret = devm_request_irq(&pdev->dev, irq, svip_ssc_fir_handler,
|
||||
IRQF_DISABLED, ssc_dev->intname[3], pdev);
|
||||
if (ret != 0)
|
||||
goto irqerr;
|
||||
|
||||
/*
|
||||
* Initialize the Hardware
|
||||
*/
|
||||
|
||||
/* Clear enable bit, i.e. put SSC into configuration mode */
|
||||
ssc_dev->regs->whbstate = SSC_WHBSTATE_CLREN;
|
||||
/* enable SSC core to run at fpi clock */
|
||||
ssc_dev->regs->clc = SSC_CLC_RMC_VAL(1);
|
||||
asm("sync");
|
||||
|
||||
/* GPIO CS */
|
||||
ssc_dev->regs->gpocon = SSC_GPOCON_ISCSBN_VAL(0xFF);
|
||||
ssc_dev->regs->whbgpostat = SSC_WHBGPOSTAT_SETOUTN_VAL(0xFF); /* CS to high */
|
||||
|
||||
/* Set Master mode */
|
||||
ssc_dev->regs->whbstate = SSC_WHBSTATE_SETMS;
|
||||
|
||||
/* enable and flush RX/TX FIFO */
|
||||
ssc_dev->regs->rxfcon = SSC_RXFCON_RXFITL_VAL(SVIP_SSC_RFIFO_WORDS-FIFO_HEADROOM) |
|
||||
SSC_RXFCON_RXFLU | /* Receive FIFO Flush */
|
||||
SSC_RXFCON_RXFEN; /* Receive FIFO Enable */
|
||||
|
||||
ssc_dev->regs->txfcon = SSC_TXFCON_TXFITL_VAL(FIFO_HEADROOM) |
|
||||
SSC_TXFCON_TXFLU | /* Transmit FIFO Flush */
|
||||
SSC_TXFCON_TXFEN; /* Transmit FIFO Enable */
|
||||
asm("sync");
|
||||
|
||||
/* enable IRQ */
|
||||
ssc_dev->regs->irnen = SSC_IRNEN_E;
|
||||
|
||||
dev_info(&pdev->dev, "controller at 0x%08lx (irq %d)\n",
|
||||
(unsigned long)ssc_dev->regs, platform_get_irq_byname (pdev, "rx"));
|
||||
|
||||
ret = spi_register_master(master);
|
||||
if (ret)
|
||||
goto out_reset_hw;
|
||||
|
||||
return 0;
|
||||
|
||||
out_reset_hw:
|
||||
|
||||
irqerr:
|
||||
devm_free_irq (&pdev->dev, platform_get_irq_byname (pdev, "tx"), pdev);
|
||||
devm_free_irq (&pdev->dev, platform_get_irq_byname (pdev, "rx"), pdev);
|
||||
devm_free_irq (&pdev->dev, platform_get_irq_byname (pdev, "err"), pdev);
|
||||
devm_free_irq (&pdev->dev, platform_get_irq_byname (pdev, "frm"), pdev);
|
||||
|
||||
spierr:
|
||||
|
||||
spi_master_put(master);
|
||||
|
||||
errout:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit svip_ssc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct svip_ssc_device *ssc_dev = spi_master_get_devdata(master);
|
||||
struct spi_message *msg;
|
||||
|
||||
/* reset the hardware and block queue progress */
|
||||
spin_lock_irq(&ssc_dev->lock);
|
||||
ssc_dev->stopping = 1;
|
||||
/* TODO: shutdown hardware */
|
||||
spin_unlock_irq(&ssc_dev->lock);
|
||||
|
||||
/* Terminate remaining queued transfers */
|
||||
list_for_each_entry(msg, &ssc_dev->queue, queue) {
|
||||
/* REVISIT unmapping the dma is a NOP on ARM and AVR32
|
||||
* but we shouldn't depend on that...
|
||||
*/
|
||||
msg->status = -ESHUTDOWN;
|
||||
msg->complete(msg->context);
|
||||
}
|
||||
|
||||
devm_free_irq (&pdev->dev, platform_get_irq_byname (pdev, "tx"), pdev);
|
||||
devm_free_irq (&pdev->dev, platform_get_irq_byname (pdev, "rx"), pdev);
|
||||
devm_free_irq (&pdev->dev, platform_get_irq_byname (pdev, "err"), pdev);
|
||||
devm_free_irq (&pdev->dev, platform_get_irq_byname (pdev, "frm"), pdev);
|
||||
|
||||
spi_unregister_master(master);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
spi_master_put(master);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int svip_ssc_suspend(struct platform_device *pdev, pm_message_t mesg)
|
||||
{
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct svip_ssc_device *ssc_dev = spi_master_get_devdata(master);
|
||||
|
||||
clk_disable(ssc_dev->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int svip_ssc_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct svip_ssc_device *ssc_dev = spi_master_get_devdata(master);
|
||||
|
||||
clk_enable(ssc_dev->clk);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct platform_driver svip_ssc_driver = {
|
||||
.driver = {
|
||||
.name = "ifx_ssc",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = svip_ssc_probe,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = svip_ssc_suspend,
|
||||
.resume = svip_ssc_resume,
|
||||
#endif
|
||||
.remove = __exit_p(svip_ssc_remove)
|
||||
};
|
||||
|
||||
int __init svip_ssc_init(void)
|
||||
{
|
||||
return platform_driver_register(&svip_ssc_driver);
|
||||
}
|
||||
|
||||
void __exit svip_ssc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&svip_ssc_driver);
|
||||
}
|
||||
|
||||
module_init(svip_ssc_init);
|
||||
module_exit(svip_ssc_exit);
|
||||
|
||||
MODULE_ALIAS("platform:ifx_ssc");
|
||||
MODULE_DESCRIPTION("Lantiq SSC Controller driver");
|
||||
MODULE_AUTHOR("Andreas Schmidt <andreas.schmidt@infineon.com>");
|
||||
MODULE_AUTHOR("Jevgenijs Grigorjevs <Jevgenijs.Grigorjevs@lantiq.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
Reference in New Issue
Block a user