// SPDX-License-Identifier: GPL-2.0+
//
// Freescale i.MX7ULP LPSPI driver
//
// Copyright 2016 Freescale Semiconductor, Inc.
// Copyright 2018 NXP Semiconductors
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/platform_data/dma-imx.h>
#include <linux/platform_data/spi-imx.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/types.h>
#define DRIVER_NAME "fsl_lpspi"
#define FSL_LPSPI_RPM_TIMEOUT 50 /* 50ms */
/* The maximum bytes that edma can transfer once.*/
#define FSL_LPSPI_MAX_EDMA_BYTES ((1 << 15) - 1)
/* i.MX7ULP LPSPI registers */
#define IMX7ULP_VERID 0x0
#define IMX7ULP_PARAM 0x4
#define IMX7ULP_CR 0x10
#define IMX7ULP_SR 0x14
#define IMX7ULP_IER 0x18
#define IMX7ULP_DER 0x1c
#define IMX7ULP_CFGR0 0x20
#define IMX7ULP_CFGR1 0x24
#define IMX7ULP_DMR0 0x30
#define IMX7ULP_DMR1 0x34
#define IMX7ULP_CCR 0x40
#define IMX7ULP_FCR 0x58
#define IMX7ULP_FSR 0x5c
#define IMX7ULP_TCR 0x60
#define IMX7ULP_TDR 0x64
#define IMX7ULP_RSR 0x70
#define IMX7ULP_RDR 0x74
/* General control register field define */
#define CR_RRF BIT(9)
#define CR_RTF BIT(8)
#define CR_RST BIT(1)
#define CR_MEN BIT(0)
#define SR_MBF BIT(24)
#define SR_TCF BIT(10)
#define SR_FCF BIT(9)
#define SR_RDF BIT(1)
#define SR_TDF BIT(0)
#define IER_TCIE BIT(10)
#define IER_FCIE BIT(9)
#define IER_RDIE BIT(1)
#define IER_TDIE BIT(0)
#define DER_RDDE BIT(1)
#define DER_TDDE BIT(0)
#define CFGR1_PCSCFG BIT(27)
#define CFGR1_PINCFG (BIT(24)|BIT(25))
#define CFGR1_PCSPOL BIT(8)
#define CFGR1_NOSTALL BIT(3)
#define CFGR1_MASTER BIT(0)
#define FSR_TXCOUNT (0xFF)
#define RSR_RXEMPTY BIT(1)
#define TCR_CPOL BIT(31)
#define TCR_CPHA BIT(30)
#define TCR_CONT BIT(21)
#define TCR_CONTC BIT(20)
#define TCR_RXMSK BIT(19)
#define TCR_TXMSK BIT(18)
static int clkdivs[] = {1, 2, 4, 8, 16, 32, 64, 128};
struct lpspi_config {
u8 bpw;
u8 chip_select;
u8 prescale;
u16 mode;
u32 speed_hz;
};
struct fsl_lpspi_data {
struct device *dev;
void __iomem *base;
unsigned long base_phys;
struct clk *clk_ipg;
struct clk *clk_per;
bool is_slave;
bool is_first_byte;
void *rx_buf;
const void *tx_buf;
void (*tx)(struct fsl_lpspi_data *);
void (*rx)(struct fsl_lpspi_data *);
u32 remain;
u8 watermark;
u8 txfifosize;
u8 rxfifosize;
struct lpspi_config config;
struct completion xfer_done;
bool slave_aborted;
/* DMA */
bool usedma;
struct completion dma_rx_completion;
struct completion dma_tx_completion;
int chipselect[0];
};
static const struct of_device_id fsl_lpspi_dt_ids[] = {
{ .compatible = "fsl,imx7ulp-spi", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_lpspi_dt_ids);
#define LPSPI_BUF_RX(type) \
static void fsl_lpspi_buf_rx_##type(struct fsl_lpspi_data *fsl_lpspi) \
{ \
unsigned int val = readl(fsl_lpspi->base + IMX7ULP_RDR); \
\
if (fsl_lpspi->rx_buf) { \
*(type *)fsl_lpspi->rx_buf = val; \
fsl_lpspi->rx_buf += sizeof(type); \
} \
}
#define LPSPI_BUF_TX(type) \
static void fsl_lpspi_buf_tx_##type(struct fsl_lpspi_data *fsl_lpspi) \
{ \
type val = 0; \
\
if (fsl_lpspi->tx_buf) { \
val = *(type *)fsl_lpspi->tx_buf; \
fsl_lpspi->tx_buf += sizeof(type); \
} \
\
fsl_lpspi->remain -= sizeof(type); \
writel(val, fsl_lpspi->base + IMX7ULP_TDR); \
}
LPSPI_BUF_RX(u8)
LPSPI_BUF_TX(u8)
LPSPI_BUF_RX(u16)
LPSPI_BUF_TX(u16)
LPSPI_BUF_RX(u32)
LPSPI_BUF_TX(u32)
static void fsl_lpspi_intctrl(struct fsl_lpspi_data *fsl_lpspi,
unsigned int enable)
{
writel(enable, fsl_lpspi->base + IMX7ULP_IER);
}
static int fsl_lpspi_bytes_per_word(const int bpw)
{
return DIV_ROUND_UP(bpw, BITS_PER_BYTE);
}
static bool fsl_lpspi_can_dma(struct spi_controller *controller,
struct spi_device *spi,
struct spi_transfer *transfer)
{
unsigned int bytes_per_word;
if (!controller->dma_rx)
return false;
bytes_per_word = fsl_lpspi_bytes_per_word(transfer->bits_per_word);
switch (bytes_per_word)
{
case 1:
case 2:
case 4:
break;
default:
return false;
}
return true;
}
static int lpspi_prepare_xfer_hardware(struct spi_controller *controller)
{
struct fsl_lpspi_data *fsl_lpspi =
spi_controller_get_devdata(controller);
int ret;
ret = pm_runtime_get_sync(fsl_lpspi->dev);
if (ret < 0) {
dev_err(fsl_lpspi->dev, "failed to enable clock\n");
return ret;
}
return 0;
}
static int lpspi_unprepare_xfer_hardware(struct spi_controller *controller)
{
struct fsl_lpspi_data <