From nobody Wed Jun 17 05:16:06 2026 Received: from mail-pl1-f176.google.com (mail-pl1-f176.google.com [209.85.214.176]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B40103D3481 for ; Mon, 27 Apr 2026 14:02:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777298526; cv=none; b=VcUL1xYYHn3a+f586UMOw5PjFA70lPJo/lUYtF3MPUs9ViQ761yBnjGmr8HLuUzBFkrlXgNQEMHkDz3yoEyQR9TJ+IEflW+vlw6ukEIWtZIYYBIWfDKvSZvBvW5JE44ynX86Sz+4IKWS4QjfDONM9MXf/s4WZLc/jIXRKzWRSr0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777298526; c=relaxed/simple; bh=fJhg2u/iTIaljZD3q69iV8pLDNtu38nnqnZrJ0s0POI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=P9qk/7VhSFVC+2CNuR0AT80293i1ugOdzM2c69Dp5c5gL7to550KbCRoYazVu6+mMt62Js3atbzU7RnRN8tNfCbKOihZtm1QLqIIq8I/8l+f40vDZPgNoZkdsCm0P4ccVBRwURyHByMALTgIoqdKj9jHtjVdxVxI68t/iOEC1a0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=riscstar.com; spf=pass smtp.mailfrom=riscstar.com; dkim=pass (2048-bit key) header.d=riscstar-com.20251104.gappssmtp.com header.i=@riscstar-com.20251104.gappssmtp.com header.b=D/ZOW1AM; arc=none smtp.client-ip=209.85.214.176 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=riscstar.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=riscstar.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=riscstar-com.20251104.gappssmtp.com header.i=@riscstar-com.20251104.gappssmtp.com header.b="D/ZOW1AM" Received: by mail-pl1-f176.google.com with SMTP id d9443c01a7336-2b0afa0210bso46879355ad.2 for ; Mon, 27 Apr 2026 07:02:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=riscstar-com.20251104.gappssmtp.com; s=20251104; t=1777298524; x=1777903324; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=N0oaBw5eNc2BclV3Ait+0PeOTuCPczoggHsdIRqUpS0=; b=D/ZOW1AMf0rANDoVE71Sm5+Z627wVrAIKlwfEqS6pH3arI2ywSd2qgI5uPq1Tt0VhN PN1Uq/1RC4DKjb0XTZ13MQICScn9KcKR9kX4PuALJHJc2gZbqHAiwH+OAUF0jadEx9ph Ij9l01wVfwsoSXjRKKq86iUBszQFQbuVk6b12YnuLz8ECxRcXfiBGG8drmVuLyu6QREa p6JjDydc7b935K/fTOZrcZPlmT9P+sHfMp09amptLHqsw2ULldG2rCbwW4a75rPXUbyh vGD8OMMmmQFvXORrRe4juTFZvaLWRlbbxaD2ZtvDXnRNBHrQ8RuHyXGfgwfwHrKokgGV sKPQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777298524; x=1777903324; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=N0oaBw5eNc2BclV3Ait+0PeOTuCPczoggHsdIRqUpS0=; b=sMhS7fAm7KSaX9wliQ3Kb8DDnxRfBDd8eVhTRETbAJ1xjFwJQnWSeRaRF54FRKIYhk kKfOY1nm1UA08F3KO7KqiqJUmZ9EejATzRRKfDi1weD7L1SvCQSUe5NwUPQzgfMeVipH FE0GoWUprgK1BNU0UuqvKSO7tsFzNrEgTdnM5QbKbcbsj29omSJcp0ELx0fIjtlVu1Iz FVHrkVCo0j5rlWv94RKa8fgXx1xQXAkG74UUINcU8aaq9C2WILt4PUqhVcd7MBsyH2VM aQ9MjrKFBvTzCIInzPZ8iVDtw6l9A5KEqoHdI0taNF+GBhUpXlClbnegP116hFsFU4sh 0/Kg== X-Forwarded-Encrypted: i=1; AFNElJ8GVg+A8Z4A5d5f6qbN8hzvmezAN5taUu1jRCafEEStiIfB9fpLNK0vgnJvQKXgpG59vj4YK7YxUlI5nSs=@vger.kernel.org X-Gm-Message-State: AOJu0YyC79T7/GSDnyEfcoYkjdkpy/2jX3uNCUz4MyAHD1II0WGwS/mx aD+rSEVmS5tGljzphjRu0NaNPSJqK8l3WwNEIw2bmLQ3osZrT/vqjtL8ty+PpddkgwI= X-Gm-Gg: AeBDietWPrZRvb8YO+sYbC52iBV25PFsmjm/JWbftnF1UgahTsEAd/0uSTZimWFhQYS 6/c+OOvvdZLir3FsKeWfTnBAzBWnKkwrXs0Fm3g9KbXgdJEkfmXF3YTIYjyEEL/osqwWksHV3gh klT8z3RUyIexBY37ffD6RqltXeYjL3HqVJTyTxNdo7MKLgZD5A1lOQhm3+DXVyxlIRhWqcFItNN fgOoU+6iQ0lfpA+etGVOpbL2VHqbQWSiut1+UsnJYGaUxbFnRIpFuLUZNiUZVbIjIQpYlHlU0dt It6YjtlRWcHVdCopjkcwYosC7NbwddKJKH2QWtnLJB4Bt2CnzWTJgyACcZGD3DyNDv2Vf2UNWY3 2v6r1f1+H69rUm1NKaD7Pe6K8WiAzdzr1yyAMQHOJY+uohq1/U25ZD7tz9DTcmydOG0o31J9DD3 D/SCWCEHPx+HvMXIi3ctDQ8Ltv0OFoLIcffpotScNUzrcqLTtPxsT+eA== X-Received: by 2002:a17:902:bd44:b0:2b2:5da8:14be with SMTP id d9443c01a7336-2b5f9fcd9eemr318233315ad.41.1777298523356; Mon, 27 Apr 2026 07:02:03 -0700 (PDT) Received: from [127.0.1.1] ([2a12:a305:4::3016]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2b5fa9ff98csm307143625ad.3.2026.04.27.07.01.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 27 Apr 2026 07:02:02 -0700 (PDT) From: Guodong Xu Date: Mon, 27 Apr 2026 22:01:27 -0400 Subject: [PATCH v9 1/3] spi: dt-bindings: add SpacemiT K1 SPI support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260427-spi-spacemit-k1-v9-1-ff753b551302@riscstar.com> References: <20260427-spi-spacemit-k1-v9-0-ff753b551302@riscstar.com> In-Reply-To: <20260427-spi-spacemit-k1-v9-0-ff753b551302@riscstar.com> To: Mark Brown , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Yixun Lan , Alex Elder , Philipp Zabel , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti Cc: linux-spi@vger.kernel.org, devicetree@vger.kernel.org, linux-riscv@lists.infradead.org, spacemit@lists.linux.dev, linux-kernel@vger.kernel.org, Guodong Xu , Alex Elder , Conor Dooley , Troy Mitchell X-Mailer: b4 0.15.1 From: Alex Elder Add support for the SPI controller implemented by the SpacemiT K1 SoC. Acked-by: Conor Dooley Acked-by: Troy Mitchell Reviewed-by: Rob Herring (Arm) Signed-off-by: Alex Elder Signed-off-by: Guodong Xu --- v9: Fix commit subject prefix to starting with spi --- .../devicetree/bindings/spi/spacemit,k1-spi.yaml | 84 ++++++++++++++++++= ++++ 1 file changed, 84 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/spacemit,k1-spi.yaml b/D= ocumentation/devicetree/bindings/spi/spacemit,k1-spi.yaml new file mode 100644 index 0000000000000..e82c7f8d0b981 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spacemit,k1-spi.yaml @@ -0,0 +1,84 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/spacemit,k1-spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SpacemiT K1 SoC Serial Peripheral Interface (SPI) + +maintainers: + - Alex Elder + +description: + The SpacemiT K1 SoC implements a SPI controller that has two 32-entry + FIFOs, for transmit and receive. Details are currently available in + section 18.2.1 of the K1 User Manual, found in the SpacemiT Keystone + K1 Documentation[1]. The controller transfers words using PIO. DMA + transfers are supported as well, if both TX and RX DMA channels are + specified, + + [1] https://developer.spacemit.com/documentation + +allOf: + - $ref: /schemas/spi/spi-controller.yaml# + +properties: + compatible: + const: spacemit,k1-spi + + reg: + maxItems: 1 + + clocks: + items: + - description: Core clock + - description: Bus clock + + clock-names: + items: + - const: core + - const: bus + + resets: + maxItems: 1 + + interrupts: + maxItems: 1 + + dmas: + items: + - description: RX DMA channel + - description: TX DMA channel + + dma-names: + items: + - const: rx + - const: tx + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - interrupts + +unevaluatedProperties: false + +examples: + - | + + #include + spi@d401c000 { + compatible =3D "spacemit,k1-spi"; + reg =3D <0xd401c000 0x30>; + #address-cells =3D <1>; + #size-cells =3D <0>; + clocks =3D <&syscon_apbc CLK_SSP3>, + <&syscon_apbc CLK_SSP3_BUS>; + clock-names =3D "core", "bus"; + resets =3D <&syscon_apbc RESET_SSP3>; + interrupts =3D <55>; + dmas =3D <&pdma 20>, <&pdma 19>; + dma-names =3D "rx", "tx"; + }; --=20 2.43.0 From nobody Wed Jun 17 05:16:06 2026 Received: from mail-pg1-f172.google.com (mail-pg1-f172.google.com [209.85.215.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C95593D3CED for ; Mon, 27 Apr 2026 14:02:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777298535; cv=none; b=Ky2okm0iXSfjg+GRAkU3FRU9D3Zhe+9Ql8I5LymJwcX+xIqvvGruEqBYQCTvFhV7NIdVZgpQdOVbVLo3wuC8AbKmtLGJtsAjtFLnj1YucVYlxCFQjDP5p1uHFvBTSil3aHIZ4df8wEHIbYtvpvGyvXS3Z42EHoG7t+zVP+cO7p4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777298535; c=relaxed/simple; bh=M5upoTOm0DDfiZ6KO4b0x5xgiwTVy6xaB8cPMzOIupg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=DIEeIQKOQWkHEWm7oqy9ItzR9PqHtBLotVnb+EmqjSJ2GXa9j0sTjhZ8JModlSkc4Ol0NJoGk6K4YXviJNDm95STSqQUBCMqchUQGaVSFqG7KuzB+QdjSdAI7CpxEqwK1avc5zUZ30MHkV5HKUJwBHbVM+j5/URaXXEcupv3yJM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=riscstar.com; spf=pass smtp.mailfrom=riscstar.com; dkim=pass (2048-bit key) header.d=riscstar-com.20251104.gappssmtp.com header.i=@riscstar-com.20251104.gappssmtp.com header.b=KFOHhqMB; arc=none smtp.client-ip=209.85.215.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=riscstar.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=riscstar.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=riscstar-com.20251104.gappssmtp.com header.i=@riscstar-com.20251104.gappssmtp.com header.b="KFOHhqMB" Received: by mail-pg1-f172.google.com with SMTP id 41be03b00d2f7-c70ea5e9e9dso4529970a12.1 for ; Mon, 27 Apr 2026 07:02:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=riscstar-com.20251104.gappssmtp.com; s=20251104; t=1777298533; x=1777903333; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=2cwxB0RA96sd/sS/i60SFWb1dEX7cuvCJrS5ka+VIH8=; b=KFOHhqMBB/W8mkAJxCIlCg5Fz0lzPv8pfy3nuKW8kDoy+jcyb2i0rb4TSR14uMfvRi jRLpu7umRliePr2qzSzQHpKnoX9htlmRgnDiTQjZYwgRIVJNycsyw2Dm4Vd5kfbW7dVW tflT5fa/iY0Wf/ircBHgD7OyODW+CcU40uUucdzyvy+xTvhTcb08dEBjhTT7DUWKGU1/ UUD8xycvMGWq+QtD4Ymal5fH4fHhDQbSxT27NU3NvKNE454YPXe5eRA30zkQyBtWvLCS U48tOVZTMWnaKR1oPQ64/CSG7h5jDzzbbskxruRDbqTH2IK6ov40Xt66Clq3mT78d3uA skcw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777298533; x=1777903333; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=2cwxB0RA96sd/sS/i60SFWb1dEX7cuvCJrS5ka+VIH8=; b=TdaNazY/80oPuypibBWz9B+AkntqFCI6sBcgN8oXO9KaDx2fAiEUKmp2i3N1ZuTlMc RPR0WDOK80VNSh1pS7Xp/+0cMidEJ0xjog5N0gheLJxlYuyU6lfbVW5GryATMwqtpv2b ySwMFPO9tz+ttcvaHh9mExiALuEb25eP9ftdAEIETBxFqCd5aTceTnEqd8lgtGOmtDBV yS1iATRA2q1D8kl0vXb9ddp7QwgX27jdfFn4gnHAtB3bYmGJrUOJaBak1PkAJsmLTUt5 wTmI2O9UEkvwI1fAm6rVV0NMid8Qg7Q/MTebf3ZSKg2tJVFCN5eCT7CvT2Hp8OHRBs3g xIEA== X-Forwarded-Encrypted: i=1; AFNElJ9EBxIFQZSiBsE82VSZ4IhrbDVoEijvUG5lZZ5OOGcMCVu/DeDrLY4vKjMQ7eL4SAcsRqlgunkNiNko+aU=@vger.kernel.org X-Gm-Message-State: AOJu0YwlCG69jYASxjS5rlR/J4rKDiPT9m4RxAsaq8lyTxco5HcPdFqK rS8xU1Bm6uw5BEJ3K6Fh+W4wYOODO1wQJbpQgNySH8OCtecNrtg/6SQcM9mU7MhQN58= X-Gm-Gg: AeBDievQR3j+KoqeloK5txluVcwR1f3riYeF0o5PZmnzKvAu6Hw/RtcygX9Ga4TPXun 1S/CJyOdmoRWmK1jnWZeDH8YR19ptvqJJS0y5yu7AiBurFrJfDegWbZ7L63Eh2npFhVKDtkCRO2 b34RcLVxkg9a20pVznLUbQwZaRCfRfa5v8A/jm1F+jjm75aZ6MFgF0NskKUTnnliKVtpUTixWoW pjaCSu6Rlru27kOKnPy/iACz03OYWq2mxiTSrgcNwkrAbbPuILDBh6ncOrmnap9/gqnXPuRJs7n wSY0ygPSEqT8DzLo/P+E8o9mzP1vg568XhwaGhCJHlZHSbRRxmkWqWhzFdFjX7BlB7rVUC0rHV1 JHZtUYbbMR3EwKoVvsd/ybv6QMO3fR2qa52PE8ia9vAZCpPZ9DOpZNZGsVdMy2BVxUj0arMMPxl mumUKT7fimfy0cQzy2ylrPIQyVQmS0oKeyJVrCk0EBYSQ9GPm6Q2hk5A== X-Received: by 2002:a17:903:48c:b0:2b0:4ec8:fb92 with SMTP id d9443c01a7336-2b5fa035bb5mr290080195ad.39.1777298532394; Mon, 27 Apr 2026 07:02:12 -0700 (PDT) Received: from [127.0.1.1] ([2a12:a305:4::3016]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2b5fa9ff98csm307143625ad.3.2026.04.27.07.02.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 27 Apr 2026 07:02:12 -0700 (PDT) From: Guodong Xu Date: Mon, 27 Apr 2026 22:01:28 -0400 Subject: [PATCH v9 2/3] spi: spacemit: introduce SpacemiT K1 SPI controller driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260427-spi-spacemit-k1-v9-2-ff753b551302@riscstar.com> References: <20260427-spi-spacemit-k1-v9-0-ff753b551302@riscstar.com> In-Reply-To: <20260427-spi-spacemit-k1-v9-0-ff753b551302@riscstar.com> To: Mark Brown , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Yixun Lan , Alex Elder , Philipp Zabel , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti Cc: linux-spi@vger.kernel.org, devicetree@vger.kernel.org, linux-riscv@lists.infradead.org, spacemit@lists.linux.dev, linux-kernel@vger.kernel.org, Guodong Xu , Alex Elder X-Mailer: b4 0.15.1 From: Alex Elder This patch introduces the driver for the SPI controller found in the SpacemiT K1 SoC. Currently the driver supports master mode only. The SPI hardware implements RX and TX FIFOs, 32 entries each, and supports both PIO and DMA mode transfers. Signed-off-by: Alex Elder Signed-off-by: Guodong Xu --- v9: Addressing Mark Brown's v8 review: - k1_spi_dma_prep(): nested ternary to switch. - k1_spi_ssp_isr(): return IRQ_NONE based on SSP_STATUS bits, not on drv_data->transfer. - k1_spi_ssp_isr(): rename reading of SSP_STATUS to 'status' (and 'top_ctrl' for SSP_TOP_CTRL); stop overwriting it. --- drivers/spi/Kconfig | 9 + drivers/spi/Makefile | 1 + drivers/spi/spi-spacemit-k1.c | 792 ++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 802 insertions(+) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b563f49e21977..8782514bb89b0 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -1085,6 +1085,15 @@ config SPI_SG2044_NOR also supporting 3Byte address devices and 4Byte address devices. =20 +config SPI_SPACEMIT_K1 + tristate "K1 SPI Controller" + depends on ARCH_SPACEMIT || COMPILE_TEST + depends on OF + imply MMP_PDMA if ARCH_SPACEMIT + default m if ARCH_SPACEMIT + help + Enable support for the SpacemiT K1 SPI controller. + config SPI_SPRD tristate "Spreadtrum SPI controller" depends on ARCH_SPRD || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 9d36190a98848..9fa12498ce8c0 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -143,6 +143,7 @@ obj-$(CONFIG_SPI_SIFIVE) +=3D spi-sifive.o obj-$(CONFIG_SPI_SLAVE_MT27XX) +=3D spi-slave-mt27xx.o obj-$(CONFIG_SPI_SN_F_OSPI) +=3D spi-sn-f-ospi.o obj-$(CONFIG_SPI_SG2044_NOR) +=3D spi-sg2044-nor.o +obj-$(CONFIG_SPI_SPACEMIT_K1) +=3D spi-spacemit-k1.o obj-$(CONFIG_SPI_SPRD) +=3D spi-sprd.o obj-$(CONFIG_SPI_SPRD_ADI) +=3D spi-sprd-adi.o obj-$(CONFIG_SPI_STM32) +=3D spi-stm32.o diff --git a/drivers/spi/spi-spacemit-k1.c b/drivers/spi/spi-spacemit-k1.c new file mode 100644 index 0000000000000..a48bf5f5b7b30 --- /dev/null +++ b/drivers/spi/spi-spacemit-k1.c @@ -0,0 +1,792 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// SpacemiT K1 SPI controller driver +// +// Copyright (C) 2026, RISCstar Solutions Corporation +// Copyright (C) 2023, SpacemiT Corporation + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internals.h" + +/* This is the range of transfer rates supported by the K1 SoC */ +#define K1_SPI_MIN_SPEED_HZ 6250 +#define K1_SPI_MAX_SPEED_HZ 51200000 + +/* DMA constraints */ +#define K1_SPI_DMA_ALIGNMENT 64 +#define K1_SPI_MAX_DMA_LEN SZ_512K + +/* SSP Top Control Register */ +#define SSP_TOP_CTRL 0x00 +#define TOP_SSE BIT(0) /* Enable port */ +#define TOP_FRF_MASK GENMASK(2, 1) /* Frame format */ +#define TOP_FRF_MOTOROLA 0 /* Motorola SPI */ +#define TOP_DSS_MASK GENMASK(9, 5) /* Data size (1-32) */ +#define TOP_SPO BIT(10) /* Polarity: 0=3Dlow */ +#define TOP_SPH BIT(11) /* Half-cycle phase */ +#define TOP_LBM BIT(12) /* Loopback mode */ +#define TOP_TRAIL BIT(13) /* Trailing bytes */ +#define TOP_HOLD_FRAME_LOW BIT(14) /* Chip select */ + +/* SSP FIFO Control Register */ +#define SSP_FIFO_CTRL 0x04 +#define FIFO_TFT_MASK GENMASK(4, 0) /* TX FIFO threshold */ +#define FIFO_RFT_MASK GENMASK(9, 5) /* RX FIFO threshold */ +#define FIFO_TSRE BIT(10) /* TX service request */ +#define FIFO_RSRE BIT(11) /* RX service request */ + +/* SSP Interrupt Enable Register */ +#define SSP_INT_EN 0x08 +#define SSP_INT_EN_TINTE BIT(1) /* RX timeout */ +#define SSP_INT_EN_RIE BIT(2) /* RX FIFO */ +#define SSP_INT_EN_TIE BIT(3) /* TX FIFO */ +#define SSP_INT_EN_RIM BIT(4) /* RX FIFO overrun */ +#define SSP_INT_EN_TIM BIT(5) /* TX FIFO underrun */ +#define SSP_INT_EN_EBCEI BIT(6) /* Bit count error */ + +/* TX interrupts, RX interrupts, and error interrupts */ +#define SSP_INT_EN_TX SSP_INT_EN_TIE +#define SSP_INT_EN_RX \ + (SSP_INT_EN_TINTE | SSP_INT_EN_RIE) +#define SSP_INT_EN_ERROR \ + (SSP_INT_EN_RIM | SSP_INT_EN_TIM | SSP_INT_EN_EBCEI) + +/* SSP Time Out Register */ +#define SSP_TIMEOUT 0x0c +#define SSP_TIMEOUT_MASK GENMASK(23, 0) + +/* SSP Data Register */ +#define SSP_DATAR 0x10 + +/* SSP Status Register */ +#define SSP_STATUS 0x14 +#define SSP_STATUS_BSY BIT(0) /* SPI/I2S busy */ +#define SSP_STATUS_TNF BIT(6) /* TX FIFO not full */ +#define SSP_STATUS_TFL GENMASK(11, 7) /* TX FIFO level */ +#define SSP_STATUS_TUR BIT(12) /* TX FIFO underrun */ +#define SSP_STATUS_RNE BIT(14) /* RX FIFO not empty */ +#define SSP_STATUS_RFL GENMASK(19, 15) /* RX FIFO level */ +#define SSP_STATUS_ROR BIT(20) /* RX FIFO overrun */ +#define SSP_STATUS_BCE BIT(21) /* Bit count error */ + +/* Error status mask */ +#define SSP_STATUS_ERROR \ + (SSP_STATUS_TUR | SSP_STATUS_ROR | SSP_STATUS_BCE) + +/* The FIFO sizes and thresholds are the same for RX and TX */ +#define K1_SPI_FIFO_SIZE 32 +#define K1_SPI_THRESH (K1_SPI_FIFO_SIZE / 2) + +struct k1_spi_driver_data { + struct spi_controller *host; + void __iomem *base; + phys_addr_t base_addr; + unsigned long bus_rate; + struct clk *clk; + unsigned long rate; + int irq; + + /* Current transfer information; not valid if message is null */ + u32 bytes; /* Bytes used for bits_per_word */ + unsigned int rx_resid; /* RX bytes left in transfer */ + unsigned int tx_resid; /* TX bytes left in transfer */ + struct spi_transfer *transfer; /* Current transfer */ + + bool dma_enabled; +}; + +/* Set our registers to a known initial state */ +static void +k1_spi_register_reset(struct k1_spi_driver_data *drv_data, bool initial) +{ + u32 val =3D 0; + + writel(0, drv_data->base + SSP_TOP_CTRL); + + if (initial) { + /* + * The TX and RX FIFO thresholds are the same no matter + * what the speed or bits per word, so we can just set + * them once. The thresholds are one more than the values + * in the register. + */ + val =3D FIELD_PREP(FIFO_RFT_MASK, K1_SPI_THRESH - 1); + val |=3D FIELD_PREP(FIFO_TFT_MASK, K1_SPI_THRESH - 1); + } + writel(val, drv_data->base + SSP_FIFO_CTRL); + + writel(0, drv_data->base + SSP_INT_EN); + writel(0, drv_data->base + SSP_TIMEOUT); + + /* Clear any pending interrupt conditions */ + writel(~0, drv_data->base + SSP_STATUS); +} + +/* + * The client can call the setup function multiple times, and each call + * can specify a different SPI mode (and transfer speed). Each transfer + * can specify its own speed though, and the core code ensures each + * transfer's speed is set to something nonzero and supported by both + * the controller and the device. We just set the speed for each transfer. + */ +static int k1_spi_setup(struct spi_device *spi) +{ + struct k1_spi_driver_data *drv_data; + u32 val; + + drv_data =3D spi_controller_get_devdata(spi->controller); + + /* + * Configure the message format for this device. We only + * support Motorola SPI format in master mode. + */ + val =3D FIELD_PREP(TOP_FRF_MASK, TOP_FRF_MOTOROLA); + + /* Translate the mode into the value used to program the hardware. */ + if (spi->mode & SPI_CPHA) + val |=3D TOP_SPH; /* 1/2 cycle */ + if (spi->mode & SPI_CPOL) + val |=3D TOP_SPO; /* active low */ + if (spi->mode & SPI_LOOP) + val |=3D TOP_LBM; /* enable loopback */ + writel(val, drv_data->base + SSP_TOP_CTRL); + + return 0; +} + +static void k1_spi_cleanup(struct spi_device *spi) +{ + struct k1_spi_driver_data *drv_data; + + drv_data =3D spi_controller_get_devdata(spi->controller); + k1_spi_register_reset(drv_data, false); +} + +static bool k1_spi_can_dma(struct spi_controller *host, struct spi_device = *spi, + struct spi_transfer *transfer) +{ + struct k1_spi_driver_data *drv_data =3D spi_controller_get_devdata(host); + u32 burst_size; + + if (!drv_data->dma_enabled) + return false; + + if (transfer->len > SZ_2K) + return false; + + /* Don't bother with DMA if we can't do even a single burst */ + burst_size =3D K1_SPI_THRESH * spi_bpw_to_bytes(transfer->bits_per_word); + + return transfer->len >=3D burst_size; +} + +static void k1_spi_dma_callback(void *param) +{ + struct k1_spi_driver_data *drv_data =3D param; + u32 val; + + val =3D readl(drv_data->base + SSP_FIFO_CTRL); + val &=3D ~(FIFO_TSRE | FIFO_RSRE); + writel(val, drv_data->base + SSP_FIFO_CTRL); + + val =3D readl(drv_data->base + SSP_TOP_CTRL); + val &=3D ~TOP_TRAIL; + writel(val, drv_data->base + SSP_TOP_CTRL); + + /* Check for any error conditions */ + val =3D readl(drv_data->base + SSP_STATUS); + if (val & SSP_STATUS_ERROR) + drv_data->transfer->error |=3D SPI_TRANS_FAIL_IO; + + /* Disable the port */ + val =3D readl(drv_data->base + SSP_TOP_CTRL); + val &=3D ~TOP_SSE; + writel(val, drv_data->base + SSP_TOP_CTRL); + + drv_data->transfer =3D NULL; + + spi_finalize_current_transfer(drv_data->host); +} + +/* Prepare a descriptor for TX or RX DMA */ +static struct dma_async_tx_descriptor * +k1_spi_dma_prep(struct k1_spi_driver_data *drv_data, + struct spi_transfer *transfer, bool tx) +{ + phys_addr_t addr =3D drv_data->base_addr + SSP_DATAR; + u32 burst_size =3D K1_SPI_THRESH * drv_data->bytes; + struct dma_slave_config cfg =3D { }; + enum dma_transfer_direction dir; + enum dma_slave_buswidth width; + struct dma_chan *chan; + struct sg_table *sgt; + + switch (drv_data->bytes) { + case 1: + width =3D DMA_SLAVE_BUSWIDTH_1_BYTE; + break; + case 2: + width =3D DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + default: /* bytes =3D=3D 4 */ + width =3D DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + } + + if (tx) { + chan =3D drv_data->host->dma_tx; + sgt =3D &transfer->tx_sg; + dir =3D DMA_MEM_TO_DEV; + + cfg.dst_addr =3D addr; + cfg.dst_addr_width =3D width; + cfg.dst_maxburst =3D burst_size; + } else { + chan =3D drv_data->host->dma_rx; + sgt =3D &transfer->rx_sg; + dir =3D DMA_DEV_TO_MEM; + + cfg.src_addr =3D addr; + cfg.src_addr_width =3D width; + cfg.src_maxburst =3D burst_size; + } + cfg.direction =3D dir; + + if (dmaengine_slave_config(chan, &cfg)) + return NULL; + + return dmaengine_prep_slave_sg(chan, sgt->sgl, sgt->nents, dir, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + +} + +static int k1_spi_dma_one(struct spi_controller *host, struct spi_device *= spi, + struct spi_transfer *transfer) +{ + struct k1_spi_driver_data *drv_data =3D spi_controller_get_devdata(host); + struct dma_async_tx_descriptor *desc; + u32 val; + + /* Prepare the TX descriptor and submit it */ + desc =3D k1_spi_dma_prep(drv_data, transfer, true); + if (!desc) + goto fallback; + dmaengine_submit(desc); + + /* Prepare the RX descriptor and submit it */ + desc =3D k1_spi_dma_prep(drv_data, transfer, false); + if (!desc) + goto fallback; + + /* When RX is complete we also know TX has completed */ + desc->callback =3D k1_spi_dma_callback; + desc->callback_param =3D drv_data; + + dmaengine_submit(desc); + + val =3D readl(drv_data->base + SSP_TOP_CTRL); + val |=3D TOP_TRAIL; /* Trailing bytes handled by DMA */ + writel(val, drv_data->base + SSP_TOP_CTRL); + + val =3D readl(drv_data->base + SSP_FIFO_CTRL); + val |=3D FIFO_TSRE | FIFO_RSRE; + writel(val, drv_data->base + SSP_FIFO_CTRL); + + /* Start RX first so we're ready the instant we start transmitting */ + dma_async_issue_pending(host->dma_rx); + dma_async_issue_pending(host->dma_tx); + + return 1; +fallback: + transfer->error |=3D SPI_TRANS_FAIL_NO_START; + + return -EAGAIN; +} + +/* Flush the RX FIFO of any leftover data before processing a message */ +static int k1_spi_prepare_message(struct spi_controller *host, + struct spi_message *message) +{ + struct k1_spi_driver_data *drv_data =3D spi_controller_get_devdata(host); + u32 val =3D readl(drv_data->base + SSP_STATUS); + u32 count; + + /* If there's nothing in the FIFO, we're done */ + if (!(val & SSP_STATUS_RNE)) + return 0; + + /* Read and discard what's there (one more than what the field says) */ + count =3D FIELD_GET(SSP_STATUS_RFL, val) + 1; + do + (void)readl(drv_data->base + SSP_DATAR); + while (--count); + + return 0; +} + +/* Set logic level of chip select line (high=3Dtrue means CS deasserted) */ +static void k1_spi_set_cs(struct spi_device *spi, bool high) +{ + struct k1_spi_driver_data *drv_data; + u32 val; + + drv_data =3D spi_controller_get_devdata(spi->controller); + + val =3D readl(drv_data->base + SSP_TOP_CTRL); + if (high) + val &=3D ~TOP_HOLD_FRAME_LOW; + else + val |=3D TOP_HOLD_FRAME_LOW; + writel(val, drv_data->base + SSP_TOP_CTRL); +} + +/* Set the transfer speed; the SPI core code ensures it is supported */ +static int k1_spi_set_speed(struct k1_spi_driver_data *drv_data, + struct spi_transfer *transfer) +{ + struct clk *clk =3D drv_data->clk; + u64 nsec_per_word; + u64 bus_ticks; + u32 timeout; + u32 val; + int ret; + + ret =3D clk_set_rate(clk, transfer->speed_hz); + if (ret) + return ret; + + drv_data->rate =3D clk_get_rate(clk); + + /* No need for RX FIFO timeout if we're not receiving anything */ + if (!transfer->rx_buf) + return 0; + + /* + * Compute the RX FIFO inactivity timeout value that should be used. + * The inactivity timer restarts with each word that lands in the + * FIFO. If several "word transfer times" pass without any new data + * in the RX FIFO, we might as well read what's there. + * + * The rate at which words land in the FIFO is determined by the + * word size and the transfer rate. One bit is transferred per + * clock tick, and 8 (or 16 or 32) bits are transferred per word. + * + * So we can get word transfer time (in nanoseconds) from: + * nsec_per_tick =3D NSEC_PER_SEC / drv_data->rate; + * ticks_per_word =3D BITS_PER_BYTE * drv_data->bytes; + * We do the divide last for better accuracy. + */ + nsec_per_word =3D NSEC_PER_SEC * BITS_PER_BYTE * drv_data->bytes; + nsec_per_word =3D DIV_ROUND_UP_ULL(nsec_per_word, drv_data->rate); + + /* + * The timeout (which we'll set to three word transfer times) is + * expressed as a number of APB clock ticks. + * bus_ticks =3D 3 * nsec * (drv_data->bus_rate / NSEC_PER_SEC) + */ + bus_ticks =3D 3 * nsec_per_word * drv_data->bus_rate; + timeout =3D DIV_ROUND_UP_ULL(bus_ticks, NSEC_PER_SEC); + + /* Set the RX timeout period (required for both DMA and PIO) */ + val =3D FIELD_PREP(SSP_TIMEOUT_MASK, timeout); + writel(val, drv_data->base + SSP_TIMEOUT); + + return 0; +} + +static int k1_spi_transfer_one(struct spi_controller *host, + struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct k1_spi_driver_data *drv_data =3D spi_controller_get_devdata(host); + u32 count; + u32 ctrl; + u32 val; + int ret; + + /* Bits per word can change on a per-transfer basis */ + drv_data->bytes =3D spi_bpw_to_bytes(transfer->bits_per_word); + + /* Each transfer can also specify a different rate */ + ret =3D k1_spi_set_speed(drv_data, transfer); + if (ret) { + dev_err(&host->dev, + "failed to set transfer speed: %d\n", ret); + return ret; + } + + /* Record how many words the len bytes represent */ + count =3D transfer->len / drv_data->bytes; + drv_data->rx_resid =3D count; + drv_data->tx_resid =3D count; + + drv_data->transfer =3D transfer; + + /* Clear any existing interrupt conditions */ + writel(~0, drv_data->base + SSP_STATUS); + + /* Set the data (word) size, and enable the port */ + ctrl =3D readl(drv_data->base + SSP_TOP_CTRL); + ctrl &=3D ~TOP_DSS_MASK; + ctrl |=3D FIELD_PREP(TOP_DSS_MASK, transfer->bits_per_word - 1); + ctrl |=3D TOP_SSE; + writel(ctrl, drv_data->base + SSP_TOP_CTRL); + + if (spi_xfer_is_dma_mapped(host, spi, transfer)) + return k1_spi_dma_one(host, spi, transfer); + + /* An interrupt will initiate the transfer */ + val =3D SSP_INT_EN_TX | SSP_INT_EN_RX | SSP_INT_EN_ERROR; + writel(val, drv_data->base + SSP_INT_EN); + + return 1; /* We will call spi_finalize_current_transfer() */ +} + +static void +k1_spi_handle_err(struct spi_controller *host, struct spi_message *message) +{ + struct k1_spi_driver_data *drv_data =3D spi_controller_get_devdata(host); + + if (drv_data->dma_enabled) { + dmaengine_terminate_sync(host->dma_rx); + dmaengine_terminate_sync(host->dma_tx); + } +} + +static void k1_spi_write_word(struct k1_spi_driver_data *drv_data) +{ + struct spi_transfer *transfer =3D drv_data->transfer; + u32 bytes =3D drv_data->bytes; + u32 val; + + if (transfer->tx_buf) { + const void *buf; + + buf =3D transfer->tx_buf + (transfer->len - drv_data->tx_resid); + if (bytes =3D=3D 1) + val =3D *(u8 *)buf; + else if (bytes =3D=3D 2) + val =3D *(u16 *)buf; + else /* bytes =3D=3D 4 */ + val =3D *(u32 *)buf; + } else { + val =3D 0; /* Null writer; write 1, 2, or 4 zero bytes */ + } + /* Fill the next TX FIFO entry */ + writel(val, drv_data->base + SSP_DATAR); + + drv_data->tx_resid -=3D bytes; +} + +/* The last-read status value is provided; we know SSP_STATUS_TNF is set */ +static bool k1_spi_write(struct k1_spi_driver_data *drv_data, u32 val) +{ + unsigned int count; + + /* Get the number of open slots in the FIFO; zero means all */ + count =3D FIELD_GET(SSP_STATUS_TFL, val) ? : K1_SPI_FIFO_SIZE; + + /* + * Limit how much we try to send at a time, to reduce the + * chance the other side can overrun our RX FIFO. + */ + count =3D min3(count, K1_SPI_THRESH, drv_data->tx_resid); + do + k1_spi_write_word(drv_data); + while (--count); + + return !drv_data->tx_resid; +} + +static void k1_spi_read_word(struct k1_spi_driver_data *drv_data) +{ + struct spi_transfer *transfer =3D drv_data->transfer; + u32 bytes =3D drv_data->bytes; + u32 val; + + /* Consume the next RX FIFO entry */ + val =3D readl(drv_data->base + SSP_DATAR); + if (transfer->rx_buf) { + void *buf; + + buf =3D transfer->rx_buf + (transfer->len - drv_data->rx_resid); + + if (bytes =3D=3D 1) + *(u8 *)buf =3D val; + else if (bytes =3D=3D 2) + *(u16 *)buf =3D val; + else /* bytes =3D=3D 4 */ + *(u32 *)buf =3D val; + } /* Otherwise null reader: discard the data */ + + drv_data->rx_resid -=3D bytes; +} + +/* The last-read status value is provided; we know SSP_STATUS_RNE is set */ +static bool k1_spi_read(struct k1_spi_driver_data *drv_data, u32 val) +{ + do { + unsigned int count =3D FIELD_GET(SSP_STATUS_RFL, val) + 1; + + /* Only read what we need */ + count =3D min(count, drv_data->rx_resid); + do + k1_spi_read_word(drv_data); + while (--count); + + /* If there's no more to read, we're done */ + if (!drv_data->rx_resid) + return true; + + /* Check again in case more became available to read */ + val =3D readl(drv_data->base + SSP_STATUS); + if (val & SSP_STATUS_RNE) + writel(SSP_STATUS_RNE, drv_data->base + SSP_STATUS); + else + return false; + } while (true); +} + +static irqreturn_t k1_spi_ssp_isr(int irq, void *dev_id) +{ + struct k1_spi_driver_data *drv_data =3D dev_id; + u32 status; + u32 top_ctrl; + + /* Get status and clear pending interrupts */ + status =3D readl(drv_data->base + SSP_STATUS); + writel(status, drv_data->base + SSP_STATUS); + + /* If no actionable status bits are set, this is not our interrupt */ + if (!(status & (SSP_STATUS_ERROR | SSP_STATUS_TNF | SSP_STATUS_RNE))) + return IRQ_NONE; + + /* Check for any error conditions first */ + if (status & SSP_STATUS_ERROR) { + if (drv_data->transfer) + drv_data->transfer->error |=3D SPI_TRANS_FAIL_IO; + goto done; + } + + /* + * For SPI, bytes are transferred in both directions equally, and + * RX always follows TX. Start by writing if there is anything to + * write, then read. Once there's no more to read, we're done. + */ + if (drv_data->tx_resid && (status & SSP_STATUS_TNF)) { + /* If we finish writing, disable TX interrupts */ + if (k1_spi_write(drv_data, status)) + writel(SSP_INT_EN_RX | SSP_INT_EN_ERROR, + drv_data->base + SSP_INT_EN); + } + + /* We're not done unless we've read all that was requested */ + if (drv_data->rx_resid) { + /* Read more if the FIFO is not empty */ + if (status & SSP_STATUS_RNE) + if (k1_spi_read(drv_data, status)) + goto done; + + return IRQ_HANDLED; + } +done: + /* Disable the port */ + top_ctrl =3D readl(drv_data->base + SSP_TOP_CTRL); + top_ctrl &=3D ~TOP_SSE; + writel(top_ctrl, drv_data->base + SSP_TOP_CTRL); + + /* Disable all interrupts */ + writel(0, drv_data->base + SSP_INT_EN); + + if (drv_data->transfer) { + drv_data->transfer =3D NULL; + spi_finalize_current_transfer(drv_data->host); + } + + return IRQ_HANDLED; +} + +static int +k1_spi_dma_setup(struct k1_spi_driver_data *drv_data, struct device *dev) +{ + struct spi_controller *host =3D drv_data->host; + struct dma_chan *chan; + + chan =3D dma_request_chan(dev, "tx"); + if (IS_ERR(chan)) + return PTR_ERR(chan); + host->dma_tx =3D chan; + + chan =3D dma_request_chan(dev, "rx"); + if (IS_ERR(chan)) { + dma_release_channel(host->dma_tx); + host->dma_tx =3D NULL; + return PTR_ERR(chan); + } + host->dma_rx =3D chan; + + drv_data->dma_enabled =3D true; + + return 0; +} + +static void k1_spi_dma_cleanup(struct device *dev, void *res) +{ + struct k1_spi_driver_data **ptr =3D res; + struct k1_spi_driver_data *drv_data =3D *ptr; + struct spi_controller *host =3D drv_data->host; + + if (!drv_data->dma_enabled) + return; + + drv_data->dma_enabled =3D false; + + dma_release_channel(host->dma_rx); + host->dma_rx =3D NULL; + dma_release_channel(host->dma_tx); + host->dma_tx =3D NULL; +} + +static int +devm_k1_spi_dma_setup(struct k1_spi_driver_data *drv_data, struct device *= dev) +{ + struct k1_spi_driver_data **ptr; + int ret; + + if (!IS_ENABLED(CONFIG_MMP_PDMA)) { + dev_info(dev, "DMA not available; using PIO\n"); + return 0; + } + + ptr =3D devres_alloc(k1_spi_dma_cleanup, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret =3D k1_spi_dma_setup(drv_data, dev); + if (ret) { + devres_free(ptr); + return ret; + } + + *ptr =3D drv_data; + devres_add(dev, ptr); + + return 0; +} + +static int k1_spi_probe(struct platform_device *pdev) +{ + struct k1_spi_driver_data *drv_data; + struct device *dev =3D &pdev->dev; + struct reset_control *reset; + struct spi_controller *host; + struct resource *iores; + struct clk *clk_bus; + int ret; + + host =3D devm_spi_alloc_host(dev, sizeof(*drv_data)); + if (!host) + return -ENOMEM; + drv_data =3D spi_controller_get_devdata(host); + drv_data->host =3D host; + platform_set_drvdata(pdev, drv_data); + + ret =3D devm_k1_spi_dma_setup(drv_data, dev); + if (ret =3D=3D -EPROBE_DEFER) + return ret; + if (ret) + dev_warn(dev, "DMA setup failed (%d), falling back to PIO\n", ret); + + drv_data->base =3D devm_platform_get_and_ioremap_resource(pdev, 0, + &iores); + if (IS_ERR(drv_data->base)) + return dev_err_probe(dev, PTR_ERR(drv_data->base), + "error mapping memory\n"); + drv_data->base_addr =3D iores->start; + + clk_bus =3D devm_clk_get_enabled(dev, "bus"); + if (IS_ERR(clk_bus)) + return dev_err_probe(dev, PTR_ERR(clk_bus), + "error getting/enabling bus clock\n"); + drv_data->bus_rate =3D clk_get_rate(clk_bus); + + drv_data->clk =3D devm_clk_get_enabled(dev, "core"); + if (IS_ERR(drv_data->clk)) + return dev_err_probe(dev, PTR_ERR(drv_data->clk), + "error getting/enabling core clock\n"); + + reset =3D devm_reset_control_get_exclusive_deasserted(dev, NULL); + if (IS_ERR(reset)) + return dev_err_probe(dev, PTR_ERR(reset), + "error getting/deasserting reset\n"); + + k1_spi_register_reset(drv_data, true); + + drv_data->irq =3D platform_get_irq(pdev, 0); + if (drv_data->irq < 0) + return dev_err_probe(dev, drv_data->irq, "error getting IRQ\n"); + + ret =3D devm_request_irq(dev, drv_data->irq, k1_spi_ssp_isr, + IRQF_SHARED, dev_name(dev), drv_data); + if (ret < 0) + return dev_err_probe(dev, ret, "error requesting IRQ\n"); + + /* Initialize the host structure, then register it */ + host->dev.of_node =3D dev_of_node(dev); + host->dev.parent =3D dev; + host->num_chipselect =3D 1; + if (drv_data->dma_enabled) + host->dma_alignment =3D K1_SPI_DMA_ALIGNMENT; + host->mode_bits =3D SPI_CPOL | SPI_CPHA | SPI_LOOP; + host->bits_per_word_mask =3D SPI_BPW_RANGE_MASK(4, 32); + host->min_speed_hz =3D K1_SPI_MIN_SPEED_HZ; + host->max_speed_hz =3D K1_SPI_MAX_SPEED_HZ; + host->flags =3D SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX; + host->max_dma_len =3D K1_SPI_MAX_DMA_LEN; + + host->setup =3D k1_spi_setup; + host->cleanup =3D k1_spi_cleanup; + host->can_dma =3D k1_spi_can_dma; + host->prepare_message =3D k1_spi_prepare_message; + host->set_cs =3D k1_spi_set_cs; + host->transfer_one =3D k1_spi_transfer_one; + host->handle_err =3D k1_spi_handle_err; + + ret =3D devm_spi_register_controller(dev, host); + if (ret) + dev_err(dev, "error registering controller\n"); + + return ret; +} + +static const struct of_device_id k1_spi_dt_ids[] =3D { + { .compatible =3D "spacemit,k1-spi", }, + {} +}; +MODULE_DEVICE_TABLE(of, k1_spi_dt_ids); + +static struct platform_driver k1_spi_driver =3D { + .probe =3D k1_spi_probe, + .driver =3D { + .name =3D "k1-spi", + .of_match_table =3D k1_spi_dt_ids, + }, +}; +module_platform_driver(k1_spi_driver); + +MODULE_DESCRIPTION("SpacemiT K1 SPI controller driver"); +MODULE_LICENSE("GPL"); --=20 2.43.0 From nobody Wed Jun 17 05:16:06 2026 Received: from mail-pl1-f171.google.com (mail-pl1-f171.google.com [209.85.214.171]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ACE8E3D3CE6 for ; Mon, 27 Apr 2026 14:02:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777298541; cv=none; b=hPO78YqpUIPeOn4DySTd+dogJQoo3KIpZOUc9+HwPRk3KJcimN+7nv0VASwAuV48W3e6ccN4drHoHUAq9KMdQYoHSeVqnRsIBvb6MBzts053puWQauDw4g9qH2MgR9hVAQtrYWVCy9T/YJJdDqrv9bduBQL1U7c/9Q/Zl+l1Pf0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777298541; c=relaxed/simple; bh=DL7WXDFY1O5eBh2tmSI3Zsd/h0SrUvqrCzd486m+vE8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=KAHSpSVoY3bj0UvJnKgWBr42dgdaYhkbEVrfeR0o7hhzLozAGdEfm4J/d4VeuQhHFKypcKCe1hQkwZGZQzO4VyKDEjZe/cGhH0G9seNd7VxcSSY+KzKoTEwEwaHOVH0n6Q21ZPedVwQ3oGM/e95aReEUlp9H7Ygfcv3mr80e5z4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=riscstar.com; spf=pass smtp.mailfrom=riscstar.com; dkim=pass (2048-bit key) header.d=riscstar-com.20251104.gappssmtp.com header.i=@riscstar-com.20251104.gappssmtp.com header.b=GKkemFHd; arc=none smtp.client-ip=209.85.214.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=riscstar.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=riscstar.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=riscstar-com.20251104.gappssmtp.com header.i=@riscstar-com.20251104.gappssmtp.com header.b="GKkemFHd" Received: by mail-pl1-f171.google.com with SMTP id d9443c01a7336-2b7d3ecc10dso22312255ad.2 for ; Mon, 27 Apr 2026 07:02:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=riscstar-com.20251104.gappssmtp.com; s=20251104; t=1777298540; x=1777903340; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=4Zn1VHf2vGq/Rgp6tmVMmYAtaKJKXMEkXpNhjeJ8VMM=; b=GKkemFHd+RZyEWGbir64WPIi7wKWUu2IoBYL7safr3T1j34Ps51hF/6GKW7IxuQRlj uN4IUh/UzSHgCQIPkEcjk0SOyvVcFJb6VAyPgGMb5T7rE4ENHsLGisIajoxS50uUvDie /oZ0NvcCvPO63bmT7BmsByPuT6L6l3B7ZsptJObuO+Bc618+qelaWSYqe2MNpK3IMlRq rq2Tg+NM2EzTCTyCNeSoHrjYMA/0faSPydcQKpcFPuAfFEcyv5Aqm0zgy8d111pQXYR9 NOaS1U+HysVCJm2w9br3lLYhB8qrW+OJKhZiciypfDyGsG2leWlAoPC5qaZ29HWX3kaK za0g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777298540; x=1777903340; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=4Zn1VHf2vGq/Rgp6tmVMmYAtaKJKXMEkXpNhjeJ8VMM=; b=G7ju4gEey0f0cGiUHq6qxPBPTXXJ4XCrh/KBEnw3Q5ulYHmbOPh/Jpms+cyzhNlDbC wp/MvlircP0mxNqJ7SqZrGRYPYbjX3ffZYBfmdbX3GM0tgBVrPOy/+CmHOz7s9h5nJgx j5ue6HNgaPAUz37uU0zBiDhgahoqbjwntxXK2SVj+xNZpwppRQokkUn6VaKanu0aMokz 9OaMom28xkfSpXWwe+bCjOXH80ND7/xlyyiQ8uFQCsHTcVZ7b3AsLq1ZKI8BGjmKgGXh gZKaWeQi29Lbqan46Lo2iB7f4byyD5IkIZyPY/hvHNjZS5UEen01FYn1Ug6Xq0Fjgbhk fEGQ== X-Forwarded-Encrypted: i=1; AFNElJ/ACc0rt0m+P65sNDeG5H79y2c4TjeW1hYENl87l2EDbkpU7p2U1AVuIxpMoiyjwMXceJ4zHbEV13GASkY=@vger.kernel.org X-Gm-Message-State: AOJu0Yz4sqoNxATlywAFJyUxXGHMEZzoyHtmgrapl8EY2IUNg0IhgYGE O0Qzto2EM25MRXS96JB6NAT4pFivhZ5w6j82BZRC2kllEVqL8nqAAaK3aN11+KAJw+o= X-Gm-Gg: AeBDies8AeKb/glpvY9T5WeRG3HyeHPCIyN4h6UDMmwaYaFAe/uIXwX8x09BF9TctXT coEiPULuT9LHYYDKKdNfTmQiQiyANaFgbR9X0FE8hSRqZY8ceSZtoq7xeFI26SOALhAA1pi7yRL J1HCDgTCgLNHgkfx+mGsrXfUtz8lEYSFQQVa2183okkF0AVB8LF9+OyRVC95Hfw09wRCv3ru8pB XeKBLVmba+gbnqCdaCgO/PMzTP4w9DLc5jQoLKfEJHflUwOK5PoCqXay3ONIgoiUHsO2Oof+Zbw EFzl1Dht8k09cKylgeSPLPpqz3fIbzUcvHY+QNFCznkGmQckeEvE2TP7QUZNTgBmYvUK8LHKYEm hyz/IL63aWUgkTudZH7RjZMONL4+mMUDxXEAjkCsQmGTXJzjpGyfQpBJx9EtP+wWjbrUecOjNWO 0YDwbqsEorypaqlAWGP3SN2icrwOhIJw+NxSLoGu16JyTQjpIUrEABcg== X-Received: by 2002:a17:903:3db3:b0:2b2:58a2:c8a with SMTP id d9443c01a7336-2b5f9d68827mr300145985ad.0.1777298539927; Mon, 27 Apr 2026 07:02:19 -0700 (PDT) Received: from [127.0.1.1] ([2a12:a305:4::3016]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2b5fa9ff98csm307143625ad.3.2026.04.27.07.02.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 27 Apr 2026 07:02:19 -0700 (PDT) From: Guodong Xu Date: Mon, 27 Apr 2026 22:01:29 -0400 Subject: [PATCH v9 3/3] riscv: dts: spacemit: define a SPI controller node Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260427-spi-spacemit-k1-v9-3-ff753b551302@riscstar.com> References: <20260427-spi-spacemit-k1-v9-0-ff753b551302@riscstar.com> In-Reply-To: <20260427-spi-spacemit-k1-v9-0-ff753b551302@riscstar.com> To: Mark Brown , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Yixun Lan , Alex Elder , Philipp Zabel , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti Cc: linux-spi@vger.kernel.org, devicetree@vger.kernel.org, linux-riscv@lists.infradead.org, spacemit@lists.linux.dev, linux-kernel@vger.kernel.org, Guodong Xu , Alex Elder , Yixun Lan X-Mailer: b4 0.15.1 From: Alex Elder Define a node for the fourth SoC SPI controller (number 3) on the SpacemiT K1 SoC. Enable it on the Banana Pi BPI-F3 board, which exposes this feature via its GPIO block: GPIO PIN 19: MOSI GPIO PIN 21: MISO GPIO PIN 23: SCLK GPIO PIN 24: SS (inverted) Define pincontrol configurations for the pins as used on that board. (This was tested using a GigaDevice GD25Q64E SPI NOR chip.) Reviewed-by: Yixun Lan Signed-off-by: Alex Elder Signed-off-by: Guodong Xu --- arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts | 7 +++++++ arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi | 20 ++++++++++++++++++++ arch/riscv/boot/dts/spacemit/k1.dtsi | 15 +++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts b/arch/riscv/b= oot/dts/spacemit/k1-bananapi-f3.dts index 5790d927b93db..9429189354d6a 100644 --- a/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts +++ b/arch/riscv/boot/dts/spacemit/k1-bananapi-f3.dts @@ -14,6 +14,7 @@ aliases { ethernet0 =3D ð0; ethernet1 =3D ð1; serial0 =3D &uart0; + spi3 =3D &spi3; i2c2 =3D &i2c2; i2c8 =3D &i2c8; }; @@ -335,6 +336,12 @@ &pcie2 { status =3D "okay"; }; =20 +&spi3 { + pinctrl-0 =3D <&ssp3_0_cfg>; + pinctrl-names =3D "default"; + status =3D "okay"; +}; + &uart0 { pinctrl-names =3D "default"; pinctrl-0 =3D <&uart0_2_cfg>; diff --git a/arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi b/arch/riscv/boot= /dts/spacemit/k1-pinctrl.dtsi index b13dcb10f4d66..34d88334e95e4 100644 --- a/arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi +++ b/arch/riscv/boot/dts/spacemit/k1-pinctrl.dtsi @@ -570,4 +570,24 @@ pwm14-1-pins { drive-strength =3D <32>; }; }; + + ssp3_0_cfg: ssp3-0-cfg { + ssp3-0-pins { + pinmux =3D , /* SCLK */ + , /* MOSI */ + ; /* MISO */ + + bias-disable; + drive-strength =3D <19>; + power-source =3D <3300>; + }; + + ssp3-0-frm-pins { + pinmux =3D ; /* FRM (frame) */ + + bias-pull-up =3D <0>; + drive-strength =3D <19>; + power-source =3D <3300>; + }; + }; }; diff --git a/arch/riscv/boot/dts/spacemit/k1.dtsi b/arch/riscv/boot/dts/spa= cemit/k1.dtsi index f0bad6855c970..f8747190d2e12 100644 --- a/arch/riscv/boot/dts/spacemit/k1.dtsi +++ b/arch/riscv/boot/dts/spacemit/k1.dtsi @@ -983,6 +983,21 @@ qspi: spi@d420c000 { status =3D "disabled"; }; =20 + spi3: spi@d401c000 { + compatible =3D "spacemit,k1-spi"; + reg =3D <0x0 0xd401c000 0x0 0x30>; + #address-cells =3D <1>; + #size-cells =3D <0>; + clocks =3D <&syscon_apbc CLK_SSP3>, + <&syscon_apbc CLK_SSP3_BUS>; + clock-names =3D "core", "bus"; + resets =3D <&syscon_apbc RESET_SSP3>; + interrupts =3D <55>; + dmas =3D <&pdma 20>, <&pdma 19>; + dma-names =3D "rx", "tx"; + status =3D "disabled"; + }; + /* sec_uart1: 0xf0612000, not available from Linux */ }; =20 --=20 2.43.0