162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// Copyright (c) 2017-2018, The Linux foundation. All rights reserved. 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/clk.h> 562306a36Sopenharmony_ci#include <linux/dmapool.h> 662306a36Sopenharmony_ci#include <linux/dma-mapping.h> 762306a36Sopenharmony_ci#include <linux/interconnect.h> 862306a36Sopenharmony_ci#include <linux/interrupt.h> 962306a36Sopenharmony_ci#include <linux/io.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/of.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h> 1462306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1562306a36Sopenharmony_ci#include <linux/pm_opp.h> 1662306a36Sopenharmony_ci#include <linux/spi/spi.h> 1762306a36Sopenharmony_ci#include <linux/spi/spi-mem.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define QSPI_NUM_CS 2 2162306a36Sopenharmony_ci#define QSPI_BYTES_PER_WORD 4 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define MSTR_CONFIG 0x0000 2462306a36Sopenharmony_ci#define FULL_CYCLE_MODE BIT(3) 2562306a36Sopenharmony_ci#define FB_CLK_EN BIT(4) 2662306a36Sopenharmony_ci#define PIN_HOLDN BIT(6) 2762306a36Sopenharmony_ci#define PIN_WPN BIT(7) 2862306a36Sopenharmony_ci#define DMA_ENABLE BIT(8) 2962306a36Sopenharmony_ci#define BIG_ENDIAN_MODE BIT(9) 3062306a36Sopenharmony_ci#define SPI_MODE_MSK 0xc00 3162306a36Sopenharmony_ci#define SPI_MODE_SHFT 10 3262306a36Sopenharmony_ci#define CHIP_SELECT_NUM BIT(12) 3362306a36Sopenharmony_ci#define SBL_EN BIT(13) 3462306a36Sopenharmony_ci#define LPA_BASE_MSK 0x3c000 3562306a36Sopenharmony_ci#define LPA_BASE_SHFT 14 3662306a36Sopenharmony_ci#define TX_DATA_DELAY_MSK 0xc0000 3762306a36Sopenharmony_ci#define TX_DATA_DELAY_SHFT 18 3862306a36Sopenharmony_ci#define TX_CLK_DELAY_MSK 0x300000 3962306a36Sopenharmony_ci#define TX_CLK_DELAY_SHFT 20 4062306a36Sopenharmony_ci#define TX_CS_N_DELAY_MSK 0xc00000 4162306a36Sopenharmony_ci#define TX_CS_N_DELAY_SHFT 22 4262306a36Sopenharmony_ci#define TX_DATA_OE_DELAY_MSK 0x3000000 4362306a36Sopenharmony_ci#define TX_DATA_OE_DELAY_SHFT 24 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define AHB_MASTER_CFG 0x0004 4662306a36Sopenharmony_ci#define HMEM_TYPE_START_MID_TRANS_MSK 0x7 4762306a36Sopenharmony_ci#define HMEM_TYPE_START_MID_TRANS_SHFT 0 4862306a36Sopenharmony_ci#define HMEM_TYPE_LAST_TRANS_MSK 0x38 4962306a36Sopenharmony_ci#define HMEM_TYPE_LAST_TRANS_SHFT 3 5062306a36Sopenharmony_ci#define USE_HMEMTYPE_LAST_ON_DESC_OR_CHAIN_MSK 0xc0 5162306a36Sopenharmony_ci#define USE_HMEMTYPE_LAST_ON_DESC_OR_CHAIN_SHFT 6 5262306a36Sopenharmony_ci#define HMEMTYPE_READ_TRANS_MSK 0x700 5362306a36Sopenharmony_ci#define HMEMTYPE_READ_TRANS_SHFT 8 5462306a36Sopenharmony_ci#define HSHARED BIT(11) 5562306a36Sopenharmony_ci#define HINNERSHARED BIT(12) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define MSTR_INT_EN 0x000C 5862306a36Sopenharmony_ci#define MSTR_INT_STATUS 0x0010 5962306a36Sopenharmony_ci#define RESP_FIFO_UNDERRUN BIT(0) 6062306a36Sopenharmony_ci#define RESP_FIFO_NOT_EMPTY BIT(1) 6162306a36Sopenharmony_ci#define RESP_FIFO_RDY BIT(2) 6262306a36Sopenharmony_ci#define HRESP_FROM_NOC_ERR BIT(3) 6362306a36Sopenharmony_ci#define WR_FIFO_EMPTY BIT(9) 6462306a36Sopenharmony_ci#define WR_FIFO_FULL BIT(10) 6562306a36Sopenharmony_ci#define WR_FIFO_OVERRUN BIT(11) 6662306a36Sopenharmony_ci#define TRANSACTION_DONE BIT(16) 6762306a36Sopenharmony_ci#define DMA_CHAIN_DONE BIT(31) 6862306a36Sopenharmony_ci#define QSPI_ERR_IRQS (RESP_FIFO_UNDERRUN | HRESP_FROM_NOC_ERR | \ 6962306a36Sopenharmony_ci WR_FIFO_OVERRUN) 7062306a36Sopenharmony_ci#define QSPI_ALL_IRQS (QSPI_ERR_IRQS | RESP_FIFO_RDY | \ 7162306a36Sopenharmony_ci WR_FIFO_EMPTY | WR_FIFO_FULL | \ 7262306a36Sopenharmony_ci TRANSACTION_DONE | DMA_CHAIN_DONE) 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define PIO_XFER_CTRL 0x0014 7562306a36Sopenharmony_ci#define REQUEST_COUNT_MSK 0xffff 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define PIO_XFER_CFG 0x0018 7862306a36Sopenharmony_ci#define TRANSFER_DIRECTION BIT(0) 7962306a36Sopenharmony_ci#define MULTI_IO_MODE_MSK 0xe 8062306a36Sopenharmony_ci#define MULTI_IO_MODE_SHFT 1 8162306a36Sopenharmony_ci#define TRANSFER_FRAGMENT BIT(8) 8262306a36Sopenharmony_ci#define SDR_1BIT 1 8362306a36Sopenharmony_ci#define SDR_2BIT 2 8462306a36Sopenharmony_ci#define SDR_4BIT 3 8562306a36Sopenharmony_ci#define DDR_1BIT 5 8662306a36Sopenharmony_ci#define DDR_2BIT 6 8762306a36Sopenharmony_ci#define DDR_4BIT 7 8862306a36Sopenharmony_ci#define DMA_DESC_SINGLE_SPI 1 8962306a36Sopenharmony_ci#define DMA_DESC_DUAL_SPI 2 9062306a36Sopenharmony_ci#define DMA_DESC_QUAD_SPI 3 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#define PIO_XFER_STATUS 0x001c 9362306a36Sopenharmony_ci#define WR_FIFO_BYTES_MSK 0xffff0000 9462306a36Sopenharmony_ci#define WR_FIFO_BYTES_SHFT 16 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define PIO_DATAOUT_1B 0x0020 9762306a36Sopenharmony_ci#define PIO_DATAOUT_4B 0x0024 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#define RD_FIFO_CFG 0x0028 10062306a36Sopenharmony_ci#define CONTINUOUS_MODE BIT(0) 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci#define RD_FIFO_STATUS 0x002c 10362306a36Sopenharmony_ci#define FIFO_EMPTY BIT(11) 10462306a36Sopenharmony_ci#define WR_CNTS_MSK 0x7f0 10562306a36Sopenharmony_ci#define WR_CNTS_SHFT 4 10662306a36Sopenharmony_ci#define RDY_64BYTE BIT(3) 10762306a36Sopenharmony_ci#define RDY_32BYTE BIT(2) 10862306a36Sopenharmony_ci#define RDY_16BYTE BIT(1) 10962306a36Sopenharmony_ci#define FIFO_RDY BIT(0) 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci#define RD_FIFO_RESET 0x0030 11262306a36Sopenharmony_ci#define RESET_FIFO BIT(0) 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#define NEXT_DMA_DESC_ADDR 0x0040 11562306a36Sopenharmony_ci#define CURRENT_DMA_DESC_ADDR 0x0044 11662306a36Sopenharmony_ci#define CURRENT_MEM_ADDR 0x0048 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci#define CUR_MEM_ADDR 0x0048 11962306a36Sopenharmony_ci#define HW_VERSION 0x004c 12062306a36Sopenharmony_ci#define RD_FIFO 0x0050 12162306a36Sopenharmony_ci#define SAMPLING_CLK_CFG 0x0090 12262306a36Sopenharmony_ci#define SAMPLING_CLK_STATUS 0x0094 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci#define QSPI_ALIGN_REQ 32 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cienum qspi_dir { 12762306a36Sopenharmony_ci QSPI_READ, 12862306a36Sopenharmony_ci QSPI_WRITE, 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistruct qspi_cmd_desc { 13262306a36Sopenharmony_ci u32 data_address; 13362306a36Sopenharmony_ci u32 next_descriptor; 13462306a36Sopenharmony_ci u32 direction:1; 13562306a36Sopenharmony_ci u32 multi_io_mode:3; 13662306a36Sopenharmony_ci u32 reserved1:4; 13762306a36Sopenharmony_ci u32 fragment:1; 13862306a36Sopenharmony_ci u32 reserved2:7; 13962306a36Sopenharmony_ci u32 length:16; 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistruct qspi_xfer { 14362306a36Sopenharmony_ci union { 14462306a36Sopenharmony_ci const void *tx_buf; 14562306a36Sopenharmony_ci void *rx_buf; 14662306a36Sopenharmony_ci }; 14762306a36Sopenharmony_ci unsigned int rem_bytes; 14862306a36Sopenharmony_ci unsigned int buswidth; 14962306a36Sopenharmony_ci enum qspi_dir dir; 15062306a36Sopenharmony_ci bool is_last; 15162306a36Sopenharmony_ci}; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cienum qspi_clocks { 15462306a36Sopenharmony_ci QSPI_CLK_CORE, 15562306a36Sopenharmony_ci QSPI_CLK_IFACE, 15662306a36Sopenharmony_ci QSPI_NUM_CLKS 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* 16062306a36Sopenharmony_ci * Number of entries in sgt returned from spi framework that- 16162306a36Sopenharmony_ci * will be supported. Can be modified as required. 16262306a36Sopenharmony_ci * In practice, given max_dma_len is 64KB, the number of 16362306a36Sopenharmony_ci * entries is not expected to exceed 1. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci#define QSPI_MAX_SG 5 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistruct qcom_qspi { 16862306a36Sopenharmony_ci void __iomem *base; 16962306a36Sopenharmony_ci struct device *dev; 17062306a36Sopenharmony_ci struct clk_bulk_data *clks; 17162306a36Sopenharmony_ci struct qspi_xfer xfer; 17262306a36Sopenharmony_ci struct dma_pool *dma_cmd_pool; 17362306a36Sopenharmony_ci dma_addr_t dma_cmd_desc[QSPI_MAX_SG]; 17462306a36Sopenharmony_ci void *virt_cmd_desc[QSPI_MAX_SG]; 17562306a36Sopenharmony_ci unsigned int n_cmd_desc; 17662306a36Sopenharmony_ci struct icc_path *icc_path_cpu_to_qspi; 17762306a36Sopenharmony_ci unsigned long last_speed; 17862306a36Sopenharmony_ci /* Lock to protect data accessed by IRQs */ 17962306a36Sopenharmony_ci spinlock_t lock; 18062306a36Sopenharmony_ci}; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic u32 qspi_buswidth_to_iomode(struct qcom_qspi *ctrl, 18362306a36Sopenharmony_ci unsigned int buswidth) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci switch (buswidth) { 18662306a36Sopenharmony_ci case 1: 18762306a36Sopenharmony_ci return SDR_1BIT; 18862306a36Sopenharmony_ci case 2: 18962306a36Sopenharmony_ci return SDR_2BIT; 19062306a36Sopenharmony_ci case 4: 19162306a36Sopenharmony_ci return SDR_4BIT; 19262306a36Sopenharmony_ci default: 19362306a36Sopenharmony_ci dev_warn_once(ctrl->dev, 19462306a36Sopenharmony_ci "Unexpected bus width: %u\n", buswidth); 19562306a36Sopenharmony_ci return SDR_1BIT; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic void qcom_qspi_pio_xfer_cfg(struct qcom_qspi *ctrl) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci u32 pio_xfer_cfg; 20262306a36Sopenharmony_ci u32 iomode; 20362306a36Sopenharmony_ci const struct qspi_xfer *xfer; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci xfer = &ctrl->xfer; 20662306a36Sopenharmony_ci pio_xfer_cfg = readl(ctrl->base + PIO_XFER_CFG); 20762306a36Sopenharmony_ci pio_xfer_cfg &= ~TRANSFER_DIRECTION; 20862306a36Sopenharmony_ci pio_xfer_cfg |= xfer->dir; 20962306a36Sopenharmony_ci if (xfer->is_last) 21062306a36Sopenharmony_ci pio_xfer_cfg &= ~TRANSFER_FRAGMENT; 21162306a36Sopenharmony_ci else 21262306a36Sopenharmony_ci pio_xfer_cfg |= TRANSFER_FRAGMENT; 21362306a36Sopenharmony_ci pio_xfer_cfg &= ~MULTI_IO_MODE_MSK; 21462306a36Sopenharmony_ci iomode = qspi_buswidth_to_iomode(ctrl, xfer->buswidth); 21562306a36Sopenharmony_ci pio_xfer_cfg |= iomode << MULTI_IO_MODE_SHFT; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci writel(pio_xfer_cfg, ctrl->base + PIO_XFER_CFG); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic void qcom_qspi_pio_xfer_ctrl(struct qcom_qspi *ctrl) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci u32 pio_xfer_ctrl; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci pio_xfer_ctrl = readl(ctrl->base + PIO_XFER_CTRL); 22562306a36Sopenharmony_ci pio_xfer_ctrl &= ~REQUEST_COUNT_MSK; 22662306a36Sopenharmony_ci pio_xfer_ctrl |= ctrl->xfer.rem_bytes; 22762306a36Sopenharmony_ci writel(pio_xfer_ctrl, ctrl->base + PIO_XFER_CTRL); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic void qcom_qspi_pio_xfer(struct qcom_qspi *ctrl) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci u32 ints; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci qcom_qspi_pio_xfer_cfg(ctrl); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* Ack any previous interrupts that might be hanging around */ 23762306a36Sopenharmony_ci writel(QSPI_ALL_IRQS, ctrl->base + MSTR_INT_STATUS); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* Setup new interrupts */ 24062306a36Sopenharmony_ci if (ctrl->xfer.dir == QSPI_WRITE) 24162306a36Sopenharmony_ci ints = QSPI_ERR_IRQS | WR_FIFO_EMPTY; 24262306a36Sopenharmony_ci else 24362306a36Sopenharmony_ci ints = QSPI_ERR_IRQS | RESP_FIFO_RDY; 24462306a36Sopenharmony_ci writel(ints, ctrl->base + MSTR_INT_EN); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* Kick off the transfer */ 24762306a36Sopenharmony_ci qcom_qspi_pio_xfer_ctrl(ctrl); 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic void qcom_qspi_handle_err(struct spi_controller *host, 25162306a36Sopenharmony_ci struct spi_message *msg) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci u32 int_status; 25462306a36Sopenharmony_ci struct qcom_qspi *ctrl = spi_controller_get_devdata(host); 25562306a36Sopenharmony_ci unsigned long flags; 25662306a36Sopenharmony_ci int i; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci spin_lock_irqsave(&ctrl->lock, flags); 25962306a36Sopenharmony_ci writel(0, ctrl->base + MSTR_INT_EN); 26062306a36Sopenharmony_ci int_status = readl(ctrl->base + MSTR_INT_STATUS); 26162306a36Sopenharmony_ci writel(int_status, ctrl->base + MSTR_INT_STATUS); 26262306a36Sopenharmony_ci ctrl->xfer.rem_bytes = 0; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* free cmd descriptors if they are around (DMA mode) */ 26562306a36Sopenharmony_ci for (i = 0; i < ctrl->n_cmd_desc; i++) 26662306a36Sopenharmony_ci dma_pool_free(ctrl->dma_cmd_pool, ctrl->virt_cmd_desc[i], 26762306a36Sopenharmony_ci ctrl->dma_cmd_desc[i]); 26862306a36Sopenharmony_ci ctrl->n_cmd_desc = 0; 26962306a36Sopenharmony_ci spin_unlock_irqrestore(&ctrl->lock, flags); 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic int qcom_qspi_set_speed(struct qcom_qspi *ctrl, unsigned long speed_hz) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci int ret; 27562306a36Sopenharmony_ci unsigned int avg_bw_cpu; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (speed_hz == ctrl->last_speed) 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* In regular operation (SBL_EN=1) core must be 4x transfer clock */ 28162306a36Sopenharmony_ci ret = dev_pm_opp_set_rate(ctrl->dev, speed_hz * 4); 28262306a36Sopenharmony_ci if (ret) { 28362306a36Sopenharmony_ci dev_err(ctrl->dev, "Failed to set core clk %d\n", ret); 28462306a36Sopenharmony_ci return ret; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* 28862306a36Sopenharmony_ci * Set BW quota for CPU. 28962306a36Sopenharmony_ci * We don't have explicit peak requirement so keep it equal to avg_bw. 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_ci avg_bw_cpu = Bps_to_icc(speed_hz); 29262306a36Sopenharmony_ci ret = icc_set_bw(ctrl->icc_path_cpu_to_qspi, avg_bw_cpu, avg_bw_cpu); 29362306a36Sopenharmony_ci if (ret) { 29462306a36Sopenharmony_ci dev_err(ctrl->dev, "%s: ICC BW voting failed for cpu: %d\n", 29562306a36Sopenharmony_ci __func__, ret); 29662306a36Sopenharmony_ci return ret; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci ctrl->last_speed = speed_hz; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci return 0; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic int qcom_qspi_alloc_desc(struct qcom_qspi *ctrl, dma_addr_t dma_ptr, 30562306a36Sopenharmony_ci uint32_t n_bytes) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct qspi_cmd_desc *virt_cmd_desc, *prev; 30862306a36Sopenharmony_ci dma_addr_t dma_cmd_desc; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* allocate for dma cmd descriptor */ 31162306a36Sopenharmony_ci virt_cmd_desc = dma_pool_alloc(ctrl->dma_cmd_pool, GFP_ATOMIC | __GFP_ZERO, &dma_cmd_desc); 31262306a36Sopenharmony_ci if (!virt_cmd_desc) { 31362306a36Sopenharmony_ci dev_warn_once(ctrl->dev, "Couldn't find memory for descriptor\n"); 31462306a36Sopenharmony_ci return -EAGAIN; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci ctrl->virt_cmd_desc[ctrl->n_cmd_desc] = virt_cmd_desc; 31862306a36Sopenharmony_ci ctrl->dma_cmd_desc[ctrl->n_cmd_desc] = dma_cmd_desc; 31962306a36Sopenharmony_ci ctrl->n_cmd_desc++; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* setup cmd descriptor */ 32262306a36Sopenharmony_ci virt_cmd_desc->data_address = dma_ptr; 32362306a36Sopenharmony_ci virt_cmd_desc->direction = ctrl->xfer.dir; 32462306a36Sopenharmony_ci virt_cmd_desc->multi_io_mode = qspi_buswidth_to_iomode(ctrl, ctrl->xfer.buswidth); 32562306a36Sopenharmony_ci virt_cmd_desc->fragment = !ctrl->xfer.is_last; 32662306a36Sopenharmony_ci virt_cmd_desc->length = n_bytes; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* update previous descriptor */ 32962306a36Sopenharmony_ci if (ctrl->n_cmd_desc >= 2) { 33062306a36Sopenharmony_ci prev = (ctrl->virt_cmd_desc)[ctrl->n_cmd_desc - 2]; 33162306a36Sopenharmony_ci prev->next_descriptor = dma_cmd_desc; 33262306a36Sopenharmony_ci prev->fragment = 1; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci return 0; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic int qcom_qspi_setup_dma_desc(struct qcom_qspi *ctrl, 33962306a36Sopenharmony_ci struct spi_transfer *xfer) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci int ret; 34262306a36Sopenharmony_ci struct sg_table *sgt; 34362306a36Sopenharmony_ci dma_addr_t dma_ptr_sg; 34462306a36Sopenharmony_ci unsigned int dma_len_sg; 34562306a36Sopenharmony_ci int i; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (ctrl->n_cmd_desc) { 34862306a36Sopenharmony_ci dev_err(ctrl->dev, "Remnant dma buffers n_cmd_desc-%d\n", ctrl->n_cmd_desc); 34962306a36Sopenharmony_ci return -EIO; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci sgt = (ctrl->xfer.dir == QSPI_READ) ? &xfer->rx_sg : &xfer->tx_sg; 35362306a36Sopenharmony_ci if (!sgt->nents || sgt->nents > QSPI_MAX_SG) { 35462306a36Sopenharmony_ci dev_warn_once(ctrl->dev, "Cannot handle %d entries in scatter list\n", sgt->nents); 35562306a36Sopenharmony_ci return -EAGAIN; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci for (i = 0; i < sgt->nents; i++) { 35962306a36Sopenharmony_ci dma_ptr_sg = sg_dma_address(sgt->sgl + i); 36062306a36Sopenharmony_ci dma_len_sg = sg_dma_len(sgt->sgl + i); 36162306a36Sopenharmony_ci if (!IS_ALIGNED(dma_ptr_sg, QSPI_ALIGN_REQ)) { 36262306a36Sopenharmony_ci dev_warn_once(ctrl->dev, "dma_address not aligned to %d\n", QSPI_ALIGN_REQ); 36362306a36Sopenharmony_ci return -EAGAIN; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci /* 36662306a36Sopenharmony_ci * When reading with DMA the controller writes to memory 1 word 36762306a36Sopenharmony_ci * at a time. If the length isn't a multiple of 4 bytes then 36862306a36Sopenharmony_ci * the controller can clobber the things later in memory. 36962306a36Sopenharmony_ci * Fallback to PIO to be safe. 37062306a36Sopenharmony_ci */ 37162306a36Sopenharmony_ci if (ctrl->xfer.dir == QSPI_READ && (dma_len_sg & 0x03)) { 37262306a36Sopenharmony_ci dev_warn_once(ctrl->dev, "fallback to PIO for read of size %#010x\n", 37362306a36Sopenharmony_ci dma_len_sg); 37462306a36Sopenharmony_ci return -EAGAIN; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci for (i = 0; i < sgt->nents; i++) { 37962306a36Sopenharmony_ci dma_ptr_sg = sg_dma_address(sgt->sgl + i); 38062306a36Sopenharmony_ci dma_len_sg = sg_dma_len(sgt->sgl + i); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci ret = qcom_qspi_alloc_desc(ctrl, dma_ptr_sg, dma_len_sg); 38362306a36Sopenharmony_ci if (ret) 38462306a36Sopenharmony_ci goto cleanup; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci return 0; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cicleanup: 38962306a36Sopenharmony_ci for (i = 0; i < ctrl->n_cmd_desc; i++) 39062306a36Sopenharmony_ci dma_pool_free(ctrl->dma_cmd_pool, ctrl->virt_cmd_desc[i], 39162306a36Sopenharmony_ci ctrl->dma_cmd_desc[i]); 39262306a36Sopenharmony_ci ctrl->n_cmd_desc = 0; 39362306a36Sopenharmony_ci return ret; 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic void qcom_qspi_dma_xfer(struct qcom_qspi *ctrl) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci /* Setup new interrupts */ 39962306a36Sopenharmony_ci writel(DMA_CHAIN_DONE, ctrl->base + MSTR_INT_EN); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* kick off transfer */ 40262306a36Sopenharmony_ci writel((u32)((ctrl->dma_cmd_desc)[0]), ctrl->base + NEXT_DMA_DESC_ADDR); 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci/* Switch to DMA if transfer length exceeds this */ 40662306a36Sopenharmony_ci#define QSPI_MAX_BYTES_FIFO 64 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic bool qcom_qspi_can_dma(struct spi_controller *ctlr, 40962306a36Sopenharmony_ci struct spi_device *slv, struct spi_transfer *xfer) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci return xfer->len > QSPI_MAX_BYTES_FIFO; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic int qcom_qspi_transfer_one(struct spi_controller *host, 41562306a36Sopenharmony_ci struct spi_device *slv, 41662306a36Sopenharmony_ci struct spi_transfer *xfer) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci struct qcom_qspi *ctrl = spi_controller_get_devdata(host); 41962306a36Sopenharmony_ci int ret; 42062306a36Sopenharmony_ci unsigned long speed_hz; 42162306a36Sopenharmony_ci unsigned long flags; 42262306a36Sopenharmony_ci u32 mstr_cfg; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci speed_hz = slv->max_speed_hz; 42562306a36Sopenharmony_ci if (xfer->speed_hz) 42662306a36Sopenharmony_ci speed_hz = xfer->speed_hz; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci ret = qcom_qspi_set_speed(ctrl, speed_hz); 42962306a36Sopenharmony_ci if (ret) 43062306a36Sopenharmony_ci return ret; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci spin_lock_irqsave(&ctrl->lock, flags); 43362306a36Sopenharmony_ci mstr_cfg = readl(ctrl->base + MSTR_CONFIG); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* We are half duplex, so either rx or tx will be set */ 43662306a36Sopenharmony_ci if (xfer->rx_buf) { 43762306a36Sopenharmony_ci ctrl->xfer.dir = QSPI_READ; 43862306a36Sopenharmony_ci ctrl->xfer.buswidth = xfer->rx_nbits; 43962306a36Sopenharmony_ci ctrl->xfer.rx_buf = xfer->rx_buf; 44062306a36Sopenharmony_ci } else { 44162306a36Sopenharmony_ci ctrl->xfer.dir = QSPI_WRITE; 44262306a36Sopenharmony_ci ctrl->xfer.buswidth = xfer->tx_nbits; 44362306a36Sopenharmony_ci ctrl->xfer.tx_buf = xfer->tx_buf; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci ctrl->xfer.is_last = list_is_last(&xfer->transfer_list, 44662306a36Sopenharmony_ci &host->cur_msg->transfers); 44762306a36Sopenharmony_ci ctrl->xfer.rem_bytes = xfer->len; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (xfer->rx_sg.nents || xfer->tx_sg.nents) { 45062306a36Sopenharmony_ci /* do DMA transfer */ 45162306a36Sopenharmony_ci if (!(mstr_cfg & DMA_ENABLE)) { 45262306a36Sopenharmony_ci mstr_cfg |= DMA_ENABLE; 45362306a36Sopenharmony_ci writel(mstr_cfg, ctrl->base + MSTR_CONFIG); 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci ret = qcom_qspi_setup_dma_desc(ctrl, xfer); 45762306a36Sopenharmony_ci if (ret != -EAGAIN) { 45862306a36Sopenharmony_ci if (!ret) { 45962306a36Sopenharmony_ci dma_wmb(); 46062306a36Sopenharmony_ci qcom_qspi_dma_xfer(ctrl); 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci goto exit; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci dev_warn_once(ctrl->dev, "DMA failure, falling back to PIO\n"); 46562306a36Sopenharmony_ci ret = 0; /* We'll retry w/ PIO */ 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (mstr_cfg & DMA_ENABLE) { 46962306a36Sopenharmony_ci mstr_cfg &= ~DMA_ENABLE; 47062306a36Sopenharmony_ci writel(mstr_cfg, ctrl->base + MSTR_CONFIG); 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci qcom_qspi_pio_xfer(ctrl); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ciexit: 47562306a36Sopenharmony_ci spin_unlock_irqrestore(&ctrl->lock, flags); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (ret) 47862306a36Sopenharmony_ci return ret; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* We'll call spi_finalize_current_transfer() when done */ 48162306a36Sopenharmony_ci return 1; 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic int qcom_qspi_prepare_message(struct spi_controller *host, 48562306a36Sopenharmony_ci struct spi_message *message) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci u32 mstr_cfg; 48862306a36Sopenharmony_ci struct qcom_qspi *ctrl; 48962306a36Sopenharmony_ci int tx_data_oe_delay = 1; 49062306a36Sopenharmony_ci int tx_data_delay = 1; 49162306a36Sopenharmony_ci unsigned long flags; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci ctrl = spi_controller_get_devdata(host); 49462306a36Sopenharmony_ci spin_lock_irqsave(&ctrl->lock, flags); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci mstr_cfg = readl(ctrl->base + MSTR_CONFIG); 49762306a36Sopenharmony_ci mstr_cfg &= ~CHIP_SELECT_NUM; 49862306a36Sopenharmony_ci if (spi_get_chipselect(message->spi, 0)) 49962306a36Sopenharmony_ci mstr_cfg |= CHIP_SELECT_NUM; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci mstr_cfg |= FB_CLK_EN | PIN_WPN | PIN_HOLDN | SBL_EN | FULL_CYCLE_MODE; 50262306a36Sopenharmony_ci mstr_cfg &= ~(SPI_MODE_MSK | TX_DATA_OE_DELAY_MSK | TX_DATA_DELAY_MSK); 50362306a36Sopenharmony_ci mstr_cfg |= message->spi->mode << SPI_MODE_SHFT; 50462306a36Sopenharmony_ci mstr_cfg |= tx_data_oe_delay << TX_DATA_OE_DELAY_SHFT; 50562306a36Sopenharmony_ci mstr_cfg |= tx_data_delay << TX_DATA_DELAY_SHFT; 50662306a36Sopenharmony_ci mstr_cfg &= ~DMA_ENABLE; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci writel(mstr_cfg, ctrl->base + MSTR_CONFIG); 50962306a36Sopenharmony_ci spin_unlock_irqrestore(&ctrl->lock, flags); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci return 0; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic int qcom_qspi_alloc_dma(struct qcom_qspi *ctrl) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci ctrl->dma_cmd_pool = dmam_pool_create("qspi cmd desc pool", 51762306a36Sopenharmony_ci ctrl->dev, sizeof(struct qspi_cmd_desc), 0, 0); 51862306a36Sopenharmony_ci if (!ctrl->dma_cmd_pool) 51962306a36Sopenharmony_ci return -ENOMEM; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci return 0; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic irqreturn_t pio_read(struct qcom_qspi *ctrl) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci u32 rd_fifo_status; 52762306a36Sopenharmony_ci u32 rd_fifo; 52862306a36Sopenharmony_ci unsigned int wr_cnts; 52962306a36Sopenharmony_ci unsigned int bytes_to_read; 53062306a36Sopenharmony_ci unsigned int words_to_read; 53162306a36Sopenharmony_ci u32 *word_buf; 53262306a36Sopenharmony_ci u8 *byte_buf; 53362306a36Sopenharmony_ci int i; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci rd_fifo_status = readl(ctrl->base + RD_FIFO_STATUS); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (!(rd_fifo_status & FIFO_RDY)) { 53862306a36Sopenharmony_ci dev_dbg(ctrl->dev, "Spurious IRQ %#x\n", rd_fifo_status); 53962306a36Sopenharmony_ci return IRQ_NONE; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci wr_cnts = (rd_fifo_status & WR_CNTS_MSK) >> WR_CNTS_SHFT; 54362306a36Sopenharmony_ci wr_cnts = min(wr_cnts, ctrl->xfer.rem_bytes); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci words_to_read = wr_cnts / QSPI_BYTES_PER_WORD; 54662306a36Sopenharmony_ci bytes_to_read = wr_cnts % QSPI_BYTES_PER_WORD; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if (words_to_read) { 54962306a36Sopenharmony_ci word_buf = ctrl->xfer.rx_buf; 55062306a36Sopenharmony_ci ctrl->xfer.rem_bytes -= words_to_read * QSPI_BYTES_PER_WORD; 55162306a36Sopenharmony_ci ioread32_rep(ctrl->base + RD_FIFO, word_buf, words_to_read); 55262306a36Sopenharmony_ci ctrl->xfer.rx_buf = word_buf + words_to_read; 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (bytes_to_read) { 55662306a36Sopenharmony_ci byte_buf = ctrl->xfer.rx_buf; 55762306a36Sopenharmony_ci rd_fifo = readl(ctrl->base + RD_FIFO); 55862306a36Sopenharmony_ci ctrl->xfer.rem_bytes -= bytes_to_read; 55962306a36Sopenharmony_ci for (i = 0; i < bytes_to_read; i++) 56062306a36Sopenharmony_ci *byte_buf++ = rd_fifo >> (i * BITS_PER_BYTE); 56162306a36Sopenharmony_ci ctrl->xfer.rx_buf = byte_buf; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci return IRQ_HANDLED; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic irqreturn_t pio_write(struct qcom_qspi *ctrl) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci const void *xfer_buf = ctrl->xfer.tx_buf; 57062306a36Sopenharmony_ci const int *word_buf; 57162306a36Sopenharmony_ci const char *byte_buf; 57262306a36Sopenharmony_ci unsigned int wr_fifo_bytes; 57362306a36Sopenharmony_ci unsigned int wr_fifo_words; 57462306a36Sopenharmony_ci unsigned int wr_size; 57562306a36Sopenharmony_ci unsigned int rem_words; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci wr_fifo_bytes = readl(ctrl->base + PIO_XFER_STATUS); 57862306a36Sopenharmony_ci wr_fifo_bytes >>= WR_FIFO_BYTES_SHFT; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (ctrl->xfer.rem_bytes < QSPI_BYTES_PER_WORD) { 58162306a36Sopenharmony_ci /* Process the last 1-3 bytes */ 58262306a36Sopenharmony_ci wr_size = min(wr_fifo_bytes, ctrl->xfer.rem_bytes); 58362306a36Sopenharmony_ci ctrl->xfer.rem_bytes -= wr_size; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci byte_buf = xfer_buf; 58662306a36Sopenharmony_ci while (wr_size--) 58762306a36Sopenharmony_ci writel(*byte_buf++, 58862306a36Sopenharmony_ci ctrl->base + PIO_DATAOUT_1B); 58962306a36Sopenharmony_ci ctrl->xfer.tx_buf = byte_buf; 59062306a36Sopenharmony_ci } else { 59162306a36Sopenharmony_ci /* 59262306a36Sopenharmony_ci * Process all the whole words; to keep things simple we'll 59362306a36Sopenharmony_ci * just wait for the next interrupt to handle the last 1-3 59462306a36Sopenharmony_ci * bytes if we don't have an even number of words. 59562306a36Sopenharmony_ci */ 59662306a36Sopenharmony_ci rem_words = ctrl->xfer.rem_bytes / QSPI_BYTES_PER_WORD; 59762306a36Sopenharmony_ci wr_fifo_words = wr_fifo_bytes / QSPI_BYTES_PER_WORD; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci wr_size = min(rem_words, wr_fifo_words); 60062306a36Sopenharmony_ci ctrl->xfer.rem_bytes -= wr_size * QSPI_BYTES_PER_WORD; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci word_buf = xfer_buf; 60362306a36Sopenharmony_ci iowrite32_rep(ctrl->base + PIO_DATAOUT_4B, word_buf, wr_size); 60462306a36Sopenharmony_ci ctrl->xfer.tx_buf = word_buf + wr_size; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci return IRQ_HANDLED; 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cistatic irqreturn_t qcom_qspi_irq(int irq, void *dev_id) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci u32 int_status; 61462306a36Sopenharmony_ci struct qcom_qspi *ctrl = dev_id; 61562306a36Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci spin_lock(&ctrl->lock); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci int_status = readl(ctrl->base + MSTR_INT_STATUS); 62062306a36Sopenharmony_ci writel(int_status, ctrl->base + MSTR_INT_STATUS); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci /* Ignore disabled interrupts */ 62362306a36Sopenharmony_ci int_status &= readl(ctrl->base + MSTR_INT_EN); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci /* PIO mode handling */ 62662306a36Sopenharmony_ci if (ctrl->xfer.dir == QSPI_WRITE) { 62762306a36Sopenharmony_ci if (int_status & WR_FIFO_EMPTY) 62862306a36Sopenharmony_ci ret = pio_write(ctrl); 62962306a36Sopenharmony_ci } else { 63062306a36Sopenharmony_ci if (int_status & RESP_FIFO_RDY) 63162306a36Sopenharmony_ci ret = pio_read(ctrl); 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (int_status & QSPI_ERR_IRQS) { 63562306a36Sopenharmony_ci if (int_status & RESP_FIFO_UNDERRUN) 63662306a36Sopenharmony_ci dev_err(ctrl->dev, "IRQ error: FIFO underrun\n"); 63762306a36Sopenharmony_ci if (int_status & WR_FIFO_OVERRUN) 63862306a36Sopenharmony_ci dev_err(ctrl->dev, "IRQ error: FIFO overrun\n"); 63962306a36Sopenharmony_ci if (int_status & HRESP_FROM_NOC_ERR) 64062306a36Sopenharmony_ci dev_err(ctrl->dev, "IRQ error: NOC response error\n"); 64162306a36Sopenharmony_ci ret = IRQ_HANDLED; 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (!ctrl->xfer.rem_bytes) { 64562306a36Sopenharmony_ci writel(0, ctrl->base + MSTR_INT_EN); 64662306a36Sopenharmony_ci spi_finalize_current_transfer(dev_get_drvdata(ctrl->dev)); 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci /* DMA mode handling */ 65062306a36Sopenharmony_ci if (int_status & DMA_CHAIN_DONE) { 65162306a36Sopenharmony_ci int i; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci writel(0, ctrl->base + MSTR_INT_EN); 65462306a36Sopenharmony_ci ctrl->xfer.rem_bytes = 0; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci for (i = 0; i < ctrl->n_cmd_desc; i++) 65762306a36Sopenharmony_ci dma_pool_free(ctrl->dma_cmd_pool, ctrl->virt_cmd_desc[i], 65862306a36Sopenharmony_ci ctrl->dma_cmd_desc[i]); 65962306a36Sopenharmony_ci ctrl->n_cmd_desc = 0; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci ret = IRQ_HANDLED; 66262306a36Sopenharmony_ci spi_finalize_current_transfer(dev_get_drvdata(ctrl->dev)); 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci spin_unlock(&ctrl->lock); 66662306a36Sopenharmony_ci return ret; 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic int qcom_qspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci /* 67262306a36Sopenharmony_ci * If qcom_qspi_can_dma() is going to return false we don't need to 67362306a36Sopenharmony_ci * adjust anything. 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_ci if (op->data.nbytes <= QSPI_MAX_BYTES_FIFO) 67662306a36Sopenharmony_ci return 0; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci /* 67962306a36Sopenharmony_ci * When reading, the transfer needs to be a multiple of 4 bytes so 68062306a36Sopenharmony_ci * shrink the transfer if that's not true. The caller will then do a 68162306a36Sopenharmony_ci * second transfer to finish things up. 68262306a36Sopenharmony_ci */ 68362306a36Sopenharmony_ci if (op->data.dir == SPI_MEM_DATA_IN && (op->data.nbytes & 0x3)) 68462306a36Sopenharmony_ci op->data.nbytes &= ~0x3; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci return 0; 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_cistatic const struct spi_controller_mem_ops qcom_qspi_mem_ops = { 69062306a36Sopenharmony_ci .adjust_op_size = qcom_qspi_adjust_op_size, 69162306a36Sopenharmony_ci}; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_cistatic int qcom_qspi_probe(struct platform_device *pdev) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci int ret; 69662306a36Sopenharmony_ci struct device *dev; 69762306a36Sopenharmony_ci struct spi_controller *host; 69862306a36Sopenharmony_ci struct qcom_qspi *ctrl; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci dev = &pdev->dev; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci host = devm_spi_alloc_host(dev, sizeof(*ctrl)); 70362306a36Sopenharmony_ci if (!host) 70462306a36Sopenharmony_ci return -ENOMEM; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci platform_set_drvdata(pdev, host); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci ctrl = spi_controller_get_devdata(host); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci spin_lock_init(&ctrl->lock); 71162306a36Sopenharmony_ci ctrl->dev = dev; 71262306a36Sopenharmony_ci ctrl->base = devm_platform_ioremap_resource(pdev, 0); 71362306a36Sopenharmony_ci if (IS_ERR(ctrl->base)) 71462306a36Sopenharmony_ci return PTR_ERR(ctrl->base); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci ctrl->clks = devm_kcalloc(dev, QSPI_NUM_CLKS, 71762306a36Sopenharmony_ci sizeof(*ctrl->clks), GFP_KERNEL); 71862306a36Sopenharmony_ci if (!ctrl->clks) 71962306a36Sopenharmony_ci return -ENOMEM; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci ctrl->clks[QSPI_CLK_CORE].id = "core"; 72262306a36Sopenharmony_ci ctrl->clks[QSPI_CLK_IFACE].id = "iface"; 72362306a36Sopenharmony_ci ret = devm_clk_bulk_get(dev, QSPI_NUM_CLKS, ctrl->clks); 72462306a36Sopenharmony_ci if (ret) 72562306a36Sopenharmony_ci return ret; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci ctrl->icc_path_cpu_to_qspi = devm_of_icc_get(dev, "qspi-config"); 72862306a36Sopenharmony_ci if (IS_ERR(ctrl->icc_path_cpu_to_qspi)) 72962306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(ctrl->icc_path_cpu_to_qspi), 73062306a36Sopenharmony_ci "Failed to get cpu path\n"); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* Set BW vote for register access */ 73362306a36Sopenharmony_ci ret = icc_set_bw(ctrl->icc_path_cpu_to_qspi, Bps_to_icc(1000), 73462306a36Sopenharmony_ci Bps_to_icc(1000)); 73562306a36Sopenharmony_ci if (ret) { 73662306a36Sopenharmony_ci dev_err(ctrl->dev, "%s: ICC BW voting failed for cpu: %d\n", 73762306a36Sopenharmony_ci __func__, ret); 73862306a36Sopenharmony_ci return ret; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci ret = icc_disable(ctrl->icc_path_cpu_to_qspi); 74262306a36Sopenharmony_ci if (ret) { 74362306a36Sopenharmony_ci dev_err(ctrl->dev, "%s: ICC disable failed for cpu: %d\n", 74462306a36Sopenharmony_ci __func__, ret); 74562306a36Sopenharmony_ci return ret; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci ret = platform_get_irq(pdev, 0); 74962306a36Sopenharmony_ci if (ret < 0) 75062306a36Sopenharmony_ci return ret; 75162306a36Sopenharmony_ci ret = devm_request_irq(dev, ret, qcom_qspi_irq, 0, dev_name(dev), ctrl); 75262306a36Sopenharmony_ci if (ret) { 75362306a36Sopenharmony_ci dev_err(dev, "Failed to request irq %d\n", ret); 75462306a36Sopenharmony_ci return ret; 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); 75862306a36Sopenharmony_ci if (ret) 75962306a36Sopenharmony_ci return dev_err_probe(dev, ret, "could not set DMA mask\n"); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci host->max_speed_hz = 300000000; 76262306a36Sopenharmony_ci host->max_dma_len = 65536; /* as per HPG */ 76362306a36Sopenharmony_ci host->dma_alignment = QSPI_ALIGN_REQ; 76462306a36Sopenharmony_ci host->num_chipselect = QSPI_NUM_CS; 76562306a36Sopenharmony_ci host->bus_num = -1; 76662306a36Sopenharmony_ci host->dev.of_node = pdev->dev.of_node; 76762306a36Sopenharmony_ci host->mode_bits = SPI_MODE_0 | 76862306a36Sopenharmony_ci SPI_TX_DUAL | SPI_RX_DUAL | 76962306a36Sopenharmony_ci SPI_TX_QUAD | SPI_RX_QUAD; 77062306a36Sopenharmony_ci host->flags = SPI_CONTROLLER_HALF_DUPLEX; 77162306a36Sopenharmony_ci host->prepare_message = qcom_qspi_prepare_message; 77262306a36Sopenharmony_ci host->transfer_one = qcom_qspi_transfer_one; 77362306a36Sopenharmony_ci host->handle_err = qcom_qspi_handle_err; 77462306a36Sopenharmony_ci if (of_property_read_bool(pdev->dev.of_node, "iommus")) 77562306a36Sopenharmony_ci host->can_dma = qcom_qspi_can_dma; 77662306a36Sopenharmony_ci host->auto_runtime_pm = true; 77762306a36Sopenharmony_ci host->mem_ops = &qcom_qspi_mem_ops; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci ret = devm_pm_opp_set_clkname(&pdev->dev, "core"); 78062306a36Sopenharmony_ci if (ret) 78162306a36Sopenharmony_ci return ret; 78262306a36Sopenharmony_ci /* OPP table is optional */ 78362306a36Sopenharmony_ci ret = devm_pm_opp_of_add_table(&pdev->dev); 78462306a36Sopenharmony_ci if (ret && ret != -ENODEV) { 78562306a36Sopenharmony_ci dev_err(&pdev->dev, "invalid OPP table in device tree\n"); 78662306a36Sopenharmony_ci return ret; 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci ret = qcom_qspi_alloc_dma(ctrl); 79062306a36Sopenharmony_ci if (ret) 79162306a36Sopenharmony_ci return ret; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci pm_runtime_use_autosuspend(dev); 79462306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(dev, 250); 79562306a36Sopenharmony_ci pm_runtime_enable(dev); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci ret = spi_register_controller(host); 79862306a36Sopenharmony_ci if (!ret) 79962306a36Sopenharmony_ci return 0; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci pm_runtime_disable(dev); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci return ret; 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cistatic void qcom_qspi_remove(struct platform_device *pdev) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci struct spi_controller *host = platform_get_drvdata(pdev); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci /* Unregister _before_ disabling pm_runtime() so we stop transfers */ 81162306a36Sopenharmony_ci spi_unregister_controller(host); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cistatic int __maybe_unused qcom_qspi_runtime_suspend(struct device *dev) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci struct spi_controller *host = dev_get_drvdata(dev); 81962306a36Sopenharmony_ci struct qcom_qspi *ctrl = spi_controller_get_devdata(host); 82062306a36Sopenharmony_ci int ret; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci /* Drop the performance state vote */ 82362306a36Sopenharmony_ci dev_pm_opp_set_rate(dev, 0); 82462306a36Sopenharmony_ci clk_bulk_disable_unprepare(QSPI_NUM_CLKS, ctrl->clks); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci ret = icc_disable(ctrl->icc_path_cpu_to_qspi); 82762306a36Sopenharmony_ci if (ret) { 82862306a36Sopenharmony_ci dev_err_ratelimited(ctrl->dev, "%s: ICC disable failed for cpu: %d\n", 82962306a36Sopenharmony_ci __func__, ret); 83062306a36Sopenharmony_ci return ret; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci pinctrl_pm_select_sleep_state(dev); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci return 0; 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_cistatic int __maybe_unused qcom_qspi_runtime_resume(struct device *dev) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci struct spi_controller *host = dev_get_drvdata(dev); 84162306a36Sopenharmony_ci struct qcom_qspi *ctrl = spi_controller_get_devdata(host); 84262306a36Sopenharmony_ci int ret; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci pinctrl_pm_select_default_state(dev); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci ret = icc_enable(ctrl->icc_path_cpu_to_qspi); 84762306a36Sopenharmony_ci if (ret) { 84862306a36Sopenharmony_ci dev_err_ratelimited(ctrl->dev, "%s: ICC enable failed for cpu: %d\n", 84962306a36Sopenharmony_ci __func__, ret); 85062306a36Sopenharmony_ci return ret; 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci ret = clk_bulk_prepare_enable(QSPI_NUM_CLKS, ctrl->clks); 85462306a36Sopenharmony_ci if (ret) 85562306a36Sopenharmony_ci return ret; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci return dev_pm_opp_set_rate(dev, ctrl->last_speed * 4); 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cistatic int __maybe_unused qcom_qspi_suspend(struct device *dev) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci struct spi_controller *host = dev_get_drvdata(dev); 86362306a36Sopenharmony_ci int ret; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci ret = spi_controller_suspend(host); 86662306a36Sopenharmony_ci if (ret) 86762306a36Sopenharmony_ci return ret; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci ret = pm_runtime_force_suspend(dev); 87062306a36Sopenharmony_ci if (ret) 87162306a36Sopenharmony_ci spi_controller_resume(host); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci return ret; 87462306a36Sopenharmony_ci} 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_cistatic int __maybe_unused qcom_qspi_resume(struct device *dev) 87762306a36Sopenharmony_ci{ 87862306a36Sopenharmony_ci struct spi_controller *host = dev_get_drvdata(dev); 87962306a36Sopenharmony_ci int ret; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci ret = pm_runtime_force_resume(dev); 88262306a36Sopenharmony_ci if (ret) 88362306a36Sopenharmony_ci return ret; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci ret = spi_controller_resume(host); 88662306a36Sopenharmony_ci if (ret) 88762306a36Sopenharmony_ci pm_runtime_force_suspend(dev); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci return ret; 89062306a36Sopenharmony_ci} 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_cistatic const struct dev_pm_ops qcom_qspi_dev_pm_ops = { 89362306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(qcom_qspi_runtime_suspend, 89462306a36Sopenharmony_ci qcom_qspi_runtime_resume, NULL) 89562306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(qcom_qspi_suspend, qcom_qspi_resume) 89662306a36Sopenharmony_ci}; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_cistatic const struct of_device_id qcom_qspi_dt_match[] = { 89962306a36Sopenharmony_ci { .compatible = "qcom,qspi-v1", }, 90062306a36Sopenharmony_ci { } 90162306a36Sopenharmony_ci}; 90262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, qcom_qspi_dt_match); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_cistatic struct platform_driver qcom_qspi_driver = { 90562306a36Sopenharmony_ci .driver = { 90662306a36Sopenharmony_ci .name = "qcom_qspi", 90762306a36Sopenharmony_ci .pm = &qcom_qspi_dev_pm_ops, 90862306a36Sopenharmony_ci .of_match_table = qcom_qspi_dt_match, 90962306a36Sopenharmony_ci }, 91062306a36Sopenharmony_ci .probe = qcom_qspi_probe, 91162306a36Sopenharmony_ci .remove_new = qcom_qspi_remove, 91262306a36Sopenharmony_ci}; 91362306a36Sopenharmony_cimodule_platform_driver(qcom_qspi_driver); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ciMODULE_DESCRIPTION("SPI driver for QSPI cores"); 91662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 917