1a38a2233SWilliam Zhang // SPDX-License-Identifier: GPL-2.0-only 2a38a2233SWilliam Zhang /* 3a38a2233SWilliam Zhang * Broadcom BCMBCA High Speed SPI Controller driver 4a38a2233SWilliam Zhang * 5a38a2233SWilliam Zhang * Copyright 2000-2010 Broadcom Corporation 6*879a879cSJonas Gorski * Copyright 2012-2013 Jonas Gorski <jonas.gorski@gmail.com> 7a38a2233SWilliam Zhang * Copyright 2019-2022 Broadcom Ltd 8a38a2233SWilliam Zhang */ 9a38a2233SWilliam Zhang 10a38a2233SWilliam Zhang #include <linux/kernel.h> 11a38a2233SWilliam Zhang #include <linux/init.h> 12a38a2233SWilliam Zhang #include <linux/io.h> 13a38a2233SWilliam Zhang #include <linux/clk.h> 14a38a2233SWilliam Zhang #include <linux/module.h> 15a38a2233SWilliam Zhang #include <linux/platform_device.h> 16a38a2233SWilliam Zhang #include <linux/delay.h> 17a38a2233SWilliam Zhang #include <linux/dma-mapping.h> 18a38a2233SWilliam Zhang #include <linux/err.h> 19a38a2233SWilliam Zhang #include <linux/interrupt.h> 20a38a2233SWilliam Zhang #include <linux/spi/spi.h> 21a38a2233SWilliam Zhang #include <linux/mutex.h> 22a38a2233SWilliam Zhang #include <linux/of.h> 23a38a2233SWilliam Zhang #include <linux/spi/spi-mem.h> 24a38a2233SWilliam Zhang #include <linux/pm_runtime.h> 25a38a2233SWilliam Zhang 26a38a2233SWilliam Zhang #define HSSPI_GLOBAL_CTRL_REG 0x0 27a38a2233SWilliam Zhang #define GLOBAL_CTRL_CS_POLARITY_SHIFT 0 28a38a2233SWilliam Zhang #define GLOBAL_CTRL_CS_POLARITY_MASK 0x000000ff 29a38a2233SWilliam Zhang #define GLOBAL_CTRL_PLL_CLK_CTRL_SHIFT 8 30a38a2233SWilliam Zhang #define GLOBAL_CTRL_PLL_CLK_CTRL_MASK 0x0000ff00 31a38a2233SWilliam Zhang #define GLOBAL_CTRL_CLK_GATE_SSOFF BIT(16) 32a38a2233SWilliam Zhang #define GLOBAL_CTRL_CLK_POLARITY BIT(17) 33a38a2233SWilliam Zhang #define GLOBAL_CTRL_MOSI_IDLE BIT(18) 34a38a2233SWilliam Zhang 35a38a2233SWilliam Zhang #define HSSPI_GLOBAL_EXT_TRIGGER_REG 0x4 36a38a2233SWilliam Zhang 37a38a2233SWilliam Zhang #define HSSPI_INT_STATUS_REG 0x8 38a38a2233SWilliam Zhang #define HSSPI_INT_STATUS_MASKED_REG 0xc 39a38a2233SWilliam Zhang #define HSSPI_INT_MASK_REG 0x10 40a38a2233SWilliam Zhang 41a38a2233SWilliam Zhang #define HSSPI_PINGx_CMD_DONE(i) BIT((i * 8) + 0) 42a38a2233SWilliam Zhang #define HSSPI_PINGx_RX_OVER(i) BIT((i * 8) + 1) 43a38a2233SWilliam Zhang #define HSSPI_PINGx_TX_UNDER(i) BIT((i * 8) + 2) 44a38a2233SWilliam Zhang #define HSSPI_PINGx_POLL_TIMEOUT(i) BIT((i * 8) + 3) 45a38a2233SWilliam Zhang #define HSSPI_PINGx_CTRL_INVAL(i) BIT((i * 8) + 4) 46a38a2233SWilliam Zhang 47a38a2233SWilliam Zhang #define HSSPI_INT_CLEAR_ALL 0xff001f1f 48a38a2233SWilliam Zhang 49a38a2233SWilliam Zhang #define HSSPI_PINGPONG_COMMAND_REG(x) (0x80 + (x) * 0x40) 50a38a2233SWilliam Zhang #define PINGPONG_CMD_COMMAND_MASK 0xf 51a38a2233SWilliam Zhang #define PINGPONG_COMMAND_NOOP 0 52a38a2233SWilliam Zhang #define PINGPONG_COMMAND_START_NOW 1 53a38a2233SWilliam Zhang #define PINGPONG_COMMAND_START_TRIGGER 2 54a38a2233SWilliam Zhang #define PINGPONG_COMMAND_HALT 3 55a38a2233SWilliam Zhang #define PINGPONG_COMMAND_FLUSH 4 56a38a2233SWilliam Zhang #define PINGPONG_CMD_PROFILE_SHIFT 8 57a38a2233SWilliam Zhang #define PINGPONG_CMD_SS_SHIFT 12 58a38a2233SWilliam Zhang 59a38a2233SWilliam Zhang #define HSSPI_PINGPONG_STATUS_REG(x) (0x84 + (x) * 0x40) 60a38a2233SWilliam Zhang #define HSSPI_PINGPONG_STATUS_SRC_BUSY BIT(1) 61a38a2233SWilliam Zhang 62a38a2233SWilliam Zhang #define HSSPI_PROFILE_CLK_CTRL_REG(x) (0x100 + (x) * 0x20) 63a38a2233SWilliam Zhang #define CLK_CTRL_FREQ_CTRL_MASK 0x0000ffff 64a38a2233SWilliam Zhang #define CLK_CTRL_SPI_CLK_2X_SEL BIT(14) 65a38a2233SWilliam Zhang #define CLK_CTRL_ACCUM_RST_ON_LOOP BIT(15) 66a38a2233SWilliam Zhang #define CLK_CTRL_CLK_POLARITY BIT(16) 67a38a2233SWilliam Zhang 68a38a2233SWilliam Zhang #define HSSPI_PROFILE_SIGNAL_CTRL_REG(x) (0x104 + (x) * 0x20) 69a38a2233SWilliam Zhang #define SIGNAL_CTRL_LATCH_RISING BIT(12) 70a38a2233SWilliam Zhang #define SIGNAL_CTRL_LAUNCH_RISING BIT(13) 71a38a2233SWilliam Zhang #define SIGNAL_CTRL_ASYNC_INPUT_PATH BIT(16) 72a38a2233SWilliam Zhang 73a38a2233SWilliam Zhang #define HSSPI_PROFILE_MODE_CTRL_REG(x) (0x108 + (x) * 0x20) 74a38a2233SWilliam Zhang #define MODE_CTRL_MULTIDATA_RD_STRT_SHIFT 8 75a38a2233SWilliam Zhang #define MODE_CTRL_MULTIDATA_WR_STRT_SHIFT 12 76a38a2233SWilliam Zhang #define MODE_CTRL_MULTIDATA_RD_SIZE_SHIFT 16 77a38a2233SWilliam Zhang #define MODE_CTRL_MULTIDATA_WR_SIZE_SHIFT 18 78a38a2233SWilliam Zhang #define MODE_CTRL_MODE_3WIRE BIT(20) 79a38a2233SWilliam Zhang #define MODE_CTRL_PREPENDBYTE_CNT_SHIFT 24 80a38a2233SWilliam Zhang 81a38a2233SWilliam Zhang #define HSSPI_FIFO_REG(x) (0x200 + (x) * 0x200) 82a38a2233SWilliam Zhang 83a38a2233SWilliam Zhang #define HSSPI_OP_MULTIBIT BIT(11) 84a38a2233SWilliam Zhang #define HSSPI_OP_CODE_SHIFT 13 85a38a2233SWilliam Zhang #define HSSPI_OP_SLEEP (0 << HSSPI_OP_CODE_SHIFT) 86a38a2233SWilliam Zhang #define HSSPI_OP_READ_WRITE (1 << HSSPI_OP_CODE_SHIFT) 87a38a2233SWilliam Zhang #define HSSPI_OP_WRITE (2 << HSSPI_OP_CODE_SHIFT) 88a38a2233SWilliam Zhang #define HSSPI_OP_READ (3 << HSSPI_OP_CODE_SHIFT) 89a38a2233SWilliam Zhang #define HSSPI_OP_SETIRQ (4 << HSSPI_OP_CODE_SHIFT) 90a38a2233SWilliam Zhang 91a38a2233SWilliam Zhang #define HSSPI_BUFFER_LEN 512 92a38a2233SWilliam Zhang #define HSSPI_OPCODE_LEN 2 93a38a2233SWilliam Zhang 94a38a2233SWilliam Zhang #define HSSPI_MAX_PREPEND_LEN 15 95a38a2233SWilliam Zhang 96a38a2233SWilliam Zhang #define HSSPI_MAX_SYNC_CLOCK 30000000 97a38a2233SWilliam Zhang 98a38a2233SWilliam Zhang #define HSSPI_SPI_MAX_CS 8 99a38a2233SWilliam Zhang #define HSSPI_BUS_NUM 1 /* 0 is legacy SPI */ 100a38a2233SWilliam Zhang #define HSSPI_POLL_STATUS_TIMEOUT_MS 100 101a38a2233SWilliam Zhang 102a38a2233SWilliam Zhang #define HSSPI_WAIT_MODE_POLLING 0 103a38a2233SWilliam Zhang #define HSSPI_WAIT_MODE_INTR 1 104a38a2233SWilliam Zhang #define HSSPI_WAIT_MODE_MAX HSSPI_WAIT_MODE_INTR 105a38a2233SWilliam Zhang 106a38a2233SWilliam Zhang #define SPIM_CTRL_CS_OVERRIDE_SEL_SHIFT 0 107a38a2233SWilliam Zhang #define SPIM_CTRL_CS_OVERRIDE_SEL_MASK 0xff 108a38a2233SWilliam Zhang #define SPIM_CTRL_CS_OVERRIDE_VAL_SHIFT 8 109a38a2233SWilliam Zhang #define SPIM_CTRL_CS_OVERRIDE_VAL_MASK 0xff 110a38a2233SWilliam Zhang 111a38a2233SWilliam Zhang struct bcmbca_hsspi { 112a38a2233SWilliam Zhang struct completion done; 113a38a2233SWilliam Zhang struct mutex bus_mutex; 114a38a2233SWilliam Zhang struct mutex msg_mutex; 115a38a2233SWilliam Zhang struct platform_device *pdev; 116a38a2233SWilliam Zhang struct clk *clk; 117a38a2233SWilliam Zhang struct clk *pll_clk; 118a38a2233SWilliam Zhang void __iomem *regs; 119a38a2233SWilliam Zhang void __iomem *spim_ctrl; 120a38a2233SWilliam Zhang u8 __iomem *fifo; 121a38a2233SWilliam Zhang u32 speed_hz; 122a38a2233SWilliam Zhang u8 cs_polarity; 123a38a2233SWilliam Zhang u32 wait_mode; 124a38a2233SWilliam Zhang }; 125a38a2233SWilliam Zhang 126a38a2233SWilliam Zhang static ssize_t wait_mode_show(struct device *dev, struct device_attribute *attr, 127a38a2233SWilliam Zhang char *buf) 128a38a2233SWilliam Zhang { 129a38a2233SWilliam Zhang struct spi_controller *ctrl = dev_get_drvdata(dev); 130a38a2233SWilliam Zhang struct bcmbca_hsspi *bs = spi_master_get_devdata(ctrl); 131a38a2233SWilliam Zhang 132a38a2233SWilliam Zhang return sprintf(buf, "%d\n", bs->wait_mode); 133a38a2233SWilliam Zhang } 134a38a2233SWilliam Zhang 135a38a2233SWilliam Zhang static ssize_t wait_mode_store(struct device *dev, struct device_attribute *attr, 136a38a2233SWilliam Zhang const char *buf, size_t count) 137a38a2233SWilliam Zhang { 138a38a2233SWilliam Zhang struct spi_controller *ctrl = dev_get_drvdata(dev); 139a38a2233SWilliam Zhang struct bcmbca_hsspi *bs = spi_master_get_devdata(ctrl); 140a38a2233SWilliam Zhang u32 val; 141a38a2233SWilliam Zhang 142a38a2233SWilliam Zhang if (kstrtou32(buf, 10, &val)) 143a38a2233SWilliam Zhang return -EINVAL; 144a38a2233SWilliam Zhang 145a38a2233SWilliam Zhang if (val > HSSPI_WAIT_MODE_MAX) { 146a38a2233SWilliam Zhang dev_warn(dev, "invalid wait mode %u\n", val); 147a38a2233SWilliam Zhang return -EINVAL; 148a38a2233SWilliam Zhang } 149a38a2233SWilliam Zhang 150a38a2233SWilliam Zhang mutex_lock(&bs->msg_mutex); 151a38a2233SWilliam Zhang bs->wait_mode = val; 152a38a2233SWilliam Zhang /* clear interrupt status to avoid spurious int on next transfer */ 153a38a2233SWilliam Zhang if (val == HSSPI_WAIT_MODE_INTR) 154a38a2233SWilliam Zhang __raw_writel(HSSPI_INT_CLEAR_ALL, bs->regs + HSSPI_INT_STATUS_REG); 155a38a2233SWilliam Zhang mutex_unlock(&bs->msg_mutex); 156a38a2233SWilliam Zhang 157a38a2233SWilliam Zhang return count; 158a38a2233SWilliam Zhang } 159a38a2233SWilliam Zhang 160a38a2233SWilliam Zhang static DEVICE_ATTR_RW(wait_mode); 161a38a2233SWilliam Zhang 162a38a2233SWilliam Zhang static struct attribute *bcmbca_hsspi_attrs[] = { 163a38a2233SWilliam Zhang &dev_attr_wait_mode.attr, 164a38a2233SWilliam Zhang NULL, 165a38a2233SWilliam Zhang }; 166a38a2233SWilliam Zhang 167a38a2233SWilliam Zhang static const struct attribute_group bcmbca_hsspi_group = { 168a38a2233SWilliam Zhang .attrs = bcmbca_hsspi_attrs, 169a38a2233SWilliam Zhang }; 170a38a2233SWilliam Zhang 171a38a2233SWilliam Zhang static void bcmbca_hsspi_set_cs(struct bcmbca_hsspi *bs, unsigned int cs, 172a38a2233SWilliam Zhang bool active) 173a38a2233SWilliam Zhang { 174a38a2233SWilliam Zhang u32 reg; 175a38a2233SWilliam Zhang 176a38a2233SWilliam Zhang /* No cs orerriden needed for SS7 internal cs on pcm based voice dev */ 177a38a2233SWilliam Zhang if (cs == 7) 178a38a2233SWilliam Zhang return; 179a38a2233SWilliam Zhang 180a38a2233SWilliam Zhang mutex_lock(&bs->bus_mutex); 181a38a2233SWilliam Zhang 182a38a2233SWilliam Zhang reg = __raw_readl(bs->spim_ctrl); 183a38a2233SWilliam Zhang if (active) 184a38a2233SWilliam Zhang reg |= BIT(cs + SPIM_CTRL_CS_OVERRIDE_SEL_SHIFT); 185a38a2233SWilliam Zhang else 186a38a2233SWilliam Zhang reg &= ~BIT(cs + SPIM_CTRL_CS_OVERRIDE_SEL_SHIFT); 187a38a2233SWilliam Zhang 188a38a2233SWilliam Zhang __raw_writel(reg, bs->spim_ctrl); 189a38a2233SWilliam Zhang 190a38a2233SWilliam Zhang mutex_unlock(&bs->bus_mutex); 191a38a2233SWilliam Zhang } 192a38a2233SWilliam Zhang 193a38a2233SWilliam Zhang static void bcmbca_hsspi_set_clk(struct bcmbca_hsspi *bs, 194a38a2233SWilliam Zhang struct spi_device *spi, int hz) 195a38a2233SWilliam Zhang { 1969e264f3fSAmit Kumar Mahapatra via Alsa-devel unsigned int profile = spi_get_chipselect(spi, 0); 197a38a2233SWilliam Zhang u32 reg; 198a38a2233SWilliam Zhang 199a38a2233SWilliam Zhang reg = DIV_ROUND_UP(2048, DIV_ROUND_UP(bs->speed_hz, hz)); 200a38a2233SWilliam Zhang __raw_writel(CLK_CTRL_ACCUM_RST_ON_LOOP | reg, 201a38a2233SWilliam Zhang bs->regs + HSSPI_PROFILE_CLK_CTRL_REG(profile)); 202a38a2233SWilliam Zhang 203a38a2233SWilliam Zhang reg = __raw_readl(bs->regs + HSSPI_PROFILE_SIGNAL_CTRL_REG(profile)); 204a38a2233SWilliam Zhang if (hz > HSSPI_MAX_SYNC_CLOCK) 205a38a2233SWilliam Zhang reg |= SIGNAL_CTRL_ASYNC_INPUT_PATH; 206a38a2233SWilliam Zhang else 207a38a2233SWilliam Zhang reg &= ~SIGNAL_CTRL_ASYNC_INPUT_PATH; 208a38a2233SWilliam Zhang __raw_writel(reg, bs->regs + HSSPI_PROFILE_SIGNAL_CTRL_REG(profile)); 209a38a2233SWilliam Zhang 210a38a2233SWilliam Zhang mutex_lock(&bs->bus_mutex); 211a38a2233SWilliam Zhang /* setup clock polarity */ 212a38a2233SWilliam Zhang reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG); 213a38a2233SWilliam Zhang reg &= ~GLOBAL_CTRL_CLK_POLARITY; 214a38a2233SWilliam Zhang if (spi->mode & SPI_CPOL) 215a38a2233SWilliam Zhang reg |= GLOBAL_CTRL_CLK_POLARITY; 216a38a2233SWilliam Zhang __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG); 217a38a2233SWilliam Zhang 218a38a2233SWilliam Zhang mutex_unlock(&bs->bus_mutex); 219a38a2233SWilliam Zhang } 220a38a2233SWilliam Zhang 221a38a2233SWilliam Zhang static int bcmbca_hsspi_wait_cmd(struct bcmbca_hsspi *bs, unsigned int cs) 222a38a2233SWilliam Zhang { 223a38a2233SWilliam Zhang unsigned long limit; 224a38a2233SWilliam Zhang u32 reg = 0; 225a38a2233SWilliam Zhang int rc = 0; 226a38a2233SWilliam Zhang 227a38a2233SWilliam Zhang if (bs->wait_mode == HSSPI_WAIT_MODE_INTR) { 228a38a2233SWilliam Zhang if (wait_for_completion_timeout(&bs->done, HZ) == 0) 229a38a2233SWilliam Zhang rc = 1; 230a38a2233SWilliam Zhang } else { 231a38a2233SWilliam Zhang limit = jiffies + msecs_to_jiffies(HSSPI_POLL_STATUS_TIMEOUT_MS); 232a38a2233SWilliam Zhang 233a38a2233SWilliam Zhang while (!time_after(jiffies, limit)) { 234a38a2233SWilliam Zhang reg = __raw_readl(bs->regs + HSSPI_PINGPONG_STATUS_REG(0)); 235a38a2233SWilliam Zhang if (reg & HSSPI_PINGPONG_STATUS_SRC_BUSY) 236a38a2233SWilliam Zhang cpu_relax(); 237a38a2233SWilliam Zhang else 238a38a2233SWilliam Zhang break; 239a38a2233SWilliam Zhang } 240a38a2233SWilliam Zhang if (reg & HSSPI_PINGPONG_STATUS_SRC_BUSY) 241a38a2233SWilliam Zhang rc = 1; 242a38a2233SWilliam Zhang } 243a38a2233SWilliam Zhang 244a38a2233SWilliam Zhang if (rc) 245a38a2233SWilliam Zhang dev_err(&bs->pdev->dev, "transfer timed out!\n"); 246a38a2233SWilliam Zhang 247a38a2233SWilliam Zhang return rc; 248a38a2233SWilliam Zhang } 249a38a2233SWilliam Zhang 250a38a2233SWilliam Zhang static int bcmbca_hsspi_do_txrx(struct spi_device *spi, struct spi_transfer *t, 251a38a2233SWilliam Zhang struct spi_message *msg) 252a38a2233SWilliam Zhang { 253a38a2233SWilliam Zhang struct bcmbca_hsspi *bs = spi_master_get_devdata(spi->master); 2549e264f3fSAmit Kumar Mahapatra via Alsa-devel unsigned int chip_select = spi_get_chipselect(spi, 0); 2552cca486cSWilliam Zhang u16 opcode = 0, val; 256a38a2233SWilliam Zhang int pending = t->len; 257a38a2233SWilliam Zhang int step_size = HSSPI_BUFFER_LEN; 258a38a2233SWilliam Zhang const u8 *tx = t->tx_buf; 259a38a2233SWilliam Zhang u8 *rx = t->rx_buf; 260a38a2233SWilliam Zhang u32 reg = 0, cs_act = 0; 261a38a2233SWilliam Zhang 262a38a2233SWilliam Zhang bcmbca_hsspi_set_clk(bs, spi, t->speed_hz); 263a38a2233SWilliam Zhang 264a38a2233SWilliam Zhang if (tx && rx) 265a38a2233SWilliam Zhang opcode = HSSPI_OP_READ_WRITE; 266a38a2233SWilliam Zhang else if (tx) 267a38a2233SWilliam Zhang opcode = HSSPI_OP_WRITE; 268a38a2233SWilliam Zhang else if (rx) 269a38a2233SWilliam Zhang opcode = HSSPI_OP_READ; 270a38a2233SWilliam Zhang 271a38a2233SWilliam Zhang if (opcode != HSSPI_OP_READ) 272a38a2233SWilliam Zhang step_size -= HSSPI_OPCODE_LEN; 273a38a2233SWilliam Zhang 274a38a2233SWilliam Zhang if ((opcode == HSSPI_OP_READ && t->rx_nbits == SPI_NBITS_DUAL) || 275a38a2233SWilliam Zhang (opcode == HSSPI_OP_WRITE && t->tx_nbits == SPI_NBITS_DUAL)) { 276a38a2233SWilliam Zhang opcode |= HSSPI_OP_MULTIBIT; 277a38a2233SWilliam Zhang 278a38a2233SWilliam Zhang if (t->rx_nbits == SPI_NBITS_DUAL) 279a38a2233SWilliam Zhang reg |= 1 << MODE_CTRL_MULTIDATA_RD_SIZE_SHIFT; 280a38a2233SWilliam Zhang if (t->tx_nbits == SPI_NBITS_DUAL) 281a38a2233SWilliam Zhang reg |= 1 << MODE_CTRL_MULTIDATA_WR_SIZE_SHIFT; 282a38a2233SWilliam Zhang } 283a38a2233SWilliam Zhang 284a38a2233SWilliam Zhang __raw_writel(reg | 0xff, 285a38a2233SWilliam Zhang bs->regs + HSSPI_PROFILE_MODE_CTRL_REG(chip_select)); 286a38a2233SWilliam Zhang 287a38a2233SWilliam Zhang while (pending > 0) { 288a38a2233SWilliam Zhang int curr_step = min_t(int, step_size, pending); 289a38a2233SWilliam Zhang 290a38a2233SWilliam Zhang reinit_completion(&bs->done); 291a38a2233SWilliam Zhang if (tx) { 292a38a2233SWilliam Zhang memcpy_toio(bs->fifo + HSSPI_OPCODE_LEN, tx, curr_step); 293a38a2233SWilliam Zhang tx += curr_step; 294a38a2233SWilliam Zhang } 2952cca486cSWilliam Zhang 2962cca486cSWilliam Zhang *(__be16 *)(&val) = cpu_to_be16(opcode | curr_step); 2972cca486cSWilliam Zhang __raw_writew(val, bs->fifo); 298a38a2233SWilliam Zhang 299a38a2233SWilliam Zhang /* enable interrupt */ 300a38a2233SWilliam Zhang if (bs->wait_mode == HSSPI_WAIT_MODE_INTR) 301a38a2233SWilliam Zhang __raw_writel(HSSPI_PINGx_CMD_DONE(0), 302a38a2233SWilliam Zhang bs->regs + HSSPI_INT_MASK_REG); 303a38a2233SWilliam Zhang 304a38a2233SWilliam Zhang if (!cs_act) { 305a38a2233SWilliam Zhang /* must apply cs signal as close as the cmd starts */ 306a38a2233SWilliam Zhang bcmbca_hsspi_set_cs(bs, chip_select, true); 307a38a2233SWilliam Zhang cs_act = 1; 308a38a2233SWilliam Zhang } 309a38a2233SWilliam Zhang 310a38a2233SWilliam Zhang reg = chip_select << PINGPONG_CMD_SS_SHIFT | 311a38a2233SWilliam Zhang chip_select << PINGPONG_CMD_PROFILE_SHIFT | 312a38a2233SWilliam Zhang PINGPONG_COMMAND_START_NOW; 313a38a2233SWilliam Zhang __raw_writel(reg, bs->regs + HSSPI_PINGPONG_COMMAND_REG(0)); 314a38a2233SWilliam Zhang 3159e264f3fSAmit Kumar Mahapatra via Alsa-devel if (bcmbca_hsspi_wait_cmd(bs, spi_get_chipselect(spi, 0))) 316a38a2233SWilliam Zhang return -ETIMEDOUT; 317a38a2233SWilliam Zhang 318a38a2233SWilliam Zhang pending -= curr_step; 319a38a2233SWilliam Zhang 320a38a2233SWilliam Zhang if (rx) { 321a38a2233SWilliam Zhang memcpy_fromio(rx, bs->fifo, curr_step); 322a38a2233SWilliam Zhang rx += curr_step; 323a38a2233SWilliam Zhang } 324a38a2233SWilliam Zhang } 325a38a2233SWilliam Zhang 326a38a2233SWilliam Zhang return 0; 327a38a2233SWilliam Zhang } 328a38a2233SWilliam Zhang 329a38a2233SWilliam Zhang static int bcmbca_hsspi_setup(struct spi_device *spi) 330a38a2233SWilliam Zhang { 331a38a2233SWilliam Zhang struct bcmbca_hsspi *bs = spi_master_get_devdata(spi->master); 332a38a2233SWilliam Zhang u32 reg; 333a38a2233SWilliam Zhang 334a38a2233SWilliam Zhang reg = __raw_readl(bs->regs + 3359e264f3fSAmit Kumar Mahapatra via Alsa-devel HSSPI_PROFILE_SIGNAL_CTRL_REG(spi_get_chipselect(spi, 0))); 336a38a2233SWilliam Zhang reg &= ~(SIGNAL_CTRL_LAUNCH_RISING | SIGNAL_CTRL_LATCH_RISING); 337a38a2233SWilliam Zhang if (spi->mode & SPI_CPHA) 338a38a2233SWilliam Zhang reg |= SIGNAL_CTRL_LAUNCH_RISING; 339a38a2233SWilliam Zhang else 340a38a2233SWilliam Zhang reg |= SIGNAL_CTRL_LATCH_RISING; 341a38a2233SWilliam Zhang __raw_writel(reg, bs->regs + 3429e264f3fSAmit Kumar Mahapatra via Alsa-devel HSSPI_PROFILE_SIGNAL_CTRL_REG(spi_get_chipselect(spi, 0))); 343a38a2233SWilliam Zhang 344a38a2233SWilliam Zhang mutex_lock(&bs->bus_mutex); 345a38a2233SWilliam Zhang reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG); 346a38a2233SWilliam Zhang 347a38a2233SWilliam Zhang if (spi->mode & SPI_CS_HIGH) 3489e264f3fSAmit Kumar Mahapatra via Alsa-devel reg |= BIT(spi_get_chipselect(spi, 0)); 349a38a2233SWilliam Zhang else 3509e264f3fSAmit Kumar Mahapatra via Alsa-devel reg &= ~BIT(spi_get_chipselect(spi, 0)); 351a38a2233SWilliam Zhang __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG); 352a38a2233SWilliam Zhang 353a38a2233SWilliam Zhang if (spi->mode & SPI_CS_HIGH) 3549e264f3fSAmit Kumar Mahapatra via Alsa-devel bs->cs_polarity |= BIT(spi_get_chipselect(spi, 0)); 355a38a2233SWilliam Zhang else 3569e264f3fSAmit Kumar Mahapatra via Alsa-devel bs->cs_polarity &= ~BIT(spi_get_chipselect(spi, 0)); 357a38a2233SWilliam Zhang 358a38a2233SWilliam Zhang reg = __raw_readl(bs->spim_ctrl); 3599e264f3fSAmit Kumar Mahapatra via Alsa-devel reg &= ~BIT(spi_get_chipselect(spi, 0) + SPIM_CTRL_CS_OVERRIDE_VAL_SHIFT); 360a38a2233SWilliam Zhang if (spi->mode & SPI_CS_HIGH) 3619e264f3fSAmit Kumar Mahapatra via Alsa-devel reg |= BIT(spi_get_chipselect(spi, 0) + SPIM_CTRL_CS_OVERRIDE_VAL_SHIFT); 362a38a2233SWilliam Zhang __raw_writel(reg, bs->spim_ctrl); 363a38a2233SWilliam Zhang 364a38a2233SWilliam Zhang mutex_unlock(&bs->bus_mutex); 365a38a2233SWilliam Zhang 366a38a2233SWilliam Zhang return 0; 367a38a2233SWilliam Zhang } 368a38a2233SWilliam Zhang 369a38a2233SWilliam Zhang static int bcmbca_hsspi_transfer_one(struct spi_master *master, 370a38a2233SWilliam Zhang struct spi_message *msg) 371a38a2233SWilliam Zhang { 372a38a2233SWilliam Zhang struct bcmbca_hsspi *bs = spi_master_get_devdata(master); 373a38a2233SWilliam Zhang struct spi_transfer *t; 374a38a2233SWilliam Zhang struct spi_device *spi = msg->spi; 375a38a2233SWilliam Zhang int status = -EINVAL; 376a38a2233SWilliam Zhang bool keep_cs = false; 377a38a2233SWilliam Zhang 378a38a2233SWilliam Zhang mutex_lock(&bs->msg_mutex); 379a38a2233SWilliam Zhang list_for_each_entry(t, &msg->transfers, transfer_list) { 380a38a2233SWilliam Zhang status = bcmbca_hsspi_do_txrx(spi, t, msg); 381a38a2233SWilliam Zhang if (status) 382a38a2233SWilliam Zhang break; 383a38a2233SWilliam Zhang 384a38a2233SWilliam Zhang spi_transfer_delay_exec(t); 385a38a2233SWilliam Zhang 386a38a2233SWilliam Zhang if (t->cs_change) { 387a38a2233SWilliam Zhang if (list_is_last(&t->transfer_list, &msg->transfers)) { 388a38a2233SWilliam Zhang keep_cs = true; 389a38a2233SWilliam Zhang } else { 390a38a2233SWilliam Zhang if (!t->cs_off) 3919e264f3fSAmit Kumar Mahapatra via Alsa-devel bcmbca_hsspi_set_cs(bs, spi_get_chipselect(spi, 0), false); 392a38a2233SWilliam Zhang 393a38a2233SWilliam Zhang spi_transfer_cs_change_delay_exec(msg, t); 394a38a2233SWilliam Zhang 395a38a2233SWilliam Zhang if (!list_next_entry(t, transfer_list)->cs_off) 3969e264f3fSAmit Kumar Mahapatra via Alsa-devel bcmbca_hsspi_set_cs(bs, spi_get_chipselect(spi, 0), true); 397a38a2233SWilliam Zhang } 398a38a2233SWilliam Zhang } else if (!list_is_last(&t->transfer_list, &msg->transfers) && 399a38a2233SWilliam Zhang t->cs_off != list_next_entry(t, transfer_list)->cs_off) { 4009e264f3fSAmit Kumar Mahapatra via Alsa-devel bcmbca_hsspi_set_cs(bs, spi_get_chipselect(spi, 0), t->cs_off); 401a38a2233SWilliam Zhang } 402a38a2233SWilliam Zhang 403a38a2233SWilliam Zhang msg->actual_length += t->len; 404a38a2233SWilliam Zhang } 405a38a2233SWilliam Zhang 406a38a2233SWilliam Zhang mutex_unlock(&bs->msg_mutex); 407a38a2233SWilliam Zhang 408a38a2233SWilliam Zhang if (status || !keep_cs) 4099e264f3fSAmit Kumar Mahapatra via Alsa-devel bcmbca_hsspi_set_cs(bs, spi_get_chipselect(spi, 0), false); 410a38a2233SWilliam Zhang 411a38a2233SWilliam Zhang msg->status = status; 412a38a2233SWilliam Zhang spi_finalize_current_message(master); 413a38a2233SWilliam Zhang 414a38a2233SWilliam Zhang return 0; 415a38a2233SWilliam Zhang } 416a38a2233SWilliam Zhang 417a38a2233SWilliam Zhang static irqreturn_t bcmbca_hsspi_interrupt(int irq, void *dev_id) 418a38a2233SWilliam Zhang { 419a38a2233SWilliam Zhang struct bcmbca_hsspi *bs = (struct bcmbca_hsspi *)dev_id; 420a38a2233SWilliam Zhang 421a38a2233SWilliam Zhang if (__raw_readl(bs->regs + HSSPI_INT_STATUS_MASKED_REG) == 0) 422a38a2233SWilliam Zhang return IRQ_NONE; 423a38a2233SWilliam Zhang 424a38a2233SWilliam Zhang __raw_writel(HSSPI_INT_CLEAR_ALL, bs->regs + HSSPI_INT_STATUS_REG); 425a38a2233SWilliam Zhang __raw_writel(0, bs->regs + HSSPI_INT_MASK_REG); 426a38a2233SWilliam Zhang 427a38a2233SWilliam Zhang complete(&bs->done); 428a38a2233SWilliam Zhang 429a38a2233SWilliam Zhang return IRQ_HANDLED; 430a38a2233SWilliam Zhang } 431a38a2233SWilliam Zhang 432a38a2233SWilliam Zhang static int bcmbca_hsspi_probe(struct platform_device *pdev) 433a38a2233SWilliam Zhang { 434a38a2233SWilliam Zhang struct spi_master *master; 435a38a2233SWilliam Zhang struct bcmbca_hsspi *bs; 436a38a2233SWilliam Zhang struct resource *res_mem; 437a38a2233SWilliam Zhang void __iomem *spim_ctrl; 438a38a2233SWilliam Zhang void __iomem *regs; 439a38a2233SWilliam Zhang struct device *dev = &pdev->dev; 440a38a2233SWilliam Zhang struct clk *clk, *pll_clk = NULL; 441a38a2233SWilliam Zhang int irq, ret; 442a38a2233SWilliam Zhang u32 reg, rate, num_cs = HSSPI_SPI_MAX_CS; 443a38a2233SWilliam Zhang 444a38a2233SWilliam Zhang irq = platform_get_irq(pdev, 0); 445a38a2233SWilliam Zhang if (irq < 0) 446a38a2233SWilliam Zhang return irq; 447a38a2233SWilliam Zhang 448a38a2233SWilliam Zhang res_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hsspi"); 449a38a2233SWilliam Zhang if (!res_mem) 450a38a2233SWilliam Zhang return -EINVAL; 451a38a2233SWilliam Zhang regs = devm_ioremap_resource(dev, res_mem); 452a38a2233SWilliam Zhang if (IS_ERR(regs)) 453a38a2233SWilliam Zhang return PTR_ERR(regs); 454a38a2233SWilliam Zhang 455a38a2233SWilliam Zhang res_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "spim-ctrl"); 456a38a2233SWilliam Zhang if (!res_mem) 457a38a2233SWilliam Zhang return -EINVAL; 458a38a2233SWilliam Zhang spim_ctrl = devm_ioremap_resource(dev, res_mem); 459a38a2233SWilliam Zhang if (IS_ERR(spim_ctrl)) 460a38a2233SWilliam Zhang return PTR_ERR(spim_ctrl); 461a38a2233SWilliam Zhang 462a38a2233SWilliam Zhang clk = devm_clk_get(dev, "hsspi"); 463a38a2233SWilliam Zhang if (IS_ERR(clk)) 464a38a2233SWilliam Zhang return PTR_ERR(clk); 465a38a2233SWilliam Zhang 466a38a2233SWilliam Zhang ret = clk_prepare_enable(clk); 467a38a2233SWilliam Zhang if (ret) 468a38a2233SWilliam Zhang return ret; 469a38a2233SWilliam Zhang 470a38a2233SWilliam Zhang rate = clk_get_rate(clk); 471a38a2233SWilliam Zhang if (!rate) { 472a38a2233SWilliam Zhang pll_clk = devm_clk_get(dev, "pll"); 473a38a2233SWilliam Zhang 474a38a2233SWilliam Zhang if (IS_ERR(pll_clk)) { 475a38a2233SWilliam Zhang ret = PTR_ERR(pll_clk); 476a38a2233SWilliam Zhang goto out_disable_clk; 477a38a2233SWilliam Zhang } 478a38a2233SWilliam Zhang 479a38a2233SWilliam Zhang ret = clk_prepare_enable(pll_clk); 480a38a2233SWilliam Zhang if (ret) 481a38a2233SWilliam Zhang goto out_disable_clk; 482a38a2233SWilliam Zhang 483a38a2233SWilliam Zhang rate = clk_get_rate(pll_clk); 484a38a2233SWilliam Zhang if (!rate) { 485a38a2233SWilliam Zhang ret = -EINVAL; 486a38a2233SWilliam Zhang goto out_disable_pll_clk; 487a38a2233SWilliam Zhang } 488a38a2233SWilliam Zhang } 489a38a2233SWilliam Zhang 490a38a2233SWilliam Zhang master = spi_alloc_master(&pdev->dev, sizeof(*bs)); 491a38a2233SWilliam Zhang if (!master) { 492a38a2233SWilliam Zhang ret = -ENOMEM; 493a38a2233SWilliam Zhang goto out_disable_pll_clk; 494a38a2233SWilliam Zhang } 495a38a2233SWilliam Zhang 496a38a2233SWilliam Zhang bs = spi_master_get_devdata(master); 497a38a2233SWilliam Zhang bs->pdev = pdev; 498a38a2233SWilliam Zhang bs->clk = clk; 499a38a2233SWilliam Zhang bs->pll_clk = pll_clk; 500a38a2233SWilliam Zhang bs->regs = regs; 501a38a2233SWilliam Zhang bs->spim_ctrl = spim_ctrl; 502a38a2233SWilliam Zhang bs->speed_hz = rate; 503a38a2233SWilliam Zhang bs->fifo = (u8 __iomem *) (bs->regs + HSSPI_FIFO_REG(0)); 504a38a2233SWilliam Zhang bs->wait_mode = HSSPI_WAIT_MODE_POLLING; 505a38a2233SWilliam Zhang 506a38a2233SWilliam Zhang mutex_init(&bs->bus_mutex); 507a38a2233SWilliam Zhang mutex_init(&bs->msg_mutex); 508a38a2233SWilliam Zhang init_completion(&bs->done); 509a38a2233SWilliam Zhang 510a38a2233SWilliam Zhang master->dev.of_node = dev->of_node; 511a38a2233SWilliam Zhang if (!dev->of_node) 512a38a2233SWilliam Zhang master->bus_num = HSSPI_BUS_NUM; 513a38a2233SWilliam Zhang 514a38a2233SWilliam Zhang of_property_read_u32(dev->of_node, "num-cs", &num_cs); 515a38a2233SWilliam Zhang if (num_cs > 8) { 516a38a2233SWilliam Zhang dev_warn(dev, "unsupported number of cs (%i), reducing to 8\n", 517a38a2233SWilliam Zhang num_cs); 518a38a2233SWilliam Zhang num_cs = HSSPI_SPI_MAX_CS; 519a38a2233SWilliam Zhang } 520a38a2233SWilliam Zhang master->num_chipselect = num_cs; 521a38a2233SWilliam Zhang master->setup = bcmbca_hsspi_setup; 522a38a2233SWilliam Zhang master->transfer_one_message = bcmbca_hsspi_transfer_one; 523a38a2233SWilliam Zhang master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | 524a38a2233SWilliam Zhang SPI_RX_DUAL | SPI_TX_DUAL; 525a38a2233SWilliam Zhang master->bits_per_word_mask = SPI_BPW_MASK(8); 526a38a2233SWilliam Zhang master->auto_runtime_pm = true; 527a38a2233SWilliam Zhang 528a38a2233SWilliam Zhang platform_set_drvdata(pdev, master); 529a38a2233SWilliam Zhang 530a38a2233SWilliam Zhang /* Initialize the hardware */ 531a38a2233SWilliam Zhang __raw_writel(0, bs->regs + HSSPI_INT_MASK_REG); 532a38a2233SWilliam Zhang 533a38a2233SWilliam Zhang /* clean up any pending interrupts */ 534a38a2233SWilliam Zhang __raw_writel(HSSPI_INT_CLEAR_ALL, bs->regs + HSSPI_INT_STATUS_REG); 535a38a2233SWilliam Zhang 536a38a2233SWilliam Zhang /* read out default CS polarities */ 537a38a2233SWilliam Zhang reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG); 538a38a2233SWilliam Zhang bs->cs_polarity = reg & GLOBAL_CTRL_CS_POLARITY_MASK; 539a38a2233SWilliam Zhang __raw_writel(reg | GLOBAL_CTRL_CLK_GATE_SSOFF, 540a38a2233SWilliam Zhang bs->regs + HSSPI_GLOBAL_CTRL_REG); 541a38a2233SWilliam Zhang 542a38a2233SWilliam Zhang if (irq > 0) { 543a38a2233SWilliam Zhang ret = devm_request_irq(dev, irq, bcmbca_hsspi_interrupt, IRQF_SHARED, 544a38a2233SWilliam Zhang pdev->name, bs); 545a38a2233SWilliam Zhang if (ret) 546a38a2233SWilliam Zhang goto out_put_master; 547a38a2233SWilliam Zhang } 548a38a2233SWilliam Zhang 549a38a2233SWilliam Zhang pm_runtime_enable(&pdev->dev); 550a38a2233SWilliam Zhang 5510696532eSDan Carpenter ret = sysfs_create_group(&pdev->dev.kobj, &bcmbca_hsspi_group); 5520696532eSDan Carpenter if (ret) { 553a38a2233SWilliam Zhang dev_err(&pdev->dev, "couldn't register sysfs group\n"); 554a38a2233SWilliam Zhang goto out_pm_disable; 555a38a2233SWilliam Zhang } 556a38a2233SWilliam Zhang 557a38a2233SWilliam Zhang /* register and we are done */ 558a38a2233SWilliam Zhang ret = devm_spi_register_master(dev, master); 559a38a2233SWilliam Zhang if (ret) 560a38a2233SWilliam Zhang goto out_sysgroup_disable; 561a38a2233SWilliam Zhang 562a38a2233SWilliam Zhang dev_info(dev, "Broadcom BCMBCA High Speed SPI Controller driver"); 563a38a2233SWilliam Zhang 564a38a2233SWilliam Zhang return 0; 565a38a2233SWilliam Zhang 566a38a2233SWilliam Zhang out_sysgroup_disable: 567a38a2233SWilliam Zhang sysfs_remove_group(&pdev->dev.kobj, &bcmbca_hsspi_group); 568a38a2233SWilliam Zhang out_pm_disable: 569a38a2233SWilliam Zhang pm_runtime_disable(&pdev->dev); 570a38a2233SWilliam Zhang out_put_master: 571a38a2233SWilliam Zhang spi_master_put(master); 572a38a2233SWilliam Zhang out_disable_pll_clk: 573a38a2233SWilliam Zhang clk_disable_unprepare(pll_clk); 574a38a2233SWilliam Zhang out_disable_clk: 575a38a2233SWilliam Zhang clk_disable_unprepare(clk); 576a38a2233SWilliam Zhang return ret; 577a38a2233SWilliam Zhang } 578a38a2233SWilliam Zhang 579f54f9b00SUwe Kleine-König static void bcmbca_hsspi_remove(struct platform_device *pdev) 580a38a2233SWilliam Zhang { 581a38a2233SWilliam Zhang struct spi_master *master = platform_get_drvdata(pdev); 582a38a2233SWilliam Zhang struct bcmbca_hsspi *bs = spi_master_get_devdata(master); 583a38a2233SWilliam Zhang 584a38a2233SWilliam Zhang /* reset the hardware and block queue progress */ 585a38a2233SWilliam Zhang __raw_writel(0, bs->regs + HSSPI_INT_MASK_REG); 586a38a2233SWilliam Zhang clk_disable_unprepare(bs->pll_clk); 587a38a2233SWilliam Zhang clk_disable_unprepare(bs->clk); 588a38a2233SWilliam Zhang sysfs_remove_group(&pdev->dev.kobj, &bcmbca_hsspi_group); 589a38a2233SWilliam Zhang } 590a38a2233SWilliam Zhang 591a38a2233SWilliam Zhang #ifdef CONFIG_PM_SLEEP 592a38a2233SWilliam Zhang static int bcmbca_hsspi_suspend(struct device *dev) 593a38a2233SWilliam Zhang { 594a38a2233SWilliam Zhang struct spi_master *master = dev_get_drvdata(dev); 595a38a2233SWilliam Zhang struct bcmbca_hsspi *bs = spi_master_get_devdata(master); 596a38a2233SWilliam Zhang 597a38a2233SWilliam Zhang spi_master_suspend(master); 598a38a2233SWilliam Zhang clk_disable_unprepare(bs->pll_clk); 599a38a2233SWilliam Zhang clk_disable_unprepare(bs->clk); 600a38a2233SWilliam Zhang 601a38a2233SWilliam Zhang return 0; 602a38a2233SWilliam Zhang } 603a38a2233SWilliam Zhang 604a38a2233SWilliam Zhang static int bcmbca_hsspi_resume(struct device *dev) 605a38a2233SWilliam Zhang { 606a38a2233SWilliam Zhang struct spi_master *master = dev_get_drvdata(dev); 607a38a2233SWilliam Zhang struct bcmbca_hsspi *bs = spi_master_get_devdata(master); 608a38a2233SWilliam Zhang int ret; 609a38a2233SWilliam Zhang 610a38a2233SWilliam Zhang ret = clk_prepare_enable(bs->clk); 611a38a2233SWilliam Zhang if (ret) 612a38a2233SWilliam Zhang return ret; 613a38a2233SWilliam Zhang 614a38a2233SWilliam Zhang if (bs->pll_clk) { 615a38a2233SWilliam Zhang ret = clk_prepare_enable(bs->pll_clk); 616a38a2233SWilliam Zhang if (ret) { 617a38a2233SWilliam Zhang clk_disable_unprepare(bs->clk); 618a38a2233SWilliam Zhang return ret; 619a38a2233SWilliam Zhang } 620a38a2233SWilliam Zhang } 621a38a2233SWilliam Zhang 622a38a2233SWilliam Zhang spi_master_resume(master); 623a38a2233SWilliam Zhang 624a38a2233SWilliam Zhang return 0; 625a38a2233SWilliam Zhang } 626a38a2233SWilliam Zhang #endif 627a38a2233SWilliam Zhang 628a38a2233SWilliam Zhang static SIMPLE_DEV_PM_OPS(bcmbca_hsspi_pm_ops, bcmbca_hsspi_suspend, 629a38a2233SWilliam Zhang bcmbca_hsspi_resume); 630a38a2233SWilliam Zhang 631a38a2233SWilliam Zhang static const struct of_device_id bcmbca_hsspi_of_match[] = { 632a38a2233SWilliam Zhang { .compatible = "brcm,bcmbca-hsspi-v1.1", }, 633a38a2233SWilliam Zhang {}, 634a38a2233SWilliam Zhang }; 635a38a2233SWilliam Zhang 636a38a2233SWilliam Zhang MODULE_DEVICE_TABLE(of, bcmbca_hsspi_of_match); 637a38a2233SWilliam Zhang 638a38a2233SWilliam Zhang static struct platform_driver bcmbca_hsspi_driver = { 639a38a2233SWilliam Zhang .driver = { 640a38a2233SWilliam Zhang .name = "bcmbca-hsspi", 641a38a2233SWilliam Zhang .pm = &bcmbca_hsspi_pm_ops, 642a38a2233SWilliam Zhang .of_match_table = bcmbca_hsspi_of_match, 643a38a2233SWilliam Zhang }, 644a38a2233SWilliam Zhang .probe = bcmbca_hsspi_probe, 645f54f9b00SUwe Kleine-König .remove_new = bcmbca_hsspi_remove, 646a38a2233SWilliam Zhang }; 647a38a2233SWilliam Zhang 648a38a2233SWilliam Zhang module_platform_driver(bcmbca_hsspi_driver); 649a38a2233SWilliam Zhang 650a38a2233SWilliam Zhang MODULE_ALIAS("platform:bcmbca_hsspi"); 651a38a2233SWilliam Zhang MODULE_DESCRIPTION("Broadcom BCMBCA High Speed SPI Controller driver"); 652a38a2233SWilliam Zhang MODULE_LICENSE("GPL"); 653