All of lore.kernel.org
 help / color / mirror / Atom feed
* [linux dev-4.19 00/11] Add NPCM7xx patches to dev-4.19
@ 2018-12-16 11:00 Tomer Maimon
  2018-12-16 11:00 ` [linux dev-4.19 01/11] dt-binding: mtd: add NPCM FIU controller Tomer Maimon
                   ` (11 more replies)
  0 siblings, 12 replies; 13+ messages in thread
From: Tomer Maimon @ 2018-12-16 11:00 UTC (permalink / raw)
  To: joel; +Cc: openbmc, Tomer Maimon

Add the following NPCM7xx patches to dev-4.19:

1. NPCM7xx FIU driver(SPI-NOR).
2. NPCM7xx I2C driver.
3. NPCM7xx BIOS post code driver.
4. NPCM7xx PCI mailbox driver.
5. NPCM7xx ETHERNET MAC Controller driver.
6. NPCM7xx defconfig.

Tomer Maimon (11):
  dt-binding: mtd: add NPCM FIU controller
  mtd: spi-nor: add NPCM FIU controller driver
  dt-bindings: i2c: npcm7xx: add binding for i2c controller
  i2c: npcm: driver for Poleg i2c controller
  dt-binding: bmc: Add NPCM7xx LPC BPC documentation
  misc: npcm7xx-lpc-bpc: add NPCM7xx BIOS post code driver
  dt-binding: bmc: add npcm7xx pci mailbox document
  misc: mbox: add npcm7xx pci mailbox driver
  dt-binding: net: document NPCM7xx EMC DT bindings
  net: npcm: add NPCM7xx Ethernet MAC controller
  arm: npcm: add configuration for EVB based NPCM7xx BMC

 .../devicetree/bindings/bmc/npcm7xx-lpc-bpc.txt    |   26 +
 .../devicetree/bindings/bmc/npcm7xx-pci-mbox.txt   |   19 +
 .../devicetree/bindings/i2c/i2c-npcm7xx.txt        |   29 +
 Documentation/devicetree/bindings/mtd/npcm-fiu.txt |   64 +
 .../bindings/net/nuvoton,npcm7xx-emc.txt           |   36 +
 arch/arm/configs/npcm7xx_defconfig                 |  106 +
 drivers/i2c/busses/Kconfig                         |   11 +
 drivers/i2c/busses/Makefile                        |    1 +
 drivers/i2c/busses/i2c-npcm7xx.c                   | 3128 ++++++++++++++++++++
 drivers/misc/Kconfig                               |   15 +
 drivers/misc/Makefile                              |    2 +
 drivers/misc/npcm7xx-lpc-bpc.c                     |  392 +++
 drivers/misc/npcm7xx-pci-mbox.c                    |  288 ++
 drivers/mtd/spi-nor/Kconfig                        |    8 +
 drivers/mtd/spi-nor/Makefile                       |    1 +
 drivers/mtd/spi-nor/npcm-fiu.c                     |  930 ++++++
 drivers/net/ethernet/nuvoton/Kconfig               |   17 +-
 drivers/net/ethernet/nuvoton/Makefile              |    2 +
 drivers/net/ethernet/nuvoton/npcm7xx_emc.c         | 2099 +++++++++++++
 19 files changed, 7173 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/bmc/npcm7xx-lpc-bpc.txt
 create mode 100644 Documentation/devicetree/bindings/bmc/npcm7xx-pci-mbox.txt
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-npcm7xx.txt
 create mode 100644 Documentation/devicetree/bindings/mtd/npcm-fiu.txt
 create mode 100644 Documentation/devicetree/bindings/net/nuvoton,npcm7xx-emc.txt
 create mode 100644 arch/arm/configs/npcm7xx_defconfig
 create mode 100644 drivers/i2c/busses/i2c-npcm7xx.c
 create mode 100644 drivers/misc/npcm7xx-lpc-bpc.c
 create mode 100644 drivers/misc/npcm7xx-pci-mbox.c
 create mode 100644 drivers/mtd/spi-nor/npcm-fiu.c
 create mode 100644 drivers/net/ethernet/nuvoton/npcm7xx_emc.c

-- 
2.14.1

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [linux dev-4.19 01/11] dt-binding: mtd: add NPCM FIU controller
  2018-12-16 11:00 [linux dev-4.19 00/11] Add NPCM7xx patches to dev-4.19 Tomer Maimon
@ 2018-12-16 11:00 ` Tomer Maimon
  2018-12-16 11:00 ` [linux dev-4.19 02/11] mtd: spi-nor: add NPCM FIU controller driver Tomer Maimon
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Tomer Maimon @ 2018-12-16 11:00 UTC (permalink / raw)
  To: joel; +Cc: openbmc, Tomer Maimon

Added device tree binding documentation for Nuvoton BMC
NPCM Flash Interface Unit(FIU) SPI-NOR controller.

Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
---
 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>;
+		};
+	};
+};
+
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [linux dev-4.19 02/11] mtd: spi-nor: add NPCM FIU controller driver
  2018-12-16 11:00 [linux dev-4.19 00/11] Add NPCM7xx patches to dev-4.19 Tomer Maimon
  2018-12-16 11:00 ` [linux dev-4.19 01/11] dt-binding: mtd: add NPCM FIU controller Tomer Maimon
@ 2018-12-16 11:00 ` Tomer Maimon
  2018-12-16 11:00 ` [linux dev-4.19 03/11] dt-bindings: i2c: npcm7xx: add binding for i2c controller Tomer Maimon
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Tomer Maimon @ 2018-12-16 11:00 UTC (permalink / raw)
  To: joel; +Cc: openbmc, Tomer Maimon

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 <tmaimon77@gmail.com>
---
 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 6cc9c929ff57..e3451637240a 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 <linux/slab.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/of_irq.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/spi-nor.h>
+#include <linux/vmalloc.h>
+#include <linux/regmap.h>
+#include <linux/log2.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of_device.h>
+
+#include <asm/sizes.h>
+#include <mtd/mtd-abi.h>
+
+/* 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 <tomer.maimon@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [linux dev-4.19 03/11] dt-bindings: i2c: npcm7xx: add binding for i2c controller
  2018-12-16 11:00 [linux dev-4.19 00/11] Add NPCM7xx patches to dev-4.19 Tomer Maimon
  2018-12-16 11:00 ` [linux dev-4.19 01/11] dt-binding: mtd: add NPCM FIU controller Tomer Maimon
  2018-12-16 11:00 ` [linux dev-4.19 02/11] mtd: spi-nor: add NPCM FIU controller driver Tomer Maimon
@ 2018-12-16 11:00 ` Tomer Maimon
  2018-12-16 11:00 ` [linux dev-4.19 04/11] i2c: npcm: driver for Poleg " Tomer Maimon
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Tomer Maimon @ 2018-12-16 11:00 UTC (permalink / raw)
  To: joel; +Cc: openbmc, Tomer Maimon, Tali Perry

Signed-off-by: Tali Perry <tali.perry1@gmail.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../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 = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&smb0_pins>;
+	};
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [linux dev-4.19 04/11] i2c: npcm: driver for Poleg i2c controller
  2018-12-16 11:00 [linux dev-4.19 00/11] Add NPCM7xx patches to dev-4.19 Tomer Maimon
                   ` (2 preceding siblings ...)
  2018-12-16 11:00 ` [linux dev-4.19 03/11] dt-bindings: i2c: npcm7xx: add binding for i2c controller Tomer Maimon
@ 2018-12-16 11:00 ` Tomer Maimon
  2018-12-16 11:00 ` [linux dev-4.19 05/11] dt-binding: bmc: Add NPCM7xx LPC BPC documentation Tomer Maimon
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Tomer Maimon @ 2018-12-16 11:00 UTC (permalink / raw)
  To: joel; +Cc: openbmc, Tomer Maimon, Tali Perry

Signed-off-by: Tali Perry <tali.perry1@gmail.com>
---
 drivers/i2c/busses/Kconfig       |   11 +
 drivers/i2c/busses/Makefile      |    1 +
 drivers/i2c/busses/i2c-npcm7xx.c | 3128 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 3140 insertions(+)
 create mode 100644 drivers/i2c/busses/i2c-npcm7xx.c

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index ac4b09642f63..5874326727b4 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 CRC8
+	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..08cef07100d7
--- /dev/null
+++ b/drivers/i2c/busses/i2c-npcm7xx.c
@@ -0,0 +1,3128 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Nuvoton NPCM7xx SMB Controller driver
+ *
+ * Copyright (C) 2018 Nuvoton Technologies tali.perry@nuvoton.com
+ */
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/clk/nuvoton.h>
+#include <linux/crc8.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/jiffies.h>
+
+#define I2C_VERSION "0.0.5"
+
+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_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 // Both FIFOs Control and status
+#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 =	 10000;
+
+// 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_STALL	BIT(9)
+#define NPCM_I2C_EVENT_CB	BIT(10)
+#define NPCM_I2C_EVENT_DONE	BIT(11)
+
+#define NPCM_I2C_EVENT_READ1	BIT(12)
+#define NPCM_I2C_EVENT_READ2	BIT(13)
+#define NPCM_I2C_EVENT_READ3	BIT(14)
+#define NPCM_I2C_EVENT_READ4	BIT(15)
+
+
+#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;
+	u16			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			event_log_prev;
+	u32			clk_period_us;
+	unsigned long 			int_time_stamp;
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	u8				own_slave_addr;
+	struct i2c_client		*slave;
+
+	// currently I2C slave IF only supports single byte operations.
+	// in order to utilyze the npcm HW FIFO, the driver will ask for 16bytes
+	// at a time, pack them in buffer, and then transmit them all together
+	// to the FIFO and onward to the bus .
+	// NACK on read will be once reached to bus->adap->quirks->max_read_len
+	// sending a NACK whever the backend requests for it is not supported.
+
+	// This module can be master and slave at the same time. seperate ptrs
+	// and counters:
+	u16			slv_rd_size;
+	u16			slv_rd_ind;
+	u16			slv_wr_size;
+	u16			slv_wr_ind;
+
+	u8 			slv_rd_buf[SMBUS_FIFO_SIZE];
+	u8 			slv_wr_buf[SMBUS_FIFO_SIZE];
+#endif
+
+};
+
+
+
+static inline void npcm_smb_select_bank(struct npcm_i2c *bus,
+					enum smb_bank bank)
+{
+	if (bank == SMB_BANK_0)
+		iowrite8(ioread8(bus->reg + NPCM_SMBCTL3) & ~SMBCTL3_BNK_SEL,
+			 bus->reg + NPCM_SMBCTL3);
+	else
+		iowrite8(ioread8(bus->reg + NPCM_SMBCTL3) | SMBCTL3_BNK_SEL,
+			 bus->reg + NPCM_SMBCTL3);
+}
+
+
+static inline bool npcm_smb_is_quick(struct npcm_i2c *bus);
+static void pdebug(struct npcm_i2c *bus, char str[20])
+{
+	char str2[65];
+	char *s = str2;
+	int rd_size, wr_size, rd_ind, wr_ind;
+
+	return; // for debug, remove this line..
+
+	//if(npcm_smb_is_quick(bus))
+	//	return;
+
+	if (bus->master_or_slave == SMB_MASTER) {
+		rd_size = bus->rd_size;
+		wr_size = bus->wr_size;
+		rd_ind = bus->rd_ind;
+		wr_ind = bus->wr_ind;
+	} else {
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+		rd_size = bus->slv_rd_size;
+		wr_size = bus->slv_wr_size;
+		rd_ind = bus->slv_rd_ind;
+		wr_ind = bus->slv_wr_ind;
+#endif
+	}
+
+	s += sprintf(s, "bus%d ", bus->num);
+	s += sprintf(s, "SA%02x ", bus->dest_addr);
+
+	switch (bus->stop_ind) {
+	case SMB_NO_STATUS_IND:
+		s += sprintf(s, "NO_STATUS_IND	            ");
+		break;
+	case SMB_SLAVE_RCV_IND:
+		s += sprintf(s, "SLAVE_RCV_IND	            ");
+		break;
+	case SMB_SLAVE_XMIT_IND:
+		s += sprintf(s, "SLAVE_XMIT_IND	            ");
+		break;
+	case SMB_SLAVE_XMIT_MISSING_DATA_IND:
+		s += sprintf(s, "SLAVE_XMIT_MISSING_DATA_IND ");
+		break;
+	case SMB_SLAVE_RESTART_IND:
+		s += sprintf(s, "SLAVE_RESTART_IND	    ");
+		break;
+	case SMB_SLAVE_DONE_IND:
+		s += sprintf(s, "SLAVE_DONE_IND	            ");
+		break;
+	case SMB_MASTER_DONE_IND:
+		s += sprintf(s, "MASTER_DONE_IND	    ");
+		break;
+	case SMB_NO_DATA_IND:
+		s += sprintf(s, "NO_DATA_IND		    ");
+		break;
+	case SMB_NACK_IND:
+		return;
+		s += sprintf(s, "NACK_IND		    ");
+		break;
+	case SMB_BUS_ERR_IND:
+		s += sprintf(s, "BUS_ERR_IND		    ");
+		break;
+	case SMB_WAKE_UP_IND:
+		s += sprintf(s, "WAKE_UP_IND                ");
+		break;
+	case SMB_MASTER_PEC_ERR_IND:
+		s += sprintf(s, "MASTER_PEC_ERR_IND	    ");
+		break;
+	case SMB_BLOCK_BYTES_ERR_IND:
+		s += sprintf(s, "BLOCK_BYTES_ERR_IND	    ");
+		break;
+	case SMB_SLAVE_PEC_ERR_IND:
+		s += sprintf(s, "SLAVE_PEC_ERR_IND	    ");
+		break;
+	case SMB_SLAVE_RCV_MISSING_DATA_IND:
+		s += sprintf(s, "SLAVE_RCV_MISSING_DATA_IND ");
+		break;
+	}
+	switch (bus->operation) {
+	case SMB_NO_OPER:
+		s += sprintf(s, "NO_OPER    ");
+		break;
+	case SMB_WRITE_OPER:
+		s += sprintf(s, "WRITE_OPER ");
+		break;
+	case SMB_READ_OPER:
+		s += sprintf(s, "READ_OPER  ");
+		break;
+	}
+	switch (bus->state) {
+	case  SMB_DISABLE:
+		s += sprintf(s, "DISABLE	");
+		break;
+	case  SMB_IDLE:
+		s += sprintf(s, "IDLE	        ");
+		break;
+	case  SMB_MASTER_START:
+		s += sprintf(s, "MASTER_START   ");
+		break;
+	case  SMB_SLAVE_MATCH:
+		s += sprintf(s, "SLAVE_MATCH    ");
+		break;
+	case  SMB_OPER_STARTED:
+		s += sprintf(s, "OPER_STARTED   ");
+		break;
+	case  SMB_STOP_PENDING:
+		s += sprintf(s, "STOP_PENDING   ");
+		break;
+	}
+
+	npcm_smb_select_bank(bus, SMB_BANK_1);
+
+	pr_err("%s %s wr%d,%d rd%d,%d int%d ev0x%02x (prv=0x%02x) blk%d quick%d\tST=%x\tCST=%x\tCTL1=%x\t CTL3=%x\tCST2=%x\tCST3=%x\tFIF_CTS=%x\tTXF_CTL=%x\tT_OUT=%x\tCST2=%x\tCST3=%x\tTXF_STS=%x\tRXF_STS=%x\tRXF_CTL=%x\n",
+		str2, str, wr_size, wr_ind, rd_size, rd_ind,
+		bus->int_cnt, bus->event_log, bus->event_log_prev, (int)bus->read_block_use,
+		npcm_smb_is_quick(bus),
+		ioread8(bus->reg + NPCM_SMBST),
+		ioread8(bus->reg + NPCM_SMBCST),
+		ioread8(bus->reg + NPCM_SMBCTL1 ),
+		ioread8(bus->reg + NPCM_SMBCTL3 ),
+		ioread8(bus->reg + NPCM_SMBCST2 ),
+		ioread8(bus->reg + NPCM_SMBCST3 ),
+
+		ioread8(bus->reg + NPCM_SMBFIF_CTS ),
+		ioread8(bus->reg + NPCM_SMBTXF_CTL ),
+		ioread8(bus->reg + NPCM_SMBT_OUT),
+		ioread8(bus->reg + NPCM_SMBCST2 ),
+		ioread8(bus->reg + NPCM_SMBCST3 ),
+		ioread8(bus->reg + NPCM_SMBTXF_STS ),
+		ioread8(bus->reg + NPCM_SMBRXF_STS ),
+		ioread8(bus->reg + NPCM_SMBRXF_CTL )  );
+
+	npcm_smb_select_bank(bus, SMB_BANK_0);
+	pr_err("\tCTL4=%x\tCTL5=%x\tFIF_CTL=%x\n",
+		ioread8(bus->reg + NPCM_SMBCTL4 ),
+		ioread8(bus->reg + NPCM_SMBCTL5 ),
+		ioread8(bus->reg + NPCM_SMBFIF_CTL ));
+	npcm_smb_select_bank(bus, SMB_BANK_1);
+
+
+}
+
+static void pdebug_lvl2(struct npcm_i2c *bus, char str[20])
+{
+	// remove for in-depth debug:
+	return;
+	pdebug(bus, str);
+}
+
+
+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));
+	}
+}
+
+static int npcm_smb_get_SCL(struct i2c_adapter *_adap)
+{
+	unsigned int ret = 0;
+	struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap);
+
+	// Get SCL level
+	ret = FIELD_GET(SMBCTL3_SCL_LVL,  ioread8(bus->reg + NPCM_SMBCTL3));
+
+	return ret;
+}
+
+static int npcm_smb_get_SDA(struct i2c_adapter *_adap)
+{
+	unsigned int ret = 0;
+	struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap);
+
+	// Get SDA level
+	ret = FIELD_GET(SMBCTL3_SDA_LVL,  ioread8(bus->reg + NPCM_SMBCTL3));
+
+	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_empty(struct npcm_i2c *bus)
+{
+	u8 tx_fifo_sts = ioread8(bus->reg + NPCM_SMBTXF_STS);
+
+	// check if TX FIFO is not empty
+	if ((tx_fifo_sts & NPCM_SMBTXF_STS_TX_BYTES) == 0)
+		return false;
+
+	// check if TX FIFO empty:
+	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)
+{
+	u8 rx_fifo_sts = ioread8(bus->reg + NPCM_SMBRXF_STS);
+
+	// check if RX FIFO is not empty:
+	if ((rx_fifo_sts & NPCM_SMBRXF_STS_RX_BYTES) == 0)
+		return false;
+
+	// check if rx fifo full status is set:
+	return (bool)FIELD_GET(NPCM_SMBRXF_STS_RX_THST,
+			       ioread8(bus->reg + NPCM_SMBRXF_STS));
+}
+
+
+static inline void npcm_smb_clear_fifo_int(struct npcm_i2c *bus)
+{
+	iowrite8((ioread8(bus->reg + NPCM_SMBFIF_CTS) &
+			NPCM_SMBFIF_CTS_SLVRSTR) |
+			NPCM_SMBFIF_CTS_RXF_TXE,
+			bus->reg + NPCM_SMBFIF_CTS);
+}
+
+
+static inline void npcm_smb_clear_tx_fifo(struct npcm_i2c *bus)
+{
+	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)
+{
+	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 void npcm_smb_set_fifo(struct npcm_i2c *bus, int nread, int nwrite);
+
+
+static inline void npcm_smb_master_stop(struct npcm_i2c *bus)
+{
+	NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_STOP);
+
+	pdebug_lvl2(bus, "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
+	udelay(10);
+
+	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_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)
+{
+	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;
+	pdebug(bus, "*** reset bus ***");
+
+	smbctl1 = ioread8(bus->reg + NPCM_SMBCTL1);
+
+	// 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);
+
+	// Clear BB (BUS BUSY) bit
+	iowrite8(NPCM_SMBCST_BB, bus->reg + NPCM_SMBCST);
+
+	iowrite8(0xFF, bus->reg + NPCM_SMBST);
+
+	// Clear EOB bit
+	iowrite8(NPCM_SMBCST3_EO_BUSY, bus->reg + NPCM_SMBCST3);
+
+	// Clear all fifo bits:
+	iowrite8(NPCM_SMBFIF_CTS_CLR_FIFO, bus->reg + NPCM_SMBFIF_CTS);
+
+	// Reset driver status
+	bus->state = SMB_IDLE;
+}
+
+static inline bool npcm_smb_is_master(struct npcm_i2c *bus)
+{
+	return (bool)FIELD_GET(NPCM_SMBST_MASTER,
+			       ioread8(bus->reg + NPCM_SMBST));
+}
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static bool npcm_smb_slave_start_receive(struct npcm_i2c *bus, u16 nread,
+					 u8 *read_data);
+static bool npcm_smb_slave_start_xmit(struct npcm_i2c *bus, u16 nwrite,
+				      u8 *write_data);
+static void npcm_smb_slave_abort(struct npcm_i2c *bus);
+//static int  npcm_i2c_slave_get_rd_buf(struct npcm_i2c *bus);
+static int  npcm_i2c_slave_get_wr_buf(struct npcm_i2c *bus);
+static void npcm_i2c_slave_send_rd_buf(struct npcm_i2c *bus);
+static void npcm_i2c_slave_restart_or_stop_flush_wr_buf(struct npcm_i2c *bus);
+#endif
+
+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;
+
+	NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_CB);
+
+	//if (op_status != 6)
+		pdebug_lvl2(bus, "CB");
+	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;
+		NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_DONE);
+
+		if((ioread8(bus->reg + NPCM_SMBCST) & NPCM_SMBCST_BB) != 0)
+			pdebug(bus, "WARNING busy at done");
+		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;
+		pdebug(bus, "CB NO DATA");
+		if (bus->master_or_slave == SMB_MASTER)
+			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;
+		pdebug_lvl2(bus, "CB-NACK ");
+		if (bus->master_or_slave == SMB_MASTER)
+			complete(&bus->cmd_complete);
+
+		break;
+	case SMB_BUS_ERR_IND:
+		// Bus error
+		// info: has no meaning
+		bus->cmd_err = -EIO;
+		pdebug(bus, "CB BER  ");
+		if (bus->master_or_slave == SMB_MASTER)
+			complete(&bus->cmd_complete);
+		break;
+	case SMB_WAKE_UP_IND:
+		pdebug(bus, "wake_up");
+		// SMBus wake up
+		// info: has no meaning
+		break;
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+		//extern u8  rd_buf[PAGE_SIZE];
+		//extern u16 rd_size;
+		//extern u8  wr_buf[32];
+		//extern u16 wr_size;
+	case SMB_SLAVE_RCV_IND:
+		// Slave got an address match with direction bit clear so it
+		//	should receive datathe int must call
+		//      npcm_smb_slave_start_receive()
+		//      info: the enum SMB_ADDR_T address match
+
+
+		//npcm_i2c_slave_get_rd_buf(bus); // if the buffer is empty nothing will be sent
+
+		// this module does not support saying no to bytes. it will always ACK.
+
+		if(!npcm_smb_slave_start_receive(bus, 32*1024, bus->slv_rd_buf))
+			printk("npcm_smb_slave_start_receive fail");
+
+		pdebug_lvl2(bus, "CB start slv rcv   ");
+		break;
+
+	case SMB_SLAVE_RESTART_IND: // if switch from read to write forget the bytes saved from before.
+	case SMB_SLAVE_XMIT_IND:
+		// Slave got an address match with direction bit set so it
+		//      should transmit data
+		//	the int must call npcm_smb_slave_start_xmit()
+		// info: the enum SMB_ADDR_T address match
+
+		npcm_i2c_slave_send_rd_buf(bus); // send up whatever is on the buffer.
+
+		npcm_i2c_slave_restart_or_stop_flush_wr_buf(bus);
+
+		if(!npcm_smb_slave_start_xmit(bus,bus->slv_wr_size,
+					  bus->slv_wr_buf))
+			printk("npcm_smb_slave_start_xmit fail");
+
+		pdebug_lvl2(bus, "CB start slv xmit  ");
+		break;
+	case SMB_SLAVE_DONE_IND:
+		// Slave done transmitting or receiving
+		// info:
+		//	on receive: number of actual bytes received
+		//	on transmit: number of actual bytes transmitted,
+		//	when PEC is used 'info' should be (nwrite+1) which means
+		//		 that 'nwrite' bytes were sent + the PEC byte
+		//		'nwrite' is the second parameter
+		//               npcm_smb_slave_start_xmit()
+
+		npcm_i2c_slave_send_rd_buf(bus); // if the buffer is empty nothing will be sent
+
+		i2c_slave_event(bus->slave, I2C_SLAVE_STOP, (u8 *)&info);
+
+		bus->int_cnt = 0;
+		pdebug_lvl2(bus, "CB_DONE");
+		break;
+#endif // CONFIG_I2C_SLAVE
+	default:
+		pdebug(bus, "CB 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_master(struct npcm_i2c *bus,
+					  u16 max_bytes_to_send)
+{
+	pdebug_lvl2(bus, "wr_fifo_master");
+	// 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)) {
+				// 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 {
+			npcm_smb_wr_byte(bus, 0xFF);
+		}
+	}
+}
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+static void npcm_smb_write_to_fifo_slave(struct npcm_i2c *bus, u16 max_bytes_to_send)
+{
+	pdebug_lvl2(bus, "wr_fifo");
+	// 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->slv_wr_ind < bus->slv_wr_size) {
+			if (bus->PEC_use &&
+			    (bus->slv_wr_ind + 1 == bus->slv_wr_size) &&
+			    (bus->slv_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->slv_wr_ind++;
+			} else {
+				npcm_smb_wr_byte(bus,
+						 bus->slv_wr_buf[bus->slv_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->slv_wr_ind);
+
+			iowrite8(NPCM_SMBST_SDAST, bus->reg + NPCM_SMBST);
+#else
+			npcm_smb_wr_byte(bus, 0xFF);
+#endif
+		}
+	}
+}
+#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)
+{
+	u16 rxf_ctl = 0;
+	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) {
+
+		rxf_ctl = min((u16)nread, (u16)SMBUS_FIFO_SIZE);
+		rxf_ctl |= NPCM_SMBRXF_CTL_THR_RXIE;
+
+		// set LAST bit. if LAST is set enxt FIFO packet is nacked at the end.
+
+		// regular read of less then buffer size:
+		if (nread <= SMBUS_FIFO_SIZE)
+			rxf_ctl |= NPCM_SMBRXF_CTL_LAST_PEC;
+		// if we are about to read the first byte in blk rd mode,
+		// don't NACK it. BTW , if slave return zero size HW can't NACK
+		// it immidiattly, it will read enxtra byte and then NACK.
+		if (bus->rd_ind == 0 && bus->read_block_use){
+			rxf_ctl = NPCM_SMBRXF_CTL_THR_RXIE | 0x01;
+		}
+
+		// set fifo size:
+		iowrite8(rxf_ctl, 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
+			iowrite8(NPCM_SMBTXF_CTL_THR_TXIE |
+				nwrite, //  / 2),
+				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)
+{
+	u8 data;
+	while (bytes_in_fifo--) {
+		npcm_smb_rd_byte(bus, &data);
+
+		if (bus->master_or_slave == SMB_MASTER) {
+			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;
+					pdebug(bus, "blk rcv");
+				}
+			}
+		} else { // SMB_SLAVE:
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+			if (bus->slv_rd_ind < bus->slv_rd_size) {
+				bus->slv_rd_buf[bus->slv_rd_ind++] = data;
+				if (bus->slv_rd_ind == 1 && bus->read_block_use)
+					// First byte indicates length in block protocol
+					bus->rd_size = data;
+			}
+#endif
+		}
+	}
+}
+
+
+static int npcm_smb_master_abort(struct npcm_i2c *bus)
+{
+	int ret = 0;
+	u8 data;
+	int len;
+
+	NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_ABORT);
+
+	// Only current master is allowed to issue Stop Condition
+	if (npcm_smb_is_master(bus)) {
+		// stoping in the middle, not waing for interrupts anymore
+		npcm_smb_eob_int(bus,  false);
+
+		// Generate a STOP condition (after next wr\rd from fifo:
+		npcm_smb_master_stop(bus);
+		if (bus->operation == SMB_WRITE_OPER){
+			npcm_smb_set_fifo(bus, 0, 1);
+			// dummy write to FIFO:
+			npcm_smb_wr_byte(bus, 0xFF);
+			pdebug(bus, " abort data wr");
+		}
+		else {
+			// gracefully abort read transaction
+			len = npcm_smb_get_fifo_fullness(bus);
+
+			if (len > 0)
+				npcm_smb_read_from_fifo(bus, len);
+			npcm_smb_set_fifo(bus, 1, 0);
+			npcm_smb_rd_byte(bus, &data);
+			pdebug(bus, " abort data rd");
+		}
+
+		udelay(100); // TODO, replace with TO polling on BB bit.
+
+		// Clear NEGACK, STASTR and BER bits
+		iowrite8(0xFF, bus->reg + NPCM_SMBST);
+	}
+
+	pdebug(bus, " abort data 3 ");
+
+	npcm_smb_reset(bus);
+
+	return ret;
+}
+
+
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+
+static irqreturn_t npcm_i2c_bus_irq(int irq, void *dev_id);
+
+
+static int  npcm_smb_slave_enable_l(struct npcm_i2c *bus,
+			enum smb_addr addr_type, u8 addr, bool enable)
+{
+	u8 SmbAddrX_Addr = FIELD_PREP(NPCM_SMBADDR_ADDR, addr) |
+		FIELD_PREP(NPCM_SMBADDR_SAEN, enable);
+
+
+	pdebug_lvl2(bus, " slave enable ");
+	if (addr_type == SMB_GC_ADDR) {
+		iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) &
+			~NPCM_SMBCTL1_GCMEN) |
+			FIELD_PREP(NPCM_SMBCTL1_GCMEN, enable),
+			bus->reg + NPCM_SMBCTL1);
+		return 0;
+	}
+	if (addr_type == SMB_ARP_ADDR) {
+
+		iowrite8((ioread8(bus->reg + NPCM_SMBCTL3) &
+			~SMBCTL3_ARPMEN) |
+			FIELD_PREP(SMBCTL3_ARPMEN, enable),
+			bus->reg + NPCM_SMBCTL3);
+		return 0;
+	}
+	if (addr_type >= SMB_NUM_OF_ADDR)
+		return -EFAULT;
+
+	// Disable ints and select bank 0 for address 3 to ...
+	npcm_smb_select_bank(bus, SMB_BANK_0);
+
+	// Set and enable the address
+	iowrite8(SmbAddrX_Addr, bus->reg + NPCM_SMBADDR[(int)addr_type]);
+
+	// return to bank 1 and enable ints (if needed)
+	npcm_smb_select_bank(bus, SMB_BANK_1);
+
+	pdebug(bus, " slave enable done ");
+
+	return 0;
+}
+
+static u8 npcm_smb_get_slave_addr_l(struct npcm_i2c *bus,
+				       enum smb_addr addr_type)
+{
+	unsigned long flags;
+	u8 slaveAddress;
+
+	// disable ints and select bank 0 for address 3 to ...
+	spin_lock_irqsave(&bus->lock, flags);
+	npcm_smb_select_bank(bus, SMB_BANK_0);
+
+	// printk("I2C%d get addr %d", bus->num, (int)addr_type);
+
+	slaveAddress = ioread8(bus->reg + NPCM_SMBADDR[(int)addr_type]);
+
+	// return to bank 1 and enable ints (if needed)
+	npcm_smb_select_bank(bus, SMB_BANK_1);
+	return  slaveAddress;
+}
+
+#if defined(TODO_MULTI_SA) // poleg support up to 10 SA.
+static bool npcm_smb_is_slave_addr_exist(struct npcm_i2c *bus, u8 addr)
+{
+	int i;
+
+	addr |= 0x80; //Set the enable bit
+
+	for (i = SMB_SLAVE_ADDR1; i < SMB_NUM_OF_ADDR; i++)
+		if (addr == npcm_smb_get_slave_addr_l(bus, (enum smb_addr)i))
+			return true;
+
+	return false;
+}
+
+static int  npcm_smb_add_slave_addr(struct npcm_i2c *bus,
+				       u8 slaveAddrToAssign, bool use_PEC)
+{
+	u16 i;
+	int ret = -EFAULT;
+	// printk("slaveAddrToAssign = %02X\n", slaveAddrToAssign);
+
+	slaveAddrToAssign |= 0x80; //set the enable bit
+
+	for (i = SMB_SLAVE_ADDR1; i < SMB_NUM_OF_ADDR; i++) {
+		u8 currentSlaveAddr = npcm_smb_get_slave_addr_l(bus,
+							(enum smb_addr)i);
+		if (currentSlaveAddr == slaveAddrToAssign) {
+			ret = 0;
+			break;
+		}
+		else if ((currentSlaveAddr & 0x7F) == 0) {
+			ret = npcm_smb_slave_enable_l(bus,
+				(enum smb_addr)i, slaveAddrToAssign, true);
+			break;
+		}
+	}
+
+	if (ret == 0) {
+		if (use_PEC)
+			bus->PEC_mask |= 1 << i;
+		else
+			bus->PEC_mask &= ~(1 << i);
+	}
+	return ret;
+}
+
+static int  npcm_smb_get_current_slave_addr(struct npcm_i2c *bus,
+					       u8 *currSlaveAddr)
+{
+	if (currSlaveAddr != NULL) {
+		*currSlaveAddr = bus->own_slave_addr;
+		return 0;
+	}
+
+	return -EFAULT;
+}
+#endif //TODO_MULTI_SA
+
+static int  npcm_smb_remove_slave_addr(struct npcm_i2c *bus,
+					  u8 slaveAddrToRemove)
+{
+	int i;
+	unsigned long flags;
+
+	slaveAddrToRemove |= 0x80; //Set the enable bit
+
+	// disable ints and select bank 0 for address 3 to ...
+	spin_lock_irqsave(&bus->lock, flags);
+	npcm_smb_select_bank(bus, SMB_BANK_0);
+
+	for (i = SMB_SLAVE_ADDR1; i < SMB_NUM_OF_ADDR; i++) {
+		if (ioread8(bus->reg + NPCM_SMBADDR[i]) == slaveAddrToRemove)
+			iowrite8(0, bus->reg + NPCM_SMBADDR[i]);
+	}
+
+	// return to bank 1 and enable ints (if needed)
+	npcm_smb_select_bank(bus, SMB_BANK_1);
+	spin_unlock_irqrestore(&bus->lock, flags);
+
+	return 0;
+}
+
+#if defined(TODO_MULTI_SA)  // poleg support up to 10 SA.
+static int  npcm_smb_slave_global_call_enable(struct npcm_i2c *bus,
+						 bool enable)
+{
+	return npcm_smb_slave_enable_l(bus, SMB_GC_ADDR, 0, enable);
+}
+
+static int  npcm_smb_slave_ARP_enable(struct npcm_i2c *bus, bool enable)
+{
+	return npcm_smb_slave_enable_l(bus, SMB_ARP_ADDR, 0, enable);
+}
+#endif // TODO_MULTI_SA
+
+static bool npcm_smb_slave_start_receive(struct npcm_i2c *bus, u16 nread,
+					    u8 *read_data)
+{
+
+	// Allow only if bus is not busy
+	if ((bus->state != SMB_SLAVE_MATCH) || nread > 32*1024)
+		return false;
+
+	// Update driver state
+	bus->state = SMB_OPER_STARTED;
+	bus->operation	 = SMB_READ_OPER;
+	//bus->slv_rd_buf   = read_data;
+	bus->slv_rd_size	 = nread;
+	bus->slv_rd_ind	= 0;
+	bus->slv_wr_size	= 0;
+	bus->slv_wr_ind	= 0;
+
+	npcm_smb_set_fifo(bus, min(nread, bus->threshold_fifo), -1);
+
+	// triggers new data reception
+	iowrite8(NPCM_SMBST_NMATCH, bus->reg + NPCM_SMBST);
+
+	return true;
+}
+
+static bool npcm_smb_slave_start_xmit(struct npcm_i2c *bus, u16 nwrite,
+					 u8 *write_data)
+{
+
+	// Allow only if bus is not busy
+	if ((bus->state != SMB_SLAVE_MATCH) || (nwrite == 0))
+		return false;
+
+
+	// Update driver state
+	if (bus->PEC_use)
+		nwrite++;
+
+	bus->state = SMB_OPER_STARTED;
+	bus->operation	 = SMB_WRITE_OPER;
+	bus->slv_wr_size	= nwrite;
+	bus->slv_wr_ind	= 0;
+
+	if (bus->fifo_use == true) {
+		// triggers new data reception
+		iowrite8(NPCM_SMBST_NMATCH, bus->reg + NPCM_SMBST);
+
+		if (nwrite > 0) {
+
+			npcm_smb_set_fifo(bus, -1, min((u16)bus->threshold_fifo, nwrite));
+
+			// Fill the FIFO with data
+			npcm_smb_write_to_fifo_slave(bus, min((u16)bus->threshold_fifo, nwrite));
+		}
+	}
+	else // bus->fifo_use == FALSE
+		npcm_smb_wr_byte(bus, bus->slv_wr_buf[bus->slv_wr_ind++]);
+
+	return true;
+}
+
+
+static int npcm_smb_int_slave_handler(struct npcm_i2c *bus)
+{
+	enum smb_state_ind ind;
+
+	pdebug_lvl2(bus, "int_slave_handler ");
+
+	// Slave: A negative acknowledge has occurred
+	if (FIELD_GET(NPCM_SMBST_NEGACK , ioread8(bus->reg + NPCM_SMBST))) {
+		pdebug_lvl2(bus, "NACK slave");
+		NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_NACK);
+		if (bus->fifo_use) {
+
+			// if there are still untransmitted bytes in TX FIFO reduce them from slv_wr_ind
+			bus->slv_wr_ind -= FIELD_GET(NPCM_SMBTXF_STS_TX_BYTES,
+						      ioread8(bus->reg + NPCM_SMBTXF_STS));
+
+			if (bus->operation == SMB_WRITE_OPER)
+				bus->slv_wr_ind -= npcm_smb_get_fifo_fullness(bus);
+			// clear the FIFO
+			iowrite8(NPCM_SMBFIF_CTS_CLR_FIFO,
+				 bus->reg + NPCM_SMBFIF_CTS);
+		}
+
+		// In slave write operation, NACK is OK, otherwise it is a problem
+		if (!(	(bus->slv_wr_ind != 0) && (bus->slv_wr_ind == bus->slv_wr_size)))
+			// Either not slave, or number of bytes sent to master less than required
+			// In either case notify upper layer. If we are slave - the upper layer
+			// should still wait for a Slave Stop.
+		{
+
+
+			// iowrite8(NPCM_SMBST_NEGACK, bus->reg + NPCM_SMBST);
+			bus->state = SMB_IDLE;
+			npcm_smb_callback(bus, SMB_NACK_IND, bus->slv_wr_ind);
+		}
+
+
+		// Slave has to wait for SMB_STOP to decide this is the end of the transaction.
+		// Therefore transaction is not yet considered as done
+		iowrite8(NPCM_SMBST_NEGACK, bus->reg + NPCM_SMBST);
+
+		return 0;
+	}
+
+#if defined (SMB_CAPABILITY_TIMEOUT_SUPPORT)
+
+	// Slave: A Bus Timeout has been identified
+	if ((FIELD_GET(NPCM_SMBT_OUT_T_OUTIE, ioread8(bus->reg + NPCM_SMBT_OUT) ) == 1) &&  // bus timeout int is on
+	    (FIELD_GET(NPCM_SMBT_OUT_T_OUTST, ioread8(bus->reg + NPCM_SMBT_OUT) ))) {         // and bus timeout status is set
+		pdebug(bus, "TO slave ");
+		// Reset the module
+		npcm_smb_reset(bus);
+		iowrite8(ioread8(bus->reg + NPCM_SMBT_OUT) | NPCM_SMBT_OUT_T_OUTST, bus->reg + NPCM_SMBT_OUT);// Clear EO_BUSY pending bit
+		bus->state = SMB_IDLE;
+		npcm_smb_callback(bus, SMB_BUS_ERR_IND, npcm_smb_get_index(bus));
+		return 0;
+	}
+#endif
+
+	// A Slave End of Busy (meaning Stop Condition happened)
+	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);
+		pdebug_lvl2(bus, "slave EOB");
+
+		npcm_smb_eob_int(bus, false);
+
+		bus->state = SMB_IDLE;
+		npcm_smb_callback(bus, bus->stop_ind, bus->slv_rd_ind);
+		return 0;
+	}
+
+	// A Slave Stop Condition has been identified
+	if (FIELD_GET(NPCM_SMBST_SLVSTP , ioread8(bus->reg + NPCM_SMBST))) {
+		enum smb_state_ind ind;
+
+		pdebug_lvl2(bus, "slave stop ");
+		if (bus->master_or_slave != SMB_SLAVE)
+			pdebug(bus, "slave error ");
+		iowrite8(NPCM_SMBST_SLVSTP, bus->reg + NPCM_SMBST);
+
+
+		// Check whether bus arbitration or Start or Stop during data xfer
+		bus->state = SMB_IDLE;
+		if (bus->fifo_use) {
+			if (bus->operation == SMB_READ_OPER) {
+				npcm_smb_read_from_fifo(bus,
+							   FIELD_GET(NPCM_SMBRXF_STS_RX_BYTES,
+							   ioread8(bus->reg + NPCM_SMBRXF_STS)));
+
+				// Be prepared for new transactions
+				//bus->state = SMB_IDLE;
+
+				// if PEC is not used or PEC is used and PEC is correct
+				if ((bus->PEC_use == false) ||
+				    (npcm_smb_get_PEC(bus) == 0)){
+					ind = SMB_SLAVE_DONE_IND;
+				}
+
+				// PEC value is not correct
+				else {
+					ind = SMB_SLAVE_PEC_ERR_IND;
+				}
+				npcm_smb_callback(bus, ind, bus->slv_rd_ind);
+			}
+			if (bus->operation == SMB_WRITE_OPER) {
+				//bus->state = SMB_IDLE;
+				npcm_smb_callback(bus, SMB_SLAVE_DONE_IND,
+						     bus->slv_wr_ind);
+			}
+
+			iowrite8(NPCM_SMBFIF_CTS_SLVRSTR |
+				 NPCM_SMBFIF_CTS_CLR_FIFO |
+				 NPCM_SMBFIF_CTS_RXF_TXE, bus->reg + NPCM_SMBFIF_CTS);
+		}
+
+		// FIFO is not used (slave)
+		else {
+			if (bus->operation == SMB_READ_OPER) {
+
+				// if PEC is not used or PEC is used and PEC is correct
+				if ((bus->PEC_use == false) ||
+				    (npcm_smb_get_PEC(bus) == 0))
+				    ind = SMB_SLAVE_DONE_IND;
+
+				// PEC value is not correct
+				else
+					ind = SMB_SLAVE_PEC_ERR_IND;
+
+				npcm_smb_callback(bus, ind, bus->slv_rd_ind);
+			} else
+				//bus->state = SMB_IDLE;
+				npcm_smb_callback(bus, SMB_SLAVE_DONE_IND,
+				bus->slv_wr_ind);
+		}
+
+		return 0;
+	}
+
+	// A Slave restart Condition has been identified
+	if (bus->fifo_use && FIELD_GET(NPCM_SMBFIF_CTS_SLVRSTR,
+				       ioread8(bus->reg + NPCM_SMBFIF_CTS))) {
+		pdebug_lvl2(bus, "slave restart ");
+		if (bus->master_or_slave != SMB_SLAVE)
+			pdebug(bus, "slave restart error ");
+
+		if (bus->operation == SMB_READ_OPER)
+			npcm_smb_read_from_fifo(bus,
+						FIELD_GET(NPCM_SMBRXF_STS_RX_BYTES,
+						ioread8(bus->reg + NPCM_SMBRXF_STS)));
+
+		iowrite8(NPCM_SMBFIF_CTS_SLVRSTR, bus->reg + NPCM_SMBFIF_CTS);
+	}
+
+	// A Slave Address Match has been identified
+	if (FIELD_GET(NPCM_SMBST_NMATCH , ioread8(bus->reg + NPCM_SMBST))) {
+		bool slave_tx;
+		enum smb_state_ind ind = SMB_NO_STATUS_IND;
+		u8 info = 0;
+
+		pdebug_lvl2(bus, "slave match ");
+
+		if (bus->fifo_use == false)
+			iowrite8(NPCM_SMBST_NMATCH, bus->reg + NPCM_SMBST);
+
+		if (FIELD_GET(NPCM_SMBST_XMIT, ioread8(bus->reg + NPCM_SMBST))) {
+			slave_tx = true;
+			ind = SMB_SLAVE_XMIT_IND;
+		} else {
+			slave_tx = false;
+			if (bus->state == SMB_IDLE)
+				i2c_slave_event(bus->slave, I2C_SLAVE_WRITE_REQUESTED, &info);
+			ind = SMB_SLAVE_RCV_IND;
+		}
+
+		if (bus->state == SMB_IDLE) {
+			// Check which type of address match
+			if (FIELD_GET(NPCM_SMBCST_MATCH , ioread8(bus->reg + NPCM_SMBCST))) {
+				u16 address_match = ((ioread8(bus->reg + NPCM_SMBCST3) & 0x7) << 7) | (ioread8(bus->reg + NPCM_SMBCST2) & 0x7F);
+				info = 0;
+
+				while (address_match) {
+					if (address_match & 1)
+						break;
+					info++;
+					address_match = address_match >> 1;
+				}
+				bus->own_slave_addr = FIELD_GET(NPCM_SMBADDR_ADDR, npcm_smb_get_slave_addr_l(bus, (enum smb_addr)info));
+				if (bus->PEC_mask & BIT(info)) {
+					bus->PEC_use = true;
+					bus->crc_data = 0;
+					if (slave_tx)
+						npcm_smb_calc_PEC(bus, (bus->own_slave_addr & 0x7F) << 1 | 1);
+					else
+						npcm_smb_calc_PEC(bus, (bus->own_slave_addr & 0x7F) << 1);
+				} else
+					bus->PEC_use = false;
+			} else {
+				if (FIELD_GET(NPCM_SMBCST_GCMATCH , ioread8(bus->reg + NPCM_SMBCST))) {
+					info = (u8)SMB_GC_ADDR;
+					bus->own_slave_addr = 0;
+				} else {
+					if (FIELD_GET(NPCM_SMBCST_ARPMATCH , ioread8(bus->reg + NPCM_SMBCST))) {
+						info = (u8)SMB_ARP_ADDR;
+						bus->own_slave_addr = 0x61;
+					}
+				}
+				// printk("slave match addr 0x%x", bus->own_slave_addr);
+			}
+		} else {
+			//  Slave match can happen in two options:
+			//  1. Start, SA, read	( slave read without further ado).
+			//  2. Start, SA, read , data , restart, SA, read,  ... ( slave read in fragmented mode)
+			//  3. Start, SA, write, data, restart, SA, read, .. ( regular write-read mode)
+			if ((bus->state == SMB_OPER_STARTED &&
+				bus->operation == SMB_READ_OPER && slave_tx) ||
+				(!slave_tx)){
+				// slave transmit after slave receive w/o Slave Stop implies repeated start
+				ind = SMB_SLAVE_RESTART_IND;
+				info = (u8)(bus->slv_rd_ind);
+				npcm_smb_calc_PEC(bus, (bus->own_slave_addr & 0x7F) << 1 | 1);
+				pdebug_lvl2(bus, "slave Sr ");
+			}
+		}
+
+		// Address match automatically implies slave mode
+		bus->master_or_slave = SMB_SLAVE;
+		bus->state = SMB_SLAVE_MATCH;
+
+		// Notify upper layer
+		// Upper layer must at this stage call the driver routine for slave tx or rx,
+		// to eliminate a condition of slave being notified but not yet starting
+		// transaction - and thus an endless int from SDAST for the slave RCV or TX !
+		npcm_smb_callback(bus, ind, info);
+
+#ifdef SMB_RECOVERY_SUPPORT
+
+		// By now, SMB operation state should have been changed from MATCH to SMB_OPER_STARTED.
+		// If state hasn't been changed already, this may suggest that the SMB slave is not ready to
+		// transmit or receive data.
+		//
+		// In addition, when using FIFO, NMATCH bit is cleared only when moving to SMB_OPER_STARTED state.
+		// If NMATCH is not cleared, we would get an endless SMB int.
+		// Therefore, Abort the slave, such that SMB HW and state machine return to a default, functional
+		// state.
+		if (bus->state == SMB_SLAVE_MATCH) {
+			npcm_smb_slave_abort(bus);
+			npcm_smb_callback(bus, SMB_BUS_ERR_IND, npcm_smb_get_index(bus));
+			return 0;
+		}
+
+		// Slave abort data
+		// if the SMBus's status is not match current status reg of XMIT
+		// the Slave device will enter dead-lock and stall bus forever
+		// Add this check rule to avoid this condition
+		if ((bus->operation == SMB_READ_OPER  &&
+			ind == SMB_SLAVE_XMIT_IND) ||
+			(bus->operation == SMB_WRITE_OPER
+			&& ind == SMB_SLAVE_RCV_IND)) {
+			npcm_smb_slave_abort(bus);
+
+			npcm_smb_callback(bus, SMB_BUS_ERR_IND, npcm_smb_get_index(bus));
+			return 0;
+		}
+#endif
+		return 0;
+
+		// If none of the above - BER should occur
+	}
+
+
+
+	// Slave SDA status is set - transmit or receive, slave
+	if (FIELD_GET(NPCM_SMBST_SDAST, ioread8(bus->reg + NPCM_SMBST)) ||
+	    (bus->fifo_use   &&
+	    (npcm_smb_tx_fifo_empty(bus) || npcm_smb_rx_fifo_full(bus)))) {
+		// Status Bit is cleared by writing to or reading from SDA
+		// (depending on current direction)
+
+		pdebug_lvl2(bus, "SDA slave set");
+
+		// SDA status is set - transmit or receive: Handle slave mode
+
+		// Perform slave read. No need to distinguish between last byte and the rest of the bytes.
+		if ((bus->operation == SMB_READ_OPER)) {
+			if (bus->fifo_use == false) {
+				u8 data;
+
+				(void)npcm_smb_rd_byte(bus, &data);
+				if (bus->slv_rd_ind < bus->slv_rd_size) {
+					// Keep read data
+					bus->slv_rd_buf[bus->slv_rd_ind++] = data;
+					if ((bus->slv_rd_ind == 1) && bus->read_block_use)
+						// First byte indicates length in block protocol
+						bus->slv_rd_size = data;
+
+#ifdef SMB_WRAP_AROUND_BUFFER
+					if (bus->slv_rd_ind == bus->slv_rd_size) {
+						// Reset state for the remaining bytes transaction
+						bus->state = SMB_SLAVE_MATCH;
+
+						// Notify upper layer of that a byte had received
+						npcm_smb_callback(bus,
+								     SMB_SLAVE_RCV_MISSING_DATA_IND,
+								     npcm_smb_get_index(bus));
+					}
+#endif
+				}
+			}
+			// FIFO is used
+			else {
+				if (npcm_smb_rx_fifo_full(bus)) {
+					npcm_smb_read_from_fifo(bus,
+								   FIELD_GET(NPCM_SMBRXF_CTL_RX_THR,
+								   ioread8(bus->reg + NPCM_SMBRXF_CTL)));
+
+					npcm_smb_clear_rx_fifo(bus);
+				}
+			}
+		}
+		// Perform slave write.
+		else {
+			// More bytes to write
+			if ((bus->operation == SMB_WRITE_OPER) && (bus->slv_wr_ind < bus->slv_wr_size)) {
+				if (bus->fifo_use == false) {
+					if (bus->slv_wr_ind + 1 == bus->slv_wr_size)
+						npcm_smb_write_PEC(bus);
+					else if (bus->slv_wr_ind < bus->slv_wr_size)
+						npcm_smb_wr_byte(bus, bus->slv_wr_buf[bus->slv_wr_ind]);
+					bus->slv_wr_ind++;
+				}
+				// FIFO is used
+				else {
+					u16 wcount;
+					wcount =  (bus->slv_wr_size - bus->slv_wr_ind);
+
+					// TODO: replace :
+					if (wcount >= SMBUS_FIFO_SIZE)
+						wcount = SMBUS_FIFO_SIZE;
+
+					iowrite8((u8)wcount, bus->reg + NPCM_SMBTXF_CTL);
+
+					// with:
+					npcm_smb_set_fifo(bus, -1, wcount);
+					npcm_smb_write_to_fifo_slave(bus, wcount);
+
+					npcm_smb_clear_tx_fifo(bus);
+				}
+			}
+
+			// If all bytes were written, ignore further master read requests.
+			else {
+				if (bus->fifo_use == false) {
+					// Clear SDA Status bit
+					if (bus->slv_wr_ind != 0){
+						// Was writing
+#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
+
+						// Reset state for the remaining bytes transaction
+						bus->state = SMB_SLAVE_MATCH;
+
+						// Notify upper layer of transaction completion
+						// by overrind ind and info_p in next EXECUTE_FUNC()
+						ind = SMB_SLAVE_XMIT_MISSING_DATA_IND;
+						//  TBD : info_p = &(bus->slv_wr_ind);
+#else
+						npcm_smb_wr_byte(bus, 0xFF);
+#endif
+					} else {
+						u8 data;
+						// Was reading
+						(void)npcm_smb_rd_byte(bus, &data);
+					}
+				}
+				// write\read redundant bytes with FIFO (if there are any bytes to write)
+				else {
+					// Set threshold size
+					iowrite8((u8)SMBUS_FIFO_SIZE, bus->reg + NPCM_SMBTXF_CTL);
+
+					// TODO: replace with:
+					// npcm_smb_set_fifo(bus, -1, wcount);
+
+					npcm_smb_write_to_fifo_slave(bus, SMBUS_FIFO_SIZE);
+
+					npcm_smb_clear_tx_fifo(bus);
+				}
+				// Notify upper layer of transaction completion
+				npcm_smb_callback(bus, SMB_NO_DATA_IND, bus->slv_rd_ind);
+			} // All bytes sent/received
+		}
+		return 0;
+	} //SDAST
+	return 1;
+}
+
+static void npcm_smb_slave_abort(struct npcm_i2c *bus)
+{
+	volatile u8 temp;
+
+	// Disable int.
+	npcm_smb_int_enable(bus, false);
+
+	// Dummy read to clear interface.
+	temp = ioread8(bus->reg + NPCM_SMBSDA);
+
+	// Clear NMATCH and BER bits by writing 1s to them.
+	iowrite8(ioread8(bus->reg + NPCM_SMBST) | NPCM_SMBST_BER
+		 | NPCM_SMBST_NMATCH,
+		 bus->reg + NPCM_SMBST);
+
+
+	// Reset driver status
+	bus->state = SMB_IDLE;
+
+	// Disable SMB Module
+	iowrite8((ioread8(bus->reg + NPCM_SMBCTL2)	& ~SMBCTL2_ENABLE),
+			bus->reg + NPCM_SMBCTL2);
+
+	// Delay 100 us
+	udelay(100); // TBD must be out of int
+
+	// Enable SMB Module
+	npcm_smb_enable(bus);
+
+	// Enable int.
+	npcm_smb_int_enable(bus, true);
+}
+
+
+// currently slave IF only supports single byte operations.
+// in order to utilyze the npcm HW FIFO, the driver will ask for 16 bytes
+// at a time, pack them in buffer, and then transmit them all together
+// to the FIFO and onward to the bus .
+// NACK on read will be once reached to bus->adap->quirks->max_read_len
+// sending a NACK wherever the backend requests for it is not supported.
+// the next two functions allow reading to local buffer before writung it all
+// to the HW FIFO.
+// ret val: number of bytes read form the IF:
+
+static int npcm_i2c_slave_get_wr_buf(struct npcm_i2c *bus)
+{
+	int max_length = 0;
+	u8 value = 0;
+	int ret = bus->slv_wr_ind;
+	int i;
+
+	max_length = bus->adap.quirks->max_write_len;
+
+	if (bus->slv_wr_size == SMBUS_FIFO_SIZE) {
+		//printk("fifo full, ind=%d", bus->slv_wr_ind);
+		return 0; // you can't fill a cup which is already full
+	}
+
+	for (i = bus->slv_wr_ind; i < min(max_length, SMBUS_FIFO_SIZE); i++){
+		i2c_slave_event(bus->slave, I2C_SLAVE_READ_REQUESTED, &value);
+		bus->slv_wr_buf[i] = value;
+		bus->slv_wr_size++;
+		i2c_slave_event(bus->slave, I2C_SLAVE_READ_PROCESSED, &value);
+	}
+
+	bus->slv_wr_ind = 0;
+
+	// printk("I2C%d get wr buf %d bytes slv_wr_size\n\t  %x %x %x %x   %x %x %x %x    %x %x %x %x    %x %x %x %x\n",
+	//bus->num, bus->slv_wr_size,
+	//bus->slv_wr_buf[0], bus->slv_wr_buf[1], bus->slv_wr_buf[2], bus->slv_wr_buf[3],
+	//bus->slv_wr_buf[4], bus->slv_wr_buf[5], bus->slv_wr_buf[6], bus->slv_wr_buf[7],
+	//bus->slv_wr_buf[8], bus->slv_wr_buf[9], bus->slv_wr_buf[10], bus->slv_wr_buf[11],
+	//bus->slv_wr_buf[12], bus->slv_wr_buf[13], bus->slv_wr_buf[14], bus->slv_wr_buf[15]);
+
+	return SMBUS_FIFO_SIZE - ret;
+}
+
+
+static void npcm_i2c_slave_send_rd_buf(struct npcm_i2c *bus)
+{
+	int i;
+
+	for (i = 0; i < bus->slv_rd_ind; i++){
+		// printk("->   send 0x%x\n", bus->slv_rd_buf[i]);
+		i2c_slave_event(bus->slave, I2C_SLAVE_WRITE_RECEIVED, &bus->slv_rd_buf[i]);
+	}
+
+	bus->slv_rd_ind = 0;
+	bus->slv_rd_size = 32*1024;
+
+	// printk("I2C%d npcm_i2c_slave_send_rd_buf %d bytes\n", bus->num, i);
+
+}
+
+// handle un processed bytes
+static void npcm_i2c_slave_restart_or_stop_flush_wr_buf(struct npcm_i2c *bus)
+{
+	int i;
+
+	if (bus->operation != SMB_WRITE_OPER) {
+		// forget those bytes, master changed address:
+		// printk("npcm_i2c_slave_restart_or_stop_flush_wr_buf: forget");
+		bus->slv_wr_ind = 0;
+		bus->slv_wr_size = 0;
+	} else {
+		// shift remaining requested bytes to start of buffer. There they will
+		// wait till next read command will come. If write will come can forget those bytes.
+		bus->slv_wr_size = SMBUS_FIFO_SIZE - bus->slv_wr_ind;
+		for (i = bus->slv_wr_ind; i < SMBUS_FIFO_SIZE ; i++){
+			bus->slv_wr_buf[i - bus->slv_wr_ind] = bus->slv_wr_buf[i];
+		}
+
+		bus->slv_wr_ind = SMBUS_FIFO_SIZE - bus->slv_wr_ind;
+	}
+
+	npcm_i2c_slave_get_wr_buf(bus);
+
+	// printk("I2C%d npcm_i2c_slave_restart_or_stop_flush_wr_buf %d\n\t  %x %x %x %x   %x %x %x %x    %x %x %x %x    %x %x %x %x\n",
+	// bus->num, bus->slv_wr_size,
+	// bus->slv_wr_buf[0], bus->slv_wr_buf[1], bus->slv_wr_buf[2], bus->slv_wr_buf[3],
+	// bus->slv_wr_buf[4], bus->slv_wr_buf[5], bus->slv_wr_buf[6], bus->slv_wr_buf[7],
+	// bus->slv_wr_buf[8], bus->slv_wr_buf[9], bus->slv_wr_buf[10], bus->slv_wr_buf[11],
+	// bus->slv_wr_buf[12], bus->slv_wr_buf[13], bus->slv_wr_buf[14], bus->slv_wr_buf[15]);
+
+	bus->operation = SMB_WRITE_OPER;
+}
+
+
+
+
+
+static int  npcm_i2c_reg_slave(struct i2c_client *client)
+{
+	unsigned long lock_flags;
+	struct npcm_i2c *bus = i2c_get_adapdata(client->adapter);
+
+	bus->slave = client;
+
+	if (!bus->slave)	{
+		return -EINVAL;
+	}
+
+	if (client->flags & I2C_CLIENT_TEN)
+		return -EAFNOSUPPORT;
+
+	spin_lock_irqsave(&bus->lock, lock_flags);
+
+
+	bus->PEC_use = false;
+	bus->PEC_mask = 0;
+	bus->master_or_slave = SMB_SLAVE;
+
+	// printk("I2C%d register slave SA=0x%x, PEC=%d, BA = 0x%p\n", bus->num,
+	//		client->addr, bus->PEC_use, bus->reg);
+
+	//if (0 != npcm_smb_add_slave_addr(bus, client->addr, bus->PEC_use)) {
+	//	// printk("I2C%d can't add addr", bus->num);
+	//	return -EINVAL;
+	//}
+
+
+	// printk("slave irq = %d\n", bus->irq);
+
+
+	// Disable ints and select bank 0 for address 3 to ...
+	npcm_smb_select_bank(bus, SMB_BANK_0);
+
+	// printk("I2C%d config addr 1 only\n", bus->num);
+
+	// Set and enable the address
+	iowrite8(client->addr, bus->reg + NPCM_SMBADDR1);
+
+	// printk("I2C%d config addr 1 done\n", bus->num);
+
+	// return to bank 1 and enable ints (if needed)
+	npcm_smb_select_bank(bus, SMB_BANK_1);
+
+	npcm_smb_slave_enable_l(bus, SMB_SLAVE_ADDR1, client->addr, true);
+
+	//npcm_i2c_slave_get_wr_buf(bus);
+
+	//npcm_i2c_slave_get_rd_buf(bus);
+
+	spin_unlock_irqrestore(&bus->lock, lock_flags);
+
+	return 0;
+}
+
+static int  npcm_i2c_unreg_slave(struct i2c_client *client)
+{
+	struct npcm_i2c *bus = client->adapter->algo_data;
+	unsigned long lock_flags;
+
+	spin_lock_irqsave(&bus->lock, lock_flags);
+	if (!bus->slave) {
+		spin_unlock_irqrestore(&bus->lock, lock_flags);
+		return -EINVAL;
+	}
+
+
+	// Turn off slave mode.
+	npcm_smb_remove_slave_addr(bus, client->addr);
+
+	bus->slave = NULL;
+	spin_unlock_irqrestore(&bus->lock, lock_flags);
+
+	return 0;
+}
+#endif // CONFIG_I2C_SLAVE
+
+
+static void npcm_smb_master_fifo_read(struct npcm_i2c *bus)
+{
+	int rcount;
+	int fifo_bytes;
+	enum smb_state_ind ind = SMB_MASTER_DONE_IND;
+
+	fifo_bytes = npcm_smb_get_fifo_fullness(bus);
+
+	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);
+
+
+	if ((rcount - fifo_bytes) <= 0) {
+		// last bytes are about to be read - end of transaction.
+		// Stop should be set before reading last byte.
+
+		NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_READ4);
+
+
+		if (npcm_smb_get_PEC(bus) != 0)
+			ind = SMB_MASTER_PEC_ERR_IND;
+
+		bus->state = SMB_STOP_PENDING;
+		bus->stop_ind = ind;
+
+		npcm_smb_eob_int(bus, true);
+		npcm_smb_master_stop(bus);
+
+		npcm_smb_read_from_fifo(bus, fifo_bytes);
+
+	} else {
+
+		NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_READ3);
+		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);
+	pdebug_lvl2(bus, "handler wr");
+	if (bus->fifo_use)
+		npcm_smb_clear_tx_fifo(bus); // clear the TX fifo status bit
+
+	// Master write operation - last byte handling
+	if (bus->wr_ind == bus->wr_size) {
+		pdebug_lvl2(bus, "last wr");
+		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);
+
+			bus->state = SMB_STOP_PENDING;
+			bus->stop_ind = SMB_MASTER_DONE_IND;
+
+			// 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);
+
+		} 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);
+					// set fifo to read one byte:
+					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);
+
+			// Next int will occur on read
+			bus->operation = SMB_READ_OPER;
+
+
+
+			// send the slave address in read direction
+			npcm_smb_wr_byte(bus, bus->dest_addr | 0x1);
+
+		}
+	} 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);
+			//   WHY NOT: npcm_smb_write_to_fifo_master(bus, wcount);
+			npcm_smb_write_to_fifo_master(bus, wcount);
+		}
+	}
+}
+
+static void npcm_smb_int_master_handler_read(struct npcm_i2c *bus)
+{
+	u16 block_extra_bytes_size;
+	u8 data;
+
+	// Master read operation (pure read or following a write operation).
+	NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_READ);
+	pdebug_lvl2(bus, "handler rd");
+
+	if (bus->rd_ind > bus->rd_size)
+		pdebug(bus, "master rd error");
+
+	// 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_extra_bytes_size = 1;
+	if (bus->PEC_use)
+		block_extra_bytes_size++;
+
+	// 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 == 0) { //first byte handling:
+		// in block protocol first byte is the size
+		NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_READ1);
+		if (bus->read_block_use) {
+
+			// first byte in block protocol is the size:
+			npcm_smb_rd_byte(bus, &data);
+
+			if (bus->PEC_use)
+				data += 1;
+
+			// if slave returned illgal size. read up to 32 bytes.
+			if (data >= I2C_SMBUS_BLOCK_MAX)
+				data = I2C_SMBUS_BLOCK_MAX;
+
+			// is data is 0 -> not supported. read at least one byte
+			if (data == 0)
+				data = 1;
+
+			bus->rd_size = data + block_extra_bytes_size;
+
+			// First byte indicates length in block protocol
+			bus->rd_buf[bus->rd_ind++] = data;
+
+			// clear RX FIFO interrupt status:
+			if (bus->fifo_use) {
+				iowrite8(NPCM_SMBFIF_CTS_RXF_TXE |
+					 ioread8(bus->reg +
+						 NPCM_SMBFIF_CTS),
+					 bus->reg + NPCM_SMBFIF_CTS);
+				//npcm_smb_clear_fifo_int(bus);
+			}
+
+			npcm_smb_set_fifo(bus, (bus->rd_size - 1), -1);
+
+			// Reset stall
+			npcm_smb_stall_after_start(bus, false);
+		} else {
+
+			npcm_smb_clear_tx_fifo(bus);
+			npcm_smb_master_fifo_read(bus);
+
+		}
+
+	} else {
+
+		if (bus->rd_size == block_extra_bytes_size &&
+		    bus->read_block_use) {
+		    	bus->state = SMB_STOP_PENDING;
+			bus->stop_ind = SMB_BLOCK_BYTES_ERR_IND;
+			npcm_smb_eob_int(bus, true);
+			npcm_smb_master_stop(bus);
+			npcm_smb_read_from_fifo(bus, npcm_smb_get_fifo_fullness(bus));
+
+		} else {
+			NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_READ2);
+			npcm_smb_master_fifo_read(bus);
+		}
+
+	}
+
+}
+
+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))) {
+		pdebug_lvl2(bus, "NACK");
+		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
+		bus->stop_ind = SMB_NACK_IND;
+		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
+		NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_BER);
+		if (npcm_smb_is_master(bus)) {
+			// Only current master is allowed to issue stop
+			bus->stop_ind = SMB_BUS_ERR_IND;
+			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
+				pdebug(bus, "retry-BER");
+				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);
+		pdebug_lvl2(bus, "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))) {
+		pdebug_lvl2(bus, "stall");
+		NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_STALL);
+
+		// Check for Quick Command SMBus protocol
+		if (npcm_smb_is_quick(bus)) {
+			// Update status
+			bus->state = SMB_STOP_PENDING;
+			bus->stop_ind = SMB_MASTER_DONE_IND;
+			npcm_smb_eob_int(bus, true);
+			npcm_smb_master_stop(bus);
+
+		} else if ((bus->rd_size == 1) && !bus->read_block_use){
+			// 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_empty(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:
+			bus->stop_ind = SMB_WAKE_UP_IND;
+			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 in write direction:
+				npcm_smb_wr_byte(bus, bus->dest_addr);
+
+				// Generate a STOP condition
+				npcm_smb_master_stop(bus);
+			}
+			return 0;
+		}
+	}
+
+	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;
+
+	npcm_smb_select_bank(bus, SMB_BANK_0);
+	iowrite8(ioread8(bus->reg + NPCM_SMBFIF_CTL) & ~NPCM_SMBFIF_CTL_FIFO_EN,
+		 bus->reg + NPCM_SMBFIF_CTL);
+
+	npcm_smb_select_bank(bus, SMB_BANK_1);
+
+	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;
+	bus->int_time_stamp = 0;
+
+	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++;
+
+	if (bus->master_or_slave == SMB_MASTER)	{
+ 		bus->int_time_stamp = jiffies;
+		npcm_smb_int_master_handler(bus);
+		return IRQ_HANDLED;
+	}
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	else if (!npcm_smb_int_slave_handler(bus)) {
+		return IRQ_HANDLED;
+	}
+#endif
+
+	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)
+{
+	u8 smbfif_cts;
+
+	//
+	// Allow only if bus is not busy
+	//
+	if (bus->state != SMB_IDLE) {
+		dev_err(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;
+		npcm_smb_select_bank(bus, SMB_BANK_0);
+		iowrite8(ioread8(bus->reg + NPCM_SMBFIF_CTL) |
+			 NPCM_SMBFIF_CTL_FIFO_EN, bus->reg + NPCM_SMBFIF_CTL);
+		npcm_smb_select_bank(bus, SMB_BANK_1);
+	} 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->PEC_mask = 0;
+	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);
+
+		smbfif_cts = ioread8(bus->reg + NPCM_SMBFIF_CTS);
+
+		// clear FIFO and relevant status bits.
+		iowrite8((smbfif_cts & NPCM_SMBFIF_CTS_SLVRSTR) |
+			 NPCM_SMBFIF_CTS_CLR_FIFO,
+			 bus->reg + NPCM_SMBFIF_CTS);
+
+		// and enable it (if slave was enabled don't disable).
+
+		iowrite8((smbfif_cts & NPCM_SMBFIF_CTS_SLVRSTR) |
+			 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;
+	pdebug_lvl2(bus, "xmit ");
+
+
+	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;
+	int timeout = bus->adap.timeout;
+
+	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;
+	bus->event_log_prev = bus->event_log;
+	bus->int_time_stamp = 0;
+
+	if(ioread8(bus->reg + NPCM_SMBCST) & NPCM_SMBCST_BB){
+		pdebug(bus, " xfer bus busy ");
+		npcm_smb_master_abort(bus); // recover the bus
+		spin_unlock_irqrestore(&bus->lock, flags);
+		return -EAGAIN;
+	}
+
+
+	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(nread == 0 && nwrite == 0)
+		timeout = msecs_to_jiffies(1);
+	else if (bus->read_block_use)
+		timeout = usecs_to_jiffies((2 + I2C_SMBUS_BLOCK_MAX + nwrite)*1300);
+	else
+		// resonable assumption which leaves time for clock stretching.
+		timeout = usecs_to_jiffies((2 + nread + nwrite)*1300);
+
+
+	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,
+							timeout);
+
+		if (time_left == 0 && bus->cmd_err == -EPERM) {
+			NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_TO);
+			pdebug(bus, "xfer TO");
+
+			// timeout while bus is busy:
+			npcm_smb_master_abort(bus); // don't stop in the middle.
+
+			// Reset driver status
+			bus->state = SMB_IDLE;
+
+			ret = -ETIMEDOUT;
+		} else {
+			ret = bus->cmd_err;
+		}
+	} else {
+#if defined(CONFIG_I2C_DEBUG_BUS)
+		pdebug(bus, "busy");
+#endif
+	}
+
+	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 | I2C_FUNC_SLAVE;
+}
+
+static const struct i2c_adapter_quirks npcm_i2c_quirks = {
+	.max_read_len = 32768,
+	.max_write_len = 32768,
+	.max_num_msgs = 2,
+	.flags = I2C_AQ_COMB_WRITE_THEN_READ
+};
+
+
+static const struct i2c_algorithm npcm_i2c_algo = {
+	.master_xfer = npcm_i2c_master_xfer,
+	.functionality = npcm_i2c_functionality,
+#if IS_ENABLED(CONFIG_I2C_SLAVE)
+	.reg_slave	= npcm_i2c_reg_slave,
+	.unreg_slave	= npcm_i2c_unreg_slave,
+#endif // CONFIG_I2C_SLAVE
+};
+
+static struct i2c_bus_recovery_info npcm_i2c_recovery = {
+	.recover_bus = npcm_smb_recovery,
+	.get_scl = npcm_smb_get_SCL,
+	.get_sda = npcm_smb_get_SDA,
+};
+
+static int  npcm_i2c_probe_bus(struct platform_device *pdev)
+{
+	struct npcm_i2c *bus;
+	struct i2c_adapter *adap;
+	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);
+
+	adap = &bus->adap;
+	adap->owner = THIS_MODULE;
+	adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD | I2C_CLIENT_SLAVE;
+	adap->retries = 0;
+	adap->timeout = msecs_to_jiffies(25);
+	adap->algo = &npcm_i2c_algo;
+	adap->quirks = &npcm_i2c_quirks;
+	adap->algo_data = bus;
+	adap->dev.parent = &pdev->dev;
+	adap->dev.of_node = pdev->dev.of_node;
+	adap->bus_recovery_info = &npcm_i2c_recovery;
+	adap->nr = pdev->id;
+
+	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 = devm_request_irq(&pdev->dev, 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;
+	}
+
+	i2c_set_adapdata(adap, bus);
+
+
+	snprintf(bus->adap.name, sizeof(bus->adap.name), "Nuvoton i2c");
+
+	ret = i2c_add_numbered_adapter(&bus->adap);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "I2C%d: i2c_add_numbered_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 <avi.fishman@gmail.com>");
+MODULE_AUTHOR("Tali Perry <tali.perry@nuvoton.com>");
+MODULE_DESCRIPTION("Nuvoton I2C Bus Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(I2C_VERSION);
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [linux dev-4.19 05/11] dt-binding: bmc: Add NPCM7xx LPC BPC documentation
  2018-12-16 11:00 [linux dev-4.19 00/11] Add NPCM7xx patches to dev-4.19 Tomer Maimon
                   ` (3 preceding siblings ...)
  2018-12-16 11:00 ` [linux dev-4.19 04/11] i2c: npcm: driver for Poleg " Tomer Maimon
@ 2018-12-16 11:00 ` Tomer Maimon
  2018-12-16 11:00 ` [linux dev-4.19 06/11] misc: npcm7xx-lpc-bpc: add NPCM7xx BIOS post code driver Tomer Maimon
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Tomer Maimon @ 2018-12-16 11:00 UTC (permalink / raw)
  To: joel; +Cc: openbmc, Tomer Maimon

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 <tmaimon77@gmail.com>
---
 .../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 = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
+	};
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [linux dev-4.19 06/11] misc: npcm7xx-lpc-bpc: add NPCM7xx BIOS post code driver
  2018-12-16 11:00 [linux dev-4.19 00/11] Add NPCM7xx patches to dev-4.19 Tomer Maimon
                   ` (4 preceding siblings ...)
  2018-12-16 11:00 ` [linux dev-4.19 05/11] dt-binding: bmc: Add NPCM7xx LPC BPC documentation Tomer Maimon
@ 2018-12-16 11:00 ` Tomer Maimon
  2018-12-16 11:00 ` [linux dev-4.19 07/11] dt-binding: bmc: add npcm7xx pci mailbox document Tomer Maimon
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Tomer Maimon @ 2018-12-16 11:00 UTC (permalink / raw)
  To: joel; +Cc: openbmc, Tomer Maimon

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 <tmaimon77@gmail.com>
---
 drivers/misc/Kconfig           |   8 +
 drivers/misc/Makefile          |   1 +
 drivers/misc/npcm7xx-lpc-bpc.c | 392 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 401 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..2d5722b8286f
--- /dev/null
+++ b/drivers/misc/npcm7xx-lpc-bpc.c
@@ -0,0 +1,392 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2014-2018 Nuvoton Technology corporation.
+
+#include <linux/fs.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/miscdevice.h>
+#include <linux/poll.h>
+
+#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;
+	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 <tomer.maimon@nuvoton.com>");
+MODULE_DESCRIPTION("Linux driver to control NPCM7XX LPC BIOS post code monitoring");
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [linux dev-4.19 07/11] dt-binding: bmc: add npcm7xx pci mailbox document
  2018-12-16 11:00 [linux dev-4.19 00/11] Add NPCM7xx patches to dev-4.19 Tomer Maimon
                   ` (5 preceding siblings ...)
  2018-12-16 11:00 ` [linux dev-4.19 06/11] misc: npcm7xx-lpc-bpc: add NPCM7xx BIOS post code driver Tomer Maimon
@ 2018-12-16 11:00 ` Tomer Maimon
  2018-12-16 11:00 ` [linux dev-4.19 08/11] misc: mbox: add npcm7xx pci mailbox driver Tomer Maimon
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Tomer Maimon @ 2018-12-16 11:00 UTC (permalink / raw)
  To: joel; +Cc: openbmc, Tomer Maimon

Added device tree binding documentation for Nuvoton BMC
NPCM7XX PCI mailbox.

Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
---
 .../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 = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+	};
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [linux dev-4.19 08/11] misc: mbox: add npcm7xx pci mailbox driver
  2018-12-16 11:00 [linux dev-4.19 00/11] Add NPCM7xx patches to dev-4.19 Tomer Maimon
                   ` (6 preceding siblings ...)
  2018-12-16 11:00 ` [linux dev-4.19 07/11] dt-binding: bmc: add npcm7xx pci mailbox document Tomer Maimon
@ 2018-12-16 11:00 ` Tomer Maimon
  2018-12-16 11:00 ` [linux dev-4.19 09/11] dt-binding: net: document NPCM7xx EMC DT bindings Tomer Maimon
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Tomer Maimon @ 2018-12-16 11:00 UTC (permalink / raw)
  To: joel; +Cc: openbmc, Tomer Maimon

Add Nuvoton BMC NPCM7XX PCI Mailbox driver.

Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
---
 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..0ced167d1f01 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..4720510d4160
--- /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 <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#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 (count + *ppos > 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 (count + *ppos > 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 <tomer.maimon@nuvoton.com>");
+MODULE_DESCRIPTION("NPCM7XX mailbox device driver");
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [linux dev-4.19 09/11] dt-binding: net: document NPCM7xx EMC DT bindings
  2018-12-16 11:00 [linux dev-4.19 00/11] Add NPCM7xx patches to dev-4.19 Tomer Maimon
                   ` (7 preceding siblings ...)
  2018-12-16 11:00 ` [linux dev-4.19 08/11] misc: mbox: add npcm7xx pci mailbox driver Tomer Maimon
@ 2018-12-16 11:00 ` Tomer Maimon
  2018-12-16 11:00 ` [linux dev-4.19 10/11] net: npcm: add NPCM7xx Ethernet MAC controller Tomer Maimon
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Tomer Maimon @ 2018-12-16 11:00 UTC (permalink / raw)
  To: joel; +Cc: openbmc, Tomer Maimon, Avi Fishman

Added device tree binding documentation for
Nuvoton NPCM7xx Ethernet MAC Controller (EMC).

Signed-off-by: Avi Fishman <avifishman70@gmail.com>
---
 .../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 = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>,
+	             <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
+	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
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [linux dev-4.19 10/11] net: npcm: add NPCM7xx Ethernet MAC controller
  2018-12-16 11:00 [linux dev-4.19 00/11] Add NPCM7xx patches to dev-4.19 Tomer Maimon
                   ` (8 preceding siblings ...)
  2018-12-16 11:00 ` [linux dev-4.19 09/11] dt-binding: net: document NPCM7xx EMC DT bindings Tomer Maimon
@ 2018-12-16 11:00 ` Tomer Maimon
  2018-12-16 11:00 ` [linux dev-4.19 11/11] arm: npcm: add configuration for EVB based NPCM7xx BMC Tomer Maimon
  2019-01-03  6:11 ` [linux dev-4.19 00/11] Add NPCM7xx patches to dev-4.19 Joel Stanley
  11 siblings, 0 replies; 13+ messages in thread
From: Tomer Maimon @ 2018-12-16 11:00 UTC (permalink / raw)
  To: joel; +Cc: openbmc, Tomer Maimon, Avi Fishman

Add Nuvoton BMC NPCM7xx Ethernet MAC controller (EMC) driver.

Signed-off-by: Avi Fishman <avifishman70@gmail.com>
---
 drivers/net/ethernet/nuvoton/Kconfig       |   17 +-
 drivers/net/ethernet/nuvoton/Makefile      |    2 +
 drivers/net/ethernet/nuvoton/npcm7xx_emc.c | 2099 ++++++++++++++++++++++++++++
 3 files changed, 2117 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..8d45a151be57 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..1a45eb80af97
--- /dev/null
+++ b/drivers/net/ethernet/nuvoton/npcm7xx_emc.c
@@ -0,0 +1,2099 @@
+/*
+ * Copyright (c) 2014-2017 Nuvoton Technology corporation.
+ *
+ * Released under the GPLv2 only.
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/platform_device.h>
+#include <linux/gfp.h>
+#include <linux/kthread.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/ctype.h>
+#include <linux/debugfs.h>
+
+#include <linux/clk.h>
+
+#include <linux/of.h>
+#include <linux/of_net.h>
+#include <linux/of_device.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#include <linux/if_ether.h>
+
+#include <net/ip.h>
+#include <net/ncsi.h>
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *npcm7xx_fs_dir = NULL;
+#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/Recieve 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;
+	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(&ether->lock);
+
+	if (!is_locked)
+		spin_lock_irqsave(&ether->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,
+						      &ether->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(&ether->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, "emc_dump_buf = kmalloc(PAGE_SIZE, "
+				    "GFP_KERNEL) 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;
+		}
+		printk("%s", tmp_buf);
+		kfree(emc_dump_buf);
+	}
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/seq_file.h>
+
+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 == NULL) {
+		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_printf(sf, "Ask to reset the module\n");
+	spin_lock_irqsave(&ether->lock, flags);
+	writel(0,  (ether->reg + REG_MIEN));
+	spin_unlock_irqrestore(&ether->lock, flags);
+	ether->need_reset = 1;
+	napi_schedule(&ether->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(&ether->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(&ether->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(&ether->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(&ether->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(&ether->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(&ether->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(&ether->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 == NULL)
+		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 == NULL) {
+		ether->tdesc = (struct npcm7xx_txbd *)
+				dma_alloc_coherent(&pdev->dev,
+						   sizeof(struct npcm7xx_txbd) *
+						   TX_QUEUE_LEN,
+						   &ether->tdesc_phys,
+						   GFP_KERNEL);
+
+		if (!ether->tdesc) {
+			dev_err(&pdev->dev, "Failed to allocate memory for "
+					    "tx desc\n");
+			return -ENOMEM;
+		}
+	}
+
+	if (ether->rdesc == NULL) {
+		ether->rdesc = (struct npcm7xx_rxbd *)
+				dma_alloc_coherent(&pdev->dev,
+						   sizeof(struct npcm7xx_rxbd) *
+						   RX_QUEUE_LEN,
+						   &ether->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) == NULL) {
+			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 != NULL) {
+			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 != NULL) {
+			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 choos (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(&ether->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(&ether->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(&ether->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)
+{
+
+	/* reser ENAC 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(10);
+
+	free_irq(ether->txirq, dev);
+	free_irq(ether->rxirq, dev);
+
+	netif_stop_queue(dev);
+	napi_disable(&ether->napi);
+
+	npcm7xx_free_desc(dev, true);
+
+	if (ether->dump_buf) {
+		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 &ether->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 == NULL)
+			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(&ether->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(&ether->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)
+			writel(reg_mien | ENTDU, (ether->reg + REG_MIEN)); /* Enable TDU interrupt */
+
+		ether->tx_tdu++;
+		netif_stop_queue(dev);
+	}
+
+	spin_unlock_irqrestore(&ether->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(&ether->lock, flags);
+		writel(0, (ether->reg + REG_MIEN)); /* disable any interrupt */
+		spin_unlock_irqrestore(&ether->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(&ether->lock, flags);
+		reg_mien = readl((ether->reg + REG_MIEN));
+		if (reg_mien & ENTDU)
+			writel(reg_mien & (~ENTDU), (ether->reg + REG_MIEN)); /* Disable TDU interrupt */
+
+		spin_unlock_irqrestore(&ether->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(&ether->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(&ether->lock, flags);
+		writel(0, (ether->reg + REG_MIEN)); /* disable any interrupt */
+		spin_unlock_irqrestore(&ether->lock, flags);
+		ether->need_reset = 1;
+		napi_schedule(&ether->napi);
+		return IRQ_HANDLED;
+	}
+
+	if (unlikely(status & (MISTA_RXOV | MISTA_RDU))) {
+		/* filter out all received packets until we have enough avaiable 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(&ether->lock, flags);
+#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG
+				npcm7xx_info_print(dev);
+#endif
+				writel(0,  (ether->reg + REG_MIEN));
+				spin_unlock_irqrestore(&ether->lock, flags);
+				ether->need_reset = 1;
+				    napi_schedule(&ether->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 == 0) && ((status & MISTA_RXGD) == 0))
+		dev_err(&pdev->dev, "emc rx MISTA status=0x%08X\n", status);
+
+	spin_lock_irqsave(&ether->lock, flags);
+	writel(readl((ether->reg + REG_MIEN)) & ~ENRXGD,  (ether->reg + REG_MIEN));
+	spin_unlock_irqrestore(&ether->lock, flags);
+	napi_schedule(&ether->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 comming 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(&ether->lock, flags);
+	npcm7xx_clean_tx(dev, false);
+	spin_unlock_irqrestore(&ether->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;
+		}
+		rxbd->reserved = status; /* for debug puposes we save the previous value */
+		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(&ether->lock, flags);
+		writel(readl((ether->reg + REG_MIEN)) | ENRXGD,  (ether->reg + REG_MIEN));
+		spin_unlock_irqrestore(&ether->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 comming 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(&ether->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 == NULL)
+		return -ENODEV;
+
+	dev_info(&ether->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 == NULL)
+		return -ENODEV;
+
+	dev_info(&ether->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 = &ether->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 == NULL) {
+		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 == NULL) {
+		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 == NULL) {
+		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(&ether->lock);
+
+	netif_napi_add(dev, &ether->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(&ether->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
+	if (ether->dbgfs_dir)
+		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);
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [linux dev-4.19 11/11] arm: npcm: add configuration for EVB based NPCM7xx BMC
  2018-12-16 11:00 [linux dev-4.19 00/11] Add NPCM7xx patches to dev-4.19 Tomer Maimon
                   ` (9 preceding siblings ...)
  2018-12-16 11:00 ` [linux dev-4.19 10/11] net: npcm: add NPCM7xx Ethernet MAC controller Tomer Maimon
@ 2018-12-16 11:00 ` Tomer Maimon
  2019-01-03  6:11 ` [linux dev-4.19 00/11] Add NPCM7xx patches to dev-4.19 Joel Stanley
  11 siblings, 0 replies; 13+ messages in thread
From: Tomer Maimon @ 2018-12-16 11:00 UTC (permalink / raw)
  To: joel; +Cc: openbmc, Tomer Maimon

add configuration definition for evaluation board based
NPCM7xx (Poleg) BMC.

Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
---
 arch/arm/configs/npcm7xx_defconfig | 106 +++++++++++++++++++++++++++++++++++++
 1 file changed, 106 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..f3cefe303a21
--- /dev/null
+++ b/arch/arm/configs/npcm7xx_defconfig
@@ -0,0 +1,106 @@
+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_ARCH_NPCM=y
+CONFIG_ARCH_NPCM7XX=y
+CONFIG_SMP=y
+CONFIG_VMSPLIT_3G_OPT=y
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_ARM_CRYPTO=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_DEFAULT_DEADLINE=y
+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_NPCM7XX_KCS_IPMI_BMC=y
+CONFIG_HW_RANDOM=y
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_NPCM7XX=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_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_MASS_STORAGE=m
+CONFIG_MMC=y
+CONFIG_MMC_SDHCI=y
+CONFIG_IIO=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_CRYPTO_CBC=y
+CONFIG_CRYPTO_CRC32C=y
+CONFIG_CRYPTO_DEFLATE=y
+CONFIG_CRYPTO_LZO=y
+CONFIG_CRYPTO_USER_API_SKCIPHER=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
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [linux dev-4.19 00/11] Add NPCM7xx patches to dev-4.19
  2018-12-16 11:00 [linux dev-4.19 00/11] Add NPCM7xx patches to dev-4.19 Tomer Maimon
                   ` (10 preceding siblings ...)
  2018-12-16 11:00 ` [linux dev-4.19 11/11] arm: npcm: add configuration for EVB based NPCM7xx BMC Tomer Maimon
@ 2019-01-03  6:11 ` Joel Stanley
  11 siblings, 0 replies; 13+ messages in thread
From: Joel Stanley @ 2019-01-03  6:11 UTC (permalink / raw)
  To: Tomer Maimon; +Cc: OpenBMC Maillist

Hi Tomer,

On Sun, 16 Dec 2018 at 21:30, Tomer Maimon <tmaimon77@gmail.com> wrote:
>
> Add the following NPCM7xx patches to dev-4.19:

I had some email issues where not all of these patches arrived in my
inbox. They are on patchwork, so I was able to review them.

>
> 1. NPCM7xx FIU driver(SPI-NOR).
> 2. NPCM7xx I2C driver.

The i2c does not build, so I did not include that patch:

../drivers/i2c/busses/i2c-npcm7xx.c:9:10: fatal error:
linux/clk/nuvoton.h: No such file or directory
 #include <linux/clk/nuvoton.h>
          ^~~~~~~~~~~~~~~~~~~~~

Please re-send it once you've tested that it applies and builds. It
would be great if you had also tested it on hardware before sending.

> 3. NPCM7xx BIOS post code driver.

This introduces a warning:

../drivers/misc/npcm7xx-lpc-bpc.c: In function ‘npcm7xx_bpc_irq’:
../drivers/misc/npcm7xx-lpc-bpc.c:150:25: warning: ‘last_addr_bit’ may
be used uninitialized in this function [-Wmaybe-uninitialized]
   if (lpc_bpc->en_dwcap && last_addr_bit) {
       ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~

I would strongly encourage you to look at the aspeed snoop driver and
adopt a common userspace interface for this code. It's important to do
that before userspace code is written that depends on this interface.

> 4. NPCM7xx PCI mailbox driver.

I didn't merge this one has it contained a bad size check. Please take
a look at npcm7xx_mbox_write:

+       if (count + *ppos > mbox->max_buf_size)
+               return -EINVAL;

If you resend just the mailbox driver (and bindings) I will merge them
separately.

> 5. NPCM7xx ETHERNET MAC Controller driver.
> 6. NPCM7xx defconfig.

I found some warnings and errors when building your patches

The device tree updates cause this warning when building:

  DTC     arch/arm/boot/dts/nuvoton-npcm750-evb.dtb
arch/arm/boot/dts/nuvoton-npcm750-evb.dtb: Warning (pci_bridge):
/ahb/axi-pcie@e1000000: node name is not "pci" or "pcie"

The ADC driver causes this warning:

../drivers/iio/adc/npcm_adc.c: In function ‘npcm_adc_probe’:
../drivers/iio/adc/npcm_adc.c:301:9: warning: ‘ret’ may be used
uninitialized in this function [-Wmaybe-uninitialized]
  return ret;
         ^~~

I took a look at the driver and it is clear it needs some further work. eg:

                if (PTR_ERR(info->vref) != -ENODEV) {
                        goto err_disable_clk;
                        return PTR_ERR(info->vref);
                }

and:

        if (of_device_is_compatible(np, "nuvoton,npcm750-adc")) {

Can you please take another look at the series before we merge it?
Here's a checklist:

1. Build the code
2. Check for warnings
3. Run the patches through checkpatch.pl

Thanks,

Joel

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2019-01-03  6:11 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-12-16 11:00 [linux dev-4.19 00/11] Add NPCM7xx patches to dev-4.19 Tomer Maimon
2018-12-16 11:00 ` [linux dev-4.19 01/11] dt-binding: mtd: add NPCM FIU controller Tomer Maimon
2018-12-16 11:00 ` [linux dev-4.19 02/11] mtd: spi-nor: add NPCM FIU controller driver Tomer Maimon
2018-12-16 11:00 ` [linux dev-4.19 03/11] dt-bindings: i2c: npcm7xx: add binding for i2c controller Tomer Maimon
2018-12-16 11:00 ` [linux dev-4.19 04/11] i2c: npcm: driver for Poleg " Tomer Maimon
2018-12-16 11:00 ` [linux dev-4.19 05/11] dt-binding: bmc: Add NPCM7xx LPC BPC documentation Tomer Maimon
2018-12-16 11:00 ` [linux dev-4.19 06/11] misc: npcm7xx-lpc-bpc: add NPCM7xx BIOS post code driver Tomer Maimon
2018-12-16 11:00 ` [linux dev-4.19 07/11] dt-binding: bmc: add npcm7xx pci mailbox document Tomer Maimon
2018-12-16 11:00 ` [linux dev-4.19 08/11] misc: mbox: add npcm7xx pci mailbox driver Tomer Maimon
2018-12-16 11:00 ` [linux dev-4.19 09/11] dt-binding: net: document NPCM7xx EMC DT bindings Tomer Maimon
2018-12-16 11:00 ` [linux dev-4.19 10/11] net: npcm: add NPCM7xx Ethernet MAC controller Tomer Maimon
2018-12-16 11:00 ` [linux dev-4.19 11/11] arm: npcm: add configuration for EVB based NPCM7xx BMC Tomer Maimon
2019-01-03  6:11 ` [linux dev-4.19 00/11] Add NPCM7xx patches to dev-4.19 Joel Stanley

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.