From patchwork Mon Jan 14 13:06:56 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomer Maimon X-Patchwork-Id: 1024500 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 43dZ306KTjz9sD4 for ; Tue, 15 Jan 2019 00:25:16 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 43dZ300jLfzDqPg for ; Tue, 15 Jan 2019 00:25:16 +1100 (AEDT) X-Original-To: openbmc@lists.ozlabs.org Delivered-To: openbmc@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (mailfrom) smtp.mailfrom=nuvoton.com (client-ip=212.199.177.27; helo=herzl.nuvoton.co.il; envelope-from=tomer.maimon@nuvoton.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from herzl.nuvoton.co.il (212.199.177.27.static.012.net.il [212.199.177.27]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 43dYfR6NLQzDqW4 for ; Tue, 15 Jan 2019 00:07:25 +1100 (AEDT) Received: from taln60.nuvoton.co.il (ntil-fw [212.199.177.25]) by herzl.nuvoton.co.il (8.13.8/8.13.8) with ESMTP id x0ECbFTR018973; Mon, 14 Jan 2019 14:37:15 +0200 Received: by taln60.nuvoton.co.il (Postfix, from userid 10070) id 9F87B62DA6; Mon, 14 Jan 2019 15:07:13 +0200 (IST) From: Tomer Maimon To: joel@jms.id.au Subject: [linux dev-4.19 01/15] clk: nuvoton: add npcm750 clock function prototype initialization Date: Mon, 14 Jan 2019 15:06:56 +0200 Message-Id: <20190114130710.427600-2-tmaimon77@gmail.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20190114130710.427600-1-tmaimon77@gmail.com> References: <20190114130710.427600-1-tmaimon77@gmail.com> X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: openbmc@lists.ozlabs.org, Tomer Maimon Errors-To: openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "openbmc" Signed-off-by: Tomer Maimon --- include/linux/clk/nuvoton.h | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 include/linux/clk/nuvoton.h diff --git a/include/linux/clk/nuvoton.h b/include/linux/clk/nuvoton.h new file mode 100644 index 000000000000..9a474d691786 --- /dev/null +++ b/include/linux/clk/nuvoton.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2014-2019 Nuvoton Technology corporation. */ + +#ifndef __LINUX_CLK_NUVOTON_H_ +#define __LINUX_CLK_NUVOTON_H_ + +void nuvoton_npcm750_clock_init(void); + +#endif From patchwork Mon Jan 14 13:06:57 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomer Maimon X-Patchwork-Id: 1024496 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 43dZ0w6Z3cz9s9G for ; Tue, 15 Jan 2019 00:23:28 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 43dZ0w4kwQzDqXJ for ; Tue, 15 Jan 2019 00:23:28 +1100 (AEDT) X-Original-To: openbmc@lists.ozlabs.org Delivered-To: openbmc@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (mailfrom) smtp.mailfrom=nuvoton.com (client-ip=212.199.177.27; helo=herzl.nuvoton.co.il; envelope-from=tomer.maimon@nuvoton.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from herzl.nuvoton.co.il (212.199.177.27.static.012.net.il [212.199.177.27]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 43dYfR6KvHzDqVZ for ; Tue, 15 Jan 2019 00:07:25 +1100 (AEDT) Received: from taln60.nuvoton.co.il (ntil-fw [212.199.177.25]) by herzl.nuvoton.co.il (8.13.8/8.13.8) with ESMTP id x0ECbG2h018974; Mon, 14 Jan 2019 14:37:16 +0200 Received: by taln60.nuvoton.co.il (Postfix, from userid 10070) id 2327B62DA7; Mon, 14 Jan 2019 15:07:14 +0200 (IST) From: Tomer Maimon To: joel@jms.id.au Subject: [linux dev-4.19 02/15] dt-binding: mtd: add NPCM FIU controller Date: Mon, 14 Jan 2019 15:06:57 +0200 Message-Id: <20190114130710.427600-3-tmaimon77@gmail.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20190114130710.427600-1-tmaimon77@gmail.com> References: <20190114130710.427600-1-tmaimon77@gmail.com> X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: openbmc@lists.ozlabs.org, Tomer Maimon Errors-To: openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "openbmc" Added device tree binding documentation for Nuvoton BMC NPCM Flash Interface Unit(FIU) SPI-NOR controller. Signed-off-by: Tomer Maimon --- Documentation/devicetree/bindings/mtd/npcm-fiu.txt | 64 ++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 Documentation/devicetree/bindings/mtd/npcm-fiu.txt diff --git a/Documentation/devicetree/bindings/mtd/npcm-fiu.txt b/Documentation/devicetree/bindings/mtd/npcm-fiu.txt new file mode 100644 index 000000000000..9746cb5b1ced --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/npcm-fiu.txt @@ -0,0 +1,64 @@ +* Nuvoton FLASH Interface Unit (FIU) SPI Controller + +NPCM FIU supports single, dual and quad communication interface. + +The NPCM7XX supports three FIU modules, +FIU0 and FIUx supports two chip selects, +FIU3 support four chip select. + +Required properties: + - compatible : "nuvoton,npcm750-fiu" for the NPCM7XX BMC + - #address-cells : should be 1. + - #size-cells : should be 0. + - reg : the first contains the register location and length, + the second contains the memory mapping address and length + - reg-names: Should contain the reg names "control" and "memory" + - clocks : phandle of F reference clock. + +Required properties in case the pins can be muxed: + - pinctrl-names : a pinctrl state named "default" must be defined. + - pinctrl-0 : phandle referencing pin configuration of the device. + +Optional property: + - spix-mode: enable spix-mode for an expansion bus to an ASIC or CPLD. + +The SPI device must be a child of the FIU node and must have a +compatible property as specified in bindings/mtd/jedec,spi-nor.txt + +Required property: +- reg: chip select number. + +Optional property: +- spi-rx-bus-width: see ../spi/spi-bus.txt for the description. + +Aliases: +- All the FIU controller nodes should be represented in the aliases node using + the following format 'fiu{n}' where n is a unique number for the alias. + In the NPCM7XX BMC: + fiu0 represent fiu 0 controller + fiu1 represent fiu 3 controller + fiu2 represent fiu x controller + +Example: +fiu3: fiu@c00000000 { + compatible = "nuvoton,npcm750-fiu"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xfb000000 0x1000>, <0x80000000 0x10000000>; + reg-names = "control", "memory"; + clocks = <&clk NPCM7XX_CLK_AHB>; + pinctrl-names = "default"; + pinctrl-0 = <&spi3_pins>; + spi-nor@0 { + compatible = "jedec,spi-nor"; + spi-rx-bus-width = <2>; + #address-cells = <1>; + #size-cells = <1>; + reg = <0>; + partition@0 { + label = "flash_data"; + reg = <0x0 0x800000>; + }; + }; +}; + From patchwork Mon Jan 14 13:06:58 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomer Maimon X-Patchwork-Id: 1024542 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 43dZC606KYz9s3q for ; Tue, 15 Jan 2019 00:32:18 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 43dZC563TczDqWT for ; Tue, 15 Jan 2019 00:32:17 +1100 (AEDT) X-Original-To: openbmc@lists.ozlabs.org Delivered-To: openbmc@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (mailfrom) smtp.mailfrom=nuvoton.com (client-ip=212.199.177.27; helo=herzl.nuvoton.co.il; envelope-from=tomer.maimon@nuvoton.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from herzl.nuvoton.co.il (212.199.177.27.static.012.net.il [212.199.177.27]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 43dYfY1kFgzDqVv for ; Tue, 15 Jan 2019 00:07:32 +1100 (AEDT) Received: from taln60.nuvoton.co.il (ntil-fw [212.199.177.25]) by herzl.nuvoton.co.il (8.13.8/8.13.8) with ESMTP id x0ECbGcB018981; Mon, 14 Jan 2019 14:37:16 +0200 Received: by taln60.nuvoton.co.il (Postfix, from userid 10070) id 8A19462DA4; Mon, 14 Jan 2019 15:07:14 +0200 (IST) From: Tomer Maimon To: joel@jms.id.au Subject: [linux dev-4.19 03/15] mtd: spi-nor: add NPCM FIU controller driver Date: Mon, 14 Jan 2019 15:06:58 +0200 Message-Id: <20190114130710.427600-4-tmaimon77@gmail.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20190114130710.427600-1-tmaimon77@gmail.com> References: <20190114130710.427600-1-tmaimon77@gmail.com> X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: openbmc@lists.ozlabs.org, Tomer Maimon Errors-To: openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "openbmc" Add Nuvoton NPCM BMC Flash Interface Unit(FIU) SPI-NOR controller driver The FIU supports single, dual or quad communication interface. the FIU controller can operate in following modes: - User Mode Access(UMA): provides flash access by using an indirect address/data mechanism. - direct rd/wr mode: maps the flash memory into the core address space. - SPI-X mode: used for an expansion bus to an ASIC or CPLD. Signed-off-by: Tomer Maimon --- drivers/mtd/spi-nor/Kconfig | 8 + drivers/mtd/spi-nor/Makefile | 1 + drivers/mtd/spi-nor/npcm-fiu.c | 930 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 939 insertions(+) create mode 100644 drivers/mtd/spi-nor/npcm-fiu.c diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 37775fc09e09..5af9d5f77076 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -75,6 +75,14 @@ config SPI_HISI_SFC help This enables support for hisilicon SPI-NOR flash controller. +config SPI_NPCM_FIU + tristate "NPCM FLASH Interface unit(FIU) controller " + depends on ARCH_NPCM || COMPILE_TEST + help + This enables support for the FLASH Interface unit(FIU) controller. + This driver does not support generic SPI. The implementation only + supports SPI NOR. + config SPI_NXP_SPIFI tristate "NXP SPI Flash Interface (SPIFI)" depends on OF && (ARCH_LPC18XX || COMPILE_TEST) diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index f4c61d282abd..fe0e2bdef9cd 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_SPI_CADENCE_QUADSPI) += cadence-quadspi.o obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o obj-$(CONFIG_MTD_MT81xx_NOR) += mtk-quadspi.o +obj-$(CONFIG_SPI_NPCM_FIU) += npcm-fiu.o obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o obj-$(CONFIG_SPI_INTEL_SPI) += intel-spi.o obj-$(CONFIG_SPI_INTEL_SPI_PCI) += intel-spi-pci.o diff --git a/drivers/mtd/spi-nor/npcm-fiu.c b/drivers/mtd/spi-nor/npcm-fiu.c new file mode 100644 index 000000000000..9b6e7747d678 --- /dev/null +++ b/drivers/mtd/spi-nor/npcm-fiu.c @@ -0,0 +1,930 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 Nuvoton Technology corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Flash Interface Unit (FIU) Registers */ +#define NPCM_FIU_DRD_CFG 0x00 +#define NPCM_FIU_DWR_CFG 0x04 +#define NPCM_FIU_UMA_CFG 0x08 +#define NPCM_FIU_UMA_CTS 0x0C +#define NPCM_FIU_UMA_CMD 0x10 +#define NPCM_FIU_UMA_ADDR 0x14 +#define NPCM_FIU_PRT_CFG 0x18 +#define NPCM_FIU_UMA_DW0 0x20 +#define NPCM_FIU_UMA_DW1 0x24 +#define NPCM_FIU_UMA_DW2 0x28 +#define NPCM_FIU_UMA_DW3 0x2C +#define NPCM_FIU_UMA_DR0 0x30 +#define NPCM_FIU_UMA_DR1 0x34 +#define NPCM_FIU_UMA_DR2 0x38 +#define NPCM_FIU_UMA_DR3 0x3C +#define NPCM_FIU_MAX_REG_LIMIT 0x80 + +/* FIU Direct Read Configuration Register */ +#define NPCM_FIU_DRD_CFG_LCK BIT(31) +#define NPCM_FIU_DRD_CFG_R_BURST GENMASK(25, 24) +#define NPCM_FIU_DRD_CFG_ADDSIZ GENMASK(17, 16) +#define NPCM_FIU_DRD_CFG_DBW GENMASK(13, 12) +#define NPCM_FIU_DRD_CFG_ACCTYPE GENMASK(9, 8) +#define NPCM_FIU_DRD_CFG_RDCMD GENMASK(7, 0) +#define NPCM_FIU_DRD_ADDSIZ_SHIFT 16 +#define NPCM_FIU_DRD_DBW_SHIFT 12 +#define NPCM_FIU_DRD_ACCTYPE_SHIFT 8 + +/* FIU Direct Write Configuration Register */ +#define NPCM_FIU_DWR_CFG_LCK BIT(31) +#define NPCM_FIU_DWR_CFG_W_BURST GENMASK(25, 24) +#define NPCM_FIU_DWR_CFG_ADDSIZ GENMASK(17, 16) +#define NPCM_FIU_DWR_CFG_ABPCK GENMASK(11, 10) +#define NPCM_FIU_DWR_CFG_DBPCK GENMASK(9, 8) +#define NPCM_FIU_DWR_CFG_WRCMD GENMASK(7, 0) +#define NPCM_FIU_DWR_ADDSIZ_SHIFT 16 +#define NPCM_FIU_DWR_ABPCK_SHIFT 10 +#define NPCM_FIU_DWR_DBPCK_SHIFT 8 + +/* FIU UMA Configuration Register */ +#define NPCM_FIU_UMA_CFG_LCK BIT(31) +#define NPCM_FIU_UMA_CFG_CMMLCK BIT(30) +#define NPCM_FIU_UMA_CFG_RDATSIZ GENMASK(28, 24) +#define NPCM_FIU_UMA_CFG_DBSIZ GENMASK(23, 21) +#define NPCM_FIU_UMA_CFG_WDATSIZ GENMASK(20, 16) +#define NPCM_FIU_UMA_CFG_ADDSIZ GENMASK(13, 11) +#define NPCM_FIU_UMA_CFG_CMDSIZ BIT(10) +#define NPCM_FIU_UMA_CFG_RDBPCK GENMASK(9, 8) +#define NPCM_FIU_UMA_CFG_DBPCK GENMASK(7, 6) +#define NPCM_FIU_UMA_CFG_WDBPCK GENMASK(5, 4) +#define NPCM_FIU_UMA_CFG_ADBPCK GENMASK(3, 2) +#define NPCM_FIU_UMA_CFG_CMBPCK GENMASK(1, 0) +#define NPCM_FIU_UMA_CFG_ADBPCK_SHIFT 2 +#define NPCM_FIU_UMA_CFG_WDBPCK_SHIFT 4 +#define NPCM_FIU_UMA_CFG_DBPCK_SHIFT 6 +#define NPCM_FIU_UMA_CFG_RDBPCK_SHIFT 8 +#define NPCM_FIU_UMA_CFG_ADDSIZ_SHIFT 11 +#define NPCM_FIU_UMA_CFG_WDATSIZ_SHIFT 16 +#define NPCM_FIU_UMA_CFG_DBSIZ_SHIFT 21 +#define NPCM_FIU_UMA_CFG_RDATSIZ_SHIFT 24 + +/* FIU UMA Control and Status Register */ +#define NPCM_FIU_UMA_CTS_RDYIE BIT(25) +#define NPCM_FIU_UMA_CTS_RDYST BIT(24) +#define NPCM_FIU_UMA_CTS_SW_CS BIT(16) +#define NPCM_FIU_UMA_CTS_DEV_NUM GENMASK(9, 8) +#define NPCM_FIU_UMA_CTS_EXEC_DONE BIT(0) +#define NPCM_FIU_UMA_CTS_DEV_NUM_SHIFT 8 + +/* FIU UMA Command Register */ +#define NPCM_FIU_UMA_CMD_DUM3 GENMASK(31, 24) +#define NPCM_FIU_UMA_CMD_DUM2 GENMASK(23, 16) +#define NPCM_FIU_UMA_CMD_DUM1 GENMASK(15, 8) +#define NPCM_FIU_UMA_CMD_CMD GENMASK(7, 0) + +/* FIU UMA Address Register */ +#define NPCM_FIU_UMA_ADDR_UMA_ADDR GENMASK(31, 0) +#define NPCM_FIU_UMA_ADDR_AB3 GENMASK(31, 24) +#define NPCM_FIU_UMA_ADDR_AB2 GENMASK(23, 16) +#define NPCM_FIU_UMA_ADDR_AB1 GENMASK(15, 8) +#define NPCM_FIU_UMA_ADDR_AB0 GENMASK(7, 0) + +/* FIU UMA Write Data Bytes 0-3 Register */ +#define NPCM_FIU_UMA_DW0_WB3 GENMASK(31, 24) +#define NPCM_FIU_UMA_DW0_WB2 GENMASK(23, 16) +#define NPCM_FIU_UMA_DW0_WB1 GENMASK(15, 8) +#define NPCM_FIU_UMA_DW0_WB0 GENMASK(7, 0) + +/* FIU UMA Write Data Bytes 4-7 Register */ +#define NPCM_FIU_UMA_DW1_WB7 GENMASK(31, 24) +#define NPCM_FIU_UMA_DW1_WB6 GENMASK(23, 16) +#define NPCM_FIU_UMA_DW1_WB5 GENMASK(15, 8) +#define NPCM_FIU_UMA_DW1_WB4 GENMASK(7, 0) + +/* FIU UMA Write Data Bytes 8-11 Register */ +#define NPCM_FIU_UMA_DW2_WB11 GENMASK(31, 24) +#define NPCM_FIU_UMA_DW2_WB10 GENMASK(23, 16) +#define NPCM_FIU_UMA_DW2_WB9 GENMASK(15, 8) +#define NPCM_FIU_UMA_DW2_WB8 GENMASK(7, 0) + +/* FIU UMA Write Data Bytes 12-15 Register */ +#define NPCM_FIU_UMA_DW3_WB15 GENMASK(31, 24) +#define NPCM_FIU_UMA_DW3_WB14 GENMASK(23, 16) +#define NPCM_FIU_UMA_DW3_WB13 GENMASK(15, 8) +#define NPCM_FIU_UMA_DW3_WB12 GENMASK(7, 0) + +/* FIU UMA Read Data Bytes 0-3 Register */ +#define NPCM_FIU_UMA_DR0_RB3 GENMASK(31, 24) +#define NPCM_FIU_UMA_DR0_RB2 GENMASK(23, 16) +#define NPCM_FIU_UMA_DR0_RB1 GENMASK(15, 8) +#define NPCM_FIU_UMA_DR0_RB0 GENMASK(7, 0) + +/* FIU UMA Read Data Bytes 4-7 Register */ +#define NPCM_FIU_UMA_DR1_RB15 GENMASK(31, 24) +#define NPCM_FIU_UMA_DR1_RB14 GENMASK(23, 16) +#define NPCM_FIU_UMA_DR1_RB13 GENMASK(15, 8) +#define NPCM_FIU_UMA_DR1_RB12 GENMASK(7, 0) + +/* FIU UMA Read Data Bytes 8-11 Register */ +#define NPCM_FIU_UMA_DR2_RB15 GENMASK(31, 24) +#define NPCM_FIU_UMA_DR2_RB14 GENMASK(23, 16) +#define NPCM_FIU_UMA_DR2_RB13 GENMASK(15, 8) +#define NPCM_FIU_UMA_DR2_RB12 GENMASK(7, 0) + +/* FIU UMA Read Data Bytes 12-15 Register */ +#define NPCM_FIU_UMA_DR3_RB15 GENMASK(31, 24) +#define NPCM_FIU_UMA_DR3_RB14 GENMASK(23, 16) +#define NPCM_FIU_UMA_DR3_RB13 GENMASK(15, 8) +#define NPCM_FIU_UMA_DR3_RB12 GENMASK(7, 0) + +/* FIU Read Mode */ +enum { + DRD_SINGLE_WIRE_MODE = 0, + DRD_DUAL_IO_MODE = 1, + DRD_QUAD_IO_MODE = 2, + DRD_SPI_X_MODE = 3, +}; + +enum { + DWR_ABPCK_BIT_PER_CLK = 0, + DWR_ABPCK_2_BIT_PER_CLK = 1, + DWR_ABPCK_4_BIT_PER_CLK = 2, +}; + +enum { + DWR_DBPCK_BIT_PER_CLK = 0, + DWR_DBPCK_2_BIT_PER_CLK = 1, + DWR_DBPCK_4_BIT_PER_CLK = 2, +}; + +#define NPCM_FIU_DRD_16_BYTE_BURST 0x3000000 +#define NPCM_FIU_DWR_16_BYTE_BURST 0x3000000 + +#define MAP_SIZE_128MB 0x8000000 +#define MAP_SIZE_16MB 0x1000000 +#define MAP_SIZE_8MB 0x800000 + +#define NUM_BITS_IN_BYTE 8 +#define FIU_DRD_MAX_DUMMY_NUMBER 3 +#define NPCM_MAX_CHIP_NUM 4 +#define CHUNK_SIZE 16 +#define UMA_MICRO_SEC_TIMEOUT 150 + +enum { + FIU0 = 0, + FIU3, + FIUX, +}; + +struct npcm_fiu_info { + char *name; + u32 fiu_id; + u32 max_map_size; + u32 max_cs; +}; + +struct fiu_data { + const struct npcm_fiu_info *npcm_fiu_data_info; + int fiu_max; +}; + +static const struct npcm_fiu_info npxm7xx_fiu_info[] = { + {.name = "FIU0", .fiu_id = FIU0, + .max_map_size = MAP_SIZE_128MB, .max_cs = 2}, + {.name = "FIU3", .fiu_id = FIU3, + .max_map_size = MAP_SIZE_128MB, .max_cs = 4}, + {.name = "FIUX", .fiu_id = FIUX, + .max_map_size = MAP_SIZE_16MB, .max_cs = 2} }; + +static const struct fiu_data npxm7xx_fiu_data = { + .npcm_fiu_data_info = npxm7xx_fiu_info, + .fiu_max = 3, +}; + +struct npcm_fiu_bus; + +struct npcm_chip { + void __iomem *flash_region_mapped_ptr; + enum spi_nor_protocol direct_rd_proto; + struct npcm_fiu_bus *host; + struct spi_nor nor; + bool direct_read; + u32 read_proto; + u32 chipselect; + u32 clkrate; +}; + +struct npcm_fiu_bus { + struct npcm_chip *chip[NPCM_MAX_CHIP_NUM]; + enum spi_nor_protocol direct_rd_proto; + const struct npcm_fiu_info *info; + struct resource *res_mem; + resource_size_t iosize; + struct regmap *regmap; + void __iomem *regbase; + u32 direct_read_proto; + struct device *dev; + struct mutex lock; /* controller access mutex */ + struct clk *clk; + bool spix_mode; + int id; +}; + +static const struct regmap_config npcm_mtd_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = NPCM_FIU_MAX_REG_LIMIT, +}; + +static int npcm_fiu_direct_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct spi_nor *nor = mtd->priv; + struct npcm_chip *chip = nor->priv; + + memcpy_fromio(buf, chip->flash_region_mapped_ptr + from, len); + + *retlen = len; + return 0; +} + +static int npcm_fiu_direct_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct spi_nor *nor = mtd->priv; + struct npcm_chip *chip = nor->priv; + + memcpy_toio(chip->flash_region_mapped_ptr + to, buf, len); + + *retlen = len; + return 0; +} + +static int npcm_fiu_uma_read(struct spi_nor *nor, u8 transaction_code, + u32 address, bool is_address_size, u8 *data, + u32 data_size) +{ + struct npcm_chip *chip = nor->priv; + struct npcm_fiu_bus *host = chip->host; + u32 uma_cfg = BIT(10); + u32 dummy_bytes; + u32 data_reg[4]; + int ret; + u32 val; + u32 i; + + regmap_update_bits(host->regmap, NPCM_FIU_UMA_CTS, + NPCM_FIU_UMA_CTS_DEV_NUM, + (chip->chipselect << + NPCM_FIU_UMA_CTS_DEV_NUM_SHIFT)); + regmap_update_bits(host->regmap, NPCM_FIU_UMA_CMD, + NPCM_FIU_UMA_CMD_CMD, transaction_code); + regmap_write(host->regmap, NPCM_FIU_UMA_ADDR, address); + + if (is_address_size) { + uma_cfg |= + ilog2(spi_nor_get_protocol_inst_nbits(nor->read_proto)); + uma_cfg |= + ilog2(spi_nor_get_protocol_addr_nbits(nor->read_proto)) + << NPCM_FIU_UMA_CFG_ADBPCK_SHIFT; + uma_cfg |= + ilog2(spi_nor_get_protocol_addr_nbits(nor->read_proto)) + << NPCM_FIU_UMA_CFG_DBPCK_SHIFT; + uma_cfg |= + ilog2(spi_nor_get_protocol_data_nbits(nor->read_proto)) + << NPCM_FIU_UMA_CFG_RDBPCK_SHIFT; + dummy_bytes = + (nor->read_dummy * + spi_nor_get_protocol_addr_nbits(nor->read_proto)) / + NUM_BITS_IN_BYTE; + uma_cfg |= dummy_bytes << NPCM_FIU_UMA_CFG_DBSIZ_SHIFT; + uma_cfg |= (nor->addr_width << NPCM_FIU_UMA_CFG_ADDSIZ_SHIFT); + } + + uma_cfg |= data_size << NPCM_FIU_UMA_CFG_RDATSIZ_SHIFT; + regmap_write(host->regmap, NPCM_FIU_UMA_CFG, uma_cfg); + + regmap_write_bits(host->regmap, NPCM_FIU_UMA_CTS, + NPCM_FIU_UMA_CTS_EXEC_DONE, + NPCM_FIU_UMA_CTS_EXEC_DONE); + + ret = regmap_read_poll_timeout(host->regmap, NPCM_FIU_UMA_CTS, val, + (!(val & NPCM_FIU_UMA_CTS_EXEC_DONE)), 0, + UMA_MICRO_SEC_TIMEOUT); + if (ret) + return ret; + + if (data_size) { + for (i = 0; i <= data_size / 4; i++) + regmap_read(host->regmap, NPCM_FIU_UMA_DR0 + (i * 4), + &data_reg[i]); + memcpy(data, data_reg, data_size); + } + + return 0; +} + +static int npcm_fiu_uma_write(struct spi_nor *nor, u8 transaction_code, + u32 address, bool is_address_size, u8 *data, + u32 data_size) +{ + struct npcm_chip *chip = nor->priv; + struct npcm_fiu_bus *host = chip->host; + u32 uma_cfg = BIT(10); + u32 data_reg[4] = {0}; + u32 val; + u32 i; + + regmap_update_bits(host->regmap, NPCM_FIU_UMA_CTS, + NPCM_FIU_UMA_CTS_DEV_NUM, + (chip->chipselect << + NPCM_FIU_UMA_CTS_DEV_NUM_SHIFT)); + + regmap_update_bits(host->regmap, NPCM_FIU_UMA_CMD, + NPCM_FIU_UMA_CMD_CMD, transaction_code); + regmap_write(host->regmap, NPCM_FIU_UMA_ADDR, address); + + if (data_size) { + memcpy(data_reg, data, data_size); + for (i = 0; i <= data_size / 4; i++) + regmap_write(host->regmap, NPCM_FIU_UMA_DW0 + (i * 4), + data_reg[i]); + } + + if (is_address_size) { + uma_cfg |= + ilog2(spi_nor_get_protocol_inst_nbits + (nor->write_proto)); + uma_cfg |= + ilog2(spi_nor_get_protocol_addr_nbits(nor->write_proto)) + << NPCM_FIU_UMA_CFG_ADBPCK_SHIFT; + uma_cfg |= + ilog2(spi_nor_get_protocol_data_nbits(nor->write_proto)) + << NPCM_FIU_UMA_CFG_WDBPCK_SHIFT; + uma_cfg |= (nor->addr_width << NPCM_FIU_UMA_CFG_ADDSIZ_SHIFT); + } + + uma_cfg |= (data_size << NPCM_FIU_UMA_CFG_WDATSIZ_SHIFT); + regmap_write(host->regmap, NPCM_FIU_UMA_CFG, uma_cfg); + + regmap_write_bits(host->regmap, NPCM_FIU_UMA_CTS, + NPCM_FIU_UMA_CTS_EXEC_DONE, + NPCM_FIU_UMA_CTS_EXEC_DONE); + + return regmap_read_poll_timeout(host->regmap, NPCM_FIU_UMA_CTS, val, + (!(val & NPCM_FIU_UMA_CTS_EXEC_DONE)), 0, + UMA_MICRO_SEC_TIMEOUT); +} + +static int npcm_fiu_manualwrite(struct spi_nor *nor, u8 transaction_code, + u32 address, u8 *data, u32 data_size) +{ + u32 num_data_chunks; + u32 remain_data; + u32 idx = 0; + int ret; + + struct npcm_chip *chip = nor->priv; + struct npcm_fiu_bus *host = chip->host; + + num_data_chunks = data_size / CHUNK_SIZE; + remain_data = data_size % CHUNK_SIZE; + + regmap_update_bits(host->regmap, NPCM_FIU_UMA_CTS, + NPCM_FIU_UMA_CTS_DEV_NUM, + (chip->chipselect << + NPCM_FIU_UMA_CTS_DEV_NUM_SHIFT)); + regmap_update_bits(host->regmap, NPCM_FIU_UMA_CTS, + NPCM_FIU_UMA_CTS_SW_CS, 0); + + ret = npcm_fiu_uma_write(nor, transaction_code, address, true, + NULL, 0); + if (ret) + return ret; + + /* Starting the data writing loop in multiples of 8 */ + for (idx = 0; idx < num_data_chunks; ++idx) { + ret = npcm_fiu_uma_write(nor, data[0], (u32)NULL, false, + &data[1], CHUNK_SIZE - 1); + if (ret) + return ret; + + data += CHUNK_SIZE; + } + + /* Handling chunk remains */ + if (remain_data > 0) { + ret = npcm_fiu_uma_write(nor, data[0], (u32)NULL, false, + &data[1], remain_data - 1); + if (ret) + return ret; + } + + regmap_update_bits(host->regmap, NPCM_FIU_UMA_CTS, + NPCM_FIU_UMA_CTS_SW_CS, NPCM_FIU_UMA_CTS_SW_CS); + + return 0; +} + +static ssize_t npcm_fiu_write(struct spi_nor *nor, loff_t to, + size_t len, const u_char *write_buf) +{ + u32 local_addr = (u32)to; + struct mtd_info *mtd; + u32 actual_size = 0; + u32 cnt = (u32)len; + int ret; + + mtd = &nor->mtd; + + if (cnt != 0) { + while (cnt) { + actual_size = ((((local_addr) / nor->page_size) + 1) + * nor->page_size) - (local_addr); + if (actual_size > cnt) + actual_size = cnt; + + ret = npcm_fiu_manualwrite(nor, nor->program_opcode, + local_addr, + (u_char *)write_buf, + actual_size); + if (ret) + return ret; + + write_buf += actual_size; + local_addr += actual_size; + cnt -= actual_size; + } + } + + return (len - cnt); +} + +static void npcm_fiu_set_drd(struct spi_nor *nor, struct npcm_fiu_bus *host) +{ + regmap_update_bits(host->regmap, NPCM_FIU_DRD_CFG, + NPCM_FIU_DRD_CFG_ACCTYPE, + ilog2(spi_nor_get_protocol_addr_nbits + (nor->read_proto)) << + NPCM_FIU_DRD_ACCTYPE_SHIFT); + regmap_update_bits(host->regmap, NPCM_FIU_DRD_CFG, + NPCM_FIU_DRD_CFG_DBW, + ((nor->read_dummy * + spi_nor_get_protocol_addr_nbits(nor->read_proto)) + / NUM_BITS_IN_BYTE) << NPCM_FIU_DRD_DBW_SHIFT); + regmap_update_bits(host->regmap, NPCM_FIU_DRD_CFG, + NPCM_FIU_DRD_CFG_RDCMD, nor->read_opcode); + regmap_update_bits(host->regmap, NPCM_FIU_DRD_CFG, + NPCM_FIU_DRD_CFG_ADDSIZ, + (nor->addr_width - 3) << NPCM_FIU_DRD_ADDSIZ_SHIFT); +} + +static ssize_t npcm_fiu_read(struct spi_nor *nor, loff_t from, size_t len, + u_char *read_buf) +{ + struct npcm_chip *chip = nor->priv; + struct npcm_fiu_bus *host = chip->host; + int i, readlen, currlen; + struct mtd_info *mtd; + size_t retlen = 0; + u8 *buf_ptr; + u32 addr; + int ret; + + mtd = &nor->mtd; + + if (chip->direct_read) { + regmap_read(host->regmap, NPCM_FIU_DRD_CFG, &addr); + if (host->direct_rd_proto != chip->direct_rd_proto) { + npcm_fiu_set_drd(nor, host); + host->direct_rd_proto = chip->direct_rd_proto; + } + npcm_fiu_direct_read(mtd, from, len, &retlen, read_buf); + } else { + i = 0; + currlen = (int)len; + + do { + addr = ((u32)from + i); + if (currlen < 4) + readlen = currlen; + else + readlen = 4; + + buf_ptr = read_buf + i; + ret = npcm_fiu_uma_read(nor, nor->read_opcode, addr, + true, buf_ptr, readlen); + if (ret) + return ret; + + i += readlen; + currlen -= 4; + } while (currlen > 0); + + retlen = i; + } + + return retlen; +} + +static int npcm_fiu_erase(struct spi_nor *nor, loff_t offs) +{ + return npcm_fiu_uma_write(nor, nor->erase_opcode, (u32)offs, true, + NULL, 0); +} + +static int npcm_fiu_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, + int len) +{ + return npcm_fiu_uma_read(nor, opcode, 0, false, buf, len); +} + +static int npcm_fiu_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, + int len) +{ + return npcm_fiu_uma_write(nor, opcode, 0, false, buf, len); +} + +static int npcm_fiu_nor_prep(struct spi_nor *nor, enum spi_nor_ops ops) +{ + struct npcm_chip *chip = nor->priv; + struct npcm_fiu_bus *host = chip->host; + + mutex_lock(&host->lock); + + return 0; +} + +static void npcm_fiu_nor_unprep(struct spi_nor *nor, enum spi_nor_ops ops) +{ + struct npcm_chip *chip = nor->priv; + struct npcm_fiu_bus *host = chip->host; + + mutex_unlock(&host->lock); +} + +/* Expansion bus registers as mtd_ram device */ +static int npcm_mtd_ram_register(struct device_node *np, + struct npcm_fiu_bus *host) +{ + struct device *dev = host->dev; + struct npcm_chip *chip; + struct mtd_info *mtd; + struct spi_nor *nor; + u32 chipselect; + u32 rx_dummy = 0; + int ret; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + ret = of_property_read_u32(np, "reg", &chipselect); + if (ret) { + dev_err(dev, "There's no reg property for %s\n", + dev->of_node->full_name); + return ret; + } + + of_property_read_u32(np, "npcm,fiu-spix-rx-dummy-num", &rx_dummy); + if (rx_dummy > FIU_DRD_MAX_DUMMY_NUMBER) { + dev_warn(dev, "npcm,fiu-spix-rx-dummy-num %d not supported\n", + rx_dummy); + rx_dummy = 0; + } + + chip->host = host; + chip->chipselect = chipselect; + nor = &chip->nor; + nor->dev = dev; + nor->priv = chip; + mtd = &nor->mtd; + + chip->flash_region_mapped_ptr = + devm_ioremap(dev, (host->res_mem->start + + (host->info->max_map_size * + chip->chipselect)), MAP_SIZE_8MB); + if (!chip->flash_region_mapped_ptr) { + dev_err(dev, "Error mapping memory region!\n"); + return -ENOMEM; + } + + /* Populate mtd_info data structure */ + *mtd = (struct mtd_info) { + .dev = { .parent = dev }, + .name = "exp-bus", + .type = MTD_RAM, + .priv = nor, + .size = MAP_SIZE_8MB, + .writesize = 1, + .writebufsize = 1, + .flags = MTD_CAP_RAM, + ._read = npcm_fiu_direct_read, + ._write = npcm_fiu_direct_write, + }; + + /* set read and write direct to configuration to SPI-X mode */ + regmap_write(host->regmap, NPCM_FIU_DRD_CFG, + NPCM_FIU_DRD_16_BYTE_BURST); + regmap_update_bits(host->regmap, NPCM_FIU_DRD_CFG, + NPCM_FIU_DRD_CFG_ACCTYPE, + DRD_SPI_X_MODE << NPCM_FIU_DRD_ACCTYPE_SHIFT); + regmap_update_bits(host->regmap, NPCM_FIU_DRD_CFG, + NPCM_FIU_DRD_CFG_DBW, + rx_dummy << NPCM_FIU_DRD_DBW_SHIFT); + regmap_write(host->regmap, NPCM_FIU_DWR_CFG, + NPCM_FIU_DWR_16_BYTE_BURST); + regmap_update_bits(host->regmap, NPCM_FIU_DWR_CFG, + NPCM_FIU_DWR_CFG_ABPCK, + DWR_ABPCK_4_BIT_PER_CLK << NPCM_FIU_DWR_ABPCK_SHIFT); + regmap_update_bits(host->regmap, NPCM_FIU_DWR_CFG, + NPCM_FIU_DWR_CFG_DBPCK, + DWR_DBPCK_4_BIT_PER_CLK << NPCM_FIU_DWR_DBPCK_SHIFT); + + ret = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0); + if (ret) + return ret; + + host->chip[chip->chipselect] = chip; + + return 0; +} + +static void npcm_fiu_enable_direct_rd(struct spi_nor *nor, + struct npcm_fiu_bus *host, + struct npcm_chip *chip) +{ + struct device *dev = host->dev; + u32 flashsize; + + if (!host->res_mem) { + dev_warn(dev, "Reserved memory not defined, direct read disabled\n"); + return; + } + + /* direct read supports only I/O read mode */ + if (nor->read_proto != SNOR_PROTO_1_1_1 && + nor->read_proto != SNOR_PROTO_1_2_2 && + nor->read_proto != SNOR_PROTO_1_4_4) { + dev_warn(dev, "Only Read I/O commands supported, direct read disabled\n"); + return; + } + + flashsize = (u32)(nor->mtd.size >> 10) * 1024; + if (flashsize == 0 || flashsize > host->info->max_map_size) { + dev_warn(dev, "Flash size ecxeed(0x%x) map size(0x%x), direct read disabled\n" + , flashsize, host->info->max_map_size); + return; + } + + chip->flash_region_mapped_ptr = + devm_ioremap(dev, (host->res_mem->start + + (host->info->max_map_size * + chip->chipselect)), flashsize); + if (!chip->flash_region_mapped_ptr) { + dev_warn(dev, "Error mapping memory region, direct read disabled\n"); + return; + } + + npcm_fiu_set_drd(nor, host); + + host->direct_rd_proto = nor->read_proto; + chip->direct_rd_proto = nor->read_proto; + chip->direct_read = true; +} + +/* Get spi flash device information and register it as a mtd device. */ +static int npcm_fiu_nor_register(struct device_node *np, + struct npcm_fiu_bus *host) +{ + struct device *dev = host->dev; + struct npcm_chip *chip; + struct mtd_info *mtd; + struct spi_nor *nor; + u32 chipselect; + int ret; + u32 val; + struct spi_nor_hwcaps hwcaps = { + .mask = SNOR_HWCAPS_READ | + SNOR_HWCAPS_READ_FAST | + SNOR_HWCAPS_PP | + SNOR_HWCAPS_PP_1_1_4 | + SNOR_HWCAPS_PP_1_4_4 | + SNOR_HWCAPS_PP_4_4_4, + }; + + /* This driver mode supports only NOR flash devices. */ + if (!of_device_is_compatible(np, "jedec,spi-nor")) { + dev_err(dev, "The device is no compatible to jedec,spi-nor\n"); + return -ENOMEM; + } + + ret = of_property_read_u32(np, "reg", &chipselect); + if (ret) { + dev_err(dev, "There's no reg property for %s\n", np->full_name); + return ret; + } + + if (chipselect >= host->info->max_cs) { + dev_err(dev, "Flash device number exceeds the maximum chipselect number\n"); + return -ENOMEM; + } + + if (!of_property_read_u32(np, "spi-rx-bus-width", &val)) { + switch (val) { + case 1: + break; + case 2: + hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2 + | SNOR_HWCAPS_READ_1_2_2 + | SNOR_HWCAPS_READ_2_2_2; + break; + case 4: + hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4 + | SNOR_HWCAPS_READ_1_4_4 + | SNOR_HWCAPS_READ_4_4_4; + break; + default: + dev_warn(dev, "spi-rx-bus-width %d not supported\n", val); + break; + } + } + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->host = host; + chip->chipselect = chipselect; + + nor = &chip->nor; + mtd = &nor->mtd; + + nor->dev = dev; + nor->priv = chip; + + spi_nor_set_flash_node(nor, np); + + nor->prepare = npcm_fiu_nor_prep; + nor->unprepare = npcm_fiu_nor_unprep; + nor->read_reg = npcm_fiu_read_reg; + nor->write_reg = npcm_fiu_write_reg; + nor->read = npcm_fiu_read; + nor->write = npcm_fiu_write; + nor->erase = npcm_fiu_erase; + + ret = spi_nor_scan(nor, NULL, &hwcaps); + if (ret) + return ret; + + npcm_fiu_enable_direct_rd(nor, host, chip); + ret = mtd_device_register(mtd, NULL, 0); + if (ret) { + dev_err(dev, "MTD NOR device register failed\n"); + return ret; + } + + host->chip[chip->chipselect] = chip; + return 0; +} + +static void npcm_fiu_unregister_all(struct npcm_fiu_bus *host) +{ + struct npcm_chip *chip; + int n; + + for (n = 0; n < host->info->max_cs; n++) { + chip = host->chip[n]; + if (chip) + mtd_device_unregister(&chip->nor.mtd); + } +} + +static void npcm_fiu_register_all(struct npcm_fiu_bus *host) +{ + struct device *dev = host->dev; + struct device_node *np; + int ret; + + for_each_available_child_of_node(dev->of_node, np) { + if (host->spix_mode) + ret = npcm_mtd_ram_register(np, host); + else + ret = npcm_fiu_nor_register(np, host); + if (ret) + dev_warn(dev, "npcm fiu %s registration failed\n", np->full_name); + } +} + +static const struct of_device_id npcm_fiu_dt_ids[] = { + { .compatible = "nuvoton,npcm750-fiu", .data = &npxm7xx_fiu_data }, + { /* sentinel */ } +}; + +static int npcm_fiu_probe(struct platform_device *pdev) +{ + const struct fiu_data *fiu_data_match; + const struct of_device_id *match; + struct device *dev = &pdev->dev; + struct npcm_fiu_bus *host; + struct resource *res; + + host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + + match = of_match_device(npcm_fiu_dt_ids, dev); + if (!match || !match->data) { + dev_err(dev, "No compatible OF match\n"); + return -ENODEV; + } + + fiu_data_match = match->data; + + host->id = of_alias_get_id(dev->of_node, "fiu"); + if (host->id < 0 || host->id >= fiu_data_match->fiu_max) { + dev_err(dev, "Invalid platform device id: %d\n", host->id); + return -EINVAL; + } + + host->info = &fiu_data_match->npcm_fiu_data_info[host->id]; + + platform_set_drvdata(pdev, host); + host->dev = dev; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control"); + host->regbase = devm_ioremap_resource(dev, res); + if (IS_ERR(host->regbase)) + return PTR_ERR(host->regbase); + + host->regmap = devm_regmap_init_mmio(dev, host->regbase, + &npcm_mtd_regmap_config); + if (IS_ERR(host->regmap)) { + dev_err(dev, "Failed to create regmap\n"); + return PTR_ERR(host->regmap); + } + + host->res_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "memory"); + host->clk = devm_clk_get(dev, NULL); + if (IS_ERR(host->clk)) + return PTR_ERR(host->clk); + + host->spix_mode = of_property_read_bool(dev->of_node, "spix-mode"); + + mutex_init(&host->lock); + clk_prepare_enable(host->clk); + + npcm_fiu_register_all(host); + + dev_info(dev, "NPCM %s probe succeed\n", host->info->name); + + return 0; +} + +static int npcm_fiu_remove(struct platform_device *pdev) +{ + struct npcm_fiu_bus *host = platform_get_drvdata(pdev); + + npcm_fiu_unregister_all(host); + mutex_destroy(&host->lock); + clk_disable_unprepare(host->clk); + return 0; +} + +MODULE_DEVICE_TABLE(of, npcm_fiu_dt_ids); + +static struct platform_driver npcm_fiu_driver = { + .driver = { + .name = "NPCM-FIU", + .bus = &platform_bus_type, + .of_match_table = npcm_fiu_dt_ids, + }, + .probe = npcm_fiu_probe, + .remove = npcm_fiu_remove, +}; +module_platform_driver(npcm_fiu_driver); + +MODULE_DESCRIPTION("Nuvoton FLASH Interface Unit SPI Controller Driver"); +MODULE_AUTHOR("Tomer Maimon "); +MODULE_LICENSE("GPL v2"); From patchwork Mon Jan 14 13:06:59 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomer Maimon X-Patchwork-Id: 1024546 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 43dZFf1r7Tz9s3q for ; Tue, 15 Jan 2019 00:34:30 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 43dZFf0kJzzDqMH for ; Tue, 15 Jan 2019 00:34:30 +1100 (AEDT) X-Original-To: openbmc@lists.ozlabs.org Delivered-To: openbmc@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (mailfrom) smtp.mailfrom=nuvoton.com (client-ip=212.199.177.27; helo=herzl.nuvoton.co.il; envelope-from=tomer.maimon@nuvoton.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from herzl.nuvoton.co.il (212.199.177.27.static.012.net.il [212.199.177.27]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 43dYfd3HY9zDqVr for ; Tue, 15 Jan 2019 00:07:36 +1100 (AEDT) Received: from taln60.nuvoton.co.il (ntil-fw [212.199.177.25]) by herzl.nuvoton.co.il (8.13.8/8.13.8) with ESMTP id x0ECbHLe018984; Mon, 14 Jan 2019 14:37:17 +0200 Received: by taln60.nuvoton.co.il (Postfix, from userid 10070) id F39A762DA4; Mon, 14 Jan 2019 15:07:14 +0200 (IST) From: Tomer Maimon To: joel@jms.id.au Subject: [linux dev-4.19 04/15] dt-bindings: i2c: npcm7xx: add binding for i2c controller Date: Mon, 14 Jan 2019 15:06:59 +0200 Message-Id: <20190114130710.427600-5-tmaimon77@gmail.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20190114130710.427600-1-tmaimon77@gmail.com> References: <20190114130710.427600-1-tmaimon77@gmail.com> X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: openbmc@lists.ozlabs.org, Tomer Maimon , Tali Perry Errors-To: openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "openbmc" Signed-off-by: Tali Perry Reviewed-by: Rob Herring --- .../devicetree/bindings/i2c/i2c-npcm7xx.txt | 29 ++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/i2c-npcm7xx.txt diff --git a/Documentation/devicetree/bindings/i2c/i2c-npcm7xx.txt b/Documentation/devicetree/bindings/i2c/i2c-npcm7xx.txt new file mode 100644 index 000000000000..0ecae950748b --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-npcm7xx.txt @@ -0,0 +1,29 @@ +Nuvoton NPCM7XX I2C bus + +The NPCM750x includes sixteen I2C bus controllers. All Controllers support +both master and slave mode. Each controller has two 16 byte HW FIFO for TX and +RX. + +Required properties: +- compatible : must be "nuvoton,npcm750-i2c" +- reg : Offset and length of the register set for the device. +- interrupts : Contain the I2C interrupt with flags for falling edge. +- clocks : phandle of I2C reference clock. + +Optional: +- bus-frequency : Contain the I2C bus frequency, + the default I2C bus frequency is 100000. +- pinctrl-0 : must be <&smbX_pins>, X is module number + (on NPCM7XX it's 0 to 15) +- pinctrl-names : should be set to "default" +Example: + + i2c0: i2c@80000 { + compatible = "nuvoton,npcm750-i2c"; + reg = <0x80000 0x1000>; + clocks = <&clk NPCM7XX_CLK_APB2>; + bus-frequency = <100000>; + interrupts = ; + pinctrl-names = "default"; + pinctrl-0 = <&smb0_pins>; + }; From patchwork Mon Jan 14 13:07:00 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomer Maimon X-Patchwork-Id: 1024551 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 43dZHg4TTpz9s3q for ; Tue, 15 Jan 2019 00:36:15 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 43dZHg3CCQzDqWY for ; Tue, 15 Jan 2019 00:36:15 +1100 (AEDT) X-Original-To: openbmc@lists.ozlabs.org Delivered-To: openbmc@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (mailfrom) smtp.mailfrom=nuvoton.com (client-ip=212.199.177.27; helo=herzl.nuvoton.co.il; envelope-from=tomer.maimon@nuvoton.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from herzl.nuvoton.co.il (212.199.177.27.static.012.net.il [212.199.177.27]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 43dYfh5tHpzDqWK for ; Tue, 15 Jan 2019 00:07:40 +1100 (AEDT) Received: from taln60.nuvoton.co.il (ntil-fw [212.199.177.25]) by herzl.nuvoton.co.il (8.13.8/8.13.8) with ESMTP id x0ECbHEV018987; Mon, 14 Jan 2019 14:37:17 +0200 Received: by taln60.nuvoton.co.il (Postfix, from userid 10070) id 66F1762DA4; Mon, 14 Jan 2019 15:07:15 +0200 (IST) From: Tomer Maimon To: joel@jms.id.au Subject: [linux dev-4.19 05/15] i2c: npcm: driver for Poleg i2c controller Date: Mon, 14 Jan 2019 15:07:00 +0200 Message-Id: <20190114130710.427600-6-tmaimon77@gmail.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20190114130710.427600-1-tmaimon77@gmail.com> References: <20190114130710.427600-1-tmaimon77@gmail.com> X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: openbmc@lists.ozlabs.org, Tomer Maimon , Tali Perry Errors-To: openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "openbmc" Signed-off-by: Tali Perry Signed-off-by: Tomer Maimon --- drivers/i2c/busses/Kconfig | 11 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-npcm7xx.c | 2017 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 2029 insertions(+) create mode 100644 drivers/i2c/busses/i2c-npcm7xx.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index ac4b09642f63..6dde79365bf8 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -739,6 +739,17 @@ config I2C_NOMADIK I2C interface from ST-Ericsson's Nomadik and Ux500 architectures, as well as the STA2X11 PCIe I/O HUB. +config I2C_NPCM7XX + tristate "Nuvoton I2C Controller" + depends on ARCH_NPCM7XX + select I2C_SLAVE + help + If you say yes to this option, support will be included for the + Nuvoton I2C controller. + + This driver can also be built as a module. If so, the module + will be called i2c-npcm7xx. + config I2C_OCORES tristate "OpenCores I2C Controller" help diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 18b26af82b1c..fec5cb593e78 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -74,6 +74,7 @@ obj-$(CONFIG_I2C_MT65XX) += i2c-mt65xx.o obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o obj-$(CONFIG_I2C_MXS) += i2c-mxs.o obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o +obj-$(CONFIG_I2C_NPCM7XX) += i2c-npcm7xx.o obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o obj-$(CONFIG_I2C_OMAP) += i2c-omap.o obj-$(CONFIG_I2C_OWL) += i2c-owl.o diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c new file mode 100644 index 000000000000..6d177d3f0e0b --- /dev/null +++ b/drivers/i2c/busses/i2c-npcm7xx.c @@ -0,0 +1,2017 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Nuvoton NPCM7xx SMB Controller driver + * + * Copyright (C) 2018 Nuvoton Technologies tali.perry@nuvoton.com + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define I2C_VERSION "0.0.3" + +enum smb_mode { + SMB_SLAVE = 1, + SMB_MASTER +}; + +/* + * External SMB Interface driver xfer indication values, which indicate status + * of the bus. + */ +enum smb_state_ind { + SMB_NO_STATUS_IND = 0, + SMB_SLAVE_RCV_IND = 1, + SMB_SLAVE_XMIT_IND = 2, + SMB_SLAVE_XMIT_MISSING_DATA_IND = 3, + SMB_SLAVE_RESTART_IND = 4, + SMB_SLAVE_DONE_IND = 5, + SMB_MASTER_DONE_IND = 6, + SMB_NO_DATA_IND = 7, + SMB_NACK_IND = 8, + SMB_BUS_ERR_IND = 9, + SMB_WAKE_UP_IND = 10, + SMB_MASTER_PEC_ERR_IND = 11, + SMB_BLOCK_BYTES_ERR_IND = 12, + SMB_SLAVE_PEC_ERR_IND = 13, + SMB_SLAVE_RCV_MISSING_DATA_IND = 14, +}; + +// SMBus Operation type values +enum smb_oper { + SMB_NO_OPER = 0, + SMB_WRITE_OPER = 1, + SMB_READ_OPER = 2 +}; + +// SMBus Bank (FIFO mode) +enum smb_bank { + SMB_BANK_0 = 0, + SMB_BANK_1 = 1 +}; + +// Internal SMB states values (for the SMB module state machine). +enum smb_state { + SMB_DISABLE = 0, + SMB_IDLE, + SMB_MASTER_START, + SMB_SLAVE_MATCH, + SMB_OPER_STARTED, + SMB_REPEATED_START, + SMB_STOP_PENDING +}; + +// Module supports setting multiple own slave addresses: +enum smb_addr { + SMB_SLAVE_ADDR1 = 0, + SMB_SLAVE_ADDR2, + SMB_SLAVE_ADDR3, + SMB_SLAVE_ADDR4, + SMB_SLAVE_ADDR5, + SMB_SLAVE_ADDR6, + SMB_SLAVE_ADDR7, + SMB_SLAVE_ADDR8, + SMB_SLAVE_ADDR9, + SMB_SLAVE_ADDR10, + SMB_GC_ADDR, + SMB_ARP_ADDR +}; + +// global regs +static struct regmap *gcr_regmap; +static struct regmap *clk_regmap; + +#define NPCM_I2CSEGCTL 0xE4 +#define NPCM_SECCNT 0x68 +#define NPCM_CNTR25M 0x6C +#define I2CSEGCTL_VAL 0x0333F000 + +// Common regs +#define NPCM_SMBSDA 0x000 +#define NPCM_SMBST 0x002 +#define NPCM_SMBCST 0x004 +#define NPCM_SMBCTL1 0x006 +#define NPCM_SMBADDR1 0x008 +#define NPCM_SMBCTL2 0x00A +#define NPCM_SMBADDR2 0x00C +#define NPCM_SMBCTL3 0x00E +#define NPCM_SMBCST2 0x018 +#define NPCM_SMBCST3 0x019 +#define SMB_VER 0x01F + +// BANK 0 regs +#define NPCM_SMBADDR3 0x010 +#define NPCM_SMBADDR7 0x011 +#define NPCM_SMBADDR4 0x012 +#define NPCM_SMBADDR8 0x013 +#define NPCM_SMBADDR5 0x014 +#define NPCM_SMBADDR9 0x015 +#define NPCM_SMBADDR6 0x016 +#define NPCM_SMBADDR10 0x017 + +// SMBADDR array: because the addr regs are sprincled all over the address space +const int NPCM_SMBADDR[10] = {NPCM_SMBADDR1, NPCM_SMBADDR2, NPCM_SMBADDR3, + NPCM_SMBADDR4, NPCM_SMBADDR5, NPCM_SMBADDR6, + NPCM_SMBADDR7, NPCM_SMBADDR8, NPCM_SMBADDR9, + NPCM_SMBADDR10}; + +#define NPCM_SMBCTL4 0x01A +#define NPCM_SMBCTL5 0x01B +#define NPCM_SMBSCLLT 0x01C // SCL Low Time +#define NPCM_SMBFIF_CTL 0x01D // FIFO Control +#define NPCM_SMBSCLHT 0x01E // SCL High Time + +// BANK 1 regs +#define NPCM_SMBFIF_CTS 0x010 // FIFO Control +#define NPCM_SMBTXF_CTL 0x012 // Tx-FIFO Control +#define NPCM_SMBT_OUT 0x014 // Bus T.O. +#define NPCM_SMBPEC 0x016 // PEC Data +#define NPCM_SMBTXF_STS 0x01A // Tx-FIFO Status +#define NPCM_SMBRXF_STS 0x01C // Rx-FIFO Status +#define NPCM_SMBRXF_CTL 0x01E // Rx-FIFO Control + +// NPCM_SMBST reg fields +#define NPCM_SMBST_XMIT BIT(0) +#define NPCM_SMBST_MASTER BIT(1) +#define NPCM_SMBST_NMATCH BIT(2) +#define NPCM_SMBST_STASTR BIT(3) +#define NPCM_SMBST_NEGACK BIT(4) +#define NPCM_SMBST_BER BIT(5) +#define NPCM_SMBST_SDAST BIT(6) +#define NPCM_SMBST_SLVSTP BIT(7) + +// NPCM_SMBCST reg fields +#define NPCM_SMBCST_BUSY BIT(0) +#define NPCM_SMBCST_BB BIT(1) +#define NPCM_SMBCST_MATCH BIT(2) +#define NPCM_SMBCST_GCMATCH BIT(3) +#define NPCM_SMBCST_TSDA BIT(4) +#define NPCM_SMBCST_TGSCL BIT(5) +#define NPCM_SMBCST_MATCHAF BIT(6) +#define NPCM_SMBCST_ARPMATCH BIT(7) + +// NPCM_SMBCTL1 reg fields +#define NPCM_SMBCTL1_START BIT(0) +#define NPCM_SMBCTL1_STOP BIT(1) +#define NPCM_SMBCTL1_INTEN BIT(2) +#define NPCM_SMBCTL1_EOBINTE BIT(3) +#define NPCM_SMBCTL1_ACK BIT(4) +#define NPCM_SMBCTL1_GCMEN BIT(5) +#define NPCM_SMBCTL1_NMINTE BIT(6) +#define NPCM_SMBCTL1_STASTRE BIT(7) + +// RW1S fields (inside a RW reg): +#define NPCM_SMBCTL1_RWS_FIELDS (NPCM_SMBCTL1_START | NPCM_SMBCTL1_STOP | \ + NPCM_SMBCTL1_ACK) +// NPCM_SMBADDR reg fields +#define NPCM_SMBADDR_ADDR GENMASK(6, 0) +#define NPCM_SMBADDR_SAEN BIT(7) + +// NPCM_SMBCTL2 reg fields +#define SMBCTL2_ENABLE BIT(0) +#define SMBCTL2_SCLFRQ6_0 GENMASK(7, 1) + +// NPCM_SMBCTL3 reg fields +#define SMBCTL3_SCLFRQ8_7 GENMASK(1, 0) +#define SMBCTL3_ARPMEN BIT(2) +#define SMBCTL3_IDL_START BIT(3) +#define SMBCTL3_400K_MODE BIT(4) +#define SMBCTL3_BNK_SEL BIT(5) +#define SMBCTL3_SDA_LVL BIT(6) +#define SMBCTL3_SCL_LVL BIT(7) + +// NPCM_SMBCST2 reg fields +#define NPCM_SMBCST2_MATCHA1F BIT(0) +#define NPCM_SMBCST2_MATCHA2F BIT(1) +#define NPCM_SMBCST2_MATCHA3F BIT(2) +#define NPCM_SMBCST2_MATCHA4F BIT(3) +#define NPCM_SMBCST2_MATCHA5F BIT(4) +#define NPCM_SMBCST2_MATCHA6F BIT(5) +#define NPCM_SMBCST2_MATCHA7F BIT(5) +#define NPCM_SMBCST2_INTSTS BIT(7) + +// NPCM_SMBCST3 reg fields +#define NPCM_SMBCST3_MATCHA8F BIT(0) +#define NPCM_SMBCST3_MATCHA9F BIT(1) +#define NPCM_SMBCST3_MATCHA10F BIT(2) +#define NPCM_SMBCST3_EO_BUSY BIT(7) + +// NPCM_SMBCTL4 reg fields +#define SMBCTL4_HLDT GENMASK(5, 0) +#define SMBCTL4_LVL_WE BIT(7) + +// NPCM_SMBCTL5 reg fields +#define SMBCTL5_DBNCT GENMASK(3, 0) + +// NPCM_SMBFIF_CTS reg fields +#define NPCM_SMBFIF_CTS_RXF_TXE BIT(1) +#define NPCM_SMBFIF_CTS_RFTE_IE BIT(3) +#define NPCM_SMBFIF_CTS_CLR_FIFO BIT(6) +#define NPCM_SMBFIF_CTS_SLVRSTR BIT(7) + +// NPCM_SMBTXF_CTL reg fields +#ifdef SMB_CAPABILITY_32B_FIFO +#define NPCM_SMBTXF_CTL_TX_THR GENMASK(5, 0) +#else +#define NPCM_SMBTXF_CTL_TX_THR GENMASK(4, 0) +#endif +#define NPCM_SMBTXF_CTL_THR_TXIE BIT(6) + +// NPCM_SMBT_OUT reg fields +#define NPCM_SMBT_OUT_TO_CKDIV GENMASK(5, 0) +#define NPCM_SMBT_OUT_T_OUTIE BIT(6) +#define NPCM_SMBT_OUT_T_OUTST BIT(7) + +// NPCM_SMBTXF_STS reg fields +#ifdef SMB_CAPABILITY_32B_FIFO +#define NPCM_SMBTXF_STS_TX_BYTES GENMASK(5, 0) +#else +#define NPCM_SMBTXF_STS_TX_BYTES GENMASK(4, 0) +#endif +#define NPCM_SMBTXF_STS_TX_THST BIT(6) + +// NPCM_SMBRXF_STS reg fields +#ifdef SMB_CAPABILITY_32B_FIFO +#define NPCM_SMBRXF_STS_RX_BYTES GENMASK(5, 0) +#else +#define NPCM_SMBRXF_STS_RX_BYTES GENMASK(4, 0) +#endif +#define NPCM_SMBRXF_STS_RX_THST BIT(6) + +// NPCM_SMBFIF_CTL reg fields +#define NPCM_SMBFIF_CTL_FIFO_EN BIT(4) + +// NPCM_SMBRXF_CTL reg fields +// Note: on the next HW version of this module, this HW is about to switch to +// 32 bytes FIFO. This size will be set using a config. +// on current version 16 bytes FIFO is set using a define +#ifdef SMB_CAPABILITY_32B_FIFO +#define NPCM_SMBRXF_CTL_RX_THR GENMASK(5, 0) +#define NPCM_SMBRXF_CTL_THR_RXIE BIT(6) +#define NPCM_SMBRXF_CTL_LAST_PEC BIT(7) +#define SMBUS_FIFO_SIZE 32 +#else +#define NPCM_SMBRXF_CTL_RX_THR GENMASK(4, 0) +#define NPCM_SMBRXF_CTL_LAST_PEC BIT(5) +#define NPCM_SMBRXF_CTL_THR_RXIE BIT(6) +#define SMBUS_FIFO_SIZE 16 +#endif + +// SMB_VER reg fields +#define SMB_VER_VERSION GENMASK(6, 0) +#define SMB_VER_FIFO_EN BIT(7) + +// stall/stuck timeout +const unsigned int DEFAULT_STALL_COUNT = 25; + +// Data abort timeout +const unsigned int ABORT_TIMEOUT = 1000; + +// SMBus spec. values in KHZ +const unsigned int SMBUS_FREQ_MIN = 10; +const unsigned int SMBUS_FREQ_MAX = 1000; +const unsigned int SMBUS_FREQ_100KHZ = 100; +const unsigned int SMBUS_FREQ_400KHZ = 400; +const unsigned int SMBUS_FREQ_1MHZ = 1000; + +// SCLFRQ min/max field values +const unsigned int SCLFRQ_MIN = 10; +const unsigned int SCLFRQ_MAX = 511; + +// SCLFRQ field position +#define SCLFRQ_0_TO_6 GENMASK(6, 0) +#define SCLFRQ_7_TO_8 GENMASK(8, 7) + +// SMB Maximum Retry Trials (on Bus Arbitration Loss) +const unsigned int SMB_RETRY_MAX_COUNT = 2; +const unsigned int SMB_NUM_OF_ADDR = 10; + +// for logging: +#define NPCM_I2C_EVENT_START BIT(0) +#define NPCM_I2C_EVENT_STOP BIT(1) +#define NPCM_I2C_EVENT_ABORT BIT(2) +#define NPCM_I2C_EVENT_WRITE BIT(3) +#define NPCM_I2C_EVENT_READ BIT(4) +#define NPCM_I2C_EVENT_BER BIT(5) +#define NPCM_I2C_EVENT_NACK BIT(6) +#define NPCM_I2C_EVENT_TO BIT(7) +#define NPCM_I2C_EVENT_EOB BIT(8) + +#define NPCM_I2C_EVENT_LOG(event) (bus->event_log |= event) + +#define SMB_RECOVERY_SUPPORT + +// slave mode: if end device reads more data than available, ask issuer or +// request for more data: +#define SMB_WRAP_AROUND_BUFFER + +// Status of one SMBus module +struct npcm_i2c { + struct i2c_adapter adap; + struct device *dev; + unsigned char __iomem *reg; + spinlock_t lock; /* IRQ synchronization */ + struct completion cmd_complete; + int irq; + int cmd_err; + struct i2c_msg *msgs; + int msgs_num; + int num; + u32 apb_clk; + enum smb_state state; + enum smb_oper operation; + enum smb_mode master_or_slave; + enum smb_state_ind stop_ind; + u8 dest_addr; + u8 *rd_buf; + u16 rd_size; + u16 rd_ind; + u8 *wr_buf; + u16 wr_size; + u16 wr_ind; + bool fifo_use; + u8 threshold_fifo; + + // PEC bit mask per slave address. + // 1: use PEC for this address, + // 0: do not use PEC for this address + u16 PEC_mask; + bool PEC_use; + u8 crc_data; + bool read_block_use; + u8 retry_count; + u8 int_cnt; + u32 event_log; + u32 clk_period_us; + u32 int_time_stamp[2]; +}; + +static inline void _npcm7xx_get_time_stamp(u32 *time_quad0, u32 *time_quad1) +{ + u32 seconds, seconds_last; + u32 ref_clock; + + regmap_read(clk_regmap, NPCM_SECCNT, &seconds_last); + + do { + regmap_read(clk_regmap, NPCM_SECCNT, &seconds); + regmap_read(clk_regmap, NPCM_CNTR25M, &ref_clock); + regmap_read(clk_regmap, NPCM_SECCNT, &seconds_last); + } while (seconds_last != seconds); + + *time_quad0 = ref_clock; + *time_quad1 = seconds; +} + +#define EXT_CLOCK_FREQUENCY_MHZ 25 +#define CNTR25M_ACCURECY EXT_CLOCK_FREQUENCY_MHZ // minimum accurecy + +// Function: _npcm7xx_delay_relative +// Parameters: +// us_delay - number of microseconds to delay since t0_time. +// if zero: no delay. +// +// t0_time - start time , to measure time from. +// get a time stamp, delay us_delay from it. If us_delay has already passed +// since the time stamp , then no delay is executed. returns the time elapsed +// since t0_time + +static inline u32 _npcm7xx_delay_relative(u32 us_delay, u32 t0_time0, + u32 t0_time1) +{ + u32 t1_time_0, t1_time_1; + u32 time_elapsed; + u32 minimum_delay = (us_delay * EXT_CLOCK_FREQUENCY_MHZ) + + CNTR25M_ACCURECY; + + // this is equivalent to microSec/0.64 + minimal tic length. + do { + _npcm7xx_get_time_stamp(&t1_time_0, &t1_time_1); + time_elapsed = ((EXT_CLOCK_FREQUENCY_MHZ * 1000000) * + (t1_time_1 - t0_time1)) + + (t1_time_0 - t0_time0); + } while (time_elapsed < minimum_delay); + + // return elapsed time + return (u32)(time_elapsed / EXT_CLOCK_FREQUENCY_MHZ); +} + +static inline void npcm_smb_select_bank(struct npcm_i2c *bus, + enum smb_bank bank) +{ + if (bus->fifo_use) + iowrite8((ioread8(bus->reg + NPCM_SMBCTL3) & ~SMBCTL3_BNK_SEL) | + FIELD_PREP(SMBCTL3_BNK_SEL, bank), + bus->reg + NPCM_SMBCTL3); +} + +DECLARE_CRC8_TABLE(npcm7xx_crc8); + +static u8 npcm_smb_calc_crc8(u8 crc_data, u8 data) +{ + crc_data = crc8(npcm7xx_crc8, &data, 1, crc_data); + return crc_data; +} + +static void npcm_smb_calc_PEC(struct npcm_i2c *bus, u8 data) +{ + if (bus->PEC_use) + bus->crc_data = npcm_smb_calc_crc8(bus->crc_data, data); +} + +static inline void npcm_smb_wr_byte(struct npcm_i2c *bus, u8 data) +{ + iowrite8(data, bus->reg + NPCM_SMBSDA); + npcm_smb_calc_PEC(bus, data); +} + +static inline void npcm_smb_rd_byte(struct npcm_i2c *bus, u8 *data) +{ + *data = ioread8(bus->reg + NPCM_SMBSDA); + npcm_smb_calc_PEC(bus, *data); +} + +static inline u8 npcm_smb_get_PEC(struct npcm_i2c *bus) +{ + if (bus->PEC_use) + return bus->crc_data; + else + return 0; +} + +static inline void npcm_smb_write_PEC(struct npcm_i2c *bus) +{ + if (bus->PEC_use) { + // get PAC value and write to the bus: + npcm_smb_wr_byte(bus, npcm_smb_get_PEC(bus)); + } +} + +// +// NPCM7XX SMB module allows writing to SCL and SDA pins directly +// without the need to change muxing of pins. +// This feature will be used for recovery sequences i.e. +// +static void npcm_smb_set_SCL(struct i2c_adapter *_adap, int level) +{ +#ifdef SMB_CAPABILITY_FORCE_SCL_SDA + unsigned long flags; + struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap); + + // Select Bank 0 to access NPCM_SMBCTL4 + spin_lock_irqsave(&bus->lock, flags); + npcm_smb_select_bank(bus, SMB_BANK_0); + + // Set SCL_LVL, SDA_LVL bits as Read/Write (R/W) + iowrite8(ioread8(bus->reg + NPCM_SMBCTL4) | SMBCTL4_LVL_WE, + bus->reg + NPCM_SMBCTL4); + + // Set level + iowrite8((ioread8(bus->reg + NPCM_SMBCTL3) + & ~SMBCTL3_SCL_LVL) | FIELD_PREP(SMBCTL3_SCL_LVL, + level), bus->reg + NPCM_SMBCTL3); + + // Set SCL_LVL, SDA_LVL bits as Read Only (RO) + iowrite8(ioread8(bus->reg + NPCM_SMBCTL4) + & ~SMBCTL4_LVL_WE, bus->reg + NPCM_SMBCTL4); + + // Return to Bank 1 + npcm_smb_select_bank(bus, SMB_BANK_1); + spin_unlock_irqrestore(&bus->lock, flags); +#endif +} + +static int npcm_smb_get_SCL(struct i2c_adapter *_adap) +{ + unsigned long flags; + unsigned int ret = 0; + struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap); + + // Select Bank 0 to access NPCM_SMBCTL4 + spin_lock_irqsave(&bus->lock, flags); + npcm_smb_select_bank(bus, SMB_BANK_0); + + // Get SCL level + ret = FIELD_GET(SMBCTL3_SCL_LVL, ioread8(bus->reg + NPCM_SMBCTL3)); + + // Return to Bank 1 + npcm_smb_select_bank(bus, SMB_BANK_1); + spin_unlock_irqrestore(&bus->lock, flags); + return ret; +} + +static int npcm_smb_get_SDA(struct i2c_adapter *_adap) +{ + unsigned long flags; + unsigned int ret = 0; + struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap); + + // Select Bank 0 to access NPCM_SMBCTL4 + spin_lock_irqsave(&bus->lock, flags); + npcm_smb_select_bank(bus, SMB_BANK_0); + + // Get SDA level + ret = FIELD_GET(SMBCTL3_SDA_LVL, ioread8(bus->reg + NPCM_SMBCTL3)); + + // Return to Bank 1 + npcm_smb_select_bank(bus, SMB_BANK_1); + spin_unlock_irqrestore(&bus->lock, flags); + return ret; +} + +static inline u16 npcm_smb_get_index(struct npcm_i2c *bus) +{ + u16 index = 0; + + if (bus->operation == SMB_READ_OPER) + index = bus->rd_ind; + else if (bus->operation == SMB_WRITE_OPER) + index = bus->wr_ind; + + return index; +} + +// quick protocol: +static inline bool npcm_smb_is_quick(struct npcm_i2c *bus) +{ + if (bus->wr_size == 0 && bus->rd_size == 0) + return true; + return false; +} + +static void npcm_smb_disable(struct npcm_i2c *bus) +{ + int i; + + // select bank 0 for SMB addresses + npcm_smb_select_bank(bus, SMB_BANK_0); + + // Slave Addresses Removal + for (i = SMB_SLAVE_ADDR1; i < SMB_NUM_OF_ADDR; i++) + iowrite8(0, bus->reg + NPCM_SMBADDR[i]); + + // select bank 0 for SMB addresses + npcm_smb_select_bank(bus, SMB_BANK_1); + + // Disable module. + iowrite8(ioread8(bus->reg + NPCM_SMBCTL2) & ~SMBCTL2_ENABLE, + bus->reg + NPCM_SMBCTL2); + + // Set module disable + bus->state = SMB_DISABLE; +} + +static void npcm_smb_enable(struct npcm_i2c *bus) +{ + iowrite8((ioread8(bus->reg + NPCM_SMBCTL2) | SMBCTL2_ENABLE), + bus->reg + NPCM_SMBCTL2); +} + +// enable\disable end of busy (EOB) interrupt +static inline void npcm_smb_eob_int(struct npcm_i2c *bus, bool enable) +{ + if (enable) { + iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) | + NPCM_SMBCTL1_EOBINTE) & ~NPCM_SMBCTL1_RWS_FIELDS, + bus->reg + NPCM_SMBCTL1); + } else { + iowrite8(ioread8(bus->reg + NPCM_SMBCTL1) & + ~NPCM_SMBCTL1_EOBINTE & ~NPCM_SMBCTL1_RWS_FIELDS, + bus->reg + NPCM_SMBCTL1); + + // Clear EO_BUSY pending bit: + iowrite8(ioread8(bus->reg + NPCM_SMBCST3) | + NPCM_SMBCST3_EO_BUSY, bus->reg + NPCM_SMBCST3); + } +} + +static inline bool npcm_smb_tx_fifo_full(struct npcm_i2c *bus) +{ + // check if TX FIFO full: + return (bool)FIELD_GET(NPCM_SMBTXF_STS_TX_THST, + ioread8(bus->reg + NPCM_SMBTXF_STS)); +} + +static inline bool npcm_smb_rx_fifo_full(struct npcm_i2c *bus) +{ + // check if RX FIFO full: + return (bool)FIELD_GET(NPCM_SMBRXF_STS_RX_THST, + ioread8(bus->reg + NPCM_SMBRXF_STS)); +} + +static inline void npcm_smb_clear_tx_fifo(struct npcm_i2c *bus) +{ + // clear TX FIFO: + iowrite8(ioread8(bus->reg + NPCM_SMBTXF_STS) | + NPCM_SMBTXF_STS_TX_THST, + bus->reg + NPCM_SMBTXF_STS); +} + +static inline void npcm_smb_clear_rx_fifo(struct npcm_i2c *bus) +{ + // clear RX FIFO: + iowrite8(ioread8(bus->reg + NPCM_SMBRXF_STS) | + NPCM_SMBRXF_STS_RX_THST, + bus->reg + NPCM_SMBRXF_STS); +} + +static void npcm_smb_int_enable(struct npcm_i2c *bus, bool enable) +{ + if (enable) + iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) | + NPCM_SMBCTL1_INTEN) & ~NPCM_SMBCTL1_RWS_FIELDS, + bus->reg + NPCM_SMBCTL1); + else + iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) & + ~NPCM_SMBCTL1_INTEN) & ~NPCM_SMBCTL1_RWS_FIELDS, + bus->reg + NPCM_SMBCTL1); +} + +static inline void npcm_smb_master_start(struct npcm_i2c *bus) +{ + NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_START); + + iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) | NPCM_SMBCTL1_START) & + ~(NPCM_SMBCTL1_STOP | NPCM_SMBCTL1_ACK), + bus->reg + NPCM_SMBCTL1); +} + +static inline void npcm_smb_master_stop(struct npcm_i2c *bus) +{ + NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_STOP); + + // override HW issue: SMBus may fail to supply stop condition in Master + // Write operation. + // Need to delay at least 5 us from the last int, before issueing a stop + _npcm7xx_delay_relative(5, bus->int_time_stamp[0], + bus->int_time_stamp[1]); + + iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) | NPCM_SMBCTL1_STOP) & + ~(NPCM_SMBCTL1_START | NPCM_SMBCTL1_ACK), + bus->reg + NPCM_SMBCTL1); + + if (bus->fifo_use) { + npcm_smb_select_bank(bus, SMB_BANK_1); + + if (bus->operation == SMB_READ_OPER) + npcm_smb_clear_rx_fifo(bus); + else + npcm_smb_clear_tx_fifo(bus); + + iowrite8(ioread8(bus->reg + NPCM_SMBFIF_CTS) | + NPCM_SMBFIF_CTS_SLVRSTR | + NPCM_SMBFIF_CTS_RXF_TXE, + bus->reg + NPCM_SMBFIF_CTS); + + iowrite8(0, bus->reg + NPCM_SMBTXF_CTL); + } +} + +static inline void npcm_smb_abort_data(struct npcm_i2c *bus) +{ + unsigned int timeout = ABORT_TIMEOUT; + u8 data; + + NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_ABORT); + // Generate a STOP condition + npcm_smb_master_stop(bus); + npcm_smb_rd_byte(bus, &data); + + // Clear NEGACK, STASTR and BER bits + iowrite8(NPCM_SMBST_STASTR | NPCM_SMBST_NEGACK | + NPCM_SMBST_BER, bus->reg + NPCM_SMBST); + + // Wait till STOP condition is generated + while (FIELD_GET(NPCM_SMBCTL1_STOP, ioread8(bus->reg + NPCM_SMBCTL1))) { + timeout--; + if (!FIELD_GET(NPCM_SMBCTL1_STOP, + ioread8(bus->reg + NPCM_SMBCTL1))) + break; + if (timeout <= 1) { + dev_err(bus->dev, "%s, abort timeout!\n", __func__); + break; + } + } +} + +static inline void npcm_smb_stall_after_start(struct npcm_i2c *bus, bool stall) +{ + if (stall) + iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) | + NPCM_SMBCTL1_STASTRE) & ~NPCM_SMBCTL1_RWS_FIELDS, + bus->reg + NPCM_SMBCTL1); + else + iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) & + ~NPCM_SMBCTL1_STASTRE) & ~NPCM_SMBCTL1_RWS_FIELDS, + bus->reg + NPCM_SMBCTL1); +} + +static inline void npcm_smb_nack(struct npcm_i2c *bus) +{ + if (bus->rd_ind < (bus->rd_size - 1)) + dev_info(bus->dev, + "\tNACK err bus%d, SA=0x%x, rd(%d\%d), op=%d st=%d\n", + bus->num, bus->dest_addr, bus->rd_ind, bus->rd_size, + bus->operation, bus->state); + iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) | NPCM_SMBCTL1_ACK) & + ~(NPCM_SMBCTL1_STOP | NPCM_SMBCTL1_START), + bus->reg + NPCM_SMBCTL1); +} + +static void npcm_smb_reset(struct npcm_i2c *bus) +{ + // Save NPCM_SMBCTL1 relevant bits. It is being cleared when the + // module is disabled + u8 smbctl1 = ioread8(bus->reg + NPCM_SMBCTL1) & (NPCM_SMBCTL1_GCMEN + | NPCM_SMBCTL1_INTEN + | NPCM_SMBCTL1_NMINTE); + + // Disable the SMB module + iowrite8((ioread8(bus->reg + NPCM_SMBCTL2) & ~SMBCTL2_ENABLE), + bus->reg + NPCM_SMBCTL2); + + // Enable the SMB module + npcm_smb_enable(bus); + + // Restore NPCM_SMBCTL1 status + iowrite8(smbctl1 & ~NPCM_SMBCTL1_RWS_FIELDS, bus->reg + NPCM_SMBCTL1); + + // Reset driver status + bus->state = SMB_IDLE; + // + // Configure FIFO disabled mode so slave will not use fifo + // (master will set it on if supported) + iowrite8(ioread8(bus->reg + NPCM_SMBFIF_CTL) & + ~NPCM_SMBFIF_CTL_FIFO_EN, + bus->reg + NPCM_SMBFIF_CTL); + bus->fifo_use = false; +} + +static inline bool npcm_smb_is_master(struct npcm_i2c *bus) +{ + return (bool)FIELD_GET(NPCM_SMBST_MASTER, + ioread8(bus->reg + NPCM_SMBST)); +} + +static int npcm_smb_master_abort(struct npcm_i2c *bus) +{ + int ret = -(EIO); + + // Only current master is allowed to issue Stop Condition + if (npcm_smb_is_master(bus)) { + npcm_smb_abort_data(bus); + ret = 0; + } + + npcm_smb_reset(bus); + + return ret; +} + +static void npcm_smb_callback(struct npcm_i2c *bus, + enum smb_state_ind op_status, u16 info) +{ + struct i2c_msg *msgs = bus->msgs; + int msgs_num = bus->msgs_num; + + switch (op_status) { + case SMB_MASTER_DONE_IND: + // Master transaction finished and all transmit bytes were sent + // info: number of bytes actually received after the Master + // receive operation (if Master didn't issue receive it + // should be 0) + // Notify that not all data was received on Master or Slave + // info: + // on receive: number of actual bytes received + // when PEC is used even if 'info' is the expected number + // of bytes, it means that PEC error occurred. + { + if (msgs[0].flags & I2C_M_RD) + msgs[0].len = info; + else if (msgs_num == 2 && msgs[1].flags & I2C_M_RD) + msgs[1].len = info; + + bus->cmd_err = 0; + complete(&bus->cmd_complete); + } + break; + + case SMB_NO_DATA_IND: + // Notify that not all data was received on Master or Slave + // info: + //on receive: number of actual bytes received + // when PEC is used even if 'info' is the expected number + // of bytes,it means that PEC error occurred. + { + if (msgs[0].flags & I2C_M_RD) + msgs[0].len = info; + else if (msgs_num == 2 && msgs[1].flags & I2C_M_RD) + msgs[1].len = info; + + bus->cmd_err = -EFAULT; + complete(&bus->cmd_complete); + } + break; + case SMB_NACK_IND: + // MASTER transmit got a NAK before transmitting all bytes + // info: number of transmitted bytes + bus->cmd_err = -EAGAIN; + complete(&bus->cmd_complete); + + break; + case SMB_BUS_ERR_IND: + // Bus error + // info: has no meaning + bus->cmd_err = -EIO; + complete(&bus->cmd_complete); + break; + case SMB_WAKE_UP_IND: + // SMBus wake up + // info: has no meaning + break; + default: + break; + } +} + +static u32 npcm_smb_get_fifo_fullness(struct npcm_i2c *bus) +{ + if (bus->operation == SMB_WRITE_OPER) + return FIELD_GET(NPCM_SMBTXF_STS_TX_BYTES, + ioread8(bus->reg + NPCM_SMBTXF_STS)); + else if (bus->operation == SMB_READ_OPER) + return FIELD_GET(NPCM_SMBRXF_STS_RX_BYTES, + ioread8(bus->reg + NPCM_SMBRXF_STS)); + return 0; +} + +static void npcm_smb_write_to_fifo(struct npcm_i2c *bus, u16 max_bytes_to_send) +{ + // Fill the FIFO, while the FIFO is not full and there are more bytes to + // write + while ((max_bytes_to_send--) && (SMBUS_FIFO_SIZE - + npcm_smb_get_fifo_fullness(bus))) { + // write the data + if (bus->wr_ind < bus->wr_size) { + if (bus->PEC_use && + (bus->wr_ind + 1 == bus->wr_size) && + (bus->rd_size == 0 || + bus->master_or_slave == SMB_SLAVE)) { + // Master send PEC in write protocol, Slave send + // PEC in read protocol. + npcm_smb_write_PEC(bus); + bus->wr_ind++; + } else { + npcm_smb_wr_byte(bus, + bus->wr_buf[bus->wr_ind++]); + } + } else { +#ifdef SMB_WRAP_AROUND_BUFFER + // We're out of bytes. Ask the higher level for + // more bytes. Let it know that driver + // used all its' bytes + + npcm_smb_clear_tx_fifo(bus); + + // Reset state for the remaining bytes transaction + bus->state = SMB_SLAVE_MATCH; + + // Notify upper layer of transaction completion + npcm_smb_callback(bus, SMB_SLAVE_XMIT_MISSING_DATA_IND, + bus->wr_ind); + + iowrite8(NPCM_SMBST_SDAST, bus->reg + NPCM_SMBST); +#else + npcm_smb_wr_byte(bus, 0xFF); +#endif + } + } +} + +// configure the FIFO before using it. If nread is -1 RX FIFO will not be +// configured. same for nwrite +static void npcm_smb_set_fifo(struct npcm_i2c *bus, int nread, int nwrite) +{ + if (!bus->fifo_use) + return; + npcm_smb_select_bank(bus, SMB_BANK_1); + npcm_smb_clear_tx_fifo(bus); + npcm_smb_clear_rx_fifo(bus); + + // configure RX FIFO + if (nread > 0) { + // clear LAST bit: + iowrite8(ioread8(bus->reg + NPCM_SMBRXF_CTL) & + (~NPCM_SMBRXF_CTL_LAST_PEC), + bus->reg + NPCM_SMBRXF_CTL); + + if (nread > SMBUS_FIFO_SIZE) + iowrite8((ioread8(bus->reg + NPCM_SMBRXF_CTL) & + ~NPCM_SMBRXF_CTL_RX_THR) + | FIELD_PREP(NPCM_SMBRXF_CTL_RX_THR, + SMBUS_FIFO_SIZE), bus->reg + NPCM_SMBRXF_CTL); + else + iowrite8((ioread8(bus->reg + NPCM_SMBRXF_CTL) & + ~NPCM_SMBRXF_CTL_RX_THR) | + FIELD_PREP(NPCM_SMBRXF_CTL_RX_THR, + (u8)(nread)), + bus->reg + NPCM_SMBRXF_CTL); + + if (nread <= SMBUS_FIFO_SIZE && !bus->read_block_use) + iowrite8(ioread8(bus->reg + NPCM_SMBRXF_CTL) | + NPCM_SMBRXF_CTL_LAST_PEC, + bus->reg + NPCM_SMBRXF_CTL); + } + + // configure TX FIFO + if (nwrite > 0) { + if (nwrite > SMBUS_FIFO_SIZE) + // data to send is more then FIFO size. + // Configure the FIFO int to be mid of FIFO. + iowrite8(NPCM_SMBTXF_CTL_THR_TXIE | + (SMBUS_FIFO_SIZE / 2), + bus->reg + NPCM_SMBTXF_CTL); + else if (nwrite > (SMBUS_FIFO_SIZE / 2) && + bus->wr_ind != 0) + // wr_ind != 0 means that this is not the first + // write. since int is in the mid of FIFO, only + // half of the fifo is empty. + // Continue to configure the FIFO int to be mid + // of FIFO. + iowrite8(NPCM_SMBTXF_CTL_THR_TXIE | + (SMBUS_FIFO_SIZE / 2), + bus->reg + NPCM_SMBTXF_CTL); + else + // This is the either first write (wr_ind = 0) + // and data to send is less or equal to FIFO + // size. + // Or this is the last write and data to send + // is less or equal half FIFO size. + // In both cases disable the FIFO threshold int. + // The next int will happen after the FIFO will + // get empty. + iowrite8(0, bus->reg + NPCM_SMBTXF_CTL); + npcm_smb_clear_tx_fifo(bus); + } +} + +static void npcm_smb_read_from_fifo(struct npcm_i2c *bus, u8 bytes_in_fifo) +{ + while (bytes_in_fifo--) { + // Keep read data + u8 data = ioread8(bus->reg + NPCM_SMBSDA); + + npcm_smb_calc_PEC(bus, data); + if (bus->rd_ind < bus->rd_size) { + bus->rd_buf[bus->rd_ind++] = data; + if (bus->rd_ind == 1 && bus->read_block_use) + // First byte indicates length in block protocol + bus->rd_size = data; + } + } +} + +static void npcm_smb_master_fifo_read(struct npcm_i2c *bus) +{ + u16 rcount; + u8 fifo_bytes; + enum smb_state_ind ind = SMB_MASTER_DONE_IND; + + rcount = bus->rd_size - bus->rd_ind; + + // In order not to change the RX_TRH during transaction (we found that + // this might be problematic if it takes too much time to read the FIFO) + // we read the data in the following way. If the number of bytes to + // read == FIFO Size + C (where C < FIFO Size)then first read C bytes + // and in the next int we read rest of the data. + if (rcount < (2 * SMBUS_FIFO_SIZE) && rcount > SMBUS_FIFO_SIZE) + fifo_bytes = (u8)(rcount - SMBUS_FIFO_SIZE); + else + fifo_bytes = npcm_smb_get_fifo_fullness(bus); + + if (rcount - fifo_bytes == 0) { + // last byte is about to be read - end of transaction. + // Stop should be set before reading last byte. + npcm_smb_eob_int(bus, true); + npcm_smb_master_stop(bus); + npcm_smb_read_from_fifo(bus, fifo_bytes); + + if (npcm_smb_get_PEC(bus) != 0) + ind = SMB_MASTER_PEC_ERR_IND; + bus->state = SMB_STOP_PENDING; + bus->stop_ind = ind; + + } else { + npcm_smb_read_from_fifo(bus, fifo_bytes); + rcount = bus->rd_size - bus->rd_ind; + npcm_smb_set_fifo(bus, rcount, -1); + } +} + +static void npcm_smb_int_master_handler_write(struct npcm_i2c *bus) +{ + u16 wcount; + + NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_WRITE); + if (bus->fifo_use) + npcm_smb_clear_tx_fifo(bus); + + // Master write operation - last byte handling + if (bus->wr_ind == bus->wr_size) { + if (bus->fifo_use && npcm_smb_get_fifo_fullness(bus) > 0) + // No more bytes to send (to add to the FIFO), however the FIFO is not + // empty yet. It is still in the middle of tx. Currently there's nothing + // to do except for waiting to the end of the tx. + // We will get an int when the FIFO will get empty. + return; + + if (bus->rd_size == 0) { + // all bytes have been written, in a pure wr operation + npcm_smb_eob_int(bus, true); + + // Issue a STOP condition on the bus + npcm_smb_master_stop(bus); + // Clear SDA Status bit (by writing dummy byte) + npcm_smb_wr_byte(bus, 0xFF); + + bus->state = SMB_STOP_PENDING; + bus->stop_ind = SMB_MASTER_DONE_IND; + } else { + // last write-byte written on previous int - need to + // restart & send slave address + if (bus->PEC_use && !bus->read_block_use && + !npcm_smb_is_quick(bus)) + // PEC is used but the protocol is not block read + // then we add extra bytes for PEC support + bus->rd_size += 1; + + if (bus->fifo_use) { + if (bus->rd_size == 1 || bus->read_block_use) { + // SMBus Block read transaction. + iowrite8(0, bus->reg + NPCM_SMBTXF_CTL); + iowrite8(1, bus->reg + NPCM_SMBRXF_CTL); + } + } + + npcm_smb_set_fifo(bus, bus->rd_size, -1); + + // Generate (Repeated) Start upon next write to SDA + npcm_smb_master_start(bus); + + if (bus->rd_size == 1) + + // Receiving one byte only - stall after successful completion of send + // address byte. If we NACK here, and slave doesn't ACK the address, we + // might unintentionally NACK the next multi-byte read + + npcm_smb_stall_after_start(bus, true); + + // send the slave address in read direction + npcm_smb_wr_byte(bus, bus->dest_addr | 0x1); + + // Next int will occur on read + bus->operation = SMB_READ_OPER; + } + } else { + if (bus->PEC_use && !npcm_smb_is_quick(bus)) + // extra bytes for PEC support + bus->wr_size += 1; + + // write next byte not last byte and not slave address + if (!bus->fifo_use || bus->wr_size == 1) { + if (bus->PEC_use && bus->rd_size == 0 && + (bus->wr_ind + 1 == bus->wr_size)) { + // Master write protocol to send PEC byte. + npcm_smb_write_PEC(bus); + bus->wr_ind++; + } else { + npcm_smb_wr_byte(bus, + bus->wr_buf[bus->wr_ind++]); + } + } else { // FIFO is used + wcount = bus->wr_size - bus->wr_ind; + npcm_smb_set_fifo(bus, -1, wcount); + npcm_smb_write_to_fifo(bus, wcount); + } + } +} + +static void npcm_smb_int_master_handler_read(struct npcm_i2c *bus) +{ + u16 block_zero_bytes; + u32 fifo_bytes; + + // Master read operation (pure read or following a write operation). + NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_READ); + + // Initialize number of bytes to include only the first byte (presents + // a case where number of bytes to read is zero); add PEC if applicable + block_zero_bytes = 1; + if (bus->PEC_use) + block_zero_bytes++; + + fifo_bytes = FIELD_GET(NPCM_SMBRXF_CTL_RX_THR, + ioread8(bus->reg + NPCM_SMBRXF_CTL)); + + // Perform master read, distinguishing between last byte and the rest of + // the bytes. The last byte should be read when the clock is stopped + if ((bus->rd_ind < (bus->rd_size - 1)) || bus->fifo_use) { + u8 data; + + // byte to be read is not the last one + // Check if byte-before-last is about to be read + if ((bus->rd_ind == (bus->rd_size - 2)) && + !bus->fifo_use){ + // Set nack before reading byte-before-last, so that + // nack will be generated after receive of last byte + npcm_smb_nack(bus); + + if (!FIELD_GET(NPCM_SMBST_SDAST, + ioread8(bus->reg + NPCM_SMBST))) { + // No data available - reset state for new xfer + bus->state = SMB_IDLE; + + // Notify upper layer of rx completion + npcm_smb_callback(bus, SMB_NO_DATA_IND, + bus->rd_ind); + } + } else if (bus->rd_ind == 0) { //first byte handling: + // in block protocol first byte is the size + if (bus->read_block_use) { + npcm_smb_rd_byte(bus, &data); + + // First byte indicates length in block protocol + bus->rd_buf[bus->rd_ind++] = data; + bus->rd_size = data + 1; + + if (bus->PEC_use) { + bus->rd_size += 1; + data += 1; + } + + if (bus->fifo_use) { + iowrite8(NPCM_SMBFIF_CTS_RXF_TXE | + ioread8(bus->reg + + NPCM_SMBFIF_CTS), + bus->reg + NPCM_SMBFIF_CTS); + + // first byte in block protocol + // is zero -> not supported. read at + // least one byte + if (data == 0) + data = 1; + } + npcm_smb_set_fifo(bus, bus->rd_size, -1); + } else { + if (!bus->fifo_use) { + npcm_smb_rd_byte(bus, &data); + bus->rd_buf[bus->rd_ind++] = data; + } else { + npcm_smb_clear_tx_fifo(bus); + npcm_smb_master_fifo_read(bus); + } + } + + } else { + if (bus->fifo_use) { + if (bus->rd_size == block_zero_bytes && + bus->read_block_use) { + npcm_smb_eob_int(bus, true); + npcm_smb_master_stop(bus); + npcm_smb_read_from_fifo(bus, + fifo_bytes); + bus->state = SMB_STOP_PENDING; + bus->stop_ind = SMB_BLOCK_BYTES_ERR_IND; + + } else { + npcm_smb_master_fifo_read(bus); + } + } else { + npcm_smb_rd_byte(bus, &data); + bus->rd_buf[bus->rd_ind++] = data; + } + } + } else { + // last byte is about to be read - end of transaction. + // Stop should be set before reading last byte. + u8 data; + enum smb_state_ind ind = SMB_MASTER_DONE_IND; + + npcm_smb_eob_int(bus, true); + + npcm_smb_master_stop(bus); + + npcm_smb_rd_byte(bus, &data); + + if (bus->rd_size == block_zero_bytes && bus->read_block_use) { + ind = SMB_BLOCK_BYTES_ERR_IND; + } else { + bus->rd_buf[bus->rd_ind++] = data; + if (npcm_smb_get_PEC(bus) != 0) + ind = SMB_MASTER_PEC_ERR_IND; + } + + bus->state = SMB_STOP_PENDING; + bus->stop_ind = ind; + } +} + +static void npcm_smb_int_master_handler(struct npcm_i2c *bus) +{ + // A negative acknowledge has occurred + if (FIELD_GET(NPCM_SMBST_NEGACK, ioread8(bus->reg + NPCM_SMBST))) { + NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_NACK); + if (bus->fifo_use) { + // if there are still untransmitted bytes in TX FIFO + // reduce them from wr_ind + + if (bus->operation == SMB_WRITE_OPER) + bus->wr_ind -= npcm_smb_get_fifo_fullness(bus); + // clear the FIFO + iowrite8(NPCM_SMBFIF_CTS_CLR_FIFO, + bus->reg + NPCM_SMBFIF_CTS); + } + + // In master write operation, NACK is a problem + // number of bytes sent to master less than required + npcm_smb_master_abort(bus); + bus->state = SMB_IDLE; + + // In Master mode, NEGACK should be cleared only after + // generating STOP. + // In such case, the bus is released from stall only after the + // software clears NEGACK bit. + // Then a Stop condition is sent. + iowrite8(NPCM_SMBST_NEGACK, bus->reg + NPCM_SMBST); + npcm_smb_callback(bus, SMB_NACK_IND, bus->wr_ind); + return; + } + + // Master mode: a Bus Error has been identified + if (FIELD_GET(NPCM_SMBST_BER, ioread8(bus->reg + NPCM_SMBST))) { + // Check whether bus arbitration or Start or Stop during data + // xfer bus arbitration problem should not result in recovery + if (npcm_smb_is_master(bus)) { + // Only current master is allowed to issue stop + npcm_smb_master_abort(bus); + } else { + // Bus arbitration loss + if (bus->retry_count-- > 0) { + // Perform a retry (generate a start condition) + // as soon as the SMBus is free + iowrite8(NPCM_SMBST_BER, bus->reg + NPCM_SMBST); + npcm_smb_master_start(bus); + return; + } + } + iowrite8(NPCM_SMBST_BER, bus->reg + NPCM_SMBST); + bus->state = SMB_IDLE; + npcm_smb_callback(bus, SMB_BUS_ERR_IND, + npcm_smb_get_index(bus)); + return; + } + + // A Master End of Busy (meaning Stop Condition happened) + // End of Busy int is on and End of Busy is set + if ((FIELD_GET(NPCM_SMBCTL1_EOBINTE, + ioread8(bus->reg + NPCM_SMBCTL1)) == 1) && + (FIELD_GET(NPCM_SMBCST3_EO_BUSY, + ioread8(bus->reg + NPCM_SMBCST3)))) { + NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_EOB); + npcm_smb_eob_int(bus, false); + bus->state = SMB_IDLE; + if (npcm_smb_is_quick(bus)) + npcm_smb_callback(bus, bus->stop_ind, 0); + else + npcm_smb_callback(bus, bus->stop_ind, bus->rd_ind); + return; + } + + // Address sent and requested stall occurred (Master mode) + if (FIELD_GET(NPCM_SMBST_STASTR, ioread8(bus->reg + NPCM_SMBST))) { + // Check for Quick Command SMBus protocol + if (npcm_smb_is_quick(bus)) { + npcm_smb_eob_int(bus, true); + npcm_smb_master_stop(bus); + + // Update status + bus->state = SMB_STOP_PENDING; + bus->stop_ind = SMB_MASTER_DONE_IND; + + } else if (bus->rd_size == 1) { + // Receiving one byte only - set NACK after ensuring + // slave ACKed the address byte + npcm_smb_nack(bus); + } + + // Reset stall-after-address-byte + npcm_smb_stall_after_start(bus, false); + + // Clear stall only after setting STOP + iowrite8(NPCM_SMBST_STASTR, bus->reg + NPCM_SMBST); + + return; + } + + // SDA status is set - transmit or receive, master + if (FIELD_GET(NPCM_SMBST_SDAST, ioread8(bus->reg + NPCM_SMBST)) || + (bus->fifo_use && + (npcm_smb_tx_fifo_full(bus) || npcm_smb_rx_fifo_full(bus)))) { + // Status Bit is cleared by writing to or reading from SDA + // (depending on current direction) + switch (bus->state) { + // Handle unsuccessful bus mastership + case SMB_IDLE: + npcm_smb_master_abort(bus); + return; + + case SMB_MASTER_START: + if (npcm_smb_is_master(bus)) { + u8 addr_byte = bus->dest_addr; + + bus->crc_data = 0; + if (npcm_smb_is_quick(bus)) { + // Need to stall after successful + // completion of sending address byte + npcm_smb_stall_after_start(bus, true); + } else if (bus->wr_size == 0) { + // Set direction to Read + addr_byte |= (u8)0x1; + bus->operation = SMB_READ_OPER; + } else { + bus->operation = SMB_WRITE_OPER; + } + + // Receiving one byte only - stall after successful completion of + // sending address byte. If we NACK here, and slave doesn't ACK the + // address, we might unintentionally NACK the next multi-byte read + if (bus->wr_size == 0 && bus->rd_size == 1) + npcm_smb_stall_after_start(bus, true); + + // Write the address to the bus + bus->state = SMB_OPER_STARTED; + npcm_smb_wr_byte(bus, addr_byte); + } else { + dev_err(bus->dev, + "SDA, bus%d is not master, wr %d 0x%x...\n", + bus->num, bus->wr_size, + bus->wr_buf[0]); + } + break; + + // SDA status is set - transmit or receive: Handle master mode + case SMB_OPER_STARTED: + if (bus->operation == SMB_WRITE_OPER) + npcm_smb_int_master_handler_write(bus); + else if (bus->operation == SMB_READ_OPER) + npcm_smb_int_master_handler_read(bus); + else + pr_err("I2C%d: unknown operation\n", bus->num); + break; + default: + dev_err(bus->dev, "i2c%d master sda err on state machine\n", + bus->num); + } + } +} + +static int npcm_smb_recovery(struct i2c_adapter *_adap) +{ + u8 iter = 27; // Allow one byte to be sent by the Slave + u16 timeout; + bool done = false; + struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap); + + dev_info(bus->dev, "recovery bus%d\n", bus->num); + + might_sleep(); + + // Disable int + npcm_smb_int_enable(bus, false); + + // Check If the SDA line is active (low) + if (FIELD_GET(NPCM_SMBCST_TSDA, ioread8(bus->reg + NPCM_SMBCST)) == 0) { + // Repeat the following sequence until SDA is released + do { + // Issue a single SCL cycle + iowrite8(NPCM_SMBCST_TGSCL, bus->reg + NPCM_SMBCST); + timeout = ABORT_TIMEOUT; + while (timeout != 0 && + FIELD_GET(NPCM_SMBCST_TGSCL, + ioread8(bus->reg + NPCM_SMBCST) == 0)) + timeout--; + + // If SDA line is inactive (high), stop + if (FIELD_GET(NPCM_SMBCST_TSDA, + ioread8(bus->reg + NPCM_SMBCST)) == 1) + done = true; + } while ((!done) && (--iter != 0)); + + // If SDA line is released (high) + if (done) { + // Clear BB (BUS BUSY) bit + iowrite8(NPCM_SMBCST_BB, bus->reg + NPCM_SMBCST); + + // Generate a START, to synchronize Master and Slave + npcm_smb_master_start(bus); + + // Wait until START condition is sent, or timeout + timeout = ABORT_TIMEOUT; + while (timeout != 0 && !npcm_smb_is_master(bus)) + timeout--; + + // If START condition was sent + if (timeout > 0) { + // Send an address byte + npcm_smb_wr_byte(bus, bus->dest_addr); + + // Generate a STOP condition + npcm_smb_master_stop(bus); + } + return 0; + } + } + + // check if success: + if (npcm_smb_get_SCL(_adap) == 1 && npcm_smb_get_SDA(_adap) == 1) + goto npcm_smb_recovery_done; + + // hold clock low for 35ms: 25 and some spair: + npcm_smb_set_SCL(_adap, 0); + usleep_range(35000, 40000); + npcm_smb_set_SCL(_adap, 1); + usleep_range(1000, 2000); + + // check if success: + if (npcm_smb_get_SCL(_adap) == 1 && npcm_smb_get_SDA(_adap) == 1) + goto npcm_smb_recovery_done; + + return 0; + +npcm_smb_recovery_done: + + npcm_smb_int_enable(bus, true); + return -(ENOTRECOVERABLE); +} + +static bool npcm_smb_init_clk(struct npcm_i2c *bus, enum smb_mode mode, + u32 bus_freq) +{ + u32 k1 = 0; + u32 k2 = 0; + u8 dbnct = 0; + u32 sclfrq = 0; + u8 hldt = 7; + bool fast_mode = false; + u32 src_clk_freq; // in KHz + + src_clk_freq = bus->apb_clk / 1000; + + if (bus_freq <= SMBUS_FREQ_100KHZ) { + sclfrq = src_clk_freq / (bus_freq * 4); + + if (sclfrq < SCLFRQ_MIN || sclfrq > SCLFRQ_MAX) + return false; + + if (src_clk_freq >= 40000) + hldt = 17; + else if (src_clk_freq >= 12500) + hldt = 15; + else + hldt = 7; + } + + else if (bus_freq == SMBUS_FREQ_400KHZ) { + sclfrq = 0; + fast_mode = true; + + if ((mode == SMB_MASTER && src_clk_freq < 7500) || + (mode == SMB_SLAVE && src_clk_freq < 10000)) + // 400KHZ cannot be supported for master core clock < 7.5 MHZ + // or slave core clock < 10 MHZ + return false; + + // Master or Slave with frequency > 25 MHZ + if (mode == SMB_MASTER || src_clk_freq > 25000) { + hldt = (u8)__KERNEL_DIV_ROUND_UP(src_clk_freq * 300, + 1000000) + 7; + if (mode == SMB_MASTER) { + k1 = __KERNEL_DIV_ROUND_UP(src_clk_freq * 1600, + 1000000); + k2 = __KERNEL_DIV_ROUND_UP(src_clk_freq * 900, + 1000000); + k1 = round_up(k1, 2); + k2 = round_up(k2 + 1, 2); + if (k1 < SCLFRQ_MIN || k1 > SCLFRQ_MAX || + k2 < SCLFRQ_MIN || k2 > SCLFRQ_MAX) + return false; + } + } else { // Slave with frequency 10-25 MHZ + hldt = 7; + dbnct = 2; + } + } + + else if (bus_freq == SMBUS_FREQ_1MHZ) { + sclfrq = 0; + fast_mode = true; + + if ((mode == SMB_MASTER && src_clk_freq < 15000) || + (mode == SMB_SLAVE && src_clk_freq < 24000)) + // 1MHZ cannot be supported for master core clock < 15 MHZ + // or slave core clock < 24 MHZ + return false; + + if (mode == SMB_MASTER) { + k1 = round_up((__KERNEL_DIV_ROUND_UP(src_clk_freq * 620, + 1000000)), 2); + k2 = round_up((__KERNEL_DIV_ROUND_UP(src_clk_freq * 380, + 1000000) + 1), 2); + if (k1 < SCLFRQ_MIN || k1 > SCLFRQ_MAX || + k2 < SCLFRQ_MIN || k2 > SCLFRQ_MAX) { + return false; + } + } + + // Master or Slave with frequency > 40 MHZ + if (mode == SMB_MASTER || src_clk_freq > 40000) { + // Set HLDT: + // SDA hold time: (HLDT-7) * T(CLK) >= 120 + // HLDT = 120/T(CLK) + 7 = 120 * FREQ(CLK) + 7 + hldt = (u8)__KERNEL_DIV_ROUND_UP(src_clk_freq * 120, + 1000000) + 7; + + // Slave with frequency 24-40 MHZ + } else { + hldt = 7; + dbnct = 2; + } + } + + // Frequency larger than 1 MHZ + else + return false; + + // After clock parameters calculation update the reg + iowrite8((ioread8(bus->reg + NPCM_SMBCTL2) + & ~SMBCTL2_SCLFRQ6_0) | FIELD_PREP(SMBCTL2_SCLFRQ6_0, + sclfrq & 0x7F), bus->reg + NPCM_SMBCTL2); + + iowrite8((ioread8(bus->reg + NPCM_SMBCTL3) & ~SMBCTL3_SCLFRQ8_7) | + FIELD_PREP(SMBCTL3_SCLFRQ8_7, (sclfrq >> 7) & 0x3), + bus->reg + NPCM_SMBCTL3); + + iowrite8((ioread8(bus->reg + NPCM_SMBCTL3) & ~SMBCTL3_400K_MODE) | + FIELD_PREP(SMBCTL3_400K_MODE, fast_mode), + bus->reg + NPCM_SMBCTL3); + + // Select Bank 0 to access NPCM_SMBCTL4/NPCM_SMBCTL5 + npcm_smb_select_bank(bus, SMB_BANK_0); + + if (bus_freq >= SMBUS_FREQ_400KHZ) { + // k1 and k2 are relevant for master mode only + if (mode == SMB_MASTER) { + // Set SCL Low/High Time: + // k1 = 2 * SCLLT7-0 -> Low Time = k1 / 2 + // k2 = 2 * SCLLT7-0 -> High Time = k2 / 2 + iowrite8((u8)k1 / 2, bus->reg + NPCM_SMBSCLLT); + iowrite8((u8)k2 / 2, bus->reg + NPCM_SMBSCLHT); + } else { // DBNCT is relevant for slave mode only + iowrite8((ioread8(bus->reg + NPCM_SMBCTL5) & + ~SMBCTL5_DBNCT) | + FIELD_PREP(SMBCTL5_DBNCT, dbnct), + bus->reg + NPCM_SMBCTL5); + } + } + + iowrite8((ioread8(bus->reg + NPCM_SMBCTL4) & ~SMBCTL4_HLDT) + | FIELD_PREP(SMBCTL4_HLDT, hldt), bus->reg + NPCM_SMBCTL4); + + // Return to Bank 1, and stay there by default: + npcm_smb_select_bank(bus, SMB_BANK_1); + + dev_dbg(bus->dev, "k1 = %d k2 = %d dbnct = %d sclfrq = %d hldt = %d src_clk_freq %d fast_mode %d\n", + k1, k2, dbnct, sclfrq, hldt, src_clk_freq, fast_mode); + + return true; +} + +static bool npcm_smb_init_module(struct npcm_i2c *bus, enum smb_mode mode, + u32 bus_freq) +{ + // Check whether module already enabled or frequency is out of bounds + if ((bus->state != SMB_DISABLE && bus->state != SMB_IDLE) || + bus_freq < SMBUS_FREQ_MIN || bus_freq > SMBUS_FREQ_MAX) + return false; + // Configure FIFO disabled mode so slave will not use fifo + // (maste will set it on if supported) + bus->threshold_fifo = SMBUS_FIFO_SIZE; + iowrite8(ioread8(bus->reg + NPCM_SMBFIF_CTL) & ~NPCM_SMBFIF_CTL_FIFO_EN, + bus->reg + NPCM_SMBFIF_CTL); + + bus->fifo_use = false; + + // Configure SMB module clock frequency + if (!npcm_smb_init_clk(bus, mode, bus_freq)) { + pr_err("npcm_smb_init_clk failed\n"); + return false; + } + npcm_smb_disable(bus); + + // Enable module (before configuring CTL1) + npcm_smb_enable(bus); + bus->state = SMB_IDLE; + + // Enable SMB int and New Address Match int source + iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) | NPCM_SMBCTL1_NMINTE) & + ~NPCM_SMBCTL1_RWS_FIELDS, + bus->reg + NPCM_SMBCTL1); + + npcm_smb_int_enable(bus, true); + return true; +} + +static int __npcm_i2c_init(struct npcm_i2c *bus, struct platform_device *pdev) +{ + u32 clk_freq; + int ret; + + // Initialize the internal data structures + bus->state = SMB_DISABLE; + bus->master_or_slave = SMB_SLAVE; + + ret = of_property_read_u32(pdev->dev.of_node, + "bus-frequency", &clk_freq); + if (ret < 0) { + dev_err(&pdev->dev, + "Could not read bus-frequency property\n"); + clk_freq = 100000; + } + ret = npcm_smb_init_module(bus, SMB_MASTER, clk_freq / 1000); + if (!ret) { + dev_err(&pdev->dev, + "npcm_smb_init_module() failed\n"); + return -1; + } + + crc8_populate_lsb(npcm7xx_crc8, 0x07); + crc8_populate_msb(npcm7xx_crc8, 0x07); + return 0; +} + +static irqreturn_t npcm_i2c_bus_irq(int irq, void *dev_id) +{ + struct npcm_i2c *bus = dev_id; + + bus->int_cnt++; + _npcm7xx_get_time_stamp(&bus->int_time_stamp[0], + &bus->int_time_stamp[1]); + if (bus->master_or_slave == SMB_MASTER) { + npcm_smb_int_master_handler(bus); + return IRQ_HANDLED; + } + + dev_err(bus->dev, "int unknown on bus%d\n", bus->num); + return IRQ_NONE; +} + +static bool npcm_smb_master_start_xmit(struct npcm_i2c *bus, + u8 slave_addr, u16 nwrite, u16 nread, + u8 *write_data, u8 *read_data, + bool use_PEC) +{ + // + // Allow only if bus is not busy + // + if (bus->state != SMB_IDLE) { + dev_info(bus->dev, "\tbus%d->state != SMB_IDLE\n", bus->num); + return false; + } + + // Configure FIFO mode : + if (FIELD_GET(SMB_VER_FIFO_EN, ioread8(bus->reg + SMB_VER))) { + bus->fifo_use = true; + iowrite8(ioread8(bus->reg + NPCM_SMBFIF_CTL) | + NPCM_SMBFIF_CTL_FIFO_EN, bus->reg + NPCM_SMBFIF_CTL); + } else { + bus->fifo_use = false; + } + + // Update driver state + bus->master_or_slave = SMB_MASTER; + bus->state = SMB_MASTER_START; + if (nwrite > 0) + bus->operation = SMB_WRITE_OPER; + else + bus->operation = SMB_READ_OPER; + + if (npcm_smb_is_quick(bus)) + bus->operation = SMB_WRITE_OPER; // send the address with W bit. + + bus->dest_addr = (u8)(slave_addr << 1);// Translate 7bit to 8bit format + bus->wr_buf = write_data; + bus->wr_size = nwrite; + bus->wr_ind = 0; + bus->rd_buf = read_data; + bus->rd_size = nread; + bus->rd_ind = 0; + bus->PEC_use = use_PEC; + bus->retry_count = SMB_RETRY_MAX_COUNT; + + // clear BER just in case it is set due to a previous transaction + iowrite8(NPCM_SMBST_BER, bus->reg + NPCM_SMBST); + + // Initiate SMBus master transaction + // Generate a Start condition on the SMBus + if (bus->fifo_use) { + // select bank 1 for FIFO regs + npcm_smb_select_bank(bus, SMB_BANK_1); + + // clear FIFO and relevant status bits. + iowrite8(ioread8(bus->reg + NPCM_SMBFIF_CTS) | + NPCM_SMBFIF_CTS_SLVRSTR | + NPCM_SMBFIF_CTS_CLR_FIFO | + NPCM_SMBFIF_CTS_RXF_TXE, bus->reg + NPCM_SMBFIF_CTS); + + if (bus->operation == SMB_READ_OPER) { + //This is a read only operation. Configure the FIFO + //threshold according to the needed # of bytes to read. + npcm_smb_set_fifo(bus, nread, -1); + } else if (bus->operation == SMB_WRITE_OPER) { + npcm_smb_set_fifo(bus, -1, nwrite); + } + } + + bus->int_cnt = 0; + bus->event_log = 0; + npcm_smb_master_start(bus); + + return true; +} + +static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct npcm_i2c *bus = adap->algo_data; + struct i2c_msg *msg0, *msg1; + unsigned long time_left, flags; + u16 nwrite, nread; + u8 *write_data, *read_data; + u8 slave_addr; + int ret = 0; + + spin_lock_irqsave(&bus->lock, flags); + bus->cmd_err = -EPERM; + bus->int_cnt = 0; + bus->stop_ind = SMB_NO_STATUS_IND; + bus->read_block_use = false; + + iowrite8(0xFF, bus->reg + NPCM_SMBST); + + if (num > 2 || num < 1) { + pr_err("I2C command not supported, num of msgs = %d\n", num); + spin_unlock_irqrestore(&bus->lock, flags); + return -EINVAL; + } + + msg0 = &msgs[0]; + slave_addr = msg0->addr; + if (msg0->flags & I2C_M_RD) { // read + if (num == 2) { + pr_err(" num = 2 but first msg is rd instead of wr\n"); + spin_unlock_irqrestore(&bus->lock, flags); + return -EINVAL; + } + nwrite = 0; + write_data = NULL; + if (msg0->flags & I2C_M_RECV_LEN) { + nread = 1; + bus->read_block_use = true; + + } else { + nread = msg0->len; + } + read_data = msg0->buf; + + } else { // write + nwrite = msg0->len; + write_data = msg0->buf; + nread = 0; + read_data = NULL; + if (num == 2) { + msg1 = &msgs[1]; + if (slave_addr != msg1->addr) { + pr_err("SA==%02x but msg1->addr == %02x\n", + slave_addr, msg1->addr); + spin_unlock_irqrestore(&bus->lock, flags); + return -EINVAL; + } + if ((msg1->flags & I2C_M_RD) == 0) { + pr_err("num = 2 but both msg are write.\n"); + spin_unlock_irqrestore(&bus->lock, flags); + return -EINVAL; + } + if (msg1->flags & I2C_M_RECV_LEN) { + nread = 1; + bus->read_block_use = true; + } else { + nread = msg1->len; + bus->read_block_use = false; + } + + read_data = msg1->buf; + } + } + + bus->msgs = msgs; + bus->msgs_num = num; + + if (nwrite >= 32 * 1024 || nread >= 32 * 1024) { + pr_err("i2c%d buffer too big\n", bus->num); + return -EINVAL; + } + + reinit_completion(&bus->cmd_complete); + + if (npcm_smb_master_start_xmit(bus, slave_addr, nwrite, nread, + write_data, read_data, 0) == false) + ret = -(EBUSY); + + if (ret != -(EBUSY)) { + time_left = wait_for_completion_timeout(&bus->cmd_complete, + bus->adap.timeout); + + if (time_left == 0 && bus->cmd_err == -EPERM) { + npcm_smb_master_abort(bus); + ret = -ETIMEDOUT; + } else { + ret = bus->cmd_err; + } + } + + bus->msgs = NULL; + bus->msgs_num = 0; + spin_unlock_irqrestore(&bus->lock, flags); + + // If nothing went wrong, return number of messages xferred. + if (ret >= 0) + return num; + else + return ret; +} + +static u32 npcm_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static const struct i2c_algorithm npcm_i2c_algo = { + .master_xfer = npcm_i2c_master_xfer, + .functionality = npcm_i2c_functionality, +}; + +static struct i2c_bus_recovery_info npcm_i2c_recovery = { + .recover_bus = npcm_smb_recovery, + .get_scl = npcm_smb_get_SCL, + .set_scl = npcm_smb_set_SCL, + .get_sda = npcm_smb_get_SDA, +}; + +static int npcm_i2c_probe_bus(struct platform_device *pdev) +{ + struct npcm_i2c *bus; + struct resource *res; + struct clk *i2c_clk; + int ret; + int num; + + bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL); + if (!bus) + return -ENOMEM; + +#ifdef CONFIG_OF + num = of_alias_get_id(pdev->dev.of_node, "i2c"); + bus->num = num; + i2c_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(i2c_clk)) { + pr_err(" I2C probe failed: can't read clk.\n"); + return -EPROBE_DEFER; + } + bus->apb_clk = clk_get_rate(i2c_clk); + dev_dbg(bus->dev, "I2C APB clock is %d\n", bus->apb_clk); +#endif // CONFIG_OF + + gcr_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-gcr"); + if (IS_ERR(gcr_regmap)) { + pr_err("%s: failed to find nuvoton,npcm750-gcr\n", __func__); + return IS_ERR(gcr_regmap); + } + regmap_write(gcr_regmap, NPCM_I2CSEGCTL, I2CSEGCTL_VAL); + dev_dbg(bus->dev, "I2C%d: gcr mapped\n", bus->num); + + clk_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-clk"); + if (IS_ERR(clk_regmap)) { + pr_err("%s: failed to find nuvoton,npcm750-clk\n", __func__); + return IS_ERR(clk_regmap); + } + dev_dbg(bus->dev, "I2C%d: clk mapped\n", bus->num); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dev_dbg(bus->dev, "resource: %pR\n", res); + bus->reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR((bus)->reg)) + return PTR_ERR((bus)->reg); + dev_dbg(bus->dev, "base = %p\n", bus->reg); + + // Initialize the I2C adapter + spin_lock_init(&bus->lock); + init_completion(&bus->cmd_complete); + bus->adap.owner = THIS_MODULE; + bus->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + bus->adap.retries = 0; + bus->adap.timeout = 500 * HZ / 1000; + bus->adap.algo = &npcm_i2c_algo; + bus->adap.algo_data = bus; + bus->adap.dev.parent = &pdev->dev; + bus->adap.dev.of_node = pdev->dev.of_node; + bus->adap.bus_recovery_info = &npcm_i2c_recovery; + + snprintf(bus->adap.name, sizeof(bus->adap.name), "Nuvoton i2c"); + + bus->dev = &pdev->dev; + + ret = __npcm_i2c_init(bus, pdev); + if (ret < 0) + return ret; + + bus->irq = platform_get_irq(pdev, 0); + if (bus->irq < 0) { + pr_err("I2C platform_get_irq error."); + return -ENODEV; + } + dev_dbg(bus->dev, "irq = %d\n", bus->irq); + + ret = request_irq(bus->irq, npcm_i2c_bus_irq, 0, + dev_name(&pdev->dev), (void *)bus); + if (ret) { + dev_err(&pdev->dev, "I2C%d: request_irq fail\n", bus->num); + return ret; + } + + ret = i2c_add_adapter(&bus->adap); + if (ret < 0) { + dev_err(&pdev->dev, "I2C%d: i2c_add_adapter fail\n", bus->num); + return ret; + } + + platform_set_drvdata(pdev, bus); + pr_info("i2c bus %d registered\n", bus->adap.nr); + + return 0; +} + +static int npcm_i2c_remove_bus(struct platform_device *pdev) +{ + unsigned long lock_flags; + struct npcm_i2c *bus = platform_get_drvdata(pdev); + + spin_lock_irqsave(&bus->lock, lock_flags); + npcm_smb_disable(bus); + spin_unlock_irqrestore(&bus->lock, lock_flags); + i2c_del_adapter(&bus->adap); + + return 0; +} + +static const struct of_device_id npcm_i2c_bus_of_table[] = { + { .compatible = "nuvoton,npcm750-i2c", }, + {}, +}; +MODULE_DEVICE_TABLE(of, npcm_i2c_bus_of_table); + +static struct platform_driver npcm_i2c_bus_driver = { + .probe = npcm_i2c_probe_bus, + .remove = npcm_i2c_remove_bus, + .driver = { + .name = "nuvoton-i2c", + .of_match_table = npcm_i2c_bus_of_table, + } +}; +module_platform_driver(npcm_i2c_bus_driver); + +MODULE_AUTHOR("Avi Fishman "); +MODULE_AUTHOR("Tali Perry "); +MODULE_DESCRIPTION("Nuvoton I2C Bus Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(I2C_VERSION); From patchwork Mon Jan 14 13:07:01 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomer Maimon X-Patchwork-Id: 1024497 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 43dZ1L05Dmz9s9G for ; Tue, 15 Jan 2019 00:23:50 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 43dZ1K67RmzDqXJ for ; Tue, 15 Jan 2019 00:23:49 +1100 (AEDT) X-Original-To: openbmc@lists.ozlabs.org Delivered-To: openbmc@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (mailfrom) smtp.mailfrom=nuvoton.com (client-ip=212.199.177.27; helo=herzl.nuvoton.co.il; envelope-from=tomer.maimon@nuvoton.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from herzl.nuvoton.co.il (212.199.177.27.static.012.net.il [212.199.177.27]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 43dYfR6ZySzDqWH for ; Tue, 15 Jan 2019 00:07:25 +1100 (AEDT) Received: from taln60.nuvoton.co.il (ntil-fw [212.199.177.25]) by herzl.nuvoton.co.il (8.13.8/8.13.8) with ESMTP id x0ECbI8m018990; Mon, 14 Jan 2019 14:37:18 +0200 Received: by taln60.nuvoton.co.il (Postfix, from userid 10070) id D5DD562DA4; Mon, 14 Jan 2019 15:07:15 +0200 (IST) From: Tomer Maimon To: joel@jms.id.au Subject: [linux dev-4.19 06/15] dt-binding: bmc: Add NPCM7xx LPC BPC documentation Date: Mon, 14 Jan 2019 15:07:01 +0200 Message-Id: <20190114130710.427600-7-tmaimon77@gmail.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20190114130710.427600-1-tmaimon77@gmail.com> References: <20190114130710.427600-1-tmaimon77@gmail.com> X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: openbmc@lists.ozlabs.org, Tomer Maimon Errors-To: openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "openbmc" Added device tree binding documentation for Nuvoton BMC NPCM7xx BIOS Post Code (BPC). The NPCM7xx BPC monitoring two configurable I/O addresses written by the host on Low Pin Count (LPC) bus. Signed-off-by: Tomer Maimon --- .../devicetree/bindings/bmc/npcm7xx-lpc-bpc.txt | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Documentation/devicetree/bindings/bmc/npcm7xx-lpc-bpc.txt diff --git a/Documentation/devicetree/bindings/bmc/npcm7xx-lpc-bpc.txt b/Documentation/devicetree/bindings/bmc/npcm7xx-lpc-bpc.txt new file mode 100644 index 000000000000..0832c9cbea32 --- /dev/null +++ b/Documentation/devicetree/bindings/bmc/npcm7xx-lpc-bpc.txt @@ -0,0 +1,26 @@ +Nuvoton NPCM7xx LPC BPC interface + +Nuvoton BMC NPCM7xx BIOS Post Code (BPC) monitoring two +configurable I/O addresses written by the host on the +Low Pin Count (LPC) bus, the capure data stored in 128-word FIFO. + +NPCM7xx BPC supports capture double words, when using capture +double word only I/O address 1 is monitored. + +Required properties for lpc_bpc node +- compatible : "nuvoton,npcm750-lpc-bpc" for Poleg NPCM7XX. +- reg : specifies physical base address and size of the registers. +- interrupts : contain the LPC BPC with flags for falling edge. +- monitor-ports : contain monitor I/O addresses, at least one monitor I/O + address required + +Optional property for lpc_bpc node +- bpc-en-dwcapture : enable capture double words support. + +Example: + lpc_bpc: lpc-bpc@f0007040 { + compatible = "nuvoton,npcm7xx-lpc-bpc"; + reg = <0xf0007040 0x14>; + monitor-ports = <0x80>; + interrupts = ; + }; From patchwork Mon Jan 14 13:07:02 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomer Maimon X-Patchwork-Id: 1024516 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 43dZ5F43P0z9sDB for ; Tue, 15 Jan 2019 00:27:13 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 43dZ5D24YzzDqPn for ; Tue, 15 Jan 2019 00:27:12 +1100 (AEDT) X-Original-To: openbmc@lists.ozlabs.org Delivered-To: openbmc@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (mailfrom) smtp.mailfrom=nuvoton.com (client-ip=212.199.177.27; helo=herzl.nuvoton.co.il; envelope-from=tomer.maimon@nuvoton.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from herzl.nuvoton.co.il (212.199.177.27.static.012.net.il [212.199.177.27]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 43dYfS0jtkzDqWL for ; Tue, 15 Jan 2019 00:07:25 +1100 (AEDT) Received: from taln60.nuvoton.co.il (ntil-fw [212.199.177.25]) by herzl.nuvoton.co.il (8.13.8/8.13.8) with ESMTP id x0ECbIOO018993; Mon, 14 Jan 2019 14:37:18 +0200 Received: by taln60.nuvoton.co.il (Postfix, from userid 10070) id 551A162DA4; Mon, 14 Jan 2019 15:07:16 +0200 (IST) From: Tomer Maimon To: joel@jms.id.au Subject: [linux dev-4.19 07/15] misc: npcm7xx-lpc-bpc: add NPCM7xx BIOS post code driver Date: Mon, 14 Jan 2019 15:07:02 +0200 Message-Id: <20190114130710.427600-8-tmaimon77@gmail.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20190114130710.427600-1-tmaimon77@gmail.com> References: <20190114130710.427600-1-tmaimon77@gmail.com> X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: openbmc@lists.ozlabs.org, Tomer Maimon Errors-To: openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "openbmc" Add NPCM7xx BIOS post code (BPC) driver, the BPC monitoring two I/O address written by the host on the LPC. Signed-off-by: Tomer Maimon --- drivers/misc/Kconfig | 8 + drivers/misc/Makefile | 1 + drivers/misc/npcm7xx-lpc-bpc.c | 394 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 403 insertions(+) create mode 100644 drivers/misc/npcm7xx-lpc-bpc.c diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 689d07ea7ded..c0cbd68238cf 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -520,6 +520,14 @@ config MISC_RTSX tristate default MISC_RTSX_PCI || MISC_RTSX_USB +config NPCM7XX_LPC_BPC + tristate "NPCM7xx LPC BIOS Post Code support" + depends on (ARCH_NPCM7XX || COMPILE_TEST) + help + Provides a NPCM7xx driver to control the LPC BIOS Post Code + interface which allows the BMC to monitoring and save + the data written by the host to an arbitrary LPC I/O port. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index e4170f62ab98..2dc74726e3b0 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -59,3 +59,4 @@ obj-$(CONFIG_ASPEED_LPC_MBOX) += aspeed-lpc-mbox.o obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o obj-$(CONFIG_OCXL) += ocxl/ obj-$(CONFIG_MISC_RTSX) += cardreader/ +obj-$(CONFIG_NPCM7XX_LPC_BPC) += npcm7xx-lpc-bpc.o diff --git a/drivers/misc/npcm7xx-lpc-bpc.c b/drivers/misc/npcm7xx-lpc-bpc.c new file mode 100644 index 000000000000..e014e07cd4a4 --- /dev/null +++ b/drivers/misc/npcm7xx-lpc-bpc.c @@ -0,0 +1,394 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2014-2018 Nuvoton Technology corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEVICE_NAME "npcm7xx-lpc-bpc" + +#define NUM_BPC_CHANNELS 2 +#define DW_PAD_SIZE 3 + +/* BIOS POST Code FIFO Registers */ +#define NPCM7XX_BPCFA2L_REG 0x2 //BIOS POST Code FIFO Address 2 LSB +#define NPCM7XX_BPCFA2M_REG 0x4 //BIOS POST Code FIFO Address 2 MSB +#define NPCM7XX_BPCFEN_REG 0x6 //BIOS POST Code FIFO Enable +#define NPCM7XX_BPCFSTAT_REG 0x8 //BIOS POST Code FIFO Status +#define NPCM7XX_BPCFDATA_REG 0xA //BIOS POST Code FIFO Data +#define NPCM7XX_BPCFMSTAT_REG 0xC //BIOS POST Code FIFO Miscellaneous Status +#define NPCM7XX_BPCFA1L_REG 0x10 //BIOS POST Code FIFO Address 1 LSB +#define NPCM7XX_BPCFA1M_REG 0x12 //BIOS POST Code FIFO Address 1 MSB + +/*BIOS regiser data*/ +#define FIFO_IOADDR1_ENABLE 0x80 +#define FIFO_IOADDR2_ENABLE 0x40 + +/* BPC interface package and structure definition */ +#define BPC_KFIFO_SIZE 0x400 + +/*BPC regiser data*/ +#define FIFO_DATA_VALID 0x80 +#define FIFO_OVERFLOW 0x20 +#define FIFO_READY_INT_ENABLE 0x8 +#define FIFO_DWCAPTURE 0x4 +#define FIFO_ADDR_DECODE 0x1 + +/*Host Reset*/ +#define HOST_RESET_INT_ENABLE 0x10 +#define HOST_RESET_CHANGED 0x40 + +struct npcm7xx_bpc_channel { + struct npcm7xx_bpc *data; + struct kfifo fifo; + wait_queue_head_t wq; + bool host_reset; + struct miscdevice miscdev; +}; + +struct npcm7xx_bpc { + void __iomem *base; + int irq; + bool en_dwcap; + struct npcm7xx_bpc_channel ch[NUM_BPC_CHANNELS]; +}; + +static struct npcm7xx_bpc_channel *npcm7xx_file_to_ch(struct file *file) +{ + return container_of(file->private_data, struct npcm7xx_bpc_channel, + miscdev); +} + +static ssize_t npcm7xx_bpc_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct npcm7xx_bpc_channel *chan = npcm7xx_file_to_ch(file); + struct npcm7xx_bpc *lpc_bpc = chan->data; + unsigned int copied; + int ret = 0; + int cond_size = 1; + + if (lpc_bpc->en_dwcap) + cond_size = 3; + + if (kfifo_len(&chan->fifo) < cond_size) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + ret = wait_event_interruptible + (chan->wq, kfifo_len(&chan->fifo) > cond_size); + if (ret == -ERESTARTSYS) + return -EINTR; + } + + ret = kfifo_to_user(&chan->fifo, buffer, count, &copied); + + return ret ? ret : copied; +} + +static __poll_t npcm7xx_bpc_poll(struct file *file, + struct poll_table_struct *pt) +{ + struct npcm7xx_bpc_channel *chan = npcm7xx_file_to_ch(file); + __poll_t mask = 0; + + poll_wait(file, &chan->wq, pt); + if (!kfifo_is_empty(&chan->fifo)) + mask |= POLLIN; + + if (chan->host_reset) { + mask |= POLLHUP; + chan->host_reset = false; + } + + return mask; +} + +static const struct file_operations npcm7xx_bpc_fops = { + .owner = THIS_MODULE, + .read = npcm7xx_bpc_read, + .poll = npcm7xx_bpc_poll, + .llseek = noop_llseek, +}; + +static irqreturn_t npcm7xx_bpc_irq(int irq, void *arg) +{ + struct npcm7xx_bpc *lpc_bpc = arg; + u8 fifo_st; + u8 host_st; + u8 addr_index = 0; + u8 Data; + u8 padzero[3] = {0}; + u8 last_addr_bit = 0; + bool isr_flag = false; + + fifo_st = ioread8(lpc_bpc->base + NPCM7XX_BPCFSTAT_REG); + while (FIFO_DATA_VALID & fifo_st) { + /* If dwcapture enabled only channel 0 (FIFO 0) used */ + if (!lpc_bpc->en_dwcap) + addr_index = fifo_st & FIFO_ADDR_DECODE; + else + last_addr_bit = fifo_st & FIFO_ADDR_DECODE; + + /*Read data from FIFO to clear interrupt*/ + Data = ioread8(lpc_bpc->base + NPCM7XX_BPCFDATA_REG); + if (kfifo_is_full(&lpc_bpc->ch[addr_index].fifo)) + kfifo_skip(&lpc_bpc->ch[addr_index].fifo); + kfifo_put(&lpc_bpc->ch[addr_index].fifo, Data); + if (fifo_st & FIFO_OVERFLOW) + pr_info("BIOS Post Codes FIFO Overflow!!!\n"); + + fifo_st = ioread8(lpc_bpc->base + NPCM7XX_BPCFSTAT_REG); + if (lpc_bpc->en_dwcap && last_addr_bit) { + if ((fifo_st & FIFO_ADDR_DECODE) || + ((FIFO_DATA_VALID & fifo_st) == 0)) { + while (kfifo_avail(&lpc_bpc->ch[addr_index].fifo) < DW_PAD_SIZE) + kfifo_skip(&lpc_bpc->ch[addr_index].fifo); + kfifo_in(&lpc_bpc->ch[addr_index].fifo, + padzero, DW_PAD_SIZE); + } + } + isr_flag = true; + } + + host_st = ioread8(lpc_bpc->base + NPCM7XX_BPCFMSTAT_REG); + if (host_st & HOST_RESET_CHANGED) { + iowrite8(HOST_RESET_CHANGED, + lpc_bpc->base + NPCM7XX_BPCFMSTAT_REG); + lpc_bpc->ch[addr_index].host_reset = true; + isr_flag = true; + } + + if (isr_flag) { + wake_up_interruptible(&lpc_bpc->ch[addr_index].wq); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int npcm7xx_bpc_config_irq(struct npcm7xx_bpc *lpc_bpc, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int rc; + + lpc_bpc->irq = platform_get_irq(pdev, 0); + if (lpc_bpc->irq < 0) { + dev_err(dev, "get IRQ failed\n"); + return lpc_bpc->irq; + } + + rc = devm_request_irq(dev, lpc_bpc->irq, + npcm7xx_bpc_irq, IRQF_SHARED, + DEVICE_NAME, lpc_bpc); + if (rc < 0) { + dev_warn(dev, "Unable to request IRQ %d\n", lpc_bpc->irq); + return rc; + } + + return 0; +} + +static int npcm7xx_enable_bpc(struct npcm7xx_bpc *lpc_bpc, struct device *dev, + int channel, u16 lpc_port) +{ + int rc; + u8 addr_en, reg_en; + + init_waitqueue_head(&lpc_bpc->ch[channel].wq); + + rc = kfifo_alloc(&lpc_bpc->ch[channel].fifo, + BPC_KFIFO_SIZE, GFP_KERNEL); + if (rc) + return rc; + + lpc_bpc->ch[channel].miscdev.minor = MISC_DYNAMIC_MINOR; + lpc_bpc->ch[channel].miscdev.name = + devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel); + lpc_bpc->ch[channel].miscdev.fops = &npcm7xx_bpc_fops; + lpc_bpc->ch[channel].miscdev.parent = dev; + rc = misc_register(&lpc_bpc->ch[channel].miscdev); + if (rc) + return rc; + + lpc_bpc->ch[channel].data = lpc_bpc; + lpc_bpc->ch[channel].host_reset = false; + + /* Enable LPC snoop channel at requested port */ + switch (channel) { + case 0: + addr_en = FIFO_IOADDR1_ENABLE; + iowrite8((u8)lpc_port & 0xFF, + lpc_bpc->base + NPCM7XX_BPCFA1L_REG); + iowrite8((u8)(lpc_port >> 8), + lpc_bpc->base + NPCM7XX_BPCFA1M_REG); + break; + case 1: + addr_en = FIFO_IOADDR2_ENABLE; + iowrite8((u8)lpc_port & 0xFF, + lpc_bpc->base + NPCM7XX_BPCFA2L_REG); + iowrite8((u8)(lpc_port >> 8), + lpc_bpc->base + NPCM7XX_BPCFA2M_REG); + break; + default: + return -EINVAL; + } + + if (lpc_bpc->en_dwcap) + addr_en = FIFO_DWCAPTURE; + + /* + * Enable FIFO Ready Interrupt, FIFO Capture of I/O addr, + * and Host Reset + */ + reg_en = ioread8(lpc_bpc->base + NPCM7XX_BPCFEN_REG); + iowrite8(reg_en | addr_en | FIFO_READY_INT_ENABLE | + HOST_RESET_INT_ENABLE, lpc_bpc->base + NPCM7XX_BPCFEN_REG); + + return 0; +} + +static void npcm7xx_disable_bpc(struct npcm7xx_bpc *lpc_bpc, int channel) +{ + u8 reg_en; + + switch (channel) { + case 0: + reg_en = ioread8(lpc_bpc->base + NPCM7XX_BPCFEN_REG); + if (lpc_bpc->en_dwcap) + iowrite8(reg_en & ~FIFO_DWCAPTURE, + lpc_bpc->base + NPCM7XX_BPCFEN_REG); + else + iowrite8(reg_en & ~FIFO_IOADDR1_ENABLE, + lpc_bpc->base + NPCM7XX_BPCFEN_REG); + break; + case 1: + reg_en = ioread8(lpc_bpc->base + NPCM7XX_BPCFEN_REG); + iowrite8(reg_en & ~FIFO_IOADDR2_ENABLE, + lpc_bpc->base + NPCM7XX_BPCFEN_REG); + break; + default: + return; + } + + if (!(reg_en & (FIFO_IOADDR1_ENABLE | FIFO_IOADDR2_ENABLE))) + iowrite8(reg_en & + ~(FIFO_READY_INT_ENABLE | HOST_RESET_INT_ENABLE), + lpc_bpc->base + NPCM7XX_BPCFEN_REG); + + kfifo_free(&lpc_bpc->ch[channel].fifo); + misc_deregister(&lpc_bpc->ch[channel].miscdev); +} + +static int npcm7xx_bpc_probe(struct platform_device *pdev) +{ + struct npcm7xx_bpc *lpc_bpc; + struct resource *res; + struct device *dev; + u32 port; + int rc; + + dev = &pdev->dev; + + lpc_bpc = devm_kzalloc(dev, sizeof(*lpc_bpc), GFP_KERNEL); + if (!lpc_bpc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "BIOS post code reg resource not found\n"); + return -ENODEV; + } + + dev_dbg(dev, "BIOS post code base resource is %pR\n", res); + lpc_bpc->base = devm_ioremap_resource(dev, res); + if (IS_ERR(lpc_bpc->base)) + return PTR_ERR(lpc_bpc->base); + + dev_set_drvdata(&pdev->dev, lpc_bpc); + + rc = of_property_read_u32_index(dev->of_node, "monitor-ports", 0, + &port); + if (rc) { + dev_err(dev, "no monitor ports configured\n"); + return -ENODEV; + } + + lpc_bpc->en_dwcap = + of_property_read_bool(dev->of_node, "bpc-en-dwcapture"); + + rc = npcm7xx_bpc_config_irq(lpc_bpc, pdev); + if (rc) + return rc; + + rc = npcm7xx_enable_bpc(lpc_bpc, dev, 0, port); + if (rc) { + dev_err(dev, "Enable BIOS post code I/O port 0 failed\n"); + return rc; + } + + /* + * Configuration of second BPC channel port is optional + * Double-Word Capture ignoring address 2 + */ + if (!lpc_bpc->en_dwcap) { + if (of_property_read_u32_index(dev->of_node, "monitor-ports", + 1, &port) == 0) { + rc = npcm7xx_enable_bpc(lpc_bpc, dev, 1, port); + if (rc) { + dev_err(dev, "Enable BIOS post code I/O port 1 failed, disable I/O port 0\n"); + npcm7xx_disable_bpc(lpc_bpc, 0); + return rc; + } + } + } + + pr_info("npcm7xx BIOS post code probe\n"); + + return rc; +} + +static int npcm7xx_bpc_remove(struct platform_device *pdev) +{ + struct npcm7xx_bpc *lpc_bpc = dev_get_drvdata(&pdev->dev); + u8 reg_en; + + reg_en = ioread8(lpc_bpc->base + NPCM7XX_BPCFEN_REG); + + if (reg_en & FIFO_IOADDR1_ENABLE) + npcm7xx_disable_bpc(lpc_bpc, 0); + if (reg_en & FIFO_IOADDR2_ENABLE) + npcm7xx_disable_bpc(lpc_bpc, 1); + + return 0; +} + +static const struct of_device_id npcm7xx_bpc_match[] = { + { .compatible = "nuvoton,npcm750-lpc-bpc" }, + { }, +}; + +static struct platform_driver npcm7xx_bpc_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = npcm7xx_bpc_match, + }, + .probe = npcm7xx_bpc_probe, + .remove = npcm7xx_bpc_remove, +}; + +module_platform_driver(npcm7xx_bpc_driver); + +MODULE_DEVICE_TABLE(of, npcm7xx_bpc_match); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tomer Maimon "); +MODULE_DESCRIPTION("Linux driver to control NPCM7XX LPC BIOS post code monitoring"); From patchwork Mon Jan 14 13:07:03 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomer Maimon X-Patchwork-Id: 1024534 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 43dZ7h3Qhhz9s3q for ; Tue, 15 Jan 2019 00:29:20 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 43dZ7h2J3SzDqWT for ; Tue, 15 Jan 2019 00:29:20 +1100 (AEDT) X-Original-To: openbmc@lists.ozlabs.org Delivered-To: openbmc@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (mailfrom) smtp.mailfrom=nuvoton.com (client-ip=212.199.177.27; helo=herzl.nuvoton.co.il; envelope-from=tomer.maimon@nuvoton.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from herzl.nuvoton.co.il (212.199.177.27.static.012.net.il [212.199.177.27]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 43dYfW0YFSzDqVW for ; Tue, 15 Jan 2019 00:07:30 +1100 (AEDT) Received: from taln60.nuvoton.co.il (ntil-fw [212.199.177.25]) by herzl.nuvoton.co.il (8.13.8/8.13.8) with ESMTP id x0ECbJe2018996; Mon, 14 Jan 2019 14:37:19 +0200 Received: by taln60.nuvoton.co.il (Postfix, from userid 10070) id C301662DA4; Mon, 14 Jan 2019 15:07:16 +0200 (IST) From: Tomer Maimon To: joel@jms.id.au Subject: [linux dev-4.19 08/15] dt-binding: bmc: add npcm7xx pci mailbox document Date: Mon, 14 Jan 2019 15:07:03 +0200 Message-Id: <20190114130710.427600-9-tmaimon77@gmail.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20190114130710.427600-1-tmaimon77@gmail.com> References: <20190114130710.427600-1-tmaimon77@gmail.com> X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: openbmc@lists.ozlabs.org, Tomer Maimon Errors-To: openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "openbmc" Added device tree binding documentation for Nuvoton BMC NPCM7XX PCI mailbox. Signed-off-by: Tomer Maimon --- .../devicetree/bindings/bmc/npcm7xx-pci-mbox.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 Documentation/devicetree/bindings/bmc/npcm7xx-pci-mbox.txt diff --git a/Documentation/devicetree/bindings/bmc/npcm7xx-pci-mbox.txt b/Documentation/devicetree/bindings/bmc/npcm7xx-pci-mbox.txt new file mode 100644 index 000000000000..e5585f38041b --- /dev/null +++ b/Documentation/devicetree/bindings/bmc/npcm7xx-pci-mbox.txt @@ -0,0 +1,19 @@ +Nuvoton NPCM7xx PCI mail box interface + +Nuvoton BMC NPCM7xx PCI mail box, The mailbox is a high-bandwidth +communication module between the BMC CPU and host CPU. + +Required properties for lpc_bpc node +- compatible : "nuvoton,npcm750-pci-mbox" for Poleg NPCM7XX. +- reg : specifies two address space + 1. physical base address and size of the registers. + 2. physical base address and size of the dual-ported RAM. +- interrupts : contain the PCI mail box interrupt with flags for falling edge. + +Example: + pcimbox: pcimbox@f0848000 { + compatible = "nuvoton,npcm750-pci-mbox", "simple-mfd", "syscon"; + reg = <0xf084C000 0x8 + 0xf0848000 0x3F00>; + interrupts = ; + }; From patchwork Mon Jan 14 13:07:04 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomer Maimon X-Patchwork-Id: 1024535 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 43dZ824wQmz9s3q for ; Tue, 15 Jan 2019 00:29:38 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 43dZ823fRqzDqWn for ; Tue, 15 Jan 2019 00:29:38 +1100 (AEDT) X-Original-To: openbmc@lists.ozlabs.org Delivered-To: openbmc@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (mailfrom) smtp.mailfrom=nuvoton.com (client-ip=212.199.177.27; helo=herzl.nuvoton.co.il; envelope-from=tomer.maimon@nuvoton.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from herzl.nuvoton.co.il (212.199.177.27.static.012.net.il [212.199.177.27]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 43dYfV5Tm6zDqVL for ; Tue, 15 Jan 2019 00:07:29 +1100 (AEDT) Received: from taln60.nuvoton.co.il (ntil-fw [212.199.177.25]) by herzl.nuvoton.co.il (8.13.8/8.13.8) with ESMTP id x0ECbJME018999; Mon, 14 Jan 2019 14:37:19 +0200 Received: by taln60.nuvoton.co.il (Postfix, from userid 10070) id 49B8862DA4; Mon, 14 Jan 2019 15:07:17 +0200 (IST) From: Tomer Maimon To: joel@jms.id.au Subject: [linux dev-4.19 09/15] misc: mbox: add npcm7xx pci mailbox driver Date: Mon, 14 Jan 2019 15:07:04 +0200 Message-Id: <20190114130710.427600-10-tmaimon77@gmail.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20190114130710.427600-1-tmaimon77@gmail.com> References: <20190114130710.427600-1-tmaimon77@gmail.com> X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: openbmc@lists.ozlabs.org, Tomer Maimon Errors-To: openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "openbmc" Add Nuvoton BMC NPCM7XX PCI Mailbox driver. Signed-off-by: Tomer Maimon --- drivers/misc/Kconfig | 7 + drivers/misc/Makefile | 1 + drivers/misc/npcm7xx-pci-mbox.c | 288 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 296 insertions(+) create mode 100644 drivers/misc/npcm7xx-pci-mbox.c diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index c0cbd68238cf..00d1c547ece7 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -528,6 +528,13 @@ config NPCM7XX_LPC_BPC interface which allows the BMC to monitoring and save the data written by the host to an arbitrary LPC I/O port. +config NPCM7XX_PCI_MBOX + tristate "NPCM7xx PCI Mailbox Controller" + depends on (ARCH_NPCM7XX || COMPILE_TEST) && REGMAP && MFD_SYSCON + help + Expose the NPCM750/730/715/705 PCI MBOX registers found on + Nuvoton SOCs to userspace. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 2dc74726e3b0..768278b059c3 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -60,3 +60,4 @@ obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o obj-$(CONFIG_OCXL) += ocxl/ obj-$(CONFIG_MISC_RTSX) += cardreader/ obj-$(CONFIG_NPCM7XX_LPC_BPC) += npcm7xx-lpc-bpc.o +obj-$(CONFIG_NPCM7XX_PCI_MBOX) += npcm7xx-pci-mbox.o diff --git a/drivers/misc/npcm7xx-pci-mbox.c b/drivers/misc/npcm7xx-pci-mbox.c new file mode 100644 index 000000000000..df7f18fef1e4 --- /dev/null +++ b/drivers/misc/npcm7xx-pci-mbox.c @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2014-2018 Nuvoton Technology corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEVICE_NAME "npcm7xx-pci-mbox" + +#define NPCM7XX_MBOX_BMBXSTAT 0x0 +#define NPCM7XX_MBOX_BMBXCTL 0x4 +#define NPCM7XX_MBOX_BMBXCMD 0x8 + +#define NPCM7XX_MBOX_CIF_0 BIT(0) +#define NPCM7XX_MBOX_CIE_0 BIT(0) +#define NPCM7XX_MBOX_HIF_0 BIT(0) + +#define NPCM7XX_MBOX_ALL_CIF GENMASK(7, 0) +#define NPCM7XX_MBOX_ALL_CIE GENMASK(7, 0) +#define NPCM7XX_MBOX_ALL_HIF GENMASK(7, 0) + +struct npcm7xx_mbox { + struct miscdevice miscdev; + struct regmap *regmap; + void __iomem *memory; + wait_queue_head_t queue; + spinlock_t lock; /* mbox access mutex */ + bool cif0; + u32 max_buf_size; +}; + +static atomic_t npcm7xx_mbox_open_count = ATOMIC_INIT(0); + +static struct npcm7xx_mbox *file_mbox(struct file *file) +{ + return container_of(file->private_data, struct npcm7xx_mbox, miscdev); +} + +static int npcm7xx_mbox_open(struct inode *inode, struct file *file) +{ + struct npcm7xx_mbox *mbox = file_mbox(file); + + if (atomic_inc_return(&npcm7xx_mbox_open_count) == 1) { + /* enable mailbox interrupt */ + regmap_update_bits(mbox->regmap, NPCM7XX_MBOX_BMBXCTL, + NPCM7XX_MBOX_ALL_CIE, NPCM7XX_MBOX_CIE_0); + return 0; + } + + atomic_dec(&npcm7xx_mbox_open_count); + return -EBUSY; +} + +static ssize_t npcm7xx_mbox_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct npcm7xx_mbox *mbox = file_mbox(file); + unsigned long flags; + + if (!access_ok(VERIFY_WRITE, buf, count)) + return -EFAULT; + + if ((*ppos + count) > mbox->max_buf_size) + return -EINVAL; + + if (file->f_flags & O_NONBLOCK) { + if (!mbox->cif0) + return -EAGAIN; + } else if (wait_event_interruptible(mbox->queue, mbox->cif0)) { + return -ERESTARTSYS; + } + + spin_lock_irqsave(&mbox->lock, flags); + + if (copy_to_user((void __user *)buf, + (const void *)(mbox->memory + *ppos), count)) { + spin_unlock_irqrestore(&mbox->lock, flags); + return -EFAULT; + } + + mbox->cif0 = false; + spin_unlock_irqrestore(&mbox->lock, flags); + return count; +} + +static ssize_t npcm7xx_mbox_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct npcm7xx_mbox *mbox = file_mbox(file); + unsigned long flags; + + if (!access_ok(VERIFY_READ, buf, count)) + return -EFAULT; + + if ((*ppos + count) > mbox->max_buf_size) + return -EINVAL; + + spin_lock_irqsave(&mbox->lock, flags); + + if (copy_from_user((void *)(mbox->memory + *ppos), + (void __user *)buf, count)) { + spin_unlock_irqrestore(&mbox->lock, flags); + return -EFAULT; + } + + regmap_update_bits(mbox->regmap, NPCM7XX_MBOX_BMBXCMD, + NPCM7XX_MBOX_ALL_HIF, NPCM7XX_MBOX_HIF_0); + + spin_unlock_irqrestore(&mbox->lock, flags); + return count; +} + +static unsigned int npcm7xx_mbox_poll(struct file *file, poll_table *wait) +{ + struct npcm7xx_mbox *mbox = file_mbox(file); + unsigned int mask = 0; + + poll_wait(file, &mbox->queue, wait); + if (mbox->cif0) + mask |= POLLIN; + + return mask; +} + +static int npcm7xx_mbox_release(struct inode *inode, struct file *file) +{ + atomic_dec(&npcm7xx_mbox_open_count); + return 0; +} + +static const struct file_operations npcm7xx_mbox_fops = { + .owner = THIS_MODULE, + .llseek = no_seek_end_llseek, + .read = npcm7xx_mbox_read, + .write = npcm7xx_mbox_write, + .open = npcm7xx_mbox_open, + .release = npcm7xx_mbox_release, + .poll = npcm7xx_mbox_poll, +}; + +static irqreturn_t npcm7xx_mbox_irq(int irq, void *arg) +{ + struct npcm7xx_mbox *mbox = arg; + u32 val; + + regmap_read(mbox->regmap, NPCM7XX_MBOX_BMBXSTAT, &val); + if ((val & NPCM7XX_MBOX_CIF_0) != NPCM7XX_MBOX_CIF_0) + return IRQ_NONE; + + /* + * Leave the status bit set so that we know the data is for us, + * clear it once it has been read. + */ + mbox->cif0 = true; + + /* Mask it off, we'll clear it when we the data gets read */ + regmap_write_bits(mbox->regmap, NPCM7XX_MBOX_BMBXSTAT, + NPCM7XX_MBOX_ALL_CIF, NPCM7XX_MBOX_CIF_0); + + wake_up(&mbox->queue); + + return IRQ_HANDLED; +} + +static int npcm7xx_mbox_config_irq(struct npcm7xx_mbox *mbox, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int rc, irq; + u32 val; + + /* Disable all register based interrupts */ + regmap_update_bits(mbox->regmap, NPCM7XX_MBOX_BMBXCTL, + NPCM7XX_MBOX_ALL_CIE, 0); +/* + * These registers are write one to clear. Clear them. + * Per spec, cleared bits should not be re-cleared. + * Need to read and clear needed bits only, instead of blindly clearing all. + */ + regmap_read(mbox->regmap, NPCM7XX_MBOX_BMBXSTAT, &val); + val &= NPCM7XX_MBOX_ALL_CIF; + + /* If any bit is set, write back to clear */ + if (val) + regmap_write_bits(mbox->regmap, NPCM7XX_MBOX_BMBXSTAT, + NPCM7XX_MBOX_ALL_CIF, val); + + irq = irq_of_parse_and_map(dev->of_node, 0); + if (!irq) + return -ENODEV; + + rc = devm_request_irq(dev, irq, npcm7xx_mbox_irq, 0, DEVICE_NAME, mbox); + if (rc < 0) { + dev_err(dev, "Unable to request IRQ %d\n", irq); + return rc; + } + + return 0; +} + +static int npcm7xx_mbox_probe(struct platform_device *pdev) +{ + struct npcm7xx_mbox *mbox; + struct device *dev; + struct resource *res; + int rc; + + dev = &pdev->dev; + + mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); + if (!mbox) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, mbox); + + mbox->regmap = syscon_node_to_regmap(dev->of_node); + if (IS_ERR(mbox->regmap)) { + dev_err(dev, "Couldn't get regmap\n"); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + mbox->memory = devm_ioremap_resource(dev, res); + if (IS_ERR(mbox->memory)) + return PTR_ERR(mbox->memory); + mbox->max_buf_size = resource_size(res); + + spin_lock_init(&mbox->lock); + init_waitqueue_head(&mbox->queue); + + mbox->miscdev.minor = MISC_DYNAMIC_MINOR; + mbox->miscdev.name = DEVICE_NAME; + mbox->miscdev.fops = &npcm7xx_mbox_fops; + mbox->miscdev.parent = dev; + mbox->cif0 = false; + rc = misc_register(&mbox->miscdev); + if (rc) { + dev_err(dev, "Unable to register device\n"); + return rc; + } + + rc = npcm7xx_mbox_config_irq(mbox, pdev); + if (rc) { + dev_err(dev, "Failed to configure IRQ\n"); + misc_deregister(&mbox->miscdev); + return rc; + } + + pr_info("NPCM7xx PCI Mailbox probed\n"); + + return 0; +} + +static int npcm7xx_mbox_remove(struct platform_device *pdev) +{ + struct npcm7xx_mbox *mbox = dev_get_drvdata(&pdev->dev); + + misc_deregister(&mbox->miscdev); + + return 0; +} + +static const struct of_device_id npcm7xx_mbox_match[] = { + { .compatible = "nuvoton,npcm750-pci-mbox" }, + { }, +}; + +static struct platform_driver npcm7xx_mbox_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = npcm7xx_mbox_match, + }, + .probe = npcm7xx_mbox_probe, + .remove = npcm7xx_mbox_remove, +}; + +module_platform_driver(npcm7xx_mbox_driver); + +MODULE_DEVICE_TABLE(of, npcm7xx_mbox_match); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tomer Maimon "); +MODULE_DESCRIPTION("NPCM7XX mailbox device driver"); From patchwork Mon Jan 14 13:07:05 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomer Maimon X-Patchwork-Id: 1024545 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 43dZFF25nWz9s3q for ; Tue, 15 Jan 2019 00:34:09 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 43dZFF10WlzDqWY for ; Tue, 15 Jan 2019 00:34:09 +1100 (AEDT) X-Original-To: openbmc@lists.ozlabs.org Delivered-To: openbmc@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (mailfrom) smtp.mailfrom=nuvoton.com (client-ip=212.199.177.27; helo=herzl.nuvoton.co.il; envelope-from=tomer.maimon@nuvoton.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from herzl.nuvoton.co.il (212.199.177.27.static.012.net.il [212.199.177.27]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 43dYfc0mxgzDqVn for ; Tue, 15 Jan 2019 00:07:35 +1100 (AEDT) Received: from taln60.nuvoton.co.il (ntil-fw [212.199.177.25]) by herzl.nuvoton.co.il (8.13.8/8.13.8) with ESMTP id x0ECbK1Q019002; Mon, 14 Jan 2019 14:37:20 +0200 Received: by taln60.nuvoton.co.il (Postfix, from userid 10070) id B454B62DA4; Mon, 14 Jan 2019 15:07:17 +0200 (IST) From: Tomer Maimon To: joel@jms.id.au Subject: [linux dev-4.19 10/15] dt-binding: net: document NPCM7xx EMC DT bindings Date: Mon, 14 Jan 2019 15:07:05 +0200 Message-Id: <20190114130710.427600-11-tmaimon77@gmail.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20190114130710.427600-1-tmaimon77@gmail.com> References: <20190114130710.427600-1-tmaimon77@gmail.com> X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Avi Fishman , openbmc@lists.ozlabs.org, Tomer Maimon Errors-To: openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "openbmc" Added device tree binding documentation for Nuvoton NPCM7xx Ethernet MAC Controller (EMC). Signed-off-by: Avi Fishman Signed-off-by: Tomer Maimon --- .../bindings/net/nuvoton,npcm7xx-emc.txt | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/nuvoton,npcm7xx-emc.txt diff --git a/Documentation/devicetree/bindings/net/nuvoton,npcm7xx-emc.txt b/Documentation/devicetree/bindings/net/nuvoton,npcm7xx-emc.txt new file mode 100644 index 000000000000..4227597401f5 --- /dev/null +++ b/Documentation/devicetree/bindings/net/nuvoton,npcm7xx-emc.txt @@ -0,0 +1,36 @@ +Nuvoton NPCM7XX 10/100 Ethernet MAC Controller (EMC) + +The NPCM750x provides two identical Ethernet MAC Controllers +for WAN/LAN applications + +Required properties: +- device_type : Should be "network" +- compatible : "nuvoton,npcm750-emc" for Poleg NPCM750. +- reg : Offset and length of the register set for the device. +- interrupts : Contain the emc interrupts with flags for falling edge. + first interrupt dedicated to Txirq + second interrupt dedicated to Rxirq +- phy-mode : Should be "rmii" (see ethernet.txt in the same directory) +- clocks : phandle of emc reference clock. +- use-ncsi : Use the NC-SI stack instead of an MDIO PHY + +Example: + +emc0: eth@f0825000 { + device_type = "network"; + compatible = "nuvoton,npcm750-emc"; + reg = <0xf0825000 0x1000>; + interrupts = , + ; + phy-mode = "rmii"; + clocks = <&clk NPCM7XX_CLK_AHB>; + + #use-ncsi; /* add this to support ncsi */ + + clock-names = "clk_emc"; + pinctrl-names = "default"; + pinctrl-0 = <&r1_pins + &r1err_pins + &r1md_pins>; + status = "okay"; +}; \ No newline at end of file From patchwork Mon Jan 14 13:07:06 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomer Maimon X-Patchwork-Id: 1024524 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 43dZ6G0QjMz9s9G for ; Tue, 15 Jan 2019 00:28:06 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 43dZ6F542DzDqP9 for ; Tue, 15 Jan 2019 00:28:05 +1100 (AEDT) X-Original-To: openbmc@lists.ozlabs.org Delivered-To: openbmc@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (mailfrom) smtp.mailfrom=nuvoton.com (client-ip=212.199.177.27; helo=herzl.nuvoton.co.il; envelope-from=tomer.maimon@nuvoton.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from herzl.nuvoton.co.il (212.199.177.27.static.012.net.il [212.199.177.27]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 43dYfS05fdzDqVv for ; Tue, 15 Jan 2019 00:07:25 +1100 (AEDT) Received: from taln60.nuvoton.co.il (ntil-fw [212.199.177.25]) by herzl.nuvoton.co.il (8.13.8/8.13.8) with ESMTP id x0ECbKtS019005; Mon, 14 Jan 2019 14:37:20 +0200 Received: by taln60.nuvoton.co.il (Postfix, from userid 10070) id 2E43C62DA4; Mon, 14 Jan 2019 15:07:18 +0200 (IST) From: Tomer Maimon To: joel@jms.id.au Subject: [linux dev-4.19 11/15] net: npcm: add NPCM7xx Ethernet MAC controller Date: Mon, 14 Jan 2019 15:07:06 +0200 Message-Id: <20190114130710.427600-12-tmaimon77@gmail.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20190114130710.427600-1-tmaimon77@gmail.com> References: <20190114130710.427600-1-tmaimon77@gmail.com> X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Avi Fishman , openbmc@lists.ozlabs.org, Tomer Maimon Errors-To: openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "openbmc" Add Nuvoton BMC NPCM7xx Ethernet MAC controller (EMC) driver. Signed-off-by: Avi Fishman Signed-off-by: Tomer Maimon --- drivers/net/ethernet/nuvoton/Kconfig | 17 +- drivers/net/ethernet/nuvoton/Makefile | 2 + drivers/net/ethernet/nuvoton/npcm7xx_emc.c | 2091 ++++++++++++++++++++++++++++ 3 files changed, 2109 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/nuvoton/npcm7xx_emc.c diff --git a/drivers/net/ethernet/nuvoton/Kconfig b/drivers/net/ethernet/nuvoton/Kconfig index 71c973f8e50f..b12997687cb1 100644 --- a/drivers/net/ethernet/nuvoton/Kconfig +++ b/drivers/net/ethernet/nuvoton/Kconfig @@ -5,7 +5,7 @@ config NET_VENDOR_NUVOTON bool "Nuvoton devices" default y - depends on ARM && ARCH_W90X900 + depends on ARM && (ARCH_W90X900 || ARCH_NPCM7XX) ---help--- If you have a network (Ethernet) card belonging to this class, say Y. @@ -25,4 +25,19 @@ config W90P910_ETH Say Y here if you want to use built-in Ethernet ports on w90p910 processor. +config NPCM7XX_EMC_ETH + bool "Nuvoton NPCM7XX Ethernet EMC" + depends on ARM && ARCH_NPCM7XX + select PHYLIB + select MII + help + Say Y here if you want to use built-in Ethernet MAC + on NPCM750 MCU. + +config NPCM7XX_EMC_ETH_DEBUG + bool "Nuvoton NPCM7XX Ethernet EMC debug" + depends on NPCM7XX_EMC_ETH + help + Say Y here if you want debug info via /proc/driver/npcm7xx_emc.x + endif # NET_VENDOR_NUVOTON diff --git a/drivers/net/ethernet/nuvoton/Makefile b/drivers/net/ethernet/nuvoton/Makefile index 171aa044bd3b..513a60647c55 100644 --- a/drivers/net/ethernet/nuvoton/Makefile +++ b/drivers/net/ethernet/nuvoton/Makefile @@ -2,4 +2,6 @@ # Makefile for the Nuvoton network device drivers. # +#Eternet 10/100 EMC obj-$(CONFIG_W90P910_ETH) += w90p910_ether.o +obj-$(CONFIG_NPCM7XX_EMC_ETH) += npcm7xx_emc.o diff --git a/drivers/net/ethernet/nuvoton/npcm7xx_emc.c b/drivers/net/ethernet/nuvoton/npcm7xx_emc.c new file mode 100644 index 000000000000..3615e227545e --- /dev/null +++ b/drivers/net/ethernet/nuvoton/npcm7xx_emc.c @@ -0,0 +1,2091 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2014-2019 Nuvoton Technology corporation. + +#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG +#define DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#ifdef CONFIG_DEBUG_FS +static struct dentry *npcm7xx_fs_dir; +#endif + +#define MFSEL1_OFFSET 0x00C +#define MFSEL3_OFFSET 0x064 +#define INTCR_OFFSET 0x03C + +#define IPSRST1_OFFSET 0x020 + +#define DRV_MODULE_NAME "npcm7xx-emc" +#define DRV_MODULE_VERSION "3.90" + +/* Ethernet MAC Registers */ +#define REG_CAMCMR 0x00 +#define REG_CAMEN 0x04 +#define REG_CAMM_BASE 0x08 +#define REG_CAML_BASE 0x0c +#define REG_TXDLSA 0x88 +#define REG_RXDLSA 0x8C +#define REG_MCMDR 0x90 +#define REG_MIID 0x94 +#define REG_MIIDA 0x98 +#define REG_FFTCR 0x9C +#define REG_TSDR 0xa0 +#define REG_RSDR 0xa4 +#define REG_DMARFC 0xa8 +#define REG_MIEN 0xac +#define REG_MISTA 0xb0 +#define REG_MGSTA 0xb4 +#define REG_MPCNT 0xb8 +#define REG_MRPC 0xbc +#define REG_MRPCC 0xc0 +#define REG_MREPC 0xc4 +#define REG_DMARFS 0xc8 +#define REG_CTXDSA 0xcc +#define REG_CTXBSA 0xd0 +#define REG_CRXDSA 0xd4 +#define REG_CRXBSA 0xd8 + +/* EMC Diagnostic Registers */ +#define REG_RXFSM 0x200 +#define REG_TXFSM 0x204 +#define REG_FSM0 0x208 +#define REG_FSM1 0x20c +#define REG_DCR 0x210 +#define REG_DMMIR 0x214 +#define REG_BISTR 0x300 + +/* mac controller bit */ +#define MCMDR_RXON BIT(0) +#define MCMDR_ALP BIT(1) +#define MCMDR_ACP BIT(3) +#define MCMDR_SPCRC BIT(5) +#define MCMDR_TXON BIT(8) +#define MCMDR_NDEF BIT(9) +#define MCMDR_FDUP BIT(18) +#define MCMDR_ENMDC BIT(19) +#define MCMDR_OPMOD BIT(20) +#define SWR BIT(24) + +/* cam command regiser */ +#define CAMCMR_AUP BIT(0) +#define CAMCMR_AMP BIT(1) +#define CAMCMR_ABP BIT(2) +#define CAMCMR_CCAM BIT(3) +#define CAMCMR_ECMP BIT(4) + +/* cam enable regiser */ +#define CAM0EN BIT(0) + +/* mac mii controller bit */ +#define PHYAD BIT(8) +#define PHYWR BIT(16) +#define PHYBUSY BIT(17) +#define PHYPRESP BIT(18) +#define MDCON BIT(19) +#define CAM_ENTRY_SIZE 0x08 + +/* rx and tx status */ +#define TXDS_TXCP BIT(19) +#define RXDS_CRCE BIT(17) +#define RXDS_PTLE BIT(19) +#define RXDS_RXGD BIT(20) +#define RXDS_ALIE BIT(21) +#define RXDS_RP BIT(22) + +/* mac interrupt status*/ +#define MISTA_RXINTR BIT(0) +#define MISTA_CRCE BIT(1) +#define MISTA_RXOV BIT(2) +#define MISTA_PTLE BIT(3) +#define MISTA_RXGD BIT(4) +#define MISTA_ALIE BIT(5) +#define MISTA_RP BIT(6) +#define MISTA_MMP BIT(7) +#define MISTA_DFOI BIT(8) +#define MISTA_DENI BIT(9) +#define MISTA_RDU BIT(10) +#define MISTA_RXBERR BIT(11) +#define MISTA_CFR BIT(14) +#define MISTA_TXINTR BIT(16) +#define MISTA_TXEMP BIT(17) +#define MISTA_TXCP BIT(18) +#define MISTA_EXDEF BIT(19) +#define MISTA_NCS BIT(20) +#define MISTA_TXABT BIT(21) +#define MISTA_LC BIT(22) +#define MISTA_TDU BIT(23) +#define MISTA_TXBERR BIT(24) + +/* Transmit/Receive Start Demand Register */ +#define ENSTART BIT(0) + +#define ENRXINTR BIT(0) +#define ENCRCE BIT(1) +#define EMRXOV BIT(2) +#define ENPTLE BIT(3) +#define ENRXGD BIT(4) +#define ENALIE BIT(5) +#define ENRP BIT(6) +#define ENMMP BIT(7) +#define ENDFO BIT(8) +#define ENDENI BIT(9) +#define ENRDU BIT(10) +#define ENRXBERR BIT(11) +#define ENCFR BIT(14) +#define ENTXINTR BIT(16) +#define ENTXEMP BIT(17) +#define ENTXCP BIT(18) +#define ENTXDEF BIT(19) +#define ENNCS BIT(20) +#define ENTXABT BIT(21) +#define ENLC BIT(22) +#define ENTDU BIT(23) +#define ENTXBERR BIT(24) + +/* rx and tx owner bit */ +#define RX_OWN_DMA BIT(31) +#define TX_OWN_DMA BIT(31) + +/* tx frame desc controller bit */ +#define MACTXINTEN BIT(2) +#define CRCMODE BIT(1) +#define PADDINGMODE BIT(0) + +/* fftcr controller bit */ +#define RXTHD (0x03 << 0) +#define TXTHD (0x02 << 8) +#define BLENGTH (0x02 << 20) + +/* global setting for driver */ +#define RX_QUEUE_LEN 128 +#define TX_QUEUE_LEN 64 +#define MAX_RBUFF_SZ 0x600 +#define MAX_TBUFF_SZ 0x600 +#define TX_TIMEOUT 50 +#define DELAY 1000 +#define CAM0 0x0 +#define RX_POLL_SIZE 16 + +#ifdef CONFIG_VLAN_8021Q +#define IS_VLAN 1 +#else +#define IS_VLAN 0 +#endif + +#define MAX_PACKET_SIZE (1514 + (IS_VLAN * 4)) +#define MAX_PACKET_SIZE_W_CRC (MAX_PACKET_SIZE + 4) /* 1518 */ + +#define MHZ (1000 * 1000) +#define MII_TIMEOUT 100 + +struct plat_npcm7xx_emc_data { + char *phy_bus_name; + int phy_addr; + unsigned char mac_addr[ETH_ALEN]; +}; + +struct npcm7xx_rxbd { + __le32 sl; + __le32 buffer; + __le32 reserved; + __le32 next; +}; + +struct npcm7xx_txbd { + __le32 mode; /* Ownership bit and some other bits */ + __le32 buffer; /* Transmit Buffer Starting Address */ + __le32 sl; /* Transmit Byte Count and status bits */ + __le32 next; /* Next Tx Descriptor Starting Address */ +}; + +struct npcm7xx_ether { + struct sk_buff *rx_skb[RX_QUEUE_LEN]; + struct sk_buff *tx_skb[TX_QUEUE_LEN]; + spinlock_t lock; /* lock sk */ + struct npcm7xx_rxbd *rdesc; + struct npcm7xx_txbd *tdesc; + dma_addr_t rdesc_phys; + dma_addr_t tdesc_phys; + struct net_device_stats stats; + struct platform_device *pdev; + struct net_device *ndev; + struct resource *res; + unsigned int msg_enable; + struct mii_bus *mii_bus; + struct phy_device *phy_dev; + struct napi_struct napi; + struct ncsi_dev *ncsidev; + bool use_ncsi; + void __iomem *reg; + int rxirq; + int txirq; + unsigned int cur_tx; + unsigned int cur_rx; + unsigned int finish_tx; + unsigned int pending_tx; + __le32 start_tx_ptr; + __le32 start_rx_ptr; + unsigned int rx_berr; + unsigned int rx_err; + unsigned int rdu; + unsigned int rxov; + __le32 camcmr; + unsigned int rx_stuck; + int link; + int speed; + int duplex; + int need_reset; + char *dump_buf; + struct regmap *rst_regmap; + + /* debug counters */ + unsigned int max_waiting_rx; + unsigned int rx_count_pool; + unsigned int count_xmit; + unsigned int rx_int_count; + unsigned int rx_err_count; + unsigned int tx_int_count; + unsigned int tx_tdu; + unsigned int tx_tdu_i; + unsigned int tx_cp_i; + unsigned int count_finish; + +#ifdef CONFIG_DEBUG_FS + struct dentry *dbgfs_dir; + struct dentry *dbgfs_status; + struct dentry *dbgfs_dma_cap; +#endif +}; + +#if defined CONFIG_NPCM7XX_EMC_ETH_DEBUG || defined CONFIG_DEBUG_FS +#define REG_PRINT(reg_name) {t = scnprintf(next, size, "%-10s = %08X\n", \ + #reg_name, readl(ether->reg + reg_name)); size -= t; next += t; } +#define DUMP_PRINT(f, x...) {t = scnprintf(next, size, f, ## x); size -= t; \ + next += t; } + +static int npcm7xx_info_dump(char *buf, int count, struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + struct npcm7xx_txbd *txbd; + struct npcm7xx_rxbd *rxbd; + unsigned long flags; + unsigned int i, cur, txd_offset, rxd_offset; + char *next = buf; + unsigned int size = count; + int t; + int is_locked = spin_is_locked(ðer->lock); + + if (!is_locked) + spin_lock_irqsave(ðer->lock, flags); + + /* ------basic driver information ---- */ + DUMP_PRINT("NPCM7XX EMC %s driver version: %s\n", dev->name, + DRV_MODULE_VERSION); + + REG_PRINT(REG_CAMCMR); + REG_PRINT(REG_CAMEN); + REG_PRINT(REG_CAMM_BASE); + REG_PRINT(REG_CAML_BASE); + REG_PRINT(REG_TXDLSA); + REG_PRINT(REG_RXDLSA); + REG_PRINT(REG_MCMDR); + REG_PRINT(REG_MIID); + REG_PRINT(REG_MIIDA); + REG_PRINT(REG_FFTCR); + REG_PRINT(REG_TSDR); + REG_PRINT(REG_RSDR); + REG_PRINT(REG_DMARFC); + REG_PRINT(REG_MIEN); + REG_PRINT(REG_MISTA); + REG_PRINT(REG_MGSTA); + REG_PRINT(REG_MPCNT); + writel(0x7FFF, (ether->reg + REG_MPCNT)); + REG_PRINT(REG_MRPC); + REG_PRINT(REG_MRPCC); + REG_PRINT(REG_MREPC); + REG_PRINT(REG_DMARFS); + REG_PRINT(REG_CTXDSA); + REG_PRINT(REG_CTXBSA); + REG_PRINT(REG_CRXDSA); + REG_PRINT(REG_CRXBSA); + REG_PRINT(REG_RXFSM); + REG_PRINT(REG_TXFSM); + REG_PRINT(REG_FSM0); + REG_PRINT(REG_FSM1); + REG_PRINT(REG_DCR); + REG_PRINT(REG_DMMIR); + REG_PRINT(REG_BISTR); + DUMP_PRINT("\n"); + + DUMP_PRINT("netif_queue %s\n\n", netif_queue_stopped(dev) ? + "Stopped" : "Running"); + if (ether->rdesc) + DUMP_PRINT("napi is %s\n\n", test_bit(NAPI_STATE_SCHED, + ðer->napi.state) ? + "scheduled" : + "not scheduled"); + + txd_offset = (readl((ether->reg + REG_CTXDSA)) - + readl((ether->reg + REG_TXDLSA))) / + sizeof(struct npcm7xx_txbd); + DUMP_PRINT("TXD offset %6d\n", txd_offset); + DUMP_PRINT("cur_tx %6d\n", ether->cur_tx); + DUMP_PRINT("finish_tx %6d\n", ether->finish_tx); + DUMP_PRINT("pending_tx %6d\n", ether->pending_tx); + /* debug counters */ + DUMP_PRINT("tx_tdu %6d\n", ether->tx_tdu); + ether->tx_tdu = 0; + DUMP_PRINT("tx_tdu_i %6d\n", ether->tx_tdu_i); + ether->tx_tdu_i = 0; + DUMP_PRINT("tx_cp_i %6d\n", ether->tx_cp_i); + ether->tx_cp_i = 0; + DUMP_PRINT("tx_int_count %6d\n", ether->tx_int_count); + ether->tx_int_count = 0; + DUMP_PRINT("count_xmit tx %6d\n", ether->count_xmit); + ether->count_xmit = 0; + DUMP_PRINT("count_finish %6d\n", ether->count_finish); + ether->count_finish = 0; + DUMP_PRINT("\n"); + + rxd_offset = (readl((ether->reg + REG_CRXDSA)) - + readl((ether->reg + REG_RXDLSA))) + / sizeof(struct npcm7xx_txbd); + DUMP_PRINT("RXD offset %6d\n", rxd_offset); + DUMP_PRINT("cur_rx %6d\n", ether->cur_rx); + DUMP_PRINT("rx_err %6d\n", ether->rx_err); + ether->rx_err = 0; + DUMP_PRINT("rx_berr %6d\n", ether->rx_berr); + ether->rx_berr = 0; + DUMP_PRINT("rx_stuck %6d\n", ether->rx_stuck); + ether->rx_stuck = 0; + DUMP_PRINT("rdu %6d\n", ether->rdu); + ether->rdu = 0; + DUMP_PRINT("rxov rx %6d\n", ether->rxov); + ether->rxov = 0; + /* debug counters */ + DUMP_PRINT("rx_int_count %6d\n", ether->rx_int_count); + ether->rx_int_count = 0; + DUMP_PRINT("rx_err_count %6d\n", ether->rx_err_count); + ether->rx_err_count = 0; + DUMP_PRINT("rx_count_pool %6d\n", ether->rx_count_pool); + ether->rx_count_pool = 0; + DUMP_PRINT("max_waiting_rx %5d\n", ether->max_waiting_rx); + ether->max_waiting_rx = 0; + DUMP_PRINT("\n"); + DUMP_PRINT("need_reset %5d\n", ether->need_reset); + + if (ether->tdesc && ether->rdesc) { + cur = ether->finish_tx - 2; + for (i = 0; i < 3; i++) { + cur = (cur + 1) % TX_QUEUE_LEN; + txbd = (ether->tdesc + cur); + DUMP_PRINT("finish %3d txbd mode %08X buffer %08X sl %08X next %08X tx_skb %p\n", + cur, txbd->mode, txbd->buffer, + txbd->sl, txbd->next, ether->tx_skb[cur]); + } + DUMP_PRINT("\n"); + + cur = txd_offset - 2; + for (i = 0; i < 3; i++) { + cur = (cur + 1) % TX_QUEUE_LEN; + txbd = (ether->tdesc + cur); + DUMP_PRINT("txd_of %3d txbd mode %08X buffer %08X sl %08X next %08X\n", + cur, txbd->mode, txbd->buffer, + txbd->sl, txbd->next); + } + DUMP_PRINT("\n"); + + cur = ether->cur_tx - 63; + for (i = 0; i < 64; i++) { + cur = (cur + 1) % TX_QUEUE_LEN; + txbd = (ether->tdesc + cur); + DUMP_PRINT("cur_tx %3d txbd mode %08X buffer %08X sl %08X next %08X\n", + cur, txbd->mode, txbd->buffer, + txbd->sl, txbd->next); + } + DUMP_PRINT("\n"); + + cur = ether->cur_rx - 63; + for (i = 0; i < 64; i++) { + cur = (cur + 1) % RX_QUEUE_LEN; + rxbd = (ether->rdesc + cur); + DUMP_PRINT("cur_rx %3d rxbd sl %08X buffer %08X sl %08X next %08X\n", + cur, rxbd->sl, rxbd->buffer, + rxbd->reserved, rxbd->next); + } + DUMP_PRINT("\n"); + + cur = rxd_offset - 2; + for (i = 0; i < 3; i++) { + cur = (cur + 1) % RX_QUEUE_LEN; + rxbd = (ether->rdesc + cur); + DUMP_PRINT("rxd_of %3d rxbd sl %08X buffer %08X sl %08X next %08X\n", + cur, rxbd->sl, rxbd->buffer, + rxbd->reserved, rxbd->next); + } + DUMP_PRINT("\n"); + } + + if (!is_locked) + spin_unlock_irqrestore(ðer->lock, flags); + + return count - size; +} +#endif + +#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG +static void npcm7xx_info_print(struct net_device *dev) +{ + char *emc_dump_buf; + int count; + struct npcm7xx_ether *ether; + struct platform_device *pdev; + const size_t print_size = 5 * PAGE_SIZE; + + ether = netdev_priv(dev); + pdev = ether->pdev; + + emc_dump_buf = kmalloc(print_size, GFP_KERNEL); + if (!emc_dump_buf) { + dev_err(&pdev->dev, "kmalloc failed\n"); + } else { + char c; + char *tmp_buf = emc_dump_buf; + + count = npcm7xx_info_dump(emc_dump_buf, print_size, dev); + while (count > 512) { + c = tmp_buf[512]; + tmp_buf[512] = 0; + dev_info(&pdev->dev, "%s", tmp_buf); + tmp_buf += 512; + tmp_buf[0] = c; + count -= 512; + } + dev_info(&pdev->dev, "%s", tmp_buf); + kfree(emc_dump_buf); + } +} +#endif + +#ifdef CONFIG_DEBUG_FS +#include + +static int npcm7xx_debug_show(struct seq_file *sf, void *v) +{ + struct net_device *dev = (struct net_device *)sf->private; + struct npcm7xx_ether *ether = netdev_priv(dev); + const size_t print_size = 5 * PAGE_SIZE; + + if (!ether->dump_buf) { + ether->dump_buf = kmalloc(print_size, GFP_KERNEL); + if (!ether->dump_buf) + return -1; + npcm7xx_info_dump(ether->dump_buf, print_size, dev); + } + + seq_printf(sf, "%s", ether->dump_buf); + if (sf->count < sf->size) { + kfree(ether->dump_buf); + ether->dump_buf = NULL; + } + + return 0; +} + +static int npcm7xx_debug_show_open(struct inode *inode, struct file *file) +{ + return single_open(file, npcm7xx_debug_show, inode->i_private); +} + +static const struct file_operations npcm7xx_debug_show_fops = { + .open = npcm7xx_debug_show_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int npcm7xx_debug_reset(struct seq_file *sf, void *v) +{ + struct net_device *dev = (struct net_device *)sf->private; + struct npcm7xx_ether *ether = netdev_priv(dev); + unsigned long flags; + + seq_puts(sf, "Ask to reset the module\n"); + spin_lock_irqsave(ðer->lock, flags); + writel(0, (ether->reg + REG_MIEN)); + spin_unlock_irqrestore(ðer->lock, flags); + ether->need_reset = 1; + napi_schedule(ðer->napi); + + return 0; +} + +static int npcm7xx_debug_reset_open(struct inode *inode, struct file *file) +{ + return single_open(file, npcm7xx_debug_reset, inode->i_private); +} + +static const struct file_operations npcm7xx_debug_reset_fops = { + .owner = THIS_MODULE, + .open = npcm7xx_debug_reset_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int npcm7xx_debug_fs(struct npcm7xx_ether *ether) +{ + /* Create debugfs main directory if it doesn't exist yet */ + if (!npcm7xx_fs_dir) { + npcm7xx_fs_dir = debugfs_create_dir(DRV_MODULE_NAME, NULL); + + if (!npcm7xx_fs_dir || IS_ERR(npcm7xx_fs_dir)) { + dev_err(ðer->pdev->dev, "ERROR %s, debugfs create directory failed\n", + DRV_MODULE_NAME); + return -ENOMEM; + } + } + + /* Create per netdev entries */ + ether->dbgfs_dir = debugfs_create_dir(ether->ndev->name, + npcm7xx_fs_dir); + if (!ether->dbgfs_dir || IS_ERR(ether->dbgfs_dir)) { + dev_err(ðer->pdev->dev, "ERROR failed to create %s directory\n", ether->ndev->name); + return -ENOMEM; + } + + /* Entry to report DMA RX/TX rings */ + ether->dbgfs_status = + debugfs_create_file("status", 0444, + ether->dbgfs_dir, ether->ndev, + &npcm7xx_debug_show_fops); + + if (!ether->dbgfs_status || IS_ERR(ether->dbgfs_status)) { + dev_err(ðer->pdev->dev, "ERROR creating \'status\' debugfs file\n"); + debugfs_remove_recursive(ether->dbgfs_dir); + + return -ENOMEM; + } + + /* Entry to report the DMA HW features */ + ether->dbgfs_dma_cap = debugfs_create_file("do_reset", 0444, + ether->dbgfs_dir, + ether->ndev, + &npcm7xx_debug_reset_fops); + + if (!ether->dbgfs_dma_cap || IS_ERR(ether->dbgfs_dma_cap)) { + dev_err(ðer->pdev->dev, "ERROR creating stmmac \'do_reset\' debugfs file\n"); + debugfs_remove_recursive(ether->dbgfs_dir); + + return -ENOMEM; + } + + return 0; +} +#endif + +static void npcm7xx_opmode(struct net_device *dev, int speed, int duplex) +{ + __le32 val; + struct npcm7xx_ether *ether = netdev_priv(dev); + + val = readl((ether->reg + REG_MCMDR)); + if (speed == 100) + val |= MCMDR_OPMOD; + else + val &= ~MCMDR_OPMOD; + + if (duplex == DUPLEX_FULL) + val |= MCMDR_FDUP; + else + val &= ~MCMDR_FDUP; + + writel(val, (ether->reg + REG_MCMDR)); +} + +static void adjust_link(struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + struct phy_device *phydev = ether->phy_dev; + bool status_change = false; + unsigned long flags; + + /* clear GPIO interrupt status whihc indicates PHY statu change? */ + spin_lock_irqsave(ðer->lock, flags); + + if (phydev->link) { + if (ether->speed != phydev->speed || + ether->duplex != phydev->duplex) { + ether->speed = phydev->speed; + ether->duplex = phydev->duplex; + status_change = true; + } + } else { + ether->speed = 0; + ether->duplex = -1; + } + + if (phydev->link != ether->link) { + ether->link = phydev->link; + status_change = true; + } + + spin_unlock_irqrestore(ðer->lock, flags); + + if (status_change) + npcm7xx_opmode(dev, ether->speed, ether->duplex); +} + +static void npcm7xx_write_cam(struct net_device *dev, + unsigned int x, unsigned char *pval) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + __le32 msw, lsw; + + msw = (pval[0] << 24) | (pval[1] << 16) | (pval[2] << 8) | pval[3]; + + lsw = (pval[4] << 24) | (pval[5] << 16); + + writel(lsw, (ether->reg + REG_CAML_BASE) + x * CAM_ENTRY_SIZE); + writel(msw, (ether->reg + REG_CAMM_BASE) + x * CAM_ENTRY_SIZE); + dev_dbg(ðer->pdev->dev, "REG_CAML_BASE = 0x%08X REG_CAMM_BASE = 0x%08X", lsw, msw); +} + +static struct sk_buff *get_new_skb(struct net_device *dev, u32 i) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + struct sk_buff *skb = dev_alloc_skb(roundup(MAX_PACKET_SIZE_W_CRC, 4)); + + if (!skb) + return NULL; + + /* Do not unmark the following skb_reserve() Receive Buffer Starting + * Address must be aligned to 4 bytes and the following line + * if unmarked will make it align to 2 and this likely will + * hult the RX and crash the linux skb_reserve(skb, NET_IP_ALIGN); + */ + skb->dev = dev; + (ether->rdesc + i)->buffer = + dma_map_single(&dev->dev, skb->data, + roundup(MAX_PACKET_SIZE_W_CRC, 4), + DMA_FROM_DEVICE); + ether->rx_skb[i] = skb; + + return skb; +} + +static int npcm7xx_init_desc(struct net_device *dev) +{ + struct npcm7xx_ether *ether; + struct npcm7xx_txbd *tdesc; + struct npcm7xx_rxbd *rdesc; + struct platform_device *pdev; + unsigned int i; + + ether = netdev_priv(dev); + pdev = ether->pdev; + + if (!ether->tdesc) { + ether->tdesc = (struct npcm7xx_txbd *) + dma_alloc_coherent(&pdev->dev, + sizeof(struct npcm7xx_txbd) * + TX_QUEUE_LEN, + ðer->tdesc_phys, + GFP_KERNEL); + + if (!ether->tdesc) { + dev_err(&pdev->dev, "Failed to allocate memory for tx desc\n"); + return -ENOMEM; + } + } + + if (!ether->rdesc) { + ether->rdesc = (struct npcm7xx_rxbd *) + dma_alloc_coherent(&pdev->dev, + sizeof(struct npcm7xx_rxbd) * + RX_QUEUE_LEN, + ðer->rdesc_phys, + GFP_KERNEL); + + if (!ether->rdesc) { + dev_err(&pdev->dev, "Failed to allocate memory for rx desc\n"); + dma_free_coherent(&pdev->dev, + sizeof(struct npcm7xx_txbd) * + TX_QUEUE_LEN, ether->tdesc, + ether->tdesc_phys); + ether->tdesc = NULL; + return -ENOMEM; + } + } + + for (i = 0; i < TX_QUEUE_LEN; i++) { + unsigned int offset; + + tdesc = (ether->tdesc + i); + + if (i == TX_QUEUE_LEN - 1) + offset = 0; + else + offset = sizeof(struct npcm7xx_txbd) * (i + 1); + + tdesc->next = ether->tdesc_phys + offset; + tdesc->buffer = (__le32)NULL; + tdesc->sl = 0; + tdesc->mode = 0; + } + + ether->start_tx_ptr = ether->tdesc_phys; + + for (i = 0; i < RX_QUEUE_LEN; i++) { + unsigned int offset; + + rdesc = (ether->rdesc + i); + + if (i == RX_QUEUE_LEN - 1) + offset = 0; + else + offset = sizeof(struct npcm7xx_rxbd) * (i + 1); + + rdesc->next = ether->rdesc_phys + offset; + rdesc->sl = RX_OWN_DMA; + + if (!get_new_skb(dev, i)) { + dev_err(&pdev->dev, "get_new_skb() failed\n"); + + for (; i != 0; i--) { + dma_unmap_single(&dev->dev, (dma_addr_t) + ((ether->rdesc + i)->buffer), + roundup(MAX_PACKET_SIZE_W_CRC, + 4), DMA_FROM_DEVICE); + dev_kfree_skb_any(ether->rx_skb[i]); + ether->rx_skb[i] = NULL; + } + + dma_free_coherent(&pdev->dev, + sizeof(struct npcm7xx_txbd) * + TX_QUEUE_LEN, + ether->tdesc, ether->tdesc_phys); + dma_free_coherent(&pdev->dev, + sizeof(struct npcm7xx_rxbd) * + RX_QUEUE_LEN, + ether->rdesc, ether->rdesc_phys); + + return -ENOMEM; + } + } + + ether->start_rx_ptr = ether->rdesc_phys; + wmb(); + for (i = 0; i < TX_QUEUE_LEN; i++) + ether->tx_skb[i] = NULL; + + return 0; +} + +/* This API must call with Tx/Rx stopped */ +static void npcm7xx_free_desc(struct net_device *dev, + bool free_also_descriptors) +{ + struct sk_buff *skb; + u32 i; + struct npcm7xx_ether *ether = netdev_priv(dev); + struct platform_device *pdev = ether->pdev; + + for (i = 0; i < TX_QUEUE_LEN; i++) { + skb = ether->tx_skb[i]; + if (skb) { + dma_unmap_single(&dev->dev, (dma_addr_t)((ether->tdesc + + i)->buffer), + skb->len, DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + ether->tx_skb[i] = NULL; + } + } + + for (i = 0; i < RX_QUEUE_LEN; i++) { + skb = ether->rx_skb[i]; + if (skb) { + dma_unmap_single(&dev->dev, (dma_addr_t)((ether->rdesc + + i)->buffer), + roundup(MAX_PACKET_SIZE_W_CRC, 4), + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + ether->rx_skb[i] = NULL; + } + } + + if (free_also_descriptors) { + if (ether->tdesc) + dma_free_coherent(&pdev->dev, + sizeof(struct npcm7xx_txbd) * + TX_QUEUE_LEN, + ether->tdesc, ether->tdesc_phys); + ether->tdesc = NULL; + + if (ether->rdesc) + dma_free_coherent(&pdev->dev, + sizeof(struct npcm7xx_rxbd) * + RX_QUEUE_LEN, + ether->rdesc, ether->rdesc_phys); + ether->rdesc = NULL; + } +} + +static void npcm7xx_set_fifo_threshold(struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + __le32 val; + + val = RXTHD | TXTHD | BLENGTH; + writel(val, (ether->reg + REG_FFTCR)); +} + +static void npcm7xx_return_default_idle(struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + __le32 val; + __le32 saved_bits; + + val = readl((ether->reg + REG_MCMDR)); + saved_bits = val & (MCMDR_FDUP | MCMDR_OPMOD); + val |= SWR; + writel(val, (ether->reg + REG_MCMDR)); + + /* During the EMC reset the AHB will read 0 from all registers, + * so in order to see if the reset finished we can't count on + * (ether->reg + REG_MCMDR).SWR to become 0, instead we read another + * register that its reset value is not 0, + * we choose (ether->reg + REG_FFTCR). + */ + do { + val = readl((ether->reg + REG_FFTCR)); + } while (val == 0); + + /* + * Now we can verify if (ether->reg + REG_MCMDR).SWR became + * 0 (probably it will be 0 on the first read). + */ + do { + val = readl((ether->reg + REG_MCMDR)); + } while (val & SWR); + + /* restore values */ + writel(saved_bits, (ether->reg + REG_MCMDR)); +} + +static void npcm7xx_enable_mac_interrupt(struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + __le32 val; + + val = ENRXINTR | /* Start of RX interrupts */ + ENCRCE | + EMRXOV | + (ENPTLE * (!IS_VLAN)) | /* If we don't support VLAN we want interrupt on long packets */ + ENRXGD | + ENALIE | + ENRP | + ENMMP | + ENDFO | + /* ENDENI | */ /* We don't need interrupt on DMA Early Notification */ + ENRDU | /* We don't need interrupt on Receive Descriptor Unavailable Interrupt */ + ENRXBERR | + /* ENCFR | */ + ENTXINTR | /* Start of TX interrupts */ + ENTXEMP | + ENTXCP | + ENTXDEF | + ENNCS | + ENTXABT | + ENLC | + /* ENTDU | */ /* We don't need interrupt on Transmit Descriptor Unavailable at start of operation */ + ENTXBERR; + writel(val, (ether->reg + REG_MIEN)); +} + +static void npcm7xx_get_and_clear_int(struct net_device *dev, + __le32 *val, __le32 mask) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + + *val = readl((ether->reg + REG_MISTA)) & mask; + writel(*val, (ether->reg + REG_MISTA)); +} + +static void npcm7xx_set_global_maccmd(struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + __le32 val; + + val = readl((ether->reg + REG_MCMDR)); + + val |= MCMDR_SPCRC | MCMDR_ENMDC | MCMDR_ACP | MCMDR_NDEF; + if (IS_VLAN) { + /* + * we set ALP accept long packets since VLAN packets + * are 4 bytes longer than 1518 + */ + val |= MCMDR_ALP; + /* limit receive length to 1522 bytes due to VLAN */ + writel(MAX_PACKET_SIZE_W_CRC, (ether->reg + REG_DMARFC)); + } + writel(val, (ether->reg + REG_MCMDR)); +} + +static void npcm7xx_enable_cam(struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + __le32 val; + + npcm7xx_write_cam(dev, CAM0, dev->dev_addr); + + val = readl((ether->reg + REG_CAMEN)); + val |= CAM0EN; + writel(val, (ether->reg + REG_CAMEN)); +} + +static void npcm7xx_set_curdest(struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + + writel(ether->start_rx_ptr, (ether->reg + REG_RXDLSA)); + writel(ether->start_tx_ptr, (ether->reg + REG_TXDLSA)); +} + +static void npcm7xx_ether_set_rx_mode(struct net_device *dev) +{ + struct npcm7xx_ether *ether; + __le32 rx_mode; + + ether = netdev_priv(dev); + + dev_dbg(ðer->pdev->dev, "%s CAMCMR_AUP\n", + (dev->flags & IFF_PROMISC) ? "Set" : "Clear"); + if (dev->flags & IFF_PROMISC) + rx_mode = CAMCMR_AUP | CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP; + else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) + rx_mode = CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP; + else + rx_mode = CAMCMR_ECMP | CAMCMR_ABP; + writel(rx_mode, (ether->reg + REG_CAMCMR)); + ether->camcmr = rx_mode; +} + +static void npcm7xx_reset_mac(struct net_device *dev, int need_free) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + + netif_tx_lock(dev); + + /* disable RX and TX */ + writel(readl((ether->reg + REG_MCMDR)) & ~(MCMDR_TXON | MCMDR_RXON), + (ether->reg + REG_MCMDR)); + + npcm7xx_return_default_idle(dev); + npcm7xx_set_fifo_threshold(dev); + + if (need_free) + npcm7xx_free_desc(dev, false); + + npcm7xx_init_desc(dev); + + ether->cur_tx = 0x0; + ether->finish_tx = 0x0; + ether->pending_tx = 0x0; + ether->cur_rx = 0x0; + ether->tx_tdu = 0; + ether->tx_tdu_i = 0; + ether->tx_cp_i = 0; + + npcm7xx_set_curdest(dev); + npcm7xx_enable_cam(dev); + npcm7xx_ether_set_rx_mode(dev); + npcm7xx_enable_mac_interrupt(dev); + npcm7xx_set_global_maccmd(dev); + + /* enable RX and TX */ + writel(readl((ether->reg + REG_MCMDR)) | MCMDR_TXON | MCMDR_RXON, + (ether->reg + REG_MCMDR)); + + /* trigger RX */ + writel(ENSTART, (ether->reg + REG_RSDR)); + + ether->need_reset = 0; + + netif_wake_queue(dev); + netif_tx_unlock(dev); +} + +static int npcm7xx_mdio_write(struct mii_bus *bus, int phy_id, int regnum, + u16 value) +{ + struct npcm7xx_ether *ether = bus->priv; + unsigned long timeout = jiffies + msecs_to_jiffies(MII_TIMEOUT * 100); + + writel(value, (ether->reg + REG_MIID)); + writel((phy_id << 0x08) | regnum | PHYBUSY | PHYWR, + (ether->reg + REG_MIIDA)); + + /* Wait for completion */ + while (readl((ether->reg + REG_MIIDA)) & PHYBUSY) { + if (time_after(jiffies, timeout)) { + dev_dbg(ðer->pdev->dev, "mdio read timed out\n ether->reg = 0x%x phy_id=0x%x REG_MIIDA=0x%x\n", + (unsigned int)ether->reg, phy_id + , readl((ether->reg + REG_MIIDA))); + return -ETIMEDOUT; + } + cpu_relax(); + } + + return 0; +} + +static int npcm7xx_mdio_read(struct mii_bus *bus, int phy_id, int regnum) +{ + struct npcm7xx_ether *ether = bus->priv; + unsigned long timeout = jiffies + msecs_to_jiffies(MII_TIMEOUT * 100); + + writel((phy_id << 0x08) | regnum | PHYBUSY, (ether->reg + REG_MIIDA)); + + /* Wait for completion */ + while (readl((ether->reg + REG_MIIDA)) & PHYBUSY) { + if (time_after(jiffies, timeout)) { + dev_dbg(ðer->pdev->dev, "mdio read timed out\n ether->reg = 0x%x phy_id=0x%x REG_MIIDA=0x%x\n", + (unsigned int)ether->reg, phy_id + , readl((ether->reg + REG_MIIDA))); + return -ETIMEDOUT; + } + cpu_relax(); + } + + return readl((ether->reg + REG_MIID)); +} + +static int npcm7xx_mdio_reset(struct mii_bus *bus) +{ + /* reset EMAC engine?? */ + return 0; +} + +static int npcm7xx_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *address = addr; + + if (!is_valid_ether_addr((u8 *)address->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(dev->dev_addr, address->sa_data, dev->addr_len); + npcm7xx_write_cam(dev, CAM0, dev->dev_addr); + + return 0; +} + +static int npcm7xx_ether_close(struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + + npcm7xx_return_default_idle(dev); + + if (ether->phy_dev) + phy_stop(ether->phy_dev); + else if (ether->use_ncsi) + ncsi_stop_dev(ether->ncsidev); + + msleep(20); + + free_irq(ether->txirq, dev); + free_irq(ether->rxirq, dev); + + netif_stop_queue(dev); + napi_disable(ðer->napi); + + npcm7xx_free_desc(dev, true); + + kfree(ether->dump_buf); + ether->dump_buf = NULL; + + return 0; +} + +static struct net_device_stats *npcm7xx_ether_stats(struct net_device *dev) +{ + struct npcm7xx_ether *ether; + + ether = netdev_priv(dev); + return ðer->stats; +} + +static int npcm7xx_clean_tx(struct net_device *dev, bool from_xmit) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + struct npcm7xx_txbd *txbd; + struct sk_buff *s; + dma_addr_t cur_entry, entry; + __le32 sl; + + if (ether->pending_tx == 0) + return (0); + + cur_entry = readl((ether->reg + REG_CTXDSA)); + + /* Release old used buffers */ + entry = ether->tdesc_phys + sizeof(struct npcm7xx_txbd) * + (ether->finish_tx); + + while (entry != cur_entry) { + txbd = (ether->tdesc + ether->finish_tx); + s = ether->tx_skb[ether->finish_tx]; + if (!s) + break; + + ether->count_finish++; + + dma_unmap_single(&dev->dev, txbd->buffer, s->len, + DMA_TO_DEVICE); + consume_skb(s); + ether->tx_skb[ether->finish_tx] = NULL; + + if (++ether->finish_tx >= TX_QUEUE_LEN) + ether->finish_tx = 0; + ether->pending_tx--; + + sl = txbd->sl; + if (sl & TXDS_TXCP) { + ether->stats.tx_packets++; + ether->stats.tx_bytes += (sl & 0xFFFF); + } else { + ether->stats.tx_errors++; + } + + entry = ether->tdesc_phys + sizeof(struct npcm7xx_txbd) * + (ether->finish_tx); + } + + if (!from_xmit && unlikely(netif_queue_stopped(dev) && + (TX_QUEUE_LEN - ether->pending_tx) > 1)) { + netif_tx_lock(dev); + if (netif_queue_stopped(dev) && + (TX_QUEUE_LEN - ether->pending_tx) > 1) { + netif_wake_queue(dev); + } + netif_tx_unlock(dev); + } + + return(0); +} + +static int npcm7xx_ether_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + struct npcm7xx_txbd *txbd; + unsigned long flags; + + ether->count_xmit++; + + /* Insert new buffer */ + txbd = (ether->tdesc + ether->cur_tx); + txbd->buffer = dma_map_single(&dev->dev, skb->data, skb->len, + DMA_TO_DEVICE); + ether->tx_skb[ether->cur_tx] = skb; + if (skb->len > MAX_PACKET_SIZE) + dev_err(ðer->pdev->dev, "skb->len (= %d) > MAX_PACKET_SIZE (= %d)\n", + skb->len, MAX_PACKET_SIZE); + + txbd->sl = skb->len > MAX_PACKET_SIZE ? MAX_PACKET_SIZE : skb->len; + dma_wmb(); + + txbd->mode = TX_OWN_DMA | PADDINGMODE | CRCMODE; + wmb(); + + /* trigger TX */ + writel(ENSTART, (ether->reg + REG_TSDR)); + + if (++ether->cur_tx >= TX_QUEUE_LEN) + ether->cur_tx = 0; + + spin_lock_irqsave(ðer->lock, flags); + ether->pending_tx++; + + npcm7xx_clean_tx(dev, true); + + if (ether->pending_tx >= TX_QUEUE_LEN - 1) { + __le32 reg_mien; + unsigned int index_to_wake = ether->cur_tx + + ((TX_QUEUE_LEN * 3) / 4); + + if (index_to_wake >= TX_QUEUE_LEN) + index_to_wake -= TX_QUEUE_LEN; + + txbd = (ether->tdesc + index_to_wake); + txbd->mode = TX_OWN_DMA | PADDINGMODE | CRCMODE | MACTXINTEN; + wmb(); + + writel(MISTA_TDU, (ether->reg + REG_MISTA)); + /* Clear TDU interrupt */ + reg_mien = readl((ether->reg + REG_MIEN)); + + if (reg_mien != 0) + /* Enable TDU interrupt */ + writel(reg_mien | ENTDU, (ether->reg + REG_MIEN)); + + ether->tx_tdu++; + netif_stop_queue(dev); + } + + spin_unlock_irqrestore(ðer->lock, flags); + + return 0; +} + +static irqreturn_t npcm7xx_tx_interrupt(int irq, void *dev_id) +{ + struct npcm7xx_ether *ether; + struct platform_device *pdev; + struct net_device *dev; + __le32 status; + unsigned long flags; + + dev = dev_id; + ether = netdev_priv(dev); + pdev = ether->pdev; + + npcm7xx_get_and_clear_int(dev, &status, 0xFFFF0000); + + ether->tx_int_count++; + + if (status & MISTA_EXDEF) + dev_err(&pdev->dev, "emc defer exceed interrupt status=0x%08X\n" + , status); + else if (status & MISTA_TXBERR) { + dev_err(&pdev->dev, "emc bus error interrupt status=0x%08X\n", + status); +#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG + npcm7xx_info_print(dev); +#endif + spin_lock_irqsave(ðer->lock, flags); + writel(0, (ether->reg + REG_MIEN)); /* disable any interrupt */ + spin_unlock_irqrestore(ðer->lock, flags); + ether->need_reset = 1; + } else if (status & ~(MISTA_TXINTR | MISTA_TXCP | MISTA_TDU)) + dev_err(&pdev->dev, "emc other error interrupt status=0x%08X\n", + status); + + /* if we got MISTA_TXCP | MISTA_TDU remove those interrupt and call napi */ + if (status & (MISTA_TXCP | MISTA_TDU) & + readl((ether->reg + REG_MIEN))) { + __le32 reg_mien; + + spin_lock_irqsave(ðer->lock, flags); + reg_mien = readl((ether->reg + REG_MIEN)); + if (reg_mien & ENTDU) + /* Disable TDU interrupt */ + writel(reg_mien & (~ENTDU), (ether->reg + REG_MIEN)); + + spin_unlock_irqrestore(ðer->lock, flags); + + if (status & MISTA_TXCP) + ether->tx_cp_i++; + if (status & MISTA_TDU) + ether->tx_tdu_i++; + } else { + dev_dbg(&pdev->dev, "status=0x%08X\n", status); + } + + napi_schedule(ðer->napi); + + return IRQ_HANDLED; +} + +static irqreturn_t npcm7xx_rx_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct npcm7xx_ether *ether = netdev_priv(dev); + struct platform_device *pdev = ether->pdev; + __le32 status; + unsigned long flags; + unsigned int any_err = 0; + __le32 rxfsm; + + npcm7xx_get_and_clear_int(dev, &status, 0xFFFF); + ether->rx_int_count++; + + if (unlikely(status & MISTA_RXBERR)) { + ether->rx_berr++; + dev_err(&pdev->dev, "emc rx bus error status=0x%08X\n", status); +#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG + npcm7xx_info_print(dev); +#endif + spin_lock_irqsave(ðer->lock, flags); + writel(0, (ether->reg + REG_MIEN)); /* disable any interrupt */ + spin_unlock_irqrestore(ðer->lock, flags); + ether->need_reset = 1; + napi_schedule(ðer->napi); + return IRQ_HANDLED; + } + + if (unlikely(status & (MISTA_RXOV | MISTA_RDU))) { + /* + * filter out all received packets until we have + * enough available buffer descriptors + */ + writel(0, (ether->reg + REG_CAMCMR)); + any_err = 1; + if (status & (MISTA_RXOV)) + ether->rxov++; + if (status & (MISTA_RDU)) + ether->rdu++; + + /* + * workaround Errata 1.36: EMC Hangs on receiving 253-256 + * byte packet + */ + rxfsm = readl((ether->reg + REG_RXFSM)); + + if ((rxfsm & 0xFFFFF000) == 0x08044000) { + int i; + + for (i = 0; i < 32; i++) { + rxfsm = readl((ether->reg + REG_RXFSM)); + if ((rxfsm & 0xFFFFF000) != 0x08044000) + break; + } + if (i == 32) { + ether->rx_stuck++; + spin_lock_irqsave(ðer->lock, flags); +#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG + npcm7xx_info_print(dev); +#endif + writel(0, (ether->reg + REG_MIEN)); + spin_unlock_irqrestore(ðer->lock, flags); + ether->need_reset = 1; + napi_schedule(ðer->napi); + dev_err(&pdev->dev, "stuck on REG_RXFSM = 0x%08X status=%08X doing reset!\n", rxfsm, status); + return IRQ_HANDLED; + } + } + } + + /* echo MISTA status on unexpected flags although we don't do anithing with them */ + if (unlikely(status & + (/* MISTA_RXINTR | */ /* Receive - all RX interrupt set this */ + MISTA_CRCE | /* CRC Error */ + /* MISTA_RXOV | */ /* Receive FIFO Overflow - we alread handled it */ + (MISTA_PTLE * !IS_VLAN) | /* Packet Too Long is needed if VLAN is not supported */ + /* MISTA_RXGD | */ /* Receive Good - this is the common good case */ + MISTA_ALIE | /* Alignment Error */ + MISTA_RP | /* Runt Packet */ + MISTA_MMP | /* More Missed Packet */ + MISTA_DFOI | /* Maximum Frame Length */ + /* MISTA_DENI | */ /* DMA Early Notification - every packet get this */ + /* MISTA_RDU | */ /* Receive Descriptor Unavailable */ + /* MISTA_RXBERR | */ /* Receive Bus Error Interrupt - we alread handled it */ + /* MISTA_CFR | */ /* Control Frame Receive - not an error */ + 0))) { + dev_dbg(&pdev->dev, "emc rx MISTA status=0x%08X\n", status); + any_err = 1; + ether->rx_err++; + } + + if (!any_err && ((status & MISTA_RXGD) == 0)) + dev_err(&pdev->dev, "emc rx MISTA status=0x%08X\n", status); + + spin_lock_irqsave(ðer->lock, flags); + writel(readl((ether->reg + REG_MIEN)) & ~ENRXGD, + (ether->reg + REG_MIEN)); + spin_unlock_irqrestore(ðer->lock, flags); + napi_schedule(ðer->napi); + + return IRQ_HANDLED; +} + +static int npcm7xx_poll(struct napi_struct *napi, int budget) +{ + struct npcm7xx_ether *ether = + container_of(napi, struct npcm7xx_ether, napi); + struct npcm7xx_rxbd *rxbd; + struct net_device *dev = ether->ndev; + struct platform_device *pdev = ether->pdev; + struct sk_buff *skb, *s; + unsigned int length; + __le32 status; + unsigned long flags; + int rx_cnt = 0; + int complete = 0; + unsigned int rx_offset = (readl((ether->reg + REG_CRXDSA)) - + ether->start_rx_ptr) / + sizeof(struct npcm7xx_txbd); + unsigned int local_count = (rx_offset >= ether->cur_rx) ? + rx_offset - ether->cur_rx : rx_offset + + RX_QUEUE_LEN - ether->cur_rx; + + if (local_count > ether->max_waiting_rx) + ether->max_waiting_rx = local_count; + + if (local_count > (4 * RX_POLL_SIZE)) + /* + * we are porbably in a storm of short packets and we don't + * want to get into RDU since short packets in RDU cause + * many RXOV which may cause EMC halt, so we filter out all + * coming packets + */ + writel(0, (ether->reg + REG_CAMCMR)); + + if (local_count <= budget) + /* we can restore accepting of packets */ + writel(ether->camcmr, (ether->reg + REG_CAMCMR)); + + spin_lock_irqsave(ðer->lock, flags); + npcm7xx_clean_tx(dev, false); + spin_unlock_irqrestore(ðer->lock, flags); + + rxbd = (ether->rdesc + ether->cur_rx); + + while (rx_cnt < budget) { + status = rxbd->sl; + if ((status & RX_OWN_DMA) == RX_OWN_DMA) { + complete = 1; + break; + } + /* for debug puposes we save the previous value */ + rxbd->reserved = status; + s = ether->rx_skb[ether->cur_rx]; + length = status & 0xFFFF; + + /* + * If VLAN is not supporte RXDS_PTLE (packet too long) is also + * an error + */ + if (likely((status & (RXDS_RXGD | RXDS_CRCE | RXDS_ALIE | + RXDS_RP | (IS_VLAN ? 0 : RXDS_PTLE))) == + RXDS_RXGD) && likely(length <= MAX_PACKET_SIZE)) { + dma_unmap_single(&dev->dev, (dma_addr_t)rxbd->buffer, + roundup(MAX_PACKET_SIZE_W_CRC, 4), + DMA_FROM_DEVICE); + + skb_put(s, length); + s->protocol = eth_type_trans(s, dev); + netif_receive_skb(s); + ether->stats.rx_packets++; + ether->stats.rx_bytes += length; + rx_cnt++; + ether->rx_count_pool++; + + /* now we allocate new skb instead if the used one. */ + skb = dev_alloc_skb(roundup(MAX_PACKET_SIZE_W_CRC, 4)); + if (!skb) { + dev_err(&pdev->dev, "get skb buffer error\n"); + ether->stats.rx_dropped++; + goto rx_out; + } + + /* Do not unmark the following skb_reserve() Receive + * Buffer Starting Address must be aligned + * to 4 bytes and the following line if unmarked + * will make it align to 2 and this likely + * will hult the RX and crash the linux + * skb_reserve(skb, NET_IP_ALIGN); + */ + skb->dev = dev; + + rxbd->buffer = dma_map_single(&dev->dev, skb->data, + roundup(MAX_PACKET_SIZE_W_CRC, 4), + DMA_FROM_DEVICE); + ether->rx_skb[ether->cur_rx] = skb; + } else { + ether->rx_err_count++; + ether->stats.rx_errors++; + dev_dbg(&pdev->dev, "rx_errors = %lu status = 0x%08X\n", + ether->stats.rx_errors, status); + + if (status & RXDS_RP) { + ether->stats.rx_length_errors++; + dev_dbg(&pdev->dev, "rx_length_errors = %lu\n", + ether->stats.rx_length_errors); + } else if (status & RXDS_CRCE) { + ether->stats.rx_crc_errors++; + dev_dbg(&pdev->dev, "rx_crc_errors = %lu\n", + ether->stats.rx_crc_errors); + } else if (status & RXDS_ALIE) { + ether->stats.rx_frame_errors++; + dev_dbg(&pdev->dev, "rx_frame_errors = %lu\n", + ether->stats.rx_frame_errors); + } else if (((!IS_VLAN) && (status & RXDS_PTLE)) || + length > MAX_PACKET_SIZE) { + ether->stats.rx_length_errors++; + dev_dbg(&pdev->dev, "rx_length_errors = %lu\n", + ether->stats.rx_length_errors); + } + } + + wmb(); + rxbd->sl = RX_OWN_DMA; + wmb(); + + if (++ether->cur_rx >= RX_QUEUE_LEN) + ether->cur_rx = 0; + + rxbd = (ether->rdesc + ether->cur_rx); + } + + if (complete) { + napi_complete(napi); + + if (ether->need_reset) { + dev_dbg(&pdev->dev, "Reset\n"); + npcm7xx_reset_mac(dev, 1); + } + + spin_lock_irqsave(ðer->lock, flags); + writel(readl((ether->reg + REG_MIEN)) | ENRXGD, (ether->reg + + REG_MIEN)); + spin_unlock_irqrestore(ðer->lock, flags); + } else { + rx_offset = (readl((ether->reg + REG_CRXDSA)) - + ether->start_rx_ptr) / sizeof(struct npcm7xx_txbd); + local_count = (rx_offset >= ether->cur_rx) ? rx_offset - + ether->cur_rx : rx_offset + RX_QUEUE_LEN - + ether->cur_rx; + + if (local_count > ether->max_waiting_rx) + ether->max_waiting_rx = local_count; + + if (local_count > (3 * RX_POLL_SIZE)) + /* + * we are porbably in a storm of short packets and + * we don't want to get into RDU since short packets in + * RDU cause many RXOV which may cause + * EMC halt, so we filter out all coming packets + */ + writel(0, (ether->reg + REG_CAMCMR)); + if (local_count <= RX_POLL_SIZE) + /* we can restore accepting of packets */ + writel(ether->camcmr, (ether->reg + REG_CAMCMR)); + } +rx_out: + + /* trigger RX */ + writel(ENSTART, (ether->reg + REG_RSDR)); + return rx_cnt; +} + +static int npcm7xx_ether_open(struct net_device *dev) +{ + struct npcm7xx_ether *ether; + struct platform_device *pdev; + + ether = netdev_priv(dev); + pdev = ether->pdev; + + if (ether->use_ncsi) { + ether->speed = 100; + ether->duplex = DUPLEX_FULL; + npcm7xx_opmode(dev, 100, DUPLEX_FULL); + } + npcm7xx_reset_mac(dev, 0); + + if (request_irq(ether->txirq, npcm7xx_tx_interrupt, 0x0, pdev->name, + dev)) { + dev_err(&pdev->dev, "register irq tx failed\n"); + npcm7xx_ether_close(dev); + return -EAGAIN; + } + + if (request_irq(ether->rxirq, npcm7xx_rx_interrupt, 0x0, pdev->name, + dev)) { + dev_err(&pdev->dev, "register irq rx failed\n"); + npcm7xx_ether_close(dev); + return -EAGAIN; + } + + if (ether->phy_dev) + phy_start(ether->phy_dev); + else if (ether->use_ncsi) + netif_carrier_on(dev); + + netif_start_queue(dev); + napi_enable(ðer->napi); + + /* trigger RX */ + writel(ENSTART, (ether->reg + REG_RSDR)); + + /* Start the NCSI device */ + if (ether->use_ncsi) { + int err = ncsi_start_dev(ether->ncsidev); + + if (err) { + npcm7xx_ether_close(dev); + return err; + } + } + + dev_info(&pdev->dev, "%s is OPENED\n", dev->name); + + return 0; +} + +static int npcm7xx_ether_ioctl(struct net_device *dev, + struct ifreq *ifr, int cmd) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + struct phy_device *phydev = ether->phy_dev; + + if (!netif_running(dev)) + return -EINVAL; + + if (!phydev) + return -ENODEV; + + return phy_mii_ioctl(phydev, ifr, cmd); +} + +static void npcm7xx_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); + strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); + strlcpy(info->bus_info, "N/A", sizeof(info->bus_info)); +} + +static int npcm7xx_get_settings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + struct phy_device *phydev = ether->phy_dev; + + if (!phydev) + return -ENODEV; + + dev_info(ðer->pdev->dev, "\n\nnpcm7xx_get_settings\n"); + phy_ethtool_ksettings_get(phydev, cmd); + + return 0; +} + +static int npcm7xx_set_settings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + struct phy_device *phydev = ether->phy_dev; + int ret; + + if (!phydev) + return -ENODEV; + + dev_info(ðer->pdev->dev, "\n\nnpcm7xx_set_settings\n"); + ret = phy_ethtool_ksettings_set(phydev, cmd); + + return ret; +} + +static u32 npcm7xx_get_msglevel(struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + + return ether->msg_enable; +} + +static void npcm7xx_set_msglevel(struct net_device *dev, u32 level) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + + ether->msg_enable = level; +} + +static const struct ethtool_ops npcm7xx_ether_ethtool_ops = { + .get_link_ksettings = npcm7xx_get_settings, + .set_link_ksettings = npcm7xx_set_settings, + .get_drvinfo = npcm7xx_get_drvinfo, + .get_msglevel = npcm7xx_get_msglevel, + .set_msglevel = npcm7xx_set_msglevel, + .get_link = ethtool_op_get_link, +}; + +static const struct net_device_ops npcm7xx_ether_netdev_ops = { + .ndo_open = npcm7xx_ether_open, + .ndo_stop = npcm7xx_ether_close, + .ndo_start_xmit = npcm7xx_ether_start_xmit, + .ndo_get_stats = npcm7xx_ether_stats, + .ndo_set_rx_mode = npcm7xx_ether_set_rx_mode, + .ndo_set_mac_address = npcm7xx_set_mac_address, + .ndo_do_ioctl = npcm7xx_ether_ioctl, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = eth_change_mtu, +}; + +static void get_mac_address(struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + struct platform_device *pdev = ether->pdev; + struct device_node *np = ether->pdev->dev.of_node; + const u8 *mac_address = NULL; + + mac_address = of_get_mac_address(np); + + if (mac_address != 0) + ether_addr_copy(dev->dev_addr, mac_address); + + if (is_valid_ether_addr(dev->dev_addr)) { + dev_info(&pdev->dev, "%s: device MAC address : %pM\n", + pdev->name, dev->dev_addr); + } else { + eth_hw_addr_random(dev); + dev_info(&pdev->dev, "%s: device MAC address (random generator) %pM\n", + dev->name, dev->dev_addr); + } +} + +static int npcm7xx_mii_setup(struct net_device *dev) +{ + struct npcm7xx_ether *ether = netdev_priv(dev); + struct platform_device *pdev; + struct phy_device *phydev = NULL; + int i, err = 0; + + pdev = ether->pdev; + + ether->mii_bus = mdiobus_alloc(); + if (!ether->mii_bus) { + err = -ENOMEM; + dev_err(&pdev->dev, "mdiobus_alloc() failed\n"); + goto out0; + } + + ether->mii_bus->name = "npcm7xx_rmii"; + ether->mii_bus->read = &npcm7xx_mdio_read; + ether->mii_bus->write = &npcm7xx_mdio_write; + ether->mii_bus->reset = &npcm7xx_mdio_reset; + snprintf(ether->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", + ether->pdev->name, ether->pdev->id); + dev_dbg(&pdev->dev, "%s ether->mii_bus->id=%s\n", __func__, + ether->mii_bus->id); + ether->mii_bus->priv = ether; + ether->mii_bus->parent = ðer->pdev->dev; + + for (i = 0; i < PHY_MAX_ADDR; i++) + ether->mii_bus->irq[i] = PHY_POLL; + + platform_set_drvdata(ether->pdev, ether->mii_bus); + + /* Enable MDIO Clock */ + writel(readl((ether->reg + REG_MCMDR)) | MCMDR_ENMDC, + (ether->reg + REG_MCMDR)); + + if (mdiobus_register(ether->mii_bus)) { + dev_err(&pdev->dev, "mdiobus_register() failed\n"); + goto out2; + } + + phydev = phy_find_first(ether->mii_bus); + if (!phydev) { + dev_err(&pdev->dev, "phy_find_first() failed\n"); + goto out3; + } + + dev_info(&pdev->dev, " name = %s ETH-Phy-Id = 0x%x\n", + phydev_name(phydev), phydev->phy_id); + + phydev = phy_connect(dev, phydev_name(phydev), + &adjust_link, + PHY_INTERFACE_MODE_RMII); + + dev_info(&pdev->dev, " ETH-Phy-Id = 0x%x name = %s\n", + phydev->phy_id, phydev->drv->name); + + if (IS_ERR(phydev)) { + err = PTR_ERR(phydev); + dev_err(&pdev->dev, "phy_connect() failed - %d\n", err); + goto out3; + } + + phydev->supported &= PHY_BASIC_FEATURES; + phydev->advertising = phydev->supported; + ether->phy_dev = phydev; + + return 0; + +out3: + mdiobus_unregister(ether->mii_bus); +out2: + kfree(ether->mii_bus->irq); + mdiobus_free(ether->mii_bus); +out0: + + return err; +} + +static const struct of_device_id emc_dt_id[] = { + { .compatible = "nuvoton,npcm750-emc", }, + {}, +}; +MODULE_DEVICE_TABLE(of, emc_dt_id); + +static void npcm7xx_ncsi_handler(struct ncsi_dev *nd) +{ + if (unlikely(nd->state != ncsi_dev_state_functional)) + return; + + netdev_info(nd->dev, "NCSI interface %s\n", + nd->link_up ? "up" : "down"); +} + +static int npcm7xx_ether_probe(struct platform_device *pdev) +{ + struct npcm7xx_ether *ether; + struct net_device *dev; + int error; + + struct clk *emc_clk = NULL; + struct device_node *np = pdev->dev.of_node; + + pdev->id = of_alias_get_id(np, "ethernet"); + if (pdev->id < 0) + pdev->id = 0; + + emc_clk = devm_clk_get(&pdev->dev, NULL); + + if (IS_ERR(emc_clk)) + return PTR_ERR(emc_clk); + + /* Enable Clock */ + clk_prepare_enable(emc_clk); + + error = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (error) + return -ENODEV; + + dev = alloc_etherdev(sizeof(struct npcm7xx_ether)); + if (!dev) + return -ENOMEM; + + ether = netdev_priv(dev); + + ether->rst_regmap = + syscon_regmap_lookup_by_compatible("nuvoton,npcm750-rst"); + if (IS_ERR(ether->rst_regmap)) { + dev_err(&pdev->dev, "%s: failed to find nuvoton,npcm750-rst\n", __func__); + return IS_ERR(ether->rst_regmap); + } + + /* Reset EMC module */ + if (pdev->id == 0) { + regmap_update_bits(ether->rst_regmap, IPSRST1_OFFSET, + (0x1 << 6), (0x1 << 6)); + regmap_update_bits(ether->rst_regmap, IPSRST1_OFFSET, + (0x1 << 6), 0); + } + if (pdev->id == 1) { + regmap_update_bits(ether->rst_regmap, IPSRST1_OFFSET, + (0x1 << 21), (0x1 << 21)); + regmap_update_bits(ether->rst_regmap, IPSRST1_OFFSET, + (0x1 << 21), 0); + } + + ether->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!ether->res) { + dev_err(&pdev->dev, "failed to get I/O memory\n"); + error = -ENXIO; + goto failed_free; + } + + if (!request_mem_region(ether->res->start, + resource_size(ether->res), pdev->name)) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + error = -EBUSY; + goto failed_free; + } + + ether->reg = ioremap(ether->res->start, resource_size(ether->res)); + dev_dbg(&pdev->dev, "%s ether->reg = 0x%x\n", __func__, + (unsigned int)ether->reg); + + if (!ether->reg) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + error = -ENXIO; + goto failed_free_mem; + } + + ether->txirq = platform_get_irq(pdev, 0); + if (ether->txirq < 0) { + dev_err(&pdev->dev, "failed to get ether tx irq\n"); + error = -ENXIO; + goto failed_free_io; + } + + ether->rxirq = platform_get_irq(pdev, 1); + if (ether->rxirq < 0) { + dev_err(&pdev->dev, "failed to get ether rx irq\n"); + error = -ENXIO; + goto failed_free_io; + } + + SET_NETDEV_DEV(dev, &pdev->dev); + platform_set_drvdata(pdev, dev); + ether->ndev = dev; + + ether->pdev = pdev; + ether->msg_enable = NETIF_MSG_LINK; + + dev->netdev_ops = &npcm7xx_ether_netdev_ops; + dev->ethtool_ops = &npcm7xx_ether_ethtool_ops; + + dev->tx_queue_len = TX_QUEUE_LEN; + dev->dma = 0x0; + dev->watchdog_timeo = TX_TIMEOUT; + + get_mac_address(dev); + + ether->cur_tx = 0x0; + ether->cur_rx = 0x0; + ether->finish_tx = 0x0; + ether->pending_tx = 0x0; + ether->link = 0; + ether->speed = 100; + ether->duplex = DUPLEX_FULL; + ether->need_reset = 0; + ether->dump_buf = NULL; + ether->rx_berr = 0; + ether->rx_err = 0; + ether->rdu = 0; + ether->rxov = 0; + ether->rx_stuck = 0; + /* debug counters */ + ether->max_waiting_rx = 0; + ether->rx_count_pool = 0; + ether->count_xmit = 0; + ether->rx_int_count = 0; + ether->rx_err_count = 0; + ether->tx_int_count = 0; + ether->count_finish = 0; + ether->tx_tdu = 0; + ether->tx_tdu_i = 0; + ether->tx_cp_i = 0; + + spin_lock_init(ðer->lock); + + netif_napi_add(dev, ðer->napi, npcm7xx_poll, RX_POLL_SIZE); + + if (pdev->dev.of_node && + of_get_property(pdev->dev.of_node, "use-ncsi", NULL)) { + if (!IS_ENABLED(CONFIG_NET_NCSI)) { + dev_err(&pdev->dev, "CONFIG_NET_NCSI not enabled\n"); + error = -ENODEV; + goto failed_free_napi; + } + dev_info(&pdev->dev, "Using NCSI interface\n"); + ether->use_ncsi = true; + ether->ncsidev = ncsi_register_dev(dev, npcm7xx_ncsi_handler); + if (!ether->ncsidev) { + error = -ENODEV; + goto failed_free_napi; + } + } else { + ether->use_ncsi = false; + error = npcm7xx_mii_setup(dev); + if (error < 0) { + dev_err(&pdev->dev, "npcm7xx_mii_setup err\n"); + goto failed_free_napi; + } + } + + error = register_netdev(dev); + if (error != 0) { + dev_err(&pdev->dev, "register_netdev() failed\n"); + error = -ENODEV; + goto failed_free_napi; + } + +#ifdef CONFIG_DEBUG_FS + npcm7xx_debug_fs(ether); +#endif + + return 0; + +failed_free_napi: + netif_napi_del(ðer->napi); + platform_set_drvdata(pdev, NULL); +failed_free_io: + iounmap(ether->reg); +failed_free_mem: + release_mem_region(ether->res->start, resource_size(ether->res)); +failed_free: + free_netdev(dev); + + return error; +} + +static int npcm7xx_ether_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct npcm7xx_ether *ether = netdev_priv(dev); + +#ifdef CONFIG_DEBUG_FS + debugfs_remove_recursive(ether->dbgfs_dir); +#endif + + unregister_netdev(dev); + + free_irq(ether->txirq, dev); + free_irq(ether->rxirq, dev); + + if (ether->phy_dev) + phy_disconnect(ether->phy_dev); + + mdiobus_unregister(ether->mii_bus); + kfree(ether->mii_bus->irq); + mdiobus_free(ether->mii_bus); + + platform_set_drvdata(pdev, NULL); + + free_netdev(dev); + return 0; +} + +static struct platform_driver npcm7xx_ether_driver = { + .probe = npcm7xx_ether_probe, + .remove = npcm7xx_ether_remove, + .driver = { + .name = DRV_MODULE_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(emc_dt_id), + }, +}; + +module_platform_driver(npcm7xx_ether_driver); + +MODULE_AUTHOR("Nuvoton Technology Corp."); +MODULE_DESCRIPTION("NPCM750 EMC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:npcm750-emc"); +MODULE_VERSION(DRV_MODULE_VERSION); From patchwork Mon Jan 14 13:07:07 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomer Maimon X-Patchwork-Id: 1024540 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 43dZ9N1tbjz9s3q for ; Tue, 15 Jan 2019 00:30:48 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 43dZ9N0nSzzDqWG for ; Tue, 15 Jan 2019 00:30:48 +1100 (AEDT) X-Original-To: openbmc@lists.ozlabs.org Delivered-To: openbmc@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (mailfrom) smtp.mailfrom=nuvoton.com (client-ip=212.199.177.27; helo=herzl.nuvoton.co.il; envelope-from=tomer.maimon@nuvoton.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from herzl.nuvoton.co.il (212.199.177.27.static.012.net.il [212.199.177.27]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 43dYfX5S4DzDqVW for ; Tue, 15 Jan 2019 00:07:32 +1100 (AEDT) Received: from taln60.nuvoton.co.il (ntil-fw [212.199.177.25]) by herzl.nuvoton.co.il (8.13.8/8.13.8) with ESMTP id x0ECbK0O019008; Mon, 14 Jan 2019 14:37:20 +0200 Received: by taln60.nuvoton.co.il (Postfix, from userid 10070) id 97BDE62DA4; Mon, 14 Jan 2019 15:07:18 +0200 (IST) From: Tomer Maimon To: joel@jms.id.au Subject: [linux dev-4.19 12/15] dt-binding: iio: add NPCM ADC documentation Date: Mon, 14 Jan 2019 15:07:07 +0200 Message-Id: <20190114130710.427600-13-tmaimon77@gmail.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20190114130710.427600-1-tmaimon77@gmail.com> References: <20190114130710.427600-1-tmaimon77@gmail.com> X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: openbmc@lists.ozlabs.org, Tomer Maimon Errors-To: openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "openbmc" Added device tree binding documentation for Nuvoton BMC NPCM Analog-to-Digital Converter(ADC). Signed-off-by: Tomer Maimon --- .../bindings/iio/adc/nuvoton,npcm-adc.txt | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt diff --git a/Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt b/Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt new file mode 100644 index 000000000000..6f0843d837cc --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt @@ -0,0 +1,35 @@ +Nuvoton NPCM Analog to Digital Converter (ADC) + +The NPCM ADC is a 10-bit converter for eight channel inputs. + +Required properties: +- compatible : "nuvoton,npcm750-adc" for the NPCM7XX BMC. +- reg : specifies physical base address and size of the registers. +- interrupts : Contain the ADC interrupt with flags for falling edge. + +Optional properties: +- clocks : phandle of ADC reference clock, in case the clock is not + added the ADC will use the default ADC sample rate. +- vref-supply : The regulator supply ADC reference voltage, in case the + vref-supply is not added the ADC will use internal voltage + reference. + +Required Node in the NPCM7xx BMC: +An additional register is present in the NPCM7xx SOC which is +assumed to be in the same device tree, with and marked as +compatible with "nuvoton,npcm750-rst". + +Example: + +adc: adc@f000c000 { + compatible = "nuvoton,npcm750-adc"; + reg = <0xf000c000 0x8>; + interrupts = ; + clocks = <&clk NPCM7XX_CLK_ADC>; +}; + +rst: rst@f0801000 { + compatible = "nuvoton,npcm750-rst", "syscon", + "simple-mfd"; + reg = <0xf0801000 0x6C>; +}; From patchwork Mon Jan 14 13:07:08 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomer Maimon X-Patchwork-Id: 1024543 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 43dZCV269tz9s9G for ; Tue, 15 Jan 2019 00:32:38 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 43dZCT6kCvzDqX6 for ; Tue, 15 Jan 2019 00:32:37 +1100 (AEDT) X-Original-To: openbmc@lists.ozlabs.org Delivered-To: openbmc@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (mailfrom) smtp.mailfrom=nuvoton.com (client-ip=212.199.177.27; helo=herzl.nuvoton.co.il; envelope-from=tomer.maimon@nuvoton.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from herzl.nuvoton.co.il (212.199.177.27.static.012.net.il [212.199.177.27]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 43dYfY6qlwzDqVV for ; Tue, 15 Jan 2019 00:07:33 +1100 (AEDT) Received: from taln60.nuvoton.co.il (ntil-fw [212.199.177.25]) by herzl.nuvoton.co.il (8.13.8/8.13.8) with ESMTP id x0ECbL85019011; Mon, 14 Jan 2019 14:37:21 +0200 Received: by taln60.nuvoton.co.il (Postfix, from userid 10070) id 1091362DA4; Mon, 14 Jan 2019 15:07:19 +0200 (IST) From: Tomer Maimon To: joel@jms.id.au Subject: [linux dev-4.19 13/15] iio: adc: add NPCM ADC driver Date: Mon, 14 Jan 2019 15:07:08 +0200 Message-Id: <20190114130710.427600-14-tmaimon77@gmail.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20190114130710.427600-1-tmaimon77@gmail.com> References: <20190114130710.427600-1-tmaimon77@gmail.com> X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: openbmc@lists.ozlabs.org, Tomer Maimon Errors-To: openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "openbmc" Add Nuvoton NPCM BMC Analog-to-Digital Converter(ADC) driver. The NPCM ADC is a 10-bit converter for eight channel inputs. Signed-off-by: Tomer Maimon --- drivers/iio/adc/Kconfig | 10 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/npcm_adc.c | 336 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 347 insertions(+) create mode 100644 drivers/iio/adc/npcm_adc.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 4a754921fb6f..c261e12bdc01 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -545,6 +545,16 @@ config NAU7802 To compile this driver as a module, choose M here: the module will be called nau7802. +config NPCM_ADC + tristate "Nuvoton NPCM ADC driver" + depends on ARCH_NPCM || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to build support for Nuvoton NPCM ADC. + + This driver can also be built as a module. If so, the module + will be called npcm_adc. + config PALMAS_GPADC tristate "TI Palmas General Purpose ADC" depends on MFD_PALMAS diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 03db7b578f9c..68c7aa898c62 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o obj-$(CONFIG_MESON_SARADC) += meson_saradc.o obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o obj-$(CONFIG_NAU7802) += nau7802.o +obj-$(CONFIG_NPCM_ADC) += npcm_adc.o obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c new file mode 100644 index 000000000000..4f7851472997 --- /dev/null +++ b/drivers/iio/adc/npcm_adc.c @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2016-2018 Nuvoton Technology corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct npcm_adc { + u32 vref_mv; + bool int_status; + u32 adc_sample_hz; + struct device *dev; + void __iomem *regs; + struct clk *adc_clk; + wait_queue_head_t wq; + struct regulator *vref; + struct regmap *rst_regmap; +}; + +/* NPCM7xx reset module */ +#define IPSRST1_OFFSET 0x020 +#define IPSRST1_ADC_RST BIT(27) + +/* ADC registers */ +#define NPCM_ADCCON 0x00 +#define NPCM_ADCDATA 0x04 + +/* ADCCON Register Bits */ +#define NPCM_ADCCON_ADC_INT_EN BIT(21) +#define NPCM_ADCCON_REFSEL BIT(19) +#define NPCM_ADCCON_ADC_INT_ST BIT(18) +#define NPCM_ADCCON_ADC_EN BIT(17) +#define NPCM_ADCCON_ADC_RST BIT(16) +#define NPCM_ADCCON_ADC_CONV BIT(13) + +#define NPCM_ADCCON_CH_MASK GENMASK(27, 24) +#define NPCM_ADCCON_CH(x) ((x) << 24) +#define NPCM_ADCCON_DIV_SHIFT 1 +#define NPCM_ADCCON_DIV_MASK GENMASK(8, 1) +#define NPCM_ADC_DATA_MASK(x) ((x) & GENMASK(9, 0)) + +#define NPCM_ADC_ENABLE (NPCM_ADCCON_ADC_EN | NPCM_ADCCON_ADC_INT_EN) + +/* ADC General Definition */ +#define NPCM_RESOLUTION_BITS 10 +#define ADC_DEFAULT_SAMPLE_RATE 12500000 +#define INT_VREF_MV 2000 + +#define NPCM_ADC_CHAN(ch) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = ch, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec npcm_adc_iio_channels[] = { + NPCM_ADC_CHAN(0), + NPCM_ADC_CHAN(1), + NPCM_ADC_CHAN(2), + NPCM_ADC_CHAN(3), + NPCM_ADC_CHAN(4), + NPCM_ADC_CHAN(5), + NPCM_ADC_CHAN(6), + NPCM_ADC_CHAN(7), +}; + +static irqreturn_t npcm_adc_isr(int irq, void *data) +{ + u32 regtemp = 0; + struct iio_dev *indio_dev = data; + struct npcm_adc *info = iio_priv(indio_dev); + + regtemp = ioread32(info->regs + NPCM_ADCCON); + if (regtemp & NPCM_ADCCON_ADC_INT_ST) { + iowrite32(regtemp, info->regs + NPCM_ADCCON); + wake_up_interruptible(&info->wq); + info->int_status = true; + } + + return IRQ_HANDLED; +} + +static int npcm_adc_read(struct npcm_adc *info, int *val, u8 channel) +{ + int ret; + u32 regtemp = 0; + + /* Select ADC channal */ + regtemp = ioread32(info->regs + NPCM_ADCCON); + regtemp &= ~NPCM_ADCCON_CH_MASK; + iowrite32(regtemp | NPCM_ADCCON_CH(channel) | + NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON); + + info->int_status = false; + ret = wait_event_interruptible_timeout(info->wq, info->int_status, + msecs_to_jiffies(10)); + if (ret == 0) { + regtemp = ioread32(info->regs + NPCM_ADCCON); + if ((regtemp & NPCM_ADCCON_ADC_CONV) && info->rst_regmap) { + /* if conversion failed - reset ADC module */ + regmap_write(info->rst_regmap, IPSRST1_OFFSET, + IPSRST1_ADC_RST); + msleep(100); + regmap_write(info->rst_regmap, IPSRST1_OFFSET, 0x0); + msleep(100); + + /* Enable ADC and start conversion Module */ + iowrite32(NPCM_ADC_ENABLE | NPCM_ADCCON_ADC_CONV, + info->regs + NPCM_ADCCON); + dev_err(info->dev, "RESET ADC Complete\n"); + } + return -ETIMEDOUT; + } + if (ret < 0) + return ret; + + *val = NPCM_ADC_DATA_MASK(ioread32(info->regs + NPCM_ADCDATA)); + + return 0; +} + +static int npcm_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int ret; + struct npcm_adc *info = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&indio_dev->mlock); + ret = npcm_adc_read(info, val, chan->channel); + if (ret) { + dev_err(info->dev, "NPCM ADC read failed\n"); + mutex_unlock(&indio_dev->mlock); + return ret; + } + mutex_unlock(&indio_dev->mlock); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = info->vref_mv; + *val2 = NPCM_RESOLUTION_BITS; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = info->adc_sample_hz; + return IIO_VAL_INT; + default: + return -EINVAL; + } + + return 0; +} + +static const struct iio_info npcm_adc_iio_info = { + .read_raw = &npcm_adc_read_raw, +}; + +static const struct of_device_id npcm_adc_match[] = { + { .compatible = "nuvoton,npcm750-adc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, npcm_adc_match); + +static int npcm_adc_probe(struct platform_device *pdev) +{ + int ret; + int irq; + u32 div; + u32 reg_con; + struct resource *res; + struct npcm_adc *info; + struct iio_dev *indio_dev; + struct device *dev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + info = iio_priv(indio_dev); + + info->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + info->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(info->regs)) + return PTR_ERR(info->regs); + + info->adc_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(info->adc_clk)) { + dev_warn(&pdev->dev, "ADC clock failed: can't read clk, Assuming ADC clock Rate 12.5MHz\n"); + info->adc_sample_hz = ADC_DEFAULT_SAMPLE_RATE; + } else { + /* calculate ADC clock sample rate */ + reg_con = ioread32(info->regs + NPCM_ADCCON); + div = reg_con & NPCM_ADCCON_DIV_MASK; + div = div >> NPCM_ADCCON_DIV_SHIFT; + info->adc_sample_hz = clk_get_rate(info->adc_clk) / + ((div + 1) * 2); + } + + if (of_device_is_compatible(np, "nuvoton,npcm750-adc")) { + info->rst_regmap = syscon_regmap_lookup_by_compatible + ("nuvoton,npcm750-rst"); + if (IS_ERR(info->rst_regmap)) { + dev_err(&pdev->dev, "Failed to find nuvoton,npcm750-rst\n"); + ret = PTR_ERR(info->rst_regmap); + goto err_disable_clk; + } + } + + reg_con = ioread32(info->regs + NPCM_ADCCON); + info->vref = devm_regulator_get_optional(&pdev->dev, "vref"); + if (!IS_ERR(info->vref)) { + ret = regulator_enable(info->vref); + if (ret) { + dev_err(&pdev->dev, "Can't enable ADC reference voltage\n"); + goto err_disable_clk; + } + + ret = regulator_get_voltage(info->vref); + if (ret < 0) + goto err_disable_vref_reg; + + info->vref_mv = ret / 1000; + iowrite32(reg_con & ~NPCM_ADCCON_REFSEL, + info->regs + NPCM_ADCCON); + } else { + /* Any other error indicates that the regulator does exist */ + if (PTR_ERR(info->vref) != -ENODEV) { + goto err_disable_clk; + return PTR_ERR(info->vref); + } + + /* Use internal reference */ + info->vref_mv = INT_VREF_MV; + iowrite32(reg_con | NPCM_ADCCON_REFSEL, + info->regs + NPCM_ADCCON); + } + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(dev, "failed getting interrupt resource\n"); + ret = -EINVAL; + goto err_disable_vref_reg; + } + + ret = devm_request_irq(&pdev->dev, irq, npcm_adc_isr, 0, + "NPCM_ADC", indio_dev); + if (ret < 0) { + dev_err(dev, "failed requesting interrupt\n"); + goto err_disable_vref_reg; + } + + init_waitqueue_head(&info->wq); + + platform_set_drvdata(pdev, indio_dev); + indio_dev->name = dev_name(&pdev->dev); + indio_dev->dev.parent = &pdev->dev; + indio_dev->info = &npcm_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = npcm_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(npcm_adc_iio_channels); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "Couldn't register the device.\n"); + goto err_disable_vref_reg; + } + + reg_con = ioread32(info->regs + NPCM_ADCCON); + reg_con |= NPCM_ADC_ENABLE; + + /* Enable the ADC Module */ + iowrite32(reg_con, info->regs + NPCM_ADCCON); + + /* Start ADC conversion */ + iowrite32(reg_con | NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON); + + pr_info("NPCM ADC driver probed, sample Rate %dHz\n", + info->adc_sample_hz); + + return 0; + +err_disable_vref_reg: + if (!IS_ERR(info->vref)) + regulator_disable(info->vref); +err_disable_clk: + clk_disable_unprepare(info->adc_clk); + + return ret; +} + +static int npcm_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct npcm_adc *info = iio_priv(indio_dev); + u32 regtemp = 0; + + regtemp = ioread32(info->regs + NPCM_ADCCON); + + /* Disable the ADC Module */ + iowrite32(regtemp & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON); + + iio_device_unregister(indio_dev); + clk_disable_unprepare(info->adc_clk); + if (!IS_ERR(info->vref)) + regulator_disable(info->vref); + + return 0; +} + +static struct platform_driver npcm_adc_driver = { + .probe = npcm_adc_probe, + .remove = npcm_adc_remove, + .driver = { + .name = "npcm_adc", + .of_match_table = npcm_adc_match, + }, +}; + +module_platform_driver(npcm_adc_driver); + +MODULE_DESCRIPTION("Nuvoton NPCM ADC Sensor Driver"); +MODULE_AUTHOR("Tomer Maimon "); +MODULE_LICENSE("GPL v2"); From patchwork Mon Jan 14 13:07:09 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomer Maimon X-Patchwork-Id: 1024541 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 43dZ9s32ncz9s3q for ; Tue, 15 Jan 2019 00:31:13 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 43dZ9s22GxzDq8v for ; Tue, 15 Jan 2019 00:31:13 +1100 (AEDT) X-Original-To: openbmc@lists.ozlabs.org Delivered-To: openbmc@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (mailfrom) smtp.mailfrom=nuvoton.com (client-ip=212.199.177.27; helo=herzl.nuvoton.co.il; envelope-from=tomer.maimon@nuvoton.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from herzl.nuvoton.co.il (212.199.177.27.static.012.net.il [212.199.177.27]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 43dYfY2w2fzDqVy for ; Tue, 15 Jan 2019 00:07:32 +1100 (AEDT) Received: from taln60.nuvoton.co.il (ntil-fw [212.199.177.25]) by herzl.nuvoton.co.il (8.13.8/8.13.8) with ESMTP id x0ECbLOZ019014; Mon, 14 Jan 2019 14:37:21 +0200 Received: by taln60.nuvoton.co.il (Postfix, from userid 10070) id 7735562DA4; Mon, 14 Jan 2019 15:07:19 +0200 (IST) From: Tomer Maimon To: joel@jms.id.au Subject: [linux dev-4.19 14/15] arm: npcm: add defconfig for Nuvoton NPCM7xx BMC Date: Mon, 14 Jan 2019 15:07:09 +0200 Message-Id: <20190114130710.427600-15-tmaimon77@gmail.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20190114130710.427600-1-tmaimon77@gmail.com> References: <20190114130710.427600-1-tmaimon77@gmail.com> X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: openbmc@lists.ozlabs.org, Tomer Maimon Errors-To: openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "openbmc" Add configuration definition for Nuvoton NPCM7xx (Poleg) BMC. Signed-off-by: Tomer Maimon --- arch/arm/configs/npcm7xx_defconfig | 122 +++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 arch/arm/configs/npcm7xx_defconfig diff --git a/arch/arm/configs/npcm7xx_defconfig b/arch/arm/configs/npcm7xx_defconfig new file mode 100644 index 000000000000..afddb4e88300 --- /dev/null +++ b/arch/arm/configs/npcm7xx_defconfig @@ -0,0 +1,122 @@ +CONFIG_KERNEL_XZ=y +CONFIG_SYSVIPC=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_LOG_BUF_SHIFT=21 +CONFIG_CGROUPS=y +CONFIG_SYSFS_DEPRECATED=y +CONFIG_SYSFS_DEPRECATED_V2=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_EMBEDDED=y +CONFIG_SLAB=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_DEFAULT_DEADLINE=y +CONFIG_ARCH_NPCM=y +CONFIG_ARCH_NPCM7XX=y +CONFIG_SMP=y +CONFIG_VMSPLIT_3G_OPT=y +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_BINFMT_MISC=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_MTD=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_RAM=y +CONFIG_MTD_COMPLEX_MAPPINGS=y +CONFIG_MTD_M25P80=y +CONFIG_MTD_SPI_NOR=y +CONFIG_SPI_NPCM_FIU=y +CONFIG_OF_OVERLAY=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=1 +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_NPCM7XX_LPC_BPC=y +CONFIG_NPCM7XX_PCI_MBOX=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_NETDEVICES=y +CONFIG_NPCM7XX_EMC_ETH=y +CONFIG_STMMAC_ETH=y +CONFIG_BROADCOM_PHY=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_NPCM750_OTP=y +CONFIG_NPCM750_OTP_WRITE_ENABLE=y +CONFIG_NPCM7XX_KCS_IPMI_BMC=y +CONFIG_HW_RANDOM=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_NPCM7XX=y +CONFIG_I2C_SLAVE_EEPROM=y +CONFIG_SPI=y +CONFIG_SPI_NPCM_PSPI=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_SENSORS_LM75=y +CONFIG_SENSORS_NPCM7XX=y +CONFIG_SENSORS_TMP102=y +CONFIG_WATCHDOG=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_NPCM750_VCD=y +CONFIG_NPCM750_ECE=y +CONFIG_USB_HIDDEV=y +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_STORAGE=y +CONFIG_USB_CHIPIDEA=y +CONFIG_USB_CHIPIDEA_UDC=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_NPCMX50_USB2=y +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_F_HID=y +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_EDM_KBD_MOUSE=m +CONFIG_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_NPCM750=y +CONFIG_IIO=y +CONFIG_NPCM_ADC=y +CONFIG_IIO_MUX=y +CONFIG_MUX_MMIO=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_ROMFS_FS=y +CONFIG_NFS_FS=y +CONFIG_ROOT_NFS=y +CONFIG_CIFS=y +CONFIG_CIFS_XATTR=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_INFO_REDUCED=y +CONFIG_READABLE_ASM=y +CONFIG_DEBUG_SECTION_MISMATCH=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_FUNCTION_TRACER=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_USER_API_SKCIPHER=y +CONFIG_CRYPTO_DEV_NPCMX50=y +CONFIG_ARM_CRYPTO=y From patchwork Mon Jan 14 13:07:10 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomer Maimon X-Patchwork-Id: 1024549 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 43dZHF3935z9s3q for ; Tue, 15 Jan 2019 00:35:53 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 43dZHF1pjczDqNT for ; Tue, 15 Jan 2019 00:35:53 +1100 (AEDT) X-Original-To: openbmc@lists.ozlabs.org Delivered-To: openbmc@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (mailfrom) smtp.mailfrom=nuvoton.com (client-ip=212.199.177.27; helo=herzl.nuvoton.co.il; envelope-from=tomer.maimon@nuvoton.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Received: from herzl.nuvoton.co.il (212.199.177.27.static.012.net.il [212.199.177.27]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 43dYfh2GhKzDqVL for ; Tue, 15 Jan 2019 00:07:39 +1100 (AEDT) Received: from taln60.nuvoton.co.il (ntil-fw [212.199.177.25]) by herzl.nuvoton.co.il (8.13.8/8.13.8) with ESMTP id x0ECbMNh019017; Mon, 14 Jan 2019 14:37:22 +0200 Received: by taln60.nuvoton.co.il (Postfix, from userid 10070) id DBC8262DA4; Mon, 14 Jan 2019 15:07:19 +0200 (IST) From: Tomer Maimon To: joel@jms.id.au Subject: [linux dev-4.19 15/15] dts: npcm7xx: Modify NPCM7xx device tree Date: Mon, 14 Jan 2019 15:07:10 +0200 Message-Id: <20190114130710.427600-16-tmaimon77@gmail.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20190114130710.427600-1-tmaimon77@gmail.com> References: <20190114130710.427600-1-tmaimon77@gmail.com> X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: openbmc@lists.ozlabs.org, Tomer Maimon Errors-To: openbmc-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "openbmc" Modify NPCM7xx device tree FIU, ADC, RST, VCD and SPI nodes Add regulator and HGPIO pins nodes. Signed-off-by: Tomer Maimon --- arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi | 199 +++++++++++++++----------- arch/arm/boot/dts/nuvoton-npcm750-evb.dts | 165 +++++++++++++++------ arch/arm/boot/dts/nuvoton-npcm750-gpio.dtsi | 18 +-- arch/arm/boot/dts/nuvoton-npcm750.dtsi | 10 +- 4 files changed, 249 insertions(+), 143 deletions(-) diff --git a/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi b/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi index afe0d3cb516d..a24a06170660 100644 --- a/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi +++ b/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi @@ -5,6 +5,7 @@ #include "skeleton.dtsi" #include #include +#include / { #address-cells = <1>; @@ -74,7 +75,7 @@ rst: rst@f0801000 { compatible = "nuvoton,npcm750-rst", "syscon", "simple-mfd"; - reg = <0x801000 0x1000>; + reg = <0x801000 0x6C>; }; scu: scu@3fe000 { @@ -128,7 +129,7 @@ clock-names = "stmmaceth", "clk_gmac"; pinctrl-names = "default"; pinctrl-0 = <&rg1_pins - &rg1mdio_pins>; + &rg1mdio_pins>; status = "disabled"; }; @@ -142,8 +143,9 @@ clock-names = "clk_emc"; pinctrl-names = "default"; pinctrl-0 = <&r1_pins - &r1err_pins - &r1md_pins>; + &r1err_pins + &r1md_pins>; + status = "disabled"; }; ehci1:usb@f0806000 { @@ -213,52 +215,38 @@ interrupts = ; }; - spi0: spi@fb000000 { - compatible = "nuvoton,npcm750-spi"; + fiu0: fiu@fb000000 { + compatible = "nuvoton,npcm750-fiu"; #address-cells = <1>; #size-cells = <0>; reg = <0xfb000000 0x1000>, <0x80000000 0x10000000>; reg-names = "control", "memory"; - chip-max-address-map = <0x8000000>; clocks = <&clk NPCM7XX_CLK_AHB>; clock-names = "clk_ahb"; - spi-nor@0 { - compatible = "jedec,spi-nor"; - #address-cells = <1>; - #size-cells = <1>; - reg = <0>; - }; + status = "disabled"; }; - spi3: spi@c0000000 { - compatible = "nuvoton,npcm750-spi"; + + fiu3: fiu@c0000000 { + compatible = "nuvoton,npcm750-fiu"; #address-cells = <1>; #size-cells = <0>; reg = <0xc0000000 0x1000>, <0xA0000000 0x20000000>; reg-names = "control", "memory"; - chip-max-address-map = <0x8000000>; clocks = <&clk NPCM7XX_CLK_AHB>; clock-names = "clk_ahb"; pinctrl-names = "default"; - pinctrl-0 = <&spi3_pins &spi3quad_pins>; - spi-nor@0 { - compatible = "jedec,spi-nor"; - #address-cells = <1>; - #size-cells = <1>; - reg = <0>; - }; + pinctrl-0 = <&spi3_pins>; + status = "disabled"; }; - pci_rc: axi-pcie@e1000000 { - #address-cells = <3>; - #size-cells = <2>; - #interrupt-cells = <1>; - compatible = "nuvoton,npcm750-pcirc"; - reg = <0xe1000000 0x1000>; - device_type = "pci"; - interrupts = ; - bus-range = <0x00 0xff>; - ranges = <0x02000000 0 0xea000000 - 0xea000000 0 0x02000000>; + fiux: fiu@fb001000 { + compatible = "nuvoton,npcm750-fiu"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xfb001000 0x1000>, <0xf8000000 0x2000000>; + reg-names = "control", "memory"; + clocks = <&clk NPCM7XX_CLK_AHB>; + clock-names = "clk_ahb"; status = "disabled"; }; @@ -271,17 +259,18 @@ vcd: vcd@f0810000 { compatible = "nuvoton,npcm750-vcd"; reg = <0xf0810000 0x10000>; - phy-memory = <0x3e200000 0x600000>; - de-mode = /bits/ 8 <1>; - interrupts = <0 22 4>; + mem-addr = <0x3e200000>; + mem-size = <0x600000>; + interrupts = ; status = "disabled"; }; ece: ece@f0820000 { compatible = "nuvoton,npcm750-ece"; reg = <0xf0820000 0x2000>; - phy-memory = <0x3e800000 0x600000>; - interrupts = <0 24 4>; + mem-addr = <0x3e800000>; + mem-size = <0x600000>; + interrupts = ; status = "disabled"; }; @@ -352,13 +341,30 @@ }; }; - pspi: pspi@0 { + spi0: spi@200000 { compatible = "nuvoton,npcm750-pspi"; - reg = <0x200000 0x2000>; - interrupts = , - ; + reg = <0x200000 0x1000>; + pinctrl-names = "default"; + pinctrl-0 = <&pspi1_pins>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = ; clocks = <&clk NPCM7XX_CLK_APB5>; clock-names = "clk_apb5"; + status = "disabled"; + }; + + spi1: spi@201000 { + compatible = "nuvoton,npcm750-pspi"; + reg = <0x201000 0x1000>; + pinctrl-names = "default"; + pinctrl-0 = <&pspi2_pins>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = ; + clocks = <&clk NPCM7XX_CLK_APB5>; + clock-names = "clk_apb5"; + status = "disabled"; }; timer0: timer@8000 { @@ -438,10 +444,10 @@ adc: adc@c000 { compatible = "nuvoton,npcm750-adc"; - reg = <0xc000 0x1000>; + reg = <0xc000 0x8>; + interrupts = ; clocks = <&clk NPCM7XX_CLK_ADC>; - clock-names = "clk_adc"; - vref = <2048>; + status = "disabled"; }; otp:otp@189000 { @@ -453,7 +459,7 @@ clock-names = "clk_apb4"; }; - pwm_fan:pwm-fan-controller@103000 { + pwm_fan:pwm-fan-controller@103000 { #address-cells = <1>; #size-cells = <0>; compatible = "nuvoton,npcm750-pwm-fan"; @@ -487,9 +493,9 @@ status = "disabled"; }; - i2c0: i2c-bus@80000 { + i2c0: i2c@80000 { reg = <0x80000 0x1000>; - compatible = "nuvoton,npcm750-i2c-bus"; + compatible = "nuvoton,npcm750-i2c"; clocks = <&clk NPCM7XX_CLK_APB2>; bus-frequency = <100000>; interrupts = ; @@ -498,9 +504,9 @@ status = "disabled"; }; - i2c1: i2c-bus@81000 { + i2c1: i2c@81000 { reg = <0x81000 0x1000>; - compatible = "nuvoton,npcm750-i2c-bus"; + compatible = "nuvoton,npcm750-i2c"; clocks = <&clk NPCM7XX_CLK_APB2>; bus-frequency = <100000>; interrupts = ; @@ -509,9 +515,9 @@ status = "disabled"; }; - i2c2: i2c-bus@82000 { + i2c2: i2c@82000 { reg = <0x82000 0x1000>; - compatible = "nuvoton,npcm750-i2c-bus"; + compatible = "nuvoton,npcm750-i2c"; clocks = <&clk NPCM7XX_CLK_APB2>; bus-frequency = <100000>; interrupts = ; @@ -520,9 +526,9 @@ status = "disabled"; }; - i2c3: i2c-bus@83000 { + i2c3: i2c@83000 { reg = <0x83000 0x1000>; - compatible = "nuvoton,npcm750-i2c-bus"; + compatible = "nuvoton,npcm750-i2c"; clocks = <&clk NPCM7XX_CLK_APB2>; bus-frequency = <100000>; interrupts = ; @@ -531,9 +537,9 @@ status = "disabled"; }; - i2c4: i2c-bus@84000 { + i2c4: i2c@84000 { reg = <0x84000 0x1000>; - compatible = "nuvoton,npcm750-i2c-bus"; + compatible = "nuvoton,npcm750-i2c"; clocks = <&clk NPCM7XX_CLK_APB2>; bus-frequency = <100000>; interrupts = ; @@ -542,9 +548,9 @@ status = "disabled"; }; - i2c5: i2c-bus@85000 { + i2c5: i2c@85000 { reg = <0x85000 0x1000>; - compatible = "nuvoton,npcm750-i2c-bus"; + compatible = "nuvoton,npcm750-i2c"; clocks = <&clk NPCM7XX_CLK_APB2>; bus-frequency = <100000>; interrupts = ; @@ -553,9 +559,9 @@ status = "disabled"; }; - i2c6: i2c-bus@86000 { + i2c6: i2c@86000 { reg = <0x86000 0x1000>; - compatible = "nuvoton,npcm750-i2c-bus"; + compatible = "nuvoton,npcm750-i2c"; clocks = <&clk NPCM7XX_CLK_APB2>; bus-frequency = <100000>; interrupts = ; @@ -564,9 +570,9 @@ status = "disabled"; }; - i2c7: i2c-bus@87000 { + i2c7: i2c@87000 { reg = <0x87000 0x1000>; - compatible = "nuvoton,npcm750-i2c-bus"; + compatible = "nuvoton,npcm750-i2c"; clocks = <&clk NPCM7XX_CLK_APB2>; bus-frequency = <100000>; interrupts = ; @@ -575,9 +581,9 @@ status = "disabled"; }; - i2c8: i2c-bus@88000 { + i2c8: i2c@88000 { reg = <0x88000 0x1000>; - compatible = "nuvoton,npcm750-i2c-bus"; + compatible = "nuvoton,npcm750-i2c"; clocks = <&clk NPCM7XX_CLK_APB2>; bus-frequency = <100000>; interrupts = ; @@ -586,9 +592,9 @@ status = "disabled"; }; - i2c9: i2c-bus@89000 { + i2c9: i2c@89000 { reg = <0x89000 0x1000>; - compatible = "nuvoton,npcm750-i2c-bus"; + compatible = "nuvoton,npcm750-i2c"; clocks = <&clk NPCM7XX_CLK_APB2>; bus-frequency = <100000>; interrupts = ; @@ -597,9 +603,9 @@ status = "disabled"; }; - i2c10: i2c-bus@8a000 { + i2c10: i2c@8a000 { reg = <0x8a000 0x1000>; - compatible = "nuvoton,npcm750-i2c-bus"; + compatible = "nuvoton,npcm750-i2c"; clocks = <&clk NPCM7XX_CLK_APB2>; bus-frequency = <100000>; interrupts = ; @@ -608,9 +614,9 @@ status = "disabled"; }; - i2c11: i2c-bus@8b000 { + i2c11: i2c@8b000 { reg = <0x8b000 0x1000>; - compatible = "nuvoton,npcm750-i2c-bus"; + compatible = "nuvoton,npcm750-i2c"; clocks = <&clk NPCM7XX_CLK_APB2>; bus-frequency = <100000>; interrupts = ; @@ -619,9 +625,9 @@ status = "disabled"; }; - i2c12: i2c-bus@8c000 { + i2c12: i2c@8c000 { reg = <0x8c000 0x1000>; - compatible = "nuvoton,npcm750-i2c-bus"; + compatible = "nuvoton,npcm750-i2c"; clocks = <&clk NPCM7XX_CLK_APB2>; bus-frequency = <100000>; interrupts = ; @@ -630,9 +636,9 @@ status = "disabled"; }; - i2c13: i2c-bus@8d000 { + i2c13: i2c@8d000 { reg = <0x8d000 0x1000>; - compatible = "nuvoton,npcm750-i2c-bus"; + compatible = "nuvoton,npcm750-i2c"; clocks = <&clk NPCM7XX_CLK_APB2>; bus-frequency = <100000>; interrupts = ; @@ -641,9 +647,9 @@ status = "disabled"; }; - i2c14: i2c-bus@8e000 { + i2c14: i2c@8e000 { reg = <0x8e000 0x1000>; - compatible = "nuvoton,npcm750-i2c-bus"; + compatible = "nuvoton,npcm750-i2c"; clocks = <&clk NPCM7XX_CLK_APB2>; bus-frequency = <100000>; interrupts = ; @@ -652,9 +658,9 @@ status = "disabled"; }; - i2c15: i2c-bus@8f000 { + i2c15: i2c@8f000 { reg = <0x8f000 0x1000>; - compatible = "nuvoton,npcm750-i2c-bus"; + compatible = "nuvoton,npcm750-i2c"; clocks = <&clk NPCM7XX_CLK_APB2>; bus-frequency = <100000>; interrupts = ; @@ -664,7 +670,8 @@ }; gfxi: gfxi@f000e000 { - compatible = "nuvoton,npcm750-gfxi", "syscon", "simple-mfd"; + compatible = "nuvoton,npcm750-gfxi", "syscon", + "simple-mfd"; reg = <0xf000e000 0x100>; }; @@ -1202,5 +1209,37 @@ groups = "clkreq"; function = "clkreq"; }; + hgpio0_pins: hgpio0-pins { + groups = "hgpio0"; + function = "hgpio0"; + }; + hgpio1_pins: hgpio1-pins { + groups = "hgpio1"; + function = "hgpio1"; + }; + hgpio2_pins: hgpio2-pins { + groups = "hgpio2"; + function = "hgpio2"; + }; + hgpio3_pins: hgpio3-pins { + groups = "hgpio3"; + function = "hgpio3"; + }; + hgpio4_pins: hgpio4-pins { + groups = "hgpio4"; + function = "hgpio4"; + }; + hgpio5_pins: hgpio5-pins { + groups = "hgpio5"; + function = "hgpio5"; + }; + hgpio6_pins: hgpio6-pins { + groups = "hgpio6"; + function = "hgpio6"; + }; + hgpio7_pins: hgpio7-pins { + groups = "hgpio7"; + function = "hgpio7"; + }; }; }; diff --git a/arch/arm/boot/dts/nuvoton-npcm750-evb.dts b/arch/arm/boot/dts/nuvoton-npcm750-evb.dts index 8c568b380706..d951ac997872 100644 --- a/arch/arm/boot/dts/nuvoton-npcm750-evb.dts +++ b/arch/arm/boot/dts/nuvoton-npcm750-evb.dts @@ -46,6 +46,11 @@ i2c13 = &i2c13; i2c14 = &i2c14; i2c15 = &i2c15; + spi0 = &spi0; + spi1 = &spi1; + fiu0 = &fiu0; + fiu1 = &fiu3; + fiu2 = &fiux; }; chosen { @@ -56,8 +61,29 @@ reg = <0 0x40000000>; }; + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + reg_vref1_2: regulator@0 { + compatible = "regulator-fixed"; + reg = <0>; + regulator-name = "vref_1_2v"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + reg_vref3_3: regulator@1 { + compatible = "regulator-fixed"; + reg = <0>; + regulator-name = "vref_3_3v"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + }; + ahb { - gmac0: eth@f0802000 { + gmac0: eth@f0802000 { phy-mode = "rgmii-id"; status = "okay"; }; @@ -135,8 +161,14 @@ status = "okay"; }; - spi0: spi@fb000000 { + fiu0: fiu@fb000000 { + status = "okay"; spi-nor@0 { + compatible = "jedec,spi-nor"; + #address-cells = <1>; + #size-cells = <1>; + spi-rx-bus-width = <2>; + reg = <0>; partitions@80000000 { compatible = "fixed-partitions"; #address-cells = <1>; @@ -188,24 +220,31 @@ }; }; - spi3: spi@c0000000 { - spi-nor@0 { + fiu3: fiu@c0000000 { + pinctrl-0 = <&spi3_pins>, <&spi3quad_pins>; + status = "okay"; + spi-nor@0 { + compatible = "jedec,spi-nor"; + #address-cells = <1>; + #size-cells = <1>; + spi-rx-bus-width = <2>; + reg = <0>; partitions@A0000000 { compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; system1@0 { label = "spi3-system1"; - reg = <0x0 0x800000>; - }; - system2@800000 { - label = "spi3-system2"; - reg = <0x800000 0x0>; + reg = <0x0 0x0>; }; }; }; }; + fiux: fiu@fb001000 { + spix-mode; + }; + sdhci0: sdhci@f0842000 { status = "okay"; }; @@ -252,6 +291,12 @@ status = "okay"; }; + adc: adc@c000 { + /* enable external vref */ + /*vref-supply = <®_vref1_2>;*/ + status = "okay"; + }; + otp:otp@189000 { status = "okay"; }; @@ -278,7 +323,7 @@ }; /* lm75 on SVB */ - i2c0: i2c-bus@80000 { + i2c0: i2c@80000 { #address-cells = <1>; #size-cells = <0>; bus-frequency = <100000>; @@ -292,7 +337,7 @@ }; /* lm75 on EB */ - i2c1: i2c-bus@81000 { + i2c1: i2c@81000 { #address-cells = <1>; #size-cells = <0>; bus-frequency = <100000>; @@ -306,7 +351,7 @@ }; /* tmp100 on EB */ - i2c2: i2c-bus@82000 { + i2c2: i2c@82000 { #address-cells = <1>; #size-cells = <0>; bus-frequency = <100000>; @@ -320,7 +365,7 @@ }; /* tmp100 on SVB */ - i2c6: i2c-bus@86000 { + i2c6: i2c@86000 { #address-cells = <1>; #size-cells = <0>; bus-frequency = <100000>; @@ -332,74 +377,75 @@ status = "okay"; }; }; - i2c3: i2c-bus@83000 { + i2c3: i2c@83000 { #address-cells = <1>; #size-cells = <0>; bus-frequency = <100000>; status = "okay"; }; - i2c4: i2c-bus@84000 { + i2c4: i2c@84000 { #address-cells = <1>; #size-cells = <0>; bus-frequency = <100000>; status = "disabled"; }; - i2c5: i2c-bus@85000 { + i2c5: i2c@85000 { #address-cells = <1>; #size-cells = <0>; bus-frequency = <100000>; - status = "disabled"; + status = "okay"; }; - i2c7: i2c-bus@87000 { + i2c7: i2c@87000 { #address-cells = <1>; #size-cells = <0>; bus-frequency = <100000>; - status = "disabled"; + status = "okay"; }; - i2c8: i2c-bus@88000 { + i2c8: i2c@88000 { #address-cells = <1>; #size-cells = <0>; bus-frequency = <100000>; - status = "disabled"; + status = "okay"; }; - i2c9: i2c-bus@89000 { + i2c9: i2c@89000 { #address-cells = <1>; #size-cells = <0>; bus-frequency = <100000>; - status = "disabled"; + status = "okay"; }; - i2c10: i2c-bus@8a000 { + i2c10: i2c@8a000 { #address-cells = <1>; #size-cells = <0>; bus-frequency = <100000>; - status = "disabled"; + status = "okay"; }; - i2c11: i2c-bus@8b000 { + i2c11: i2c@8b000 { #address-cells = <1>; #size-cells = <0>; bus-frequency = <100000>; - status = "disabled"; + status = "okay"; }; - i2c14: i2c-bus@8e000 { + i2c14: i2c@8e000 { #address-cells = <1>; #size-cells = <0>; bus-frequency = <100000>; - status = "disabled"; + status = "okay"; }; - i2c15: i2c-bus@8f000 { + i2c15: i2c@8f000 { #address-cells = <1>; #size-cells = <0>; bus-frequency = <100000>; - status = "disabled"; /* SVB conflict with pspi2 cs gpio20o_pins */ + /* SVB conflict with pspi2 cs gpio20o_pins */ + status = "disabled"; }; pwm_fan:pwm-fan-controller@103000 { @@ -446,16 +492,47 @@ }; }; - /* example for future pspi binding */ - /* - pspi: pspi@0 { - pinctrl-names = "default"; - pinctrl-0 = <&pspi1_pins &pspi2_pins - &gpio20o_pins &gpio203o_pins>; - cs-gpios = <&gpio 20 1>, <&gpio 203 1>; + spi0: spi@200000 { + cs-gpios = <&gpio6 11 GPIO_ACTIVE_LOW>; status = "okay"; + Flash@0 { + compatible = "winbond,w25q128", + "jedec,spi-nor"; + reg = <0x0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <5000000>; + partition@0 { + label = "spi_spare1"; + reg = <0x0000000 0x800000>; + }; + partition@1 { + label = "spi_spare2"; + reg = <0x800000 0x0>; + }; + }; + }; + + spi1: spi@201000 { + cs-gpios = <&gpio0 20 GPIO_ACTIVE_LOW>; + status = "okay"; + Flash@0 { + compatible = "winbond,w25q128fw", + "jedec,spi-nor"; + reg = <0x0>; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <5000000>; + partition@0 { + label = "spi_spare1"; + reg = <0x0000000 0x800000>; + }; + partition@1 { + label = "spi_spare2"; + reg = <0x800000 0x0>; + }; + }; }; - */ }; }; @@ -480,7 +557,7 @@ &gpio82_pins &gpio83_pins &lpc_pins - &gpio132o_pins + &gpio132_pins &gpio133_pins &gpio134_pins &gpio135_pins @@ -514,10 +591,10 @@ &gcr { serial_port_mux: mux-controller { - compatible = "mmio-mux"; - #mux-control-cells = <1>; + compatible = "mmio-mux"; + #mux-control-cells = <1>; - mux-reg-masks = <0x38 0x07>; - idle-states = <2>; /* Serial port mode 3 (takeover) */ + mux-reg-masks = <0x38 0x07>; + idle-states = <2>; /* Serial port mode 3 (takeover) */ }; }; diff --git a/arch/arm/boot/dts/nuvoton-npcm750-gpio.dtsi b/arch/arm/boot/dts/nuvoton-npcm750-gpio.dtsi index b9675fbce4e6..a912910bc7ec 100644 --- a/arch/arm/boot/dts/nuvoton-npcm750-gpio.dtsi +++ b/arch/arm/boot/dts/nuvoton-npcm750-gpio.dtsi @@ -1,15 +1,5 @@ -/* - * DTSi file for the NPCM750 pin controller - * - * Copyright (c) 2014-2017 Nuvoton Technology corporation. - * - * Released under the GPLv2 only. - * SPDX-License-Identifier: GPL-2.0 - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 Nuvoton Technology tomer.maimon@nuvoton.com / { pinctrl: pinctrl@f0800000 { @@ -1107,10 +1097,10 @@ bias-disable; input-enable; }; - gpio132o_pins: gpio132o-pins { + gpio132_pins: gpio132-pins { pins = "GPIO132/SMB10SCL"; bias-disable; - output-high; + input-enable; }; gpio133_pins: gpio133-pins { pins = "GPIO133/SMB10SDA"; diff --git a/arch/arm/boot/dts/nuvoton-npcm750.dtsi b/arch/arm/boot/dts/nuvoton-npcm750.dtsi index 421a4ed54bad..14b3d5b1206f 100644 --- a/arch/arm/boot/dts/nuvoton-npcm750.dtsi +++ b/arch/arm/boot/dts/nuvoton-npcm750.dtsi @@ -44,7 +44,6 @@ }; }; - ahb { gmac1: eth@f0804000 { device_type = "network"; @@ -56,8 +55,8 @@ clocks = <&clk_rg2refck>, <&clk NPCM7XX_CLK_AHB>; clock-names = "stmmaceth", "clk_gmac"; pinctrl-names = "default"; - /*pinctrl-0 = <&rg2_pins - &rg2mdio_pins>;*/ + pinctrl-0 = <&rg2_pins + &rg2mdio_pins>; status = "disabled"; }; @@ -71,8 +70,9 @@ clock-names = "clk_emc"; pinctrl-names = "default"; pinctrl-0 = <&r2_pins - &r2err_pins - &r2md_pins>; + &r2err_pins + &r2md_pins>; + status = "disabled"; }; udc0:udc@f0830000 {