Blame view
drivers/spi/spi-s3c64xx.c
39.4 KB
78b5d705b spi: s3c64xx: add... |
1 2 3 4 |
// SPDX-License-Identifier: GPL-2.0+ // // Copyright (c) 2009 Samsung Electronics Co., Ltd. // Jaswinder Singh <jassi.brar@samsung.com> |
230d42d42 spi: Add s3c64xx ... |
5 6 7 |
#include <linux/init.h> #include <linux/module.h> |
c2573128a spi/s3c64xx: Log ... |
8 |
#include <linux/interrupt.h> |
230d42d42 spi: Add s3c64xx ... |
9 10 11 |
#include <linux/delay.h> #include <linux/clk.h> #include <linux/dma-mapping.h> |
788437273 spi: s3c64xx: mov... |
12 |
#include <linux/dmaengine.h> |
230d42d42 spi: Add s3c64xx ... |
13 |
#include <linux/platform_device.h> |
b97b66217 spi/s3c64xx: Impl... |
14 |
#include <linux/pm_runtime.h> |
230d42d42 spi: Add s3c64xx ... |
15 |
#include <linux/spi/spi.h> |
1c20c200e spi: s3c64xx: Rem... |
16 |
#include <linux/gpio.h> |
2b9080754 spi: s3c64xx: add... |
17 18 |
#include <linux/of.h> #include <linux/of_gpio.h> |
230d42d42 spi: Add s3c64xx ... |
19 |
|
436d42c61 ARM: samsung: mov... |
20 |
#include <linux/platform_data/spi-s3c64xx.h> |
230d42d42 spi: Add s3c64xx ... |
21 |
|
bf77cba95 spi: s3c64xx: add... |
22 |
#define MAX_SPI_PORTS 6 |
7e9955567 spi: s3c64xx: add... |
23 |
#define S3C64XX_SPI_QUIRK_POLL (1 << 0) |
bf77cba95 spi: s3c64xx: add... |
24 |
#define S3C64XX_SPI_QUIRK_CS_AUTO (1 << 1) |
483867ee2 spi: s3c64xx: ext... |
25 |
#define AUTOSUSPEND_TIMEOUT 2000 |
a5238e360 spi: s3c64xx: mov... |
26 |
|
230d42d42 spi: Add s3c64xx ... |
27 28 29 30 |
/* Registers and bit-fields */ #define S3C64XX_SPI_CH_CFG 0x00 #define S3C64XX_SPI_CLK_CFG 0x04 |
bfbd0ea85 spi: spi-s3c64xx:... |
31 |
#define S3C64XX_SPI_MODE_CFG 0x08 |
913ba5c9e spi: spi-s3c64xx:... |
32 |
#define S3C64XX_SPI_CS_REG 0x0C |
230d42d42 spi: Add s3c64xx ... |
33 34 35 36 |
#define S3C64XX_SPI_INT_EN 0x10 #define S3C64XX_SPI_STATUS 0x14 #define S3C64XX_SPI_TX_DATA 0x18 #define S3C64XX_SPI_RX_DATA 0x1C |
bfbd0ea85 spi: spi-s3c64xx:... |
37 38 39 |
#define S3C64XX_SPI_PACKET_CNT 0x20 #define S3C64XX_SPI_PENDING_CLR 0x24 #define S3C64XX_SPI_SWAP_CFG 0x28 |
230d42d42 spi: Add s3c64xx ... |
40 41 42 43 44 45 46 47 48 49 50 51 52 |
#define S3C64XX_SPI_FB_CLK 0x2C #define S3C64XX_SPI_CH_HS_EN (1<<6) /* High Speed Enable */ #define S3C64XX_SPI_CH_SW_RST (1<<5) #define S3C64XX_SPI_CH_SLAVE (1<<4) #define S3C64XX_SPI_CPOL_L (1<<3) #define S3C64XX_SPI_CPHA_B (1<<2) #define S3C64XX_SPI_CH_RXCH_ON (1<<1) #define S3C64XX_SPI_CH_TXCH_ON (1<<0) #define S3C64XX_SPI_CLKSEL_SRCMSK (3<<9) #define S3C64XX_SPI_CLKSEL_SRCSHFT 9 #define S3C64XX_SPI_ENCLK_ENABLE (1<<8) |
75bf33611 spi/s3c64xx: fix ... |
53 |
#define S3C64XX_SPI_PSR_MASK 0xff |
230d42d42 spi: Add s3c64xx ... |
54 55 56 57 58 59 60 61 62 63 64 65 |
#define S3C64XX_SPI_MODE_CH_TSZ_BYTE (0<<29) #define S3C64XX_SPI_MODE_CH_TSZ_HALFWORD (1<<29) #define S3C64XX_SPI_MODE_CH_TSZ_WORD (2<<29) #define S3C64XX_SPI_MODE_CH_TSZ_MASK (3<<29) #define S3C64XX_SPI_MODE_BUS_TSZ_BYTE (0<<17) #define S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD (1<<17) #define S3C64XX_SPI_MODE_BUS_TSZ_WORD (2<<17) #define S3C64XX_SPI_MODE_BUS_TSZ_MASK (3<<17) #define S3C64XX_SPI_MODE_RXDMA_ON (1<<2) #define S3C64XX_SPI_MODE_TXDMA_ON (1<<1) #define S3C64XX_SPI_MODE_4BURST (1<<0) |
913ba5c9e spi: spi-s3c64xx:... |
66 67 68 |
#define S3C64XX_SPI_CS_NSC_CNT_2 (2<<4) #define S3C64XX_SPI_CS_AUTO (1<<1) #define S3C64XX_SPI_CS_SIG_INACT (1<<0) |
230d42d42 spi: Add s3c64xx ... |
69 |
|
230d42d42 spi: Add s3c64xx ... |
70 71 72 73 74 75 76 77 78 |
#define S3C64XX_SPI_INT_TRAILING_EN (1<<6) #define S3C64XX_SPI_INT_RX_OVERRUN_EN (1<<5) #define S3C64XX_SPI_INT_RX_UNDERRUN_EN (1<<4) #define S3C64XX_SPI_INT_TX_OVERRUN_EN (1<<3) #define S3C64XX_SPI_INT_TX_UNDERRUN_EN (1<<2) #define S3C64XX_SPI_INT_RX_FIFORDY_EN (1<<1) #define S3C64XX_SPI_INT_TX_FIFORDY_EN (1<<0) #define S3C64XX_SPI_ST_RX_OVERRUN_ERR (1<<5) |
bfbd0ea85 spi: spi-s3c64xx:... |
79 |
#define S3C64XX_SPI_ST_RX_UNDERRUN_ERR (1<<4) |
230d42d42 spi: Add s3c64xx ... |
80 |
#define S3C64XX_SPI_ST_TX_OVERRUN_ERR (1<<3) |
bfbd0ea85 spi: spi-s3c64xx:... |
81 |
#define S3C64XX_SPI_ST_TX_UNDERRUN_ERR (1<<2) |
230d42d42 spi: Add s3c64xx ... |
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
#define S3C64XX_SPI_ST_RX_FIFORDY (1<<1) #define S3C64XX_SPI_ST_TX_FIFORDY (1<<0) #define S3C64XX_SPI_PACKET_CNT_EN (1<<16) #define S3C64XX_SPI_PND_TX_UNDERRUN_CLR (1<<4) #define S3C64XX_SPI_PND_TX_OVERRUN_CLR (1<<3) #define S3C64XX_SPI_PND_RX_UNDERRUN_CLR (1<<2) #define S3C64XX_SPI_PND_RX_OVERRUN_CLR (1<<1) #define S3C64XX_SPI_PND_TRAILING_CLR (1<<0) #define S3C64XX_SPI_SWAP_RX_HALF_WORD (1<<7) #define S3C64XX_SPI_SWAP_RX_BYTE (1<<6) #define S3C64XX_SPI_SWAP_RX_BIT (1<<5) #define S3C64XX_SPI_SWAP_RX_EN (1<<4) #define S3C64XX_SPI_SWAP_TX_HALF_WORD (1<<3) #define S3C64XX_SPI_SWAP_TX_BYTE (1<<2) #define S3C64XX_SPI_SWAP_TX_BIT (1<<1) #define S3C64XX_SPI_SWAP_TX_EN (1<<0) |
bfbd0ea85 spi: spi-s3c64xx:... |
101 |
#define S3C64XX_SPI_FBCLK_MSK (3<<0) |
230d42d42 spi: Add s3c64xx ... |
102 |
|
a5238e360 spi: s3c64xx: mov... |
103 104 105 106 107 108 |
#define FIFO_LVL_MASK(i) ((i)->port_conf->fifo_lvl_mask[i->port_id]) #define S3C64XX_SPI_ST_TX_DONE(v, i) (((v) & \ (1 << (i)->port_conf->tx_st_done)) ? 1 : 0) #define TX_FIFO_LVL(v, i) (((v) >> 6) & FIFO_LVL_MASK(i)) #define RX_FIFO_LVL(v, i) (((v) >> (i)->port_conf->rx_lvl_offset) & \ FIFO_LVL_MASK(i)) |
230d42d42 spi: Add s3c64xx ... |
109 110 111 112 113 114 115 |
#define S3C64XX_SPI_MAX_TRAILCNT 0x3ff #define S3C64XX_SPI_TRAILCNT_OFF 19 #define S3C64XX_SPI_TRAILCNT S3C64XX_SPI_MAX_TRAILCNT #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) |
7e9955567 spi: s3c64xx: add... |
116 |
#define is_polling(x) (x->port_conf->quirks & S3C64XX_SPI_QUIRK_POLL) |
230d42d42 spi: Add s3c64xx ... |
117 |
|
230d42d42 spi: Add s3c64xx ... |
118 119 |
#define RXBUSY (1<<2) #define TXBUSY (1<<3) |
82ab8cd7e spi/s3c64xx: Merg... |
120 |
struct s3c64xx_spi_dma_data { |
788437273 spi: s3c64xx: mov... |
121 |
struct dma_chan *ch; |
2f4db6f70 spi: spi-s3c64xx:... |
122 |
dma_cookie_t cookie; |
c10356b98 spi/s3c64xx: use ... |
123 |
enum dma_transfer_direction direction; |
82ab8cd7e spi/s3c64xx: Merg... |
124 |
}; |
230d42d42 spi: Add s3c64xx ... |
125 |
/** |
a5238e360 spi: s3c64xx: mov... |
126 127 128 129 |
* struct s3c64xx_spi_info - SPI Controller hardware info * @fifo_lvl_mask: Bit-mask for {TX|RX}_FIFO_LVL bits in SPI_STATUS register. * @rx_lvl_offset: Bit offset of RX_FIFO_LVL bits in SPI_STATUS regiter. * @tx_st_done: Bit offset of TX_DONE bit in SPI_STATUS regiter. |
6b8d1e473 spi: spi-s3c64xx:... |
130 |
* @quirks: Bitmask of known quirks |
a5238e360 spi: s3c64xx: mov... |
131 132 133 |
* @high_speed: True, if the controller supports HIGH_SPEED_EN bit. * @clk_from_cmu: True, if the controller does not include a clock mux and * prescaler unit. |
6b8d1e473 spi: spi-s3c64xx:... |
134 |
* @clk_ioclk: True if clock is present on this device |
a5238e360 spi: s3c64xx: mov... |
135 136 137 138 139 140 141 142 143 144 |
* * The Samsung s3c64xx SPI controller are used on various Samsung SoC's but * differ in some aspects such as the size of the fifo and spi bus clock * setup. Such differences are specified to the driver using this structure * which is provided as driver data to the driver. */ struct s3c64xx_spi_port_config { int fifo_lvl_mask[MAX_SPI_PORTS]; int rx_lvl_offset; int tx_st_done; |
7e9955567 spi: s3c64xx: add... |
145 |
int quirks; |
a5238e360 spi: s3c64xx: mov... |
146 147 |
bool high_speed; bool clk_from_cmu; |
7990b0081 spi: s3c64xx: add... |
148 |
bool clk_ioclk; |
a5238e360 spi: s3c64xx: mov... |
149 150 151 |
}; /** |
230d42d42 spi: Add s3c64xx ... |
152 153 |
* struct s3c64xx_spi_driver_data - Runtime info holder for SPI driver. * @clk: Pointer to the spi clock. |
b0d5d6e55 spi/s3c64xx: Move... |
154 |
* @src_clk: Pointer to the clock used to generate SPI signals. |
7990b0081 spi: s3c64xx: add... |
155 |
* @ioclk: Pointer to the i/o clock between master and slave |
6b8d1e473 spi: spi-s3c64xx:... |
156 |
* @pdev: Pointer to device's platform device data |
230d42d42 spi: Add s3c64xx ... |
157 |
* @master: Pointer to the SPI Protocol master. |
230d42d42 spi: Add s3c64xx ... |
158 |
* @cntrlr_info: Platform specific data for the controller this driver manages. |
230d42d42 spi: Add s3c64xx ... |
159 160 |
* @lock: Controller specific lock. * @state: Set of FLAGS to indicate status. |
230d42d42 spi: Add s3c64xx ... |
161 162 163 164 165 |
* @sfr_start: BUS address of SPI controller regs. * @regs: Pointer to ioremap'ed controller registers. * @xfer_completion: To indicate completion of xfer task. * @cur_mode: Stores the active configuration of the controller. * @cur_bpw: Stores the active bits per word settings. |
6b8d1e473 spi: spi-s3c64xx:... |
166 167 168 169 170 |
* @cur_speed: Current clock speed * @rx_dma: Local receive DMA data (e.g. chan and direction) * @tx_dma: Local transmit DMA data (e.g. chan and direction) * @port_conf: Local SPI port configuartion data * @port_id: Port identification number |
230d42d42 spi: Add s3c64xx ... |
171 172 173 174 |
*/ struct s3c64xx_spi_driver_data { void __iomem *regs; struct clk *clk; |
b0d5d6e55 spi/s3c64xx: Move... |
175 |
struct clk *src_clk; |
7990b0081 spi: s3c64xx: add... |
176 |
struct clk *ioclk; |
230d42d42 spi: Add s3c64xx ... |
177 178 |
struct platform_device *pdev; struct spi_master *master; |
58d547814 spi: spi-s3c64xx:... |
179 |
struct s3c64xx_spi_info *cntrlr_info; |
230d42d42 spi: Add s3c64xx ... |
180 |
spinlock_t lock; |
230d42d42 spi: Add s3c64xx ... |
181 182 183 184 185 |
unsigned long sfr_start; struct completion xfer_completion; unsigned state; unsigned cur_mode, cur_bpw; unsigned cur_speed; |
82ab8cd7e spi/s3c64xx: Merg... |
186 187 |
struct s3c64xx_spi_dma_data rx_dma; struct s3c64xx_spi_dma_data tx_dma; |
a5238e360 spi: s3c64xx: mov... |
188 189 |
struct s3c64xx_spi_port_config *port_conf; unsigned int port_id; |
230d42d42 spi: Add s3c64xx ... |
190 |
}; |
3655d30c0 spi: spi-s3c64xx:... |
191 |
static void s3c64xx_flush_fifo(struct s3c64xx_spi_driver_data *sdd) |
230d42d42 spi: Add s3c64xx ... |
192 |
{ |
230d42d42 spi: Add s3c64xx ... |
193 194 195 196 197 198 199 |
void __iomem *regs = sdd->regs; unsigned long loops; u32 val; writel(0, regs + S3C64XX_SPI_PACKET_CNT); val = readl(regs + S3C64XX_SPI_CH_CFG); |
7d859ff49 spi: Change FIFO ... |
200 201 202 203 |
val &= ~(S3C64XX_SPI_CH_RXCH_ON | S3C64XX_SPI_CH_TXCH_ON); writel(val, regs + S3C64XX_SPI_CH_CFG); val = readl(regs + S3C64XX_SPI_CH_CFG); |
230d42d42 spi: Add s3c64xx ... |
204 205 206 207 208 209 210 211 |
val |= S3C64XX_SPI_CH_SW_RST; val &= ~S3C64XX_SPI_CH_HS_EN; writel(val, regs + S3C64XX_SPI_CH_CFG); /* Flush TxFIFO*/ loops = msecs_to_loops(1); do { val = readl(regs + S3C64XX_SPI_STATUS); |
a5238e360 spi: s3c64xx: mov... |
212 |
} while (TX_FIFO_LVL(val, sdd) && loops--); |
230d42d42 spi: Add s3c64xx ... |
213 |
|
be7852a83 spi/spi_s3c64xx: ... |
214 215 216 |
if (loops == 0) dev_warn(&sdd->pdev->dev, "Timed out flushing TX FIFO "); |
230d42d42 spi: Add s3c64xx ... |
217 218 219 220 |
/* Flush RxFIFO*/ loops = msecs_to_loops(1); do { val = readl(regs + S3C64XX_SPI_STATUS); |
a5238e360 spi: s3c64xx: mov... |
221 |
if (RX_FIFO_LVL(val, sdd)) |
230d42d42 spi: Add s3c64xx ... |
222 223 224 225 |
readl(regs + S3C64XX_SPI_RX_DATA); else break; } while (loops--); |
be7852a83 spi/spi_s3c64xx: ... |
226 227 228 |
if (loops == 0) dev_warn(&sdd->pdev->dev, "Timed out flushing RX FIFO "); |
230d42d42 spi: Add s3c64xx ... |
229 230 231 232 233 234 235 |
val = readl(regs + S3C64XX_SPI_CH_CFG); val &= ~S3C64XX_SPI_CH_SW_RST; writel(val, regs + S3C64XX_SPI_CH_CFG); val = readl(regs + S3C64XX_SPI_MODE_CFG); val &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON); writel(val, regs + S3C64XX_SPI_MODE_CFG); |
230d42d42 spi: Add s3c64xx ... |
236 |
} |
82ab8cd7e spi/s3c64xx: Merg... |
237 |
static void s3c64xx_spi_dmacb(void *data) |
39d3e8074 spi/s3c64xx: Add ... |
238 |
{ |
82ab8cd7e spi/s3c64xx: Merg... |
239 240 |
struct s3c64xx_spi_driver_data *sdd; struct s3c64xx_spi_dma_data *dma = data; |
39d3e8074 spi/s3c64xx: Add ... |
241 |
unsigned long flags; |
054ebcc4a spi: Compatibilit... |
242 |
if (dma->direction == DMA_DEV_TO_MEM) |
82ab8cd7e spi/s3c64xx: Merg... |
243 244 245 246 247 |
sdd = container_of(data, struct s3c64xx_spi_driver_data, rx_dma); else sdd = container_of(data, struct s3c64xx_spi_driver_data, tx_dma); |
39d3e8074 spi/s3c64xx: Add ... |
248 |
spin_lock_irqsave(&sdd->lock, flags); |
054ebcc4a spi: Compatibilit... |
249 |
if (dma->direction == DMA_DEV_TO_MEM) { |
82ab8cd7e spi/s3c64xx: Merg... |
250 251 252 253 254 255 256 257 |
sdd->state &= ~RXBUSY; if (!(sdd->state & TXBUSY)) complete(&sdd->xfer_completion); } else { sdd->state &= ~TXBUSY; if (!(sdd->state & RXBUSY)) complete(&sdd->xfer_completion); } |
39d3e8074 spi/s3c64xx: Add ... |
258 259 260 |
spin_unlock_irqrestore(&sdd->lock, flags); } |
2f4db6f70 spi: spi-s3c64xx:... |
261 |
static int prepare_dma(struct s3c64xx_spi_dma_data *dma, |
6ad45a27c spi: Make core DM... |
262 |
struct sg_table *sgt) |
788437273 spi: s3c64xx: mov... |
263 264 265 |
{ struct s3c64xx_spi_driver_data *sdd; struct dma_slave_config config; |
788437273 spi: s3c64xx: mov... |
266 |
struct dma_async_tx_descriptor *desc; |
2f4db6f70 spi: spi-s3c64xx:... |
267 |
int ret; |
788437273 spi: s3c64xx: mov... |
268 |
|
b1a8e78d1 spi: s3c64xx: Zer... |
269 |
memset(&config, 0, sizeof(config)); |
788437273 spi: s3c64xx: mov... |
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
if (dma->direction == DMA_DEV_TO_MEM) { sdd = container_of((void *)dma, struct s3c64xx_spi_driver_data, rx_dma); config.direction = dma->direction; config.src_addr = sdd->sfr_start + S3C64XX_SPI_RX_DATA; config.src_addr_width = sdd->cur_bpw / 8; config.src_maxburst = 1; dmaengine_slave_config(dma->ch, &config); } else { sdd = container_of((void *)dma, struct s3c64xx_spi_driver_data, tx_dma); config.direction = dma->direction; config.dst_addr = sdd->sfr_start + S3C64XX_SPI_TX_DATA; config.dst_addr_width = sdd->cur_bpw / 8; config.dst_maxburst = 1; dmaengine_slave_config(dma->ch, &config); } |
6ad45a27c spi: Make core DM... |
287 288 |
desc = dmaengine_prep_slave_sg(dma->ch, sgt->sgl, sgt->nents, dma->direction, DMA_PREP_INTERRUPT); |
2f4db6f70 spi: spi-s3c64xx:... |
289 290 291 292 293 |
if (!desc) { dev_err(&sdd->pdev->dev, "unable to prepare %s scatterlist", dma->direction == DMA_DEV_TO_MEM ? "rx" : "tx"); return -ENOMEM; } |
788437273 spi: s3c64xx: mov... |
294 295 296 |
desc->callback = s3c64xx_spi_dmacb; desc->callback_param = dma; |
2f4db6f70 spi: spi-s3c64xx:... |
297 298 299 300 301 302 |
dma->cookie = dmaengine_submit(desc); ret = dma_submit_error(dma->cookie); if (ret) { dev_err(&sdd->pdev->dev, "DMA submission failed"); return -EIO; } |
788437273 spi: s3c64xx: mov... |
303 |
dma_async_issue_pending(dma->ch); |
2f4db6f70 spi: spi-s3c64xx:... |
304 |
return 0; |
788437273 spi: s3c64xx: mov... |
305 |
} |
aa4964c4e spi: s3c64xx: gro... |
306 307 308 309 |
static void s3c64xx_spi_set_cs(struct spi_device *spi, bool enable) { struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi->master); |
a92e7c3d8 spi: s3c64xx: con... |
310 311 |
if (sdd->cntrlr_info->no_cs) return; |
aa4964c4e spi: s3c64xx: gro... |
312 313 |
if (enable) { if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) { |
913ba5c9e spi: spi-s3c64xx:... |
314 |
writel(0, sdd->regs + S3C64XX_SPI_CS_REG); |
aa4964c4e spi: s3c64xx: gro... |
315 |
} else { |
913ba5c9e spi: spi-s3c64xx:... |
316 |
u32 ssel = readl(sdd->regs + S3C64XX_SPI_CS_REG); |
aa4964c4e spi: s3c64xx: gro... |
317 |
|
913ba5c9e spi: spi-s3c64xx:... |
318 319 320 |
ssel |= (S3C64XX_SPI_CS_AUTO | S3C64XX_SPI_CS_NSC_CNT_2); writel(ssel, sdd->regs + S3C64XX_SPI_CS_REG); |
aa4964c4e spi: s3c64xx: gro... |
321 322 323 |
} } else { if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) |
913ba5c9e spi: spi-s3c64xx:... |
324 325 |
writel(S3C64XX_SPI_CS_SIG_INACT, sdd->regs + S3C64XX_SPI_CS_REG); |
aa4964c4e spi: s3c64xx: gro... |
326 327 |
} } |
788437273 spi: s3c64xx: mov... |
328 329 330 |
static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) { struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi); |
788437273 spi: s3c64xx: mov... |
331 |
|
730d9d4d1 spi: s3c64xx: sim... |
332 333 |
if (is_polling(sdd)) return 0; |
730d9d4d1 spi: s3c64xx: sim... |
334 |
spi->dma_rx = sdd->rx_dma.ch; |
730d9d4d1 spi: s3c64xx: sim... |
335 |
spi->dma_tx = sdd->tx_dma.ch; |
fb9d044ef spi/s3c64xx: Chec... |
336 |
|
730d9d4d1 spi: s3c64xx: sim... |
337 |
return 0; |
788437273 spi: s3c64xx: mov... |
338 |
} |
3f2958879 spi/s3c64xx: Use ... |
339 340 341 342 343 344 345 346 |
static bool s3c64xx_spi_can_dma(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) { struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); return xfer->len > (FIFO_LVL_MASK(sdd) >> 1) + 1; } |
2f4db6f70 spi: spi-s3c64xx:... |
347 |
static int s3c64xx_enable_datapath(struct s3c64xx_spi_driver_data *sdd, |
3655d30c0 spi: spi-s3c64xx:... |
348 |
struct spi_transfer *xfer, int dma_mode) |
230d42d42 spi: Add s3c64xx ... |
349 |
{ |
230d42d42 spi: Add s3c64xx ... |
350 351 |
void __iomem *regs = sdd->regs; u32 modecfg, chcfg; |
2f4db6f70 spi: spi-s3c64xx:... |
352 |
int ret = 0; |
230d42d42 spi: Add s3c64xx ... |
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
modecfg = readl(regs + S3C64XX_SPI_MODE_CFG); modecfg &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON); chcfg = readl(regs + S3C64XX_SPI_CH_CFG); chcfg &= ~S3C64XX_SPI_CH_TXCH_ON; if (dma_mode) { chcfg &= ~S3C64XX_SPI_CH_RXCH_ON; } else { /* Always shift in data in FIFO, even if xfer is Tx only, * this helps setting PCKT_CNT value for generating clocks * as exactly needed. */ chcfg |= S3C64XX_SPI_CH_RXCH_ON; writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff) | S3C64XX_SPI_PACKET_CNT_EN, regs + S3C64XX_SPI_PACKET_CNT); } if (xfer->tx_buf != NULL) { sdd->state |= TXBUSY; chcfg |= S3C64XX_SPI_CH_TXCH_ON; if (dma_mode) { modecfg |= S3C64XX_SPI_MODE_TXDMA_ON; |
2f4db6f70 spi: spi-s3c64xx:... |
378 |
ret = prepare_dma(&sdd->tx_dma, &xfer->tx_sg); |
230d42d42 spi: Add s3c64xx ... |
379 |
} else { |
0c92ecf10 spi/s3c64xx: Corr... |
380 381 382 383 384 385 386 387 388 389 390 391 392 393 |
switch (sdd->cur_bpw) { case 32: iowrite32_rep(regs + S3C64XX_SPI_TX_DATA, xfer->tx_buf, xfer->len / 4); break; case 16: iowrite16_rep(regs + S3C64XX_SPI_TX_DATA, xfer->tx_buf, xfer->len / 2); break; default: iowrite8_rep(regs + S3C64XX_SPI_TX_DATA, xfer->tx_buf, xfer->len); break; } |
230d42d42 spi: Add s3c64xx ... |
394 395 396 397 398 |
} } if (xfer->rx_buf != NULL) { sdd->state |= RXBUSY; |
a5238e360 spi: s3c64xx: mov... |
399 |
if (sdd->port_conf->high_speed && sdd->cur_speed >= 30000000UL |
230d42d42 spi: Add s3c64xx ... |
400 401 402 403 404 405 406 407 408 |
&& !(sdd->cur_mode & SPI_CPHA)) chcfg |= S3C64XX_SPI_CH_HS_EN; if (dma_mode) { modecfg |= S3C64XX_SPI_MODE_RXDMA_ON; chcfg |= S3C64XX_SPI_CH_RXCH_ON; writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff) | S3C64XX_SPI_PACKET_CNT_EN, regs + S3C64XX_SPI_PACKET_CNT); |
2f4db6f70 spi: spi-s3c64xx:... |
409 |
ret = prepare_dma(&sdd->rx_dma, &xfer->rx_sg); |
230d42d42 spi: Add s3c64xx ... |
410 411 |
} } |
2f4db6f70 spi: spi-s3c64xx:... |
412 413 |
if (ret) return ret; |
230d42d42 spi: Add s3c64xx ... |
414 415 |
writel(modecfg, regs + S3C64XX_SPI_MODE_CFG); writel(chcfg, regs + S3C64XX_SPI_CH_CFG); |
2f4db6f70 spi: spi-s3c64xx:... |
416 417 |
return 0; |
230d42d42 spi: Add s3c64xx ... |
418 |
} |
796170733 spi/s3c64xx: Make... |
419 |
static u32 s3c64xx_spi_wait_for_timeout(struct s3c64xx_spi_driver_data *sdd, |
7e9955567 spi: s3c64xx: add... |
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 |
int timeout_ms) { void __iomem *regs = sdd->regs; unsigned long val = 1; u32 status; /* max fifo depth available */ u32 max_fifo = (FIFO_LVL_MASK(sdd) >> 1) + 1; if (timeout_ms) val = msecs_to_loops(timeout_ms); do { status = readl(regs + S3C64XX_SPI_STATUS); } while (RX_FIFO_LVL(status, sdd) < max_fifo && --val); /* return the actual received data length */ return RX_FIFO_LVL(status, sdd); |
230d42d42 spi: Add s3c64xx ... |
438 |
} |
3655d30c0 spi: spi-s3c64xx:... |
439 440 |
static int s3c64xx_wait_for_dma(struct s3c64xx_spi_driver_data *sdd, struct spi_transfer *xfer) |
230d42d42 spi: Add s3c64xx ... |
441 |
{ |
230d42d42 spi: Add s3c64xx ... |
442 443 |
void __iomem *regs = sdd->regs; unsigned long val; |
3700c6eb1 spi/s3c64xx: Spli... |
444 |
u32 status; |
230d42d42 spi: Add s3c64xx ... |
445 446 447 448 |
int ms; /* millisecs to xfer 'len' bytes @ 'cur_speed' */ ms = xfer->len * 8 * 1000 / sdd->cur_speed; |
9fe26adbe spi: spi-s3c64xx:... |
449 450 |
ms += 30; /* some tolerance */ ms = max(ms, 100); /* minimum timeout */ |
230d42d42 spi: Add s3c64xx ... |
451 |
|
3700c6eb1 spi/s3c64xx: Spli... |
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 |
val = msecs_to_jiffies(ms) + 10; val = wait_for_completion_timeout(&sdd->xfer_completion, val); /* * If the previous xfer was completed within timeout, then * proceed further else return -EIO. * DmaTx returns after simply writing data in the FIFO, * w/o waiting for real transmission on the bus to finish. * DmaRx returns only after Dma read data from FIFO which * needs bus transmission to finish, so we don't worry if * Xfer involved Rx(with or without Tx). */ if (val && !xfer->rx_buf) { val = msecs_to_loops(10); status = readl(regs + S3C64XX_SPI_STATUS); while ((TX_FIFO_LVL(status, sdd) || !S3C64XX_SPI_ST_TX_DONE(status, sdd)) && --val) { cpu_relax(); |
c3f139b65 spi/s3c64xx: Fix ... |
471 |
status = readl(regs + S3C64XX_SPI_STATUS); |
3700c6eb1 spi/s3c64xx: Spli... |
472 |
} |
230d42d42 spi: Add s3c64xx ... |
473 |
} |
3700c6eb1 spi/s3c64xx: Spli... |
474 475 476 |
/* If timed out while checking rx/tx status return error */ if (!val) return -EIO; |
230d42d42 spi: Add s3c64xx ... |
477 |
|
3700c6eb1 spi/s3c64xx: Spli... |
478 479 |
return 0; } |
7e9955567 spi: s3c64xx: add... |
480 |
|
3655d30c0 spi: spi-s3c64xx:... |
481 482 |
static int s3c64xx_wait_for_pio(struct s3c64xx_spi_driver_data *sdd, struct spi_transfer *xfer) |
3700c6eb1 spi/s3c64xx: Spli... |
483 484 485 486 487 488 489 490 |
{ void __iomem *regs = sdd->regs; unsigned long val; u32 status; int loops; u32 cpy_len; u8 *buf; int ms; |
230d42d42 spi: Add s3c64xx ... |
491 |
|
3700c6eb1 spi/s3c64xx: Spli... |
492 493 494 |
/* millisecs to xfer 'len' bytes @ 'cur_speed' */ ms = xfer->len * 8 * 1000 / sdd->cur_speed; ms += 10; /* some tolerance */ |
7e9955567 spi: s3c64xx: add... |
495 |
|
3700c6eb1 spi/s3c64xx: Spli... |
496 497 498 499 |
val = msecs_to_loops(ms); do { status = readl(regs + S3C64XX_SPI_STATUS); } while (RX_FIFO_LVL(status, sdd) < xfer->len && --val); |
7e9955567 spi: s3c64xx: add... |
500 |
|
4e0b82ee3 spi: spi-s3c64xx:... |
501 502 |
if (!val) return -EIO; |
3700c6eb1 spi/s3c64xx: Spli... |
503 504 505 506 507 |
/* If it was only Tx */ if (!xfer->rx_buf) { sdd->state &= ~TXBUSY; return 0; |
230d42d42 spi: Add s3c64xx ... |
508 |
} |
3700c6eb1 spi/s3c64xx: Spli... |
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 |
/* * If the receive length is bigger than the controller fifo * size, calculate the loops and read the fifo as many times. * loops = length / max fifo size (calculated by using the * fifo mask). * For any size less than the fifo size the below code is * executed atleast once. */ loops = xfer->len / ((FIFO_LVL_MASK(sdd) >> 1) + 1); buf = xfer->rx_buf; do { /* wait for data to be received in the fifo */ cpy_len = s3c64xx_spi_wait_for_timeout(sdd, (loops ? ms : 0)); switch (sdd->cur_bpw) { case 32: ioread32_rep(regs + S3C64XX_SPI_RX_DATA, buf, cpy_len / 4); break; case 16: ioread16_rep(regs + S3C64XX_SPI_RX_DATA, buf, cpy_len / 2); break; default: ioread8_rep(regs + S3C64XX_SPI_RX_DATA, buf, cpy_len); break; } buf = buf + cpy_len; } while (loops--); sdd->state &= ~RXBUSY; |
230d42d42 spi: Add s3c64xx ... |
542 543 |
return 0; } |
2f4db6f70 spi: spi-s3c64xx:... |
544 |
static int s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) |
230d42d42 spi: Add s3c64xx ... |
545 |
{ |
230d42d42 spi: Add s3c64xx ... |
546 |
void __iomem *regs = sdd->regs; |
2f4db6f70 spi: spi-s3c64xx:... |
547 |
int ret; |
230d42d42 spi: Add s3c64xx ... |
548 549 550 |
u32 val; /* Disable Clock */ |
d9aaf1dc9 spi: s3c64xx: do ... |
551 |
if (!sdd->port_conf->clk_from_cmu) { |
b42a81ca0 spi/s3c64xx: Cons... |
552 553 554 555 |
val = readl(regs + S3C64XX_SPI_CLK_CFG); val &= ~S3C64XX_SPI_ENCLK_ENABLE; writel(val, regs + S3C64XX_SPI_CLK_CFG); } |
230d42d42 spi: Add s3c64xx ... |
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 |
/* Set Polarity and Phase */ val = readl(regs + S3C64XX_SPI_CH_CFG); val &= ~(S3C64XX_SPI_CH_SLAVE | S3C64XX_SPI_CPOL_L | S3C64XX_SPI_CPHA_B); if (sdd->cur_mode & SPI_CPOL) val |= S3C64XX_SPI_CPOL_L; if (sdd->cur_mode & SPI_CPHA) val |= S3C64XX_SPI_CPHA_B; writel(val, regs + S3C64XX_SPI_CH_CFG); /* Set Channel & DMA Mode */ val = readl(regs + S3C64XX_SPI_MODE_CFG); val &= ~(S3C64XX_SPI_MODE_BUS_TSZ_MASK | S3C64XX_SPI_MODE_CH_TSZ_MASK); switch (sdd->cur_bpw) { case 32: val |= S3C64XX_SPI_MODE_BUS_TSZ_WORD; |
0c92ecf10 spi/s3c64xx: Corr... |
579 |
val |= S3C64XX_SPI_MODE_CH_TSZ_WORD; |
230d42d42 spi: Add s3c64xx ... |
580 581 582 |
break; case 16: val |= S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD; |
0c92ecf10 spi/s3c64xx: Corr... |
583 |
val |= S3C64XX_SPI_MODE_CH_TSZ_HALFWORD; |
230d42d42 spi: Add s3c64xx ... |
584 585 586 |
break; default: val |= S3C64XX_SPI_MODE_BUS_TSZ_BYTE; |
0c92ecf10 spi/s3c64xx: Corr... |
587 |
val |= S3C64XX_SPI_MODE_CH_TSZ_BYTE; |
230d42d42 spi: Add s3c64xx ... |
588 589 |
break; } |
230d42d42 spi: Add s3c64xx ... |
590 591 |
writel(val, regs + S3C64XX_SPI_MODE_CFG); |
a5238e360 spi: s3c64xx: mov... |
592 |
if (sdd->port_conf->clk_from_cmu) { |
0dbe70a1f spi: s3c64xx: res... |
593 |
/* The src_clk clock is divided internally by 2 */ |
2f4db6f70 spi: spi-s3c64xx:... |
594 595 596 |
ret = clk_set_rate(sdd->src_clk, sdd->cur_speed * 2); if (ret) return ret; |
20b4016a3 spi: spi-s3c64xx:... |
597 |
sdd->cur_speed = clk_get_rate(sdd->src_clk) / 2; |
b42a81ca0 spi/s3c64xx: Cons... |
598 599 600 601 602 603 604 605 606 607 608 609 610 |
} else { /* Configure Clock */ val = readl(regs + S3C64XX_SPI_CLK_CFG); val &= ~S3C64XX_SPI_PSR_MASK; val |= ((clk_get_rate(sdd->src_clk) / sdd->cur_speed / 2 - 1) & S3C64XX_SPI_PSR_MASK); writel(val, regs + S3C64XX_SPI_CLK_CFG); /* Enable Clock */ val = readl(regs + S3C64XX_SPI_CLK_CFG); val |= S3C64XX_SPI_ENCLK_ENABLE; writel(val, regs + S3C64XX_SPI_CLK_CFG); } |
2f4db6f70 spi: spi-s3c64xx:... |
611 612 |
return 0; |
230d42d42 spi: Add s3c64xx ... |
613 |
} |
230d42d42 spi: Add s3c64xx ... |
614 |
#define XFER_DMAADDR_INVALID DMA_BIT_MASK(32) |
6bb9c0e34 spi/s3c64xx: Use ... |
615 616 |
static int s3c64xx_spi_prepare_message(struct spi_master *master, struct spi_message *msg) |
230d42d42 spi: Add s3c64xx ... |
617 |
{ |
ad2a99af0 spi/s3c64xx: Conv... |
618 |
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); |
230d42d42 spi: Add s3c64xx ... |
619 620 |
struct spi_device *spi = msg->spi; struct s3c64xx_spi_csinfo *cs = spi->controller_data; |
230d42d42 spi: Add s3c64xx ... |
621 |
|
230d42d42 spi: Add s3c64xx ... |
622 623 |
/* Configure feedback delay */ writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK); |
6bb9c0e34 spi/s3c64xx: Use ... |
624 625 |
return 0; } |
0c92ecf10 spi/s3c64xx: Corr... |
626 |
|
0732a9d2a spi/s3c64xx: Use ... |
627 628 629 |
static int s3c64xx_spi_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) |
6bb9c0e34 spi/s3c64xx: Use ... |
630 631 |
{ struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); |
f6364e66c spi: spi-s3c64xx:... |
632 |
const unsigned int fifo_len = (FIFO_LVL_MASK(sdd) >> 1) + 1; |
0af7af7da spi: spi-s3c64xx:... |
633 634 635 636 |
const void *tx_buf = NULL; void *rx_buf = NULL; int target_len = 0, origin_len = 0; int use_dma = 0; |
0732a9d2a spi/s3c64xx: Use ... |
637 |
int status; |
6bb9c0e34 spi/s3c64xx: Use ... |
638 639 |
u32 speed; u8 bpw; |
0732a9d2a spi/s3c64xx: Use ... |
640 |
unsigned long flags; |
230d42d42 spi: Add s3c64xx ... |
641 |
|
3e83c1949 spi/s3c64xx: Corr... |
642 |
reinit_completion(&sdd->xfer_completion); |
230d42d42 spi: Add s3c64xx ... |
643 |
|
0732a9d2a spi/s3c64xx: Use ... |
644 645 |
/* Only BPW and Speed may change across transfers */ bpw = xfer->bits_per_word; |
88d4a7440 spi: s3c64xx: Use... |
646 |
speed = xfer->speed_hz; |
230d42d42 spi: Add s3c64xx ... |
647 |
|
0732a9d2a spi/s3c64xx: Use ... |
648 649 650 |
if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) { sdd->cur_bpw = bpw; sdd->cur_speed = speed; |
11f66f092 spi: s3c64xx: do ... |
651 |
sdd->cur_mode = spi->mode; |
2f4db6f70 spi: spi-s3c64xx:... |
652 653 654 |
status = s3c64xx_spi_config(sdd); if (status) return status; |
0732a9d2a spi/s3c64xx: Use ... |
655 |
} |
230d42d42 spi: Add s3c64xx ... |
656 |
|
f6364e66c spi: spi-s3c64xx:... |
657 |
if (!is_polling(sdd) && (xfer->len > fifo_len) && |
0af7af7da spi: spi-s3c64xx:... |
658 |
sdd->rx_dma.ch && sdd->tx_dma.ch) { |
0732a9d2a spi/s3c64xx: Use ... |
659 |
use_dma = 1; |
230d42d42 spi: Add s3c64xx ... |
660 |
|
0af7af7da spi: spi-s3c64xx:... |
661 662 663 664 |
} else if (is_polling(sdd) && xfer->len > fifo_len) { tx_buf = xfer->tx_buf; rx_buf = xfer->rx_buf; origin_len = xfer->len; |
230d42d42 spi: Add s3c64xx ... |
665 |
|
0af7af7da spi: spi-s3c64xx:... |
666 667 668 669 670 671 672 |
target_len = xfer->len; if (xfer->len > fifo_len) xfer->len = fifo_len; } do { spin_lock_irqsave(&sdd->lock, flags); |
230d42d42 spi: Add s3c64xx ... |
673 |
|
0af7af7da spi: spi-s3c64xx:... |
674 675 676 |
/* Pending only which is to be done */ sdd->state &= ~RXBUSY; sdd->state &= ~TXBUSY; |
230d42d42 spi: Add s3c64xx ... |
677 |
|
0af7af7da spi: spi-s3c64xx:... |
678 679 |
/* Start the signals */ s3c64xx_spi_set_cs(spi, true); |
230d42d42 spi: Add s3c64xx ... |
680 |
|
2f4db6f70 spi: spi-s3c64xx:... |
681 |
status = s3c64xx_enable_datapath(sdd, xfer, use_dma); |
581e2b419 spi: spi-s3c64xx:... |
682 |
|
0af7af7da spi: spi-s3c64xx:... |
683 |
spin_unlock_irqrestore(&sdd->lock, flags); |
2f4db6f70 spi: spi-s3c64xx:... |
684 685 686 687 688 |
if (status) { dev_err(&spi->dev, "failed to enable data path for transfer: %d ", status); break; } |
0af7af7da spi: spi-s3c64xx:... |
689 690 691 692 693 694 695 |
if (use_dma) status = s3c64xx_wait_for_dma(sdd, xfer); else status = s3c64xx_wait_for_pio(sdd, xfer); if (status) { dev_err(&spi->dev, |
df7cd1bba spi: spi-s3c64xx:... |
696 697 |
"I/O Error: rx-%d tx-%d rx-%c tx-%c len-%d dma-%d res-(%d) ", |
0af7af7da spi: spi-s3c64xx:... |
698 699 700 |
xfer->rx_buf ? 1 : 0, xfer->tx_buf ? 1 : 0, (sdd->state & RXBUSY) ? 'f' : 'p', (sdd->state & TXBUSY) ? 'f' : 'p', |
df7cd1bba spi: spi-s3c64xx:... |
701 |
xfer->len, use_dma ? 1 : 0, status); |
0af7af7da spi: spi-s3c64xx:... |
702 703 |
if (use_dma) { |
df7cd1bba spi: spi-s3c64xx:... |
704 705 706 707 708 |
struct dma_tx_state s; if (xfer->tx_buf && (sdd->state & TXBUSY)) { dmaengine_pause(sdd->tx_dma.ch); dmaengine_tx_status(sdd->tx_dma.ch, sdd->tx_dma.cookie, &s); |
0af7af7da spi: spi-s3c64xx:... |
709 |
dmaengine_terminate_all(sdd->tx_dma.ch); |
df7cd1bba spi: spi-s3c64xx:... |
710 711 712 713 714 715 716 |
dev_err(&spi->dev, "TX residue: %d ", s.residue); } if (xfer->rx_buf && (sdd->state & RXBUSY)) { dmaengine_pause(sdd->rx_dma.ch); dmaengine_tx_status(sdd->rx_dma.ch, sdd->rx_dma.cookie, &s); |
0af7af7da spi: spi-s3c64xx:... |
717 |
dmaengine_terminate_all(sdd->rx_dma.ch); |
df7cd1bba spi: spi-s3c64xx:... |
718 719 720 |
dev_err(&spi->dev, "RX residue: %d ", s.residue); } |
0af7af7da spi: spi-s3c64xx:... |
721 722 723 |
} } else { s3c64xx_flush_fifo(sdd); |
230d42d42 spi: Add s3c64xx ... |
724 |
} |
0af7af7da spi: spi-s3c64xx:... |
725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 |
if (target_len > 0) { target_len -= xfer->len; if (xfer->tx_buf) xfer->tx_buf += xfer->len; if (xfer->rx_buf) xfer->rx_buf += xfer->len; if (target_len > fifo_len) xfer->len = fifo_len; else xfer->len = target_len; } } while (target_len > 0); if (origin_len) { /* Restore original xfer buffers and length */ xfer->tx_buf = tx_buf; xfer->rx_buf = rx_buf; xfer->len = origin_len; |
230d42d42 spi: Add s3c64xx ... |
746 |
} |
0732a9d2a spi/s3c64xx: Use ... |
747 |
return status; |
230d42d42 spi: Add s3c64xx ... |
748 |
} |
230d42d42 spi: Add s3c64xx ... |
749 |
|
2b9080754 spi: s3c64xx: add... |
750 |
static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata( |
2b9080754 spi: s3c64xx: add... |
751 752 753 |
struct spi_device *spi) { struct s3c64xx_spi_csinfo *cs; |
4732cc636 spi/s3c64xx: impr... |
754 |
struct device_node *slave_np, *data_np = NULL; |
2b9080754 spi: s3c64xx: add... |
755 756 757 758 759 760 761 762 |
u32 fb_delay = 0; slave_np = spi->dev.of_node; if (!slave_np) { dev_err(&spi->dev, "device node not found "); return ERR_PTR(-EINVAL); } |
06455bbca dt/s3c64xx/spi: U... |
763 |
data_np = of_get_child_by_name(slave_np, "controller-data"); |
2b9080754 spi: s3c64xx: add... |
764 765 766 767 768 769 770 771 |
if (!data_np) { dev_err(&spi->dev, "child node 'controller-data' not found "); return ERR_PTR(-EINVAL); } cs = kzalloc(sizeof(*cs), GFP_KERNEL); if (!cs) { |
06455bbca dt/s3c64xx/spi: U... |
772 |
of_node_put(data_np); |
2b9080754 spi: s3c64xx: add... |
773 774 |
return ERR_PTR(-ENOMEM); } |
2b9080754 spi: s3c64xx: add... |
775 776 |
of_property_read_u32(data_np, "samsung,spi-feedback-delay", &fb_delay); cs->fb_delay = fb_delay; |
06455bbca dt/s3c64xx/spi: U... |
777 |
of_node_put(data_np); |
2b9080754 spi: s3c64xx: add... |
778 779 |
return cs; } |
230d42d42 spi: Add s3c64xx ... |
780 781 782 783 784 785 786 787 788 789 |
/* * Here we only check the validity of requested configuration * and save the configuration in a local data-structure. * The controller is actually configured only just before we * get a message to transfer. */ static int s3c64xx_spi_setup(struct spi_device *spi) { struct s3c64xx_spi_csinfo *cs = spi->controller_data; struct s3c64xx_spi_driver_data *sdd; |
2b9080754 spi: s3c64xx: add... |
790 |
int err; |
230d42d42 spi: Add s3c64xx ... |
791 |
|
2b9080754 spi: s3c64xx: add... |
792 |
sdd = spi_master_get_devdata(spi->master); |
306972ced spi: s3c64xx: use... |
793 |
if (spi->dev.of_node) { |
5c725b34d spi: spi-s3c64xx.... |
794 |
cs = s3c64xx_get_slave_ctrldata(spi); |
2b9080754 spi: s3c64xx: add... |
795 |
spi->controller_data = cs; |
306972ced spi: s3c64xx: use... |
796 797 798 799 800 801 802 |
} else if (cs) { /* On non-DT platforms the SPI core will set spi->cs_gpio * to -ENOENT. The GPIO pin used to drive the chip select * is defined by using platform data so spi->cs_gpio value * has to be override to have the proper GPIO pin number. */ spi->cs_gpio = cs->line; |
2b9080754 spi: s3c64xx: add... |
803 804 805 |
} if (IS_ERR_OR_NULL(cs)) { |
230d42d42 spi: Add s3c64xx ... |
806 807 808 809 |
dev_err(&spi->dev, "No CS for SPI(%d) ", spi->chip_select); return -ENODEV; } |
0149871c4 spi: s3c64xx: Do ... |
810 |
if (!spi_get_ctldata(spi)) { |
306972ced spi: s3c64xx: use... |
811 812 813 814 815 816 817 818 819 820 |
if (gpio_is_valid(spi->cs_gpio)) { err = gpio_request_one(spi->cs_gpio, GPIOF_OUT_INIT_HIGH, dev_name(&spi->dev)); if (err) { dev_err(&spi->dev, "Failed to get /CS gpio [%d]: %d ", spi->cs_gpio, err); goto err_gpio_req; } |
1c20c200e spi: s3c64xx: Rem... |
821 |
} |
1c20c200e spi: s3c64xx: Rem... |
822 |
|
3146beec2 spi: s3c64xx: Add... |
823 |
spi_set_ctldata(spi, cs); |
230d42d42 spi: Add s3c64xx ... |
824 |
} |
b97b66217 spi/s3c64xx: Impl... |
825 |
pm_runtime_get_sync(&sdd->pdev->dev); |
230d42d42 spi: Add s3c64xx ... |
826 |
/* Check if we can provide the requested rate */ |
a5238e360 spi: s3c64xx: mov... |
827 |
if (!sdd->port_conf->clk_from_cmu) { |
b42a81ca0 spi/s3c64xx: Cons... |
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 |
u32 psr, speed; /* Max possible */ speed = clk_get_rate(sdd->src_clk) / 2 / (0 + 1); if (spi->max_speed_hz > speed) spi->max_speed_hz = speed; psr = clk_get_rate(sdd->src_clk) / 2 / spi->max_speed_hz - 1; psr &= S3C64XX_SPI_PSR_MASK; if (psr == S3C64XX_SPI_PSR_MASK) psr--; speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1); if (spi->max_speed_hz < speed) { if (psr+1 < S3C64XX_SPI_PSR_MASK) { psr++; } else { err = -EINVAL; goto setup_exit; } } |
230d42d42 spi: Add s3c64xx ... |
850 |
|
b42a81ca0 spi/s3c64xx: Cons... |
851 |
speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1); |
2b9080754 spi: s3c64xx: add... |
852 |
if (spi->max_speed_hz >= speed) { |
b42a81ca0 spi/s3c64xx: Cons... |
853 |
spi->max_speed_hz = speed; |
2b9080754 spi: s3c64xx: add... |
854 |
} else { |
e1b0f0df6 spi/s3c64xx: Comp... |
855 856 857 |
dev_err(&spi->dev, "Can't set %dHz transfer speed ", spi->max_speed_hz); |
230d42d42 spi: Add s3c64xx ... |
858 |
err = -EINVAL; |
2b9080754 spi: s3c64xx: add... |
859 860 |
goto setup_exit; } |
230d42d42 spi: Add s3c64xx ... |
861 |
} |
483867ee2 spi: s3c64xx: ext... |
862 863 |
pm_runtime_mark_last_busy(&sdd->pdev->dev); pm_runtime_put_autosuspend(&sdd->pdev->dev); |
aa4964c4e spi: s3c64xx: gro... |
864 |
s3c64xx_spi_set_cs(spi, false); |
2b9080754 spi: s3c64xx: add... |
865 |
return 0; |
b97b66217 spi/s3c64xx: Impl... |
866 |
|
230d42d42 spi: Add s3c64xx ... |
867 |
setup_exit: |
483867ee2 spi: s3c64xx: ext... |
868 869 |
pm_runtime_mark_last_busy(&sdd->pdev->dev); pm_runtime_put_autosuspend(&sdd->pdev->dev); |
230d42d42 spi: Add s3c64xx ... |
870 |
/* setup() returns with device de-selected */ |
aa4964c4e spi: s3c64xx: gro... |
871 |
s3c64xx_spi_set_cs(spi, false); |
230d42d42 spi: Add s3c64xx ... |
872 |
|
306972ced spi: s3c64xx: use... |
873 874 |
if (gpio_is_valid(spi->cs_gpio)) gpio_free(spi->cs_gpio); |
2b9080754 spi: s3c64xx: add... |
875 876 877 |
spi_set_ctldata(spi, NULL); err_gpio_req: |
5bee3b94d spi/s3c64xx: Don'... |
878 879 |
if (spi->dev.of_node) kfree(cs); |
2b9080754 spi: s3c64xx: add... |
880 |
|
230d42d42 spi: Add s3c64xx ... |
881 882 |
return err; } |
1c20c200e spi: s3c64xx: Rem... |
883 884 885 |
static void s3c64xx_spi_cleanup(struct spi_device *spi) { struct s3c64xx_spi_csinfo *cs = spi_get_ctldata(spi); |
306972ced spi: s3c64xx: use... |
886 |
if (gpio_is_valid(spi->cs_gpio)) { |
dd97e2684 spi/s3c64xx: Use ... |
887 |
gpio_free(spi->cs_gpio); |
2b9080754 spi: s3c64xx: add... |
888 889 |
if (spi->dev.of_node) kfree(cs); |
306972ced spi: s3c64xx: use... |
890 891 892 893 894 895 896 897 |
else { /* On non-DT platforms, the SPI core sets * spi->cs_gpio to -ENOENT and .setup() * overrides it with the GPIO pin value * passed using platform data. */ spi->cs_gpio = -ENOENT; } |
2b9080754 spi: s3c64xx: add... |
898 |
} |
306972ced spi: s3c64xx: use... |
899 |
|
1c20c200e spi: s3c64xx: Rem... |
900 901 |
spi_set_ctldata(spi, NULL); } |
c2573128a spi/s3c64xx: Log ... |
902 903 904 905 |
static irqreturn_t s3c64xx_spi_irq(int irq, void *data) { struct s3c64xx_spi_driver_data *sdd = data; struct spi_master *spi = sdd->master; |
375981f2e spi/s3c64xx: modi... |
906 |
unsigned int val, clr = 0; |
c2573128a spi/s3c64xx: Log ... |
907 |
|
375981f2e spi/s3c64xx: modi... |
908 |
val = readl(sdd->regs + S3C64XX_SPI_STATUS); |
c2573128a spi/s3c64xx: Log ... |
909 |
|
375981f2e spi/s3c64xx: modi... |
910 911 |
if (val & S3C64XX_SPI_ST_RX_OVERRUN_ERR) { clr = S3C64XX_SPI_PND_RX_OVERRUN_CLR; |
c2573128a spi/s3c64xx: Log ... |
912 913 |
dev_err(&spi->dev, "RX overrun "); |
375981f2e spi/s3c64xx: modi... |
914 915 916 |
} if (val & S3C64XX_SPI_ST_RX_UNDERRUN_ERR) { clr |= S3C64XX_SPI_PND_RX_UNDERRUN_CLR; |
c2573128a spi/s3c64xx: Log ... |
917 918 |
dev_err(&spi->dev, "RX underrun "); |
375981f2e spi/s3c64xx: modi... |
919 920 921 |
} if (val & S3C64XX_SPI_ST_TX_OVERRUN_ERR) { clr |= S3C64XX_SPI_PND_TX_OVERRUN_CLR; |
c2573128a spi/s3c64xx: Log ... |
922 923 |
dev_err(&spi->dev, "TX overrun "); |
375981f2e spi/s3c64xx: modi... |
924 925 926 |
} if (val & S3C64XX_SPI_ST_TX_UNDERRUN_ERR) { clr |= S3C64XX_SPI_PND_TX_UNDERRUN_CLR; |
c2573128a spi/s3c64xx: Log ... |
927 928 |
dev_err(&spi->dev, "TX underrun "); |
375981f2e spi/s3c64xx: modi... |
929 930 931 932 933 |
} /* Clear the pending irq by setting and then clearing it */ writel(clr, sdd->regs + S3C64XX_SPI_PENDING_CLR); writel(0, sdd->regs + S3C64XX_SPI_PENDING_CLR); |
c2573128a spi/s3c64xx: Log ... |
934 935 936 |
return IRQ_HANDLED; } |
1c75862d8 spi: spi-s3c64xx:... |
937 |
static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd) |
230d42d42 spi: Add s3c64xx ... |
938 |
{ |
ad7de729c spi/s3c64xx: Rena... |
939 |
struct s3c64xx_spi_info *sci = sdd->cntrlr_info; |
230d42d42 spi: Add s3c64xx ... |
940 941 942 943 |
void __iomem *regs = sdd->regs; unsigned int val; sdd->cur_speed = 0; |
a92e7c3d8 spi: s3c64xx: con... |
944 |
if (sci->no_cs) |
913ba5c9e spi: spi-s3c64xx:... |
945 |
writel(0, sdd->regs + S3C64XX_SPI_CS_REG); |
a92e7c3d8 spi: s3c64xx: con... |
946 |
else if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) |
913ba5c9e spi: spi-s3c64xx:... |
947 |
writel(S3C64XX_SPI_CS_SIG_INACT, sdd->regs + S3C64XX_SPI_CS_REG); |
230d42d42 spi: Add s3c64xx ... |
948 949 950 |
/* Disable Interrupts - we use Polling if not DMA mode */ writel(0, regs + S3C64XX_SPI_INT_EN); |
a5238e360 spi: s3c64xx: mov... |
951 |
if (!sdd->port_conf->clk_from_cmu) |
b42a81ca0 spi/s3c64xx: Cons... |
952 |
writel(sci->src_clk_nr << S3C64XX_SPI_CLKSEL_SRCSHFT, |
230d42d42 spi: Add s3c64xx ... |
953 954 955 |
regs + S3C64XX_SPI_CLK_CFG); writel(0, regs + S3C64XX_SPI_MODE_CFG); writel(0, regs + S3C64XX_SPI_PACKET_CNT); |
375981f2e spi/s3c64xx: modi... |
956 957 958 959 960 961 962 |
/* Clear any irq pending bits, should set and clear the bits */ val = S3C64XX_SPI_PND_RX_OVERRUN_CLR | S3C64XX_SPI_PND_RX_UNDERRUN_CLR | S3C64XX_SPI_PND_TX_OVERRUN_CLR | S3C64XX_SPI_PND_TX_UNDERRUN_CLR; writel(val, regs + S3C64XX_SPI_PENDING_CLR); writel(0, regs + S3C64XX_SPI_PENDING_CLR); |
230d42d42 spi: Add s3c64xx ... |
963 964 965 966 967 968 969 970 |
writel(0, regs + S3C64XX_SPI_SWAP_CFG); val = readl(regs + S3C64XX_SPI_MODE_CFG); val &= ~S3C64XX_SPI_MODE_4BURST; val &= ~(S3C64XX_SPI_MAX_TRAILCNT << S3C64XX_SPI_TRAILCNT_OFF); val |= (S3C64XX_SPI_TRAILCNT << S3C64XX_SPI_TRAILCNT_OFF); writel(val, regs + S3C64XX_SPI_MODE_CFG); |
3655d30c0 spi: spi-s3c64xx:... |
971 |
s3c64xx_flush_fifo(sdd); |
230d42d42 spi: Add s3c64xx ... |
972 |
} |
2b9080754 spi: s3c64xx: add... |
973 |
#ifdef CONFIG_OF |
75bf33611 spi/s3c64xx: fix ... |
974 |
static struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev) |
2b9080754 spi: s3c64xx: add... |
975 976 977 978 979 |
{ struct s3c64xx_spi_info *sci; u32 temp; sci = devm_kzalloc(dev, sizeof(*sci), GFP_KERNEL); |
1273eb050 spi: s3c64xx: rem... |
980 |
if (!sci) |
2b9080754 spi: s3c64xx: add... |
981 |
return ERR_PTR(-ENOMEM); |
2b9080754 spi: s3c64xx: add... |
982 983 |
if (of_property_read_u32(dev->of_node, "samsung,spi-src-clk", &temp)) { |
75bf33611 spi/s3c64xx: fix ... |
984 985 |
dev_warn(dev, "spi bus clock parent not specified, using clock at index 0 as parent "); |
2b9080754 spi: s3c64xx: add... |
986 987 988 989 990 991 |
sci->src_clk_nr = 0; } else { sci->src_clk_nr = temp; } if (of_property_read_u32(dev->of_node, "num-cs", &temp)) { |
75bf33611 spi/s3c64xx: fix ... |
992 993 |
dev_warn(dev, "number of chip select lines not specified, assuming 1 chip select line "); |
2b9080754 spi: s3c64xx: add... |
994 995 996 997 |
sci->num_cs = 1; } else { sci->num_cs = temp; } |
379f831a9 spi: s3c64xx: fix... |
998 |
sci->no_cs = of_property_read_bool(dev->of_node, "no-cs-readback"); |
a92e7c3d8 spi: s3c64xx: con... |
999 |
|
2b9080754 spi: s3c64xx: add... |
1000 1001 1002 1003 1004 |
return sci; } #else static struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev) { |
8074cf063 spi: use dev_get_... |
1005 |
return dev_get_platdata(dev); |
2b9080754 spi: s3c64xx: add... |
1006 |
} |
2b9080754 spi: s3c64xx: add... |
1007 1008 1009 |
#endif static const struct of_device_id s3c64xx_spi_dt_match[]; |
a5238e360 spi: s3c64xx: mov... |
1010 1011 1012 |
static inline struct s3c64xx_spi_port_config *s3c64xx_spi_get_port_config( struct platform_device *pdev) { |
2b9080754 spi: s3c64xx: add... |
1013 1014 1015 1016 1017 1018 1019 |
#ifdef CONFIG_OF if (pdev->dev.of_node) { const struct of_device_id *match; match = of_match_node(s3c64xx_spi_dt_match, pdev->dev.of_node); return (struct s3c64xx_spi_port_config *)match->data; } #endif |
a5238e360 spi: s3c64xx: mov... |
1020 1021 1022 |
return (struct s3c64xx_spi_port_config *) platform_get_device_id(pdev)->driver_data; } |
2deff8d60 spi: Remove erron... |
1023 |
static int s3c64xx_spi_probe(struct platform_device *pdev) |
230d42d42 spi: Add s3c64xx ... |
1024 |
{ |
2b9080754 spi: s3c64xx: add... |
1025 |
struct resource *mem_res; |
230d42d42 spi: Add s3c64xx ... |
1026 |
struct s3c64xx_spi_driver_data *sdd; |
8074cf063 spi: use dev_get_... |
1027 |
struct s3c64xx_spi_info *sci = dev_get_platdata(&pdev->dev); |
230d42d42 spi: Add s3c64xx ... |
1028 |
struct spi_master *master; |
c2573128a spi/s3c64xx: Log ... |
1029 |
int ret, irq; |
a24d850b9 spi/s3c64xx: Use ... |
1030 |
char clk_name[16]; |
230d42d42 spi: Add s3c64xx ... |
1031 |
|
2b9080754 spi: s3c64xx: add... |
1032 1033 1034 1035 |
if (!sci && pdev->dev.of_node) { sci = s3c64xx_spi_parse_dt(&pdev->dev); if (IS_ERR(sci)) return PTR_ERR(sci); |
230d42d42 spi: Add s3c64xx ... |
1036 |
} |
2b9080754 spi: s3c64xx: add... |
1037 |
if (!sci) { |
230d42d42 spi: Add s3c64xx ... |
1038 1039 1040 1041 |
dev_err(&pdev->dev, "platform_data missing! "); return -ENODEV; } |
230d42d42 spi: Add s3c64xx ... |
1042 1043 1044 1045 1046 1047 |
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (mem_res == NULL) { dev_err(&pdev->dev, "Unable to get SPI MEM resource "); return -ENXIO; } |
c2573128a spi/s3c64xx: Log ... |
1048 1049 1050 1051 1052 1053 |
irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_warn(&pdev->dev, "Failed to get IRQ: %d ", irq); return irq; } |
230d42d42 spi: Add s3c64xx ... |
1054 1055 1056 1057 1058 1059 1060 |
master = spi_alloc_master(&pdev->dev, sizeof(struct s3c64xx_spi_driver_data)); if (master == NULL) { dev_err(&pdev->dev, "Unable to allocate SPI Master "); return -ENOMEM; } |
230d42d42 spi: Add s3c64xx ... |
1061 1062 1063 |
platform_set_drvdata(pdev, master); sdd = spi_master_get_devdata(master); |
a5238e360 spi: s3c64xx: mov... |
1064 |
sdd->port_conf = s3c64xx_spi_get_port_config(pdev); |
230d42d42 spi: Add s3c64xx ... |
1065 1066 1067 1068 |
sdd->master = master; sdd->cntrlr_info = sci; sdd->pdev = pdev; sdd->sfr_start = mem_res->start; |
2b9080754 spi: s3c64xx: add... |
1069 1070 1071 |
if (pdev->dev.of_node) { ret = of_alias_get_id(pdev->dev.of_node, "spi"); if (ret < 0) { |
75bf33611 spi/s3c64xx: fix ... |
1072 1073 1074 |
dev_err(&pdev->dev, "failed to get alias id, errno %d ", ret); |
60a9a9644 spi: s3c64xx: ren... |
1075 |
goto err_deref_master; |
2b9080754 spi: s3c64xx: add... |
1076 1077 1078 1079 1080 |
} sdd->port_id = ret; } else { sdd->port_id = pdev->id; } |
230d42d42 spi: Add s3c64xx ... |
1081 1082 |
sdd->cur_bpw = 8; |
b5be04d35 spi: s3c64xx: Mod... |
1083 1084 |
sdd->tx_dma.direction = DMA_MEM_TO_DEV; sdd->rx_dma.direction = DMA_DEV_TO_MEM; |
2b9080754 spi: s3c64xx: add... |
1085 1086 |
master->dev.of_node = pdev->dev.of_node; |
a5238e360 spi: s3c64xx: mov... |
1087 |
master->bus_num = sdd->port_id; |
230d42d42 spi: Add s3c64xx ... |
1088 |
master->setup = s3c64xx_spi_setup; |
1c20c200e spi: s3c64xx: Rem... |
1089 |
master->cleanup = s3c64xx_spi_cleanup; |
ad2a99af0 spi/s3c64xx: Conv... |
1090 |
master->prepare_transfer_hardware = s3c64xx_spi_prepare_transfer; |
6bb9c0e34 spi/s3c64xx: Use ... |
1091 |
master->prepare_message = s3c64xx_spi_prepare_message; |
0732a9d2a spi/s3c64xx: Use ... |
1092 |
master->transfer_one = s3c64xx_spi_transfer_one; |
230d42d42 spi: Add s3c64xx ... |
1093 1094 |
master->num_chipselect = sci->num_cs; master->dma_alignment = 8; |
24778be20 spi: convert driv... |
1095 1096 |
master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) | SPI_BPW_MASK(8); |
230d42d42 spi: Add s3c64xx ... |
1097 1098 |
/* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; |
fc0f81b76 spi/s3c64xx: Use ... |
1099 |
master->auto_runtime_pm = true; |
3f2958879 spi/s3c64xx: Use ... |
1100 1101 |
if (!is_polling(sdd)) master->can_dma = s3c64xx_spi_can_dma; |
230d42d42 spi: Add s3c64xx ... |
1102 |
|
b0ee56052 spi: Convert to d... |
1103 1104 1105 |
sdd->regs = devm_ioremap_resource(&pdev->dev, mem_res); if (IS_ERR(sdd->regs)) { ret = PTR_ERR(sdd->regs); |
60a9a9644 spi: s3c64xx: ren... |
1106 |
goto err_deref_master; |
230d42d42 spi: Add s3c64xx ... |
1107 |
} |
00ab5392c spi/s3c64xx: let ... |
1108 |
if (sci->cfg_gpio && sci->cfg_gpio()) { |
230d42d42 spi: Add s3c64xx ... |
1109 1110 1111 |
dev_err(&pdev->dev, "Unable to config gpio "); ret = -EBUSY; |
60a9a9644 spi: s3c64xx: ren... |
1112 |
goto err_deref_master; |
230d42d42 spi: Add s3c64xx ... |
1113 1114 1115 |
} /* Setup clocks */ |
4eb770067 spi/s3c64xx: Use ... |
1116 |
sdd->clk = devm_clk_get(&pdev->dev, "spi"); |
230d42d42 spi: Add s3c64xx ... |
1117 1118 1119 1120 |
if (IS_ERR(sdd->clk)) { dev_err(&pdev->dev, "Unable to acquire clock 'spi' "); ret = PTR_ERR(sdd->clk); |
60a9a9644 spi: s3c64xx: ren... |
1121 |
goto err_deref_master; |
230d42d42 spi: Add s3c64xx ... |
1122 |
} |
25981d828 spi: s3c64xx: use... |
1123 1124 |
ret = clk_prepare_enable(sdd->clk); if (ret) { |
230d42d42 spi: Add s3c64xx ... |
1125 1126 |
dev_err(&pdev->dev, "Couldn't enable clock 'spi' "); |
60a9a9644 spi: s3c64xx: ren... |
1127 |
goto err_deref_master; |
230d42d42 spi: Add s3c64xx ... |
1128 |
} |
a24d850b9 spi/s3c64xx: Use ... |
1129 |
sprintf(clk_name, "spi_busclk%d", sci->src_clk_nr); |
4eb770067 spi/s3c64xx: Use ... |
1130 |
sdd->src_clk = devm_clk_get(&pdev->dev, clk_name); |
b0d5d6e55 spi/s3c64xx: Move... |
1131 |
if (IS_ERR(sdd->src_clk)) { |
230d42d42 spi: Add s3c64xx ... |
1132 |
dev_err(&pdev->dev, |
a24d850b9 spi/s3c64xx: Use ... |
1133 1134 |
"Unable to acquire clock '%s' ", clk_name); |
b0d5d6e55 spi/s3c64xx: Move... |
1135 |
ret = PTR_ERR(sdd->src_clk); |
60a9a9644 spi: s3c64xx: ren... |
1136 |
goto err_disable_clk; |
230d42d42 spi: Add s3c64xx ... |
1137 |
} |
25981d828 spi: s3c64xx: use... |
1138 1139 |
ret = clk_prepare_enable(sdd->src_clk); if (ret) { |
a24d850b9 spi/s3c64xx: Use ... |
1140 1141 |
dev_err(&pdev->dev, "Couldn't enable clock '%s' ", clk_name); |
60a9a9644 spi: s3c64xx: ren... |
1142 |
goto err_disable_clk; |
230d42d42 spi: Add s3c64xx ... |
1143 |
} |
7990b0081 spi: s3c64xx: add... |
1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 |
if (sdd->port_conf->clk_ioclk) { sdd->ioclk = devm_clk_get(&pdev->dev, "spi_ioclk"); if (IS_ERR(sdd->ioclk)) { dev_err(&pdev->dev, "Unable to acquire 'ioclk' "); ret = PTR_ERR(sdd->ioclk); goto err_disable_src_clk; } ret = clk_prepare_enable(sdd->ioclk); if (ret) { dev_err(&pdev->dev, "Couldn't enable clock 'ioclk' "); goto err_disable_src_clk; } } |
3d63a47a3 spi: s3c64xx: Don... |
1160 1161 |
if (!is_polling(sdd)) { /* Acquire DMA channels */ |
df1b01417 spi: s3c64xx: Use... |
1162 |
sdd->rx_dma.ch = dma_request_chan(&pdev->dev, "rx"); |
3d63a47a3 spi: s3c64xx: Don... |
1163 1164 1165 1166 1167 1168 |
if (IS_ERR(sdd->rx_dma.ch)) { dev_err(&pdev->dev, "Failed to get RX DMA channel "); ret = PTR_ERR(sdd->rx_dma.ch); goto err_disable_io_clk; } |
df1b01417 spi: s3c64xx: Use... |
1169 |
sdd->tx_dma.ch = dma_request_chan(&pdev->dev, "tx"); |
3d63a47a3 spi: s3c64xx: Don... |
1170 1171 1172 1173 |
if (IS_ERR(sdd->tx_dma.ch)) { dev_err(&pdev->dev, "Failed to get TX DMA channel "); ret = PTR_ERR(sdd->tx_dma.ch); |
72bc7ae06 spi: s3c64xx: pot... |
1174 |
goto err_release_rx_dma; |
3d63a47a3 spi: s3c64xx: Don... |
1175 1176 |
} } |
483867ee2 spi: s3c64xx: ext... |
1177 1178 1179 1180 1181 |
pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_TIMEOUT); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); |
230d42d42 spi: Add s3c64xx ... |
1182 |
/* Setup Deufult Mode */ |
1c75862d8 spi: spi-s3c64xx:... |
1183 |
s3c64xx_spi_hwinit(sdd); |
230d42d42 spi: Add s3c64xx ... |
1184 1185 1186 |
spin_lock_init(&sdd->lock); init_completion(&sdd->xfer_completion); |
230d42d42 spi: Add s3c64xx ... |
1187 |
|
4eb770067 spi/s3c64xx: Use ... |
1188 1189 |
ret = devm_request_irq(&pdev->dev, irq, s3c64xx_spi_irq, 0, "spi-s3c64xx", sdd); |
c2573128a spi/s3c64xx: Log ... |
1190 1191 1192 1193 |
if (ret != 0) { dev_err(&pdev->dev, "Failed to request IRQ %d: %d ", irq, ret); |
60a9a9644 spi: s3c64xx: ren... |
1194 |
goto err_pm_put; |
c2573128a spi/s3c64xx: Log ... |
1195 1196 1197 1198 1199 |
} writel(S3C64XX_SPI_INT_RX_OVERRUN_EN | S3C64XX_SPI_INT_RX_UNDERRUN_EN | S3C64XX_SPI_INT_TX_OVERRUN_EN | S3C64XX_SPI_INT_TX_UNDERRUN_EN, sdd->regs + S3C64XX_SPI_INT_EN); |
91800f0e9 spi/s3c64xx: Use ... |
1200 1201 1202 1203 |
ret = devm_spi_register_master(&pdev->dev, master); if (ret != 0) { dev_err(&pdev->dev, "cannot register SPI master: %d ", ret); |
60a9a9644 spi: s3c64xx: ren... |
1204 |
goto err_pm_put; |
230d42d42 spi: Add s3c64xx ... |
1205 |
} |
75bf33611 spi/s3c64xx: fix ... |
1206 1207 |
dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Slaves attached ", |
a5238e360 spi: s3c64xx: mov... |
1208 |
sdd->port_id, master->num_chipselect); |
6f8dc9d48 spi: s3c64xx: Do ... |
1209 1210 1211 |
dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tFIFO %dbytes ", mem_res, (FIFO_LVL_MASK(sdd) >> 1) + 1); |
230d42d42 spi: Add s3c64xx ... |
1212 |
|
483867ee2 spi: s3c64xx: ext... |
1213 1214 |
pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); |
230d42d42 spi: Add s3c64xx ... |
1215 |
return 0; |
60a9a9644 spi: s3c64xx: ren... |
1216 |
err_pm_put: |
483867ee2 spi: s3c64xx: ext... |
1217 |
pm_runtime_put_noidle(&pdev->dev); |
3c863792e spi: s3c64xx: cle... |
1218 1219 |
pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); |
483867ee2 spi: s3c64xx: ext... |
1220 |
|
3d63a47a3 spi: s3c64xx: Don... |
1221 |
if (!is_polling(sdd)) |
3d63a47a3 spi: s3c64xx: Don... |
1222 |
dma_release_channel(sdd->tx_dma.ch); |
72bc7ae06 spi: s3c64xx: pot... |
1223 1224 1225 |
err_release_rx_dma: if (!is_polling(sdd)) dma_release_channel(sdd->rx_dma.ch); |
3d63a47a3 spi: s3c64xx: Don... |
1226 |
err_disable_io_clk: |
7990b0081 spi: s3c64xx: add... |
1227 1228 |
clk_disable_unprepare(sdd->ioclk); err_disable_src_clk: |
9f667bff0 spi/s3c64xx: use ... |
1229 |
clk_disable_unprepare(sdd->src_clk); |
60a9a9644 spi: s3c64xx: ren... |
1230 |
err_disable_clk: |
9f667bff0 spi/s3c64xx: use ... |
1231 |
clk_disable_unprepare(sdd->clk); |
60a9a9644 spi: s3c64xx: ren... |
1232 |
err_deref_master: |
230d42d42 spi: Add s3c64xx ... |
1233 1234 1235 1236 1237 1238 1239 |
spi_master_put(master); return ret; } static int s3c64xx_spi_remove(struct platform_device *pdev) { |
9f135787b spi: s3c64xx: fix... |
1240 |
struct spi_master *master = platform_get_drvdata(pdev); |
230d42d42 spi: Add s3c64xx ... |
1241 |
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); |
230d42d42 spi: Add s3c64xx ... |
1242 |
|
8ebe9d163 spi: s3c64xx: rep... |
1243 |
pm_runtime_get_sync(&pdev->dev); |
b97b66217 spi/s3c64xx: Impl... |
1244 |
|
c2573128a spi/s3c64xx: Log ... |
1245 |
writel(0, sdd->regs + S3C64XX_SPI_INT_EN); |
3d63a47a3 spi: s3c64xx: Don... |
1246 1247 1248 1249 |
if (!is_polling(sdd)) { dma_release_channel(sdd->rx_dma.ch); dma_release_channel(sdd->tx_dma.ch); } |
7990b0081 spi: s3c64xx: add... |
1250 |
clk_disable_unprepare(sdd->ioclk); |
9f667bff0 spi/s3c64xx: use ... |
1251 |
clk_disable_unprepare(sdd->src_clk); |
230d42d42 spi: Add s3c64xx ... |
1252 |
|
9f667bff0 spi/s3c64xx: use ... |
1253 |
clk_disable_unprepare(sdd->clk); |
230d42d42 spi: Add s3c64xx ... |
1254 |
|
8ebe9d163 spi: s3c64xx: rep... |
1255 1256 1257 |
pm_runtime_put_noidle(&pdev->dev); pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); |
230d42d42 spi: Add s3c64xx ... |
1258 1259 |
return 0; } |
997230d02 spi/s3c64xx: add ... |
1260 |
#ifdef CONFIG_PM_SLEEP |
e25d0bf91 spi/s3c64xx: Conv... |
1261 |
static int s3c64xx_spi_suspend(struct device *dev) |
230d42d42 spi: Add s3c64xx ... |
1262 |
{ |
9a2a52452 spi/s3c64xx: Drop... |
1263 |
struct spi_master *master = dev_get_drvdata(dev); |
230d42d42 spi: Add s3c64xx ... |
1264 |
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); |
230d42d42 spi: Add s3c64xx ... |
1265 |
|
347de6bab spi/s3c64xx: Do n... |
1266 1267 1268 |
int ret = spi_master_suspend(master); if (ret) return ret; |
230d42d42 spi: Add s3c64xx ... |
1269 |
|
4fcd9b9e0 spi: s3c64xx: sim... |
1270 1271 1272 |
ret = pm_runtime_force_suspend(dev); if (ret < 0) return ret; |
230d42d42 spi: Add s3c64xx ... |
1273 1274 1275 1276 1277 |
sdd->cur_speed = 0; /* Output Clock is stopped */ return 0; } |
e25d0bf91 spi/s3c64xx: Conv... |
1278 |
static int s3c64xx_spi_resume(struct device *dev) |
230d42d42 spi: Add s3c64xx ... |
1279 |
{ |
9a2a52452 spi/s3c64xx: Drop... |
1280 |
struct spi_master *master = dev_get_drvdata(dev); |
230d42d42 spi: Add s3c64xx ... |
1281 |
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); |
ad7de729c spi/s3c64xx: Rena... |
1282 |
struct s3c64xx_spi_info *sci = sdd->cntrlr_info; |
4fcd9b9e0 spi: s3c64xx: sim... |
1283 |
int ret; |
230d42d42 spi: Add s3c64xx ... |
1284 |
|
00ab5392c spi/s3c64xx: let ... |
1285 |
if (sci->cfg_gpio) |
2b9080754 spi: s3c64xx: add... |
1286 |
sci->cfg_gpio(); |
230d42d42 spi: Add s3c64xx ... |
1287 |
|
4fcd9b9e0 spi: s3c64xx: sim... |
1288 1289 1290 |
ret = pm_runtime_force_resume(dev); if (ret < 0) return ret; |
230d42d42 spi: Add s3c64xx ... |
1291 |
|
347de6bab spi/s3c64xx: Do n... |
1292 |
return spi_master_resume(master); |
230d42d42 spi: Add s3c64xx ... |
1293 |
} |
997230d02 spi/s3c64xx: add ... |
1294 |
#endif /* CONFIG_PM_SLEEP */ |
230d42d42 spi: Add s3c64xx ... |
1295 |
|
ec8330503 spi: Replace CONF... |
1296 |
#ifdef CONFIG_PM |
b97b66217 spi/s3c64xx: Impl... |
1297 1298 |
static int s3c64xx_spi_runtime_suspend(struct device *dev) { |
9a2a52452 spi/s3c64xx: Drop... |
1299 |
struct spi_master *master = dev_get_drvdata(dev); |
b97b66217 spi/s3c64xx: Impl... |
1300 |
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); |
9f667bff0 spi/s3c64xx: use ... |
1301 1302 |
clk_disable_unprepare(sdd->clk); clk_disable_unprepare(sdd->src_clk); |
7990b0081 spi: s3c64xx: add... |
1303 |
clk_disable_unprepare(sdd->ioclk); |
b97b66217 spi/s3c64xx: Impl... |
1304 1305 1306 1307 1308 1309 |
return 0; } static int s3c64xx_spi_runtime_resume(struct device *dev) { |
9a2a52452 spi/s3c64xx: Drop... |
1310 |
struct spi_master *master = dev_get_drvdata(dev); |
b97b66217 spi/s3c64xx: Impl... |
1311 |
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); |
8b06d5b85 spi/s3c64xx: Chec... |
1312 |
int ret; |
b97b66217 spi/s3c64xx: Impl... |
1313 |
|
7990b0081 spi: s3c64xx: add... |
1314 1315 1316 1317 1318 |
if (sdd->port_conf->clk_ioclk) { ret = clk_prepare_enable(sdd->ioclk); if (ret != 0) return ret; } |
8b06d5b85 spi/s3c64xx: Chec... |
1319 1320 |
ret = clk_prepare_enable(sdd->src_clk); if (ret != 0) |
7990b0081 spi: s3c64xx: add... |
1321 |
goto err_disable_ioclk; |
8b06d5b85 spi/s3c64xx: Chec... |
1322 1323 |
ret = clk_prepare_enable(sdd->clk); |
7990b0081 spi: s3c64xx: add... |
1324 1325 |
if (ret != 0) goto err_disable_src_clk; |
b97b66217 spi/s3c64xx: Impl... |
1326 |
|
e935dba11 spi: spi-s3c64xx:... |
1327 |
s3c64xx_spi_hwinit(sdd); |
3f32131fb spi: spi-s3c64xx:... |
1328 1329 1330 |
writel(S3C64XX_SPI_INT_RX_OVERRUN_EN | S3C64XX_SPI_INT_RX_UNDERRUN_EN | S3C64XX_SPI_INT_TX_OVERRUN_EN | S3C64XX_SPI_INT_TX_UNDERRUN_EN, sdd->regs + S3C64XX_SPI_INT_EN); |
b97b66217 spi/s3c64xx: Impl... |
1331 |
return 0; |
7990b0081 spi: s3c64xx: add... |
1332 1333 1334 1335 1336 1337 1338 |
err_disable_src_clk: clk_disable_unprepare(sdd->src_clk); err_disable_ioclk: clk_disable_unprepare(sdd->ioclk); return ret; |
b97b66217 spi/s3c64xx: Impl... |
1339 |
} |
ec8330503 spi: Replace CONF... |
1340 |
#endif /* CONFIG_PM */ |
b97b66217 spi/s3c64xx: Impl... |
1341 |
|
e25d0bf91 spi/s3c64xx: Conv... |
1342 1343 |
static const struct dev_pm_ops s3c64xx_spi_pm = { SET_SYSTEM_SLEEP_PM_OPS(s3c64xx_spi_suspend, s3c64xx_spi_resume) |
b97b66217 spi/s3c64xx: Impl... |
1344 1345 |
SET_RUNTIME_PM_OPS(s3c64xx_spi_runtime_suspend, s3c64xx_spi_runtime_resume, NULL) |
e25d0bf91 spi/s3c64xx: Conv... |
1346 |
}; |
10ce0473e spi/s3c64xx: Add ... |
1347 |
static struct s3c64xx_spi_port_config s3c2443_spi_port_config = { |
a5238e360 spi: s3c64xx: mov... |
1348 1349 1350 1351 1352 |
.fifo_lvl_mask = { 0x7f }, .rx_lvl_offset = 13, .tx_st_done = 21, .high_speed = true, }; |
10ce0473e spi/s3c64xx: Add ... |
1353 |
static struct s3c64xx_spi_port_config s3c6410_spi_port_config = { |
a5238e360 spi: s3c64xx: mov... |
1354 1355 1356 1357 |
.fifo_lvl_mask = { 0x7f, 0x7F }, .rx_lvl_offset = 13, .tx_st_done = 21, }; |
10ce0473e spi/s3c64xx: Add ... |
1358 |
static struct s3c64xx_spi_port_config s5pv210_spi_port_config = { |
a5238e360 spi: s3c64xx: mov... |
1359 1360 1361 1362 1363 |
.fifo_lvl_mask = { 0x1ff, 0x7F }, .rx_lvl_offset = 15, .tx_st_done = 25, .high_speed = true, }; |
10ce0473e spi/s3c64xx: Add ... |
1364 |
static struct s3c64xx_spi_port_config exynos4_spi_port_config = { |
a5238e360 spi: s3c64xx: mov... |
1365 1366 1367 1368 1369 |
.fifo_lvl_mask = { 0x1ff, 0x7F, 0x7F }, .rx_lvl_offset = 15, .tx_st_done = 25, .high_speed = true, .clk_from_cmu = true, |
ab4efca29 spi: spi-s3s64xx:... |
1370 |
.quirks = S3C64XX_SPI_QUIRK_CS_AUTO, |
a5238e360 spi: s3c64xx: mov... |
1371 |
}; |
bf77cba95 spi: s3c64xx: add... |
1372 1373 1374 1375 1376 1377 1378 1379 |
static struct s3c64xx_spi_port_config exynos7_spi_port_config = { .fifo_lvl_mask = { 0x1ff, 0x7F, 0x7F, 0x7F, 0x7F, 0x1ff}, .rx_lvl_offset = 15, .tx_st_done = 25, .high_speed = true, .clk_from_cmu = true, .quirks = S3C64XX_SPI_QUIRK_CS_AUTO, }; |
7990b0081 spi: s3c64xx: add... |
1380 1381 1382 1383 1384 1385 1386 1387 1388 |
static struct s3c64xx_spi_port_config exynos5433_spi_port_config = { .fifo_lvl_mask = { 0x1ff, 0x7f, 0x7f, 0x7f, 0x7f, 0x1ff}, .rx_lvl_offset = 15, .tx_st_done = 25, .high_speed = true, .clk_from_cmu = true, .clk_ioclk = true, .quirks = S3C64XX_SPI_QUIRK_CS_AUTO, }; |
23f6d39ec spi: s3c64xx: Con... |
1389 |
static const struct platform_device_id s3c64xx_spi_driver_ids[] = { |
a5238e360 spi: s3c64xx: mov... |
1390 1391 1392 1393 1394 1395 |
{ .name = "s3c2443-spi", .driver_data = (kernel_ulong_t)&s3c2443_spi_port_config, }, { .name = "s3c6410-spi", .driver_data = (kernel_ulong_t)&s3c6410_spi_port_config, |
a5238e360 spi: s3c64xx: mov... |
1396 1397 1398 |
}, { }, }; |
2b9080754 spi: s3c64xx: add... |
1399 |
static const struct of_device_id s3c64xx_spi_dt_match[] = { |
a3b924df8 spi: s3c64xx: Add... |
1400 1401 1402 1403 1404 1405 |
{ .compatible = "samsung,s3c2443-spi", .data = (void *)&s3c2443_spi_port_config, }, { .compatible = "samsung,s3c6410-spi", .data = (void *)&s3c6410_spi_port_config, }, |
a3b924df8 spi: s3c64xx: Add... |
1406 1407 1408 |
{ .compatible = "samsung,s5pv210-spi", .data = (void *)&s5pv210_spi_port_config, }, |
2b9080754 spi: s3c64xx: add... |
1409 1410 1411 |
{ .compatible = "samsung,exynos4210-spi", .data = (void *)&exynos4_spi_port_config, }, |
bf77cba95 spi: s3c64xx: add... |
1412 1413 1414 |
{ .compatible = "samsung,exynos7-spi", .data = (void *)&exynos7_spi_port_config, }, |
7990b0081 spi: s3c64xx: add... |
1415 1416 1417 |
{ .compatible = "samsung,exynos5433-spi", .data = (void *)&exynos5433_spi_port_config, }, |
2b9080754 spi: s3c64xx: add... |
1418 1419 1420 |
{ }, }; MODULE_DEVICE_TABLE(of, s3c64xx_spi_dt_match); |
2b9080754 spi: s3c64xx: add... |
1421 |
|
230d42d42 spi: Add s3c64xx ... |
1422 1423 1424 |
static struct platform_driver s3c64xx_spi_driver = { .driver = { .name = "s3c64xx-spi", |
e25d0bf91 spi/s3c64xx: Conv... |
1425 |
.pm = &s3c64xx_spi_pm, |
2b9080754 spi: s3c64xx: add... |
1426 |
.of_match_table = of_match_ptr(s3c64xx_spi_dt_match), |
230d42d42 spi: Add s3c64xx ... |
1427 |
}, |
50c959fc3 spi: spi-s3c64xx:... |
1428 |
.probe = s3c64xx_spi_probe, |
230d42d42 spi: Add s3c64xx ... |
1429 |
.remove = s3c64xx_spi_remove, |
a5238e360 spi: s3c64xx: mov... |
1430 |
.id_table = s3c64xx_spi_driver_ids, |
230d42d42 spi: Add s3c64xx ... |
1431 1432 |
}; MODULE_ALIAS("platform:s3c64xx-spi"); |
50c959fc3 spi: spi-s3c64xx:... |
1433 |
module_platform_driver(s3c64xx_spi_driver); |
230d42d42 spi: Add s3c64xx ... |
1434 1435 1436 1437 |
MODULE_AUTHOR("Jaswinder Singh <jassi.brar@samsung.com>"); MODULE_DESCRIPTION("S3C64XX SPI Controller Driver"); MODULE_LICENSE("GPL"); |