HunZI a462d2b757
target: add phytium support (#11798)
* target: add phytium support

* kernel/video: add phytium platform ARM GPU support

* config: add EFI support to phytium armv8

* target: phytium: remove rtl8821cs driver

* target: phytium: refresh dts
2024-01-18 15:16:24 +08:00

802 lines
22 KiB
C
Executable File

// SPDX-License-Identifier: GPL-2.0
/*
* Phytium Quad SPI controller driver.
*
* Copyright (c) 2022-2023, Phytium Technology Co., Ltd.
*/
#include <linux/clk.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi-mem.h>
#include <linux/mtd/spi-nor.h>
#define QSPI_FLASH_CAP_REG 0x00
#define QSPI_FLASH_CAP_NUM_SHIFT 3
#define QSPI_FLASH_CAP_NUM_MASK (0x3 << QSPI_FLASH_CAP_NUM_SHIFT)
#define QSPI_FLASH_CAP_CAP_SHIFT 0
#define QSPI_FLASH_CAP_CAP_MASK (0x7 << QSPI_FLASH_CAP_CAP_SHIFT)
#define QSPI_RD_CFG_REG 0x04
#define QSPI_RD_CFG_RD_CMD_SHIFT 24
#define QSPI_RD_CFG_RD_CMD_MASK (0xff << QSPI_RD_CFG_RD_CMD_SHIFT)
#define QSPI_RD_CFG_RD_THROUGH_SHIFT 23
#define QSPI_RD_CFG_RD_THROUGH_MASK (0x1 << QSPI_RD_CFG_RD_THROUGH_SHIFT)
#define QSPI_RD_CFG_RD_TRANSFER_SHIFT 20
#define QSPI_RD_CFG_RD_TRANSFER_MASK (0x7 << QSPI_RD_CFG_RD_TRANSFER_SHIFT)
#define QSPI_RD_CFG_RD_ADDR_SEL_SHIFT 19
#define QSPI_RD_CFG_RD_ADDR_SEL_MASK (0x1 << QSPI_RD_CFG_RD_ADDR_SEL_SHIFT)
#define QSPI_RD_CFG_RD_LATENCY_SHIFT 18
#define QSPI_RD_CFG_RD_LATENCY_MASK (0x1 << QSPI_RD_CFG_RD_LATENCY_SHIFT)
#define QSPI_RD_CFG_MODE_BYTE_SHIFT 17
#define QSPI_RD_CFG_MODE_BYTE_MASK (0x1 << QSPI_RD_CFG_MODE_BYTE_SHIFT)
#define QSPI_RD_CFG_CMD_SIGN_SHIFT 9
#define QSPI_RD_CFG_CMD_SIGN_MASK (0xff << QSPI_RD_CFG_CMD_SIGN_SHIFT)
#define QSPI_RD_CFG_DUMMY_SHIFT 4
#define QSPI_RD_CFG_DUMMY_MASK (0x1f << QSPI_RD_CFG_DUMMY_SHIFT)
#define QSPI_RD_CFG_D_BUFFER_SHIFT 3
#define QSPI_RD_CFG_D_BUFFER_MASK (0x1 << QSPI_RD_CFG_D_BUFFER_SHIFT)
#define QSPI_RD_CFG_RD_SCK_SEL_SHIFT 0
#define QSPI_RD_CFG_RD_SCK_SEL_MASK (0x7 << QSPI_RD_CFG_RD_SCK_SEL_SHIFT)
#define QSPI_WR_CFG_REG 0x08
#define QSPI_WR_CFG_WR_CMD_SHIFT 24
#define QSPI_WR_CFG_WR_CMD_MASK (0xff << QSPI_WR_CFG_WR_CMD_SHIFT)
#define QSPI_WR_CFG_WR_WAIT_SHIFT 9
#define QSPI_WR_CFG_WR_WAIT_MASK (0x01 << QSPI_WR_CFG_WR_WAIT_SHIFT)
#define QSPI_WR_CFG_WR_THROUGH_SHIFT 8
#define QSPI_WR_CFG_WR_THROUGH_MASK (0x01 << QSPI_WR_CFG_WR_THROUGH_SHIFT)
#define QSPI_WR_CFG_WR_TRANSFER_SHIFT 5
#define QSPI_WR_CFG_WR_TRANSFER_MASK (0X7 << QSPI_WR_CFG_WR_TRANSFER_SHIFT)
#define QSPI_WR_CFG_WR_ADDR_SEL_SHIFT 4
#define QSPI_WR_CFG_WR_ADDR_SEL_MASK (0x1 << QSPI_WR_CFG_WR_ADDR_SEL_SHIFT)
#define QSPI_WR_CFG_WR_MODE_SHIFT 3
#define QSPI_WR_CFG_WR_MODE_MASK (0x1 << QSPI_WR_CFG_WR_MODE_SHIFT)
#define QSPI_WR_CFG_WR_SCK_SEL_SHIFT 0
#define QSPI_WR_CFG_WR_SCK_SEL_MASK (0x7 << QSPI_WR_CFG_WR_SCK_SEL_SHIFT)
#define QSPI_FLUSH_REG 0x0c
#define QSPI_FLUSH_EN (0x1 << 0)
#define QSPI_CMD_PORT_REG 0x10
#define QSPI_CMD_PORT_CMD_SHIFT 24
#define QSPI_CMD_PORT_CMD_MASK (0xff << QSPI_CMD_PORT_CMD_SHIFT)
#define QSPI_CMD_PORT_WAIT_SHIFT 22
#define QSPI_CMD_PORT_WAIT_MASK (0x1 << QSPI_CMD_PORT_WAIT_SHIFT)
#define QSPI_CMD_PORT_THROUGH_SHIFT 21
#define QSPI_CMD_PORT_THROUGH_MASK (0x1 << QSPI_CMD_PORT_THROUGH_SHIFT)
#define QSPI_CMD_PORT_CS_SHIFT 19
#define QSPI_CMD_PORT_CS_MASK (0x3 << QSPI_CMD_PORT_CS_SHIFT)
#define QSPI_CMD_PORT_TRANSFER_SHIFT 16
#define QSPI_CMD_PORT_TRANSFER_MASK (0x7 << QSPI_CMD_PORT_TRANSFER_SHIFT)
#define QSPI_CMD_PORT_CMD_ADDR_SHIFT 15
#define QSPI_CMD_PORT_CMD_ADDR_MASK (0x1 << QSPI_CMD_PORT_CMD_ADDR_SHIFT)
#define QSPI_CMD_PORT_LATENCY_SHIFT 14
#define QSPI_CMD_PORT_LATENCY_MASK (0x1 << QSPI_CMD_PORT_LATENCY_SHIFT)
#define QSPI_CMD_PORT_DATA_XFER_SHIFT 13
#define QSPI_CMD_PORT_DATA_XFER_MASK (0x1 << QSPI_CMD_PORT_DATA_XFER_SHIFT)
#define QSPI_CMD_PORT_ADDR_SEL_SHIFT 12
#define QSPI_CMD_PORT_ADDR_SEL_MASK (0x1 << QSPI_CMD_PORT_ADDR_SEL_SHIFT)
#define QSPI_CMD_PORT_DUMMY_SHIFT 7
#define QSPI_CMD_PORT_DUMMY_MASK (0x1f << QSPI_CMD_PORT_DUMMY_SHIFT)
#define QSPI_CMD_PORT_P_BUFFER_SHIFT 6
#define QSPI_CMD_PORT_P_BUFFER_MASK (0x1 << QSPI_CMD_PORT_P_BUFFER_SHIFT)
#define QSPI_CMD_PORT_RW_NUM_SHIFT 3
#define QSPI_CMD_PORT_RW_NUM_MASK (0x7 << QSPI_CMD_PORT_RW_NUM_SHIFT)
#define QSPI_CMD_PORT_SCK_SEL_SHIFT 0
#define QSPI_CMD_PORT_SCK_SEL_MASK (0x7 << QSPI_CMD_PORT_SCK_SEL_SHIFT)
#define QSPI_ADDR_PORT_REG 0x14
#define QSPI_HD_PORT_REG 0x18
#define QSPI_LD_PORT_REG 0x1c
#define QSPI_FUN_SET_REG 0x20
#define QSPI_FUN_SET_HOLD_SHIFT 24
#define QSPI_FUN_SET_HOLD_MASK (0xff << QSPI_FUN_SET_HOLD_SHIFT)
#define QSPI_FUN_SET_SETUP_SHIFT 16
#define QSPI_FUN_SET_SETUP_MASK (0xff << QSPI_FUN_SET_SETUP_SHIFT)
#define QSPI_FUN_SET_DELAY_SHIFT 0
#define QSPI_FUN_SET_DELAY_MASK (0xffff << QSPI_FUN_SET_DELAY_SHIFT)
#define QSPI_WIP_REG 0x24
#define QSPI_WIP_W_CMD_SHIFT 24
#define QSPI_WIP_W_CMD_MASK (0xff << QSPI_WIP_W_CMD_SHIFT)
#define QSPI_WIP_W_TRANSFER_SHIFT 3
#define QSPI_WIP_W_TRANSFER_MASK (0x3 << QSPI_WIP_W_TRANSFER_SHIFT)
#define QSPI_WIP_W_SCK_SEL_SHIFT 0
#define QSPI_WIP_W_SCK_SEL_MASK (0x7 << QSPI_WIP_W_SCK_SEL_SHIFT)
#define QSPI_WP_REG 0x28
#define QSPI_WP_EN_SHIFT 17
#define QSPI_WP_EN_MASK (0x1 << QSPI_WP_EN_SHIFT)
#define QSPI_WP_IO2_SHIFT 16
#define QSPI_WP_IO2_MASK (0x1 << QSPI_WP_IO2_SHIFT)
#define QSPI_WP_HOLD_SHIFT 8
#define QSPI_WP_HOLD_MASK (0xff << QSPI_WP_HOLD_SHIFT)
#define QSPI_WP_SETUP_SHIFT 0
#define QSPI_WP_SETUP_MASK (0xff << QSPI_WP_SETUP_SHIFT)
#define QSPI_MODE_REG 0x2c
#define QSPI_MODE_VALID_SHIFT 8
#define QSPI_MODE_VALID_MASK (0xff << QSPI_MODE_VALID_SHIFT)
#define QSPI_MODE_SHIFT 0
#define QSPI_MODE_MASK (0xff << QSPI_MODE_SHIFT)
#define PHYTIUM_QSPI_MAX_NORCHIP 4
#define PHYTIUM_QSPI_MAX_MMAP_SZ (SZ_256M * PHYTIUM_QSPI_MAX_NORCHIP)
#define PHYTIUM_QSPI_MAX_XFER_SZ 8
#define PHYTIUM_QSPI_DEFAULT_SCK_SEL 5
#define XFER_PROTO_1_1_1 0x0
#define XFER_PROTO_1_1_2 0x1
#define XFER_PROTO_1_1_4 0x2
#define XFER_PROTO_1_2_2 0x3
#define XFER_PROTO_1_4_4 0x4
#define XFER_PROTO_2_2_2 0x5
#define XFER_PROTO_4_4_4 0x6
struct phytium_qspi_flash {
u32 cs;
u32 clk_div;
void __iomem *base;
resource_size_t size;
struct spi_device *spi;
};
struct phytium_qspi {
struct device *dev;
struct spi_controller *ctrl;
void __iomem *io_base;
void __iomem *mm_base;
resource_size_t mm_size;
resource_size_t used_size;
struct clk *clk;
u32 clk_rate;
struct phytium_qspi_flash flash[PHYTIUM_QSPI_MAX_NORCHIP];
u8 fnum;
bool nodirmap;
};
static bool phytium_qspi_check_buswidth(u8 width)
{
switch (width) {
case 1:
case 2:
case 4:
return 0;
}
return -ENOTSUPP;
}
static uint phytium_spi_nor_clac_clk_div(int div)
{
uint clk_div = 0;
if (div <= 2)
clk_div = 1;
else if (div <= 4)
clk_div = 2;
else if (div <= 8)
clk_div = 3;
else if (div <= 16)
clk_div = 4;
else if (div <= 32)
clk_div = 5;
else if (div <= 64)
clk_div = 6;
else if (div <= 128)
clk_div = 7;
else
clk_div = 65535;
return clk_div;
}
static int phytium_spi_nor_protocol_encode(const struct spi_mem_op *op, u32 *code)
{
int ret = 0;
if (op->cmd.buswidth == 1 &&
op->addr.buswidth == 1 &&
op->data.buswidth == 1)
*code = XFER_PROTO_1_1_1;
else if (op->cmd.buswidth == 1 &&
op->addr.buswidth == 1 &&
op->data.buswidth == 2)
*code = XFER_PROTO_1_1_2;
else if (op->cmd.buswidth == 1 &&
op->addr.buswidth == 1 &&
op->data.buswidth == 4)
*code = XFER_PROTO_1_1_4;
else if (op->cmd.buswidth == 1 &&
op->addr.buswidth == 2 &&
op->data.buswidth == 2)
*code = XFER_PROTO_1_2_2;
else if (op->cmd.buswidth == 1 &&
op->addr.buswidth == 4 &&
op->data.buswidth == 4)
*code = XFER_PROTO_1_4_4;
else if (op->cmd.buswidth == 2 &&
op->addr.buswidth == 2 &&
op->data.buswidth == 2)
*code = XFER_PROTO_2_2_2;
else if (op->cmd.buswidth == 4 &&
op->addr.buswidth == 4 &&
op->data.buswidth == 4)
*code = XFER_PROTO_4_4_4;
else
*code = XFER_PROTO_1_1_1;
return ret;
}
static int phytium_qspi_flash_capacity_encode(u32 size, u32 *cap)
{
int ret = 0;
switch (size) {
case SZ_4M:
*cap = 0x0;
break;
case SZ_8M:
*cap = 0x1;
break;
case SZ_16M:
*cap = 0x2;
break;
case SZ_32M:
*cap = 0x3;
break;
case SZ_64M:
*cap = 0x4;
break;
case SZ_128M:
*cap = 0x5;
break;
case SZ_256M:
*cap = 0x6;
break;
case SZ_512M:
*cap = 0x7;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int phytium_qspi_write_port(struct phytium_qspi *qspi,
const u8 *buf, const size_t len)
{
u32 bouncebuf[2] = { 0 };
if (len > PHYTIUM_QSPI_MAX_XFER_SZ) {
dev_err(qspi->dev, "WRITE data exceeds 8 bytes.\n");
return -EINVAL;
}
memcpy(bouncebuf, buf, len);
if (len > 4)
writel_relaxed(bouncebuf[1], qspi->io_base + QSPI_HD_PORT_REG);
writel_relaxed(bouncebuf[0], qspi->io_base + QSPI_LD_PORT_REG);
return 0;
}
static int phytium_qspi_read_port(struct phytium_qspi *qspi,
u8 *buf, size_t len)
{
u32 bouncebuf[2] = { 0 };
if (len > PHYTIUM_QSPI_MAX_XFER_SZ) {
dev_err(qspi->dev, "READ data exceeds 8 bytes.\n");
return -EINVAL;
}
/* Dummy write to LD_PORT register and issue READ ops*/
writel_relaxed(0, qspi->io_base + QSPI_LD_PORT_REG);
/* Read data */
bouncebuf[0] = readl_relaxed(qspi->io_base + QSPI_LD_PORT_REG);
if (len > 4)
bouncebuf[1] = readl_relaxed(qspi->io_base + QSPI_HD_PORT_REG);
memcpy(buf, bouncebuf, len);
return 0;
}
static int phytium_qspi_adjust_op_size(struct spi_mem *mem,
struct spi_mem_op *op)
{
if (op->data.nbytes > PHYTIUM_QSPI_MAX_XFER_SZ)
op->data.nbytes = PHYTIUM_QSPI_MAX_XFER_SZ;
return 0;
}
static bool phytium_qspi_supports_op(struct spi_mem *mem,
const struct spi_mem_op *op)
{
int ret;
ret = phytium_qspi_check_buswidth(op->cmd.buswidth);
if (op->addr.nbytes)
ret |= phytium_qspi_check_buswidth(op->addr.buswidth);
if (op->dummy.nbytes)
ret |= phytium_qspi_check_buswidth(op->dummy.buswidth);
if (op->data.nbytes)
ret |= phytium_qspi_check_buswidth(op->data.buswidth);
if (ret)
return false;
/* Max 32 dummy clock cycles supported */
if (op->dummy.nbytes &&
(op->dummy.nbytes * 8 / op->dummy.buswidth > 32))
return false;
return spi_mem_default_supports_op(mem, op);
}
static int phytium_qspi_exec_op(struct spi_mem *mem,
const struct spi_mem_op *op)
{
struct phytium_qspi *qspi = spi_controller_get_devdata(mem->spi->master);
struct phytium_qspi_flash *flash = &qspi->flash[mem->spi->chip_select];
u32 cmd, transfer;
int ret;
dev_dbg(qspi->dev, "cmd:%#x mode: %d.%d.%d.%d addr:%#llx len:%#x\n",
op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth,
op->dummy.buswidth, op->data.buswidth, op->addr.val,
op->data.nbytes);
cmd = op->cmd.opcode << QSPI_CMD_PORT_CMD_SHIFT;
cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT;
ret = phytium_spi_nor_protocol_encode(op, &transfer);
if (ret) {
dev_err(qspi->dev, "Unsupported SPI NOR protocol.\n");
goto out;
}
cmd |= transfer << QSPI_CMD_PORT_TRANSFER_SHIFT;
if (op->addr.nbytes) {
cmd |= QSPI_CMD_PORT_CMD_ADDR_MASK;
if (op->addr.nbytes == 4)
cmd |= QSPI_CMD_PORT_ADDR_SEL_MASK;
/* Write target address to ADDR_PORT register */
writel_relaxed(op->addr.val, qspi->io_base + QSPI_ADDR_PORT_REG);
}
if (op->dummy.nbytes) {
cmd |= QSPI_CMD_PORT_LATENCY_MASK;
cmd |= ((op->dummy.nbytes * 8) / op->dummy.buswidth) <<
QSPI_CMD_PORT_LATENCY_SHIFT;
}
if (op->data.nbytes) {
cmd |= QSPI_CMD_PORT_DATA_XFER_MASK;
cmd &= ~QSPI_CMD_PORT_P_BUFFER_MASK;
cmd |= (op->data.nbytes-1) << QSPI_CMD_PORT_RW_NUM_SHIFT;
}
cmd |= flash->clk_div;
writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG);
if (op->data.dir == SPI_MEM_DATA_IN) {
ret = phytium_qspi_read_port(qspi, op->data.buf.in, op->data.nbytes);
if (ret) {
dev_err(qspi->dev, "Failed to read data from the port.\n");
goto out;
}
} else if (op->data.dir == SPI_MEM_DATA_OUT) {
ret = phytium_qspi_write_port(qspi, op->data.buf.out, op->data.nbytes);
if (ret) {
dev_err(qspi->dev, "Failed to write data to the port.\n");
goto out;
}
} else {
/* Dummy write to LD_PORT register and issue the command */
writel_relaxed(0, qspi->io_base + QSPI_LD_PORT_REG);
}
out:
return ret;
}
static int phytium_qspi_dirmap_create(struct spi_mem_dirmap_desc *desc)
{
struct spi_device *spi = desc->mem->spi;
struct phytium_qspi *qspi = spi_controller_get_devdata(spi->master);
struct phytium_qspi_flash *flash = &qspi->flash[spi->chip_select];
struct spi_nor *nor = spi_mem_get_drvdata(desc->mem);
u32 cmd, transfer;
int ret = 0;
if (!qspi->mm_base || !qspi->mm_size) {
ret = -EOPNOTSUPP;
goto out;
}
if (!flash->base) {
flash->base = qspi->mm_base + qspi->used_size;
qspi->used_size += nor->mtd.size;
}
/* Setup RD/WR_CFG register */
if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN) {
cmd = desc->info.op_tmpl.cmd.opcode << QSPI_RD_CFG_RD_CMD_SHIFT;
ret = phytium_spi_nor_protocol_encode(&desc->info.op_tmpl, &transfer);
if (ret) {
dev_err(qspi->dev, "Unsupported SPI NOR protocol.\n");
goto out;
}
cmd |= transfer << QSPI_RD_CFG_RD_TRANSFER_SHIFT;
if (desc->info.op_tmpl.addr.nbytes == 4)
cmd |= QSPI_RD_CFG_RD_ADDR_SEL_MASK;
if (nor->read_dummy) {
cmd |= QSPI_RD_CFG_RD_LATENCY_MASK;
cmd |= (nor->read_dummy - 1) << QSPI_RD_CFG_DUMMY_SHIFT;
}
cmd |= QSPI_RD_CFG_D_BUFFER_MASK;
cmd |= flash->clk_div & QSPI_RD_CFG_RD_SCK_SEL_MASK;
writel_relaxed(cmd, qspi->io_base + QSPI_RD_CFG_REG);
dev_dbg(qspi->dev, "Create read dirmap and setup RD_CFG_REG [%#x].\n", cmd);
} else if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) {
cmd = desc->info.op_tmpl.cmd.opcode << QSPI_WR_CFG_WR_CMD_SHIFT;
ret = phytium_spi_nor_protocol_encode(&desc->info.op_tmpl, &transfer);
if (ret) {
dev_err(qspi->dev, "Unsupported SPI NOR protocol.\n");
goto out;
}
cmd |= transfer << QSPI_WR_CFG_WR_TRANSFER_SHIFT;
if (desc->info.op_tmpl.addr.nbytes == 4)
cmd |= QSPI_WR_CFG_WR_ADDR_SEL_MASK;
cmd |= QSPI_WR_CFG_WR_MODE_MASK;
cmd |= flash->clk_div & QSPI_WR_CFG_WR_SCK_SEL_MASK;
writel_relaxed(cmd, qspi->io_base + QSPI_WR_CFG_REG);
dev_dbg(qspi->dev, "Create write dirmap and setup WR_CFG_REG [%#x].\n", cmd);
} else {
ret = -EINVAL;
}
out:
return ret;
}
static ssize_t phytium_qspi_dirmap_read(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, void *buf)
{
struct spi_device *spi = desc->mem->spi;
struct phytium_qspi *qspi = spi_controller_get_devdata(spi->master);
struct phytium_qspi_flash *flash = &qspi->flash[spi->chip_select];
void __iomem *src = flash->base + offs;
u8 *buf_rx = buf;
memcpy_fromio(buf_rx, src, len);
return len;
}
static ssize_t phytium_qspi_dirmap_write(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, const void *buf)
{
struct spi_device *spi = desc->mem->spi;
struct phytium_qspi *qspi = spi_controller_get_devdata(spi->master);
struct phytium_qspi_flash *flash = &qspi->flash[spi->chip_select];
void __iomem *dst = flash->base + offs;
void __iomem *addr;
int i;
size_t mask = 0x03;
u_char tmp[4] = {0};
if (offs & 0x03) {
dev_err(qspi->dev, "Addr not four-byte aligned!\n");
return -EINVAL;
}
for (i = 0; i < len / 4; i++)
writel_relaxed(*(u32 *)(buf + 4 * i), dst + 4 * i);
if (len & mask) {
addr = dst + (len & ~mask);
memcpy(tmp, buf + (len & ~mask), len & mask);
writel_relaxed(*(u32 *)(tmp), addr);
}
//write cache data to flash
writel_relaxed(QSPI_FLUSH_EN, qspi->io_base + QSPI_FLUSH_REG);
return len;
}
static int phytium_qspi_setup(struct spi_device *spi)
{
struct spi_controller *ctrl = spi->master;
struct phytium_qspi *qspi = spi_controller_get_devdata(ctrl);
struct phytium_qspi_flash *flash;
uint clk_div;
if (ctrl->busy)
return -EBUSY;
flash = &qspi->flash[spi->chip_select];
flash->cs = spi->chip_select;
flash->spi = spi;
if (flash->cs >= PHYTIUM_QSPI_MAX_NORCHIP) {
dev_err(qspi->dev, "Flash CS is out of range.\n");
return -EINVAL;
}
qspi->fnum++;
if (spi->max_speed_hz) {
clk_div = DIV_ROUND_UP(qspi->clk_rate, spi->max_speed_hz);
flash->clk_div = phytium_spi_nor_clac_clk_div(clk_div);
if (flash->clk_div == 65535) {
dev_err(qspi->dev, "qspi maximum frequency setting is error.\n");
return -EINVAL;
}
} else
flash->clk_div = PHYTIUM_QSPI_DEFAULT_SCK_SEL;
return 0;
}
static struct spi_controller_mem_ops phytium_qspi_mem_ops = {
.adjust_op_size = phytium_qspi_adjust_op_size,
.supports_op = phytium_qspi_supports_op,
.exec_op = phytium_qspi_exec_op,
.dirmap_create = phytium_qspi_dirmap_create,
.dirmap_read = phytium_qspi_dirmap_read,
.dirmap_write = phytium_qspi_dirmap_write,
};
/**
* Direct mapping is supported only when all flashes under the controller
* are of the same size and the mapping address is continuous. For those
* cases which flashes are of different sizes, the driver offered a non-dirmap
* mem_ops with which read/write ops is executed through command port.
*/
static struct spi_controller_mem_ops phytium_qspi_mem_ops_nodirmap = {
.adjust_op_size = phytium_qspi_adjust_op_size,
.supports_op = phytium_qspi_supports_op,
.exec_op = phytium_qspi_exec_op,
};
/**
* phytium_qspi_probe - Probe method for the QSPI driver
* @pdev: Pointer to the platform_device structure
*
* This function initializes the driver data structures and the hardware.
*
* Return: 0 on success and error value on failure
*/
static int phytium_qspi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct spi_controller *ctrl;
struct resource *res;
struct phytium_qspi *qspi;
int i, ret;
u32 flash_cap;
struct spi_mem *mem;
struct spi_nor *nor;
ctrl = spi_alloc_master(dev, sizeof(*qspi));
if (!ctrl)
return -ENOMEM;
ctrl->mode_bits = SPI_CPOL | SPI_CPHA |
SPI_RX_DUAL | SPI_RX_QUAD |
SPI_TX_DUAL | SPI_TX_QUAD;
ctrl->setup = phytium_qspi_setup;
ctrl->num_chipselect = PHYTIUM_QSPI_MAX_NORCHIP;
ctrl->dev.of_node = dev->of_node;
qspi = spi_controller_get_devdata(ctrl);
qspi->ctrl = ctrl;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi");
qspi->io_base = devm_ioremap_resource(dev, res);
if (IS_ERR(qspi->io_base)) {
ret = PTR_ERR(qspi->io_base);
goto probe_master_put;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi_mm");
qspi->mm_base = devm_ioremap_resource(dev, res);
if (IS_ERR(qspi->mm_base)) {
ret = PTR_ERR(qspi->mm_base);
goto probe_master_put;
}
qspi->mm_size = resource_size(res);
if (qspi->mm_size > PHYTIUM_QSPI_MAX_MMAP_SZ) {
ret = -EINVAL;
goto probe_master_put;
}
qspi->used_size = 0;
qspi->clk = devm_clk_get(dev, NULL);
if (IS_ERR(qspi->clk)) {
ret = PTR_ERR(qspi->clk);
goto probe_master_put;
}
qspi->clk_rate = clk_get_rate(qspi->clk);
if (!qspi->clk_rate) {
ret = -EINVAL;
goto probe_master_put;
}
pm_runtime_enable(dev);
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
pm_runtime_put_noidle(dev);
goto probe_master_put;
}
ret = clk_prepare_enable(qspi->clk);
if (ret) {
dev_err(dev, "Failed to enable PCLK of the controller.\n");
goto probe_clk_failed;
}
qspi->nodirmap = device_property_present(dev, "no-direct-mapping");
ctrl->mem_ops = qspi->nodirmap ?
&phytium_qspi_mem_ops_nodirmap :
&phytium_qspi_mem_ops;
qspi->dev = dev;
platform_set_drvdata(pdev, qspi);
ret = devm_spi_register_controller(dev, ctrl);
if (ret) {
dev_err(dev, "failed to register SPI controller: %d\n", ret);
goto probe_setup_failed;
}
if (!qspi->nodirmap) {
/*
* The controller supports direct mapping access only if all
* flashes are of same size.
*/
i = 0;
for (i = 0; qspi->fnum > i && i < PHYTIUM_QSPI_MAX_NORCHIP; i++) {
if (qspi->flash[i].spi) {
mem = spi_get_drvdata(qspi->flash[i].spi);
if (mem) {
nor = spi_mem_get_drvdata(mem);
if (nor)
qspi->flash[i].size = nor->mtd.size;
}
}
}
for (i = 1; qspi->fnum > i && i < PHYTIUM_QSPI_MAX_NORCHIP; i++) {
if (qspi->flash[i].size != qspi->flash[0].size) {
dev_err(dev, "Flashes are of different sizes.\n");
ret = -EINVAL;
goto probe_setup_failed;
}
}
ret = phytium_qspi_flash_capacity_encode(qspi->flash[0].size,
&flash_cap);
if (ret) {
dev_err(dev, "Flash size is invalid.\n");
goto probe_setup_failed;
}
flash_cap |= qspi->fnum << QSPI_FLASH_CAP_NUM_SHIFT;
writel_relaxed(flash_cap, qspi->io_base + QSPI_FLASH_CAP_REG);
}
return 0;
probe_setup_failed:
clk_disable_unprepare(qspi->clk);
probe_clk_failed:
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
probe_master_put:
return ret;
}
/**
* phytium_qspi_remove - Remove method for the QSPI driver
* @pdev: Pointer to the platform_device structure
*
* This function is called if a device is physically removed from the system
* or if the driver module is being unloaded. It free all resources allocated
* to the device.
*
* Return: 0 on success and error value on failure
*/
static int phytium_qspi_remove(struct platform_device *pdev)
{
struct phytium_qspi *qspi = platform_get_drvdata(pdev);
clk_disable_unprepare(qspi->clk);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return 0;
}
static int __maybe_unused phytium_qspi_suspend(struct device *dev)
{
return pm_runtime_force_suspend(dev);
}
static int __maybe_unused phytium_qspi_resume(struct device *dev)
{
return pm_runtime_force_resume(dev);
}
static const struct dev_pm_ops phytium_qspi_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(phytium_qspi_suspend,
phytium_qspi_resume)
};
static const struct of_device_id phytium_qspi_of_match[] = {
{ .compatible = "phytium,qspi-nor" },
{ }
};
MODULE_DEVICE_TABLE(of, phytium_qspi_of_match);
static struct platform_driver phytium_qspi_driver = {
.probe = phytium_qspi_probe,
.remove = phytium_qspi_remove,
.driver = {
.name = "phytium-qspi",
.of_match_table = of_match_ptr(phytium_qspi_of_match),
.pm = &phytium_qspi_pm_ops,
},
};
module_platform_driver(phytium_qspi_driver);
MODULE_AUTHOR("Chen Baozi <chenbaozi@phytium.com.cn>");
MODULE_DESCRIPTION("Phytium Quad SPI driver");
MODULE_LICENSE("GPL v2");