Linux Audio

Check our new training course

Loading...
/* spi_dw.h - Designware SPI driver private definitions */

/*
 * Copyright (c) 2015 Intel Corporation.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#ifndef __SPI_DW_H__
#define __SPI_DW_H__

#include <spi.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef void (*spi_dw_config_t)(void);

/* Private structures */
struct spi_dw_config {
	u32_t regs;
#ifdef CONFIG_SPI_DW_CLOCK_GATE
	void *clock_data;
#endif /* CONFIG_SPI_DW_CLOCK_GATE */
	spi_dw_config_t config_func;
};

#if defined(CONFIG_SPI_LEGACY_API)
struct spi_dw_data {
	struct k_sem device_sync_sem;
	u32_t error:1;
	u32_t dfs:3; /* dfs in bytes: 1,2 or 4 */
	u32_t slave:17; /* up 16 slaves */
	u32_t fifo_diff:9; /* cannot be bigger than FIFO depth */
	u32_t last_tx:1;
	u32_t _unused:1;
#ifdef CONFIG_SPI_DW_CLOCK_GATE
	struct device *clock;
#endif /* CONFIG_SPI_DW_CLOCK_GATE */
	const u8_t *tx_buf;
	u32_t tx_buf_len;
	u8_t *rx_buf;
	u32_t rx_buf_len;
};
#else

#include "spi_context.h"

struct spi_dw_data {
#ifdef CONFIG_SPI_DW_CLOCK_GATE
	struct device *clock;
#endif /* CONFIG_SPI_DW_CLOCK_GATE */
	struct spi_context ctx;
	u8_t error;
	u8_t dfs;	/* dfs in bytes: 1,2 or 4 */
	u8_t fifo_diff;	/* cannot be bigger than FIFO depth */
	u8_t _unused;
};
#endif /* CONFIG_SPI_LEGACY_API */

/* Helper macros */

#ifdef SPI_DW_SPI_CLOCK
#define SPI_DW_CLK_DIVIDER(ssi_clk_hz) \
		((SPI_DW_SPI_CLOCK / ssi_clk_hz) & 0xFFFF)
/* provision for soc.h providing a clock that is different than CPU clock */
#else
#define SPI_DW_CLK_DIVIDER(ssi_clk_hz) \
		((CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / ssi_clk_hz) & 0xFFFF)
#endif


#ifdef CONFIG_SPI_DW_ARC_AUX_REGS
#define _REG_READ(__sz) sys_in##__sz
#define _REG_WRITE(__sz) sys_out##__sz
#define _REG_SET_BIT sys_io_set_bit
#define _REG_CLEAR_BIT sys_io_clear_bit
#define _REG_TEST_BIT sys_io_test_bit
#else
#define _REG_READ(__sz) sys_read##__sz
#define _REG_WRITE(__sz) sys_write##__sz
#define _REG_SET_BIT sys_set_bit
#define _REG_CLEAR_BIT sys_clear_bit
#define _REG_TEST_BIT sys_test_bit
#endif /* CONFIG_SPI_DW_ARC_AUX_REGS */

#define DEFINE_MM_REG_READ(__reg, __off, __sz)				\
	static inline u32_t read_##__reg(u32_t addr)			\
	{								\
		return _REG_READ(__sz)(addr + __off);			\
	}
#define DEFINE_MM_REG_WRITE(__reg, __off, __sz)				\
	static inline void write_##__reg(u32_t data, u32_t addr)	\
	{								\
		_REG_WRITE(__sz)(data, addr + __off);			\
	}

#define DEFINE_SET_BIT_OP(__reg_bit, __reg_off, __bit)			\
	static inline void set_bit_##__reg_bit(u32_t addr)		\
	{								\
		_REG_SET_BIT(addr + __reg_off, __bit);			\
	}

#define DEFINE_CLEAR_BIT_OP(__reg_bit, __reg_off, __bit)		\
	static inline void clear_bit_##__reg_bit(u32_t addr)		\
	{								\
		_REG_CLEAR_BIT(addr + __reg_off, __bit);		\
	}

#define DEFINE_TEST_BIT_OP(__reg_bit, __reg_off, __bit)			\
	static inline int test_bit_##__reg_bit(u32_t addr)		\
	{								\
		return _REG_TEST_BIT(addr + __reg_off, __bit);		\
	}

/* Common registers settings, bits etc... */

/* CTRLR0 settings */
#define DW_SPI_CTRLR0_SCPH_BIT		(6)
#define DW_SPI_CTRLR0_SCPOL_BIT		(7)
#define DW_SPI_CTRLR0_SRL_BIT		(11)

#define DW_SPI_CTRLR0_SCPH		BIT(DW_SPI_CTRLR0_SCPH_BIT)
#define DW_SPI_CTRLR0_SCPOL		BIT(DW_SPI_CTRLR0_SCPOL_BIT)
#define DW_SPI_CTRLR0_SRL		BIT(DW_SPI_CTRLR0_SRL_BIT)

#define DW_SPI_CTRLR0_DFS_16(__bpw)	((__bpw) - 1)
#define DW_SPI_CTRLR0_DFS_32(__bpw)	(((__bpw) - 1) << 16)

#ifdef CONFIG_ARC
#define DW_SPI_CTRLR0_DFS		DW_SPI_CTRLR0_DFS_16
#else
#define DW_SPI_CTRLR0_DFS		DW_SPI_CTRLR0_DFS_32
#endif

/* 0x38 represents the bits 8, 16 and 32. Knowing that 24 is bits 8 and 16
 * These are the bits were when you divide by 8, you keep the result as it is.
 * For all the other ones, 4 to 7, 9 to 15, etc... you need a +1,
 * since on such division it takes only the result above 0
 */
#define SPI_WS_TO_DFS(__bpw)		(((__bpw) & ~0x38) ?		\
					 (((__bpw) / 8) + 1) :		\
					 ((__bpw) / 8))

/* SSIENR bits */
#define DW_SPI_SSIENR_SSIEN_BIT		(0)

/* SR bits and values */
#define DW_SPI_SR_BUSY_BIT		(0)
#define DW_SPI_SR_TFNF_BIT		(1)
#define DW_SPI_SR_RFNE_BIT		(3)

/* IMR bits (ISR valid as well) */
#define DW_SPI_IMR_TXEIM_BIT		(0)
#define DW_SPI_IMR_TXOIM_BIT		(1)
#define DW_SPI_IMR_RXUIM_BIT		(2)
#define DW_SPI_IMR_RXOIM_BIT		(3)
#define DW_SPI_IMR_RXFIM_BIT		(4)
#define DW_SPI_IMR_MSTIM_BIT		(5)

/* IMR values */
#define DW_SPI_IMR_TXEIM		BIT(DW_SPI_IMR_TXEIM_BIT)
#define DW_SPI_IMR_TXOIM		BIT(DW_SPI_IMR_TXOIM_BIT)
#define DW_SPI_IMR_RXUIM		BIT(DW_SPI_IMR_RXUIM_BIT)
#define DW_SPI_IMR_RXOIM		BIT(DW_SPI_IMR_RXOIM_BIT)
#define DW_SPI_IMR_RXFIM		BIT(DW_SPI_IMR_RXFIM_BIT)
#define DW_SPI_IMR_MSTIM		BIT(DW_SPI_IMR_MSTIM_BIT)

/* ISR values (same as IMR) */
#define DW_SPI_ISR_TXEIS		DW_SPI_IMR_TXEIM
#define DW_SPI_ISR_TXOIS		DW_SPI_IMR_TXOIM
#define DW_SPI_ISR_RXUIS		DW_SPI_IMR_RXUIM
#define DW_SPI_ISR_RXOIS		DW_SPI_IMR_RXOIM
#define DW_SPI_ISR_RXFIS		DW_SPI_IMR_RXFIM
#define DW_SPI_ISR_MSTIS		DW_SPI_IMR_MSTIM

/* Error interrupt */
#define DW_SPI_ISR_ERRORS_MASK		(DW_SPI_ISR_TXOIS | \
					 DW_SPI_ISR_RXUIS | \
					 DW_SPI_ISR_RXOIS | \
					 DW_SPI_ISR_MSTIS)
/* ICR Bit */
#define DW_SPI_SR_ICR_BIT		(0)

/* Threshold defaults */
#define DW_SPI_FIFO_DEPTH		CONFIG_SPI_DW_FIFO_DEPTH
#define DW_SPI_TXFTLR_DFLT		((DW_SPI_FIFO_DEPTH * 1) / 2) /* 50% */
#define DW_SPI_RXFTLR_DFLT		((DW_SPI_FIFO_DEPTH * 5) / 8)

/* Interrupt mask (IMR) */
#define DW_SPI_IMR_MASK			(0x0)
#define DW_SPI_IMR_UNMASK		(DW_SPI_IMR_TXEIM | \
					 DW_SPI_IMR_TXOIM | \
					 DW_SPI_IMR_RXUIM | \
					 DW_SPI_IMR_RXOIM | \
					 DW_SPI_IMR_RXFIM)
#define DW_SPI_IMR_MASK_TX		(~(DW_SPI_IMR_TXEIM | \
					   DW_SPI_IMR_TXOIM))
#define DW_SPI_IMR_MASK_RX		(~(DW_SPI_IMR_RXUIM | \
					   DW_SPI_IMR_RXOIM | \
					   DW_SPI_IMR_RXFIM))

/*
 * Including the right register definition file
 * SoC SPECIFIC!
 */
#ifdef CONFIG_SOC_QUARK_SE_C1000_SS
#include "spi_dw_quark_se_ss_regs.h"
#else
#include "spi_dw_regs.h"
#endif

/* GPIO used to emulate CS */
#if defined(CONFIG_SPI_LEGACY_API)
#ifdef CONFIG_SPI_DW_CS_GPIO

#include <gpio.h>

static inline void _spi_config_cs(struct device *dev)
{
	const struct spi_dw_config *info = dev->config->config_info;
	struct spi_dw_data *spi = dev->driver_data;
	struct device *gpio;

	gpio = device_get_binding(info->cs_gpio_name);
	if (!gpio) {
		spi->cs_gpio_port = NULL;
		return;
	}

	gpio_pin_configure(gpio, info->cs_gpio_pin, GPIO_DIR_OUT);
	/* Default CS line to high (idling) */
	gpio_pin_write(gpio, info->cs_gpio_pin, 1);

	spi->cs_gpio_port = gpio;
}

static inline void _spi_control_cs(struct device *dev, int on)
{
	const struct spi_dw_config *info = dev->config->config_info;
	struct spi_dw_data *spi = dev->driver_data;

	if (spi->cs_gpio_port) {
		gpio_pin_write(spi->cs_gpio_port, info->cs_gpio_pin, !on);
	}
}
#else
#define _spi_control_cs(...)
#define _spi_config_cs(...)
#endif /* CONFIG_SPI_DW_CS_GPIO */
#endif /* CONFIG_SPI_LEGACY_API */

/* Interrupt mask
 * SoC SPECIFIC!
 */
#if defined(CONFIG_SOC_QUARK_SE_C1000) || defined(CONFIG_SOC_QUARK_SE_C1000_SS)
#ifdef CONFIG_ARC
#define _INT_UNMASK     INT_ENABLE_ARC
#else
#define _INT_UNMASK	INT_UNMASK_IA
#endif

#define _spi_int_unmask(__mask)						\
	sys_write32(sys_read32(__mask) & _INT_UNMASK, __mask)
#else
#define _spi_int_unmask(...)
#endif /* CONFIG_SOC_QUARK_SE_C1000 || CONFIG_SOC_QUARK_SE_C1000_SS */

/* Based on those macros above, here are common helpers for some registers */
DEFINE_MM_REG_WRITE(baudr, DW_SPI_REG_BAUDR, 16)
DEFINE_MM_REG_READ(txflr, DW_SPI_REG_TXFLR, 32)
DEFINE_MM_REG_READ(rxflr, DW_SPI_REG_RXFLR, 32)
DEFINE_MM_REG_WRITE(imr, DW_SPI_REG_IMR, 8)
DEFINE_MM_REG_READ(isr, DW_SPI_REG_ISR, 8)

DEFINE_SET_BIT_OP(ssienr, DW_SPI_REG_SSIENR, DW_SPI_SSIENR_SSIEN_BIT)
DEFINE_CLEAR_BIT_OP(ssienr, DW_SPI_REG_SSIENR, DW_SPI_SSIENR_SSIEN_BIT)
DEFINE_TEST_BIT_OP(ssienr, DW_SPI_REG_SSIENR, DW_SPI_SSIENR_SSIEN_BIT)
DEFINE_TEST_BIT_OP(sr_busy, DW_SPI_REG_SR, DW_SPI_SR_BUSY_BIT)

#ifdef __cplusplus
}
#endif
#endif /* __SPI_DW_H__ */