1*e75a6b00SVladimir Moravcevic // SPDX-License-Identifier: GPL-2.0-or-later 2*e75a6b00SVladimir Moravcevic // 3*e75a6b00SVladimir Moravcevic // Axiado SPI controller driver (Host mode only) 4*e75a6b00SVladimir Moravcevic // 5*e75a6b00SVladimir Moravcevic // Copyright (C) 2022-2025 Axiado Corporation (or its affiliates). 6*e75a6b00SVladimir Moravcevic // 7*e75a6b00SVladimir Moravcevic 8*e75a6b00SVladimir Moravcevic #include <linux/clk.h> 9*e75a6b00SVladimir Moravcevic #include <linux/delay.h> 10*e75a6b00SVladimir Moravcevic #include <linux/gpio/consumer.h> 11*e75a6b00SVladimir Moravcevic #include <linux/interrupt.h> 12*e75a6b00SVladimir Moravcevic #include <linux/io.h> 13*e75a6b00SVladimir Moravcevic #include <linux/module.h> 14*e75a6b00SVladimir Moravcevic #include <linux/of_irq.h> 15*e75a6b00SVladimir Moravcevic #include <linux/of_address.h> 16*e75a6b00SVladimir Moravcevic #include <linux/platform_device.h> 17*e75a6b00SVladimir Moravcevic #include <linux/pm_runtime.h> 18*e75a6b00SVladimir Moravcevic #include <linux/spi/spi.h> 19*e75a6b00SVladimir Moravcevic #include <linux/spi/spi-mem.h> 20*e75a6b00SVladimir Moravcevic #include <linux/sizes.h> 21*e75a6b00SVladimir Moravcevic 22*e75a6b00SVladimir Moravcevic #include "spi-axiado.h" 23*e75a6b00SVladimir Moravcevic 24*e75a6b00SVladimir Moravcevic /** 25*e75a6b00SVladimir Moravcevic * ax_spi_read - Register Read - 32 bit per word 26*e75a6b00SVladimir Moravcevic * @xspi: Pointer to the ax_spi structure 27*e75a6b00SVladimir Moravcevic * @offset: Register offset address 28*e75a6b00SVladimir Moravcevic * 29*e75a6b00SVladimir Moravcevic * @return: Returns the value of that register 30*e75a6b00SVladimir Moravcevic */ 31*e75a6b00SVladimir Moravcevic static inline u32 ax_spi_read(struct ax_spi *xspi, u32 offset) 32*e75a6b00SVladimir Moravcevic { 33*e75a6b00SVladimir Moravcevic return readl_relaxed(xspi->regs + offset); 34*e75a6b00SVladimir Moravcevic } 35*e75a6b00SVladimir Moravcevic 36*e75a6b00SVladimir Moravcevic /** 37*e75a6b00SVladimir Moravcevic * ax_spi_write - Register write - 32 bit per word 38*e75a6b00SVladimir Moravcevic * @xspi: Pointer to the ax_spi structure 39*e75a6b00SVladimir Moravcevic * @offset: Register offset address 40*e75a6b00SVladimir Moravcevic * @val: Value to write into that register 41*e75a6b00SVladimir Moravcevic */ 42*e75a6b00SVladimir Moravcevic static inline void ax_spi_write(struct ax_spi *xspi, u32 offset, u32 val) 43*e75a6b00SVladimir Moravcevic { 44*e75a6b00SVladimir Moravcevic writel_relaxed(val, xspi->regs + offset); 45*e75a6b00SVladimir Moravcevic } 46*e75a6b00SVladimir Moravcevic 47*e75a6b00SVladimir Moravcevic /** 48*e75a6b00SVladimir Moravcevic * ax_spi_write_b - Register Read - 8 bit per word 49*e75a6b00SVladimir Moravcevic * @xspi: Pointer to the ax_spi structure 50*e75a6b00SVladimir Moravcevic * @offset: Register offset address 51*e75a6b00SVladimir Moravcevic * @val: Value to write into that register 52*e75a6b00SVladimir Moravcevic */ 53*e75a6b00SVladimir Moravcevic static inline void ax_spi_write_b(struct ax_spi *xspi, u32 offset, u8 val) 54*e75a6b00SVladimir Moravcevic { 55*e75a6b00SVladimir Moravcevic writeb_relaxed(val, xspi->regs + offset); 56*e75a6b00SVladimir Moravcevic } 57*e75a6b00SVladimir Moravcevic 58*e75a6b00SVladimir Moravcevic /** 59*e75a6b00SVladimir Moravcevic * ax_spi_init_hw - Initialize the hardware and configure the SPI controller 60*e75a6b00SVladimir Moravcevic * @xspi: Pointer to the ax_spi structure 61*e75a6b00SVladimir Moravcevic * 62*e75a6b00SVladimir Moravcevic * * On reset the SPI controller is configured to be in host mode. 63*e75a6b00SVladimir Moravcevic * In host mode baud rate divisor is set to 4, threshold value for TX FIFO 64*e75a6b00SVladimir Moravcevic * not full interrupt is set to 1 and size of the word to be transferred as 8 bit. 65*e75a6b00SVladimir Moravcevic * 66*e75a6b00SVladimir Moravcevic * This function initializes the SPI controller to disable and clear all the 67*e75a6b00SVladimir Moravcevic * interrupts, enable manual target select and manual start, deselect all the 68*e75a6b00SVladimir Moravcevic * chip select lines, and enable the SPI controller. 69*e75a6b00SVladimir Moravcevic */ 70*e75a6b00SVladimir Moravcevic static void ax_spi_init_hw(struct ax_spi *xspi) 71*e75a6b00SVladimir Moravcevic { 72*e75a6b00SVladimir Moravcevic u32 reg_value; 73*e75a6b00SVladimir Moravcevic 74*e75a6b00SVladimir Moravcevic /* Clear CR1 */ 75*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_CR1, AX_SPI_CR1_CLR); 76*e75a6b00SVladimir Moravcevic 77*e75a6b00SVladimir Moravcevic /* CR1 - CPO CHP MSS SCE SCR */ 78*e75a6b00SVladimir Moravcevic reg_value = ax_spi_read(xspi, AX_SPI_CR1); 79*e75a6b00SVladimir Moravcevic reg_value |= AX_SPI_CR1_SCR | AX_SPI_CR1_SCE; 80*e75a6b00SVladimir Moravcevic 81*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_CR1, reg_value); 82*e75a6b00SVladimir Moravcevic 83*e75a6b00SVladimir Moravcevic /* CR2 - MTE SRD SWD SSO */ 84*e75a6b00SVladimir Moravcevic reg_value = ax_spi_read(xspi, AX_SPI_CR2); 85*e75a6b00SVladimir Moravcevic reg_value |= AX_SPI_CR2_SWD | AX_SPI_CR2_SRD; 86*e75a6b00SVladimir Moravcevic 87*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_CR2, reg_value); 88*e75a6b00SVladimir Moravcevic 89*e75a6b00SVladimir Moravcevic /* CR3 - Reserverd bits S3W SDL */ 90*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_CR3, AX_SPI_CR3_SDL); 91*e75a6b00SVladimir Moravcevic 92*e75a6b00SVladimir Moravcevic /* SCDR - Reserved bits SCS SCD */ 93*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_SCDR, (AX_SPI_SCDR_SCS | AX_SPI_SCD_DEFAULT)); 94*e75a6b00SVladimir Moravcevic 95*e75a6b00SVladimir Moravcevic /* IMR */ 96*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_IMR, AX_SPI_IMR_CLR); 97*e75a6b00SVladimir Moravcevic 98*e75a6b00SVladimir Moravcevic /* ISR - Clear all the interrupt */ 99*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_ISR, AX_SPI_ISR_CLR); 100*e75a6b00SVladimir Moravcevic } 101*e75a6b00SVladimir Moravcevic 102*e75a6b00SVladimir Moravcevic /** 103*e75a6b00SVladimir Moravcevic * ax_spi_chipselect - Select or deselect the chip select line 104*e75a6b00SVladimir Moravcevic * @spi: Pointer to the spi_device structure 105*e75a6b00SVladimir Moravcevic * @is_high: Select(0) or deselect (1) the chip select line 106*e75a6b00SVladimir Moravcevic */ 107*e75a6b00SVladimir Moravcevic static void ax_spi_chipselect(struct spi_device *spi, bool is_high) 108*e75a6b00SVladimir Moravcevic { 109*e75a6b00SVladimir Moravcevic struct ax_spi *xspi = spi_controller_get_devdata(spi->controller); 110*e75a6b00SVladimir Moravcevic u32 ctrl_reg; 111*e75a6b00SVladimir Moravcevic 112*e75a6b00SVladimir Moravcevic ctrl_reg = ax_spi_read(xspi, AX_SPI_CR2); 113*e75a6b00SVladimir Moravcevic /* Reset the chip select */ 114*e75a6b00SVladimir Moravcevic ctrl_reg &= ~AX_SPI_DEFAULT_TS_MASK; 115*e75a6b00SVladimir Moravcevic ctrl_reg |= spi_get_chipselect(spi, 0); 116*e75a6b00SVladimir Moravcevic 117*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_CR2, ctrl_reg); 118*e75a6b00SVladimir Moravcevic } 119*e75a6b00SVladimir Moravcevic 120*e75a6b00SVladimir Moravcevic /** 121*e75a6b00SVladimir Moravcevic * ax_spi_config_clock_mode - Sets clock polarity and phase 122*e75a6b00SVladimir Moravcevic * @spi: Pointer to the spi_device structure 123*e75a6b00SVladimir Moravcevic * 124*e75a6b00SVladimir Moravcevic * Sets the requested clock polarity and phase. 125*e75a6b00SVladimir Moravcevic */ 126*e75a6b00SVladimir Moravcevic static void ax_spi_config_clock_mode(struct spi_device *spi) 127*e75a6b00SVladimir Moravcevic { 128*e75a6b00SVladimir Moravcevic struct ax_spi *xspi = spi_controller_get_devdata(spi->controller); 129*e75a6b00SVladimir Moravcevic u32 ctrl_reg, new_ctrl_reg; 130*e75a6b00SVladimir Moravcevic 131*e75a6b00SVladimir Moravcevic new_ctrl_reg = ax_spi_read(xspi, AX_SPI_CR1); 132*e75a6b00SVladimir Moravcevic ctrl_reg = new_ctrl_reg; 133*e75a6b00SVladimir Moravcevic 134*e75a6b00SVladimir Moravcevic /* Set the SPI clock phase and clock polarity */ 135*e75a6b00SVladimir Moravcevic new_ctrl_reg &= ~(AX_SPI_CR1_CPHA | AX_SPI_CR1_CPOL); 136*e75a6b00SVladimir Moravcevic if (spi->mode & SPI_CPHA) 137*e75a6b00SVladimir Moravcevic new_ctrl_reg |= AX_SPI_CR1_CPHA; 138*e75a6b00SVladimir Moravcevic if (spi->mode & SPI_CPOL) 139*e75a6b00SVladimir Moravcevic new_ctrl_reg |= AX_SPI_CR1_CPOL; 140*e75a6b00SVladimir Moravcevic 141*e75a6b00SVladimir Moravcevic if (new_ctrl_reg != ctrl_reg) 142*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_CR1, new_ctrl_reg); 143*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_CR1, 0x03); 144*e75a6b00SVladimir Moravcevic } 145*e75a6b00SVladimir Moravcevic 146*e75a6b00SVladimir Moravcevic /** 147*e75a6b00SVladimir Moravcevic * ax_spi_config_clock_freq - Sets clock frequency 148*e75a6b00SVladimir Moravcevic * @spi: Pointer to the spi_device structure 149*e75a6b00SVladimir Moravcevic * @transfer: Pointer to the spi_transfer structure which provides 150*e75a6b00SVladimir Moravcevic * information about next transfer setup parameters 151*e75a6b00SVladimir Moravcevic * 152*e75a6b00SVladimir Moravcevic * Sets the requested clock frequency. 153*e75a6b00SVladimir Moravcevic * Note: If the requested frequency is not an exact match with what can be 154*e75a6b00SVladimir Moravcevic * obtained using the prescalar value the driver sets the clock frequency which 155*e75a6b00SVladimir Moravcevic * is lower than the requested frequency (maximum lower) for the transfer. If 156*e75a6b00SVladimir Moravcevic * the requested frequency is higher or lower than that is supported by the SPI 157*e75a6b00SVladimir Moravcevic * controller the driver will set the highest or lowest frequency supported by 158*e75a6b00SVladimir Moravcevic * controller. 159*e75a6b00SVladimir Moravcevic */ 160*e75a6b00SVladimir Moravcevic static void ax_spi_config_clock_freq(struct spi_device *spi, 161*e75a6b00SVladimir Moravcevic struct spi_transfer *transfer) 162*e75a6b00SVladimir Moravcevic { 163*e75a6b00SVladimir Moravcevic struct ax_spi *xspi = spi_controller_get_devdata(spi->controller); 164*e75a6b00SVladimir Moravcevic 165*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_SCDR, (AX_SPI_SCDR_SCS | AX_SPI_SCD_DEFAULT)); 166*e75a6b00SVladimir Moravcevic } 167*e75a6b00SVladimir Moravcevic 168*e75a6b00SVladimir Moravcevic /** 169*e75a6b00SVladimir Moravcevic * ax_spi_setup_transfer - Configure SPI controller for specified transfer 170*e75a6b00SVladimir Moravcevic * @spi: Pointer to the spi_device structure 171*e75a6b00SVladimir Moravcevic * @transfer: Pointer to the spi_transfer structure which provides 172*e75a6b00SVladimir Moravcevic * information about next transfer setup parameters 173*e75a6b00SVladimir Moravcevic * 174*e75a6b00SVladimir Moravcevic * Sets the operational mode of SPI controller for the next SPI transfer and 175*e75a6b00SVladimir Moravcevic * sets the requested clock frequency. 176*e75a6b00SVladimir Moravcevic * 177*e75a6b00SVladimir Moravcevic */ 178*e75a6b00SVladimir Moravcevic static void ax_spi_setup_transfer(struct spi_device *spi, 179*e75a6b00SVladimir Moravcevic struct spi_transfer *transfer) 180*e75a6b00SVladimir Moravcevic { 181*e75a6b00SVladimir Moravcevic struct ax_spi *xspi = spi_controller_get_devdata(spi->controller); 182*e75a6b00SVladimir Moravcevic 183*e75a6b00SVladimir Moravcevic ax_spi_config_clock_freq(spi, transfer); 184*e75a6b00SVladimir Moravcevic 185*e75a6b00SVladimir Moravcevic dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u clock speed\n", 186*e75a6b00SVladimir Moravcevic __func__, spi->mode, spi->bits_per_word, 187*e75a6b00SVladimir Moravcevic xspi->speed_hz); 188*e75a6b00SVladimir Moravcevic } 189*e75a6b00SVladimir Moravcevic 190*e75a6b00SVladimir Moravcevic /** 191*e75a6b00SVladimir Moravcevic * ax_spi_fill_tx_fifo - Fills the TX FIFO with as many bytes as possible 192*e75a6b00SVladimir Moravcevic * @xspi: Pointer to the ax_spi structure 193*e75a6b00SVladimir Moravcevic */ 194*e75a6b00SVladimir Moravcevic static void ax_spi_fill_tx_fifo(struct ax_spi *xspi) 195*e75a6b00SVladimir Moravcevic { 196*e75a6b00SVladimir Moravcevic unsigned long trans_cnt = 0; 197*e75a6b00SVladimir Moravcevic 198*e75a6b00SVladimir Moravcevic while ((trans_cnt < xspi->tx_fifo_depth) && 199*e75a6b00SVladimir Moravcevic (xspi->tx_bytes > 0)) { 200*e75a6b00SVladimir Moravcevic /* When xspi in busy condition, bytes may send failed, 201*e75a6b00SVladimir Moravcevic * then spi control did't work thoroughly, add one byte delay 202*e75a6b00SVladimir Moravcevic */ 203*e75a6b00SVladimir Moravcevic if (ax_spi_read(xspi, AX_SPI_IVR) & AX_SPI_IVR_TFOV) 204*e75a6b00SVladimir Moravcevic usleep_range(10, 10); 205*e75a6b00SVladimir Moravcevic if (xspi->tx_buf) 206*e75a6b00SVladimir Moravcevic ax_spi_write_b(xspi, AX_SPI_TXFIFO, *xspi->tx_buf++); 207*e75a6b00SVladimir Moravcevic else 208*e75a6b00SVladimir Moravcevic ax_spi_write_b(xspi, AX_SPI_TXFIFO, 0); 209*e75a6b00SVladimir Moravcevic 210*e75a6b00SVladimir Moravcevic xspi->tx_bytes--; 211*e75a6b00SVladimir Moravcevic trans_cnt++; 212*e75a6b00SVladimir Moravcevic } 213*e75a6b00SVladimir Moravcevic } 214*e75a6b00SVladimir Moravcevic 215*e75a6b00SVladimir Moravcevic /** 216*e75a6b00SVladimir Moravcevic * ax_spi_get_rx_byte - Gets a byte from the RX FIFO buffer 217*e75a6b00SVladimir Moravcevic * @xspi: Controller private data (struct ax_spi *) 218*e75a6b00SVladimir Moravcevic * 219*e75a6b00SVladimir Moravcevic * This function handles the logic of extracting bytes from the 32-bit RX FIFO. 220*e75a6b00SVladimir Moravcevic * It reads a new 32-bit word from AX_SPI_RXFIFO only when the current buffered 221*e75a6b00SVladimir Moravcevic * word has been fully processed (all 4 bytes extracted). It then extracts 222*e75a6b00SVladimir Moravcevic * bytes one by one, assuming the controller is little-endian. 223*e75a6b00SVladimir Moravcevic * 224*e75a6b00SVladimir Moravcevic * Returns: The next 8-bit byte read from the RX FIFO stream. 225*e75a6b00SVladimir Moravcevic */ 226*e75a6b00SVladimir Moravcevic static u8 ax_spi_get_rx_byte_for_irq(struct ax_spi *xspi) 227*e75a6b00SVladimir Moravcevic { 228*e75a6b00SVladimir Moravcevic u8 byte_val; 229*e75a6b00SVladimir Moravcevic 230*e75a6b00SVladimir Moravcevic /* If all bytes from the current 32-bit word have been extracted, 231*e75a6b00SVladimir Moravcevic * read a new word from the hardware RX FIFO. 232*e75a6b00SVladimir Moravcevic */ 233*e75a6b00SVladimir Moravcevic if (xspi->bytes_left_in_current_rx_word_for_irq == 0) { 234*e75a6b00SVladimir Moravcevic xspi->current_rx_fifo_word_for_irq = ax_spi_read(xspi, AX_SPI_RXFIFO); 235*e75a6b00SVladimir Moravcevic xspi->bytes_left_in_current_rx_word_for_irq = 4; // A new 32-bit word has 4 bytes 236*e75a6b00SVladimir Moravcevic } 237*e75a6b00SVladimir Moravcevic 238*e75a6b00SVladimir Moravcevic /* Extract the least significant byte from the current 32-bit word */ 239*e75a6b00SVladimir Moravcevic byte_val = (u8)(xspi->current_rx_fifo_word_for_irq & 0xFF); 240*e75a6b00SVladimir Moravcevic 241*e75a6b00SVladimir Moravcevic /* Shift the word right by 8 bits to prepare the next byte for extraction */ 242*e75a6b00SVladimir Moravcevic xspi->current_rx_fifo_word_for_irq >>= 8; 243*e75a6b00SVladimir Moravcevic xspi->bytes_left_in_current_rx_word_for_irq--; 244*e75a6b00SVladimir Moravcevic 245*e75a6b00SVladimir Moravcevic return byte_val; 246*e75a6b00SVladimir Moravcevic } 247*e75a6b00SVladimir Moravcevic 248*e75a6b00SVladimir Moravcevic /** 249*e75a6b00SVladimir Moravcevic * Helper function to process received bytes and check for transfer completion. 250*e75a6b00SVladimir Moravcevic * This avoids code duplication and centralizes the completion logic. 251*e75a6b00SVladimir Moravcevic * Returns true if the transfer was finalized. 252*e75a6b00SVladimir Moravcevic */ 253*e75a6b00SVladimir Moravcevic static bool ax_spi_process_rx_and_finalize(struct spi_controller *ctlr) 254*e75a6b00SVladimir Moravcevic { 255*e75a6b00SVladimir Moravcevic struct ax_spi *xspi = spi_controller_get_devdata(ctlr); 256*e75a6b00SVladimir Moravcevic 257*e75a6b00SVladimir Moravcevic /* Process any remaining bytes in the RX FIFO */ 258*e75a6b00SVladimir Moravcevic u32 avail_bytes = ax_spi_read(xspi, AX_SPI_RX_FBCAR); 259*e75a6b00SVladimir Moravcevic 260*e75a6b00SVladimir Moravcevic /* This loop handles bytes that are already staged from a previous word read */ 261*e75a6b00SVladimir Moravcevic while (xspi->bytes_left_in_current_rx_word_for_irq && 262*e75a6b00SVladimir Moravcevic (xspi->rx_copy_remaining || xspi->rx_discard)) { 263*e75a6b00SVladimir Moravcevic u8 b = ax_spi_get_rx_byte_for_irq(xspi); 264*e75a6b00SVladimir Moravcevic 265*e75a6b00SVladimir Moravcevic if (xspi->rx_discard) { 266*e75a6b00SVladimir Moravcevic xspi->rx_discard--; 267*e75a6b00SVladimir Moravcevic } else { 268*e75a6b00SVladimir Moravcevic *xspi->rx_buf++ = b; 269*e75a6b00SVladimir Moravcevic xspi->rx_copy_remaining--; 270*e75a6b00SVladimir Moravcevic } 271*e75a6b00SVladimir Moravcevic } 272*e75a6b00SVladimir Moravcevic 273*e75a6b00SVladimir Moravcevic /* This loop processes new words directly from the FIFO */ 274*e75a6b00SVladimir Moravcevic while (avail_bytes >= 4 && (xspi->rx_copy_remaining || xspi->rx_discard)) { 275*e75a6b00SVladimir Moravcevic /* This function should handle reading from the FIFO */ 276*e75a6b00SVladimir Moravcevic u8 b = ax_spi_get_rx_byte_for_irq(xspi); 277*e75a6b00SVladimir Moravcevic 278*e75a6b00SVladimir Moravcevic if (xspi->rx_discard) { 279*e75a6b00SVladimir Moravcevic xspi->rx_discard--; 280*e75a6b00SVladimir Moravcevic } else { 281*e75a6b00SVladimir Moravcevic *xspi->rx_buf++ = b; 282*e75a6b00SVladimir Moravcevic xspi->rx_copy_remaining--; 283*e75a6b00SVladimir Moravcevic } 284*e75a6b00SVladimir Moravcevic /* ax_spi_get_rx_byte_for_irq fetches a new word when needed 285*e75a6b00SVladimir Moravcevic * and updates internal state. 286*e75a6b00SVladimir Moravcevic */ 287*e75a6b00SVladimir Moravcevic if (xspi->bytes_left_in_current_rx_word_for_irq == 3) 288*e75a6b00SVladimir Moravcevic avail_bytes -= 4; 289*e75a6b00SVladimir Moravcevic } 290*e75a6b00SVladimir Moravcevic 291*e75a6b00SVladimir Moravcevic /* Completion Check: The transfer is truly complete if all expected 292*e75a6b00SVladimir Moravcevic * RX bytes have been copied or discarded. 293*e75a6b00SVladimir Moravcevic */ 294*e75a6b00SVladimir Moravcevic if (xspi->rx_copy_remaining == 0 && xspi->rx_discard == 0) { 295*e75a6b00SVladimir Moravcevic /* Defensive drain: If for some reason there are leftover bytes 296*e75a6b00SVladimir Moravcevic * in the HW FIFO after we've logically finished, 297*e75a6b00SVladimir Moravcevic * read and discard them to prevent them from corrupting the next transfer. 298*e75a6b00SVladimir Moravcevic * This should be a bounded operation. 299*e75a6b00SVladimir Moravcevic */ 300*e75a6b00SVladimir Moravcevic int safety_words = AX_SPI_RX_FIFO_DRAIN_LIMIT; // Limit to avoid getting stuck 301*e75a6b00SVladimir Moravcevic 302*e75a6b00SVladimir Moravcevic while (ax_spi_read(xspi, AX_SPI_RX_FBCAR) > 0 && safety_words-- > 0) 303*e75a6b00SVladimir Moravcevic ax_spi_read(xspi, AX_SPI_RXFIFO); 304*e75a6b00SVladimir Moravcevic 305*e75a6b00SVladimir Moravcevic /* Disable all interrupts for this transfer and finalize. */ 306*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_IMR, 0x00); 307*e75a6b00SVladimir Moravcevic spi_finalize_current_transfer(ctlr); 308*e75a6b00SVladimir Moravcevic return true; 309*e75a6b00SVladimir Moravcevic } 310*e75a6b00SVladimir Moravcevic 311*e75a6b00SVladimir Moravcevic return false; 312*e75a6b00SVladimir Moravcevic } 313*e75a6b00SVladimir Moravcevic 314*e75a6b00SVladimir Moravcevic /** 315*e75a6b00SVladimir Moravcevic * ax_spi_irq - Interrupt service routine of the SPI controller 316*e75a6b00SVladimir Moravcevic * @irq: IRQ number 317*e75a6b00SVladimir Moravcevic * @dev_id: Pointer to the xspi structure 318*e75a6b00SVladimir Moravcevic * 319*e75a6b00SVladimir Moravcevic * This function handles RX FIFO almost full and Host Transfer Completed interrupts only. 320*e75a6b00SVladimir Moravcevic * On RX FIFO amlost full interrupt this function reads the received data from RX FIFO and 321*e75a6b00SVladimir Moravcevic * fills the TX FIFO if there is any data remaining to be transferred. 322*e75a6b00SVladimir Moravcevic * On Host Transfer Completed interrupt this function indicates that transfer is completed, 323*e75a6b00SVladimir Moravcevic * the SPI subsystem will clear MTC bit. 324*e75a6b00SVladimir Moravcevic * 325*e75a6b00SVladimir Moravcevic * Return: IRQ_HANDLED when handled; IRQ_NONE otherwise. 326*e75a6b00SVladimir Moravcevic */ 327*e75a6b00SVladimir Moravcevic static irqreturn_t ax_spi_irq(int irq, void *dev_id) 328*e75a6b00SVladimir Moravcevic { 329*e75a6b00SVladimir Moravcevic struct spi_controller *ctlr = dev_id; 330*e75a6b00SVladimir Moravcevic struct ax_spi *xspi = spi_controller_get_devdata(ctlr); 331*e75a6b00SVladimir Moravcevic u32 intr_status; 332*e75a6b00SVladimir Moravcevic 333*e75a6b00SVladimir Moravcevic intr_status = ax_spi_read(xspi, AX_SPI_IVR); 334*e75a6b00SVladimir Moravcevic if (!intr_status) 335*e75a6b00SVladimir Moravcevic return IRQ_NONE; 336*e75a6b00SVladimir Moravcevic 337*e75a6b00SVladimir Moravcevic /* Handle "Message Transfer Complete" interrupt. 338*e75a6b00SVladimir Moravcevic * This means all bytes have been shifted out of the TX FIFO. 339*e75a6b00SVladimir Moravcevic * It's time to harvest the final incoming bytes from the RX FIFO. 340*e75a6b00SVladimir Moravcevic */ 341*e75a6b00SVladimir Moravcevic if (intr_status & AX_SPI_IVR_MTCV) { 342*e75a6b00SVladimir Moravcevic /* Clear the MTC interrupt flag immediately. */ 343*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_ISR, AX_SPI_ISR_MTC); 344*e75a6b00SVladimir Moravcevic 345*e75a6b00SVladimir Moravcevic /* For a TX-only transfer, rx_buf would be NULL. 346*e75a6b00SVladimir Moravcevic * In the spi-core, rx_copy_remaining would be 0. 347*e75a6b00SVladimir Moravcevic * So we can finalize immediately. 348*e75a6b00SVladimir Moravcevic */ 349*e75a6b00SVladimir Moravcevic if (!xspi->rx_buf) { 350*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_IMR, 0x00); 351*e75a6b00SVladimir Moravcevic spi_finalize_current_transfer(ctlr); 352*e75a6b00SVladimir Moravcevic return IRQ_HANDLED; 353*e75a6b00SVladimir Moravcevic } 354*e75a6b00SVladimir Moravcevic /* For a full-duplex transfer, process any remaining RX data. 355*e75a6b00SVladimir Moravcevic * The helper function will handle finalization if everything is received. 356*e75a6b00SVladimir Moravcevic */ 357*e75a6b00SVladimir Moravcevic ax_spi_process_rx_and_finalize(ctlr); 358*e75a6b00SVladimir Moravcevic return IRQ_HANDLED; 359*e75a6b00SVladimir Moravcevic } 360*e75a6b00SVladimir Moravcevic 361*e75a6b00SVladimir Moravcevic /* Handle "RX FIFO Full / Threshold Met" interrupt. 362*e75a6b00SVladimir Moravcevic * This means we need to make space in the RX FIFO by reading from it. 363*e75a6b00SVladimir Moravcevic */ 364*e75a6b00SVladimir Moravcevic if (intr_status & AX_SPI_IVR_RFFV) { 365*e75a6b00SVladimir Moravcevic if (ax_spi_process_rx_and_finalize(ctlr)) { 366*e75a6b00SVladimir Moravcevic /* Transfer was finalized inside the helper, we are done. */ 367*e75a6b00SVladimir Moravcevic } else { 368*e75a6b00SVladimir Moravcevic /* RX is not yet complete. If there are still TX bytes to send 369*e75a6b00SVladimir Moravcevic * (for very long transfers), we can fill the TX FIFO again. 370*e75a6b00SVladimir Moravcevic */ 371*e75a6b00SVladimir Moravcevic if (xspi->tx_bytes) 372*e75a6b00SVladimir Moravcevic ax_spi_fill_tx_fifo(xspi); 373*e75a6b00SVladimir Moravcevic } 374*e75a6b00SVladimir Moravcevic return IRQ_HANDLED; 375*e75a6b00SVladimir Moravcevic } 376*e75a6b00SVladimir Moravcevic 377*e75a6b00SVladimir Moravcevic return IRQ_NONE; 378*e75a6b00SVladimir Moravcevic } 379*e75a6b00SVladimir Moravcevic 380*e75a6b00SVladimir Moravcevic static int ax_prepare_message(struct spi_controller *ctlr, 381*e75a6b00SVladimir Moravcevic struct spi_message *msg) 382*e75a6b00SVladimir Moravcevic { 383*e75a6b00SVladimir Moravcevic ax_spi_config_clock_mode(msg->spi); 384*e75a6b00SVladimir Moravcevic return 0; 385*e75a6b00SVladimir Moravcevic } 386*e75a6b00SVladimir Moravcevic 387*e75a6b00SVladimir Moravcevic /** 388*e75a6b00SVladimir Moravcevic * ax_transfer_one - Initiates the SPI transfer 389*e75a6b00SVladimir Moravcevic * @ctlr: Pointer to spi_controller structure 390*e75a6b00SVladimir Moravcevic * @spi: Pointer to the spi_device structure 391*e75a6b00SVladimir Moravcevic * @transfer: Pointer to the spi_transfer structure which provides 392*e75a6b00SVladimir Moravcevic * information about next transfer parameters 393*e75a6b00SVladimir Moravcevic * 394*e75a6b00SVladimir Moravcevic * This function fills the TX FIFO, starts the SPI transfer and 395*e75a6b00SVladimir Moravcevic * returns a positive transfer count so that core will wait for completion. 396*e75a6b00SVladimir Moravcevic * 397*e75a6b00SVladimir Moravcevic * Return: Number of bytes transferred in the last transfer 398*e75a6b00SVladimir Moravcevic */ 399*e75a6b00SVladimir Moravcevic static int ax_transfer_one(struct spi_controller *ctlr, 400*e75a6b00SVladimir Moravcevic struct spi_device *spi, 401*e75a6b00SVladimir Moravcevic struct spi_transfer *transfer) 402*e75a6b00SVladimir Moravcevic { 403*e75a6b00SVladimir Moravcevic struct ax_spi *xspi = spi_controller_get_devdata(ctlr); 404*e75a6b00SVladimir Moravcevic int drain_limit; 405*e75a6b00SVladimir Moravcevic 406*e75a6b00SVladimir Moravcevic /* Pre-transfer cleanup:Flush the RX FIFO to discard any stale data. 407*e75a6b00SVladimir Moravcevic * This is the crucial part. Before every new transfer, we must ensure 408*e75a6b00SVladimir Moravcevic * the HW is in a clean state to avoid processing stale data 409*e75a6b00SVladimir Moravcevic * from a previous, possibly failed or interrupted, transfer. 410*e75a6b00SVladimir Moravcevic */ 411*e75a6b00SVladimir Moravcevic drain_limit = AX_SPI_RX_FIFO_DRAIN_LIMIT; // Sane limit to prevent infinite loop on HW error 412*e75a6b00SVladimir Moravcevic while (ax_spi_read(xspi, AX_SPI_RX_FBCAR) > 0 && drain_limit-- > 0) 413*e75a6b00SVladimir Moravcevic ax_spi_read(xspi, AX_SPI_RXFIFO); // Read and discard 414*e75a6b00SVladimir Moravcevic 415*e75a6b00SVladimir Moravcevic if (drain_limit <= 0) 416*e75a6b00SVladimir Moravcevic dev_warn(&ctlr->dev, "RX FIFO drain timeout before transfer\n"); 417*e75a6b00SVladimir Moravcevic 418*e75a6b00SVladimir Moravcevic /* Clear any stale interrupt flags from a previous transfer. 419*e75a6b00SVladimir Moravcevic * This prevents an immediate, false interrupt trigger. 420*e75a6b00SVladimir Moravcevic */ 421*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_ISR, AX_SPI_ISR_CLR); 422*e75a6b00SVladimir Moravcevic 423*e75a6b00SVladimir Moravcevic xspi->tx_buf = transfer->tx_buf; 424*e75a6b00SVladimir Moravcevic xspi->rx_buf = transfer->rx_buf; 425*e75a6b00SVladimir Moravcevic xspi->tx_bytes = transfer->len; 426*e75a6b00SVladimir Moravcevic xspi->rx_bytes = transfer->len; 427*e75a6b00SVladimir Moravcevic 428*e75a6b00SVladimir Moravcevic /* Reset RX 32-bit to byte buffer for each new transfer */ 429*e75a6b00SVladimir Moravcevic if (transfer->tx_buf && !transfer->rx_buf) { 430*e75a6b00SVladimir Moravcevic /* TX mode: discard all received data */ 431*e75a6b00SVladimir Moravcevic xspi->rx_discard = transfer->len; 432*e75a6b00SVladimir Moravcevic xspi->rx_copy_remaining = 0; 433*e75a6b00SVladimir Moravcevic } else if ((!transfer->tx_buf && transfer->rx_buf) || 434*e75a6b00SVladimir Moravcevic (transfer->tx_buf && transfer->rx_buf)) { 435*e75a6b00SVladimir Moravcevic /* RX mode: generate clock by filling TX FIFO with dummy bytes 436*e75a6b00SVladimir Moravcevic * Full-duplex mode: generate clock by filling TX FIFO 437*e75a6b00SVladimir Moravcevic */ 438*e75a6b00SVladimir Moravcevic xspi->rx_discard = 0; 439*e75a6b00SVladimir Moravcevic xspi->rx_copy_remaining = transfer->len; 440*e75a6b00SVladimir Moravcevic } else { 441*e75a6b00SVladimir Moravcevic /* No TX and RX */ 442*e75a6b00SVladimir Moravcevic xspi->rx_discard = 0; 443*e75a6b00SVladimir Moravcevic xspi->rx_copy_remaining = transfer->len; 444*e75a6b00SVladimir Moravcevic } 445*e75a6b00SVladimir Moravcevic 446*e75a6b00SVladimir Moravcevic ax_spi_setup_transfer(spi, transfer); 447*e75a6b00SVladimir Moravcevic ax_spi_fill_tx_fifo(xspi); 448*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_CR2, (AX_SPI_CR2_HTE | AX_SPI_CR2_SRD | AX_SPI_CR2_SWD)); 449*e75a6b00SVladimir Moravcevic 450*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_IMR, (AX_SPI_IMR_MTCM | AX_SPI_IMR_RFFM)); 451*e75a6b00SVladimir Moravcevic return transfer->len; 452*e75a6b00SVladimir Moravcevic } 453*e75a6b00SVladimir Moravcevic 454*e75a6b00SVladimir Moravcevic /** 455*e75a6b00SVladimir Moravcevic * ax_prepare_transfer_hardware - Prepares hardware for transfer. 456*e75a6b00SVladimir Moravcevic * @ctlr: Pointer to the spi_controller structure which provides 457*e75a6b00SVladimir Moravcevic * information about the controller. 458*e75a6b00SVladimir Moravcevic * 459*e75a6b00SVladimir Moravcevic * This function enables SPI host controller. 460*e75a6b00SVladimir Moravcevic * 461*e75a6b00SVladimir Moravcevic * Return: 0 always 462*e75a6b00SVladimir Moravcevic */ 463*e75a6b00SVladimir Moravcevic static int ax_prepare_transfer_hardware(struct spi_controller *ctlr) 464*e75a6b00SVladimir Moravcevic { 465*e75a6b00SVladimir Moravcevic struct ax_spi *xspi = spi_controller_get_devdata(ctlr); 466*e75a6b00SVladimir Moravcevic 467*e75a6b00SVladimir Moravcevic u32 reg_value; 468*e75a6b00SVladimir Moravcevic 469*e75a6b00SVladimir Moravcevic reg_value = ax_spi_read(xspi, AX_SPI_CR1); 470*e75a6b00SVladimir Moravcevic reg_value |= AX_SPI_CR1_SCE; 471*e75a6b00SVladimir Moravcevic 472*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_CR1, reg_value); 473*e75a6b00SVladimir Moravcevic 474*e75a6b00SVladimir Moravcevic return 0; 475*e75a6b00SVladimir Moravcevic } 476*e75a6b00SVladimir Moravcevic 477*e75a6b00SVladimir Moravcevic /** 478*e75a6b00SVladimir Moravcevic * ax_unprepare_transfer_hardware - Relaxes hardware after transfer 479*e75a6b00SVladimir Moravcevic * @ctlr: Pointer to the spi_controller structure which provides 480*e75a6b00SVladimir Moravcevic * information about the controller. 481*e75a6b00SVladimir Moravcevic * 482*e75a6b00SVladimir Moravcevic * This function disables the SPI host controller when no target selected. 483*e75a6b00SVladimir Moravcevic * 484*e75a6b00SVladimir Moravcevic * Return: 0 always 485*e75a6b00SVladimir Moravcevic */ 486*e75a6b00SVladimir Moravcevic static int ax_unprepare_transfer_hardware(struct spi_controller *ctlr) 487*e75a6b00SVladimir Moravcevic { 488*e75a6b00SVladimir Moravcevic struct ax_spi *xspi = spi_controller_get_devdata(ctlr); 489*e75a6b00SVladimir Moravcevic 490*e75a6b00SVladimir Moravcevic u32 reg_value; 491*e75a6b00SVladimir Moravcevic 492*e75a6b00SVladimir Moravcevic /* Disable the SPI if target is deselected */ 493*e75a6b00SVladimir Moravcevic reg_value = ax_spi_read(xspi, AX_SPI_CR1); 494*e75a6b00SVladimir Moravcevic reg_value &= ~AX_SPI_CR1_SCE; 495*e75a6b00SVladimir Moravcevic 496*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_CR1, reg_value); 497*e75a6b00SVladimir Moravcevic 498*e75a6b00SVladimir Moravcevic return 0; 499*e75a6b00SVladimir Moravcevic } 500*e75a6b00SVladimir Moravcevic 501*e75a6b00SVladimir Moravcevic /** 502*e75a6b00SVladimir Moravcevic * ax_spi_detect_fifo_depth - Detect the FIFO depth of the hardware 503*e75a6b00SVladimir Moravcevic * @xspi: Pointer to the ax_spi structure 504*e75a6b00SVladimir Moravcevic * 505*e75a6b00SVladimir Moravcevic * The depth of the TX FIFO is a synthesis configuration parameter of the SPI 506*e75a6b00SVladimir Moravcevic * IP. The FIFO threshold register is sized so that its maximum value can be the 507*e75a6b00SVladimir Moravcevic * FIFO size - 1. This is used to detect the size of the FIFO. 508*e75a6b00SVladimir Moravcevic */ 509*e75a6b00SVladimir Moravcevic static void ax_spi_detect_fifo_depth(struct ax_spi *xspi) 510*e75a6b00SVladimir Moravcevic { 511*e75a6b00SVladimir Moravcevic /* The MSBs will get truncated giving us the size of the FIFO */ 512*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_TX_FAETR, ALMOST_EMPTY_TRESHOLD); 513*e75a6b00SVladimir Moravcevic xspi->tx_fifo_depth = FIFO_DEPTH; 514*e75a6b00SVladimir Moravcevic 515*e75a6b00SVladimir Moravcevic /* Set the threshold limit */ 516*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_TX_FAETR, ALMOST_EMPTY_TRESHOLD); 517*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_RX_FAFTR, ALMOST_FULL_TRESHOLD); 518*e75a6b00SVladimir Moravcevic } 519*e75a6b00SVladimir Moravcevic 520*e75a6b00SVladimir Moravcevic /* --- Internal Helper Function for 32-bit RX FIFO Read --- */ 521*e75a6b00SVladimir Moravcevic /** 522*e75a6b00SVladimir Moravcevic * ax_spi_get_rx_byte - Gets a byte from the RX FIFO buffer 523*e75a6b00SVladimir Moravcevic * @xspi: Controller private data (struct ax_spi *) 524*e75a6b00SVladimir Moravcevic * 525*e75a6b00SVladimir Moravcevic * This function handles the logic of extracting bytes from the 32-bit RX FIFO. 526*e75a6b00SVladimir Moravcevic * It reads a new 32-bit word from AX_SPI_RXFIFO only when the current buffered 527*e75a6b00SVladimir Moravcevic * word has been fully processed (all 4 bytes extracted). It then extracts 528*e75a6b00SVladimir Moravcevic * bytes one by one, assuming the controller is little-endian. 529*e75a6b00SVladimir Moravcevic * 530*e75a6b00SVladimir Moravcevic * Returns: The next 8-bit byte read from the RX FIFO stream. 531*e75a6b00SVladimir Moravcevic */ 532*e75a6b00SVladimir Moravcevic static u8 ax_spi_get_rx_byte(struct ax_spi *xspi) 533*e75a6b00SVladimir Moravcevic { 534*e75a6b00SVladimir Moravcevic u8 byte_val; 535*e75a6b00SVladimir Moravcevic 536*e75a6b00SVladimir Moravcevic /* If all bytes from the current 32-bit word have been extracted, 537*e75a6b00SVladimir Moravcevic * read a new word from the hardware RX FIFO. 538*e75a6b00SVladimir Moravcevic */ 539*e75a6b00SVladimir Moravcevic if (xspi->bytes_left_in_current_rx_word == 0) { 540*e75a6b00SVladimir Moravcevic xspi->current_rx_fifo_word = ax_spi_read(xspi, AX_SPI_RXFIFO); 541*e75a6b00SVladimir Moravcevic xspi->bytes_left_in_current_rx_word = 4; // A new 32-bit word has 4 bytes 542*e75a6b00SVladimir Moravcevic } 543*e75a6b00SVladimir Moravcevic 544*e75a6b00SVladimir Moravcevic /* Extract the least significant byte from the current 32-bit word */ 545*e75a6b00SVladimir Moravcevic byte_val = (u8)(xspi->current_rx_fifo_word & 0xFF); 546*e75a6b00SVladimir Moravcevic 547*e75a6b00SVladimir Moravcevic /* Shift the word right by 8 bits to prepare the next byte for extraction */ 548*e75a6b00SVladimir Moravcevic xspi->current_rx_fifo_word >>= 8; 549*e75a6b00SVladimir Moravcevic xspi->bytes_left_in_current_rx_word--; 550*e75a6b00SVladimir Moravcevic 551*e75a6b00SVladimir Moravcevic return byte_val; 552*e75a6b00SVladimir Moravcevic } 553*e75a6b00SVladimir Moravcevic 554*e75a6b00SVladimir Moravcevic static int ax_spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) 555*e75a6b00SVladimir Moravcevic { 556*e75a6b00SVladimir Moravcevic struct spi_device *spi = mem->spi; 557*e75a6b00SVladimir Moravcevic struct ax_spi *xspi = spi_controller_get_devdata(spi->controller); 558*e75a6b00SVladimir Moravcevic u32 reg_val; 559*e75a6b00SVladimir Moravcevic int ret = 0; 560*e75a6b00SVladimir Moravcevic u8 cmd_buf[AX_SPI_COMMAND_BUFFER_SIZE]; 561*e75a6b00SVladimir Moravcevic int cmd_len = 0; 562*e75a6b00SVladimir Moravcevic int i = 0, timeout = AX_SPI_TRX_FIFO_TIMEOUT; 563*e75a6b00SVladimir Moravcevic int bytes_to_discard_from_rx; 564*e75a6b00SVladimir Moravcevic u8 *rx_buf_ptr = (u8 *)op->data.buf.in; 565*e75a6b00SVladimir Moravcevic u8 *tx_buf_ptr = (u8 *)op->data.buf.out; 566*e75a6b00SVladimir Moravcevic u32 rx_count_reg = 0; 567*e75a6b00SVladimir Moravcevic 568*e75a6b00SVladimir Moravcevic dev_dbg(&spi->dev, 569*e75a6b00SVladimir Moravcevic "%s: cmd:%02x mode:%d.%d.%d.%d addr:%llx len:%d\n", 570*e75a6b00SVladimir Moravcevic __func__, op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth, 571*e75a6b00SVladimir Moravcevic op->dummy.buswidth, op->data.buswidth, op->addr.val, 572*e75a6b00SVladimir Moravcevic op->data.nbytes); 573*e75a6b00SVladimir Moravcevic 574*e75a6b00SVladimir Moravcevic /* Validate operation parameters: Only 1-bit bus width supported */ 575*e75a6b00SVladimir Moravcevic if (op->cmd.buswidth != 1 || 576*e75a6b00SVladimir Moravcevic (op->addr.nbytes && op->addr.buswidth != 0 && 577*e75a6b00SVladimir Moravcevic op->addr.buswidth != 1) || 578*e75a6b00SVladimir Moravcevic (op->dummy.nbytes && op->dummy.buswidth != 0 && 579*e75a6b00SVladimir Moravcevic op->dummy.buswidth != 1) || 580*e75a6b00SVladimir Moravcevic (op->data.nbytes && op->data.buswidth != 1)) { 581*e75a6b00SVladimir Moravcevic dev_err(&spi->dev, "Unsupported bus width, only 1-bit bus width supported\n"); 582*e75a6b00SVladimir Moravcevic return -EOPNOTSUPP; 583*e75a6b00SVladimir Moravcevic } 584*e75a6b00SVladimir Moravcevic 585*e75a6b00SVladimir Moravcevic /* Initialize controller hardware */ 586*e75a6b00SVladimir Moravcevic ax_spi_init_hw(xspi); 587*e75a6b00SVladimir Moravcevic 588*e75a6b00SVladimir Moravcevic /* Assert chip select (pull low) */ 589*e75a6b00SVladimir Moravcevic ax_spi_chipselect(spi, false); 590*e75a6b00SVladimir Moravcevic 591*e75a6b00SVladimir Moravcevic /* Build command phase: Copy opcode to cmd_buf */ 592*e75a6b00SVladimir Moravcevic if (op->cmd.nbytes == 2) { 593*e75a6b00SVladimir Moravcevic cmd_buf[cmd_len++] = (op->cmd.opcode >> 8) & 0xFF; 594*e75a6b00SVladimir Moravcevic cmd_buf[cmd_len++] = op->cmd.opcode & 0xFF; 595*e75a6b00SVladimir Moravcevic } else { 596*e75a6b00SVladimir Moravcevic cmd_buf[cmd_len++] = op->cmd.opcode; 597*e75a6b00SVladimir Moravcevic } 598*e75a6b00SVladimir Moravcevic 599*e75a6b00SVladimir Moravcevic /* Put address bytes to cmd_buf */ 600*e75a6b00SVladimir Moravcevic if (op->addr.nbytes) { 601*e75a6b00SVladimir Moravcevic for (i = op->addr.nbytes - 1; i >= 0; i--) { 602*e75a6b00SVladimir Moravcevic cmd_buf[cmd_len] = (op->addr.val >> (i * 8)) & 0xFF; 603*e75a6b00SVladimir Moravcevic cmd_len++; 604*e75a6b00SVladimir Moravcevic } 605*e75a6b00SVladimir Moravcevic } 606*e75a6b00SVladimir Moravcevic 607*e75a6b00SVladimir Moravcevic /* Configure controller for desired operation mode (write/read) */ 608*e75a6b00SVladimir Moravcevic reg_val = ax_spi_read(xspi, AX_SPI_CR2); 609*e75a6b00SVladimir Moravcevic reg_val |= AX_SPI_CR2_SWD | AX_SPI_CR2_SRI | AX_SPI_CR2_SRD; 610*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_CR2, reg_val); 611*e75a6b00SVladimir Moravcevic 612*e75a6b00SVladimir Moravcevic /* Write command and address bytes to TX_FIFO */ 613*e75a6b00SVladimir Moravcevic for (i = 0; i < cmd_len; i++) 614*e75a6b00SVladimir Moravcevic ax_spi_write_b(xspi, AX_SPI_TXFIFO, cmd_buf[i]); 615*e75a6b00SVladimir Moravcevic 616*e75a6b00SVladimir Moravcevic /* Add dummy bytes (for clock generation) or actual data bytes to TX_FIFO */ 617*e75a6b00SVladimir Moravcevic if (op->data.dir == SPI_MEM_DATA_IN) { 618*e75a6b00SVladimir Moravcevic for (i = 0; i < op->dummy.nbytes; i++) 619*e75a6b00SVladimir Moravcevic ax_spi_write_b(xspi, AX_SPI_TXFIFO, 0x00); 620*e75a6b00SVladimir Moravcevic for (i = 0; i < op->data.nbytes; i++) 621*e75a6b00SVladimir Moravcevic ax_spi_write_b(xspi, AX_SPI_TXFIFO, 0x00); 622*e75a6b00SVladimir Moravcevic } else { 623*e75a6b00SVladimir Moravcevic for (i = 0; i < op->data.nbytes; i++) 624*e75a6b00SVladimir Moravcevic ax_spi_write_b(xspi, AX_SPI_TXFIFO, tx_buf_ptr[i]); 625*e75a6b00SVladimir Moravcevic } 626*e75a6b00SVladimir Moravcevic 627*e75a6b00SVladimir Moravcevic /* Start the SPI transmission */ 628*e75a6b00SVladimir Moravcevic reg_val = ax_spi_read(xspi, AX_SPI_CR2); 629*e75a6b00SVladimir Moravcevic reg_val |= AX_SPI_CR2_HTE; 630*e75a6b00SVladimir Moravcevic ax_spi_write(xspi, AX_SPI_CR2, reg_val); 631*e75a6b00SVladimir Moravcevic 632*e75a6b00SVladimir Moravcevic /* Wait for TX FIFO to become empty */ 633*e75a6b00SVladimir Moravcevic while (timeout-- > 0) { 634*e75a6b00SVladimir Moravcevic u32 tx_count_reg = ax_spi_read(xspi, AX_SPI_TX_FBCAR); 635*e75a6b00SVladimir Moravcevic 636*e75a6b00SVladimir Moravcevic if (tx_count_reg == 0) { 637*e75a6b00SVladimir Moravcevic udelay(1); 638*e75a6b00SVladimir Moravcevic break; 639*e75a6b00SVladimir Moravcevic } 640*e75a6b00SVladimir Moravcevic udelay(1); 641*e75a6b00SVladimir Moravcevic } 642*e75a6b00SVladimir Moravcevic 643*e75a6b00SVladimir Moravcevic /* Handle Data Reception (for read operations) */ 644*e75a6b00SVladimir Moravcevic if (op->data.dir == SPI_MEM_DATA_IN) { 645*e75a6b00SVladimir Moravcevic /* Reset the internal RX byte buffer for this new operation. 646*e75a6b00SVladimir Moravcevic * This ensures ax_spi_get_rx_byte starts fresh for each exec_op call. 647*e75a6b00SVladimir Moravcevic */ 648*e75a6b00SVladimir Moravcevic xspi->bytes_left_in_current_rx_word = 0; 649*e75a6b00SVladimir Moravcevic xspi->current_rx_fifo_word = 0; 650*e75a6b00SVladimir Moravcevic 651*e75a6b00SVladimir Moravcevic timeout = AX_SPI_TRX_FIFO_TIMEOUT; 652*e75a6b00SVladimir Moravcevic while (timeout-- > 0) { 653*e75a6b00SVladimir Moravcevic rx_count_reg = ax_spi_read(xspi, AX_SPI_RX_FBCAR); 654*e75a6b00SVladimir Moravcevic if (rx_count_reg >= op->data.nbytes) 655*e75a6b00SVladimir Moravcevic break; 656*e75a6b00SVladimir Moravcevic udelay(1); /* Small delay to prevent aggressive busy-waiting */ 657*e75a6b00SVladimir Moravcevic } 658*e75a6b00SVladimir Moravcevic 659*e75a6b00SVladimir Moravcevic if (timeout < 0) { 660*e75a6b00SVladimir Moravcevic ret = -ETIMEDOUT; 661*e75a6b00SVladimir Moravcevic goto out_unlock; 662*e75a6b00SVladimir Moravcevic } 663*e75a6b00SVladimir Moravcevic 664*e75a6b00SVladimir Moravcevic /* Calculate how many bytes we need to discard from the RX FIFO. 665*e75a6b00SVladimir Moravcevic * Since we set SRI, we only need to discard the address bytes and 666*e75a6b00SVladimir Moravcevic * dummy bytes from the RX FIFO. 667*e75a6b00SVladimir Moravcevic */ 668*e75a6b00SVladimir Moravcevic bytes_to_discard_from_rx = op->addr.nbytes + op->dummy.nbytes; 669*e75a6b00SVladimir Moravcevic for (i = 0; i < bytes_to_discard_from_rx; i++) 670*e75a6b00SVladimir Moravcevic ax_spi_get_rx_byte(xspi); 671*e75a6b00SVladimir Moravcevic 672*e75a6b00SVladimir Moravcevic /* Read actual data bytes into op->data.buf.in */ 673*e75a6b00SVladimir Moravcevic for (i = 0; i < op->data.nbytes; i++) { 674*e75a6b00SVladimir Moravcevic *rx_buf_ptr = ax_spi_get_rx_byte(xspi); 675*e75a6b00SVladimir Moravcevic rx_buf_ptr++; 676*e75a6b00SVladimir Moravcevic } 677*e75a6b00SVladimir Moravcevic } else if (op->data.dir == SPI_MEM_DATA_OUT) { 678*e75a6b00SVladimir Moravcevic timeout = AX_SPI_TRX_FIFO_TIMEOUT; 679*e75a6b00SVladimir Moravcevic while (timeout-- > 0) { 680*e75a6b00SVladimir Moravcevic u32 tx_fifo_level = ax_spi_read(xspi, AX_SPI_TX_FBCAR); 681*e75a6b00SVladimir Moravcevic 682*e75a6b00SVladimir Moravcevic if (tx_fifo_level == 0) 683*e75a6b00SVladimir Moravcevic break; 684*e75a6b00SVladimir Moravcevic udelay(1); 685*e75a6b00SVladimir Moravcevic } 686*e75a6b00SVladimir Moravcevic if (timeout < 0) { 687*e75a6b00SVladimir Moravcevic ret = -ETIMEDOUT; 688*e75a6b00SVladimir Moravcevic goto out_unlock; 689*e75a6b00SVladimir Moravcevic } 690*e75a6b00SVladimir Moravcevic } 691*e75a6b00SVladimir Moravcevic 692*e75a6b00SVladimir Moravcevic out_unlock: 693*e75a6b00SVladimir Moravcevic /* Deassert chip select (pull high) */ 694*e75a6b00SVladimir Moravcevic ax_spi_chipselect(spi, true); 695*e75a6b00SVladimir Moravcevic 696*e75a6b00SVladimir Moravcevic return ret; 697*e75a6b00SVladimir Moravcevic } 698*e75a6b00SVladimir Moravcevic 699*e75a6b00SVladimir Moravcevic static int ax_spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) 700*e75a6b00SVladimir Moravcevic { 701*e75a6b00SVladimir Moravcevic struct spi_device *spi = mem->spi; 702*e75a6b00SVladimir Moravcevic struct ax_spi *xspi = spi_controller_get_devdata(spi->controller); 703*e75a6b00SVladimir Moravcevic size_t max_transfer_payload_bytes; 704*e75a6b00SVladimir Moravcevic size_t fifo_total_bytes; 705*e75a6b00SVladimir Moravcevic size_t protocol_overhead_bytes; 706*e75a6b00SVladimir Moravcevic 707*e75a6b00SVladimir Moravcevic fifo_total_bytes = xspi->tx_fifo_depth; 708*e75a6b00SVladimir Moravcevic /* Calculate protocol overhead bytes according to the real operation each time. */ 709*e75a6b00SVladimir Moravcevic protocol_overhead_bytes = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes; 710*e75a6b00SVladimir Moravcevic 711*e75a6b00SVladimir Moravcevic /* Calculate the maximum data payload that can fit into the FIFO. */ 712*e75a6b00SVladimir Moravcevic if (fifo_total_bytes <= protocol_overhead_bytes) { 713*e75a6b00SVladimir Moravcevic max_transfer_payload_bytes = 0; 714*e75a6b00SVladimir Moravcevic dev_warn_once(&spi->dev, "SPI FIFO (%zu bytes) is too small for protocol overhead (%zu bytes)! Max data size forced to 0.\n", 715*e75a6b00SVladimir Moravcevic fifo_total_bytes, protocol_overhead_bytes); 716*e75a6b00SVladimir Moravcevic } else { 717*e75a6b00SVladimir Moravcevic max_transfer_payload_bytes = fifo_total_bytes - protocol_overhead_bytes; 718*e75a6b00SVladimir Moravcevic } 719*e75a6b00SVladimir Moravcevic 720*e75a6b00SVladimir Moravcevic /* Limit op->data.nbytes based on the calculated max payload and SZ_64K. 721*e75a6b00SVladimir Moravcevic * This is the value that spi-mem will then use to split requests. 722*e75a6b00SVladimir Moravcevic */ 723*e75a6b00SVladimir Moravcevic if (op->data.nbytes > max_transfer_payload_bytes) { 724*e75a6b00SVladimir Moravcevic op->data.nbytes = max_transfer_payload_bytes; 725*e75a6b00SVladimir Moravcevic dev_dbg(&spi->dev, "%s %d: op->data.nbytes adjusted to %u due to FIFO overhead\n", 726*e75a6b00SVladimir Moravcevic __func__, __LINE__, op->data.nbytes); 727*e75a6b00SVladimir Moravcevic } 728*e75a6b00SVladimir Moravcevic 729*e75a6b00SVladimir Moravcevic /* Also apply the overall max transfer size */ 730*e75a6b00SVladimir Moravcevic if (op->data.nbytes > SZ_64K) { 731*e75a6b00SVladimir Moravcevic op->data.nbytes = SZ_64K; 732*e75a6b00SVladimir Moravcevic dev_dbg(&spi->dev, "%s %d: op->data.nbytes adjusted to %u due to SZ_64K limit\n", 733*e75a6b00SVladimir Moravcevic __func__, __LINE__, op->data.nbytes); 734*e75a6b00SVladimir Moravcevic } 735*e75a6b00SVladimir Moravcevic 736*e75a6b00SVladimir Moravcevic return 0; 737*e75a6b00SVladimir Moravcevic } 738*e75a6b00SVladimir Moravcevic 739*e75a6b00SVladimir Moravcevic static const struct spi_controller_mem_ops ax_spi_mem_ops = { 740*e75a6b00SVladimir Moravcevic .exec_op = ax_spi_mem_exec_op, 741*e75a6b00SVladimir Moravcevic .adjust_op_size = ax_spi_mem_adjust_op_size, 742*e75a6b00SVladimir Moravcevic }; 743*e75a6b00SVladimir Moravcevic 744*e75a6b00SVladimir Moravcevic /** 745*e75a6b00SVladimir Moravcevic * ax_spi_probe - Probe method for the SPI driver 746*e75a6b00SVladimir Moravcevic * @pdev: Pointer to the platform_device structure 747*e75a6b00SVladimir Moravcevic * 748*e75a6b00SVladimir Moravcevic * This function initializes the driver data structures and the hardware. 749*e75a6b00SVladimir Moravcevic * 750*e75a6b00SVladimir Moravcevic * Return: 0 on success and error value on error 751*e75a6b00SVladimir Moravcevic */ 752*e75a6b00SVladimir Moravcevic static int ax_spi_probe(struct platform_device *pdev) 753*e75a6b00SVladimir Moravcevic { 754*e75a6b00SVladimir Moravcevic int ret = 0, irq; 755*e75a6b00SVladimir Moravcevic struct spi_controller *ctlr; 756*e75a6b00SVladimir Moravcevic struct ax_spi *xspi; 757*e75a6b00SVladimir Moravcevic u32 num_cs; 758*e75a6b00SVladimir Moravcevic 759*e75a6b00SVladimir Moravcevic ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*xspi)); 760*e75a6b00SVladimir Moravcevic if (!ctlr) 761*e75a6b00SVladimir Moravcevic return -ENOMEM; 762*e75a6b00SVladimir Moravcevic 763*e75a6b00SVladimir Moravcevic xspi = spi_controller_get_devdata(ctlr); 764*e75a6b00SVladimir Moravcevic ctlr->dev.of_node = pdev->dev.of_node; 765*e75a6b00SVladimir Moravcevic platform_set_drvdata(pdev, ctlr); 766*e75a6b00SVladimir Moravcevic 767*e75a6b00SVladimir Moravcevic xspi->regs = devm_platform_ioremap_resource(pdev, 0); 768*e75a6b00SVladimir Moravcevic if (IS_ERR(xspi->regs)) { 769*e75a6b00SVladimir Moravcevic ret = PTR_ERR(xspi->regs); 770*e75a6b00SVladimir Moravcevic goto remove_ctlr; 771*e75a6b00SVladimir Moravcevic } 772*e75a6b00SVladimir Moravcevic 773*e75a6b00SVladimir Moravcevic xspi->pclk = devm_clk_get(&pdev->dev, "pclk"); 774*e75a6b00SVladimir Moravcevic if (IS_ERR(xspi->pclk)) { 775*e75a6b00SVladimir Moravcevic dev_err(&pdev->dev, "pclk clock not found.\n"); 776*e75a6b00SVladimir Moravcevic ret = PTR_ERR(xspi->pclk); 777*e75a6b00SVladimir Moravcevic goto remove_ctlr; 778*e75a6b00SVladimir Moravcevic } 779*e75a6b00SVladimir Moravcevic 780*e75a6b00SVladimir Moravcevic xspi->ref_clk = devm_clk_get(&pdev->dev, "ref"); 781*e75a6b00SVladimir Moravcevic if (IS_ERR(xspi->ref_clk)) { 782*e75a6b00SVladimir Moravcevic dev_err(&pdev->dev, "ref clock not found.\n"); 783*e75a6b00SVladimir Moravcevic ret = PTR_ERR(xspi->ref_clk); 784*e75a6b00SVladimir Moravcevic goto remove_ctlr; 785*e75a6b00SVladimir Moravcevic } 786*e75a6b00SVladimir Moravcevic 787*e75a6b00SVladimir Moravcevic ret = clk_prepare_enable(xspi->pclk); 788*e75a6b00SVladimir Moravcevic if (ret) { 789*e75a6b00SVladimir Moravcevic dev_err(&pdev->dev, "Unable to enable APB clock.\n"); 790*e75a6b00SVladimir Moravcevic goto remove_ctlr; 791*e75a6b00SVladimir Moravcevic } 792*e75a6b00SVladimir Moravcevic 793*e75a6b00SVladimir Moravcevic ret = clk_prepare_enable(xspi->ref_clk); 794*e75a6b00SVladimir Moravcevic if (ret) { 795*e75a6b00SVladimir Moravcevic dev_err(&pdev->dev, "Unable to enable device clock.\n"); 796*e75a6b00SVladimir Moravcevic goto clk_dis_apb; 797*e75a6b00SVladimir Moravcevic } 798*e75a6b00SVladimir Moravcevic 799*e75a6b00SVladimir Moravcevic pm_runtime_use_autosuspend(&pdev->dev); 800*e75a6b00SVladimir Moravcevic pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT); 801*e75a6b00SVladimir Moravcevic pm_runtime_get_noresume(&pdev->dev); 802*e75a6b00SVladimir Moravcevic pm_runtime_set_active(&pdev->dev); 803*e75a6b00SVladimir Moravcevic pm_runtime_enable(&pdev->dev); 804*e75a6b00SVladimir Moravcevic 805*e75a6b00SVladimir Moravcevic ret = of_property_read_u32(pdev->dev.of_node, "num-cs", &num_cs); 806*e75a6b00SVladimir Moravcevic if (ret < 0) 807*e75a6b00SVladimir Moravcevic ctlr->num_chipselect = AX_SPI_DEFAULT_NUM_CS; 808*e75a6b00SVladimir Moravcevic else 809*e75a6b00SVladimir Moravcevic ctlr->num_chipselect = num_cs; 810*e75a6b00SVladimir Moravcevic 811*e75a6b00SVladimir Moravcevic ax_spi_detect_fifo_depth(xspi); 812*e75a6b00SVladimir Moravcevic 813*e75a6b00SVladimir Moravcevic xspi->current_rx_fifo_word = 0; 814*e75a6b00SVladimir Moravcevic xspi->bytes_left_in_current_rx_word = 0; 815*e75a6b00SVladimir Moravcevic 816*e75a6b00SVladimir Moravcevic /* Initialize IRQ-related variables */ 817*e75a6b00SVladimir Moravcevic xspi->bytes_left_in_current_rx_word_for_irq = 0; 818*e75a6b00SVladimir Moravcevic xspi->current_rx_fifo_word_for_irq = 0; 819*e75a6b00SVladimir Moravcevic 820*e75a6b00SVladimir Moravcevic /* SPI controller initializations */ 821*e75a6b00SVladimir Moravcevic ax_spi_init_hw(xspi); 822*e75a6b00SVladimir Moravcevic 823*e75a6b00SVladimir Moravcevic irq = platform_get_irq(pdev, 0); 824*e75a6b00SVladimir Moravcevic if (irq <= 0) { 825*e75a6b00SVladimir Moravcevic ret = -ENXIO; 826*e75a6b00SVladimir Moravcevic goto clk_dis_all; 827*e75a6b00SVladimir Moravcevic } 828*e75a6b00SVladimir Moravcevic 829*e75a6b00SVladimir Moravcevic ret = devm_request_irq(&pdev->dev, irq, ax_spi_irq, 830*e75a6b00SVladimir Moravcevic 0, pdev->name, ctlr); 831*e75a6b00SVladimir Moravcevic if (ret != 0) { 832*e75a6b00SVladimir Moravcevic ret = -ENXIO; 833*e75a6b00SVladimir Moravcevic dev_err(&pdev->dev, "request_irq failed\n"); 834*e75a6b00SVladimir Moravcevic goto clk_dis_all; 835*e75a6b00SVladimir Moravcevic } 836*e75a6b00SVladimir Moravcevic 837*e75a6b00SVladimir Moravcevic ctlr->use_gpio_descriptors = true; 838*e75a6b00SVladimir Moravcevic ctlr->prepare_transfer_hardware = ax_prepare_transfer_hardware; 839*e75a6b00SVladimir Moravcevic ctlr->prepare_message = ax_prepare_message; 840*e75a6b00SVladimir Moravcevic ctlr->transfer_one = ax_transfer_one; 841*e75a6b00SVladimir Moravcevic ctlr->unprepare_transfer_hardware = ax_unprepare_transfer_hardware; 842*e75a6b00SVladimir Moravcevic ctlr->set_cs = ax_spi_chipselect; 843*e75a6b00SVladimir Moravcevic ctlr->auto_runtime_pm = true; 844*e75a6b00SVladimir Moravcevic ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; 845*e75a6b00SVladimir Moravcevic 846*e75a6b00SVladimir Moravcevic xspi->clk_rate = clk_get_rate(xspi->ref_clk); 847*e75a6b00SVladimir Moravcevic /* Set to default valid value */ 848*e75a6b00SVladimir Moravcevic ctlr->max_speed_hz = xspi->clk_rate / 2; 849*e75a6b00SVladimir Moravcevic xspi->speed_hz = ctlr->max_speed_hz; 850*e75a6b00SVladimir Moravcevic 851*e75a6b00SVladimir Moravcevic ctlr->bits_per_word_mask = SPI_BPW_MASK(8); 852*e75a6b00SVladimir Moravcevic 853*e75a6b00SVladimir Moravcevic pm_runtime_mark_last_busy(&pdev->dev); 854*e75a6b00SVladimir Moravcevic pm_runtime_put_autosuspend(&pdev->dev); 855*e75a6b00SVladimir Moravcevic 856*e75a6b00SVladimir Moravcevic ctlr->mem_ops = &ax_spi_mem_ops; 857*e75a6b00SVladimir Moravcevic 858*e75a6b00SVladimir Moravcevic ret = spi_register_controller(ctlr); 859*e75a6b00SVladimir Moravcevic if (ret) { 860*e75a6b00SVladimir Moravcevic dev_err(&pdev->dev, "spi_register_controller failed\n"); 861*e75a6b00SVladimir Moravcevic goto clk_dis_all; 862*e75a6b00SVladimir Moravcevic } 863*e75a6b00SVladimir Moravcevic 864*e75a6b00SVladimir Moravcevic return ret; 865*e75a6b00SVladimir Moravcevic 866*e75a6b00SVladimir Moravcevic clk_dis_all: 867*e75a6b00SVladimir Moravcevic pm_runtime_set_suspended(&pdev->dev); 868*e75a6b00SVladimir Moravcevic pm_runtime_disable(&pdev->dev); 869*e75a6b00SVladimir Moravcevic clk_disable_unprepare(xspi->ref_clk); 870*e75a6b00SVladimir Moravcevic clk_dis_apb: 871*e75a6b00SVladimir Moravcevic clk_disable_unprepare(xspi->pclk); 872*e75a6b00SVladimir Moravcevic remove_ctlr: 873*e75a6b00SVladimir Moravcevic spi_controller_put(ctlr); 874*e75a6b00SVladimir Moravcevic return ret; 875*e75a6b00SVladimir Moravcevic } 876*e75a6b00SVladimir Moravcevic 877*e75a6b00SVladimir Moravcevic /** 878*e75a6b00SVladimir Moravcevic * ax_spi_remove - Remove method for the SPI driver 879*e75a6b00SVladimir Moravcevic * @pdev: Pointer to the platform_device structure 880*e75a6b00SVladimir Moravcevic * 881*e75a6b00SVladimir Moravcevic * This function is called if a device is physically removed from the system or 882*e75a6b00SVladimir Moravcevic * if the driver module is being unloaded. It frees all resources allocated to 883*e75a6b00SVladimir Moravcevic * the device. 884*e75a6b00SVladimir Moravcevic */ 885*e75a6b00SVladimir Moravcevic static void ax_spi_remove(struct platform_device *pdev) 886*e75a6b00SVladimir Moravcevic { 887*e75a6b00SVladimir Moravcevic struct spi_controller *ctlr = platform_get_drvdata(pdev); 888*e75a6b00SVladimir Moravcevic struct ax_spi *xspi = spi_controller_get_devdata(ctlr); 889*e75a6b00SVladimir Moravcevic 890*e75a6b00SVladimir Moravcevic spi_unregister_controller(ctlr); 891*e75a6b00SVladimir Moravcevic 892*e75a6b00SVladimir Moravcevic pm_runtime_set_suspended(&pdev->dev); 893*e75a6b00SVladimir Moravcevic pm_runtime_disable(&pdev->dev); 894*e75a6b00SVladimir Moravcevic 895*e75a6b00SVladimir Moravcevic clk_disable_unprepare(xspi->ref_clk); 896*e75a6b00SVladimir Moravcevic clk_disable_unprepare(xspi->pclk); 897*e75a6b00SVladimir Moravcevic } 898*e75a6b00SVladimir Moravcevic 899*e75a6b00SVladimir Moravcevic /** 900*e75a6b00SVladimir Moravcevic * ax_spi_suspend - Suspend method for the SPI driver 901*e75a6b00SVladimir Moravcevic * @dev: Address of the platform_device structure 902*e75a6b00SVladimir Moravcevic * 903*e75a6b00SVladimir Moravcevic * This function disables the SPI controller and 904*e75a6b00SVladimir Moravcevic * changes the driver state to "suspend" 905*e75a6b00SVladimir Moravcevic * 906*e75a6b00SVladimir Moravcevic * Return: 0 on success and error value on error 907*e75a6b00SVladimir Moravcevic */ 908*e75a6b00SVladimir Moravcevic static int __maybe_unused ax_spi_suspend(struct device *dev) 909*e75a6b00SVladimir Moravcevic { 910*e75a6b00SVladimir Moravcevic struct spi_controller *ctlr = dev_get_drvdata(dev); 911*e75a6b00SVladimir Moravcevic 912*e75a6b00SVladimir Moravcevic return spi_controller_suspend(ctlr); 913*e75a6b00SVladimir Moravcevic } 914*e75a6b00SVladimir Moravcevic 915*e75a6b00SVladimir Moravcevic /** 916*e75a6b00SVladimir Moravcevic * ax_spi_resume - Resume method for the SPI driver 917*e75a6b00SVladimir Moravcevic * @dev: Address of the platform_device structure 918*e75a6b00SVladimir Moravcevic * 919*e75a6b00SVladimir Moravcevic * This function changes the driver state to "ready" 920*e75a6b00SVladimir Moravcevic * 921*e75a6b00SVladimir Moravcevic * Return: 0 on success and error value on error 922*e75a6b00SVladimir Moravcevic */ 923*e75a6b00SVladimir Moravcevic static int __maybe_unused ax_spi_resume(struct device *dev) 924*e75a6b00SVladimir Moravcevic { 925*e75a6b00SVladimir Moravcevic struct spi_controller *ctlr = dev_get_drvdata(dev); 926*e75a6b00SVladimir Moravcevic struct ax_spi *xspi = spi_controller_get_devdata(ctlr); 927*e75a6b00SVladimir Moravcevic 928*e75a6b00SVladimir Moravcevic ax_spi_init_hw(xspi); 929*e75a6b00SVladimir Moravcevic return spi_controller_resume(ctlr); 930*e75a6b00SVladimir Moravcevic } 931*e75a6b00SVladimir Moravcevic 932*e75a6b00SVladimir Moravcevic /** 933*e75a6b00SVladimir Moravcevic * ax_spi_runtime_resume - Runtime resume method for the SPI driver 934*e75a6b00SVladimir Moravcevic * @dev: Address of the platform_device structure 935*e75a6b00SVladimir Moravcevic * 936*e75a6b00SVladimir Moravcevic * This function enables the clocks 937*e75a6b00SVladimir Moravcevic * 938*e75a6b00SVladimir Moravcevic * Return: 0 on success and error value on error 939*e75a6b00SVladimir Moravcevic */ 940*e75a6b00SVladimir Moravcevic static int __maybe_unused ax_spi_runtime_resume(struct device *dev) 941*e75a6b00SVladimir Moravcevic { 942*e75a6b00SVladimir Moravcevic struct spi_controller *ctlr = dev_get_drvdata(dev); 943*e75a6b00SVladimir Moravcevic struct ax_spi *xspi = spi_controller_get_devdata(ctlr); 944*e75a6b00SVladimir Moravcevic int ret; 945*e75a6b00SVladimir Moravcevic 946*e75a6b00SVladimir Moravcevic ret = clk_prepare_enable(xspi->pclk); 947*e75a6b00SVladimir Moravcevic if (ret) { 948*e75a6b00SVladimir Moravcevic dev_err(dev, "Cannot enable APB clock.\n"); 949*e75a6b00SVladimir Moravcevic return ret; 950*e75a6b00SVladimir Moravcevic } 951*e75a6b00SVladimir Moravcevic 952*e75a6b00SVladimir Moravcevic ret = clk_prepare_enable(xspi->ref_clk); 953*e75a6b00SVladimir Moravcevic if (ret) { 954*e75a6b00SVladimir Moravcevic dev_err(dev, "Cannot enable device clock.\n"); 955*e75a6b00SVladimir Moravcevic clk_disable_unprepare(xspi->pclk); 956*e75a6b00SVladimir Moravcevic return ret; 957*e75a6b00SVladimir Moravcevic } 958*e75a6b00SVladimir Moravcevic return 0; 959*e75a6b00SVladimir Moravcevic } 960*e75a6b00SVladimir Moravcevic 961*e75a6b00SVladimir Moravcevic /** 962*e75a6b00SVladimir Moravcevic * ax_spi_runtime_suspend - Runtime suspend method for the SPI driver 963*e75a6b00SVladimir Moravcevic * @dev: Address of the platform_device structure 964*e75a6b00SVladimir Moravcevic * 965*e75a6b00SVladimir Moravcevic * This function disables the clocks 966*e75a6b00SVladimir Moravcevic * 967*e75a6b00SVladimir Moravcevic * Return: Always 0 968*e75a6b00SVladimir Moravcevic */ 969*e75a6b00SVladimir Moravcevic static int __maybe_unused ax_spi_runtime_suspend(struct device *dev) 970*e75a6b00SVladimir Moravcevic { 971*e75a6b00SVladimir Moravcevic struct spi_controller *ctlr = dev_get_drvdata(dev); 972*e75a6b00SVladimir Moravcevic struct ax_spi *xspi = spi_controller_get_devdata(ctlr); 973*e75a6b00SVladimir Moravcevic 974*e75a6b00SVladimir Moravcevic clk_disable_unprepare(xspi->ref_clk); 975*e75a6b00SVladimir Moravcevic clk_disable_unprepare(xspi->pclk); 976*e75a6b00SVladimir Moravcevic 977*e75a6b00SVladimir Moravcevic return 0; 978*e75a6b00SVladimir Moravcevic } 979*e75a6b00SVladimir Moravcevic 980*e75a6b00SVladimir Moravcevic static const struct dev_pm_ops ax_spi_dev_pm_ops = { 981*e75a6b00SVladimir Moravcevic SET_RUNTIME_PM_OPS(ax_spi_runtime_suspend, 982*e75a6b00SVladimir Moravcevic ax_spi_runtime_resume, NULL) 983*e75a6b00SVladimir Moravcevic SET_SYSTEM_SLEEP_PM_OPS(ax_spi_suspend, ax_spi_resume) 984*e75a6b00SVladimir Moravcevic }; 985*e75a6b00SVladimir Moravcevic 986*e75a6b00SVladimir Moravcevic static const struct of_device_id ax_spi_of_match[] = { 987*e75a6b00SVladimir Moravcevic { .compatible = "axiado,ax3000-spi" }, 988*e75a6b00SVladimir Moravcevic { /* end of table */ } 989*e75a6b00SVladimir Moravcevic }; 990*e75a6b00SVladimir Moravcevic MODULE_DEVICE_TABLE(of, ax_spi_of_match); 991*e75a6b00SVladimir Moravcevic 992*e75a6b00SVladimir Moravcevic /* ax_spi_driver - This structure defines the SPI subsystem platform driver */ 993*e75a6b00SVladimir Moravcevic static struct platform_driver ax_spi_driver = { 994*e75a6b00SVladimir Moravcevic .probe = ax_spi_probe, 995*e75a6b00SVladimir Moravcevic .remove = ax_spi_remove, 996*e75a6b00SVladimir Moravcevic .driver = { 997*e75a6b00SVladimir Moravcevic .name = AX_SPI_NAME, 998*e75a6b00SVladimir Moravcevic .of_match_table = ax_spi_of_match, 999*e75a6b00SVladimir Moravcevic .pm = &ax_spi_dev_pm_ops, 1000*e75a6b00SVladimir Moravcevic }, 1001*e75a6b00SVladimir Moravcevic }; 1002*e75a6b00SVladimir Moravcevic 1003*e75a6b00SVladimir Moravcevic module_platform_driver(ax_spi_driver); 1004*e75a6b00SVladimir Moravcevic 1005*e75a6b00SVladimir Moravcevic MODULE_AUTHOR("Axiado Corporation"); 1006*e75a6b00SVladimir Moravcevic MODULE_DESCRIPTION("Axiado SPI Host driver"); 1007*e75a6b00SVladimir Moravcevic MODULE_LICENSE("GPL"); 1008