From nobody Tue Dec 16 22:14:30 2025 Received: from mx0b-0016f401.pphosted.com (mx0b-0016f401.pphosted.com [67.231.156.173]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 599FA15D5A3; Wed, 29 May 2024 22:00:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=67.231.156.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1717020039; cv=none; b=Y4ER0JfMXQPnXScyjdSU7WACp22cJcNYvb/1MKpjEpQXhwQezX8PTUZdvYMqfEylN+JIdRCutFmSzpAYMrVmXkM6E3u+a+ZnUcpTLwrk3ZrJvlcz8Suui9puzMUkrqGocgoEyOFDRYQ5rQkrBqsKOcWzcYN5jI3DVRVImXGZi5g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1717020039; c=relaxed/simple; bh=r/aE+f+SQbJG3ozKay0GlDI7/LC67ye9uc0/R7BeTd4=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=KmkZ/oLDteGnDYi/hnvqkdkDIcrWbqw6RiPm0dx257J2jkkh8XgH0N52aA75oHwqQdAe4oebOdTW2oy94l+ke+MpU4S/cokhNJGlxQ0OqvagawYLB1xYEB1nWp8LlMyP241PO3QooKSwFxQwbCZscNbjlZha1BeWy3+xQCSESGI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=marvell.com; spf=pass smtp.mailfrom=marvell.com; dkim=pass (2048-bit key) header.d=marvell.com header.i=@marvell.com header.b=fQqtc9Db; arc=none smtp.client-ip=67.231.156.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=marvell.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=marvell.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=marvell.com header.i=@marvell.com header.b="fQqtc9Db" Received: from pps.filterd (m0045851.ppops.net [127.0.0.1]) by mx0b-0016f401.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 44TIdMZ6015558; Wed, 29 May 2024 15:00:32 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=pfpt0220; bh=Z I9+uU4quOo3lWwYZ/skqAd4eQQMLx6FgwuZHgq4ogE=; b=fQqtc9DbE2X0qj3E1 /Q7j/Xz2dOlFRWRoRusiqAWww+tR1zcz5Mpb78k+9mMyxM/L8WFXRK9PyDzPGtTn hN3AeSFKz2PcG5Us7RE7DdhURGUvW6gZDE3YVLgqZYUKD6Auuc2jOg7b6B3Fw7sg h92G7J5DAhejhccdo563QhP9WaqTQ3SFQvgcT2HO2xoIfSGPJ7dy+CkvelaMBQUN VowPhAAwlAknit6zIsABoaASFn4sei2JVSvRmoIqkvMytPbW5D/cSHPBVcD4Vda7 Okhw++9M+MGuuM6vl/+IhsSMT+DgkBEcxaUHgaZBu+hUHn2mO5ERQ/aAnhPfINV1 dZv1Q== Received: from dc6wp-exch02.marvell.com ([4.21.29.225]) by mx0b-0016f401.pphosted.com (PPS) with ESMTPS id 3ye1r12k8p-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 29 May 2024 15:00:32 -0700 (PDT) Received: from DC6WP-EXCH02.marvell.com (10.76.176.209) by DC6WP-EXCH02.marvell.com (10.76.176.209) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.4; Wed, 29 May 2024 15:00:31 -0700 Received: from maili.marvell.com (10.69.176.80) by DC6WP-EXCH02.marvell.com (10.76.176.209) with Microsoft SMTP Server id 15.2.1544.4 via Frontend Transport; Wed, 29 May 2024 15:00:31 -0700 Received: from Dell2s-9.sclab.marvell.com (unknown [10.110.150.250]) by maili.marvell.com (Postfix) with ESMTP id E885E5B6951; Wed, 29 May 2024 15:00:30 -0700 (PDT) From: Witold Sadowski To: , , CC: , , , , , Witold Sadowski Subject: [PATCH v7 4/4] spi: cadence: Add MRVL overlay xfer operation support Date: Wed, 29 May 2024 15:00:26 -0700 Message-ID: <20240529220026.1644986-5-wsadowski@marvell.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240529220026.1644986-1-wsadowski@marvell.com> References: <20240529220026.1644986-1-wsadowski@marvell.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-ORIG-GUID: MHEy2aCKKjQWt3xBdsXNKqZygt3Cdgyc X-Proofpoint-GUID: MHEy2aCKKjQWt3xBdsXNKqZygt3Cdgyc X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1039,Hydra:6.0.650,FMLib:17.12.28.16 definitions=2024-05-29_16,2024-05-28_01,2024-05-17_01 Content-Type: text/plain; charset="utf-8" MRVL Xfer overlay extends xSPI capabilities to support non-memory SPI operations. The Marvell overlay, combined with a generic command, allows for full-duplex SPI transactions. It also enables transactions with undetermined lengths using the cs_hold parameter and the ability to extend CS signal assertion, even if the xSPI block requests CS signal de-assertion. Signed-off-by: Witold Sadowski --- drivers/spi/spi-cadence-xspi.c | 245 +++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c index 490bba7a0fc8..7279a73162ce 100644 --- a/drivers/spi/spi-cadence-xspi.c +++ b/drivers/spi/spi-cadence-xspi.c @@ -219,6 +219,7 @@ #define CDNS_XSPI_DLL_RST_N BIT(24) #define CDNS_XSPI_DLL_LOCK BIT(0) =20 + /* Marvell overlay registers - clock */ #define MRVL_XSPI_CLK_CTRL_AUX_REG 0x2020 #define MRVL_XSPI_CLK_ENABLE BIT(0) @@ -232,6 +233,22 @@ #define MRVL_XSPI_SPIX_INTR_AUX 0x2000 #define MRVL_MSIX_CLEAR_IRQ 0x01 =20 +/* Marvell overlay registers - xfer */ +#define MRVL_XFER_FUNC_CTRL 0x210 +#define MRVL_XFER_FUNC_CTRL_READ_DATA(i) (0x000 + 8 * (i)) +#define MRVL_XFER_SOFT_RESET BIT(11) +#define MRVL_XFER_CS_N_HOLD GENMASK(9, 6) +#define MRVL_XFER_RECEIVE_ENABLE BIT(4) +#define MRVL_XFER_FUNC_ENABLE BIT(3) +#define MRVL_XFER_CLK_CAPTURE_POL BIT(2) +#define MRVL_XFER_CLK_DRIVE_POL BIT(1) +#define MRVL_XFER_FUNC_START BIT(0) +#define MRVL_XFER_QWORD_COUNT 32 +#define MRVL_XFER_QWORD_BYTECOUNT 8 + +#define MRVL_XSPI_POLL_TIMEOUT_US 1000 +#define MRVL_XSPI_POLL_DELAY_US 10 + enum cdns_xspi_stig_instr_type { CDNS_XSPI_STIG_INSTR_TYPE_0, CDNS_XSPI_STIG_INSTR_TYPE_1, @@ -256,6 +273,7 @@ struct cdns_xspi_dev { void __iomem *iobase; void __iomem *auxbase; void __iomem *sdmabase; + void __iomem *xferbase; =20 int irq; int cur_cs; @@ -270,6 +288,9 @@ struct cdns_xspi_dev { const void *out_buffer; =20 u8 hw_num_banks; + + bool xfer_in_progress; + int current_xfer_qword; }; =20 struct cdns_xspi_driver_data { @@ -836,6 +857,220 @@ static int cdns_xspi_setup(struct spi_device *spi_dev) return 0; } =20 +static int cdns_xspi_prepare_generic(int cs, const void *dout, int len, in= t glue, u32 *cmd_regs) +{ + u8 *data =3D (u8 *)dout; + int i; + int data_counter =3D 0; + + memset(cmd_regs, 0x00, 6*4); + + if (len > 7) { + for (i =3D (len >=3D 10 ? 2 : len - 8); i >=3D 0 ; i--) + cmd_regs[3] |=3D data[data_counter++] << (8*i); + } + if (len > 3) { + for (i =3D (len >=3D 7 ? 3 : len - 4); i >=3D 0; i--) + cmd_regs[2] |=3D data[data_counter++] << (8*i); + } + for (i =3D (len >=3D 3 ? 2 : len - 1); i >=3D 0 ; i--) + cmd_regs[1] |=3D data[data_counter++] << (8 + 8*i); + + cmd_regs[1] |=3D 96; + cmd_regs[3] |=3D len << 24; + cmd_regs[4] |=3D cs << 12; + + if (glue =3D=3D 1) + cmd_regs[4] |=3D 1 << 28; + + return 0; +} + +static unsigned char reverse_bits(unsigned char num) +{ + unsigned int count =3D sizeof(num) * 8 - 1; + unsigned int reverse_num =3D num; + + num >>=3D 1; + while (num) { + reverse_num <<=3D 1; + reverse_num |=3D num & 1; + num >>=3D 1; + count--; + } + reverse_num <<=3D count; + return reverse_num; +} + +static void cdns_xspi_read_single_qword(struct cdns_xspi_dev *cdns_xspi, u= 8 **buffer) +{ + u64 d =3D readq(cdns_xspi->xferbase + + MRVL_XFER_FUNC_CTRL_READ_DATA(cdns_xspi->current_xfer_qword)); + u8 *ptr =3D (u8 *)&d; + int k; + + for (k =3D 0; k < 8; k++) { + u8 val =3D reverse_bits((ptr[k])); + **buffer =3D val; + *buffer =3D *buffer + 1; + } + + cdns_xspi->current_xfer_qword++; + cdns_xspi->current_xfer_qword %=3D MRVL_XFER_QWORD_COUNT; +} + +static void cdns_xspi_finish_read(struct cdns_xspi_dev *cdns_xspi, u8 **bu= ffer, u32 data_count) +{ + u64 d =3D readq(cdns_xspi->xferbase + + MRVL_XFER_FUNC_CTRL_READ_DATA(cdns_xspi->current_xfer_qword)); + u8 *ptr =3D (u8 *)&d; + int k; + + for (k =3D 0; k < data_count % MRVL_XFER_QWORD_BYTECOUNT; k++) { + u8 val =3D reverse_bits((ptr[k])); + **buffer =3D val; + *buffer =3D *buffer + 1; + } + + cdns_xspi->current_xfer_qword++; + cdns_xspi->current_xfer_qword %=3D MRVL_XFER_QWORD_COUNT; +} + +static int cdns_xspi_prepare_transfer(int cs, int dir, int len, u32 *cmd_r= egs) +{ + memset(cmd_regs, 0x00, 6*4); + + cmd_regs[1] |=3D 127; + cmd_regs[2] |=3D len << 16; + cmd_regs[4] |=3D dir << 4; //dir =3D 0 read, dir =3D1 write + cmd_regs[4] |=3D cs << 12; + + return 0; +} + +static bool cdns_xspi_stig_ready(struct cdns_xspi_dev *cdns_xspi, bool sle= ep) +{ + u32 ctrl_stat; + + return readl_relaxed_poll_timeout + (cdns_xspi->iobase + CDNS_XSPI_CTRL_STATUS_REG, + ctrl_stat, + ((ctrl_stat & BIT(3)) =3D=3D 0), + sleep ? MRVL_XSPI_POLL_DELAY_US : 0, + sleep ? MRVL_XSPI_POLL_TIMEOUT_US : 0); +} + +static bool cdns_xspi_sdma_ready(struct cdns_xspi_dev *cdns_xspi, bool sle= ep) +{ + u32 ctrl_stat; + + return readl_relaxed_poll_timeout + (cdns_xspi->iobase + CDNS_XSPI_INTR_STATUS_REG, + ctrl_stat, + (ctrl_stat & CDNS_XSPI_SDMA_TRIGGER), + sleep ? MRVL_XSPI_POLL_DELAY_US : 0, + sleep ? MRVL_XSPI_POLL_TIMEOUT_US : 0); +} + +static int cdns_xspi_transfer_one_message_b0(struct spi_controller *contro= ller, + struct spi_message *m) +{ + struct cdns_xspi_dev *cdns_xspi =3D spi_controller_get_devdata(controller= ); + struct spi_device *spi =3D m->spi; + struct spi_transfer *t =3D NULL; + + const int max_len =3D MRVL_XFER_QWORD_BYTECOUNT * MRVL_XFER_QWORD_COUNT; + int current_cycle_count; + int cs =3D spi_get_chipselect(spi, 0); + int cs_change =3D 0; + + /* Enable xfer state machine */ + if (!cdns_xspi->xfer_in_progress) { + u32 xfer_control =3D readl(cdns_xspi->xferbase + MRVL_XFER_FUNC_CTRL); + + cdns_xspi->current_xfer_qword =3D 0; + cdns_xspi->xfer_in_progress =3D true; + xfer_control |=3D (MRVL_XFER_RECEIVE_ENABLE | + MRVL_XFER_CLK_CAPTURE_POL | + MRVL_XFER_FUNC_START | + MRVL_XFER_SOFT_RESET | + FIELD_PREP(MRVL_XFER_CS_N_HOLD, (1 << cs))); + xfer_control &=3D ~(MRVL_XFER_FUNC_ENABLE | MRVL_XFER_CLK_DRIVE_POL); + writel(xfer_control, cdns_xspi->xferbase + MRVL_XFER_FUNC_CTRL); + } + + list_for_each_entry(t, &m->transfers, transfer_list) { + u8 *txd =3D (u8 *) t->tx_buf; + u8 *rxd =3D (u8 *) t->rx_buf; + u8 data[10]; + u32 cmd_regs[6]; + + if (!txd) + txd =3D data; + + cdns_xspi->in_buffer =3D txd + 1; + cdns_xspi->out_buffer =3D txd + 1; + + while (t->len) { + + current_cycle_count =3D t->len > max_len ? max_len : t->len; + + if (current_cycle_count < 10) { + cdns_xspi_prepare_generic(cs, txd, current_cycle_count, + false, cmd_regs); + cdns_xspi_trigger_command(cdns_xspi, cmd_regs); + if (cdns_xspi_stig_ready(cdns_xspi, true)) + return -EIO; + } else { + cdns_xspi_prepare_generic(cs, txd, 1, true, cmd_regs); + cdns_xspi_trigger_command(cdns_xspi, cmd_regs); + cdns_xspi_prepare_transfer(cs, 1, current_cycle_count - 1, + cmd_regs); + cdns_xspi_trigger_command(cdns_xspi, cmd_regs); + if (cdns_xspi_sdma_ready(cdns_xspi, true)) + return -EIO; + cdns_xspi_sdma_handle(cdns_xspi); + if (cdns_xspi_stig_ready(cdns_xspi, true)) + return -EIO; + + cdns_xspi->in_buffer +=3D current_cycle_count; + cdns_xspi->out_buffer +=3D current_cycle_count; + } + + if (rxd) { + int j; + + for (j =3D 0; j < current_cycle_count / 8; j++) + cdns_xspi_read_single_qword(cdns_xspi, &rxd); + cdns_xspi_finish_read(cdns_xspi, &rxd, current_cycle_count); + } else { + cdns_xspi->current_xfer_qword +=3D current_cycle_count / + MRVL_XFER_QWORD_BYTECOUNT; + if (current_cycle_count % MRVL_XFER_QWORD_BYTECOUNT) + cdns_xspi->current_xfer_qword++; + + cdns_xspi->current_xfer_qword %=3D MRVL_XFER_QWORD_COUNT; + } + cs_change =3D t->cs_change; + t->len -=3D current_cycle_count; + } + } + + if (!cs_change) { + u32 xfer_control =3D readl(cdns_xspi->xferbase + MRVL_XFER_FUNC_CTRL); + + xfer_control &=3D ~(MRVL_XFER_RECEIVE_ENABLE | + MRVL_XFER_SOFT_RESET); + writel(xfer_control, cdns_xspi->xferbase + MRVL_XFER_FUNC_CTRL); + cdns_xspi->xfer_in_progress =3D false; + } + + m->status =3D 0; + spi_finalize_current_message(controller); + + return 0; +} + static int cdns_xspi_probe(struct platform_device *pdev) { struct device *dev =3D &pdev->dev; @@ -903,6 +1138,15 @@ static int cdns_xspi_probe(struct platform_device *pd= ev) return PTR_ERR(cdns_xspi->auxbase); } =20 + if (cdns_xspi->mrvl_hw_overlay) { + cdns_xspi->xferbase =3D devm_platform_ioremap_resource(pdev, 3); + if (IS_ERR(cdns_xspi->xferbase)) { + dev_info(dev, "XFER register base not found, set it\n"); + // For compatibility with older firmware + cdns_xspi->xferbase =3D cdns_xspi->iobase + 0x8000; + } + } + cdns_xspi->irq =3D platform_get_irq(pdev, 0); if (cdns_xspi->irq < 0) return -ENXIO; @@ -917,6 +1161,7 @@ static int cdns_xspi_probe(struct platform_device *pde= v) if (drv_data->mrvl_hw_overlay) { cdns_mrvl_xspi_setup_clock(cdns_xspi, MRVL_DEFAULT_CLK); cdns_xspi_configure_phy(cdns_xspi); + host->transfer_one_message =3D cdns_xspi_transfer_one_message_b0; } =20 cdns_xspi_print_phy_config(cdns_xspi); --=20 2.43.0