162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ASPEED FMC/SPI Memory Controller Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2015-2022, IBM Corporation. 662306a36Sopenharmony_ci * Copyright (c) 2020, ASPEED Corporation. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/clk.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/of.h> 1262306a36Sopenharmony_ci#include <linux/of_platform.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/spi/spi.h> 1562306a36Sopenharmony_ci#include <linux/spi/spi-mem.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define DEVICE_NAME "spi-aspeed-smc" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* Type setting Register */ 2062306a36Sopenharmony_ci#define CONFIG_REG 0x0 2162306a36Sopenharmony_ci#define CONFIG_TYPE_SPI 0x2 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* CE Control Register */ 2462306a36Sopenharmony_ci#define CE_CTRL_REG 0x4 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* CEx Control Register */ 2762306a36Sopenharmony_ci#define CE0_CTRL_REG 0x10 2862306a36Sopenharmony_ci#define CTRL_IO_MODE_MASK GENMASK(30, 28) 2962306a36Sopenharmony_ci#define CTRL_IO_SINGLE_DATA 0x0 3062306a36Sopenharmony_ci#define CTRL_IO_DUAL_DATA BIT(29) 3162306a36Sopenharmony_ci#define CTRL_IO_QUAD_DATA BIT(30) 3262306a36Sopenharmony_ci#define CTRL_COMMAND_SHIFT 16 3362306a36Sopenharmony_ci#define CTRL_IO_ADDRESS_4B BIT(13) /* AST2400 SPI only */ 3462306a36Sopenharmony_ci#define CTRL_IO_DUMMY_SET(dummy) \ 3562306a36Sopenharmony_ci (((((dummy) >> 2) & 0x1) << 14) | (((dummy) & 0x3) << 6)) 3662306a36Sopenharmony_ci#define CTRL_FREQ_SEL_SHIFT 8 3762306a36Sopenharmony_ci#define CTRL_FREQ_SEL_MASK GENMASK(11, CTRL_FREQ_SEL_SHIFT) 3862306a36Sopenharmony_ci#define CTRL_CE_STOP_ACTIVE BIT(2) 3962306a36Sopenharmony_ci#define CTRL_IO_MODE_CMD_MASK GENMASK(1, 0) 4062306a36Sopenharmony_ci#define CTRL_IO_MODE_NORMAL 0x0 4162306a36Sopenharmony_ci#define CTRL_IO_MODE_READ 0x1 4262306a36Sopenharmony_ci#define CTRL_IO_MODE_WRITE 0x2 4362306a36Sopenharmony_ci#define CTRL_IO_MODE_USER 0x3 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define CTRL_IO_CMD_MASK 0xf0ff40c3 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* CEx Address Decoding Range Register */ 4862306a36Sopenharmony_ci#define CE0_SEGMENT_ADDR_REG 0x30 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* CEx Read timing compensation register */ 5162306a36Sopenharmony_ci#define CE0_TIMING_COMPENSATION_REG 0x94 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cienum aspeed_spi_ctl_reg_value { 5462306a36Sopenharmony_ci ASPEED_SPI_BASE, 5562306a36Sopenharmony_ci ASPEED_SPI_READ, 5662306a36Sopenharmony_ci ASPEED_SPI_WRITE, 5762306a36Sopenharmony_ci ASPEED_SPI_MAX, 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct aspeed_spi; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistruct aspeed_spi_chip { 6362306a36Sopenharmony_ci struct aspeed_spi *aspi; 6462306a36Sopenharmony_ci u32 cs; 6562306a36Sopenharmony_ci void __iomem *ctl; 6662306a36Sopenharmony_ci void __iomem *ahb_base; 6762306a36Sopenharmony_ci u32 ahb_window_size; 6862306a36Sopenharmony_ci u32 ctl_val[ASPEED_SPI_MAX]; 6962306a36Sopenharmony_ci u32 clk_freq; 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistruct aspeed_spi_data { 7362306a36Sopenharmony_ci u32 ctl0; 7462306a36Sopenharmony_ci u32 max_cs; 7562306a36Sopenharmony_ci bool hastype; 7662306a36Sopenharmony_ci u32 mode_bits; 7762306a36Sopenharmony_ci u32 we0; 7862306a36Sopenharmony_ci u32 timing; 7962306a36Sopenharmony_ci u32 hclk_mask; 8062306a36Sopenharmony_ci u32 hdiv_max; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci u32 (*segment_start)(struct aspeed_spi *aspi, u32 reg); 8362306a36Sopenharmony_ci u32 (*segment_end)(struct aspeed_spi *aspi, u32 reg); 8462306a36Sopenharmony_ci u32 (*segment_reg)(struct aspeed_spi *aspi, u32 start, u32 end); 8562306a36Sopenharmony_ci int (*calibrate)(struct aspeed_spi_chip *chip, u32 hdiv, 8662306a36Sopenharmony_ci const u8 *golden_buf, u8 *test_buf); 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define ASPEED_SPI_MAX_NUM_CS 5 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistruct aspeed_spi { 9262306a36Sopenharmony_ci const struct aspeed_spi_data *data; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci void __iomem *regs; 9562306a36Sopenharmony_ci void __iomem *ahb_base; 9662306a36Sopenharmony_ci u32 ahb_base_phy; 9762306a36Sopenharmony_ci u32 ahb_window_size; 9862306a36Sopenharmony_ci struct device *dev; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci struct clk *clk; 10162306a36Sopenharmony_ci u32 clk_freq; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci struct aspeed_spi_chip chips[ASPEED_SPI_MAX_NUM_CS]; 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic u32 aspeed_spi_get_io_mode(const struct spi_mem_op *op) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci switch (op->data.buswidth) { 10962306a36Sopenharmony_ci case 1: 11062306a36Sopenharmony_ci return CTRL_IO_SINGLE_DATA; 11162306a36Sopenharmony_ci case 2: 11262306a36Sopenharmony_ci return CTRL_IO_DUAL_DATA; 11362306a36Sopenharmony_ci case 4: 11462306a36Sopenharmony_ci return CTRL_IO_QUAD_DATA; 11562306a36Sopenharmony_ci default: 11662306a36Sopenharmony_ci return CTRL_IO_SINGLE_DATA; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic void aspeed_spi_set_io_mode(struct aspeed_spi_chip *chip, u32 io_mode) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci u32 ctl; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (io_mode > 0) { 12562306a36Sopenharmony_ci ctl = readl(chip->ctl) & ~CTRL_IO_MODE_MASK; 12662306a36Sopenharmony_ci ctl |= io_mode; 12762306a36Sopenharmony_ci writel(ctl, chip->ctl); 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic void aspeed_spi_start_user(struct aspeed_spi_chip *chip) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci u32 ctl = chip->ctl_val[ASPEED_SPI_BASE]; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci ctl |= CTRL_IO_MODE_USER | CTRL_CE_STOP_ACTIVE; 13662306a36Sopenharmony_ci writel(ctl, chip->ctl); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci ctl &= ~CTRL_CE_STOP_ACTIVE; 13962306a36Sopenharmony_ci writel(ctl, chip->ctl); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic void aspeed_spi_stop_user(struct aspeed_spi_chip *chip) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci u32 ctl = chip->ctl_val[ASPEED_SPI_READ] | 14562306a36Sopenharmony_ci CTRL_IO_MODE_USER | CTRL_CE_STOP_ACTIVE; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci writel(ctl, chip->ctl); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* Restore defaults */ 15062306a36Sopenharmony_ci writel(chip->ctl_val[ASPEED_SPI_READ], chip->ctl); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic int aspeed_spi_read_from_ahb(void *buf, void __iomem *src, size_t len) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci size_t offset = 0; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (IS_ALIGNED((uintptr_t)src, sizeof(uintptr_t)) && 15862306a36Sopenharmony_ci IS_ALIGNED((uintptr_t)buf, sizeof(uintptr_t))) { 15962306a36Sopenharmony_ci ioread32_rep(src, buf, len >> 2); 16062306a36Sopenharmony_ci offset = len & ~0x3; 16162306a36Sopenharmony_ci len -= offset; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci ioread8_rep(src, (u8 *)buf + offset, len); 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic int aspeed_spi_write_to_ahb(void __iomem *dst, const void *buf, size_t len) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci size_t offset = 0; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (IS_ALIGNED((uintptr_t)dst, sizeof(uintptr_t)) && 17262306a36Sopenharmony_ci IS_ALIGNED((uintptr_t)buf, sizeof(uintptr_t))) { 17362306a36Sopenharmony_ci iowrite32_rep(dst, buf, len >> 2); 17462306a36Sopenharmony_ci offset = len & ~0x3; 17562306a36Sopenharmony_ci len -= offset; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci iowrite8_rep(dst, (const u8 *)buf + offset, len); 17862306a36Sopenharmony_ci return 0; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic int aspeed_spi_send_cmd_addr(struct aspeed_spi_chip *chip, u8 addr_nbytes, 18262306a36Sopenharmony_ci u64 offset, u32 opcode) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci __be32 temp; 18562306a36Sopenharmony_ci u32 cmdaddr; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci switch (addr_nbytes) { 18862306a36Sopenharmony_ci case 3: 18962306a36Sopenharmony_ci cmdaddr = offset & 0xFFFFFF; 19062306a36Sopenharmony_ci cmdaddr |= opcode << 24; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci temp = cpu_to_be32(cmdaddr); 19362306a36Sopenharmony_ci aspeed_spi_write_to_ahb(chip->ahb_base, &temp, 4); 19462306a36Sopenharmony_ci break; 19562306a36Sopenharmony_ci case 4: 19662306a36Sopenharmony_ci temp = cpu_to_be32(offset); 19762306a36Sopenharmony_ci aspeed_spi_write_to_ahb(chip->ahb_base, &opcode, 1); 19862306a36Sopenharmony_ci aspeed_spi_write_to_ahb(chip->ahb_base, &temp, 4); 19962306a36Sopenharmony_ci break; 20062306a36Sopenharmony_ci default: 20162306a36Sopenharmony_ci WARN_ONCE(1, "Unexpected address width %u", addr_nbytes); 20262306a36Sopenharmony_ci return -EOPNOTSUPP; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic int aspeed_spi_read_reg(struct aspeed_spi_chip *chip, 20862306a36Sopenharmony_ci const struct spi_mem_op *op) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci aspeed_spi_start_user(chip); 21162306a36Sopenharmony_ci aspeed_spi_write_to_ahb(chip->ahb_base, &op->cmd.opcode, 1); 21262306a36Sopenharmony_ci aspeed_spi_read_from_ahb(op->data.buf.in, 21362306a36Sopenharmony_ci chip->ahb_base, op->data.nbytes); 21462306a36Sopenharmony_ci aspeed_spi_stop_user(chip); 21562306a36Sopenharmony_ci return 0; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int aspeed_spi_write_reg(struct aspeed_spi_chip *chip, 21962306a36Sopenharmony_ci const struct spi_mem_op *op) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci aspeed_spi_start_user(chip); 22262306a36Sopenharmony_ci aspeed_spi_write_to_ahb(chip->ahb_base, &op->cmd.opcode, 1); 22362306a36Sopenharmony_ci aspeed_spi_write_to_ahb(chip->ahb_base, op->data.buf.out, 22462306a36Sopenharmony_ci op->data.nbytes); 22562306a36Sopenharmony_ci aspeed_spi_stop_user(chip); 22662306a36Sopenharmony_ci return 0; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic ssize_t aspeed_spi_read_user(struct aspeed_spi_chip *chip, 23062306a36Sopenharmony_ci const struct spi_mem_op *op, 23162306a36Sopenharmony_ci u64 offset, size_t len, void *buf) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci int io_mode = aspeed_spi_get_io_mode(op); 23462306a36Sopenharmony_ci u8 dummy = 0xFF; 23562306a36Sopenharmony_ci int i; 23662306a36Sopenharmony_ci int ret; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci aspeed_spi_start_user(chip); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci ret = aspeed_spi_send_cmd_addr(chip, op->addr.nbytes, offset, op->cmd.opcode); 24162306a36Sopenharmony_ci if (ret < 0) 24262306a36Sopenharmony_ci return ret; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (op->dummy.buswidth && op->dummy.nbytes) { 24562306a36Sopenharmony_ci for (i = 0; i < op->dummy.nbytes / op->dummy.buswidth; i++) 24662306a36Sopenharmony_ci aspeed_spi_write_to_ahb(chip->ahb_base, &dummy, sizeof(dummy)); 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci aspeed_spi_set_io_mode(chip, io_mode); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci aspeed_spi_read_from_ahb(buf, chip->ahb_base, len); 25262306a36Sopenharmony_ci aspeed_spi_stop_user(chip); 25362306a36Sopenharmony_ci return 0; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic ssize_t aspeed_spi_write_user(struct aspeed_spi_chip *chip, 25762306a36Sopenharmony_ci const struct spi_mem_op *op) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci int ret; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci aspeed_spi_start_user(chip); 26262306a36Sopenharmony_ci ret = aspeed_spi_send_cmd_addr(chip, op->addr.nbytes, op->addr.val, op->cmd.opcode); 26362306a36Sopenharmony_ci if (ret < 0) 26462306a36Sopenharmony_ci return ret; 26562306a36Sopenharmony_ci aspeed_spi_write_to_ahb(chip->ahb_base, op->data.buf.out, op->data.nbytes); 26662306a36Sopenharmony_ci aspeed_spi_stop_user(chip); 26762306a36Sopenharmony_ci return 0; 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/* support for 1-1-1, 1-1-2 or 1-1-4 */ 27162306a36Sopenharmony_cistatic bool aspeed_spi_supports_op(struct spi_mem *mem, const struct spi_mem_op *op) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci if (op->cmd.buswidth > 1) 27462306a36Sopenharmony_ci return false; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (op->addr.nbytes != 0) { 27762306a36Sopenharmony_ci if (op->addr.buswidth > 1) 27862306a36Sopenharmony_ci return false; 27962306a36Sopenharmony_ci if (op->addr.nbytes < 3 || op->addr.nbytes > 4) 28062306a36Sopenharmony_ci return false; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (op->dummy.nbytes != 0) { 28462306a36Sopenharmony_ci if (op->dummy.buswidth > 1 || op->dummy.nbytes > 7) 28562306a36Sopenharmony_ci return false; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (op->data.nbytes != 0 && op->data.buswidth > 4) 28962306a36Sopenharmony_ci return false; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return spi_mem_default_supports_op(mem, op); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic const struct aspeed_spi_data ast2400_spi_data; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic int do_aspeed_spi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct aspeed_spi *aspi = spi_controller_get_devdata(mem->spi->controller); 29962306a36Sopenharmony_ci struct aspeed_spi_chip *chip = &aspi->chips[spi_get_chipselect(mem->spi, 0)]; 30062306a36Sopenharmony_ci u32 addr_mode, addr_mode_backup; 30162306a36Sopenharmony_ci u32 ctl_val; 30262306a36Sopenharmony_ci int ret = 0; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci dev_dbg(aspi->dev, 30562306a36Sopenharmony_ci "CE%d %s OP %#x mode:%d.%d.%d.%d naddr:%#x ndummies:%#x len:%#x", 30662306a36Sopenharmony_ci chip->cs, op->data.dir == SPI_MEM_DATA_IN ? "read" : "write", 30762306a36Sopenharmony_ci op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth, 30862306a36Sopenharmony_ci op->dummy.buswidth, op->data.buswidth, 30962306a36Sopenharmony_ci op->addr.nbytes, op->dummy.nbytes, op->data.nbytes); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci addr_mode = readl(aspi->regs + CE_CTRL_REG); 31262306a36Sopenharmony_ci addr_mode_backup = addr_mode; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci ctl_val = chip->ctl_val[ASPEED_SPI_BASE]; 31562306a36Sopenharmony_ci ctl_val &= ~CTRL_IO_CMD_MASK; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci ctl_val |= op->cmd.opcode << CTRL_COMMAND_SHIFT; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* 4BYTE address mode */ 32062306a36Sopenharmony_ci if (op->addr.nbytes) { 32162306a36Sopenharmony_ci if (op->addr.nbytes == 4) 32262306a36Sopenharmony_ci addr_mode |= (0x11 << chip->cs); 32362306a36Sopenharmony_ci else 32462306a36Sopenharmony_ci addr_mode &= ~(0x11 << chip->cs); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (op->addr.nbytes == 4 && chip->aspi->data == &ast2400_spi_data) 32762306a36Sopenharmony_ci ctl_val |= CTRL_IO_ADDRESS_4B; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (op->dummy.nbytes) 33162306a36Sopenharmony_ci ctl_val |= CTRL_IO_DUMMY_SET(op->dummy.nbytes / op->dummy.buswidth); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (op->data.nbytes) 33462306a36Sopenharmony_ci ctl_val |= aspeed_spi_get_io_mode(op); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (op->data.dir == SPI_MEM_DATA_OUT) 33762306a36Sopenharmony_ci ctl_val |= CTRL_IO_MODE_WRITE; 33862306a36Sopenharmony_ci else 33962306a36Sopenharmony_ci ctl_val |= CTRL_IO_MODE_READ; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (addr_mode != addr_mode_backup) 34262306a36Sopenharmony_ci writel(addr_mode, aspi->regs + CE_CTRL_REG); 34362306a36Sopenharmony_ci writel(ctl_val, chip->ctl); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (op->data.dir == SPI_MEM_DATA_IN) { 34662306a36Sopenharmony_ci if (!op->addr.nbytes) 34762306a36Sopenharmony_ci ret = aspeed_spi_read_reg(chip, op); 34862306a36Sopenharmony_ci else 34962306a36Sopenharmony_ci ret = aspeed_spi_read_user(chip, op, op->addr.val, 35062306a36Sopenharmony_ci op->data.nbytes, op->data.buf.in); 35162306a36Sopenharmony_ci } else { 35262306a36Sopenharmony_ci if (!op->addr.nbytes) 35362306a36Sopenharmony_ci ret = aspeed_spi_write_reg(chip, op); 35462306a36Sopenharmony_ci else 35562306a36Sopenharmony_ci ret = aspeed_spi_write_user(chip, op); 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* Restore defaults */ 35962306a36Sopenharmony_ci if (addr_mode != addr_mode_backup) 36062306a36Sopenharmony_ci writel(addr_mode_backup, aspi->regs + CE_CTRL_REG); 36162306a36Sopenharmony_ci writel(chip->ctl_val[ASPEED_SPI_READ], chip->ctl); 36262306a36Sopenharmony_ci return ret; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic int aspeed_spi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci int ret; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci ret = do_aspeed_spi_exec_op(mem, op); 37062306a36Sopenharmony_ci if (ret) 37162306a36Sopenharmony_ci dev_err(&mem->spi->dev, "operation failed: %d\n", ret); 37262306a36Sopenharmony_ci return ret; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic const char *aspeed_spi_get_name(struct spi_mem *mem) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct aspeed_spi *aspi = spi_controller_get_devdata(mem->spi->controller); 37862306a36Sopenharmony_ci struct device *dev = aspi->dev; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci return devm_kasprintf(dev, GFP_KERNEL, "%s.%d", dev_name(dev), 38162306a36Sopenharmony_ci spi_get_chipselect(mem->spi, 0)); 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistruct aspeed_spi_window { 38562306a36Sopenharmony_ci u32 cs; 38662306a36Sopenharmony_ci u32 offset; 38762306a36Sopenharmony_ci u32 size; 38862306a36Sopenharmony_ci}; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic void aspeed_spi_get_windows(struct aspeed_spi *aspi, 39162306a36Sopenharmony_ci struct aspeed_spi_window windows[ASPEED_SPI_MAX_NUM_CS]) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci const struct aspeed_spi_data *data = aspi->data; 39462306a36Sopenharmony_ci u32 reg_val; 39562306a36Sopenharmony_ci u32 cs; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci for (cs = 0; cs < aspi->data->max_cs; cs++) { 39862306a36Sopenharmony_ci reg_val = readl(aspi->regs + CE0_SEGMENT_ADDR_REG + cs * 4); 39962306a36Sopenharmony_ci windows[cs].cs = cs; 40062306a36Sopenharmony_ci windows[cs].size = data->segment_end(aspi, reg_val) - 40162306a36Sopenharmony_ci data->segment_start(aspi, reg_val); 40262306a36Sopenharmony_ci windows[cs].offset = data->segment_start(aspi, reg_val) - aspi->ahb_base_phy; 40362306a36Sopenharmony_ci dev_vdbg(aspi->dev, "CE%d offset=0x%.8x size=0x%x\n", cs, 40462306a36Sopenharmony_ci windows[cs].offset, windows[cs].size); 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci/* 40962306a36Sopenharmony_ci * On the AST2600, some CE windows are closed by default at reset but 41062306a36Sopenharmony_ci * U-Boot should open all. 41162306a36Sopenharmony_ci */ 41262306a36Sopenharmony_cistatic int aspeed_spi_chip_set_default_window(struct aspeed_spi_chip *chip) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct aspeed_spi *aspi = chip->aspi; 41562306a36Sopenharmony_ci struct aspeed_spi_window windows[ASPEED_SPI_MAX_NUM_CS] = { 0 }; 41662306a36Sopenharmony_ci struct aspeed_spi_window *win = &windows[chip->cs]; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* No segment registers for the AST2400 SPI controller */ 41962306a36Sopenharmony_ci if (aspi->data == &ast2400_spi_data) { 42062306a36Sopenharmony_ci win->offset = 0; 42162306a36Sopenharmony_ci win->size = aspi->ahb_window_size; 42262306a36Sopenharmony_ci } else { 42362306a36Sopenharmony_ci aspeed_spi_get_windows(aspi, windows); 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci chip->ahb_base = aspi->ahb_base + win->offset; 42762306a36Sopenharmony_ci chip->ahb_window_size = win->size; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci dev_dbg(aspi->dev, "CE%d default window [ 0x%.8x - 0x%.8x ] %dMB", 43062306a36Sopenharmony_ci chip->cs, aspi->ahb_base_phy + win->offset, 43162306a36Sopenharmony_ci aspi->ahb_base_phy + win->offset + win->size - 1, 43262306a36Sopenharmony_ci win->size >> 20); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci return chip->ahb_window_size ? 0 : -1; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic int aspeed_spi_set_window(struct aspeed_spi *aspi, 43862306a36Sopenharmony_ci const struct aspeed_spi_window *win) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci u32 start = aspi->ahb_base_phy + win->offset; 44162306a36Sopenharmony_ci u32 end = start + win->size; 44262306a36Sopenharmony_ci void __iomem *seg_reg = aspi->regs + CE0_SEGMENT_ADDR_REG + win->cs * 4; 44362306a36Sopenharmony_ci u32 seg_val_backup = readl(seg_reg); 44462306a36Sopenharmony_ci u32 seg_val = aspi->data->segment_reg(aspi, start, end); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (seg_val == seg_val_backup) 44762306a36Sopenharmony_ci return 0; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci writel(seg_val, seg_reg); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* 45262306a36Sopenharmony_ci * Restore initial value if something goes wrong else we could 45362306a36Sopenharmony_ci * loose access to the chip. 45462306a36Sopenharmony_ci */ 45562306a36Sopenharmony_ci if (seg_val != readl(seg_reg)) { 45662306a36Sopenharmony_ci dev_err(aspi->dev, "CE%d invalid window [ 0x%.8x - 0x%.8x ] %dMB", 45762306a36Sopenharmony_ci win->cs, start, end - 1, win->size >> 20); 45862306a36Sopenharmony_ci writel(seg_val_backup, seg_reg); 45962306a36Sopenharmony_ci return -EIO; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if (win->size) 46362306a36Sopenharmony_ci dev_dbg(aspi->dev, "CE%d new window [ 0x%.8x - 0x%.8x ] %dMB", 46462306a36Sopenharmony_ci win->cs, start, end - 1, win->size >> 20); 46562306a36Sopenharmony_ci else 46662306a36Sopenharmony_ci dev_dbg(aspi->dev, "CE%d window closed", win->cs); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci return 0; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci/* 47262306a36Sopenharmony_ci * Yet to be done when possible : 47362306a36Sopenharmony_ci * - Align mappings on flash size (we don't have the info) 47462306a36Sopenharmony_ci * - ioremap each window, not strictly necessary since the overall window 47562306a36Sopenharmony_ci * is correct. 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_cistatic const struct aspeed_spi_data ast2500_spi_data; 47862306a36Sopenharmony_cistatic const struct aspeed_spi_data ast2600_spi_data; 47962306a36Sopenharmony_cistatic const struct aspeed_spi_data ast2600_fmc_data; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic int aspeed_spi_chip_adjust_window(struct aspeed_spi_chip *chip, 48262306a36Sopenharmony_ci u32 local_offset, u32 size) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct aspeed_spi *aspi = chip->aspi; 48562306a36Sopenharmony_ci struct aspeed_spi_window windows[ASPEED_SPI_MAX_NUM_CS] = { 0 }; 48662306a36Sopenharmony_ci struct aspeed_spi_window *win = &windows[chip->cs]; 48762306a36Sopenharmony_ci int ret; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* No segment registers for the AST2400 SPI controller */ 49062306a36Sopenharmony_ci if (aspi->data == &ast2400_spi_data) 49162306a36Sopenharmony_ci return 0; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* 49462306a36Sopenharmony_ci * Due to an HW issue on the AST2500 SPI controller, the CE0 49562306a36Sopenharmony_ci * window size should be smaller than the maximum 128MB. 49662306a36Sopenharmony_ci */ 49762306a36Sopenharmony_ci if (aspi->data == &ast2500_spi_data && chip->cs == 0 && size == SZ_128M) { 49862306a36Sopenharmony_ci size = 120 << 20; 49962306a36Sopenharmony_ci dev_info(aspi->dev, "CE%d window resized to %dMB (AST2500 HW quirk)", 50062306a36Sopenharmony_ci chip->cs, size >> 20); 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci /* 50462306a36Sopenharmony_ci * The decoding size of AST2600 SPI controller should set at 50562306a36Sopenharmony_ci * least 2MB. 50662306a36Sopenharmony_ci */ 50762306a36Sopenharmony_ci if ((aspi->data == &ast2600_spi_data || aspi->data == &ast2600_fmc_data) && 50862306a36Sopenharmony_ci size < SZ_2M) { 50962306a36Sopenharmony_ci size = SZ_2M; 51062306a36Sopenharmony_ci dev_info(aspi->dev, "CE%d window resized to %dMB (AST2600 Decoding)", 51162306a36Sopenharmony_ci chip->cs, size >> 20); 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci aspeed_spi_get_windows(aspi, windows); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* Adjust this chip window */ 51762306a36Sopenharmony_ci win->offset += local_offset; 51862306a36Sopenharmony_ci win->size = size; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (win->offset + win->size > aspi->ahb_window_size) { 52162306a36Sopenharmony_ci win->size = aspi->ahb_window_size - win->offset; 52262306a36Sopenharmony_ci dev_warn(aspi->dev, "CE%d window resized to %dMB", chip->cs, win->size >> 20); 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci ret = aspeed_spi_set_window(aspi, win); 52662306a36Sopenharmony_ci if (ret) 52762306a36Sopenharmony_ci return ret; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* Update chip mapping info */ 53062306a36Sopenharmony_ci chip->ahb_base = aspi->ahb_base + win->offset; 53162306a36Sopenharmony_ci chip->ahb_window_size = win->size; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* 53462306a36Sopenharmony_ci * Also adjust next chip window to make sure that it does not 53562306a36Sopenharmony_ci * overlap with the current window. 53662306a36Sopenharmony_ci */ 53762306a36Sopenharmony_ci if (chip->cs < aspi->data->max_cs - 1) { 53862306a36Sopenharmony_ci struct aspeed_spi_window *next = &windows[chip->cs + 1]; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* Change offset and size to keep the same end address */ 54162306a36Sopenharmony_ci if ((next->offset + next->size) > (win->offset + win->size)) 54262306a36Sopenharmony_ci next->size = (next->offset + next->size) - (win->offset + win->size); 54362306a36Sopenharmony_ci else 54462306a36Sopenharmony_ci next->size = 0; 54562306a36Sopenharmony_ci next->offset = win->offset + win->size; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci aspeed_spi_set_window(aspi, next); 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci return 0; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic int aspeed_spi_do_calibration(struct aspeed_spi_chip *chip); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci struct aspeed_spi *aspi = spi_controller_get_devdata(desc->mem->spi->controller); 55762306a36Sopenharmony_ci struct aspeed_spi_chip *chip = &aspi->chips[spi_get_chipselect(desc->mem->spi, 0)]; 55862306a36Sopenharmony_ci struct spi_mem_op *op = &desc->info.op_tmpl; 55962306a36Sopenharmony_ci u32 ctl_val; 56062306a36Sopenharmony_ci int ret = 0; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci dev_dbg(aspi->dev, 56362306a36Sopenharmony_ci "CE%d %s dirmap [ 0x%.8llx - 0x%.8llx ] OP %#x mode:%d.%d.%d.%d naddr:%#x ndummies:%#x\n", 56462306a36Sopenharmony_ci chip->cs, op->data.dir == SPI_MEM_DATA_IN ? "read" : "write", 56562306a36Sopenharmony_ci desc->info.offset, desc->info.offset + desc->info.length, 56662306a36Sopenharmony_ci op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth, 56762306a36Sopenharmony_ci op->dummy.buswidth, op->data.buswidth, 56862306a36Sopenharmony_ci op->addr.nbytes, op->dummy.nbytes); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci chip->clk_freq = desc->mem->spi->max_speed_hz; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* Only for reads */ 57362306a36Sopenharmony_ci if (op->data.dir != SPI_MEM_DATA_IN) 57462306a36Sopenharmony_ci return -EOPNOTSUPP; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci aspeed_spi_chip_adjust_window(chip, desc->info.offset, desc->info.length); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (desc->info.length > chip->ahb_window_size) 57962306a36Sopenharmony_ci dev_warn(aspi->dev, "CE%d window (%dMB) too small for mapping", 58062306a36Sopenharmony_ci chip->cs, chip->ahb_window_size >> 20); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* Define the default IO read settings */ 58362306a36Sopenharmony_ci ctl_val = readl(chip->ctl) & ~CTRL_IO_CMD_MASK; 58462306a36Sopenharmony_ci ctl_val |= aspeed_spi_get_io_mode(op) | 58562306a36Sopenharmony_ci op->cmd.opcode << CTRL_COMMAND_SHIFT | 58662306a36Sopenharmony_ci CTRL_IO_MODE_READ; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (op->dummy.nbytes) 58962306a36Sopenharmony_ci ctl_val |= CTRL_IO_DUMMY_SET(op->dummy.nbytes / op->dummy.buswidth); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* Tune 4BYTE address mode */ 59262306a36Sopenharmony_ci if (op->addr.nbytes) { 59362306a36Sopenharmony_ci u32 addr_mode = readl(aspi->regs + CE_CTRL_REG); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (op->addr.nbytes == 4) 59662306a36Sopenharmony_ci addr_mode |= (0x11 << chip->cs); 59762306a36Sopenharmony_ci else 59862306a36Sopenharmony_ci addr_mode &= ~(0x11 << chip->cs); 59962306a36Sopenharmony_ci writel(addr_mode, aspi->regs + CE_CTRL_REG); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* AST2400 SPI controller sets 4BYTE address mode in 60262306a36Sopenharmony_ci * CE0 Control Register 60362306a36Sopenharmony_ci */ 60462306a36Sopenharmony_ci if (op->addr.nbytes == 4 && chip->aspi->data == &ast2400_spi_data) 60562306a36Sopenharmony_ci ctl_val |= CTRL_IO_ADDRESS_4B; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* READ mode is the controller default setting */ 60962306a36Sopenharmony_ci chip->ctl_val[ASPEED_SPI_READ] = ctl_val; 61062306a36Sopenharmony_ci writel(chip->ctl_val[ASPEED_SPI_READ], chip->ctl); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci ret = aspeed_spi_do_calibration(chip); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci dev_info(aspi->dev, "CE%d read buswidth:%d [0x%08x]\n", 61562306a36Sopenharmony_ci chip->cs, op->data.buswidth, chip->ctl_val[ASPEED_SPI_READ]); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci return ret; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic ssize_t aspeed_spi_dirmap_read(struct spi_mem_dirmap_desc *desc, 62162306a36Sopenharmony_ci u64 offset, size_t len, void *buf) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci struct aspeed_spi *aspi = spi_controller_get_devdata(desc->mem->spi->controller); 62462306a36Sopenharmony_ci struct aspeed_spi_chip *chip = &aspi->chips[spi_get_chipselect(desc->mem->spi, 0)]; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci /* Switch to USER command mode if mapping window is too small */ 62762306a36Sopenharmony_ci if (chip->ahb_window_size < offset + len) { 62862306a36Sopenharmony_ci int ret; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci ret = aspeed_spi_read_user(chip, &desc->info.op_tmpl, offset, len, buf); 63162306a36Sopenharmony_ci if (ret < 0) 63262306a36Sopenharmony_ci return ret; 63362306a36Sopenharmony_ci } else { 63462306a36Sopenharmony_ci memcpy_fromio(buf, chip->ahb_base + offset, len); 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci return len; 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic const struct spi_controller_mem_ops aspeed_spi_mem_ops = { 64162306a36Sopenharmony_ci .supports_op = aspeed_spi_supports_op, 64262306a36Sopenharmony_ci .exec_op = aspeed_spi_exec_op, 64362306a36Sopenharmony_ci .get_name = aspeed_spi_get_name, 64462306a36Sopenharmony_ci .dirmap_create = aspeed_spi_dirmap_create, 64562306a36Sopenharmony_ci .dirmap_read = aspeed_spi_dirmap_read, 64662306a36Sopenharmony_ci}; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic void aspeed_spi_chip_set_type(struct aspeed_spi *aspi, unsigned int cs, int type) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci u32 reg; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci reg = readl(aspi->regs + CONFIG_REG); 65362306a36Sopenharmony_ci reg &= ~(0x3 << (cs * 2)); 65462306a36Sopenharmony_ci reg |= type << (cs * 2); 65562306a36Sopenharmony_ci writel(reg, aspi->regs + CONFIG_REG); 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic void aspeed_spi_chip_enable(struct aspeed_spi *aspi, unsigned int cs, bool enable) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci u32 we_bit = BIT(aspi->data->we0 + cs); 66162306a36Sopenharmony_ci u32 reg = readl(aspi->regs + CONFIG_REG); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (enable) 66462306a36Sopenharmony_ci reg |= we_bit; 66562306a36Sopenharmony_ci else 66662306a36Sopenharmony_ci reg &= ~we_bit; 66762306a36Sopenharmony_ci writel(reg, aspi->regs + CONFIG_REG); 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic int aspeed_spi_setup(struct spi_device *spi) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci struct aspeed_spi *aspi = spi_controller_get_devdata(spi->controller); 67362306a36Sopenharmony_ci const struct aspeed_spi_data *data = aspi->data; 67462306a36Sopenharmony_ci unsigned int cs = spi_get_chipselect(spi, 0); 67562306a36Sopenharmony_ci struct aspeed_spi_chip *chip = &aspi->chips[cs]; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci chip->aspi = aspi; 67862306a36Sopenharmony_ci chip->cs = cs; 67962306a36Sopenharmony_ci chip->ctl = aspi->regs + data->ctl0 + cs * 4; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci /* The driver only supports SPI type flash */ 68262306a36Sopenharmony_ci if (data->hastype) 68362306a36Sopenharmony_ci aspeed_spi_chip_set_type(aspi, cs, CONFIG_TYPE_SPI); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (aspeed_spi_chip_set_default_window(chip) < 0) { 68662306a36Sopenharmony_ci dev_warn(aspi->dev, "CE%d window invalid", cs); 68762306a36Sopenharmony_ci return -EINVAL; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci aspeed_spi_chip_enable(aspi, cs, true); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci chip->ctl_val[ASPEED_SPI_BASE] = CTRL_CE_STOP_ACTIVE | CTRL_IO_MODE_USER; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci dev_dbg(aspi->dev, "CE%d setup done\n", cs); 69562306a36Sopenharmony_ci return 0; 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic void aspeed_spi_cleanup(struct spi_device *spi) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci struct aspeed_spi *aspi = spi_controller_get_devdata(spi->controller); 70162306a36Sopenharmony_ci unsigned int cs = spi_get_chipselect(spi, 0); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci aspeed_spi_chip_enable(aspi, cs, false); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci dev_dbg(aspi->dev, "CE%d cleanup done\n", cs); 70662306a36Sopenharmony_ci} 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cistatic void aspeed_spi_enable(struct aspeed_spi *aspi, bool enable) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci int cs; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci for (cs = 0; cs < aspi->data->max_cs; cs++) 71362306a36Sopenharmony_ci aspeed_spi_chip_enable(aspi, cs, enable); 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic int aspeed_spi_probe(struct platform_device *pdev) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 71962306a36Sopenharmony_ci const struct aspeed_spi_data *data; 72062306a36Sopenharmony_ci struct spi_controller *ctlr; 72162306a36Sopenharmony_ci struct aspeed_spi *aspi; 72262306a36Sopenharmony_ci struct resource *res; 72362306a36Sopenharmony_ci int ret; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci data = of_device_get_match_data(&pdev->dev); 72662306a36Sopenharmony_ci if (!data) 72762306a36Sopenharmony_ci return -ENODEV; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci ctlr = devm_spi_alloc_host(dev, sizeof(*aspi)); 73062306a36Sopenharmony_ci if (!ctlr) 73162306a36Sopenharmony_ci return -ENOMEM; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci aspi = spi_controller_get_devdata(ctlr); 73462306a36Sopenharmony_ci platform_set_drvdata(pdev, aspi); 73562306a36Sopenharmony_ci aspi->data = data; 73662306a36Sopenharmony_ci aspi->dev = dev; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci aspi->regs = devm_platform_ioremap_resource(pdev, 0); 73962306a36Sopenharmony_ci if (IS_ERR(aspi->regs)) 74062306a36Sopenharmony_ci return PTR_ERR(aspi->regs); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci aspi->ahb_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res); 74362306a36Sopenharmony_ci if (IS_ERR(aspi->ahb_base)) { 74462306a36Sopenharmony_ci dev_err(dev, "missing AHB mapping window\n"); 74562306a36Sopenharmony_ci return PTR_ERR(aspi->ahb_base); 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci aspi->ahb_window_size = resource_size(res); 74962306a36Sopenharmony_ci aspi->ahb_base_phy = res->start; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci aspi->clk = devm_clk_get(&pdev->dev, NULL); 75262306a36Sopenharmony_ci if (IS_ERR(aspi->clk)) { 75362306a36Sopenharmony_ci dev_err(dev, "missing clock\n"); 75462306a36Sopenharmony_ci return PTR_ERR(aspi->clk); 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci aspi->clk_freq = clk_get_rate(aspi->clk); 75862306a36Sopenharmony_ci if (!aspi->clk_freq) { 75962306a36Sopenharmony_ci dev_err(dev, "invalid clock\n"); 76062306a36Sopenharmony_ci return -EINVAL; 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci ret = clk_prepare_enable(aspi->clk); 76462306a36Sopenharmony_ci if (ret) { 76562306a36Sopenharmony_ci dev_err(dev, "can not enable the clock\n"); 76662306a36Sopenharmony_ci return ret; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* IRQ is for DMA, which the driver doesn't support yet */ 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci ctlr->mode_bits = SPI_RX_DUAL | SPI_TX_DUAL | data->mode_bits; 77262306a36Sopenharmony_ci ctlr->bus_num = pdev->id; 77362306a36Sopenharmony_ci ctlr->mem_ops = &aspeed_spi_mem_ops; 77462306a36Sopenharmony_ci ctlr->setup = aspeed_spi_setup; 77562306a36Sopenharmony_ci ctlr->cleanup = aspeed_spi_cleanup; 77662306a36Sopenharmony_ci ctlr->num_chipselect = data->max_cs; 77762306a36Sopenharmony_ci ctlr->dev.of_node = dev->of_node; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci ret = devm_spi_register_controller(dev, ctlr); 78062306a36Sopenharmony_ci if (ret) { 78162306a36Sopenharmony_ci dev_err(&pdev->dev, "spi_register_controller failed\n"); 78262306a36Sopenharmony_ci goto disable_clk; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci return 0; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cidisable_clk: 78762306a36Sopenharmony_ci clk_disable_unprepare(aspi->clk); 78862306a36Sopenharmony_ci return ret; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cistatic void aspeed_spi_remove(struct platform_device *pdev) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci struct aspeed_spi *aspi = platform_get_drvdata(pdev); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci aspeed_spi_enable(aspi, false); 79662306a36Sopenharmony_ci clk_disable_unprepare(aspi->clk); 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci/* 80062306a36Sopenharmony_ci * AHB mappings 80162306a36Sopenharmony_ci */ 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci/* 80462306a36Sopenharmony_ci * The Segment Registers of the AST2400 and AST2500 use a 8MB unit. 80562306a36Sopenharmony_ci * The address range is encoded with absolute addresses in the overall 80662306a36Sopenharmony_ci * mapping window. 80762306a36Sopenharmony_ci */ 80862306a36Sopenharmony_cistatic u32 aspeed_spi_segment_start(struct aspeed_spi *aspi, u32 reg) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci return ((reg >> 16) & 0xFF) << 23; 81162306a36Sopenharmony_ci} 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_cistatic u32 aspeed_spi_segment_end(struct aspeed_spi *aspi, u32 reg) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci return ((reg >> 24) & 0xFF) << 23; 81662306a36Sopenharmony_ci} 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_cistatic u32 aspeed_spi_segment_reg(struct aspeed_spi *aspi, u32 start, u32 end) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci return (((start >> 23) & 0xFF) << 16) | (((end >> 23) & 0xFF) << 24); 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci/* 82462306a36Sopenharmony_ci * The Segment Registers of the AST2600 use a 1MB unit. The address 82562306a36Sopenharmony_ci * range is encoded with offsets in the overall mapping window. 82662306a36Sopenharmony_ci */ 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci#define AST2600_SEG_ADDR_MASK 0x0ff00000 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_cistatic u32 aspeed_spi_segment_ast2600_start(struct aspeed_spi *aspi, 83162306a36Sopenharmony_ci u32 reg) 83262306a36Sopenharmony_ci{ 83362306a36Sopenharmony_ci u32 start_offset = (reg << 16) & AST2600_SEG_ADDR_MASK; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci return aspi->ahb_base_phy + start_offset; 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_cistatic u32 aspeed_spi_segment_ast2600_end(struct aspeed_spi *aspi, 83962306a36Sopenharmony_ci u32 reg) 84062306a36Sopenharmony_ci{ 84162306a36Sopenharmony_ci u32 end_offset = reg & AST2600_SEG_ADDR_MASK; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci /* segment is disabled */ 84462306a36Sopenharmony_ci if (!end_offset) 84562306a36Sopenharmony_ci return aspi->ahb_base_phy; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci return aspi->ahb_base_phy + end_offset + 0x100000; 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic u32 aspeed_spi_segment_ast2600_reg(struct aspeed_spi *aspi, 85162306a36Sopenharmony_ci u32 start, u32 end) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci /* disable zero size segments */ 85462306a36Sopenharmony_ci if (start == end) 85562306a36Sopenharmony_ci return 0; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci return ((start & AST2600_SEG_ADDR_MASK) >> 16) | 85862306a36Sopenharmony_ci ((end - 1) & AST2600_SEG_ADDR_MASK); 85962306a36Sopenharmony_ci} 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci/* 86262306a36Sopenharmony_ci * Read timing compensation sequences 86362306a36Sopenharmony_ci */ 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci#define CALIBRATE_BUF_SIZE SZ_16K 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_cistatic bool aspeed_spi_check_reads(struct aspeed_spi_chip *chip, 86862306a36Sopenharmony_ci const u8 *golden_buf, u8 *test_buf) 86962306a36Sopenharmony_ci{ 87062306a36Sopenharmony_ci int i; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci for (i = 0; i < 10; i++) { 87362306a36Sopenharmony_ci memcpy_fromio(test_buf, chip->ahb_base, CALIBRATE_BUF_SIZE); 87462306a36Sopenharmony_ci if (memcmp(test_buf, golden_buf, CALIBRATE_BUF_SIZE) != 0) { 87562306a36Sopenharmony_ci#if defined(VERBOSE_DEBUG) 87662306a36Sopenharmony_ci print_hex_dump_bytes(DEVICE_NAME " fail: ", DUMP_PREFIX_NONE, 87762306a36Sopenharmony_ci test_buf, 0x100); 87862306a36Sopenharmony_ci#endif 87962306a36Sopenharmony_ci return false; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci return true; 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci#define FREAD_TPASS(i) (((i) / 2) | (((i) & 1) ? 0 : 8)) 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci/* 88862306a36Sopenharmony_ci * The timing register is shared by all devices. Only update for CE0. 88962306a36Sopenharmony_ci */ 89062306a36Sopenharmony_cistatic int aspeed_spi_calibrate(struct aspeed_spi_chip *chip, u32 hdiv, 89162306a36Sopenharmony_ci const u8 *golden_buf, u8 *test_buf) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci struct aspeed_spi *aspi = chip->aspi; 89462306a36Sopenharmony_ci const struct aspeed_spi_data *data = aspi->data; 89562306a36Sopenharmony_ci int i; 89662306a36Sopenharmony_ci int good_pass = -1, pass_count = 0; 89762306a36Sopenharmony_ci u32 shift = (hdiv - 1) << 2; 89862306a36Sopenharmony_ci u32 mask = ~(0xfu << shift); 89962306a36Sopenharmony_ci u32 fread_timing_val = 0; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci /* Try HCLK delay 0..5, each one with/without delay and look for a 90262306a36Sopenharmony_ci * good pair. 90362306a36Sopenharmony_ci */ 90462306a36Sopenharmony_ci for (i = 0; i < 12; i++) { 90562306a36Sopenharmony_ci bool pass; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci if (chip->cs == 0) { 90862306a36Sopenharmony_ci fread_timing_val &= mask; 90962306a36Sopenharmony_ci fread_timing_val |= FREAD_TPASS(i) << shift; 91062306a36Sopenharmony_ci writel(fread_timing_val, aspi->regs + data->timing); 91162306a36Sopenharmony_ci } 91262306a36Sopenharmony_ci pass = aspeed_spi_check_reads(chip, golden_buf, test_buf); 91362306a36Sopenharmony_ci dev_dbg(aspi->dev, 91462306a36Sopenharmony_ci " * [%08x] %d HCLK delay, %dns DI delay : %s", 91562306a36Sopenharmony_ci fread_timing_val, i / 2, (i & 1) ? 0 : 4, 91662306a36Sopenharmony_ci pass ? "PASS" : "FAIL"); 91762306a36Sopenharmony_ci if (pass) { 91862306a36Sopenharmony_ci pass_count++; 91962306a36Sopenharmony_ci if (pass_count == 3) { 92062306a36Sopenharmony_ci good_pass = i - 1; 92162306a36Sopenharmony_ci break; 92262306a36Sopenharmony_ci } 92362306a36Sopenharmony_ci } else { 92462306a36Sopenharmony_ci pass_count = 0; 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci /* No good setting for this frequency */ 92962306a36Sopenharmony_ci if (good_pass < 0) 93062306a36Sopenharmony_ci return -1; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci /* We have at least one pass of margin, let's use first pass */ 93362306a36Sopenharmony_ci if (chip->cs == 0) { 93462306a36Sopenharmony_ci fread_timing_val &= mask; 93562306a36Sopenharmony_ci fread_timing_val |= FREAD_TPASS(good_pass) << shift; 93662306a36Sopenharmony_ci writel(fread_timing_val, aspi->regs + data->timing); 93762306a36Sopenharmony_ci } 93862306a36Sopenharmony_ci dev_dbg(aspi->dev, " * -> good is pass %d [0x%08x]", 93962306a36Sopenharmony_ci good_pass, fread_timing_val); 94062306a36Sopenharmony_ci return 0; 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic bool aspeed_spi_check_calib_data(const u8 *test_buf, u32 size) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci const u32 *tb32 = (const u32 *)test_buf; 94662306a36Sopenharmony_ci u32 i, cnt = 0; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci /* We check if we have enough words that are neither all 0 94962306a36Sopenharmony_ci * nor all 1's so the calibration can be considered valid. 95062306a36Sopenharmony_ci * 95162306a36Sopenharmony_ci * I use an arbitrary threshold for now of 64 95262306a36Sopenharmony_ci */ 95362306a36Sopenharmony_ci size >>= 2; 95462306a36Sopenharmony_ci for (i = 0; i < size; i++) { 95562306a36Sopenharmony_ci if (tb32[i] != 0 && tb32[i] != 0xffffffff) 95662306a36Sopenharmony_ci cnt++; 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci return cnt >= 64; 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_cistatic const u32 aspeed_spi_hclk_divs[] = { 96262306a36Sopenharmony_ci 0xf, /* HCLK */ 96362306a36Sopenharmony_ci 0x7, /* HCLK/2 */ 96462306a36Sopenharmony_ci 0xe, /* HCLK/3 */ 96562306a36Sopenharmony_ci 0x6, /* HCLK/4 */ 96662306a36Sopenharmony_ci 0xd, /* HCLK/5 */ 96762306a36Sopenharmony_ci}; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci#define ASPEED_SPI_HCLK_DIV(i) \ 97062306a36Sopenharmony_ci (aspeed_spi_hclk_divs[(i) - 1] << CTRL_FREQ_SEL_SHIFT) 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_cistatic int aspeed_spi_do_calibration(struct aspeed_spi_chip *chip) 97362306a36Sopenharmony_ci{ 97462306a36Sopenharmony_ci struct aspeed_spi *aspi = chip->aspi; 97562306a36Sopenharmony_ci const struct aspeed_spi_data *data = aspi->data; 97662306a36Sopenharmony_ci u32 ahb_freq = aspi->clk_freq; 97762306a36Sopenharmony_ci u32 max_freq = chip->clk_freq; 97862306a36Sopenharmony_ci u32 ctl_val; 97962306a36Sopenharmony_ci u8 *golden_buf = NULL; 98062306a36Sopenharmony_ci u8 *test_buf = NULL; 98162306a36Sopenharmony_ci int i, rc, best_div = -1; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci dev_dbg(aspi->dev, "calculate timing compensation - AHB freq: %d MHz", 98462306a36Sopenharmony_ci ahb_freq / 1000000); 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci /* 98762306a36Sopenharmony_ci * use the related low frequency to get check calibration data 98862306a36Sopenharmony_ci * and get golden data. 98962306a36Sopenharmony_ci */ 99062306a36Sopenharmony_ci ctl_val = chip->ctl_val[ASPEED_SPI_READ] & data->hclk_mask; 99162306a36Sopenharmony_ci writel(ctl_val, chip->ctl); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci test_buf = kzalloc(CALIBRATE_BUF_SIZE * 2, GFP_KERNEL); 99462306a36Sopenharmony_ci if (!test_buf) 99562306a36Sopenharmony_ci return -ENOMEM; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci golden_buf = test_buf + CALIBRATE_BUF_SIZE; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci memcpy_fromio(golden_buf, chip->ahb_base, CALIBRATE_BUF_SIZE); 100062306a36Sopenharmony_ci if (!aspeed_spi_check_calib_data(golden_buf, CALIBRATE_BUF_SIZE)) { 100162306a36Sopenharmony_ci dev_info(aspi->dev, "Calibration area too uniform, using low speed"); 100262306a36Sopenharmony_ci goto no_calib; 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci#if defined(VERBOSE_DEBUG) 100662306a36Sopenharmony_ci print_hex_dump_bytes(DEVICE_NAME " good: ", DUMP_PREFIX_NONE, 100762306a36Sopenharmony_ci golden_buf, 0x100); 100862306a36Sopenharmony_ci#endif 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci /* Now we iterate the HCLK dividers until we find our breaking point */ 101162306a36Sopenharmony_ci for (i = ARRAY_SIZE(aspeed_spi_hclk_divs); i > data->hdiv_max - 1; i--) { 101262306a36Sopenharmony_ci u32 tv, freq; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci freq = ahb_freq / i; 101562306a36Sopenharmony_ci if (freq > max_freq) 101662306a36Sopenharmony_ci continue; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci /* Set the timing */ 101962306a36Sopenharmony_ci tv = chip->ctl_val[ASPEED_SPI_READ] | ASPEED_SPI_HCLK_DIV(i); 102062306a36Sopenharmony_ci writel(tv, chip->ctl); 102162306a36Sopenharmony_ci dev_dbg(aspi->dev, "Trying HCLK/%d [%08x] ...", i, tv); 102262306a36Sopenharmony_ci rc = data->calibrate(chip, i, golden_buf, test_buf); 102362306a36Sopenharmony_ci if (rc == 0) 102462306a36Sopenharmony_ci best_div = i; 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci /* Nothing found ? */ 102862306a36Sopenharmony_ci if (best_div < 0) { 102962306a36Sopenharmony_ci dev_warn(aspi->dev, "No good frequency, using dumb slow"); 103062306a36Sopenharmony_ci } else { 103162306a36Sopenharmony_ci dev_dbg(aspi->dev, "Found good read timings at HCLK/%d", best_div); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci /* Record the freq */ 103462306a36Sopenharmony_ci for (i = 0; i < ASPEED_SPI_MAX; i++) 103562306a36Sopenharmony_ci chip->ctl_val[i] = (chip->ctl_val[i] & data->hclk_mask) | 103662306a36Sopenharmony_ci ASPEED_SPI_HCLK_DIV(best_div); 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_cino_calib: 104062306a36Sopenharmony_ci writel(chip->ctl_val[ASPEED_SPI_READ], chip->ctl); 104162306a36Sopenharmony_ci kfree(test_buf); 104262306a36Sopenharmony_ci return 0; 104362306a36Sopenharmony_ci} 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci#define TIMING_DELAY_DI BIT(3) 104662306a36Sopenharmony_ci#define TIMING_DELAY_HCYCLE_MAX 5 104762306a36Sopenharmony_ci#define TIMING_REG_AST2600(chip) \ 104862306a36Sopenharmony_ci ((chip)->aspi->regs + (chip)->aspi->data->timing + \ 104962306a36Sopenharmony_ci (chip)->cs * 4) 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_cistatic int aspeed_spi_ast2600_calibrate(struct aspeed_spi_chip *chip, u32 hdiv, 105262306a36Sopenharmony_ci const u8 *golden_buf, u8 *test_buf) 105362306a36Sopenharmony_ci{ 105462306a36Sopenharmony_ci struct aspeed_spi *aspi = chip->aspi; 105562306a36Sopenharmony_ci int hcycle; 105662306a36Sopenharmony_ci u32 shift = (hdiv - 2) << 3; 105762306a36Sopenharmony_ci u32 mask = ~(0xfu << shift); 105862306a36Sopenharmony_ci u32 fread_timing_val = 0; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci for (hcycle = 0; hcycle <= TIMING_DELAY_HCYCLE_MAX; hcycle++) { 106162306a36Sopenharmony_ci int delay_ns; 106262306a36Sopenharmony_ci bool pass = false; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci fread_timing_val &= mask; 106562306a36Sopenharmony_ci fread_timing_val |= hcycle << shift; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci /* no DI input delay first */ 106862306a36Sopenharmony_ci writel(fread_timing_val, TIMING_REG_AST2600(chip)); 106962306a36Sopenharmony_ci pass = aspeed_spi_check_reads(chip, golden_buf, test_buf); 107062306a36Sopenharmony_ci dev_dbg(aspi->dev, 107162306a36Sopenharmony_ci " * [%08x] %d HCLK delay, DI delay none : %s", 107262306a36Sopenharmony_ci fread_timing_val, hcycle, pass ? "PASS" : "FAIL"); 107362306a36Sopenharmony_ci if (pass) 107462306a36Sopenharmony_ci return 0; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci /* Add DI input delays */ 107762306a36Sopenharmony_ci fread_timing_val &= mask; 107862306a36Sopenharmony_ci fread_timing_val |= (TIMING_DELAY_DI | hcycle) << shift; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci for (delay_ns = 0; delay_ns < 0x10; delay_ns++) { 108162306a36Sopenharmony_ci fread_timing_val &= ~(0xf << (4 + shift)); 108262306a36Sopenharmony_ci fread_timing_val |= delay_ns << (4 + shift); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci writel(fread_timing_val, TIMING_REG_AST2600(chip)); 108562306a36Sopenharmony_ci pass = aspeed_spi_check_reads(chip, golden_buf, test_buf); 108662306a36Sopenharmony_ci dev_dbg(aspi->dev, 108762306a36Sopenharmony_ci " * [%08x] %d HCLK delay, DI delay %d.%dns : %s", 108862306a36Sopenharmony_ci fread_timing_val, hcycle, (delay_ns + 1) / 2, 108962306a36Sopenharmony_ci (delay_ns + 1) & 1 ? 5 : 5, pass ? "PASS" : "FAIL"); 109062306a36Sopenharmony_ci /* 109162306a36Sopenharmony_ci * TODO: This is optimistic. We should look 109262306a36Sopenharmony_ci * for a working interval and save the middle 109362306a36Sopenharmony_ci * value in the read timing register. 109462306a36Sopenharmony_ci */ 109562306a36Sopenharmony_ci if (pass) 109662306a36Sopenharmony_ci return 0; 109762306a36Sopenharmony_ci } 109862306a36Sopenharmony_ci } 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci /* No good setting for this frequency */ 110162306a36Sopenharmony_ci return -1; 110262306a36Sopenharmony_ci} 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci/* 110562306a36Sopenharmony_ci * Platform definitions 110662306a36Sopenharmony_ci */ 110762306a36Sopenharmony_cistatic const struct aspeed_spi_data ast2400_fmc_data = { 110862306a36Sopenharmony_ci .max_cs = 5, 110962306a36Sopenharmony_ci .hastype = true, 111062306a36Sopenharmony_ci .we0 = 16, 111162306a36Sopenharmony_ci .ctl0 = CE0_CTRL_REG, 111262306a36Sopenharmony_ci .timing = CE0_TIMING_COMPENSATION_REG, 111362306a36Sopenharmony_ci .hclk_mask = 0xfffff0ff, 111462306a36Sopenharmony_ci .hdiv_max = 1, 111562306a36Sopenharmony_ci .calibrate = aspeed_spi_calibrate, 111662306a36Sopenharmony_ci .segment_start = aspeed_spi_segment_start, 111762306a36Sopenharmony_ci .segment_end = aspeed_spi_segment_end, 111862306a36Sopenharmony_ci .segment_reg = aspeed_spi_segment_reg, 111962306a36Sopenharmony_ci}; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_cistatic const struct aspeed_spi_data ast2400_spi_data = { 112262306a36Sopenharmony_ci .max_cs = 1, 112362306a36Sopenharmony_ci .hastype = false, 112462306a36Sopenharmony_ci .we0 = 0, 112562306a36Sopenharmony_ci .ctl0 = 0x04, 112662306a36Sopenharmony_ci .timing = 0x14, 112762306a36Sopenharmony_ci .hclk_mask = 0xfffff0ff, 112862306a36Sopenharmony_ci .hdiv_max = 1, 112962306a36Sopenharmony_ci .calibrate = aspeed_spi_calibrate, 113062306a36Sopenharmony_ci /* No segment registers */ 113162306a36Sopenharmony_ci}; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_cistatic const struct aspeed_spi_data ast2500_fmc_data = { 113462306a36Sopenharmony_ci .max_cs = 3, 113562306a36Sopenharmony_ci .hastype = true, 113662306a36Sopenharmony_ci .we0 = 16, 113762306a36Sopenharmony_ci .ctl0 = CE0_CTRL_REG, 113862306a36Sopenharmony_ci .timing = CE0_TIMING_COMPENSATION_REG, 113962306a36Sopenharmony_ci .hclk_mask = 0xffffd0ff, 114062306a36Sopenharmony_ci .hdiv_max = 1, 114162306a36Sopenharmony_ci .calibrate = aspeed_spi_calibrate, 114262306a36Sopenharmony_ci .segment_start = aspeed_spi_segment_start, 114362306a36Sopenharmony_ci .segment_end = aspeed_spi_segment_end, 114462306a36Sopenharmony_ci .segment_reg = aspeed_spi_segment_reg, 114562306a36Sopenharmony_ci}; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_cistatic const struct aspeed_spi_data ast2500_spi_data = { 114862306a36Sopenharmony_ci .max_cs = 2, 114962306a36Sopenharmony_ci .hastype = false, 115062306a36Sopenharmony_ci .we0 = 16, 115162306a36Sopenharmony_ci .ctl0 = CE0_CTRL_REG, 115262306a36Sopenharmony_ci .timing = CE0_TIMING_COMPENSATION_REG, 115362306a36Sopenharmony_ci .hclk_mask = 0xffffd0ff, 115462306a36Sopenharmony_ci .hdiv_max = 1, 115562306a36Sopenharmony_ci .calibrate = aspeed_spi_calibrate, 115662306a36Sopenharmony_ci .segment_start = aspeed_spi_segment_start, 115762306a36Sopenharmony_ci .segment_end = aspeed_spi_segment_end, 115862306a36Sopenharmony_ci .segment_reg = aspeed_spi_segment_reg, 115962306a36Sopenharmony_ci}; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_cistatic const struct aspeed_spi_data ast2600_fmc_data = { 116262306a36Sopenharmony_ci .max_cs = 3, 116362306a36Sopenharmony_ci .hastype = false, 116462306a36Sopenharmony_ci .mode_bits = SPI_RX_QUAD | SPI_TX_QUAD, 116562306a36Sopenharmony_ci .we0 = 16, 116662306a36Sopenharmony_ci .ctl0 = CE0_CTRL_REG, 116762306a36Sopenharmony_ci .timing = CE0_TIMING_COMPENSATION_REG, 116862306a36Sopenharmony_ci .hclk_mask = 0xf0fff0ff, 116962306a36Sopenharmony_ci .hdiv_max = 2, 117062306a36Sopenharmony_ci .calibrate = aspeed_spi_ast2600_calibrate, 117162306a36Sopenharmony_ci .segment_start = aspeed_spi_segment_ast2600_start, 117262306a36Sopenharmony_ci .segment_end = aspeed_spi_segment_ast2600_end, 117362306a36Sopenharmony_ci .segment_reg = aspeed_spi_segment_ast2600_reg, 117462306a36Sopenharmony_ci}; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_cistatic const struct aspeed_spi_data ast2600_spi_data = { 117762306a36Sopenharmony_ci .max_cs = 2, 117862306a36Sopenharmony_ci .hastype = false, 117962306a36Sopenharmony_ci .mode_bits = SPI_RX_QUAD | SPI_TX_QUAD, 118062306a36Sopenharmony_ci .we0 = 16, 118162306a36Sopenharmony_ci .ctl0 = CE0_CTRL_REG, 118262306a36Sopenharmony_ci .timing = CE0_TIMING_COMPENSATION_REG, 118362306a36Sopenharmony_ci .hclk_mask = 0xf0fff0ff, 118462306a36Sopenharmony_ci .hdiv_max = 2, 118562306a36Sopenharmony_ci .calibrate = aspeed_spi_ast2600_calibrate, 118662306a36Sopenharmony_ci .segment_start = aspeed_spi_segment_ast2600_start, 118762306a36Sopenharmony_ci .segment_end = aspeed_spi_segment_ast2600_end, 118862306a36Sopenharmony_ci .segment_reg = aspeed_spi_segment_ast2600_reg, 118962306a36Sopenharmony_ci}; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_cistatic const struct of_device_id aspeed_spi_matches[] = { 119262306a36Sopenharmony_ci { .compatible = "aspeed,ast2400-fmc", .data = &ast2400_fmc_data }, 119362306a36Sopenharmony_ci { .compatible = "aspeed,ast2400-spi", .data = &ast2400_spi_data }, 119462306a36Sopenharmony_ci { .compatible = "aspeed,ast2500-fmc", .data = &ast2500_fmc_data }, 119562306a36Sopenharmony_ci { .compatible = "aspeed,ast2500-spi", .data = &ast2500_spi_data }, 119662306a36Sopenharmony_ci { .compatible = "aspeed,ast2600-fmc", .data = &ast2600_fmc_data }, 119762306a36Sopenharmony_ci { .compatible = "aspeed,ast2600-spi", .data = &ast2600_spi_data }, 119862306a36Sopenharmony_ci { } 119962306a36Sopenharmony_ci}; 120062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, aspeed_spi_matches); 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_cistatic struct platform_driver aspeed_spi_driver = { 120362306a36Sopenharmony_ci .probe = aspeed_spi_probe, 120462306a36Sopenharmony_ci .remove_new = aspeed_spi_remove, 120562306a36Sopenharmony_ci .driver = { 120662306a36Sopenharmony_ci .name = DEVICE_NAME, 120762306a36Sopenharmony_ci .of_match_table = aspeed_spi_matches, 120862306a36Sopenharmony_ci } 120962306a36Sopenharmony_ci}; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_cimodule_platform_driver(aspeed_spi_driver); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ciMODULE_DESCRIPTION("ASPEED Static Memory Controller Driver"); 121462306a36Sopenharmony_ciMODULE_AUTHOR("Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>"); 121562306a36Sopenharmony_ciMODULE_AUTHOR("Cedric Le Goater <clg@kaod.org>"); 121662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1217