From nobody Sun Jun 14 06:13:47 2026 Received: from mail-pj1-f51.google.com (mail-pj1-f51.google.com [209.85.216.51]) (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 44AE822A7F0 for ; Sat, 2 May 2026 13:31:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777728705; cv=none; b=LpXzY1e1vPCzjbyE2BcMol3lvdiU1QJbum6HlFaNJo8gV5KfpEbg2IXGQHRhbIGamePuA+FPNGu4xmDNePcEYuIC7S58vcGDADrwNU3t7T0DsNnN3/QTZFKcE/eRAKE9HKYMA28ZoY/xOWeXmwNcyEw7oS/yzK31h9IEPfdXqmI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777728705; c=relaxed/simple; bh=t/N7N/XSOQKrEzUs/D2pxm3VCD+rv1UW1/RKIJtP2xg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=O5oT+HbxWymz/MlbdzdhDvDH1Rd/LSRUN3JCf0iUrGoCCRxzFmkrN5MkH68VniqdQVrBNLB8xR6SQRUpSKay+AvPaHl0/2GiGQnevE3uTpzy/ImDx5eaoREf+W21gCTsZd+WSGuvikKP5IWG86DMC3zMol7p50IO/Rlrl7uxgaY= 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=pawa4wko; arc=none smtp.client-ip=209.85.216.51 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="pawa4wko" Received: by mail-pj1-f51.google.com with SMTP id 98e67ed59e1d1-35fb16e56efso1830913a91.2 for ; Sat, 02 May 2026 06:31:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=riscstar-com.20251104.gappssmtp.com; s=20251104; t=1777728703; x=1778333503; 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=nc44VxR2FzsNOtDOyeTwEzMXfi//+xA2TCBmgHk8I1I=; b=pawa4wkoyH7T4yuRnXttG9Sfb6UNHdBH3nDmeuYOgYnGynkbSOsi5WBEpPGlhlqbzs trlJNsGIWDzraEZoxHpWZkXFomQGKNsDA7Ihu9EOu5m2+ABKrTQG1IlcmGuMz+GvFoSS ATCDjED9QRs0i8E+80ps6V1XcdmBhOUXE1/SZxFCvi+iJNkZR88gqVWXecyzYvyN+92d WhWf2ICSbXT54dPVCiR4Z6m+70vKVtNb79QSi731EV2yk+w3gm//0P+NVZGvKy18XLby ZK4oARnbSw25hFy2EscUK27F5i00fx3sH42mNO5d3BCOh8wPfENQkRiARnLkttvIaxDT gJ8A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777728703; x=1778333503; 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=nc44VxR2FzsNOtDOyeTwEzMXfi//+xA2TCBmgHk8I1I=; b=Nc55CpvI4OGdEeRM0EC7bxPHSgXs0nybgj0CouuEF0FPlGYhNP+g5/T+sjgBC1FfSi DY2WU838ZAUjdVMjjwD+Zt7eLuj7Wp2T7EyUrKHRcnXMuFq/rHXVlDGYwLPQCzVhcWbJ Icg3W8XZGsw2EZ1gYOV+ocvXAnxyTinMwXXtXSzCb6wbAJBgVAfYGDcYKQZwWX6JybUu PXAU/CGjkXmaxfJBMv3JHCVjPhDDRhBkE8taTafDLghd6peEdP6sZnxC8OhtLOOsspaF FOnfoSSi1nlWadwdbhdzxpFohvawUufU/oH9IWiB9kJs1fnnpfZdQDrIgEBgCsqytJIc Qm2Q== X-Forwarded-Encrypted: i=1; AFNElJ8GkjgW9Bh9tPvZYKpGRGFNwQ3t7NddTFQYkK7HRxf35d7FdrrApWcOL2V/8U7zRAnWeknuWkf/YtByuJo=@vger.kernel.org X-Gm-Message-State: AOJu0YwaG9reJisSFbXUtqia4rJCuP3HvYAXq+8H4M0MeNVDXXQ9K/nb fOTZOP0V9+TxIBVz4itWk5Hnr/Wlx9Z4XPewUlamed/N4me/OU0nchOOmjxn9KLYVFiSMGkRF2g omo/DiRIdMQ== X-Gm-Gg: AeBDietI42MRPTZRS5AWQYy5LrqmvulgQUwUvsnAR6aB6ckcoqG+DXVsSQkcEGH1+UY 23MgR3L5pb5FvCaI6/z+REPZIC9t5PGu9tHPP1CVnMT/nS9BKxiSkP+Za41nmR6vxDZDE+4fLyu q19tZUtkawRAsnd60pKIaNp7eHdpfoWIiPizpUGh9oCKEcK6zbgyfQfJ8fL8fDmyDOH08CngPaz h7qvCqxjWcwRnEsTV4YWp6MbAUZj0g7HBbS+nxcdWNs8WBFB5ngyWOCJsyQRUzbSgmnS7Dtifwt vhqPvg6w2onbWUm5lSmxHFZxp1m5xevbs+wEvI15nf4izhHLLXQVVrfPnkEeEmOTzGHtm4cXUQF O/R4x8g7DRsxiU48yaGef2q3xDD/PV58wZ3vdoYGyDZjH8ZOKvlFQihbpgl7OPaLNmTSHjuBCDj STgVgG14WGnlKgM9u9DOBEgLUUoZXlprOMUBOdK+1TBcOz3tMJXqYmSg== X-Received: by 2002:a17:90b:530b:b0:35d:a3b4:2f00 with SMTP id 98e67ed59e1d1-3650cdd0ab3mr3593006a91.8.1777728702492; Sat, 02 May 2026 06:31:42 -0700 (PDT) Received: from [127.0.1.1] ([2a12:a305:4::3016]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-364ec02ab2fsm5647937a91.14.2026.05.02.06.31.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 02 May 2026 06:31:42 -0700 (PDT) From: Guodong Xu Date: Sat, 02 May 2026 21:30:51 -0400 Subject: [PATCH v10 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: <20260502-spi-spacemit-k1-v10-1-f412e1ae8a34@riscstar.com> References: <20260502-spi-spacemit-k1-v10-0-f412e1ae8a34@riscstar.com> In-Reply-To: <20260502-spi-spacemit-k1-v10-0-f412e1ae8a34@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 --- v10: No change --- .../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 Sun Jun 14 06:13:47 2026 Received: from mail-pj1-f51.google.com (mail-pj1-f51.google.com [209.85.216.51]) (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 6855A22A7F0 for ; Sat, 2 May 2026 13:31:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777728712; cv=none; b=eux5s0nNMi+J/maUgw1E67Bz5mhRb5F/jc5q2b1FujGkN/NRQ93iZnb/UZnCPpc7yA9MD92rwAz/lBV/xpWycGf+3QG7KUZJpzCrMrDx1ngPP34icVliJT9u9aogIQIxQ7J9J23khFyWMdiY0MTVfgNDLKlh2pTttchhC/GZiVA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777728712; c=relaxed/simple; bh=AMMg8F7ANMyLVd1AzwX+yMXyhhJnF00G50L4O+/TKqw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=qV6v5YS1+gMWWD7+uXUSmtfW47/C9cXDsw0lsWlC1HEZXoNQ0Vz9KzXNoR/0X78CTxq6Z5Ns3V5QoI0mJN00fKoW5IKyl9uXDMaJggco5Jv3rHR4cdSPbpFBt7nzyhTEhpvXo+eKgrKyjPjKuZaFWKXnMqG/aSbvMSZwSKD4GJU= 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=ftyojVwh; arc=none smtp.client-ip=209.85.216.51 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="ftyojVwh" Received: by mail-pj1-f51.google.com with SMTP id 98e67ed59e1d1-3567e2b4159so2166116a91.0 for ; Sat, 02 May 2026 06:31:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=riscstar-com.20251104.gappssmtp.com; s=20251104; t=1777728710; x=1778333510; 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=+RVIkDmvyxAGGbfRweDHo0as/3b5dCSV5kLsT/RXhVI=; b=ftyojVwhFbXXsjvGxUyKq4H6hN+kwwE4gsPpv9xR3ahlWPg6m7SnZh5Zx8dKaa2umU zsk42CIkiUwkS9Jq+hvZnI7OT+g5TvvIc9arvyMBo0mu3ofyStOrphdiAhLH4L6ySuNS 25/czu40zfmo/Qd37igOqT3OBilFf72upxOruQnevSEoLs24dtTtRiY6wOOjgp2nWPBf sFKa65e+Oiz/kF0+OGyIdWIGq31jKxiVjPZEVgCxioFGLw/ZleRIGf6OpCaJHAvfDtCy ies0KqqFgjNJKuSC59yZKfhZKYstBr2Z7mx5S0ZW8ALO/L1Amj4LK9slK8XNKUSwiado YQOQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777728710; x=1778333510; 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=+RVIkDmvyxAGGbfRweDHo0as/3b5dCSV5kLsT/RXhVI=; b=McRIf56DtcbxcEkOMHNGUrsYK/U/B7AqtUH9bkiNIcbjWlXxEDjFgfiMZcjVXH0j55 W59XMnWXpMDkl2U0nu42yj17ktNNYqxqEUmeYVoUjqitpNNb08FeeFsnKMJ3LqE4/zbj clVX++4kEztBptptp30nPuQk39xYPwU7GTCEWPRQALcmluqY41qpYOVc1d5N/hH1rBSj 9VBgymoY2EoHCdmyC6mOpZoI1TrzbsZfevQdQsefc7RCqygTyKpzY+GgoNNts+NX0gOI 7YN93mV+aHy4xjcgY7Px0r4/BXU/sRixT+HRCj65XAKLtkVyvjt9IWpathsS/rMfez29 t53w== X-Forwarded-Encrypted: i=1; AFNElJ8PyCuZ22efr75xN7DSJQ0AaLm8KoICl5SnUsBRmeDie0X9AF7BLVuZSdbjGIGM2AmIU3NtIPUCO1DLG/4=@vger.kernel.org X-Gm-Message-State: AOJu0YzwdpZbjVLes+mh+e5hxOKuq0IbBUWOSSdXjjzAWEgL6KjTFi7P F8a5iV+qEVtRMY7RH0OdL363H42sWT2ci0HWxjCHDFiu7RTg0im84YH1z5uK2uTHspQ= X-Gm-Gg: AeBDietbTJkGeKB7/uINVtTR+IZVjlTxbpXYlcacbBVy0nuXxQ2TYjDBB/ZUG/GFXtc hGLHstuZyHxCZpUySkNc987mxjx/wZYpErW53XzzQLcpDjFMXW9hhXihFCJCUkXm+Uf60C+iwKp 1WbwcH410DbFyil3pYg3ri/gcUyuoSUrRKI3xbRGq8EelGSraey5pRxla/DW3CCTMCKdN01ocm+ LVoKcL/fs5EswBwKPMoVWJHme4sQUJQqN1gwBsY6Z5tkd8PfWd1YkYwP716mJPclY9x1mC29XPx PKfb9GO6QTsnE9xzXlHCnqCWPv1WnRfE1Kt7dhdHmZIOj19pwVxuiz5BZg6B8DwPGDEqdKTWQt4 XtowlLA79h9W438lvBomQwAnCA2WNQXbC+52xUumQT5btdXJ2NGR1lhKNu7yK29YtAVw4UpchMR SbMYiaSo65nGBYEM636w7JOp8Iu//aa/hLZeATM7CqpX6l221tTBIabw== X-Received: by 2002:a17:90b:17d0:b0:364:a6a4:596e with SMTP id 98e67ed59e1d1-3650ce642cbmr3062831a91.24.1777728709439; Sat, 02 May 2026 06:31:49 -0700 (PDT) Received: from [127.0.1.1] ([2a12:a305:4::3016]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-364ec02ab2fsm5647937a91.14.2026.05.02.06.31.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 02 May 2026 06:31:49 -0700 (PDT) From: Guodong Xu Date: Sat, 02 May 2026 21:30:52 -0400 Subject: [PATCH v10 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: <20260502-spi-spacemit-k1-v10-2-f412e1ae8a34@riscstar.com> References: <20260502-spi-spacemit-k1-v10-0-f412e1ae8a34@riscstar.com> In-Reply-To: <20260502-spi-spacemit-k1-v10-0-f412e1ae8a34@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 --- v10: Addressing Mark Brown's v9 review, fixes a buffer-offset + unsigned underflow on 16/32 bpw transfers (8 bpw worked because bytes =3D=3D 1= ). Changes in: - Initialise tx_resid/rx_resid to transfer->len (matching the "bytes left in transfer" struct comment and the byte-stride decrement in the per-word helpers) and divide by drv_data->bytes where the FIFO burst is capped in words. --- drivers/spi/Kconfig | 9 + drivers/spi/Makefile | 1 + drivers/spi/spi-spacemit-k1.c | 789 ++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 799 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..99db429db0b26 --- /dev/null +++ b/drivers/spi/spi-spacemit-k1.c @@ -0,0 +1,789 @@ +// 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 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; + } + + drv_data->rx_resid =3D transfer->len; + drv_data->tx_resid =3D transfer->len; + + 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 / drv_data->bytes= ); + 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 / drv_data->bytes); + 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 Sun Jun 14 06:13:47 2026 Received: from mail-pj1-f54.google.com (mail-pj1-f54.google.com [209.85.216.54]) (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 5136622F388 for ; Sat, 2 May 2026 13:31:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777728718; cv=none; b=TqmvQ183HY0SLyMuVL24mRmIeUn7b4+dwH1JeFt7jXxrnK6o8LdScDOzkB2dEfq7aFsmtcrJaOhZPACOkWNjxRkzSLWtpGqusw2H1BC+/7ch07TXfeX+1hFThrzPWUpoQJzLtYgeSq+AMLJDfzGIz9A8ojBA63VjS2vK25OCRpQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777728718; c=relaxed/simple; bh=mybpnbkUFkw7cm61GvY6X09yZBkBBXqM1ozOHvrnueg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=fR2f+sbZpm1aKmE+sj3x7JTlzsCNdJxkHYdzWTiDVKiW20PQsIP10YOFcLp8pKBhbDNLWYRJell0eqorVog5yIlV1yIN7e3jr191I8gtCtNOm1oF8iNmsy2lD9yCnauRHVqHPDYP1d3+J/LK39zg5rTXlQ+jOaR8NHfdP7C/9vs= 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=eIVEICBu; arc=none smtp.client-ip=209.85.216.54 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="eIVEICBu" Received: by mail-pj1-f54.google.com with SMTP id 98e67ed59e1d1-364eef1891dso934857a91.1 for ; Sat, 02 May 2026 06:31:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=riscstar-com.20251104.gappssmtp.com; s=20251104; t=1777728717; x=1778333517; 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=jVhcCTnPlX7VHPRMEWLneWc1gnjPxqjBSj0V35oqvLw=; b=eIVEICBuxvjSBVXdGqTIjX4pRzpD18LlOPAW93sFpR89tvO0INfjGTdHIoW8Z5w2Tg akWu3wKLQwWVQl8iF9iAXu+vlfec3cNsUm0/+wWTRafpijit2MDCmpwmmgflZB6/iLZ9 zQ7A171rLU4FKBZDBHw/qTR1Do0k/Vo/uxSyXN/OuqoqvK4+BQywu7OwgvWUf0u79atv 6UQA4y/GwQ9irXwW0g8ZGo65/RmmJ24M5hSNAhKRiTAtIX+NH5CDsZQGsQex5+N1qDSE 8gZGM/She+HJWkHAZot+F8BPf10bJcIn9SO1tiKFSuy3OKolCV+Slhy4poh3jfrLuPKX ck0Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777728717; x=1778333517; 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=jVhcCTnPlX7VHPRMEWLneWc1gnjPxqjBSj0V35oqvLw=; b=NIJAE9s7vLqRe9wN0rlhZLZTqiJZ+s1Jx16fp5hTkZA6psh2pnD4RILLcunvODtN4Q s3hBan2m+6EWLBJCqidyRiQaCUzNF9kkc6ka6g1ZGxqWcTmD6po3jX/N1AhgUAIhD9bV mnT1LRscW/B5wgmIFCiMSRnK4KB71UbvIvALGCHi55VGxlczTo9J22YrgwfgcpwdMSdq jwmW6rK9zqHoUMwqDg/zbfpGXlLVsfNFLuXoHRbSmWenS76vUZRu+tce6KJkEV5Mxyln 5697OSc4rH3tNUfRLDoxoKWCoD+SqOVEVe9s05+eRVMuTJcrjZP4r5wKvlRDmoJQw9v7 /EXA== X-Forwarded-Encrypted: i=1; AFNElJ9bHXqARuKoDLfie/crS/rmbAjg+TUwT9Lcpd6cykPTlsqNg+JPJO9Y6F5ZQWNTGKMg8BvStUM2upqrRvg=@vger.kernel.org X-Gm-Message-State: AOJu0YwkwCkiBCqF4KKjkQ6DLKTOF3+/Zq3TWQ5jQit1Zu4gyl8Y1VKi hYO5SFJqvq8F0KVNeoQRgW0s58yriDvybTLwxnV8+r5VPSY2OcnDRT+p677FIYYsXJU= X-Gm-Gg: AeBDievwXDfUe2AsZ67DcNXv1NjuvZXV1fsab1knNnmmWMLzeEGHMJH+S9QPvowwV2Q Xdi0JgmA0m5uqlXh0apHIyz3J0Wsbw45ifNcln/mMBJcDhC5i1LMlbtlDkx+fi9lHa2P21YdO76 At4Fg/VDpavv/UvIwxXZ4zpNJNhzvuTOjESW8u4ns+6A6BTvamIrDRDXx0laP+JD3JdJ03mV1FC jWo2hKSsr8cmjZLnAh8kscBCVAEkOX8NeK1V2n7064BntrUgAS0oE/pbui9+MI6FZbqUewA8R6I /7cEqR3fBUtG6cMFiqtaQUd3MtbISnbF30gNfHR1MfFKCg81+EbDbJFu7ecUR+6hivjduYHRfFq fkIZRBMs210uQ0YVp18EggmHZ14n+/jh2Daw9BPdVEfZqeX5F9Y5De4j7dIVbpmALbsC4VvDwhY cGvJR0DJwBbZr1z2KdF9oIn19pVA1/5+TSWInhxH+rq0uYWE5jMgDq7x8QhdbCll3G X-Received: by 2002:a17:90a:da8e:b0:35d:9560:3f09 with SMTP id 98e67ed59e1d1-3650ce70a0bmr2989866a91.24.1777728716810; Sat, 02 May 2026 06:31:56 -0700 (PDT) Received: from [127.0.1.1] ([2a12:a305:4::3016]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-364ec02ab2fsm5647937a91.14.2026.05.02.06.31.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 02 May 2026 06:31:56 -0700 (PDT) From: Guodong Xu Date: Sat, 02 May 2026 21:30:53 -0400 Subject: [PATCH v10 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: <20260502-spi-spacemit-k1-v10-3-f412e1ae8a34@riscstar.com> References: <20260502-spi-spacemit-k1-v10-0-f412e1ae8a34@riscstar.com> In-Reply-To: <20260502-spi-spacemit-k1-v10-0-f412e1ae8a34@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 --- v10: No change --- 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