blob: f8b14727243456d5f06a3c7880f285462df07dff [file] [log] [blame]
/*
* c2k_spi.c
*
* Copyright (C) 2004,2005 Mindspeed Technologies, Inc.
*
* 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 <init.h>
#include <linux/byteorder/generic.h>
#include <linux/byteorder/little_endian.h>
#include <spi/spi.h>
#include <common.h>
#include <asm/io.h>
#include <mach/comcerto_spi.h>
#define SPI_FRAME_SIZE_MAX 16
#define SPI_FRAME_SIZE_MIN 4
#define SPI_CHIP_SELECT_MAX 15
#define SPI_BAUDR_MIN 2
#define SPI_BAUDR_MAX 0xFFFE
#define SPI_SPEED_MAX (4*1000*1000)
#define SPI_CTRLR0_SCPOL (1 << 7)
#define SPI_CTRLR0_SCPH (1 << 6)
#define COMCERTO_DEFAULTAXICLK 200000000 /* Hz */
#ifdef COMCERTO_SPI_DEBUG
#define spi_debug(fmt, arg...) printf(fmt, ##arg)
#else
#define spi_debug(fmt, arg...) ;
#endif
static int spi_udelay;
extern int do_write_read_transfer(struct spi_adapter *, u8, u8 *, int *, u8 *rbuf, int *);
extern int do_write_only_transfer8(struct spi_adapter *, u8 *, int *);
extern int do_write_only_transfer16(struct spi_adapter *, u16 *, int *);
extern int do_read_only_transfer8(struct spi_adapter *, u8 *, int *);
extern int do_read_only_transfer16(struct spi_adapter *, u16 *, int *);
extern u32 HAL_get_axi_clk(void);
extern int spi_register_master(struct spi_master *master);
/**
* comcerto_spi_hw_init -
*
*
*/
static void comcerto_spi_hw_init(struct spi_adapter *adaptr)
{
/* disable SPI operation */
writel(0, adaptr->membase + COMCERTO_SPI_SSIENR);
/* mask all SPI irq's */
writel(0, adaptr->membase + COMCERTO_SPI_IMR);
}
/**
* comcerto_spi_hw_reset -
*
*
*/
#if 0
static void comcerto_spi_hw_reset(struct spi_adapter *adaptr)
{
/* disable SPI operation */
writel(0, adaptr->membase + COMCERTO_SPI_SSIENR);
/* mask all SPI irq's */
writel(0, adaptr->membase + COMCERTO_SPI_IMR);
}
#endif
static int c2k_spi_setup(struct spi_device *spi)
{
u32 hz;
spi_debug("%s mode 0x%08x bits_per_word: %d speed: %d\n",
__FUNCTION__, spi->mode, spi->bits_per_word,
spi->max_speed_hz);
hz = COMCERTO_DEFAULTAXICLK; //HAL_get_axi_clk();
if (!spi->bits_per_word)
spi->bits_per_word = 8;
if(spi->bits_per_word < SPI_FRAME_SIZE_MIN || spi->bits_per_word > SPI_FRAME_SIZE_MAX)
return -1;
if (spi->max_speed_hz < (hz/SPI_BAUDR_MAX))
return -1;
if (spi->max_speed_hz > SPI_SPEED_MAX) {
printf("decreasing speed %u to max supported %u\n",
spi->max_speed_hz, SPI_SPEED_MAX);
spi->max_speed_hz = SPI_SPEED_MAX;
}
if (spi->chip_select > SPI_CHIP_SELECT_MAX) {
printf("chip select %u out of range 0..%u\n",
spi->max_speed_hz, SPI_CHIP_SELECT_MAX);
return -1;
}
spi_udelay = 1 + ((1000000 * spi->bits_per_word) / spi->max_speed_hz);
return 0;
}
static int c2k_spi_transfer(struct spi_device *spi, struct spi_message *mesg)
{
struct spi_transfer *t = NULL;
unsigned int op = mesg->status & 0xff; /* what operation */
int ret = -1;
struct spi_adapter *adapter = container_of(spi->master, struct spi_adapter, master);
u32 ctrlr0 = 0;
u32 baudr = 0;
u32 ser = 0;
u32 hz;
if (spi->mode & SPI_CPOL)
ctrlr0 |= SPI_CTRLR0_SCPOL;
if (spi->mode & SPI_CPHA)
ctrlr0 |= SPI_CTRLR0_SCPH;
ctrlr0 |= (spi->bits_per_word - 1) & 0xf;
hz = COMCERTO_DEFAULTAXICLK; //Later on will use HAL_get_axi_clk() to get the current AXI freq
if (spi->max_speed_hz > 0) {
baudr = hz / spi->max_speed_hz;
if (baudr < SPI_BAUDR_MIN)
baudr = SPI_BAUDR_MIN;
if (baudr > SPI_BAUDR_MAX)
baudr = SPI_BAUDR_MAX;
}
else
{
baudr = 0;
printf("%s: Baud rate not set!!\n", __func__);
}
ser = (1 << spi->chip_select) ;
while(readl(adapter->membase + COMCERTO_SPI_SR) & BUSY);
writel(0, adapter->membase + COMCERTO_SPI_SSIENR);
list_for_each_entry (t, &mesg->transfers, transfer_list) {
u8 *txbuf = (u8 *)t->tx_buf;
u8 *rxbuf = t->rx_buf;
switch(op) {
case SPI_TRANSFER_MODE_WRITE_ONLY:
ctrlr0 |= (0x0001 << 8);
writel(ctrlr0, adapter->membase + COMCERTO_SPI_CTRLR0);
writel(baudr, adapter->membase + COMCERTO_SPI_BAUDR);
writel(ser, adapter->membase + COMCERTO_SPI_SER);
writel(8, adapter->membase + COMCERTO_SPI_RXFTLR);
writel(0, adapter->membase + COMCERTO_SPI_TXFTLR);
writel(0, adapter->membase + COMCERTO_SPI_IMR);
writel(1, adapter->membase + COMCERTO_SPI_SSIENR);
if (spi->bits_per_word <= 8)
ret = do_write_only_transfer8(adapter, txbuf, &t->len);
else
ret = do_write_only_transfer16(adapter, (u16*)txbuf, &t->len);
break;
case SPI_TRANSFER_MODE_READ_ONLY:
ctrlr0 |= (0x0002 << 8);
writel(ctrlr0, adapter->membase + COMCERTO_SPI_CTRLR0);
writel(baudr, adapter->membase + COMCERTO_SPI_BAUDR);
writel(ser, adapter->membase + COMCERTO_SPI_SER);
writel(8, adapter->membase + COMCERTO_SPI_RXFTLR);
writel(0, adapter->membase + COMCERTO_SPI_TXFTLR);
writel(0, adapter->membase + COMCERTO_SPI_IMR);
writel(1, adapter->membase + COMCERTO_SPI_SSIENR);
if (spi->bits_per_word <= 8)
ret = do_read_only_transfer8(adapter, rxbuf, &t->len);
else
ret = do_read_only_transfer16(adapter, (u16 *)rxbuf, &t->len);
break;
case SPI_TRANSFER_MODE_WRITE_READ:
ctrlr0 |= (0x0000 << 8);
writel(ctrlr0, adapter->membase + COMCERTO_SPI_CTRLR0);
writel(baudr, adapter->membase + COMCERTO_SPI_BAUDR);
writel(ser, adapter->membase + COMCERTO_SPI_SER);
writel(8, adapter->membase + COMCERTO_SPI_RXFTLR);
writel(0, adapter->membase + COMCERTO_SPI_TXFTLR);
writel(0, adapter->membase + COMCERTO_SPI_IMR);
writel(1, adapter->membase + COMCERTO_SPI_SSIENR);
ret = do_write_read_transfer(adapter, spi->bits_per_word, txbuf, &t->len, rxbuf, &t->len);
break;
default:
printk ("Transfer mode not supported.\n");
return ret;
};
/* deassert the chip select at least for this long */
udelay (spi_udelay);
while(readl(adapter->membase + COMCERTO_SPI_SR) & BUSY);
}
return 0;
}
static int c2k_spi_probe(struct device_d *dev)
{
struct spi_master *master;
struct spi_adapter *adapter;
struct c2k_spi_master *pdata = dev->platform_data;
printf("%s\n",__func__);
adapter = (struct spi_adapter *) xzalloc(sizeof(struct spi_adapter));
master = &adapter->master;
master->dev = dev;
master->setup = c2k_spi_setup;
master->transfer = c2k_spi_transfer;
master->bus_num = 0;
master->num_chipselect = pdata->num_chipselect;
adapter->membase = dev->map_base;
comcerto_spi_hw_init(adapter);
spi_register_master(master);
return 0;
}
/**
* comcerto_spi_remove -
*
*
*/
#if 0
static int comcerto_spi_remove(struct spi_adapter *spi)
{
spi_unregister_master(spi->master);
comcerto_spi_hw_reset(spi);
return 0;
}
#endif
/* barebox framework */
static struct driver_d c2k_spi_driver = {
.name = "c2k_spi",
.probe = c2k_spi_probe,
};
int c2k_spi_init(void)
{
register_driver(&c2k_spi_driver);
return 0;
}
device_initcall(c2k_spi_init);
/***/