Commit ff280e36 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'spi-v4.6' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi

Pull spi updates from Mark Brown:
 "Not the biggest set of changes for SPI but a bit of a pickup in
  activity on the core:

   - Support for memory mapped read from flash devices via a SPI
     controller.

   - The beginnings of a message rewriting framework in the core which
     should in time allow us to support transforming messages to work
     around the limits of controllers or optimise the performance for
     controllers transparently to calling drivers.

   - Updates to the PXA2xx, the main functional change being to improve
     the ACPI support.

   - A new driver for the Analog Devices AXI SPI engine"

* tag 'spi-v4.6' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (66 commits)
  spi: Add gfp parameter to kernel-doc to fix build warning
  spi: Fix htmldocs build error due struct spi_replaced_transfers
  spi: rockchip: covert rsd_nsecs to u32 type
  spi: rockchip: header file cleanup
  spi: xilinx: Add devicetree binding for spi-xilinx
  spi: respect the maximum segment size of DMA device
  spi: rockchip: check requesting dma channel with EPROBE_DEFER
  spi: rockchip: migrate to dmaengine_terminate_async
  spi: rockchip: check return value of dmaengine_prep_slave_sg
  spi: core: Fix deadlock when sending messages
  spi/rockchip: fix endian mode for 16-bit transfers
  spi/rockchip: Make sure spi clk is on in rockchip_spi_set_cs
  spi: pxa2xx: Use newer more explicit DMAengine terminate API
  spi: pxa2xx: Add support for Intel Broxton B-Step
  spi: lp-8841: return correct error code from probe
  spi: imx: drop bogus tests for rx/tx bufs in DMA transfer
  spi: imx: set MX51_ECSPI_CTRL_SMC bit in setup function
  spi: imx: make some register defines simpler
  spi: imx: remove unnecessary bit clearing in mx51_ecspi_config
  spi: imx: add support for all SPI word width for DMA
  ...
parents 5ca5446e c508709b
Analog Devices AXI SPI Engine controller Device Tree Bindings
Required properties:
- compatible : Must be "adi,axi-spi-engine-1.00.a""
- reg : Physical base address and size of the register map.
- interrupts : Property with a value describing the interrupt
number.
- clock-names : List of input clock names - "s_axi_aclk", "spi_clk"
- clocks : Clock phandles and specifiers (See clock bindings for
details on clock-names and clocks).
- #address-cells : Must be <1>
- #size-cells : Must be <0>
Optional subnodes:
Subnodes are use to represent the SPI slave devices connected to the SPI
master. They follow the generic SPI bindings as outlined in spi-bus.txt.
Example:
spi@@44a00000 {
compatible = "adi,axi-spi-engine-1.00.a";
reg = <0x44a00000 0x1000>;
interrupts = <0 56 4>;
clocks = <&clkc 15 &clkc 15>;
clock-names = "s_axi_aclk", "spi_clk";
#address-cells = <1>;
#size-cells = <0>;
/* SPI devices */
};
* ICP DAS LP-8841 SPI Controller for RTC
ICP DAS LP-8841 contains a DS-1302 RTC. RTC is connected to an IO
memory register, which acts as an SPI master device.
The device uses the standard MicroWire half-duplex transfer timing.
Master output is set on low clock and sensed by the RTC on the rising
edge. Master input is set by the RTC on the trailing edge and is sensed
by the master on low clock.
Required properties:
- #address-cells: should be 1
- #size-cells: should be 0
- compatible: should be "icpdas,lp8841-spi-rtc"
- reg: should provide IO memory address
Requirements to SPI slave nodes:
- There can be only one slave device.
- The spi slave node should claim the following flags which are
required by the spi controller.
- spi-3wire: The master itself has only 3 wire. It cannor work in
full duplex mode.
- spi-cs-high: DS-1302 has active high chip select line. The master
doesn't support active low.
- spi-lsb-first: DS-1302 requires least significant bit first
transfers. The master only support this type of bit ordering.
Example:
spi@901c {
#address-cells = <1>;
#size-cells = <0>;
compatible = "icpdas,lp8841-spi-rtc";
reg = <0x901c 0x1>;
rtc@0 {
compatible = "maxim,ds1302";
reg = <0>;
spi-max-frequency = <500000>;
spi-3wire;
spi-lsb-first;
spi-cs-high;
};
};
......@@ -9,6 +9,7 @@ Required Properties:
"rockchip,rk3066-spi" for rk3066.
"rockchip,rk3188-spi", "rockchip,rk3066-spi" for rk3188.
"rockchip,rk3288-spi", "rockchip,rk3066-spi" for rk3288.
"rockchip,rk3399-spi", "rockchip,rk3066-spi" for rk3399.
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: The interrupt number to the cpu. The interrupt specifier format
......
Xilinx SPI controller Device Tree Bindings
-------------------------------------------------
Required properties:
- compatible : Should be "xlnx,xps-spi-2.00.a" or "xlnx,xps-spi-2.00.b"
- reg : Physical base address and size of SPI registers map.
- interrupts : Property with a value describing the interrupt
number.
- interrupt-parent : Must be core interrupt controller
Optional properties:
- xlnx,num-ss-bits : Number of chip selects used.
Example:
axi_quad_spi@41e00000 {
compatible = "xlnx,xps-spi-2.00.a";
interrupt-parent = <&intc>;
interrupts = <0 31 1>;
reg = <0x41e00000 0x10000>;
xlnx,num-ss-bits = <0x1>;
};
......@@ -75,11 +75,26 @@ config SPI_ATMEL
This selects a driver for the Atmel SPI Controller, present on
many AT32 (AVR32) and AT91 (ARM) chips.
config SPI_AU1550
tristate "Au1550/Au1200/Au1300 SPI Controller"
depends on MIPS_ALCHEMY
select SPI_BITBANG
help
If you say yes to this option, support will be included for the
PSC SPI controller found on Au1550, Au1200 and Au1300 series.
config SPI_AXI_SPI_ENGINE
tristate "Analog Devices AXI SPI Engine controller"
depends on HAS_IOMEM
help
This enables support for the Analog Devices AXI SPI Engine SPI controller.
It is part of the SPI Engine framework that is used in some Analog Devices
reference designs for FPGAs.
config SPI_BCM2835
tristate "BCM2835 SPI controller"
depends on GPIOLIB
depends on ARCH_BCM2835 || COMPILE_TEST
depends on GPIOLIB
help
This selects a driver for the Broadcom BCM2835 SPI master.
......@@ -90,8 +105,7 @@ config SPI_BCM2835
config SPI_BCM2835AUX
tristate "BCM2835 SPI auxiliary controller"
depends on ARCH_BCM2835 || COMPILE_TEST
depends on GPIOLIB
depends on (ARCH_BCM2835 && GPIOLIB) || COMPILE_TEST
help
This selects a driver for the Broadcom BCM2835 SPI aux master.
......@@ -118,14 +132,6 @@ config SPI_BFIN_SPORT
help
Enable support for a SPI bus via the Blackfin SPORT peripheral.
config SPI_AU1550
tristate "Au1550/Au1200/Au1300 SPI Controller"
depends on MIPS_ALCHEMY
select SPI_BITBANG
help
If you say yes to this option, support will be included for the
PSC SPI controller found on Au1550, Au1200 and Au1300 series.
config SPI_BCM53XX
tristate "Broadcom BCM53xx SPI controller"
depends on ARCH_BCM_5301X
......@@ -197,6 +203,23 @@ config SPI_DAVINCI
help
SPI master controller for DaVinci/DA8x/OMAP-L/AM1x SPI modules.
config SPI_DESIGNWARE
tristate "DesignWare SPI controller core support"
help
general driver for SPI controller core from DesignWare
config SPI_DW_PCI
tristate "PCI interface driver for DW SPI core"
depends on SPI_DESIGNWARE && PCI
config SPI_DW_MID_DMA
bool "DMA support for DW SPI controller on Intel MID platform"
depends on SPI_DW_PCI && DW_DMAC_PCI
config SPI_DW_MMIO
tristate "Memory-mapped io interface driver for DW SPI core"
depends on SPI_DESIGNWARE
config SPI_DLN2
tristate "Diolan DLN-2 USB SPI adapter"
depends on MFD_DLN2
......@@ -271,6 +294,16 @@ config SPI_LM70_LLP
which interfaces to an LM70 temperature sensor using
a parallel port.
config SPI_LP8841_RTC
tristate "ICP DAS LP-8841 SPI Controller for RTC"
depends on MACH_PXA27X_DT || COMPILE_TEST
help
This driver provides an SPI master device to drive Maxim
DS-1302 real time clock.
Say N here unless you plan to run the kernel on an ICP DAS
LP-8x4x industrial computer.
config SPI_MPC52xx
tristate "Freescale MPC52xx SPI (non-PSC) controller support"
depends on PPC_MPC52xx
......@@ -346,6 +379,13 @@ config SPI_MT65XX
say Y or M here.If you are not sure, say N.
SPI drivers for Mediatek MT65XX and MT81XX series ARM SoCs.
config SPI_NUC900
tristate "Nuvoton NUC900 series SPI"
depends on ARCH_W90X900
select SPI_BITBANG
help
SPI driver for Nuvoton NUC900 series ARM SoCs
config SPI_OC_TINY
tristate "OpenCores tiny SPI"
depends on GPIOLIB || COMPILE_TEST
......@@ -415,10 +455,6 @@ config SPI_PPC4xx
help
This selects a driver for the PPC4xx SPI Controller.
config SPI_PXA2XX_DMA
def_bool y
depends on SPI_PXA2XX
config SPI_PXA2XX
tristate "PXA2xx SSP SPI master"
depends on (ARCH_PXA || PCI || ACPI)
......@@ -451,7 +487,7 @@ config SPI_RB4XX
config SPI_RSPI
tristate "Renesas RSPI/QSPI controller"
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
help
SPI driver for Renesas RSPI and QSPI blocks.
......@@ -501,7 +537,7 @@ config SPI_SC18IS602
config SPI_SH_MSIOF
tristate "SuperH MSIOF SPI controller"
depends on HAVE_CLK && HAS_DMA
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
help
SPI driver for SuperH and SH Mobile MSIOF blocks.
......@@ -520,7 +556,7 @@ config SPI_SH_SCI
config SPI_SH_HSPI
tristate "SuperH HSPI controller"
depends on ARCH_SHMOBILE || COMPILE_TEST
depends on ARCH_RENESAS || COMPILE_TEST
help
SPI driver for SuperH HSPI blocks.
......@@ -647,34 +683,10 @@ config SPI_ZYNQMP_GQSPI
help
Enables Xilinx GQSPI controller driver for Zynq UltraScale+ MPSoC.
config SPI_NUC900
tristate "Nuvoton NUC900 series SPI"
depends on ARCH_W90X900
select SPI_BITBANG
help
SPI driver for Nuvoton NUC900 series ARM SoCs
#
# Add new SPI master controllers in alphabetical order above this line
#
config SPI_DESIGNWARE
tristate "DesignWare SPI controller core support"
help
general driver for SPI controller core from DesignWare
config SPI_DW_PCI
tristate "PCI interface driver for DW SPI core"
depends on SPI_DESIGNWARE && PCI
config SPI_DW_MID_DMA
bool "DMA support for DW SPI controller on Intel MID platform"
depends on SPI_DW_PCI && DW_DMAC_PCI
config SPI_DW_MMIO
tristate "Memory-mapped io interface driver for DW SPI core"
depends on SPI_DESIGNWARE
#
# There are lots of SPI device types, with sensors and memory
# being probably the most widely used ones.
......
......@@ -15,6 +15,7 @@ obj-$(CONFIG_SPI_ALTERA) += spi-altera.o
obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o
obj-$(CONFIG_SPI_ATH79) += spi-ath79.o
obj-$(CONFIG_SPI_AU1550) += spi-au1550.o
obj-$(CONFIG_SPI_AXI_SPI_ENGINE) += spi-axi-spi-engine.o
obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o
obj-$(CONFIG_SPI_BCM2835AUX) += spi-bcm2835aux.o
obj-$(CONFIG_SPI_BCM53XX) += spi-bcm53xx.o
......@@ -46,6 +47,7 @@ obj-$(CONFIG_SPI_GPIO) += spi-gpio.o
obj-$(CONFIG_SPI_IMG_SPFI) += spi-img-spfi.o
obj-$(CONFIG_SPI_IMX) += spi-imx.o
obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o
obj-$(CONFIG_SPI_LP8841_RTC) += spi-lp8841-rtc.o
obj-$(CONFIG_SPI_MESON_SPIFC) += spi-meson-spifc.o
obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o
obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o
......@@ -62,8 +64,7 @@ obj-$(CONFIG_SPI_TI_QSPI) += spi-ti-qspi.o
obj-$(CONFIG_SPI_ORION) += spi-orion.o
obj-$(CONFIG_SPI_PL022) += spi-pl022.o
obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o
spi-pxa2xx-platform-objs := spi-pxa2xx.o
spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o
spi-pxa2xx-platform-objs := spi-pxa2xx.o spi-pxa2xx-dma.o
obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o
obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o
obj-$(CONFIG_SPI_QUP) += spi-qup.o
......
/*
* SPI-Engine SPI controller driver
* Copyright 2015 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#define SPI_ENGINE_VERSION_MAJOR(x) ((x >> 16) & 0xff)
#define SPI_ENGINE_VERSION_MINOR(x) ((x >> 8) & 0xff)
#define SPI_ENGINE_VERSION_PATCH(x) (x & 0xff)
#define SPI_ENGINE_REG_VERSION 0x00
#define SPI_ENGINE_REG_RESET 0x40
#define SPI_ENGINE_REG_INT_ENABLE 0x80
#define SPI_ENGINE_REG_INT_PENDING 0x84
#define SPI_ENGINE_REG_INT_SOURCE 0x88
#define SPI_ENGINE_REG_SYNC_ID 0xc0
#define SPI_ENGINE_REG_CMD_FIFO_ROOM 0xd0
#define SPI_ENGINE_REG_SDO_FIFO_ROOM 0xd4
#define SPI_ENGINE_REG_SDI_FIFO_LEVEL 0xd8
#define SPI_ENGINE_REG_CMD_FIFO 0xe0
#define SPI_ENGINE_REG_SDO_DATA_FIFO 0xe4
#define SPI_ENGINE_REG_SDI_DATA_FIFO 0xe8
#define SPI_ENGINE_REG_SDI_DATA_FIFO_PEEK 0xec
#define SPI_ENGINE_INT_CMD_ALMOST_EMPTY BIT(0)
#define SPI_ENGINE_INT_SDO_ALMOST_EMPTY BIT(1)
#define SPI_ENGINE_INT_SDI_ALMOST_FULL BIT(2)
#define SPI_ENGINE_INT_SYNC BIT(3)
#define SPI_ENGINE_CONFIG_CPHA BIT(0)
#define SPI_ENGINE_CONFIG_CPOL BIT(1)
#define SPI_ENGINE_CONFIG_3WIRE BIT(2)
#define SPI_ENGINE_INST_TRANSFER 0x0
#define SPI_ENGINE_INST_ASSERT 0x1
#define SPI_ENGINE_INST_WRITE 0x2
#define SPI_ENGINE_INST_MISC 0x3
#define SPI_ENGINE_CMD_REG_CLK_DIV 0x0
#define SPI_ENGINE_CMD_REG_CONFIG 0x1
#define SPI_ENGINE_MISC_SYNC 0x0
#define SPI_ENGINE_MISC_SLEEP 0x1
#define SPI_ENGINE_TRANSFER_WRITE 0x1
#define SPI_ENGINE_TRANSFER_READ 0x2
#define SPI_ENGINE_CMD(inst, arg1, arg2) \
(((inst) << 12) | ((arg1) << 8) | (arg2))
#define SPI_ENGINE_CMD_TRANSFER(flags, n) \
SPI_ENGINE_CMD(SPI_ENGINE_INST_TRANSFER, (flags), (n))
#define SPI_ENGINE_CMD_ASSERT(delay, cs) \
SPI_ENGINE_CMD(SPI_ENGINE_INST_ASSERT, (delay), (cs))
#define SPI_ENGINE_CMD_WRITE(reg, val) \
SPI_ENGINE_CMD(SPI_ENGINE_INST_WRITE, (reg), (val))
#define SPI_ENGINE_CMD_SLEEP(delay) \
SPI_ENGINE_CMD(SPI_ENGINE_INST_MISC, SPI_ENGINE_MISC_SLEEP, (delay))
#define SPI_ENGINE_CMD_SYNC(id) \
SPI_ENGINE_CMD(SPI_ENGINE_INST_MISC, SPI_ENGINE_MISC_SYNC, (id))
struct spi_engine_program {
unsigned int length;
uint16_t instructions[];
};
struct spi_engine {
struct clk *clk;
struct clk *ref_clk;
spinlock_t lock;
void __iomem *base;
struct spi_message *msg;
struct spi_engine_program *p;
unsigned cmd_length;
const uint16_t *cmd_buf;
struct spi_transfer *tx_xfer;
unsigned int tx_length;
const uint8_t *tx_buf;
struct spi_transfer *rx_xfer;
unsigned int rx_length;
uint8_t *rx_buf;
unsigned int sync_id;
unsigned int completed_id;
unsigned int int_enable;
};
static void spi_engine_program_add_cmd(struct spi_engine_program *p,
bool dry, uint16_t cmd)
{
if (!dry)
p->instructions[p->length] = cmd;
p->length++;
}
static unsigned int spi_engine_get_config(struct spi_device *spi)
{
unsigned int config = 0;
if (spi->mode & SPI_CPOL)
config |= SPI_ENGINE_CONFIG_CPOL;
if (spi->mode & SPI_CPHA)
config |= SPI_ENGINE_CONFIG_CPHA;
if (spi->mode & SPI_3WIRE)
config |= SPI_ENGINE_CONFIG_3WIRE;
return config;
}
static unsigned int spi_engine_get_clk_div(struct spi_engine *spi_engine,
struct spi_device *spi, struct spi_transfer *xfer)
{
unsigned int clk_div;
clk_div = DIV_ROUND_UP(clk_get_rate(spi_engine->ref_clk),
xfer->speed_hz * 2);
if (clk_div > 255)
clk_div = 255;
else if (clk_div > 0)
clk_div -= 1;
return clk_div;
}
static void spi_engine_gen_xfer(struct spi_engine_program *p, bool dry,
struct spi_transfer *xfer)
{
unsigned int len = xfer->len;
while (len) {
unsigned int n = min(len, 256U);
unsigned int flags = 0;
if (xfer->tx_buf)
flags |= SPI_ENGINE_TRANSFER_WRITE;
if (xfer->rx_buf)
flags |= SPI_ENGINE_TRANSFER_READ;
spi_engine_program_add_cmd(p, dry,
SPI_ENGINE_CMD_TRANSFER(flags, n - 1));
len -= n;
}
}
static void spi_engine_gen_sleep(struct spi_engine_program *p, bool dry,
struct spi_engine *spi_engine, unsigned int clk_div, unsigned int delay)
{
unsigned int spi_clk = clk_get_rate(spi_engine->ref_clk);
unsigned int t;
if (delay == 0)
return;
t = DIV_ROUND_UP(delay * spi_clk, (clk_div + 1) * 2);
while (t) {
unsigned int n = min(t, 256U);
spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_SLEEP(n - 1));
t -= n;
}
}
static void spi_engine_gen_cs(struct spi_engine_program *p, bool dry,
struct spi_device *spi, bool assert)
{
unsigned int mask = 0xff;
if (assert)
mask ^= BIT(spi->chip_select);
spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_ASSERT(1, mask));
}
static int spi_engine_compile_message(struct spi_engine *spi_engine,
struct spi_message *msg, bool dry, struct spi_engine_program *p)
{
struct spi_device *spi = msg->spi;
struct spi_transfer *xfer;
int clk_div, new_clk_div;
bool cs_change = true;
clk_div = -1;
spi_engine_program_add_cmd(p, dry,
SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CONFIG,
spi_engine_get_config(spi)));
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
new_clk_div = spi_engine_get_clk_div(spi_engine, spi, xfer);
if (new_clk_div != clk_div) {
clk_div = new_clk_div;
spi_engine_program_add_cmd(p, dry,
SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CLK_DIV,
clk_div));
}
if (cs_change)
spi_engine_gen_cs(p, dry, spi, true);
spi_engine_gen_xfer(p, dry, xfer);
spi_engine_gen_sleep(p, dry, spi_engine, clk_div,
xfer->delay_usecs);
cs_change = xfer->cs_change;
if (list_is_last(&xfer->transfer_list, &msg->transfers))
cs_change = !cs_change;
if (cs_change)
spi_engine_gen_cs(p, dry, spi, false);
}
return 0;
}
static void spi_engine_xfer_next(struct spi_engine *spi_engine,
struct spi_transfer **_xfer)
{
struct spi_message *msg = spi_engine->msg;
struct spi_transfer *xfer = *_xfer;
if (!xfer) {
xfer = list_first_entry(&msg->transfers,
struct spi_transfer, transfer_list);
} else if (list_is_last(&xfer->transfer_list, &msg->transfers)) {
xfer = NULL;
} else {
xfer = list_next_entry(xfer, transfer_list);
}
*_xfer = xfer;
}
static void spi_engine_tx_next(struct spi_engine *spi_engine)
{
struct spi_transfer *xfer = spi_engine->tx_xfer;
do {
spi_engine_xfer_next(spi_engine, &xfer);
} while (xfer && !xfer->tx_buf);
spi_engine->tx_xfer = xfer;
if (xfer) {
spi_engine->tx_length = xfer->len;
spi_engine->tx_buf = xfer->tx_buf;
} else {
spi_engine->tx_buf = NULL;
}
}
static void spi_engine_rx_next(struct spi_engine *spi_engine)
{
struct spi_transfer *xfer = spi_engine->rx_xfer;
do {
spi_engine_xfer_next(spi_engine, &xfer);
} while (xfer && !xfer->rx_buf);
spi_engine->rx_xfer = xfer;
if (xfer) {
spi_engine->rx_length = xfer->len;
spi_engine->rx_buf = xfer->rx_buf;
} else {
spi_engine->rx_buf = NULL;
}
}
static bool spi_engine_write_cmd_fifo(struct spi_engine *spi_engine)
{
void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_CMD_FIFO;
unsigned int n, m, i;
const uint16_t *buf;
n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_CMD_FIFO_ROOM);
while (n && spi_engine->cmd_length) {
m = min(n, spi_engine->cmd_length);
buf = spi_engine->cmd_buf;
for (i = 0; i < m; i++)
writel_relaxed(buf[i], addr);
spi_engine->cmd_buf += m;
spi_engine->cmd_length -= m;
n -= m;
}
return spi_engine->cmd_length != 0;
}
static bool spi_engine_write_tx_fifo(struct spi_engine *spi_engine)
{
void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_SDO_DATA_FIFO;