1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (C) 2019 MediaTek Inc. All Rights Reserved. 4 * 5 * Author: Weijie Gao <weijie.gao@mediatek.com> 6 */ 7 8#include <common.h> 9#include <clk.h> 10#include <dm.h> 11#include <errno.h> 12#include <spi.h> 13#include <spi-mem.h> 14#include <stdbool.h> 15#include <watchdog.h> 16#include <dm/pinctrl.h> 17#include <linux/bitops.h> 18#include <linux/io.h> 19#include <linux/iopoll.h> 20 21#define SNFI_MAC_CTL 0x500 22#define MAC_XIO_SEL BIT(4) 23#define SF_MAC_EN BIT(3) 24#define SF_TRIG BIT(2) 25#define WIP_READY BIT(1) 26#define WIP BIT(0) 27 28#define SNFI_MAC_OUTL 0x504 29#define SNFI_MAC_INL 0x508 30 31#define SNFI_MISC_CTL 0x538 32#define SW_RST BIT(28) 33#define FIFO_RD_LTC_SHIFT 25 34#define FIFO_RD_LTC GENMASK(26, 25) 35#define LATCH_LAT_SHIFT 8 36#define LATCH_LAT GENMASK(9, 8) 37#define CS_DESELECT_CYC_SHIFT 0 38#define CS_DESELECT_CYC GENMASK(4, 0) 39 40#define SNF_STA_CTL1 0x550 41#define SPI_STATE GENMASK(3, 0) 42 43#define SNFI_GPRAM_OFFSET 0x800 44#define SNFI_GPRAM_SIZE 0x80 45 46#define SNFI_POLL_INTERVAL 500000 47#define SNFI_RST_POLL_INTERVAL 1000000 48 49struct mtk_snfi_priv { 50 void __iomem *base; 51 52 struct clk nfi_clk; 53 struct clk pad_clk; 54}; 55 56static int mtk_snfi_adjust_op_size(struct spi_slave *slave, 57 struct spi_mem_op *op) 58{ 59 u32 nbytes; 60 61 /* 62 * When there is input data, it will be appended after the output 63 * data in the GPRAM. So the total size of either pure output data 64 * or the output+input data must not exceed the GPRAM size. 65 */ 66 67 nbytes = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes; 68 69 if (nbytes + op->data.nbytes <= SNFI_GPRAM_SIZE) 70 return 0; 71 72 if (nbytes >= SNFI_GPRAM_SIZE) 73 return -ENOTSUPP; 74 75 op->data.nbytes = SNFI_GPRAM_SIZE - nbytes; 76 77 return 0; 78} 79 80static bool mtk_snfi_supports_op(struct spi_slave *slave, 81 const struct spi_mem_op *op) 82{ 83 if (op->cmd.buswidth > 1 || op->addr.buswidth > 1 || 84 op->dummy.buswidth > 1 || op->data.buswidth > 1) 85 return false; 86 87 return true; 88} 89 90static int mtk_snfi_mac_trigger(struct mtk_snfi_priv *priv, 91 struct udevice *bus, u32 outlen, u32 inlen) 92{ 93 int ret; 94 u32 val; 95 96#ifdef CONFIG_PINCTRL 97 pinctrl_select_state(bus, "snfi"); 98#endif 99 100 writel(SF_MAC_EN, priv->base + SNFI_MAC_CTL); 101 writel(outlen, priv->base + SNFI_MAC_OUTL); 102 writel(inlen, priv->base + SNFI_MAC_INL); 103 104 writel(SF_MAC_EN | SF_TRIG, priv->base + SNFI_MAC_CTL); 105 106 ret = readl_poll_timeout(priv->base + SNFI_MAC_CTL, val, 107 val & WIP_READY, SNFI_POLL_INTERVAL); 108 if (ret) { 109 printf("%s: timed out waiting for WIP_READY\n", __func__); 110 goto cleanup; 111 } 112 113 ret = readl_poll_timeout(priv->base + SNFI_MAC_CTL, val, 114 !(val & WIP), SNFI_POLL_INTERVAL); 115 if (ret) 116 printf("%s: timed out waiting for WIP cleared\n", __func__); 117 118 writel(0, priv->base + SNFI_MAC_CTL); 119 120cleanup: 121#ifdef CONFIG_PINCTRL 122 pinctrl_select_state(bus, "default"); 123#endif 124 125 return ret; 126} 127 128static int mtk_snfi_mac_reset(struct mtk_snfi_priv *priv) 129{ 130 int ret; 131 u32 val; 132 133 setbits_32(priv->base + SNFI_MISC_CTL, SW_RST); 134 135 ret = readl_poll_timeout(priv->base + SNF_STA_CTL1, val, 136 !(val & SPI_STATE), SNFI_POLL_INTERVAL); 137 if (ret) 138 printf("%s: failed to reset snfi mac\n", __func__); 139 140 writel((2 << FIFO_RD_LTC_SHIFT) | 141 (10 << CS_DESELECT_CYC_SHIFT), 142 priv->base + SNFI_MISC_CTL); 143 144 return ret; 145} 146 147static void mtk_snfi_copy_to_gpram(struct mtk_snfi_priv *priv, 148 const void *data, size_t len) 149{ 150 void __iomem *gpram = priv->base + SNFI_GPRAM_OFFSET; 151 size_t i, n = (len + sizeof(u32) - 1) / sizeof(u32); 152 const u32 *buff = data; 153 154 /* 155 * The output data will always be copied to the beginning of 156 * the GPRAM. Uses word write for better performance. 157 * 158 * Trailing bytes in the last word are not cared. 159 */ 160 161 for (i = 0; i < n; i++) 162 writel(buff[i], gpram + i * sizeof(u32)); 163} 164 165static void mtk_snfi_copy_from_gpram(struct mtk_snfi_priv *priv, u8 *cache, 166 void *data, size_t pos, size_t len) 167{ 168 void __iomem *gpram = priv->base + SNFI_GPRAM_OFFSET; 169 u32 *buff = (u32 *)cache; 170 size_t i, off, end; 171 172 /* Start position in the buffer */ 173 off = pos & (sizeof(u32) - 1); 174 175 /* End position for copy */ 176 end = (len + pos + sizeof(u32) - 1) & (~(sizeof(u32) - 1)); 177 178 /* Start position for copy */ 179 pos &= ~(sizeof(u32) - 1); 180 181 /* 182 * Read aligned data from GPRAM to buffer first. 183 * Uses word read for better performance. 184 */ 185 i = 0; 186 while (pos < end) { 187 buff[i++] = readl(gpram + pos); 188 pos += sizeof(u32); 189 } 190 191 /* Copy rx data */ 192 memcpy(data, cache + off, len); 193} 194 195static int mtk_snfi_exec_op(struct spi_slave *slave, 196 const struct spi_mem_op *op) 197{ 198 struct udevice *bus = dev_get_parent(slave->dev); 199 struct mtk_snfi_priv *priv = dev_get_priv(bus); 200 u8 gpram_cache[SNFI_GPRAM_SIZE]; 201 u32 i, len = 0, inlen = 0; 202 int addr_sh; 203 int ret; 204 205 schedule(); 206 207 ret = mtk_snfi_mac_reset(priv); 208 if (ret) 209 return ret; 210 211 /* Put opcode */ 212 gpram_cache[len++] = op->cmd.opcode; 213 214 /* Put address */ 215 addr_sh = (op->addr.nbytes - 1) * 8; 216 while (addr_sh >= 0) { 217 gpram_cache[len++] = (op->addr.val >> addr_sh) & 0xff; 218 addr_sh -= 8; 219 } 220 221 /* Put dummy bytes */ 222 for (i = 0; i < op->dummy.nbytes; i++) 223 gpram_cache[len++] = 0; 224 225 /* Put output data */ 226 if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT) { 227 memcpy(gpram_cache + len, op->data.buf.out, op->data.nbytes); 228 len += op->data.nbytes; 229 } 230 231 /* Copy final output data to GPRAM */ 232 mtk_snfi_copy_to_gpram(priv, gpram_cache, len); 233 234 /* Start one SPI transaction */ 235 if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_IN) 236 inlen = op->data.nbytes; 237 238 ret = mtk_snfi_mac_trigger(priv, bus, len, inlen); 239 if (ret) 240 return ret; 241 242 /* Copy input data from GPRAM */ 243 if (inlen) 244 mtk_snfi_copy_from_gpram(priv, gpram_cache, op->data.buf.in, 245 len, inlen); 246 247 return 0; 248} 249 250static int mtk_snfi_spi_probe(struct udevice *bus) 251{ 252 struct mtk_snfi_priv *priv = dev_get_priv(bus); 253 int ret; 254 255 priv->base = dev_read_addr_ptr(bus); 256 if (!priv->base) 257 return -EINVAL; 258 259 ret = clk_get_by_name(bus, "nfi_clk", &priv->nfi_clk); 260 if (ret < 0) 261 return ret; 262 263 ret = clk_get_by_name(bus, "pad_clk", &priv->pad_clk); 264 if (ret < 0) 265 return ret; 266 267 clk_enable(&priv->nfi_clk); 268 clk_enable(&priv->pad_clk); 269 270 return 0; 271} 272 273static int mtk_snfi_set_speed(struct udevice *bus, uint speed) 274{ 275 /* 276 * The SNFI does not have a bus clock divider. 277 * The bus clock is set in dts (pad_clk, UNIVPLL2_D8 = 50MHz). 278 */ 279 280 return 0; 281} 282 283static int mtk_snfi_set_mode(struct udevice *bus, uint mode) 284{ 285 /* The SNFI supports only mode 0 */ 286 287 if (mode) 288 return -EINVAL; 289 290 return 0; 291} 292 293static const struct spi_controller_mem_ops mtk_snfi_mem_ops = { 294 .adjust_op_size = mtk_snfi_adjust_op_size, 295 .supports_op = mtk_snfi_supports_op, 296 .exec_op = mtk_snfi_exec_op, 297}; 298 299static const struct dm_spi_ops mtk_snfi_spi_ops = { 300 .mem_ops = &mtk_snfi_mem_ops, 301 .set_speed = mtk_snfi_set_speed, 302 .set_mode = mtk_snfi_set_mode, 303}; 304 305static const struct udevice_id mtk_snfi_spi_ids[] = { 306 { .compatible = "mediatek,mtk-snfi-spi" }, 307 { } 308}; 309 310U_BOOT_DRIVER(mtk_snfi_spi) = { 311 .name = "mtk_snfi_spi", 312 .id = UCLASS_SPI, 313 .of_match = mtk_snfi_spi_ids, 314 .ops = &mtk_snfi_spi_ops, 315 .priv_auto = sizeof(struct mtk_snfi_priv), 316 .probe = mtk_snfi_spi_probe, 317}; 318