18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Copyright (c) 2009 Samsung Electronics Co., Ltd. 48c2ecf20Sopenharmony_ci// Jaswinder Singh <jassi.brar@samsung.com> 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/init.h> 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 128c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 158c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 168c2ecf20Sopenharmony_ci#include <linux/gpio.h> 178c2ecf20Sopenharmony_ci#include <linux/of.h> 188c2ecf20Sopenharmony_ci#include <linux/of_gpio.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/platform_data/spi-s3c64xx.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define MAX_SPI_PORTS 6 238c2ecf20Sopenharmony_ci#define S3C64XX_SPI_QUIRK_POLL (1 << 0) 248c2ecf20Sopenharmony_ci#define S3C64XX_SPI_QUIRK_CS_AUTO (1 << 1) 258c2ecf20Sopenharmony_ci#define AUTOSUSPEND_TIMEOUT 2000 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* Registers and bit-fields */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define S3C64XX_SPI_CH_CFG 0x00 308c2ecf20Sopenharmony_ci#define S3C64XX_SPI_CLK_CFG 0x04 318c2ecf20Sopenharmony_ci#define S3C64XX_SPI_MODE_CFG 0x08 328c2ecf20Sopenharmony_ci#define S3C64XX_SPI_CS_REG 0x0C 338c2ecf20Sopenharmony_ci#define S3C64XX_SPI_INT_EN 0x10 348c2ecf20Sopenharmony_ci#define S3C64XX_SPI_STATUS 0x14 358c2ecf20Sopenharmony_ci#define S3C64XX_SPI_TX_DATA 0x18 368c2ecf20Sopenharmony_ci#define S3C64XX_SPI_RX_DATA 0x1C 378c2ecf20Sopenharmony_ci#define S3C64XX_SPI_PACKET_CNT 0x20 388c2ecf20Sopenharmony_ci#define S3C64XX_SPI_PENDING_CLR 0x24 398c2ecf20Sopenharmony_ci#define S3C64XX_SPI_SWAP_CFG 0x28 408c2ecf20Sopenharmony_ci#define S3C64XX_SPI_FB_CLK 0x2C 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define S3C64XX_SPI_CH_HS_EN (1<<6) /* High Speed Enable */ 438c2ecf20Sopenharmony_ci#define S3C64XX_SPI_CH_SW_RST (1<<5) 448c2ecf20Sopenharmony_ci#define S3C64XX_SPI_CH_SLAVE (1<<4) 458c2ecf20Sopenharmony_ci#define S3C64XX_SPI_CPOL_L (1<<3) 468c2ecf20Sopenharmony_ci#define S3C64XX_SPI_CPHA_B (1<<2) 478c2ecf20Sopenharmony_ci#define S3C64XX_SPI_CH_RXCH_ON (1<<1) 488c2ecf20Sopenharmony_ci#define S3C64XX_SPI_CH_TXCH_ON (1<<0) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define S3C64XX_SPI_CLKSEL_SRCMSK (3<<9) 518c2ecf20Sopenharmony_ci#define S3C64XX_SPI_CLKSEL_SRCSHFT 9 528c2ecf20Sopenharmony_ci#define S3C64XX_SPI_ENCLK_ENABLE (1<<8) 538c2ecf20Sopenharmony_ci#define S3C64XX_SPI_PSR_MASK 0xff 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define S3C64XX_SPI_MODE_CH_TSZ_BYTE (0<<29) 568c2ecf20Sopenharmony_ci#define S3C64XX_SPI_MODE_CH_TSZ_HALFWORD (1<<29) 578c2ecf20Sopenharmony_ci#define S3C64XX_SPI_MODE_CH_TSZ_WORD (2<<29) 588c2ecf20Sopenharmony_ci#define S3C64XX_SPI_MODE_CH_TSZ_MASK (3<<29) 598c2ecf20Sopenharmony_ci#define S3C64XX_SPI_MODE_BUS_TSZ_BYTE (0<<17) 608c2ecf20Sopenharmony_ci#define S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD (1<<17) 618c2ecf20Sopenharmony_ci#define S3C64XX_SPI_MODE_BUS_TSZ_WORD (2<<17) 628c2ecf20Sopenharmony_ci#define S3C64XX_SPI_MODE_BUS_TSZ_MASK (3<<17) 638c2ecf20Sopenharmony_ci#define S3C64XX_SPI_MODE_RXDMA_ON (1<<2) 648c2ecf20Sopenharmony_ci#define S3C64XX_SPI_MODE_TXDMA_ON (1<<1) 658c2ecf20Sopenharmony_ci#define S3C64XX_SPI_MODE_4BURST (1<<0) 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define S3C64XX_SPI_CS_NSC_CNT_2 (2<<4) 688c2ecf20Sopenharmony_ci#define S3C64XX_SPI_CS_AUTO (1<<1) 698c2ecf20Sopenharmony_ci#define S3C64XX_SPI_CS_SIG_INACT (1<<0) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define S3C64XX_SPI_INT_TRAILING_EN (1<<6) 728c2ecf20Sopenharmony_ci#define S3C64XX_SPI_INT_RX_OVERRUN_EN (1<<5) 738c2ecf20Sopenharmony_ci#define S3C64XX_SPI_INT_RX_UNDERRUN_EN (1<<4) 748c2ecf20Sopenharmony_ci#define S3C64XX_SPI_INT_TX_OVERRUN_EN (1<<3) 758c2ecf20Sopenharmony_ci#define S3C64XX_SPI_INT_TX_UNDERRUN_EN (1<<2) 768c2ecf20Sopenharmony_ci#define S3C64XX_SPI_INT_RX_FIFORDY_EN (1<<1) 778c2ecf20Sopenharmony_ci#define S3C64XX_SPI_INT_TX_FIFORDY_EN (1<<0) 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define S3C64XX_SPI_ST_RX_OVERRUN_ERR (1<<5) 808c2ecf20Sopenharmony_ci#define S3C64XX_SPI_ST_RX_UNDERRUN_ERR (1<<4) 818c2ecf20Sopenharmony_ci#define S3C64XX_SPI_ST_TX_OVERRUN_ERR (1<<3) 828c2ecf20Sopenharmony_ci#define S3C64XX_SPI_ST_TX_UNDERRUN_ERR (1<<2) 838c2ecf20Sopenharmony_ci#define S3C64XX_SPI_ST_RX_FIFORDY (1<<1) 848c2ecf20Sopenharmony_ci#define S3C64XX_SPI_ST_TX_FIFORDY (1<<0) 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define S3C64XX_SPI_PACKET_CNT_EN (1<<16) 878c2ecf20Sopenharmony_ci#define S3C64XX_SPI_PACKET_CNT_MASK GENMASK(15, 0) 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#define S3C64XX_SPI_PND_TX_UNDERRUN_CLR (1<<4) 908c2ecf20Sopenharmony_ci#define S3C64XX_SPI_PND_TX_OVERRUN_CLR (1<<3) 918c2ecf20Sopenharmony_ci#define S3C64XX_SPI_PND_RX_UNDERRUN_CLR (1<<2) 928c2ecf20Sopenharmony_ci#define S3C64XX_SPI_PND_RX_OVERRUN_CLR (1<<1) 938c2ecf20Sopenharmony_ci#define S3C64XX_SPI_PND_TRAILING_CLR (1<<0) 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#define S3C64XX_SPI_SWAP_RX_HALF_WORD (1<<7) 968c2ecf20Sopenharmony_ci#define S3C64XX_SPI_SWAP_RX_BYTE (1<<6) 978c2ecf20Sopenharmony_ci#define S3C64XX_SPI_SWAP_RX_BIT (1<<5) 988c2ecf20Sopenharmony_ci#define S3C64XX_SPI_SWAP_RX_EN (1<<4) 998c2ecf20Sopenharmony_ci#define S3C64XX_SPI_SWAP_TX_HALF_WORD (1<<3) 1008c2ecf20Sopenharmony_ci#define S3C64XX_SPI_SWAP_TX_BYTE (1<<2) 1018c2ecf20Sopenharmony_ci#define S3C64XX_SPI_SWAP_TX_BIT (1<<1) 1028c2ecf20Sopenharmony_ci#define S3C64XX_SPI_SWAP_TX_EN (1<<0) 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci#define S3C64XX_SPI_FBCLK_MSK (3<<0) 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci#define FIFO_LVL_MASK(i) ((i)->port_conf->fifo_lvl_mask[i->port_id]) 1078c2ecf20Sopenharmony_ci#define S3C64XX_SPI_ST_TX_DONE(v, i) (((v) & \ 1088c2ecf20Sopenharmony_ci (1 << (i)->port_conf->tx_st_done)) ? 1 : 0) 1098c2ecf20Sopenharmony_ci#define TX_FIFO_LVL(v, i) (((v) >> 6) & FIFO_LVL_MASK(i)) 1108c2ecf20Sopenharmony_ci#define RX_FIFO_LVL(v, i) (((v) >> (i)->port_conf->rx_lvl_offset) & \ 1118c2ecf20Sopenharmony_ci FIFO_LVL_MASK(i)) 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci#define S3C64XX_SPI_MAX_TRAILCNT 0x3ff 1148c2ecf20Sopenharmony_ci#define S3C64XX_SPI_TRAILCNT_OFF 19 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci#define S3C64XX_SPI_TRAILCNT S3C64XX_SPI_MAX_TRAILCNT 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) 1198c2ecf20Sopenharmony_ci#define is_polling(x) (x->port_conf->quirks & S3C64XX_SPI_QUIRK_POLL) 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci#define RXBUSY (1<<2) 1228c2ecf20Sopenharmony_ci#define TXBUSY (1<<3) 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistruct s3c64xx_spi_dma_data { 1258c2ecf20Sopenharmony_ci struct dma_chan *ch; 1268c2ecf20Sopenharmony_ci dma_cookie_t cookie; 1278c2ecf20Sopenharmony_ci enum dma_transfer_direction direction; 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/** 1318c2ecf20Sopenharmony_ci * struct s3c64xx_spi_info - SPI Controller hardware info 1328c2ecf20Sopenharmony_ci * @fifo_lvl_mask: Bit-mask for {TX|RX}_FIFO_LVL bits in SPI_STATUS register. 1338c2ecf20Sopenharmony_ci * @rx_lvl_offset: Bit offset of RX_FIFO_LVL bits in SPI_STATUS regiter. 1348c2ecf20Sopenharmony_ci * @tx_st_done: Bit offset of TX_DONE bit in SPI_STATUS regiter. 1358c2ecf20Sopenharmony_ci * @quirks: Bitmask of known quirks 1368c2ecf20Sopenharmony_ci * @high_speed: True, if the controller supports HIGH_SPEED_EN bit. 1378c2ecf20Sopenharmony_ci * @clk_from_cmu: True, if the controller does not include a clock mux and 1388c2ecf20Sopenharmony_ci * prescaler unit. 1398c2ecf20Sopenharmony_ci * @clk_ioclk: True if clock is present on this device 1408c2ecf20Sopenharmony_ci * 1418c2ecf20Sopenharmony_ci * The Samsung s3c64xx SPI controller are used on various Samsung SoC's but 1428c2ecf20Sopenharmony_ci * differ in some aspects such as the size of the fifo and spi bus clock 1438c2ecf20Sopenharmony_ci * setup. Such differences are specified to the driver using this structure 1448c2ecf20Sopenharmony_ci * which is provided as driver data to the driver. 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_cistruct s3c64xx_spi_port_config { 1478c2ecf20Sopenharmony_ci int fifo_lvl_mask[MAX_SPI_PORTS]; 1488c2ecf20Sopenharmony_ci int rx_lvl_offset; 1498c2ecf20Sopenharmony_ci int tx_st_done; 1508c2ecf20Sopenharmony_ci int quirks; 1518c2ecf20Sopenharmony_ci bool high_speed; 1528c2ecf20Sopenharmony_ci bool clk_from_cmu; 1538c2ecf20Sopenharmony_ci bool clk_ioclk; 1548c2ecf20Sopenharmony_ci}; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/** 1578c2ecf20Sopenharmony_ci * struct s3c64xx_spi_driver_data - Runtime info holder for SPI driver. 1588c2ecf20Sopenharmony_ci * @clk: Pointer to the spi clock. 1598c2ecf20Sopenharmony_ci * @src_clk: Pointer to the clock used to generate SPI signals. 1608c2ecf20Sopenharmony_ci * @ioclk: Pointer to the i/o clock between master and slave 1618c2ecf20Sopenharmony_ci * @pdev: Pointer to device's platform device data 1628c2ecf20Sopenharmony_ci * @master: Pointer to the SPI Protocol master. 1638c2ecf20Sopenharmony_ci * @cntrlr_info: Platform specific data for the controller this driver manages. 1648c2ecf20Sopenharmony_ci * @lock: Controller specific lock. 1658c2ecf20Sopenharmony_ci * @state: Set of FLAGS to indicate status. 1668c2ecf20Sopenharmony_ci * @sfr_start: BUS address of SPI controller regs. 1678c2ecf20Sopenharmony_ci * @regs: Pointer to ioremap'ed controller registers. 1688c2ecf20Sopenharmony_ci * @xfer_completion: To indicate completion of xfer task. 1698c2ecf20Sopenharmony_ci * @cur_mode: Stores the active configuration of the controller. 1708c2ecf20Sopenharmony_ci * @cur_bpw: Stores the active bits per word settings. 1718c2ecf20Sopenharmony_ci * @cur_speed: Current clock speed 1728c2ecf20Sopenharmony_ci * @rx_dma: Local receive DMA data (e.g. chan and direction) 1738c2ecf20Sopenharmony_ci * @tx_dma: Local transmit DMA data (e.g. chan and direction) 1748c2ecf20Sopenharmony_ci * @port_conf: Local SPI port configuartion data 1758c2ecf20Sopenharmony_ci * @port_id: Port identification number 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_cistruct s3c64xx_spi_driver_data { 1788c2ecf20Sopenharmony_ci void __iomem *regs; 1798c2ecf20Sopenharmony_ci struct clk *clk; 1808c2ecf20Sopenharmony_ci struct clk *src_clk; 1818c2ecf20Sopenharmony_ci struct clk *ioclk; 1828c2ecf20Sopenharmony_ci struct platform_device *pdev; 1838c2ecf20Sopenharmony_ci struct spi_master *master; 1848c2ecf20Sopenharmony_ci struct s3c64xx_spi_info *cntrlr_info; 1858c2ecf20Sopenharmony_ci spinlock_t lock; 1868c2ecf20Sopenharmony_ci unsigned long sfr_start; 1878c2ecf20Sopenharmony_ci struct completion xfer_completion; 1888c2ecf20Sopenharmony_ci unsigned state; 1898c2ecf20Sopenharmony_ci unsigned cur_mode, cur_bpw; 1908c2ecf20Sopenharmony_ci unsigned cur_speed; 1918c2ecf20Sopenharmony_ci struct s3c64xx_spi_dma_data rx_dma; 1928c2ecf20Sopenharmony_ci struct s3c64xx_spi_dma_data tx_dma; 1938c2ecf20Sopenharmony_ci struct s3c64xx_spi_port_config *port_conf; 1948c2ecf20Sopenharmony_ci unsigned int port_id; 1958c2ecf20Sopenharmony_ci}; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic void s3c64xx_flush_fifo(struct s3c64xx_spi_driver_data *sdd) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci void __iomem *regs = sdd->regs; 2008c2ecf20Sopenharmony_ci unsigned long loops; 2018c2ecf20Sopenharmony_ci u32 val; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci writel(0, regs + S3C64XX_SPI_PACKET_CNT); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci val = readl(regs + S3C64XX_SPI_CH_CFG); 2068c2ecf20Sopenharmony_ci val &= ~(S3C64XX_SPI_CH_RXCH_ON | S3C64XX_SPI_CH_TXCH_ON); 2078c2ecf20Sopenharmony_ci writel(val, regs + S3C64XX_SPI_CH_CFG); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci val = readl(regs + S3C64XX_SPI_CH_CFG); 2108c2ecf20Sopenharmony_ci val |= S3C64XX_SPI_CH_SW_RST; 2118c2ecf20Sopenharmony_ci val &= ~S3C64XX_SPI_CH_HS_EN; 2128c2ecf20Sopenharmony_ci writel(val, regs + S3C64XX_SPI_CH_CFG); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* Flush TxFIFO*/ 2158c2ecf20Sopenharmony_ci loops = msecs_to_loops(1); 2168c2ecf20Sopenharmony_ci do { 2178c2ecf20Sopenharmony_ci val = readl(regs + S3C64XX_SPI_STATUS); 2188c2ecf20Sopenharmony_ci } while (TX_FIFO_LVL(val, sdd) && loops--); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (loops == 0) 2218c2ecf20Sopenharmony_ci dev_warn(&sdd->pdev->dev, "Timed out flushing TX FIFO\n"); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci /* Flush RxFIFO*/ 2248c2ecf20Sopenharmony_ci loops = msecs_to_loops(1); 2258c2ecf20Sopenharmony_ci do { 2268c2ecf20Sopenharmony_ci val = readl(regs + S3C64XX_SPI_STATUS); 2278c2ecf20Sopenharmony_ci if (RX_FIFO_LVL(val, sdd)) 2288c2ecf20Sopenharmony_ci readl(regs + S3C64XX_SPI_RX_DATA); 2298c2ecf20Sopenharmony_ci else 2308c2ecf20Sopenharmony_ci break; 2318c2ecf20Sopenharmony_ci } while (loops--); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (loops == 0) 2348c2ecf20Sopenharmony_ci dev_warn(&sdd->pdev->dev, "Timed out flushing RX FIFO\n"); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci val = readl(regs + S3C64XX_SPI_CH_CFG); 2378c2ecf20Sopenharmony_ci val &= ~S3C64XX_SPI_CH_SW_RST; 2388c2ecf20Sopenharmony_ci writel(val, regs + S3C64XX_SPI_CH_CFG); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci val = readl(regs + S3C64XX_SPI_MODE_CFG); 2418c2ecf20Sopenharmony_ci val &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON); 2428c2ecf20Sopenharmony_ci writel(val, regs + S3C64XX_SPI_MODE_CFG); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic void s3c64xx_spi_dmacb(void *data) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd; 2488c2ecf20Sopenharmony_ci struct s3c64xx_spi_dma_data *dma = data; 2498c2ecf20Sopenharmony_ci unsigned long flags; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (dma->direction == DMA_DEV_TO_MEM) 2528c2ecf20Sopenharmony_ci sdd = container_of(data, 2538c2ecf20Sopenharmony_ci struct s3c64xx_spi_driver_data, rx_dma); 2548c2ecf20Sopenharmony_ci else 2558c2ecf20Sopenharmony_ci sdd = container_of(data, 2568c2ecf20Sopenharmony_ci struct s3c64xx_spi_driver_data, tx_dma); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci spin_lock_irqsave(&sdd->lock, flags); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (dma->direction == DMA_DEV_TO_MEM) { 2618c2ecf20Sopenharmony_ci sdd->state &= ~RXBUSY; 2628c2ecf20Sopenharmony_ci if (!(sdd->state & TXBUSY)) 2638c2ecf20Sopenharmony_ci complete(&sdd->xfer_completion); 2648c2ecf20Sopenharmony_ci } else { 2658c2ecf20Sopenharmony_ci sdd->state &= ~TXBUSY; 2668c2ecf20Sopenharmony_ci if (!(sdd->state & RXBUSY)) 2678c2ecf20Sopenharmony_ci complete(&sdd->xfer_completion); 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sdd->lock, flags); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic int prepare_dma(struct s3c64xx_spi_dma_data *dma, 2748c2ecf20Sopenharmony_ci struct sg_table *sgt) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd; 2778c2ecf20Sopenharmony_ci struct dma_slave_config config; 2788c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *desc; 2798c2ecf20Sopenharmony_ci int ret; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci memset(&config, 0, sizeof(config)); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (dma->direction == DMA_DEV_TO_MEM) { 2848c2ecf20Sopenharmony_ci sdd = container_of((void *)dma, 2858c2ecf20Sopenharmony_ci struct s3c64xx_spi_driver_data, rx_dma); 2868c2ecf20Sopenharmony_ci config.direction = dma->direction; 2878c2ecf20Sopenharmony_ci config.src_addr = sdd->sfr_start + S3C64XX_SPI_RX_DATA; 2888c2ecf20Sopenharmony_ci config.src_addr_width = sdd->cur_bpw / 8; 2898c2ecf20Sopenharmony_ci config.src_maxburst = 1; 2908c2ecf20Sopenharmony_ci dmaengine_slave_config(dma->ch, &config); 2918c2ecf20Sopenharmony_ci } else { 2928c2ecf20Sopenharmony_ci sdd = container_of((void *)dma, 2938c2ecf20Sopenharmony_ci struct s3c64xx_spi_driver_data, tx_dma); 2948c2ecf20Sopenharmony_ci config.direction = dma->direction; 2958c2ecf20Sopenharmony_ci config.dst_addr = sdd->sfr_start + S3C64XX_SPI_TX_DATA; 2968c2ecf20Sopenharmony_ci config.dst_addr_width = sdd->cur_bpw / 8; 2978c2ecf20Sopenharmony_ci config.dst_maxburst = 1; 2988c2ecf20Sopenharmony_ci dmaengine_slave_config(dma->ch, &config); 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci desc = dmaengine_prep_slave_sg(dma->ch, sgt->sgl, sgt->nents, 3028c2ecf20Sopenharmony_ci dma->direction, DMA_PREP_INTERRUPT); 3038c2ecf20Sopenharmony_ci if (!desc) { 3048c2ecf20Sopenharmony_ci dev_err(&sdd->pdev->dev, "unable to prepare %s scatterlist", 3058c2ecf20Sopenharmony_ci dma->direction == DMA_DEV_TO_MEM ? "rx" : "tx"); 3068c2ecf20Sopenharmony_ci return -ENOMEM; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci desc->callback = s3c64xx_spi_dmacb; 3108c2ecf20Sopenharmony_ci desc->callback_param = dma; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci dma->cookie = dmaengine_submit(desc); 3138c2ecf20Sopenharmony_ci ret = dma_submit_error(dma->cookie); 3148c2ecf20Sopenharmony_ci if (ret) { 3158c2ecf20Sopenharmony_ci dev_err(&sdd->pdev->dev, "DMA submission failed"); 3168c2ecf20Sopenharmony_ci return -EIO; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci dma_async_issue_pending(dma->ch); 3208c2ecf20Sopenharmony_ci return 0; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic void s3c64xx_spi_set_cs(struct spi_device *spi, bool enable) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = 3268c2ecf20Sopenharmony_ci spi_master_get_devdata(spi->master); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (sdd->cntrlr_info->no_cs) 3298c2ecf20Sopenharmony_ci return; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (enable) { 3328c2ecf20Sopenharmony_ci if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) { 3338c2ecf20Sopenharmony_ci writel(0, sdd->regs + S3C64XX_SPI_CS_REG); 3348c2ecf20Sopenharmony_ci } else { 3358c2ecf20Sopenharmony_ci u32 ssel = readl(sdd->regs + S3C64XX_SPI_CS_REG); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci ssel |= (S3C64XX_SPI_CS_AUTO | 3388c2ecf20Sopenharmony_ci S3C64XX_SPI_CS_NSC_CNT_2); 3398c2ecf20Sopenharmony_ci writel(ssel, sdd->regs + S3C64XX_SPI_CS_REG); 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci } else { 3428c2ecf20Sopenharmony_ci if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) 3438c2ecf20Sopenharmony_ci writel(S3C64XX_SPI_CS_SIG_INACT, 3448c2ecf20Sopenharmony_ci sdd->regs + S3C64XX_SPI_CS_REG); 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic int s3c64xx_spi_prepare_transfer(struct spi_master *spi) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (is_polling(sdd)) 3538c2ecf20Sopenharmony_ci return 0; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci spi->dma_rx = sdd->rx_dma.ch; 3568c2ecf20Sopenharmony_ci spi->dma_tx = sdd->tx_dma.ch; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return 0; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic bool s3c64xx_spi_can_dma(struct spi_master *master, 3628c2ecf20Sopenharmony_ci struct spi_device *spi, 3638c2ecf20Sopenharmony_ci struct spi_transfer *xfer) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci return xfer->len > (FIFO_LVL_MASK(sdd) >> 1) + 1; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic int s3c64xx_enable_datapath(struct s3c64xx_spi_driver_data *sdd, 3718c2ecf20Sopenharmony_ci struct spi_transfer *xfer, int dma_mode) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci void __iomem *regs = sdd->regs; 3748c2ecf20Sopenharmony_ci u32 modecfg, chcfg; 3758c2ecf20Sopenharmony_ci int ret = 0; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci modecfg = readl(regs + S3C64XX_SPI_MODE_CFG); 3788c2ecf20Sopenharmony_ci modecfg &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci chcfg = readl(regs + S3C64XX_SPI_CH_CFG); 3818c2ecf20Sopenharmony_ci chcfg &= ~S3C64XX_SPI_CH_TXCH_ON; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (dma_mode) { 3848c2ecf20Sopenharmony_ci chcfg &= ~S3C64XX_SPI_CH_RXCH_ON; 3858c2ecf20Sopenharmony_ci } else { 3868c2ecf20Sopenharmony_ci /* Always shift in data in FIFO, even if xfer is Tx only, 3878c2ecf20Sopenharmony_ci * this helps setting PCKT_CNT value for generating clocks 3888c2ecf20Sopenharmony_ci * as exactly needed. 3898c2ecf20Sopenharmony_ci */ 3908c2ecf20Sopenharmony_ci chcfg |= S3C64XX_SPI_CH_RXCH_ON; 3918c2ecf20Sopenharmony_ci writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff) 3928c2ecf20Sopenharmony_ci | S3C64XX_SPI_PACKET_CNT_EN, 3938c2ecf20Sopenharmony_ci regs + S3C64XX_SPI_PACKET_CNT); 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (xfer->tx_buf != NULL) { 3978c2ecf20Sopenharmony_ci sdd->state |= TXBUSY; 3988c2ecf20Sopenharmony_ci chcfg |= S3C64XX_SPI_CH_TXCH_ON; 3998c2ecf20Sopenharmony_ci if (dma_mode) { 4008c2ecf20Sopenharmony_ci modecfg |= S3C64XX_SPI_MODE_TXDMA_ON; 4018c2ecf20Sopenharmony_ci ret = prepare_dma(&sdd->tx_dma, &xfer->tx_sg); 4028c2ecf20Sopenharmony_ci } else { 4038c2ecf20Sopenharmony_ci switch (sdd->cur_bpw) { 4048c2ecf20Sopenharmony_ci case 32: 4058c2ecf20Sopenharmony_ci iowrite32_rep(regs + S3C64XX_SPI_TX_DATA, 4068c2ecf20Sopenharmony_ci xfer->tx_buf, xfer->len / 4); 4078c2ecf20Sopenharmony_ci break; 4088c2ecf20Sopenharmony_ci case 16: 4098c2ecf20Sopenharmony_ci iowrite16_rep(regs + S3C64XX_SPI_TX_DATA, 4108c2ecf20Sopenharmony_ci xfer->tx_buf, xfer->len / 2); 4118c2ecf20Sopenharmony_ci break; 4128c2ecf20Sopenharmony_ci default: 4138c2ecf20Sopenharmony_ci iowrite8_rep(regs + S3C64XX_SPI_TX_DATA, 4148c2ecf20Sopenharmony_ci xfer->tx_buf, xfer->len); 4158c2ecf20Sopenharmony_ci break; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (xfer->rx_buf != NULL) { 4218c2ecf20Sopenharmony_ci sdd->state |= RXBUSY; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (sdd->port_conf->high_speed && sdd->cur_speed >= 30000000UL 4248c2ecf20Sopenharmony_ci && !(sdd->cur_mode & SPI_CPHA)) 4258c2ecf20Sopenharmony_ci chcfg |= S3C64XX_SPI_CH_HS_EN; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (dma_mode) { 4288c2ecf20Sopenharmony_ci modecfg |= S3C64XX_SPI_MODE_RXDMA_ON; 4298c2ecf20Sopenharmony_ci chcfg |= S3C64XX_SPI_CH_RXCH_ON; 4308c2ecf20Sopenharmony_ci writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff) 4318c2ecf20Sopenharmony_ci | S3C64XX_SPI_PACKET_CNT_EN, 4328c2ecf20Sopenharmony_ci regs + S3C64XX_SPI_PACKET_CNT); 4338c2ecf20Sopenharmony_ci ret = prepare_dma(&sdd->rx_dma, &xfer->rx_sg); 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (ret) 4388c2ecf20Sopenharmony_ci return ret; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci writel(modecfg, regs + S3C64XX_SPI_MODE_CFG); 4418c2ecf20Sopenharmony_ci writel(chcfg, regs + S3C64XX_SPI_CH_CFG); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci return 0; 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic u32 s3c64xx_spi_wait_for_timeout(struct s3c64xx_spi_driver_data *sdd, 4478c2ecf20Sopenharmony_ci int timeout_ms) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci void __iomem *regs = sdd->regs; 4508c2ecf20Sopenharmony_ci unsigned long val = 1; 4518c2ecf20Sopenharmony_ci u32 status; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* max fifo depth available */ 4548c2ecf20Sopenharmony_ci u32 max_fifo = (FIFO_LVL_MASK(sdd) >> 1) + 1; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (timeout_ms) 4578c2ecf20Sopenharmony_ci val = msecs_to_loops(timeout_ms); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci do { 4608c2ecf20Sopenharmony_ci status = readl(regs + S3C64XX_SPI_STATUS); 4618c2ecf20Sopenharmony_ci } while (RX_FIFO_LVL(status, sdd) < max_fifo && --val); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* return the actual received data length */ 4648c2ecf20Sopenharmony_ci return RX_FIFO_LVL(status, sdd); 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic int s3c64xx_wait_for_dma(struct s3c64xx_spi_driver_data *sdd, 4688c2ecf20Sopenharmony_ci struct spi_transfer *xfer) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci void __iomem *regs = sdd->regs; 4718c2ecf20Sopenharmony_ci unsigned long val; 4728c2ecf20Sopenharmony_ci u32 status; 4738c2ecf20Sopenharmony_ci int ms; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* millisecs to xfer 'len' bytes @ 'cur_speed' */ 4768c2ecf20Sopenharmony_ci ms = xfer->len * 8 * 1000 / sdd->cur_speed; 4778c2ecf20Sopenharmony_ci ms += 30; /* some tolerance */ 4788c2ecf20Sopenharmony_ci ms = max(ms, 100); /* minimum timeout */ 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci val = msecs_to_jiffies(ms) + 10; 4818c2ecf20Sopenharmony_ci val = wait_for_completion_timeout(&sdd->xfer_completion, val); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* 4848c2ecf20Sopenharmony_ci * If the previous xfer was completed within timeout, then 4858c2ecf20Sopenharmony_ci * proceed further else return -EIO. 4868c2ecf20Sopenharmony_ci * DmaTx returns after simply writing data in the FIFO, 4878c2ecf20Sopenharmony_ci * w/o waiting for real transmission on the bus to finish. 4888c2ecf20Sopenharmony_ci * DmaRx returns only after Dma read data from FIFO which 4898c2ecf20Sopenharmony_ci * needs bus transmission to finish, so we don't worry if 4908c2ecf20Sopenharmony_ci * Xfer involved Rx(with or without Tx). 4918c2ecf20Sopenharmony_ci */ 4928c2ecf20Sopenharmony_ci if (val && !xfer->rx_buf) { 4938c2ecf20Sopenharmony_ci val = msecs_to_loops(10); 4948c2ecf20Sopenharmony_ci status = readl(regs + S3C64XX_SPI_STATUS); 4958c2ecf20Sopenharmony_ci while ((TX_FIFO_LVL(status, sdd) 4968c2ecf20Sopenharmony_ci || !S3C64XX_SPI_ST_TX_DONE(status, sdd)) 4978c2ecf20Sopenharmony_ci && --val) { 4988c2ecf20Sopenharmony_ci cpu_relax(); 4998c2ecf20Sopenharmony_ci status = readl(regs + S3C64XX_SPI_STATUS); 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* If timed out while checking rx/tx status return error */ 5058c2ecf20Sopenharmony_ci if (!val) 5068c2ecf20Sopenharmony_ci return -EIO; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci return 0; 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cistatic int s3c64xx_wait_for_pio(struct s3c64xx_spi_driver_data *sdd, 5128c2ecf20Sopenharmony_ci struct spi_transfer *xfer) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci void __iomem *regs = sdd->regs; 5158c2ecf20Sopenharmony_ci unsigned long val; 5168c2ecf20Sopenharmony_ci u32 status; 5178c2ecf20Sopenharmony_ci int loops; 5188c2ecf20Sopenharmony_ci u32 cpy_len; 5198c2ecf20Sopenharmony_ci u8 *buf; 5208c2ecf20Sopenharmony_ci int ms; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* millisecs to xfer 'len' bytes @ 'cur_speed' */ 5238c2ecf20Sopenharmony_ci ms = xfer->len * 8 * 1000 / sdd->cur_speed; 5248c2ecf20Sopenharmony_ci ms += 10; /* some tolerance */ 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci val = msecs_to_loops(ms); 5278c2ecf20Sopenharmony_ci do { 5288c2ecf20Sopenharmony_ci status = readl(regs + S3C64XX_SPI_STATUS); 5298c2ecf20Sopenharmony_ci } while (RX_FIFO_LVL(status, sdd) < xfer->len && --val); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (!val) 5328c2ecf20Sopenharmony_ci return -EIO; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* If it was only Tx */ 5358c2ecf20Sopenharmony_ci if (!xfer->rx_buf) { 5368c2ecf20Sopenharmony_ci sdd->state &= ~TXBUSY; 5378c2ecf20Sopenharmony_ci return 0; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci /* 5418c2ecf20Sopenharmony_ci * If the receive length is bigger than the controller fifo 5428c2ecf20Sopenharmony_ci * size, calculate the loops and read the fifo as many times. 5438c2ecf20Sopenharmony_ci * loops = length / max fifo size (calculated by using the 5448c2ecf20Sopenharmony_ci * fifo mask). 5458c2ecf20Sopenharmony_ci * For any size less than the fifo size the below code is 5468c2ecf20Sopenharmony_ci * executed atleast once. 5478c2ecf20Sopenharmony_ci */ 5488c2ecf20Sopenharmony_ci loops = xfer->len / ((FIFO_LVL_MASK(sdd) >> 1) + 1); 5498c2ecf20Sopenharmony_ci buf = xfer->rx_buf; 5508c2ecf20Sopenharmony_ci do { 5518c2ecf20Sopenharmony_ci /* wait for data to be received in the fifo */ 5528c2ecf20Sopenharmony_ci cpy_len = s3c64xx_spi_wait_for_timeout(sdd, 5538c2ecf20Sopenharmony_ci (loops ? ms : 0)); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci switch (sdd->cur_bpw) { 5568c2ecf20Sopenharmony_ci case 32: 5578c2ecf20Sopenharmony_ci ioread32_rep(regs + S3C64XX_SPI_RX_DATA, 5588c2ecf20Sopenharmony_ci buf, cpy_len / 4); 5598c2ecf20Sopenharmony_ci break; 5608c2ecf20Sopenharmony_ci case 16: 5618c2ecf20Sopenharmony_ci ioread16_rep(regs + S3C64XX_SPI_RX_DATA, 5628c2ecf20Sopenharmony_ci buf, cpy_len / 2); 5638c2ecf20Sopenharmony_ci break; 5648c2ecf20Sopenharmony_ci default: 5658c2ecf20Sopenharmony_ci ioread8_rep(regs + S3C64XX_SPI_RX_DATA, 5668c2ecf20Sopenharmony_ci buf, cpy_len); 5678c2ecf20Sopenharmony_ci break; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci buf = buf + cpy_len; 5718c2ecf20Sopenharmony_ci } while (loops--); 5728c2ecf20Sopenharmony_ci sdd->state &= ~RXBUSY; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci return 0; 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic int s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci void __iomem *regs = sdd->regs; 5808c2ecf20Sopenharmony_ci int ret; 5818c2ecf20Sopenharmony_ci u32 val; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci /* Disable Clock */ 5848c2ecf20Sopenharmony_ci if (!sdd->port_conf->clk_from_cmu) { 5858c2ecf20Sopenharmony_ci val = readl(regs + S3C64XX_SPI_CLK_CFG); 5868c2ecf20Sopenharmony_ci val &= ~S3C64XX_SPI_ENCLK_ENABLE; 5878c2ecf20Sopenharmony_ci writel(val, regs + S3C64XX_SPI_CLK_CFG); 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci /* Set Polarity and Phase */ 5918c2ecf20Sopenharmony_ci val = readl(regs + S3C64XX_SPI_CH_CFG); 5928c2ecf20Sopenharmony_ci val &= ~(S3C64XX_SPI_CH_SLAVE | 5938c2ecf20Sopenharmony_ci S3C64XX_SPI_CPOL_L | 5948c2ecf20Sopenharmony_ci S3C64XX_SPI_CPHA_B); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci if (sdd->cur_mode & SPI_CPOL) 5978c2ecf20Sopenharmony_ci val |= S3C64XX_SPI_CPOL_L; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (sdd->cur_mode & SPI_CPHA) 6008c2ecf20Sopenharmony_ci val |= S3C64XX_SPI_CPHA_B; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci writel(val, regs + S3C64XX_SPI_CH_CFG); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci /* Set Channel & DMA Mode */ 6058c2ecf20Sopenharmony_ci val = readl(regs + S3C64XX_SPI_MODE_CFG); 6068c2ecf20Sopenharmony_ci val &= ~(S3C64XX_SPI_MODE_BUS_TSZ_MASK 6078c2ecf20Sopenharmony_ci | S3C64XX_SPI_MODE_CH_TSZ_MASK); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci switch (sdd->cur_bpw) { 6108c2ecf20Sopenharmony_ci case 32: 6118c2ecf20Sopenharmony_ci val |= S3C64XX_SPI_MODE_BUS_TSZ_WORD; 6128c2ecf20Sopenharmony_ci val |= S3C64XX_SPI_MODE_CH_TSZ_WORD; 6138c2ecf20Sopenharmony_ci break; 6148c2ecf20Sopenharmony_ci case 16: 6158c2ecf20Sopenharmony_ci val |= S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD; 6168c2ecf20Sopenharmony_ci val |= S3C64XX_SPI_MODE_CH_TSZ_HALFWORD; 6178c2ecf20Sopenharmony_ci break; 6188c2ecf20Sopenharmony_ci default: 6198c2ecf20Sopenharmony_ci val |= S3C64XX_SPI_MODE_BUS_TSZ_BYTE; 6208c2ecf20Sopenharmony_ci val |= S3C64XX_SPI_MODE_CH_TSZ_BYTE; 6218c2ecf20Sopenharmony_ci break; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci writel(val, regs + S3C64XX_SPI_MODE_CFG); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (sdd->port_conf->clk_from_cmu) { 6278c2ecf20Sopenharmony_ci /* The src_clk clock is divided internally by 2 */ 6288c2ecf20Sopenharmony_ci ret = clk_set_rate(sdd->src_clk, sdd->cur_speed * 2); 6298c2ecf20Sopenharmony_ci if (ret) 6308c2ecf20Sopenharmony_ci return ret; 6318c2ecf20Sopenharmony_ci sdd->cur_speed = clk_get_rate(sdd->src_clk) / 2; 6328c2ecf20Sopenharmony_ci } else { 6338c2ecf20Sopenharmony_ci /* Configure Clock */ 6348c2ecf20Sopenharmony_ci val = readl(regs + S3C64XX_SPI_CLK_CFG); 6358c2ecf20Sopenharmony_ci val &= ~S3C64XX_SPI_PSR_MASK; 6368c2ecf20Sopenharmony_ci val |= ((clk_get_rate(sdd->src_clk) / sdd->cur_speed / 2 - 1) 6378c2ecf20Sopenharmony_ci & S3C64XX_SPI_PSR_MASK); 6388c2ecf20Sopenharmony_ci writel(val, regs + S3C64XX_SPI_CLK_CFG); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci /* Enable Clock */ 6418c2ecf20Sopenharmony_ci val = readl(regs + S3C64XX_SPI_CLK_CFG); 6428c2ecf20Sopenharmony_ci val |= S3C64XX_SPI_ENCLK_ENABLE; 6438c2ecf20Sopenharmony_ci writel(val, regs + S3C64XX_SPI_CLK_CFG); 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci return 0; 6478c2ecf20Sopenharmony_ci} 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci#define XFER_DMAADDR_INVALID DMA_BIT_MASK(32) 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic int s3c64xx_spi_prepare_message(struct spi_master *master, 6528c2ecf20Sopenharmony_ci struct spi_message *msg) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); 6558c2ecf20Sopenharmony_ci struct spi_device *spi = msg->spi; 6568c2ecf20Sopenharmony_ci struct s3c64xx_spi_csinfo *cs = spi->controller_data; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci /* Configure feedback delay */ 6598c2ecf20Sopenharmony_ci writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci return 0; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic size_t s3c64xx_spi_max_transfer_size(struct spi_device *spi) 6658c2ecf20Sopenharmony_ci{ 6668c2ecf20Sopenharmony_ci struct spi_controller *ctlr = spi->controller; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci return ctlr->can_dma ? S3C64XX_SPI_PACKET_CNT_MASK : SIZE_MAX; 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cistatic int s3c64xx_spi_transfer_one(struct spi_master *master, 6728c2ecf20Sopenharmony_ci struct spi_device *spi, 6738c2ecf20Sopenharmony_ci struct spi_transfer *xfer) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); 6768c2ecf20Sopenharmony_ci const unsigned int fifo_len = (FIFO_LVL_MASK(sdd) >> 1) + 1; 6778c2ecf20Sopenharmony_ci const void *tx_buf = NULL; 6788c2ecf20Sopenharmony_ci void *rx_buf = NULL; 6798c2ecf20Sopenharmony_ci int target_len = 0, origin_len = 0; 6808c2ecf20Sopenharmony_ci int use_dma = 0; 6818c2ecf20Sopenharmony_ci int status; 6828c2ecf20Sopenharmony_ci u32 speed; 6838c2ecf20Sopenharmony_ci u8 bpw; 6848c2ecf20Sopenharmony_ci unsigned long flags; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci reinit_completion(&sdd->xfer_completion); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci /* Only BPW and Speed may change across transfers */ 6898c2ecf20Sopenharmony_ci bpw = xfer->bits_per_word; 6908c2ecf20Sopenharmony_ci speed = xfer->speed_hz; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) { 6938c2ecf20Sopenharmony_ci sdd->cur_bpw = bpw; 6948c2ecf20Sopenharmony_ci sdd->cur_speed = speed; 6958c2ecf20Sopenharmony_ci sdd->cur_mode = spi->mode; 6968c2ecf20Sopenharmony_ci status = s3c64xx_spi_config(sdd); 6978c2ecf20Sopenharmony_ci if (status) 6988c2ecf20Sopenharmony_ci return status; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci if (!is_polling(sdd) && (xfer->len > fifo_len) && 7028c2ecf20Sopenharmony_ci sdd->rx_dma.ch && sdd->tx_dma.ch) { 7038c2ecf20Sopenharmony_ci use_dma = 1; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci } else if (is_polling(sdd) && xfer->len > fifo_len) { 7068c2ecf20Sopenharmony_ci tx_buf = xfer->tx_buf; 7078c2ecf20Sopenharmony_ci rx_buf = xfer->rx_buf; 7088c2ecf20Sopenharmony_ci origin_len = xfer->len; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci target_len = xfer->len; 7118c2ecf20Sopenharmony_ci if (xfer->len > fifo_len) 7128c2ecf20Sopenharmony_ci xfer->len = fifo_len; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci do { 7168c2ecf20Sopenharmony_ci spin_lock_irqsave(&sdd->lock, flags); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci /* Pending only which is to be done */ 7198c2ecf20Sopenharmony_ci sdd->state &= ~RXBUSY; 7208c2ecf20Sopenharmony_ci sdd->state &= ~TXBUSY; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci /* Start the signals */ 7238c2ecf20Sopenharmony_ci s3c64xx_spi_set_cs(spi, true); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci status = s3c64xx_enable_datapath(sdd, xfer, use_dma); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sdd->lock, flags); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci if (status) { 7308c2ecf20Sopenharmony_ci dev_err(&spi->dev, "failed to enable data path for transfer: %d\n", status); 7318c2ecf20Sopenharmony_ci break; 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci if (use_dma) 7358c2ecf20Sopenharmony_ci status = s3c64xx_wait_for_dma(sdd, xfer); 7368c2ecf20Sopenharmony_ci else 7378c2ecf20Sopenharmony_ci status = s3c64xx_wait_for_pio(sdd, xfer); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci if (status) { 7408c2ecf20Sopenharmony_ci dev_err(&spi->dev, 7418c2ecf20Sopenharmony_ci "I/O Error: rx-%d tx-%d rx-%c tx-%c len-%d dma-%d res-(%d)\n", 7428c2ecf20Sopenharmony_ci xfer->rx_buf ? 1 : 0, xfer->tx_buf ? 1 : 0, 7438c2ecf20Sopenharmony_ci (sdd->state & RXBUSY) ? 'f' : 'p', 7448c2ecf20Sopenharmony_ci (sdd->state & TXBUSY) ? 'f' : 'p', 7458c2ecf20Sopenharmony_ci xfer->len, use_dma ? 1 : 0, status); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (use_dma) { 7488c2ecf20Sopenharmony_ci struct dma_tx_state s; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci if (xfer->tx_buf && (sdd->state & TXBUSY)) { 7518c2ecf20Sopenharmony_ci dmaengine_pause(sdd->tx_dma.ch); 7528c2ecf20Sopenharmony_ci dmaengine_tx_status(sdd->tx_dma.ch, sdd->tx_dma.cookie, &s); 7538c2ecf20Sopenharmony_ci dmaengine_terminate_all(sdd->tx_dma.ch); 7548c2ecf20Sopenharmony_ci dev_err(&spi->dev, "TX residue: %d\n", s.residue); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci if (xfer->rx_buf && (sdd->state & RXBUSY)) { 7588c2ecf20Sopenharmony_ci dmaengine_pause(sdd->rx_dma.ch); 7598c2ecf20Sopenharmony_ci dmaengine_tx_status(sdd->rx_dma.ch, sdd->rx_dma.cookie, &s); 7608c2ecf20Sopenharmony_ci dmaengine_terminate_all(sdd->rx_dma.ch); 7618c2ecf20Sopenharmony_ci dev_err(&spi->dev, "RX residue: %d\n", s.residue); 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci } else { 7658c2ecf20Sopenharmony_ci s3c64xx_flush_fifo(sdd); 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci if (target_len > 0) { 7688c2ecf20Sopenharmony_ci target_len -= xfer->len; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci if (xfer->tx_buf) 7718c2ecf20Sopenharmony_ci xfer->tx_buf += xfer->len; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci if (xfer->rx_buf) 7748c2ecf20Sopenharmony_ci xfer->rx_buf += xfer->len; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci if (target_len > fifo_len) 7778c2ecf20Sopenharmony_ci xfer->len = fifo_len; 7788c2ecf20Sopenharmony_ci else 7798c2ecf20Sopenharmony_ci xfer->len = target_len; 7808c2ecf20Sopenharmony_ci } 7818c2ecf20Sopenharmony_ci } while (target_len > 0); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci if (origin_len) { 7848c2ecf20Sopenharmony_ci /* Restore original xfer buffers and length */ 7858c2ecf20Sopenharmony_ci xfer->tx_buf = tx_buf; 7868c2ecf20Sopenharmony_ci xfer->rx_buf = rx_buf; 7878c2ecf20Sopenharmony_ci xfer->len = origin_len; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci return status; 7918c2ecf20Sopenharmony_ci} 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_cistatic struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata( 7948c2ecf20Sopenharmony_ci struct spi_device *spi) 7958c2ecf20Sopenharmony_ci{ 7968c2ecf20Sopenharmony_ci struct s3c64xx_spi_csinfo *cs; 7978c2ecf20Sopenharmony_ci struct device_node *slave_np, *data_np = NULL; 7988c2ecf20Sopenharmony_ci u32 fb_delay = 0; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci slave_np = spi->dev.of_node; 8018c2ecf20Sopenharmony_ci if (!slave_np) { 8028c2ecf20Sopenharmony_ci dev_err(&spi->dev, "device node not found\n"); 8038c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci data_np = of_get_child_by_name(slave_np, "controller-data"); 8078c2ecf20Sopenharmony_ci if (!data_np) { 8088c2ecf20Sopenharmony_ci dev_err(&spi->dev, "child node 'controller-data' not found\n"); 8098c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci cs = kzalloc(sizeof(*cs), GFP_KERNEL); 8138c2ecf20Sopenharmony_ci if (!cs) { 8148c2ecf20Sopenharmony_ci of_node_put(data_np); 8158c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci of_property_read_u32(data_np, "samsung,spi-feedback-delay", &fb_delay); 8198c2ecf20Sopenharmony_ci cs->fb_delay = fb_delay; 8208c2ecf20Sopenharmony_ci of_node_put(data_np); 8218c2ecf20Sopenharmony_ci return cs; 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci/* 8258c2ecf20Sopenharmony_ci * Here we only check the validity of requested configuration 8268c2ecf20Sopenharmony_ci * and save the configuration in a local data-structure. 8278c2ecf20Sopenharmony_ci * The controller is actually configured only just before we 8288c2ecf20Sopenharmony_ci * get a message to transfer. 8298c2ecf20Sopenharmony_ci */ 8308c2ecf20Sopenharmony_cistatic int s3c64xx_spi_setup(struct spi_device *spi) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci struct s3c64xx_spi_csinfo *cs = spi->controller_data; 8338c2ecf20Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd; 8348c2ecf20Sopenharmony_ci int err; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci sdd = spi_master_get_devdata(spi->master); 8378c2ecf20Sopenharmony_ci if (spi->dev.of_node) { 8388c2ecf20Sopenharmony_ci cs = s3c64xx_get_slave_ctrldata(spi); 8398c2ecf20Sopenharmony_ci spi->controller_data = cs; 8408c2ecf20Sopenharmony_ci } else if (cs) { 8418c2ecf20Sopenharmony_ci /* On non-DT platforms the SPI core will set spi->cs_gpio 8428c2ecf20Sopenharmony_ci * to -ENOENT. The GPIO pin used to drive the chip select 8438c2ecf20Sopenharmony_ci * is defined by using platform data so spi->cs_gpio value 8448c2ecf20Sopenharmony_ci * has to be override to have the proper GPIO pin number. 8458c2ecf20Sopenharmony_ci */ 8468c2ecf20Sopenharmony_ci spi->cs_gpio = cs->line; 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(cs)) { 8508c2ecf20Sopenharmony_ci dev_err(&spi->dev, "No CS for SPI(%d)\n", spi->chip_select); 8518c2ecf20Sopenharmony_ci return -ENODEV; 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci if (!spi_get_ctldata(spi)) { 8558c2ecf20Sopenharmony_ci if (gpio_is_valid(spi->cs_gpio)) { 8568c2ecf20Sopenharmony_ci err = gpio_request_one(spi->cs_gpio, GPIOF_OUT_INIT_HIGH, 8578c2ecf20Sopenharmony_ci dev_name(&spi->dev)); 8588c2ecf20Sopenharmony_ci if (err) { 8598c2ecf20Sopenharmony_ci dev_err(&spi->dev, 8608c2ecf20Sopenharmony_ci "Failed to get /CS gpio [%d]: %d\n", 8618c2ecf20Sopenharmony_ci spi->cs_gpio, err); 8628c2ecf20Sopenharmony_ci goto err_gpio_req; 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci spi_set_ctldata(spi, cs); 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci pm_runtime_get_sync(&sdd->pdev->dev); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci /* Check if we can provide the requested rate */ 8728c2ecf20Sopenharmony_ci if (!sdd->port_conf->clk_from_cmu) { 8738c2ecf20Sopenharmony_ci u32 psr, speed; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci /* Max possible */ 8768c2ecf20Sopenharmony_ci speed = clk_get_rate(sdd->src_clk) / 2 / (0 + 1); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci if (spi->max_speed_hz > speed) 8798c2ecf20Sopenharmony_ci spi->max_speed_hz = speed; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci psr = clk_get_rate(sdd->src_clk) / 2 / spi->max_speed_hz - 1; 8828c2ecf20Sopenharmony_ci psr &= S3C64XX_SPI_PSR_MASK; 8838c2ecf20Sopenharmony_ci if (psr == S3C64XX_SPI_PSR_MASK) 8848c2ecf20Sopenharmony_ci psr--; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1); 8878c2ecf20Sopenharmony_ci if (spi->max_speed_hz < speed) { 8888c2ecf20Sopenharmony_ci if (psr+1 < S3C64XX_SPI_PSR_MASK) { 8898c2ecf20Sopenharmony_ci psr++; 8908c2ecf20Sopenharmony_ci } else { 8918c2ecf20Sopenharmony_ci err = -EINVAL; 8928c2ecf20Sopenharmony_ci goto setup_exit; 8938c2ecf20Sopenharmony_ci } 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1); 8978c2ecf20Sopenharmony_ci if (spi->max_speed_hz >= speed) { 8988c2ecf20Sopenharmony_ci spi->max_speed_hz = speed; 8998c2ecf20Sopenharmony_ci } else { 9008c2ecf20Sopenharmony_ci dev_err(&spi->dev, "Can't set %dHz transfer speed\n", 9018c2ecf20Sopenharmony_ci spi->max_speed_hz); 9028c2ecf20Sopenharmony_ci err = -EINVAL; 9038c2ecf20Sopenharmony_ci goto setup_exit; 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci } 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(&sdd->pdev->dev); 9088c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(&sdd->pdev->dev); 9098c2ecf20Sopenharmony_ci s3c64xx_spi_set_cs(spi, false); 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci return 0; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_cisetup_exit: 9148c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(&sdd->pdev->dev); 9158c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(&sdd->pdev->dev); 9168c2ecf20Sopenharmony_ci /* setup() returns with device de-selected */ 9178c2ecf20Sopenharmony_ci s3c64xx_spi_set_cs(spi, false); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci if (gpio_is_valid(spi->cs_gpio)) 9208c2ecf20Sopenharmony_ci gpio_free(spi->cs_gpio); 9218c2ecf20Sopenharmony_ci spi_set_ctldata(spi, NULL); 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_cierr_gpio_req: 9248c2ecf20Sopenharmony_ci if (spi->dev.of_node) 9258c2ecf20Sopenharmony_ci kfree(cs); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci return err; 9288c2ecf20Sopenharmony_ci} 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_cistatic void s3c64xx_spi_cleanup(struct spi_device *spi) 9318c2ecf20Sopenharmony_ci{ 9328c2ecf20Sopenharmony_ci struct s3c64xx_spi_csinfo *cs = spi_get_ctldata(spi); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci if (gpio_is_valid(spi->cs_gpio)) { 9358c2ecf20Sopenharmony_ci gpio_free(spi->cs_gpio); 9368c2ecf20Sopenharmony_ci if (spi->dev.of_node) 9378c2ecf20Sopenharmony_ci kfree(cs); 9388c2ecf20Sopenharmony_ci else { 9398c2ecf20Sopenharmony_ci /* On non-DT platforms, the SPI core sets 9408c2ecf20Sopenharmony_ci * spi->cs_gpio to -ENOENT and .setup() 9418c2ecf20Sopenharmony_ci * overrides it with the GPIO pin value 9428c2ecf20Sopenharmony_ci * passed using platform data. 9438c2ecf20Sopenharmony_ci */ 9448c2ecf20Sopenharmony_ci spi->cs_gpio = -ENOENT; 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci } 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci spi_set_ctldata(spi, NULL); 9498c2ecf20Sopenharmony_ci} 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_cistatic irqreturn_t s3c64xx_spi_irq(int irq, void *data) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = data; 9548c2ecf20Sopenharmony_ci struct spi_master *spi = sdd->master; 9558c2ecf20Sopenharmony_ci unsigned int val, clr = 0; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci val = readl(sdd->regs + S3C64XX_SPI_STATUS); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci if (val & S3C64XX_SPI_ST_RX_OVERRUN_ERR) { 9608c2ecf20Sopenharmony_ci clr = S3C64XX_SPI_PND_RX_OVERRUN_CLR; 9618c2ecf20Sopenharmony_ci dev_err(&spi->dev, "RX overrun\n"); 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci if (val & S3C64XX_SPI_ST_RX_UNDERRUN_ERR) { 9648c2ecf20Sopenharmony_ci clr |= S3C64XX_SPI_PND_RX_UNDERRUN_CLR; 9658c2ecf20Sopenharmony_ci dev_err(&spi->dev, "RX underrun\n"); 9668c2ecf20Sopenharmony_ci } 9678c2ecf20Sopenharmony_ci if (val & S3C64XX_SPI_ST_TX_OVERRUN_ERR) { 9688c2ecf20Sopenharmony_ci clr |= S3C64XX_SPI_PND_TX_OVERRUN_CLR; 9698c2ecf20Sopenharmony_ci dev_err(&spi->dev, "TX overrun\n"); 9708c2ecf20Sopenharmony_ci } 9718c2ecf20Sopenharmony_ci if (val & S3C64XX_SPI_ST_TX_UNDERRUN_ERR) { 9728c2ecf20Sopenharmony_ci clr |= S3C64XX_SPI_PND_TX_UNDERRUN_CLR; 9738c2ecf20Sopenharmony_ci dev_err(&spi->dev, "TX underrun\n"); 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci /* Clear the pending irq by setting and then clearing it */ 9778c2ecf20Sopenharmony_ci writel(clr, sdd->regs + S3C64XX_SPI_PENDING_CLR); 9788c2ecf20Sopenharmony_ci writel(0, sdd->regs + S3C64XX_SPI_PENDING_CLR); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci return IRQ_HANDLED; 9818c2ecf20Sopenharmony_ci} 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_cistatic void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd) 9848c2ecf20Sopenharmony_ci{ 9858c2ecf20Sopenharmony_ci struct s3c64xx_spi_info *sci = sdd->cntrlr_info; 9868c2ecf20Sopenharmony_ci void __iomem *regs = sdd->regs; 9878c2ecf20Sopenharmony_ci unsigned int val; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci sdd->cur_speed = 0; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci if (sci->no_cs) 9928c2ecf20Sopenharmony_ci writel(0, sdd->regs + S3C64XX_SPI_CS_REG); 9938c2ecf20Sopenharmony_ci else if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) 9948c2ecf20Sopenharmony_ci writel(S3C64XX_SPI_CS_SIG_INACT, sdd->regs + S3C64XX_SPI_CS_REG); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci /* Disable Interrupts - we use Polling if not DMA mode */ 9978c2ecf20Sopenharmony_ci writel(0, regs + S3C64XX_SPI_INT_EN); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci if (!sdd->port_conf->clk_from_cmu) 10008c2ecf20Sopenharmony_ci writel(sci->src_clk_nr << S3C64XX_SPI_CLKSEL_SRCSHFT, 10018c2ecf20Sopenharmony_ci regs + S3C64XX_SPI_CLK_CFG); 10028c2ecf20Sopenharmony_ci writel(0, regs + S3C64XX_SPI_MODE_CFG); 10038c2ecf20Sopenharmony_ci writel(0, regs + S3C64XX_SPI_PACKET_CNT); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci /* Clear any irq pending bits, should set and clear the bits */ 10068c2ecf20Sopenharmony_ci val = S3C64XX_SPI_PND_RX_OVERRUN_CLR | 10078c2ecf20Sopenharmony_ci S3C64XX_SPI_PND_RX_UNDERRUN_CLR | 10088c2ecf20Sopenharmony_ci S3C64XX_SPI_PND_TX_OVERRUN_CLR | 10098c2ecf20Sopenharmony_ci S3C64XX_SPI_PND_TX_UNDERRUN_CLR; 10108c2ecf20Sopenharmony_ci writel(val, regs + S3C64XX_SPI_PENDING_CLR); 10118c2ecf20Sopenharmony_ci writel(0, regs + S3C64XX_SPI_PENDING_CLR); 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci writel(0, regs + S3C64XX_SPI_SWAP_CFG); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci val = readl(regs + S3C64XX_SPI_MODE_CFG); 10168c2ecf20Sopenharmony_ci val &= ~S3C64XX_SPI_MODE_4BURST; 10178c2ecf20Sopenharmony_ci val &= ~(S3C64XX_SPI_MAX_TRAILCNT << S3C64XX_SPI_TRAILCNT_OFF); 10188c2ecf20Sopenharmony_ci val |= (S3C64XX_SPI_TRAILCNT << S3C64XX_SPI_TRAILCNT_OFF); 10198c2ecf20Sopenharmony_ci writel(val, regs + S3C64XX_SPI_MODE_CFG); 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci s3c64xx_flush_fifo(sdd); 10228c2ecf20Sopenharmony_ci} 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 10258c2ecf20Sopenharmony_cistatic struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev) 10268c2ecf20Sopenharmony_ci{ 10278c2ecf20Sopenharmony_ci struct s3c64xx_spi_info *sci; 10288c2ecf20Sopenharmony_ci u32 temp; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci sci = devm_kzalloc(dev, sizeof(*sci), GFP_KERNEL); 10318c2ecf20Sopenharmony_ci if (!sci) 10328c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci if (of_property_read_u32(dev->of_node, "samsung,spi-src-clk", &temp)) { 10358c2ecf20Sopenharmony_ci dev_warn(dev, "spi bus clock parent not specified, using clock at index 0 as parent\n"); 10368c2ecf20Sopenharmony_ci sci->src_clk_nr = 0; 10378c2ecf20Sopenharmony_ci } else { 10388c2ecf20Sopenharmony_ci sci->src_clk_nr = temp; 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci if (of_property_read_u32(dev->of_node, "num-cs", &temp)) { 10428c2ecf20Sopenharmony_ci dev_warn(dev, "number of chip select lines not specified, assuming 1 chip select line\n"); 10438c2ecf20Sopenharmony_ci sci->num_cs = 1; 10448c2ecf20Sopenharmony_ci } else { 10458c2ecf20Sopenharmony_ci sci->num_cs = temp; 10468c2ecf20Sopenharmony_ci } 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci sci->no_cs = of_property_read_bool(dev->of_node, "no-cs-readback"); 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci return sci; 10518c2ecf20Sopenharmony_ci} 10528c2ecf20Sopenharmony_ci#else 10538c2ecf20Sopenharmony_cistatic struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev) 10548c2ecf20Sopenharmony_ci{ 10558c2ecf20Sopenharmony_ci return dev_get_platdata(dev); 10568c2ecf20Sopenharmony_ci} 10578c2ecf20Sopenharmony_ci#endif 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_cistatic const struct of_device_id s3c64xx_spi_dt_match[]; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_cistatic inline struct s3c64xx_spi_port_config *s3c64xx_spi_get_port_config( 10628c2ecf20Sopenharmony_ci struct platform_device *pdev) 10638c2ecf20Sopenharmony_ci{ 10648c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 10658c2ecf20Sopenharmony_ci if (pdev->dev.of_node) { 10668c2ecf20Sopenharmony_ci const struct of_device_id *match; 10678c2ecf20Sopenharmony_ci match = of_match_node(s3c64xx_spi_dt_match, pdev->dev.of_node); 10688c2ecf20Sopenharmony_ci return (struct s3c64xx_spi_port_config *)match->data; 10698c2ecf20Sopenharmony_ci } 10708c2ecf20Sopenharmony_ci#endif 10718c2ecf20Sopenharmony_ci return (struct s3c64xx_spi_port_config *) 10728c2ecf20Sopenharmony_ci platform_get_device_id(pdev)->driver_data; 10738c2ecf20Sopenharmony_ci} 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_cistatic int s3c64xx_spi_probe(struct platform_device *pdev) 10768c2ecf20Sopenharmony_ci{ 10778c2ecf20Sopenharmony_ci struct resource *mem_res; 10788c2ecf20Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd; 10798c2ecf20Sopenharmony_ci struct s3c64xx_spi_info *sci = dev_get_platdata(&pdev->dev); 10808c2ecf20Sopenharmony_ci struct spi_master *master; 10818c2ecf20Sopenharmony_ci int ret, irq; 10828c2ecf20Sopenharmony_ci char clk_name[16]; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci if (!sci && pdev->dev.of_node) { 10858c2ecf20Sopenharmony_ci sci = s3c64xx_spi_parse_dt(&pdev->dev); 10868c2ecf20Sopenharmony_ci if (IS_ERR(sci)) 10878c2ecf20Sopenharmony_ci return PTR_ERR(sci); 10888c2ecf20Sopenharmony_ci } 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci if (!sci) { 10918c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "platform_data missing!\n"); 10928c2ecf20Sopenharmony_ci return -ENODEV; 10938c2ecf20Sopenharmony_ci } 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 10968c2ecf20Sopenharmony_ci if (mem_res == NULL) { 10978c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to get SPI MEM resource\n"); 10988c2ecf20Sopenharmony_ci return -ENXIO; 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 11028c2ecf20Sopenharmony_ci if (irq < 0) { 11038c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "Failed to get IRQ: %d\n", irq); 11048c2ecf20Sopenharmony_ci return irq; 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci master = spi_alloc_master(&pdev->dev, 11088c2ecf20Sopenharmony_ci sizeof(struct s3c64xx_spi_driver_data)); 11098c2ecf20Sopenharmony_ci if (master == NULL) { 11108c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to allocate SPI Master\n"); 11118c2ecf20Sopenharmony_ci return -ENOMEM; 11128c2ecf20Sopenharmony_ci } 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, master); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci sdd = spi_master_get_devdata(master); 11178c2ecf20Sopenharmony_ci sdd->port_conf = s3c64xx_spi_get_port_config(pdev); 11188c2ecf20Sopenharmony_ci sdd->master = master; 11198c2ecf20Sopenharmony_ci sdd->cntrlr_info = sci; 11208c2ecf20Sopenharmony_ci sdd->pdev = pdev; 11218c2ecf20Sopenharmony_ci sdd->sfr_start = mem_res->start; 11228c2ecf20Sopenharmony_ci if (pdev->dev.of_node) { 11238c2ecf20Sopenharmony_ci ret = of_alias_get_id(pdev->dev.of_node, "spi"); 11248c2ecf20Sopenharmony_ci if (ret < 0) { 11258c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get alias id, errno %d\n", 11268c2ecf20Sopenharmony_ci ret); 11278c2ecf20Sopenharmony_ci goto err_deref_master; 11288c2ecf20Sopenharmony_ci } 11298c2ecf20Sopenharmony_ci sdd->port_id = ret; 11308c2ecf20Sopenharmony_ci } else { 11318c2ecf20Sopenharmony_ci sdd->port_id = pdev->id; 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci sdd->cur_bpw = 8; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci sdd->tx_dma.direction = DMA_MEM_TO_DEV; 11378c2ecf20Sopenharmony_ci sdd->rx_dma.direction = DMA_DEV_TO_MEM; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci master->dev.of_node = pdev->dev.of_node; 11408c2ecf20Sopenharmony_ci master->bus_num = sdd->port_id; 11418c2ecf20Sopenharmony_ci master->setup = s3c64xx_spi_setup; 11428c2ecf20Sopenharmony_ci master->cleanup = s3c64xx_spi_cleanup; 11438c2ecf20Sopenharmony_ci master->prepare_transfer_hardware = s3c64xx_spi_prepare_transfer; 11448c2ecf20Sopenharmony_ci master->prepare_message = s3c64xx_spi_prepare_message; 11458c2ecf20Sopenharmony_ci master->transfer_one = s3c64xx_spi_transfer_one; 11468c2ecf20Sopenharmony_ci master->max_transfer_size = s3c64xx_spi_max_transfer_size; 11478c2ecf20Sopenharmony_ci master->num_chipselect = sci->num_cs; 11488c2ecf20Sopenharmony_ci master->dma_alignment = 8; 11498c2ecf20Sopenharmony_ci master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) | 11508c2ecf20Sopenharmony_ci SPI_BPW_MASK(8); 11518c2ecf20Sopenharmony_ci /* the spi->mode bits understood by this driver: */ 11528c2ecf20Sopenharmony_ci master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; 11538c2ecf20Sopenharmony_ci master->auto_runtime_pm = true; 11548c2ecf20Sopenharmony_ci if (!is_polling(sdd)) 11558c2ecf20Sopenharmony_ci master->can_dma = s3c64xx_spi_can_dma; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci sdd->regs = devm_ioremap_resource(&pdev->dev, mem_res); 11588c2ecf20Sopenharmony_ci if (IS_ERR(sdd->regs)) { 11598c2ecf20Sopenharmony_ci ret = PTR_ERR(sdd->regs); 11608c2ecf20Sopenharmony_ci goto err_deref_master; 11618c2ecf20Sopenharmony_ci } 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci if (sci->cfg_gpio && sci->cfg_gpio()) { 11648c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to config gpio\n"); 11658c2ecf20Sopenharmony_ci ret = -EBUSY; 11668c2ecf20Sopenharmony_ci goto err_deref_master; 11678c2ecf20Sopenharmony_ci } 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci /* Setup clocks */ 11708c2ecf20Sopenharmony_ci sdd->clk = devm_clk_get(&pdev->dev, "spi"); 11718c2ecf20Sopenharmony_ci if (IS_ERR(sdd->clk)) { 11728c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n"); 11738c2ecf20Sopenharmony_ci ret = PTR_ERR(sdd->clk); 11748c2ecf20Sopenharmony_ci goto err_deref_master; 11758c2ecf20Sopenharmony_ci } 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci ret = clk_prepare_enable(sdd->clk); 11788c2ecf20Sopenharmony_ci if (ret) { 11798c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n"); 11808c2ecf20Sopenharmony_ci goto err_deref_master; 11818c2ecf20Sopenharmony_ci } 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci sprintf(clk_name, "spi_busclk%d", sci->src_clk_nr); 11848c2ecf20Sopenharmony_ci sdd->src_clk = devm_clk_get(&pdev->dev, clk_name); 11858c2ecf20Sopenharmony_ci if (IS_ERR(sdd->src_clk)) { 11868c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 11878c2ecf20Sopenharmony_ci "Unable to acquire clock '%s'\n", clk_name); 11888c2ecf20Sopenharmony_ci ret = PTR_ERR(sdd->src_clk); 11898c2ecf20Sopenharmony_ci goto err_disable_clk; 11908c2ecf20Sopenharmony_ci } 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci ret = clk_prepare_enable(sdd->src_clk); 11938c2ecf20Sopenharmony_ci if (ret) { 11948c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Couldn't enable clock '%s'\n", clk_name); 11958c2ecf20Sopenharmony_ci goto err_disable_clk; 11968c2ecf20Sopenharmony_ci } 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci if (sdd->port_conf->clk_ioclk) { 11998c2ecf20Sopenharmony_ci sdd->ioclk = devm_clk_get(&pdev->dev, "spi_ioclk"); 12008c2ecf20Sopenharmony_ci if (IS_ERR(sdd->ioclk)) { 12018c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to acquire 'ioclk'\n"); 12028c2ecf20Sopenharmony_ci ret = PTR_ERR(sdd->ioclk); 12038c2ecf20Sopenharmony_ci goto err_disable_src_clk; 12048c2ecf20Sopenharmony_ci } 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci ret = clk_prepare_enable(sdd->ioclk); 12078c2ecf20Sopenharmony_ci if (ret) { 12088c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Couldn't enable clock 'ioclk'\n"); 12098c2ecf20Sopenharmony_ci goto err_disable_src_clk; 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci } 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci if (!is_polling(sdd)) { 12148c2ecf20Sopenharmony_ci /* Acquire DMA channels */ 12158c2ecf20Sopenharmony_ci sdd->rx_dma.ch = dma_request_chan(&pdev->dev, "rx"); 12168c2ecf20Sopenharmony_ci if (IS_ERR(sdd->rx_dma.ch)) { 12178c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to get RX DMA channel\n"); 12188c2ecf20Sopenharmony_ci ret = PTR_ERR(sdd->rx_dma.ch); 12198c2ecf20Sopenharmony_ci goto err_disable_io_clk; 12208c2ecf20Sopenharmony_ci } 12218c2ecf20Sopenharmony_ci sdd->tx_dma.ch = dma_request_chan(&pdev->dev, "tx"); 12228c2ecf20Sopenharmony_ci if (IS_ERR(sdd->tx_dma.ch)) { 12238c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to get TX DMA channel\n"); 12248c2ecf20Sopenharmony_ci ret = PTR_ERR(sdd->tx_dma.ch); 12258c2ecf20Sopenharmony_ci goto err_release_rx_dma; 12268c2ecf20Sopenharmony_ci } 12278c2ecf20Sopenharmony_ci } 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_TIMEOUT); 12308c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(&pdev->dev); 12318c2ecf20Sopenharmony_ci pm_runtime_set_active(&pdev->dev); 12328c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 12338c2ecf20Sopenharmony_ci pm_runtime_get_sync(&pdev->dev); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci /* Setup Deufult Mode */ 12368c2ecf20Sopenharmony_ci s3c64xx_spi_hwinit(sdd); 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci spin_lock_init(&sdd->lock); 12398c2ecf20Sopenharmony_ci init_completion(&sdd->xfer_completion); 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, s3c64xx_spi_irq, 0, 12428c2ecf20Sopenharmony_ci "spi-s3c64xx", sdd); 12438c2ecf20Sopenharmony_ci if (ret != 0) { 12448c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to request IRQ %d: %d\n", 12458c2ecf20Sopenharmony_ci irq, ret); 12468c2ecf20Sopenharmony_ci goto err_pm_put; 12478c2ecf20Sopenharmony_ci } 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci writel(S3C64XX_SPI_INT_RX_OVERRUN_EN | S3C64XX_SPI_INT_RX_UNDERRUN_EN | 12508c2ecf20Sopenharmony_ci S3C64XX_SPI_INT_TX_OVERRUN_EN | S3C64XX_SPI_INT_TX_UNDERRUN_EN, 12518c2ecf20Sopenharmony_ci sdd->regs + S3C64XX_SPI_INT_EN); 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci ret = devm_spi_register_master(&pdev->dev, master); 12548c2ecf20Sopenharmony_ci if (ret != 0) { 12558c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot register SPI master: %d\n", ret); 12568c2ecf20Sopenharmony_ci goto err_pm_put; 12578c2ecf20Sopenharmony_ci } 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Slaves attached\n", 12608c2ecf20Sopenharmony_ci sdd->port_id, master->num_chipselect); 12618c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tFIFO %dbytes\n", 12628c2ecf20Sopenharmony_ci mem_res, (FIFO_LVL_MASK(sdd) >> 1) + 1); 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(&pdev->dev); 12658c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(&pdev->dev); 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci return 0; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_cierr_pm_put: 12708c2ecf20Sopenharmony_ci pm_runtime_put_noidle(&pdev->dev); 12718c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 12728c2ecf20Sopenharmony_ci pm_runtime_set_suspended(&pdev->dev); 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci if (!is_polling(sdd)) 12758c2ecf20Sopenharmony_ci dma_release_channel(sdd->tx_dma.ch); 12768c2ecf20Sopenharmony_cierr_release_rx_dma: 12778c2ecf20Sopenharmony_ci if (!is_polling(sdd)) 12788c2ecf20Sopenharmony_ci dma_release_channel(sdd->rx_dma.ch); 12798c2ecf20Sopenharmony_cierr_disable_io_clk: 12808c2ecf20Sopenharmony_ci clk_disable_unprepare(sdd->ioclk); 12818c2ecf20Sopenharmony_cierr_disable_src_clk: 12828c2ecf20Sopenharmony_ci clk_disable_unprepare(sdd->src_clk); 12838c2ecf20Sopenharmony_cierr_disable_clk: 12848c2ecf20Sopenharmony_ci clk_disable_unprepare(sdd->clk); 12858c2ecf20Sopenharmony_cierr_deref_master: 12868c2ecf20Sopenharmony_ci spi_master_put(master); 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci return ret; 12898c2ecf20Sopenharmony_ci} 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_cistatic int s3c64xx_spi_remove(struct platform_device *pdev) 12928c2ecf20Sopenharmony_ci{ 12938c2ecf20Sopenharmony_ci struct spi_master *master = platform_get_drvdata(pdev); 12948c2ecf20Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci pm_runtime_get_sync(&pdev->dev); 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci writel(0, sdd->regs + S3C64XX_SPI_INT_EN); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci if (!is_polling(sdd)) { 13018c2ecf20Sopenharmony_ci dma_release_channel(sdd->rx_dma.ch); 13028c2ecf20Sopenharmony_ci dma_release_channel(sdd->tx_dma.ch); 13038c2ecf20Sopenharmony_ci } 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci clk_disable_unprepare(sdd->ioclk); 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci clk_disable_unprepare(sdd->src_clk); 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci clk_disable_unprepare(sdd->clk); 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci pm_runtime_put_noidle(&pdev->dev); 13128c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 13138c2ecf20Sopenharmony_ci pm_runtime_set_suspended(&pdev->dev); 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci return 0; 13168c2ecf20Sopenharmony_ci} 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 13198c2ecf20Sopenharmony_cistatic int s3c64xx_spi_suspend(struct device *dev) 13208c2ecf20Sopenharmony_ci{ 13218c2ecf20Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 13228c2ecf20Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci int ret = spi_master_suspend(master); 13258c2ecf20Sopenharmony_ci if (ret) 13268c2ecf20Sopenharmony_ci return ret; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci ret = pm_runtime_force_suspend(dev); 13298c2ecf20Sopenharmony_ci if (ret < 0) 13308c2ecf20Sopenharmony_ci return ret; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci sdd->cur_speed = 0; /* Output Clock is stopped */ 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci return 0; 13358c2ecf20Sopenharmony_ci} 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_cistatic int s3c64xx_spi_resume(struct device *dev) 13388c2ecf20Sopenharmony_ci{ 13398c2ecf20Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 13408c2ecf20Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); 13418c2ecf20Sopenharmony_ci struct s3c64xx_spi_info *sci = sdd->cntrlr_info; 13428c2ecf20Sopenharmony_ci int ret; 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci if (sci->cfg_gpio) 13458c2ecf20Sopenharmony_ci sci->cfg_gpio(); 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci ret = pm_runtime_force_resume(dev); 13488c2ecf20Sopenharmony_ci if (ret < 0) 13498c2ecf20Sopenharmony_ci return ret; 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci return spi_master_resume(master); 13528c2ecf20Sopenharmony_ci} 13538c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 13568c2ecf20Sopenharmony_cistatic int s3c64xx_spi_runtime_suspend(struct device *dev) 13578c2ecf20Sopenharmony_ci{ 13588c2ecf20Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 13598c2ecf20Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci clk_disable_unprepare(sdd->clk); 13628c2ecf20Sopenharmony_ci clk_disable_unprepare(sdd->src_clk); 13638c2ecf20Sopenharmony_ci clk_disable_unprepare(sdd->ioclk); 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci return 0; 13668c2ecf20Sopenharmony_ci} 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_cistatic int s3c64xx_spi_runtime_resume(struct device *dev) 13698c2ecf20Sopenharmony_ci{ 13708c2ecf20Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 13718c2ecf20Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); 13728c2ecf20Sopenharmony_ci int ret; 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci if (sdd->port_conf->clk_ioclk) { 13758c2ecf20Sopenharmony_ci ret = clk_prepare_enable(sdd->ioclk); 13768c2ecf20Sopenharmony_ci if (ret != 0) 13778c2ecf20Sopenharmony_ci return ret; 13788c2ecf20Sopenharmony_ci } 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci ret = clk_prepare_enable(sdd->src_clk); 13818c2ecf20Sopenharmony_ci if (ret != 0) 13828c2ecf20Sopenharmony_ci goto err_disable_ioclk; 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci ret = clk_prepare_enable(sdd->clk); 13858c2ecf20Sopenharmony_ci if (ret != 0) 13868c2ecf20Sopenharmony_ci goto err_disable_src_clk; 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci s3c64xx_spi_hwinit(sdd); 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci writel(S3C64XX_SPI_INT_RX_OVERRUN_EN | S3C64XX_SPI_INT_RX_UNDERRUN_EN | 13918c2ecf20Sopenharmony_ci S3C64XX_SPI_INT_TX_OVERRUN_EN | S3C64XX_SPI_INT_TX_UNDERRUN_EN, 13928c2ecf20Sopenharmony_ci sdd->regs + S3C64XX_SPI_INT_EN); 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci return 0; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_cierr_disable_src_clk: 13978c2ecf20Sopenharmony_ci clk_disable_unprepare(sdd->src_clk); 13988c2ecf20Sopenharmony_cierr_disable_ioclk: 13998c2ecf20Sopenharmony_ci clk_disable_unprepare(sdd->ioclk); 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci return ret; 14028c2ecf20Sopenharmony_ci} 14038c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_cistatic const struct dev_pm_ops s3c64xx_spi_pm = { 14068c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(s3c64xx_spi_suspend, s3c64xx_spi_resume) 14078c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(s3c64xx_spi_runtime_suspend, 14088c2ecf20Sopenharmony_ci s3c64xx_spi_runtime_resume, NULL) 14098c2ecf20Sopenharmony_ci}; 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_cistatic struct s3c64xx_spi_port_config s3c2443_spi_port_config = { 14128c2ecf20Sopenharmony_ci .fifo_lvl_mask = { 0x7f }, 14138c2ecf20Sopenharmony_ci .rx_lvl_offset = 13, 14148c2ecf20Sopenharmony_ci .tx_st_done = 21, 14158c2ecf20Sopenharmony_ci .high_speed = true, 14168c2ecf20Sopenharmony_ci}; 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_cistatic struct s3c64xx_spi_port_config s3c6410_spi_port_config = { 14198c2ecf20Sopenharmony_ci .fifo_lvl_mask = { 0x7f, 0x7F }, 14208c2ecf20Sopenharmony_ci .rx_lvl_offset = 13, 14218c2ecf20Sopenharmony_ci .tx_st_done = 21, 14228c2ecf20Sopenharmony_ci}; 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_cistatic struct s3c64xx_spi_port_config s5pv210_spi_port_config = { 14258c2ecf20Sopenharmony_ci .fifo_lvl_mask = { 0x1ff, 0x7F }, 14268c2ecf20Sopenharmony_ci .rx_lvl_offset = 15, 14278c2ecf20Sopenharmony_ci .tx_st_done = 25, 14288c2ecf20Sopenharmony_ci .high_speed = true, 14298c2ecf20Sopenharmony_ci}; 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_cistatic struct s3c64xx_spi_port_config exynos4_spi_port_config = { 14328c2ecf20Sopenharmony_ci .fifo_lvl_mask = { 0x1ff, 0x7F, 0x7F }, 14338c2ecf20Sopenharmony_ci .rx_lvl_offset = 15, 14348c2ecf20Sopenharmony_ci .tx_st_done = 25, 14358c2ecf20Sopenharmony_ci .high_speed = true, 14368c2ecf20Sopenharmony_ci .clk_from_cmu = true, 14378c2ecf20Sopenharmony_ci .quirks = S3C64XX_SPI_QUIRK_CS_AUTO, 14388c2ecf20Sopenharmony_ci}; 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_cistatic struct s3c64xx_spi_port_config exynos7_spi_port_config = { 14418c2ecf20Sopenharmony_ci .fifo_lvl_mask = { 0x1ff, 0x7F, 0x7F, 0x7F, 0x7F, 0x1ff}, 14428c2ecf20Sopenharmony_ci .rx_lvl_offset = 15, 14438c2ecf20Sopenharmony_ci .tx_st_done = 25, 14448c2ecf20Sopenharmony_ci .high_speed = true, 14458c2ecf20Sopenharmony_ci .clk_from_cmu = true, 14468c2ecf20Sopenharmony_ci .quirks = S3C64XX_SPI_QUIRK_CS_AUTO, 14478c2ecf20Sopenharmony_ci}; 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_cistatic struct s3c64xx_spi_port_config exynos5433_spi_port_config = { 14508c2ecf20Sopenharmony_ci .fifo_lvl_mask = { 0x1ff, 0x7f, 0x7f, 0x7f, 0x7f, 0x1ff}, 14518c2ecf20Sopenharmony_ci .rx_lvl_offset = 15, 14528c2ecf20Sopenharmony_ci .tx_st_done = 25, 14538c2ecf20Sopenharmony_ci .high_speed = true, 14548c2ecf20Sopenharmony_ci .clk_from_cmu = true, 14558c2ecf20Sopenharmony_ci .clk_ioclk = true, 14568c2ecf20Sopenharmony_ci .quirks = S3C64XX_SPI_QUIRK_CS_AUTO, 14578c2ecf20Sopenharmony_ci}; 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_cistatic const struct platform_device_id s3c64xx_spi_driver_ids[] = { 14608c2ecf20Sopenharmony_ci { 14618c2ecf20Sopenharmony_ci .name = "s3c2443-spi", 14628c2ecf20Sopenharmony_ci .driver_data = (kernel_ulong_t)&s3c2443_spi_port_config, 14638c2ecf20Sopenharmony_ci }, { 14648c2ecf20Sopenharmony_ci .name = "s3c6410-spi", 14658c2ecf20Sopenharmony_ci .driver_data = (kernel_ulong_t)&s3c6410_spi_port_config, 14668c2ecf20Sopenharmony_ci }, 14678c2ecf20Sopenharmony_ci { }, 14688c2ecf20Sopenharmony_ci}; 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_cistatic const struct of_device_id s3c64xx_spi_dt_match[] = { 14718c2ecf20Sopenharmony_ci { .compatible = "samsung,s3c2443-spi", 14728c2ecf20Sopenharmony_ci .data = (void *)&s3c2443_spi_port_config, 14738c2ecf20Sopenharmony_ci }, 14748c2ecf20Sopenharmony_ci { .compatible = "samsung,s3c6410-spi", 14758c2ecf20Sopenharmony_ci .data = (void *)&s3c6410_spi_port_config, 14768c2ecf20Sopenharmony_ci }, 14778c2ecf20Sopenharmony_ci { .compatible = "samsung,s5pv210-spi", 14788c2ecf20Sopenharmony_ci .data = (void *)&s5pv210_spi_port_config, 14798c2ecf20Sopenharmony_ci }, 14808c2ecf20Sopenharmony_ci { .compatible = "samsung,exynos4210-spi", 14818c2ecf20Sopenharmony_ci .data = (void *)&exynos4_spi_port_config, 14828c2ecf20Sopenharmony_ci }, 14838c2ecf20Sopenharmony_ci { .compatible = "samsung,exynos7-spi", 14848c2ecf20Sopenharmony_ci .data = (void *)&exynos7_spi_port_config, 14858c2ecf20Sopenharmony_ci }, 14868c2ecf20Sopenharmony_ci { .compatible = "samsung,exynos5433-spi", 14878c2ecf20Sopenharmony_ci .data = (void *)&exynos5433_spi_port_config, 14888c2ecf20Sopenharmony_ci }, 14898c2ecf20Sopenharmony_ci { }, 14908c2ecf20Sopenharmony_ci}; 14918c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, s3c64xx_spi_dt_match); 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_cistatic struct platform_driver s3c64xx_spi_driver = { 14948c2ecf20Sopenharmony_ci .driver = { 14958c2ecf20Sopenharmony_ci .name = "s3c64xx-spi", 14968c2ecf20Sopenharmony_ci .pm = &s3c64xx_spi_pm, 14978c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(s3c64xx_spi_dt_match), 14988c2ecf20Sopenharmony_ci }, 14998c2ecf20Sopenharmony_ci .probe = s3c64xx_spi_probe, 15008c2ecf20Sopenharmony_ci .remove = s3c64xx_spi_remove, 15018c2ecf20Sopenharmony_ci .id_table = s3c64xx_spi_driver_ids, 15028c2ecf20Sopenharmony_ci}; 15038c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:s3c64xx-spi"); 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_cimodule_platform_driver(s3c64xx_spi_driver); 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaswinder Singh <jassi.brar@samsung.com>"); 15088c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("S3C64XX SPI Controller Driver"); 15098c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1510