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 172b17de076SJonas Gorski len -= prepend_len; 173b42dfed8SFlorian Fainelli 174cde4384eSFlorian Fainelli init_completion(&bs->done); 175b42dfed8SFlorian Fainelli 176b42dfed8SFlorian Fainelli /* Fill in the Message control register */ 177b17de076SJonas Gorski msg_ctl = (len << SPI_BYTE_CNT_SHIFT); 178b42dfed8SFlorian Fainelli 179b17de076SJonas Gorski if (do_rx && do_tx && prepend_len == 0) 1805a670445SFlorian Fainelli msg_ctl |= (SPI_FD_RW << bs->msg_type_shift); 181b17de076SJonas Gorski else if (do_rx) 1825a670445SFlorian Fainelli msg_ctl |= (SPI_HD_R << bs->msg_type_shift); 183b17de076SJonas Gorski else if (do_tx) 1845a670445SFlorian Fainelli msg_ctl |= (SPI_HD_W << bs->msg_type_shift); 185b42dfed8SFlorian Fainelli 1865a670445SFlorian Fainelli switch (bs->msg_ctl_width) { 1875a670445SFlorian Fainelli case 8: 1885a670445SFlorian Fainelli bcm_spi_writeb(bs, msg_ctl, SPI_MSG_CTL); 1895a670445SFlorian Fainelli break; 1905a670445SFlorian Fainelli case 16: 191b42dfed8SFlorian Fainelli bcm_spi_writew(bs, msg_ctl, SPI_MSG_CTL); 1925a670445SFlorian Fainelli break; 1935a670445SFlorian Fainelli } 194b42dfed8SFlorian Fainelli 195b42dfed8SFlorian Fainelli /* Issue the transfer */ 196b42dfed8SFlorian Fainelli cmd = SPI_CMD_START_IMMEDIATE; 197b17de076SJonas Gorski cmd |= (prepend_len << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); 198b42dfed8SFlorian Fainelli cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT); 199b42dfed8SFlorian Fainelli bcm_spi_writew(bs, cmd, SPI_CMD); 200b42dfed8SFlorian Fainelli 201cde4384eSFlorian Fainelli /* Enable the CMD_DONE interrupt */ 202cde4384eSFlorian Fainelli bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK); 203b42dfed8SFlorian Fainelli 204c0fde3baSJonas Gorski timeout = wait_for_completion_timeout(&bs->done, HZ); 205c0fde3baSJonas Gorski if (!timeout) 206c0fde3baSJonas Gorski return -ETIMEDOUT; 207c0fde3baSJonas Gorski 208c0fde3baSJonas Gorski /* read out all data */ 209c0fde3baSJonas Gorski rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); 210c0fde3baSJonas Gorski 211b17de076SJonas Gorski if (do_rx && rx_tail != len) 212b17de076SJonas Gorski return -EIO; 213b17de076SJonas Gorski 214b17de076SJonas Gorski if (!rx_tail) 215b17de076SJonas Gorski return 0; 216b17de076SJonas Gorski 217b17de076SJonas Gorski len = 0; 218b17de076SJonas Gorski t = first; 219c0fde3baSJonas Gorski /* Read out all the data */ 220b17de076SJonas Gorski for (i = 0; i < num_transfers; i++) { 221b17de076SJonas Gorski if (t->rx_buf) 222b17de076SJonas Gorski memcpy_fromio(t->rx_buf, bs->rx_io + len, t->len); 223b17de076SJonas Gorski 224b17de076SJonas Gorski if (t != first || prepend_len == 0) 225b17de076SJonas Gorski len += t->len; 226b17de076SJonas Gorski 227b17de076SJonas Gorski t = list_entry(t->transfer_list.next, struct spi_transfer, 228b17de076SJonas Gorski transfer_list); 229b17de076SJonas Gorski } 230c0fde3baSJonas Gorski 231c0fde3baSJonas Gorski return 0; 232b42dfed8SFlorian Fainelli } 233b42dfed8SFlorian Fainelli 234cde4384eSFlorian Fainelli static int bcm63xx_spi_transfer_one(struct spi_master *master, 235cde4384eSFlorian Fainelli struct spi_message *m) 236cde4384eSFlorian Fainelli { 237cde4384eSFlorian Fainelli struct bcm63xx_spi *bs = spi_master_get_devdata(master); 238b17de076SJonas Gorski struct spi_transfer *t, *first = NULL; 239cde4384eSFlorian Fainelli struct spi_device *spi = m->spi; 240cde4384eSFlorian Fainelli int status = 0; 241b17de076SJonas Gorski unsigned int n_transfers = 0, total_len = 0; 242b17de076SJonas Gorski bool can_use_prepend = false; 243cde4384eSFlorian Fainelli 244b17de076SJonas Gorski /* 245b17de076SJonas Gorski * This SPI controller does not support keeping CS active after a 246b17de076SJonas Gorski * transfer. 247b17de076SJonas Gorski * Work around this by merging as many transfers we can into one big 248b17de076SJonas Gorski * full-duplex transfers. 249b17de076SJonas Gorski */ 250cde4384eSFlorian Fainelli list_for_each_entry(t, &m->transfers, transfer_list) { 251b17de076SJonas Gorski if (!first) 252b17de076SJonas Gorski first = t; 253b17de076SJonas Gorski 254b17de076SJonas Gorski n_transfers++; 255b17de076SJonas Gorski total_len += t->len; 256b17de076SJonas Gorski 257b17de076SJonas Gorski if (n_transfers == 2 && !first->rx_buf && !t->tx_buf && 258b17de076SJonas Gorski first->len <= BCM63XX_SPI_MAX_PREPEND) 259b17de076SJonas Gorski can_use_prepend = true; 260b17de076SJonas Gorski else if (can_use_prepend && t->tx_buf) 261b17de076SJonas Gorski can_use_prepend = false; 262b17de076SJonas Gorski 263c0fde3baSJonas Gorski /* we can only transfer one fifo worth of data */ 264b17de076SJonas Gorski if ((can_use_prepend && 265b17de076SJonas Gorski total_len > (bs->fifo_size + BCM63XX_SPI_MAX_PREPEND)) || 266b17de076SJonas Gorski (!can_use_prepend && total_len > bs->fifo_size)) { 267c0fde3baSJonas Gorski dev_err(&spi->dev, "unable to do transfers larger than FIFO size (%i > %i)\n", 268b17de076SJonas Gorski total_len, bs->fifo_size); 269b17de076SJonas Gorski status = -EINVAL; 270b17de076SJonas Gorski goto exit; 271b17de076SJonas Gorski } 272b17de076SJonas Gorski 273b17de076SJonas Gorski /* all combined transfers have to have the same speed */ 274b17de076SJonas Gorski if (t->speed_hz != first->speed_hz) { 275b17de076SJonas Gorski dev_err(&spi->dev, "unable to change speed between transfers\n"); 276c0fde3baSJonas Gorski status = -EINVAL; 277cde4384eSFlorian Fainelli goto exit; 278cde4384eSFlorian Fainelli } 279cde4384eSFlorian Fainelli 280c0fde3baSJonas Gorski /* CS will be deasserted directly after transfer */ 281c0fde3baSJonas Gorski if (t->delay_usecs) { 282c0fde3baSJonas Gorski dev_err(&spi->dev, "unable to keep CS asserted after transfer\n"); 283c0fde3baSJonas Gorski status = -EINVAL; 284c0fde3baSJonas Gorski goto exit; 285cde4384eSFlorian Fainelli } 286cde4384eSFlorian Fainelli 287b17de076SJonas Gorski if (t->cs_change || 288b17de076SJonas Gorski list_is_last(&t->transfer_list, &m->transfers)) { 289c0fde3baSJonas Gorski /* configure adapter for a new transfer */ 290b17de076SJonas Gorski bcm63xx_spi_setup_transfer(spi, first); 291c0fde3baSJonas Gorski 292c0fde3baSJonas Gorski /* send the data */ 293b17de076SJonas Gorski status = bcm63xx_txrx_bufs(spi, first, n_transfers); 294c0fde3baSJonas Gorski if (status) 295c0fde3baSJonas Gorski goto exit; 296c0fde3baSJonas Gorski 297b17de076SJonas Gorski m->actual_length += total_len; 298b17de076SJonas Gorski 299b17de076SJonas Gorski first = NULL; 300b17de076SJonas Gorski n_transfers = 0; 301b17de076SJonas Gorski total_len = 0; 302b17de076SJonas Gorski can_use_prepend = false; 303b17de076SJonas Gorski } 304cde4384eSFlorian Fainelli } 305cde4384eSFlorian Fainelli exit: 306cde4384eSFlorian Fainelli m->status = status; 307cde4384eSFlorian Fainelli spi_finalize_current_message(master); 308cde4384eSFlorian Fainelli 309cde4384eSFlorian Fainelli return 0; 310b42dfed8SFlorian Fainelli } 311b42dfed8SFlorian Fainelli 312b42dfed8SFlorian Fainelli /* This driver supports single master mode only. Hence 313b42dfed8SFlorian Fainelli * CMD_DONE is the only interrupt we care about 314b42dfed8SFlorian Fainelli */ 315b42dfed8SFlorian Fainelli static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id) 316b42dfed8SFlorian Fainelli { 317b42dfed8SFlorian Fainelli struct spi_master *master = (struct spi_master *)dev_id; 318b42dfed8SFlorian Fainelli struct bcm63xx_spi *bs = spi_master_get_devdata(master); 319b42dfed8SFlorian Fainelli u8 intr; 320b42dfed8SFlorian Fainelli 321b42dfed8SFlorian Fainelli /* Read interupts and clear them immediately */ 322b42dfed8SFlorian Fainelli intr = bcm_spi_readb(bs, SPI_INT_STATUS); 323b42dfed8SFlorian Fainelli bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); 324b42dfed8SFlorian Fainelli bcm_spi_writeb(bs, 0, SPI_INT_MASK); 325b42dfed8SFlorian Fainelli 326cde4384eSFlorian Fainelli /* A transfer completed */ 327cde4384eSFlorian Fainelli if (intr & SPI_INTR_CMD_DONE) 328b42dfed8SFlorian Fainelli complete(&bs->done); 329b42dfed8SFlorian Fainelli 330b42dfed8SFlorian Fainelli return IRQ_HANDLED; 331b42dfed8SFlorian Fainelli } 332b42dfed8SFlorian Fainelli 333b42dfed8SFlorian Fainelli 334fd4a319bSGrant Likely static int bcm63xx_spi_probe(struct platform_device *pdev) 335b42dfed8SFlorian Fainelli { 336b42dfed8SFlorian Fainelli struct resource *r; 337b42dfed8SFlorian Fainelli struct device *dev = &pdev->dev; 338b42dfed8SFlorian Fainelli struct bcm63xx_spi_pdata *pdata = pdev->dev.platform_data; 339b42dfed8SFlorian Fainelli int irq; 340b42dfed8SFlorian Fainelli struct spi_master *master; 341b42dfed8SFlorian Fainelli struct clk *clk; 342b42dfed8SFlorian Fainelli struct bcm63xx_spi *bs; 343b42dfed8SFlorian Fainelli int ret; 344b42dfed8SFlorian Fainelli 345b42dfed8SFlorian Fainelli r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 346b42dfed8SFlorian Fainelli if (!r) { 347b42dfed8SFlorian Fainelli dev_err(dev, "no iomem\n"); 348b42dfed8SFlorian Fainelli ret = -ENXIO; 349b42dfed8SFlorian Fainelli goto out; 350b42dfed8SFlorian Fainelli } 351b42dfed8SFlorian Fainelli 352b42dfed8SFlorian Fainelli irq = platform_get_irq(pdev, 0); 353b42dfed8SFlorian Fainelli if (irq < 0) { 354b42dfed8SFlorian Fainelli dev_err(dev, "no irq\n"); 355b42dfed8SFlorian Fainelli ret = -ENXIO; 356b42dfed8SFlorian Fainelli goto out; 357b42dfed8SFlorian Fainelli } 358b42dfed8SFlorian Fainelli 359b42dfed8SFlorian Fainelli clk = clk_get(dev, "spi"); 360b42dfed8SFlorian Fainelli if (IS_ERR(clk)) { 361b42dfed8SFlorian Fainelli dev_err(dev, "no clock for device\n"); 362b42dfed8SFlorian Fainelli ret = PTR_ERR(clk); 363b42dfed8SFlorian Fainelli goto out; 364b42dfed8SFlorian Fainelli } 365b42dfed8SFlorian Fainelli 366b42dfed8SFlorian Fainelli master = spi_alloc_master(dev, sizeof(*bs)); 367b42dfed8SFlorian Fainelli if (!master) { 368b42dfed8SFlorian Fainelli dev_err(dev, "out of memory\n"); 369b42dfed8SFlorian Fainelli ret = -ENOMEM; 370b42dfed8SFlorian Fainelli goto out_clk; 371b42dfed8SFlorian Fainelli } 372b42dfed8SFlorian Fainelli 373b42dfed8SFlorian Fainelli bs = spi_master_get_devdata(master); 374b42dfed8SFlorian Fainelli 375b42dfed8SFlorian Fainelli platform_set_drvdata(pdev, master); 376b42dfed8SFlorian Fainelli bs->pdev = pdev; 377b42dfed8SFlorian Fainelli 378b66c7730SJonas Gorski bs->regs = devm_ioremap_resource(&pdev->dev, r); 379b66c7730SJonas Gorski if (IS_ERR(bs->regs)) { 380b66c7730SJonas Gorski ret = PTR_ERR(bs->regs); 381b42dfed8SFlorian Fainelli goto out_err; 382b42dfed8SFlorian Fainelli } 383b42dfed8SFlorian Fainelli 384b42dfed8SFlorian Fainelli bs->irq = irq; 385b42dfed8SFlorian Fainelli bs->clk = clk; 386b42dfed8SFlorian Fainelli bs->fifo_size = pdata->fifo_size; 387b42dfed8SFlorian Fainelli 388b42dfed8SFlorian Fainelli ret = devm_request_irq(&pdev->dev, irq, bcm63xx_spi_interrupt, 0, 389b42dfed8SFlorian Fainelli pdev->name, master); 390b42dfed8SFlorian Fainelli if (ret) { 391b42dfed8SFlorian Fainelli dev_err(dev, "unable to request irq\n"); 392b42dfed8SFlorian Fainelli goto out_err; 393b42dfed8SFlorian Fainelli } 394b42dfed8SFlorian Fainelli 395b42dfed8SFlorian Fainelli master->bus_num = pdata->bus_num; 396b42dfed8SFlorian Fainelli master->num_chipselect = pdata->num_chipselect; 397cde4384eSFlorian Fainelli master->transfer_one_message = bcm63xx_spi_transfer_one; 39888a3a255SFlorian Fainelli master->mode_bits = MODEBITS; 39924778be2SStephen Warren master->bits_per_word_mask = SPI_BPW_MASK(8); 400*5355d96dSMark Brown master->auto_runtime_pm = true; 4015a670445SFlorian Fainelli bs->msg_type_shift = pdata->msg_type_shift; 4025a670445SFlorian Fainelli bs->msg_ctl_width = pdata->msg_ctl_width; 403b42dfed8SFlorian Fainelli bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA)); 404b42dfed8SFlorian Fainelli bs->rx_io = (const u8 *)(bs->regs + bcm63xx_spireg(SPI_RX_DATA)); 405b42dfed8SFlorian Fainelli 4065a670445SFlorian Fainelli switch (bs->msg_ctl_width) { 4075a670445SFlorian Fainelli case 8: 4085a670445SFlorian Fainelli case 16: 4095a670445SFlorian Fainelli break; 4105a670445SFlorian Fainelli default: 4115a670445SFlorian Fainelli dev_err(dev, "unsupported MSG_CTL width: %d\n", 4125a670445SFlorian Fainelli bs->msg_ctl_width); 413b435ff21SJonas Gorski goto out_err; 4145a670445SFlorian Fainelli } 4155a670445SFlorian Fainelli 416b42dfed8SFlorian Fainelli /* Initialize hardware */ 4174fbb82a7SJonas Gorski clk_prepare_enable(bs->clk); 418b42dfed8SFlorian Fainelli bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); 419b42dfed8SFlorian Fainelli 420b42dfed8SFlorian Fainelli /* register and we are done */ 421b42dfed8SFlorian Fainelli ret = spi_register_master(master); 422b42dfed8SFlorian Fainelli if (ret) { 423b42dfed8SFlorian Fainelli dev_err(dev, "spi register failed\n"); 424b42dfed8SFlorian Fainelli goto out_clk_disable; 425b42dfed8SFlorian Fainelli } 426b42dfed8SFlorian Fainelli 42761d15963SFlorian Fainelli dev_info(dev, "at 0x%08x (irq %d, FIFOs size %d)\n", 42861d15963SFlorian Fainelli r->start, irq, bs->fifo_size); 429b42dfed8SFlorian Fainelli 430b42dfed8SFlorian Fainelli return 0; 431b42dfed8SFlorian Fainelli 432b42dfed8SFlorian Fainelli out_clk_disable: 4334fbb82a7SJonas Gorski clk_disable_unprepare(clk); 434b42dfed8SFlorian Fainelli out_err: 435b42dfed8SFlorian Fainelli spi_master_put(master); 436b42dfed8SFlorian Fainelli out_clk: 437b42dfed8SFlorian Fainelli clk_put(clk); 438b42dfed8SFlorian Fainelli out: 439b42dfed8SFlorian Fainelli return ret; 440b42dfed8SFlorian Fainelli } 441b42dfed8SFlorian Fainelli 442fd4a319bSGrant Likely static int bcm63xx_spi_remove(struct platform_device *pdev) 443b42dfed8SFlorian Fainelli { 4441f682378SGuenter Roeck struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); 445b42dfed8SFlorian Fainelli struct bcm63xx_spi *bs = spi_master_get_devdata(master); 446b42dfed8SFlorian Fainelli 4471e41dc0eSFlorian Fainelli spi_unregister_master(master); 4481e41dc0eSFlorian Fainelli 449b42dfed8SFlorian Fainelli /* reset spi block */ 450b42dfed8SFlorian Fainelli bcm_spi_writeb(bs, 0, SPI_INT_MASK); 451b42dfed8SFlorian Fainelli 452b42dfed8SFlorian Fainelli /* HW shutdown */ 4534fbb82a7SJonas Gorski clk_disable_unprepare(bs->clk); 454b42dfed8SFlorian Fainelli clk_put(bs->clk); 455b42dfed8SFlorian Fainelli 4561f682378SGuenter Roeck spi_master_put(master); 4571f682378SGuenter Roeck 458b42dfed8SFlorian Fainelli return 0; 459b42dfed8SFlorian Fainelli } 460b42dfed8SFlorian Fainelli 461b42dfed8SFlorian Fainelli #ifdef CONFIG_PM 462b42dfed8SFlorian Fainelli static int bcm63xx_spi_suspend(struct device *dev) 463b42dfed8SFlorian Fainelli { 464b42dfed8SFlorian Fainelli struct spi_master *master = 465b42dfed8SFlorian Fainelli platform_get_drvdata(to_platform_device(dev)); 466b42dfed8SFlorian Fainelli struct bcm63xx_spi *bs = spi_master_get_devdata(master); 467b42dfed8SFlorian Fainelli 46896519957SFlorian Fainelli spi_master_suspend(master); 46996519957SFlorian Fainelli 4704fbb82a7SJonas Gorski clk_disable_unprepare(bs->clk); 471b42dfed8SFlorian Fainelli 472b42dfed8SFlorian Fainelli return 0; 473b42dfed8SFlorian Fainelli } 474b42dfed8SFlorian Fainelli 475b42dfed8SFlorian Fainelli static int bcm63xx_spi_resume(struct device *dev) 476b42dfed8SFlorian Fainelli { 477b42dfed8SFlorian Fainelli struct spi_master *master = 478b42dfed8SFlorian Fainelli platform_get_drvdata(to_platform_device(dev)); 479b42dfed8SFlorian Fainelli struct bcm63xx_spi *bs = spi_master_get_devdata(master); 480b42dfed8SFlorian Fainelli 4814fbb82a7SJonas Gorski clk_prepare_enable(bs->clk); 482b42dfed8SFlorian Fainelli 48396519957SFlorian Fainelli spi_master_resume(master); 48496519957SFlorian Fainelli 485b42dfed8SFlorian Fainelli return 0; 486b42dfed8SFlorian Fainelli } 487b42dfed8SFlorian Fainelli 488b42dfed8SFlorian Fainelli static const struct dev_pm_ops bcm63xx_spi_pm_ops = { 489b42dfed8SFlorian Fainelli .suspend = bcm63xx_spi_suspend, 490b42dfed8SFlorian Fainelli .resume = bcm63xx_spi_resume, 491b42dfed8SFlorian Fainelli }; 492b42dfed8SFlorian Fainelli 493b42dfed8SFlorian Fainelli #define BCM63XX_SPI_PM_OPS (&bcm63xx_spi_pm_ops) 494b42dfed8SFlorian Fainelli #else 495b42dfed8SFlorian Fainelli #define BCM63XX_SPI_PM_OPS NULL 496b42dfed8SFlorian Fainelli #endif 497b42dfed8SFlorian Fainelli 498b42dfed8SFlorian Fainelli static struct platform_driver bcm63xx_spi_driver = { 499b42dfed8SFlorian Fainelli .driver = { 500b42dfed8SFlorian Fainelli .name = "bcm63xx-spi", 501b42dfed8SFlorian Fainelli .owner = THIS_MODULE, 502b42dfed8SFlorian Fainelli .pm = BCM63XX_SPI_PM_OPS, 503b42dfed8SFlorian Fainelli }, 504b42dfed8SFlorian Fainelli .probe = bcm63xx_spi_probe, 505fd4a319bSGrant Likely .remove = bcm63xx_spi_remove, 506b42dfed8SFlorian Fainelli }; 507b42dfed8SFlorian Fainelli 508b42dfed8SFlorian Fainelli module_platform_driver(bcm63xx_spi_driver); 509b42dfed8SFlorian Fainelli 510b42dfed8SFlorian Fainelli MODULE_ALIAS("platform:bcm63xx_spi"); 511b42dfed8SFlorian Fainelli MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); 512b42dfed8SFlorian Fainelli MODULE_AUTHOR("Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>"); 513b42dfed8SFlorian Fainelli MODULE_DESCRIPTION("Broadcom BCM63xx SPI Controller driver"); 514b42dfed8SFlorian Fainelli MODULE_LICENSE("GPL"); 515