162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Microchip PIC32 SPI controller driver. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Purna Chandra Mandal <purna.mandal@microchip.com> 662306a36Sopenharmony_ci * Copyright (c) 2016, Microchip Technology Inc. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/clk.h> 1062306a36Sopenharmony_ci#include <linux/clkdev.h> 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/dmaengine.h> 1362306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1462306a36Sopenharmony_ci#include <linux/highmem.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <linux/interrupt.h> 1862306a36Sopenharmony_ci#include <linux/of.h> 1962306a36Sopenharmony_ci#include <linux/of_irq.h> 2062306a36Sopenharmony_ci#include <linux/of_gpio.h> 2162306a36Sopenharmony_ci#include <linux/of_address.h> 2262306a36Sopenharmony_ci#include <linux/platform_device.h> 2362306a36Sopenharmony_ci#include <linux/spi/spi.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* SPI controller registers */ 2662306a36Sopenharmony_cistruct pic32_spi_regs { 2762306a36Sopenharmony_ci u32 ctrl; 2862306a36Sopenharmony_ci u32 ctrl_clr; 2962306a36Sopenharmony_ci u32 ctrl_set; 3062306a36Sopenharmony_ci u32 ctrl_inv; 3162306a36Sopenharmony_ci u32 status; 3262306a36Sopenharmony_ci u32 status_clr; 3362306a36Sopenharmony_ci u32 status_set; 3462306a36Sopenharmony_ci u32 status_inv; 3562306a36Sopenharmony_ci u32 buf; 3662306a36Sopenharmony_ci u32 dontuse[3]; 3762306a36Sopenharmony_ci u32 baud; 3862306a36Sopenharmony_ci u32 dontuse2[3]; 3962306a36Sopenharmony_ci u32 ctrl2; 4062306a36Sopenharmony_ci u32 ctrl2_clr; 4162306a36Sopenharmony_ci u32 ctrl2_set; 4262306a36Sopenharmony_ci u32 ctrl2_inv; 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* Bit fields of SPI Control Register */ 4662306a36Sopenharmony_ci#define CTRL_RX_INT_SHIFT 0 /* Rx interrupt generation */ 4762306a36Sopenharmony_ci#define RX_FIFO_EMPTY 0 4862306a36Sopenharmony_ci#define RX_FIFO_NOT_EMPTY 1 /* not empty */ 4962306a36Sopenharmony_ci#define RX_FIFO_HALF_FULL 2 /* full by half or more */ 5062306a36Sopenharmony_ci#define RX_FIFO_FULL 3 /* completely full */ 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define CTRL_TX_INT_SHIFT 2 /* TX interrupt generation */ 5362306a36Sopenharmony_ci#define TX_FIFO_ALL_EMPTY 0 /* completely empty */ 5462306a36Sopenharmony_ci#define TX_FIFO_EMPTY 1 /* empty */ 5562306a36Sopenharmony_ci#define TX_FIFO_HALF_EMPTY 2 /* empty by half or more */ 5662306a36Sopenharmony_ci#define TX_FIFO_NOT_FULL 3 /* atleast one empty */ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define CTRL_MSTEN BIT(5) /* enable master mode */ 5962306a36Sopenharmony_ci#define CTRL_CKP BIT(6) /* active low */ 6062306a36Sopenharmony_ci#define CTRL_CKE BIT(8) /* Tx on falling edge */ 6162306a36Sopenharmony_ci#define CTRL_SMP BIT(9) /* Rx at middle or end of tx */ 6262306a36Sopenharmony_ci#define CTRL_BPW_MASK 0x03 /* bits per word/sample */ 6362306a36Sopenharmony_ci#define CTRL_BPW_SHIFT 10 6462306a36Sopenharmony_ci#define PIC32_BPW_8 0 6562306a36Sopenharmony_ci#define PIC32_BPW_16 1 6662306a36Sopenharmony_ci#define PIC32_BPW_32 2 6762306a36Sopenharmony_ci#define CTRL_SIDL BIT(13) /* sleep when idle */ 6862306a36Sopenharmony_ci#define CTRL_ON BIT(15) /* enable macro */ 6962306a36Sopenharmony_ci#define CTRL_ENHBUF BIT(16) /* enable enhanced buffering */ 7062306a36Sopenharmony_ci#define CTRL_MCLKSEL BIT(23) /* select clock source */ 7162306a36Sopenharmony_ci#define CTRL_MSSEN BIT(28) /* macro driven /SS */ 7262306a36Sopenharmony_ci#define CTRL_FRMEN BIT(31) /* enable framing mode */ 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* Bit fields of SPI Status Register */ 7562306a36Sopenharmony_ci#define STAT_RF_EMPTY BIT(5) /* RX Fifo empty */ 7662306a36Sopenharmony_ci#define STAT_RX_OV BIT(6) /* err, s/w needs to clear */ 7762306a36Sopenharmony_ci#define STAT_TX_UR BIT(8) /* UR in Framed SPI modes */ 7862306a36Sopenharmony_ci#define STAT_FRM_ERR BIT(12) /* Multiple Frame Sync pulse */ 7962306a36Sopenharmony_ci#define STAT_TF_LVL_MASK 0x1F 8062306a36Sopenharmony_ci#define STAT_TF_LVL_SHIFT 16 8162306a36Sopenharmony_ci#define STAT_RF_LVL_MASK 0x1F 8262306a36Sopenharmony_ci#define STAT_RF_LVL_SHIFT 24 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* Bit fields of SPI Baud Register */ 8562306a36Sopenharmony_ci#define BAUD_MASK 0x1ff 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* Bit fields of SPI Control2 Register */ 8862306a36Sopenharmony_ci#define CTRL2_TX_UR_EN BIT(10) /* Enable int on Tx under-run */ 8962306a36Sopenharmony_ci#define CTRL2_RX_OV_EN BIT(11) /* Enable int on Rx over-run */ 9062306a36Sopenharmony_ci#define CTRL2_FRM_ERR_EN BIT(12) /* Enable frame err int */ 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* Minimum DMA transfer size */ 9362306a36Sopenharmony_ci#define PIC32_DMA_LEN_MIN 64 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistruct pic32_spi { 9662306a36Sopenharmony_ci dma_addr_t dma_base; 9762306a36Sopenharmony_ci struct pic32_spi_regs __iomem *regs; 9862306a36Sopenharmony_ci int fault_irq; 9962306a36Sopenharmony_ci int rx_irq; 10062306a36Sopenharmony_ci int tx_irq; 10162306a36Sopenharmony_ci u32 fifo_n_byte; /* FIFO depth in bytes */ 10262306a36Sopenharmony_ci struct clk *clk; 10362306a36Sopenharmony_ci struct spi_controller *host; 10462306a36Sopenharmony_ci /* Current controller setting */ 10562306a36Sopenharmony_ci u32 speed_hz; /* spi-clk rate */ 10662306a36Sopenharmony_ci u32 mode; 10762306a36Sopenharmony_ci u32 bits_per_word; 10862306a36Sopenharmony_ci u32 fifo_n_elm; /* FIFO depth in words */ 10962306a36Sopenharmony_ci#define PIC32F_DMA_PREP 0 /* DMA chnls configured */ 11062306a36Sopenharmony_ci unsigned long flags; 11162306a36Sopenharmony_ci /* Current transfer state */ 11262306a36Sopenharmony_ci struct completion xfer_done; 11362306a36Sopenharmony_ci /* PIO transfer specific */ 11462306a36Sopenharmony_ci const void *tx; 11562306a36Sopenharmony_ci const void *tx_end; 11662306a36Sopenharmony_ci const void *rx; 11762306a36Sopenharmony_ci const void *rx_end; 11862306a36Sopenharmony_ci int len; 11962306a36Sopenharmony_ci void (*rx_fifo)(struct pic32_spi *); 12062306a36Sopenharmony_ci void (*tx_fifo)(struct pic32_spi *); 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic inline void pic32_spi_enable(struct pic32_spi *pic32s) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci writel(CTRL_ON | CTRL_SIDL, &pic32s->regs->ctrl_set); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic inline void pic32_spi_disable(struct pic32_spi *pic32s) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci writel(CTRL_ON | CTRL_SIDL, &pic32s->regs->ctrl_clr); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* avoid SPI registers read/write at immediate next CPU clock */ 13362306a36Sopenharmony_ci ndelay(20); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic void pic32_spi_set_clk_rate(struct pic32_spi *pic32s, u32 spi_ck) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci u32 div; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* div = (clk_in / 2 * spi_ck) - 1 */ 14162306a36Sopenharmony_ci div = DIV_ROUND_CLOSEST(clk_get_rate(pic32s->clk), 2 * spi_ck) - 1; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci writel(div & BAUD_MASK, &pic32s->regs->baud); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic inline u32 pic32_rx_fifo_level(struct pic32_spi *pic32s) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci u32 sr = readl(&pic32s->regs->status); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return (sr >> STAT_RF_LVL_SHIFT) & STAT_RF_LVL_MASK; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic inline u32 pic32_tx_fifo_level(struct pic32_spi *pic32s) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci u32 sr = readl(&pic32s->regs->status); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci return (sr >> STAT_TF_LVL_SHIFT) & STAT_TF_LVL_MASK; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/* Return the max entries we can fill into tx fifo */ 16162306a36Sopenharmony_cistatic u32 pic32_tx_max(struct pic32_spi *pic32s, int n_bytes) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci u32 tx_left, tx_room, rxtx_gap; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci tx_left = (pic32s->tx_end - pic32s->tx) / n_bytes; 16662306a36Sopenharmony_ci tx_room = pic32s->fifo_n_elm - pic32_tx_fifo_level(pic32s); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* 16962306a36Sopenharmony_ci * Another concern is about the tx/rx mismatch, we 17062306a36Sopenharmony_ci * though to use (pic32s->fifo_n_byte - rxfl - txfl) as 17162306a36Sopenharmony_ci * one maximum value for tx, but it doesn't cover the 17262306a36Sopenharmony_ci * data which is out of tx/rx fifo and inside the 17362306a36Sopenharmony_ci * shift registers. So a ctrl from sw point of 17462306a36Sopenharmony_ci * view is taken. 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_ci rxtx_gap = ((pic32s->rx_end - pic32s->rx) - 17762306a36Sopenharmony_ci (pic32s->tx_end - pic32s->tx)) / n_bytes; 17862306a36Sopenharmony_ci return min3(tx_left, tx_room, (u32)(pic32s->fifo_n_elm - rxtx_gap)); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci/* Return the max entries we should read out of rx fifo */ 18262306a36Sopenharmony_cistatic u32 pic32_rx_max(struct pic32_spi *pic32s, int n_bytes) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci u32 rx_left = (pic32s->rx_end - pic32s->rx) / n_bytes; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return min_t(u32, rx_left, pic32_rx_fifo_level(pic32s)); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci#define BUILD_SPI_FIFO_RW(__name, __type, __bwl) \ 19062306a36Sopenharmony_cistatic void pic32_spi_rx_##__name(struct pic32_spi *pic32s) \ 19162306a36Sopenharmony_ci{ \ 19262306a36Sopenharmony_ci __type v; \ 19362306a36Sopenharmony_ci u32 mx = pic32_rx_max(pic32s, sizeof(__type)); \ 19462306a36Sopenharmony_ci for (; mx; mx--) { \ 19562306a36Sopenharmony_ci v = read##__bwl(&pic32s->regs->buf); \ 19662306a36Sopenharmony_ci if (pic32s->rx_end - pic32s->len) \ 19762306a36Sopenharmony_ci *(__type *)(pic32s->rx) = v; \ 19862306a36Sopenharmony_ci pic32s->rx += sizeof(__type); \ 19962306a36Sopenharmony_ci } \ 20062306a36Sopenharmony_ci} \ 20162306a36Sopenharmony_ci \ 20262306a36Sopenharmony_cistatic void pic32_spi_tx_##__name(struct pic32_spi *pic32s) \ 20362306a36Sopenharmony_ci{ \ 20462306a36Sopenharmony_ci __type v; \ 20562306a36Sopenharmony_ci u32 mx = pic32_tx_max(pic32s, sizeof(__type)); \ 20662306a36Sopenharmony_ci for (; mx ; mx--) { \ 20762306a36Sopenharmony_ci v = (__type)~0U; \ 20862306a36Sopenharmony_ci if (pic32s->tx_end - pic32s->len) \ 20962306a36Sopenharmony_ci v = *(__type *)(pic32s->tx); \ 21062306a36Sopenharmony_ci write##__bwl(v, &pic32s->regs->buf); \ 21162306a36Sopenharmony_ci pic32s->tx += sizeof(__type); \ 21262306a36Sopenharmony_ci } \ 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ciBUILD_SPI_FIFO_RW(byte, u8, b); 21662306a36Sopenharmony_ciBUILD_SPI_FIFO_RW(word, u16, w); 21762306a36Sopenharmony_ciBUILD_SPI_FIFO_RW(dword, u32, l); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void pic32_err_stop(struct pic32_spi *pic32s, const char *msg) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci /* disable all interrupts */ 22262306a36Sopenharmony_ci disable_irq_nosync(pic32s->fault_irq); 22362306a36Sopenharmony_ci disable_irq_nosync(pic32s->rx_irq); 22462306a36Sopenharmony_ci disable_irq_nosync(pic32s->tx_irq); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* Show err message and abort xfer with err */ 22762306a36Sopenharmony_ci dev_err(&pic32s->host->dev, "%s\n", msg); 22862306a36Sopenharmony_ci if (pic32s->host->cur_msg) 22962306a36Sopenharmony_ci pic32s->host->cur_msg->status = -EIO; 23062306a36Sopenharmony_ci complete(&pic32s->xfer_done); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic irqreturn_t pic32_spi_fault_irq(int irq, void *dev_id) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct pic32_spi *pic32s = dev_id; 23662306a36Sopenharmony_ci u32 status; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci status = readl(&pic32s->regs->status); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* Error handling */ 24162306a36Sopenharmony_ci if (status & (STAT_RX_OV | STAT_TX_UR)) { 24262306a36Sopenharmony_ci writel(STAT_RX_OV, &pic32s->regs->status_clr); 24362306a36Sopenharmony_ci writel(STAT_TX_UR, &pic32s->regs->status_clr); 24462306a36Sopenharmony_ci pic32_err_stop(pic32s, "err_irq: fifo ov/ur-run\n"); 24562306a36Sopenharmony_ci return IRQ_HANDLED; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (status & STAT_FRM_ERR) { 24962306a36Sopenharmony_ci pic32_err_stop(pic32s, "err_irq: frame error"); 25062306a36Sopenharmony_ci return IRQ_HANDLED; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (!pic32s->host->cur_msg) { 25462306a36Sopenharmony_ci pic32_err_stop(pic32s, "err_irq: no mesg"); 25562306a36Sopenharmony_ci return IRQ_NONE; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci return IRQ_NONE; 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic irqreturn_t pic32_spi_rx_irq(int irq, void *dev_id) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct pic32_spi *pic32s = dev_id; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci pic32s->rx_fifo(pic32s); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* rx complete ? */ 26862306a36Sopenharmony_ci if (pic32s->rx_end == pic32s->rx) { 26962306a36Sopenharmony_ci /* disable all interrupts */ 27062306a36Sopenharmony_ci disable_irq_nosync(pic32s->fault_irq); 27162306a36Sopenharmony_ci disable_irq_nosync(pic32s->rx_irq); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* complete current xfer */ 27462306a36Sopenharmony_ci complete(&pic32s->xfer_done); 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return IRQ_HANDLED; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic irqreturn_t pic32_spi_tx_irq(int irq, void *dev_id) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct pic32_spi *pic32s = dev_id; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci pic32s->tx_fifo(pic32s); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* tx complete? disable tx interrupt */ 28762306a36Sopenharmony_ci if (pic32s->tx_end == pic32s->tx) 28862306a36Sopenharmony_ci disable_irq_nosync(pic32s->tx_irq); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return IRQ_HANDLED; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic void pic32_spi_dma_rx_notify(void *data) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct pic32_spi *pic32s = data; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci complete(&pic32s->xfer_done); 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic int pic32_spi_dma_transfer(struct pic32_spi *pic32s, 30162306a36Sopenharmony_ci struct spi_transfer *xfer) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct spi_controller *host = pic32s->host; 30462306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc_rx; 30562306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc_tx; 30662306a36Sopenharmony_ci dma_cookie_t cookie; 30762306a36Sopenharmony_ci int ret; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (!host->dma_rx || !host->dma_tx) 31062306a36Sopenharmony_ci return -ENODEV; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci desc_rx = dmaengine_prep_slave_sg(host->dma_rx, 31362306a36Sopenharmony_ci xfer->rx_sg.sgl, 31462306a36Sopenharmony_ci xfer->rx_sg.nents, 31562306a36Sopenharmony_ci DMA_DEV_TO_MEM, 31662306a36Sopenharmony_ci DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 31762306a36Sopenharmony_ci if (!desc_rx) { 31862306a36Sopenharmony_ci ret = -EINVAL; 31962306a36Sopenharmony_ci goto err_dma; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci desc_tx = dmaengine_prep_slave_sg(host->dma_tx, 32362306a36Sopenharmony_ci xfer->tx_sg.sgl, 32462306a36Sopenharmony_ci xfer->tx_sg.nents, 32562306a36Sopenharmony_ci DMA_MEM_TO_DEV, 32662306a36Sopenharmony_ci DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 32762306a36Sopenharmony_ci if (!desc_tx) { 32862306a36Sopenharmony_ci ret = -EINVAL; 32962306a36Sopenharmony_ci goto err_dma; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* Put callback on the RX transfer, that should finish last */ 33362306a36Sopenharmony_ci desc_rx->callback = pic32_spi_dma_rx_notify; 33462306a36Sopenharmony_ci desc_rx->callback_param = pic32s; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci cookie = dmaengine_submit(desc_rx); 33762306a36Sopenharmony_ci ret = dma_submit_error(cookie); 33862306a36Sopenharmony_ci if (ret) 33962306a36Sopenharmony_ci goto err_dma; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci cookie = dmaengine_submit(desc_tx); 34262306a36Sopenharmony_ci ret = dma_submit_error(cookie); 34362306a36Sopenharmony_ci if (ret) 34462306a36Sopenharmony_ci goto err_dma_tx; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci dma_async_issue_pending(host->dma_rx); 34762306a36Sopenharmony_ci dma_async_issue_pending(host->dma_tx); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cierr_dma_tx: 35262306a36Sopenharmony_ci dmaengine_terminate_all(host->dma_rx); 35362306a36Sopenharmony_cierr_dma: 35462306a36Sopenharmony_ci return ret; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic int pic32_spi_dma_config(struct pic32_spi *pic32s, u32 dma_width) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci int buf_offset = offsetof(struct pic32_spi_regs, buf); 36062306a36Sopenharmony_ci struct spi_controller *host = pic32s->host; 36162306a36Sopenharmony_ci struct dma_slave_config cfg; 36262306a36Sopenharmony_ci int ret; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci memset(&cfg, 0, sizeof(cfg)); 36562306a36Sopenharmony_ci cfg.device_fc = true; 36662306a36Sopenharmony_ci cfg.src_addr = pic32s->dma_base + buf_offset; 36762306a36Sopenharmony_ci cfg.dst_addr = pic32s->dma_base + buf_offset; 36862306a36Sopenharmony_ci cfg.src_maxburst = pic32s->fifo_n_elm / 2; /* fill one-half */ 36962306a36Sopenharmony_ci cfg.dst_maxburst = pic32s->fifo_n_elm / 2; /* drain one-half */ 37062306a36Sopenharmony_ci cfg.src_addr_width = dma_width; 37162306a36Sopenharmony_ci cfg.dst_addr_width = dma_width; 37262306a36Sopenharmony_ci /* tx channel */ 37362306a36Sopenharmony_ci cfg.direction = DMA_MEM_TO_DEV; 37462306a36Sopenharmony_ci ret = dmaengine_slave_config(host->dma_tx, &cfg); 37562306a36Sopenharmony_ci if (ret) { 37662306a36Sopenharmony_ci dev_err(&host->dev, "tx channel setup failed\n"); 37762306a36Sopenharmony_ci return ret; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci /* rx channel */ 38062306a36Sopenharmony_ci cfg.direction = DMA_DEV_TO_MEM; 38162306a36Sopenharmony_ci ret = dmaengine_slave_config(host->dma_rx, &cfg); 38262306a36Sopenharmony_ci if (ret) 38362306a36Sopenharmony_ci dev_err(&host->dev, "rx channel setup failed\n"); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci return ret; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic int pic32_spi_set_word_size(struct pic32_spi *pic32s, u8 bits_per_word) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci enum dma_slave_buswidth dmawidth; 39162306a36Sopenharmony_ci u32 buswidth, v; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci switch (bits_per_word) { 39462306a36Sopenharmony_ci case 8: 39562306a36Sopenharmony_ci pic32s->rx_fifo = pic32_spi_rx_byte; 39662306a36Sopenharmony_ci pic32s->tx_fifo = pic32_spi_tx_byte; 39762306a36Sopenharmony_ci buswidth = PIC32_BPW_8; 39862306a36Sopenharmony_ci dmawidth = DMA_SLAVE_BUSWIDTH_1_BYTE; 39962306a36Sopenharmony_ci break; 40062306a36Sopenharmony_ci case 16: 40162306a36Sopenharmony_ci pic32s->rx_fifo = pic32_spi_rx_word; 40262306a36Sopenharmony_ci pic32s->tx_fifo = pic32_spi_tx_word; 40362306a36Sopenharmony_ci buswidth = PIC32_BPW_16; 40462306a36Sopenharmony_ci dmawidth = DMA_SLAVE_BUSWIDTH_2_BYTES; 40562306a36Sopenharmony_ci break; 40662306a36Sopenharmony_ci case 32: 40762306a36Sopenharmony_ci pic32s->rx_fifo = pic32_spi_rx_dword; 40862306a36Sopenharmony_ci pic32s->tx_fifo = pic32_spi_tx_dword; 40962306a36Sopenharmony_ci buswidth = PIC32_BPW_32; 41062306a36Sopenharmony_ci dmawidth = DMA_SLAVE_BUSWIDTH_4_BYTES; 41162306a36Sopenharmony_ci break; 41262306a36Sopenharmony_ci default: 41362306a36Sopenharmony_ci /* not supported */ 41462306a36Sopenharmony_ci return -EINVAL; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* calculate maximum number of words fifos can hold */ 41862306a36Sopenharmony_ci pic32s->fifo_n_elm = DIV_ROUND_UP(pic32s->fifo_n_byte, 41962306a36Sopenharmony_ci bits_per_word / 8); 42062306a36Sopenharmony_ci /* set word size */ 42162306a36Sopenharmony_ci v = readl(&pic32s->regs->ctrl); 42262306a36Sopenharmony_ci v &= ~(CTRL_BPW_MASK << CTRL_BPW_SHIFT); 42362306a36Sopenharmony_ci v |= buswidth << CTRL_BPW_SHIFT; 42462306a36Sopenharmony_ci writel(v, &pic32s->regs->ctrl); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* re-configure dma width, if required */ 42762306a36Sopenharmony_ci if (test_bit(PIC32F_DMA_PREP, &pic32s->flags)) 42862306a36Sopenharmony_ci pic32_spi_dma_config(pic32s, dmawidth); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci return 0; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic int pic32_spi_prepare_hardware(struct spi_controller *host) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci struct pic32_spi *pic32s = spi_controller_get_devdata(host); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci pic32_spi_enable(pic32s); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci return 0; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic int pic32_spi_prepare_message(struct spi_controller *host, 44362306a36Sopenharmony_ci struct spi_message *msg) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci struct pic32_spi *pic32s = spi_controller_get_devdata(host); 44662306a36Sopenharmony_ci struct spi_device *spi = msg->spi; 44762306a36Sopenharmony_ci u32 val; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* set device specific bits_per_word */ 45062306a36Sopenharmony_ci if (pic32s->bits_per_word != spi->bits_per_word) { 45162306a36Sopenharmony_ci pic32_spi_set_word_size(pic32s, spi->bits_per_word); 45262306a36Sopenharmony_ci pic32s->bits_per_word = spi->bits_per_word; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* device specific speed change */ 45662306a36Sopenharmony_ci if (pic32s->speed_hz != spi->max_speed_hz) { 45762306a36Sopenharmony_ci pic32_spi_set_clk_rate(pic32s, spi->max_speed_hz); 45862306a36Sopenharmony_ci pic32s->speed_hz = spi->max_speed_hz; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* device specific mode change */ 46262306a36Sopenharmony_ci if (pic32s->mode != spi->mode) { 46362306a36Sopenharmony_ci val = readl(&pic32s->regs->ctrl); 46462306a36Sopenharmony_ci /* active low */ 46562306a36Sopenharmony_ci if (spi->mode & SPI_CPOL) 46662306a36Sopenharmony_ci val |= CTRL_CKP; 46762306a36Sopenharmony_ci else 46862306a36Sopenharmony_ci val &= ~CTRL_CKP; 46962306a36Sopenharmony_ci /* tx on rising edge */ 47062306a36Sopenharmony_ci if (spi->mode & SPI_CPHA) 47162306a36Sopenharmony_ci val &= ~CTRL_CKE; 47262306a36Sopenharmony_ci else 47362306a36Sopenharmony_ci val |= CTRL_CKE; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* rx at end of tx */ 47662306a36Sopenharmony_ci val |= CTRL_SMP; 47762306a36Sopenharmony_ci writel(val, &pic32s->regs->ctrl); 47862306a36Sopenharmony_ci pic32s->mode = spi->mode; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci return 0; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic bool pic32_spi_can_dma(struct spi_controller *host, 48562306a36Sopenharmony_ci struct spi_device *spi, 48662306a36Sopenharmony_ci struct spi_transfer *xfer) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci struct pic32_spi *pic32s = spi_controller_get_devdata(host); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* skip using DMA on small size transfer to avoid overhead.*/ 49162306a36Sopenharmony_ci return (xfer->len >= PIC32_DMA_LEN_MIN) && 49262306a36Sopenharmony_ci test_bit(PIC32F_DMA_PREP, &pic32s->flags); 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic int pic32_spi_one_transfer(struct spi_controller *host, 49662306a36Sopenharmony_ci struct spi_device *spi, 49762306a36Sopenharmony_ci struct spi_transfer *transfer) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci struct pic32_spi *pic32s; 50062306a36Sopenharmony_ci bool dma_issued = false; 50162306a36Sopenharmony_ci unsigned long timeout; 50262306a36Sopenharmony_ci int ret; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci pic32s = spi_controller_get_devdata(host); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci /* handle transfer specific word size change */ 50762306a36Sopenharmony_ci if (transfer->bits_per_word && 50862306a36Sopenharmony_ci (transfer->bits_per_word != pic32s->bits_per_word)) { 50962306a36Sopenharmony_ci ret = pic32_spi_set_word_size(pic32s, transfer->bits_per_word); 51062306a36Sopenharmony_ci if (ret) 51162306a36Sopenharmony_ci return ret; 51262306a36Sopenharmony_ci pic32s->bits_per_word = transfer->bits_per_word; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* handle transfer specific speed change */ 51662306a36Sopenharmony_ci if (transfer->speed_hz && (transfer->speed_hz != pic32s->speed_hz)) { 51762306a36Sopenharmony_ci pic32_spi_set_clk_rate(pic32s, transfer->speed_hz); 51862306a36Sopenharmony_ci pic32s->speed_hz = transfer->speed_hz; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci reinit_completion(&pic32s->xfer_done); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* transact by DMA mode */ 52462306a36Sopenharmony_ci if (transfer->rx_sg.nents && transfer->tx_sg.nents) { 52562306a36Sopenharmony_ci ret = pic32_spi_dma_transfer(pic32s, transfer); 52662306a36Sopenharmony_ci if (ret) { 52762306a36Sopenharmony_ci dev_err(&spi->dev, "dma submit error\n"); 52862306a36Sopenharmony_ci return ret; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* DMA issued */ 53262306a36Sopenharmony_ci dma_issued = true; 53362306a36Sopenharmony_ci } else { 53462306a36Sopenharmony_ci /* set current transfer information */ 53562306a36Sopenharmony_ci pic32s->tx = (const void *)transfer->tx_buf; 53662306a36Sopenharmony_ci pic32s->rx = (const void *)transfer->rx_buf; 53762306a36Sopenharmony_ci pic32s->tx_end = pic32s->tx + transfer->len; 53862306a36Sopenharmony_ci pic32s->rx_end = pic32s->rx + transfer->len; 53962306a36Sopenharmony_ci pic32s->len = transfer->len; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* transact by interrupt driven PIO */ 54262306a36Sopenharmony_ci enable_irq(pic32s->fault_irq); 54362306a36Sopenharmony_ci enable_irq(pic32s->rx_irq); 54462306a36Sopenharmony_ci enable_irq(pic32s->tx_irq); 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* wait for completion */ 54862306a36Sopenharmony_ci timeout = wait_for_completion_timeout(&pic32s->xfer_done, 2 * HZ); 54962306a36Sopenharmony_ci if (timeout == 0) { 55062306a36Sopenharmony_ci dev_err(&spi->dev, "wait error/timedout\n"); 55162306a36Sopenharmony_ci if (dma_issued) { 55262306a36Sopenharmony_ci dmaengine_terminate_all(host->dma_rx); 55362306a36Sopenharmony_ci dmaengine_terminate_all(host->dma_tx); 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci ret = -ETIMEDOUT; 55662306a36Sopenharmony_ci } else { 55762306a36Sopenharmony_ci ret = 0; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci return ret; 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic int pic32_spi_unprepare_message(struct spi_controller *host, 56462306a36Sopenharmony_ci struct spi_message *msg) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci /* nothing to do */ 56762306a36Sopenharmony_ci return 0; 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cistatic int pic32_spi_unprepare_hardware(struct spi_controller *host) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci struct pic32_spi *pic32s = spi_controller_get_devdata(host); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci pic32_spi_disable(pic32s); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci return 0; 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci/* This may be called multiple times by same spi dev */ 58062306a36Sopenharmony_cistatic int pic32_spi_setup(struct spi_device *spi) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci if (!spi->max_speed_hz) { 58362306a36Sopenharmony_ci dev_err(&spi->dev, "No max speed HZ parameter\n"); 58462306a36Sopenharmony_ci return -EINVAL; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* PIC32 spi controller can drive /CS during transfer depending 58862306a36Sopenharmony_ci * on tx fifo fill-level. /CS will stay asserted as long as TX 58962306a36Sopenharmony_ci * fifo is non-empty, else will be deasserted indicating 59062306a36Sopenharmony_ci * completion of the ongoing transfer. This might result into 59162306a36Sopenharmony_ci * unreliable/erroneous SPI transactions. 59262306a36Sopenharmony_ci * To avoid that we will always handle /CS by toggling GPIO. 59362306a36Sopenharmony_ci */ 59462306a36Sopenharmony_ci if (!spi_get_csgpiod(spi, 0)) 59562306a36Sopenharmony_ci return -EINVAL; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci return 0; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic void pic32_spi_cleanup(struct spi_device *spi) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci /* de-activate cs-gpio, gpiolib will handle inversion */ 60362306a36Sopenharmony_ci gpiod_direction_output(spi_get_csgpiod(spi, 0), 0); 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic int pic32_spi_dma_prep(struct pic32_spi *pic32s, struct device *dev) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci struct spi_controller *host = pic32s->host; 60962306a36Sopenharmony_ci int ret = 0; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci host->dma_rx = dma_request_chan(dev, "spi-rx"); 61262306a36Sopenharmony_ci if (IS_ERR(host->dma_rx)) { 61362306a36Sopenharmony_ci if (PTR_ERR(host->dma_rx) == -EPROBE_DEFER) 61462306a36Sopenharmony_ci ret = -EPROBE_DEFER; 61562306a36Sopenharmony_ci else 61662306a36Sopenharmony_ci dev_warn(dev, "RX channel not found.\n"); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci host->dma_rx = NULL; 61962306a36Sopenharmony_ci goto out_err; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci host->dma_tx = dma_request_chan(dev, "spi-tx"); 62362306a36Sopenharmony_ci if (IS_ERR(host->dma_tx)) { 62462306a36Sopenharmony_ci if (PTR_ERR(host->dma_tx) == -EPROBE_DEFER) 62562306a36Sopenharmony_ci ret = -EPROBE_DEFER; 62662306a36Sopenharmony_ci else 62762306a36Sopenharmony_ci dev_warn(dev, "TX channel not found.\n"); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci host->dma_tx = NULL; 63062306a36Sopenharmony_ci goto out_err; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (pic32_spi_dma_config(pic32s, DMA_SLAVE_BUSWIDTH_1_BYTE)) 63462306a36Sopenharmony_ci goto out_err; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* DMA chnls allocated and prepared */ 63762306a36Sopenharmony_ci set_bit(PIC32F_DMA_PREP, &pic32s->flags); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci return 0; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ciout_err: 64262306a36Sopenharmony_ci if (host->dma_rx) { 64362306a36Sopenharmony_ci dma_release_channel(host->dma_rx); 64462306a36Sopenharmony_ci host->dma_rx = NULL; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci if (host->dma_tx) { 64862306a36Sopenharmony_ci dma_release_channel(host->dma_tx); 64962306a36Sopenharmony_ci host->dma_tx = NULL; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci return ret; 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cistatic void pic32_spi_dma_unprep(struct pic32_spi *pic32s) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci if (!test_bit(PIC32F_DMA_PREP, &pic32s->flags)) 65862306a36Sopenharmony_ci return; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci clear_bit(PIC32F_DMA_PREP, &pic32s->flags); 66162306a36Sopenharmony_ci if (pic32s->host->dma_rx) 66262306a36Sopenharmony_ci dma_release_channel(pic32s->host->dma_rx); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci if (pic32s->host->dma_tx) 66562306a36Sopenharmony_ci dma_release_channel(pic32s->host->dma_tx); 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic void pic32_spi_hw_init(struct pic32_spi *pic32s) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci u32 ctrl; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci /* disable hardware */ 67362306a36Sopenharmony_ci pic32_spi_disable(pic32s); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci ctrl = readl(&pic32s->regs->ctrl); 67662306a36Sopenharmony_ci /* enable enhanced fifo of 128bit deep */ 67762306a36Sopenharmony_ci ctrl |= CTRL_ENHBUF; 67862306a36Sopenharmony_ci pic32s->fifo_n_byte = 16; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci /* disable framing mode */ 68162306a36Sopenharmony_ci ctrl &= ~CTRL_FRMEN; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci /* enable host mode while disabled */ 68462306a36Sopenharmony_ci ctrl |= CTRL_MSTEN; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* set tx fifo threshold interrupt */ 68762306a36Sopenharmony_ci ctrl &= ~(0x3 << CTRL_TX_INT_SHIFT); 68862306a36Sopenharmony_ci ctrl |= (TX_FIFO_HALF_EMPTY << CTRL_TX_INT_SHIFT); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci /* set rx fifo threshold interrupt */ 69162306a36Sopenharmony_ci ctrl &= ~(0x3 << CTRL_RX_INT_SHIFT); 69262306a36Sopenharmony_ci ctrl |= (RX_FIFO_NOT_EMPTY << CTRL_RX_INT_SHIFT); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci /* select clk source */ 69562306a36Sopenharmony_ci ctrl &= ~CTRL_MCLKSEL; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* set manual /CS mode */ 69862306a36Sopenharmony_ci ctrl &= ~CTRL_MSSEN; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci writel(ctrl, &pic32s->regs->ctrl); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* enable error reporting */ 70362306a36Sopenharmony_ci ctrl = CTRL2_TX_UR_EN | CTRL2_RX_OV_EN | CTRL2_FRM_ERR_EN; 70462306a36Sopenharmony_ci writel(ctrl, &pic32s->regs->ctrl2_set); 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cistatic int pic32_spi_hw_probe(struct platform_device *pdev, 70862306a36Sopenharmony_ci struct pic32_spi *pic32s) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci struct resource *mem; 71162306a36Sopenharmony_ci int ret; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci pic32s->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); 71462306a36Sopenharmony_ci if (IS_ERR(pic32s->regs)) 71562306a36Sopenharmony_ci return PTR_ERR(pic32s->regs); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci pic32s->dma_base = mem->start; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci /* get irq resources: err-irq, rx-irq, tx-irq */ 72062306a36Sopenharmony_ci pic32s->fault_irq = platform_get_irq_byname(pdev, "fault"); 72162306a36Sopenharmony_ci if (pic32s->fault_irq < 0) 72262306a36Sopenharmony_ci return pic32s->fault_irq; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci pic32s->rx_irq = platform_get_irq_byname(pdev, "rx"); 72562306a36Sopenharmony_ci if (pic32s->rx_irq < 0) 72662306a36Sopenharmony_ci return pic32s->rx_irq; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci pic32s->tx_irq = platform_get_irq_byname(pdev, "tx"); 72962306a36Sopenharmony_ci if (pic32s->tx_irq < 0) 73062306a36Sopenharmony_ci return pic32s->tx_irq; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* get clock */ 73362306a36Sopenharmony_ci pic32s->clk = devm_clk_get(&pdev->dev, "mck0"); 73462306a36Sopenharmony_ci if (IS_ERR(pic32s->clk)) { 73562306a36Sopenharmony_ci dev_err(&pdev->dev, "clk not found\n"); 73662306a36Sopenharmony_ci ret = PTR_ERR(pic32s->clk); 73762306a36Sopenharmony_ci goto err_unmap_mem; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci ret = clk_prepare_enable(pic32s->clk); 74162306a36Sopenharmony_ci if (ret) 74262306a36Sopenharmony_ci goto err_unmap_mem; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci pic32_spi_hw_init(pic32s); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci return 0; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cierr_unmap_mem: 74962306a36Sopenharmony_ci dev_err(&pdev->dev, "%s failed, err %d\n", __func__, ret); 75062306a36Sopenharmony_ci return ret; 75162306a36Sopenharmony_ci} 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_cistatic int pic32_spi_probe(struct platform_device *pdev) 75462306a36Sopenharmony_ci{ 75562306a36Sopenharmony_ci struct spi_controller *host; 75662306a36Sopenharmony_ci struct pic32_spi *pic32s; 75762306a36Sopenharmony_ci int ret; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci host = spi_alloc_host(&pdev->dev, sizeof(*pic32s)); 76062306a36Sopenharmony_ci if (!host) 76162306a36Sopenharmony_ci return -ENOMEM; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci pic32s = spi_controller_get_devdata(host); 76462306a36Sopenharmony_ci pic32s->host = host; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci ret = pic32_spi_hw_probe(pdev, pic32s); 76762306a36Sopenharmony_ci if (ret) 76862306a36Sopenharmony_ci goto err_host; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci host->dev.of_node = pdev->dev.of_node; 77162306a36Sopenharmony_ci host->mode_bits = SPI_MODE_3 | SPI_MODE_0 | SPI_CS_HIGH; 77262306a36Sopenharmony_ci host->num_chipselect = 1; /* single chip-select */ 77362306a36Sopenharmony_ci host->max_speed_hz = clk_get_rate(pic32s->clk); 77462306a36Sopenharmony_ci host->setup = pic32_spi_setup; 77562306a36Sopenharmony_ci host->cleanup = pic32_spi_cleanup; 77662306a36Sopenharmony_ci host->flags = SPI_CONTROLLER_MUST_TX | SPI_CONTROLLER_MUST_RX; 77762306a36Sopenharmony_ci host->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16) | 77862306a36Sopenharmony_ci SPI_BPW_MASK(32); 77962306a36Sopenharmony_ci host->transfer_one = pic32_spi_one_transfer; 78062306a36Sopenharmony_ci host->prepare_message = pic32_spi_prepare_message; 78162306a36Sopenharmony_ci host->unprepare_message = pic32_spi_unprepare_message; 78262306a36Sopenharmony_ci host->prepare_transfer_hardware = pic32_spi_prepare_hardware; 78362306a36Sopenharmony_ci host->unprepare_transfer_hardware = pic32_spi_unprepare_hardware; 78462306a36Sopenharmony_ci host->use_gpio_descriptors = true; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci /* optional DMA support */ 78762306a36Sopenharmony_ci ret = pic32_spi_dma_prep(pic32s, &pdev->dev); 78862306a36Sopenharmony_ci if (ret) 78962306a36Sopenharmony_ci goto err_bailout; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci if (test_bit(PIC32F_DMA_PREP, &pic32s->flags)) 79262306a36Sopenharmony_ci host->can_dma = pic32_spi_can_dma; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci init_completion(&pic32s->xfer_done); 79562306a36Sopenharmony_ci pic32s->mode = -1; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci /* install irq handlers (with irq-disabled) */ 79862306a36Sopenharmony_ci irq_set_status_flags(pic32s->fault_irq, IRQ_NOAUTOEN); 79962306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, pic32s->fault_irq, 80062306a36Sopenharmony_ci pic32_spi_fault_irq, IRQF_NO_THREAD, 80162306a36Sopenharmony_ci dev_name(&pdev->dev), pic32s); 80262306a36Sopenharmony_ci if (ret < 0) { 80362306a36Sopenharmony_ci dev_err(&pdev->dev, "request fault-irq %d\n", pic32s->rx_irq); 80462306a36Sopenharmony_ci goto err_bailout; 80562306a36Sopenharmony_ci } 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci /* receive interrupt handler */ 80862306a36Sopenharmony_ci irq_set_status_flags(pic32s->rx_irq, IRQ_NOAUTOEN); 80962306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, pic32s->rx_irq, 81062306a36Sopenharmony_ci pic32_spi_rx_irq, IRQF_NO_THREAD, 81162306a36Sopenharmony_ci dev_name(&pdev->dev), pic32s); 81262306a36Sopenharmony_ci if (ret < 0) { 81362306a36Sopenharmony_ci dev_err(&pdev->dev, "request rx-irq %d\n", pic32s->rx_irq); 81462306a36Sopenharmony_ci goto err_bailout; 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci /* transmit interrupt handler */ 81862306a36Sopenharmony_ci irq_set_status_flags(pic32s->tx_irq, IRQ_NOAUTOEN); 81962306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, pic32s->tx_irq, 82062306a36Sopenharmony_ci pic32_spi_tx_irq, IRQF_NO_THREAD, 82162306a36Sopenharmony_ci dev_name(&pdev->dev), pic32s); 82262306a36Sopenharmony_ci if (ret < 0) { 82362306a36Sopenharmony_ci dev_err(&pdev->dev, "request tx-irq %d\n", pic32s->tx_irq); 82462306a36Sopenharmony_ci goto err_bailout; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci /* register host */ 82862306a36Sopenharmony_ci ret = devm_spi_register_controller(&pdev->dev, host); 82962306a36Sopenharmony_ci if (ret) { 83062306a36Sopenharmony_ci dev_err(&host->dev, "failed registering spi host\n"); 83162306a36Sopenharmony_ci goto err_bailout; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci platform_set_drvdata(pdev, pic32s); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci return 0; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_cierr_bailout: 83962306a36Sopenharmony_ci pic32_spi_dma_unprep(pic32s); 84062306a36Sopenharmony_ci clk_disable_unprepare(pic32s->clk); 84162306a36Sopenharmony_cierr_host: 84262306a36Sopenharmony_ci spi_controller_put(host); 84362306a36Sopenharmony_ci return ret; 84462306a36Sopenharmony_ci} 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_cistatic void pic32_spi_remove(struct platform_device *pdev) 84762306a36Sopenharmony_ci{ 84862306a36Sopenharmony_ci struct pic32_spi *pic32s; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci pic32s = platform_get_drvdata(pdev); 85162306a36Sopenharmony_ci pic32_spi_disable(pic32s); 85262306a36Sopenharmony_ci clk_disable_unprepare(pic32s->clk); 85362306a36Sopenharmony_ci pic32_spi_dma_unprep(pic32s); 85462306a36Sopenharmony_ci} 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_cistatic const struct of_device_id pic32_spi_of_match[] = { 85762306a36Sopenharmony_ci {.compatible = "microchip,pic32mzda-spi",}, 85862306a36Sopenharmony_ci {}, 85962306a36Sopenharmony_ci}; 86062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, pic32_spi_of_match); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistatic struct platform_driver pic32_spi_driver = { 86362306a36Sopenharmony_ci .driver = { 86462306a36Sopenharmony_ci .name = "spi-pic32", 86562306a36Sopenharmony_ci .of_match_table = of_match_ptr(pic32_spi_of_match), 86662306a36Sopenharmony_ci }, 86762306a36Sopenharmony_ci .probe = pic32_spi_probe, 86862306a36Sopenharmony_ci .remove_new = pic32_spi_remove, 86962306a36Sopenharmony_ci}; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cimodule_platform_driver(pic32_spi_driver); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ciMODULE_AUTHOR("Purna Chandra Mandal <purna.mandal@microchip.com>"); 87462306a36Sopenharmony_ciMODULE_DESCRIPTION("Microchip SPI driver for PIC32 SPI controller."); 87562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 876