blob: 4211eded4395c60cc3e47a63a9f1f73ff33665da [file] [log] [blame]
/*
* designware_spi.c
*
* Synopsys DesignWare AMBA SPI controller driver (master mode only)
*
* Author: Baruch Siach, Tk Open Systems
* baruch-NswTu9S1W3P6gbPvEgmw2w@...org
*
* Base on the Xilinx SPI controller driver by MontaVista
*
* 2002-2007 (c) MontaVista Software, Inc. This file is licensed under the
* terms of the GNU General Public License version 2. This program is licensed
* "as is" without any warranty of any kind, whether express or implied.
*
* 2008, 2009 (c) Provigent Ltd.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/spi/designware.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <mach/reset.h>
#define DESIGNWARE_SPI_NAME "comcerto_spi"
/* Register definitions as per "DesignWare DW_apb_ssi Databook", Capture 6. */
#define DWSPI_CTRLR0 0x00
#define DWSPI_CTRLR1 0x04
#define DWSPI_SSIENR 0x08
#define DWSPI_MWCR 0x0C
#define DWSPI_SER 0x10
#define DWSPI_BAUDR 0x14
#define DWSPI_TXFTLR 0x18
#define DWSPI_RXFTLR 0x1C
#define DWSPI_TXFLR 0x20
#define DWSPI_RXFLR 0x24
#define DWSPI_SR 0x28
#define DWSPI_IMR 0x2C
#define DWSPI_ISR 0x30
#define DWSPI_RISR 0x34
#define DWSPI_TXOICR 0x38
#define DWSPI_RXOICR 0x3C
#define DWSPI_RXUICR 0x40
#define DWSPI_ICR 0x44
#define DWSPI_DMACR 0x4C
#define DWSPI_DMATDLR 0x50
#define DWSPI_DMARDLR 0x54
#define DWSPI_IDR 0x58
#define DWSPI_SSI_COMP_VERSION 0x5C
#define DWSPI_DR 0x60
#define DWSPI_CTRLR0_DFS_MASK 0x000f
#define DWSPI_CTRLR0_SCPOL 0x0080
#define DWSPI_CTRLR0_SCPH 0x0040
#define DWSPI_CTRLR0_TMOD_MASK 0x0300
#define DWSPI_SR_BUSY_MASK 0x01
#define DWSPI_SR_TFNF_MASK 0x02
#define DWSPI_SR_TFE_MASK 0x04
#define DWSPI_SR_RFNE_MASK 0x08
#define DWSPI_SR_RFF_MASK 0x10
#define DWSPI_ISR_TXEIS_MASK 0x01
#define DWSPI_ISR_RXFIS_MASK 0x10
#define DWSPI_IMR_TXEIM_MASK 0x01
#define DWSPI_IMR_RXFIM_MASK 0x10
struct designware_spi {
struct device *dev;
struct workqueue_struct *workqueue;
struct work_struct work;
struct tasklet_struct pump_transfers;
struct mutex lock; /* lock this struct except from queue */
struct list_head queue; /* spi_message queue */
spinlock_t qlock; /* lock the queue */
void __iomem *regs; /* virt. address of the control registers */
unsigned int ssi_clk; /* clock in Hz */
unsigned int tx_fifo_depth; /* bytes in TX FIFO */
unsigned int rx_fifo_depth; /* bytes in RX FIFO */
u32 irq;
u8 bits_per_word; /* current data frame size */
struct spi_transfer *tx_t; /* current tx transfer */
struct spi_transfer *rx_t; /* current rx transfer */
const u8 *tx_ptr; /* current tx buffer */
u8 *rx_ptr; /* current rx buffer */
int remaining_tx_bytes; /* bytes left to tx in the current transfer */
int remaining_rx_bytes; /* bytes left to rx in the current transfer */
int status; /* status of the current spi_transfer */
struct completion done; /* signal the end of tx for current sequence */
struct spi_device *spi; /* current spi slave device */
struct clk *clk_spi;
struct list_head *transfers_list; /* head of the current sequence */
unsigned int tx_count, rx_count; /* bytes in the current sequence */
};
static void dwspi_init_hw(struct designware_spi *dwspi)
{
u16 ctrlr0;
/* Disable the SPI master */
writel(0, dwspi->regs + DWSPI_SSIENR);
/* Disable all the interrupts just in case */
writel(0, dwspi->regs + DWSPI_IMR);
/* Set TX empty IRQ threshold */
writew(dwspi->tx_fifo_depth / 2, dwspi->regs + DWSPI_TXFTLR);
/* Set transmit & receive mode */
ctrlr0 = readw(dwspi->regs + DWSPI_CTRLR0);
ctrlr0 &= ~DWSPI_CTRLR0_TMOD_MASK;
writew(ctrlr0, dwspi->regs + DWSPI_CTRLR0);
}
static void dwspi_baudcfg(struct designware_spi *dwspi, u32 speed_hz)
{
/* Divisor must be an even number */
u16 div = (speed_hz) ? DIV_ROUND_UP(dwspi->ssi_clk, speed_hz) + 1 &
0xfffe : 0xffff;
writew(div, dwspi->regs + DWSPI_BAUDR);
}
static void dwspi_enable(struct designware_spi *dwspi, int on)
{
writel(on ? 1 : 0, dwspi->regs + DWSPI_SSIENR);
}
static void designware_spi_chipselect(struct spi_device *spi, int on)
{
struct designware_spi *dwspi = spi_master_get_devdata(spi->master);
#if 0
long gpio = (long) spi->controller_data;
unsigned active = spi->mode & SPI_CS_HIGH;
#endif
/*
* Note, the SPI controller must have been enabled at this point, i.e.
* SSIENR == 1
*/
if (on) {
#if 0
/* Turn the actual chip select on for GPIO chip selects */
if (gpio >= 0)
gpio_set_value(gpio, active);
#endif
/* Activate slave on the SPI controller */
writel(1 << spi->chip_select, dwspi->regs + DWSPI_SER);
} else {
/* Deselect the slave on the SPI bus */
writel(0, dwspi->regs + DWSPI_SER);
#if 0
if (gpio >= 0)
gpio_set_value(gpio, !active);
#endif
}
}
static int designware_spi_setup_transfer(struct spi_device *spi,
struct spi_transfer *t)
{
u8 bits_per_word;
u32 hz;
struct designware_spi *dwspi = spi_master_get_devdata(spi->master);
u16 ctrlr0 = readw(dwspi->regs + DWSPI_CTRLR0);
bits_per_word = (t) ? (t->bits_per_word ? t->bits_per_word : spi->bits_per_word) : spi->bits_per_word;
hz = (t) ? (t->speed_hz ? t->speed_hz : spi->max_speed_hz) : spi->max_speed_hz;
if (bits_per_word < 4 || bits_per_word > 16) {
dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
__func__, bits_per_word);
return -EINVAL;
} else {
ctrlr0 &= ~DWSPI_CTRLR0_DFS_MASK;
ctrlr0 |= bits_per_word - 1;
dwspi->bits_per_word = bits_per_word;
}
/* Set the SPI clock phase and polarity */
if (spi->mode & SPI_CPHA)
ctrlr0 |= DWSPI_CTRLR0_SCPH;
else
ctrlr0 &= ~DWSPI_CTRLR0_SCPH;
if (spi->mode & SPI_CPOL)
ctrlr0 |= DWSPI_CTRLR0_SCPOL;
else
ctrlr0 &= ~DWSPI_CTRLR0_SCPOL;
writew(ctrlr0, dwspi->regs + DWSPI_CTRLR0);
/* set speed */
dwspi_baudcfg(dwspi, hz);
return 0;
}
/* the spi->mode bits currently understood by this driver: */
#define MODEBITS (SPI_CPOL | SPI_CPHA)
static int designware_spi_setup(struct spi_device *spi)
{
struct designware_spi *dwspi;
int retval;
dwspi = spi_master_get_devdata(spi->master);
if (!spi->bits_per_word)
spi->bits_per_word = 8;
if (spi->mode & ~MODEBITS) {
dev_err(&spi->dev, "%s, SP unsupported mode bits %x\n",
__func__, spi->mode & ~MODEBITS);
return -EINVAL;
}
if (spi->chip_select > spi->master->num_chipselect) {
dev_err(&spi->dev,
"setup: invalid chipselect %u (%u defined)\n",
spi->chip_select, spi->master->num_chipselect);
return -EINVAL;
}
retval = designware_spi_setup_transfer(spi, NULL);
if (retval < 0)
return retval;
dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n",
__func__, spi->mode & MODEBITS, spi->bits_per_word, 0);
return 0;
}
static void designware_spi_do_tx(struct designware_spi *dwspi)
{
u8 sr;
int bytes_to_tx = dwspi->remaining_tx_bytes;
u8 valid_tx_fifo_bytes = readb(dwspi->regs + DWSPI_TXFLR);
u8 valid_rx_fifo_bytes = readb(dwspi->regs + DWSPI_RXFLR);
int tx_limit = min(dwspi->tx_fifo_depth - valid_tx_fifo_bytes,\
dwspi->rx_fifo_depth - valid_rx_fifo_bytes);
/* Fill the Tx FIFO with as many bytes as possible */
sr = readb(dwspi->regs + DWSPI_SR);
while ((sr & DWSPI_SR_TFNF_MASK) && dwspi->remaining_tx_bytes > 0) {
if (dwspi->bits_per_word <= 8) {
u8 dr = (dwspi->tx_ptr) ? *dwspi->tx_ptr++ : 0;
writeb(dr, dwspi->regs + DWSPI_DR);
dwspi->remaining_tx_bytes--;
} else {
u16 dr = (dwspi->tx_ptr) ? *(u16 *) dwspi->tx_ptr : 0;
dwspi->tx_ptr += 2;
writew(dr, dwspi->regs + DWSPI_DR);
dwspi->remaining_tx_bytes -= 2;
}
if(dwspi->bits_per_word <= 8){
--tx_limit;
}else{
tx_limit -= 2;
}
if (tx_limit <= 0)
break;
sr = readb(dwspi->regs + DWSPI_SR);
}
dwspi->tx_count += bytes_to_tx - dwspi->remaining_tx_bytes;
}
/* Return 1 when done, 0 otherwise */
static int designware_spi_fill_tx_fifo(struct designware_spi *dwspi)
{
unsigned cs_change = 0;
unsigned int ser;
ser = readw(dwspi->regs + DWSPI_SER);
list_for_each_entry_from(dwspi->tx_t, dwspi->transfers_list,
transfer_list) {
if (dwspi->remaining_tx_bytes == 0) {
/* Initialize new spi_transfer */
dwspi->tx_ptr = dwspi->tx_t->tx_buf;
dwspi->remaining_tx_bytes = dwspi->tx_t->len;
dwspi->status = 0;
if (!dwspi->tx_t->tx_buf && !dwspi->tx_t->rx_buf
&& dwspi->tx_t->len) {
dwspi->status = -EINVAL;
break;
}
if (cs_change)
break;
}
designware_spi_do_tx(dwspi);
/* Don't advance dwspi->tx_t, we'll get back to this
* spi_transfer later
*/
if (dwspi->remaining_tx_bytes > 0)
{
return 0;
}
cs_change = dwspi->tx_t->cs_change;
}
complete(&dwspi->done);
return 1;
}
static void designware_spi_do_rx(struct designware_spi *dwspi)
{
u8 sr;
int bytes_to_rx = dwspi->remaining_rx_bytes;
sr = readb(dwspi->regs + DWSPI_SR);
if (sr & DWSPI_SR_RFF_MASK) {
#if 0
dev_err(dwspi->dev, "%s: RX FIFO overflow\n", __func__);
dwspi->status = -EIO;
#endif
}
/* Read as long as RX FIFO is not empty */
while ((sr & DWSPI_SR_RFNE_MASK) != 0
&& dwspi->remaining_rx_bytes > 0) {
int rx_level = readl(dwspi->regs + DWSPI_RXFLR);
while (rx_level-- && dwspi->remaining_rx_bytes > 0) {
if (dwspi->bits_per_word <= 8) {
u8 data;
data = readb(dwspi->regs + DWSPI_DR);
dwspi->remaining_rx_bytes--;
if (dwspi->rx_ptr)
*dwspi->rx_ptr++ = data;
} else {
u16 data;
data = readw(dwspi->regs + DWSPI_DR);
dwspi->remaining_rx_bytes -= 2;
if (dwspi->rx_ptr) {
*(u16 *) dwspi->rx_ptr = data;
dwspi->rx_ptr += 2;
}
}
}
sr = readb(dwspi->regs + DWSPI_SR);
}
dwspi->rx_count += (bytes_to_rx - dwspi->remaining_rx_bytes);
}
/* return 1 if cs_change is true, 0 otherwise */
static int designware_spi_read_rx_fifo(struct designware_spi *dwspi)
{
unsigned cs_change = 0;
unsigned int ser;
ser = readw(dwspi->regs + DWSPI_SER);
list_for_each_entry_from(dwspi->rx_t, dwspi->transfers_list,
transfer_list) {
if (dwspi->remaining_rx_bytes == 0) {
dwspi->rx_ptr = dwspi->rx_t->rx_buf;
dwspi->remaining_rx_bytes = dwspi->rx_t->len;
if (cs_change)
return 1;
}
designware_spi_do_rx(dwspi);
/* The rx buffer is filling up with more bytes. Don't advance
* dwspi->rx_t, as we have more bytes to read in this
* spi_transfer.
*/
if (dwspi->remaining_rx_bytes > 0)
return 0;
cs_change = dwspi->rx_t->cs_change;
}
return 0;
}
/* interate through the list of spi_transfer elements.
* stop at the end of the list or when t->cs_change is true.
*/
static void designware_spi_do_transfers(struct designware_spi *dwspi)
{
int tx_done, cs_change;
init_completion(&dwspi->done);
/* transfer kickoff */
tx_done = designware_spi_fill_tx_fifo(dwspi);
designware_spi_chipselect(dwspi->spi, 1);
if (!tx_done) {
/* Enable the transmit empty interrupt, which we use to
* determine progress on the transmission in case we're
* not done yet.
*/
writeb(DWSPI_IMR_TXEIM_MASK, dwspi->regs + DWSPI_IMR);
/* wait for tx completion */
wait_for_completion(&dwspi->done);
}
/* This delay should be good enough for 100KHz spi transfers. Slower
* transfers may need a longer delay.
*/
//udelay(10);
/* get remaining rx bytes */
do {
cs_change = designware_spi_read_rx_fifo(dwspi);
} while (readb(dwspi->regs + DWSPI_SR) &
(DWSPI_SR_BUSY_MASK | DWSPI_SR_RFNE_MASK));
/* transaction is done */
designware_spi_chipselect(dwspi->spi, 0);
if (dwspi->status < 0)
return;
if (!cs_change && (dwspi->remaining_rx_bytes > 0 ||
dwspi->remaining_tx_bytes > 0)) {
#if 0
dev_err(dwspi->dev, "%s: remaining_rx_bytes = %d, "
"remaining_tx_bytes = %d\n",
__func__, dwspi->remaining_rx_bytes,
dwspi->remaining_tx_bytes);
#endif
dwspi->status = -EIO;
}
if (dwspi->rx_count != dwspi->tx_count) {
#if 0
dev_err(dwspi->dev, "%s: rx_count == %d, tx_count == %d\n",
__func__, dwspi->rx_count, dwspi->tx_count);
#endif
dwspi->status = -EIO;
}
}
static int designware_spi_transfer(struct spi_device *spi,
struct spi_message *mesg)
{
struct designware_spi *dwspi = spi_master_get_devdata(spi->master);
mesg->actual_length = 0;
mesg->status = -EINPROGRESS;
/* we can't block here, so we use a spinlock
* here instead of the global mutex
*/
spin_lock(&dwspi->qlock);
list_add_tail(&mesg->queue, &dwspi->queue);
queue_work(dwspi->workqueue, &dwspi->work);
spin_unlock(&dwspi->qlock);
return 0;
}
static void designware_work(struct work_struct *work)
{
struct designware_spi *dwspi = container_of(work,
struct designware_spi, work);
mutex_lock(&dwspi->lock);
spin_lock(&dwspi->qlock);
while (!list_empty(&dwspi->queue)) {
struct spi_message *m;
m = container_of(dwspi->queue.next, struct spi_message, queue);
list_del_init(&m->queue);
spin_unlock(&dwspi->qlock);
dwspi->spi = m->spi;
dwspi->tx_t = dwspi->rx_t =
list_first_entry(&m->transfers, struct spi_transfer,
transfer_list);
/*
* Interate through groups of spi_transfer structs
* that are separated by cs_change being true
*/
dwspi->transfers_list = &m->transfers;
do {
dwspi->remaining_tx_bytes =
dwspi->remaining_rx_bytes = 0;
dwspi->tx_count = dwspi->rx_count = 0;
designware_spi_setup_transfer(m->spi, dwspi->tx_t);
dwspi_enable(dwspi, 1);
designware_spi_do_transfers(dwspi);
dwspi_enable(dwspi, 0);
if (dwspi->status < 0)
break;
m->actual_length +=
dwspi->tx_count; /* same as rx_count */
} while (&dwspi->tx_t->transfer_list != &m->transfers);
m->status = dwspi->status;
m->complete(m->context);
spin_lock(&dwspi->qlock);
}
spin_unlock(&dwspi->qlock);
mutex_unlock(&dwspi->lock);
}
static void designware_pump_transfers(unsigned long data)
{
struct designware_spi *dwspi = (struct designware_spi *) data;
designware_spi_read_rx_fifo(dwspi);
if (!designware_spi_fill_tx_fifo(dwspi))
/* reenable the interrupt */
writeb(DWSPI_IMR_TXEIM_MASK, dwspi->regs + DWSPI_IMR);
}
static irqreturn_t designware_spi_irq(int irq, void *dev_id)
{
struct designware_spi *dwspi = dev_id;
tasklet_schedule(&dwspi->pump_transfers);
/* disable the interrupt for now */
writeb(0, dwspi->regs + DWSPI_IMR);
return IRQ_HANDLED;
}
static void designware_spi_cleanup(struct spi_device *spi)
{
}
static int __init designware_spi_probe(struct platform_device *dev)
{
int ret = 0;
struct spi_master *master;
struct designware_spi *dwspi;
//struct designware_platform_data *pdata;
struct spi_controller_pdata *pdata;
struct resource *r;
struct clk *clk_spi;
pdata = dev->dev.platform_data;
if (pdata == NULL) {
dev_err(&dev->dev, "no device data specified\n");
return -EINVAL;
}
if(memcmp(pdata->clk_name, "DUS", 3))
c2000_block_reset(COMPONENT_AXI_LEGACY_SPI, 0);
else
c2000_block_reset(COMPONENT_AXI_FAST_SPI, 0);
clk_spi = clk_get(NULL,pdata->clk_name);
if (IS_ERR(clk_spi)) {
ret = PTR_ERR(clk_spi);
pr_err("%s:Unable to obtain spi clock: %d\n",\
__func__, ret);
goto err_clk;
}
clk_enable(clk_spi);
printk ("%s:Initializing SPI Controller : Using dma=%d CLK(%s)=%ld Hz\n", __func__, \
pdata->use_dma, pdata->clk_name, clk_get_rate(clk_spi));
/* Get resources(memory, IRQ) associated with the device */
master = spi_alloc_master(&dev->dev, sizeof(struct designware_spi));
if (master == NULL) {
ret = -ENOMEM;
goto err_nomem;
}
master->mode_bits = SPI_CPOL | SPI_CPHA;
master->bus_num = pdata->bus_num;
master->num_chipselect = pdata->num_chipselects;
master->setup = designware_spi_setup;
master->transfer = designware_spi_transfer;
master->cleanup = designware_spi_cleanup;
platform_set_drvdata(dev, master);
r = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (r == NULL) {
ret = -ENODEV;
goto put_master;
}
dwspi = spi_master_get_devdata(master);
dwspi->clk_spi = clk_spi;
dwspi->ssi_clk = clk_get_rate(dwspi->clk_spi);
//dwspi->ssi_clk = pdata->max_freq;
dwspi->tx_fifo_depth = TX_FIFO_DEPTH;
dwspi->rx_fifo_depth = RX_FIFO_DEPTH;
#if 0
dwspi->tx_fifo_depth = pdata->tx_fifo_depth;
dwspi->rx_fifo_depth = pdata->rx_fifo_depth;
#endif
dwspi->dev = &dev->dev;
spin_lock_init(&dwspi->qlock);
mutex_init(&dwspi->lock);
INIT_LIST_HEAD(&dwspi->queue);
INIT_WORK(&dwspi->work, designware_work);
dwspi->workqueue =
create_singlethread_workqueue(dev_name(master->dev.parent));
if (dwspi->workqueue == NULL) {
ret = -EBUSY;
goto put_master;
}
tasklet_init(&dwspi->pump_transfers, designware_pump_transfers,
(unsigned long) dwspi);
if (!request_mem_region(r->start,
r->end - r->start + 1, DESIGNWARE_SPI_NAME)) {
ret = -ENXIO;
goto destroy_wq;
}
dwspi->regs = ioremap(r->start, r->end - r->start + 1);
if (dwspi->regs == NULL) {
ret = -ENOMEM;
goto destroy_wq;
}
dwspi->irq = platform_get_irq(dev, 0);
if (dwspi->irq < 0) {
ret = -ENXIO;
goto unmap_io;
}
/* SPI controller initializations */
dwspi_init_hw(dwspi);
/* Register for SPI Interrupt */
ret = request_irq(dwspi->irq, designware_spi_irq, 0,
DESIGNWARE_SPI_NAME, dwspi);
if (ret != 0)
goto unmap_io;
ret = spi_register_master(master);
if (ret < 0)
goto free_irq;
dev_info(&dev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n",
r->start, (u32)dwspi->regs, dwspi->irq);
return ret;
free_irq:
free_irq(dwspi->irq, dwspi);
unmap_io:
iounmap(dwspi->regs);
destroy_wq:
destroy_workqueue(dwspi->workqueue);
put_master:
spi_master_put(master);
err_nomem:
clk_disable(clk_spi);
clk_put(clk_spi);
err_clk:
if(memcmp(pdata->clk_name, "DUS", 3))
c2000_block_reset(COMPONENT_AXI_LEGACY_SPI, 1);
else
c2000_block_reset(COMPONENT_AXI_FAST_SPI, 1);
return ret;
}
static int __devexit designware_spi_remove(struct platform_device *dev)
{
struct designware_spi *dwspi;
struct spi_master *master;
struct spi_controller_pdata *pdata;
master = platform_get_drvdata(dev);
dwspi = spi_master_get_devdata(master);
free_irq(dwspi->irq, dwspi);
iounmap(dwspi->regs);
destroy_workqueue(dwspi->workqueue);
tasklet_kill(&dwspi->pump_transfers);
platform_set_drvdata(dev, 0);
spi_master_put(master);
clk_disable(dwspi->clk_spi);
clk_put(dwspi->clk_spi);
pdata = dev->dev.platform_data;
if(!pdata)
{
return -EINVAL;
}
if(memcmp(pdata->clk_name, "DUS", 3))
c2000_block_reset(COMPONENT_AXI_LEGACY_SPI, 1);
else
c2000_block_reset(COMPONENT_AXI_FAST_SPI, 1);
return 0;
}
#if CONFIG_PM
static int designware_spi_suspend(struct platform_device *pdev, pm_message_t state)
{
struct designware_spi *dwspi;
struct spi_master *master;
master = platform_get_drvdata(pdev);
dwspi = spi_master_get_devdata(master);
/* Disable the SPI master */
writel(0, dwspi->regs + DWSPI_SSIENR);
clk_disable(dwspi->clk_spi);
return 0;
}
static int designware_spi_resume(struct platform_device *pdev)
{
struct designware_spi *dwspi;
struct spi_master *master;
master = platform_get_drvdata(pdev);
dwspi = spi_master_get_devdata(master);
clk_enable(dwspi->clk_spi);
dwspi_init_hw(dwspi);
return 0;
}
#endif
/* work with hotplug and coldplug */
MODULE_ALIAS("platform:" DESIGNWARE_SPI_NAME);
static struct platform_driver designware_spi_driver = {
.probe = designware_spi_probe,
.remove = __devexit_p(designware_spi_remove),
#if CONFIG_PM
.suspend = designware_spi_suspend,
.resume = designware_spi_resume,
#endif
.driver = {
.name = DESIGNWARE_SPI_NAME,
.owner = THIS_MODULE,
},
};
static int __init designware_spi_init(void)
{
return platform_driver_register(&designware_spi_driver);
}
module_init(designware_spi_init);
static void __exit designware_spi_exit(void)
{
platform_driver_unregister(&designware_spi_driver);
}
module_exit(designware_spi_exit);
MODULE_AUTHOR("Baruch Siach <baruch-NswTu9S1W3P6gbPvEgmw2w@...org>");
MODULE_DESCRIPTION("Synopsys DesignWare SPI driver");
MODULE_LICENSE("GPL");