Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v6.3-rc6 391 lines 9.1 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2// 3// spi-mt7621.c -- MediaTek MT7621 SPI controller driver 4// 5// Copyright (C) 2011 Sergiy <piratfm@gmail.com> 6// Copyright (C) 2011-2013 Gabor Juhos <juhosg@openwrt.org> 7// Copyright (C) 2014-2015 Felix Fietkau <nbd@nbd.name> 8// 9// Some parts are based on spi-orion.c: 10// Author: Shadi Ammouri <shadi@marvell.com> 11// Copyright (C) 2007-2008 Marvell Ltd. 12 13#include <linux/clk.h> 14#include <linux/delay.h> 15#include <linux/io.h> 16#include <linux/module.h> 17#include <linux/of_device.h> 18#include <linux/reset.h> 19#include <linux/spi/spi.h> 20 21#define DRIVER_NAME "spi-mt7621" 22 23/* in usec */ 24#define RALINK_SPI_WAIT_MAX_LOOP 2000 25 26/* SPISTAT register bit field */ 27#define SPISTAT_BUSY BIT(0) 28 29#define MT7621_SPI_TRANS 0x00 30#define SPITRANS_BUSY BIT(16) 31 32#define MT7621_SPI_OPCODE 0x04 33#define MT7621_SPI_DATA0 0x08 34#define MT7621_SPI_DATA4 0x18 35#define SPI_CTL_TX_RX_CNT_MASK 0xff 36#define SPI_CTL_START BIT(8) 37 38#define MT7621_SPI_MASTER 0x28 39#define MASTER_MORE_BUFMODE BIT(2) 40#define MASTER_FULL_DUPLEX BIT(10) 41#define MASTER_RS_CLK_SEL GENMASK(27, 16) 42#define MASTER_RS_CLK_SEL_SHIFT 16 43#define MASTER_RS_SLAVE_SEL GENMASK(31, 29) 44 45#define MT7621_SPI_MOREBUF 0x2c 46#define MT7621_SPI_POLAR 0x38 47#define MT7621_SPI_SPACE 0x3c 48 49#define MT7621_CPHA BIT(5) 50#define MT7621_CPOL BIT(4) 51#define MT7621_LSB_FIRST BIT(3) 52 53struct mt7621_spi { 54 struct spi_controller *master; 55 void __iomem *base; 56 unsigned int sys_freq; 57 unsigned int speed; 58 int pending_write; 59}; 60 61static inline struct mt7621_spi *spidev_to_mt7621_spi(struct spi_device *spi) 62{ 63 return spi_controller_get_devdata(spi->master); 64} 65 66static inline u32 mt7621_spi_read(struct mt7621_spi *rs, u32 reg) 67{ 68 return ioread32(rs->base + reg); 69} 70 71static inline void mt7621_spi_write(struct mt7621_spi *rs, u32 reg, u32 val) 72{ 73 iowrite32(val, rs->base + reg); 74} 75 76static void mt7621_spi_set_cs(struct spi_device *spi, int enable) 77{ 78 struct mt7621_spi *rs = spidev_to_mt7621_spi(spi); 79 int cs = spi->chip_select; 80 u32 polar = 0; 81 u32 master; 82 83 /* 84 * Select SPI device 7, enable "more buffer mode" and disable 85 * full-duplex (only half-duplex really works on this chip 86 * reliably) 87 */ 88 master = mt7621_spi_read(rs, MT7621_SPI_MASTER); 89 master |= MASTER_RS_SLAVE_SEL | MASTER_MORE_BUFMODE; 90 master &= ~MASTER_FULL_DUPLEX; 91 mt7621_spi_write(rs, MT7621_SPI_MASTER, master); 92 93 rs->pending_write = 0; 94 95 if (enable) 96 polar = BIT(cs); 97 mt7621_spi_write(rs, MT7621_SPI_POLAR, polar); 98} 99 100static int mt7621_spi_prepare(struct spi_device *spi, unsigned int speed) 101{ 102 struct mt7621_spi *rs = spidev_to_mt7621_spi(spi); 103 u32 rate; 104 u32 reg; 105 106 dev_dbg(&spi->dev, "speed:%u\n", speed); 107 108 rate = DIV_ROUND_UP(rs->sys_freq, speed); 109 dev_dbg(&spi->dev, "rate-1:%u\n", rate); 110 111 if (rate > 4097) 112 return -EINVAL; 113 114 if (rate < 2) 115 rate = 2; 116 117 reg = mt7621_spi_read(rs, MT7621_SPI_MASTER); 118 reg &= ~MASTER_RS_CLK_SEL; 119 reg |= (rate - 2) << MASTER_RS_CLK_SEL_SHIFT; 120 rs->speed = speed; 121 122 reg &= ~MT7621_LSB_FIRST; 123 if (spi->mode & SPI_LSB_FIRST) 124 reg |= MT7621_LSB_FIRST; 125 126 /* 127 * This SPI controller seems to be tested on SPI flash only and some 128 * bits are swizzled under other SPI modes probably due to incorrect 129 * wiring inside the silicon. Only mode 0 works correctly. 130 */ 131 reg &= ~(MT7621_CPHA | MT7621_CPOL); 132 133 mt7621_spi_write(rs, MT7621_SPI_MASTER, reg); 134 135 return 0; 136} 137 138static inline int mt7621_spi_wait_till_ready(struct mt7621_spi *rs) 139{ 140 int i; 141 142 for (i = 0; i < RALINK_SPI_WAIT_MAX_LOOP; i++) { 143 u32 status; 144 145 status = mt7621_spi_read(rs, MT7621_SPI_TRANS); 146 if ((status & SPITRANS_BUSY) == 0) 147 return 0; 148 cpu_relax(); 149 udelay(1); 150 } 151 152 return -ETIMEDOUT; 153} 154 155static void mt7621_spi_read_half_duplex(struct mt7621_spi *rs, 156 int rx_len, u8 *buf) 157{ 158 int tx_len; 159 160 /* 161 * Combine with any pending write, and perform one or more half-duplex 162 * transactions reading 'len' bytes. Data to be written is already in 163 * MT7621_SPI_DATA. 164 */ 165 tx_len = rs->pending_write; 166 rs->pending_write = 0; 167 168 while (rx_len || tx_len) { 169 int i; 170 u32 val = (min(tx_len, 4) * 8) << 24; 171 int rx = min(rx_len, 32); 172 173 if (tx_len > 4) 174 val |= (tx_len - 4) * 8; 175 val |= (rx * 8) << 12; 176 mt7621_spi_write(rs, MT7621_SPI_MOREBUF, val); 177 178 tx_len = 0; 179 180 val = mt7621_spi_read(rs, MT7621_SPI_TRANS); 181 val |= SPI_CTL_START; 182 mt7621_spi_write(rs, MT7621_SPI_TRANS, val); 183 184 mt7621_spi_wait_till_ready(rs); 185 186 for (i = 0; i < rx; i++) { 187 if ((i % 4) == 0) 188 val = mt7621_spi_read(rs, MT7621_SPI_DATA0 + i); 189 *buf++ = val & 0xff; 190 val >>= 8; 191 } 192 193 rx_len -= i; 194 } 195} 196 197static inline void mt7621_spi_flush(struct mt7621_spi *rs) 198{ 199 mt7621_spi_read_half_duplex(rs, 0, NULL); 200} 201 202static void mt7621_spi_write_half_duplex(struct mt7621_spi *rs, 203 int tx_len, const u8 *buf) 204{ 205 int len = rs->pending_write; 206 int val = 0; 207 208 if (len & 3) { 209 val = mt7621_spi_read(rs, MT7621_SPI_OPCODE + (len & ~3)); 210 if (len < 4) { 211 val <<= (4 - len) * 8; 212 val = swab32(val); 213 } 214 } 215 216 while (tx_len > 0) { 217 if (len >= 36) { 218 rs->pending_write = len; 219 mt7621_spi_flush(rs); 220 len = 0; 221 } 222 223 val |= *buf++ << (8 * (len & 3)); 224 len++; 225 if ((len & 3) == 0) { 226 if (len == 4) 227 /* The byte-order of the opcode is weird! */ 228 val = swab32(val); 229 mt7621_spi_write(rs, MT7621_SPI_OPCODE + len - 4, val); 230 val = 0; 231 } 232 tx_len -= 1; 233 } 234 235 if (len & 3) { 236 if (len < 4) { 237 val = swab32(val); 238 val >>= (4 - len) * 8; 239 } 240 mt7621_spi_write(rs, MT7621_SPI_OPCODE + (len & ~3), val); 241 } 242 243 rs->pending_write = len; 244} 245 246static int mt7621_spi_transfer_one_message(struct spi_controller *master, 247 struct spi_message *m) 248{ 249 struct mt7621_spi *rs = spi_controller_get_devdata(master); 250 struct spi_device *spi = m->spi; 251 unsigned int speed = spi->max_speed_hz; 252 struct spi_transfer *t = NULL; 253 int status = 0; 254 255 mt7621_spi_wait_till_ready(rs); 256 257 list_for_each_entry(t, &m->transfers, transfer_list) 258 if (t->speed_hz < speed) 259 speed = t->speed_hz; 260 261 if (mt7621_spi_prepare(spi, speed)) { 262 status = -EIO; 263 goto msg_done; 264 } 265 266 /* Assert CS */ 267 mt7621_spi_set_cs(spi, 1); 268 269 m->actual_length = 0; 270 list_for_each_entry(t, &m->transfers, transfer_list) { 271 if ((t->rx_buf) && (t->tx_buf)) { 272 /* 273 * This controller will shift some extra data out 274 * of spi_opcode if (mosi_bit_cnt > 0) && 275 * (cmd_bit_cnt == 0). So the claimed full-duplex 276 * support is broken since we have no way to read 277 * the MISO value during that bit. 278 */ 279 status = -EIO; 280 goto msg_done; 281 } else if (t->rx_buf) { 282 mt7621_spi_read_half_duplex(rs, t->len, t->rx_buf); 283 } else if (t->tx_buf) { 284 mt7621_spi_write_half_duplex(rs, t->len, t->tx_buf); 285 } 286 m->actual_length += t->len; 287 } 288 289 /* Flush data and deassert CS */ 290 mt7621_spi_flush(rs); 291 mt7621_spi_set_cs(spi, 0); 292 293msg_done: 294 m->status = status; 295 spi_finalize_current_message(master); 296 297 return 0; 298} 299 300static int mt7621_spi_setup(struct spi_device *spi) 301{ 302 struct mt7621_spi *rs = spidev_to_mt7621_spi(spi); 303 304 if ((spi->max_speed_hz == 0) || 305 (spi->max_speed_hz > (rs->sys_freq / 2))) 306 spi->max_speed_hz = rs->sys_freq / 2; 307 308 if (spi->max_speed_hz < (rs->sys_freq / 4097)) { 309 dev_err(&spi->dev, "setup: requested speed is too low %d Hz\n", 310 spi->max_speed_hz); 311 return -EINVAL; 312 } 313 314 return 0; 315} 316 317static const struct of_device_id mt7621_spi_match[] = { 318 { .compatible = "ralink,mt7621-spi" }, 319 {}, 320}; 321MODULE_DEVICE_TABLE(of, mt7621_spi_match); 322 323static int mt7621_spi_probe(struct platform_device *pdev) 324{ 325 const struct of_device_id *match; 326 struct spi_controller *master; 327 struct mt7621_spi *rs; 328 void __iomem *base; 329 struct clk *clk; 330 int ret; 331 332 match = of_match_device(mt7621_spi_match, &pdev->dev); 333 if (!match) 334 return -EINVAL; 335 336 base = devm_platform_ioremap_resource(pdev, 0); 337 if (IS_ERR(base)) 338 return PTR_ERR(base); 339 340 clk = devm_clk_get_enabled(&pdev->dev, NULL); 341 if (IS_ERR(clk)) 342 return dev_err_probe(&pdev->dev, PTR_ERR(clk), 343 "unable to get SYS clock\n"); 344 345 master = devm_spi_alloc_master(&pdev->dev, sizeof(*rs)); 346 if (!master) { 347 dev_info(&pdev->dev, "master allocation failed\n"); 348 return -ENOMEM; 349 } 350 351 master->mode_bits = SPI_LSB_FIRST; 352 master->flags = SPI_CONTROLLER_HALF_DUPLEX; 353 master->setup = mt7621_spi_setup; 354 master->transfer_one_message = mt7621_spi_transfer_one_message; 355 master->bits_per_word_mask = SPI_BPW_MASK(8); 356 master->dev.of_node = pdev->dev.of_node; 357 master->num_chipselect = 2; 358 359 dev_set_drvdata(&pdev->dev, master); 360 361 rs = spi_controller_get_devdata(master); 362 rs->base = base; 363 rs->master = master; 364 rs->sys_freq = clk_get_rate(clk); 365 rs->pending_write = 0; 366 dev_info(&pdev->dev, "sys_freq: %u\n", rs->sys_freq); 367 368 ret = device_reset(&pdev->dev); 369 if (ret) { 370 dev_err(&pdev->dev, "SPI reset failed!\n"); 371 return ret; 372 } 373 374 return devm_spi_register_controller(&pdev->dev, master); 375} 376 377MODULE_ALIAS("platform:" DRIVER_NAME); 378 379static struct platform_driver mt7621_spi_driver = { 380 .driver = { 381 .name = DRIVER_NAME, 382 .of_match_table = mt7621_spi_match, 383 }, 384 .probe = mt7621_spi_probe, 385}; 386 387module_platform_driver(mt7621_spi_driver); 388 389MODULE_DESCRIPTION("MT7621 SPI driver"); 390MODULE_AUTHOR("Felix Fietkau <nbd@nbd.name>"); 391MODULE_LICENSE("GPL");