/** * zynq_qspi_start_transfer - Initiates the QSPI transfer * @master: Pointer to the spi_master structure which provides * information about the controller. * @qspi: Pointer to the spi_device structure * @transfer: Pointer to the spi_transfer structure which provide information * about next transfer parameters * * This function fills the TX FIFO, starts the QSPI transfer, and waits for the * transfer to be completed. * * Return: Number of bytes transferred in the last transfer */ static int zynq_qspi_start_transfer(struct spi_master *master, struct spi_device *qspi, struct spi_transfer *transfer) { struct zynq_qspi *xqspi = spi_master_get_devdata(master); u32 data; xqspi->txbuf = transfer->tx_buf; xqspi->rxbuf = transfer->rx_buf; xqspi->bytes_to_transfer = transfer->len; xqspi->bytes_to_receive = transfer->len; zynq_qspi_setup_transfer(qspi, transfer); if (transfer->len >= 4) { zynq_qspi_fill_tx_fifo(xqspi, ZYNQ_QSPI_FIFO_DEPTH); } else { zynq_qspi_copy_write_data(xqspi, &data, transfer->len); if (!xqspi->is_dual || xqspi->is_instr) zynq_qspi_write(xqspi, ZYNQ_QSPI_TXD_00_01_OFFSET + ((transfer->len - 1) * 4), data); else { zynq_qspi_tx_dual_parallel(xqspi, data, transfer->len); } } zynq_qspi_write(xqspi, ZYNQ_QSPI_IEN_OFFSET, ZYNQ_QSPI_IXR_ALL_MASK); return transfer->len; }
/* * zynq_qspi_fill_tx_fifo - Fills the TX FIFO with as many bytes as possible * @zqspi: Pointer to the zynq_qspi structure */ static void zynq_qspi_fill_tx_fifo(struct zynq_qspi *zqspi, u32 size) { u32 data = 0; u32 fifocount = 0; unsigned len, offset; static const unsigned offsets[4] = { ZYNQ_QSPI_TXD_00_00_OFFSET, ZYNQ_QSPI_TXD_00_01_OFFSET, ZYNQ_QSPI_TXD_00_10_OFFSET, ZYNQ_QSPI_TXD_00_11_OFFSET }; while ((fifocount < size) && (zqspi->bytes_to_transfer > 0)) { if (zqspi->bytes_to_transfer >= 4) { if (zqspi->txbuf) { memcpy(&data, zqspi->txbuf, 4); zqspi->txbuf += 4; } else { data = 0; } writel(data, &zynq_qspi_base->txd0r); zqspi->bytes_to_transfer -= 4; fifocount++; } else { /* Write TXD1, TXD2, TXD3 only if TxFIFO is empty. */ if (!(readl(&zynq_qspi_base->isr) & ZYNQ_QSPI_IXR_TXNFULL_MASK) && !zqspi->rxbuf) return; len = zqspi->bytes_to_transfer; zynq_qspi_copy_write_data(zqspi, &data, len); offset = (zqspi->rxbuf) ? offsets[0] : offsets[len]; writel(data, &zynq_qspi_base->confr + (offset / 4)); } } }
/** * zynq_qspi_irq - Interrupt service routine of the QSPI controller * @irq: IRQ number * @dev_id: Pointer to the xqspi structure * * This function handles TX empty only. * On TX empty interrupt this function reads the received data from RX FIFO and * fills the TX FIFO if there is any data remaining to be transferred. * * Return: IRQ_HANDLED when interrupt is handled; IRQ_NONE otherwise. */ static irqreturn_t zynq_qspi_irq(int irq, void *dev_id) { struct spi_master *master = dev_id; struct zynq_qspi *xqspi = spi_master_get_devdata(master); u32 intr_status, rxcount, rxindex = 0; u8 offset[3] = {ZYNQ_QSPI_TXD_00_01_OFFSET, ZYNQ_QSPI_TXD_00_10_OFFSET, ZYNQ_QSPI_TXD_00_11_OFFSET}; intr_status = zynq_qspi_read(xqspi, ZYNQ_QSPI_STATUS_OFFSET); zynq_qspi_write(xqspi, ZYNQ_QSPI_STATUS_OFFSET , intr_status); if ((intr_status & ZYNQ_QSPI_IXR_TXNFULL_MASK) || (intr_status & ZYNQ_QSPI_IXR_RXNEMTY_MASK)) { /* * This bit is set when Tx FIFO has < THRESHOLD entries. * We have the THRESHOLD value set to 1, * so this bit indicates Tx FIFO is empty. */ u32 data; rxcount = xqspi->bytes_to_receive - xqspi->bytes_to_transfer; rxcount = (rxcount % 4) ? ((rxcount/4) + 1) : (rxcount/4); /* Read out the data from the RX FIFO */ while ((rxindex < rxcount) && (rxindex < ZYNQ_QSPI_RX_THRESHOLD)) { if (xqspi->bytes_to_receive >= 4) { if (xqspi->rxbuf) { (*(u32 *)xqspi->rxbuf) = zynq_qspi_read(xqspi, ZYNQ_QSPI_RXD_OFFSET); xqspi->rxbuf += 4; } else { data = zynq_qspi_read(xqspi, ZYNQ_QSPI_RXD_OFFSET); } xqspi->bytes_to_receive -= 4; } else { data = zynq_qspi_read(xqspi, ZYNQ_QSPI_RXD_OFFSET); zynq_qspi_copy_read_data(xqspi, data, xqspi->bytes_to_receive); } rxindex++; } if (xqspi->bytes_to_transfer) { if (xqspi->bytes_to_transfer >= 4) { /* There is more data to send */ zynq_qspi_fill_tx_fifo(xqspi, ZYNQ_QSPI_RX_THRESHOLD); } else if (intr_status & ZYNQ_QSPI_IXR_TXNFULL_MASK) { int tmp; tmp = xqspi->bytes_to_transfer; zynq_qspi_copy_write_data(xqspi, &data, xqspi->bytes_to_transfer); if (!xqspi->is_dual || xqspi->is_instr) zynq_qspi_write(xqspi, offset[tmp - 1], data); else { zynq_qspi_tx_dual_parallel(xqspi, data, tmp); } } } else { /* * If transfer and receive is completed then only send * complete signal. */ if (!xqspi->bytes_to_receive) { zynq_qspi_write(xqspi, ZYNQ_QSPI_IDIS_OFFSET, ZYNQ_QSPI_IXR_ALL_MASK); spi_finalize_current_transfer(master); xqspi->is_instr = 0; } } return IRQ_HANDLED; } return IRQ_NONE; }
/* * zynq_qspi_start_transfer - Initiates the QSPI transfer * @qspi: Pointer to the spi_device structure * @transfer: Pointer to the spi_transfer structure which provide information * about next transfer parameters * * This function fills the TX FIFO, starts the QSPI transfer, and waits for the * transfer to be completed. * * returns: Number of bytes transferred in the last transfer */ static int zynq_qspi_start_transfer(struct spi_device *qspi, struct spi_transfer *transfer) { struct zynq_qspi *zqspi = &qspi->master; static u8 current_u_page; u32 data = 0; u8 instruction = 0; u8 index; debug("%s: qspi: 0x%08x transfer: 0x%08x len: %d\n", __func__, (u32)qspi, (u32)transfer, transfer->len); zqspi->txbuf = transfer->tx_buf; zqspi->rxbuf = transfer->rx_buf; zqspi->bytes_to_transfer = transfer->len; zqspi->bytes_to_receive = transfer->len; if (zqspi->txbuf) instruction = *(u8 *)zqspi->txbuf; if (instruction && zqspi->is_inst) { for (index = 0; index < ARRAY_SIZE(flash_inst); index++) if (instruction == flash_inst[index].opcode) break; /* * Instruction might have already been transmitted. This is a * 'data only' transfer */ if (index == ARRAY_SIZE(flash_inst)) goto xfer_data; zqspi->curr_inst = &flash_inst[index]; zqspi->inst_response = 1; if ((zqspi->is_dual == MODE_DUAL_STACKED) && (current_u_page != zqspi->u_page)) { if (zqspi->u_page) { /* Configure two memories on shared bus * by enabling upper mem */ writel((ZYNQ_QSPI_LCFG_TWO_MEM_MASK | ZYNQ_QSPI_LCFG_U_PAGE | (1 << ZYNQ_QSPI_LCFG_DUMMY_SHIFT) | ZYNQ_QSPI_FR_QOUT_CODE), &zynq_qspi_base->lcr); } else { /* Configure two memories on shared bus * by enabling lower mem */ writel((ZYNQ_QSPI_LCFG_TWO_MEM_MASK | (1 << ZYNQ_QSPI_LCFG_DUMMY_SHIFT) | ZYNQ_QSPI_FR_QOUT_CODE), &zynq_qspi_base->lcr); } current_u_page = zqspi->u_page; } /* Get the instruction */ data = 0; zynq_qspi_copy_write_data(zqspi, &data, zqspi->curr_inst->inst_size); /* * Write the instruction to LSB of the FIFO. The core is * designed such that it is not necessary to check whether the * write FIFO is full before writing. However, write would be * delayed if the user tries to write when write FIFO is full */ writel(data, &zynq_qspi_base->confr + (zqspi->curr_inst->offset / 4)); /* * Read status register and Read ID instructions don't require * to ignore the extra bytes in response of instruction as * response contains the value */ if ((instruction == ZYNQ_QSPI_FLASH_OPCODE_RDSR1) || (instruction == ZYNQ_QSPI_FLASH_OPCODE_RDSR2) || (instruction == ZYNQ_QSPI_FLASH_OPCODE_RDID) || (instruction == ZYNQ_QSPI_FLASH_OPCODE_BRRD) || (instruction == ZYNQ_QSPI_FLASH_OPCODE_RDEAR)) { if (zqspi->bytes_to_transfer < 4) zqspi->bytes_to_transfer = 0; else zqspi->bytes_to_transfer -= 3; } } xfer_data: /* * In case of Fast, Dual and Quad reads, transmit the instruction first. * Address and dummy byte should be transmitted after instruction * is transmitted */ if (((zqspi->is_inst == 0) && (zqspi->bytes_to_transfer)) || ((zqspi->bytes_to_transfer) && (instruction != ZYNQ_QSPI_FLASH_OPCODE_FR) && (instruction != ZYNQ_QSPI_FLASH_OPCODE_DR) && (instruction != ZYNQ_QSPI_FLASH_OPCODE_QR) && (instruction != ZYNQ_QSPI_FLASH_OPCODE_DIOR))) zynq_qspi_fill_tx_fifo(zqspi, ZYNQ_QSPI_FIFO_DEPTH); writel(ZYNQ_QSPI_IXR_ALL_MASK, &zynq_qspi_base->ier); /* Start the transfer by enabling manual start bit */ /* wait for completion */ do { data = zynq_qspi_irq_poll(zqspi); } while (data == 0); return (transfer->len) - (zqspi->bytes_to_transfer); }