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 * You should have received a copy of the GNU General Public License 18b42dfed8SFlorian Fainelli * along with this program; if not, write to the 19b42dfed8SFlorian Fainelli * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20b42dfed8SFlorian Fainelli */ 21b42dfed8SFlorian Fainelli 22b42dfed8SFlorian Fainelli #include <linux/kernel.h> 23b42dfed8SFlorian Fainelli #include <linux/init.h> 24b42dfed8SFlorian Fainelli #include <linux/clk.h> 25b42dfed8SFlorian Fainelli #include <linux/io.h> 26b42dfed8SFlorian Fainelli #include <linux/module.h> 27b42dfed8SFlorian Fainelli #include <linux/platform_device.h> 28b42dfed8SFlorian Fainelli #include <linux/delay.h> 29b42dfed8SFlorian Fainelli #include <linux/interrupt.h> 30b42dfed8SFlorian Fainelli #include <linux/spi/spi.h> 31b42dfed8SFlorian Fainelli #include <linux/completion.h> 32b42dfed8SFlorian Fainelli #include <linux/err.h> 33cde4384eSFlorian Fainelli #include <linux/workqueue.h> 34cde4384eSFlorian Fainelli #include <linux/pm_runtime.h> 35b42dfed8SFlorian Fainelli 36b42dfed8SFlorian Fainelli #include <bcm63xx_dev_spi.h> 37b42dfed8SFlorian Fainelli 38b42dfed8SFlorian Fainelli #define PFX KBUILD_MODNAME 39b42dfed8SFlorian Fainelli 40b17de076SJonas Gorski #define BCM63XX_SPI_MAX_PREPEND 15 41b17de076SJonas Gorski 42b42dfed8SFlorian Fainelli struct bcm63xx_spi { 43b42dfed8SFlorian Fainelli struct completion done; 44b42dfed8SFlorian Fainelli 45b42dfed8SFlorian Fainelli void __iomem *regs; 46b42dfed8SFlorian Fainelli int irq; 47b42dfed8SFlorian Fainelli 48b42dfed8SFlorian Fainelli /* Platform data */ 49b42dfed8SFlorian Fainelli unsigned fifo_size; 505a670445SFlorian Fainelli unsigned int msg_type_shift; 515a670445SFlorian Fainelli unsigned int msg_ctl_width; 52b42dfed8SFlorian Fainelli 53b42dfed8SFlorian Fainelli /* data iomem */ 54b42dfed8SFlorian Fainelli u8 __iomem *tx_io; 55b42dfed8SFlorian Fainelli const u8 __iomem *rx_io; 56b42dfed8SFlorian Fainelli 57b42dfed8SFlorian Fainelli struct clk *clk; 58b42dfed8SFlorian Fainelli struct platform_device *pdev; 59b42dfed8SFlorian Fainelli }; 60b42dfed8SFlorian Fainelli 61b42dfed8SFlorian Fainelli static inline u8 bcm_spi_readb(struct bcm63xx_spi *bs, 62b42dfed8SFlorian Fainelli unsigned int offset) 63b42dfed8SFlorian Fainelli { 64b42dfed8SFlorian Fainelli return bcm_readb(bs->regs + bcm63xx_spireg(offset)); 65b42dfed8SFlorian Fainelli } 66b42dfed8SFlorian Fainelli 67b42dfed8SFlorian Fainelli static inline u16 bcm_spi_readw(struct bcm63xx_spi *bs, 68b42dfed8SFlorian Fainelli unsigned int offset) 69b42dfed8SFlorian Fainelli { 70b42dfed8SFlorian Fainelli return bcm_readw(bs->regs + bcm63xx_spireg(offset)); 71b42dfed8SFlorian Fainelli } 72b42dfed8SFlorian Fainelli 73b42dfed8SFlorian Fainelli static inline void bcm_spi_writeb(struct bcm63xx_spi *bs, 74b42dfed8SFlorian Fainelli u8 value, unsigned int offset) 75b42dfed8SFlorian Fainelli { 76b42dfed8SFlorian Fainelli bcm_writeb(value, bs->regs + bcm63xx_spireg(offset)); 77b42dfed8SFlorian Fainelli } 78b42dfed8SFlorian Fainelli 79b42dfed8SFlorian Fainelli static inline void bcm_spi_writew(struct bcm63xx_spi *bs, 80b42dfed8SFlorian Fainelli u16 value, unsigned int offset) 81b42dfed8SFlorian Fainelli { 82b42dfed8SFlorian Fainelli bcm_writew(value, bs->regs + bcm63xx_spireg(offset)); 83b42dfed8SFlorian Fainelli } 84b42dfed8SFlorian Fainelli 85b42dfed8SFlorian Fainelli static const unsigned bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = { 86b42dfed8SFlorian Fainelli { 20000000, SPI_CLK_20MHZ }, 87b42dfed8SFlorian Fainelli { 12500000, SPI_CLK_12_50MHZ }, 88b42dfed8SFlorian Fainelli { 6250000, SPI_CLK_6_250MHZ }, 89b42dfed8SFlorian Fainelli { 3125000, SPI_CLK_3_125MHZ }, 90b42dfed8SFlorian Fainelli { 1563000, SPI_CLK_1_563MHZ }, 91b42dfed8SFlorian Fainelli { 781000, SPI_CLK_0_781MHZ }, 92b42dfed8SFlorian Fainelli { 391000, SPI_CLK_0_391MHZ } 93b42dfed8SFlorian Fainelli }; 94b42dfed8SFlorian Fainelli 95cde4384eSFlorian Fainelli static void bcm63xx_spi_setup_transfer(struct spi_device *spi, 96cde4384eSFlorian Fainelli struct spi_transfer *t) 97cde4384eSFlorian Fainelli { 98cde4384eSFlorian Fainelli struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); 99cde4384eSFlorian Fainelli u8 clk_cfg, reg; 100cde4384eSFlorian Fainelli int i; 101cde4384eSFlorian Fainelli 102b42dfed8SFlorian Fainelli /* Find the closest clock configuration */ 103b42dfed8SFlorian Fainelli for (i = 0; i < SPI_CLK_MASK; i++) { 10468792e2aSJonas Gorski if (t->speed_hz >= bcm63xx_spi_freq_table[i][0]) { 105b42dfed8SFlorian Fainelli clk_cfg = bcm63xx_spi_freq_table[i][1]; 106b42dfed8SFlorian Fainelli break; 107b42dfed8SFlorian Fainelli } 108b42dfed8SFlorian Fainelli } 109b42dfed8SFlorian Fainelli 110b42dfed8SFlorian Fainelli /* No matching configuration found, default to lowest */ 111b42dfed8SFlorian Fainelli if (i == SPI_CLK_MASK) 112b42dfed8SFlorian Fainelli clk_cfg = SPI_CLK_0_391MHZ; 113b42dfed8SFlorian Fainelli 114b42dfed8SFlorian Fainelli /* clear existing clock configuration bits of the register */ 115b42dfed8SFlorian Fainelli reg = bcm_spi_readb(bs, SPI_CLK_CFG); 116b42dfed8SFlorian Fainelli reg &= ~SPI_CLK_MASK; 117b42dfed8SFlorian Fainelli reg |= clk_cfg; 118b42dfed8SFlorian Fainelli 119b42dfed8SFlorian Fainelli bcm_spi_writeb(bs, reg, SPI_CLK_CFG); 120b42dfed8SFlorian Fainelli dev_dbg(&spi->dev, "Setting clock register to %02x (hz %d)\n", 12168792e2aSJonas Gorski clk_cfg, t->speed_hz); 122b42dfed8SFlorian Fainelli } 123b42dfed8SFlorian Fainelli 124b42dfed8SFlorian Fainelli /* the spi->mode bits understood by this driver: */ 125b42dfed8SFlorian Fainelli #define MODEBITS (SPI_CPOL | SPI_CPHA) 126b42dfed8SFlorian Fainelli 127b17de076SJonas Gorski static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first, 128b17de076SJonas Gorski unsigned int num_transfers) 129b42dfed8SFlorian Fainelli { 130b42dfed8SFlorian Fainelli struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); 131b42dfed8SFlorian Fainelli u16 msg_ctl; 132b42dfed8SFlorian Fainelli u16 cmd; 133c0fde3baSJonas Gorski u8 rx_tail; 134b17de076SJonas Gorski unsigned int i, timeout = 0, prepend_len = 0, len = 0; 135b17de076SJonas Gorski struct spi_transfer *t = first; 136b17de076SJonas Gorski bool do_rx = false; 137b17de076SJonas Gorski bool do_tx = false; 138b42dfed8SFlorian Fainelli 139cde4384eSFlorian Fainelli /* Disable the CMD_DONE interrupt */ 140cde4384eSFlorian Fainelli bcm_spi_writeb(bs, 0, SPI_INT_MASK); 141cde4384eSFlorian Fainelli 142b42dfed8SFlorian Fainelli dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", 143b42dfed8SFlorian Fainelli t->tx_buf, t->rx_buf, t->len); 144b42dfed8SFlorian Fainelli 145b17de076SJonas Gorski if (num_transfers > 1 && t->tx_buf && t->len <= BCM63XX_SPI_MAX_PREPEND) 146b17de076SJonas Gorski prepend_len = t->len; 147b17de076SJonas Gorski 148b17de076SJonas Gorski /* prepare the buffer */ 149b17de076SJonas Gorski for (i = 0; i < num_transfers; i++) { 150b17de076SJonas Gorski if (t->tx_buf) { 151b17de076SJonas Gorski do_tx = true; 152b17de076SJonas Gorski memcpy_toio(bs->tx_io + len, t->tx_buf, t->len); 153b17de076SJonas Gorski 154b17de076SJonas Gorski /* don't prepend more than one tx */ 155b17de076SJonas Gorski if (t != first) 156b17de076SJonas Gorski prepend_len = 0; 157b17de076SJonas Gorski } 158b17de076SJonas Gorski 159b17de076SJonas Gorski if (t->rx_buf) { 160b17de076SJonas Gorski do_rx = true; 161b17de076SJonas Gorski /* prepend is half-duplex write only */ 162b17de076SJonas Gorski if (t == first) 163b17de076SJonas Gorski prepend_len = 0; 164b17de076SJonas Gorski } 165b17de076SJonas Gorski 166b17de076SJonas Gorski len += t->len; 167b17de076SJonas Gorski 168b17de076SJonas Gorski t = list_entry(t->transfer_list.next, struct spi_transfer, 169b17de076SJonas Gorski transfer_list); 170b17de076SJonas Gorski } 171b17de076SJonas Gorski 172cde4384eSFlorian Fainelli init_completion(&bs->done); 173b42dfed8SFlorian Fainelli 174b42dfed8SFlorian Fainelli /* Fill in the Message control register */ 175b17de076SJonas Gorski msg_ctl = (len << SPI_BYTE_CNT_SHIFT); 176b42dfed8SFlorian Fainelli 177b17de076SJonas Gorski if (do_rx && do_tx && prepend_len == 0) 1785a670445SFlorian Fainelli msg_ctl |= (SPI_FD_RW << bs->msg_type_shift); 179b17de076SJonas Gorski else if (do_rx) 1805a670445SFlorian Fainelli msg_ctl |= (SPI_HD_R << bs->msg_type_shift); 181b17de076SJonas Gorski else if (do_tx) 1825a670445SFlorian Fainelli msg_ctl |= (SPI_HD_W << bs->msg_type_shift); 183b42dfed8SFlorian Fainelli 1845a670445SFlorian Fainelli switch (bs->msg_ctl_width) { 1855a670445SFlorian Fainelli case 8: 1865a670445SFlorian Fainelli bcm_spi_writeb(bs, msg_ctl, SPI_MSG_CTL); 1875a670445SFlorian Fainelli break; 1885a670445SFlorian Fainelli case 16: 189b42dfed8SFlorian Fainelli bcm_spi_writew(bs, msg_ctl, SPI_MSG_CTL); 1905a670445SFlorian Fainelli break; 1915a670445SFlorian Fainelli } 192b42dfed8SFlorian Fainelli 193b42dfed8SFlorian Fainelli /* Issue the transfer */ 194b42dfed8SFlorian Fainelli cmd = SPI_CMD_START_IMMEDIATE; 195b17de076SJonas Gorski cmd |= (prepend_len << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); 196b42dfed8SFlorian Fainelli cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT); 197b42dfed8SFlorian Fainelli bcm_spi_writew(bs, cmd, SPI_CMD); 198b42dfed8SFlorian Fainelli 199cde4384eSFlorian Fainelli /* Enable the CMD_DONE interrupt */ 200cde4384eSFlorian Fainelli bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK); 201b42dfed8SFlorian Fainelli 202c0fde3baSJonas Gorski timeout = wait_for_completion_timeout(&bs->done, HZ); 203c0fde3baSJonas Gorski if (!timeout) 204c0fde3baSJonas Gorski return -ETIMEDOUT; 205c0fde3baSJonas Gorski 206c0fde3baSJonas Gorski /* read out all data */ 207c0fde3baSJonas Gorski rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); 208c0fde3baSJonas Gorski 209b17de076SJonas Gorski if (do_rx && rx_tail != len) 210b17de076SJonas Gorski return -EIO; 211b17de076SJonas Gorski 212b17de076SJonas Gorski if (!rx_tail) 213b17de076SJonas Gorski return 0; 214b17de076SJonas Gorski 215b17de076SJonas Gorski len = 0; 216b17de076SJonas Gorski t = first; 217c0fde3baSJonas Gorski /* Read out all the data */ 218b17de076SJonas Gorski for (i = 0; i < num_transfers; i++) { 219b17de076SJonas Gorski if (t->rx_buf) 220b17de076SJonas Gorski memcpy_fromio(t->rx_buf, bs->rx_io + len, t->len); 221b17de076SJonas Gorski 222b17de076SJonas Gorski if (t != first || prepend_len == 0) 223b17de076SJonas Gorski len += t->len; 224b17de076SJonas Gorski 225b17de076SJonas Gorski t = list_entry(t->transfer_list.next, struct spi_transfer, 226b17de076SJonas Gorski transfer_list); 227b17de076SJonas Gorski } 228c0fde3baSJonas Gorski 229c0fde3baSJonas Gorski return 0; 230b42dfed8SFlorian Fainelli } 231b42dfed8SFlorian Fainelli 232cde4384eSFlorian Fainelli static int bcm63xx_spi_transfer_one(struct spi_master *master, 233cde4384eSFlorian Fainelli struct spi_message *m) 234cde4384eSFlorian Fainelli { 235cde4384eSFlorian Fainelli struct bcm63xx_spi *bs = spi_master_get_devdata(master); 236b17de076SJonas Gorski struct spi_transfer *t, *first = NULL; 237cde4384eSFlorian Fainelli struct spi_device *spi = m->spi; 238cde4384eSFlorian Fainelli int status = 0; 239b17de076SJonas Gorski unsigned int n_transfers = 0, total_len = 0; 240b17de076SJonas Gorski bool can_use_prepend = false; 241cde4384eSFlorian Fainelli 242b17de076SJonas Gorski /* 243b17de076SJonas Gorski * This SPI controller does not support keeping CS active after a 244b17de076SJonas Gorski * transfer. 245b17de076SJonas Gorski * Work around this by merging as many transfers we can into one big 246b17de076SJonas Gorski * full-duplex transfers. 247b17de076SJonas Gorski */ 248cde4384eSFlorian Fainelli list_for_each_entry(t, &m->transfers, transfer_list) { 249b17de076SJonas Gorski if (!first) 250b17de076SJonas Gorski first = t; 251b17de076SJonas Gorski 252b17de076SJonas Gorski n_transfers++; 253b17de076SJonas Gorski total_len += t->len; 254b17de076SJonas Gorski 255b17de076SJonas Gorski if (n_transfers == 2 && !first->rx_buf && !t->tx_buf && 256b17de076SJonas Gorski first->len <= BCM63XX_SPI_MAX_PREPEND) 257b17de076SJonas Gorski can_use_prepend = true; 258b17de076SJonas Gorski else if (can_use_prepend && t->tx_buf) 259b17de076SJonas Gorski can_use_prepend = false; 260b17de076SJonas Gorski 261c0fde3baSJonas Gorski /* we can only transfer one fifo worth of data */ 262b17de076SJonas Gorski if ((can_use_prepend && 263b17de076SJonas Gorski total_len > (bs->fifo_size + BCM63XX_SPI_MAX_PREPEND)) || 264b17de076SJonas Gorski (!can_use_prepend && total_len > bs->fifo_size)) { 265c0fde3baSJonas Gorski dev_err(&spi->dev, "unable to do transfers larger than FIFO size (%i > %i)\n", 266b17de076SJonas Gorski total_len, bs->fifo_size); 267b17de076SJonas Gorski status = -EINVAL; 268b17de076SJonas Gorski goto exit; 269b17de076SJonas Gorski } 270b17de076SJonas Gorski 271b17de076SJonas Gorski /* all combined transfers have to have the same speed */ 272b17de076SJonas Gorski if (t->speed_hz != first->speed_hz) { 273b17de076SJonas Gorski dev_err(&spi->dev, "unable to change speed between transfers\n"); 274c0fde3baSJonas Gorski status = -EINVAL; 275cde4384eSFlorian Fainelli goto exit; 276cde4384eSFlorian Fainelli } 277cde4384eSFlorian Fainelli 278c0fde3baSJonas Gorski /* CS will be deasserted directly after transfer */ 279c0fde3baSJonas Gorski if (t->delay_usecs) { 280c0fde3baSJonas Gorski dev_err(&spi->dev, "unable to keep CS asserted after transfer\n"); 281c0fde3baSJonas Gorski status = -EINVAL; 282c0fde3baSJonas Gorski goto exit; 283cde4384eSFlorian Fainelli } 284cde4384eSFlorian Fainelli 285b17de076SJonas Gorski if (t->cs_change || 286b17de076SJonas Gorski list_is_last(&t->transfer_list, &m->transfers)) { 287c0fde3baSJonas Gorski /* configure adapter for a new transfer */ 288b17de076SJonas Gorski bcm63xx_spi_setup_transfer(spi, first); 289c0fde3baSJonas Gorski 290c0fde3baSJonas Gorski /* send the data */ 291b17de076SJonas Gorski status = bcm63xx_txrx_bufs(spi, first, n_transfers); 292c0fde3baSJonas Gorski if (status) 293c0fde3baSJonas Gorski goto exit; 294c0fde3baSJonas Gorski 295b17de076SJonas Gorski m->actual_length += total_len; 296b17de076SJonas Gorski 297b17de076SJonas Gorski first = NULL; 298b17de076SJonas Gorski n_transfers = 0; 299b17de076SJonas Gorski total_len = 0; 300b17de076SJonas Gorski can_use_prepend = false; 301b17de076SJonas Gorski } 302cde4384eSFlorian Fainelli } 303cde4384eSFlorian Fainelli exit: 304cde4384eSFlorian Fainelli m->status = status; 305cde4384eSFlorian Fainelli spi_finalize_current_message(master); 306cde4384eSFlorian Fainelli 307cde4384eSFlorian Fainelli return 0; 308b42dfed8SFlorian Fainelli } 309b42dfed8SFlorian Fainelli 310b42dfed8SFlorian Fainelli /* This driver supports single master mode only. Hence 311b42dfed8SFlorian Fainelli * CMD_DONE is the only interrupt we care about 312b42dfed8SFlorian Fainelli */ 313b42dfed8SFlorian Fainelli static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id) 314b42dfed8SFlorian Fainelli { 315b42dfed8SFlorian Fainelli struct spi_master *master = (struct spi_master *)dev_id; 316b42dfed8SFlorian Fainelli struct bcm63xx_spi *bs = spi_master_get_devdata(master); 317b42dfed8SFlorian Fainelli u8 intr; 318b42dfed8SFlorian Fainelli 319b42dfed8SFlorian Fainelli /* Read interupts and clear them immediately */ 320b42dfed8SFlorian Fainelli intr = bcm_spi_readb(bs, SPI_INT_STATUS); 321b42dfed8SFlorian Fainelli bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); 322b42dfed8SFlorian Fainelli bcm_spi_writeb(bs, 0, SPI_INT_MASK); 323b42dfed8SFlorian Fainelli 324cde4384eSFlorian Fainelli /* A transfer completed */ 325cde4384eSFlorian Fainelli if (intr & SPI_INTR_CMD_DONE) 326b42dfed8SFlorian Fainelli complete(&bs->done); 327b42dfed8SFlorian Fainelli 328b42dfed8SFlorian Fainelli return IRQ_HANDLED; 329b42dfed8SFlorian Fainelli } 330b42dfed8SFlorian Fainelli 331b42dfed8SFlorian Fainelli 332fd4a319bSGrant Likely static int bcm63xx_spi_probe(struct platform_device *pdev) 333b42dfed8SFlorian Fainelli { 334b42dfed8SFlorian Fainelli struct resource *r; 335b42dfed8SFlorian Fainelli struct device *dev = &pdev->dev; 3368074cf06SJingoo Han struct bcm63xx_spi_pdata *pdata = dev_get_platdata(&pdev->dev); 337b42dfed8SFlorian Fainelli int irq; 338b42dfed8SFlorian Fainelli struct spi_master *master; 339b42dfed8SFlorian Fainelli struct clk *clk; 340b42dfed8SFlorian Fainelli struct bcm63xx_spi *bs; 341b42dfed8SFlorian Fainelli int ret; 342b42dfed8SFlorian Fainelli 343b42dfed8SFlorian Fainelli irq = platform_get_irq(pdev, 0); 344b42dfed8SFlorian Fainelli if (irq < 0) { 345b42dfed8SFlorian Fainelli dev_err(dev, "no irq\n"); 346b42dfed8SFlorian Fainelli ret = -ENXIO; 347b42dfed8SFlorian Fainelli goto out; 348b42dfed8SFlorian Fainelli } 349b42dfed8SFlorian Fainelli 350b42dfed8SFlorian Fainelli clk = clk_get(dev, "spi"); 351b42dfed8SFlorian Fainelli if (IS_ERR(clk)) { 352b42dfed8SFlorian Fainelli dev_err(dev, "no clock for device\n"); 353b42dfed8SFlorian Fainelli ret = PTR_ERR(clk); 354b42dfed8SFlorian Fainelli goto out; 355b42dfed8SFlorian Fainelli } 356b42dfed8SFlorian Fainelli 357b42dfed8SFlorian Fainelli master = spi_alloc_master(dev, sizeof(*bs)); 358b42dfed8SFlorian Fainelli if (!master) { 359b42dfed8SFlorian Fainelli dev_err(dev, "out of memory\n"); 360b42dfed8SFlorian Fainelli ret = -ENOMEM; 361b42dfed8SFlorian Fainelli goto out_clk; 362b42dfed8SFlorian Fainelli } 363b42dfed8SFlorian Fainelli 364b42dfed8SFlorian Fainelli bs = spi_master_get_devdata(master); 365b42dfed8SFlorian Fainelli 366b42dfed8SFlorian Fainelli platform_set_drvdata(pdev, master); 367b42dfed8SFlorian Fainelli bs->pdev = pdev; 368b42dfed8SFlorian Fainelli 369de0fa83cSJulia Lawall r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 370b66c7730SJonas Gorski bs->regs = devm_ioremap_resource(&pdev->dev, r); 371b66c7730SJonas Gorski if (IS_ERR(bs->regs)) { 372b66c7730SJonas Gorski ret = PTR_ERR(bs->regs); 373b42dfed8SFlorian Fainelli goto out_err; 374b42dfed8SFlorian Fainelli } 375b42dfed8SFlorian Fainelli 376b42dfed8SFlorian Fainelli bs->irq = irq; 377b42dfed8SFlorian Fainelli bs->clk = clk; 378b42dfed8SFlorian Fainelli bs->fifo_size = pdata->fifo_size; 379b42dfed8SFlorian Fainelli 380b42dfed8SFlorian Fainelli ret = devm_request_irq(&pdev->dev, irq, bcm63xx_spi_interrupt, 0, 381b42dfed8SFlorian Fainelli pdev->name, master); 382b42dfed8SFlorian Fainelli if (ret) { 383b42dfed8SFlorian Fainelli dev_err(dev, "unable to request irq\n"); 384b42dfed8SFlorian Fainelli goto out_err; 385b42dfed8SFlorian Fainelli } 386b42dfed8SFlorian Fainelli 387b42dfed8SFlorian Fainelli master->bus_num = pdata->bus_num; 388b42dfed8SFlorian Fainelli master->num_chipselect = pdata->num_chipselect; 389cde4384eSFlorian Fainelli master->transfer_one_message = bcm63xx_spi_transfer_one; 39088a3a255SFlorian Fainelli master->mode_bits = MODEBITS; 39124778be2SStephen Warren master->bits_per_word_mask = SPI_BPW_MASK(8); 3925355d96dSMark Brown master->auto_runtime_pm = true; 3935a670445SFlorian Fainelli bs->msg_type_shift = pdata->msg_type_shift; 3945a670445SFlorian Fainelli bs->msg_ctl_width = pdata->msg_ctl_width; 395b42dfed8SFlorian Fainelli bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA)); 396b42dfed8SFlorian Fainelli bs->rx_io = (const u8 *)(bs->regs + bcm63xx_spireg(SPI_RX_DATA)); 397b42dfed8SFlorian Fainelli 3985a670445SFlorian Fainelli switch (bs->msg_ctl_width) { 3995a670445SFlorian Fainelli case 8: 4005a670445SFlorian Fainelli case 16: 4015a670445SFlorian Fainelli break; 4025a670445SFlorian Fainelli default: 4035a670445SFlorian Fainelli dev_err(dev, "unsupported MSG_CTL width: %d\n", 4045a670445SFlorian Fainelli bs->msg_ctl_width); 405b435ff21SJonas Gorski goto out_err; 4065a670445SFlorian Fainelli } 4075a670445SFlorian Fainelli 408b42dfed8SFlorian Fainelli /* Initialize hardware */ 4094fbb82a7SJonas Gorski clk_prepare_enable(bs->clk); 410b42dfed8SFlorian Fainelli bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); 411b42dfed8SFlorian Fainelli 412b42dfed8SFlorian Fainelli /* register and we are done */ 413bca76931SJingoo Han ret = devm_spi_register_master(dev, master); 414b42dfed8SFlorian Fainelli if (ret) { 415b42dfed8SFlorian Fainelli dev_err(dev, "spi register failed\n"); 416b42dfed8SFlorian Fainelli goto out_clk_disable; 417b42dfed8SFlorian Fainelli } 418b42dfed8SFlorian Fainelli 41961d15963SFlorian Fainelli dev_info(dev, "at 0x%08x (irq %d, FIFOs size %d)\n", 42061d15963SFlorian Fainelli r->start, irq, bs->fifo_size); 421b42dfed8SFlorian Fainelli 422b42dfed8SFlorian Fainelli return 0; 423b42dfed8SFlorian Fainelli 424b42dfed8SFlorian Fainelli out_clk_disable: 4254fbb82a7SJonas Gorski clk_disable_unprepare(clk); 426b42dfed8SFlorian Fainelli out_err: 427b42dfed8SFlorian Fainelli spi_master_put(master); 428b42dfed8SFlorian Fainelli out_clk: 429b42dfed8SFlorian Fainelli clk_put(clk); 430b42dfed8SFlorian Fainelli out: 431b42dfed8SFlorian Fainelli return ret; 432b42dfed8SFlorian Fainelli } 433b42dfed8SFlorian Fainelli 434fd4a319bSGrant Likely static int bcm63xx_spi_remove(struct platform_device *pdev) 435b42dfed8SFlorian Fainelli { 436*9637b86fSWei Yongjun struct spi_master *master = platform_get_drvdata(pdev); 437b42dfed8SFlorian Fainelli struct bcm63xx_spi *bs = spi_master_get_devdata(master); 438b42dfed8SFlorian Fainelli 439b42dfed8SFlorian Fainelli /* reset spi block */ 440b42dfed8SFlorian Fainelli bcm_spi_writeb(bs, 0, SPI_INT_MASK); 441b42dfed8SFlorian Fainelli 442b42dfed8SFlorian Fainelli /* HW shutdown */ 4434fbb82a7SJonas Gorski clk_disable_unprepare(bs->clk); 444b42dfed8SFlorian Fainelli clk_put(bs->clk); 445b42dfed8SFlorian Fainelli 446b42dfed8SFlorian Fainelli return 0; 447b42dfed8SFlorian Fainelli } 448b42dfed8SFlorian Fainelli 449b42dfed8SFlorian Fainelli #ifdef CONFIG_PM 450b42dfed8SFlorian Fainelli static int bcm63xx_spi_suspend(struct device *dev) 451b42dfed8SFlorian Fainelli { 452a1216394SAxel Lin struct spi_master *master = dev_get_drvdata(dev); 453b42dfed8SFlorian Fainelli struct bcm63xx_spi *bs = spi_master_get_devdata(master); 454b42dfed8SFlorian Fainelli 45596519957SFlorian Fainelli spi_master_suspend(master); 45696519957SFlorian Fainelli 4574fbb82a7SJonas Gorski clk_disable_unprepare(bs->clk); 458b42dfed8SFlorian Fainelli 459b42dfed8SFlorian Fainelli return 0; 460b42dfed8SFlorian Fainelli } 461b42dfed8SFlorian Fainelli 462b42dfed8SFlorian Fainelli static int bcm63xx_spi_resume(struct device *dev) 463b42dfed8SFlorian Fainelli { 464a1216394SAxel Lin struct spi_master *master = dev_get_drvdata(dev); 465b42dfed8SFlorian Fainelli struct bcm63xx_spi *bs = spi_master_get_devdata(master); 466b42dfed8SFlorian Fainelli 4674fbb82a7SJonas Gorski clk_prepare_enable(bs->clk); 468b42dfed8SFlorian Fainelli 46996519957SFlorian Fainelli spi_master_resume(master); 47096519957SFlorian Fainelli 471b42dfed8SFlorian Fainelli return 0; 472b42dfed8SFlorian Fainelli } 473b42dfed8SFlorian Fainelli 474b42dfed8SFlorian Fainelli static const struct dev_pm_ops bcm63xx_spi_pm_ops = { 475b42dfed8SFlorian Fainelli .suspend = bcm63xx_spi_suspend, 476b42dfed8SFlorian Fainelli .resume = bcm63xx_spi_resume, 477b42dfed8SFlorian Fainelli }; 478b42dfed8SFlorian Fainelli 479b42dfed8SFlorian Fainelli #define BCM63XX_SPI_PM_OPS (&bcm63xx_spi_pm_ops) 480b42dfed8SFlorian Fainelli #else 481b42dfed8SFlorian Fainelli #define BCM63XX_SPI_PM_OPS NULL 482b42dfed8SFlorian Fainelli #endif 483b42dfed8SFlorian Fainelli 484b42dfed8SFlorian Fainelli static struct platform_driver bcm63xx_spi_driver = { 485b42dfed8SFlorian Fainelli .driver = { 486b42dfed8SFlorian Fainelli .name = "bcm63xx-spi", 487b42dfed8SFlorian Fainelli .owner = THIS_MODULE, 488b42dfed8SFlorian Fainelli .pm = BCM63XX_SPI_PM_OPS, 489b42dfed8SFlorian Fainelli }, 490b42dfed8SFlorian Fainelli .probe = bcm63xx_spi_probe, 491fd4a319bSGrant Likely .remove = bcm63xx_spi_remove, 492b42dfed8SFlorian Fainelli }; 493b42dfed8SFlorian Fainelli 494b42dfed8SFlorian Fainelli module_platform_driver(bcm63xx_spi_driver); 495b42dfed8SFlorian Fainelli 496b42dfed8SFlorian Fainelli MODULE_ALIAS("platform:bcm63xx_spi"); 497b42dfed8SFlorian Fainelli MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); 498b42dfed8SFlorian Fainelli MODULE_AUTHOR("Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>"); 499b42dfed8SFlorian Fainelli MODULE_DESCRIPTION("Broadcom BCM63xx SPI Controller driver"); 500b42dfed8SFlorian Fainelli MODULE_LICENSE("GPL"); 501