162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SPI bus driver for the Topcliff PCH used by Intel SoCs 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/delay.h> 962306a36Sopenharmony_ci#include <linux/pci.h> 1062306a36Sopenharmony_ci#include <linux/wait.h> 1162306a36Sopenharmony_ci#include <linux/spi/spi.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/sched.h> 1462306a36Sopenharmony_ci#include <linux/spi/spidev.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/device.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/dmaengine.h> 2062306a36Sopenharmony_ci#include <linux/pch_dma.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* Register offsets */ 2362306a36Sopenharmony_ci#define PCH_SPCR 0x00 /* SPI control register */ 2462306a36Sopenharmony_ci#define PCH_SPBRR 0x04 /* SPI baud rate register */ 2562306a36Sopenharmony_ci#define PCH_SPSR 0x08 /* SPI status register */ 2662306a36Sopenharmony_ci#define PCH_SPDWR 0x0C /* SPI write data register */ 2762306a36Sopenharmony_ci#define PCH_SPDRR 0x10 /* SPI read data register */ 2862306a36Sopenharmony_ci#define PCH_SSNXCR 0x18 /* SSN Expand Control Register */ 2962306a36Sopenharmony_ci#define PCH_SRST 0x1C /* SPI reset register */ 3062306a36Sopenharmony_ci#define PCH_ADDRESS_SIZE 0x20 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define PCH_SPSR_TFD 0x000007C0 3362306a36Sopenharmony_ci#define PCH_SPSR_RFD 0x0000F800 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define PCH_READABLE(x) (((x) & PCH_SPSR_RFD)>>11) 3662306a36Sopenharmony_ci#define PCH_WRITABLE(x) (((x) & PCH_SPSR_TFD)>>6) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define PCH_RX_THOLD 7 3962306a36Sopenharmony_ci#define PCH_RX_THOLD_MAX 15 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define PCH_TX_THOLD 2 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define PCH_MAX_BAUDRATE 5000000 4462306a36Sopenharmony_ci#define PCH_MAX_FIFO_DEPTH 16 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define STATUS_RUNNING 1 4762306a36Sopenharmony_ci#define STATUS_EXITING 2 4862306a36Sopenharmony_ci#define PCH_SLEEP_TIME 10 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define SSN_LOW 0x02U 5162306a36Sopenharmony_ci#define SSN_HIGH 0x03U 5262306a36Sopenharmony_ci#define SSN_NO_CONTROL 0x00U 5362306a36Sopenharmony_ci#define PCH_MAX_CS 0xFF 5462306a36Sopenharmony_ci#define PCI_DEVICE_ID_GE_SPI 0x8816 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define SPCR_SPE_BIT (1 << 0) 5762306a36Sopenharmony_ci#define SPCR_MSTR_BIT (1 << 1) 5862306a36Sopenharmony_ci#define SPCR_LSBF_BIT (1 << 4) 5962306a36Sopenharmony_ci#define SPCR_CPHA_BIT (1 << 5) 6062306a36Sopenharmony_ci#define SPCR_CPOL_BIT (1 << 6) 6162306a36Sopenharmony_ci#define SPCR_TFIE_BIT (1 << 8) 6262306a36Sopenharmony_ci#define SPCR_RFIE_BIT (1 << 9) 6362306a36Sopenharmony_ci#define SPCR_FIE_BIT (1 << 10) 6462306a36Sopenharmony_ci#define SPCR_ORIE_BIT (1 << 11) 6562306a36Sopenharmony_ci#define SPCR_MDFIE_BIT (1 << 12) 6662306a36Sopenharmony_ci#define SPCR_FICLR_BIT (1 << 24) 6762306a36Sopenharmony_ci#define SPSR_TFI_BIT (1 << 0) 6862306a36Sopenharmony_ci#define SPSR_RFI_BIT (1 << 1) 6962306a36Sopenharmony_ci#define SPSR_FI_BIT (1 << 2) 7062306a36Sopenharmony_ci#define SPSR_ORF_BIT (1 << 3) 7162306a36Sopenharmony_ci#define SPBRR_SIZE_BIT (1 << 10) 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define PCH_ALL (SPCR_TFIE_BIT|SPCR_RFIE_BIT|SPCR_FIE_BIT|\ 7462306a36Sopenharmony_ci SPCR_ORIE_BIT|SPCR_MDFIE_BIT) 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define SPCR_RFIC_FIELD 20 7762306a36Sopenharmony_ci#define SPCR_TFIC_FIELD 16 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define MASK_SPBRR_SPBR_BITS ((1 << 10) - 1) 8062306a36Sopenharmony_ci#define MASK_RFIC_SPCR_BITS (0xf << SPCR_RFIC_FIELD) 8162306a36Sopenharmony_ci#define MASK_TFIC_SPCR_BITS (0xf << SPCR_TFIC_FIELD) 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define PCH_CLOCK_HZ 50000000 8462306a36Sopenharmony_ci#define PCH_MAX_SPBR 1023 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* Definition for ML7213/ML7223/ML7831 by LAPIS Semiconductor */ 8762306a36Sopenharmony_ci#define PCI_DEVICE_ID_ML7213_SPI 0x802c 8862306a36Sopenharmony_ci#define PCI_DEVICE_ID_ML7223_SPI 0x800F 8962306a36Sopenharmony_ci#define PCI_DEVICE_ID_ML7831_SPI 0x8816 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* 9262306a36Sopenharmony_ci * Set the number of SPI instance max 9362306a36Sopenharmony_ci * Intel EG20T PCH : 1ch 9462306a36Sopenharmony_ci * LAPIS Semiconductor ML7213 IOH : 2ch 9562306a36Sopenharmony_ci * LAPIS Semiconductor ML7223 IOH : 1ch 9662306a36Sopenharmony_ci * LAPIS Semiconductor ML7831 IOH : 1ch 9762306a36Sopenharmony_ci*/ 9862306a36Sopenharmony_ci#define PCH_SPI_MAX_DEV 2 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define PCH_BUF_SIZE 4096 10162306a36Sopenharmony_ci#define PCH_DMA_TRANS_SIZE 12 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic int use_dma = 1; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistruct pch_spi_dma_ctrl { 10662306a36Sopenharmony_ci struct pci_dev *dma_dev; 10762306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc_tx; 10862306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc_rx; 10962306a36Sopenharmony_ci struct pch_dma_slave param_tx; 11062306a36Sopenharmony_ci struct pch_dma_slave param_rx; 11162306a36Sopenharmony_ci struct dma_chan *chan_tx; 11262306a36Sopenharmony_ci struct dma_chan *chan_rx; 11362306a36Sopenharmony_ci struct scatterlist *sg_tx_p; 11462306a36Sopenharmony_ci struct scatterlist *sg_rx_p; 11562306a36Sopenharmony_ci struct scatterlist sg_tx; 11662306a36Sopenharmony_ci struct scatterlist sg_rx; 11762306a36Sopenharmony_ci int nent; 11862306a36Sopenharmony_ci void *tx_buf_virt; 11962306a36Sopenharmony_ci void *rx_buf_virt; 12062306a36Sopenharmony_ci dma_addr_t tx_buf_dma; 12162306a36Sopenharmony_ci dma_addr_t rx_buf_dma; 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci/** 12462306a36Sopenharmony_ci * struct pch_spi_data - Holds the SPI channel specific details 12562306a36Sopenharmony_ci * @io_remap_addr: The remapped PCI base address 12662306a36Sopenharmony_ci * @io_base_addr: Base address 12762306a36Sopenharmony_ci * @master: Pointer to the SPI master structure 12862306a36Sopenharmony_ci * @work: Reference to work queue handler 12962306a36Sopenharmony_ci * @wait: Wait queue for waking up upon receiving an 13062306a36Sopenharmony_ci * interrupt. 13162306a36Sopenharmony_ci * @transfer_complete: Status of SPI Transfer 13262306a36Sopenharmony_ci * @bcurrent_msg_processing: Status flag for message processing 13362306a36Sopenharmony_ci * @lock: Lock for protecting this structure 13462306a36Sopenharmony_ci * @queue: SPI Message queue 13562306a36Sopenharmony_ci * @status: Status of the SPI driver 13662306a36Sopenharmony_ci * @bpw_len: Length of data to be transferred in bits per 13762306a36Sopenharmony_ci * word 13862306a36Sopenharmony_ci * @transfer_active: Flag showing active transfer 13962306a36Sopenharmony_ci * @tx_index: Transmit data count; for bookkeeping during 14062306a36Sopenharmony_ci * transfer 14162306a36Sopenharmony_ci * @rx_index: Receive data count; for bookkeeping during 14262306a36Sopenharmony_ci * transfer 14362306a36Sopenharmony_ci * @pkt_tx_buff: Buffer for data to be transmitted 14462306a36Sopenharmony_ci * @pkt_rx_buff: Buffer for received data 14562306a36Sopenharmony_ci * @n_curnt_chip: The chip number that this SPI driver currently 14662306a36Sopenharmony_ci * operates on 14762306a36Sopenharmony_ci * @current_chip: Reference to the current chip that this SPI 14862306a36Sopenharmony_ci * driver currently operates on 14962306a36Sopenharmony_ci * @current_msg: The current message that this SPI driver is 15062306a36Sopenharmony_ci * handling 15162306a36Sopenharmony_ci * @cur_trans: The current transfer that this SPI driver is 15262306a36Sopenharmony_ci * handling 15362306a36Sopenharmony_ci * @board_dat: Reference to the SPI device data structure 15462306a36Sopenharmony_ci * @plat_dev: platform_device structure 15562306a36Sopenharmony_ci * @ch: SPI channel number 15662306a36Sopenharmony_ci * @dma: Local DMA information 15762306a36Sopenharmony_ci * @use_dma: True if DMA is to be used 15862306a36Sopenharmony_ci * @irq_reg_sts: Status of IRQ registration 15962306a36Sopenharmony_ci * @save_total_len: Save length while data is being transferred 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_cistruct pch_spi_data { 16262306a36Sopenharmony_ci void __iomem *io_remap_addr; 16362306a36Sopenharmony_ci unsigned long io_base_addr; 16462306a36Sopenharmony_ci struct spi_master *master; 16562306a36Sopenharmony_ci struct work_struct work; 16662306a36Sopenharmony_ci wait_queue_head_t wait; 16762306a36Sopenharmony_ci u8 transfer_complete; 16862306a36Sopenharmony_ci u8 bcurrent_msg_processing; 16962306a36Sopenharmony_ci spinlock_t lock; 17062306a36Sopenharmony_ci struct list_head queue; 17162306a36Sopenharmony_ci u8 status; 17262306a36Sopenharmony_ci u32 bpw_len; 17362306a36Sopenharmony_ci u8 transfer_active; 17462306a36Sopenharmony_ci u32 tx_index; 17562306a36Sopenharmony_ci u32 rx_index; 17662306a36Sopenharmony_ci u16 *pkt_tx_buff; 17762306a36Sopenharmony_ci u16 *pkt_rx_buff; 17862306a36Sopenharmony_ci u8 n_curnt_chip; 17962306a36Sopenharmony_ci struct spi_device *current_chip; 18062306a36Sopenharmony_ci struct spi_message *current_msg; 18162306a36Sopenharmony_ci struct spi_transfer *cur_trans; 18262306a36Sopenharmony_ci struct pch_spi_board_data *board_dat; 18362306a36Sopenharmony_ci struct platform_device *plat_dev; 18462306a36Sopenharmony_ci int ch; 18562306a36Sopenharmony_ci struct pch_spi_dma_ctrl dma; 18662306a36Sopenharmony_ci int use_dma; 18762306a36Sopenharmony_ci u8 irq_reg_sts; 18862306a36Sopenharmony_ci int save_total_len; 18962306a36Sopenharmony_ci}; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/** 19262306a36Sopenharmony_ci * struct pch_spi_board_data - Holds the SPI device specific details 19362306a36Sopenharmony_ci * @pdev: Pointer to the PCI device 19462306a36Sopenharmony_ci * @suspend_sts: Status of suspend 19562306a36Sopenharmony_ci * @num: The number of SPI device instance 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_cistruct pch_spi_board_data { 19862306a36Sopenharmony_ci struct pci_dev *pdev; 19962306a36Sopenharmony_ci u8 suspend_sts; 20062306a36Sopenharmony_ci int num; 20162306a36Sopenharmony_ci}; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistruct pch_pd_dev_save { 20462306a36Sopenharmony_ci int num; 20562306a36Sopenharmony_ci struct platform_device *pd_save[PCH_SPI_MAX_DEV]; 20662306a36Sopenharmony_ci struct pch_spi_board_data *board_dat; 20762306a36Sopenharmony_ci}; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic const struct pci_device_id pch_spi_pcidev_id[] = { 21062306a36Sopenharmony_ci { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_GE_SPI), 1, }, 21162306a36Sopenharmony_ci { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_SPI), 2, }, 21262306a36Sopenharmony_ci { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_SPI), 1, }, 21362306a36Sopenharmony_ci { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7831_SPI), 1, }, 21462306a36Sopenharmony_ci { } 21562306a36Sopenharmony_ci}; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci/** 21862306a36Sopenharmony_ci * pch_spi_writereg() - Performs register writes 21962306a36Sopenharmony_ci * @master: Pointer to struct spi_master. 22062306a36Sopenharmony_ci * @idx: Register offset. 22162306a36Sopenharmony_ci * @val: Value to be written to register. 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_cistatic inline void pch_spi_writereg(struct spi_master *master, int idx, u32 val) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct pch_spi_data *data = spi_master_get_devdata(master); 22662306a36Sopenharmony_ci iowrite32(val, (data->io_remap_addr + idx)); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/** 23062306a36Sopenharmony_ci * pch_spi_readreg() - Performs register reads 23162306a36Sopenharmony_ci * @master: Pointer to struct spi_master. 23262306a36Sopenharmony_ci * @idx: Register offset. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_cistatic inline u32 pch_spi_readreg(struct spi_master *master, int idx) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct pch_spi_data *data = spi_master_get_devdata(master); 23762306a36Sopenharmony_ci return ioread32(data->io_remap_addr + idx); 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic inline void pch_spi_setclr_reg(struct spi_master *master, int idx, 24162306a36Sopenharmony_ci u32 set, u32 clr) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci u32 tmp = pch_spi_readreg(master, idx); 24462306a36Sopenharmony_ci tmp = (tmp & ~clr) | set; 24562306a36Sopenharmony_ci pch_spi_writereg(master, idx, tmp); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic void pch_spi_set_master_mode(struct spi_master *master) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci pch_spi_setclr_reg(master, PCH_SPCR, SPCR_MSTR_BIT, 0); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci/** 25462306a36Sopenharmony_ci * pch_spi_clear_fifo() - Clears the Transmit and Receive FIFOs 25562306a36Sopenharmony_ci * @master: Pointer to struct spi_master. 25662306a36Sopenharmony_ci */ 25762306a36Sopenharmony_cistatic void pch_spi_clear_fifo(struct spi_master *master) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci pch_spi_setclr_reg(master, PCH_SPCR, SPCR_FICLR_BIT, 0); 26062306a36Sopenharmony_ci pch_spi_setclr_reg(master, PCH_SPCR, 0, SPCR_FICLR_BIT); 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val, 26462306a36Sopenharmony_ci void __iomem *io_remap_addr) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci u32 n_read, tx_index, rx_index, bpw_len; 26762306a36Sopenharmony_ci u16 *pkt_rx_buffer, *pkt_tx_buff; 26862306a36Sopenharmony_ci int read_cnt; 26962306a36Sopenharmony_ci u32 reg_spcr_val; 27062306a36Sopenharmony_ci void __iomem *spsr; 27162306a36Sopenharmony_ci void __iomem *spdrr; 27262306a36Sopenharmony_ci void __iomem *spdwr; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci spsr = io_remap_addr + PCH_SPSR; 27562306a36Sopenharmony_ci iowrite32(reg_spsr_val, spsr); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (data->transfer_active) { 27862306a36Sopenharmony_ci rx_index = data->rx_index; 27962306a36Sopenharmony_ci tx_index = data->tx_index; 28062306a36Sopenharmony_ci bpw_len = data->bpw_len; 28162306a36Sopenharmony_ci pkt_rx_buffer = data->pkt_rx_buff; 28262306a36Sopenharmony_ci pkt_tx_buff = data->pkt_tx_buff; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci spdrr = io_remap_addr + PCH_SPDRR; 28562306a36Sopenharmony_ci spdwr = io_remap_addr + PCH_SPDWR; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci n_read = PCH_READABLE(reg_spsr_val); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci for (read_cnt = 0; (read_cnt < n_read); read_cnt++) { 29062306a36Sopenharmony_ci pkt_rx_buffer[rx_index++] = ioread32(spdrr); 29162306a36Sopenharmony_ci if (tx_index < bpw_len) 29262306a36Sopenharmony_ci iowrite32(pkt_tx_buff[tx_index++], spdwr); 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* disable RFI if not needed */ 29662306a36Sopenharmony_ci if ((bpw_len - rx_index) <= PCH_MAX_FIFO_DEPTH) { 29762306a36Sopenharmony_ci reg_spcr_val = ioread32(io_remap_addr + PCH_SPCR); 29862306a36Sopenharmony_ci reg_spcr_val &= ~SPCR_RFIE_BIT; /* disable RFI */ 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* reset rx threshold */ 30162306a36Sopenharmony_ci reg_spcr_val &= ~MASK_RFIC_SPCR_BITS; 30262306a36Sopenharmony_ci reg_spcr_val |= (PCH_RX_THOLD_MAX << SPCR_RFIC_FIELD); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci iowrite32(reg_spcr_val, (io_remap_addr + PCH_SPCR)); 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* update counts */ 30862306a36Sopenharmony_ci data->tx_index = tx_index; 30962306a36Sopenharmony_ci data->rx_index = rx_index; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* if transfer complete interrupt */ 31262306a36Sopenharmony_ci if (reg_spsr_val & SPSR_FI_BIT) { 31362306a36Sopenharmony_ci if ((tx_index == bpw_len) && (rx_index == tx_index)) { 31462306a36Sopenharmony_ci /* disable interrupts */ 31562306a36Sopenharmony_ci pch_spi_setclr_reg(data->master, PCH_SPCR, 0, 31662306a36Sopenharmony_ci PCH_ALL); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* transfer is completed; 31962306a36Sopenharmony_ci inform pch_spi_process_messages */ 32062306a36Sopenharmony_ci data->transfer_complete = true; 32162306a36Sopenharmony_ci data->transfer_active = false; 32262306a36Sopenharmony_ci wake_up(&data->wait); 32362306a36Sopenharmony_ci } else { 32462306a36Sopenharmony_ci dev_vdbg(&data->master->dev, 32562306a36Sopenharmony_ci "%s : Transfer is not completed", 32662306a36Sopenharmony_ci __func__); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci/** 33362306a36Sopenharmony_ci * pch_spi_handler() - Interrupt handler 33462306a36Sopenharmony_ci * @irq: The interrupt number. 33562306a36Sopenharmony_ci * @dev_id: Pointer to struct pch_spi_board_data. 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_cistatic irqreturn_t pch_spi_handler(int irq, void *dev_id) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci u32 reg_spsr_val; 34062306a36Sopenharmony_ci void __iomem *spsr; 34162306a36Sopenharmony_ci void __iomem *io_remap_addr; 34262306a36Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 34362306a36Sopenharmony_ci struct pch_spi_data *data = dev_id; 34462306a36Sopenharmony_ci struct pch_spi_board_data *board_dat = data->board_dat; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (board_dat->suspend_sts) { 34762306a36Sopenharmony_ci dev_dbg(&board_dat->pdev->dev, 34862306a36Sopenharmony_ci "%s returning due to suspend\n", __func__); 34962306a36Sopenharmony_ci return IRQ_NONE; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci io_remap_addr = data->io_remap_addr; 35362306a36Sopenharmony_ci spsr = io_remap_addr + PCH_SPSR; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci reg_spsr_val = ioread32(spsr); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (reg_spsr_val & SPSR_ORF_BIT) { 35862306a36Sopenharmony_ci dev_err(&board_dat->pdev->dev, "%s Over run error\n", __func__); 35962306a36Sopenharmony_ci if (data->current_msg->complete) { 36062306a36Sopenharmony_ci data->transfer_complete = true; 36162306a36Sopenharmony_ci data->current_msg->status = -EIO; 36262306a36Sopenharmony_ci data->current_msg->complete(data->current_msg->context); 36362306a36Sopenharmony_ci data->bcurrent_msg_processing = false; 36462306a36Sopenharmony_ci data->current_msg = NULL; 36562306a36Sopenharmony_ci data->cur_trans = NULL; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (data->use_dma) 37062306a36Sopenharmony_ci return IRQ_NONE; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* Check if the interrupt is for SPI device */ 37362306a36Sopenharmony_ci if (reg_spsr_val & (SPSR_FI_BIT | SPSR_RFI_BIT)) { 37462306a36Sopenharmony_ci pch_spi_handler_sub(data, reg_spsr_val, io_remap_addr); 37562306a36Sopenharmony_ci ret = IRQ_HANDLED; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci dev_dbg(&board_dat->pdev->dev, "%s EXIT return value=%d\n", 37962306a36Sopenharmony_ci __func__, ret); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return ret; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci/** 38562306a36Sopenharmony_ci * pch_spi_set_baud_rate() - Sets SPBR field in SPBRR 38662306a36Sopenharmony_ci * @master: Pointer to struct spi_master. 38762306a36Sopenharmony_ci * @speed_hz: Baud rate. 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_cistatic void pch_spi_set_baud_rate(struct spi_master *master, u32 speed_hz) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci u32 n_spbr = PCH_CLOCK_HZ / (speed_hz * 2); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* if baud rate is less than we can support limit it */ 39462306a36Sopenharmony_ci if (n_spbr > PCH_MAX_SPBR) 39562306a36Sopenharmony_ci n_spbr = PCH_MAX_SPBR; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci pch_spi_setclr_reg(master, PCH_SPBRR, n_spbr, MASK_SPBRR_SPBR_BITS); 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci/** 40162306a36Sopenharmony_ci * pch_spi_set_bits_per_word() - Sets SIZE field in SPBRR 40262306a36Sopenharmony_ci * @master: Pointer to struct spi_master. 40362306a36Sopenharmony_ci * @bits_per_word: Bits per word for SPI transfer. 40462306a36Sopenharmony_ci */ 40562306a36Sopenharmony_cistatic void pch_spi_set_bits_per_word(struct spi_master *master, 40662306a36Sopenharmony_ci u8 bits_per_word) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci if (bits_per_word == 8) 40962306a36Sopenharmony_ci pch_spi_setclr_reg(master, PCH_SPBRR, 0, SPBRR_SIZE_BIT); 41062306a36Sopenharmony_ci else 41162306a36Sopenharmony_ci pch_spi_setclr_reg(master, PCH_SPBRR, SPBRR_SIZE_BIT, 0); 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci/** 41562306a36Sopenharmony_ci * pch_spi_setup_transfer() - Configures the PCH SPI hardware for transfer 41662306a36Sopenharmony_ci * @spi: Pointer to struct spi_device. 41762306a36Sopenharmony_ci */ 41862306a36Sopenharmony_cistatic void pch_spi_setup_transfer(struct spi_device *spi) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci u32 flags = 0; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci dev_dbg(&spi->dev, "%s SPBRR content =%x setting baud rate=%d\n", 42362306a36Sopenharmony_ci __func__, pch_spi_readreg(spi->master, PCH_SPBRR), 42462306a36Sopenharmony_ci spi->max_speed_hz); 42562306a36Sopenharmony_ci pch_spi_set_baud_rate(spi->master, spi->max_speed_hz); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* set bits per word */ 42862306a36Sopenharmony_ci pch_spi_set_bits_per_word(spi->master, spi->bits_per_word); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (!(spi->mode & SPI_LSB_FIRST)) 43162306a36Sopenharmony_ci flags |= SPCR_LSBF_BIT; 43262306a36Sopenharmony_ci if (spi->mode & SPI_CPOL) 43362306a36Sopenharmony_ci flags |= SPCR_CPOL_BIT; 43462306a36Sopenharmony_ci if (spi->mode & SPI_CPHA) 43562306a36Sopenharmony_ci flags |= SPCR_CPHA_BIT; 43662306a36Sopenharmony_ci pch_spi_setclr_reg(spi->master, PCH_SPCR, flags, 43762306a36Sopenharmony_ci (SPCR_LSBF_BIT | SPCR_CPOL_BIT | SPCR_CPHA_BIT)); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* Clear the FIFO by toggling FICLR to 1 and back to 0 */ 44062306a36Sopenharmony_ci pch_spi_clear_fifo(spi->master); 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci/** 44462306a36Sopenharmony_ci * pch_spi_reset() - Clears SPI registers 44562306a36Sopenharmony_ci * @master: Pointer to struct spi_master. 44662306a36Sopenharmony_ci */ 44762306a36Sopenharmony_cistatic void pch_spi_reset(struct spi_master *master) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci /* write 1 to reset SPI */ 45062306a36Sopenharmony_ci pch_spi_writereg(master, PCH_SRST, 0x1); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* clear reset */ 45362306a36Sopenharmony_ci pch_spi_writereg(master, PCH_SRST, 0x0); 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct pch_spi_data *data = spi_master_get_devdata(pspi->master); 45962306a36Sopenharmony_ci int retval; 46062306a36Sopenharmony_ci unsigned long flags; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* We won't process any messages if we have been asked to terminate */ 46362306a36Sopenharmony_ci if (data->status == STATUS_EXITING) { 46462306a36Sopenharmony_ci dev_err(&pspi->dev, "%s status = STATUS_EXITING.\n", __func__); 46562306a36Sopenharmony_ci retval = -ESHUTDOWN; 46662306a36Sopenharmony_ci goto err_out; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* If suspended ,return -EINVAL */ 47062306a36Sopenharmony_ci if (data->board_dat->suspend_sts) { 47162306a36Sopenharmony_ci dev_err(&pspi->dev, "%s suspend; returning EINVAL\n", __func__); 47262306a36Sopenharmony_ci retval = -EINVAL; 47362306a36Sopenharmony_ci goto err_out; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* set status of message */ 47762306a36Sopenharmony_ci pmsg->actual_length = 0; 47862306a36Sopenharmony_ci dev_dbg(&pspi->dev, "%s - pmsg->status =%d\n", __func__, pmsg->status); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci pmsg->status = -EINPROGRESS; 48162306a36Sopenharmony_ci spin_lock_irqsave(&data->lock, flags); 48262306a36Sopenharmony_ci /* add message to queue */ 48362306a36Sopenharmony_ci list_add_tail(&pmsg->queue, &data->queue); 48462306a36Sopenharmony_ci spin_unlock_irqrestore(&data->lock, flags); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci dev_dbg(&pspi->dev, "%s - Invoked list_add_tail\n", __func__); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci schedule_work(&data->work); 48962306a36Sopenharmony_ci dev_dbg(&pspi->dev, "%s - Invoked queue work\n", __func__); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci retval = 0; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cierr_out: 49462306a36Sopenharmony_ci dev_dbg(&pspi->dev, "%s RETURN=%d\n", __func__, retval); 49562306a36Sopenharmony_ci return retval; 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic inline void pch_spi_select_chip(struct pch_spi_data *data, 49962306a36Sopenharmony_ci struct spi_device *pspi) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci if (data->current_chip != NULL) { 50262306a36Sopenharmony_ci if (spi_get_chipselect(pspi, 0) != data->n_curnt_chip) { 50362306a36Sopenharmony_ci dev_dbg(&pspi->dev, "%s : different slave\n", __func__); 50462306a36Sopenharmony_ci data->current_chip = NULL; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci data->current_chip = pspi; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci data->n_curnt_chip = spi_get_chipselect(data->current_chip, 0); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci dev_dbg(&pspi->dev, "%s :Invoking pch_spi_setup_transfer\n", __func__); 51362306a36Sopenharmony_ci pch_spi_setup_transfer(pspi); 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic void pch_spi_set_tx(struct pch_spi_data *data, int *bpw) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci int size; 51962306a36Sopenharmony_ci u32 n_writes; 52062306a36Sopenharmony_ci int j; 52162306a36Sopenharmony_ci struct spi_message *pmsg, *tmp; 52262306a36Sopenharmony_ci const u8 *tx_buf; 52362306a36Sopenharmony_ci const u16 *tx_sbuf; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* set baud rate if needed */ 52662306a36Sopenharmony_ci if (data->cur_trans->speed_hz) { 52762306a36Sopenharmony_ci dev_dbg(&data->master->dev, "%s:setting baud rate\n", __func__); 52862306a36Sopenharmony_ci pch_spi_set_baud_rate(data->master, data->cur_trans->speed_hz); 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* set bits per word if needed */ 53262306a36Sopenharmony_ci if (data->cur_trans->bits_per_word && 53362306a36Sopenharmony_ci (data->current_msg->spi->bits_per_word != data->cur_trans->bits_per_word)) { 53462306a36Sopenharmony_ci dev_dbg(&data->master->dev, "%s:set bits per word\n", __func__); 53562306a36Sopenharmony_ci pch_spi_set_bits_per_word(data->master, 53662306a36Sopenharmony_ci data->cur_trans->bits_per_word); 53762306a36Sopenharmony_ci *bpw = data->cur_trans->bits_per_word; 53862306a36Sopenharmony_ci } else { 53962306a36Sopenharmony_ci *bpw = data->current_msg->spi->bits_per_word; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* reset Tx/Rx index */ 54362306a36Sopenharmony_ci data->tx_index = 0; 54462306a36Sopenharmony_ci data->rx_index = 0; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci data->bpw_len = data->cur_trans->len / (*bpw / 8); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* find alloc size */ 54962306a36Sopenharmony_ci size = data->cur_trans->len * sizeof(*data->pkt_tx_buff); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* allocate memory for pkt_tx_buff & pkt_rx_buffer */ 55262306a36Sopenharmony_ci data->pkt_tx_buff = kzalloc(size, GFP_KERNEL); 55362306a36Sopenharmony_ci if (data->pkt_tx_buff != NULL) { 55462306a36Sopenharmony_ci data->pkt_rx_buff = kzalloc(size, GFP_KERNEL); 55562306a36Sopenharmony_ci if (!data->pkt_rx_buff) { 55662306a36Sopenharmony_ci kfree(data->pkt_tx_buff); 55762306a36Sopenharmony_ci data->pkt_tx_buff = NULL; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci if (!data->pkt_rx_buff) { 56262306a36Sopenharmony_ci /* flush queue and set status of all transfers to -ENOMEM */ 56362306a36Sopenharmony_ci list_for_each_entry_safe(pmsg, tmp, data->queue.next, queue) { 56462306a36Sopenharmony_ci pmsg->status = -ENOMEM; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (pmsg->complete) 56762306a36Sopenharmony_ci pmsg->complete(pmsg->context); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* delete from queue */ 57062306a36Sopenharmony_ci list_del_init(&pmsg->queue); 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci return; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* copy Tx Data */ 57662306a36Sopenharmony_ci if (data->cur_trans->tx_buf != NULL) { 57762306a36Sopenharmony_ci if (*bpw == 8) { 57862306a36Sopenharmony_ci tx_buf = data->cur_trans->tx_buf; 57962306a36Sopenharmony_ci for (j = 0; j < data->bpw_len; j++) 58062306a36Sopenharmony_ci data->pkt_tx_buff[j] = *tx_buf++; 58162306a36Sopenharmony_ci } else { 58262306a36Sopenharmony_ci tx_sbuf = data->cur_trans->tx_buf; 58362306a36Sopenharmony_ci for (j = 0; j < data->bpw_len; j++) 58462306a36Sopenharmony_ci data->pkt_tx_buff[j] = *tx_sbuf++; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* if len greater than PCH_MAX_FIFO_DEPTH, write 16,else len bytes */ 58962306a36Sopenharmony_ci n_writes = data->bpw_len; 59062306a36Sopenharmony_ci if (n_writes > PCH_MAX_FIFO_DEPTH) 59162306a36Sopenharmony_ci n_writes = PCH_MAX_FIFO_DEPTH; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci dev_dbg(&data->master->dev, 59462306a36Sopenharmony_ci "\n%s:Pulling down SSN low - writing 0x2 to SSNXCR\n", 59562306a36Sopenharmony_ci __func__); 59662306a36Sopenharmony_ci pch_spi_writereg(data->master, PCH_SSNXCR, SSN_LOW); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci for (j = 0; j < n_writes; j++) 59962306a36Sopenharmony_ci pch_spi_writereg(data->master, PCH_SPDWR, data->pkt_tx_buff[j]); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* update tx_index */ 60262306a36Sopenharmony_ci data->tx_index = j; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci /* reset transfer complete flag */ 60562306a36Sopenharmony_ci data->transfer_complete = false; 60662306a36Sopenharmony_ci data->transfer_active = true; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic void pch_spi_nomore_transfer(struct pch_spi_data *data) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci struct spi_message *pmsg, *tmp; 61262306a36Sopenharmony_ci dev_dbg(&data->master->dev, "%s called\n", __func__); 61362306a36Sopenharmony_ci /* Invoke complete callback 61462306a36Sopenharmony_ci * [To the spi core..indicating end of transfer] */ 61562306a36Sopenharmony_ci data->current_msg->status = 0; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (data->current_msg->complete) { 61862306a36Sopenharmony_ci dev_dbg(&data->master->dev, 61962306a36Sopenharmony_ci "%s:Invoking callback of SPI core\n", __func__); 62062306a36Sopenharmony_ci data->current_msg->complete(data->current_msg->context); 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci /* update status in global variable */ 62462306a36Sopenharmony_ci data->bcurrent_msg_processing = false; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci dev_dbg(&data->master->dev, 62762306a36Sopenharmony_ci "%s:data->bcurrent_msg_processing = false\n", __func__); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci data->current_msg = NULL; 63062306a36Sopenharmony_ci data->cur_trans = NULL; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci /* check if we have items in list and not suspending 63362306a36Sopenharmony_ci * return 1 if list empty */ 63462306a36Sopenharmony_ci if ((list_empty(&data->queue) == 0) && 63562306a36Sopenharmony_ci (!data->board_dat->suspend_sts) && 63662306a36Sopenharmony_ci (data->status != STATUS_EXITING)) { 63762306a36Sopenharmony_ci /* We have some more work to do (either there is more tranint 63862306a36Sopenharmony_ci * bpw;sfer requests in the current message or there are 63962306a36Sopenharmony_ci *more messages) 64062306a36Sopenharmony_ci */ 64162306a36Sopenharmony_ci dev_dbg(&data->master->dev, "%s:Invoke queue_work\n", __func__); 64262306a36Sopenharmony_ci schedule_work(&data->work); 64362306a36Sopenharmony_ci } else if (data->board_dat->suspend_sts || 64462306a36Sopenharmony_ci data->status == STATUS_EXITING) { 64562306a36Sopenharmony_ci dev_dbg(&data->master->dev, 64662306a36Sopenharmony_ci "%s suspend/remove initiated, flushing queue\n", 64762306a36Sopenharmony_ci __func__); 64862306a36Sopenharmony_ci list_for_each_entry_safe(pmsg, tmp, data->queue.next, queue) { 64962306a36Sopenharmony_ci pmsg->status = -EIO; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (pmsg->complete) 65262306a36Sopenharmony_ci pmsg->complete(pmsg->context); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci /* delete from queue */ 65562306a36Sopenharmony_ci list_del_init(&pmsg->queue); 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic void pch_spi_set_ir(struct pch_spi_data *data) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci /* enable interrupts, set threshold, enable SPI */ 66362306a36Sopenharmony_ci if ((data->bpw_len) > PCH_MAX_FIFO_DEPTH) 66462306a36Sopenharmony_ci /* set receive threshold to PCH_RX_THOLD */ 66562306a36Sopenharmony_ci pch_spi_setclr_reg(data->master, PCH_SPCR, 66662306a36Sopenharmony_ci PCH_RX_THOLD << SPCR_RFIC_FIELD | 66762306a36Sopenharmony_ci SPCR_FIE_BIT | SPCR_RFIE_BIT | 66862306a36Sopenharmony_ci SPCR_ORIE_BIT | SPCR_SPE_BIT, 66962306a36Sopenharmony_ci MASK_RFIC_SPCR_BITS | PCH_ALL); 67062306a36Sopenharmony_ci else 67162306a36Sopenharmony_ci /* set receive threshold to maximum */ 67262306a36Sopenharmony_ci pch_spi_setclr_reg(data->master, PCH_SPCR, 67362306a36Sopenharmony_ci PCH_RX_THOLD_MAX << SPCR_RFIC_FIELD | 67462306a36Sopenharmony_ci SPCR_FIE_BIT | SPCR_ORIE_BIT | 67562306a36Sopenharmony_ci SPCR_SPE_BIT, 67662306a36Sopenharmony_ci MASK_RFIC_SPCR_BITS | PCH_ALL); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci /* Wait until the transfer completes; go to sleep after 67962306a36Sopenharmony_ci initiating the transfer. */ 68062306a36Sopenharmony_ci dev_dbg(&data->master->dev, 68162306a36Sopenharmony_ci "%s:waiting for transfer to get over\n", __func__); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci wait_event_interruptible(data->wait, data->transfer_complete); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci /* clear all interrupts */ 68662306a36Sopenharmony_ci pch_spi_writereg(data->master, PCH_SPSR, 68762306a36Sopenharmony_ci pch_spi_readreg(data->master, PCH_SPSR)); 68862306a36Sopenharmony_ci /* Disable interrupts and SPI transfer */ 68962306a36Sopenharmony_ci pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL | SPCR_SPE_BIT); 69062306a36Sopenharmony_ci /* clear FIFO */ 69162306a36Sopenharmony_ci pch_spi_clear_fifo(data->master); 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cistatic void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci int j; 69762306a36Sopenharmony_ci u8 *rx_buf; 69862306a36Sopenharmony_ci u16 *rx_sbuf; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci /* copy Rx Data */ 70162306a36Sopenharmony_ci if (!data->cur_trans->rx_buf) 70262306a36Sopenharmony_ci return; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci if (bpw == 8) { 70562306a36Sopenharmony_ci rx_buf = data->cur_trans->rx_buf; 70662306a36Sopenharmony_ci for (j = 0; j < data->bpw_len; j++) 70762306a36Sopenharmony_ci *rx_buf++ = data->pkt_rx_buff[j] & 0xFF; 70862306a36Sopenharmony_ci } else { 70962306a36Sopenharmony_ci rx_sbuf = data->cur_trans->rx_buf; 71062306a36Sopenharmony_ci for (j = 0; j < data->bpw_len; j++) 71162306a36Sopenharmony_ci *rx_sbuf++ = data->pkt_rx_buff[j]; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic void pch_spi_copy_rx_data_for_dma(struct pch_spi_data *data, int bpw) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci int j; 71862306a36Sopenharmony_ci u8 *rx_buf; 71962306a36Sopenharmony_ci u16 *rx_sbuf; 72062306a36Sopenharmony_ci const u8 *rx_dma_buf; 72162306a36Sopenharmony_ci const u16 *rx_dma_sbuf; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci /* copy Rx Data */ 72462306a36Sopenharmony_ci if (!data->cur_trans->rx_buf) 72562306a36Sopenharmony_ci return; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (bpw == 8) { 72862306a36Sopenharmony_ci rx_buf = data->cur_trans->rx_buf; 72962306a36Sopenharmony_ci rx_dma_buf = data->dma.rx_buf_virt; 73062306a36Sopenharmony_ci for (j = 0; j < data->bpw_len; j++) 73162306a36Sopenharmony_ci *rx_buf++ = *rx_dma_buf++ & 0xFF; 73262306a36Sopenharmony_ci data->cur_trans->rx_buf = rx_buf; 73362306a36Sopenharmony_ci } else { 73462306a36Sopenharmony_ci rx_sbuf = data->cur_trans->rx_buf; 73562306a36Sopenharmony_ci rx_dma_sbuf = data->dma.rx_buf_virt; 73662306a36Sopenharmony_ci for (j = 0; j < data->bpw_len; j++) 73762306a36Sopenharmony_ci *rx_sbuf++ = *rx_dma_sbuf++; 73862306a36Sopenharmony_ci data->cur_trans->rx_buf = rx_sbuf; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci} 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_cistatic int pch_spi_start_transfer(struct pch_spi_data *data) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci struct pch_spi_dma_ctrl *dma; 74562306a36Sopenharmony_ci unsigned long flags; 74662306a36Sopenharmony_ci int rtn; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci dma = &data->dma; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci spin_lock_irqsave(&data->lock, flags); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci /* disable interrupts, SPI set enable */ 75362306a36Sopenharmony_ci pch_spi_setclr_reg(data->master, PCH_SPCR, SPCR_SPE_BIT, PCH_ALL); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci spin_unlock_irqrestore(&data->lock, flags); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci /* Wait until the transfer completes; go to sleep after 75862306a36Sopenharmony_ci initiating the transfer. */ 75962306a36Sopenharmony_ci dev_dbg(&data->master->dev, 76062306a36Sopenharmony_ci "%s:waiting for transfer to get over\n", __func__); 76162306a36Sopenharmony_ci rtn = wait_event_interruptible_timeout(data->wait, 76262306a36Sopenharmony_ci data->transfer_complete, 76362306a36Sopenharmony_ci msecs_to_jiffies(2 * HZ)); 76462306a36Sopenharmony_ci if (!rtn) 76562306a36Sopenharmony_ci dev_err(&data->master->dev, 76662306a36Sopenharmony_ci "%s wait-event timeout\n", __func__); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci dma_sync_sg_for_cpu(&data->master->dev, dma->sg_rx_p, dma->nent, 76962306a36Sopenharmony_ci DMA_FROM_DEVICE); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci dma_sync_sg_for_cpu(&data->master->dev, dma->sg_tx_p, dma->nent, 77262306a36Sopenharmony_ci DMA_FROM_DEVICE); 77362306a36Sopenharmony_ci memset(data->dma.tx_buf_virt, 0, PAGE_SIZE); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci async_tx_ack(dma->desc_rx); 77662306a36Sopenharmony_ci async_tx_ack(dma->desc_tx); 77762306a36Sopenharmony_ci kfree(dma->sg_tx_p); 77862306a36Sopenharmony_ci kfree(dma->sg_rx_p); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci spin_lock_irqsave(&data->lock, flags); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci /* clear fifo threshold, disable interrupts, disable SPI transfer */ 78362306a36Sopenharmony_ci pch_spi_setclr_reg(data->master, PCH_SPCR, 0, 78462306a36Sopenharmony_ci MASK_RFIC_SPCR_BITS | MASK_TFIC_SPCR_BITS | PCH_ALL | 78562306a36Sopenharmony_ci SPCR_SPE_BIT); 78662306a36Sopenharmony_ci /* clear all interrupts */ 78762306a36Sopenharmony_ci pch_spi_writereg(data->master, PCH_SPSR, 78862306a36Sopenharmony_ci pch_spi_readreg(data->master, PCH_SPSR)); 78962306a36Sopenharmony_ci /* clear FIFO */ 79062306a36Sopenharmony_ci pch_spi_clear_fifo(data->master); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci spin_unlock_irqrestore(&data->lock, flags); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci return rtn; 79562306a36Sopenharmony_ci} 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_cistatic void pch_dma_rx_complete(void *arg) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci struct pch_spi_data *data = arg; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* transfer is completed;inform pch_spi_process_messages_dma */ 80262306a36Sopenharmony_ci data->transfer_complete = true; 80362306a36Sopenharmony_ci wake_up_interruptible(&data->wait); 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cistatic bool pch_spi_filter(struct dma_chan *chan, void *slave) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci struct pch_dma_slave *param = slave; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if ((chan->chan_id == param->chan_id) && 81162306a36Sopenharmony_ci (param->dma_dev == chan->device->dev)) { 81262306a36Sopenharmony_ci chan->private = param; 81362306a36Sopenharmony_ci return true; 81462306a36Sopenharmony_ci } else { 81562306a36Sopenharmony_ci return false; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_cistatic void pch_spi_request_dma(struct pch_spi_data *data, int bpw) 82062306a36Sopenharmony_ci{ 82162306a36Sopenharmony_ci dma_cap_mask_t mask; 82262306a36Sopenharmony_ci struct dma_chan *chan; 82362306a36Sopenharmony_ci struct pci_dev *dma_dev; 82462306a36Sopenharmony_ci struct pch_dma_slave *param; 82562306a36Sopenharmony_ci struct pch_spi_dma_ctrl *dma; 82662306a36Sopenharmony_ci unsigned int width; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (bpw == 8) 82962306a36Sopenharmony_ci width = PCH_DMA_WIDTH_1_BYTE; 83062306a36Sopenharmony_ci else 83162306a36Sopenharmony_ci width = PCH_DMA_WIDTH_2_BYTES; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci dma = &data->dma; 83462306a36Sopenharmony_ci dma_cap_zero(mask); 83562306a36Sopenharmony_ci dma_cap_set(DMA_SLAVE, mask); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci /* Get DMA's dev information */ 83862306a36Sopenharmony_ci dma_dev = pci_get_slot(data->board_dat->pdev->bus, 83962306a36Sopenharmony_ci PCI_DEVFN(PCI_SLOT(data->board_dat->pdev->devfn), 0)); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci /* Set Tx DMA */ 84262306a36Sopenharmony_ci param = &dma->param_tx; 84362306a36Sopenharmony_ci param->dma_dev = &dma_dev->dev; 84462306a36Sopenharmony_ci param->chan_id = data->ch * 2; /* Tx = 0, 2 */ 84562306a36Sopenharmony_ci param->tx_reg = data->io_base_addr + PCH_SPDWR; 84662306a36Sopenharmony_ci param->width = width; 84762306a36Sopenharmony_ci chan = dma_request_channel(mask, pch_spi_filter, param); 84862306a36Sopenharmony_ci if (!chan) { 84962306a36Sopenharmony_ci dev_err(&data->master->dev, 85062306a36Sopenharmony_ci "ERROR: dma_request_channel FAILS(Tx)\n"); 85162306a36Sopenharmony_ci goto out; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci dma->chan_tx = chan; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci /* Set Rx DMA */ 85662306a36Sopenharmony_ci param = &dma->param_rx; 85762306a36Sopenharmony_ci param->dma_dev = &dma_dev->dev; 85862306a36Sopenharmony_ci param->chan_id = data->ch * 2 + 1; /* Rx = Tx + 1 */ 85962306a36Sopenharmony_ci param->rx_reg = data->io_base_addr + PCH_SPDRR; 86062306a36Sopenharmony_ci param->width = width; 86162306a36Sopenharmony_ci chan = dma_request_channel(mask, pch_spi_filter, param); 86262306a36Sopenharmony_ci if (!chan) { 86362306a36Sopenharmony_ci dev_err(&data->master->dev, 86462306a36Sopenharmony_ci "ERROR: dma_request_channel FAILS(Rx)\n"); 86562306a36Sopenharmony_ci dma_release_channel(dma->chan_tx); 86662306a36Sopenharmony_ci dma->chan_tx = NULL; 86762306a36Sopenharmony_ci goto out; 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci dma->chan_rx = chan; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci dma->dma_dev = dma_dev; 87262306a36Sopenharmony_ci return; 87362306a36Sopenharmony_ciout: 87462306a36Sopenharmony_ci pci_dev_put(dma_dev); 87562306a36Sopenharmony_ci data->use_dma = 0; 87662306a36Sopenharmony_ci} 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_cistatic void pch_spi_release_dma(struct pch_spi_data *data) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci struct pch_spi_dma_ctrl *dma; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci dma = &data->dma; 88362306a36Sopenharmony_ci if (dma->chan_tx) { 88462306a36Sopenharmony_ci dma_release_channel(dma->chan_tx); 88562306a36Sopenharmony_ci dma->chan_tx = NULL; 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci if (dma->chan_rx) { 88862306a36Sopenharmony_ci dma_release_channel(dma->chan_rx); 88962306a36Sopenharmony_ci dma->chan_rx = NULL; 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci pci_dev_put(dma->dma_dev); 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci const u8 *tx_buf; 89862306a36Sopenharmony_ci const u16 *tx_sbuf; 89962306a36Sopenharmony_ci u8 *tx_dma_buf; 90062306a36Sopenharmony_ci u16 *tx_dma_sbuf; 90162306a36Sopenharmony_ci struct scatterlist *sg; 90262306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc_tx; 90362306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc_rx; 90462306a36Sopenharmony_ci int num; 90562306a36Sopenharmony_ci int i; 90662306a36Sopenharmony_ci int size; 90762306a36Sopenharmony_ci int rem; 90862306a36Sopenharmony_ci int head; 90962306a36Sopenharmony_ci unsigned long flags; 91062306a36Sopenharmony_ci struct pch_spi_dma_ctrl *dma; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci dma = &data->dma; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci /* set baud rate if needed */ 91562306a36Sopenharmony_ci if (data->cur_trans->speed_hz) { 91662306a36Sopenharmony_ci dev_dbg(&data->master->dev, "%s:setting baud rate\n", __func__); 91762306a36Sopenharmony_ci spin_lock_irqsave(&data->lock, flags); 91862306a36Sopenharmony_ci pch_spi_set_baud_rate(data->master, data->cur_trans->speed_hz); 91962306a36Sopenharmony_ci spin_unlock_irqrestore(&data->lock, flags); 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci /* set bits per word if needed */ 92362306a36Sopenharmony_ci if (data->cur_trans->bits_per_word && 92462306a36Sopenharmony_ci (data->current_msg->spi->bits_per_word != 92562306a36Sopenharmony_ci data->cur_trans->bits_per_word)) { 92662306a36Sopenharmony_ci dev_dbg(&data->master->dev, "%s:set bits per word\n", __func__); 92762306a36Sopenharmony_ci spin_lock_irqsave(&data->lock, flags); 92862306a36Sopenharmony_ci pch_spi_set_bits_per_word(data->master, 92962306a36Sopenharmony_ci data->cur_trans->bits_per_word); 93062306a36Sopenharmony_ci spin_unlock_irqrestore(&data->lock, flags); 93162306a36Sopenharmony_ci *bpw = data->cur_trans->bits_per_word; 93262306a36Sopenharmony_ci } else { 93362306a36Sopenharmony_ci *bpw = data->current_msg->spi->bits_per_word; 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci data->bpw_len = data->cur_trans->len / (*bpw / 8); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci if (data->bpw_len > PCH_BUF_SIZE) { 93862306a36Sopenharmony_ci data->bpw_len = PCH_BUF_SIZE; 93962306a36Sopenharmony_ci data->cur_trans->len -= PCH_BUF_SIZE; 94062306a36Sopenharmony_ci } 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci /* copy Tx Data */ 94362306a36Sopenharmony_ci if (data->cur_trans->tx_buf != NULL) { 94462306a36Sopenharmony_ci if (*bpw == 8) { 94562306a36Sopenharmony_ci tx_buf = data->cur_trans->tx_buf; 94662306a36Sopenharmony_ci tx_dma_buf = dma->tx_buf_virt; 94762306a36Sopenharmony_ci for (i = 0; i < data->bpw_len; i++) 94862306a36Sopenharmony_ci *tx_dma_buf++ = *tx_buf++; 94962306a36Sopenharmony_ci } else { 95062306a36Sopenharmony_ci tx_sbuf = data->cur_trans->tx_buf; 95162306a36Sopenharmony_ci tx_dma_sbuf = dma->tx_buf_virt; 95262306a36Sopenharmony_ci for (i = 0; i < data->bpw_len; i++) 95362306a36Sopenharmony_ci *tx_dma_sbuf++ = *tx_sbuf++; 95462306a36Sopenharmony_ci } 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci /* Calculate Rx parameter for DMA transmitting */ 95862306a36Sopenharmony_ci if (data->bpw_len > PCH_DMA_TRANS_SIZE) { 95962306a36Sopenharmony_ci if (data->bpw_len % PCH_DMA_TRANS_SIZE) { 96062306a36Sopenharmony_ci num = data->bpw_len / PCH_DMA_TRANS_SIZE + 1; 96162306a36Sopenharmony_ci rem = data->bpw_len % PCH_DMA_TRANS_SIZE; 96262306a36Sopenharmony_ci } else { 96362306a36Sopenharmony_ci num = data->bpw_len / PCH_DMA_TRANS_SIZE; 96462306a36Sopenharmony_ci rem = PCH_DMA_TRANS_SIZE; 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci size = PCH_DMA_TRANS_SIZE; 96762306a36Sopenharmony_ci } else { 96862306a36Sopenharmony_ci num = 1; 96962306a36Sopenharmony_ci size = data->bpw_len; 97062306a36Sopenharmony_ci rem = data->bpw_len; 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci dev_dbg(&data->master->dev, "%s num=%d size=%d rem=%d\n", 97362306a36Sopenharmony_ci __func__, num, size, rem); 97462306a36Sopenharmony_ci spin_lock_irqsave(&data->lock, flags); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci /* set receive fifo threshold and transmit fifo threshold */ 97762306a36Sopenharmony_ci pch_spi_setclr_reg(data->master, PCH_SPCR, 97862306a36Sopenharmony_ci ((size - 1) << SPCR_RFIC_FIELD) | 97962306a36Sopenharmony_ci (PCH_TX_THOLD << SPCR_TFIC_FIELD), 98062306a36Sopenharmony_ci MASK_RFIC_SPCR_BITS | MASK_TFIC_SPCR_BITS); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci spin_unlock_irqrestore(&data->lock, flags); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci /* RX */ 98562306a36Sopenharmony_ci dma->sg_rx_p = kmalloc_array(num, sizeof(*dma->sg_rx_p), GFP_ATOMIC); 98662306a36Sopenharmony_ci if (!dma->sg_rx_p) 98762306a36Sopenharmony_ci return; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci sg_init_table(dma->sg_rx_p, num); /* Initialize SG table */ 99062306a36Sopenharmony_ci /* offset, length setting */ 99162306a36Sopenharmony_ci sg = dma->sg_rx_p; 99262306a36Sopenharmony_ci for (i = 0; i < num; i++, sg++) { 99362306a36Sopenharmony_ci if (i == (num - 2)) { 99462306a36Sopenharmony_ci sg->offset = size * i; 99562306a36Sopenharmony_ci sg->offset = sg->offset * (*bpw / 8); 99662306a36Sopenharmony_ci sg_set_page(sg, virt_to_page(dma->rx_buf_virt), rem, 99762306a36Sopenharmony_ci sg->offset); 99862306a36Sopenharmony_ci sg_dma_len(sg) = rem; 99962306a36Sopenharmony_ci } else if (i == (num - 1)) { 100062306a36Sopenharmony_ci sg->offset = size * (i - 1) + rem; 100162306a36Sopenharmony_ci sg->offset = sg->offset * (*bpw / 8); 100262306a36Sopenharmony_ci sg_set_page(sg, virt_to_page(dma->rx_buf_virt), size, 100362306a36Sopenharmony_ci sg->offset); 100462306a36Sopenharmony_ci sg_dma_len(sg) = size; 100562306a36Sopenharmony_ci } else { 100662306a36Sopenharmony_ci sg->offset = size * i; 100762306a36Sopenharmony_ci sg->offset = sg->offset * (*bpw / 8); 100862306a36Sopenharmony_ci sg_set_page(sg, virt_to_page(dma->rx_buf_virt), size, 100962306a36Sopenharmony_ci sg->offset); 101062306a36Sopenharmony_ci sg_dma_len(sg) = size; 101162306a36Sopenharmony_ci } 101262306a36Sopenharmony_ci sg_dma_address(sg) = dma->rx_buf_dma + sg->offset; 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci sg = dma->sg_rx_p; 101562306a36Sopenharmony_ci desc_rx = dmaengine_prep_slave_sg(dma->chan_rx, sg, 101662306a36Sopenharmony_ci num, DMA_DEV_TO_MEM, 101762306a36Sopenharmony_ci DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 101862306a36Sopenharmony_ci if (!desc_rx) { 101962306a36Sopenharmony_ci dev_err(&data->master->dev, 102062306a36Sopenharmony_ci "%s:dmaengine_prep_slave_sg Failed\n", __func__); 102162306a36Sopenharmony_ci return; 102262306a36Sopenharmony_ci } 102362306a36Sopenharmony_ci dma_sync_sg_for_device(&data->master->dev, sg, num, DMA_FROM_DEVICE); 102462306a36Sopenharmony_ci desc_rx->callback = pch_dma_rx_complete; 102562306a36Sopenharmony_ci desc_rx->callback_param = data; 102662306a36Sopenharmony_ci dma->nent = num; 102762306a36Sopenharmony_ci dma->desc_rx = desc_rx; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci /* Calculate Tx parameter for DMA transmitting */ 103062306a36Sopenharmony_ci if (data->bpw_len > PCH_MAX_FIFO_DEPTH) { 103162306a36Sopenharmony_ci head = PCH_MAX_FIFO_DEPTH - PCH_DMA_TRANS_SIZE; 103262306a36Sopenharmony_ci if (data->bpw_len % PCH_DMA_TRANS_SIZE > 4) { 103362306a36Sopenharmony_ci num = data->bpw_len / PCH_DMA_TRANS_SIZE + 1; 103462306a36Sopenharmony_ci rem = data->bpw_len % PCH_DMA_TRANS_SIZE - head; 103562306a36Sopenharmony_ci } else { 103662306a36Sopenharmony_ci num = data->bpw_len / PCH_DMA_TRANS_SIZE; 103762306a36Sopenharmony_ci rem = data->bpw_len % PCH_DMA_TRANS_SIZE + 103862306a36Sopenharmony_ci PCH_DMA_TRANS_SIZE - head; 103962306a36Sopenharmony_ci } 104062306a36Sopenharmony_ci size = PCH_DMA_TRANS_SIZE; 104162306a36Sopenharmony_ci } else { 104262306a36Sopenharmony_ci num = 1; 104362306a36Sopenharmony_ci size = data->bpw_len; 104462306a36Sopenharmony_ci rem = data->bpw_len; 104562306a36Sopenharmony_ci head = 0; 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci dma->sg_tx_p = kmalloc_array(num, sizeof(*dma->sg_tx_p), GFP_ATOMIC); 104962306a36Sopenharmony_ci if (!dma->sg_tx_p) 105062306a36Sopenharmony_ci return; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci sg_init_table(dma->sg_tx_p, num); /* Initialize SG table */ 105362306a36Sopenharmony_ci /* offset, length setting */ 105462306a36Sopenharmony_ci sg = dma->sg_tx_p; 105562306a36Sopenharmony_ci for (i = 0; i < num; i++, sg++) { 105662306a36Sopenharmony_ci if (i == 0) { 105762306a36Sopenharmony_ci sg->offset = 0; 105862306a36Sopenharmony_ci sg_set_page(sg, virt_to_page(dma->tx_buf_virt), size + head, 105962306a36Sopenharmony_ci sg->offset); 106062306a36Sopenharmony_ci sg_dma_len(sg) = size + head; 106162306a36Sopenharmony_ci } else if (i == (num - 1)) { 106262306a36Sopenharmony_ci sg->offset = head + size * i; 106362306a36Sopenharmony_ci sg->offset = sg->offset * (*bpw / 8); 106462306a36Sopenharmony_ci sg_set_page(sg, virt_to_page(dma->tx_buf_virt), rem, 106562306a36Sopenharmony_ci sg->offset); 106662306a36Sopenharmony_ci sg_dma_len(sg) = rem; 106762306a36Sopenharmony_ci } else { 106862306a36Sopenharmony_ci sg->offset = head + size * i; 106962306a36Sopenharmony_ci sg->offset = sg->offset * (*bpw / 8); 107062306a36Sopenharmony_ci sg_set_page(sg, virt_to_page(dma->tx_buf_virt), size, 107162306a36Sopenharmony_ci sg->offset); 107262306a36Sopenharmony_ci sg_dma_len(sg) = size; 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci sg_dma_address(sg) = dma->tx_buf_dma + sg->offset; 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci sg = dma->sg_tx_p; 107762306a36Sopenharmony_ci desc_tx = dmaengine_prep_slave_sg(dma->chan_tx, 107862306a36Sopenharmony_ci sg, num, DMA_MEM_TO_DEV, 107962306a36Sopenharmony_ci DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 108062306a36Sopenharmony_ci if (!desc_tx) { 108162306a36Sopenharmony_ci dev_err(&data->master->dev, 108262306a36Sopenharmony_ci "%s:dmaengine_prep_slave_sg Failed\n", __func__); 108362306a36Sopenharmony_ci return; 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci dma_sync_sg_for_device(&data->master->dev, sg, num, DMA_TO_DEVICE); 108662306a36Sopenharmony_ci desc_tx->callback = NULL; 108762306a36Sopenharmony_ci desc_tx->callback_param = data; 108862306a36Sopenharmony_ci dma->nent = num; 108962306a36Sopenharmony_ci dma->desc_tx = desc_tx; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci dev_dbg(&data->master->dev, "%s:Pulling down SSN low - writing 0x2 to SSNXCR\n", __func__); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci spin_lock_irqsave(&data->lock, flags); 109462306a36Sopenharmony_ci pch_spi_writereg(data->master, PCH_SSNXCR, SSN_LOW); 109562306a36Sopenharmony_ci desc_rx->tx_submit(desc_rx); 109662306a36Sopenharmony_ci desc_tx->tx_submit(desc_tx); 109762306a36Sopenharmony_ci spin_unlock_irqrestore(&data->lock, flags); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci /* reset transfer complete flag */ 110062306a36Sopenharmony_ci data->transfer_complete = false; 110162306a36Sopenharmony_ci} 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_cistatic void pch_spi_process_messages(struct work_struct *pwork) 110462306a36Sopenharmony_ci{ 110562306a36Sopenharmony_ci struct spi_message *pmsg, *tmp; 110662306a36Sopenharmony_ci struct pch_spi_data *data; 110762306a36Sopenharmony_ci int bpw; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci data = container_of(pwork, struct pch_spi_data, work); 111062306a36Sopenharmony_ci dev_dbg(&data->master->dev, "%s data initialized\n", __func__); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci spin_lock(&data->lock); 111362306a36Sopenharmony_ci /* check if suspend has been initiated;if yes flush queue */ 111462306a36Sopenharmony_ci if (data->board_dat->suspend_sts || (data->status == STATUS_EXITING)) { 111562306a36Sopenharmony_ci dev_dbg(&data->master->dev, 111662306a36Sopenharmony_ci "%s suspend/remove initiated, flushing queue\n", __func__); 111762306a36Sopenharmony_ci list_for_each_entry_safe(pmsg, tmp, data->queue.next, queue) { 111862306a36Sopenharmony_ci pmsg->status = -EIO; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci if (pmsg->complete) { 112162306a36Sopenharmony_ci spin_unlock(&data->lock); 112262306a36Sopenharmony_ci pmsg->complete(pmsg->context); 112362306a36Sopenharmony_ci spin_lock(&data->lock); 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci /* delete from queue */ 112762306a36Sopenharmony_ci list_del_init(&pmsg->queue); 112862306a36Sopenharmony_ci } 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci spin_unlock(&data->lock); 113162306a36Sopenharmony_ci return; 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci data->bcurrent_msg_processing = true; 113562306a36Sopenharmony_ci dev_dbg(&data->master->dev, 113662306a36Sopenharmony_ci "%s Set data->bcurrent_msg_processing= true\n", __func__); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci /* Get the message from the queue and delete it from there. */ 113962306a36Sopenharmony_ci data->current_msg = list_entry(data->queue.next, struct spi_message, 114062306a36Sopenharmony_ci queue); 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci list_del_init(&data->current_msg->queue); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci data->current_msg->status = 0; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci pch_spi_select_chip(data, data->current_msg->spi); 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci spin_unlock(&data->lock); 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci if (data->use_dma) 115162306a36Sopenharmony_ci pch_spi_request_dma(data, 115262306a36Sopenharmony_ci data->current_msg->spi->bits_per_word); 115362306a36Sopenharmony_ci pch_spi_writereg(data->master, PCH_SSNXCR, SSN_NO_CONTROL); 115462306a36Sopenharmony_ci do { 115562306a36Sopenharmony_ci int cnt; 115662306a36Sopenharmony_ci /* If we are already processing a message get the next 115762306a36Sopenharmony_ci transfer structure from the message otherwise retrieve 115862306a36Sopenharmony_ci the 1st transfer request from the message. */ 115962306a36Sopenharmony_ci spin_lock(&data->lock); 116062306a36Sopenharmony_ci if (data->cur_trans == NULL) { 116162306a36Sopenharmony_ci data->cur_trans = 116262306a36Sopenharmony_ci list_entry(data->current_msg->transfers.next, 116362306a36Sopenharmony_ci struct spi_transfer, transfer_list); 116462306a36Sopenharmony_ci dev_dbg(&data->master->dev, 116562306a36Sopenharmony_ci "%s :Getting 1st transfer message\n", 116662306a36Sopenharmony_ci __func__); 116762306a36Sopenharmony_ci } else { 116862306a36Sopenharmony_ci data->cur_trans = 116962306a36Sopenharmony_ci list_entry(data->cur_trans->transfer_list.next, 117062306a36Sopenharmony_ci struct spi_transfer, transfer_list); 117162306a36Sopenharmony_ci dev_dbg(&data->master->dev, 117262306a36Sopenharmony_ci "%s :Getting next transfer message\n", 117362306a36Sopenharmony_ci __func__); 117462306a36Sopenharmony_ci } 117562306a36Sopenharmony_ci spin_unlock(&data->lock); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci if (!data->cur_trans->len) 117862306a36Sopenharmony_ci goto out; 117962306a36Sopenharmony_ci cnt = (data->cur_trans->len - 1) / PCH_BUF_SIZE + 1; 118062306a36Sopenharmony_ci data->save_total_len = data->cur_trans->len; 118162306a36Sopenharmony_ci if (data->use_dma) { 118262306a36Sopenharmony_ci int i; 118362306a36Sopenharmony_ci char *save_rx_buf = data->cur_trans->rx_buf; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci for (i = 0; i < cnt; i++) { 118662306a36Sopenharmony_ci pch_spi_handle_dma(data, &bpw); 118762306a36Sopenharmony_ci if (!pch_spi_start_transfer(data)) { 118862306a36Sopenharmony_ci data->transfer_complete = true; 118962306a36Sopenharmony_ci data->current_msg->status = -EIO; 119062306a36Sopenharmony_ci data->current_msg->complete 119162306a36Sopenharmony_ci (data->current_msg->context); 119262306a36Sopenharmony_ci data->bcurrent_msg_processing = false; 119362306a36Sopenharmony_ci data->current_msg = NULL; 119462306a36Sopenharmony_ci data->cur_trans = NULL; 119562306a36Sopenharmony_ci goto out; 119662306a36Sopenharmony_ci } 119762306a36Sopenharmony_ci pch_spi_copy_rx_data_for_dma(data, bpw); 119862306a36Sopenharmony_ci } 119962306a36Sopenharmony_ci data->cur_trans->rx_buf = save_rx_buf; 120062306a36Sopenharmony_ci } else { 120162306a36Sopenharmony_ci pch_spi_set_tx(data, &bpw); 120262306a36Sopenharmony_ci pch_spi_set_ir(data); 120362306a36Sopenharmony_ci pch_spi_copy_rx_data(data, bpw); 120462306a36Sopenharmony_ci kfree(data->pkt_rx_buff); 120562306a36Sopenharmony_ci data->pkt_rx_buff = NULL; 120662306a36Sopenharmony_ci kfree(data->pkt_tx_buff); 120762306a36Sopenharmony_ci data->pkt_tx_buff = NULL; 120862306a36Sopenharmony_ci } 120962306a36Sopenharmony_ci /* increment message count */ 121062306a36Sopenharmony_ci data->cur_trans->len = data->save_total_len; 121162306a36Sopenharmony_ci data->current_msg->actual_length += data->cur_trans->len; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci dev_dbg(&data->master->dev, 121462306a36Sopenharmony_ci "%s:data->current_msg->actual_length=%d\n", 121562306a36Sopenharmony_ci __func__, data->current_msg->actual_length); 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci spi_transfer_delay_exec(data->cur_trans); 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci spin_lock(&data->lock); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci /* No more transfer in this message. */ 122262306a36Sopenharmony_ci if ((data->cur_trans->transfer_list.next) == 122362306a36Sopenharmony_ci &(data->current_msg->transfers)) { 122462306a36Sopenharmony_ci pch_spi_nomore_transfer(data); 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci spin_unlock(&data->lock); 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci } while (data->cur_trans != NULL); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ciout: 123262306a36Sopenharmony_ci pch_spi_writereg(data->master, PCH_SSNXCR, SSN_HIGH); 123362306a36Sopenharmony_ci if (data->use_dma) 123462306a36Sopenharmony_ci pch_spi_release_dma(data); 123562306a36Sopenharmony_ci} 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_cistatic void pch_spi_free_resources(struct pch_spi_board_data *board_dat, 123862306a36Sopenharmony_ci struct pch_spi_data *data) 123962306a36Sopenharmony_ci{ 124062306a36Sopenharmony_ci dev_dbg(&board_dat->pdev->dev, "%s ENTRY\n", __func__); 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci flush_work(&data->work); 124362306a36Sopenharmony_ci} 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_cistatic int pch_spi_get_resources(struct pch_spi_board_data *board_dat, 124662306a36Sopenharmony_ci struct pch_spi_data *data) 124762306a36Sopenharmony_ci{ 124862306a36Sopenharmony_ci dev_dbg(&board_dat->pdev->dev, "%s ENTRY\n", __func__); 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci /* reset PCH SPI h/w */ 125162306a36Sopenharmony_ci pch_spi_reset(data->master); 125262306a36Sopenharmony_ci dev_dbg(&board_dat->pdev->dev, 125362306a36Sopenharmony_ci "%s pch_spi_reset invoked successfully\n", __func__); 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci dev_dbg(&board_dat->pdev->dev, "%s data->irq_reg_sts=true\n", __func__); 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci return 0; 125862306a36Sopenharmony_ci} 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_cistatic void pch_free_dma_buf(struct pch_spi_board_data *board_dat, 126162306a36Sopenharmony_ci struct pch_spi_data *data) 126262306a36Sopenharmony_ci{ 126362306a36Sopenharmony_ci struct pch_spi_dma_ctrl *dma; 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci dma = &data->dma; 126662306a36Sopenharmony_ci if (dma->tx_buf_dma) 126762306a36Sopenharmony_ci dma_free_coherent(&board_dat->pdev->dev, PCH_BUF_SIZE, 126862306a36Sopenharmony_ci dma->tx_buf_virt, dma->tx_buf_dma); 126962306a36Sopenharmony_ci if (dma->rx_buf_dma) 127062306a36Sopenharmony_ci dma_free_coherent(&board_dat->pdev->dev, PCH_BUF_SIZE, 127162306a36Sopenharmony_ci dma->rx_buf_virt, dma->rx_buf_dma); 127262306a36Sopenharmony_ci} 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_cistatic int pch_alloc_dma_buf(struct pch_spi_board_data *board_dat, 127562306a36Sopenharmony_ci struct pch_spi_data *data) 127662306a36Sopenharmony_ci{ 127762306a36Sopenharmony_ci struct pch_spi_dma_ctrl *dma; 127862306a36Sopenharmony_ci int ret; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci dma = &data->dma; 128162306a36Sopenharmony_ci ret = 0; 128262306a36Sopenharmony_ci /* Get Consistent memory for Tx DMA */ 128362306a36Sopenharmony_ci dma->tx_buf_virt = dma_alloc_coherent(&board_dat->pdev->dev, 128462306a36Sopenharmony_ci PCH_BUF_SIZE, &dma->tx_buf_dma, GFP_KERNEL); 128562306a36Sopenharmony_ci if (!dma->tx_buf_virt) 128662306a36Sopenharmony_ci ret = -ENOMEM; 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci /* Get Consistent memory for Rx DMA */ 128962306a36Sopenharmony_ci dma->rx_buf_virt = dma_alloc_coherent(&board_dat->pdev->dev, 129062306a36Sopenharmony_ci PCH_BUF_SIZE, &dma->rx_buf_dma, GFP_KERNEL); 129162306a36Sopenharmony_ci if (!dma->rx_buf_virt) 129262306a36Sopenharmony_ci ret = -ENOMEM; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci return ret; 129562306a36Sopenharmony_ci} 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_cistatic int pch_spi_pd_probe(struct platform_device *plat_dev) 129862306a36Sopenharmony_ci{ 129962306a36Sopenharmony_ci int ret; 130062306a36Sopenharmony_ci struct spi_master *master; 130162306a36Sopenharmony_ci struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev); 130262306a36Sopenharmony_ci struct pch_spi_data *data; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci dev_dbg(&plat_dev->dev, "%s:debug\n", __func__); 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci master = spi_alloc_master(&board_dat->pdev->dev, 130762306a36Sopenharmony_ci sizeof(struct pch_spi_data)); 130862306a36Sopenharmony_ci if (!master) { 130962306a36Sopenharmony_ci dev_err(&plat_dev->dev, "spi_alloc_master[%d] failed.\n", 131062306a36Sopenharmony_ci plat_dev->id); 131162306a36Sopenharmony_ci return -ENOMEM; 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci data = spi_master_get_devdata(master); 131562306a36Sopenharmony_ci data->master = master; 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci platform_set_drvdata(plat_dev, data); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci /* baseaddress + address offset) */ 132062306a36Sopenharmony_ci data->io_base_addr = pci_resource_start(board_dat->pdev, 1) + 132162306a36Sopenharmony_ci PCH_ADDRESS_SIZE * plat_dev->id; 132262306a36Sopenharmony_ci data->io_remap_addr = pci_iomap(board_dat->pdev, 1, 0); 132362306a36Sopenharmony_ci if (!data->io_remap_addr) { 132462306a36Sopenharmony_ci dev_err(&plat_dev->dev, "%s pci_iomap failed\n", __func__); 132562306a36Sopenharmony_ci ret = -ENOMEM; 132662306a36Sopenharmony_ci goto err_pci_iomap; 132762306a36Sopenharmony_ci } 132862306a36Sopenharmony_ci data->io_remap_addr += PCH_ADDRESS_SIZE * plat_dev->id; 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci dev_dbg(&plat_dev->dev, "[ch%d] remap_addr=%p\n", 133162306a36Sopenharmony_ci plat_dev->id, data->io_remap_addr); 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci /* initialize members of SPI master */ 133462306a36Sopenharmony_ci master->num_chipselect = PCH_MAX_CS; 133562306a36Sopenharmony_ci master->transfer = pch_spi_transfer; 133662306a36Sopenharmony_ci master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; 133762306a36Sopenharmony_ci master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); 133862306a36Sopenharmony_ci master->max_speed_hz = PCH_MAX_BAUDRATE; 133962306a36Sopenharmony_ci master->flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX; 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci data->board_dat = board_dat; 134262306a36Sopenharmony_ci data->plat_dev = plat_dev; 134362306a36Sopenharmony_ci data->n_curnt_chip = 255; 134462306a36Sopenharmony_ci data->status = STATUS_RUNNING; 134562306a36Sopenharmony_ci data->ch = plat_dev->id; 134662306a36Sopenharmony_ci data->use_dma = use_dma; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci INIT_LIST_HEAD(&data->queue); 134962306a36Sopenharmony_ci spin_lock_init(&data->lock); 135062306a36Sopenharmony_ci INIT_WORK(&data->work, pch_spi_process_messages); 135162306a36Sopenharmony_ci init_waitqueue_head(&data->wait); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci ret = pch_spi_get_resources(board_dat, data); 135462306a36Sopenharmony_ci if (ret) { 135562306a36Sopenharmony_ci dev_err(&plat_dev->dev, "%s fail(retval=%d)\n", __func__, ret); 135662306a36Sopenharmony_ci goto err_spi_get_resources; 135762306a36Sopenharmony_ci } 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci ret = request_irq(board_dat->pdev->irq, pch_spi_handler, 136062306a36Sopenharmony_ci IRQF_SHARED, KBUILD_MODNAME, data); 136162306a36Sopenharmony_ci if (ret) { 136262306a36Sopenharmony_ci dev_err(&plat_dev->dev, 136362306a36Sopenharmony_ci "%s request_irq failed\n", __func__); 136462306a36Sopenharmony_ci goto err_request_irq; 136562306a36Sopenharmony_ci } 136662306a36Sopenharmony_ci data->irq_reg_sts = true; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci pch_spi_set_master_mode(master); 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci if (use_dma) { 137162306a36Sopenharmony_ci dev_info(&plat_dev->dev, "Use DMA for data transfers\n"); 137262306a36Sopenharmony_ci ret = pch_alloc_dma_buf(board_dat, data); 137362306a36Sopenharmony_ci if (ret) 137462306a36Sopenharmony_ci goto err_spi_register_master; 137562306a36Sopenharmony_ci } 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci ret = spi_register_master(master); 137862306a36Sopenharmony_ci if (ret != 0) { 137962306a36Sopenharmony_ci dev_err(&plat_dev->dev, 138062306a36Sopenharmony_ci "%s spi_register_master FAILED\n", __func__); 138162306a36Sopenharmony_ci goto err_spi_register_master; 138262306a36Sopenharmony_ci } 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci return 0; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_cierr_spi_register_master: 138762306a36Sopenharmony_ci pch_free_dma_buf(board_dat, data); 138862306a36Sopenharmony_ci free_irq(board_dat->pdev->irq, data); 138962306a36Sopenharmony_cierr_request_irq: 139062306a36Sopenharmony_ci pch_spi_free_resources(board_dat, data); 139162306a36Sopenharmony_cierr_spi_get_resources: 139262306a36Sopenharmony_ci pci_iounmap(board_dat->pdev, data->io_remap_addr); 139362306a36Sopenharmony_cierr_pci_iomap: 139462306a36Sopenharmony_ci spi_master_put(master); 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci return ret; 139762306a36Sopenharmony_ci} 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_cistatic void pch_spi_pd_remove(struct platform_device *plat_dev) 140062306a36Sopenharmony_ci{ 140162306a36Sopenharmony_ci struct pch_spi_board_data *board_dat = dev_get_platdata(&plat_dev->dev); 140262306a36Sopenharmony_ci struct pch_spi_data *data = platform_get_drvdata(plat_dev); 140362306a36Sopenharmony_ci int count; 140462306a36Sopenharmony_ci unsigned long flags; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci dev_dbg(&plat_dev->dev, "%s:[ch%d] irq=%d\n", 140762306a36Sopenharmony_ci __func__, plat_dev->id, board_dat->pdev->irq); 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci if (use_dma) 141062306a36Sopenharmony_ci pch_free_dma_buf(board_dat, data); 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci /* check for any pending messages; no action is taken if the queue 141362306a36Sopenharmony_ci * is still full; but at least we tried. Unload anyway */ 141462306a36Sopenharmony_ci count = 500; 141562306a36Sopenharmony_ci spin_lock_irqsave(&data->lock, flags); 141662306a36Sopenharmony_ci data->status = STATUS_EXITING; 141762306a36Sopenharmony_ci while ((list_empty(&data->queue) == 0) && --count) { 141862306a36Sopenharmony_ci dev_dbg(&board_dat->pdev->dev, "%s :queue not empty\n", 141962306a36Sopenharmony_ci __func__); 142062306a36Sopenharmony_ci spin_unlock_irqrestore(&data->lock, flags); 142162306a36Sopenharmony_ci msleep(PCH_SLEEP_TIME); 142262306a36Sopenharmony_ci spin_lock_irqsave(&data->lock, flags); 142362306a36Sopenharmony_ci } 142462306a36Sopenharmony_ci spin_unlock_irqrestore(&data->lock, flags); 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci pch_spi_free_resources(board_dat, data); 142762306a36Sopenharmony_ci /* disable interrupts & free IRQ */ 142862306a36Sopenharmony_ci if (data->irq_reg_sts) { 142962306a36Sopenharmony_ci /* disable interrupts */ 143062306a36Sopenharmony_ci pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL); 143162306a36Sopenharmony_ci data->irq_reg_sts = false; 143262306a36Sopenharmony_ci free_irq(board_dat->pdev->irq, data); 143362306a36Sopenharmony_ci } 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci pci_iounmap(board_dat->pdev, data->io_remap_addr); 143662306a36Sopenharmony_ci spi_unregister_master(data->master); 143762306a36Sopenharmony_ci} 143862306a36Sopenharmony_ci#ifdef CONFIG_PM 143962306a36Sopenharmony_cistatic int pch_spi_pd_suspend(struct platform_device *pd_dev, 144062306a36Sopenharmony_ci pm_message_t state) 144162306a36Sopenharmony_ci{ 144262306a36Sopenharmony_ci u8 count; 144362306a36Sopenharmony_ci struct pch_spi_board_data *board_dat = dev_get_platdata(&pd_dev->dev); 144462306a36Sopenharmony_ci struct pch_spi_data *data = platform_get_drvdata(pd_dev); 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci dev_dbg(&pd_dev->dev, "%s ENTRY\n", __func__); 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci if (!board_dat) { 144962306a36Sopenharmony_ci dev_err(&pd_dev->dev, 145062306a36Sopenharmony_ci "%s pci_get_drvdata returned NULL\n", __func__); 145162306a36Sopenharmony_ci return -EFAULT; 145262306a36Sopenharmony_ci } 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci /* check if the current message is processed: 145562306a36Sopenharmony_ci Only after thats done the transfer will be suspended */ 145662306a36Sopenharmony_ci count = 255; 145762306a36Sopenharmony_ci while ((--count) > 0) { 145862306a36Sopenharmony_ci if (!(data->bcurrent_msg_processing)) 145962306a36Sopenharmony_ci break; 146062306a36Sopenharmony_ci msleep(PCH_SLEEP_TIME); 146162306a36Sopenharmony_ci } 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci /* Free IRQ */ 146462306a36Sopenharmony_ci if (data->irq_reg_sts) { 146562306a36Sopenharmony_ci /* disable all interrupts */ 146662306a36Sopenharmony_ci pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL); 146762306a36Sopenharmony_ci pch_spi_reset(data->master); 146862306a36Sopenharmony_ci free_irq(board_dat->pdev->irq, data); 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci data->irq_reg_sts = false; 147162306a36Sopenharmony_ci dev_dbg(&pd_dev->dev, 147262306a36Sopenharmony_ci "%s free_irq invoked successfully.\n", __func__); 147362306a36Sopenharmony_ci } 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci return 0; 147662306a36Sopenharmony_ci} 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_cistatic int pch_spi_pd_resume(struct platform_device *pd_dev) 147962306a36Sopenharmony_ci{ 148062306a36Sopenharmony_ci struct pch_spi_board_data *board_dat = dev_get_platdata(&pd_dev->dev); 148162306a36Sopenharmony_ci struct pch_spi_data *data = platform_get_drvdata(pd_dev); 148262306a36Sopenharmony_ci int retval; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci if (!board_dat) { 148562306a36Sopenharmony_ci dev_err(&pd_dev->dev, 148662306a36Sopenharmony_ci "%s pci_get_drvdata returned NULL\n", __func__); 148762306a36Sopenharmony_ci return -EFAULT; 148862306a36Sopenharmony_ci } 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci if (!data->irq_reg_sts) { 149162306a36Sopenharmony_ci /* register IRQ */ 149262306a36Sopenharmony_ci retval = request_irq(board_dat->pdev->irq, pch_spi_handler, 149362306a36Sopenharmony_ci IRQF_SHARED, KBUILD_MODNAME, data); 149462306a36Sopenharmony_ci if (retval < 0) { 149562306a36Sopenharmony_ci dev_err(&pd_dev->dev, 149662306a36Sopenharmony_ci "%s request_irq failed\n", __func__); 149762306a36Sopenharmony_ci return retval; 149862306a36Sopenharmony_ci } 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci /* reset PCH SPI h/w */ 150162306a36Sopenharmony_ci pch_spi_reset(data->master); 150262306a36Sopenharmony_ci pch_spi_set_master_mode(data->master); 150362306a36Sopenharmony_ci data->irq_reg_sts = true; 150462306a36Sopenharmony_ci } 150562306a36Sopenharmony_ci return 0; 150662306a36Sopenharmony_ci} 150762306a36Sopenharmony_ci#else 150862306a36Sopenharmony_ci#define pch_spi_pd_suspend NULL 150962306a36Sopenharmony_ci#define pch_spi_pd_resume NULL 151062306a36Sopenharmony_ci#endif 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_cistatic struct platform_driver pch_spi_pd_driver = { 151362306a36Sopenharmony_ci .driver = { 151462306a36Sopenharmony_ci .name = "pch-spi", 151562306a36Sopenharmony_ci }, 151662306a36Sopenharmony_ci .probe = pch_spi_pd_probe, 151762306a36Sopenharmony_ci .remove_new = pch_spi_pd_remove, 151862306a36Sopenharmony_ci .suspend = pch_spi_pd_suspend, 151962306a36Sopenharmony_ci .resume = pch_spi_pd_resume 152062306a36Sopenharmony_ci}; 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_cistatic int pch_spi_probe(struct pci_dev *pdev, const struct pci_device_id *id) 152362306a36Sopenharmony_ci{ 152462306a36Sopenharmony_ci struct pch_spi_board_data *board_dat; 152562306a36Sopenharmony_ci struct platform_device *pd_dev = NULL; 152662306a36Sopenharmony_ci int retval; 152762306a36Sopenharmony_ci int i; 152862306a36Sopenharmony_ci struct pch_pd_dev_save *pd_dev_save; 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci pd_dev_save = kzalloc(sizeof(*pd_dev_save), GFP_KERNEL); 153162306a36Sopenharmony_ci if (!pd_dev_save) 153262306a36Sopenharmony_ci return -ENOMEM; 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci board_dat = kzalloc(sizeof(*board_dat), GFP_KERNEL); 153562306a36Sopenharmony_ci if (!board_dat) { 153662306a36Sopenharmony_ci retval = -ENOMEM; 153762306a36Sopenharmony_ci goto err_no_mem; 153862306a36Sopenharmony_ci } 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci retval = pci_request_regions(pdev, KBUILD_MODNAME); 154162306a36Sopenharmony_ci if (retval) { 154262306a36Sopenharmony_ci dev_err(&pdev->dev, "%s request_region failed\n", __func__); 154362306a36Sopenharmony_ci goto pci_request_regions; 154462306a36Sopenharmony_ci } 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci board_dat->pdev = pdev; 154762306a36Sopenharmony_ci board_dat->num = id->driver_data; 154862306a36Sopenharmony_ci pd_dev_save->num = id->driver_data; 154962306a36Sopenharmony_ci pd_dev_save->board_dat = board_dat; 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci retval = pci_enable_device(pdev); 155262306a36Sopenharmony_ci if (retval) { 155362306a36Sopenharmony_ci dev_err(&pdev->dev, "%s pci_enable_device failed\n", __func__); 155462306a36Sopenharmony_ci goto pci_enable_device; 155562306a36Sopenharmony_ci } 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci for (i = 0; i < board_dat->num; i++) { 155862306a36Sopenharmony_ci pd_dev = platform_device_alloc("pch-spi", i); 155962306a36Sopenharmony_ci if (!pd_dev) { 156062306a36Sopenharmony_ci dev_err(&pdev->dev, "platform_device_alloc failed\n"); 156162306a36Sopenharmony_ci retval = -ENOMEM; 156262306a36Sopenharmony_ci goto err_platform_device; 156362306a36Sopenharmony_ci } 156462306a36Sopenharmony_ci pd_dev_save->pd_save[i] = pd_dev; 156562306a36Sopenharmony_ci pd_dev->dev.parent = &pdev->dev; 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci retval = platform_device_add_data(pd_dev, board_dat, 156862306a36Sopenharmony_ci sizeof(*board_dat)); 156962306a36Sopenharmony_ci if (retval) { 157062306a36Sopenharmony_ci dev_err(&pdev->dev, 157162306a36Sopenharmony_ci "platform_device_add_data failed\n"); 157262306a36Sopenharmony_ci platform_device_put(pd_dev); 157362306a36Sopenharmony_ci goto err_platform_device; 157462306a36Sopenharmony_ci } 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci retval = platform_device_add(pd_dev); 157762306a36Sopenharmony_ci if (retval) { 157862306a36Sopenharmony_ci dev_err(&pdev->dev, "platform_device_add failed\n"); 157962306a36Sopenharmony_ci platform_device_put(pd_dev); 158062306a36Sopenharmony_ci goto err_platform_device; 158162306a36Sopenharmony_ci } 158262306a36Sopenharmony_ci } 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci pci_set_drvdata(pdev, pd_dev_save); 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci return 0; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_cierr_platform_device: 158962306a36Sopenharmony_ci while (--i >= 0) 159062306a36Sopenharmony_ci platform_device_unregister(pd_dev_save->pd_save[i]); 159162306a36Sopenharmony_ci pci_disable_device(pdev); 159262306a36Sopenharmony_cipci_enable_device: 159362306a36Sopenharmony_ci pci_release_regions(pdev); 159462306a36Sopenharmony_cipci_request_regions: 159562306a36Sopenharmony_ci kfree(board_dat); 159662306a36Sopenharmony_cierr_no_mem: 159762306a36Sopenharmony_ci kfree(pd_dev_save); 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci return retval; 160062306a36Sopenharmony_ci} 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_cistatic void pch_spi_remove(struct pci_dev *pdev) 160362306a36Sopenharmony_ci{ 160462306a36Sopenharmony_ci int i; 160562306a36Sopenharmony_ci struct pch_pd_dev_save *pd_dev_save = pci_get_drvdata(pdev); 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci dev_dbg(&pdev->dev, "%s ENTRY:pdev=%p\n", __func__, pdev); 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci for (i = 0; i < pd_dev_save->num; i++) 161062306a36Sopenharmony_ci platform_device_unregister(pd_dev_save->pd_save[i]); 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci pci_disable_device(pdev); 161362306a36Sopenharmony_ci pci_release_regions(pdev); 161462306a36Sopenharmony_ci kfree(pd_dev_save->board_dat); 161562306a36Sopenharmony_ci kfree(pd_dev_save); 161662306a36Sopenharmony_ci} 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_cistatic int __maybe_unused pch_spi_suspend(struct device *dev) 161962306a36Sopenharmony_ci{ 162062306a36Sopenharmony_ci struct pch_pd_dev_save *pd_dev_save = dev_get_drvdata(dev); 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci dev_dbg(dev, "%s ENTRY\n", __func__); 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci pd_dev_save->board_dat->suspend_sts = true; 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci return 0; 162762306a36Sopenharmony_ci} 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_cistatic int __maybe_unused pch_spi_resume(struct device *dev) 163062306a36Sopenharmony_ci{ 163162306a36Sopenharmony_ci struct pch_pd_dev_save *pd_dev_save = dev_get_drvdata(dev); 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci dev_dbg(dev, "%s ENTRY\n", __func__); 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci /* set suspend status to false */ 163662306a36Sopenharmony_ci pd_dev_save->board_dat->suspend_sts = false; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci return 0; 163962306a36Sopenharmony_ci} 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(pch_spi_pm_ops, pch_spi_suspend, pch_spi_resume); 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_cistatic struct pci_driver pch_spi_pcidev_driver = { 164462306a36Sopenharmony_ci .name = "pch_spi", 164562306a36Sopenharmony_ci .id_table = pch_spi_pcidev_id, 164662306a36Sopenharmony_ci .probe = pch_spi_probe, 164762306a36Sopenharmony_ci .remove = pch_spi_remove, 164862306a36Sopenharmony_ci .driver.pm = &pch_spi_pm_ops, 164962306a36Sopenharmony_ci}; 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_cistatic int __init pch_spi_init(void) 165262306a36Sopenharmony_ci{ 165362306a36Sopenharmony_ci int ret; 165462306a36Sopenharmony_ci ret = platform_driver_register(&pch_spi_pd_driver); 165562306a36Sopenharmony_ci if (ret) 165662306a36Sopenharmony_ci return ret; 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci ret = pci_register_driver(&pch_spi_pcidev_driver); 165962306a36Sopenharmony_ci if (ret) { 166062306a36Sopenharmony_ci platform_driver_unregister(&pch_spi_pd_driver); 166162306a36Sopenharmony_ci return ret; 166262306a36Sopenharmony_ci } 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci return 0; 166562306a36Sopenharmony_ci} 166662306a36Sopenharmony_cimodule_init(pch_spi_init); 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_cistatic void __exit pch_spi_exit(void) 166962306a36Sopenharmony_ci{ 167062306a36Sopenharmony_ci pci_unregister_driver(&pch_spi_pcidev_driver); 167162306a36Sopenharmony_ci platform_driver_unregister(&pch_spi_pd_driver); 167262306a36Sopenharmony_ci} 167362306a36Sopenharmony_cimodule_exit(pch_spi_exit); 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_cimodule_param(use_dma, int, 0644); 167662306a36Sopenharmony_ciMODULE_PARM_DESC(use_dma, 167762306a36Sopenharmony_ci "to use DMA for data transfers pass 1 else 0; default 1"); 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 168062306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel EG20T PCH/LAPIS Semiconductor ML7xxx IOH SPI Driver"); 168162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pch_spi_pcidev_id); 168262306a36Sopenharmony_ci 1683