/**************************************************************************
 * Copyright (C) 2012-2021  Unisound
 *
 * 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
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 **************************************************************************
 *
 * Description : spi.h
 * Author      : yzs.unisound.com
 * Date        : 2021.03.01
 *
 **************************************************************************/
#ifndef __SPI_H__
#define __SPI_H__

#include <list.h>
#include <string.h>


struct spi_transfer {
	const void    *tx_buf;
	void          *rx_buf;
	unsigned      len;

	unsigned      cs_change:1;
	unsigned      tx_nbits:3;
	unsigned      rx_nbits:3;
#define SPI_NBITS_SINGLE        0x01 /* 1bit transfer */
#define SPI_NBITS_DUAL          0x02 /* 2bits transfer */
#define SPI_NBITS_QUAD          0x04 /* 4bits transfer */
	unsigned char bits_per_word;
	unsigned int  delay_usecs;
	unsigned int  speed_hz;

	struct list_head transfer_list;
};

struct spi_device ;
struct spi_message {
	struct list_head      transfers;
	struct spi_device     *spi;
	unsigned              is_dma_mapped:1;
	void                  (*complete)(void *context);
	void                  *context;
	unsigned              actual_length;
	int                   status;
	struct list_head      queue;
	void                  *state;
};

struct spi_master {
	int              bus_num;
	unsigned int     num_chipselect;
	int              (*setup)(struct spi_device *spi);
	int              (*transfer)(struct spi_device *spi,struct spi_message *mesg);
	void             (*cleanup)(struct spi_device *spi);
	void             *data;
	void             *driver_data;
	struct list_head list;
};

struct spi_device {
	struct spi_master *master;
	unsigned int      max_speed_hz;
	unsigned char     chip_select;
	unsigned char     bits_per_word;
	unsigned short    mode;
#define SPI_TX_DUAL     0x100   /* transmit with 2 wires */
#define SPI_TX_QUAD     0x200   /* transmit with 4 wires */
#define SPI_RX_DUAL     0x400   /* receive with 2 wires */
#define SPI_RX_QUAD     0x800   /* receive with 4 wires */
	void              *controller_state;
};

struct sflash_master {
	int    bus_num;
	struct spi_device spi;
	struct list_head  list;
};

static inline void spi_message_init(struct spi_message *m)
{
	memset(m, 0, sizeof *m);
	INIT_LIST_HEAD(&m->transfers);
}

static inline void
spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
{
	list_add_tail(&t->transfer_list, &m->transfers);
}

static inline void
spi_transfer_del(struct spi_transfer *t)
{
	list_del(&t->transfer_list);
}

/**
 * spi_setup - setup SPI mode and clock rate
 * @spi: the device whose settings are being modified
 * Context: can sleep, and no requests are queued to the device
 *
 * SPI protocol drivers may need to update the transfer mode if the
 * device doesn't work with its default.  They may likewise need
 * to update clock rates or word sizes from initial values.  This function
 * changes those settings, and must be called from a context that can sleep.
 * Except for SPI_CS_HIGH, which takes effect immediately, the changes take
 * effect the next time the device is selected and data is transferred to
 * or from it.  When this function returns, the spi device is deselected.
 *
 * Note that this call will fail if the protocol driver specifies an option
 * that the underlying controller or its driver does not support.  For
 * example, not all hardware supports wire transfers using nine bit words,
 * LSB-first wire encoding, or active-high chipselects.
 */
static inline int
spi_setup(struct spi_device *spi)
{
	return spi->master->setup(spi);
}


/**
 * spi_sync - synchronous SPI transfer
 * @spi: device with which data will be exchanged
 * @message: describes the data transfers, including completion callback
 * Context: any (irqs may be blocked, etc)
 *
 * This call may be used in_irq and other contexts which can't sleep,
 * as well as from task contexts which can sleep.
 *
 * The completion callback is invoked in a context which can't sleep.
 * Before that invocation, the value of message->status is undefined.
 * When the callback is issued, message->status holds either zero (to
 * indicate complete success) or a negative error code.  After that
 * callback returns, the driver which issued the transfer request may
 * deallocate the associated memory; it's no longer in use by any SPI
 * core or controller driver code.
 *
 * Note that although all messages to a spi_device are handled in
 * FIFO order, messages may go to different devices in other orders.
 * Some device might be higher priority, or have various "hard" access
 * time requirements, for example.
 *
 * On detection of any fault during the transfer, processing of
 * the entire message is aborted, and the device is deselected.
 * Until returning from the associated message completion callback,
 * no other spi_message queued to that device will be processed.
 * (This rule applies equally to all the synchronous transfer calls,
 * which are wrappers around this core asynchronous primitive.)
 */
static inline int
spi_sync(struct spi_device *spi, struct spi_message *message)
{
	message->spi = spi;
	return spi->master->transfer(spi, message);
}

/*---------------------------------------------------------------------------*/

/**
 * spi_write - SPI synchronous write
 * @spi: device to which data will be written
 * @buf: data buffer
 * @len: data buffer size
 * Context: can sleep
 *
 * This writes the buffer and returns zero or a negative error code.
 * Callable only from contexts that can sleep.
 */
static inline int
spi_write(struct spi_device *spi, const unsigned char *buf, size_t len)
{
	struct spi_transfer	t = {
			.tx_buf		= buf,
			.len		= len,
		};
	struct spi_message	m;

	spi_message_init(&m);
	spi_message_add_tail(&t, &m);
	return spi_sync(spi, &m);
}

/**
 * spi_read - SPI synchronous read
 * @spi: device from which data will be read
 * @buf: data buffer
 * @len: data buffer size
 * Context: can sleep
 *
 * This reads the buffer and returns zero or a negative error code.
 * Callable only from contexts that can sleep.
 */
static inline int
spi_read(struct spi_device *spi, unsigned char *buf, size_t len)
{
	struct spi_transfer	t = {
			.rx_buf		= buf,
			.len		= len,
		};
	struct spi_message	m;

	spi_message_init(&m);
	spi_message_add_tail(&t, &m);
	return spi_sync(spi, &m);
}

int spi_write_then_read(struct spi_device *spi,
		const unsigned char *txbuf, unsigned n_tx,
		unsigned char *rxbuf, unsigned n_rx);

void uni_spi_probe(void);

void spi_master_v3_probe(void);

#endif
