162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright (c) 2009 Samsung Electronics Co., Ltd. 462306a36Sopenharmony_ci// Jaswinder Singh <jassi.brar@samsung.com> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/init.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/interrupt.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1262306a36Sopenharmony_ci#include <linux/dmaengine.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1562306a36Sopenharmony_ci#include <linux/spi/spi.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/platform_data/spi-s3c64xx.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define MAX_SPI_PORTS 12 2162306a36Sopenharmony_ci#define S3C64XX_SPI_QUIRK_CS_AUTO (1 << 1) 2262306a36Sopenharmony_ci#define AUTOSUSPEND_TIMEOUT 2000 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* Registers and bit-fields */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define S3C64XX_SPI_CH_CFG 0x00 2762306a36Sopenharmony_ci#define S3C64XX_SPI_CLK_CFG 0x04 2862306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_CFG 0x08 2962306a36Sopenharmony_ci#define S3C64XX_SPI_CS_REG 0x0C 3062306a36Sopenharmony_ci#define S3C64XX_SPI_INT_EN 0x10 3162306a36Sopenharmony_ci#define S3C64XX_SPI_STATUS 0x14 3262306a36Sopenharmony_ci#define S3C64XX_SPI_TX_DATA 0x18 3362306a36Sopenharmony_ci#define S3C64XX_SPI_RX_DATA 0x1C 3462306a36Sopenharmony_ci#define S3C64XX_SPI_PACKET_CNT 0x20 3562306a36Sopenharmony_ci#define S3C64XX_SPI_PENDING_CLR 0x24 3662306a36Sopenharmony_ci#define S3C64XX_SPI_SWAP_CFG 0x28 3762306a36Sopenharmony_ci#define S3C64XX_SPI_FB_CLK 0x2C 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define S3C64XX_SPI_CH_HS_EN (1<<6) /* High Speed Enable */ 4062306a36Sopenharmony_ci#define S3C64XX_SPI_CH_SW_RST (1<<5) 4162306a36Sopenharmony_ci#define S3C64XX_SPI_CH_SLAVE (1<<4) 4262306a36Sopenharmony_ci#define S3C64XX_SPI_CPOL_L (1<<3) 4362306a36Sopenharmony_ci#define S3C64XX_SPI_CPHA_B (1<<2) 4462306a36Sopenharmony_ci#define S3C64XX_SPI_CH_RXCH_ON (1<<1) 4562306a36Sopenharmony_ci#define S3C64XX_SPI_CH_TXCH_ON (1<<0) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define S3C64XX_SPI_CLKSEL_SRCMSK (3<<9) 4862306a36Sopenharmony_ci#define S3C64XX_SPI_CLKSEL_SRCSHFT 9 4962306a36Sopenharmony_ci#define S3C64XX_SPI_ENCLK_ENABLE (1<<8) 5062306a36Sopenharmony_ci#define S3C64XX_SPI_PSR_MASK 0xff 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_CH_TSZ_BYTE (0<<29) 5362306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_CH_TSZ_HALFWORD (1<<29) 5462306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_CH_TSZ_WORD (2<<29) 5562306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_CH_TSZ_MASK (3<<29) 5662306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_BUS_TSZ_BYTE (0<<17) 5762306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD (1<<17) 5862306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_BUS_TSZ_WORD (2<<17) 5962306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_BUS_TSZ_MASK (3<<17) 6062306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_RX_RDY_LVL GENMASK(16, 11) 6162306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_RX_RDY_LVL_SHIFT 11 6262306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_SELF_LOOPBACK (1<<3) 6362306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_RXDMA_ON (1<<2) 6462306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_TXDMA_ON (1<<1) 6562306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_4BURST (1<<0) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define S3C64XX_SPI_CS_NSC_CNT_2 (2<<4) 6862306a36Sopenharmony_ci#define S3C64XX_SPI_CS_AUTO (1<<1) 6962306a36Sopenharmony_ci#define S3C64XX_SPI_CS_SIG_INACT (1<<0) 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#define S3C64XX_SPI_INT_TRAILING_EN (1<<6) 7262306a36Sopenharmony_ci#define S3C64XX_SPI_INT_RX_OVERRUN_EN (1<<5) 7362306a36Sopenharmony_ci#define S3C64XX_SPI_INT_RX_UNDERRUN_EN (1<<4) 7462306a36Sopenharmony_ci#define S3C64XX_SPI_INT_TX_OVERRUN_EN (1<<3) 7562306a36Sopenharmony_ci#define S3C64XX_SPI_INT_TX_UNDERRUN_EN (1<<2) 7662306a36Sopenharmony_ci#define S3C64XX_SPI_INT_RX_FIFORDY_EN (1<<1) 7762306a36Sopenharmony_ci#define S3C64XX_SPI_INT_TX_FIFORDY_EN (1<<0) 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define S3C64XX_SPI_ST_RX_OVERRUN_ERR (1<<5) 8062306a36Sopenharmony_ci#define S3C64XX_SPI_ST_RX_UNDERRUN_ERR (1<<4) 8162306a36Sopenharmony_ci#define S3C64XX_SPI_ST_TX_OVERRUN_ERR (1<<3) 8262306a36Sopenharmony_ci#define S3C64XX_SPI_ST_TX_UNDERRUN_ERR (1<<2) 8362306a36Sopenharmony_ci#define S3C64XX_SPI_ST_RX_FIFORDY (1<<1) 8462306a36Sopenharmony_ci#define S3C64XX_SPI_ST_TX_FIFORDY (1<<0) 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define S3C64XX_SPI_PACKET_CNT_EN (1<<16) 8762306a36Sopenharmony_ci#define S3C64XX_SPI_PACKET_CNT_MASK GENMASK(15, 0) 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define S3C64XX_SPI_PND_TX_UNDERRUN_CLR (1<<4) 9062306a36Sopenharmony_ci#define S3C64XX_SPI_PND_TX_OVERRUN_CLR (1<<3) 9162306a36Sopenharmony_ci#define S3C64XX_SPI_PND_RX_UNDERRUN_CLR (1<<2) 9262306a36Sopenharmony_ci#define S3C64XX_SPI_PND_RX_OVERRUN_CLR (1<<1) 9362306a36Sopenharmony_ci#define S3C64XX_SPI_PND_TRAILING_CLR (1<<0) 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define S3C64XX_SPI_SWAP_RX_HALF_WORD (1<<7) 9662306a36Sopenharmony_ci#define S3C64XX_SPI_SWAP_RX_BYTE (1<<6) 9762306a36Sopenharmony_ci#define S3C64XX_SPI_SWAP_RX_BIT (1<<5) 9862306a36Sopenharmony_ci#define S3C64XX_SPI_SWAP_RX_EN (1<<4) 9962306a36Sopenharmony_ci#define S3C64XX_SPI_SWAP_TX_HALF_WORD (1<<3) 10062306a36Sopenharmony_ci#define S3C64XX_SPI_SWAP_TX_BYTE (1<<2) 10162306a36Sopenharmony_ci#define S3C64XX_SPI_SWAP_TX_BIT (1<<1) 10262306a36Sopenharmony_ci#define S3C64XX_SPI_SWAP_TX_EN (1<<0) 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci#define S3C64XX_SPI_FBCLK_MSK (3<<0) 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci#define FIFO_LVL_MASK(i) ((i)->port_conf->fifo_lvl_mask[i->port_id]) 10762306a36Sopenharmony_ci#define S3C64XX_SPI_ST_TX_DONE(v, i) (((v) & \ 10862306a36Sopenharmony_ci (1 << (i)->port_conf->tx_st_done)) ? 1 : 0) 10962306a36Sopenharmony_ci#define TX_FIFO_LVL(v, i) (((v) >> 6) & FIFO_LVL_MASK(i)) 11062306a36Sopenharmony_ci#define RX_FIFO_LVL(v, i) (((v) >> (i)->port_conf->rx_lvl_offset) & \ 11162306a36Sopenharmony_ci FIFO_LVL_MASK(i)) 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#define S3C64XX_SPI_MAX_TRAILCNT 0x3ff 11462306a36Sopenharmony_ci#define S3C64XX_SPI_TRAILCNT_OFF 19 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci#define S3C64XX_SPI_TRAILCNT S3C64XX_SPI_MAX_TRAILCNT 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci#define S3C64XX_SPI_POLLING_SIZE 32 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) 12162306a36Sopenharmony_ci#define is_polling(x) (x->cntrlr_info->polling) 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci#define RXBUSY (1<<2) 12462306a36Sopenharmony_ci#define TXBUSY (1<<3) 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistruct s3c64xx_spi_dma_data { 12762306a36Sopenharmony_ci struct dma_chan *ch; 12862306a36Sopenharmony_ci dma_cookie_t cookie; 12962306a36Sopenharmony_ci enum dma_transfer_direction direction; 13062306a36Sopenharmony_ci}; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/** 13362306a36Sopenharmony_ci * struct s3c64xx_spi_port_config - SPI Controller hardware info 13462306a36Sopenharmony_ci * @fifo_lvl_mask: Bit-mask for {TX|RX}_FIFO_LVL bits in SPI_STATUS register. 13562306a36Sopenharmony_ci * @rx_lvl_offset: Bit offset of RX_FIFO_LVL bits in SPI_STATUS regiter. 13662306a36Sopenharmony_ci * @tx_st_done: Bit offset of TX_DONE bit in SPI_STATUS regiter. 13762306a36Sopenharmony_ci * @clk_div: Internal clock divider 13862306a36Sopenharmony_ci * @quirks: Bitmask of known quirks 13962306a36Sopenharmony_ci * @high_speed: True, if the controller supports HIGH_SPEED_EN bit. 14062306a36Sopenharmony_ci * @clk_from_cmu: True, if the controller does not include a clock mux and 14162306a36Sopenharmony_ci * prescaler unit. 14262306a36Sopenharmony_ci * @clk_ioclk: True if clock is present on this device 14362306a36Sopenharmony_ci * @has_loopback: True if loopback mode can be supported 14462306a36Sopenharmony_ci * 14562306a36Sopenharmony_ci * The Samsung s3c64xx SPI controller are used on various Samsung SoC's but 14662306a36Sopenharmony_ci * differ in some aspects such as the size of the fifo and spi bus clock 14762306a36Sopenharmony_ci * setup. Such differences are specified to the driver using this structure 14862306a36Sopenharmony_ci * which is provided as driver data to the driver. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_cistruct s3c64xx_spi_port_config { 15162306a36Sopenharmony_ci int fifo_lvl_mask[MAX_SPI_PORTS]; 15262306a36Sopenharmony_ci int rx_lvl_offset; 15362306a36Sopenharmony_ci int tx_st_done; 15462306a36Sopenharmony_ci int quirks; 15562306a36Sopenharmony_ci int clk_div; 15662306a36Sopenharmony_ci bool high_speed; 15762306a36Sopenharmony_ci bool clk_from_cmu; 15862306a36Sopenharmony_ci bool clk_ioclk; 15962306a36Sopenharmony_ci bool has_loopback; 16062306a36Sopenharmony_ci}; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/** 16362306a36Sopenharmony_ci * struct s3c64xx_spi_driver_data - Runtime info holder for SPI driver. 16462306a36Sopenharmony_ci * @clk: Pointer to the spi clock. 16562306a36Sopenharmony_ci * @src_clk: Pointer to the clock used to generate SPI signals. 16662306a36Sopenharmony_ci * @ioclk: Pointer to the i/o clock between host and target 16762306a36Sopenharmony_ci * @pdev: Pointer to device's platform device data 16862306a36Sopenharmony_ci * @host: Pointer to the SPI Protocol host. 16962306a36Sopenharmony_ci * @cntrlr_info: Platform specific data for the controller this driver manages. 17062306a36Sopenharmony_ci * @lock: Controller specific lock. 17162306a36Sopenharmony_ci * @state: Set of FLAGS to indicate status. 17262306a36Sopenharmony_ci * @sfr_start: BUS address of SPI controller regs. 17362306a36Sopenharmony_ci * @regs: Pointer to ioremap'ed controller registers. 17462306a36Sopenharmony_ci * @xfer_completion: To indicate completion of xfer task. 17562306a36Sopenharmony_ci * @cur_mode: Stores the active configuration of the controller. 17662306a36Sopenharmony_ci * @cur_bpw: Stores the active bits per word settings. 17762306a36Sopenharmony_ci * @cur_speed: Current clock speed 17862306a36Sopenharmony_ci * @rx_dma: Local receive DMA data (e.g. chan and direction) 17962306a36Sopenharmony_ci * @tx_dma: Local transmit DMA data (e.g. chan and direction) 18062306a36Sopenharmony_ci * @port_conf: Local SPI port configuartion data 18162306a36Sopenharmony_ci * @port_id: Port identification number 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_cistruct s3c64xx_spi_driver_data { 18462306a36Sopenharmony_ci void __iomem *regs; 18562306a36Sopenharmony_ci struct clk *clk; 18662306a36Sopenharmony_ci struct clk *src_clk; 18762306a36Sopenharmony_ci struct clk *ioclk; 18862306a36Sopenharmony_ci struct platform_device *pdev; 18962306a36Sopenharmony_ci struct spi_controller *host; 19062306a36Sopenharmony_ci struct s3c64xx_spi_info *cntrlr_info; 19162306a36Sopenharmony_ci spinlock_t lock; 19262306a36Sopenharmony_ci unsigned long sfr_start; 19362306a36Sopenharmony_ci struct completion xfer_completion; 19462306a36Sopenharmony_ci unsigned state; 19562306a36Sopenharmony_ci unsigned cur_mode, cur_bpw; 19662306a36Sopenharmony_ci unsigned cur_speed; 19762306a36Sopenharmony_ci struct s3c64xx_spi_dma_data rx_dma; 19862306a36Sopenharmony_ci struct s3c64xx_spi_dma_data tx_dma; 19962306a36Sopenharmony_ci const struct s3c64xx_spi_port_config *port_conf; 20062306a36Sopenharmony_ci unsigned int port_id; 20162306a36Sopenharmony_ci}; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic void s3c64xx_flush_fifo(struct s3c64xx_spi_driver_data *sdd) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci void __iomem *regs = sdd->regs; 20662306a36Sopenharmony_ci unsigned long loops; 20762306a36Sopenharmony_ci u32 val; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci writel(0, regs + S3C64XX_SPI_PACKET_CNT); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci val = readl(regs + S3C64XX_SPI_CH_CFG); 21262306a36Sopenharmony_ci val &= ~(S3C64XX_SPI_CH_RXCH_ON | S3C64XX_SPI_CH_TXCH_ON); 21362306a36Sopenharmony_ci writel(val, regs + S3C64XX_SPI_CH_CFG); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci val = readl(regs + S3C64XX_SPI_CH_CFG); 21662306a36Sopenharmony_ci val |= S3C64XX_SPI_CH_SW_RST; 21762306a36Sopenharmony_ci val &= ~S3C64XX_SPI_CH_HS_EN; 21862306a36Sopenharmony_ci writel(val, regs + S3C64XX_SPI_CH_CFG); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* Flush TxFIFO*/ 22162306a36Sopenharmony_ci loops = msecs_to_loops(1); 22262306a36Sopenharmony_ci do { 22362306a36Sopenharmony_ci val = readl(regs + S3C64XX_SPI_STATUS); 22462306a36Sopenharmony_ci } while (TX_FIFO_LVL(val, sdd) && loops--); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (loops == 0) 22762306a36Sopenharmony_ci dev_warn(&sdd->pdev->dev, "Timed out flushing TX FIFO\n"); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* Flush RxFIFO*/ 23062306a36Sopenharmony_ci loops = msecs_to_loops(1); 23162306a36Sopenharmony_ci do { 23262306a36Sopenharmony_ci val = readl(regs + S3C64XX_SPI_STATUS); 23362306a36Sopenharmony_ci if (RX_FIFO_LVL(val, sdd)) 23462306a36Sopenharmony_ci readl(regs + S3C64XX_SPI_RX_DATA); 23562306a36Sopenharmony_ci else 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci } while (loops--); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (loops == 0) 24062306a36Sopenharmony_ci dev_warn(&sdd->pdev->dev, "Timed out flushing RX FIFO\n"); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci val = readl(regs + S3C64XX_SPI_CH_CFG); 24362306a36Sopenharmony_ci val &= ~S3C64XX_SPI_CH_SW_RST; 24462306a36Sopenharmony_ci writel(val, regs + S3C64XX_SPI_CH_CFG); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci val = readl(regs + S3C64XX_SPI_MODE_CFG); 24762306a36Sopenharmony_ci val &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON); 24862306a36Sopenharmony_ci writel(val, regs + S3C64XX_SPI_MODE_CFG); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic void s3c64xx_spi_dmacb(void *data) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd; 25462306a36Sopenharmony_ci struct s3c64xx_spi_dma_data *dma = data; 25562306a36Sopenharmony_ci unsigned long flags; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (dma->direction == DMA_DEV_TO_MEM) 25862306a36Sopenharmony_ci sdd = container_of(data, 25962306a36Sopenharmony_ci struct s3c64xx_spi_driver_data, rx_dma); 26062306a36Sopenharmony_ci else 26162306a36Sopenharmony_ci sdd = container_of(data, 26262306a36Sopenharmony_ci struct s3c64xx_spi_driver_data, tx_dma); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci spin_lock_irqsave(&sdd->lock, flags); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (dma->direction == DMA_DEV_TO_MEM) { 26762306a36Sopenharmony_ci sdd->state &= ~RXBUSY; 26862306a36Sopenharmony_ci if (!(sdd->state & TXBUSY)) 26962306a36Sopenharmony_ci complete(&sdd->xfer_completion); 27062306a36Sopenharmony_ci } else { 27162306a36Sopenharmony_ci sdd->state &= ~TXBUSY; 27262306a36Sopenharmony_ci if (!(sdd->state & RXBUSY)) 27362306a36Sopenharmony_ci complete(&sdd->xfer_completion); 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci spin_unlock_irqrestore(&sdd->lock, flags); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic int prepare_dma(struct s3c64xx_spi_dma_data *dma, 28062306a36Sopenharmony_ci struct sg_table *sgt) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd; 28362306a36Sopenharmony_ci struct dma_slave_config config; 28462306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc; 28562306a36Sopenharmony_ci int ret; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci memset(&config, 0, sizeof(config)); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (dma->direction == DMA_DEV_TO_MEM) { 29062306a36Sopenharmony_ci sdd = container_of((void *)dma, 29162306a36Sopenharmony_ci struct s3c64xx_spi_driver_data, rx_dma); 29262306a36Sopenharmony_ci config.direction = dma->direction; 29362306a36Sopenharmony_ci config.src_addr = sdd->sfr_start + S3C64XX_SPI_RX_DATA; 29462306a36Sopenharmony_ci config.src_addr_width = sdd->cur_bpw / 8; 29562306a36Sopenharmony_ci config.src_maxburst = 1; 29662306a36Sopenharmony_ci dmaengine_slave_config(dma->ch, &config); 29762306a36Sopenharmony_ci } else { 29862306a36Sopenharmony_ci sdd = container_of((void *)dma, 29962306a36Sopenharmony_ci struct s3c64xx_spi_driver_data, tx_dma); 30062306a36Sopenharmony_ci config.direction = dma->direction; 30162306a36Sopenharmony_ci config.dst_addr = sdd->sfr_start + S3C64XX_SPI_TX_DATA; 30262306a36Sopenharmony_ci config.dst_addr_width = sdd->cur_bpw / 8; 30362306a36Sopenharmony_ci config.dst_maxburst = 1; 30462306a36Sopenharmony_ci dmaengine_slave_config(dma->ch, &config); 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci desc = dmaengine_prep_slave_sg(dma->ch, sgt->sgl, sgt->nents, 30862306a36Sopenharmony_ci dma->direction, DMA_PREP_INTERRUPT); 30962306a36Sopenharmony_ci if (!desc) { 31062306a36Sopenharmony_ci dev_err(&sdd->pdev->dev, "unable to prepare %s scatterlist", 31162306a36Sopenharmony_ci dma->direction == DMA_DEV_TO_MEM ? "rx" : "tx"); 31262306a36Sopenharmony_ci return -ENOMEM; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci desc->callback = s3c64xx_spi_dmacb; 31662306a36Sopenharmony_ci desc->callback_param = dma; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci dma->cookie = dmaengine_submit(desc); 31962306a36Sopenharmony_ci ret = dma_submit_error(dma->cookie); 32062306a36Sopenharmony_ci if (ret) { 32162306a36Sopenharmony_ci dev_err(&sdd->pdev->dev, "DMA submission failed"); 32262306a36Sopenharmony_ci return -EIO; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci dma_async_issue_pending(dma->ch); 32662306a36Sopenharmony_ci return 0; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic void s3c64xx_spi_set_cs(struct spi_device *spi, bool enable) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = 33262306a36Sopenharmony_ci spi_controller_get_devdata(spi->controller); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (sdd->cntrlr_info->no_cs) 33562306a36Sopenharmony_ci return; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (enable) { 33862306a36Sopenharmony_ci if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) { 33962306a36Sopenharmony_ci writel(0, sdd->regs + S3C64XX_SPI_CS_REG); 34062306a36Sopenharmony_ci } else { 34162306a36Sopenharmony_ci u32 ssel = readl(sdd->regs + S3C64XX_SPI_CS_REG); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci ssel |= (S3C64XX_SPI_CS_AUTO | 34462306a36Sopenharmony_ci S3C64XX_SPI_CS_NSC_CNT_2); 34562306a36Sopenharmony_ci writel(ssel, sdd->regs + S3C64XX_SPI_CS_REG); 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci } else { 34862306a36Sopenharmony_ci if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) 34962306a36Sopenharmony_ci writel(S3C64XX_SPI_CS_SIG_INACT, 35062306a36Sopenharmony_ci sdd->regs + S3C64XX_SPI_CS_REG); 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic int s3c64xx_spi_prepare_transfer(struct spi_controller *spi) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = spi_controller_get_devdata(spi); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (is_polling(sdd)) 35962306a36Sopenharmony_ci return 0; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* Requests DMA channels */ 36262306a36Sopenharmony_ci sdd->rx_dma.ch = dma_request_chan(&sdd->pdev->dev, "rx"); 36362306a36Sopenharmony_ci if (IS_ERR(sdd->rx_dma.ch)) { 36462306a36Sopenharmony_ci dev_err(&sdd->pdev->dev, "Failed to get RX DMA channel\n"); 36562306a36Sopenharmony_ci sdd->rx_dma.ch = NULL; 36662306a36Sopenharmony_ci return 0; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci sdd->tx_dma.ch = dma_request_chan(&sdd->pdev->dev, "tx"); 37062306a36Sopenharmony_ci if (IS_ERR(sdd->tx_dma.ch)) { 37162306a36Sopenharmony_ci dev_err(&sdd->pdev->dev, "Failed to get TX DMA channel\n"); 37262306a36Sopenharmony_ci dma_release_channel(sdd->rx_dma.ch); 37362306a36Sopenharmony_ci sdd->tx_dma.ch = NULL; 37462306a36Sopenharmony_ci sdd->rx_dma.ch = NULL; 37562306a36Sopenharmony_ci return 0; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci spi->dma_rx = sdd->rx_dma.ch; 37962306a36Sopenharmony_ci spi->dma_tx = sdd->tx_dma.ch; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return 0; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic int s3c64xx_spi_unprepare_transfer(struct spi_controller *spi) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = spi_controller_get_devdata(spi); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (is_polling(sdd)) 38962306a36Sopenharmony_ci return 0; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* Releases DMA channels if they are allocated */ 39262306a36Sopenharmony_ci if (sdd->rx_dma.ch && sdd->tx_dma.ch) { 39362306a36Sopenharmony_ci dma_release_channel(sdd->rx_dma.ch); 39462306a36Sopenharmony_ci dma_release_channel(sdd->tx_dma.ch); 39562306a36Sopenharmony_ci sdd->rx_dma.ch = NULL; 39662306a36Sopenharmony_ci sdd->tx_dma.ch = NULL; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci return 0; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic bool s3c64xx_spi_can_dma(struct spi_controller *host, 40362306a36Sopenharmony_ci struct spi_device *spi, 40462306a36Sopenharmony_ci struct spi_transfer *xfer) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = spi_controller_get_devdata(host); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (sdd->rx_dma.ch && sdd->tx_dma.ch) { 40962306a36Sopenharmony_ci return xfer->len > (FIFO_LVL_MASK(sdd) >> 1) + 1; 41062306a36Sopenharmony_ci } else { 41162306a36Sopenharmony_ci return false; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic int s3c64xx_enable_datapath(struct s3c64xx_spi_driver_data *sdd, 41762306a36Sopenharmony_ci struct spi_transfer *xfer, int dma_mode) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci void __iomem *regs = sdd->regs; 42062306a36Sopenharmony_ci u32 modecfg, chcfg; 42162306a36Sopenharmony_ci int ret = 0; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci modecfg = readl(regs + S3C64XX_SPI_MODE_CFG); 42462306a36Sopenharmony_ci modecfg &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci chcfg = readl(regs + S3C64XX_SPI_CH_CFG); 42762306a36Sopenharmony_ci chcfg &= ~S3C64XX_SPI_CH_TXCH_ON; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (dma_mode) { 43062306a36Sopenharmony_ci chcfg &= ~S3C64XX_SPI_CH_RXCH_ON; 43162306a36Sopenharmony_ci } else { 43262306a36Sopenharmony_ci /* Always shift in data in FIFO, even if xfer is Tx only, 43362306a36Sopenharmony_ci * this helps setting PCKT_CNT value for generating clocks 43462306a36Sopenharmony_ci * as exactly needed. 43562306a36Sopenharmony_ci */ 43662306a36Sopenharmony_ci chcfg |= S3C64XX_SPI_CH_RXCH_ON; 43762306a36Sopenharmony_ci writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff) 43862306a36Sopenharmony_ci | S3C64XX_SPI_PACKET_CNT_EN, 43962306a36Sopenharmony_ci regs + S3C64XX_SPI_PACKET_CNT); 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (xfer->tx_buf != NULL) { 44362306a36Sopenharmony_ci sdd->state |= TXBUSY; 44462306a36Sopenharmony_ci chcfg |= S3C64XX_SPI_CH_TXCH_ON; 44562306a36Sopenharmony_ci if (dma_mode) { 44662306a36Sopenharmony_ci modecfg |= S3C64XX_SPI_MODE_TXDMA_ON; 44762306a36Sopenharmony_ci ret = prepare_dma(&sdd->tx_dma, &xfer->tx_sg); 44862306a36Sopenharmony_ci } else { 44962306a36Sopenharmony_ci switch (sdd->cur_bpw) { 45062306a36Sopenharmony_ci case 32: 45162306a36Sopenharmony_ci iowrite32_rep(regs + S3C64XX_SPI_TX_DATA, 45262306a36Sopenharmony_ci xfer->tx_buf, xfer->len / 4); 45362306a36Sopenharmony_ci break; 45462306a36Sopenharmony_ci case 16: 45562306a36Sopenharmony_ci iowrite16_rep(regs + S3C64XX_SPI_TX_DATA, 45662306a36Sopenharmony_ci xfer->tx_buf, xfer->len / 2); 45762306a36Sopenharmony_ci break; 45862306a36Sopenharmony_ci default: 45962306a36Sopenharmony_ci iowrite8_rep(regs + S3C64XX_SPI_TX_DATA, 46062306a36Sopenharmony_ci xfer->tx_buf, xfer->len); 46162306a36Sopenharmony_ci break; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (xfer->rx_buf != NULL) { 46762306a36Sopenharmony_ci sdd->state |= RXBUSY; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (sdd->port_conf->high_speed && sdd->cur_speed >= 30000000UL 47062306a36Sopenharmony_ci && !(sdd->cur_mode & SPI_CPHA)) 47162306a36Sopenharmony_ci chcfg |= S3C64XX_SPI_CH_HS_EN; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (dma_mode) { 47462306a36Sopenharmony_ci modecfg |= S3C64XX_SPI_MODE_RXDMA_ON; 47562306a36Sopenharmony_ci chcfg |= S3C64XX_SPI_CH_RXCH_ON; 47662306a36Sopenharmony_ci writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff) 47762306a36Sopenharmony_ci | S3C64XX_SPI_PACKET_CNT_EN, 47862306a36Sopenharmony_ci regs + S3C64XX_SPI_PACKET_CNT); 47962306a36Sopenharmony_ci ret = prepare_dma(&sdd->rx_dma, &xfer->rx_sg); 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (ret) 48462306a36Sopenharmony_ci return ret; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci writel(modecfg, regs + S3C64XX_SPI_MODE_CFG); 48762306a36Sopenharmony_ci writel(chcfg, regs + S3C64XX_SPI_CH_CFG); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci return 0; 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic u32 s3c64xx_spi_wait_for_timeout(struct s3c64xx_spi_driver_data *sdd, 49362306a36Sopenharmony_ci int timeout_ms) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci void __iomem *regs = sdd->regs; 49662306a36Sopenharmony_ci unsigned long val = 1; 49762306a36Sopenharmony_ci u32 status; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci /* max fifo depth available */ 50062306a36Sopenharmony_ci u32 max_fifo = (FIFO_LVL_MASK(sdd) >> 1) + 1; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (timeout_ms) 50362306a36Sopenharmony_ci val = msecs_to_loops(timeout_ms); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci do { 50662306a36Sopenharmony_ci status = readl(regs + S3C64XX_SPI_STATUS); 50762306a36Sopenharmony_ci } while (RX_FIFO_LVL(status, sdd) < max_fifo && --val); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* return the actual received data length */ 51062306a36Sopenharmony_ci return RX_FIFO_LVL(status, sdd); 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic int s3c64xx_wait_for_dma(struct s3c64xx_spi_driver_data *sdd, 51462306a36Sopenharmony_ci struct spi_transfer *xfer) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci void __iomem *regs = sdd->regs; 51762306a36Sopenharmony_ci unsigned long val; 51862306a36Sopenharmony_ci u32 status; 51962306a36Sopenharmony_ci int ms; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* millisecs to xfer 'len' bytes @ 'cur_speed' */ 52262306a36Sopenharmony_ci ms = xfer->len * 8 * 1000 / sdd->cur_speed; 52362306a36Sopenharmony_ci ms += 30; /* some tolerance */ 52462306a36Sopenharmony_ci ms = max(ms, 100); /* minimum timeout */ 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci val = msecs_to_jiffies(ms) + 10; 52762306a36Sopenharmony_ci val = wait_for_completion_timeout(&sdd->xfer_completion, val); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* 53062306a36Sopenharmony_ci * If the previous xfer was completed within timeout, then 53162306a36Sopenharmony_ci * proceed further else return -EIO. 53262306a36Sopenharmony_ci * DmaTx returns after simply writing data in the FIFO, 53362306a36Sopenharmony_ci * w/o waiting for real transmission on the bus to finish. 53462306a36Sopenharmony_ci * DmaRx returns only after Dma read data from FIFO which 53562306a36Sopenharmony_ci * needs bus transmission to finish, so we don't worry if 53662306a36Sopenharmony_ci * Xfer involved Rx(with or without Tx). 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_ci if (val && !xfer->rx_buf) { 53962306a36Sopenharmony_ci val = msecs_to_loops(10); 54062306a36Sopenharmony_ci status = readl(regs + S3C64XX_SPI_STATUS); 54162306a36Sopenharmony_ci while ((TX_FIFO_LVL(status, sdd) 54262306a36Sopenharmony_ci || !S3C64XX_SPI_ST_TX_DONE(status, sdd)) 54362306a36Sopenharmony_ci && --val) { 54462306a36Sopenharmony_ci cpu_relax(); 54562306a36Sopenharmony_ci status = readl(regs + S3C64XX_SPI_STATUS); 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* If timed out while checking rx/tx status return error */ 55162306a36Sopenharmony_ci if (!val) 55262306a36Sopenharmony_ci return -EIO; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci return 0; 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic int s3c64xx_wait_for_pio(struct s3c64xx_spi_driver_data *sdd, 55862306a36Sopenharmony_ci struct spi_transfer *xfer, bool use_irq) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci void __iomem *regs = sdd->regs; 56162306a36Sopenharmony_ci unsigned long val; 56262306a36Sopenharmony_ci u32 status; 56362306a36Sopenharmony_ci int loops; 56462306a36Sopenharmony_ci u32 cpy_len; 56562306a36Sopenharmony_ci u8 *buf; 56662306a36Sopenharmony_ci int ms; 56762306a36Sopenharmony_ci unsigned long time_us; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* microsecs to xfer 'len' bytes @ 'cur_speed' */ 57062306a36Sopenharmony_ci time_us = (xfer->len * 8 * 1000 * 1000) / sdd->cur_speed; 57162306a36Sopenharmony_ci ms = (time_us / 1000); 57262306a36Sopenharmony_ci ms += 10; /* some tolerance */ 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* sleep during signal transfer time */ 57562306a36Sopenharmony_ci status = readl(regs + S3C64XX_SPI_STATUS); 57662306a36Sopenharmony_ci if (RX_FIFO_LVL(status, sdd) < xfer->len) 57762306a36Sopenharmony_ci usleep_range(time_us / 2, time_us); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (use_irq) { 58062306a36Sopenharmony_ci val = msecs_to_jiffies(ms); 58162306a36Sopenharmony_ci if (!wait_for_completion_timeout(&sdd->xfer_completion, val)) 58262306a36Sopenharmony_ci return -EIO; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci val = msecs_to_loops(ms); 58662306a36Sopenharmony_ci do { 58762306a36Sopenharmony_ci status = readl(regs + S3C64XX_SPI_STATUS); 58862306a36Sopenharmony_ci } while (RX_FIFO_LVL(status, sdd) < xfer->len && --val); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (!val) 59162306a36Sopenharmony_ci return -EIO; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* If it was only Tx */ 59462306a36Sopenharmony_ci if (!xfer->rx_buf) { 59562306a36Sopenharmony_ci sdd->state &= ~TXBUSY; 59662306a36Sopenharmony_ci return 0; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci /* 60062306a36Sopenharmony_ci * If the receive length is bigger than the controller fifo 60162306a36Sopenharmony_ci * size, calculate the loops and read the fifo as many times. 60262306a36Sopenharmony_ci * loops = length / max fifo size (calculated by using the 60362306a36Sopenharmony_ci * fifo mask). 60462306a36Sopenharmony_ci * For any size less than the fifo size the below code is 60562306a36Sopenharmony_ci * executed atleast once. 60662306a36Sopenharmony_ci */ 60762306a36Sopenharmony_ci loops = xfer->len / ((FIFO_LVL_MASK(sdd) >> 1) + 1); 60862306a36Sopenharmony_ci buf = xfer->rx_buf; 60962306a36Sopenharmony_ci do { 61062306a36Sopenharmony_ci /* wait for data to be received in the fifo */ 61162306a36Sopenharmony_ci cpy_len = s3c64xx_spi_wait_for_timeout(sdd, 61262306a36Sopenharmony_ci (loops ? ms : 0)); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci switch (sdd->cur_bpw) { 61562306a36Sopenharmony_ci case 32: 61662306a36Sopenharmony_ci ioread32_rep(regs + S3C64XX_SPI_RX_DATA, 61762306a36Sopenharmony_ci buf, cpy_len / 4); 61862306a36Sopenharmony_ci break; 61962306a36Sopenharmony_ci case 16: 62062306a36Sopenharmony_ci ioread16_rep(regs + S3C64XX_SPI_RX_DATA, 62162306a36Sopenharmony_ci buf, cpy_len / 2); 62262306a36Sopenharmony_ci break; 62362306a36Sopenharmony_ci default: 62462306a36Sopenharmony_ci ioread8_rep(regs + S3C64XX_SPI_RX_DATA, 62562306a36Sopenharmony_ci buf, cpy_len); 62662306a36Sopenharmony_ci break; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci buf = buf + cpy_len; 63062306a36Sopenharmony_ci } while (loops--); 63162306a36Sopenharmony_ci sdd->state &= ~RXBUSY; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci return 0; 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic int s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci void __iomem *regs = sdd->regs; 63962306a36Sopenharmony_ci int ret; 64062306a36Sopenharmony_ci u32 val; 64162306a36Sopenharmony_ci int div = sdd->port_conf->clk_div; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci /* Disable Clock */ 64462306a36Sopenharmony_ci if (!sdd->port_conf->clk_from_cmu) { 64562306a36Sopenharmony_ci val = readl(regs + S3C64XX_SPI_CLK_CFG); 64662306a36Sopenharmony_ci val &= ~S3C64XX_SPI_ENCLK_ENABLE; 64762306a36Sopenharmony_ci writel(val, regs + S3C64XX_SPI_CLK_CFG); 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci /* Set Polarity and Phase */ 65162306a36Sopenharmony_ci val = readl(regs + S3C64XX_SPI_CH_CFG); 65262306a36Sopenharmony_ci val &= ~(S3C64XX_SPI_CH_SLAVE | 65362306a36Sopenharmony_ci S3C64XX_SPI_CPOL_L | 65462306a36Sopenharmony_ci S3C64XX_SPI_CPHA_B); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (sdd->cur_mode & SPI_CPOL) 65762306a36Sopenharmony_ci val |= S3C64XX_SPI_CPOL_L; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (sdd->cur_mode & SPI_CPHA) 66062306a36Sopenharmony_ci val |= S3C64XX_SPI_CPHA_B; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci writel(val, regs + S3C64XX_SPI_CH_CFG); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci /* Set Channel & DMA Mode */ 66562306a36Sopenharmony_ci val = readl(regs + S3C64XX_SPI_MODE_CFG); 66662306a36Sopenharmony_ci val &= ~(S3C64XX_SPI_MODE_BUS_TSZ_MASK 66762306a36Sopenharmony_ci | S3C64XX_SPI_MODE_CH_TSZ_MASK); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci switch (sdd->cur_bpw) { 67062306a36Sopenharmony_ci case 32: 67162306a36Sopenharmony_ci val |= S3C64XX_SPI_MODE_BUS_TSZ_WORD; 67262306a36Sopenharmony_ci val |= S3C64XX_SPI_MODE_CH_TSZ_WORD; 67362306a36Sopenharmony_ci break; 67462306a36Sopenharmony_ci case 16: 67562306a36Sopenharmony_ci val |= S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD; 67662306a36Sopenharmony_ci val |= S3C64XX_SPI_MODE_CH_TSZ_HALFWORD; 67762306a36Sopenharmony_ci break; 67862306a36Sopenharmony_ci default: 67962306a36Sopenharmony_ci val |= S3C64XX_SPI_MODE_BUS_TSZ_BYTE; 68062306a36Sopenharmony_ci val |= S3C64XX_SPI_MODE_CH_TSZ_BYTE; 68162306a36Sopenharmony_ci break; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if ((sdd->cur_mode & SPI_LOOP) && sdd->port_conf->has_loopback) 68562306a36Sopenharmony_ci val |= S3C64XX_SPI_MODE_SELF_LOOPBACK; 68662306a36Sopenharmony_ci else 68762306a36Sopenharmony_ci val &= ~S3C64XX_SPI_MODE_SELF_LOOPBACK; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci writel(val, regs + S3C64XX_SPI_MODE_CFG); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci if (sdd->port_conf->clk_from_cmu) { 69262306a36Sopenharmony_ci ret = clk_set_rate(sdd->src_clk, sdd->cur_speed * div); 69362306a36Sopenharmony_ci if (ret) 69462306a36Sopenharmony_ci return ret; 69562306a36Sopenharmony_ci sdd->cur_speed = clk_get_rate(sdd->src_clk) / div; 69662306a36Sopenharmony_ci } else { 69762306a36Sopenharmony_ci /* Configure Clock */ 69862306a36Sopenharmony_ci val = readl(regs + S3C64XX_SPI_CLK_CFG); 69962306a36Sopenharmony_ci val &= ~S3C64XX_SPI_PSR_MASK; 70062306a36Sopenharmony_ci val |= ((clk_get_rate(sdd->src_clk) / sdd->cur_speed / div - 1) 70162306a36Sopenharmony_ci & S3C64XX_SPI_PSR_MASK); 70262306a36Sopenharmony_ci writel(val, regs + S3C64XX_SPI_CLK_CFG); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci /* Enable Clock */ 70562306a36Sopenharmony_ci val = readl(regs + S3C64XX_SPI_CLK_CFG); 70662306a36Sopenharmony_ci val |= S3C64XX_SPI_ENCLK_ENABLE; 70762306a36Sopenharmony_ci writel(val, regs + S3C64XX_SPI_CLK_CFG); 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci return 0; 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci#define XFER_DMAADDR_INVALID DMA_BIT_MASK(32) 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic int s3c64xx_spi_prepare_message(struct spi_controller *host, 71662306a36Sopenharmony_ci struct spi_message *msg) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = spi_controller_get_devdata(host); 71962306a36Sopenharmony_ci struct spi_device *spi = msg->spi; 72062306a36Sopenharmony_ci struct s3c64xx_spi_csinfo *cs = spi->controller_data; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* Configure feedback delay */ 72362306a36Sopenharmony_ci if (!cs) 72462306a36Sopenharmony_ci /* No delay if not defined */ 72562306a36Sopenharmony_ci writel(0, sdd->regs + S3C64XX_SPI_FB_CLK); 72662306a36Sopenharmony_ci else 72762306a36Sopenharmony_ci writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci return 0; 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic size_t s3c64xx_spi_max_transfer_size(struct spi_device *spi) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci struct spi_controller *ctlr = spi->controller; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci return ctlr->can_dma ? S3C64XX_SPI_PACKET_CNT_MASK : SIZE_MAX; 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistatic int s3c64xx_spi_transfer_one(struct spi_controller *host, 74062306a36Sopenharmony_ci struct spi_device *spi, 74162306a36Sopenharmony_ci struct spi_transfer *xfer) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = spi_controller_get_devdata(host); 74462306a36Sopenharmony_ci const unsigned int fifo_len = (FIFO_LVL_MASK(sdd) >> 1) + 1; 74562306a36Sopenharmony_ci const void *tx_buf = NULL; 74662306a36Sopenharmony_ci void *rx_buf = NULL; 74762306a36Sopenharmony_ci int target_len = 0, origin_len = 0; 74862306a36Sopenharmony_ci int use_dma = 0; 74962306a36Sopenharmony_ci bool use_irq = false; 75062306a36Sopenharmony_ci int status; 75162306a36Sopenharmony_ci u32 speed; 75262306a36Sopenharmony_ci u8 bpw; 75362306a36Sopenharmony_ci unsigned long flags; 75462306a36Sopenharmony_ci u32 rdy_lv; 75562306a36Sopenharmony_ci u32 val; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci reinit_completion(&sdd->xfer_completion); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci /* Only BPW and Speed may change across transfers */ 76062306a36Sopenharmony_ci bpw = xfer->bits_per_word; 76162306a36Sopenharmony_ci speed = xfer->speed_hz; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) { 76462306a36Sopenharmony_ci sdd->cur_bpw = bpw; 76562306a36Sopenharmony_ci sdd->cur_speed = speed; 76662306a36Sopenharmony_ci sdd->cur_mode = spi->mode; 76762306a36Sopenharmony_ci status = s3c64xx_spi_config(sdd); 76862306a36Sopenharmony_ci if (status) 76962306a36Sopenharmony_ci return status; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci if (!is_polling(sdd) && (xfer->len > fifo_len) && 77362306a36Sopenharmony_ci sdd->rx_dma.ch && sdd->tx_dma.ch) { 77462306a36Sopenharmony_ci use_dma = 1; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci } else if (xfer->len >= fifo_len) { 77762306a36Sopenharmony_ci tx_buf = xfer->tx_buf; 77862306a36Sopenharmony_ci rx_buf = xfer->rx_buf; 77962306a36Sopenharmony_ci origin_len = xfer->len; 78062306a36Sopenharmony_ci target_len = xfer->len; 78162306a36Sopenharmony_ci xfer->len = fifo_len - 1; 78262306a36Sopenharmony_ci } 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci do { 78562306a36Sopenharmony_ci /* transfer size is greater than 32, change to IRQ mode */ 78662306a36Sopenharmony_ci if (!use_dma && xfer->len > S3C64XX_SPI_POLLING_SIZE) 78762306a36Sopenharmony_ci use_irq = true; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if (use_irq) { 79062306a36Sopenharmony_ci reinit_completion(&sdd->xfer_completion); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci rdy_lv = xfer->len; 79362306a36Sopenharmony_ci /* Setup RDY_FIFO trigger Level 79462306a36Sopenharmony_ci * RDY_LVL = 79562306a36Sopenharmony_ci * fifo_lvl up to 64 byte -> N bytes 79662306a36Sopenharmony_ci * 128 byte -> RDY_LVL * 2 bytes 79762306a36Sopenharmony_ci * 256 byte -> RDY_LVL * 4 bytes 79862306a36Sopenharmony_ci */ 79962306a36Sopenharmony_ci if (fifo_len == 128) 80062306a36Sopenharmony_ci rdy_lv /= 2; 80162306a36Sopenharmony_ci else if (fifo_len == 256) 80262306a36Sopenharmony_ci rdy_lv /= 4; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci val = readl(sdd->regs + S3C64XX_SPI_MODE_CFG); 80562306a36Sopenharmony_ci val &= ~S3C64XX_SPI_MODE_RX_RDY_LVL; 80662306a36Sopenharmony_ci val |= (rdy_lv << S3C64XX_SPI_MODE_RX_RDY_LVL_SHIFT); 80762306a36Sopenharmony_ci writel(val, sdd->regs + S3C64XX_SPI_MODE_CFG); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci /* Enable FIFO_RDY_EN IRQ */ 81062306a36Sopenharmony_ci val = readl(sdd->regs + S3C64XX_SPI_INT_EN); 81162306a36Sopenharmony_ci writel((val | S3C64XX_SPI_INT_RX_FIFORDY_EN), 81262306a36Sopenharmony_ci sdd->regs + S3C64XX_SPI_INT_EN); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci spin_lock_irqsave(&sdd->lock, flags); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* Pending only which is to be done */ 81962306a36Sopenharmony_ci sdd->state &= ~RXBUSY; 82062306a36Sopenharmony_ci sdd->state &= ~TXBUSY; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci /* Start the signals */ 82362306a36Sopenharmony_ci s3c64xx_spi_set_cs(spi, true); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci status = s3c64xx_enable_datapath(sdd, xfer, use_dma); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci spin_unlock_irqrestore(&sdd->lock, flags); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci if (status) { 83062306a36Sopenharmony_ci dev_err(&spi->dev, "failed to enable data path for transfer: %d\n", status); 83162306a36Sopenharmony_ci break; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci if (use_dma) 83562306a36Sopenharmony_ci status = s3c64xx_wait_for_dma(sdd, xfer); 83662306a36Sopenharmony_ci else 83762306a36Sopenharmony_ci status = s3c64xx_wait_for_pio(sdd, xfer, use_irq); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (status) { 84062306a36Sopenharmony_ci dev_err(&spi->dev, 84162306a36Sopenharmony_ci "I/O Error: rx-%d tx-%d rx-%c tx-%c len-%d dma-%d res-(%d)\n", 84262306a36Sopenharmony_ci xfer->rx_buf ? 1 : 0, xfer->tx_buf ? 1 : 0, 84362306a36Sopenharmony_ci (sdd->state & RXBUSY) ? 'f' : 'p', 84462306a36Sopenharmony_ci (sdd->state & TXBUSY) ? 'f' : 'p', 84562306a36Sopenharmony_ci xfer->len, use_dma ? 1 : 0, status); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (use_dma) { 84862306a36Sopenharmony_ci struct dma_tx_state s; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci if (xfer->tx_buf && (sdd->state & TXBUSY)) { 85162306a36Sopenharmony_ci dmaengine_pause(sdd->tx_dma.ch); 85262306a36Sopenharmony_ci dmaengine_tx_status(sdd->tx_dma.ch, sdd->tx_dma.cookie, &s); 85362306a36Sopenharmony_ci dmaengine_terminate_all(sdd->tx_dma.ch); 85462306a36Sopenharmony_ci dev_err(&spi->dev, "TX residue: %d\n", s.residue); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci if (xfer->rx_buf && (sdd->state & RXBUSY)) { 85862306a36Sopenharmony_ci dmaengine_pause(sdd->rx_dma.ch); 85962306a36Sopenharmony_ci dmaengine_tx_status(sdd->rx_dma.ch, sdd->rx_dma.cookie, &s); 86062306a36Sopenharmony_ci dmaengine_terminate_all(sdd->rx_dma.ch); 86162306a36Sopenharmony_ci dev_err(&spi->dev, "RX residue: %d\n", s.residue); 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci } else { 86562306a36Sopenharmony_ci s3c64xx_flush_fifo(sdd); 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci if (target_len > 0) { 86862306a36Sopenharmony_ci target_len -= xfer->len; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci if (xfer->tx_buf) 87162306a36Sopenharmony_ci xfer->tx_buf += xfer->len; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci if (xfer->rx_buf) 87462306a36Sopenharmony_ci xfer->rx_buf += xfer->len; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci if (target_len >= fifo_len) 87762306a36Sopenharmony_ci xfer->len = fifo_len - 1; 87862306a36Sopenharmony_ci else 87962306a36Sopenharmony_ci xfer->len = target_len; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci } while (target_len > 0); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (origin_len) { 88462306a36Sopenharmony_ci /* Restore original xfer buffers and length */ 88562306a36Sopenharmony_ci xfer->tx_buf = tx_buf; 88662306a36Sopenharmony_ci xfer->rx_buf = rx_buf; 88762306a36Sopenharmony_ci xfer->len = origin_len; 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci return status; 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_cistatic struct s3c64xx_spi_csinfo *s3c64xx_get_target_ctrldata( 89462306a36Sopenharmony_ci struct spi_device *spi) 89562306a36Sopenharmony_ci{ 89662306a36Sopenharmony_ci struct s3c64xx_spi_csinfo *cs; 89762306a36Sopenharmony_ci struct device_node *target_np, *data_np = NULL; 89862306a36Sopenharmony_ci u32 fb_delay = 0; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci target_np = spi->dev.of_node; 90162306a36Sopenharmony_ci if (!target_np) { 90262306a36Sopenharmony_ci dev_err(&spi->dev, "device node not found\n"); 90362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 90462306a36Sopenharmony_ci } 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci cs = kzalloc(sizeof(*cs), GFP_KERNEL); 90762306a36Sopenharmony_ci if (!cs) 90862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci data_np = of_get_child_by_name(target_np, "controller-data"); 91162306a36Sopenharmony_ci if (!data_np) { 91262306a36Sopenharmony_ci dev_info(&spi->dev, "feedback delay set to default (0)\n"); 91362306a36Sopenharmony_ci return cs; 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci of_property_read_u32(data_np, "samsung,spi-feedback-delay", &fb_delay); 91762306a36Sopenharmony_ci cs->fb_delay = fb_delay; 91862306a36Sopenharmony_ci of_node_put(data_np); 91962306a36Sopenharmony_ci return cs; 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci/* 92362306a36Sopenharmony_ci * Here we only check the validity of requested configuration 92462306a36Sopenharmony_ci * and save the configuration in a local data-structure. 92562306a36Sopenharmony_ci * The controller is actually configured only just before we 92662306a36Sopenharmony_ci * get a message to transfer. 92762306a36Sopenharmony_ci */ 92862306a36Sopenharmony_cistatic int s3c64xx_spi_setup(struct spi_device *spi) 92962306a36Sopenharmony_ci{ 93062306a36Sopenharmony_ci struct s3c64xx_spi_csinfo *cs = spi->controller_data; 93162306a36Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd; 93262306a36Sopenharmony_ci int err; 93362306a36Sopenharmony_ci int div; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci sdd = spi_controller_get_devdata(spi->controller); 93662306a36Sopenharmony_ci if (spi->dev.of_node) { 93762306a36Sopenharmony_ci cs = s3c64xx_get_target_ctrldata(spi); 93862306a36Sopenharmony_ci spi->controller_data = cs; 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci /* NULL is fine, we just avoid using the FB delay (=0) */ 94262306a36Sopenharmony_ci if (IS_ERR(cs)) { 94362306a36Sopenharmony_ci dev_err(&spi->dev, "No CS for SPI(%d)\n", spi_get_chipselect(spi, 0)); 94462306a36Sopenharmony_ci return -ENODEV; 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci if (!spi_get_ctldata(spi)) 94862306a36Sopenharmony_ci spi_set_ctldata(spi, cs); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci pm_runtime_get_sync(&sdd->pdev->dev); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci div = sdd->port_conf->clk_div; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci /* Check if we can provide the requested rate */ 95562306a36Sopenharmony_ci if (!sdd->port_conf->clk_from_cmu) { 95662306a36Sopenharmony_ci u32 psr, speed; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci /* Max possible */ 95962306a36Sopenharmony_ci speed = clk_get_rate(sdd->src_clk) / div / (0 + 1); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci if (spi->max_speed_hz > speed) 96262306a36Sopenharmony_ci spi->max_speed_hz = speed; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci psr = clk_get_rate(sdd->src_clk) / div / spi->max_speed_hz - 1; 96562306a36Sopenharmony_ci psr &= S3C64XX_SPI_PSR_MASK; 96662306a36Sopenharmony_ci if (psr == S3C64XX_SPI_PSR_MASK) 96762306a36Sopenharmony_ci psr--; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci speed = clk_get_rate(sdd->src_clk) / div / (psr + 1); 97062306a36Sopenharmony_ci if (spi->max_speed_hz < speed) { 97162306a36Sopenharmony_ci if (psr+1 < S3C64XX_SPI_PSR_MASK) { 97262306a36Sopenharmony_ci psr++; 97362306a36Sopenharmony_ci } else { 97462306a36Sopenharmony_ci err = -EINVAL; 97562306a36Sopenharmony_ci goto setup_exit; 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci speed = clk_get_rate(sdd->src_clk) / div / (psr + 1); 98062306a36Sopenharmony_ci if (spi->max_speed_hz >= speed) { 98162306a36Sopenharmony_ci spi->max_speed_hz = speed; 98262306a36Sopenharmony_ci } else { 98362306a36Sopenharmony_ci dev_err(&spi->dev, "Can't set %dHz transfer speed\n", 98462306a36Sopenharmony_ci spi->max_speed_hz); 98562306a36Sopenharmony_ci err = -EINVAL; 98662306a36Sopenharmony_ci goto setup_exit; 98762306a36Sopenharmony_ci } 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci pm_runtime_mark_last_busy(&sdd->pdev->dev); 99162306a36Sopenharmony_ci pm_runtime_put_autosuspend(&sdd->pdev->dev); 99262306a36Sopenharmony_ci s3c64xx_spi_set_cs(spi, false); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci return 0; 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_cisetup_exit: 99762306a36Sopenharmony_ci pm_runtime_mark_last_busy(&sdd->pdev->dev); 99862306a36Sopenharmony_ci pm_runtime_put_autosuspend(&sdd->pdev->dev); 99962306a36Sopenharmony_ci /* setup() returns with device de-selected */ 100062306a36Sopenharmony_ci s3c64xx_spi_set_cs(spi, false); 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci spi_set_ctldata(spi, NULL); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci /* This was dynamically allocated on the DT path */ 100562306a36Sopenharmony_ci if (spi->dev.of_node) 100662306a36Sopenharmony_ci kfree(cs); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci return err; 100962306a36Sopenharmony_ci} 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_cistatic void s3c64xx_spi_cleanup(struct spi_device *spi) 101262306a36Sopenharmony_ci{ 101362306a36Sopenharmony_ci struct s3c64xx_spi_csinfo *cs = spi_get_ctldata(spi); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci /* This was dynamically allocated on the DT path */ 101662306a36Sopenharmony_ci if (spi->dev.of_node) 101762306a36Sopenharmony_ci kfree(cs); 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci spi_set_ctldata(spi, NULL); 102062306a36Sopenharmony_ci} 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_cistatic irqreturn_t s3c64xx_spi_irq(int irq, void *data) 102362306a36Sopenharmony_ci{ 102462306a36Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = data; 102562306a36Sopenharmony_ci struct spi_controller *spi = sdd->host; 102662306a36Sopenharmony_ci unsigned int val, clr = 0; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci val = readl(sdd->regs + S3C64XX_SPI_STATUS); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci if (val & S3C64XX_SPI_ST_RX_OVERRUN_ERR) { 103162306a36Sopenharmony_ci clr = S3C64XX_SPI_PND_RX_OVERRUN_CLR; 103262306a36Sopenharmony_ci dev_err(&spi->dev, "RX overrun\n"); 103362306a36Sopenharmony_ci } 103462306a36Sopenharmony_ci if (val & S3C64XX_SPI_ST_RX_UNDERRUN_ERR) { 103562306a36Sopenharmony_ci clr |= S3C64XX_SPI_PND_RX_UNDERRUN_CLR; 103662306a36Sopenharmony_ci dev_err(&spi->dev, "RX underrun\n"); 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci if (val & S3C64XX_SPI_ST_TX_OVERRUN_ERR) { 103962306a36Sopenharmony_ci clr |= S3C64XX_SPI_PND_TX_OVERRUN_CLR; 104062306a36Sopenharmony_ci dev_err(&spi->dev, "TX overrun\n"); 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci if (val & S3C64XX_SPI_ST_TX_UNDERRUN_ERR) { 104362306a36Sopenharmony_ci clr |= S3C64XX_SPI_PND_TX_UNDERRUN_CLR; 104462306a36Sopenharmony_ci dev_err(&spi->dev, "TX underrun\n"); 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci if (val & S3C64XX_SPI_ST_RX_FIFORDY) { 104862306a36Sopenharmony_ci complete(&sdd->xfer_completion); 104962306a36Sopenharmony_ci /* No pending clear irq, turn-off INT_EN_RX_FIFO_RDY */ 105062306a36Sopenharmony_ci val = readl(sdd->regs + S3C64XX_SPI_INT_EN); 105162306a36Sopenharmony_ci writel((val & ~S3C64XX_SPI_INT_RX_FIFORDY_EN), 105262306a36Sopenharmony_ci sdd->regs + S3C64XX_SPI_INT_EN); 105362306a36Sopenharmony_ci } 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci /* Clear the pending irq by setting and then clearing it */ 105662306a36Sopenharmony_ci writel(clr, sdd->regs + S3C64XX_SPI_PENDING_CLR); 105762306a36Sopenharmony_ci writel(0, sdd->regs + S3C64XX_SPI_PENDING_CLR); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci return IRQ_HANDLED; 106062306a36Sopenharmony_ci} 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_cistatic void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd) 106362306a36Sopenharmony_ci{ 106462306a36Sopenharmony_ci struct s3c64xx_spi_info *sci = sdd->cntrlr_info; 106562306a36Sopenharmony_ci void __iomem *regs = sdd->regs; 106662306a36Sopenharmony_ci unsigned int val; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci sdd->cur_speed = 0; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci if (sci->no_cs) 107162306a36Sopenharmony_ci writel(0, sdd->regs + S3C64XX_SPI_CS_REG); 107262306a36Sopenharmony_ci else if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) 107362306a36Sopenharmony_ci writel(S3C64XX_SPI_CS_SIG_INACT, sdd->regs + S3C64XX_SPI_CS_REG); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci /* Disable Interrupts - we use Polling if not DMA mode */ 107662306a36Sopenharmony_ci writel(0, regs + S3C64XX_SPI_INT_EN); 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci if (!sdd->port_conf->clk_from_cmu) 107962306a36Sopenharmony_ci writel(sci->src_clk_nr << S3C64XX_SPI_CLKSEL_SRCSHFT, 108062306a36Sopenharmony_ci regs + S3C64XX_SPI_CLK_CFG); 108162306a36Sopenharmony_ci writel(0, regs + S3C64XX_SPI_MODE_CFG); 108262306a36Sopenharmony_ci writel(0, regs + S3C64XX_SPI_PACKET_CNT); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci /* Clear any irq pending bits, should set and clear the bits */ 108562306a36Sopenharmony_ci val = S3C64XX_SPI_PND_RX_OVERRUN_CLR | 108662306a36Sopenharmony_ci S3C64XX_SPI_PND_RX_UNDERRUN_CLR | 108762306a36Sopenharmony_ci S3C64XX_SPI_PND_TX_OVERRUN_CLR | 108862306a36Sopenharmony_ci S3C64XX_SPI_PND_TX_UNDERRUN_CLR; 108962306a36Sopenharmony_ci writel(val, regs + S3C64XX_SPI_PENDING_CLR); 109062306a36Sopenharmony_ci writel(0, regs + S3C64XX_SPI_PENDING_CLR); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci writel(0, regs + S3C64XX_SPI_SWAP_CFG); 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci val = readl(regs + S3C64XX_SPI_MODE_CFG); 109562306a36Sopenharmony_ci val &= ~S3C64XX_SPI_MODE_4BURST; 109662306a36Sopenharmony_ci val &= ~(S3C64XX_SPI_MAX_TRAILCNT << S3C64XX_SPI_TRAILCNT_OFF); 109762306a36Sopenharmony_ci val |= (S3C64XX_SPI_TRAILCNT << S3C64XX_SPI_TRAILCNT_OFF); 109862306a36Sopenharmony_ci writel(val, regs + S3C64XX_SPI_MODE_CFG); 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci s3c64xx_flush_fifo(sdd); 110162306a36Sopenharmony_ci} 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci#ifdef CONFIG_OF 110462306a36Sopenharmony_cistatic struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev) 110562306a36Sopenharmony_ci{ 110662306a36Sopenharmony_ci struct s3c64xx_spi_info *sci; 110762306a36Sopenharmony_ci u32 temp; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci sci = devm_kzalloc(dev, sizeof(*sci), GFP_KERNEL); 111062306a36Sopenharmony_ci if (!sci) 111162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci if (of_property_read_u32(dev->of_node, "samsung,spi-src-clk", &temp)) { 111462306a36Sopenharmony_ci dev_warn(dev, "spi bus clock parent not specified, using clock at index 0 as parent\n"); 111562306a36Sopenharmony_ci sci->src_clk_nr = 0; 111662306a36Sopenharmony_ci } else { 111762306a36Sopenharmony_ci sci->src_clk_nr = temp; 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci if (of_property_read_u32(dev->of_node, "num-cs", &temp)) { 112162306a36Sopenharmony_ci dev_warn(dev, "number of chip select lines not specified, assuming 1 chip select line\n"); 112262306a36Sopenharmony_ci sci->num_cs = 1; 112362306a36Sopenharmony_ci } else { 112462306a36Sopenharmony_ci sci->num_cs = temp; 112562306a36Sopenharmony_ci } 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci sci->no_cs = of_property_read_bool(dev->of_node, "no-cs-readback"); 112862306a36Sopenharmony_ci sci->polling = !of_property_present(dev->of_node, "dmas"); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci return sci; 113162306a36Sopenharmony_ci} 113262306a36Sopenharmony_ci#else 113362306a36Sopenharmony_cistatic struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev) 113462306a36Sopenharmony_ci{ 113562306a36Sopenharmony_ci return dev_get_platdata(dev); 113662306a36Sopenharmony_ci} 113762306a36Sopenharmony_ci#endif 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_cistatic inline const struct s3c64xx_spi_port_config *s3c64xx_spi_get_port_config( 114062306a36Sopenharmony_ci struct platform_device *pdev) 114162306a36Sopenharmony_ci{ 114262306a36Sopenharmony_ci#ifdef CONFIG_OF 114362306a36Sopenharmony_ci if (pdev->dev.of_node) 114462306a36Sopenharmony_ci return of_device_get_match_data(&pdev->dev); 114562306a36Sopenharmony_ci#endif 114662306a36Sopenharmony_ci return (const struct s3c64xx_spi_port_config *)platform_get_device_id(pdev)->driver_data; 114762306a36Sopenharmony_ci} 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_cistatic int s3c64xx_spi_probe(struct platform_device *pdev) 115062306a36Sopenharmony_ci{ 115162306a36Sopenharmony_ci struct resource *mem_res; 115262306a36Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd; 115362306a36Sopenharmony_ci struct s3c64xx_spi_info *sci = dev_get_platdata(&pdev->dev); 115462306a36Sopenharmony_ci struct spi_controller *host; 115562306a36Sopenharmony_ci int ret, irq; 115662306a36Sopenharmony_ci char clk_name[16]; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci if (!sci && pdev->dev.of_node) { 115962306a36Sopenharmony_ci sci = s3c64xx_spi_parse_dt(&pdev->dev); 116062306a36Sopenharmony_ci if (IS_ERR(sci)) 116162306a36Sopenharmony_ci return PTR_ERR(sci); 116262306a36Sopenharmony_ci } 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci if (!sci) 116562306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, -ENODEV, 116662306a36Sopenharmony_ci "Platform_data missing!\n"); 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 116962306a36Sopenharmony_ci if (irq < 0) 117062306a36Sopenharmony_ci return irq; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci host = devm_spi_alloc_host(&pdev->dev, sizeof(*sdd)); 117362306a36Sopenharmony_ci if (!host) 117462306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, -ENOMEM, 117562306a36Sopenharmony_ci "Unable to allocate SPI Host\n"); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci platform_set_drvdata(pdev, host); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci sdd = spi_controller_get_devdata(host); 118062306a36Sopenharmony_ci sdd->port_conf = s3c64xx_spi_get_port_config(pdev); 118162306a36Sopenharmony_ci sdd->host = host; 118262306a36Sopenharmony_ci sdd->cntrlr_info = sci; 118362306a36Sopenharmony_ci sdd->pdev = pdev; 118462306a36Sopenharmony_ci if (pdev->dev.of_node) { 118562306a36Sopenharmony_ci ret = of_alias_get_id(pdev->dev.of_node, "spi"); 118662306a36Sopenharmony_ci if (ret < 0) 118762306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, ret, 118862306a36Sopenharmony_ci "Failed to get alias id\n"); 118962306a36Sopenharmony_ci sdd->port_id = ret; 119062306a36Sopenharmony_ci } else { 119162306a36Sopenharmony_ci sdd->port_id = pdev->id; 119262306a36Sopenharmony_ci } 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci sdd->cur_bpw = 8; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci sdd->tx_dma.direction = DMA_MEM_TO_DEV; 119762306a36Sopenharmony_ci sdd->rx_dma.direction = DMA_DEV_TO_MEM; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci host->dev.of_node = pdev->dev.of_node; 120062306a36Sopenharmony_ci host->bus_num = sdd->port_id; 120162306a36Sopenharmony_ci host->setup = s3c64xx_spi_setup; 120262306a36Sopenharmony_ci host->cleanup = s3c64xx_spi_cleanup; 120362306a36Sopenharmony_ci host->prepare_transfer_hardware = s3c64xx_spi_prepare_transfer; 120462306a36Sopenharmony_ci host->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer; 120562306a36Sopenharmony_ci host->prepare_message = s3c64xx_spi_prepare_message; 120662306a36Sopenharmony_ci host->transfer_one = s3c64xx_spi_transfer_one; 120762306a36Sopenharmony_ci host->max_transfer_size = s3c64xx_spi_max_transfer_size; 120862306a36Sopenharmony_ci host->num_chipselect = sci->num_cs; 120962306a36Sopenharmony_ci host->use_gpio_descriptors = true; 121062306a36Sopenharmony_ci host->dma_alignment = 8; 121162306a36Sopenharmony_ci host->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) | 121262306a36Sopenharmony_ci SPI_BPW_MASK(8); 121362306a36Sopenharmony_ci /* the spi->mode bits understood by this driver: */ 121462306a36Sopenharmony_ci host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; 121562306a36Sopenharmony_ci if (sdd->port_conf->has_loopback) 121662306a36Sopenharmony_ci host->mode_bits |= SPI_LOOP; 121762306a36Sopenharmony_ci host->auto_runtime_pm = true; 121862306a36Sopenharmony_ci if (!is_polling(sdd)) 121962306a36Sopenharmony_ci host->can_dma = s3c64xx_spi_can_dma; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci sdd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem_res); 122262306a36Sopenharmony_ci if (IS_ERR(sdd->regs)) 122362306a36Sopenharmony_ci return PTR_ERR(sdd->regs); 122462306a36Sopenharmony_ci sdd->sfr_start = mem_res->start; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci if (sci->cfg_gpio && sci->cfg_gpio()) 122762306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, -EBUSY, 122862306a36Sopenharmony_ci "Unable to config gpio\n"); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci /* Setup clocks */ 123162306a36Sopenharmony_ci sdd->clk = devm_clk_get_enabled(&pdev->dev, "spi"); 123262306a36Sopenharmony_ci if (IS_ERR(sdd->clk)) 123362306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, PTR_ERR(sdd->clk), 123462306a36Sopenharmony_ci "Unable to acquire clock 'spi'\n"); 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci sprintf(clk_name, "spi_busclk%d", sci->src_clk_nr); 123762306a36Sopenharmony_ci sdd->src_clk = devm_clk_get_enabled(&pdev->dev, clk_name); 123862306a36Sopenharmony_ci if (IS_ERR(sdd->src_clk)) 123962306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, PTR_ERR(sdd->src_clk), 124062306a36Sopenharmony_ci "Unable to acquire clock '%s'\n", 124162306a36Sopenharmony_ci clk_name); 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci if (sdd->port_conf->clk_ioclk) { 124462306a36Sopenharmony_ci sdd->ioclk = devm_clk_get_enabled(&pdev->dev, "spi_ioclk"); 124562306a36Sopenharmony_ci if (IS_ERR(sdd->ioclk)) 124662306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, PTR_ERR(sdd->ioclk), 124762306a36Sopenharmony_ci "Unable to acquire 'ioclk'\n"); 124862306a36Sopenharmony_ci } 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_TIMEOUT); 125162306a36Sopenharmony_ci pm_runtime_use_autosuspend(&pdev->dev); 125262306a36Sopenharmony_ci pm_runtime_set_active(&pdev->dev); 125362306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 125462306a36Sopenharmony_ci pm_runtime_get_sync(&pdev->dev); 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci /* Setup Deufult Mode */ 125762306a36Sopenharmony_ci s3c64xx_spi_hwinit(sdd); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci spin_lock_init(&sdd->lock); 126062306a36Sopenharmony_ci init_completion(&sdd->xfer_completion); 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, s3c64xx_spi_irq, 0, 126362306a36Sopenharmony_ci "spi-s3c64xx", sdd); 126462306a36Sopenharmony_ci if (ret != 0) { 126562306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to request IRQ %d: %d\n", 126662306a36Sopenharmony_ci irq, ret); 126762306a36Sopenharmony_ci goto err_pm_put; 126862306a36Sopenharmony_ci } 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci writel(S3C64XX_SPI_INT_RX_OVERRUN_EN | S3C64XX_SPI_INT_RX_UNDERRUN_EN | 127162306a36Sopenharmony_ci S3C64XX_SPI_INT_TX_OVERRUN_EN | S3C64XX_SPI_INT_TX_UNDERRUN_EN, 127262306a36Sopenharmony_ci sdd->regs + S3C64XX_SPI_INT_EN); 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci ret = devm_spi_register_controller(&pdev->dev, host); 127562306a36Sopenharmony_ci if (ret != 0) { 127662306a36Sopenharmony_ci dev_err(&pdev->dev, "cannot register SPI host: %d\n", ret); 127762306a36Sopenharmony_ci goto err_pm_put; 127862306a36Sopenharmony_ci } 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Targets attached\n", 128162306a36Sopenharmony_ci sdd->port_id, host->num_chipselect); 128262306a36Sopenharmony_ci dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tFIFO %dbytes\n", 128362306a36Sopenharmony_ci mem_res, (FIFO_LVL_MASK(sdd) >> 1) + 1); 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci pm_runtime_mark_last_busy(&pdev->dev); 128662306a36Sopenharmony_ci pm_runtime_put_autosuspend(&pdev->dev); 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci return 0; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_cierr_pm_put: 129162306a36Sopenharmony_ci pm_runtime_put_noidle(&pdev->dev); 129262306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 129362306a36Sopenharmony_ci pm_runtime_set_suspended(&pdev->dev); 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci return ret; 129662306a36Sopenharmony_ci} 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_cistatic void s3c64xx_spi_remove(struct platform_device *pdev) 129962306a36Sopenharmony_ci{ 130062306a36Sopenharmony_ci struct spi_controller *host = platform_get_drvdata(pdev); 130162306a36Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = spi_controller_get_devdata(host); 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci pm_runtime_get_sync(&pdev->dev); 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci writel(0, sdd->regs + S3C64XX_SPI_INT_EN); 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci if (!is_polling(sdd)) { 130862306a36Sopenharmony_ci dma_release_channel(sdd->rx_dma.ch); 130962306a36Sopenharmony_ci dma_release_channel(sdd->tx_dma.ch); 131062306a36Sopenharmony_ci } 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci pm_runtime_put_noidle(&pdev->dev); 131362306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 131462306a36Sopenharmony_ci pm_runtime_set_suspended(&pdev->dev); 131562306a36Sopenharmony_ci} 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 131862306a36Sopenharmony_cistatic int s3c64xx_spi_suspend(struct device *dev) 131962306a36Sopenharmony_ci{ 132062306a36Sopenharmony_ci struct spi_controller *host = dev_get_drvdata(dev); 132162306a36Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = spi_controller_get_devdata(host); 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci int ret = spi_controller_suspend(host); 132462306a36Sopenharmony_ci if (ret) 132562306a36Sopenharmony_ci return ret; 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci ret = pm_runtime_force_suspend(dev); 132862306a36Sopenharmony_ci if (ret < 0) 132962306a36Sopenharmony_ci return ret; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci sdd->cur_speed = 0; /* Output Clock is stopped */ 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci return 0; 133462306a36Sopenharmony_ci} 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_cistatic int s3c64xx_spi_resume(struct device *dev) 133762306a36Sopenharmony_ci{ 133862306a36Sopenharmony_ci struct spi_controller *host = dev_get_drvdata(dev); 133962306a36Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = spi_controller_get_devdata(host); 134062306a36Sopenharmony_ci struct s3c64xx_spi_info *sci = sdd->cntrlr_info; 134162306a36Sopenharmony_ci int ret; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci if (sci->cfg_gpio) 134462306a36Sopenharmony_ci sci->cfg_gpio(); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci ret = pm_runtime_force_resume(dev); 134762306a36Sopenharmony_ci if (ret < 0) 134862306a36Sopenharmony_ci return ret; 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci return spi_controller_resume(host); 135162306a36Sopenharmony_ci} 135262306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci#ifdef CONFIG_PM 135562306a36Sopenharmony_cistatic int s3c64xx_spi_runtime_suspend(struct device *dev) 135662306a36Sopenharmony_ci{ 135762306a36Sopenharmony_ci struct spi_controller *host = dev_get_drvdata(dev); 135862306a36Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = spi_controller_get_devdata(host); 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci clk_disable_unprepare(sdd->clk); 136162306a36Sopenharmony_ci clk_disable_unprepare(sdd->src_clk); 136262306a36Sopenharmony_ci clk_disable_unprepare(sdd->ioclk); 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci return 0; 136562306a36Sopenharmony_ci} 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_cistatic int s3c64xx_spi_runtime_resume(struct device *dev) 136862306a36Sopenharmony_ci{ 136962306a36Sopenharmony_ci struct spi_controller *host = dev_get_drvdata(dev); 137062306a36Sopenharmony_ci struct s3c64xx_spi_driver_data *sdd = spi_controller_get_devdata(host); 137162306a36Sopenharmony_ci int ret; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci if (sdd->port_conf->clk_ioclk) { 137462306a36Sopenharmony_ci ret = clk_prepare_enable(sdd->ioclk); 137562306a36Sopenharmony_ci if (ret != 0) 137662306a36Sopenharmony_ci return ret; 137762306a36Sopenharmony_ci } 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci ret = clk_prepare_enable(sdd->src_clk); 138062306a36Sopenharmony_ci if (ret != 0) 138162306a36Sopenharmony_ci goto err_disable_ioclk; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci ret = clk_prepare_enable(sdd->clk); 138462306a36Sopenharmony_ci if (ret != 0) 138562306a36Sopenharmony_ci goto err_disable_src_clk; 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci s3c64xx_spi_hwinit(sdd); 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci writel(S3C64XX_SPI_INT_RX_OVERRUN_EN | S3C64XX_SPI_INT_RX_UNDERRUN_EN | 139062306a36Sopenharmony_ci S3C64XX_SPI_INT_TX_OVERRUN_EN | S3C64XX_SPI_INT_TX_UNDERRUN_EN, 139162306a36Sopenharmony_ci sdd->regs + S3C64XX_SPI_INT_EN); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci return 0; 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_cierr_disable_src_clk: 139662306a36Sopenharmony_ci clk_disable_unprepare(sdd->src_clk); 139762306a36Sopenharmony_cierr_disable_ioclk: 139862306a36Sopenharmony_ci clk_disable_unprepare(sdd->ioclk); 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci return ret; 140162306a36Sopenharmony_ci} 140262306a36Sopenharmony_ci#endif /* CONFIG_PM */ 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_cistatic const struct dev_pm_ops s3c64xx_spi_pm = { 140562306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(s3c64xx_spi_suspend, s3c64xx_spi_resume) 140662306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(s3c64xx_spi_runtime_suspend, 140762306a36Sopenharmony_ci s3c64xx_spi_runtime_resume, NULL) 140862306a36Sopenharmony_ci}; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_cistatic const struct s3c64xx_spi_port_config s3c2443_spi_port_config = { 141162306a36Sopenharmony_ci .fifo_lvl_mask = { 0x7f }, 141262306a36Sopenharmony_ci .rx_lvl_offset = 13, 141362306a36Sopenharmony_ci .tx_st_done = 21, 141462306a36Sopenharmony_ci .clk_div = 2, 141562306a36Sopenharmony_ci .high_speed = true, 141662306a36Sopenharmony_ci}; 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_cistatic const struct s3c64xx_spi_port_config s3c6410_spi_port_config = { 141962306a36Sopenharmony_ci .fifo_lvl_mask = { 0x7f, 0x7F }, 142062306a36Sopenharmony_ci .rx_lvl_offset = 13, 142162306a36Sopenharmony_ci .tx_st_done = 21, 142262306a36Sopenharmony_ci .clk_div = 2, 142362306a36Sopenharmony_ci}; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_cistatic const struct s3c64xx_spi_port_config s5pv210_spi_port_config = { 142662306a36Sopenharmony_ci .fifo_lvl_mask = { 0x1ff, 0x7F }, 142762306a36Sopenharmony_ci .rx_lvl_offset = 15, 142862306a36Sopenharmony_ci .tx_st_done = 25, 142962306a36Sopenharmony_ci .clk_div = 2, 143062306a36Sopenharmony_ci .high_speed = true, 143162306a36Sopenharmony_ci}; 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_cistatic const struct s3c64xx_spi_port_config exynos4_spi_port_config = { 143462306a36Sopenharmony_ci .fifo_lvl_mask = { 0x1ff, 0x7F, 0x7F }, 143562306a36Sopenharmony_ci .rx_lvl_offset = 15, 143662306a36Sopenharmony_ci .tx_st_done = 25, 143762306a36Sopenharmony_ci .clk_div = 2, 143862306a36Sopenharmony_ci .high_speed = true, 143962306a36Sopenharmony_ci .clk_from_cmu = true, 144062306a36Sopenharmony_ci .quirks = S3C64XX_SPI_QUIRK_CS_AUTO, 144162306a36Sopenharmony_ci}; 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_cistatic const struct s3c64xx_spi_port_config exynos7_spi_port_config = { 144462306a36Sopenharmony_ci .fifo_lvl_mask = { 0x1ff, 0x7F, 0x7F, 0x7F, 0x7F, 0x1ff}, 144562306a36Sopenharmony_ci .rx_lvl_offset = 15, 144662306a36Sopenharmony_ci .tx_st_done = 25, 144762306a36Sopenharmony_ci .clk_div = 2, 144862306a36Sopenharmony_ci .high_speed = true, 144962306a36Sopenharmony_ci .clk_from_cmu = true, 145062306a36Sopenharmony_ci .quirks = S3C64XX_SPI_QUIRK_CS_AUTO, 145162306a36Sopenharmony_ci}; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_cistatic const struct s3c64xx_spi_port_config exynos5433_spi_port_config = { 145462306a36Sopenharmony_ci .fifo_lvl_mask = { 0x1ff, 0x7f, 0x7f, 0x7f, 0x7f, 0x1ff}, 145562306a36Sopenharmony_ci .rx_lvl_offset = 15, 145662306a36Sopenharmony_ci .tx_st_done = 25, 145762306a36Sopenharmony_ci .clk_div = 2, 145862306a36Sopenharmony_ci .high_speed = true, 145962306a36Sopenharmony_ci .clk_from_cmu = true, 146062306a36Sopenharmony_ci .clk_ioclk = true, 146162306a36Sopenharmony_ci .quirks = S3C64XX_SPI_QUIRK_CS_AUTO, 146262306a36Sopenharmony_ci}; 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_cistatic const struct s3c64xx_spi_port_config exynosautov9_spi_port_config = { 146562306a36Sopenharmony_ci .fifo_lvl_mask = { 0x1ff, 0x1ff, 0x7f, 0x7f, 0x7f, 0x7f, 0x1ff, 0x7f, 146662306a36Sopenharmony_ci 0x7f, 0x7f, 0x7f, 0x7f}, 146762306a36Sopenharmony_ci .rx_lvl_offset = 15, 146862306a36Sopenharmony_ci .tx_st_done = 25, 146962306a36Sopenharmony_ci .clk_div = 4, 147062306a36Sopenharmony_ci .high_speed = true, 147162306a36Sopenharmony_ci .clk_from_cmu = true, 147262306a36Sopenharmony_ci .clk_ioclk = true, 147362306a36Sopenharmony_ci .has_loopback = true, 147462306a36Sopenharmony_ci .quirks = S3C64XX_SPI_QUIRK_CS_AUTO, 147562306a36Sopenharmony_ci}; 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_cistatic const struct s3c64xx_spi_port_config fsd_spi_port_config = { 147862306a36Sopenharmony_ci .fifo_lvl_mask = { 0x7f, 0x7f, 0x7f, 0x7f, 0x7f}, 147962306a36Sopenharmony_ci .rx_lvl_offset = 15, 148062306a36Sopenharmony_ci .tx_st_done = 25, 148162306a36Sopenharmony_ci .clk_div = 2, 148262306a36Sopenharmony_ci .high_speed = true, 148362306a36Sopenharmony_ci .clk_from_cmu = true, 148462306a36Sopenharmony_ci .clk_ioclk = false, 148562306a36Sopenharmony_ci .quirks = S3C64XX_SPI_QUIRK_CS_AUTO, 148662306a36Sopenharmony_ci}; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_cistatic const struct platform_device_id s3c64xx_spi_driver_ids[] = { 148962306a36Sopenharmony_ci { 149062306a36Sopenharmony_ci .name = "s3c2443-spi", 149162306a36Sopenharmony_ci .driver_data = (kernel_ulong_t)&s3c2443_spi_port_config, 149262306a36Sopenharmony_ci }, { 149362306a36Sopenharmony_ci .name = "s3c6410-spi", 149462306a36Sopenharmony_ci .driver_data = (kernel_ulong_t)&s3c6410_spi_port_config, 149562306a36Sopenharmony_ci }, 149662306a36Sopenharmony_ci { }, 149762306a36Sopenharmony_ci}; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_cistatic const struct of_device_id s3c64xx_spi_dt_match[] = { 150062306a36Sopenharmony_ci { .compatible = "samsung,s3c2443-spi", 150162306a36Sopenharmony_ci .data = (void *)&s3c2443_spi_port_config, 150262306a36Sopenharmony_ci }, 150362306a36Sopenharmony_ci { .compatible = "samsung,s3c6410-spi", 150462306a36Sopenharmony_ci .data = (void *)&s3c6410_spi_port_config, 150562306a36Sopenharmony_ci }, 150662306a36Sopenharmony_ci { .compatible = "samsung,s5pv210-spi", 150762306a36Sopenharmony_ci .data = (void *)&s5pv210_spi_port_config, 150862306a36Sopenharmony_ci }, 150962306a36Sopenharmony_ci { .compatible = "samsung,exynos4210-spi", 151062306a36Sopenharmony_ci .data = (void *)&exynos4_spi_port_config, 151162306a36Sopenharmony_ci }, 151262306a36Sopenharmony_ci { .compatible = "samsung,exynos7-spi", 151362306a36Sopenharmony_ci .data = (void *)&exynos7_spi_port_config, 151462306a36Sopenharmony_ci }, 151562306a36Sopenharmony_ci { .compatible = "samsung,exynos5433-spi", 151662306a36Sopenharmony_ci .data = (void *)&exynos5433_spi_port_config, 151762306a36Sopenharmony_ci }, 151862306a36Sopenharmony_ci { .compatible = "samsung,exynosautov9-spi", 151962306a36Sopenharmony_ci .data = (void *)&exynosautov9_spi_port_config, 152062306a36Sopenharmony_ci }, 152162306a36Sopenharmony_ci { .compatible = "tesla,fsd-spi", 152262306a36Sopenharmony_ci .data = (void *)&fsd_spi_port_config, 152362306a36Sopenharmony_ci }, 152462306a36Sopenharmony_ci { }, 152562306a36Sopenharmony_ci}; 152662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, s3c64xx_spi_dt_match); 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_cistatic struct platform_driver s3c64xx_spi_driver = { 152962306a36Sopenharmony_ci .driver = { 153062306a36Sopenharmony_ci .name = "s3c64xx-spi", 153162306a36Sopenharmony_ci .pm = &s3c64xx_spi_pm, 153262306a36Sopenharmony_ci .of_match_table = of_match_ptr(s3c64xx_spi_dt_match), 153362306a36Sopenharmony_ci }, 153462306a36Sopenharmony_ci .probe = s3c64xx_spi_probe, 153562306a36Sopenharmony_ci .remove_new = s3c64xx_spi_remove, 153662306a36Sopenharmony_ci .id_table = s3c64xx_spi_driver_ids, 153762306a36Sopenharmony_ci}; 153862306a36Sopenharmony_ciMODULE_ALIAS("platform:s3c64xx-spi"); 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_cimodule_platform_driver(s3c64xx_spi_driver); 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ciMODULE_AUTHOR("Jaswinder Singh <jassi.brar@samsung.com>"); 154362306a36Sopenharmony_ciMODULE_DESCRIPTION("S3C64XX SPI Controller Driver"); 154462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1545