18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2006 Ben Dooks 48c2ecf20Sopenharmony_ci * Copyright 2006-2009 Simtec Electronics 58c2ecf20Sopenharmony_ci * Ben Dooks <ben@simtec.co.uk> 68c2ecf20Sopenharmony_ci*/ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 98c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/err.h> 138c2ecf20Sopenharmony_ci#include <linux/clk.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/gpio.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 208c2ecf20Sopenharmony_ci#include <linux/spi/spi_bitbang.h> 218c2ecf20Sopenharmony_ci#include <linux/spi/s3c24xx.h> 228c2ecf20Sopenharmony_ci#include <linux/spi/s3c24xx-fiq.h> 238c2ecf20Sopenharmony_ci#include <linux/module.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <asm/fiq.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include "spi-s3c24xx-regs.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/** 308c2ecf20Sopenharmony_ci * struct s3c24xx_spi_devstate - per device data 318c2ecf20Sopenharmony_ci * @hz: Last frequency calculated for @sppre field. 328c2ecf20Sopenharmony_ci * @mode: Last mode setting for the @spcon field. 338c2ecf20Sopenharmony_ci * @spcon: Value to write to the SPCON register. 348c2ecf20Sopenharmony_ci * @sppre: Value to write to the SPPRE register. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_cistruct s3c24xx_spi_devstate { 378c2ecf20Sopenharmony_ci unsigned int hz; 388c2ecf20Sopenharmony_ci unsigned int mode; 398c2ecf20Sopenharmony_ci u8 spcon; 408c2ecf20Sopenharmony_ci u8 sppre; 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cienum spi_fiq_mode { 448c2ecf20Sopenharmony_ci FIQ_MODE_NONE = 0, 458c2ecf20Sopenharmony_ci FIQ_MODE_TX = 1, 468c2ecf20Sopenharmony_ci FIQ_MODE_RX = 2, 478c2ecf20Sopenharmony_ci FIQ_MODE_TXRX = 3, 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct s3c24xx_spi { 518c2ecf20Sopenharmony_ci /* bitbang has to be first */ 528c2ecf20Sopenharmony_ci struct spi_bitbang bitbang; 538c2ecf20Sopenharmony_ci struct completion done; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci void __iomem *regs; 568c2ecf20Sopenharmony_ci int irq; 578c2ecf20Sopenharmony_ci int len; 588c2ecf20Sopenharmony_ci int count; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci struct fiq_handler fiq_handler; 618c2ecf20Sopenharmony_ci enum spi_fiq_mode fiq_mode; 628c2ecf20Sopenharmony_ci unsigned char fiq_inuse; 638c2ecf20Sopenharmony_ci unsigned char fiq_claimed; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci void (*set_cs)(struct s3c2410_spi_info *spi, 668c2ecf20Sopenharmony_ci int cs, int pol); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci /* data buffers */ 698c2ecf20Sopenharmony_ci const unsigned char *tx; 708c2ecf20Sopenharmony_ci unsigned char *rx; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci struct clk *clk; 738c2ecf20Sopenharmony_ci struct spi_master *master; 748c2ecf20Sopenharmony_ci struct spi_device *curdev; 758c2ecf20Sopenharmony_ci struct device *dev; 768c2ecf20Sopenharmony_ci struct s3c2410_spi_info *pdata; 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define SPCON_DEFAULT (S3C2410_SPCON_MSTR | S3C2410_SPCON_SMOD_INT) 808c2ecf20Sopenharmony_ci#define SPPIN_DEFAULT (S3C2410_SPPIN_KEEP) 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic inline struct s3c24xx_spi *to_hw(struct spi_device *sdev) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci return spi_master_get_devdata(sdev->master); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic void s3c24xx_spi_gpiocs(struct s3c2410_spi_info *spi, int cs, int pol) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci gpio_set_value(spi->pin_cs, pol); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic void s3c24xx_spi_chipsel(struct spi_device *spi, int value) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct s3c24xx_spi_devstate *cs = spi->controller_state; 958c2ecf20Sopenharmony_ci struct s3c24xx_spi *hw = to_hw(spi); 968c2ecf20Sopenharmony_ci unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* change the chipselect state and the state of the spi engine clock */ 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci switch (value) { 1018c2ecf20Sopenharmony_ci case BITBANG_CS_INACTIVE: 1028c2ecf20Sopenharmony_ci hw->set_cs(hw->pdata, spi->chip_select, cspol^1); 1038c2ecf20Sopenharmony_ci writeb(cs->spcon, hw->regs + S3C2410_SPCON); 1048c2ecf20Sopenharmony_ci break; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci case BITBANG_CS_ACTIVE: 1078c2ecf20Sopenharmony_ci writeb(cs->spcon | S3C2410_SPCON_ENSCK, 1088c2ecf20Sopenharmony_ci hw->regs + S3C2410_SPCON); 1098c2ecf20Sopenharmony_ci hw->set_cs(hw->pdata, spi->chip_select, cspol); 1108c2ecf20Sopenharmony_ci break; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int s3c24xx_spi_update_state(struct spi_device *spi, 1158c2ecf20Sopenharmony_ci struct spi_transfer *t) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct s3c24xx_spi *hw = to_hw(spi); 1188c2ecf20Sopenharmony_ci struct s3c24xx_spi_devstate *cs = spi->controller_state; 1198c2ecf20Sopenharmony_ci unsigned int hz; 1208c2ecf20Sopenharmony_ci unsigned int div; 1218c2ecf20Sopenharmony_ci unsigned long clk; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci hz = t ? t->speed_hz : spi->max_speed_hz; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (!hz) 1268c2ecf20Sopenharmony_ci hz = spi->max_speed_hz; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (spi->mode != cs->mode) { 1298c2ecf20Sopenharmony_ci u8 spcon = SPCON_DEFAULT | S3C2410_SPCON_ENSCK; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (spi->mode & SPI_CPHA) 1328c2ecf20Sopenharmony_ci spcon |= S3C2410_SPCON_CPHA_FMTB; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (spi->mode & SPI_CPOL) 1358c2ecf20Sopenharmony_ci spcon |= S3C2410_SPCON_CPOL_HIGH; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci cs->mode = spi->mode; 1388c2ecf20Sopenharmony_ci cs->spcon = spcon; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (cs->hz != hz) { 1428c2ecf20Sopenharmony_ci clk = clk_get_rate(hw->clk); 1438c2ecf20Sopenharmony_ci div = DIV_ROUND_UP(clk, hz * 2) - 1; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (div > 255) 1468c2ecf20Sopenharmony_ci div = 255; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci dev_dbg(&spi->dev, "pre-scaler=%d (wanted %d, got %ld)\n", 1498c2ecf20Sopenharmony_ci div, hz, clk / (2 * (div + 1))); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci cs->hz = hz; 1528c2ecf20Sopenharmony_ci cs->sppre = div; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int s3c24xx_spi_setupxfer(struct spi_device *spi, 1598c2ecf20Sopenharmony_ci struct spi_transfer *t) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct s3c24xx_spi_devstate *cs = spi->controller_state; 1628c2ecf20Sopenharmony_ci struct s3c24xx_spi *hw = to_hw(spi); 1638c2ecf20Sopenharmony_ci int ret; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci ret = s3c24xx_spi_update_state(spi, t); 1668c2ecf20Sopenharmony_ci if (!ret) 1678c2ecf20Sopenharmony_ci writeb(cs->sppre, hw->regs + S3C2410_SPPRE); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return ret; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int s3c24xx_spi_setup(struct spi_device *spi) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct s3c24xx_spi_devstate *cs = spi->controller_state; 1758c2ecf20Sopenharmony_ci struct s3c24xx_spi *hw = to_hw(spi); 1768c2ecf20Sopenharmony_ci int ret; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* allocate settings on the first call */ 1798c2ecf20Sopenharmony_ci if (!cs) { 1808c2ecf20Sopenharmony_ci cs = devm_kzalloc(&spi->dev, 1818c2ecf20Sopenharmony_ci sizeof(struct s3c24xx_spi_devstate), 1828c2ecf20Sopenharmony_ci GFP_KERNEL); 1838c2ecf20Sopenharmony_ci if (!cs) 1848c2ecf20Sopenharmony_ci return -ENOMEM; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci cs->spcon = SPCON_DEFAULT; 1878c2ecf20Sopenharmony_ci cs->hz = -1; 1888c2ecf20Sopenharmony_ci spi->controller_state = cs; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* initialise the state from the device */ 1928c2ecf20Sopenharmony_ci ret = s3c24xx_spi_update_state(spi, NULL); 1938c2ecf20Sopenharmony_ci if (ret) 1948c2ecf20Sopenharmony_ci return ret; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci mutex_lock(&hw->bitbang.lock); 1978c2ecf20Sopenharmony_ci if (!hw->bitbang.busy) { 1988c2ecf20Sopenharmony_ci hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE); 1998c2ecf20Sopenharmony_ci /* need to ndelay for 0.5 clocktick ? */ 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci mutex_unlock(&hw->bitbang.lock); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci return 0; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci return hw->tx ? hw->tx[count] : 0; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci#ifdef CONFIG_SPI_S3C24XX_FIQ 2128c2ecf20Sopenharmony_ci/* Support for FIQ based pseudo-DMA to improve the transfer speed. 2138c2ecf20Sopenharmony_ci * 2148c2ecf20Sopenharmony_ci * This code uses the assembly helper in spi_s3c24xx_spi.S which is 2158c2ecf20Sopenharmony_ci * used by the FIQ core to move data between main memory and the peripheral 2168c2ecf20Sopenharmony_ci * block. Since this is code running on the processor, there is no problem 2178c2ecf20Sopenharmony_ci * with cache coherency of the buffers, so we can use any buffer we like. 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci/** 2218c2ecf20Sopenharmony_ci * struct spi_fiq_code - FIQ code and header 2228c2ecf20Sopenharmony_ci * @length: The length of the code fragment, excluding this header. 2238c2ecf20Sopenharmony_ci * @ack_offset: The offset from @data to the word to place the IRQ ACK bit at. 2248c2ecf20Sopenharmony_ci * @data: The code itself to install as a FIQ handler. 2258c2ecf20Sopenharmony_ci */ 2268c2ecf20Sopenharmony_cistruct spi_fiq_code { 2278c2ecf20Sopenharmony_ci u32 length; 2288c2ecf20Sopenharmony_ci u32 ack_offset; 2298c2ecf20Sopenharmony_ci u8 data[]; 2308c2ecf20Sopenharmony_ci}; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci/** 2338c2ecf20Sopenharmony_ci * s3c24xx_spi_tryfiq - attempt to claim and setup FIQ for transfer 2348c2ecf20Sopenharmony_ci * @hw: The hardware state. 2358c2ecf20Sopenharmony_ci * 2368c2ecf20Sopenharmony_ci * Claim the FIQ handler (only one can be active at any one time) and 2378c2ecf20Sopenharmony_ci * then setup the correct transfer code for this transfer. 2388c2ecf20Sopenharmony_ci * 2398c2ecf20Sopenharmony_ci * This call updates all the necessary state information if successful, 2408c2ecf20Sopenharmony_ci * so the caller does not need to do anything more than start the transfer 2418c2ecf20Sopenharmony_ci * as normal, since the IRQ will have been re-routed to the FIQ handler. 2428c2ecf20Sopenharmony_ci*/ 2438c2ecf20Sopenharmony_cistatic void s3c24xx_spi_tryfiq(struct s3c24xx_spi *hw) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci struct pt_regs regs; 2468c2ecf20Sopenharmony_ci enum spi_fiq_mode mode; 2478c2ecf20Sopenharmony_ci struct spi_fiq_code *code; 2488c2ecf20Sopenharmony_ci u32 *ack_ptr = NULL; 2498c2ecf20Sopenharmony_ci int ret; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (!hw->fiq_claimed) { 2528c2ecf20Sopenharmony_ci /* try and claim fiq if we haven't got it, and if not 2538c2ecf20Sopenharmony_ci * then return and simply use another transfer method */ 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci ret = claim_fiq(&hw->fiq_handler); 2568c2ecf20Sopenharmony_ci if (ret) 2578c2ecf20Sopenharmony_ci return; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (hw->tx && !hw->rx) 2618c2ecf20Sopenharmony_ci mode = FIQ_MODE_TX; 2628c2ecf20Sopenharmony_ci else if (hw->rx && !hw->tx) 2638c2ecf20Sopenharmony_ci mode = FIQ_MODE_RX; 2648c2ecf20Sopenharmony_ci else 2658c2ecf20Sopenharmony_ci mode = FIQ_MODE_TXRX; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci regs.uregs[fiq_rspi] = (long)hw->regs; 2688c2ecf20Sopenharmony_ci regs.uregs[fiq_rrx] = (long)hw->rx; 2698c2ecf20Sopenharmony_ci regs.uregs[fiq_rtx] = (long)hw->tx + 1; 2708c2ecf20Sopenharmony_ci regs.uregs[fiq_rcount] = hw->len - 1; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci set_fiq_regs(®s); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (hw->fiq_mode != mode) { 2758c2ecf20Sopenharmony_ci hw->fiq_mode = mode; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci switch (mode) { 2788c2ecf20Sopenharmony_ci case FIQ_MODE_TX: 2798c2ecf20Sopenharmony_ci code = &s3c24xx_spi_fiq_tx; 2808c2ecf20Sopenharmony_ci break; 2818c2ecf20Sopenharmony_ci case FIQ_MODE_RX: 2828c2ecf20Sopenharmony_ci code = &s3c24xx_spi_fiq_rx; 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci case FIQ_MODE_TXRX: 2858c2ecf20Sopenharmony_ci code = &s3c24xx_spi_fiq_txrx; 2868c2ecf20Sopenharmony_ci break; 2878c2ecf20Sopenharmony_ci default: 2888c2ecf20Sopenharmony_ci code = NULL; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci BUG_ON(!code); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci ack_ptr = (u32 *)&code->data[code->ack_offset]; 2948c2ecf20Sopenharmony_ci set_fiq_handler(&code->data, code->length); 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci s3c24xx_set_fiq(hw->irq, ack_ptr, true); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci hw->fiq_mode = mode; 3008c2ecf20Sopenharmony_ci hw->fiq_inuse = 1; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci/** 3048c2ecf20Sopenharmony_ci * s3c24xx_spi_fiqop - FIQ core code callback 3058c2ecf20Sopenharmony_ci * @pw: Data registered with the handler 3068c2ecf20Sopenharmony_ci * @release: Whether this is a release or a return. 3078c2ecf20Sopenharmony_ci * 3088c2ecf20Sopenharmony_ci * Called by the FIQ code when another module wants to use the FIQ, so 3098c2ecf20Sopenharmony_ci * return whether we are currently using this or not and then update our 3108c2ecf20Sopenharmony_ci * internal state. 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_cistatic int s3c24xx_spi_fiqop(void *pw, int release) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci struct s3c24xx_spi *hw = pw; 3158c2ecf20Sopenharmony_ci int ret = 0; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (release) { 3188c2ecf20Sopenharmony_ci if (hw->fiq_inuse) 3198c2ecf20Sopenharmony_ci ret = -EBUSY; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* note, we do not need to unroute the FIQ, as the FIQ 3228c2ecf20Sopenharmony_ci * vector code de-routes it to signal the end of transfer */ 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci hw->fiq_mode = FIQ_MODE_NONE; 3258c2ecf20Sopenharmony_ci hw->fiq_claimed = 0; 3268c2ecf20Sopenharmony_ci } else { 3278c2ecf20Sopenharmony_ci hw->fiq_claimed = 1; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci return ret; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci/** 3348c2ecf20Sopenharmony_ci * s3c24xx_spi_initfiq - setup the information for the FIQ core 3358c2ecf20Sopenharmony_ci * @hw: The hardware state. 3368c2ecf20Sopenharmony_ci * 3378c2ecf20Sopenharmony_ci * Setup the fiq_handler block to pass to the FIQ core. 3388c2ecf20Sopenharmony_ci */ 3398c2ecf20Sopenharmony_cistatic inline void s3c24xx_spi_initfiq(struct s3c24xx_spi *hw) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci hw->fiq_handler.dev_id = hw; 3428c2ecf20Sopenharmony_ci hw->fiq_handler.name = dev_name(hw->dev); 3438c2ecf20Sopenharmony_ci hw->fiq_handler.fiq_op = s3c24xx_spi_fiqop; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci/** 3478c2ecf20Sopenharmony_ci * s3c24xx_spi_usefiq - return if we should be using FIQ. 3488c2ecf20Sopenharmony_ci * @hw: The hardware state. 3498c2ecf20Sopenharmony_ci * 3508c2ecf20Sopenharmony_ci * Return true if the platform data specifies whether this channel is 3518c2ecf20Sopenharmony_ci * allowed to use the FIQ. 3528c2ecf20Sopenharmony_ci */ 3538c2ecf20Sopenharmony_cistatic inline bool s3c24xx_spi_usefiq(struct s3c24xx_spi *hw) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci return hw->pdata->use_fiq; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci/** 3598c2ecf20Sopenharmony_ci * s3c24xx_spi_usingfiq - return if channel is using FIQ 3608c2ecf20Sopenharmony_ci * @spi: The hardware state. 3618c2ecf20Sopenharmony_ci * 3628c2ecf20Sopenharmony_ci * Return whether the channel is currently using the FIQ (separate from 3638c2ecf20Sopenharmony_ci * whether the FIQ is claimed). 3648c2ecf20Sopenharmony_ci */ 3658c2ecf20Sopenharmony_cistatic inline bool s3c24xx_spi_usingfiq(struct s3c24xx_spi *spi) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci return spi->fiq_inuse; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci#else 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic inline void s3c24xx_spi_initfiq(struct s3c24xx_spi *s) { } 3728c2ecf20Sopenharmony_cistatic inline void s3c24xx_spi_tryfiq(struct s3c24xx_spi *s) { } 3738c2ecf20Sopenharmony_cistatic inline bool s3c24xx_spi_usefiq(struct s3c24xx_spi *s) { return false; } 3748c2ecf20Sopenharmony_cistatic inline bool s3c24xx_spi_usingfiq(struct s3c24xx_spi *s) { return false; } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci#endif /* CONFIG_SPI_S3C24XX_FIQ */ 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci struct s3c24xx_spi *hw = to_hw(spi); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci hw->tx = t->tx_buf; 3838c2ecf20Sopenharmony_ci hw->rx = t->rx_buf; 3848c2ecf20Sopenharmony_ci hw->len = t->len; 3858c2ecf20Sopenharmony_ci hw->count = 0; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci init_completion(&hw->done); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci hw->fiq_inuse = 0; 3908c2ecf20Sopenharmony_ci if (s3c24xx_spi_usefiq(hw) && t->len >= 3) 3918c2ecf20Sopenharmony_ci s3c24xx_spi_tryfiq(hw); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* send the first byte */ 3948c2ecf20Sopenharmony_ci writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci wait_for_completion(&hw->done); 3978c2ecf20Sopenharmony_ci return hw->count; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic irqreturn_t s3c24xx_spi_irq(int irq, void *dev) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct s3c24xx_spi *hw = dev; 4038c2ecf20Sopenharmony_ci unsigned int spsta = readb(hw->regs + S3C2410_SPSTA); 4048c2ecf20Sopenharmony_ci unsigned int count = hw->count; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (spsta & S3C2410_SPSTA_DCOL) { 4078c2ecf20Sopenharmony_ci dev_dbg(hw->dev, "data-collision\n"); 4088c2ecf20Sopenharmony_ci complete(&hw->done); 4098c2ecf20Sopenharmony_ci goto irq_done; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (!(spsta & S3C2410_SPSTA_READY)) { 4138c2ecf20Sopenharmony_ci dev_dbg(hw->dev, "spi not ready for tx?\n"); 4148c2ecf20Sopenharmony_ci complete(&hw->done); 4158c2ecf20Sopenharmony_ci goto irq_done; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (!s3c24xx_spi_usingfiq(hw)) { 4198c2ecf20Sopenharmony_ci hw->count++; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (hw->rx) 4228c2ecf20Sopenharmony_ci hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci count++; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (count < hw->len) 4278c2ecf20Sopenharmony_ci writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT); 4288c2ecf20Sopenharmony_ci else 4298c2ecf20Sopenharmony_ci complete(&hw->done); 4308c2ecf20Sopenharmony_ci } else { 4318c2ecf20Sopenharmony_ci hw->count = hw->len; 4328c2ecf20Sopenharmony_ci hw->fiq_inuse = 0; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (hw->rx) 4358c2ecf20Sopenharmony_ci hw->rx[hw->len-1] = readb(hw->regs + S3C2410_SPRDAT); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci complete(&hw->done); 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci irq_done: 4418c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic void s3c24xx_spi_initialsetup(struct s3c24xx_spi *hw) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci /* for the moment, permanently enable the clock */ 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci clk_enable(hw->clk); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* program defaults into the registers */ 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci writeb(0xff, hw->regs + S3C2410_SPPRE); 4538c2ecf20Sopenharmony_ci writeb(SPPIN_DEFAULT, hw->regs + S3C2410_SPPIN); 4548c2ecf20Sopenharmony_ci writeb(SPCON_DEFAULT, hw->regs + S3C2410_SPCON); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (hw->pdata) { 4578c2ecf20Sopenharmony_ci if (hw->set_cs == s3c24xx_spi_gpiocs) 4588c2ecf20Sopenharmony_ci gpio_direction_output(hw->pdata->pin_cs, 1); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (hw->pdata->gpio_setup) 4618c2ecf20Sopenharmony_ci hw->pdata->gpio_setup(hw->pdata, 1); 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic int s3c24xx_spi_probe(struct platform_device *pdev) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci struct s3c2410_spi_info *pdata; 4688c2ecf20Sopenharmony_ci struct s3c24xx_spi *hw; 4698c2ecf20Sopenharmony_ci struct spi_master *master; 4708c2ecf20Sopenharmony_ci int err = 0; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi)); 4738c2ecf20Sopenharmony_ci if (master == NULL) { 4748c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No memory for spi_master\n"); 4758c2ecf20Sopenharmony_ci return -ENOMEM; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci hw = spi_master_get_devdata(master); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci hw->master = master; 4818c2ecf20Sopenharmony_ci hw->pdata = pdata = dev_get_platdata(&pdev->dev); 4828c2ecf20Sopenharmony_ci hw->dev = &pdev->dev; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (pdata == NULL) { 4858c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No platform data supplied\n"); 4868c2ecf20Sopenharmony_ci err = -ENOENT; 4878c2ecf20Sopenharmony_ci goto err_no_pdata; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, hw); 4918c2ecf20Sopenharmony_ci init_completion(&hw->done); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* initialise fiq handler */ 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci s3c24xx_spi_initfiq(hw); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci /* setup the master state. */ 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci /* the spi->mode bits understood by this driver: */ 5008c2ecf20Sopenharmony_ci master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci master->num_chipselect = hw->pdata->num_cs; 5038c2ecf20Sopenharmony_ci master->bus_num = pdata->bus_num; 5048c2ecf20Sopenharmony_ci master->bits_per_word_mask = SPI_BPW_MASK(8); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* setup the state for the bitbang driver */ 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci hw->bitbang.master = hw->master; 5098c2ecf20Sopenharmony_ci hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer; 5108c2ecf20Sopenharmony_ci hw->bitbang.chipselect = s3c24xx_spi_chipsel; 5118c2ecf20Sopenharmony_ci hw->bitbang.txrx_bufs = s3c24xx_spi_txrx; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci hw->master->setup = s3c24xx_spi_setup; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* find and map our resources */ 5188c2ecf20Sopenharmony_ci hw->regs = devm_platform_ioremap_resource(pdev, 0); 5198c2ecf20Sopenharmony_ci if (IS_ERR(hw->regs)) { 5208c2ecf20Sopenharmony_ci err = PTR_ERR(hw->regs); 5218c2ecf20Sopenharmony_ci goto err_no_pdata; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci hw->irq = platform_get_irq(pdev, 0); 5258c2ecf20Sopenharmony_ci if (hw->irq < 0) { 5268c2ecf20Sopenharmony_ci err = -ENOENT; 5278c2ecf20Sopenharmony_ci goto err_no_pdata; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci err = devm_request_irq(&pdev->dev, hw->irq, s3c24xx_spi_irq, 0, 5318c2ecf20Sopenharmony_ci pdev->name, hw); 5328c2ecf20Sopenharmony_ci if (err) { 5338c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot claim IRQ\n"); 5348c2ecf20Sopenharmony_ci goto err_no_pdata; 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci hw->clk = devm_clk_get(&pdev->dev, "spi"); 5388c2ecf20Sopenharmony_ci if (IS_ERR(hw->clk)) { 5398c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No clock for device\n"); 5408c2ecf20Sopenharmony_ci err = PTR_ERR(hw->clk); 5418c2ecf20Sopenharmony_ci goto err_no_pdata; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci /* setup any gpio we can */ 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (!pdata->set_cs) { 5478c2ecf20Sopenharmony_ci if (pdata->pin_cs < 0) { 5488c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No chipselect pin\n"); 5498c2ecf20Sopenharmony_ci err = -EINVAL; 5508c2ecf20Sopenharmony_ci goto err_register; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci err = devm_gpio_request(&pdev->dev, pdata->pin_cs, 5548c2ecf20Sopenharmony_ci dev_name(&pdev->dev)); 5558c2ecf20Sopenharmony_ci if (err) { 5568c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to get gpio for cs\n"); 5578c2ecf20Sopenharmony_ci goto err_register; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci hw->set_cs = s3c24xx_spi_gpiocs; 5618c2ecf20Sopenharmony_ci gpio_direction_output(pdata->pin_cs, 1); 5628c2ecf20Sopenharmony_ci } else 5638c2ecf20Sopenharmony_ci hw->set_cs = pdata->set_cs; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci s3c24xx_spi_initialsetup(hw); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* register our spi controller */ 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci err = spi_bitbang_start(&hw->bitbang); 5708c2ecf20Sopenharmony_ci if (err) { 5718c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to register SPI master\n"); 5728c2ecf20Sopenharmony_ci goto err_register; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci return 0; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci err_register: 5788c2ecf20Sopenharmony_ci clk_disable(hw->clk); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci err_no_pdata: 5818c2ecf20Sopenharmony_ci spi_master_put(hw->master); 5828c2ecf20Sopenharmony_ci return err; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic int s3c24xx_spi_remove(struct platform_device *dev) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci struct s3c24xx_spi *hw = platform_get_drvdata(dev); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci spi_bitbang_stop(&hw->bitbang); 5908c2ecf20Sopenharmony_ci clk_disable(hw->clk); 5918c2ecf20Sopenharmony_ci spi_master_put(hw->master); 5928c2ecf20Sopenharmony_ci return 0; 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic int s3c24xx_spi_suspend(struct device *dev) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct s3c24xx_spi *hw = dev_get_drvdata(dev); 6018c2ecf20Sopenharmony_ci int ret; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci ret = spi_master_suspend(hw->master); 6048c2ecf20Sopenharmony_ci if (ret) 6058c2ecf20Sopenharmony_ci return ret; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci if (hw->pdata && hw->pdata->gpio_setup) 6088c2ecf20Sopenharmony_ci hw->pdata->gpio_setup(hw->pdata, 0); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci clk_disable(hw->clk); 6118c2ecf20Sopenharmony_ci return 0; 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic int s3c24xx_spi_resume(struct device *dev) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci struct s3c24xx_spi *hw = dev_get_drvdata(dev); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci s3c24xx_spi_initialsetup(hw); 6198c2ecf20Sopenharmony_ci return spi_master_resume(hw->master); 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic const struct dev_pm_ops s3c24xx_spi_pmops = { 6238c2ecf20Sopenharmony_ci .suspend = s3c24xx_spi_suspend, 6248c2ecf20Sopenharmony_ci .resume = s3c24xx_spi_resume, 6258c2ecf20Sopenharmony_ci}; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci#define S3C24XX_SPI_PMOPS &s3c24xx_spi_pmops 6288c2ecf20Sopenharmony_ci#else 6298c2ecf20Sopenharmony_ci#define S3C24XX_SPI_PMOPS NULL 6308c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:s3c2410-spi"); 6338c2ecf20Sopenharmony_cistatic struct platform_driver s3c24xx_spi_driver = { 6348c2ecf20Sopenharmony_ci .probe = s3c24xx_spi_probe, 6358c2ecf20Sopenharmony_ci .remove = s3c24xx_spi_remove, 6368c2ecf20Sopenharmony_ci .driver = { 6378c2ecf20Sopenharmony_ci .name = "s3c2410-spi", 6388c2ecf20Sopenharmony_ci .pm = S3C24XX_SPI_PMOPS, 6398c2ecf20Sopenharmony_ci }, 6408c2ecf20Sopenharmony_ci}; 6418c2ecf20Sopenharmony_cimodule_platform_driver(s3c24xx_spi_driver); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("S3C24XX SPI Driver"); 6448c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); 6458c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 646