1b42dfed8SFlorian Fainelli /* 2b42dfed8SFlorian Fainelli * Broadcom BCM63xx SPI controller support 3b42dfed8SFlorian Fainelli * 4cde4384eSFlorian Fainelli * Copyright (C) 2009-2012 Florian Fainelli <florian@openwrt.org> 5b42dfed8SFlorian Fainelli * Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com> 6b42dfed8SFlorian Fainelli * 7b42dfed8SFlorian Fainelli * This program is free software; you can redistribute it and/or 8b42dfed8SFlorian Fainelli * modify it under the terms of the GNU General Public License 9b42dfed8SFlorian Fainelli * as published by the Free Software Foundation; either version 2 10b42dfed8SFlorian Fainelli * of the License, or (at your option) any later version. 11b42dfed8SFlorian Fainelli * 12b42dfed8SFlorian Fainelli * This program is distributed in the hope that it will be useful, 13b42dfed8SFlorian Fainelli * but WITHOUT ANY WARRANTY; without even the implied warranty of 14b42dfed8SFlorian Fainelli * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15b42dfed8SFlorian Fainelli * GNU General Public License for more details. 16b42dfed8SFlorian Fainelli */ 17b42dfed8SFlorian Fainelli 18b42dfed8SFlorian Fainelli #include <linux/kernel.h> 19b42dfed8SFlorian Fainelli #include <linux/clk.h> 20b42dfed8SFlorian Fainelli #include <linux/io.h> 21b42dfed8SFlorian Fainelli #include <linux/module.h> 22b42dfed8SFlorian Fainelli #include <linux/platform_device.h> 23b42dfed8SFlorian Fainelli #include <linux/delay.h> 24b42dfed8SFlorian Fainelli #include <linux/interrupt.h> 25b42dfed8SFlorian Fainelli #include <linux/spi/spi.h> 26b42dfed8SFlorian Fainelli #include <linux/completion.h> 27b42dfed8SFlorian Fainelli #include <linux/err.h> 28cde4384eSFlorian Fainelli #include <linux/pm_runtime.h> 29c29f0889SJonas Gorski #include <linux/of.h> 30b42dfed8SFlorian Fainelli 3144d8fb30SJonas Gorski /* BCM 6338/6348 SPI core */ 3244d8fb30SJonas Gorski #define SPI_6348_RSET_SIZE 64 3344d8fb30SJonas Gorski #define SPI_6348_CMD 0x00 /* 16-bits register */ 3444d8fb30SJonas Gorski #define SPI_6348_INT_STATUS 0x02 3544d8fb30SJonas Gorski #define SPI_6348_INT_MASK_ST 0x03 3644d8fb30SJonas Gorski #define SPI_6348_INT_MASK 0x04 3744d8fb30SJonas Gorski #define SPI_6348_ST 0x05 3844d8fb30SJonas Gorski #define SPI_6348_CLK_CFG 0x06 3944d8fb30SJonas Gorski #define SPI_6348_FILL_BYTE 0x07 4044d8fb30SJonas Gorski #define SPI_6348_MSG_TAIL 0x09 4144d8fb30SJonas Gorski #define SPI_6348_RX_TAIL 0x0b 4244d8fb30SJonas Gorski #define SPI_6348_MSG_CTL 0x40 /* 8-bits register */ 4344d8fb30SJonas Gorski #define SPI_6348_MSG_CTL_WIDTH 8 4444d8fb30SJonas Gorski #define SPI_6348_MSG_DATA 0x41 4544d8fb30SJonas Gorski #define SPI_6348_MSG_DATA_SIZE 0x3f 4644d8fb30SJonas Gorski #define SPI_6348_RX_DATA 0x80 4744d8fb30SJonas Gorski #define SPI_6348_RX_DATA_SIZE 0x3f 4844d8fb30SJonas Gorski 4944d8fb30SJonas Gorski /* BCM 3368/6358/6262/6368 SPI core */ 5044d8fb30SJonas Gorski #define SPI_6358_RSET_SIZE 1804 5144d8fb30SJonas Gorski #define SPI_6358_MSG_CTL 0x00 /* 16-bits register */ 5244d8fb30SJonas Gorski #define SPI_6358_MSG_CTL_WIDTH 16 5344d8fb30SJonas Gorski #define SPI_6358_MSG_DATA 0x02 5444d8fb30SJonas Gorski #define SPI_6358_MSG_DATA_SIZE 0x21e 5544d8fb30SJonas Gorski #define SPI_6358_RX_DATA 0x400 5644d8fb30SJonas Gorski #define SPI_6358_RX_DATA_SIZE 0x220 5744d8fb30SJonas Gorski #define SPI_6358_CMD 0x700 /* 16-bits register */ 5844d8fb30SJonas Gorski #define SPI_6358_INT_STATUS 0x702 5944d8fb30SJonas Gorski #define SPI_6358_INT_MASK_ST 0x703 6044d8fb30SJonas Gorski #define SPI_6358_INT_MASK 0x704 6144d8fb30SJonas Gorski #define SPI_6358_ST 0x705 6244d8fb30SJonas Gorski #define SPI_6358_CLK_CFG 0x706 6344d8fb30SJonas Gorski #define SPI_6358_FILL_BYTE 0x707 6444d8fb30SJonas Gorski #define SPI_6358_MSG_TAIL 0x709 6544d8fb30SJonas Gorski #define SPI_6358_RX_TAIL 0x70B 6644d8fb30SJonas Gorski 6744d8fb30SJonas Gorski /* Shared SPI definitions */ 6844d8fb30SJonas Gorski 6944d8fb30SJonas Gorski /* Message configuration */ 7044d8fb30SJonas Gorski #define SPI_FD_RW 0x00 7144d8fb30SJonas Gorski #define SPI_HD_W 0x01 7244d8fb30SJonas Gorski #define SPI_HD_R 0x02 7344d8fb30SJonas Gorski #define SPI_BYTE_CNT_SHIFT 0 7444d8fb30SJonas Gorski #define SPI_6348_MSG_TYPE_SHIFT 6 7544d8fb30SJonas Gorski #define SPI_6358_MSG_TYPE_SHIFT 14 7644d8fb30SJonas Gorski 7744d8fb30SJonas Gorski /* Command */ 7844d8fb30SJonas Gorski #define SPI_CMD_NOOP 0x00 7944d8fb30SJonas Gorski #define SPI_CMD_SOFT_RESET 0x01 8044d8fb30SJonas Gorski #define SPI_CMD_HARD_RESET 0x02 8144d8fb30SJonas Gorski #define SPI_CMD_START_IMMEDIATE 0x03 8244d8fb30SJonas Gorski #define SPI_CMD_COMMAND_SHIFT 0 8344d8fb30SJonas Gorski #define SPI_CMD_COMMAND_MASK 0x000f 8444d8fb30SJonas Gorski #define SPI_CMD_DEVICE_ID_SHIFT 4 8544d8fb30SJonas Gorski #define SPI_CMD_PREPEND_BYTE_CNT_SHIFT 8 8644d8fb30SJonas Gorski #define SPI_CMD_ONE_BYTE_SHIFT 11 8744d8fb30SJonas Gorski #define SPI_CMD_ONE_WIRE_SHIFT 12 8844d8fb30SJonas Gorski #define SPI_DEV_ID_0 0 8944d8fb30SJonas Gorski #define SPI_DEV_ID_1 1 9044d8fb30SJonas Gorski #define SPI_DEV_ID_2 2 9144d8fb30SJonas Gorski #define SPI_DEV_ID_3 3 9244d8fb30SJonas Gorski 9344d8fb30SJonas Gorski /* Interrupt mask */ 9444d8fb30SJonas Gorski #define SPI_INTR_CMD_DONE 0x01 9544d8fb30SJonas Gorski #define SPI_INTR_RX_OVERFLOW 0x02 9644d8fb30SJonas Gorski #define SPI_INTR_TX_UNDERFLOW 0x04 9744d8fb30SJonas Gorski #define SPI_INTR_TX_OVERFLOW 0x08 9844d8fb30SJonas Gorski #define SPI_INTR_RX_UNDERFLOW 0x10 9944d8fb30SJonas Gorski #define SPI_INTR_CLEAR_ALL 0x1f 10044d8fb30SJonas Gorski 10144d8fb30SJonas Gorski /* Status */ 10244d8fb30SJonas Gorski #define SPI_RX_EMPTY 0x02 10344d8fb30SJonas Gorski #define SPI_CMD_BUSY 0x04 10444d8fb30SJonas Gorski #define SPI_SERIAL_BUSY 0x08 10544d8fb30SJonas Gorski 10644d8fb30SJonas Gorski /* Clock configuration */ 10744d8fb30SJonas Gorski #define SPI_CLK_20MHZ 0x00 10844d8fb30SJonas Gorski #define SPI_CLK_0_391MHZ 0x01 10944d8fb30SJonas Gorski #define SPI_CLK_0_781MHZ 0x02 /* default */ 11044d8fb30SJonas Gorski #define SPI_CLK_1_563MHZ 0x03 11144d8fb30SJonas Gorski #define SPI_CLK_3_125MHZ 0x04 11244d8fb30SJonas Gorski #define SPI_CLK_6_250MHZ 0x05 11344d8fb30SJonas Gorski #define SPI_CLK_12_50MHZ 0x06 11444d8fb30SJonas Gorski #define SPI_CLK_MASK 0x07 11544d8fb30SJonas Gorski #define SPI_SSOFFTIME_MASK 0x38 11644d8fb30SJonas Gorski #define SPI_SSOFFTIME_SHIFT 3 11744d8fb30SJonas Gorski #define SPI_BYTE_SWAP 0x80 11844d8fb30SJonas Gorski 11944d8fb30SJonas Gorski enum bcm63xx_regs_spi { 12044d8fb30SJonas Gorski SPI_CMD, 12144d8fb30SJonas Gorski SPI_INT_STATUS, 12244d8fb30SJonas Gorski SPI_INT_MASK_ST, 12344d8fb30SJonas Gorski SPI_INT_MASK, 12444d8fb30SJonas Gorski SPI_ST, 12544d8fb30SJonas Gorski SPI_CLK_CFG, 12644d8fb30SJonas Gorski SPI_FILL_BYTE, 12744d8fb30SJonas Gorski SPI_MSG_TAIL, 12844d8fb30SJonas Gorski SPI_RX_TAIL, 12944d8fb30SJonas Gorski SPI_MSG_CTL, 13044d8fb30SJonas Gorski SPI_MSG_DATA, 13144d8fb30SJonas Gorski SPI_RX_DATA, 13244d8fb30SJonas Gorski SPI_MSG_TYPE_SHIFT, 13344d8fb30SJonas Gorski SPI_MSG_CTL_WIDTH, 13444d8fb30SJonas Gorski SPI_MSG_DATA_SIZE, 13544d8fb30SJonas Gorski }; 136b42dfed8SFlorian Fainelli 137b17de076SJonas Gorski #define BCM63XX_SPI_MAX_PREPEND 15 138b17de076SJonas Gorski 13965059997SJonas Gorski #define BCM63XX_SPI_MAX_CS 8 140a45fcea5SJonas Gorski #define BCM63XX_SPI_BUS_NUM 0 14165059997SJonas Gorski 142b42dfed8SFlorian Fainelli struct bcm63xx_spi { 143b42dfed8SFlorian Fainelli struct completion done; 144b42dfed8SFlorian Fainelli 145b42dfed8SFlorian Fainelli void __iomem *regs; 146b42dfed8SFlorian Fainelli int irq; 147b42dfed8SFlorian Fainelli 148b42dfed8SFlorian Fainelli /* Platform data */ 14944d8fb30SJonas Gorski const unsigned long *reg_offsets; 150*b85d65ddSAravind Thokala unsigned int fifo_size; 1515a670445SFlorian Fainelli unsigned int msg_type_shift; 1525a670445SFlorian Fainelli unsigned int msg_ctl_width; 153b42dfed8SFlorian Fainelli 154b42dfed8SFlorian Fainelli /* data iomem */ 155b42dfed8SFlorian Fainelli u8 __iomem *tx_io; 156b42dfed8SFlorian Fainelli const u8 __iomem *rx_io; 157b42dfed8SFlorian Fainelli 158b42dfed8SFlorian Fainelli struct clk *clk; 159b42dfed8SFlorian Fainelli struct platform_device *pdev; 160b42dfed8SFlorian Fainelli }; 161b42dfed8SFlorian Fainelli 162b42dfed8SFlorian Fainelli static inline u8 bcm_spi_readb(struct bcm63xx_spi *bs, 163b42dfed8SFlorian Fainelli unsigned int offset) 164b42dfed8SFlorian Fainelli { 16544d8fb30SJonas Gorski return readb(bs->regs + bs->reg_offsets[offset]); 166b42dfed8SFlorian Fainelli } 167b42dfed8SFlorian Fainelli 168b42dfed8SFlorian Fainelli static inline u16 bcm_spi_readw(struct bcm63xx_spi *bs, 169b42dfed8SFlorian Fainelli unsigned int offset) 170b42dfed8SFlorian Fainelli { 171682b5280SJonas Gorski #ifdef CONFIG_CPU_BIG_ENDIAN 17244d8fb30SJonas Gorski return ioread16be(bs->regs + bs->reg_offsets[offset]); 173158fcc4eSJonas Gorski #else 17444d8fb30SJonas Gorski return readw(bs->regs + bs->reg_offsets[offset]); 175158fcc4eSJonas Gorski #endif 176b42dfed8SFlorian Fainelli } 177b42dfed8SFlorian Fainelli 178b42dfed8SFlorian Fainelli static inline void bcm_spi_writeb(struct bcm63xx_spi *bs, 179b42dfed8SFlorian Fainelli u8 value, unsigned int offset) 180b42dfed8SFlorian Fainelli { 18144d8fb30SJonas Gorski writeb(value, bs->regs + bs->reg_offsets[offset]); 182b42dfed8SFlorian Fainelli } 183b42dfed8SFlorian Fainelli 184b42dfed8SFlorian Fainelli static inline void bcm_spi_writew(struct bcm63xx_spi *bs, 185b42dfed8SFlorian Fainelli u16 value, unsigned int offset) 186b42dfed8SFlorian Fainelli { 187682b5280SJonas Gorski #ifdef CONFIG_CPU_BIG_ENDIAN 18844d8fb30SJonas Gorski iowrite16be(value, bs->regs + bs->reg_offsets[offset]); 189158fcc4eSJonas Gorski #else 19044d8fb30SJonas Gorski writew(value, bs->regs + bs->reg_offsets[offset]); 191158fcc4eSJonas Gorski #endif 192b42dfed8SFlorian Fainelli } 193b42dfed8SFlorian Fainelli 194*b85d65ddSAravind Thokala static const unsigned int bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = { 195b42dfed8SFlorian Fainelli { 20000000, SPI_CLK_20MHZ }, 196b42dfed8SFlorian Fainelli { 12500000, SPI_CLK_12_50MHZ }, 197b42dfed8SFlorian Fainelli { 6250000, SPI_CLK_6_250MHZ }, 198b42dfed8SFlorian Fainelli { 3125000, SPI_CLK_3_125MHZ }, 199b42dfed8SFlorian Fainelli { 1563000, SPI_CLK_1_563MHZ }, 200b42dfed8SFlorian Fainelli { 781000, SPI_CLK_0_781MHZ }, 201b42dfed8SFlorian Fainelli { 391000, SPI_CLK_0_391MHZ } 202b42dfed8SFlorian Fainelli }; 203b42dfed8SFlorian Fainelli 204cde4384eSFlorian Fainelli static void bcm63xx_spi_setup_transfer(struct spi_device *spi, 205cde4384eSFlorian Fainelli struct spi_transfer *t) 206cde4384eSFlorian Fainelli { 207cde4384eSFlorian Fainelli struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); 208cde4384eSFlorian Fainelli u8 clk_cfg, reg; 209cde4384eSFlorian Fainelli int i; 210cde4384eSFlorian Fainelli 2114dde99bdSGeert Uytterhoeven /* Default to lowest clock configuration */ 2124dde99bdSGeert Uytterhoeven clk_cfg = SPI_CLK_0_391MHZ; 2134dde99bdSGeert Uytterhoeven 214b42dfed8SFlorian Fainelli /* Find the closest clock configuration */ 215b42dfed8SFlorian Fainelli for (i = 0; i < SPI_CLK_MASK; i++) { 21668792e2aSJonas Gorski if (t->speed_hz >= bcm63xx_spi_freq_table[i][0]) { 217b42dfed8SFlorian Fainelli clk_cfg = bcm63xx_spi_freq_table[i][1]; 218b42dfed8SFlorian Fainelli break; 219b42dfed8SFlorian Fainelli } 220b42dfed8SFlorian Fainelli } 221b42dfed8SFlorian Fainelli 222b42dfed8SFlorian Fainelli /* clear existing clock configuration bits of the register */ 223b42dfed8SFlorian Fainelli reg = bcm_spi_readb(bs, SPI_CLK_CFG); 224b42dfed8SFlorian Fainelli reg &= ~SPI_CLK_MASK; 225b42dfed8SFlorian Fainelli reg |= clk_cfg; 226b42dfed8SFlorian Fainelli 227b42dfed8SFlorian Fainelli bcm_spi_writeb(bs, reg, SPI_CLK_CFG); 228b42dfed8SFlorian Fainelli dev_dbg(&spi->dev, "Setting clock register to %02x (hz %d)\n", 22968792e2aSJonas Gorski clk_cfg, t->speed_hz); 230b42dfed8SFlorian Fainelli } 231b42dfed8SFlorian Fainelli 232b42dfed8SFlorian Fainelli /* the spi->mode bits understood by this driver: */ 233b42dfed8SFlorian Fainelli #define MODEBITS (SPI_CPOL | SPI_CPHA) 234b42dfed8SFlorian Fainelli 235b17de076SJonas Gorski static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first, 236b17de076SJonas Gorski unsigned int num_transfers) 237b42dfed8SFlorian Fainelli { 238b42dfed8SFlorian Fainelli struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); 239b42dfed8SFlorian Fainelli u16 msg_ctl; 240b42dfed8SFlorian Fainelli u16 cmd; 241b17de076SJonas Gorski unsigned int i, timeout = 0, prepend_len = 0, len = 0; 242b17de076SJonas Gorski struct spi_transfer *t = first; 243b17de076SJonas Gorski bool do_rx = false; 244b17de076SJonas Gorski bool do_tx = false; 245b42dfed8SFlorian Fainelli 246cde4384eSFlorian Fainelli /* Disable the CMD_DONE interrupt */ 247cde4384eSFlorian Fainelli bcm_spi_writeb(bs, 0, SPI_INT_MASK); 248cde4384eSFlorian Fainelli 249b42dfed8SFlorian Fainelli dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", 250b42dfed8SFlorian Fainelli t->tx_buf, t->rx_buf, t->len); 251b42dfed8SFlorian Fainelli 252b17de076SJonas Gorski if (num_transfers > 1 && t->tx_buf && t->len <= BCM63XX_SPI_MAX_PREPEND) 253b17de076SJonas Gorski prepend_len = t->len; 254b17de076SJonas Gorski 255b17de076SJonas Gorski /* prepare the buffer */ 256b17de076SJonas Gorski for (i = 0; i < num_transfers; i++) { 257b17de076SJonas Gorski if (t->tx_buf) { 258b17de076SJonas Gorski do_tx = true; 259b17de076SJonas Gorski memcpy_toio(bs->tx_io + len, t->tx_buf, t->len); 260b17de076SJonas Gorski 261b17de076SJonas Gorski /* don't prepend more than one tx */ 262b17de076SJonas Gorski if (t != first) 263b17de076SJonas Gorski prepend_len = 0; 264b17de076SJonas Gorski } 265b17de076SJonas Gorski 266b17de076SJonas Gorski if (t->rx_buf) { 267b17de076SJonas Gorski do_rx = true; 268b17de076SJonas Gorski /* prepend is half-duplex write only */ 269b17de076SJonas Gorski if (t == first) 270b17de076SJonas Gorski prepend_len = 0; 271b17de076SJonas Gorski } 272b17de076SJonas Gorski 273b17de076SJonas Gorski len += t->len; 274b17de076SJonas Gorski 275b17de076SJonas Gorski t = list_entry(t->transfer_list.next, struct spi_transfer, 276b17de076SJonas Gorski transfer_list); 277b17de076SJonas Gorski } 278b17de076SJonas Gorski 279aa0fe826SAxel Lin reinit_completion(&bs->done); 280b42dfed8SFlorian Fainelli 281b42dfed8SFlorian Fainelli /* Fill in the Message control register */ 282b17de076SJonas Gorski msg_ctl = (len << SPI_BYTE_CNT_SHIFT); 283b42dfed8SFlorian Fainelli 284b17de076SJonas Gorski if (do_rx && do_tx && prepend_len == 0) 2855a670445SFlorian Fainelli msg_ctl |= (SPI_FD_RW << bs->msg_type_shift); 286b17de076SJonas Gorski else if (do_rx) 2875a670445SFlorian Fainelli msg_ctl |= (SPI_HD_R << bs->msg_type_shift); 288b17de076SJonas Gorski else if (do_tx) 2895a670445SFlorian Fainelli msg_ctl |= (SPI_HD_W << bs->msg_type_shift); 290b42dfed8SFlorian Fainelli 2915a670445SFlorian Fainelli switch (bs->msg_ctl_width) { 2925a670445SFlorian Fainelli case 8: 2935a670445SFlorian Fainelli bcm_spi_writeb(bs, msg_ctl, SPI_MSG_CTL); 2945a670445SFlorian Fainelli break; 2955a670445SFlorian Fainelli case 16: 296b42dfed8SFlorian Fainelli bcm_spi_writew(bs, msg_ctl, SPI_MSG_CTL); 2975a670445SFlorian Fainelli break; 2985a670445SFlorian Fainelli } 299b42dfed8SFlorian Fainelli 300b42dfed8SFlorian Fainelli /* Issue the transfer */ 301b42dfed8SFlorian Fainelli cmd = SPI_CMD_START_IMMEDIATE; 302b17de076SJonas Gorski cmd |= (prepend_len << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); 303b42dfed8SFlorian Fainelli cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT); 304b42dfed8SFlorian Fainelli bcm_spi_writew(bs, cmd, SPI_CMD); 305b42dfed8SFlorian Fainelli 306cde4384eSFlorian Fainelli /* Enable the CMD_DONE interrupt */ 307cde4384eSFlorian Fainelli bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK); 308b42dfed8SFlorian Fainelli 309c0fde3baSJonas Gorski timeout = wait_for_completion_timeout(&bs->done, HZ); 310c0fde3baSJonas Gorski if (!timeout) 311c0fde3baSJonas Gorski return -ETIMEDOUT; 312c0fde3baSJonas Gorski 31320e9e78fSJonas Gorski if (!do_rx) 314b17de076SJonas Gorski return 0; 315b17de076SJonas Gorski 316b17de076SJonas Gorski len = 0; 317b17de076SJonas Gorski t = first; 318c0fde3baSJonas Gorski /* Read out all the data */ 319b17de076SJonas Gorski for (i = 0; i < num_transfers; i++) { 320b17de076SJonas Gorski if (t->rx_buf) 321b17de076SJonas Gorski memcpy_fromio(t->rx_buf, bs->rx_io + len, t->len); 322b17de076SJonas Gorski 323b17de076SJonas Gorski if (t != first || prepend_len == 0) 324b17de076SJonas Gorski len += t->len; 325b17de076SJonas Gorski 326b17de076SJonas Gorski t = list_entry(t->transfer_list.next, struct spi_transfer, 327b17de076SJonas Gorski transfer_list); 328b17de076SJonas Gorski } 329c0fde3baSJonas Gorski 330c0fde3baSJonas Gorski return 0; 331b42dfed8SFlorian Fainelli } 332b42dfed8SFlorian Fainelli 333cde4384eSFlorian Fainelli static int bcm63xx_spi_transfer_one(struct spi_master *master, 334cde4384eSFlorian Fainelli struct spi_message *m) 335cde4384eSFlorian Fainelli { 336cde4384eSFlorian Fainelli struct bcm63xx_spi *bs = spi_master_get_devdata(master); 337b17de076SJonas Gorski struct spi_transfer *t, *first = NULL; 338cde4384eSFlorian Fainelli struct spi_device *spi = m->spi; 339cde4384eSFlorian Fainelli int status = 0; 340b17de076SJonas Gorski unsigned int n_transfers = 0, total_len = 0; 341b17de076SJonas Gorski bool can_use_prepend = false; 342cde4384eSFlorian Fainelli 343b17de076SJonas Gorski /* 344b17de076SJonas Gorski * This SPI controller does not support keeping CS active after a 345b17de076SJonas Gorski * transfer. 346b17de076SJonas Gorski * Work around this by merging as many transfers we can into one big 347b17de076SJonas Gorski * full-duplex transfers. 348b17de076SJonas Gorski */ 349cde4384eSFlorian Fainelli list_for_each_entry(t, &m->transfers, transfer_list) { 350b17de076SJonas Gorski if (!first) 351b17de076SJonas Gorski first = t; 352b17de076SJonas Gorski 353b17de076SJonas Gorski n_transfers++; 354b17de076SJonas Gorski total_len += t->len; 355b17de076SJonas Gorski 356b17de076SJonas Gorski if (n_transfers == 2 && !first->rx_buf && !t->tx_buf && 357b17de076SJonas Gorski first->len <= BCM63XX_SPI_MAX_PREPEND) 358b17de076SJonas Gorski can_use_prepend = true; 359b17de076SJonas Gorski else if (can_use_prepend && t->tx_buf) 360b17de076SJonas Gorski can_use_prepend = false; 361b17de076SJonas Gorski 362c0fde3baSJonas Gorski /* we can only transfer one fifo worth of data */ 363b17de076SJonas Gorski if ((can_use_prepend && 364b17de076SJonas Gorski total_len > (bs->fifo_size + BCM63XX_SPI_MAX_PREPEND)) || 365b17de076SJonas Gorski (!can_use_prepend && total_len > bs->fifo_size)) { 366c0fde3baSJonas Gorski dev_err(&spi->dev, "unable to do transfers larger than FIFO size (%i > %i)\n", 367b17de076SJonas Gorski total_len, bs->fifo_size); 368b17de076SJonas Gorski status = -EINVAL; 369b17de076SJonas Gorski goto exit; 370b17de076SJonas Gorski } 371b17de076SJonas Gorski 372b17de076SJonas Gorski /* all combined transfers have to have the same speed */ 373b17de076SJonas Gorski if (t->speed_hz != first->speed_hz) { 374b17de076SJonas Gorski dev_err(&spi->dev, "unable to change speed between transfers\n"); 375c0fde3baSJonas Gorski status = -EINVAL; 376cde4384eSFlorian Fainelli goto exit; 377cde4384eSFlorian Fainelli } 378cde4384eSFlorian Fainelli 379c0fde3baSJonas Gorski /* CS will be deasserted directly after transfer */ 380c0fde3baSJonas Gorski if (t->delay_usecs) { 381c0fde3baSJonas Gorski dev_err(&spi->dev, "unable to keep CS asserted after transfer\n"); 382c0fde3baSJonas Gorski status = -EINVAL; 383c0fde3baSJonas Gorski goto exit; 384cde4384eSFlorian Fainelli } 385cde4384eSFlorian Fainelli 386b17de076SJonas Gorski if (t->cs_change || 387b17de076SJonas Gorski list_is_last(&t->transfer_list, &m->transfers)) { 388c0fde3baSJonas Gorski /* configure adapter for a new transfer */ 389b17de076SJonas Gorski bcm63xx_spi_setup_transfer(spi, first); 390c0fde3baSJonas Gorski 391c0fde3baSJonas Gorski /* send the data */ 392b17de076SJonas Gorski status = bcm63xx_txrx_bufs(spi, first, n_transfers); 393c0fde3baSJonas Gorski if (status) 394c0fde3baSJonas Gorski goto exit; 395c0fde3baSJonas Gorski 396b17de076SJonas Gorski m->actual_length += total_len; 397b17de076SJonas Gorski 398b17de076SJonas Gorski first = NULL; 399b17de076SJonas Gorski n_transfers = 0; 400b17de076SJonas Gorski total_len = 0; 401b17de076SJonas Gorski can_use_prepend = false; 402b17de076SJonas Gorski } 403cde4384eSFlorian Fainelli } 404cde4384eSFlorian Fainelli exit: 405cde4384eSFlorian Fainelli m->status = status; 406cde4384eSFlorian Fainelli spi_finalize_current_message(master); 407cde4384eSFlorian Fainelli 408cde4384eSFlorian Fainelli return 0; 409b42dfed8SFlorian Fainelli } 410b42dfed8SFlorian Fainelli 411b42dfed8SFlorian Fainelli /* This driver supports single master mode only. Hence 412b42dfed8SFlorian Fainelli * CMD_DONE is the only interrupt we care about 413b42dfed8SFlorian Fainelli */ 414b42dfed8SFlorian Fainelli static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id) 415b42dfed8SFlorian Fainelli { 416b42dfed8SFlorian Fainelli struct spi_master *master = (struct spi_master *)dev_id; 417b42dfed8SFlorian Fainelli struct bcm63xx_spi *bs = spi_master_get_devdata(master); 418b42dfed8SFlorian Fainelli u8 intr; 419b42dfed8SFlorian Fainelli 420b42dfed8SFlorian Fainelli /* Read interupts and clear them immediately */ 421b42dfed8SFlorian Fainelli intr = bcm_spi_readb(bs, SPI_INT_STATUS); 422b42dfed8SFlorian Fainelli bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); 423b42dfed8SFlorian Fainelli bcm_spi_writeb(bs, 0, SPI_INT_MASK); 424b42dfed8SFlorian Fainelli 425cde4384eSFlorian Fainelli /* A transfer completed */ 426cde4384eSFlorian Fainelli if (intr & SPI_INTR_CMD_DONE) 427b42dfed8SFlorian Fainelli complete(&bs->done); 428b42dfed8SFlorian Fainelli 429b42dfed8SFlorian Fainelli return IRQ_HANDLED; 430b42dfed8SFlorian Fainelli } 431b42dfed8SFlorian Fainelli 432ccd0657cSJonas Gorski static size_t bcm63xx_spi_max_length(struct spi_device *spi) 4330135c03dSJonas Gorski { 4340135c03dSJonas Gorski struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); 4350135c03dSJonas Gorski 4360135c03dSJonas Gorski return bs->fifo_size; 4370135c03dSJonas Gorski } 4380135c03dSJonas Gorski 43944d8fb30SJonas Gorski static const unsigned long bcm6348_spi_reg_offsets[] = { 44044d8fb30SJonas Gorski [SPI_CMD] = SPI_6348_CMD, 44144d8fb30SJonas Gorski [SPI_INT_STATUS] = SPI_6348_INT_STATUS, 44244d8fb30SJonas Gorski [SPI_INT_MASK_ST] = SPI_6348_INT_MASK_ST, 44344d8fb30SJonas Gorski [SPI_INT_MASK] = SPI_6348_INT_MASK, 44444d8fb30SJonas Gorski [SPI_ST] = SPI_6348_ST, 44544d8fb30SJonas Gorski [SPI_CLK_CFG] = SPI_6348_CLK_CFG, 44644d8fb30SJonas Gorski [SPI_FILL_BYTE] = SPI_6348_FILL_BYTE, 44744d8fb30SJonas Gorski [SPI_MSG_TAIL] = SPI_6348_MSG_TAIL, 44844d8fb30SJonas Gorski [SPI_RX_TAIL] = SPI_6348_RX_TAIL, 44944d8fb30SJonas Gorski [SPI_MSG_CTL] = SPI_6348_MSG_CTL, 45044d8fb30SJonas Gorski [SPI_MSG_DATA] = SPI_6348_MSG_DATA, 45144d8fb30SJonas Gorski [SPI_RX_DATA] = SPI_6348_RX_DATA, 45244d8fb30SJonas Gorski [SPI_MSG_TYPE_SHIFT] = SPI_6348_MSG_TYPE_SHIFT, 45344d8fb30SJonas Gorski [SPI_MSG_CTL_WIDTH] = SPI_6348_MSG_CTL_WIDTH, 45444d8fb30SJonas Gorski [SPI_MSG_DATA_SIZE] = SPI_6348_MSG_DATA_SIZE, 45544d8fb30SJonas Gorski }; 45644d8fb30SJonas Gorski 45744d8fb30SJonas Gorski static const unsigned long bcm6358_spi_reg_offsets[] = { 45844d8fb30SJonas Gorski [SPI_CMD] = SPI_6358_CMD, 45944d8fb30SJonas Gorski [SPI_INT_STATUS] = SPI_6358_INT_STATUS, 46044d8fb30SJonas Gorski [SPI_INT_MASK_ST] = SPI_6358_INT_MASK_ST, 46144d8fb30SJonas Gorski [SPI_INT_MASK] = SPI_6358_INT_MASK, 46244d8fb30SJonas Gorski [SPI_ST] = SPI_6358_ST, 46344d8fb30SJonas Gorski [SPI_CLK_CFG] = SPI_6358_CLK_CFG, 46444d8fb30SJonas Gorski [SPI_FILL_BYTE] = SPI_6358_FILL_BYTE, 46544d8fb30SJonas Gorski [SPI_MSG_TAIL] = SPI_6358_MSG_TAIL, 46644d8fb30SJonas Gorski [SPI_RX_TAIL] = SPI_6358_RX_TAIL, 46744d8fb30SJonas Gorski [SPI_MSG_CTL] = SPI_6358_MSG_CTL, 46844d8fb30SJonas Gorski [SPI_MSG_DATA] = SPI_6358_MSG_DATA, 46944d8fb30SJonas Gorski [SPI_RX_DATA] = SPI_6358_RX_DATA, 47044d8fb30SJonas Gorski [SPI_MSG_TYPE_SHIFT] = SPI_6358_MSG_TYPE_SHIFT, 47144d8fb30SJonas Gorski [SPI_MSG_CTL_WIDTH] = SPI_6358_MSG_CTL_WIDTH, 47244d8fb30SJonas Gorski [SPI_MSG_DATA_SIZE] = SPI_6358_MSG_DATA_SIZE, 47344d8fb30SJonas Gorski }; 47444d8fb30SJonas Gorski 47544d8fb30SJonas Gorski static const struct platform_device_id bcm63xx_spi_dev_match[] = { 47644d8fb30SJonas Gorski { 47744d8fb30SJonas Gorski .name = "bcm6348-spi", 47844d8fb30SJonas Gorski .driver_data = (unsigned long)bcm6348_spi_reg_offsets, 47944d8fb30SJonas Gorski }, 48044d8fb30SJonas Gorski { 48144d8fb30SJonas Gorski .name = "bcm6358-spi", 48244d8fb30SJonas Gorski .driver_data = (unsigned long)bcm6358_spi_reg_offsets, 48344d8fb30SJonas Gorski }, 48444d8fb30SJonas Gorski { 48544d8fb30SJonas Gorski }, 48644d8fb30SJonas Gorski }; 487b42dfed8SFlorian Fainelli 488c29f0889SJonas Gorski static const struct of_device_id bcm63xx_spi_of_match[] = { 489c29f0889SJonas Gorski { .compatible = "brcm,bcm6348-spi", .data = &bcm6348_spi_reg_offsets }, 490c29f0889SJonas Gorski { .compatible = "brcm,bcm6358-spi", .data = &bcm6358_spi_reg_offsets }, 491c29f0889SJonas Gorski { }, 492c29f0889SJonas Gorski }; 493c29f0889SJonas Gorski 494fd4a319bSGrant Likely static int bcm63xx_spi_probe(struct platform_device *pdev) 495b42dfed8SFlorian Fainelli { 496b42dfed8SFlorian Fainelli struct resource *r; 49744d8fb30SJonas Gorski const unsigned long *bcm63xx_spireg; 498b42dfed8SFlorian Fainelli struct device *dev = &pdev->dev; 499c29f0889SJonas Gorski int irq, bus_num; 500b42dfed8SFlorian Fainelli struct spi_master *master; 501b42dfed8SFlorian Fainelli struct clk *clk; 502b42dfed8SFlorian Fainelli struct bcm63xx_spi *bs; 503b42dfed8SFlorian Fainelli int ret; 504c29f0889SJonas Gorski u32 num_cs = BCM63XX_SPI_MAX_CS; 505b42dfed8SFlorian Fainelli 506c29f0889SJonas Gorski if (dev->of_node) { 507c29f0889SJonas Gorski const struct of_device_id *match; 508c29f0889SJonas Gorski 509c29f0889SJonas Gorski match = of_match_node(bcm63xx_spi_of_match, dev->of_node); 510c29f0889SJonas Gorski if (!match) 51144d8fb30SJonas Gorski return -EINVAL; 512c29f0889SJonas Gorski bcm63xx_spireg = match->data; 51344d8fb30SJonas Gorski 514c29f0889SJonas Gorski of_property_read_u32(dev->of_node, "num-cs", &num_cs); 515c29f0889SJonas Gorski if (num_cs > BCM63XX_SPI_MAX_CS) { 516c29f0889SJonas Gorski dev_warn(dev, "unsupported number of cs (%i), reducing to 8\n", 517c29f0889SJonas Gorski num_cs); 518c29f0889SJonas Gorski num_cs = BCM63XX_SPI_MAX_CS; 519c29f0889SJonas Gorski } 520c29f0889SJonas Gorski 521c29f0889SJonas Gorski bus_num = -1; 522c29f0889SJonas Gorski } else if (pdev->id_entry->driver_data) { 523c29f0889SJonas Gorski const struct platform_device_id *match = pdev->id_entry; 524c29f0889SJonas Gorski 525c29f0889SJonas Gorski bcm63xx_spireg = (const unsigned long *)match->driver_data; 526c29f0889SJonas Gorski bus_num = BCM63XX_SPI_BUS_NUM; 527c29f0889SJonas Gorski } else { 528c29f0889SJonas Gorski return -EINVAL; 529c29f0889SJonas Gorski } 53044d8fb30SJonas Gorski 531b42dfed8SFlorian Fainelli irq = platform_get_irq(pdev, 0); 532b42dfed8SFlorian Fainelli if (irq < 0) { 533b42dfed8SFlorian Fainelli dev_err(dev, "no irq\n"); 534acf4fc6fSJingoo Han return -ENXIO; 535b42dfed8SFlorian Fainelli } 536b42dfed8SFlorian Fainelli 537acf4fc6fSJingoo Han clk = devm_clk_get(dev, "spi"); 538b42dfed8SFlorian Fainelli if (IS_ERR(clk)) { 539b42dfed8SFlorian Fainelli dev_err(dev, "no clock for device\n"); 540acf4fc6fSJingoo Han return PTR_ERR(clk); 541b42dfed8SFlorian Fainelli } 542b42dfed8SFlorian Fainelli 543b42dfed8SFlorian Fainelli master = spi_alloc_master(dev, sizeof(*bs)); 544b42dfed8SFlorian Fainelli if (!master) { 545b42dfed8SFlorian Fainelli dev_err(dev, "out of memory\n"); 546acf4fc6fSJingoo Han return -ENOMEM; 547b42dfed8SFlorian Fainelli } 548b42dfed8SFlorian Fainelli 549b42dfed8SFlorian Fainelli bs = spi_master_get_devdata(master); 550aa0fe826SAxel Lin init_completion(&bs->done); 551b42dfed8SFlorian Fainelli 552b42dfed8SFlorian Fainelli platform_set_drvdata(pdev, master); 553b42dfed8SFlorian Fainelli bs->pdev = pdev; 554b42dfed8SFlorian Fainelli 555de0fa83cSJulia Lawall r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 556b66c7730SJonas Gorski bs->regs = devm_ioremap_resource(&pdev->dev, r); 557b66c7730SJonas Gorski if (IS_ERR(bs->regs)) { 558b66c7730SJonas Gorski ret = PTR_ERR(bs->regs); 559b42dfed8SFlorian Fainelli goto out_err; 560b42dfed8SFlorian Fainelli } 561b42dfed8SFlorian Fainelli 562b42dfed8SFlorian Fainelli bs->irq = irq; 563b42dfed8SFlorian Fainelli bs->clk = clk; 56444d8fb30SJonas Gorski bs->reg_offsets = bcm63xx_spireg; 56544d8fb30SJonas Gorski bs->fifo_size = bs->reg_offsets[SPI_MSG_DATA_SIZE]; 566b42dfed8SFlorian Fainelli 567b42dfed8SFlorian Fainelli ret = devm_request_irq(&pdev->dev, irq, bcm63xx_spi_interrupt, 0, 568b42dfed8SFlorian Fainelli pdev->name, master); 569b42dfed8SFlorian Fainelli if (ret) { 570b42dfed8SFlorian Fainelli dev_err(dev, "unable to request irq\n"); 571b42dfed8SFlorian Fainelli goto out_err; 572b42dfed8SFlorian Fainelli } 573b42dfed8SFlorian Fainelli 574c29f0889SJonas Gorski master->dev.of_node = dev->of_node; 575c29f0889SJonas Gorski master->bus_num = bus_num; 576c29f0889SJonas Gorski master->num_chipselect = num_cs; 577cde4384eSFlorian Fainelli master->transfer_one_message = bcm63xx_spi_transfer_one; 57888a3a255SFlorian Fainelli master->mode_bits = MODEBITS; 57924778be2SStephen Warren master->bits_per_word_mask = SPI_BPW_MASK(8); 5800135c03dSJonas Gorski master->max_transfer_size = bcm63xx_spi_max_length; 5810135c03dSJonas Gorski master->max_message_size = bcm63xx_spi_max_length; 5825355d96dSMark Brown master->auto_runtime_pm = true; 58344d8fb30SJonas Gorski bs->msg_type_shift = bs->reg_offsets[SPI_MSG_TYPE_SHIFT]; 58444d8fb30SJonas Gorski bs->msg_ctl_width = bs->reg_offsets[SPI_MSG_CTL_WIDTH]; 58544d8fb30SJonas Gorski bs->tx_io = (u8 *)(bs->regs + bs->reg_offsets[SPI_MSG_DATA]); 58644d8fb30SJonas Gorski bs->rx_io = (const u8 *)(bs->regs + bs->reg_offsets[SPI_RX_DATA]); 5875a670445SFlorian Fainelli 588b42dfed8SFlorian Fainelli /* Initialize hardware */ 589ea01e8a4SJonas Gorski ret = clk_prepare_enable(bs->clk); 590ea01e8a4SJonas Gorski if (ret) 591ea01e8a4SJonas Gorski goto out_err; 592ea01e8a4SJonas Gorski 593b42dfed8SFlorian Fainelli bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); 594b42dfed8SFlorian Fainelli 595b42dfed8SFlorian Fainelli /* register and we are done */ 596bca76931SJingoo Han ret = devm_spi_register_master(dev, master); 597b42dfed8SFlorian Fainelli if (ret) { 598b42dfed8SFlorian Fainelli dev_err(dev, "spi register failed\n"); 599b42dfed8SFlorian Fainelli goto out_clk_disable; 600b42dfed8SFlorian Fainelli } 601b42dfed8SFlorian Fainelli 6020ba2cf70SArnd Bergmann dev_info(dev, "at %pr (irq %d, FIFOs size %d)\n", 6030ba2cf70SArnd Bergmann r, irq, bs->fifo_size); 604b42dfed8SFlorian Fainelli 605b42dfed8SFlorian Fainelli return 0; 606b42dfed8SFlorian Fainelli 607b42dfed8SFlorian Fainelli out_clk_disable: 6084fbb82a7SJonas Gorski clk_disable_unprepare(clk); 609b42dfed8SFlorian Fainelli out_err: 610b42dfed8SFlorian Fainelli spi_master_put(master); 611b42dfed8SFlorian Fainelli return ret; 612b42dfed8SFlorian Fainelli } 613b42dfed8SFlorian Fainelli 614fd4a319bSGrant Likely static int bcm63xx_spi_remove(struct platform_device *pdev) 615b42dfed8SFlorian Fainelli { 6169637b86fSWei Yongjun struct spi_master *master = platform_get_drvdata(pdev); 617b42dfed8SFlorian Fainelli struct bcm63xx_spi *bs = spi_master_get_devdata(master); 618b42dfed8SFlorian Fainelli 619b42dfed8SFlorian Fainelli /* reset spi block */ 620b42dfed8SFlorian Fainelli bcm_spi_writeb(bs, 0, SPI_INT_MASK); 621b42dfed8SFlorian Fainelli 622b42dfed8SFlorian Fainelli /* HW shutdown */ 6234fbb82a7SJonas Gorski clk_disable_unprepare(bs->clk); 624b42dfed8SFlorian Fainelli 625b42dfed8SFlorian Fainelli return 0; 626b42dfed8SFlorian Fainelli } 627b42dfed8SFlorian Fainelli 6281bae2028SJonas Gorski #ifdef CONFIG_PM_SLEEP 629b42dfed8SFlorian Fainelli static int bcm63xx_spi_suspend(struct device *dev) 630b42dfed8SFlorian Fainelli { 631a1216394SAxel Lin struct spi_master *master = dev_get_drvdata(dev); 632b42dfed8SFlorian Fainelli struct bcm63xx_spi *bs = spi_master_get_devdata(master); 633b42dfed8SFlorian Fainelli 63496519957SFlorian Fainelli spi_master_suspend(master); 63596519957SFlorian Fainelli 6364fbb82a7SJonas Gorski clk_disable_unprepare(bs->clk); 637b42dfed8SFlorian Fainelli 638b42dfed8SFlorian Fainelli return 0; 639b42dfed8SFlorian Fainelli } 640b42dfed8SFlorian Fainelli 641b42dfed8SFlorian Fainelli static int bcm63xx_spi_resume(struct device *dev) 642b42dfed8SFlorian Fainelli { 643a1216394SAxel Lin struct spi_master *master = dev_get_drvdata(dev); 644b42dfed8SFlorian Fainelli struct bcm63xx_spi *bs = spi_master_get_devdata(master); 645ea01e8a4SJonas Gorski int ret; 646b42dfed8SFlorian Fainelli 647ea01e8a4SJonas Gorski ret = clk_prepare_enable(bs->clk); 648ea01e8a4SJonas Gorski if (ret) 649ea01e8a4SJonas Gorski return ret; 650b42dfed8SFlorian Fainelli 65196519957SFlorian Fainelli spi_master_resume(master); 65296519957SFlorian Fainelli 653b42dfed8SFlorian Fainelli return 0; 654b42dfed8SFlorian Fainelli } 6551bae2028SJonas Gorski #endif 656b42dfed8SFlorian Fainelli 657b42dfed8SFlorian Fainelli static const struct dev_pm_ops bcm63xx_spi_pm_ops = { 6581bae2028SJonas Gorski SET_SYSTEM_SLEEP_PM_OPS(bcm63xx_spi_suspend, bcm63xx_spi_resume) 659b42dfed8SFlorian Fainelli }; 660b42dfed8SFlorian Fainelli 661b42dfed8SFlorian Fainelli static struct platform_driver bcm63xx_spi_driver = { 662b42dfed8SFlorian Fainelli .driver = { 663b42dfed8SFlorian Fainelli .name = "bcm63xx-spi", 6641bae2028SJonas Gorski .pm = &bcm63xx_spi_pm_ops, 665c29f0889SJonas Gorski .of_match_table = bcm63xx_spi_of_match, 666b42dfed8SFlorian Fainelli }, 66744d8fb30SJonas Gorski .id_table = bcm63xx_spi_dev_match, 668b42dfed8SFlorian Fainelli .probe = bcm63xx_spi_probe, 669fd4a319bSGrant Likely .remove = bcm63xx_spi_remove, 670b42dfed8SFlorian Fainelli }; 671b42dfed8SFlorian Fainelli 672b42dfed8SFlorian Fainelli module_platform_driver(bcm63xx_spi_driver); 673b42dfed8SFlorian Fainelli 674b42dfed8SFlorian Fainelli MODULE_ALIAS("platform:bcm63xx_spi"); 675b42dfed8SFlorian Fainelli MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); 676b42dfed8SFlorian Fainelli MODULE_AUTHOR("Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>"); 677b42dfed8SFlorian Fainelli MODULE_DESCRIPTION("Broadcom BCM63xx SPI Controller driver"); 678b42dfed8SFlorian Fainelli MODULE_LICENSE("GPL"); 679