1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2b42dfed8SFlorian Fainelli /* 3b42dfed8SFlorian Fainelli * Broadcom BCM63xx SPI controller support 4b42dfed8SFlorian Fainelli * 5cde4384eSFlorian Fainelli * Copyright (C) 2009-2012 Florian Fainelli <florian@openwrt.org> 6b42dfed8SFlorian Fainelli * Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com> 7b42dfed8SFlorian Fainelli */ 8b42dfed8SFlorian Fainelli 9b42dfed8SFlorian Fainelli #include <linux/kernel.h> 10b42dfed8SFlorian Fainelli #include <linux/clk.h> 11b42dfed8SFlorian Fainelli #include <linux/io.h> 12b42dfed8SFlorian Fainelli #include <linux/module.h> 13b42dfed8SFlorian Fainelli #include <linux/platform_device.h> 14b42dfed8SFlorian Fainelli #include <linux/delay.h> 15b42dfed8SFlorian Fainelli #include <linux/interrupt.h> 16b42dfed8SFlorian Fainelli #include <linux/spi/spi.h> 17b42dfed8SFlorian Fainelli #include <linux/completion.h> 18b42dfed8SFlorian Fainelli #include <linux/err.h> 19cde4384eSFlorian Fainelli #include <linux/pm_runtime.h> 20c29f0889SJonas Gorski #include <linux/of.h> 2138807adeSÁlvaro Fernández Rojas #include <linux/reset.h> 22b42dfed8SFlorian Fainelli 2344d8fb30SJonas Gorski /* BCM 6338/6348 SPI core */ 2444d8fb30SJonas Gorski #define SPI_6348_RSET_SIZE 64 2544d8fb30SJonas Gorski #define SPI_6348_CMD 0x00 /* 16-bits register */ 2644d8fb30SJonas Gorski #define SPI_6348_INT_STATUS 0x02 2744d8fb30SJonas Gorski #define SPI_6348_INT_MASK_ST 0x03 2844d8fb30SJonas Gorski #define SPI_6348_INT_MASK 0x04 2944d8fb30SJonas Gorski #define SPI_6348_ST 0x05 3044d8fb30SJonas Gorski #define SPI_6348_CLK_CFG 0x06 3144d8fb30SJonas Gorski #define SPI_6348_FILL_BYTE 0x07 3244d8fb30SJonas Gorski #define SPI_6348_MSG_TAIL 0x09 3344d8fb30SJonas Gorski #define SPI_6348_RX_TAIL 0x0b 3444d8fb30SJonas Gorski #define SPI_6348_MSG_CTL 0x40 /* 8-bits register */ 3544d8fb30SJonas Gorski #define SPI_6348_MSG_CTL_WIDTH 8 3644d8fb30SJonas Gorski #define SPI_6348_MSG_DATA 0x41 3744d8fb30SJonas Gorski #define SPI_6348_MSG_DATA_SIZE 0x3f 3844d8fb30SJonas Gorski #define SPI_6348_RX_DATA 0x80 3944d8fb30SJonas Gorski #define SPI_6348_RX_DATA_SIZE 0x3f 4044d8fb30SJonas Gorski 4144d8fb30SJonas Gorski /* BCM 3368/6358/6262/6368 SPI core */ 4244d8fb30SJonas Gorski #define SPI_6358_RSET_SIZE 1804 4344d8fb30SJonas Gorski #define SPI_6358_MSG_CTL 0x00 /* 16-bits register */ 4444d8fb30SJonas Gorski #define SPI_6358_MSG_CTL_WIDTH 16 4544d8fb30SJonas Gorski #define SPI_6358_MSG_DATA 0x02 4644d8fb30SJonas Gorski #define SPI_6358_MSG_DATA_SIZE 0x21e 4744d8fb30SJonas Gorski #define SPI_6358_RX_DATA 0x400 4844d8fb30SJonas Gorski #define SPI_6358_RX_DATA_SIZE 0x220 4944d8fb30SJonas Gorski #define SPI_6358_CMD 0x700 /* 16-bits register */ 5044d8fb30SJonas Gorski #define SPI_6358_INT_STATUS 0x702 5144d8fb30SJonas Gorski #define SPI_6358_INT_MASK_ST 0x703 5244d8fb30SJonas Gorski #define SPI_6358_INT_MASK 0x704 5344d8fb30SJonas Gorski #define SPI_6358_ST 0x705 5444d8fb30SJonas Gorski #define SPI_6358_CLK_CFG 0x706 5544d8fb30SJonas Gorski #define SPI_6358_FILL_BYTE 0x707 5644d8fb30SJonas Gorski #define SPI_6358_MSG_TAIL 0x709 5744d8fb30SJonas Gorski #define SPI_6358_RX_TAIL 0x70B 5844d8fb30SJonas Gorski 5944d8fb30SJonas Gorski /* Shared SPI definitions */ 6044d8fb30SJonas Gorski 6144d8fb30SJonas Gorski /* Message configuration */ 6244d8fb30SJonas Gorski #define SPI_FD_RW 0x00 6344d8fb30SJonas Gorski #define SPI_HD_W 0x01 6444d8fb30SJonas Gorski #define SPI_HD_R 0x02 6544d8fb30SJonas Gorski #define SPI_BYTE_CNT_SHIFT 0 6644d8fb30SJonas Gorski #define SPI_6348_MSG_TYPE_SHIFT 6 6744d8fb30SJonas Gorski #define SPI_6358_MSG_TYPE_SHIFT 14 6844d8fb30SJonas Gorski 6944d8fb30SJonas Gorski /* Command */ 7044d8fb30SJonas Gorski #define SPI_CMD_NOOP 0x00 7144d8fb30SJonas Gorski #define SPI_CMD_SOFT_RESET 0x01 7244d8fb30SJonas Gorski #define SPI_CMD_HARD_RESET 0x02 7344d8fb30SJonas Gorski #define SPI_CMD_START_IMMEDIATE 0x03 7444d8fb30SJonas Gorski #define SPI_CMD_COMMAND_SHIFT 0 7544d8fb30SJonas Gorski #define SPI_CMD_COMMAND_MASK 0x000f 7644d8fb30SJonas Gorski #define SPI_CMD_DEVICE_ID_SHIFT 4 7744d8fb30SJonas Gorski #define SPI_CMD_PREPEND_BYTE_CNT_SHIFT 8 7844d8fb30SJonas Gorski #define SPI_CMD_ONE_BYTE_SHIFT 11 7944d8fb30SJonas Gorski #define SPI_CMD_ONE_WIRE_SHIFT 12 8044d8fb30SJonas Gorski #define SPI_DEV_ID_0 0 8144d8fb30SJonas Gorski #define SPI_DEV_ID_1 1 8244d8fb30SJonas Gorski #define SPI_DEV_ID_2 2 8344d8fb30SJonas Gorski #define SPI_DEV_ID_3 3 8444d8fb30SJonas Gorski 8544d8fb30SJonas Gorski /* Interrupt mask */ 8644d8fb30SJonas Gorski #define SPI_INTR_CMD_DONE 0x01 8744d8fb30SJonas Gorski #define SPI_INTR_RX_OVERFLOW 0x02 8844d8fb30SJonas Gorski #define SPI_INTR_TX_UNDERFLOW 0x04 8944d8fb30SJonas Gorski #define SPI_INTR_TX_OVERFLOW 0x08 9044d8fb30SJonas Gorski #define SPI_INTR_RX_UNDERFLOW 0x10 9144d8fb30SJonas Gorski #define SPI_INTR_CLEAR_ALL 0x1f 9244d8fb30SJonas Gorski 9344d8fb30SJonas Gorski /* Status */ 9444d8fb30SJonas Gorski #define SPI_RX_EMPTY 0x02 9544d8fb30SJonas Gorski #define SPI_CMD_BUSY 0x04 9644d8fb30SJonas Gorski #define SPI_SERIAL_BUSY 0x08 9744d8fb30SJonas Gorski 9844d8fb30SJonas Gorski /* Clock configuration */ 9944d8fb30SJonas Gorski #define SPI_CLK_20MHZ 0x00 10044d8fb30SJonas Gorski #define SPI_CLK_0_391MHZ 0x01 10144d8fb30SJonas Gorski #define SPI_CLK_0_781MHZ 0x02 /* default */ 10244d8fb30SJonas Gorski #define SPI_CLK_1_563MHZ 0x03 10344d8fb30SJonas Gorski #define SPI_CLK_3_125MHZ 0x04 10444d8fb30SJonas Gorski #define SPI_CLK_6_250MHZ 0x05 10544d8fb30SJonas Gorski #define SPI_CLK_12_50MHZ 0x06 10644d8fb30SJonas Gorski #define SPI_CLK_MASK 0x07 10744d8fb30SJonas Gorski #define SPI_SSOFFTIME_MASK 0x38 10844d8fb30SJonas Gorski #define SPI_SSOFFTIME_SHIFT 3 10944d8fb30SJonas Gorski #define SPI_BYTE_SWAP 0x80 11044d8fb30SJonas Gorski 11144d8fb30SJonas Gorski enum bcm63xx_regs_spi { 11244d8fb30SJonas Gorski SPI_CMD, 11344d8fb30SJonas Gorski SPI_INT_STATUS, 11444d8fb30SJonas Gorski SPI_INT_MASK_ST, 11544d8fb30SJonas Gorski SPI_INT_MASK, 11644d8fb30SJonas Gorski SPI_ST, 11744d8fb30SJonas Gorski SPI_CLK_CFG, 11844d8fb30SJonas Gorski SPI_FILL_BYTE, 11944d8fb30SJonas Gorski SPI_MSG_TAIL, 12044d8fb30SJonas Gorski SPI_RX_TAIL, 12144d8fb30SJonas Gorski SPI_MSG_CTL, 12244d8fb30SJonas Gorski SPI_MSG_DATA, 12344d8fb30SJonas Gorski SPI_RX_DATA, 12444d8fb30SJonas Gorski SPI_MSG_TYPE_SHIFT, 12544d8fb30SJonas Gorski SPI_MSG_CTL_WIDTH, 12644d8fb30SJonas Gorski SPI_MSG_DATA_SIZE, 12744d8fb30SJonas Gorski }; 128b42dfed8SFlorian Fainelli 1295158814cSJonas Gorski #define BCM63XX_SPI_MAX_PREPEND 7 130b17de076SJonas Gorski 13165059997SJonas Gorski #define BCM63XX_SPI_MAX_CS 8 132a45fcea5SJonas Gorski #define BCM63XX_SPI_BUS_NUM 0 13365059997SJonas Gorski 134b42dfed8SFlorian Fainelli struct bcm63xx_spi { 135b42dfed8SFlorian Fainelli struct completion done; 136b42dfed8SFlorian Fainelli 137b42dfed8SFlorian Fainelli void __iomem *regs; 138b42dfed8SFlorian Fainelli int irq; 139b42dfed8SFlorian Fainelli 140b42dfed8SFlorian Fainelli /* Platform data */ 14144d8fb30SJonas Gorski const unsigned long *reg_offsets; 142b85d65ddSAravind Thokala unsigned int fifo_size; 1435a670445SFlorian Fainelli unsigned int msg_type_shift; 1445a670445SFlorian Fainelli unsigned int msg_ctl_width; 145b42dfed8SFlorian Fainelli 146b42dfed8SFlorian Fainelli /* data iomem */ 147b42dfed8SFlorian Fainelli u8 __iomem *tx_io; 148b42dfed8SFlorian Fainelli const u8 __iomem *rx_io; 149b42dfed8SFlorian Fainelli 150b42dfed8SFlorian Fainelli struct clk *clk; 151b42dfed8SFlorian Fainelli struct platform_device *pdev; 152b42dfed8SFlorian Fainelli }; 153b42dfed8SFlorian Fainelli 154b42dfed8SFlorian Fainelli static inline u8 bcm_spi_readb(struct bcm63xx_spi *bs, 155b42dfed8SFlorian Fainelli unsigned int offset) 156b42dfed8SFlorian Fainelli { 15744d8fb30SJonas Gorski return readb(bs->regs + bs->reg_offsets[offset]); 158b42dfed8SFlorian Fainelli } 159b42dfed8SFlorian Fainelli 160b42dfed8SFlorian Fainelli static inline void bcm_spi_writeb(struct bcm63xx_spi *bs, 161b42dfed8SFlorian Fainelli u8 value, unsigned int offset) 162b42dfed8SFlorian Fainelli { 16344d8fb30SJonas Gorski writeb(value, bs->regs + bs->reg_offsets[offset]); 164b42dfed8SFlorian Fainelli } 165b42dfed8SFlorian Fainelli 166b42dfed8SFlorian Fainelli static inline void bcm_spi_writew(struct bcm63xx_spi *bs, 167b42dfed8SFlorian Fainelli u16 value, unsigned int offset) 168b42dfed8SFlorian Fainelli { 169682b5280SJonas Gorski #ifdef CONFIG_CPU_BIG_ENDIAN 17044d8fb30SJonas Gorski iowrite16be(value, bs->regs + bs->reg_offsets[offset]); 171158fcc4eSJonas Gorski #else 17244d8fb30SJonas Gorski writew(value, bs->regs + bs->reg_offsets[offset]); 173158fcc4eSJonas Gorski #endif 174b42dfed8SFlorian Fainelli } 175b42dfed8SFlorian Fainelli 176b85d65ddSAravind Thokala static const unsigned int bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = { 177b42dfed8SFlorian Fainelli { 20000000, SPI_CLK_20MHZ }, 178b42dfed8SFlorian Fainelli { 12500000, SPI_CLK_12_50MHZ }, 179b42dfed8SFlorian Fainelli { 6250000, SPI_CLK_6_250MHZ }, 180b42dfed8SFlorian Fainelli { 3125000, SPI_CLK_3_125MHZ }, 181b42dfed8SFlorian Fainelli { 1563000, SPI_CLK_1_563MHZ }, 182b42dfed8SFlorian Fainelli { 781000, SPI_CLK_0_781MHZ }, 183b42dfed8SFlorian Fainelli { 391000, SPI_CLK_0_391MHZ } 184b42dfed8SFlorian Fainelli }; 185b42dfed8SFlorian Fainelli 186cde4384eSFlorian Fainelli static void bcm63xx_spi_setup_transfer(struct spi_device *spi, 187cde4384eSFlorian Fainelli struct spi_transfer *t) 188cde4384eSFlorian Fainelli { 1891a9e7619SYang Yingliang struct bcm63xx_spi *bs = spi_controller_get_devdata(spi->controller); 190cde4384eSFlorian Fainelli u8 clk_cfg, reg; 191cde4384eSFlorian Fainelli int i; 192cde4384eSFlorian Fainelli 1934dde99bdSGeert Uytterhoeven /* Default to lowest clock configuration */ 1944dde99bdSGeert Uytterhoeven clk_cfg = SPI_CLK_0_391MHZ; 1954dde99bdSGeert Uytterhoeven 196b42dfed8SFlorian Fainelli /* Find the closest clock configuration */ 197b42dfed8SFlorian Fainelli for (i = 0; i < SPI_CLK_MASK; i++) { 19868792e2aSJonas Gorski if (t->speed_hz >= bcm63xx_spi_freq_table[i][0]) { 199b42dfed8SFlorian Fainelli clk_cfg = bcm63xx_spi_freq_table[i][1]; 200b42dfed8SFlorian Fainelli break; 201b42dfed8SFlorian Fainelli } 202b42dfed8SFlorian Fainelli } 203b42dfed8SFlorian Fainelli 204b42dfed8SFlorian Fainelli /* clear existing clock configuration bits of the register */ 205b42dfed8SFlorian Fainelli reg = bcm_spi_readb(bs, SPI_CLK_CFG); 206b42dfed8SFlorian Fainelli reg &= ~SPI_CLK_MASK; 207b42dfed8SFlorian Fainelli reg |= clk_cfg; 208b42dfed8SFlorian Fainelli 209b42dfed8SFlorian Fainelli bcm_spi_writeb(bs, reg, SPI_CLK_CFG); 210b42dfed8SFlorian Fainelli dev_dbg(&spi->dev, "Setting clock register to %02x (hz %d)\n", 21168792e2aSJonas Gorski clk_cfg, t->speed_hz); 212b42dfed8SFlorian Fainelli } 213b42dfed8SFlorian Fainelli 214b42dfed8SFlorian Fainelli /* the spi->mode bits understood by this driver: */ 215b42dfed8SFlorian Fainelli #define MODEBITS (SPI_CPOL | SPI_CPHA) 216b42dfed8SFlorian Fainelli 217b17de076SJonas Gorski static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first, 218b17de076SJonas Gorski unsigned int num_transfers) 219b42dfed8SFlorian Fainelli { 2201a9e7619SYang Yingliang struct bcm63xx_spi *bs = spi_controller_get_devdata(spi->controller); 221b42dfed8SFlorian Fainelli u16 msg_ctl; 222b42dfed8SFlorian Fainelli u16 cmd; 223b17de076SJonas Gorski unsigned int i, timeout = 0, prepend_len = 0, len = 0; 224b17de076SJonas Gorski struct spi_transfer *t = first; 225b17de076SJonas Gorski bool do_rx = false; 226b17de076SJonas Gorski bool do_tx = false; 227b42dfed8SFlorian Fainelli 228cde4384eSFlorian Fainelli /* Disable the CMD_DONE interrupt */ 229cde4384eSFlorian Fainelli bcm_spi_writeb(bs, 0, SPI_INT_MASK); 230cde4384eSFlorian Fainelli 231b42dfed8SFlorian Fainelli dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", 232b42dfed8SFlorian Fainelli t->tx_buf, t->rx_buf, t->len); 233b42dfed8SFlorian Fainelli 234b17de076SJonas Gorski if (num_transfers > 1 && t->tx_buf && t->len <= BCM63XX_SPI_MAX_PREPEND) 235b17de076SJonas Gorski prepend_len = t->len; 236b17de076SJonas Gorski 237b17de076SJonas Gorski /* prepare the buffer */ 238b17de076SJonas Gorski for (i = 0; i < num_transfers; i++) { 239b17de076SJonas Gorski if (t->tx_buf) { 240b17de076SJonas Gorski do_tx = true; 241b17de076SJonas Gorski memcpy_toio(bs->tx_io + len, t->tx_buf, t->len); 242b17de076SJonas Gorski 243b17de076SJonas Gorski /* don't prepend more than one tx */ 244b17de076SJonas Gorski if (t != first) 245b17de076SJonas Gorski prepend_len = 0; 246b17de076SJonas Gorski } 247b17de076SJonas Gorski 248b17de076SJonas Gorski if (t->rx_buf) { 249b17de076SJonas Gorski do_rx = true; 250b17de076SJonas Gorski /* prepend is half-duplex write only */ 251b17de076SJonas Gorski if (t == first) 252b17de076SJonas Gorski prepend_len = 0; 253b17de076SJonas Gorski } 254b17de076SJonas Gorski 255b17de076SJonas Gorski len += t->len; 256b17de076SJonas Gorski 257b17de076SJonas Gorski t = list_entry(t->transfer_list.next, struct spi_transfer, 258b17de076SJonas Gorski transfer_list); 259b17de076SJonas Gorski } 260b17de076SJonas Gorski 261aa0fe826SAxel Lin reinit_completion(&bs->done); 262b42dfed8SFlorian Fainelli 263b42dfed8SFlorian Fainelli /* Fill in the Message control register */ 264b17de076SJonas Gorski msg_ctl = (len << SPI_BYTE_CNT_SHIFT); 265b42dfed8SFlorian Fainelli 266b17de076SJonas Gorski if (do_rx && do_tx && prepend_len == 0) 2675a670445SFlorian Fainelli msg_ctl |= (SPI_FD_RW << bs->msg_type_shift); 268b17de076SJonas Gorski else if (do_rx) 2695a670445SFlorian Fainelli msg_ctl |= (SPI_HD_R << bs->msg_type_shift); 270b17de076SJonas Gorski else if (do_tx) 2715a670445SFlorian Fainelli msg_ctl |= (SPI_HD_W << bs->msg_type_shift); 272b42dfed8SFlorian Fainelli 2735a670445SFlorian Fainelli switch (bs->msg_ctl_width) { 2745a670445SFlorian Fainelli case 8: 2755a670445SFlorian Fainelli bcm_spi_writeb(bs, msg_ctl, SPI_MSG_CTL); 2765a670445SFlorian Fainelli break; 2775a670445SFlorian Fainelli case 16: 278b42dfed8SFlorian Fainelli bcm_spi_writew(bs, msg_ctl, SPI_MSG_CTL); 2795a670445SFlorian Fainelli break; 2805a670445SFlorian Fainelli } 281b42dfed8SFlorian Fainelli 282b42dfed8SFlorian Fainelli /* Issue the transfer */ 283b42dfed8SFlorian Fainelli cmd = SPI_CMD_START_IMMEDIATE; 284b17de076SJonas Gorski cmd |= (prepend_len << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); 2859e264f3fSAmit Kumar Mahapatra via Alsa-devel cmd |= (spi_get_chipselect(spi, 0) << SPI_CMD_DEVICE_ID_SHIFT); 286b42dfed8SFlorian Fainelli bcm_spi_writew(bs, cmd, SPI_CMD); 287b42dfed8SFlorian Fainelli 288cde4384eSFlorian Fainelli /* Enable the CMD_DONE interrupt */ 289cde4384eSFlorian Fainelli bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK); 290b42dfed8SFlorian Fainelli 291c0fde3baSJonas Gorski timeout = wait_for_completion_timeout(&bs->done, HZ); 292c0fde3baSJonas Gorski if (!timeout) 293c0fde3baSJonas Gorski return -ETIMEDOUT; 294c0fde3baSJonas Gorski 29520e9e78fSJonas Gorski if (!do_rx) 296b17de076SJonas Gorski return 0; 297b17de076SJonas Gorski 298b17de076SJonas Gorski len = 0; 299b17de076SJonas Gorski t = first; 300c0fde3baSJonas Gorski /* Read out all the data */ 301b17de076SJonas Gorski for (i = 0; i < num_transfers; i++) { 302b17de076SJonas Gorski if (t->rx_buf) 303b17de076SJonas Gorski memcpy_fromio(t->rx_buf, bs->rx_io + len, t->len); 304b17de076SJonas Gorski 305b17de076SJonas Gorski if (t != first || prepend_len == 0) 306b17de076SJonas Gorski len += t->len; 307b17de076SJonas Gorski 308b17de076SJonas Gorski t = list_entry(t->transfer_list.next, struct spi_transfer, 309b17de076SJonas Gorski transfer_list); 310b17de076SJonas Gorski } 311c0fde3baSJonas Gorski 312c0fde3baSJonas Gorski return 0; 313b42dfed8SFlorian Fainelli } 314b42dfed8SFlorian Fainelli 3151a9e7619SYang Yingliang static int bcm63xx_spi_transfer_one(struct spi_controller *host, 316cde4384eSFlorian Fainelli struct spi_message *m) 317cde4384eSFlorian Fainelli { 3181a9e7619SYang Yingliang struct bcm63xx_spi *bs = spi_controller_get_devdata(host); 319b17de076SJonas Gorski struct spi_transfer *t, *first = NULL; 320cde4384eSFlorian Fainelli struct spi_device *spi = m->spi; 321cde4384eSFlorian Fainelli int status = 0; 322b17de076SJonas Gorski unsigned int n_transfers = 0, total_len = 0; 323b17de076SJonas Gorski bool can_use_prepend = false; 324cde4384eSFlorian Fainelli 325b17de076SJonas Gorski /* 326b17de076SJonas Gorski * This SPI controller does not support keeping CS active after a 327b17de076SJonas Gorski * transfer. 328b17de076SJonas Gorski * Work around this by merging as many transfers we can into one big 329b17de076SJonas Gorski * full-duplex transfers. 330b17de076SJonas Gorski */ 331cde4384eSFlorian Fainelli list_for_each_entry(t, &m->transfers, transfer_list) { 332b17de076SJonas Gorski if (!first) 333b17de076SJonas Gorski first = t; 334b17de076SJonas Gorski 335b17de076SJonas Gorski n_transfers++; 336b17de076SJonas Gorski total_len += t->len; 337b17de076SJonas Gorski 338b17de076SJonas Gorski if (n_transfers == 2 && !first->rx_buf && !t->tx_buf && 339b17de076SJonas Gorski first->len <= BCM63XX_SPI_MAX_PREPEND) 340b17de076SJonas Gorski can_use_prepend = true; 341b17de076SJonas Gorski else if (can_use_prepend && t->tx_buf) 342b17de076SJonas Gorski can_use_prepend = false; 343b17de076SJonas Gorski 344c0fde3baSJonas Gorski /* we can only transfer one fifo worth of data */ 345b17de076SJonas Gorski if ((can_use_prepend && 346b17de076SJonas Gorski total_len > (bs->fifo_size + BCM63XX_SPI_MAX_PREPEND)) || 347b17de076SJonas Gorski (!can_use_prepend && total_len > bs->fifo_size)) { 348c0fde3baSJonas Gorski dev_err(&spi->dev, "unable to do transfers larger than FIFO size (%i > %i)\n", 349b17de076SJonas Gorski total_len, bs->fifo_size); 350b17de076SJonas Gorski status = -EINVAL; 351b17de076SJonas Gorski goto exit; 352b17de076SJonas Gorski } 353b17de076SJonas Gorski 354b17de076SJonas Gorski /* all combined transfers have to have the same speed */ 355b17de076SJonas Gorski if (t->speed_hz != first->speed_hz) { 356b17de076SJonas Gorski dev_err(&spi->dev, "unable to change speed between transfers\n"); 357c0fde3baSJonas Gorski status = -EINVAL; 358cde4384eSFlorian Fainelli goto exit; 359cde4384eSFlorian Fainelli } 360cde4384eSFlorian Fainelli 361c0fde3baSJonas Gorski /* CS will be deasserted directly after transfer */ 362e7f2d4c6SAlexandru Ardelean if (t->delay.value) { 363c0fde3baSJonas Gorski dev_err(&spi->dev, "unable to keep CS asserted after transfer\n"); 364c0fde3baSJonas Gorski status = -EINVAL; 365c0fde3baSJonas Gorski goto exit; 366cde4384eSFlorian Fainelli } 367cde4384eSFlorian Fainelli 368b17de076SJonas Gorski if (t->cs_change || 369b17de076SJonas Gorski list_is_last(&t->transfer_list, &m->transfers)) { 370c0fde3baSJonas Gorski /* configure adapter for a new transfer */ 371b17de076SJonas Gorski bcm63xx_spi_setup_transfer(spi, first); 372c0fde3baSJonas Gorski 373c0fde3baSJonas Gorski /* send the data */ 374b17de076SJonas Gorski status = bcm63xx_txrx_bufs(spi, first, n_transfers); 375c0fde3baSJonas Gorski if (status) 376c0fde3baSJonas Gorski goto exit; 377c0fde3baSJonas Gorski 378b17de076SJonas Gorski m->actual_length += total_len; 379b17de076SJonas Gorski 380b17de076SJonas Gorski first = NULL; 381b17de076SJonas Gorski n_transfers = 0; 382b17de076SJonas Gorski total_len = 0; 383b17de076SJonas Gorski can_use_prepend = false; 384b17de076SJonas Gorski } 385cde4384eSFlorian Fainelli } 386cde4384eSFlorian Fainelli exit: 387cde4384eSFlorian Fainelli m->status = status; 3881a9e7619SYang Yingliang spi_finalize_current_message(host); 389cde4384eSFlorian Fainelli 390cde4384eSFlorian Fainelli return 0; 391b42dfed8SFlorian Fainelli } 392b42dfed8SFlorian Fainelli 3931a9e7619SYang Yingliang /* This driver supports single host mode only. Hence 394b42dfed8SFlorian Fainelli * CMD_DONE is the only interrupt we care about 395b42dfed8SFlorian Fainelli */ 396b42dfed8SFlorian Fainelli static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id) 397b42dfed8SFlorian Fainelli { 3981a9e7619SYang Yingliang struct spi_controller *host = (struct spi_controller *)dev_id; 3991a9e7619SYang Yingliang struct bcm63xx_spi *bs = spi_controller_get_devdata(host); 400b42dfed8SFlorian Fainelli u8 intr; 401b42dfed8SFlorian Fainelli 402b42dfed8SFlorian Fainelli /* Read interupts and clear them immediately */ 403b42dfed8SFlorian Fainelli intr = bcm_spi_readb(bs, SPI_INT_STATUS); 404b42dfed8SFlorian Fainelli bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); 405b42dfed8SFlorian Fainelli bcm_spi_writeb(bs, 0, SPI_INT_MASK); 406b42dfed8SFlorian Fainelli 407cde4384eSFlorian Fainelli /* A transfer completed */ 408cde4384eSFlorian Fainelli if (intr & SPI_INTR_CMD_DONE) 409b42dfed8SFlorian Fainelli complete(&bs->done); 410b42dfed8SFlorian Fainelli 411b42dfed8SFlorian Fainelli return IRQ_HANDLED; 412b42dfed8SFlorian Fainelli } 413b42dfed8SFlorian Fainelli 414ccd0657cSJonas Gorski static size_t bcm63xx_spi_max_length(struct spi_device *spi) 4150135c03dSJonas Gorski { 4161a9e7619SYang Yingliang struct bcm63xx_spi *bs = spi_controller_get_devdata(spi->controller); 4170135c03dSJonas Gorski 4180135c03dSJonas Gorski return bs->fifo_size; 4190135c03dSJonas Gorski } 4200135c03dSJonas Gorski 42144d8fb30SJonas Gorski static const unsigned long bcm6348_spi_reg_offsets[] = { 42244d8fb30SJonas Gorski [SPI_CMD] = SPI_6348_CMD, 42344d8fb30SJonas Gorski [SPI_INT_STATUS] = SPI_6348_INT_STATUS, 42444d8fb30SJonas Gorski [SPI_INT_MASK_ST] = SPI_6348_INT_MASK_ST, 42544d8fb30SJonas Gorski [SPI_INT_MASK] = SPI_6348_INT_MASK, 42644d8fb30SJonas Gorski [SPI_ST] = SPI_6348_ST, 42744d8fb30SJonas Gorski [SPI_CLK_CFG] = SPI_6348_CLK_CFG, 42844d8fb30SJonas Gorski [SPI_FILL_BYTE] = SPI_6348_FILL_BYTE, 42944d8fb30SJonas Gorski [SPI_MSG_TAIL] = SPI_6348_MSG_TAIL, 43044d8fb30SJonas Gorski [SPI_RX_TAIL] = SPI_6348_RX_TAIL, 43144d8fb30SJonas Gorski [SPI_MSG_CTL] = SPI_6348_MSG_CTL, 43244d8fb30SJonas Gorski [SPI_MSG_DATA] = SPI_6348_MSG_DATA, 43344d8fb30SJonas Gorski [SPI_RX_DATA] = SPI_6348_RX_DATA, 43444d8fb30SJonas Gorski [SPI_MSG_TYPE_SHIFT] = SPI_6348_MSG_TYPE_SHIFT, 43544d8fb30SJonas Gorski [SPI_MSG_CTL_WIDTH] = SPI_6348_MSG_CTL_WIDTH, 43644d8fb30SJonas Gorski [SPI_MSG_DATA_SIZE] = SPI_6348_MSG_DATA_SIZE, 43744d8fb30SJonas Gorski }; 43844d8fb30SJonas Gorski 43944d8fb30SJonas Gorski static const unsigned long bcm6358_spi_reg_offsets[] = { 44044d8fb30SJonas Gorski [SPI_CMD] = SPI_6358_CMD, 44144d8fb30SJonas Gorski [SPI_INT_STATUS] = SPI_6358_INT_STATUS, 44244d8fb30SJonas Gorski [SPI_INT_MASK_ST] = SPI_6358_INT_MASK_ST, 44344d8fb30SJonas Gorski [SPI_INT_MASK] = SPI_6358_INT_MASK, 44444d8fb30SJonas Gorski [SPI_ST] = SPI_6358_ST, 44544d8fb30SJonas Gorski [SPI_CLK_CFG] = SPI_6358_CLK_CFG, 44644d8fb30SJonas Gorski [SPI_FILL_BYTE] = SPI_6358_FILL_BYTE, 44744d8fb30SJonas Gorski [SPI_MSG_TAIL] = SPI_6358_MSG_TAIL, 44844d8fb30SJonas Gorski [SPI_RX_TAIL] = SPI_6358_RX_TAIL, 44944d8fb30SJonas Gorski [SPI_MSG_CTL] = SPI_6358_MSG_CTL, 45044d8fb30SJonas Gorski [SPI_MSG_DATA] = SPI_6358_MSG_DATA, 45144d8fb30SJonas Gorski [SPI_RX_DATA] = SPI_6358_RX_DATA, 45244d8fb30SJonas Gorski [SPI_MSG_TYPE_SHIFT] = SPI_6358_MSG_TYPE_SHIFT, 45344d8fb30SJonas Gorski [SPI_MSG_CTL_WIDTH] = SPI_6358_MSG_CTL_WIDTH, 45444d8fb30SJonas Gorski [SPI_MSG_DATA_SIZE] = SPI_6358_MSG_DATA_SIZE, 45544d8fb30SJonas Gorski }; 45644d8fb30SJonas Gorski 45744d8fb30SJonas Gorski static const struct platform_device_id bcm63xx_spi_dev_match[] = { 45844d8fb30SJonas Gorski { 45944d8fb30SJonas Gorski .name = "bcm6348-spi", 46044d8fb30SJonas Gorski .driver_data = (unsigned long)bcm6348_spi_reg_offsets, 46144d8fb30SJonas Gorski }, 46244d8fb30SJonas Gorski { 46344d8fb30SJonas Gorski .name = "bcm6358-spi", 46444d8fb30SJonas Gorski .driver_data = (unsigned long)bcm6358_spi_reg_offsets, 46544d8fb30SJonas Gorski }, 46644d8fb30SJonas Gorski { 46744d8fb30SJonas Gorski }, 46844d8fb30SJonas Gorski }; 469b42dfed8SFlorian Fainelli 470c29f0889SJonas Gorski static const struct of_device_id bcm63xx_spi_of_match[] = { 471c29f0889SJonas Gorski { .compatible = "brcm,bcm6348-spi", .data = &bcm6348_spi_reg_offsets }, 472c29f0889SJonas Gorski { .compatible = "brcm,bcm6358-spi", .data = &bcm6358_spi_reg_offsets }, 473c29f0889SJonas Gorski { }, 474c29f0889SJonas Gorski }; 475*709df70aSLiao Chen MODULE_DEVICE_TABLE(of, bcm63xx_spi_of_match); 476c29f0889SJonas Gorski 477fd4a319bSGrant Likely static int bcm63xx_spi_probe(struct platform_device *pdev) 478b42dfed8SFlorian Fainelli { 479b42dfed8SFlorian Fainelli struct resource *r; 48044d8fb30SJonas Gorski const unsigned long *bcm63xx_spireg; 481b42dfed8SFlorian Fainelli struct device *dev = &pdev->dev; 482c29f0889SJonas Gorski int irq, bus_num; 4831a9e7619SYang Yingliang struct spi_controller *host; 484b42dfed8SFlorian Fainelli struct clk *clk; 485b42dfed8SFlorian Fainelli struct bcm63xx_spi *bs; 486b42dfed8SFlorian Fainelli int ret; 487c29f0889SJonas Gorski u32 num_cs = BCM63XX_SPI_MAX_CS; 48838807adeSÁlvaro Fernández Rojas struct reset_control *reset; 489b42dfed8SFlorian Fainelli 490c29f0889SJonas Gorski if (dev->of_node) { 491c29f0889SJonas Gorski const struct of_device_id *match; 492c29f0889SJonas Gorski 493c29f0889SJonas Gorski match = of_match_node(bcm63xx_spi_of_match, dev->of_node); 494c29f0889SJonas Gorski if (!match) 49544d8fb30SJonas Gorski return -EINVAL; 496c29f0889SJonas Gorski bcm63xx_spireg = match->data; 49744d8fb30SJonas Gorski 498c29f0889SJonas Gorski of_property_read_u32(dev->of_node, "num-cs", &num_cs); 499c29f0889SJonas Gorski if (num_cs > BCM63XX_SPI_MAX_CS) { 500c29f0889SJonas Gorski dev_warn(dev, "unsupported number of cs (%i), reducing to 8\n", 501c29f0889SJonas Gorski num_cs); 502c29f0889SJonas Gorski num_cs = BCM63XX_SPI_MAX_CS; 503c29f0889SJonas Gorski } 504c29f0889SJonas Gorski 505c29f0889SJonas Gorski bus_num = -1; 506c29f0889SJonas Gorski } else if (pdev->id_entry->driver_data) { 507c29f0889SJonas Gorski const struct platform_device_id *match = pdev->id_entry; 508c29f0889SJonas Gorski 509c29f0889SJonas Gorski bcm63xx_spireg = (const unsigned long *)match->driver_data; 510c29f0889SJonas Gorski bus_num = BCM63XX_SPI_BUS_NUM; 511c29f0889SJonas Gorski } else { 512c29f0889SJonas Gorski return -EINVAL; 513c29f0889SJonas Gorski } 51444d8fb30SJonas Gorski 515b42dfed8SFlorian Fainelli irq = platform_get_irq(pdev, 0); 5166b8ac10eSStephen Boyd if (irq < 0) 517ba8afe94SGustavo A. R. Silva return irq; 518b42dfed8SFlorian Fainelli 519acf4fc6fSJingoo Han clk = devm_clk_get(dev, "spi"); 520b42dfed8SFlorian Fainelli if (IS_ERR(clk)) { 521b42dfed8SFlorian Fainelli dev_err(dev, "no clock for device\n"); 522acf4fc6fSJingoo Han return PTR_ERR(clk); 523b42dfed8SFlorian Fainelli } 524b42dfed8SFlorian Fainelli 52538807adeSÁlvaro Fernández Rojas reset = devm_reset_control_get_optional_exclusive(dev, NULL); 52638807adeSÁlvaro Fernández Rojas if (IS_ERR(reset)) 52738807adeSÁlvaro Fernández Rojas return PTR_ERR(reset); 52838807adeSÁlvaro Fernández Rojas 5291a9e7619SYang Yingliang host = spi_alloc_host(dev, sizeof(*bs)); 5301a9e7619SYang Yingliang if (!host) { 531b42dfed8SFlorian Fainelli dev_err(dev, "out of memory\n"); 532acf4fc6fSJingoo Han return -ENOMEM; 533b42dfed8SFlorian Fainelli } 534b42dfed8SFlorian Fainelli 5351a9e7619SYang Yingliang bs = spi_controller_get_devdata(host); 536aa0fe826SAxel Lin init_completion(&bs->done); 537b42dfed8SFlorian Fainelli 5381a9e7619SYang Yingliang platform_set_drvdata(pdev, host); 539b42dfed8SFlorian Fainelli bs->pdev = pdev; 540b42dfed8SFlorian Fainelli 541a008ae9fSYang Yingliang bs->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &r); 542b66c7730SJonas Gorski if (IS_ERR(bs->regs)) { 543b66c7730SJonas Gorski ret = PTR_ERR(bs->regs); 544b42dfed8SFlorian Fainelli goto out_err; 545b42dfed8SFlorian Fainelli } 546b42dfed8SFlorian Fainelli 547b42dfed8SFlorian Fainelli bs->irq = irq; 548b42dfed8SFlorian Fainelli bs->clk = clk; 54944d8fb30SJonas Gorski bs->reg_offsets = bcm63xx_spireg; 55044d8fb30SJonas Gorski bs->fifo_size = bs->reg_offsets[SPI_MSG_DATA_SIZE]; 551b42dfed8SFlorian Fainelli 552b42dfed8SFlorian Fainelli ret = devm_request_irq(&pdev->dev, irq, bcm63xx_spi_interrupt, 0, 5531a9e7619SYang Yingliang pdev->name, host); 554b42dfed8SFlorian Fainelli if (ret) { 555b42dfed8SFlorian Fainelli dev_err(dev, "unable to request irq\n"); 556b42dfed8SFlorian Fainelli goto out_err; 557b42dfed8SFlorian Fainelli } 558b42dfed8SFlorian Fainelli 5591a9e7619SYang Yingliang host->dev.of_node = dev->of_node; 5601a9e7619SYang Yingliang host->bus_num = bus_num; 5611a9e7619SYang Yingliang host->num_chipselect = num_cs; 5621a9e7619SYang Yingliang host->transfer_one_message = bcm63xx_spi_transfer_one; 5631a9e7619SYang Yingliang host->mode_bits = MODEBITS; 5641a9e7619SYang Yingliang host->bits_per_word_mask = SPI_BPW_MASK(8); 5651a9e7619SYang Yingliang host->max_transfer_size = bcm63xx_spi_max_length; 5661a9e7619SYang Yingliang host->max_message_size = bcm63xx_spi_max_length; 5671a9e7619SYang Yingliang host->auto_runtime_pm = true; 56844d8fb30SJonas Gorski bs->msg_type_shift = bs->reg_offsets[SPI_MSG_TYPE_SHIFT]; 56944d8fb30SJonas Gorski bs->msg_ctl_width = bs->reg_offsets[SPI_MSG_CTL_WIDTH]; 57044d8fb30SJonas Gorski bs->tx_io = (u8 *)(bs->regs + bs->reg_offsets[SPI_MSG_DATA]); 57144d8fb30SJonas Gorski bs->rx_io = (const u8 *)(bs->regs + bs->reg_offsets[SPI_RX_DATA]); 5725a670445SFlorian Fainelli 573b42dfed8SFlorian Fainelli /* Initialize hardware */ 574ea01e8a4SJonas Gorski ret = clk_prepare_enable(bs->clk); 575ea01e8a4SJonas Gorski if (ret) 576ea01e8a4SJonas Gorski goto out_err; 577ea01e8a4SJonas Gorski 57838807adeSÁlvaro Fernández Rojas ret = reset_control_reset(reset); 57938807adeSÁlvaro Fernández Rojas if (ret) { 58038807adeSÁlvaro Fernández Rojas dev_err(dev, "unable to reset device: %d\n", ret); 58138807adeSÁlvaro Fernández Rojas goto out_clk_disable; 58238807adeSÁlvaro Fernández Rojas } 58338807adeSÁlvaro Fernández Rojas 584b42dfed8SFlorian Fainelli bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); 585b42dfed8SFlorian Fainelli 5862d13f2ffSÁlvaro Fernández Rojas pm_runtime_enable(&pdev->dev); 5872d13f2ffSÁlvaro Fernández Rojas 588b42dfed8SFlorian Fainelli /* register and we are done */ 5891a9e7619SYang Yingliang ret = devm_spi_register_controller(dev, host); 590b42dfed8SFlorian Fainelli if (ret) { 591b42dfed8SFlorian Fainelli dev_err(dev, "spi register failed\n"); 5922d13f2ffSÁlvaro Fernández Rojas goto out_pm_disable; 593b42dfed8SFlorian Fainelli } 594b42dfed8SFlorian Fainelli 5950ba2cf70SArnd Bergmann dev_info(dev, "at %pr (irq %d, FIFOs size %d)\n", 5960ba2cf70SArnd Bergmann r, irq, bs->fifo_size); 597b42dfed8SFlorian Fainelli 598b42dfed8SFlorian Fainelli return 0; 599b42dfed8SFlorian Fainelli 6002d13f2ffSÁlvaro Fernández Rojas out_pm_disable: 6012d13f2ffSÁlvaro Fernández Rojas pm_runtime_disable(&pdev->dev); 602b42dfed8SFlorian Fainelli out_clk_disable: 6034fbb82a7SJonas Gorski clk_disable_unprepare(clk); 604b42dfed8SFlorian Fainelli out_err: 6051a9e7619SYang Yingliang spi_controller_put(host); 606b42dfed8SFlorian Fainelli return ret; 607b42dfed8SFlorian Fainelli } 608b42dfed8SFlorian Fainelli 6098c26432eSUwe Kleine-König static void bcm63xx_spi_remove(struct platform_device *pdev) 610b42dfed8SFlorian Fainelli { 6111a9e7619SYang Yingliang struct spi_controller *host = platform_get_drvdata(pdev); 6121a9e7619SYang Yingliang struct bcm63xx_spi *bs = spi_controller_get_devdata(host); 613b42dfed8SFlorian Fainelli 614b42dfed8SFlorian Fainelli /* reset spi block */ 615b42dfed8SFlorian Fainelli bcm_spi_writeb(bs, 0, SPI_INT_MASK); 616b42dfed8SFlorian Fainelli 617b42dfed8SFlorian Fainelli /* HW shutdown */ 6184fbb82a7SJonas Gorski clk_disable_unprepare(bs->clk); 619b42dfed8SFlorian Fainelli } 620b42dfed8SFlorian Fainelli 621b42dfed8SFlorian Fainelli static int bcm63xx_spi_suspend(struct device *dev) 622b42dfed8SFlorian Fainelli { 6231a9e7619SYang Yingliang struct spi_controller *host = dev_get_drvdata(dev); 6241a9e7619SYang Yingliang struct bcm63xx_spi *bs = spi_controller_get_devdata(host); 625b42dfed8SFlorian Fainelli 6261a9e7619SYang Yingliang spi_controller_suspend(host); 62796519957SFlorian Fainelli 6284fbb82a7SJonas Gorski clk_disable_unprepare(bs->clk); 629b42dfed8SFlorian Fainelli 630b42dfed8SFlorian Fainelli return 0; 631b42dfed8SFlorian Fainelli } 632b42dfed8SFlorian Fainelli 633b42dfed8SFlorian Fainelli static int bcm63xx_spi_resume(struct device *dev) 634b42dfed8SFlorian Fainelli { 6351a9e7619SYang Yingliang struct spi_controller *host = dev_get_drvdata(dev); 6361a9e7619SYang Yingliang struct bcm63xx_spi *bs = spi_controller_get_devdata(host); 637ea01e8a4SJonas Gorski int ret; 638b42dfed8SFlorian Fainelli 639ea01e8a4SJonas Gorski ret = clk_prepare_enable(bs->clk); 640ea01e8a4SJonas Gorski if (ret) 641ea01e8a4SJonas Gorski return ret; 642b42dfed8SFlorian Fainelli 6431a9e7619SYang Yingliang spi_controller_resume(host); 64496519957SFlorian Fainelli 645b42dfed8SFlorian Fainelli return 0; 646b42dfed8SFlorian Fainelli } 647b42dfed8SFlorian Fainelli 648cc5f6fa4SDhruva Gole static DEFINE_SIMPLE_DEV_PM_OPS(bcm63xx_spi_pm_ops, bcm63xx_spi_suspend, bcm63xx_spi_resume); 649b42dfed8SFlorian Fainelli 650b42dfed8SFlorian Fainelli static struct platform_driver bcm63xx_spi_driver = { 651b42dfed8SFlorian Fainelli .driver = { 652b42dfed8SFlorian Fainelli .name = "bcm63xx-spi", 6531bae2028SJonas Gorski .pm = &bcm63xx_spi_pm_ops, 654c29f0889SJonas Gorski .of_match_table = bcm63xx_spi_of_match, 655b42dfed8SFlorian Fainelli }, 65644d8fb30SJonas Gorski .id_table = bcm63xx_spi_dev_match, 657b42dfed8SFlorian Fainelli .probe = bcm63xx_spi_probe, 6588c26432eSUwe Kleine-König .remove_new = bcm63xx_spi_remove, 659b42dfed8SFlorian Fainelli }; 660b42dfed8SFlorian Fainelli 661b42dfed8SFlorian Fainelli module_platform_driver(bcm63xx_spi_driver); 662b42dfed8SFlorian Fainelli 663b42dfed8SFlorian Fainelli MODULE_ALIAS("platform:bcm63xx_spi"); 664b42dfed8SFlorian Fainelli MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); 665b42dfed8SFlorian Fainelli MODULE_AUTHOR("Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>"); 666b42dfed8SFlorian Fainelli MODULE_DESCRIPTION("Broadcom BCM63xx SPI Controller driver"); 667b42dfed8SFlorian Fainelli MODULE_LICENSE("GPL"); 668