From nobody Mon Jun 15 15:12:05 2026 Received: from mail-pf1-f173.google.com (mail-pf1-f173.google.com [209.85.210.173]) (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 A463E22578D for ; Fri, 10 Apr 2026 15:05:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775833503; cv=none; b=OxTUIah4GLTrZ+r8mYxiPoGDZqB1OyoTCyonEKk/sOkk7z47FfitousWpalY0PNHXQjjz5Jf1eMX7sc0MUOXHkfFd9NEjroQJU7Sn4Qh6khPA+r3guS1/Da5FtuXR90lAW6ed3WBkwH0XfNGhZuyXceVQ3peyH06vPmpaAx9sQo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775833503; c=relaxed/simple; bh=eheJmdgPgeqGeik3qq47b8UOd+nLsPopHTkXCW3k7Og=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=B9yz5YXxiwXt0cLeVPRduEgBF2ETWEvOj5YcAhM5bE7YE9R7U1ILmq3RGgc+qtnEk/UhYeJDotRhsl3FZJ9vJGifeGjKFFb6Qnn/9qWJ3BO7ZHY9N4ZRFEDMALgBryvafMafW/qtiiNoBPR3QiTl1PXAVCNQoH6a92KjaxrtMso= 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=hOdpwkyv; arc=none smtp.client-ip=209.85.210.173 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="hOdpwkyv" Received: by mail-pf1-f173.google.com with SMTP id d2e1a72fcca58-82f1bfc9b8fso84005b3a.1 for ; Fri, 10 Apr 2026 08:05:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=riscstar-com.20251104.gappssmtp.com; s=20251104; t=1775833502; x=1776438302; 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=OGwxGNzPNhl8p1KQjYaA0p0dmTpV8/UPLRrlCuE9w44=; b=hOdpwkyvcmleKgCmxEJkkymEoytEuMuUwIt+HTxyvWLgbX5QhYBC9IS9Tm4VbAzV66 ONqfT+umnghPWaB3yDs1GwLIUux5ctg/7qi/hWeQezxBJ86IsdQbcdCcMTe9ltSBnQQg mPXtTL4UgUaH5L0jkVh/XUygex2GhxOp5c3pVDTlsYZrUqPgy3FY1plapG6G08E0unZA OSh8Gqo0HRRPwuhoGC1PKWToTHoFiC4BLhEEnrLJGiic47R9eg0iMGka35IdVcv4/UyJ l/Fwpdp5xLu8tXTRhkMHyU2QUmNdrtqC9se3i/FdPKtdBRlNxPkUQQeKpQj+d6MHvRuP Uvbw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775833502; x=1776438302; 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=OGwxGNzPNhl8p1KQjYaA0p0dmTpV8/UPLRrlCuE9w44=; b=CS3rQd8/5lRJn8y4JWRwgGB7hrcI+naqdJtosVhrZmmXxSpWKIX4/gw5QvPbrmtdZW PqJ/P+9+EW630xhnqvCRZWMnPz4mrCQjdwZ3r8s/PbNonKP+OPKlkxSxmal8SRXoAl+z es0O1eglPnukXOPZuL+msH717z+tcwK/8O2A4YeIcvDADFf8338TyhdDpp1F+gLYhTex RmYindxK3f29jM3VP7hzmjTyYJF0D2GZf9OdRdsgqJySfnboJONhHA3yV7Ccm93+Y2ub fWnS+HWdtYDyYt2pGtrLVejsjwO4afhQeKgmyIyynNQRzDufTnAy3Z1P7W1TZGe08/Yq 1jaA== X-Forwarded-Encrypted: i=1; AJvYcCW6zUwhixEowmsbNfIh8CcC8IglGNrvdJ85q5btv+s5/D3BYAPqlnmwk0iza02bDFOb8zgYzbbDupBqfQA=@vger.kernel.org X-Gm-Message-State: AOJu0YxF+EiShZv5f84WFoEofNxxGyzm5SKUbsa0IYaNEwwFMxcuCXfT 0o6Fk8+0BUR0+zi3nww/zKY5kOEbG+WkyX0ZFWfpwU0YqgayMeOgH+Nuu+N8k9n+hdc= X-Gm-Gg: AeBDietIG26hdNJ7zPigw84eSJp2qqZHpDLTWqVVs/QpAKT/BGVglX9xbtNyHf2kWAw TC6DebQZBxUUkVc9V2atJIxQ+iIqH30Wi3hrpSpSFfvrwxMQHDPIG+wwViOpw4tAP5Eu4zF0/eG xAvt/yM8kOlZV6NiHbpFVIcUv3fFwKo7cm9/On2IXSs3cKIXjTfiy8IC89ObXgel8ga1fForXoS f+GmB5zJA/tOE4529dnOiGlTzt/SW9d+U8OsJKcBxg+8dKV21mZMeIpU2AeXO3z9tiLh0uYRDZW hfkVj2viObB2pVO4x2KSo+PNtSs7QamI0n4AHCMWnhjpdXBAxCKeqkEtKLVr3XjqT8W0ByCxRDJ iPprNbBCWB1L1WuZ2HZspUOC98xbwe5bL1gAt1umIw/NKkow+A0jhVqMXnXCSarzH/+YngbfiAJ zEUg5os1Wwf28ghDrLKJZvl8D7XQckFsYXtvy4EIKltbfGBms3ACt7jOeNleLBkN0A X-Received: by 2002:a05:6a00:808c:b0:829:8942:2ca4 with SMTP id d2e1a72fcca58-82f0c281379mr3779253b3a.19.1775833501946; Fri, 10 Apr 2026 08:05:01 -0700 (PDT) Received: from [127.0.1.1] ([45.8.220.15]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-82f0c4d5413sm2970532b3a.40.2026.04.10.08.04.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Apr 2026 08:05:01 -0700 (PDT) From: Guodong Xu Date: Fri, 10 Apr 2026 23:04:20 -0400 Subject: [PATCH v8 1/3] dt-bindings: spi: 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: <20260410-spi-spacemit-k1-v8-1-53ebb48a4146@riscstar.com> References: <20260410-spi-spacemit-k1-v8-0-53ebb48a4146@riscstar.com> In-Reply-To: <20260410-spi-spacemit-k1-v8-0-53ebb48a4146@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 --- .../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 Mon Jun 15 15:12:05 2026 Received: from mail-pf1-f182.google.com (mail-pf1-f182.google.com [209.85.210.182]) (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 17A083D646D for ; Fri, 10 Apr 2026 15:05:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775833515; cv=none; b=Pmyi1EqlVqW8dILBQMzh80AHDnnYtsWgyKzkJUEiwCL7IX4PnEyoO5qPm+sduGPHG/5m8vRBEbwN6Ta22YTi8a5Etr7OqAewmSBcWRlEZzw3CFTOynQImWbNaEv7pJYBvEqZ3expLj/O2Tg7kOzHC7MnOa9v0FaSSE9Pc210B7c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775833515; c=relaxed/simple; bh=WihQo63zpBsokLBjls6ItPwMzscMAyPMpl1pOun5PDw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=tRqD/HAtn+vM7XZ5rfMwgelO5KNpQb7oFVA7LQpZbF/70c6KBvO7CrltC2IYIAYjXR5I2j//T4c5I52q+c49ELTzPx6je4ljyoUoZ4WkFDSJc1Z+7N+k8IQWCwI/lxea21mATxAG+X6VKee00LGfQ2/zJirUEk9CRN58r3jqnR0= 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=xDxlOIt0; arc=none smtp.client-ip=209.85.210.182 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="xDxlOIt0" Received: by mail-pf1-f182.google.com with SMTP id d2e1a72fcca58-82f138a6d5aso284950b3a.0 for ; Fri, 10 Apr 2026 08:05:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=riscstar-com.20251104.gappssmtp.com; s=20251104; t=1775833512; x=1776438312; 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=QUgQxnA32hZgJleNQEMD+FEy3TYUIXs1W0BHwR0D0Zg=; b=xDxlOIt0GuSPapN68iMxwS5jjucqescu5Rz5mM0MR/FLIjSGLbl56+K7LyfTZiBcx7 18FaAWFHl6bIANvKBgS84X/sZyR/FWIAsxFSQ4obhQyx5KAJ379TwD4pkbsAgag8TCi+ gMLvVyaRl69SRbkotXl6zMpIvEmQhTVywB/9Yr9SngwwfdQ83vMq1FxNdGsU96IL7FRq BlwuZh4MfM3DHXEdmcgrMhHNEH3Jhb3wkoQhtUrrWb4F0jrmOiTVLxkiYm/D7y5wntgc Qv/evk6/32SfTd2mg1bBToulIUnJT2wXcNnOY6XHFRZetnruriIVYX0DEf8IX9rSk2lM p12Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775833512; x=1776438312; 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=QUgQxnA32hZgJleNQEMD+FEy3TYUIXs1W0BHwR0D0Zg=; b=KUteTdK7kzUjt6rcugI7bzeawBwRlzAbcO1kawKNN+7gDjt6hQhFJyY/TgFUt7yNei 3tXJCfey6EhfyH4hfKZxQ7MaYtt3szH2Am/IsILqeBvF+QIcBVID2ZFPkdMCyv9+jE/8 qkyPJW0jvI0C7qO7aYcuFe2W6zaCvs2F5bIQCRerG0kQlFtcHgaPeJKFecMCH1/yVC9L 4CT5xkXzWnmjHv8iqxfNTg9tMapp/STivoCA2NVRy4EiLQ0d2vcaohf/lifSUVF1/yoU 54rR7LRq1JsnL5jspjRs/yNega8fPSUnSHjg7i5gLHWSSJJGfJJjbqnfyIR//K4213KA 3xhg== X-Forwarded-Encrypted: i=1; AJvYcCVb8I1lOqZTEF6dCNmHLRqKKsx9Ho+NACght3rwIZjxBW0sp3OGuKv4l+OG/0pQmssVkOniouEnePa0OIY=@vger.kernel.org X-Gm-Message-State: AOJu0YyzptsEWa9a4bwC0sMmecDZOU4Ptnv1Q/qnoxyK/YHRC5jwaEV1 mY7jugl6jqUEXCOjyhVFXBRLjyv9ETiMVN3AGe6CeMc/n83oyf7f4qxdRHwOy6fJnUs= X-Gm-Gg: AeBDiev/JtbxfwKJcq6XrtBE6vy/CISVpqEf42Kr9Gef0dHqtzsn7thGSgYUEezz9su +a5NG1KHJhNGo09UOE/J8RYE2dbvYtOUt3llojp8UVuRy6t2JUsPPerkZ4Z1edi9i4L7vZIWyf7 ToP9LMu7tKLvPY/u5kB9hr59Iuf0SPMD0cxMwECEUHxcJVT+0JCOrGQYkBCElJ0xTISvn0JfKix MHbHRlceN84YUP8JMaf2pq3gkrWYDV4ZPXe9vcDrWdRM1H/UICujclmbMmQuX2GzN08rOoExr2E YWmqDSjI1z3e3uafsO7zMqHN/rkQacyt3DflAbI80dFsy3pTS2DShLNQAqwn9eANoNfKlliipx3 DqMq9X8IPZ/zqCCm3T0+3SQdzRtJr8Z20QQcsmiJO44AQdQBkOROQLErfPJD+aZEFpPMOeT7lBW P6PAW8dh5ptx1mewMAFzCcmBQ9C4TxJvsQ/3OkHmLL6WwLuabFQjJrPrLd7+r5Da9xb7HYbGQm9 7w= X-Received: by 2002:a05:6a00:2918:b0:82a:17b8:1474 with SMTP id d2e1a72fcca58-82f0c26b5d2mr4097270b3a.1.1775833512325; Fri, 10 Apr 2026 08:05:12 -0700 (PDT) Received: from [127.0.1.1] ([45.8.220.15]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-82f0c4d5413sm2970532b3a.40.2026.04.10.08.05.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Apr 2026 08:05:11 -0700 (PDT) From: Guodong Xu Date: Fri, 10 Apr 2026 23:04:21 -0400 Subject: [PATCH v8 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: <20260410-spi-spacemit-k1-v8-2-53ebb48a4146@riscstar.com> References: <20260410-spi-spacemit-k1-v8-0-53ebb48a4146@riscstar.com> In-Reply-To: <20260410-spi-spacemit-k1-v8-0-53ebb48a4146@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 --- v8: Addressing Mark Brown's v7 review: - Use C++ style (//) comments for the entire file header - Remove all open-coded DMA mapping; rely on the SPI core to handle DMA mapping via transfer->tx_sg/rx_sg - Implement can_dma() callback, replacing open-coded transfer length checks - Implement set_cs() callback for chip select control via the TOP_HOLD_FRAME_LOW register bit - Switch from transfer_one_message() to transfer_one(), letting the SPI core handle message-level flow control - DMA completion now calls spi_finalize_current_transfer() directly instead of using a completion - Add SSP_STATUS_BCE (bit count error) to error detection - Interrupt handler returns IRQ_NONE early if no transfer is active, before acknowledging interrupts - Update copyright year to 2026 --- drivers/spi/Kconfig | 9 + drivers/spi/Makefile | 1 + drivers/spi/spi-spacemit-k1.c | 782 ++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 792 insertions(+) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index c3b2f02f5912e..b50d9ae1a498b 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..8cef633144954 --- /dev/null +++ b/drivers/spi/spi-spacemit-k1.c @@ -0,0 +1,782 @@ +// 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; + + width =3D drv_data->bytes =3D=3D 1 ? DMA_SLAVE_BUSWIDTH_1_BYTE : + drv_data->bytes =3D=3D 2 ? DMA_SLAVE_BUSWIDTH_2_BYTES : + /* bytes =3D=3D 4 */ DMA_SLAVE_BUSWIDTH_4_BYTES; + + 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 val; + + /* Return immediately if we're not expecting any interrupts */ + if (!drv_data->transfer) + return IRQ_NONE; + + /* Get status and clear pending interrupts; all are handled below */ + val =3D readl(drv_data->base + SSP_STATUS); + writel(val, drv_data->base + SSP_STATUS); + + /* Check for any error conditions first */ + if (val & SSP_STATUS_ERROR) { + 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 && (val & SSP_STATUS_TNF)) { + /* If we finish writing, disable TX interrupts */ + if (k1_spi_write(drv_data, val)) { + val =3D SSP_INT_EN_RX | SSP_INT_EN_ERROR; + writel(val, 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 there FIFO is not empty */ + if (val & SSP_STATUS_RNE) + if (k1_spi_read(drv_data, val)) + goto done; + + return IRQ_HANDLED; + } +done: + /* Disable the port */ + val =3D readl(drv_data->base + SSP_TOP_CTRL); + val &=3D ~TOP_SSE; + writel(val, drv_data->base + SSP_TOP_CTRL); + + /* Disable all interrupts */ + writel(0, drv_data->base + SSP_INT_EN); + + 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 Mon Jun 15 15:12:05 2026 Received: from mail-pg1-f180.google.com (mail-pg1-f180.google.com [209.85.215.180]) (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 44A6F3D75D4 for ; Fri, 10 Apr 2026 15:05:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775833524; cv=none; b=Gw6wARaKxvZgFKcs2CGQe/1tSrBa16bWvdSemYKBCfpfoeNFnrHABfKf443fE6w74sJzMPVGCyn139diX3kKji1qdb7KJGLHNBn7KgCkO7L+bA5UMXcVAMceHbJ7Vjm3BX8WwLSFSPvyptNGjCvGoHude+bJVUPRL9GweDCjruQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775833524; c=relaxed/simple; bh=kCQLWikKMQAZX52zP7mCbrq1YoM5AGwQQLQGtUCpDU4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=MvdKH/B2GVdmVsjHu2r5plNx7MMWBl76H/GQ2e2NWw3cWWTtWkz7ymGYvCsU8ElBWXHJwLlqoXd/QLYZyJRWg6thyQkKi8y8nehu1hEutBMdjqRH0SwyJuY/nQQ/L6M3MQ3V0BKZLQ0SIumK41sqO/PXtZJvw18jmGZzreYsRzo= 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=DkXScEmu; arc=none smtp.client-ip=209.85.215.180 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="DkXScEmu" Received: by mail-pg1-f180.google.com with SMTP id 41be03b00d2f7-c76eea1672aso797990a12.1 for ; Fri, 10 Apr 2026 08:05:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=riscstar-com.20251104.gappssmtp.com; s=20251104; t=1775833523; x=1776438323; 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=Yoe0HYlE0K0NbksazLkXaws1bR4DgN+t36khjDPYQVk=; b=DkXScEmujJTa0uFG0dkTlDW6rqHUN3pkTbgRWL2oalJg8aGaj4KTfTTUeE+x0pCgRR JH4RactEdl8YGoZfJP+D8lvjHrfW7gf4bSlDrdQx5znth1ctFhW2pvZhXPWKvvV/xSSs JZSE6ricCX5LELBOtZQJdPdsWh2pZe8w8ETosSDzy2N3qv/wnBPAg9xRUAEdi+JHMvlm g5ZemKHmA0BRhfIx6lOPEPHIPTS7mTXZ79bmNSnTYQJkVKIpa89Klvay2PiYExU/0VpO T+rZw2TKLsV14ynnF5hJAJcoyhJ3UV9JUUdNVsroV6zPaL7YQQhfKMh7XNWy4Q51Fwrk Y7Xg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775833523; x=1776438323; 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=Yoe0HYlE0K0NbksazLkXaws1bR4DgN+t36khjDPYQVk=; b=C13OZ/xb/MR0bADT4KnsRSS+NWvasseh2oeElIqMb1PFFIPbdPsJa9Kln/EOD7DO0O UxdrVcQNT3N/KlaVOeZGiOy6bebPQAomRO+tPZyx9Onx8nYuoiCC3eWt+5A/H9P0Vh6m Vfup7m9SR62n929CrztQLeDT5qNtRwPCHeV+bnIYtwEsw1ohbsFYjkQG74Hjn8BhNahc QnIbemTEGZJjKWz1R3uLgZeWUknljvvPDvRxgrO4X7Q37I8a7V+XqB9/8VtumDy6MCno WnXSFa8pj7OAzX/xRdN9+kNo5XhXpqdp0bDzJMocXN397diZKQpcTc1giSUOS64VRn+i aIkg== X-Forwarded-Encrypted: i=1; AJvYcCUdCIFkgbZkSsxMOmasf99JRokfPbvnTkbw44xc4AlJyOwf826ZuJE+I81iXEjDuDS8hrZJqX6WZxValZI=@vger.kernel.org X-Gm-Message-State: AOJu0YwUz/gVTtJYD2Ln1yCGzl7jlW6SGRw0K1F29YUesfz8kA+OhRzM ANiGXTti0/nLbBw2fE9MsOPhGYOVq4tCrEZ9f2kw2gq2tQn74LE9NPI8J82r9Dy7qDI= X-Gm-Gg: AeBDiesQvpcxenoZBPDJN73WDuzeEOcJS63ljcgvazGSp0TVyKfsIxCzfejYMPc8/2k bO8dOMVOEXqS4hJGOsFip0v0YLI6rqV7fL72ftIhg/F5L5+DDVLTqC+jJS2EYWym5meHhacL/YP /O4RYOV6yWDiosE6ep2zIHkEAY+ocmNayV6KGWME0fD8ci6WIyRA0LhRssOuCCgwTAMq2IoTM3i hFqaxzQ8YvcQoggtpjZXqCSgl8liIMTGwq97DE1D8fP2CiXUh8ukKWOTTAzXvEy1WC0B6gDvWUO Ty0ysvTAyqYGWVVSyo+aMAbo3DKdpMiM6NVT2axIXfiiSO2i+CWRIQl7aZDKVSdTbD4Z6hPSJmv qo6R9ALScmNasOKqgJfqWE99dESsoHq4OofMOFFFuLOu4Z9l+LkU30l+huOmUSVNDuiaSvA8LNR sGpzYYfptrvzaUPTyPBvpMM37DYdG7FvF79gJDQe5cigL9NW+02S8BANj8AhWoM3lS X-Received: by 2002:a05:6300:210c:b0:39f:3559:7516 with SMTP id adf61e73a8af0-39fe400f81fmr3967582637.48.1775833522543; Fri, 10 Apr 2026 08:05:22 -0700 (PDT) Received: from [127.0.1.1] ([45.8.220.15]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-82f0c4d5413sm2970532b3a.40.2026.04.10.08.05.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Apr 2026 08:05:21 -0700 (PDT) From: Guodong Xu Date: Fri, 10 Apr 2026 23:04:22 -0400 Subject: [PATCH v8 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: <20260410-spi-spacemit-k1-v8-3-53ebb48a4146@riscstar.com> References: <20260410-spi-spacemit-k1-v8-0-53ebb48a4146@riscstar.com> In-Reply-To: <20260410-spi-spacemit-k1-v8-0-53ebb48a4146@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 5971605754b35..61b93765f42cf 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; }; @@ -327,6 +328,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 529ec68e9c23e..1ecb09e58042f 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