1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com> 4 * Copyright (C) 2018 BayLibre, SAS 5 * Author: Neil Armstrong <narmstrong@baylibre.com> 6 * 7 * Amlogic Meson SPI Flash Controller driver 8 */ 9 10#include <common.h> 11#include <log.h> 12#include <spi.h> 13#include <clk.h> 14#include <dm.h> 15#include <regmap.h> 16#include <errno.h> 17#include <asm/io.h> 18#include <linux/bitfield.h> 19#include <linux/bitops.h> 20 21/* register map */ 22#define REG_CMD 0x00 23#define REG_ADDR 0x04 24#define REG_CTRL 0x08 25#define REG_CTRL1 0x0c 26#define REG_STATUS 0x10 27#define REG_CTRL2 0x14 28#define REG_CLOCK 0x18 29#define REG_USER 0x1c 30#define REG_USER1 0x20 31#define REG_USER2 0x24 32#define REG_USER3 0x28 33#define REG_USER4 0x2c 34#define REG_SLAVE 0x30 35#define REG_SLAVE1 0x34 36#define REG_SLAVE2 0x38 37#define REG_SLAVE3 0x3c 38#define REG_C0 0x40 39#define REG_B8 0x60 40#define REG_MAX 0x7c 41 42/* register fields */ 43#define CMD_USER BIT(18) 44#define CTRL_ENABLE_AHB BIT(17) 45#define CLOCK_SOURCE BIT(31) 46#define CLOCK_DIV_SHIFT 12 47#define CLOCK_DIV_MASK (0x3f << CLOCK_DIV_SHIFT) 48#define CLOCK_CNT_HIGH_SHIFT 6 49#define CLOCK_CNT_HIGH_MASK (0x3f << CLOCK_CNT_HIGH_SHIFT) 50#define CLOCK_CNT_LOW_SHIFT 0 51#define CLOCK_CNT_LOW_MASK (0x3f << CLOCK_CNT_LOW_SHIFT) 52#define USER_DIN_EN_MS BIT(0) 53#define USER_CMP_MODE BIT(2) 54#define USER_CLK_NOT_INV BIT(7) 55#define USER_UC_DOUT_SEL BIT(27) 56#define USER_UC_DIN_SEL BIT(28) 57#define USER_UC_MASK ((BIT(5) - 1) << 27) 58#define USER1_BN_UC_DOUT_SHIFT 17 59#define USER1_BN_UC_DOUT_MASK (0xff << 16) 60#define USER1_BN_UC_DIN_SHIFT 8 61#define USER1_BN_UC_DIN_MASK (0xff << 8) 62#define USER4_CS_POL_HIGH BIT(23) 63#define USER4_IDLE_CLK_HIGH BIT(29) 64#define USER4_CS_ACT BIT(30) 65#define SLAVE_TRST_DONE BIT(4) 66#define SLAVE_OP_MODE BIT(30) 67#define SLAVE_SW_RST BIT(31) 68 69#define SPIFC_BUFFER_SIZE 64 70 71struct meson_spifc_priv { 72 struct regmap *regmap; 73 struct clk clk; 74}; 75 76/** 77 * meson_spifc_drain_buffer() - copy data from device buffer to memory 78 * @spifc: the Meson SPI device 79 * @buf: the destination buffer 80 * @len: number of bytes to copy 81 */ 82static void meson_spifc_drain_buffer(struct meson_spifc_priv *spifc, 83 u8 *buf, int len) 84{ 85 u32 data; 86 int i = 0; 87 88 while (i < len) { 89 regmap_read(spifc->regmap, REG_C0 + i, &data); 90 91 if (len - i >= 4) { 92 *((u32 *)buf) = data; 93 buf += 4; 94 } else { 95 memcpy(buf, &data, len - i); 96 break; 97 } 98 i += 4; 99 } 100} 101 102/** 103 * meson_spifc_fill_buffer() - copy data from memory to device buffer 104 * @spifc: the Meson SPI device 105 * @buf: the source buffer 106 * @len: number of bytes to copy 107 */ 108static void meson_spifc_fill_buffer(struct meson_spifc_priv *spifc, 109 const u8 *buf, int len) 110{ 111 u32 data = 0; 112 int i = 0; 113 114 while (i < len) { 115 if (len - i >= 4) 116 data = *(u32 *)buf; 117 else 118 memcpy(&data, buf, len - i); 119 120 regmap_write(spifc->regmap, REG_C0 + i, data); 121 122 buf += 4; 123 i += 4; 124 } 125} 126 127/** 128 * meson_spifc_txrx() - transfer a chunk of data 129 * @spifc: the Meson SPI device 130 * @dout: data buffer for TX 131 * @din: data buffer for RX 132 * @offset: offset of the data to transfer 133 * @len: length of the data to transfer 134 * @last_xfer: whether this is the last transfer of the message 135 * @last_chunk: whether this is the last chunk of the transfer 136 * Return: 0 on success, a negative value on error 137 */ 138static int meson_spifc_txrx(struct meson_spifc_priv *spifc, 139 const u8 *dout, u8 *din, int offset, 140 int len, bool last_xfer, bool last_chunk) 141{ 142 bool keep_cs = true; 143 u32 data; 144 int ret; 145 146 if (dout) 147 meson_spifc_fill_buffer(spifc, dout + offset, len); 148 149 /* enable DOUT stage */ 150 regmap_update_bits(spifc->regmap, REG_USER, USER_UC_MASK, 151 USER_UC_DOUT_SEL); 152 regmap_write(spifc->regmap, REG_USER1, 153 (8 * len - 1) << USER1_BN_UC_DOUT_SHIFT); 154 155 /* enable data input during DOUT */ 156 regmap_update_bits(spifc->regmap, REG_USER, USER_DIN_EN_MS, 157 USER_DIN_EN_MS); 158 159 if (last_chunk && last_xfer) 160 keep_cs = false; 161 162 regmap_update_bits(spifc->regmap, REG_USER4, USER4_CS_ACT, 163 keep_cs ? USER4_CS_ACT : 0); 164 165 /* clear transition done bit */ 166 regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_TRST_DONE, 0); 167 /* start transfer */ 168 regmap_update_bits(spifc->regmap, REG_CMD, CMD_USER, CMD_USER); 169 170 /* wait for the current operation to terminate */ 171 ret = regmap_read_poll_timeout(spifc->regmap, REG_SLAVE, data, 172 (data & SLAVE_TRST_DONE), 173 0, 5 * CONFIG_SYS_HZ); 174 175 if (!ret && din) 176 meson_spifc_drain_buffer(spifc, din + offset, len); 177 178 return ret; 179} 180 181/** 182 * meson_spifc_xfer() - perform a single transfer 183 * @dev: the SPI controller device 184 * @bitlen: length of the transfer 185 * @dout: data buffer for TX 186 * @din: data buffer for RX 187 * @flags: transfer flags 188 * Return: 0 on success, a negative value on error 189 */ 190static int meson_spifc_xfer(struct udevice *slave, unsigned int bitlen, 191 const void *dout, void *din, unsigned long flags) 192{ 193 struct meson_spifc_priv *spifc = dev_get_priv(slave->parent); 194 int blen = bitlen / 8; 195 int len, done = 0, ret = 0; 196 197 if (bitlen % 8) 198 return -EINVAL; 199 200 debug("xfer len %d (%d) dout %p din %p\n", bitlen, blen, dout, din); 201 202 regmap_update_bits(spifc->regmap, REG_CTRL, CTRL_ENABLE_AHB, 0); 203 204 while (done < blen && !ret) { 205 len = min_t(int, blen - done, SPIFC_BUFFER_SIZE); 206 ret = meson_spifc_txrx(spifc, dout, din, done, len, 207 flags & SPI_XFER_END, 208 done + len >= blen); 209 done += len; 210 } 211 212 regmap_update_bits(spifc->regmap, REG_CTRL, CTRL_ENABLE_AHB, 213 CTRL_ENABLE_AHB); 214 215 return ret; 216} 217 218/** 219 * meson_spifc_set_speed() - program the clock divider 220 * @dev: the SPI controller device 221 * @speed: desired speed in Hz 222 */ 223static int meson_spifc_set_speed(struct udevice *dev, uint speed) 224{ 225 struct meson_spifc_priv *spifc = dev_get_priv(dev); 226 unsigned long parent, value; 227 int n; 228 229 parent = clk_get_rate(&spifc->clk); 230 n = max_t(int, parent / speed - 1, 1); 231 232 debug("parent %lu, speed %u, n %d\n", parent, speed, n); 233 234 value = (n << CLOCK_DIV_SHIFT) & CLOCK_DIV_MASK; 235 value |= (n << CLOCK_CNT_LOW_SHIFT) & CLOCK_CNT_LOW_MASK; 236 value |= (((n + 1) / 2 - 1) << CLOCK_CNT_HIGH_SHIFT) & 237 CLOCK_CNT_HIGH_MASK; 238 239 regmap_write(spifc->regmap, REG_CLOCK, value); 240 241 return 0; 242} 243 244/** 245 * meson_spifc_set_mode() - setups the SPI bus mode 246 * @dev: the SPI controller device 247 * @mode: desired mode bitfield 248 * Return: 0 on success, -ENODEV on error 249 */ 250static int meson_spifc_set_mode(struct udevice *dev, uint mode) 251{ 252 struct meson_spifc_priv *spifc = dev_get_priv(dev); 253 254 if (mode & (SPI_CPHA | SPI_RX_QUAD | SPI_RX_DUAL | 255 SPI_TX_QUAD | SPI_TX_DUAL)) 256 return -ENODEV; 257 258 regmap_update_bits(spifc->regmap, REG_USER, USER_CLK_NOT_INV, 259 mode & SPI_CPOL ? USER_CLK_NOT_INV : 0); 260 261 regmap_update_bits(spifc->regmap, REG_USER4, USER4_CS_POL_HIGH, 262 mode & SPI_CS_HIGH ? USER4_CS_POL_HIGH : 0); 263 264 return 0; 265} 266 267/** 268 * meson_spifc_hw_init() - reset and initialize the SPI controller 269 * @spifc: the Meson SPI device 270 */ 271static void meson_spifc_hw_init(struct meson_spifc_priv *spifc) 272{ 273 /* reset device */ 274 regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_SW_RST, 275 SLAVE_SW_RST); 276 /* disable compatible mode */ 277 regmap_update_bits(spifc->regmap, REG_USER, USER_CMP_MODE, 0); 278 /* set master mode */ 279 regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_OP_MODE, 0); 280} 281 282static const struct dm_spi_ops meson_spifc_ops = { 283 .xfer = meson_spifc_xfer, 284 .set_speed = meson_spifc_set_speed, 285 .set_mode = meson_spifc_set_mode, 286}; 287 288static int meson_spifc_probe(struct udevice *dev) 289{ 290 struct meson_spifc_priv *priv = dev_get_priv(dev); 291 int ret; 292 293 ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap); 294 if (ret) 295 return ret; 296 297 ret = clk_get_by_index(dev, 0, &priv->clk); 298 if (ret) 299 return ret; 300 301 ret = clk_enable(&priv->clk); 302 if (ret) 303 return ret; 304 305 meson_spifc_hw_init(priv); 306 307 return 0; 308} 309 310static const struct udevice_id meson_spifc_ids[] = { 311 { .compatible = "amlogic,meson-gxbb-spifc", }, 312 { } 313}; 314 315U_BOOT_DRIVER(meson_spifc) = { 316 .name = "meson_spifc", 317 .id = UCLASS_SPI, 318 .of_match = meson_spifc_ids, 319 .ops = &meson_spifc_ops, 320 .probe = meson_spifc_probe, 321 .priv_auto = sizeof(struct meson_spifc_priv), 322}; 323