// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ASPEED FMC/SPI Memory Controller Driver
*
* Copyright (c) 2015-2022, IBM Corporation.
* Copyright (c) 2020, ASPEED Corporation.
*/
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
#define DEVICE_NAME "spi-aspeed-smc"
/* Type setting Register */
#define CONFIG_REG 0x0
#define CONFIG_TYPE_SPI 0x2
/* CE Control Register */
#define CE_CTRL_REG 0x4
/* CEx Control Register */
#define CE0_CTRL_REG 0x10
#define CTRL_IO_MODE_MASK GENMASK(30, 28)
#define CTRL_IO_SINGLE_DATA 0x0
#define CTRL_IO_DUAL_DATA BIT(29)
#define CTRL_IO_QUAD_DATA BIT(30)
#define CTRL_COMMAND_SHIFT 16
#define CTRL_IO_ADDRESS_4B BIT(13) /* AST2400 SPI only */
#define CTRL_IO_DUMMY_SET(dummy) \
(((((dummy) >> 2) & 0x1) << 14) | (((dummy) & 0x3) << 6))
#define CTRL_FREQ_SEL_SHIFT 8
#define CTRL_FREQ_SEL_MASK GENMASK(11, CTRL_FREQ_SEL_SHIFT)
#define CTRL_CE_STOP_ACTIVE BIT(2)
#define CTRL_IO_MODE_CMD_MASK GENMASK(1, 0)
#define CTRL_IO_MODE_NORMAL 0x0
#define CTRL_IO_MODE_READ 0x1
#define CTRL_IO_MODE_WRITE 0x2
#define CTRL_IO_MODE_USER 0x3
#define CTRL_IO_CMD_MASK 0xf0ff40c3
/* CEx Address Decoding Range Register */
#define CE0_SEGMENT_ADDR_REG 0x30
/* CEx Read timing compensation register */
#define CE0_TIMING_COMPENSATION_REG 0x94
enum aspeed_spi_ctl_reg_value {
ASPEED_SPI_BASE,
ASPEED_SPI_READ,
ASPEED_SPI_WRITE,
ASPEED_SPI_MAX,
};
struct aspeed_spi;
struct aspeed_spi_chip {
struct aspeed_spi *aspi;
u32 cs;
void __iomem *ctl;
void __iomem *ahb_base;
u32 ahb_window_size;
u32 ctl_val[ASPEED_SPI_MAX];
u32 clk_freq;
};
struct aspeed_spi_data {
u32 ctl0;
u32 max_cs;
bool hastype;
u32 mode_bits;
u32 we0;
u32 timing;
u32 hclk_mask;
u32 hdiv_max;
u32 (*segment_start)(struct aspeed_spi *aspi, u32 reg);
u32 (*segment_end)(struct aspeed_spi *aspi, u32 reg);
u32 (*segment_reg)(struct aspeed_spi *aspi, u32 start, u32 end);
int (*calibrate)(struct aspeed_spi_chip *chip, u32 hdiv,
const u8 *golden_buf, u8 *test_buf);
};
#define ASPEED_SPI_MAX_NUM_CS 5
struct aspeed_spi {
const struct aspeed_spi_data *data;
void __iomem *regs;
void __iomem *ahb_base;
u32 ahb_base_phy;
u32 ahb_window_size;
struct device *dev;
struct clk *clk;
u32 clk_freq;
struct aspeed_spi_chip chips[ASPEED_SPI_MAX_NUM_CS];
};
static u32 aspeed_spi_get_io_mode(const struct spi_mem_op *op)
{
switch (op->data.buswidth) {
case 1:
return CTRL_IO_SINGLE_DATA;
case 2:
return CTRL_IO_DUAL_DATA;
case 4:
return CTRL_IO_QUAD_DATA;
default:
return CTRL_IO_SINGLE_DATA;
}
}
static void aspeed_spi_set_io_mode(struct aspeed_spi_chip *chip, u32 io_mode)
{
u32 ctl;
if (io_mode > 0) {
ctl = readl(chip->ctl) & ~CTRL_IO_MODE_MASK;
ctl |= io_mode;
writel(ctl, chip->ctl);
}
}
static void aspeed_spi_start_user(struct aspeed_spi_chip *chip)
{
u32 ctl = chip->ctl_val[ASPEED_SPI_BASE];
ctl |= CTRL_IO_MODE_USER | CTRL_CE_STOP_ACTIVE;
writel(ctl, chip->ctl);
ctl &= ~CTRL_CE_STOP_ACTIVE;
writel(ctl, chip->ctl);
}
static void aspeed_spi_stop_user(struct aspeed_spi_chip *chip)
{
u32 ctl = chip->ctl_val[ASPEED_SPI_READ] |
CTRL_IO_MODE_USER | CTRL_CE_STOP_ACTIVE;
writel(ctl, chip->ctl);
/* Restore defaults */
writel(chip->ctl_val[ASPEED_SPI_READ], chip->ctl);
}
static int aspeed_spi_read_from_ahb(void *buf, void __iomem *src, size_t len)
{
size_t offset = 0;
if (IS_ALIGNED((uintptr_t)src, sizeof(uintptr_t)) &&
IS_ALIGNED((uintptr_t)buf, sizeof(uintptr_t))) {
ioread32_rep(src, bu