18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Broadcom BCM63XX High Speed SPI Controller driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright 2000-2010 Broadcom Corporation 58c2ecf20Sopenharmony_ci * Copyright 2012-2013 Jonas Gorski <jogo@openwrt.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Licensed under the GNU/GPL. See COPYING for details. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/io.h> 138c2ecf20Sopenharmony_ci#include <linux/clk.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 188c2ecf20Sopenharmony_ci#include <linux/err.h> 198c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 208c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 218c2ecf20Sopenharmony_ci#include <linux/mutex.h> 228c2ecf20Sopenharmony_ci#include <linux/of.h> 238c2ecf20Sopenharmony_ci#include <linux/reset.h> 248c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define HSSPI_GLOBAL_CTRL_REG 0x0 278c2ecf20Sopenharmony_ci#define GLOBAL_CTRL_CS_POLARITY_SHIFT 0 288c2ecf20Sopenharmony_ci#define GLOBAL_CTRL_CS_POLARITY_MASK 0x000000ff 298c2ecf20Sopenharmony_ci#define GLOBAL_CTRL_PLL_CLK_CTRL_SHIFT 8 308c2ecf20Sopenharmony_ci#define GLOBAL_CTRL_PLL_CLK_CTRL_MASK 0x0000ff00 318c2ecf20Sopenharmony_ci#define GLOBAL_CTRL_CLK_GATE_SSOFF BIT(16) 328c2ecf20Sopenharmony_ci#define GLOBAL_CTRL_CLK_POLARITY BIT(17) 338c2ecf20Sopenharmony_ci#define GLOBAL_CTRL_MOSI_IDLE BIT(18) 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define HSSPI_GLOBAL_EXT_TRIGGER_REG 0x4 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define HSSPI_INT_STATUS_REG 0x8 388c2ecf20Sopenharmony_ci#define HSSPI_INT_STATUS_MASKED_REG 0xc 398c2ecf20Sopenharmony_ci#define HSSPI_INT_MASK_REG 0x10 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define HSSPI_PINGx_CMD_DONE(i) BIT((i * 8) + 0) 428c2ecf20Sopenharmony_ci#define HSSPI_PINGx_RX_OVER(i) BIT((i * 8) + 1) 438c2ecf20Sopenharmony_ci#define HSSPI_PINGx_TX_UNDER(i) BIT((i * 8) + 2) 448c2ecf20Sopenharmony_ci#define HSSPI_PINGx_POLL_TIMEOUT(i) BIT((i * 8) + 3) 458c2ecf20Sopenharmony_ci#define HSSPI_PINGx_CTRL_INVAL(i) BIT((i * 8) + 4) 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define HSSPI_INT_CLEAR_ALL 0xff001f1f 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define HSSPI_PINGPONG_COMMAND_REG(x) (0x80 + (x) * 0x40) 508c2ecf20Sopenharmony_ci#define PINGPONG_CMD_COMMAND_MASK 0xf 518c2ecf20Sopenharmony_ci#define PINGPONG_COMMAND_NOOP 0 528c2ecf20Sopenharmony_ci#define PINGPONG_COMMAND_START_NOW 1 538c2ecf20Sopenharmony_ci#define PINGPONG_COMMAND_START_TRIGGER 2 548c2ecf20Sopenharmony_ci#define PINGPONG_COMMAND_HALT 3 558c2ecf20Sopenharmony_ci#define PINGPONG_COMMAND_FLUSH 4 568c2ecf20Sopenharmony_ci#define PINGPONG_CMD_PROFILE_SHIFT 8 578c2ecf20Sopenharmony_ci#define PINGPONG_CMD_SS_SHIFT 12 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#define HSSPI_PINGPONG_STATUS_REG(x) (0x84 + (x) * 0x40) 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define HSSPI_PROFILE_CLK_CTRL_REG(x) (0x100 + (x) * 0x20) 628c2ecf20Sopenharmony_ci#define CLK_CTRL_FREQ_CTRL_MASK 0x0000ffff 638c2ecf20Sopenharmony_ci#define CLK_CTRL_SPI_CLK_2X_SEL BIT(14) 648c2ecf20Sopenharmony_ci#define CLK_CTRL_ACCUM_RST_ON_LOOP BIT(15) 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#define HSSPI_PROFILE_SIGNAL_CTRL_REG(x) (0x104 + (x) * 0x20) 678c2ecf20Sopenharmony_ci#define SIGNAL_CTRL_LATCH_RISING BIT(12) 688c2ecf20Sopenharmony_ci#define SIGNAL_CTRL_LAUNCH_RISING BIT(13) 698c2ecf20Sopenharmony_ci#define SIGNAL_CTRL_ASYNC_INPUT_PATH BIT(16) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define HSSPI_PROFILE_MODE_CTRL_REG(x) (0x108 + (x) * 0x20) 728c2ecf20Sopenharmony_ci#define MODE_CTRL_MULTIDATA_RD_STRT_SHIFT 8 738c2ecf20Sopenharmony_ci#define MODE_CTRL_MULTIDATA_WR_STRT_SHIFT 12 748c2ecf20Sopenharmony_ci#define MODE_CTRL_MULTIDATA_RD_SIZE_SHIFT 16 758c2ecf20Sopenharmony_ci#define MODE_CTRL_MULTIDATA_WR_SIZE_SHIFT 18 768c2ecf20Sopenharmony_ci#define MODE_CTRL_MODE_3WIRE BIT(20) 778c2ecf20Sopenharmony_ci#define MODE_CTRL_PREPENDBYTE_CNT_SHIFT 24 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define HSSPI_FIFO_REG(x) (0x200 + (x) * 0x200) 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#define HSSPI_OP_MULTIBIT BIT(11) 838c2ecf20Sopenharmony_ci#define HSSPI_OP_CODE_SHIFT 13 848c2ecf20Sopenharmony_ci#define HSSPI_OP_SLEEP (0 << HSSPI_OP_CODE_SHIFT) 858c2ecf20Sopenharmony_ci#define HSSPI_OP_READ_WRITE (1 << HSSPI_OP_CODE_SHIFT) 868c2ecf20Sopenharmony_ci#define HSSPI_OP_WRITE (2 << HSSPI_OP_CODE_SHIFT) 878c2ecf20Sopenharmony_ci#define HSSPI_OP_READ (3 << HSSPI_OP_CODE_SHIFT) 888c2ecf20Sopenharmony_ci#define HSSPI_OP_SETIRQ (4 << HSSPI_OP_CODE_SHIFT) 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#define HSSPI_BUFFER_LEN 512 918c2ecf20Sopenharmony_ci#define HSSPI_OPCODE_LEN 2 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#define HSSPI_MAX_PREPEND_LEN 15 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#define HSSPI_MAX_SYNC_CLOCK 30000000 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci#define HSSPI_SPI_MAX_CS 8 988c2ecf20Sopenharmony_ci#define HSSPI_BUS_NUM 1 /* 0 is legacy SPI */ 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistruct bcm63xx_hsspi { 1018c2ecf20Sopenharmony_ci struct completion done; 1028c2ecf20Sopenharmony_ci struct mutex bus_mutex; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci struct platform_device *pdev; 1058c2ecf20Sopenharmony_ci struct clk *clk; 1068c2ecf20Sopenharmony_ci struct clk *pll_clk; 1078c2ecf20Sopenharmony_ci void __iomem *regs; 1088c2ecf20Sopenharmony_ci u8 __iomem *fifo; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci u32 speed_hz; 1118c2ecf20Sopenharmony_ci u8 cs_polarity; 1128c2ecf20Sopenharmony_ci}; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic void bcm63xx_hsspi_set_cs(struct bcm63xx_hsspi *bs, unsigned int cs, 1158c2ecf20Sopenharmony_ci bool active) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci u32 reg; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci mutex_lock(&bs->bus_mutex); 1208c2ecf20Sopenharmony_ci reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci reg &= ~BIT(cs); 1238c2ecf20Sopenharmony_ci if (active == !(bs->cs_polarity & BIT(cs))) 1248c2ecf20Sopenharmony_ci reg |= BIT(cs); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG); 1278c2ecf20Sopenharmony_ci mutex_unlock(&bs->bus_mutex); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic void bcm63xx_hsspi_set_clk(struct bcm63xx_hsspi *bs, 1318c2ecf20Sopenharmony_ci struct spi_device *spi, int hz) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci unsigned int profile = spi->chip_select; 1348c2ecf20Sopenharmony_ci u32 reg; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci reg = DIV_ROUND_UP(2048, DIV_ROUND_UP(bs->speed_hz, hz)); 1378c2ecf20Sopenharmony_ci __raw_writel(CLK_CTRL_ACCUM_RST_ON_LOOP | reg, 1388c2ecf20Sopenharmony_ci bs->regs + HSSPI_PROFILE_CLK_CTRL_REG(profile)); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci reg = __raw_readl(bs->regs + HSSPI_PROFILE_SIGNAL_CTRL_REG(profile)); 1418c2ecf20Sopenharmony_ci if (hz > HSSPI_MAX_SYNC_CLOCK) 1428c2ecf20Sopenharmony_ci reg |= SIGNAL_CTRL_ASYNC_INPUT_PATH; 1438c2ecf20Sopenharmony_ci else 1448c2ecf20Sopenharmony_ci reg &= ~SIGNAL_CTRL_ASYNC_INPUT_PATH; 1458c2ecf20Sopenharmony_ci __raw_writel(reg, bs->regs + HSSPI_PROFILE_SIGNAL_CTRL_REG(profile)); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci mutex_lock(&bs->bus_mutex); 1488c2ecf20Sopenharmony_ci /* setup clock polarity */ 1498c2ecf20Sopenharmony_ci reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG); 1508c2ecf20Sopenharmony_ci reg &= ~GLOBAL_CTRL_CLK_POLARITY; 1518c2ecf20Sopenharmony_ci if (spi->mode & SPI_CPOL) 1528c2ecf20Sopenharmony_ci reg |= GLOBAL_CTRL_CLK_POLARITY; 1538c2ecf20Sopenharmony_ci __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG); 1548c2ecf20Sopenharmony_ci mutex_unlock(&bs->bus_mutex); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic int bcm63xx_hsspi_do_txrx(struct spi_device *spi, struct spi_transfer *t) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct bcm63xx_hsspi *bs = spi_master_get_devdata(spi->master); 1608c2ecf20Sopenharmony_ci unsigned int chip_select = spi->chip_select; 1618c2ecf20Sopenharmony_ci u16 opcode = 0; 1628c2ecf20Sopenharmony_ci int pending = t->len; 1638c2ecf20Sopenharmony_ci int step_size = HSSPI_BUFFER_LEN; 1648c2ecf20Sopenharmony_ci const u8 *tx = t->tx_buf; 1658c2ecf20Sopenharmony_ci u8 *rx = t->rx_buf; 1668c2ecf20Sopenharmony_ci u32 val = 0; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci bcm63xx_hsspi_set_clk(bs, spi, t->speed_hz); 1698c2ecf20Sopenharmony_ci bcm63xx_hsspi_set_cs(bs, spi->chip_select, true); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (tx && rx) 1728c2ecf20Sopenharmony_ci opcode = HSSPI_OP_READ_WRITE; 1738c2ecf20Sopenharmony_ci else if (tx) 1748c2ecf20Sopenharmony_ci opcode = HSSPI_OP_WRITE; 1758c2ecf20Sopenharmony_ci else if (rx) 1768c2ecf20Sopenharmony_ci opcode = HSSPI_OP_READ; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (opcode != HSSPI_OP_READ) 1798c2ecf20Sopenharmony_ci step_size -= HSSPI_OPCODE_LEN; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if ((opcode == HSSPI_OP_READ && t->rx_nbits == SPI_NBITS_DUAL) || 1828c2ecf20Sopenharmony_ci (opcode == HSSPI_OP_WRITE && t->tx_nbits == SPI_NBITS_DUAL)) { 1838c2ecf20Sopenharmony_ci opcode |= HSSPI_OP_MULTIBIT; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (t->rx_nbits == SPI_NBITS_DUAL) 1868c2ecf20Sopenharmony_ci val |= 1 << MODE_CTRL_MULTIDATA_RD_SIZE_SHIFT; 1878c2ecf20Sopenharmony_ci if (t->tx_nbits == SPI_NBITS_DUAL) 1888c2ecf20Sopenharmony_ci val |= 1 << MODE_CTRL_MULTIDATA_WR_SIZE_SHIFT; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci __raw_writel(val | 0xff, 1928c2ecf20Sopenharmony_ci bs->regs + HSSPI_PROFILE_MODE_CTRL_REG(chip_select)); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci while (pending > 0) { 1958c2ecf20Sopenharmony_ci int curr_step = min_t(int, step_size, pending); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci reinit_completion(&bs->done); 1988c2ecf20Sopenharmony_ci if (tx) { 1998c2ecf20Sopenharmony_ci memcpy_toio(bs->fifo + HSSPI_OPCODE_LEN, tx, curr_step); 2008c2ecf20Sopenharmony_ci tx += curr_step; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci __raw_writew(opcode | curr_step, bs->fifo); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* enable interrupt */ 2068c2ecf20Sopenharmony_ci __raw_writel(HSSPI_PINGx_CMD_DONE(0), 2078c2ecf20Sopenharmony_ci bs->regs + HSSPI_INT_MASK_REG); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* start the transfer */ 2108c2ecf20Sopenharmony_ci __raw_writel(!chip_select << PINGPONG_CMD_SS_SHIFT | 2118c2ecf20Sopenharmony_ci chip_select << PINGPONG_CMD_PROFILE_SHIFT | 2128c2ecf20Sopenharmony_ci PINGPONG_COMMAND_START_NOW, 2138c2ecf20Sopenharmony_ci bs->regs + HSSPI_PINGPONG_COMMAND_REG(0)); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (wait_for_completion_timeout(&bs->done, HZ) == 0) { 2168c2ecf20Sopenharmony_ci dev_err(&bs->pdev->dev, "transfer timed out!\n"); 2178c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (rx) { 2218c2ecf20Sopenharmony_ci memcpy_fromio(rx, bs->fifo, curr_step); 2228c2ecf20Sopenharmony_ci rx += curr_step; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci pending -= curr_step; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci return 0; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic int bcm63xx_hsspi_setup(struct spi_device *spi) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct bcm63xx_hsspi *bs = spi_master_get_devdata(spi->master); 2348c2ecf20Sopenharmony_ci u32 reg; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci reg = __raw_readl(bs->regs + 2378c2ecf20Sopenharmony_ci HSSPI_PROFILE_SIGNAL_CTRL_REG(spi->chip_select)); 2388c2ecf20Sopenharmony_ci reg &= ~(SIGNAL_CTRL_LAUNCH_RISING | SIGNAL_CTRL_LATCH_RISING); 2398c2ecf20Sopenharmony_ci if (spi->mode & SPI_CPHA) 2408c2ecf20Sopenharmony_ci reg |= SIGNAL_CTRL_LAUNCH_RISING; 2418c2ecf20Sopenharmony_ci else 2428c2ecf20Sopenharmony_ci reg |= SIGNAL_CTRL_LATCH_RISING; 2438c2ecf20Sopenharmony_ci __raw_writel(reg, bs->regs + 2448c2ecf20Sopenharmony_ci HSSPI_PROFILE_SIGNAL_CTRL_REG(spi->chip_select)); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci mutex_lock(&bs->bus_mutex); 2478c2ecf20Sopenharmony_ci reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* only change actual polarities if there is no transfer */ 2508c2ecf20Sopenharmony_ci if ((reg & GLOBAL_CTRL_CS_POLARITY_MASK) == bs->cs_polarity) { 2518c2ecf20Sopenharmony_ci if (spi->mode & SPI_CS_HIGH) 2528c2ecf20Sopenharmony_ci reg |= BIT(spi->chip_select); 2538c2ecf20Sopenharmony_ci else 2548c2ecf20Sopenharmony_ci reg &= ~BIT(spi->chip_select); 2558c2ecf20Sopenharmony_ci __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG); 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (spi->mode & SPI_CS_HIGH) 2598c2ecf20Sopenharmony_ci bs->cs_polarity |= BIT(spi->chip_select); 2608c2ecf20Sopenharmony_ci else 2618c2ecf20Sopenharmony_ci bs->cs_polarity &= ~BIT(spi->chip_select); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci mutex_unlock(&bs->bus_mutex); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int bcm63xx_hsspi_transfer_one(struct spi_master *master, 2698c2ecf20Sopenharmony_ci struct spi_message *msg) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct bcm63xx_hsspi *bs = spi_master_get_devdata(master); 2728c2ecf20Sopenharmony_ci struct spi_transfer *t; 2738c2ecf20Sopenharmony_ci struct spi_device *spi = msg->spi; 2748c2ecf20Sopenharmony_ci int status = -EINVAL; 2758c2ecf20Sopenharmony_ci int dummy_cs; 2768c2ecf20Sopenharmony_ci u32 reg; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* This controller does not support keeping CS active during idle. 2798c2ecf20Sopenharmony_ci * To work around this, we use the following ugly hack: 2808c2ecf20Sopenharmony_ci * 2818c2ecf20Sopenharmony_ci * a. Invert the target chip select's polarity so it will be active. 2828c2ecf20Sopenharmony_ci * b. Select a "dummy" chip select to use as the hardware target. 2838c2ecf20Sopenharmony_ci * c. Invert the dummy chip select's polarity so it will be inactive 2848c2ecf20Sopenharmony_ci * during the actual transfers. 2858c2ecf20Sopenharmony_ci * d. Tell the hardware to send to the dummy chip select. Thanks to 2868c2ecf20Sopenharmony_ci * the multiplexed nature of SPI the actual target will receive 2878c2ecf20Sopenharmony_ci * the transfer and we see its response. 2888c2ecf20Sopenharmony_ci * 2898c2ecf20Sopenharmony_ci * e. At the end restore the polarities again to their default values. 2908c2ecf20Sopenharmony_ci */ 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci dummy_cs = !spi->chip_select; 2938c2ecf20Sopenharmony_ci bcm63xx_hsspi_set_cs(bs, dummy_cs, true); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci list_for_each_entry(t, &msg->transfers, transfer_list) { 2968c2ecf20Sopenharmony_ci status = bcm63xx_hsspi_do_txrx(spi, t); 2978c2ecf20Sopenharmony_ci if (status) 2988c2ecf20Sopenharmony_ci break; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci msg->actual_length += t->len; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci spi_transfer_delay_exec(t); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (t->cs_change) 3058c2ecf20Sopenharmony_ci bcm63xx_hsspi_set_cs(bs, spi->chip_select, false); 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci mutex_lock(&bs->bus_mutex); 3098c2ecf20Sopenharmony_ci reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG); 3108c2ecf20Sopenharmony_ci reg &= ~GLOBAL_CTRL_CS_POLARITY_MASK; 3118c2ecf20Sopenharmony_ci reg |= bs->cs_polarity; 3128c2ecf20Sopenharmony_ci __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG); 3138c2ecf20Sopenharmony_ci mutex_unlock(&bs->bus_mutex); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci msg->status = status; 3168c2ecf20Sopenharmony_ci spi_finalize_current_message(master); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return 0; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic irqreturn_t bcm63xx_hsspi_interrupt(int irq, void *dev_id) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct bcm63xx_hsspi *bs = (struct bcm63xx_hsspi *)dev_id; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (__raw_readl(bs->regs + HSSPI_INT_STATUS_MASKED_REG) == 0) 3268c2ecf20Sopenharmony_ci return IRQ_NONE; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci __raw_writel(HSSPI_INT_CLEAR_ALL, bs->regs + HSSPI_INT_STATUS_REG); 3298c2ecf20Sopenharmony_ci __raw_writel(0, bs->regs + HSSPI_INT_MASK_REG); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci complete(&bs->done); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic int bcm63xx_hsspi_probe(struct platform_device *pdev) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci struct spi_master *master; 3398c2ecf20Sopenharmony_ci struct bcm63xx_hsspi *bs; 3408c2ecf20Sopenharmony_ci void __iomem *regs; 3418c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 3428c2ecf20Sopenharmony_ci struct clk *clk, *pll_clk = NULL; 3438c2ecf20Sopenharmony_ci int irq, ret; 3448c2ecf20Sopenharmony_ci u32 reg, rate, num_cs = HSSPI_SPI_MAX_CS; 3458c2ecf20Sopenharmony_ci struct reset_control *reset; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 3488c2ecf20Sopenharmony_ci if (irq < 0) 3498c2ecf20Sopenharmony_ci return irq; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci regs = devm_platform_ioremap_resource(pdev, 0); 3528c2ecf20Sopenharmony_ci if (IS_ERR(regs)) 3538c2ecf20Sopenharmony_ci return PTR_ERR(regs); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci clk = devm_clk_get(dev, "hsspi"); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (IS_ERR(clk)) 3588c2ecf20Sopenharmony_ci return PTR_ERR(clk); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci reset = devm_reset_control_get_optional_exclusive(dev, NULL); 3618c2ecf20Sopenharmony_ci if (IS_ERR(reset)) 3628c2ecf20Sopenharmony_ci return PTR_ERR(reset); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci ret = clk_prepare_enable(clk); 3658c2ecf20Sopenharmony_ci if (ret) 3668c2ecf20Sopenharmony_ci return ret; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci ret = reset_control_reset(reset); 3698c2ecf20Sopenharmony_ci if (ret) { 3708c2ecf20Sopenharmony_ci dev_err(dev, "unable to reset device: %d\n", ret); 3718c2ecf20Sopenharmony_ci goto out_disable_clk; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci rate = clk_get_rate(clk); 3758c2ecf20Sopenharmony_ci if (!rate) { 3768c2ecf20Sopenharmony_ci pll_clk = devm_clk_get(dev, "pll"); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (IS_ERR(pll_clk)) { 3798c2ecf20Sopenharmony_ci ret = PTR_ERR(pll_clk); 3808c2ecf20Sopenharmony_ci goto out_disable_clk; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci ret = clk_prepare_enable(pll_clk); 3848c2ecf20Sopenharmony_ci if (ret) 3858c2ecf20Sopenharmony_ci goto out_disable_clk; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci rate = clk_get_rate(pll_clk); 3888c2ecf20Sopenharmony_ci if (!rate) { 3898c2ecf20Sopenharmony_ci ret = -EINVAL; 3908c2ecf20Sopenharmony_ci goto out_disable_pll_clk; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci master = spi_alloc_master(&pdev->dev, sizeof(*bs)); 3958c2ecf20Sopenharmony_ci if (!master) { 3968c2ecf20Sopenharmony_ci ret = -ENOMEM; 3978c2ecf20Sopenharmony_ci goto out_disable_pll_clk; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci bs = spi_master_get_devdata(master); 4018c2ecf20Sopenharmony_ci bs->pdev = pdev; 4028c2ecf20Sopenharmony_ci bs->clk = clk; 4038c2ecf20Sopenharmony_ci bs->pll_clk = pll_clk; 4048c2ecf20Sopenharmony_ci bs->regs = regs; 4058c2ecf20Sopenharmony_ci bs->speed_hz = rate; 4068c2ecf20Sopenharmony_ci bs->fifo = (u8 __iomem *)(bs->regs + HSSPI_FIFO_REG(0)); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci mutex_init(&bs->bus_mutex); 4098c2ecf20Sopenharmony_ci init_completion(&bs->done); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci master->dev.of_node = dev->of_node; 4128c2ecf20Sopenharmony_ci if (!dev->of_node) 4138c2ecf20Sopenharmony_ci master->bus_num = HSSPI_BUS_NUM; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci of_property_read_u32(dev->of_node, "num-cs", &num_cs); 4168c2ecf20Sopenharmony_ci if (num_cs > 8) { 4178c2ecf20Sopenharmony_ci dev_warn(dev, "unsupported number of cs (%i), reducing to 8\n", 4188c2ecf20Sopenharmony_ci num_cs); 4198c2ecf20Sopenharmony_ci num_cs = HSSPI_SPI_MAX_CS; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci master->num_chipselect = num_cs; 4228c2ecf20Sopenharmony_ci master->setup = bcm63xx_hsspi_setup; 4238c2ecf20Sopenharmony_ci master->transfer_one_message = bcm63xx_hsspi_transfer_one; 4248c2ecf20Sopenharmony_ci master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | 4258c2ecf20Sopenharmony_ci SPI_RX_DUAL | SPI_TX_DUAL; 4268c2ecf20Sopenharmony_ci master->bits_per_word_mask = SPI_BPW_MASK(8); 4278c2ecf20Sopenharmony_ci master->auto_runtime_pm = true; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, master); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* Initialize the hardware */ 4328c2ecf20Sopenharmony_ci __raw_writel(0, bs->regs + HSSPI_INT_MASK_REG); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* clean up any pending interrupts */ 4358c2ecf20Sopenharmony_ci __raw_writel(HSSPI_INT_CLEAR_ALL, bs->regs + HSSPI_INT_STATUS_REG); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* read out default CS polarities */ 4388c2ecf20Sopenharmony_ci reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG); 4398c2ecf20Sopenharmony_ci bs->cs_polarity = reg & GLOBAL_CTRL_CS_POLARITY_MASK; 4408c2ecf20Sopenharmony_ci __raw_writel(reg | GLOBAL_CTRL_CLK_GATE_SSOFF, 4418c2ecf20Sopenharmony_ci bs->regs + HSSPI_GLOBAL_CTRL_REG); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, irq, bcm63xx_hsspi_interrupt, IRQF_SHARED, 4448c2ecf20Sopenharmony_ci pdev->name, bs); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (ret) 4478c2ecf20Sopenharmony_ci goto out_put_master; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* register and we are done */ 4528c2ecf20Sopenharmony_ci ret = devm_spi_register_master(dev, master); 4538c2ecf20Sopenharmony_ci if (ret) 4548c2ecf20Sopenharmony_ci goto out_pm_disable; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci return 0; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ciout_pm_disable: 4598c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 4608c2ecf20Sopenharmony_ciout_put_master: 4618c2ecf20Sopenharmony_ci spi_master_put(master); 4628c2ecf20Sopenharmony_ciout_disable_pll_clk: 4638c2ecf20Sopenharmony_ci clk_disable_unprepare(pll_clk); 4648c2ecf20Sopenharmony_ciout_disable_clk: 4658c2ecf20Sopenharmony_ci clk_disable_unprepare(clk); 4668c2ecf20Sopenharmony_ci return ret; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic int bcm63xx_hsspi_remove(struct platform_device *pdev) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci struct spi_master *master = platform_get_drvdata(pdev); 4738c2ecf20Sopenharmony_ci struct bcm63xx_hsspi *bs = spi_master_get_devdata(master); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* reset the hardware and block queue progress */ 4768c2ecf20Sopenharmony_ci __raw_writel(0, bs->regs + HSSPI_INT_MASK_REG); 4778c2ecf20Sopenharmony_ci clk_disable_unprepare(bs->pll_clk); 4788c2ecf20Sopenharmony_ci clk_disable_unprepare(bs->clk); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci return 0; 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4848c2ecf20Sopenharmony_cistatic int bcm63xx_hsspi_suspend(struct device *dev) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 4878c2ecf20Sopenharmony_ci struct bcm63xx_hsspi *bs = spi_master_get_devdata(master); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci spi_master_suspend(master); 4908c2ecf20Sopenharmony_ci clk_disable_unprepare(bs->pll_clk); 4918c2ecf20Sopenharmony_ci clk_disable_unprepare(bs->clk); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci return 0; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic int bcm63xx_hsspi_resume(struct device *dev) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 4998c2ecf20Sopenharmony_ci struct bcm63xx_hsspi *bs = spi_master_get_devdata(master); 5008c2ecf20Sopenharmony_ci int ret; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci ret = clk_prepare_enable(bs->clk); 5038c2ecf20Sopenharmony_ci if (ret) 5048c2ecf20Sopenharmony_ci return ret; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci if (bs->pll_clk) { 5078c2ecf20Sopenharmony_ci ret = clk_prepare_enable(bs->pll_clk); 5088c2ecf20Sopenharmony_ci if (ret) { 5098c2ecf20Sopenharmony_ci clk_disable_unprepare(bs->clk); 5108c2ecf20Sopenharmony_ci return ret; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci spi_master_resume(master); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci return 0; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci#endif 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(bcm63xx_hsspi_pm_ops, bcm63xx_hsspi_suspend, 5218c2ecf20Sopenharmony_ci bcm63xx_hsspi_resume); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_cistatic const struct of_device_id bcm63xx_hsspi_of_match[] = { 5248c2ecf20Sopenharmony_ci { .compatible = "brcm,bcm6328-hsspi", }, 5258c2ecf20Sopenharmony_ci { }, 5268c2ecf20Sopenharmony_ci}; 5278c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, bcm63xx_hsspi_of_match); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic struct platform_driver bcm63xx_hsspi_driver = { 5308c2ecf20Sopenharmony_ci .driver = { 5318c2ecf20Sopenharmony_ci .name = "bcm63xx-hsspi", 5328c2ecf20Sopenharmony_ci .pm = &bcm63xx_hsspi_pm_ops, 5338c2ecf20Sopenharmony_ci .of_match_table = bcm63xx_hsspi_of_match, 5348c2ecf20Sopenharmony_ci }, 5358c2ecf20Sopenharmony_ci .probe = bcm63xx_hsspi_probe, 5368c2ecf20Sopenharmony_ci .remove = bcm63xx_hsspi_remove, 5378c2ecf20Sopenharmony_ci}; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cimodule_platform_driver(bcm63xx_hsspi_driver); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:bcm63xx_hsspi"); 5428c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Broadcom BCM63xx High Speed SPI Controller driver"); 5438c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>"); 5448c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 545