mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-11-01 17:08:10 +00:00
725b0e3ea8
Mostly due to a long driver history it's methods and macro names look a
bit messy. In particularly that concerns the code their prefixes. A
biggest part of the driver functions and macros have got the dw_spi/DW_SPI
prefixes. But there are some entities which have been just
"spi_/SPI_"-prefixed. Especially that concerns the CSR and their fields
macro definitions. It makes the code harder to comprehend since such
methods and macros can be easily confused with the global SPI-subsystem
exports. In this case the only possible way to more or less quickly
distinguish one naming space from another is either by context or by the
argument type, which most of the times isn't that easy anyway. In addition
to that a new DW SSI IP-core support has been added in the framework of
commit e539f435cb
("spi: dw: Add support for DesignWare DWC_ssi"), which
introduced a new set or macro-prefixes to describe CTRLR0-specific fields
and worsen the situation. Finally there are methods with
no DW SPI driver-reference prefix at all, that make the code reading even
harder. So in order to ease the driver hacking let's bring the code naming
to a common base:
1) Each method is supposed to have "dw_spi_" prefix so to be easily
distinguished from the kernel API, e.g. SPI-subsystem methods and macros.
(Exception is the local implementation of the readl/writel methods since
being just the regspace accessors.)
2) Each generically used macro should have DW_SPI_-prefix thus being
easily comprehended as the local driver definition.
3) DW APB SSI and DW SSI specific macros should have prefixes as DW_PSSI_
and DW_HSSI_ respectively so referring to the system buses they support
(APB and AHB similarly to the DT clocks naming like pclk, hclk).
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Link: https://lore.kernel.org/r/20211115181917.7521-4-Sergey.Semin@baikalelectronics.ru
Signed-off-by: Mark Brown <broonie@kernel.org>
297 lines
8.1 KiB
C
297 lines
8.1 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef __SPI_DW_H__
|
|
#define __SPI_DW_H__
|
|
|
|
#include <linux/bits.h>
|
|
#include <linux/completion.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/irqreturn.h>
|
|
#include <linux/io.h>
|
|
#include <linux/scatterlist.h>
|
|
#include <linux/spi/spi-mem.h>
|
|
#include <linux/bitfield.h>
|
|
|
|
/* Register offsets (Generic for both DWC APB SSI and DWC SSI IP-cores) */
|
|
#define DW_SPI_CTRLR0 0x00
|
|
#define DW_SPI_CTRLR1 0x04
|
|
#define DW_SPI_SSIENR 0x08
|
|
#define DW_SPI_MWCR 0x0c
|
|
#define DW_SPI_SER 0x10
|
|
#define DW_SPI_BAUDR 0x14
|
|
#define DW_SPI_TXFTLR 0x18
|
|
#define DW_SPI_RXFTLR 0x1c
|
|
#define DW_SPI_TXFLR 0x20
|
|
#define DW_SPI_RXFLR 0x24
|
|
#define DW_SPI_SR 0x28
|
|
#define DW_SPI_IMR 0x2c
|
|
#define DW_SPI_ISR 0x30
|
|
#define DW_SPI_RISR 0x34
|
|
#define DW_SPI_TXOICR 0x38
|
|
#define DW_SPI_RXOICR 0x3c
|
|
#define DW_SPI_RXUICR 0x40
|
|
#define DW_SPI_MSTICR 0x44
|
|
#define DW_SPI_ICR 0x48
|
|
#define DW_SPI_DMACR 0x4c
|
|
#define DW_SPI_DMATDLR 0x50
|
|
#define DW_SPI_DMARDLR 0x54
|
|
#define DW_SPI_IDR 0x58
|
|
#define DW_SPI_VERSION 0x5c
|
|
#define DW_SPI_DR 0x60
|
|
#define DW_SPI_RX_SAMPLE_DLY 0xf0
|
|
#define DW_SPI_CS_OVERRIDE 0xf4
|
|
|
|
/* Bit fields in CTRLR0 (DWC APB SSI) */
|
|
#define DW_PSSI_CTRLR0_DFS_OFFSET 0
|
|
#define DW_PSSI_CTRLR0_DFS_MASK GENMASK(3, 0)
|
|
#define DW_PSSI_CTRLR0_DFS32_OFFSET 16
|
|
|
|
#define DW_PSSI_CTRLR0_FRF_OFFSET 4
|
|
#define DW_SPI_CTRLR0_FRF_MOTO_SPI 0x0
|
|
#define DW_SPI_CTRLR0_FRF_TI_SSP 0x1
|
|
#define DW_SPI_CTRLR0_FRF_NS_MICROWIRE 0x2
|
|
#define DW_SPI_CTRLR0_FRF_RESV 0x3
|
|
|
|
#define DW_PSSI_CTRLR0_MODE_OFFSET 6
|
|
#define DW_PSSI_CTRLR0_SCPH_OFFSET 6
|
|
#define DW_PSSI_CTRLR0_SCOL_OFFSET 7
|
|
|
|
#define DW_PSSI_CTRLR0_TMOD_OFFSET 8
|
|
#define DW_PSSI_CTRLR0_TMOD_MASK (0x3 << DW_PSSI_CTRLR0_TMOD_OFFSET)
|
|
#define DW_SPI_CTRLR0_TMOD_TR 0x0 /* xmit & recv */
|
|
#define DW_SPI_CTRLR0_TMOD_TO 0x1 /* xmit only */
|
|
#define DW_SPI_CTRLR0_TMOD_RO 0x2 /* recv only */
|
|
#define DW_SPI_CTRLR0_TMOD_EPROMREAD 0x3 /* eeprom read mode */
|
|
|
|
#define DW_PSSI_CTRLR0_SLVOE_OFFSET 10
|
|
#define DW_PSSI_CTRLR0_SRL_OFFSET 11
|
|
#define DW_PSSI_CTRLR0_CFS_OFFSET 12
|
|
|
|
/* Bit fields in CTRLR0 (DWC SSI with AHB interface) */
|
|
#define DW_HSSI_CTRLR0_SRL_OFFSET 13
|
|
#define DW_HSSI_CTRLR0_TMOD_OFFSET 10
|
|
#define DW_HSSI_CTRLR0_TMOD_MASK GENMASK(11, 10)
|
|
#define DW_HSSI_CTRLR0_SCPOL_OFFSET 9
|
|
#define DW_HSSI_CTRLR0_SCPH_OFFSET 8
|
|
#define DW_HSSI_CTRLR0_FRF_OFFSET 6
|
|
#define DW_HSSI_CTRLR0_DFS_OFFSET 0
|
|
|
|
/*
|
|
* For Keem Bay, CTRLR0[31] is used to select controller mode.
|
|
* 0: SSI is slave
|
|
* 1: SSI is master
|
|
*/
|
|
#define DW_HSSI_CTRLR0_KEEMBAY_MST BIT(31)
|
|
|
|
/* Bit fields in CTRLR1 */
|
|
#define DW_SPI_NDF_MASK GENMASK(15, 0)
|
|
|
|
/* Bit fields in SR, 7 bits */
|
|
#define DW_SPI_SR_MASK 0x7f /* cover 7 bits */
|
|
#define DW_SPI_SR_BUSY (1 << 0)
|
|
#define DW_SPI_SR_TF_NOT_FULL (1 << 1)
|
|
#define DW_SPI_SR_TF_EMPT (1 << 2)
|
|
#define DW_SPI_SR_RF_NOT_EMPT (1 << 3)
|
|
#define DW_SPI_SR_RF_FULL (1 << 4)
|
|
#define DW_SPI_SR_TX_ERR (1 << 5)
|
|
#define DW_SPI_SR_DCOL (1 << 6)
|
|
|
|
/* Bit fields in ISR, IMR, RISR, 7 bits */
|
|
#define DW_SPI_INT_TXEI (1 << 0)
|
|
#define DW_SPI_INT_TXOI (1 << 1)
|
|
#define DW_SPI_INT_RXUI (1 << 2)
|
|
#define DW_SPI_INT_RXOI (1 << 3)
|
|
#define DW_SPI_INT_RXFI (1 << 4)
|
|
#define DW_SPI_INT_MSTI (1 << 5)
|
|
|
|
/* Bit fields in DMACR */
|
|
#define DW_SPI_DMACR_RDMAE (1 << 0)
|
|
#define DW_SPI_DMACR_TDMAE (1 << 1)
|
|
|
|
/* Mem/DMA operations helpers */
|
|
#define DW_SPI_WAIT_RETRIES 5
|
|
#define DW_SPI_BUF_SIZE \
|
|
(sizeof_field(struct spi_mem_op, cmd.opcode) + \
|
|
sizeof_field(struct spi_mem_op, addr.val) + 256)
|
|
#define DW_SPI_GET_BYTE(_val, _idx) \
|
|
((_val) >> (BITS_PER_BYTE * (_idx)) & 0xff)
|
|
|
|
/* DW SPI capabilities */
|
|
#define DW_SPI_CAP_CS_OVERRIDE BIT(0)
|
|
#define DW_SPI_CAP_KEEMBAY_MST BIT(1)
|
|
#define DW_SPI_CAP_DWC_HSSI BIT(2)
|
|
#define DW_SPI_CAP_DFS32 BIT(3)
|
|
|
|
/* Slave spi_transfer/spi_mem_op related */
|
|
struct dw_spi_cfg {
|
|
u8 tmode;
|
|
u8 dfs;
|
|
u32 ndf;
|
|
u32 freq;
|
|
};
|
|
|
|
struct dw_spi;
|
|
struct dw_spi_dma_ops {
|
|
int (*dma_init)(struct device *dev, struct dw_spi *dws);
|
|
void (*dma_exit)(struct dw_spi *dws);
|
|
int (*dma_setup)(struct dw_spi *dws, struct spi_transfer *xfer);
|
|
bool (*can_dma)(struct spi_controller *master, struct spi_device *spi,
|
|
struct spi_transfer *xfer);
|
|
int (*dma_transfer)(struct dw_spi *dws, struct spi_transfer *xfer);
|
|
void (*dma_stop)(struct dw_spi *dws);
|
|
};
|
|
|
|
struct dw_spi {
|
|
struct spi_controller *master;
|
|
|
|
void __iomem *regs;
|
|
unsigned long paddr;
|
|
int irq;
|
|
u32 fifo_len; /* depth of the FIFO buffer */
|
|
unsigned int dfs_offset; /* CTRLR0 DFS field offset */
|
|
u32 max_mem_freq; /* max mem-ops bus freq */
|
|
u32 max_freq; /* max bus freq supported */
|
|
|
|
u32 caps; /* DW SPI capabilities */
|
|
|
|
u32 reg_io_width; /* DR I/O width in bytes */
|
|
u16 bus_num;
|
|
u16 num_cs; /* supported slave numbers */
|
|
void (*set_cs)(struct spi_device *spi, bool enable);
|
|
|
|
/* Current message transfer state info */
|
|
void *tx;
|
|
unsigned int tx_len;
|
|
void *rx;
|
|
unsigned int rx_len;
|
|
u8 buf[DW_SPI_BUF_SIZE];
|
|
int dma_mapped;
|
|
u8 n_bytes; /* current is a 1/2 bytes op */
|
|
irqreturn_t (*transfer_handler)(struct dw_spi *dws);
|
|
u32 current_freq; /* frequency in hz */
|
|
u32 cur_rx_sample_dly;
|
|
u32 def_rx_sample_dly_ns;
|
|
|
|
/* Custom memory operations */
|
|
struct spi_controller_mem_ops mem_ops;
|
|
|
|
/* DMA info */
|
|
struct dma_chan *txchan;
|
|
u32 txburst;
|
|
struct dma_chan *rxchan;
|
|
u32 rxburst;
|
|
u32 dma_sg_burst;
|
|
unsigned long dma_chan_busy;
|
|
dma_addr_t dma_addr; /* phy address of the Data register */
|
|
const struct dw_spi_dma_ops *dma_ops;
|
|
struct completion dma_completion;
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
struct dentry *debugfs;
|
|
struct debugfs_regset32 regset;
|
|
#endif
|
|
};
|
|
|
|
static inline u32 dw_readl(struct dw_spi *dws, u32 offset)
|
|
{
|
|
return __raw_readl(dws->regs + offset);
|
|
}
|
|
|
|
static inline void dw_writel(struct dw_spi *dws, u32 offset, u32 val)
|
|
{
|
|
__raw_writel(val, dws->regs + offset);
|
|
}
|
|
|
|
static inline u32 dw_read_io_reg(struct dw_spi *dws, u32 offset)
|
|
{
|
|
switch (dws->reg_io_width) {
|
|
case 2:
|
|
return readw_relaxed(dws->regs + offset);
|
|
case 4:
|
|
default:
|
|
return readl_relaxed(dws->regs + offset);
|
|
}
|
|
}
|
|
|
|
static inline void dw_write_io_reg(struct dw_spi *dws, u32 offset, u32 val)
|
|
{
|
|
switch (dws->reg_io_width) {
|
|
case 2:
|
|
writew_relaxed(val, dws->regs + offset);
|
|
break;
|
|
case 4:
|
|
default:
|
|
writel_relaxed(val, dws->regs + offset);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static inline void dw_spi_enable_chip(struct dw_spi *dws, int enable)
|
|
{
|
|
dw_writel(dws, DW_SPI_SSIENR, (enable ? 1 : 0));
|
|
}
|
|
|
|
static inline void dw_spi_set_clk(struct dw_spi *dws, u16 div)
|
|
{
|
|
dw_writel(dws, DW_SPI_BAUDR, div);
|
|
}
|
|
|
|
/* Disable IRQ bits */
|
|
static inline void dw_spi_mask_intr(struct dw_spi *dws, u32 mask)
|
|
{
|
|
u32 new_mask;
|
|
|
|
new_mask = dw_readl(dws, DW_SPI_IMR) & ~mask;
|
|
dw_writel(dws, DW_SPI_IMR, new_mask);
|
|
}
|
|
|
|
/* Enable IRQ bits */
|
|
static inline void dw_spi_umask_intr(struct dw_spi *dws, u32 mask)
|
|
{
|
|
u32 new_mask;
|
|
|
|
new_mask = dw_readl(dws, DW_SPI_IMR) | mask;
|
|
dw_writel(dws, DW_SPI_IMR, new_mask);
|
|
}
|
|
|
|
/*
|
|
* This disables the SPI controller, interrupts, clears the interrupts status
|
|
* and CS, then re-enables the controller back. Transmit and receive FIFO
|
|
* buffers are cleared when the device is disabled.
|
|
*/
|
|
static inline void dw_spi_reset_chip(struct dw_spi *dws)
|
|
{
|
|
dw_spi_enable_chip(dws, 0);
|
|
dw_spi_mask_intr(dws, 0xff);
|
|
dw_readl(dws, DW_SPI_ICR);
|
|
dw_writel(dws, DW_SPI_SER, 0);
|
|
dw_spi_enable_chip(dws, 1);
|
|
}
|
|
|
|
static inline void dw_spi_shutdown_chip(struct dw_spi *dws)
|
|
{
|
|
dw_spi_enable_chip(dws, 0);
|
|
dw_spi_set_clk(dws, 0);
|
|
}
|
|
|
|
extern void dw_spi_set_cs(struct spi_device *spi, bool enable);
|
|
extern void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
|
|
struct dw_spi_cfg *cfg);
|
|
extern int dw_spi_check_status(struct dw_spi *dws, bool raw);
|
|
extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws);
|
|
extern void dw_spi_remove_host(struct dw_spi *dws);
|
|
extern int dw_spi_suspend_host(struct dw_spi *dws);
|
|
extern int dw_spi_resume_host(struct dw_spi *dws);
|
|
|
|
#ifdef CONFIG_SPI_DW_DMA
|
|
|
|
extern void dw_spi_dma_setup_mfld(struct dw_spi *dws);
|
|
extern void dw_spi_dma_setup_generic(struct dw_spi *dws);
|
|
|
|
#else
|
|
|
|
static inline void dw_spi_dma_setup_mfld(struct dw_spi *dws) {}
|
|
static inline void dw_spi_dma_setup_generic(struct dw_spi *dws) {}
|
|
|
|
#endif /* !CONFIG_SPI_DW_DMA */
|
|
|
|
#endif /* __SPI_DW_H__ */
|