18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Freescale/Motorola Coldfire Queued SPI driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2010 Steven King <sfking@fdwdc.com> 68c2ecf20Sopenharmony_ci*/ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/sched.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/clk.h> 178c2ecf20Sopenharmony_ci#include <linux/err.h> 188c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 198c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <asm/coldfire.h> 228c2ecf20Sopenharmony_ci#include <asm/mcfsim.h> 238c2ecf20Sopenharmony_ci#include <asm/mcfqspi.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define DRIVER_NAME "mcfqspi" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define MCFQSPI_BUSCLK (MCF_BUSCLK / 2) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define MCFQSPI_QMR 0x00 308c2ecf20Sopenharmony_ci#define MCFQSPI_QMR_MSTR 0x8000 318c2ecf20Sopenharmony_ci#define MCFQSPI_QMR_CPOL 0x0200 328c2ecf20Sopenharmony_ci#define MCFQSPI_QMR_CPHA 0x0100 338c2ecf20Sopenharmony_ci#define MCFQSPI_QDLYR 0x04 348c2ecf20Sopenharmony_ci#define MCFQSPI_QDLYR_SPE 0x8000 358c2ecf20Sopenharmony_ci#define MCFQSPI_QWR 0x08 368c2ecf20Sopenharmony_ci#define MCFQSPI_QWR_HALT 0x8000 378c2ecf20Sopenharmony_ci#define MCFQSPI_QWR_WREN 0x4000 388c2ecf20Sopenharmony_ci#define MCFQSPI_QWR_CSIV 0x1000 398c2ecf20Sopenharmony_ci#define MCFQSPI_QIR 0x0C 408c2ecf20Sopenharmony_ci#define MCFQSPI_QIR_WCEFB 0x8000 418c2ecf20Sopenharmony_ci#define MCFQSPI_QIR_ABRTB 0x4000 428c2ecf20Sopenharmony_ci#define MCFQSPI_QIR_ABRTL 0x1000 438c2ecf20Sopenharmony_ci#define MCFQSPI_QIR_WCEFE 0x0800 448c2ecf20Sopenharmony_ci#define MCFQSPI_QIR_ABRTE 0x0400 458c2ecf20Sopenharmony_ci#define MCFQSPI_QIR_SPIFE 0x0100 468c2ecf20Sopenharmony_ci#define MCFQSPI_QIR_WCEF 0x0008 478c2ecf20Sopenharmony_ci#define MCFQSPI_QIR_ABRT 0x0004 488c2ecf20Sopenharmony_ci#define MCFQSPI_QIR_SPIF 0x0001 498c2ecf20Sopenharmony_ci#define MCFQSPI_QAR 0x010 508c2ecf20Sopenharmony_ci#define MCFQSPI_QAR_TXBUF 0x00 518c2ecf20Sopenharmony_ci#define MCFQSPI_QAR_RXBUF 0x10 528c2ecf20Sopenharmony_ci#define MCFQSPI_QAR_CMDBUF 0x20 538c2ecf20Sopenharmony_ci#define MCFQSPI_QDR 0x014 548c2ecf20Sopenharmony_ci#define MCFQSPI_QCR 0x014 558c2ecf20Sopenharmony_ci#define MCFQSPI_QCR_CONT 0x8000 568c2ecf20Sopenharmony_ci#define MCFQSPI_QCR_BITSE 0x4000 578c2ecf20Sopenharmony_ci#define MCFQSPI_QCR_DT 0x2000 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistruct mcfqspi { 608c2ecf20Sopenharmony_ci void __iomem *iobase; 618c2ecf20Sopenharmony_ci int irq; 628c2ecf20Sopenharmony_ci struct clk *clk; 638c2ecf20Sopenharmony_ci struct mcfqspi_cs_control *cs_control; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci wait_queue_head_t waitq; 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void mcfqspi_wr_qmr(struct mcfqspi *mcfqspi, u16 val) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci writew(val, mcfqspi->iobase + MCFQSPI_QMR); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void mcfqspi_wr_qdlyr(struct mcfqspi *mcfqspi, u16 val) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci writew(val, mcfqspi->iobase + MCFQSPI_QDLYR); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic u16 mcfqspi_rd_qdlyr(struct mcfqspi *mcfqspi) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci return readw(mcfqspi->iobase + MCFQSPI_QDLYR); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic void mcfqspi_wr_qwr(struct mcfqspi *mcfqspi, u16 val) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci writew(val, mcfqspi->iobase + MCFQSPI_QWR); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic void mcfqspi_wr_qir(struct mcfqspi *mcfqspi, u16 val) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci writew(val, mcfqspi->iobase + MCFQSPI_QIR); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic void mcfqspi_wr_qar(struct mcfqspi *mcfqspi, u16 val) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci writew(val, mcfqspi->iobase + MCFQSPI_QAR); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic void mcfqspi_wr_qdr(struct mcfqspi *mcfqspi, u16 val) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci writew(val, mcfqspi->iobase + MCFQSPI_QDR); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic u16 mcfqspi_rd_qdr(struct mcfqspi *mcfqspi) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci return readw(mcfqspi->iobase + MCFQSPI_QDR); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic void mcfqspi_cs_select(struct mcfqspi *mcfqspi, u8 chip_select, 1098c2ecf20Sopenharmony_ci bool cs_high) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci mcfqspi->cs_control->select(mcfqspi->cs_control, chip_select, cs_high); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic void mcfqspi_cs_deselect(struct mcfqspi *mcfqspi, u8 chip_select, 1158c2ecf20Sopenharmony_ci bool cs_high) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci mcfqspi->cs_control->deselect(mcfqspi->cs_control, chip_select, cs_high); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int mcfqspi_cs_setup(struct mcfqspi *mcfqspi) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci return (mcfqspi->cs_control->setup) ? 1238c2ecf20Sopenharmony_ci mcfqspi->cs_control->setup(mcfqspi->cs_control) : 0; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void mcfqspi_cs_teardown(struct mcfqspi *mcfqspi) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci if (mcfqspi->cs_control->teardown) 1298c2ecf20Sopenharmony_ci mcfqspi->cs_control->teardown(mcfqspi->cs_control); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic u8 mcfqspi_qmr_baud(u32 speed_hz) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci return clamp((MCFQSPI_BUSCLK + speed_hz - 1) / speed_hz, 2u, 255u); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic bool mcfqspi_qdlyr_spe(struct mcfqspi *mcfqspi) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci return mcfqspi_rd_qdlyr(mcfqspi) & MCFQSPI_QDLYR_SPE; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic irqreturn_t mcfqspi_irq_handler(int this_irq, void *dev_id) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct mcfqspi *mcfqspi = dev_id; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* clear interrupt */ 1478c2ecf20Sopenharmony_ci mcfqspi_wr_qir(mcfqspi, MCFQSPI_QIR_SPIFE | MCFQSPI_QIR_SPIF); 1488c2ecf20Sopenharmony_ci wake_up(&mcfqspi->waitq); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic void mcfqspi_transfer_msg8(struct mcfqspi *mcfqspi, unsigned count, 1548c2ecf20Sopenharmony_ci const u8 *txbuf, u8 *rxbuf) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci unsigned i, n, offset = 0; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci n = min(count, 16u); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci mcfqspi_wr_qar(mcfqspi, MCFQSPI_QAR_CMDBUF); 1618c2ecf20Sopenharmony_ci for (i = 0; i < n; ++i) 1628c2ecf20Sopenharmony_ci mcfqspi_wr_qdr(mcfqspi, MCFQSPI_QCR_BITSE); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci mcfqspi_wr_qar(mcfqspi, MCFQSPI_QAR_TXBUF); 1658c2ecf20Sopenharmony_ci if (txbuf) 1668c2ecf20Sopenharmony_ci for (i = 0; i < n; ++i) 1678c2ecf20Sopenharmony_ci mcfqspi_wr_qdr(mcfqspi, *txbuf++); 1688c2ecf20Sopenharmony_ci else 1698c2ecf20Sopenharmony_ci for (i = 0; i < count; ++i) 1708c2ecf20Sopenharmony_ci mcfqspi_wr_qdr(mcfqspi, 0); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci count -= n; 1738c2ecf20Sopenharmony_ci if (count) { 1748c2ecf20Sopenharmony_ci u16 qwr = 0xf08; 1758c2ecf20Sopenharmony_ci mcfqspi_wr_qwr(mcfqspi, 0x700); 1768c2ecf20Sopenharmony_ci mcfqspi_wr_qdlyr(mcfqspi, MCFQSPI_QDLYR_SPE); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci do { 1798c2ecf20Sopenharmony_ci wait_event(mcfqspi->waitq, !mcfqspi_qdlyr_spe(mcfqspi)); 1808c2ecf20Sopenharmony_ci mcfqspi_wr_qwr(mcfqspi, qwr); 1818c2ecf20Sopenharmony_ci mcfqspi_wr_qdlyr(mcfqspi, MCFQSPI_QDLYR_SPE); 1828c2ecf20Sopenharmony_ci if (rxbuf) { 1838c2ecf20Sopenharmony_ci mcfqspi_wr_qar(mcfqspi, 1848c2ecf20Sopenharmony_ci MCFQSPI_QAR_RXBUF + offset); 1858c2ecf20Sopenharmony_ci for (i = 0; i < 8; ++i) 1868c2ecf20Sopenharmony_ci *rxbuf++ = mcfqspi_rd_qdr(mcfqspi); 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci n = min(count, 8u); 1898c2ecf20Sopenharmony_ci if (txbuf) { 1908c2ecf20Sopenharmony_ci mcfqspi_wr_qar(mcfqspi, 1918c2ecf20Sopenharmony_ci MCFQSPI_QAR_TXBUF + offset); 1928c2ecf20Sopenharmony_ci for (i = 0; i < n; ++i) 1938c2ecf20Sopenharmony_ci mcfqspi_wr_qdr(mcfqspi, *txbuf++); 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci qwr = (offset ? 0x808 : 0) + ((n - 1) << 8); 1968c2ecf20Sopenharmony_ci offset ^= 8; 1978c2ecf20Sopenharmony_ci count -= n; 1988c2ecf20Sopenharmony_ci } while (count); 1998c2ecf20Sopenharmony_ci wait_event(mcfqspi->waitq, !mcfqspi_qdlyr_spe(mcfqspi)); 2008c2ecf20Sopenharmony_ci mcfqspi_wr_qwr(mcfqspi, qwr); 2018c2ecf20Sopenharmony_ci mcfqspi_wr_qdlyr(mcfqspi, MCFQSPI_QDLYR_SPE); 2028c2ecf20Sopenharmony_ci if (rxbuf) { 2038c2ecf20Sopenharmony_ci mcfqspi_wr_qar(mcfqspi, MCFQSPI_QAR_RXBUF + offset); 2048c2ecf20Sopenharmony_ci for (i = 0; i < 8; ++i) 2058c2ecf20Sopenharmony_ci *rxbuf++ = mcfqspi_rd_qdr(mcfqspi); 2068c2ecf20Sopenharmony_ci offset ^= 8; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci } else { 2098c2ecf20Sopenharmony_ci mcfqspi_wr_qwr(mcfqspi, (n - 1) << 8); 2108c2ecf20Sopenharmony_ci mcfqspi_wr_qdlyr(mcfqspi, MCFQSPI_QDLYR_SPE); 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci wait_event(mcfqspi->waitq, !mcfqspi_qdlyr_spe(mcfqspi)); 2138c2ecf20Sopenharmony_ci if (rxbuf) { 2148c2ecf20Sopenharmony_ci mcfqspi_wr_qar(mcfqspi, MCFQSPI_QAR_RXBUF + offset); 2158c2ecf20Sopenharmony_ci for (i = 0; i < n; ++i) 2168c2ecf20Sopenharmony_ci *rxbuf++ = mcfqspi_rd_qdr(mcfqspi); 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic void mcfqspi_transfer_msg16(struct mcfqspi *mcfqspi, unsigned count, 2218c2ecf20Sopenharmony_ci const u16 *txbuf, u16 *rxbuf) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci unsigned i, n, offset = 0; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci n = min(count, 16u); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci mcfqspi_wr_qar(mcfqspi, MCFQSPI_QAR_CMDBUF); 2288c2ecf20Sopenharmony_ci for (i = 0; i < n; ++i) 2298c2ecf20Sopenharmony_ci mcfqspi_wr_qdr(mcfqspi, MCFQSPI_QCR_BITSE); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci mcfqspi_wr_qar(mcfqspi, MCFQSPI_QAR_TXBUF); 2328c2ecf20Sopenharmony_ci if (txbuf) 2338c2ecf20Sopenharmony_ci for (i = 0; i < n; ++i) 2348c2ecf20Sopenharmony_ci mcfqspi_wr_qdr(mcfqspi, *txbuf++); 2358c2ecf20Sopenharmony_ci else 2368c2ecf20Sopenharmony_ci for (i = 0; i < count; ++i) 2378c2ecf20Sopenharmony_ci mcfqspi_wr_qdr(mcfqspi, 0); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci count -= n; 2408c2ecf20Sopenharmony_ci if (count) { 2418c2ecf20Sopenharmony_ci u16 qwr = 0xf08; 2428c2ecf20Sopenharmony_ci mcfqspi_wr_qwr(mcfqspi, 0x700); 2438c2ecf20Sopenharmony_ci mcfqspi_wr_qdlyr(mcfqspi, MCFQSPI_QDLYR_SPE); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci do { 2468c2ecf20Sopenharmony_ci wait_event(mcfqspi->waitq, !mcfqspi_qdlyr_spe(mcfqspi)); 2478c2ecf20Sopenharmony_ci mcfqspi_wr_qwr(mcfqspi, qwr); 2488c2ecf20Sopenharmony_ci mcfqspi_wr_qdlyr(mcfqspi, MCFQSPI_QDLYR_SPE); 2498c2ecf20Sopenharmony_ci if (rxbuf) { 2508c2ecf20Sopenharmony_ci mcfqspi_wr_qar(mcfqspi, 2518c2ecf20Sopenharmony_ci MCFQSPI_QAR_RXBUF + offset); 2528c2ecf20Sopenharmony_ci for (i = 0; i < 8; ++i) 2538c2ecf20Sopenharmony_ci *rxbuf++ = mcfqspi_rd_qdr(mcfqspi); 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci n = min(count, 8u); 2568c2ecf20Sopenharmony_ci if (txbuf) { 2578c2ecf20Sopenharmony_ci mcfqspi_wr_qar(mcfqspi, 2588c2ecf20Sopenharmony_ci MCFQSPI_QAR_TXBUF + offset); 2598c2ecf20Sopenharmony_ci for (i = 0; i < n; ++i) 2608c2ecf20Sopenharmony_ci mcfqspi_wr_qdr(mcfqspi, *txbuf++); 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci qwr = (offset ? 0x808 : 0x000) + ((n - 1) << 8); 2638c2ecf20Sopenharmony_ci offset ^= 8; 2648c2ecf20Sopenharmony_ci count -= n; 2658c2ecf20Sopenharmony_ci } while (count); 2668c2ecf20Sopenharmony_ci wait_event(mcfqspi->waitq, !mcfqspi_qdlyr_spe(mcfqspi)); 2678c2ecf20Sopenharmony_ci mcfqspi_wr_qwr(mcfqspi, qwr); 2688c2ecf20Sopenharmony_ci mcfqspi_wr_qdlyr(mcfqspi, MCFQSPI_QDLYR_SPE); 2698c2ecf20Sopenharmony_ci if (rxbuf) { 2708c2ecf20Sopenharmony_ci mcfqspi_wr_qar(mcfqspi, MCFQSPI_QAR_RXBUF + offset); 2718c2ecf20Sopenharmony_ci for (i = 0; i < 8; ++i) 2728c2ecf20Sopenharmony_ci *rxbuf++ = mcfqspi_rd_qdr(mcfqspi); 2738c2ecf20Sopenharmony_ci offset ^= 8; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci } else { 2768c2ecf20Sopenharmony_ci mcfqspi_wr_qwr(mcfqspi, (n - 1) << 8); 2778c2ecf20Sopenharmony_ci mcfqspi_wr_qdlyr(mcfqspi, MCFQSPI_QDLYR_SPE); 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci wait_event(mcfqspi->waitq, !mcfqspi_qdlyr_spe(mcfqspi)); 2808c2ecf20Sopenharmony_ci if (rxbuf) { 2818c2ecf20Sopenharmony_ci mcfqspi_wr_qar(mcfqspi, MCFQSPI_QAR_RXBUF + offset); 2828c2ecf20Sopenharmony_ci for (i = 0; i < n; ++i) 2838c2ecf20Sopenharmony_ci *rxbuf++ = mcfqspi_rd_qdr(mcfqspi); 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic void mcfqspi_set_cs(struct spi_device *spi, bool enable) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct mcfqspi *mcfqspi = spi_master_get_devdata(spi->master); 2908c2ecf20Sopenharmony_ci bool cs_high = spi->mode & SPI_CS_HIGH; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (enable) 2938c2ecf20Sopenharmony_ci mcfqspi_cs_select(mcfqspi, spi->chip_select, cs_high); 2948c2ecf20Sopenharmony_ci else 2958c2ecf20Sopenharmony_ci mcfqspi_cs_deselect(mcfqspi, spi->chip_select, cs_high); 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int mcfqspi_transfer_one(struct spi_master *master, 2998c2ecf20Sopenharmony_ci struct spi_device *spi, 3008c2ecf20Sopenharmony_ci struct spi_transfer *t) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct mcfqspi *mcfqspi = spi_master_get_devdata(master); 3038c2ecf20Sopenharmony_ci u16 qmr = MCFQSPI_QMR_MSTR; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci qmr |= t->bits_per_word << 10; 3068c2ecf20Sopenharmony_ci if (spi->mode & SPI_CPHA) 3078c2ecf20Sopenharmony_ci qmr |= MCFQSPI_QMR_CPHA; 3088c2ecf20Sopenharmony_ci if (spi->mode & SPI_CPOL) 3098c2ecf20Sopenharmony_ci qmr |= MCFQSPI_QMR_CPOL; 3108c2ecf20Sopenharmony_ci qmr |= mcfqspi_qmr_baud(t->speed_hz); 3118c2ecf20Sopenharmony_ci mcfqspi_wr_qmr(mcfqspi, qmr); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci mcfqspi_wr_qir(mcfqspi, MCFQSPI_QIR_SPIFE); 3148c2ecf20Sopenharmony_ci if (t->bits_per_word == 8) 3158c2ecf20Sopenharmony_ci mcfqspi_transfer_msg8(mcfqspi, t->len, t->tx_buf, t->rx_buf); 3168c2ecf20Sopenharmony_ci else 3178c2ecf20Sopenharmony_ci mcfqspi_transfer_msg16(mcfqspi, t->len / 2, t->tx_buf, 3188c2ecf20Sopenharmony_ci t->rx_buf); 3198c2ecf20Sopenharmony_ci mcfqspi_wr_qir(mcfqspi, 0); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic int mcfqspi_setup(struct spi_device *spi) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci mcfqspi_cs_deselect(spi_master_get_devdata(spi->master), 3278c2ecf20Sopenharmony_ci spi->chip_select, spi->mode & SPI_CS_HIGH); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci dev_dbg(&spi->dev, 3308c2ecf20Sopenharmony_ci "bits per word %d, chip select %d, speed %d KHz\n", 3318c2ecf20Sopenharmony_ci spi->bits_per_word, spi->chip_select, 3328c2ecf20Sopenharmony_ci (MCFQSPI_BUSCLK / mcfqspi_qmr_baud(spi->max_speed_hz)) 3338c2ecf20Sopenharmony_ci / 1000); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci return 0; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int mcfqspi_probe(struct platform_device *pdev) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct spi_master *master; 3418c2ecf20Sopenharmony_ci struct mcfqspi *mcfqspi; 3428c2ecf20Sopenharmony_ci struct mcfqspi_platform_data *pdata; 3438c2ecf20Sopenharmony_ci int status; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci pdata = dev_get_platdata(&pdev->dev); 3468c2ecf20Sopenharmony_ci if (!pdata) { 3478c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "platform data is missing\n"); 3488c2ecf20Sopenharmony_ci return -ENOENT; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (!pdata->cs_control) { 3528c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "pdata->cs_control is NULL\n"); 3538c2ecf20Sopenharmony_ci return -EINVAL; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci master = spi_alloc_master(&pdev->dev, sizeof(*mcfqspi)); 3578c2ecf20Sopenharmony_ci if (master == NULL) { 3588c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "spi_alloc_master failed\n"); 3598c2ecf20Sopenharmony_ci return -ENOMEM; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci mcfqspi = spi_master_get_devdata(master); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci mcfqspi->iobase = devm_platform_ioremap_resource(pdev, 0); 3658c2ecf20Sopenharmony_ci if (IS_ERR(mcfqspi->iobase)) { 3668c2ecf20Sopenharmony_ci status = PTR_ERR(mcfqspi->iobase); 3678c2ecf20Sopenharmony_ci goto fail0; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci mcfqspi->irq = platform_get_irq(pdev, 0); 3718c2ecf20Sopenharmony_ci if (mcfqspi->irq < 0) { 3728c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "platform_get_irq failed\n"); 3738c2ecf20Sopenharmony_ci status = -ENXIO; 3748c2ecf20Sopenharmony_ci goto fail0; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci status = devm_request_irq(&pdev->dev, mcfqspi->irq, mcfqspi_irq_handler, 3788c2ecf20Sopenharmony_ci 0, pdev->name, mcfqspi); 3798c2ecf20Sopenharmony_ci if (status) { 3808c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "request_irq failed\n"); 3818c2ecf20Sopenharmony_ci goto fail0; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci mcfqspi->clk = devm_clk_get(&pdev->dev, "qspi_clk"); 3858c2ecf20Sopenharmony_ci if (IS_ERR(mcfqspi->clk)) { 3868c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "clk_get failed\n"); 3878c2ecf20Sopenharmony_ci status = PTR_ERR(mcfqspi->clk); 3888c2ecf20Sopenharmony_ci goto fail0; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci clk_prepare_enable(mcfqspi->clk); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci master->bus_num = pdata->bus_num; 3938c2ecf20Sopenharmony_ci master->num_chipselect = pdata->num_chipselect; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci mcfqspi->cs_control = pdata->cs_control; 3968c2ecf20Sopenharmony_ci status = mcfqspi_cs_setup(mcfqspi); 3978c2ecf20Sopenharmony_ci if (status) { 3988c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "error initializing cs_control\n"); 3998c2ecf20Sopenharmony_ci goto fail1; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci init_waitqueue_head(&mcfqspi->waitq); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci master->mode_bits = SPI_CS_HIGH | SPI_CPOL | SPI_CPHA; 4058c2ecf20Sopenharmony_ci master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 16); 4068c2ecf20Sopenharmony_ci master->setup = mcfqspi_setup; 4078c2ecf20Sopenharmony_ci master->set_cs = mcfqspi_set_cs; 4088c2ecf20Sopenharmony_ci master->transfer_one = mcfqspi_transfer_one; 4098c2ecf20Sopenharmony_ci master->auto_runtime_pm = true; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, master); 4128c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci status = devm_spi_register_master(&pdev->dev, master); 4158c2ecf20Sopenharmony_ci if (status) { 4168c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "spi_register_master failed\n"); 4178c2ecf20Sopenharmony_ci goto fail2; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Coldfire QSPI bus driver\n"); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci return 0; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cifail2: 4258c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 4268c2ecf20Sopenharmony_ci mcfqspi_cs_teardown(mcfqspi); 4278c2ecf20Sopenharmony_cifail1: 4288c2ecf20Sopenharmony_ci clk_disable_unprepare(mcfqspi->clk); 4298c2ecf20Sopenharmony_cifail0: 4308c2ecf20Sopenharmony_ci spi_master_put(master); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Coldfire QSPI probe failed\n"); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci return status; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic int mcfqspi_remove(struct platform_device *pdev) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci struct spi_master *master = platform_get_drvdata(pdev); 4408c2ecf20Sopenharmony_ci struct mcfqspi *mcfqspi = spi_master_get_devdata(master); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 4438c2ecf20Sopenharmony_ci /* disable the hardware (set the baud rate to 0) */ 4448c2ecf20Sopenharmony_ci mcfqspi_wr_qmr(mcfqspi, MCFQSPI_QMR_MSTR); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci mcfqspi_cs_teardown(mcfqspi); 4478c2ecf20Sopenharmony_ci clk_disable_unprepare(mcfqspi->clk); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci return 0; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4538c2ecf20Sopenharmony_cistatic int mcfqspi_suspend(struct device *dev) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 4568c2ecf20Sopenharmony_ci struct mcfqspi *mcfqspi = spi_master_get_devdata(master); 4578c2ecf20Sopenharmony_ci int ret; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci ret = spi_master_suspend(master); 4608c2ecf20Sopenharmony_ci if (ret) 4618c2ecf20Sopenharmony_ci return ret; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci clk_disable(mcfqspi->clk); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci return 0; 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic int mcfqspi_resume(struct device *dev) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 4718c2ecf20Sopenharmony_ci struct mcfqspi *mcfqspi = spi_master_get_devdata(master); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci clk_enable(mcfqspi->clk); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci return spi_master_resume(master); 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci#endif 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 4808c2ecf20Sopenharmony_cistatic int mcfqspi_runtime_suspend(struct device *dev) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 4838c2ecf20Sopenharmony_ci struct mcfqspi *mcfqspi = spi_master_get_devdata(master); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci clk_disable(mcfqspi->clk); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci return 0; 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic int mcfqspi_runtime_resume(struct device *dev) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 4938c2ecf20Sopenharmony_ci struct mcfqspi *mcfqspi = spi_master_get_devdata(master); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci clk_enable(mcfqspi->clk); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return 0; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci#endif 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic const struct dev_pm_ops mcfqspi_pm = { 5028c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(mcfqspi_suspend, mcfqspi_resume) 5038c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(mcfqspi_runtime_suspend, mcfqspi_runtime_resume, 5048c2ecf20Sopenharmony_ci NULL) 5058c2ecf20Sopenharmony_ci}; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic struct platform_driver mcfqspi_driver = { 5088c2ecf20Sopenharmony_ci .driver.name = DRIVER_NAME, 5098c2ecf20Sopenharmony_ci .driver.owner = THIS_MODULE, 5108c2ecf20Sopenharmony_ci .driver.pm = &mcfqspi_pm, 5118c2ecf20Sopenharmony_ci .probe = mcfqspi_probe, 5128c2ecf20Sopenharmony_ci .remove = mcfqspi_remove, 5138c2ecf20Sopenharmony_ci}; 5148c2ecf20Sopenharmony_cimodule_platform_driver(mcfqspi_driver); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Steven King <sfking@fdwdc.com>"); 5178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Coldfire QSPI Controller Driver"); 5188c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5198c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" DRIVER_NAME); 520