/*
 * wcxb SPI library
 *
 * Copyright (C) 2013 Digium, Inc.
 *
 * All rights reserved.
 *
 */

/*
 * See http://www.asterisk.org for more information about
 * the Asterisk project. Please do not directly contact
 * any of the maintainers of this project for assistance;
 * the project provides a web site, mailing lists and IRC
 * channels for your use.
 *
 * This program is free software, distributed under the terms of
 * the GNU General Public License Version 2 as published by the
 * Free Software Foundation. See the LICENSE file included with
 * this program for more details.
 */

#ifndef __WCXB_SPI_H
#define __WCXB_SPI_H

#include <linux/spi/spi.h>
#include <linux/version.h>

/* Linux kernel 5.16 and greater has removed user-space headers from the kernel include path */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0)
#include <asm/types.h>
#else
#include <stdbool.h>
#endif

struct wcxb_spi_transfer {
	const void	*tx_buf;
	void		*rx_buf;
	u32		len:16;
	u16		delay_usecs;
	struct list_head node;
};

struct wcxb_spi_message {
	struct list_head transfers;
	struct list_head node;
	struct wcxb_spi_device *spi;
	void (*complete)(void *arg);
	void *arg;
	int status;
};

struct wcxb_spi_master;

struct wcxb_spi_device {
	struct wcxb_spi_master *master;
	u16 chip_select;
};

extern struct wcxb_spi_master *wcxb_spi_master_create(struct device *parent,
					void __iomem *base, bool auto_cs);
extern void wcxb_spi_master_destroy(struct wcxb_spi_master *master);
extern int wcxb_spi_sync(struct wcxb_spi_device *spi,
			 struct wcxb_spi_message *message);
extern int wcxb_spi_async(struct wcxb_spi_device *spi,
			  struct wcxb_spi_message *message);
extern void wcxb_spi_handle_interrupt(struct wcxb_spi_master *master);

static inline struct wcxb_spi_device *
wcxb_spi_device_create(struct wcxb_spi_master *master, u16 chip_select)
{
	struct wcxb_spi_device *spi = kzalloc(sizeof(*spi), GFP_KERNEL);
	if (!spi)
		return NULL;
	spi->master = master;
	spi->chip_select = chip_select;
	return spi;
}

static inline void wcxb_spi_device_destroy(struct wcxb_spi_device *spi)
{
	kfree(spi);
}

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

static inline void wcxb_spi_message_add_tail(struct wcxb_spi_transfer *t,
					     struct wcxb_spi_message *m)
{
	list_add_tail(&t->node, &m->transfers);
}

static inline int
wcxb_spi_write(struct wcxb_spi_device *spi, const void *buffer, size_t len)
{
	struct wcxb_spi_transfer	t = {
			.tx_buf		= buffer,
			.len		= len,
		};
	struct wcxb_spi_message	m;
	wcxb_spi_message_init(&m);
	wcxb_spi_message_add_tail(&t, &m);
	return wcxb_spi_sync(spi, &m);
}

static inline int
wcxb_spi_read(struct wcxb_spi_device *spi, void *buffer, size_t len)
{
	struct wcxb_spi_transfer	t = {
			.rx_buf		= buffer,
			.len		= len,
		};
	struct wcxb_spi_message	m;
	wcxb_spi_message_init(&m);
	wcxb_spi_message_add_tail(&t, &m);
	return wcxb_spi_sync(spi, &m);
}

#endif
