From patchwork Mon Jan 15 13:05:59 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jassi Brar X-Patchwork-Id: 124520 Delivered-To: patch@linaro.org Received: by 10.46.64.148 with SMTP id r20csp662153lje; Mon, 15 Jan 2018 05:06:15 -0800 (PST) X-Google-Smtp-Source: ACJfBovhzUyiCg0kbJdyCpnnFsPHPW6ohO6rY3V5qGn4z832EloDoOBGU/4R3fw1FTG/UIproPcn X-Received: by 10.101.77.68 with SMTP id j4mr11205971pgt.222.1516021575549; Mon, 15 Jan 2018 05:06:15 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1516021575; cv=none; d=google.com; s=arc-20160816; b=NIccUjaH1CjCY+kaxVX/iyvtRBl0gvp2CTGcH4RCTXIC0ZKVjj5wwkSpco68fOvn7z DyMrKBUHTm+zV5g33085ZaLvx7UQzPHTyTndz17g5mGaaQZqy2mavWkUpHxrTH6J+eW/ w7rJKhLs6xxzz8+C8gIF7YbtIAyC8D7vcD+5RWK4Fj/Ue5/OFopXcXN+LUf9K45pIugq nSHt2bH3k3e66DdS4fxxTOJnQTZzBkX+Il5fpOzhHccO9TIGdsfVZuYYQKNB20ab4/pU F9NOsUnNHdt89A3uVGU3gr9legB1JCdv3yI+kAZ5KwKRsNV7UspQkzbjK6gyp5mwJ2Bg pf/g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=Hu4XXT1jrC2QmhuAM0UjbiAtwZFI3aeezCUqWlZSpbA=; b=C2+ezrfyXYgPvqcvymtXM3O3NEqgYGrqDkSs5QTMRZ2foTILgaLVyQK8Va8YO42fJq Hyr8op2wYUUjFUr5osiSrTj+0Q3G4FC6k1I952rGa2i9R5imd+kcFxPnCTjTbczgZOQ7 +v2RSIcuLrqCuh8uC/QNwskEFkna5P1SnwPXnMY7AeVCoexjmCnzdN5kzW4qUV3I79nz uP+4PZiFpK6kxV1Gb3T1/O0y30cWmcm2MS1eiWRurlVIXGWwmjUdrC1N+rs/f/RU+OSf ai/Yn68LZ57VCYGs8OGPGkO4FCwWqK/j1wqf3Wht7+YgxcJjPU7lsg+99pRi0Znbt26K g/hA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20161025 header.b=o18HZq1D; spf=pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=devicetree-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id x6si8408054plr.268.2018.01.15.05.06.15; Mon, 15 Jan 2018 05:06:15 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20161025 header.b=o18HZq1D; spf=pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=devicetree-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S966669AbeAONGN (ORCPT + 6 others); Mon, 15 Jan 2018 08:06:13 -0500 Received: from mail-pl0-f68.google.com ([209.85.160.68]:34132 "EHLO mail-pl0-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S966647AbeAONGL (ORCPT ); Mon, 15 Jan 2018 08:06:11 -0500 Received: by mail-pl0-f68.google.com with SMTP id d21so3702838pll.1; Mon, 15 Jan 2018 05:06:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=JFGSqrUWghFspX0mzgJJMELpbdTT8SfNtX5lRVuxVGQ=; b=o18HZq1DsPTWRmXvjmMSt3uKmdknPIfosY6uceGIsWEPOMGpCR3Q7mpIJJZs8HVNHS RHtQkNK5LUMMgsdgl4+JfXERh7VWwJOU9Fb9xvXNLx5NxmBHno9HWfv4zTgt9VNaPoJ/ zaHxlsOGHMAKa5va+9KLjyDiMI4ZxbQO6ebprBzABfP9BRZ5qWyREhCMhFkpNdVXwsyR NFVkO5wxvh8ujg6ymGQz5dcFpdGKRfNnNJ8hzMkN1FalmR0/lZzQsf6x1NY+njaEiOay pDIWcoKj1O9g9ZUs3Rt1oKckmviNmQNgvlU5IO5ARLxmhCwe0jYUFB/CW3Sp6W4enlRY jagw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=JFGSqrUWghFspX0mzgJJMELpbdTT8SfNtX5lRVuxVGQ=; b=ejBbjQ6/VaaSgJoK3eILUt1i0Nsjt76RxX/Rl2dbSSsYK+CqGwyjvI/M7FTM28N83C bhH685c19yd2UrLf78Vzc4dZAaoOZixpiwDtoSDfI2Hu3j21YzHl4h7Vz6gJ1UPueEoj AklBRhIuLLe1zx5R085Cjp+TYKC4C9jTze8c30zHPEUOuAcp9vfWXF6Q+zC3bL/af8K0 ymQX2iwMTTZ/XhniSlvSle1aLRBP9577Wa59u+JhGjZGzPOm/RZb7nSeYLHho+0X9cUI h/aGGcxctxduSXFjVnoLveUDUmqXLvFmXB4N2QOog+wmnZFlNVgjDWAM0ysDB82JCEQ8 KNLw== X-Gm-Message-State: AKwxytdskdHG4IM81O/sPxUap/TNMLRcrKcXBnPKxJLD+83NJr0OM4ZN /ht+cnP7wlPvkz8zk1HpK3izlI2e X-Received: by 10.84.217.20 with SMTP id o20mr22426628pli.263.1516021570799; Mon, 15 Jan 2018 05:06:10 -0800 (PST) Received: from localhost.localdomain ([112.196.116.247]) by smtp.gmail.com with ESMTPSA id p10sm46515844pfd.106.2018.01.15.05.06.06 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 15 Jan 2018 05:06:09 -0800 (PST) From: jassisinghbrar@gmail.com To: linux-spi@vger.kernel.org, devicetree@vger.kernel.org Cc: tpiepho@impinj.com, broonie@kernel.org, ard.biesheuvel@linaro.org, robh+dt@kernel.org, mark.rutland@arm.com, masami.hiramatsu@linaro.org, Jassi Brar Subject: [PATCHv2 1/3] dt-bindings: spi: Add DT bindings for Synquacer Date: Mon, 15 Jan 2018 18:35:59 +0530 Message-Id: <1516021559-19327-1-git-send-email-jassisinghbrar@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1516021530-19236-1-git-send-email-jassisinghbrar@gmail.com> References: <1516021530-19236-1-git-send-email-jassisinghbrar@gmail.com> Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org From: Jassi Brar This patch adds documentation for Device-Tree bindings for the Socionext Synquacer spi driver. Signed-off-by: Jassi Brar --- .../devicetree/bindings/spi/spi-synquacer.txt | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/spi-synquacer.txt -- 2.7.4 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/Documentation/devicetree/bindings/spi/spi-synquacer.txt b/Documentation/devicetree/bindings/spi/spi-synquacer.txt new file mode 100644 index 0000000..d013cfd --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-synquacer.txt @@ -0,0 +1,24 @@ +* Socionext Synquacer HS-SPI bindings + +Required Properties: +- compatible: should be "socionext,synquacer-spi" +- reg: physical base address of the controller and length of memory mapped + region. +- clocks: Must contain an entry for rate source clock(s). +- clock-names: Shall be "iHCLK" or "iPCLK". iHCLK is preferred over iPCLK + +Optional Properties: +- num-cs: total number of chipselects +- socionext,use-rtm: boolean, if required to use "retimed clock" for RX +- socionext,set-aces: boolean, if same active clock edges field to be set. + +Example: + + spi0: spi@ff110000 { + compatible = "socionext,synquacer-spi"; + reg = <0xff110000 0x1000>; + clocks = <&clk_fip006_spi>; + clock-names = "iHCLK"; + socionext,use-rtm; + socionext,set-aces; + }; From patchwork Mon Jan 15 13:06:17 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jassi Brar X-Patchwork-Id: 124521 Delivered-To: patch@linaro.org Received: by 10.46.64.148 with SMTP id r20csp662229lje; Mon, 15 Jan 2018 05:06:32 -0800 (PST) X-Google-Smtp-Source: ACJfBoutxleyZJHfFMBpF2yzdor0xg6V1O5OK3Aifzi9xcGqphGHmZmfhyXnbYKB+a8zed9qxFMC X-Received: by 10.101.88.130 with SMTP id d2mr18886179pgu.110.1516021591888; Mon, 15 Jan 2018 05:06:31 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1516021591; cv=none; d=google.com; s=arc-20160816; b=aFLPyO4FmyajA5Jn9FoNXdUrH+vquQL1GxWf/YkKiHMdfKgyGDRFqGQeN/w8bdZj2U bt1WHc7ydSzJF2mDxmdNd/OVFdYmivqMnsrqLx3ob+NAWIWkSjl6I2/vIpgyw+aJxe1N 6nsbqMWfY1gnOTDDzFopXF4s4F1h7My9xcH/qvDIKUrXcFTjR9m79MZhE9WBf6ww/1XH 5xrHlGZnPLXt0OTUvKsRX8BVfHaPiznqVXcSW0UidrsbvJZsGx/oatTktHn5jsA3NL4V LUmcWLJeS4VSlu5NJCgGNE3Ii/NxDRd7fVTC4q9nnRUnEprehZaXjhe7jYQrftNMVg2Z J19w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=LScsx924SN2yjhqMquUHnFFzut3fzpYUJjC2O1h/790=; b=Mu2bbPSXyLw7MVI3Z+QkXNlPXguObCH3r3+SyKArS1B2s2on3y7R9rbyhQgcgRCe9h jPjCFWbzVlWjXXK037qFlmAxi0dKQhcmmyFl3rqrL95RHlI3jgoYyRKWIBN95AKgMVXA wIHRMaFk8MFetiKi6HCJ7mmyjj5uO+R2Y+IKkQHdHF5jYQxxq0NXHZ2IMrfPE9UocsLZ qbNXXoZANUHzSUK7oXxvSky28BSAoErv9EV1CM1fzw0nEPTlMpPzl+vmX7eSQXFsv9rN 0p4Rz7Oq9UaW4zr/2UnF8ZxyR2AS4x7SiwDO64N7gCTymnJowj1Ipgiz6m6szVc7TQ+9 rE6g== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20161025 header.b=XYAl/odl; spf=pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=devicetree-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 93si2522833plc.515.2018.01.15.05.06.31; Mon, 15 Jan 2018 05:06:31 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20161025 header.b=XYAl/odl; spf=pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=devicetree-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S967612AbeAONGa (ORCPT + 6 others); Mon, 15 Jan 2018 08:06:30 -0500 Received: from mail-pf0-f195.google.com ([209.85.192.195]:41964 "EHLO mail-pf0-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S966647AbeAONG2 (ORCPT ); Mon, 15 Jan 2018 08:06:28 -0500 Received: by mail-pf0-f195.google.com with SMTP id j3so8074144pfh.8; Mon, 15 Jan 2018 05:06:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=/rrA/fOE4CIAQFHnZRllNP/tCYwPC/jFiPxpa7lz6xM=; b=XYAl/odlSs+figXithxxrQ0T122HezCRhm1Vabl8dh3rU2RWvQbF6G9RgmiKp5RqWH hdBljR5fgW6/uIYcMZwqinDPTj779qwo3H3UfdOrbqo8pTQkWVlnj8li3IjaWxXBylll sBV+lNO6hw051WfWrtoURcTuUxujh0KT9LM5YM5Pb7Q3as2olfQAjLXujMr09RHclFaO DxiQNncHmN3ScL3GeoMXL4db8KXHVXEZR2zyUh9h/mIOY6Nxd6XmchJ3/Hz1HyPopyty xweqaDnMxeHSUSIHHC71wOuoAtdEDGE1f10AYeJzd/qvAn86Y8GKtdm9CCEnXiM/LJx/ GxDQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=/rrA/fOE4CIAQFHnZRllNP/tCYwPC/jFiPxpa7lz6xM=; b=fP+t3uABwamKZBhmiu8r2xA4jjB7iv3utb/5dicsoQDkHnvzq+nrZBCLCuE9OA4lsl B18L27rmVOMPl98agab+wt4W22UiT/+blM8o9ypYfwfe6C2kKxh87kQoawqAjaz4JW03 b3gXzdPYgu7CYar7mUqrux0SEo1DloCAZB//7pXp+Q+dRARiO9S6K5wLBsmQtpGpbreY fuVnEJjyZVztupuZQi+sExO16w3lv7f0Fi6JNZHBe5auJQb1VLFCzUAnQvInLLkD5qqr c+iovU6wYeM4Oco1CrA+OCo9zGecCcChwqZapoqdDmJ0regVgZrON5b2v89EUcX5Ga/F PCVg== X-Gm-Message-State: AKwxytdbbT0e5E+Lk1ADdiy5KdSsb+0k1F4L+RjGKG1anSf8qpAL0ZPP CVKQrJqf9n+E8BfrLvWqfhWCN0rO X-Received: by 10.101.81.7 with SMTP id f7mr5808726pgq.451.1516021586776; Mon, 15 Jan 2018 05:06:26 -0800 (PST) Received: from localhost.localdomain ([112.196.116.247]) by smtp.gmail.com with ESMTPSA id y7sm4295896pfe.48.2018.01.15.05.06.22 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 15 Jan 2018 05:06:25 -0800 (PST) From: jassisinghbrar@gmail.com To: linux-spi@vger.kernel.org, devicetree@vger.kernel.org Cc: tpiepho@impinj.com, broonie@kernel.org, ard.biesheuvel@linaro.org, robh+dt@kernel.org, mark.rutland@arm.com, masami.hiramatsu@linaro.org, Jassi Brar Subject: [PATCHv2 2/3] spi: Add spi driver for Socionext Synquacer platform Date: Mon, 15 Jan 2018 18:36:17 +0530 Message-Id: <1516021577-19381-1-git-send-email-jassisinghbrar@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1516021530-19236-1-git-send-email-jassisinghbrar@gmail.com> References: <1516021530-19236-1-git-send-email-jassisinghbrar@gmail.com> Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org From: Jassi Brar This patch adds support for controller found on synquacer platforms. Signed-off-by: Jassi Brar --- drivers/spi/Kconfig | 11 + drivers/spi/Makefile | 1 + drivers/spi/spi-synquacer.c | 639 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 651 insertions(+) create mode 100644 drivers/spi/spi-synquacer.c -- 2.7.4 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 6037839..9e04bbe 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -659,6 +659,17 @@ config SPI_SUN6I help This enables using the SPI controller on the Allwinner A31 SoCs. +config SPI_SYNQUACER + tristate "Socionext's Synquacer HighSpeed SPI controller" + depends on ARCH_SYNQUACER || COMPILE_TEST + select SPI_BITBANG + help + SPI driver for Socionext's High speed SPI controller which provides + various operating modes for interfacing to serial peripheral devices + that use the de-facto standard SPI protocol. + + It also supports the new dual-bit and quad-bit SPI protocol. + config SPI_MXS tristate "Freescale MXS SPI controller" depends on ARCH_MXS diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 34c5f28..7c222f2 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -96,6 +96,7 @@ obj-$(CONFIG_SPI_STM32) += spi-stm32.o obj-$(CONFIG_SPI_ST_SSC4) += spi-st-ssc4.o obj-$(CONFIG_SPI_SUN4I) += spi-sun4i.o obj-$(CONFIG_SPI_SUN6I) += spi-sun6i.o +obj-$(CONFIG_SPI_SYNQUACER) += spi-synquacer.o obj-$(CONFIG_SPI_TEGRA114) += spi-tegra114.o obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o obj-$(CONFIG_SPI_TEGRA20_SLINK) += spi-tegra20-slink.o diff --git a/drivers/spi/spi-synquacer.c b/drivers/spi/spi-synquacer.c new file mode 100644 index 0000000..4c68c9a --- /dev/null +++ b/drivers/spi/spi-synquacer.c @@ -0,0 +1,649 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Synquacer HSSPI controller driver +// +// Copyright (c) 2015-2018 Socionext Inc. +// Copyright (c) 2018 Linaro Ltd. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MCTRL 0x0 +#define MEN BIT(0) +#define CSEN BIT(1) +#define IPCLK BIT(3) +#define MES BIT(4) +#define SYNCON BIT(5) + +#define PCC0 0x4 +#define PCC(n) (PCC0 + (n) * 4) +#define RTM BIT(3) +#define ACES BIT(2) +#define SAFESYNC BIT(16) +#define CPHA BIT(0) +#define CPOL BIT(1) +#define SSPOL BIT(4) +#define SDIR BIT(7) +#define SS2CD 5 +#define SENDIAN BIT(8) +#define CDRS_SHIFT 9 +#define CDRS_MASK 0x7f + +#define TXF 0x14 +#define TXE 0x18 +#define TXC 0x1c +#define RXF 0x20 +#define RXE 0x24 +#define RXC 0x28 + +#define FAULTF 0x2c +#define FAULTC 0x30 + +#define DMCFG 0x34 +#define SSDC BIT(1) +#define MSTARTEN BIT(2) + +#define DMSTART 0x38 +#define TRIGGER BIT(0) +#define DMSTOP BIT(8) +#define CS_MASK 3 +#define CS_SHIFT 16 +#define DATA_TXRX 0 +#define DATA_RX 1 +#define DATA_TX 2 +#define DATA_MASK 3 +#define DATA_SHIFT 26 +#define BUS_WIDTH 24 + +#define DMBCC 0x3c +#define DMSTATUS 0x40 +#define RX_DATA_MASK 0x1f +#define RX_DATA_SHIFT 8 +#define TX_DATA_MASK 0x1f +#define TX_DATA_SHIFT 16 + +#define TXBITCNT 0x44 + +#define FIFOCFG 0x4c +#define BPW_MASK 0x3 +#define BPW_SHIFT 8 +#define RX_FLUSH BIT(11) +#define TX_FLUSH BIT(12) +#define RX_TRSHLD_MASK 0xf +#define RX_TRSHLD_SHIFT 0 +#define TX_TRSHLD_MASK 0xf +#define TX_TRSHLD_SHIFT 4 + +#define TXFIFO 0x50 +#define RXFIFO 0x90 +#define MID 0xfc + +#define FIFO_DEPTH 16 +#define TX_TRSHLD 4 +#define RX_TRSHLD (FIFO_DEPTH - TX_TRSHLD) + +#define TXBIT BIT(1) +#define RXBIT BIT(2) + +struct synquacer_spi { + struct device *dev; + struct spi_master *master; + + unsigned int cs; + unsigned int bpw; + unsigned int mode; + unsigned int speed; + bool aces, rtm; + void *rx_buf; + const void *tx_buf; + struct clk *clk; + void __iomem *regs; + unsigned int tx_words, rx_words; + unsigned int bus_width; +}; + +static void read_fifo(struct synquacer_spi *sspi) +{ + u32 len = readl_relaxed(sspi->regs + DMSTATUS); + int i; + + len = (len >> RX_DATA_SHIFT) & RX_DATA_MASK; + len = min_t(unsigned int, len, sspi->rx_words); + + switch (sspi->bpw) { + case 8: + { + u8 *buf = sspi->rx_buf; + + for (i = 0; i < len; i++) + *buf++ = readb_relaxed(sspi->regs + RXFIFO); + sspi->rx_buf = buf; + break; + } + case 16: + { + u16 *buf = sspi->rx_buf; + + for (i = 0; i < len; i++) + *buf++ = readw_relaxed(sspi->regs + RXFIFO); + sspi->rx_buf = buf; + break; + } + default: + { + u32 *buf = sspi->rx_buf; + + for (i = 0; i < len; i++) + *buf++ = readl_relaxed(sspi->regs + RXFIFO); + sspi->rx_buf = buf; + break; + } + } + + sspi->rx_words -= len; +} + +static void write_fifo(struct synquacer_spi *sspi) +{ + u32 len = readl_relaxed(sspi->regs + DMSTATUS); + int i; + + len = (len >> TX_DATA_SHIFT) & TX_DATA_MASK; + len = min_t(unsigned int, FIFO_DEPTH - len, sspi->tx_words); + + switch (sspi->bpw) { + case 8: + { + const u8 *buf = sspi->tx_buf; + + for (i = 0; i < len; i++) + writeb_relaxed(*buf++, sspi->regs + TXFIFO); + sspi->tx_buf = buf; + break; + } + case 16: + { + const u16 *buf = sspi->tx_buf; + + for (i = 0; i < len; i++) + writew_relaxed(*buf++, sspi->regs + TXFIFO); + sspi->tx_buf = buf; + break; + } + default: + { + const u32 *buf = sspi->tx_buf; + + for (i = 0; i < len; i++) + writel_relaxed(*buf++, sspi->regs + TXFIFO); + sspi->tx_buf = buf; + break; + } + } + sspi->tx_words -= len; +} + +static int synquacer_spi_config(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct synquacer_spi *sspi = spi_master_get_devdata(master); + unsigned int speed, mode, bpw, cs, bus_width; + unsigned long rate; + u32 val, div; + + /* Full Duplex only on 1bit wide bus */ + if (xfer->rx_buf && xfer->tx_buf && + (xfer->rx_nbits != 1 || xfer->tx_nbits != 1)) { + dev_err(sspi->dev, + "RX and TX bus widths must match for Full-Duplex!\n"); + return -EINVAL; + } + + if (xfer->tx_buf) + bus_width = xfer->tx_nbits; + else + bus_width = xfer->rx_nbits; + + mode = spi->mode; + cs = spi->chip_select; + speed = xfer->speed_hz; + bpw = xfer->bits_per_word; + + /* return if nothing to change */ + if (speed == sspi->speed && + bus_width == sspi->bus_width && bpw == sspi->bpw && + mode == sspi->mode && cs == sspi->cs) { + return 0; + } + + rate = clk_get_rate(sspi->clk); + + div = DIV_ROUND_UP(rate, speed); + if (div > 254) { + dev_err(sspi->dev, "Requested rate too low (%u)\n", + sspi->speed); + return -EINVAL; + } + + val = readl_relaxed(sspi->regs + PCC(cs)); + val &= ~SAFESYNC; + if (bpw == 8 && (mode & (SPI_TX_DUAL | SPI_RX_DUAL)) && div < 3) + val |= SAFESYNC; + if (bpw == 8 && (mode & (SPI_TX_QUAD | SPI_RX_QUAD)) && div < 6) + val |= SAFESYNC; + if (bpw == 16 && (mode & (SPI_TX_QUAD | SPI_RX_QUAD)) && div < 3) + val |= SAFESYNC; + + if (mode & SPI_CPHA) + val |= CPHA; + else + val &= ~CPHA; + + if (mode & SPI_CPOL) + val |= CPOL; + else + val &= ~CPOL; + + if (mode & SPI_CS_HIGH) + val |= SSPOL; + else + val &= ~SSPOL; + + if (mode & SPI_LSB_FIRST) + val |= SDIR; + else + val &= ~SDIR; + + if (sspi->aces) + val |= ACES; + else + val &= ~ACES; + + if (sspi->rtm) + val |= RTM; + else + val &= ~RTM; + + val |= (3 << SS2CD); + val |= SENDIAN; + + val &= ~(CDRS_MASK << CDRS_SHIFT); + val |= ((div >> 1) << CDRS_SHIFT); + + writel_relaxed(val, sspi->regs + PCC(cs)); + + val = readl_relaxed(sspi->regs + FIFOCFG); + val &= ~(BPW_MASK << BPW_SHIFT); + val |= ((bpw / 8 - 1) << BPW_SHIFT); + writel_relaxed(val, sspi->regs + FIFOCFG); + + val = readl_relaxed(sspi->regs + DMSTART); + val &= ~(DATA_MASK << DATA_SHIFT); + + if (xfer->tx_buf && xfer->rx_buf) + val |= (DATA_TXRX << DATA_SHIFT); + else if (xfer->rx_buf) + val |= (DATA_RX << DATA_SHIFT); + else + val |= (DATA_TX << DATA_SHIFT); + + val &= ~(3 << BUS_WIDTH); + val |= ((bus_width >> 1) << BUS_WIDTH); + writel_relaxed(val, sspi->regs + DMSTART); + + sspi->bpw = bpw; + sspi->mode = mode; + sspi->speed = speed; + sspi->cs = spi->chip_select; + sspi->bus_width = bus_width; + + return 0; +} + +static int synquacer_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct synquacer_spi *sspi = spi_master_get_devdata(master); + unsigned long bpw, flags; + int ret, words, busy = 0; + u32 val; + + val = readl_relaxed(sspi->regs + FIFOCFG); + val |= RX_FLUSH; + val |= TX_FLUSH; + writel_relaxed(val, sspi->regs + FIFOCFG); + + /* See if we can transfer 4-bytes as 1 word even if not asked */ + bpw = xfer->bits_per_word; + if (bpw == 8 && !(xfer->len % 4) && !(spi->mode & SPI_LSB_FIRST)) + xfer->bits_per_word = 32; + + ret = synquacer_spi_config(master, spi, xfer); + + /* restore */ + xfer->bits_per_word = bpw; + + if (ret) + return ret; + + sspi->tx_buf = xfer->tx_buf; + sspi->rx_buf = xfer->rx_buf; + + switch (sspi->bpw) { + case 8: + words = xfer->len; + break; + case 16: + words = xfer->len / 2; + break; + default: + words = xfer->len / 4; + break; + } + + if (xfer->tx_buf) { + busy |= TXBIT; + sspi->tx_words = words; + } else { + sspi->tx_words = 0; + } + + if (xfer->rx_buf) { + busy |= RXBIT; + sspi->rx_words = words; + } else { + sspi->rx_words = 0; + } + + if (xfer->tx_buf) + write_fifo(sspi); + + if (xfer->rx_buf) { + val = readl_relaxed(sspi->regs + FIFOCFG); + val &= ~(RX_TRSHLD_MASK << RX_TRSHLD_SHIFT); + val |= ((sspi->rx_words > FIFO_DEPTH ? + RX_TRSHLD : sspi->rx_words) << RX_TRSHLD_SHIFT); + writel_relaxed(val, sspi->regs + FIFOCFG); + } + + writel_relaxed(~0, sspi->regs + TXC); + writel_relaxed(~0, sspi->regs + RXC); + + /* Trigger */ + val = readl_relaxed(sspi->regs + DMSTART); + val |= TRIGGER; + writel_relaxed(val, sspi->regs + DMSTART); + + while (busy & (RXBIT | TXBIT)) { + if (sspi->rx_words) + read_fifo(sspi); + else + busy &= ~RXBIT; + + if (sspi->tx_words) { + write_fifo(sspi); + } else { + u32 len; + + do { /* wait for shifter to empty out */ + cpu_relax(); + len = readl_relaxed(sspi->regs + DMSTATUS); + len = (len >> TX_DATA_SHIFT) & TX_DATA_MASK; + } while (xfer->tx_buf && len); + busy &= ~TXBIT; + } + } + + return 0; +} + +static void synquacer_spi_set_cs(struct spi_device *spi, bool enable) +{ + struct synquacer_spi *sspi = spi_master_get_devdata(spi->master); + u32 val; + + val = readl_relaxed(sspi->regs + DMSTART); + val &= ~(CS_MASK << CS_SHIFT); + val |= spi->chip_select << CS_SHIFT; + + if (!enable) { + writel_relaxed(val, sspi->regs + DMSTART); + + val = readl_relaxed(sspi->regs + DMSTART); + val &= ~DMSTOP; + writel_relaxed(val, sspi->regs + DMSTART); + } else { + val |= DMSTOP; + writel_relaxed(val, sspi->regs + DMSTART); + + if (sspi->rx_buf) { + u32 buf[16]; + + sspi->rx_buf = buf; + sspi->rx_words = 16; + read_fifo(sspi); + } + } +} + +static int synquacer_spi_enable(struct spi_master *master) +{ + struct synquacer_spi *sspi = spi_master_get_devdata(master); + u32 val; + + /* Disable module */ + writel_relaxed(0, sspi->regs + MCTRL); + val = 0xfffff; + while (--val && (readl_relaxed(sspi->regs + MCTRL) & MES)) + cpu_relax(); + if (!val) + return -EBUSY; + + writel_relaxed(0, sspi->regs + TXE); + writel_relaxed(0, sspi->regs + RXE); + val = readl_relaxed(sspi->regs + TXF); + writel_relaxed(val, sspi->regs + TXC); + val = readl_relaxed(sspi->regs + RXF); + writel_relaxed(val, sspi->regs + RXC); + val = readl_relaxed(sspi->regs + FAULTF); + writel_relaxed(val, sspi->regs + FAULTC); + + val = readl_relaxed(sspi->regs + DMCFG); + val &= ~SSDC; + val &= ~MSTARTEN; + writel_relaxed(val, sspi->regs + DMCFG); + + val = readl_relaxed(sspi->regs + MCTRL); + if (!strcmp(__clk_get_name(sspi->clk), "iHCLK")) + val &= ~IPCLK; + else + val |= IPCLK; + + val &= ~CSEN; + val |= MEN; + val |= SYNCON; + writel_relaxed(val, sspi->regs + MCTRL); + + return 0; +} + +static int synquacer_spi_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct spi_master *master; + struct synquacer_spi *sspi; + struct resource *res; + u32 num_cs = 1; + int ret; + + master = spi_alloc_master(&pdev->dev, sizeof(*sspi)); + if (!master) + return -ENOMEM; + platform_set_drvdata(pdev, master); + + sspi = spi_master_get_devdata(master); + sspi->dev = &pdev->dev; + sspi->master = master; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + sspi->regs = devm_ioremap_resource(sspi->dev, res); + if (IS_ERR(sspi->regs)) { + ret = PTR_ERR(sspi->regs); + goto put_spi; + } + + sspi->clk = devm_clk_get(sspi->dev, "iHCLK"); + if (IS_ERR(sspi->clk)) { + sspi->clk = devm_clk_get(sspi->dev, "iPCLK"); + if (IS_ERR(sspi->clk)) { + dev_err(&pdev->dev, "No source clock\n"); + ret = PTR_ERR(sspi->clk); + goto put_spi; + } + } + + sspi->aces = of_property_read_bool(np, "socionext,set-aces"); + sspi->rtm = of_property_read_bool(np, "socionext,use-rtm"); + + of_property_read_u32(np, "num-cs", &num_cs); + master->num_chipselect = num_cs; + + ret = clk_prepare_enable(sspi->clk); + if (ret) + goto put_spi; + + master->dev.of_node = np; + master->auto_runtime_pm = true; + master->bus_num = pdev->id; + + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_TX_DUAL | SPI_RX_DUAL | + SPI_TX_QUAD | SPI_RX_QUAD; + master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(24) + | SPI_BPW_MASK(16) | SPI_BPW_MASK(8); + master->max_speed_hz = clk_get_rate(sspi->clk); + master->min_speed_hz = master->max_speed_hz / 254; + + master->set_cs = synquacer_spi_set_cs; + master->transfer_one = synquacer_spi_transfer_one; + + ret = synquacer_spi_enable(master); + if (ret) + goto fail_enable; + + pm_runtime_set_active(sspi->dev); + pm_runtime_enable(sspi->dev); + + ret = devm_spi_register_master(sspi->dev, master); + if (ret) + goto disable_pm; + + return 0; + +disable_pm: + pm_runtime_disable(sspi->dev); +fail_enable: + clk_disable_unprepare(sspi->clk); +put_spi: + spi_master_put(master); + + return ret; +} + +static int synquacer_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct synquacer_spi *sspi = spi_master_get_devdata(master); + + pm_runtime_disable(sspi->dev); + clk_disable_unprepare(sspi->clk); + spi_master_put(master); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int synquacer_spi_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct synquacer_spi *sspi = spi_master_get_devdata(master); + int ret; + + ret = spi_master_suspend(master); + if (ret) + return ret; + + if (!pm_runtime_suspended(dev)) + clk_disable_unprepare(sspi->clk); + + return ret; +} + +static int synquacer_spi_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct synquacer_spi *sspi = spi_master_get_devdata(master); + int ret; + + if (!pm_runtime_suspended(dev)) { + /* Ensure reconfigure during next xfer */ + sspi->speed = 0; + + ret = clk_prepare_enable(sspi->clk); + if (ret < 0) { + dev_err(dev, "failed to enable clk (%d)\n", ret); + return ret; + } + + ret = synquacer_spi_enable(master); + if (ret) { + dev_err(dev, "failed to enable spi (%d)\n", ret); + return ret; + } + } + + ret = spi_master_resume(master); + if (ret < 0) + clk_disable_unprepare(sspi->clk); + + return ret; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops synquacer_spi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(synquacer_spi_suspend, synquacer_spi_resume) +}; + +static const struct of_device_id synquacer_spi_of_match[] = { + {.compatible = "socionext,synquacer-spi",}, + {}, +}; +MODULE_DEVICE_TABLE(of, synquacer_spi_of_match); + +static struct platform_driver synquacer_spi_driver = { + .driver = { + .name = "synquacer-spi", + .pm = &synquacer_spi_pm_ops, + .of_match_table = of_match_ptr(synquacer_spi_of_match), + }, + .probe = synquacer_spi_probe, + .remove = synquacer_spi_remove, +}; +module_platform_driver(synquacer_spi_driver); + +MODULE_DESCRIPTION("Socionext Synquacer HS-SPI controller driver"); +MODULE_AUTHOR("Jassi Brar "); +MODULE_LICENSE("GPL v2");