/*
* Analog Devices SPI3 controller driver
*
* Copyright (c) 2014 Analog Devices Inc.
*
* 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.
*
* 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.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/spi/adi_spi3.h>
#include <linux/types.h>
#include <asm/dma.h>
#include <asm/portmux.h>
enum adi_spi_state {
START_STATE,
RUNNING_STATE,
DONE_STATE,
ERROR_STATE
};
struct adi_spi_master;
struct adi_spi_transfer_ops {
void (*write) (struct adi_spi_master *);
void (*read) (struct adi_spi_master *);
void (*duplex) (struct adi_spi_master *);
};
/* runtime info for spi master */
struct adi_spi_master {
/* SPI framework hookup */
struct spi_master *master;
/* Regs base of SPI controller */
struct adi_spi_regs __iomem *regs;
/* Pin request list */
u16 *pin_req;
/* Message Transfer pump */
struct tasklet_struct pump_transfers;
/* Current message transfer state info */
struct spi_message *cur_msg;
struct spi_transfer *cur_transfer;
struct adi_spi_device *cur_chip;
unsigned transfer_len;
/* transfer buffer */
void *tx;
void *tx_end;
void *rx;
void *rx_end;
/* dma info */
unsigned int tx_dma;
unsigned int rx_dma;
dma_addr_t tx_dma_addr;
dma_addr_t rx_dma_addr;
unsigned long dummy_buffer; /* used in unidirectional transfer */
unsigned long tx_dma_size;
unsigned long rx_dma_size;
int tx_num;
int rx_num;
/* store register value for suspend/resume */
u32 control;
u32 ssel;
unsigned long sclk;
enum adi_spi_state state;
const struct adi_spi_transfer_ops *ops;
};
struct adi_spi_device {
u32 control;
u32 clock;
u32 ssel;
u8 cs;
u16 cs_chg_udelay; /* Some devices require > 255usec delay */
u32 cs_gpio;
u32 tx_dummy_val; /* tx value for rx only transfer */
bool enable_dma;
const struct adi_spi_transfer_ops *ops;
};
static void adi_spi_enable(struct adi_spi_master *drv_data)
{
u32 ctl;
ctl = ioread32(&drv_data->regs->control);
ctl |= SPI_CTL_EN;
iowrite32(ctl, &drv_data->regs->control);
}
static void adi_spi_disable(struct adi_spi_master *drv_data)
{
u32 ctl;
ctl = ioread32(&drv_data->regs->control);
ctl &= ~SPI_CTL_EN;
iowrite32(ctl, &drv_data->regs->control);
}
/* Caculate the SPI_CLOCK register value based on input HZ */
static u32 hz_to_spi_clock(u32 sclk, u32 speed_hz)
{
u32 spi_clock = sclk / speed_hz;
if (spi_clock)
spi_clock--;
return spi_clock;
}
static int adi_spi_flush(struct adi_spi_master *drv_data)
{
unsigned long limit = loops_per_jiffy << 1;
/* wait for stop and clear stat */
while (!(ioread32(&drv_data->regs->status) & SPI_STAT_SPIF) && --limit)
cpu_relax();
iowrite32(0xFFFFFFFF, &drv_data->regs->status);
return limit;
}
/* Chip select operation functions for cs_change flag */
static void adi_spi_cs_active(struct adi_spi_master *drv_data, struct adi_spi_device *chip)
{
if (likely(chip->cs < MAX_CTRL_CS)) {
u32 reg;
reg = ioread32(&drv_data->regs->ssel);
reg &= ~chip->ssel;
iowrite32(reg, &drv_data->regs->ssel);
} else {
gpio_set_value(chip->cs_gpio, 0);
}
}
static void adi_spi_cs_deactive(struct adi_spi_master *drv_data,
struct adi_spi_device *chip)
{
if (likely(chip->cs < MAX_CTRL_CS)) {
u32 reg;
reg = ioread32(&drv_data->regs->ssel);
reg |= chip->ssel;
iowrite32(reg, &drv_data->regs->ssel);
} else {
gpio_set_value(chip->cs_gpio, 1);
}
/* Move delay here for consistency */
if (chip->cs_chg_udelay)
udelay(chip->