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 6879a879cSJonas 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); 1303dc6e684SYang Yingliang struct bcmbca_hsspi *bs = spi_controller_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); 1393dc6e684SYang Yingliang struct bcmbca_hsspi *bs = spi_controller_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 { 2533dc6e684SYang Yingliang struct bcmbca_hsspi *bs = spi_controller_get_devdata(spi->controller); 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 { 3313dc6e684SYang Yingliang struct bcmbca_hsspi *bs = spi_controller_get_devdata(spi->controller); 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 3693dc6e684SYang Yingliang static int bcmbca_hsspi_transfer_one(struct spi_controller *host, 370a38a2233SWilliam Zhang struct spi_message *msg) 371a38a2233SWilliam Zhang { 3723dc6e684SYang Yingliang struct bcmbca_hsspi *bs = spi_controller_get_devdata(host); 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; 4123dc6e684SYang Yingliang spi_finalize_current_message(host); 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 { 4343dc6e684SYang Yingliang struct spi_controller *host; 435a38a2233SWilliam Zhang struct bcmbca_hsspi *bs; 436a38a2233SWilliam Zhang void __iomem *spim_ctrl; 437a38a2233SWilliam Zhang void __iomem *regs; 438a38a2233SWilliam Zhang struct device *dev = &pdev->dev; 439a38a2233SWilliam Zhang struct clk *clk, *pll_clk = NULL; 440a38a2233SWilliam Zhang int irq, ret; 441a38a2233SWilliam Zhang u32 reg, rate, num_cs = HSSPI_SPI_MAX_CS; 442a38a2233SWilliam Zhang 443a38a2233SWilliam Zhang irq = platform_get_irq(pdev, 0); 444a38a2233SWilliam Zhang if (irq < 0) 445a38a2233SWilliam Zhang return irq; 446a38a2233SWilliam Zhang 44791232b00SJinjie Ruan regs = devm_platform_ioremap_resource_byname(pdev, "hsspi"); 448a38a2233SWilliam Zhang if (IS_ERR(regs)) 449a38a2233SWilliam Zhang return PTR_ERR(regs); 450a38a2233SWilliam Zhang 45191232b00SJinjie Ruan spim_ctrl = devm_platform_ioremap_resource_byname(pdev, "spim-ctrl"); 452a38a2233SWilliam Zhang if (IS_ERR(spim_ctrl)) 453a38a2233SWilliam Zhang return PTR_ERR(spim_ctrl); 454a38a2233SWilliam Zhang 455a38a2233SWilliam Zhang clk = devm_clk_get(dev, "hsspi"); 456a38a2233SWilliam Zhang if (IS_ERR(clk)) 457a38a2233SWilliam Zhang return PTR_ERR(clk); 458a38a2233SWilliam Zhang 459a38a2233SWilliam Zhang ret = clk_prepare_enable(clk); 460a38a2233SWilliam Zhang if (ret) 461a38a2233SWilliam Zhang return ret; 462a38a2233SWilliam Zhang 463a38a2233SWilliam Zhang rate = clk_get_rate(clk); 464a38a2233SWilliam Zhang if (!rate) { 465a38a2233SWilliam Zhang pll_clk = devm_clk_get(dev, "pll"); 466a38a2233SWilliam Zhang 467a38a2233SWilliam Zhang if (IS_ERR(pll_clk)) { 468a38a2233SWilliam Zhang ret = PTR_ERR(pll_clk); 469a38a2233SWilliam Zhang goto out_disable_clk; 470a38a2233SWilliam Zhang } 471a38a2233SWilliam Zhang 472a38a2233SWilliam Zhang ret = clk_prepare_enable(pll_clk); 473a38a2233SWilliam Zhang if (ret) 474a38a2233SWilliam Zhang goto out_disable_clk; 475a38a2233SWilliam Zhang 476a38a2233SWilliam Zhang rate = clk_get_rate(pll_clk); 477a38a2233SWilliam Zhang if (!rate) { 478a38a2233SWilliam Zhang ret = -EINVAL; 479a38a2233SWilliam Zhang goto out_disable_pll_clk; 480a38a2233SWilliam Zhang } 481a38a2233SWilliam Zhang } 482a38a2233SWilliam Zhang 483*deb269e0SJinjie Ruan host = devm_spi_alloc_host(&pdev->dev, sizeof(*bs)); 4843dc6e684SYang Yingliang if (!host) { 485a38a2233SWilliam Zhang ret = -ENOMEM; 486a38a2233SWilliam Zhang goto out_disable_pll_clk; 487a38a2233SWilliam Zhang } 488a38a2233SWilliam Zhang 4893dc6e684SYang Yingliang bs = spi_controller_get_devdata(host); 490a38a2233SWilliam Zhang bs->pdev = pdev; 491a38a2233SWilliam Zhang bs->clk = clk; 492a38a2233SWilliam Zhang bs->pll_clk = pll_clk; 493a38a2233SWilliam Zhang bs->regs = regs; 494a38a2233SWilliam Zhang bs->spim_ctrl = spim_ctrl; 495a38a2233SWilliam Zhang bs->speed_hz = rate; 496a38a2233SWilliam Zhang bs->fifo = (u8 __iomem *) (bs->regs + HSSPI_FIFO_REG(0)); 497a38a2233SWilliam Zhang bs->wait_mode = HSSPI_WAIT_MODE_POLLING; 498a38a2233SWilliam Zhang 499a38a2233SWilliam Zhang mutex_init(&bs->bus_mutex); 500a38a2233SWilliam Zhang mutex_init(&bs->msg_mutex); 501a38a2233SWilliam Zhang init_completion(&bs->done); 502a38a2233SWilliam Zhang 5033dc6e684SYang Yingliang host->dev.of_node = dev->of_node; 504a38a2233SWilliam Zhang if (!dev->of_node) 5053dc6e684SYang Yingliang host->bus_num = HSSPI_BUS_NUM; 506a38a2233SWilliam Zhang 507a38a2233SWilliam Zhang of_property_read_u32(dev->of_node, "num-cs", &num_cs); 508a38a2233SWilliam Zhang if (num_cs > 8) { 509a38a2233SWilliam Zhang dev_warn(dev, "unsupported number of cs (%i), reducing to 8\n", 510a38a2233SWilliam Zhang num_cs); 511a38a2233SWilliam Zhang num_cs = HSSPI_SPI_MAX_CS; 512a38a2233SWilliam Zhang } 5133dc6e684SYang Yingliang host->num_chipselect = num_cs; 5143dc6e684SYang Yingliang host->setup = bcmbca_hsspi_setup; 5153dc6e684SYang Yingliang host->transfer_one_message = bcmbca_hsspi_transfer_one; 5163dc6e684SYang Yingliang host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | 517a38a2233SWilliam Zhang SPI_RX_DUAL | SPI_TX_DUAL; 5183dc6e684SYang Yingliang host->bits_per_word_mask = SPI_BPW_MASK(8); 5193dc6e684SYang Yingliang host->auto_runtime_pm = true; 520a38a2233SWilliam Zhang 5213dc6e684SYang Yingliang platform_set_drvdata(pdev, host); 522a38a2233SWilliam Zhang 523a38a2233SWilliam Zhang /* Initialize the hardware */ 524a38a2233SWilliam Zhang __raw_writel(0, bs->regs + HSSPI_INT_MASK_REG); 525a38a2233SWilliam Zhang 526a38a2233SWilliam Zhang /* clean up any pending interrupts */ 527a38a2233SWilliam Zhang __raw_writel(HSSPI_INT_CLEAR_ALL, bs->regs + HSSPI_INT_STATUS_REG); 528a38a2233SWilliam Zhang 529a38a2233SWilliam Zhang /* read out default CS polarities */ 530a38a2233SWilliam Zhang reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG); 531a38a2233SWilliam Zhang bs->cs_polarity = reg & GLOBAL_CTRL_CS_POLARITY_MASK; 532a38a2233SWilliam Zhang __raw_writel(reg | GLOBAL_CTRL_CLK_GATE_SSOFF, 533a38a2233SWilliam Zhang bs->regs + HSSPI_GLOBAL_CTRL_REG); 534a38a2233SWilliam Zhang 535a38a2233SWilliam Zhang if (irq > 0) { 536a38a2233SWilliam Zhang ret = devm_request_irq(dev, irq, bcmbca_hsspi_interrupt, IRQF_SHARED, 537a38a2233SWilliam Zhang pdev->name, bs); 538a38a2233SWilliam Zhang if (ret) 539*deb269e0SJinjie Ruan goto out_disable_pll_clk; 540a38a2233SWilliam Zhang } 541a38a2233SWilliam Zhang 5424439a2e9SJinjie Ruan ret = devm_pm_runtime_enable(&pdev->dev); 5434439a2e9SJinjie Ruan if (ret) 544*deb269e0SJinjie Ruan goto out_disable_pll_clk; 545a38a2233SWilliam Zhang 5460696532eSDan Carpenter ret = sysfs_create_group(&pdev->dev.kobj, &bcmbca_hsspi_group); 5470696532eSDan Carpenter if (ret) { 548a38a2233SWilliam Zhang dev_err(&pdev->dev, "couldn't register sysfs group\n"); 549*deb269e0SJinjie Ruan goto out_disable_pll_clk; 550a38a2233SWilliam Zhang } 551a38a2233SWilliam Zhang 552a38a2233SWilliam Zhang /* register and we are done */ 5533dc6e684SYang Yingliang ret = devm_spi_register_controller(dev, host); 554a38a2233SWilliam Zhang if (ret) 555a38a2233SWilliam Zhang goto out_sysgroup_disable; 556a38a2233SWilliam Zhang 557a38a2233SWilliam Zhang dev_info(dev, "Broadcom BCMBCA High Speed SPI Controller driver"); 558a38a2233SWilliam Zhang 559a38a2233SWilliam Zhang return 0; 560a38a2233SWilliam Zhang 561a38a2233SWilliam Zhang out_sysgroup_disable: 562a38a2233SWilliam Zhang sysfs_remove_group(&pdev->dev.kobj, &bcmbca_hsspi_group); 563a38a2233SWilliam Zhang out_disable_pll_clk: 564a38a2233SWilliam Zhang clk_disable_unprepare(pll_clk); 565a38a2233SWilliam Zhang out_disable_clk: 566a38a2233SWilliam Zhang clk_disable_unprepare(clk); 567a38a2233SWilliam Zhang return ret; 568a38a2233SWilliam Zhang } 569a38a2233SWilliam Zhang 570f54f9b00SUwe Kleine-König static void bcmbca_hsspi_remove(struct platform_device *pdev) 571a38a2233SWilliam Zhang { 5723dc6e684SYang Yingliang struct spi_controller *host = platform_get_drvdata(pdev); 5733dc6e684SYang Yingliang struct bcmbca_hsspi *bs = spi_controller_get_devdata(host); 574a38a2233SWilliam Zhang 575a38a2233SWilliam Zhang /* reset the hardware and block queue progress */ 576a38a2233SWilliam Zhang __raw_writel(0, bs->regs + HSSPI_INT_MASK_REG); 577a38a2233SWilliam Zhang clk_disable_unprepare(bs->pll_clk); 578a38a2233SWilliam Zhang clk_disable_unprepare(bs->clk); 579a38a2233SWilliam Zhang sysfs_remove_group(&pdev->dev.kobj, &bcmbca_hsspi_group); 580a38a2233SWilliam Zhang } 581a38a2233SWilliam Zhang 582a38a2233SWilliam Zhang #ifdef CONFIG_PM_SLEEP 583a38a2233SWilliam Zhang static int bcmbca_hsspi_suspend(struct device *dev) 584a38a2233SWilliam Zhang { 5853dc6e684SYang Yingliang struct spi_controller *host = dev_get_drvdata(dev); 5863dc6e684SYang Yingliang struct bcmbca_hsspi *bs = spi_controller_get_devdata(host); 587a38a2233SWilliam Zhang 5883dc6e684SYang Yingliang spi_controller_suspend(host); 589a38a2233SWilliam Zhang clk_disable_unprepare(bs->pll_clk); 590a38a2233SWilliam Zhang clk_disable_unprepare(bs->clk); 591a38a2233SWilliam Zhang 592a38a2233SWilliam Zhang return 0; 593a38a2233SWilliam Zhang } 594a38a2233SWilliam Zhang 595a38a2233SWilliam Zhang static int bcmbca_hsspi_resume(struct device *dev) 596a38a2233SWilliam Zhang { 5973dc6e684SYang Yingliang struct spi_controller *host = dev_get_drvdata(dev); 5983dc6e684SYang Yingliang struct bcmbca_hsspi *bs = spi_controller_get_devdata(host); 599a38a2233SWilliam Zhang int ret; 600a38a2233SWilliam Zhang 601a38a2233SWilliam Zhang ret = clk_prepare_enable(bs->clk); 602a38a2233SWilliam Zhang if (ret) 603a38a2233SWilliam Zhang return ret; 604a38a2233SWilliam Zhang 605a38a2233SWilliam Zhang if (bs->pll_clk) { 606a38a2233SWilliam Zhang ret = clk_prepare_enable(bs->pll_clk); 607a38a2233SWilliam Zhang if (ret) { 608a38a2233SWilliam Zhang clk_disable_unprepare(bs->clk); 609a38a2233SWilliam Zhang return ret; 610a38a2233SWilliam Zhang } 611a38a2233SWilliam Zhang } 612a38a2233SWilliam Zhang 6133dc6e684SYang Yingliang spi_controller_resume(host); 614a38a2233SWilliam Zhang 615a38a2233SWilliam Zhang return 0; 616a38a2233SWilliam Zhang } 617a38a2233SWilliam Zhang #endif 618a38a2233SWilliam Zhang 619a38a2233SWilliam Zhang static SIMPLE_DEV_PM_OPS(bcmbca_hsspi_pm_ops, bcmbca_hsspi_suspend, 620a38a2233SWilliam Zhang bcmbca_hsspi_resume); 621a38a2233SWilliam Zhang 622a38a2233SWilliam Zhang static const struct of_device_id bcmbca_hsspi_of_match[] = { 623a38a2233SWilliam Zhang { .compatible = "brcm,bcmbca-hsspi-v1.1", }, 624a38a2233SWilliam Zhang {}, 625a38a2233SWilliam Zhang }; 626a38a2233SWilliam Zhang 627a38a2233SWilliam Zhang MODULE_DEVICE_TABLE(of, bcmbca_hsspi_of_match); 628a38a2233SWilliam Zhang 629a38a2233SWilliam Zhang static struct platform_driver bcmbca_hsspi_driver = { 630a38a2233SWilliam Zhang .driver = { 631a38a2233SWilliam Zhang .name = "bcmbca-hsspi", 632a38a2233SWilliam Zhang .pm = &bcmbca_hsspi_pm_ops, 633a38a2233SWilliam Zhang .of_match_table = bcmbca_hsspi_of_match, 634a38a2233SWilliam Zhang }, 635a38a2233SWilliam Zhang .probe = bcmbca_hsspi_probe, 636f54f9b00SUwe Kleine-König .remove_new = bcmbca_hsspi_remove, 637a38a2233SWilliam Zhang }; 638a38a2233SWilliam Zhang 639a38a2233SWilliam Zhang module_platform_driver(bcmbca_hsspi_driver); 640a38a2233SWilliam Zhang 641a38a2233SWilliam Zhang MODULE_ALIAS("platform:bcmbca_hsspi"); 642a38a2233SWilliam Zhang MODULE_DESCRIPTION("Broadcom BCMBCA High Speed SPI Controller driver"); 643a38a2233SWilliam Zhang MODULE_LICENSE("GPL"); 644