* [linux dev-4.19 01/15] clk: nuvoton: add npcm750 clock function prototype initialization
2019-01-14 13:06 [linux dev-4.19 00/15] dd NPCM7xx patches to dev-4.19 Tomer Maimon
@ 2019-01-14 13:06 ` Tomer Maimon
2019-01-14 13:06 ` [linux dev-4.19 02/15] dt-binding: mtd: add NPCM FIU controller Tomer Maimon
` (14 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Tomer Maimon @ 2019-01-14 13:06 UTC (permalink / raw)
To: joel; +Cc: openbmc, Tomer Maimon
Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
---
include/linux/clk/nuvoton.h | 9 +++++++++
1 file changed, 9 insertions(+)
create mode 100644 include/linux/clk/nuvoton.h
diff --git a/include/linux/clk/nuvoton.h b/include/linux/clk/nuvoton.h
new file mode 100644
index 000000000000..9a474d691786
--- /dev/null
+++ b/include/linux/clk/nuvoton.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2014-2019 Nuvoton Technology corporation. */
+
+#ifndef __LINUX_CLK_NUVOTON_H_
+#define __LINUX_CLK_NUVOTON_H_
+
+void nuvoton_npcm750_clock_init(void);
+
+#endif
--
2.14.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [linux dev-4.19 02/15] dt-binding: mtd: add NPCM FIU controller
2019-01-14 13:06 [linux dev-4.19 00/15] dd NPCM7xx patches to dev-4.19 Tomer Maimon
2019-01-14 13:06 ` [linux dev-4.19 01/15] clk: nuvoton: add npcm750 clock function prototype initialization Tomer Maimon
@ 2019-01-14 13:06 ` Tomer Maimon
2019-01-14 13:06 ` [linux dev-4.19 03/15] mtd: spi-nor: add NPCM FIU controller driver Tomer Maimon
` (13 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Tomer Maimon @ 2019-01-14 13:06 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] 19+ messages in thread
* [linux dev-4.19 03/15] mtd: spi-nor: add NPCM FIU controller driver
2019-01-14 13:06 [linux dev-4.19 00/15] dd NPCM7xx patches to dev-4.19 Tomer Maimon
2019-01-14 13:06 ` [linux dev-4.19 01/15] clk: nuvoton: add npcm750 clock function prototype initialization Tomer Maimon
2019-01-14 13:06 ` [linux dev-4.19 02/15] dt-binding: mtd: add NPCM FIU controller Tomer Maimon
@ 2019-01-14 13:06 ` Tomer Maimon
2019-01-15 23:53 ` Joel Stanley
2019-01-14 13:06 ` [linux dev-4.19 04/15] dt-bindings: i2c: npcm7xx: add binding for i2c controller Tomer Maimon
` (12 subsequent siblings)
15 siblings, 1 reply; 19+ messages in thread
From: Tomer Maimon @ 2019-01-14 13:06 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 37775fc09e09..5af9d5f77076 100644
--- a/drivers/mtd/spi-nor/Kconfig
+++ b/drivers/mtd/spi-nor/Kconfig
@@ -75,6 +75,14 @@ config SPI_HISI_SFC
help
This enables support for hisilicon SPI-NOR flash controller.
+config SPI_NPCM_FIU
+ tristate "NPCM FLASH Interface unit(FIU) controller "
+ depends on ARCH_NPCM || COMPILE_TEST
+ help
+ This enables support for the FLASH Interface unit(FIU) controller.
+ This driver does not support generic SPI. The implementation only
+ supports SPI NOR.
+
config SPI_NXP_SPIFI
tristate "NXP SPI Flash Interface (SPIFI)"
depends on OF && (ARCH_LPC18XX || COMPILE_TEST)
diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile
index f4c61d282abd..fe0e2bdef9cd 100644
--- a/drivers/mtd/spi-nor/Makefile
+++ b/drivers/mtd/spi-nor/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_SPI_CADENCE_QUADSPI) += cadence-quadspi.o
obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o
obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o
obj-$(CONFIG_MTD_MT81xx_NOR) += mtk-quadspi.o
+obj-$(CONFIG_SPI_NPCM_FIU) += npcm-fiu.o
obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o
obj-$(CONFIG_SPI_INTEL_SPI) += intel-spi.o
obj-$(CONFIG_SPI_INTEL_SPI_PCI) += intel-spi-pci.o
diff --git a/drivers/mtd/spi-nor/npcm-fiu.c b/drivers/mtd/spi-nor/npcm-fiu.c
new file mode 100644
index 000000000000..9b6e7747d678
--- /dev/null
+++ b/drivers/mtd/spi-nor/npcm-fiu.c
@@ -0,0 +1,930 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018 Nuvoton Technology corporation.
+
+#include <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] 19+ messages in thread
* Re: [linux dev-4.19 03/15] mtd: spi-nor: add NPCM FIU controller driver
2019-01-14 13:06 ` [linux dev-4.19 03/15] mtd: spi-nor: add NPCM FIU controller driver Tomer Maimon
@ 2019-01-15 23:53 ` Joel Stanley
0 siblings, 0 replies; 19+ messages in thread
From: Joel Stanley @ 2019-01-15 23:53 UTC (permalink / raw)
To: Tomer Maimon; +Cc: OpenBMC Maillist, Andrew Jeffery
Hi Tomer,
On Tue, 15 Jan 2019 at 00:07, Tomer Maimon <tmaimon77@gmail.com> wrote:
>
> 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.
>
> +static int npcm_fiu_uma_read(struct spi_nor *nor, u8 transaction_code,
> + u32 address, bool is_address_size, u8 *data,
> + u32 data_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_CMD,
> + NPCM_FIU_UMA_CMD_CMD, transaction_code);
> + regmap_write(host->regmap, NPCM_FIU_UMA_ADDR, address);
I notice this driver uses regmap for register reads and writes. Are
you aware that regmap has a very large overhead for every operation?
It takes a few hundred instructions to do each read/write, where as
readl/writel result in one or two instructions.
I'd strongly recommend implementing your drivers without regmap unless
you are using it to access, for example, a couple of registers that
are shared between drivers.
Cheers,
Joel
^ permalink raw reply [flat|nested] 19+ messages in thread
* [linux dev-4.19 04/15] dt-bindings: i2c: npcm7xx: add binding for i2c controller
2019-01-14 13:06 [linux dev-4.19 00/15] dd NPCM7xx patches to dev-4.19 Tomer Maimon
` (2 preceding siblings ...)
2019-01-14 13:06 ` [linux dev-4.19 03/15] mtd: spi-nor: add NPCM FIU controller driver Tomer Maimon
@ 2019-01-14 13:06 ` Tomer Maimon
2019-01-14 13:07 ` [linux dev-4.19 05/15] i2c: npcm: driver for Poleg " Tomer Maimon
` (11 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Tomer Maimon @ 2019-01-14 13:06 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] 19+ messages in thread
* [linux dev-4.19 05/15] i2c: npcm: driver for Poleg i2c controller
2019-01-14 13:06 [linux dev-4.19 00/15] dd NPCM7xx patches to dev-4.19 Tomer Maimon
` (3 preceding siblings ...)
2019-01-14 13:06 ` [linux dev-4.19 04/15] dt-bindings: i2c: npcm7xx: add binding for i2c controller Tomer Maimon
@ 2019-01-14 13:07 ` Tomer Maimon
2019-01-14 13:07 ` [linux dev-4.19 06/15] dt-binding: bmc: Add NPCM7xx LPC BPC documentation Tomer Maimon
` (10 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Tomer Maimon @ 2019-01-14 13:07 UTC (permalink / raw)
To: joel; +Cc: openbmc, Tomer Maimon, Tali Perry
Signed-off-by: Tali Perry <tali.perry1@gmail.com>
Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
---
drivers/i2c/busses/Kconfig | 11 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-npcm7xx.c | 2017 ++++++++++++++++++++++++++++++++++++++
3 files changed, 2029 insertions(+)
create mode 100644 drivers/i2c/busses/i2c-npcm7xx.c
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index ac4b09642f63..6dde79365bf8 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -739,6 +739,17 @@ config I2C_NOMADIK
I2C interface from ST-Ericsson's Nomadik and Ux500 architectures,
as well as the STA2X11 PCIe I/O HUB.
+config I2C_NPCM7XX
+ tristate "Nuvoton I2C Controller"
+ depends on ARCH_NPCM7XX
+ select I2C_SLAVE
+ help
+ If you say yes to this option, support will be included for the
+ Nuvoton I2C controller.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-npcm7xx.
+
config I2C_OCORES
tristate "OpenCores I2C Controller"
help
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 18b26af82b1c..fec5cb593e78 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -74,6 +74,7 @@ obj-$(CONFIG_I2C_MT65XX) += i2c-mt65xx.o
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
obj-$(CONFIG_I2C_MXS) += i2c-mxs.o
obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o
+obj-$(CONFIG_I2C_NPCM7XX) += i2c-npcm7xx.o
obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o
obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
obj-$(CONFIG_I2C_OWL) += i2c-owl.o
diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c
new file mode 100644
index 000000000000..6d177d3f0e0b
--- /dev/null
+++ b/drivers/i2c/busses/i2c-npcm7xx.c
@@ -0,0 +1,2017 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Nuvoton NPCM7xx SMB Controller driver
+ *
+ * Copyright (C) 2018 Nuvoton Technologies tali.perry@nuvoton.com
+ */
+#include <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>
+
+#define I2C_VERSION "0.0.3"
+
+enum smb_mode {
+ SMB_SLAVE = 1,
+ SMB_MASTER
+};
+
+/*
+ * External SMB Interface driver xfer indication values, which indicate status
+ * of the bus.
+ */
+enum smb_state_ind {
+ SMB_NO_STATUS_IND = 0,
+ SMB_SLAVE_RCV_IND = 1,
+ SMB_SLAVE_XMIT_IND = 2,
+ SMB_SLAVE_XMIT_MISSING_DATA_IND = 3,
+ SMB_SLAVE_RESTART_IND = 4,
+ SMB_SLAVE_DONE_IND = 5,
+ SMB_MASTER_DONE_IND = 6,
+ SMB_NO_DATA_IND = 7,
+ SMB_NACK_IND = 8,
+ SMB_BUS_ERR_IND = 9,
+ SMB_WAKE_UP_IND = 10,
+ SMB_MASTER_PEC_ERR_IND = 11,
+ SMB_BLOCK_BYTES_ERR_IND = 12,
+ SMB_SLAVE_PEC_ERR_IND = 13,
+ SMB_SLAVE_RCV_MISSING_DATA_IND = 14,
+};
+
+// SMBus Operation type values
+enum smb_oper {
+ SMB_NO_OPER = 0,
+ SMB_WRITE_OPER = 1,
+ SMB_READ_OPER = 2
+};
+
+// SMBus Bank (FIFO mode)
+enum smb_bank {
+ SMB_BANK_0 = 0,
+ SMB_BANK_1 = 1
+};
+
+// Internal SMB states values (for the SMB module state machine).
+enum smb_state {
+ SMB_DISABLE = 0,
+ SMB_IDLE,
+ SMB_MASTER_START,
+ SMB_SLAVE_MATCH,
+ SMB_OPER_STARTED,
+ SMB_REPEATED_START,
+ SMB_STOP_PENDING
+};
+
+// Module supports setting multiple own slave addresses:
+enum smb_addr {
+ SMB_SLAVE_ADDR1 = 0,
+ SMB_SLAVE_ADDR2,
+ SMB_SLAVE_ADDR3,
+ SMB_SLAVE_ADDR4,
+ SMB_SLAVE_ADDR5,
+ SMB_SLAVE_ADDR6,
+ SMB_SLAVE_ADDR7,
+ SMB_SLAVE_ADDR8,
+ SMB_SLAVE_ADDR9,
+ SMB_SLAVE_ADDR10,
+ SMB_GC_ADDR,
+ SMB_ARP_ADDR
+};
+
+// global regs
+static struct regmap *gcr_regmap;
+static struct regmap *clk_regmap;
+
+#define NPCM_I2CSEGCTL 0xE4
+#define NPCM_SECCNT 0x68
+#define NPCM_CNTR25M 0x6C
+#define I2CSEGCTL_VAL 0x0333F000
+
+// Common regs
+#define NPCM_SMBSDA 0x000
+#define NPCM_SMBST 0x002
+#define NPCM_SMBCST 0x004
+#define NPCM_SMBCTL1 0x006
+#define NPCM_SMBADDR1 0x008
+#define NPCM_SMBCTL2 0x00A
+#define NPCM_SMBADDR2 0x00C
+#define NPCM_SMBCTL3 0x00E
+#define NPCM_SMBCST2 0x018
+#define NPCM_SMBCST3 0x019
+#define SMB_VER 0x01F
+
+// BANK 0 regs
+#define NPCM_SMBADDR3 0x010
+#define NPCM_SMBADDR7 0x011
+#define NPCM_SMBADDR4 0x012
+#define NPCM_SMBADDR8 0x013
+#define NPCM_SMBADDR5 0x014
+#define NPCM_SMBADDR9 0x015
+#define NPCM_SMBADDR6 0x016
+#define NPCM_SMBADDR10 0x017
+
+// SMBADDR array: because the addr regs are sprincled all over the address space
+const int NPCM_SMBADDR[10] = {NPCM_SMBADDR1, NPCM_SMBADDR2, NPCM_SMBADDR3,
+ NPCM_SMBADDR4, NPCM_SMBADDR5, NPCM_SMBADDR6,
+ NPCM_SMBADDR7, NPCM_SMBADDR8, NPCM_SMBADDR9,
+ NPCM_SMBADDR10};
+
+#define NPCM_SMBCTL4 0x01A
+#define NPCM_SMBCTL5 0x01B
+#define NPCM_SMBSCLLT 0x01C // SCL Low Time
+#define NPCM_SMBFIF_CTL 0x01D // FIFO Control
+#define NPCM_SMBSCLHT 0x01E // SCL High Time
+
+// BANK 1 regs
+#define NPCM_SMBFIF_CTS 0x010 // FIFO Control
+#define NPCM_SMBTXF_CTL 0x012 // Tx-FIFO Control
+#define NPCM_SMBT_OUT 0x014 // Bus T.O.
+#define NPCM_SMBPEC 0x016 // PEC Data
+#define NPCM_SMBTXF_STS 0x01A // Tx-FIFO Status
+#define NPCM_SMBRXF_STS 0x01C // Rx-FIFO Status
+#define NPCM_SMBRXF_CTL 0x01E // Rx-FIFO Control
+
+// NPCM_SMBST reg fields
+#define NPCM_SMBST_XMIT BIT(0)
+#define NPCM_SMBST_MASTER BIT(1)
+#define NPCM_SMBST_NMATCH BIT(2)
+#define NPCM_SMBST_STASTR BIT(3)
+#define NPCM_SMBST_NEGACK BIT(4)
+#define NPCM_SMBST_BER BIT(5)
+#define NPCM_SMBST_SDAST BIT(6)
+#define NPCM_SMBST_SLVSTP BIT(7)
+
+// NPCM_SMBCST reg fields
+#define NPCM_SMBCST_BUSY BIT(0)
+#define NPCM_SMBCST_BB BIT(1)
+#define NPCM_SMBCST_MATCH BIT(2)
+#define NPCM_SMBCST_GCMATCH BIT(3)
+#define NPCM_SMBCST_TSDA BIT(4)
+#define NPCM_SMBCST_TGSCL BIT(5)
+#define NPCM_SMBCST_MATCHAF BIT(6)
+#define NPCM_SMBCST_ARPMATCH BIT(7)
+
+// NPCM_SMBCTL1 reg fields
+#define NPCM_SMBCTL1_START BIT(0)
+#define NPCM_SMBCTL1_STOP BIT(1)
+#define NPCM_SMBCTL1_INTEN BIT(2)
+#define NPCM_SMBCTL1_EOBINTE BIT(3)
+#define NPCM_SMBCTL1_ACK BIT(4)
+#define NPCM_SMBCTL1_GCMEN BIT(5)
+#define NPCM_SMBCTL1_NMINTE BIT(6)
+#define NPCM_SMBCTL1_STASTRE BIT(7)
+
+// RW1S fields (inside a RW reg):
+#define NPCM_SMBCTL1_RWS_FIELDS (NPCM_SMBCTL1_START | NPCM_SMBCTL1_STOP | \
+ NPCM_SMBCTL1_ACK)
+// NPCM_SMBADDR reg fields
+#define NPCM_SMBADDR_ADDR GENMASK(6, 0)
+#define NPCM_SMBADDR_SAEN BIT(7)
+
+// NPCM_SMBCTL2 reg fields
+#define SMBCTL2_ENABLE BIT(0)
+#define SMBCTL2_SCLFRQ6_0 GENMASK(7, 1)
+
+// NPCM_SMBCTL3 reg fields
+#define SMBCTL3_SCLFRQ8_7 GENMASK(1, 0)
+#define SMBCTL3_ARPMEN BIT(2)
+#define SMBCTL3_IDL_START BIT(3)
+#define SMBCTL3_400K_MODE BIT(4)
+#define SMBCTL3_BNK_SEL BIT(5)
+#define SMBCTL3_SDA_LVL BIT(6)
+#define SMBCTL3_SCL_LVL BIT(7)
+
+// NPCM_SMBCST2 reg fields
+#define NPCM_SMBCST2_MATCHA1F BIT(0)
+#define NPCM_SMBCST2_MATCHA2F BIT(1)
+#define NPCM_SMBCST2_MATCHA3F BIT(2)
+#define NPCM_SMBCST2_MATCHA4F BIT(3)
+#define NPCM_SMBCST2_MATCHA5F BIT(4)
+#define NPCM_SMBCST2_MATCHA6F BIT(5)
+#define NPCM_SMBCST2_MATCHA7F BIT(5)
+#define NPCM_SMBCST2_INTSTS BIT(7)
+
+// NPCM_SMBCST3 reg fields
+#define NPCM_SMBCST3_MATCHA8F BIT(0)
+#define NPCM_SMBCST3_MATCHA9F BIT(1)
+#define NPCM_SMBCST3_MATCHA10F BIT(2)
+#define NPCM_SMBCST3_EO_BUSY BIT(7)
+
+// NPCM_SMBCTL4 reg fields
+#define SMBCTL4_HLDT GENMASK(5, 0)
+#define SMBCTL4_LVL_WE BIT(7)
+
+// NPCM_SMBCTL5 reg fields
+#define SMBCTL5_DBNCT GENMASK(3, 0)
+
+// NPCM_SMBFIF_CTS reg fields
+#define NPCM_SMBFIF_CTS_RXF_TXE BIT(1)
+#define NPCM_SMBFIF_CTS_RFTE_IE BIT(3)
+#define NPCM_SMBFIF_CTS_CLR_FIFO BIT(6)
+#define NPCM_SMBFIF_CTS_SLVRSTR BIT(7)
+
+// NPCM_SMBTXF_CTL reg fields
+#ifdef SMB_CAPABILITY_32B_FIFO
+#define NPCM_SMBTXF_CTL_TX_THR GENMASK(5, 0)
+#else
+#define NPCM_SMBTXF_CTL_TX_THR GENMASK(4, 0)
+#endif
+#define NPCM_SMBTXF_CTL_THR_TXIE BIT(6)
+
+// NPCM_SMBT_OUT reg fields
+#define NPCM_SMBT_OUT_TO_CKDIV GENMASK(5, 0)
+#define NPCM_SMBT_OUT_T_OUTIE BIT(6)
+#define NPCM_SMBT_OUT_T_OUTST BIT(7)
+
+// NPCM_SMBTXF_STS reg fields
+#ifdef SMB_CAPABILITY_32B_FIFO
+#define NPCM_SMBTXF_STS_TX_BYTES GENMASK(5, 0)
+#else
+#define NPCM_SMBTXF_STS_TX_BYTES GENMASK(4, 0)
+#endif
+#define NPCM_SMBTXF_STS_TX_THST BIT(6)
+
+// NPCM_SMBRXF_STS reg fields
+#ifdef SMB_CAPABILITY_32B_FIFO
+#define NPCM_SMBRXF_STS_RX_BYTES GENMASK(5, 0)
+#else
+#define NPCM_SMBRXF_STS_RX_BYTES GENMASK(4, 0)
+#endif
+#define NPCM_SMBRXF_STS_RX_THST BIT(6)
+
+// NPCM_SMBFIF_CTL reg fields
+#define NPCM_SMBFIF_CTL_FIFO_EN BIT(4)
+
+// NPCM_SMBRXF_CTL reg fields
+// Note: on the next HW version of this module, this HW is about to switch to
+// 32 bytes FIFO. This size will be set using a config.
+// on current version 16 bytes FIFO is set using a define
+#ifdef SMB_CAPABILITY_32B_FIFO
+#define NPCM_SMBRXF_CTL_RX_THR GENMASK(5, 0)
+#define NPCM_SMBRXF_CTL_THR_RXIE BIT(6)
+#define NPCM_SMBRXF_CTL_LAST_PEC BIT(7)
+#define SMBUS_FIFO_SIZE 32
+#else
+#define NPCM_SMBRXF_CTL_RX_THR GENMASK(4, 0)
+#define NPCM_SMBRXF_CTL_LAST_PEC BIT(5)
+#define NPCM_SMBRXF_CTL_THR_RXIE BIT(6)
+#define SMBUS_FIFO_SIZE 16
+#endif
+
+// SMB_VER reg fields
+#define SMB_VER_VERSION GENMASK(6, 0)
+#define SMB_VER_FIFO_EN BIT(7)
+
+// stall/stuck timeout
+const unsigned int DEFAULT_STALL_COUNT = 25;
+
+// Data abort timeout
+const unsigned int ABORT_TIMEOUT = 1000;
+
+// SMBus spec. values in KHZ
+const unsigned int SMBUS_FREQ_MIN = 10;
+const unsigned int SMBUS_FREQ_MAX = 1000;
+const unsigned int SMBUS_FREQ_100KHZ = 100;
+const unsigned int SMBUS_FREQ_400KHZ = 400;
+const unsigned int SMBUS_FREQ_1MHZ = 1000;
+
+// SCLFRQ min/max field values
+const unsigned int SCLFRQ_MIN = 10;
+const unsigned int SCLFRQ_MAX = 511;
+
+// SCLFRQ field position
+#define SCLFRQ_0_TO_6 GENMASK(6, 0)
+#define SCLFRQ_7_TO_8 GENMASK(8, 7)
+
+// SMB Maximum Retry Trials (on Bus Arbitration Loss)
+const unsigned int SMB_RETRY_MAX_COUNT = 2;
+const unsigned int SMB_NUM_OF_ADDR = 10;
+
+// for logging:
+#define NPCM_I2C_EVENT_START BIT(0)
+#define NPCM_I2C_EVENT_STOP BIT(1)
+#define NPCM_I2C_EVENT_ABORT BIT(2)
+#define NPCM_I2C_EVENT_WRITE BIT(3)
+#define NPCM_I2C_EVENT_READ BIT(4)
+#define NPCM_I2C_EVENT_BER BIT(5)
+#define NPCM_I2C_EVENT_NACK BIT(6)
+#define NPCM_I2C_EVENT_TO BIT(7)
+#define NPCM_I2C_EVENT_EOB BIT(8)
+
+#define NPCM_I2C_EVENT_LOG(event) (bus->event_log |= event)
+
+#define SMB_RECOVERY_SUPPORT
+
+// slave mode: if end device reads more data than available, ask issuer or
+// request for more data:
+#define SMB_WRAP_AROUND_BUFFER
+
+// Status of one SMBus module
+struct npcm_i2c {
+ struct i2c_adapter adap;
+ struct device *dev;
+ unsigned char __iomem *reg;
+ spinlock_t lock; /* IRQ synchronization */
+ struct completion cmd_complete;
+ int irq;
+ int cmd_err;
+ struct i2c_msg *msgs;
+ int msgs_num;
+ int num;
+ u32 apb_clk;
+ enum smb_state state;
+ enum smb_oper operation;
+ enum smb_mode master_or_slave;
+ enum smb_state_ind stop_ind;
+ u8 dest_addr;
+ u8 *rd_buf;
+ u16 rd_size;
+ u16 rd_ind;
+ u8 *wr_buf;
+ u16 wr_size;
+ u16 wr_ind;
+ bool fifo_use;
+ u8 threshold_fifo;
+
+ // PEC bit mask per slave address.
+ // 1: use PEC for this address,
+ // 0: do not use PEC for this address
+ u16 PEC_mask;
+ bool PEC_use;
+ u8 crc_data;
+ bool read_block_use;
+ u8 retry_count;
+ u8 int_cnt;
+ u32 event_log;
+ u32 clk_period_us;
+ u32 int_time_stamp[2];
+};
+
+static inline void _npcm7xx_get_time_stamp(u32 *time_quad0, u32 *time_quad1)
+{
+ u32 seconds, seconds_last;
+ u32 ref_clock;
+
+ regmap_read(clk_regmap, NPCM_SECCNT, &seconds_last);
+
+ do {
+ regmap_read(clk_regmap, NPCM_SECCNT, &seconds);
+ regmap_read(clk_regmap, NPCM_CNTR25M, &ref_clock);
+ regmap_read(clk_regmap, NPCM_SECCNT, &seconds_last);
+ } while (seconds_last != seconds);
+
+ *time_quad0 = ref_clock;
+ *time_quad1 = seconds;
+}
+
+#define EXT_CLOCK_FREQUENCY_MHZ 25
+#define CNTR25M_ACCURECY EXT_CLOCK_FREQUENCY_MHZ // minimum accurecy
+
+// Function: _npcm7xx_delay_relative
+// Parameters:
+// us_delay - number of microseconds to delay since t0_time.
+// if zero: no delay.
+//
+// t0_time - start time , to measure time from.
+// get a time stamp, delay us_delay from it. If us_delay has already passed
+// since the time stamp , then no delay is executed. returns the time elapsed
+// since t0_time
+
+static inline u32 _npcm7xx_delay_relative(u32 us_delay, u32 t0_time0,
+ u32 t0_time1)
+{
+ u32 t1_time_0, t1_time_1;
+ u32 time_elapsed;
+ u32 minimum_delay = (us_delay * EXT_CLOCK_FREQUENCY_MHZ)
+ + CNTR25M_ACCURECY;
+
+ // this is equivalent to microSec/0.64 + minimal tic length.
+ do {
+ _npcm7xx_get_time_stamp(&t1_time_0, &t1_time_1);
+ time_elapsed = ((EXT_CLOCK_FREQUENCY_MHZ * 1000000) *
+ (t1_time_1 - t0_time1)) +
+ (t1_time_0 - t0_time0);
+ } while (time_elapsed < minimum_delay);
+
+ // return elapsed time
+ return (u32)(time_elapsed / EXT_CLOCK_FREQUENCY_MHZ);
+}
+
+static inline void npcm_smb_select_bank(struct npcm_i2c *bus,
+ enum smb_bank bank)
+{
+ if (bus->fifo_use)
+ iowrite8((ioread8(bus->reg + NPCM_SMBCTL3) & ~SMBCTL3_BNK_SEL) |
+ FIELD_PREP(SMBCTL3_BNK_SEL, bank),
+ bus->reg + NPCM_SMBCTL3);
+}
+
+DECLARE_CRC8_TABLE(npcm7xx_crc8);
+
+static u8 npcm_smb_calc_crc8(u8 crc_data, u8 data)
+{
+ crc_data = crc8(npcm7xx_crc8, &data, 1, crc_data);
+ return crc_data;
+}
+
+static void npcm_smb_calc_PEC(struct npcm_i2c *bus, u8 data)
+{
+ if (bus->PEC_use)
+ bus->crc_data = npcm_smb_calc_crc8(bus->crc_data, data);
+}
+
+static inline void npcm_smb_wr_byte(struct npcm_i2c *bus, u8 data)
+{
+ iowrite8(data, bus->reg + NPCM_SMBSDA);
+ npcm_smb_calc_PEC(bus, data);
+}
+
+static inline void npcm_smb_rd_byte(struct npcm_i2c *bus, u8 *data)
+{
+ *data = ioread8(bus->reg + NPCM_SMBSDA);
+ npcm_smb_calc_PEC(bus, *data);
+}
+
+static inline u8 npcm_smb_get_PEC(struct npcm_i2c *bus)
+{
+ if (bus->PEC_use)
+ return bus->crc_data;
+ else
+ return 0;
+}
+
+static inline void npcm_smb_write_PEC(struct npcm_i2c *bus)
+{
+ if (bus->PEC_use) {
+ // get PAC value and write to the bus:
+ npcm_smb_wr_byte(bus, npcm_smb_get_PEC(bus));
+ }
+}
+
+//
+// NPCM7XX SMB module allows writing to SCL and SDA pins directly
+// without the need to change muxing of pins.
+// This feature will be used for recovery sequences i.e.
+//
+static void npcm_smb_set_SCL(struct i2c_adapter *_adap, int level)
+{
+#ifdef SMB_CAPABILITY_FORCE_SCL_SDA
+ unsigned long flags;
+ struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap);
+
+ // Select Bank 0 to access NPCM_SMBCTL4
+ spin_lock_irqsave(&bus->lock, flags);
+ npcm_smb_select_bank(bus, SMB_BANK_0);
+
+ // Set SCL_LVL, SDA_LVL bits as Read/Write (R/W)
+ iowrite8(ioread8(bus->reg + NPCM_SMBCTL4) | SMBCTL4_LVL_WE,
+ bus->reg + NPCM_SMBCTL4);
+
+ // Set level
+ iowrite8((ioread8(bus->reg + NPCM_SMBCTL3)
+ & ~SMBCTL3_SCL_LVL) | FIELD_PREP(SMBCTL3_SCL_LVL,
+ level), bus->reg + NPCM_SMBCTL3);
+
+ // Set SCL_LVL, SDA_LVL bits as Read Only (RO)
+ iowrite8(ioread8(bus->reg + NPCM_SMBCTL4)
+ & ~SMBCTL4_LVL_WE, bus->reg + NPCM_SMBCTL4);
+
+ // Return to Bank 1
+ npcm_smb_select_bank(bus, SMB_BANK_1);
+ spin_unlock_irqrestore(&bus->lock, flags);
+#endif
+}
+
+static int npcm_smb_get_SCL(struct i2c_adapter *_adap)
+{
+ unsigned long flags;
+ unsigned int ret = 0;
+ struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap);
+
+ // Select Bank 0 to access NPCM_SMBCTL4
+ spin_lock_irqsave(&bus->lock, flags);
+ npcm_smb_select_bank(bus, SMB_BANK_0);
+
+ // Get SCL level
+ ret = FIELD_GET(SMBCTL3_SCL_LVL, ioread8(bus->reg + NPCM_SMBCTL3));
+
+ // Return to Bank 1
+ npcm_smb_select_bank(bus, SMB_BANK_1);
+ spin_unlock_irqrestore(&bus->lock, flags);
+ return ret;
+}
+
+static int npcm_smb_get_SDA(struct i2c_adapter *_adap)
+{
+ unsigned long flags;
+ unsigned int ret = 0;
+ struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap);
+
+ // Select Bank 0 to access NPCM_SMBCTL4
+ spin_lock_irqsave(&bus->lock, flags);
+ npcm_smb_select_bank(bus, SMB_BANK_0);
+
+ // Get SDA level
+ ret = FIELD_GET(SMBCTL3_SDA_LVL, ioread8(bus->reg + NPCM_SMBCTL3));
+
+ // Return to Bank 1
+ npcm_smb_select_bank(bus, SMB_BANK_1);
+ spin_unlock_irqrestore(&bus->lock, flags);
+ return ret;
+}
+
+static inline u16 npcm_smb_get_index(struct npcm_i2c *bus)
+{
+ u16 index = 0;
+
+ if (bus->operation == SMB_READ_OPER)
+ index = bus->rd_ind;
+ else if (bus->operation == SMB_WRITE_OPER)
+ index = bus->wr_ind;
+
+ return index;
+}
+
+// quick protocol:
+static inline bool npcm_smb_is_quick(struct npcm_i2c *bus)
+{
+ if (bus->wr_size == 0 && bus->rd_size == 0)
+ return true;
+ return false;
+}
+
+static void npcm_smb_disable(struct npcm_i2c *bus)
+{
+ int i;
+
+ // select bank 0 for SMB addresses
+ npcm_smb_select_bank(bus, SMB_BANK_0);
+
+ // Slave Addresses Removal
+ for (i = SMB_SLAVE_ADDR1; i < SMB_NUM_OF_ADDR; i++)
+ iowrite8(0, bus->reg + NPCM_SMBADDR[i]);
+
+ // select bank 0 for SMB addresses
+ npcm_smb_select_bank(bus, SMB_BANK_1);
+
+ // Disable module.
+ iowrite8(ioread8(bus->reg + NPCM_SMBCTL2) & ~SMBCTL2_ENABLE,
+ bus->reg + NPCM_SMBCTL2);
+
+ // Set module disable
+ bus->state = SMB_DISABLE;
+}
+
+static void npcm_smb_enable(struct npcm_i2c *bus)
+{
+ iowrite8((ioread8(bus->reg + NPCM_SMBCTL2) | SMBCTL2_ENABLE),
+ bus->reg + NPCM_SMBCTL2);
+}
+
+// enable\disable end of busy (EOB) interrupt
+static inline void npcm_smb_eob_int(struct npcm_i2c *bus, bool enable)
+{
+ if (enable) {
+ iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) |
+ NPCM_SMBCTL1_EOBINTE) & ~NPCM_SMBCTL1_RWS_FIELDS,
+ bus->reg + NPCM_SMBCTL1);
+ } else {
+ iowrite8(ioread8(bus->reg + NPCM_SMBCTL1) &
+ ~NPCM_SMBCTL1_EOBINTE & ~NPCM_SMBCTL1_RWS_FIELDS,
+ bus->reg + NPCM_SMBCTL1);
+
+ // Clear EO_BUSY pending bit:
+ iowrite8(ioread8(bus->reg + NPCM_SMBCST3) |
+ NPCM_SMBCST3_EO_BUSY, bus->reg + NPCM_SMBCST3);
+ }
+}
+
+static inline bool npcm_smb_tx_fifo_full(struct npcm_i2c *bus)
+{
+ // check if TX FIFO full:
+ return (bool)FIELD_GET(NPCM_SMBTXF_STS_TX_THST,
+ ioread8(bus->reg + NPCM_SMBTXF_STS));
+}
+
+static inline bool npcm_smb_rx_fifo_full(struct npcm_i2c *bus)
+{
+ // check if RX FIFO full:
+ return (bool)FIELD_GET(NPCM_SMBRXF_STS_RX_THST,
+ ioread8(bus->reg + NPCM_SMBRXF_STS));
+}
+
+static inline void npcm_smb_clear_tx_fifo(struct npcm_i2c *bus)
+{
+ // clear TX FIFO:
+ iowrite8(ioread8(bus->reg + NPCM_SMBTXF_STS) |
+ NPCM_SMBTXF_STS_TX_THST,
+ bus->reg + NPCM_SMBTXF_STS);
+}
+
+static inline void npcm_smb_clear_rx_fifo(struct npcm_i2c *bus)
+{
+ // clear RX FIFO:
+ iowrite8(ioread8(bus->reg + NPCM_SMBRXF_STS) |
+ NPCM_SMBRXF_STS_RX_THST,
+ bus->reg + NPCM_SMBRXF_STS);
+}
+
+static void npcm_smb_int_enable(struct npcm_i2c *bus, bool enable)
+{
+ if (enable)
+ iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) |
+ NPCM_SMBCTL1_INTEN) & ~NPCM_SMBCTL1_RWS_FIELDS,
+ bus->reg + NPCM_SMBCTL1);
+ else
+ iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) &
+ ~NPCM_SMBCTL1_INTEN) & ~NPCM_SMBCTL1_RWS_FIELDS,
+ bus->reg + NPCM_SMBCTL1);
+}
+
+static inline void npcm_smb_master_start(struct npcm_i2c *bus)
+{
+ NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_START);
+
+ iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) | NPCM_SMBCTL1_START) &
+ ~(NPCM_SMBCTL1_STOP | NPCM_SMBCTL1_ACK),
+ bus->reg + NPCM_SMBCTL1);
+}
+
+static inline void npcm_smb_master_stop(struct npcm_i2c *bus)
+{
+ NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_STOP);
+
+ // override HW issue: SMBus may fail to supply stop condition in Master
+ // Write operation.
+ // Need to delay at least 5 us from the last int, before issueing a stop
+ _npcm7xx_delay_relative(5, bus->int_time_stamp[0],
+ bus->int_time_stamp[1]);
+
+ iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) | NPCM_SMBCTL1_STOP) &
+ ~(NPCM_SMBCTL1_START | NPCM_SMBCTL1_ACK),
+ bus->reg + NPCM_SMBCTL1);
+
+ if (bus->fifo_use) {
+ npcm_smb_select_bank(bus, SMB_BANK_1);
+
+ if (bus->operation == SMB_READ_OPER)
+ npcm_smb_clear_rx_fifo(bus);
+ else
+ npcm_smb_clear_tx_fifo(bus);
+
+ iowrite8(ioread8(bus->reg + NPCM_SMBFIF_CTS) |
+ NPCM_SMBFIF_CTS_SLVRSTR |
+ NPCM_SMBFIF_CTS_RXF_TXE,
+ bus->reg + NPCM_SMBFIF_CTS);
+
+ iowrite8(0, bus->reg + NPCM_SMBTXF_CTL);
+ }
+}
+
+static inline void npcm_smb_abort_data(struct npcm_i2c *bus)
+{
+ unsigned int timeout = ABORT_TIMEOUT;
+ u8 data;
+
+ NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_ABORT);
+ // Generate a STOP condition
+ npcm_smb_master_stop(bus);
+ npcm_smb_rd_byte(bus, &data);
+
+ // Clear NEGACK, STASTR and BER bits
+ iowrite8(NPCM_SMBST_STASTR | NPCM_SMBST_NEGACK |
+ NPCM_SMBST_BER, bus->reg + NPCM_SMBST);
+
+ // Wait till STOP condition is generated
+ while (FIELD_GET(NPCM_SMBCTL1_STOP, ioread8(bus->reg + NPCM_SMBCTL1))) {
+ timeout--;
+ if (!FIELD_GET(NPCM_SMBCTL1_STOP,
+ ioread8(bus->reg + NPCM_SMBCTL1)))
+ break;
+ if (timeout <= 1) {
+ dev_err(bus->dev, "%s, abort timeout!\n", __func__);
+ break;
+ }
+ }
+}
+
+static inline void npcm_smb_stall_after_start(struct npcm_i2c *bus, bool stall)
+{
+ if (stall)
+ iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) |
+ NPCM_SMBCTL1_STASTRE) & ~NPCM_SMBCTL1_RWS_FIELDS,
+ bus->reg + NPCM_SMBCTL1);
+ else
+ iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) &
+ ~NPCM_SMBCTL1_STASTRE) & ~NPCM_SMBCTL1_RWS_FIELDS,
+ bus->reg + NPCM_SMBCTL1);
+}
+
+static inline void npcm_smb_nack(struct npcm_i2c *bus)
+{
+ if (bus->rd_ind < (bus->rd_size - 1))
+ dev_info(bus->dev,
+ "\tNACK err bus%d, SA=0x%x, rd(%d\%d), op=%d st=%d\n",
+ bus->num, bus->dest_addr, bus->rd_ind, bus->rd_size,
+ bus->operation, bus->state);
+ iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) | NPCM_SMBCTL1_ACK) &
+ ~(NPCM_SMBCTL1_STOP | NPCM_SMBCTL1_START),
+ bus->reg + NPCM_SMBCTL1);
+}
+
+static void npcm_smb_reset(struct npcm_i2c *bus)
+{
+ // Save NPCM_SMBCTL1 relevant bits. It is being cleared when the
+ // module is disabled
+ u8 smbctl1 = ioread8(bus->reg + NPCM_SMBCTL1) & (NPCM_SMBCTL1_GCMEN
+ | NPCM_SMBCTL1_INTEN
+ | NPCM_SMBCTL1_NMINTE);
+
+ // Disable the SMB module
+ iowrite8((ioread8(bus->reg + NPCM_SMBCTL2) & ~SMBCTL2_ENABLE),
+ bus->reg + NPCM_SMBCTL2);
+
+ // Enable the SMB module
+ npcm_smb_enable(bus);
+
+ // Restore NPCM_SMBCTL1 status
+ iowrite8(smbctl1 & ~NPCM_SMBCTL1_RWS_FIELDS, bus->reg + NPCM_SMBCTL1);
+
+ // Reset driver status
+ bus->state = SMB_IDLE;
+ //
+ // Configure FIFO disabled mode so slave will not use fifo
+ // (master will set it on if supported)
+ iowrite8(ioread8(bus->reg + NPCM_SMBFIF_CTL) &
+ ~NPCM_SMBFIF_CTL_FIFO_EN,
+ bus->reg + NPCM_SMBFIF_CTL);
+ bus->fifo_use = false;
+}
+
+static inline bool npcm_smb_is_master(struct npcm_i2c *bus)
+{
+ return (bool)FIELD_GET(NPCM_SMBST_MASTER,
+ ioread8(bus->reg + NPCM_SMBST));
+}
+
+static int npcm_smb_master_abort(struct npcm_i2c *bus)
+{
+ int ret = -(EIO);
+
+ // Only current master is allowed to issue Stop Condition
+ if (npcm_smb_is_master(bus)) {
+ npcm_smb_abort_data(bus);
+ ret = 0;
+ }
+
+ npcm_smb_reset(bus);
+
+ return ret;
+}
+
+static void npcm_smb_callback(struct npcm_i2c *bus,
+ enum smb_state_ind op_status, u16 info)
+{
+ struct i2c_msg *msgs = bus->msgs;
+ int msgs_num = bus->msgs_num;
+
+ switch (op_status) {
+ case SMB_MASTER_DONE_IND:
+ // Master transaction finished and all transmit bytes were sent
+ // info: number of bytes actually received after the Master
+ // receive operation (if Master didn't issue receive it
+ // should be 0)
+ // Notify that not all data was received on Master or Slave
+ // info:
+ // on receive: number of actual bytes received
+ // when PEC is used even if 'info' is the expected number
+ // of bytes, it means that PEC error occurred.
+ {
+ if (msgs[0].flags & I2C_M_RD)
+ msgs[0].len = info;
+ else if (msgs_num == 2 && msgs[1].flags & I2C_M_RD)
+ msgs[1].len = info;
+
+ bus->cmd_err = 0;
+ complete(&bus->cmd_complete);
+ }
+ break;
+
+ case SMB_NO_DATA_IND:
+ // Notify that not all data was received on Master or Slave
+ // info:
+ //on receive: number of actual bytes received
+ // when PEC is used even if 'info' is the expected number
+ // of bytes,it means that PEC error occurred.
+ {
+ if (msgs[0].flags & I2C_M_RD)
+ msgs[0].len = info;
+ else if (msgs_num == 2 && msgs[1].flags & I2C_M_RD)
+ msgs[1].len = info;
+
+ bus->cmd_err = -EFAULT;
+ complete(&bus->cmd_complete);
+ }
+ break;
+ case SMB_NACK_IND:
+ // MASTER transmit got a NAK before transmitting all bytes
+ // info: number of transmitted bytes
+ bus->cmd_err = -EAGAIN;
+ complete(&bus->cmd_complete);
+
+ break;
+ case SMB_BUS_ERR_IND:
+ // Bus error
+ // info: has no meaning
+ bus->cmd_err = -EIO;
+ complete(&bus->cmd_complete);
+ break;
+ case SMB_WAKE_UP_IND:
+ // SMBus wake up
+ // info: has no meaning
+ break;
+ default:
+ break;
+ }
+}
+
+static u32 npcm_smb_get_fifo_fullness(struct npcm_i2c *bus)
+{
+ if (bus->operation == SMB_WRITE_OPER)
+ return FIELD_GET(NPCM_SMBTXF_STS_TX_BYTES,
+ ioread8(bus->reg + NPCM_SMBTXF_STS));
+ else if (bus->operation == SMB_READ_OPER)
+ return FIELD_GET(NPCM_SMBRXF_STS_RX_BYTES,
+ ioread8(bus->reg + NPCM_SMBRXF_STS));
+ return 0;
+}
+
+static void npcm_smb_write_to_fifo(struct npcm_i2c *bus, u16 max_bytes_to_send)
+{
+ // Fill the FIFO, while the FIFO is not full and there are more bytes to
+ // write
+ while ((max_bytes_to_send--) && (SMBUS_FIFO_SIZE -
+ npcm_smb_get_fifo_fullness(bus))) {
+ // write the data
+ if (bus->wr_ind < bus->wr_size) {
+ if (bus->PEC_use &&
+ (bus->wr_ind + 1 == bus->wr_size) &&
+ (bus->rd_size == 0 ||
+ bus->master_or_slave == SMB_SLAVE)) {
+ // Master send PEC in write protocol, Slave send
+ // PEC in read protocol.
+ npcm_smb_write_PEC(bus);
+ bus->wr_ind++;
+ } else {
+ npcm_smb_wr_byte(bus,
+ bus->wr_buf[bus->wr_ind++]);
+ }
+ } else {
+#ifdef SMB_WRAP_AROUND_BUFFER
+ // We're out of bytes. Ask the higher level for
+ // more bytes. Let it know that driver
+ // used all its' bytes
+
+ npcm_smb_clear_tx_fifo(bus);
+
+ // Reset state for the remaining bytes transaction
+ bus->state = SMB_SLAVE_MATCH;
+
+ // Notify upper layer of transaction completion
+ npcm_smb_callback(bus, SMB_SLAVE_XMIT_MISSING_DATA_IND,
+ bus->wr_ind);
+
+ iowrite8(NPCM_SMBST_SDAST, bus->reg + NPCM_SMBST);
+#else
+ npcm_smb_wr_byte(bus, 0xFF);
+#endif
+ }
+ }
+}
+
+// configure the FIFO before using it. If nread is -1 RX FIFO will not be
+// configured. same for nwrite
+static void npcm_smb_set_fifo(struct npcm_i2c *bus, int nread, int nwrite)
+{
+ if (!bus->fifo_use)
+ return;
+ npcm_smb_select_bank(bus, SMB_BANK_1);
+ npcm_smb_clear_tx_fifo(bus);
+ npcm_smb_clear_rx_fifo(bus);
+
+ // configure RX FIFO
+ if (nread > 0) {
+ // clear LAST bit:
+ iowrite8(ioread8(bus->reg + NPCM_SMBRXF_CTL) &
+ (~NPCM_SMBRXF_CTL_LAST_PEC),
+ bus->reg + NPCM_SMBRXF_CTL);
+
+ if (nread > SMBUS_FIFO_SIZE)
+ iowrite8((ioread8(bus->reg + NPCM_SMBRXF_CTL) &
+ ~NPCM_SMBRXF_CTL_RX_THR)
+ | FIELD_PREP(NPCM_SMBRXF_CTL_RX_THR,
+ SMBUS_FIFO_SIZE), bus->reg + NPCM_SMBRXF_CTL);
+ else
+ iowrite8((ioread8(bus->reg + NPCM_SMBRXF_CTL) &
+ ~NPCM_SMBRXF_CTL_RX_THR) |
+ FIELD_PREP(NPCM_SMBRXF_CTL_RX_THR,
+ (u8)(nread)),
+ bus->reg + NPCM_SMBRXF_CTL);
+
+ if (nread <= SMBUS_FIFO_SIZE && !bus->read_block_use)
+ iowrite8(ioread8(bus->reg + NPCM_SMBRXF_CTL) |
+ NPCM_SMBRXF_CTL_LAST_PEC,
+ bus->reg + NPCM_SMBRXF_CTL);
+ }
+
+ // configure TX FIFO
+ if (nwrite > 0) {
+ if (nwrite > SMBUS_FIFO_SIZE)
+ // data to send is more then FIFO size.
+ // Configure the FIFO int to be mid of FIFO.
+ iowrite8(NPCM_SMBTXF_CTL_THR_TXIE |
+ (SMBUS_FIFO_SIZE / 2),
+ bus->reg + NPCM_SMBTXF_CTL);
+ else if (nwrite > (SMBUS_FIFO_SIZE / 2) &&
+ bus->wr_ind != 0)
+ // wr_ind != 0 means that this is not the first
+ // write. since int is in the mid of FIFO, only
+ // half of the fifo is empty.
+ // Continue to configure the FIFO int to be mid
+ // of FIFO.
+ iowrite8(NPCM_SMBTXF_CTL_THR_TXIE |
+ (SMBUS_FIFO_SIZE / 2),
+ bus->reg + NPCM_SMBTXF_CTL);
+ else
+ // This is the either first write (wr_ind = 0)
+ // and data to send is less or equal to FIFO
+ // size.
+ // Or this is the last write and data to send
+ // is less or equal half FIFO size.
+ // In both cases disable the FIFO threshold int.
+ // The next int will happen after the FIFO will
+ // get empty.
+ iowrite8(0, bus->reg + NPCM_SMBTXF_CTL);
+ npcm_smb_clear_tx_fifo(bus);
+ }
+}
+
+static void npcm_smb_read_from_fifo(struct npcm_i2c *bus, u8 bytes_in_fifo)
+{
+ while (bytes_in_fifo--) {
+ // Keep read data
+ u8 data = ioread8(bus->reg + NPCM_SMBSDA);
+
+ npcm_smb_calc_PEC(bus, data);
+ if (bus->rd_ind < bus->rd_size) {
+ bus->rd_buf[bus->rd_ind++] = data;
+ if (bus->rd_ind == 1 && bus->read_block_use)
+ // First byte indicates length in block protocol
+ bus->rd_size = data;
+ }
+ }
+}
+
+static void npcm_smb_master_fifo_read(struct npcm_i2c *bus)
+{
+ u16 rcount;
+ u8 fifo_bytes;
+ enum smb_state_ind ind = SMB_MASTER_DONE_IND;
+
+ rcount = bus->rd_size - bus->rd_ind;
+
+ // In order not to change the RX_TRH during transaction (we found that
+ // this might be problematic if it takes too much time to read the FIFO)
+ // we read the data in the following way. If the number of bytes to
+ // read == FIFO Size + C (where C < FIFO Size)then first read C bytes
+ // and in the next int we read rest of the data.
+ if (rcount < (2 * SMBUS_FIFO_SIZE) && rcount > SMBUS_FIFO_SIZE)
+ fifo_bytes = (u8)(rcount - SMBUS_FIFO_SIZE);
+ else
+ fifo_bytes = npcm_smb_get_fifo_fullness(bus);
+
+ if (rcount - fifo_bytes == 0) {
+ // last byte is about to be read - end of transaction.
+ // Stop should be set before reading last byte.
+ npcm_smb_eob_int(bus, true);
+ npcm_smb_master_stop(bus);
+ npcm_smb_read_from_fifo(bus, fifo_bytes);
+
+ if (npcm_smb_get_PEC(bus) != 0)
+ ind = SMB_MASTER_PEC_ERR_IND;
+ bus->state = SMB_STOP_PENDING;
+ bus->stop_ind = ind;
+
+ } else {
+ npcm_smb_read_from_fifo(bus, fifo_bytes);
+ rcount = bus->rd_size - bus->rd_ind;
+ npcm_smb_set_fifo(bus, rcount, -1);
+ }
+}
+
+static void npcm_smb_int_master_handler_write(struct npcm_i2c *bus)
+{
+ u16 wcount;
+
+ NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_WRITE);
+ if (bus->fifo_use)
+ npcm_smb_clear_tx_fifo(bus);
+
+ // Master write operation - last byte handling
+ if (bus->wr_ind == bus->wr_size) {
+ if (bus->fifo_use && npcm_smb_get_fifo_fullness(bus) > 0)
+ // No more bytes to send (to add to the FIFO), however the FIFO is not
+ // empty yet. It is still in the middle of tx. Currently there's nothing
+ // to do except for waiting to the end of the tx.
+ // We will get an int when the FIFO will get empty.
+ return;
+
+ if (bus->rd_size == 0) {
+ // all bytes have been written, in a pure wr operation
+ npcm_smb_eob_int(bus, true);
+
+ // Issue a STOP condition on the bus
+ npcm_smb_master_stop(bus);
+ // Clear SDA Status bit (by writing dummy byte)
+ npcm_smb_wr_byte(bus, 0xFF);
+
+ bus->state = SMB_STOP_PENDING;
+ bus->stop_ind = SMB_MASTER_DONE_IND;
+ } else {
+ // last write-byte written on previous int - need to
+ // restart & send slave address
+ if (bus->PEC_use && !bus->read_block_use &&
+ !npcm_smb_is_quick(bus))
+ // PEC is used but the protocol is not block read
+ // then we add extra bytes for PEC support
+ bus->rd_size += 1;
+
+ if (bus->fifo_use) {
+ if (bus->rd_size == 1 || bus->read_block_use) {
+ // SMBus Block read transaction.
+ iowrite8(0, bus->reg + NPCM_SMBTXF_CTL);
+ iowrite8(1, bus->reg + NPCM_SMBRXF_CTL);
+ }
+ }
+
+ npcm_smb_set_fifo(bus, bus->rd_size, -1);
+
+ // Generate (Repeated) Start upon next write to SDA
+ npcm_smb_master_start(bus);
+
+ if (bus->rd_size == 1)
+
+ // Receiving one byte only - stall after successful completion of send
+ // address byte. If we NACK here, and slave doesn't ACK the address, we
+ // might unintentionally NACK the next multi-byte read
+
+ npcm_smb_stall_after_start(bus, true);
+
+ // send the slave address in read direction
+ npcm_smb_wr_byte(bus, bus->dest_addr | 0x1);
+
+ // Next int will occur on read
+ bus->operation = SMB_READ_OPER;
+ }
+ } else {
+ if (bus->PEC_use && !npcm_smb_is_quick(bus))
+ // extra bytes for PEC support
+ bus->wr_size += 1;
+
+ // write next byte not last byte and not slave address
+ if (!bus->fifo_use || bus->wr_size == 1) {
+ if (bus->PEC_use && bus->rd_size == 0 &&
+ (bus->wr_ind + 1 == bus->wr_size)) {
+ // Master write protocol to send PEC byte.
+ npcm_smb_write_PEC(bus);
+ bus->wr_ind++;
+ } else {
+ npcm_smb_wr_byte(bus,
+ bus->wr_buf[bus->wr_ind++]);
+ }
+ } else { // FIFO is used
+ wcount = bus->wr_size - bus->wr_ind;
+ npcm_smb_set_fifo(bus, -1, wcount);
+ npcm_smb_write_to_fifo(bus, wcount);
+ }
+ }
+}
+
+static void npcm_smb_int_master_handler_read(struct npcm_i2c *bus)
+{
+ u16 block_zero_bytes;
+ u32 fifo_bytes;
+
+ // Master read operation (pure read or following a write operation).
+ NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_READ);
+
+ // Initialize number of bytes to include only the first byte (presents
+ // a case where number of bytes to read is zero); add PEC if applicable
+ block_zero_bytes = 1;
+ if (bus->PEC_use)
+ block_zero_bytes++;
+
+ fifo_bytes = FIELD_GET(NPCM_SMBRXF_CTL_RX_THR,
+ ioread8(bus->reg + NPCM_SMBRXF_CTL));
+
+ // Perform master read, distinguishing between last byte and the rest of
+ // the bytes. The last byte should be read when the clock is stopped
+ if ((bus->rd_ind < (bus->rd_size - 1)) || bus->fifo_use) {
+ u8 data;
+
+ // byte to be read is not the last one
+ // Check if byte-before-last is about to be read
+ if ((bus->rd_ind == (bus->rd_size - 2)) &&
+ !bus->fifo_use){
+ // Set nack before reading byte-before-last, so that
+ // nack will be generated after receive of last byte
+ npcm_smb_nack(bus);
+
+ if (!FIELD_GET(NPCM_SMBST_SDAST,
+ ioread8(bus->reg + NPCM_SMBST))) {
+ // No data available - reset state for new xfer
+ bus->state = SMB_IDLE;
+
+ // Notify upper layer of rx completion
+ npcm_smb_callback(bus, SMB_NO_DATA_IND,
+ bus->rd_ind);
+ }
+ } else if (bus->rd_ind == 0) { //first byte handling:
+ // in block protocol first byte is the size
+ if (bus->read_block_use) {
+ npcm_smb_rd_byte(bus, &data);
+
+ // First byte indicates length in block protocol
+ bus->rd_buf[bus->rd_ind++] = data;
+ bus->rd_size = data + 1;
+
+ if (bus->PEC_use) {
+ bus->rd_size += 1;
+ data += 1;
+ }
+
+ if (bus->fifo_use) {
+ iowrite8(NPCM_SMBFIF_CTS_RXF_TXE |
+ ioread8(bus->reg +
+ NPCM_SMBFIF_CTS),
+ bus->reg + NPCM_SMBFIF_CTS);
+
+ // first byte in block protocol
+ // is zero -> not supported. read at
+ // least one byte
+ if (data == 0)
+ data = 1;
+ }
+ npcm_smb_set_fifo(bus, bus->rd_size, -1);
+ } else {
+ if (!bus->fifo_use) {
+ npcm_smb_rd_byte(bus, &data);
+ bus->rd_buf[bus->rd_ind++] = data;
+ } else {
+ npcm_smb_clear_tx_fifo(bus);
+ npcm_smb_master_fifo_read(bus);
+ }
+ }
+
+ } else {
+ if (bus->fifo_use) {
+ if (bus->rd_size == block_zero_bytes &&
+ bus->read_block_use) {
+ npcm_smb_eob_int(bus, true);
+ npcm_smb_master_stop(bus);
+ npcm_smb_read_from_fifo(bus,
+ fifo_bytes);
+ bus->state = SMB_STOP_PENDING;
+ bus->stop_ind = SMB_BLOCK_BYTES_ERR_IND;
+
+ } else {
+ npcm_smb_master_fifo_read(bus);
+ }
+ } else {
+ npcm_smb_rd_byte(bus, &data);
+ bus->rd_buf[bus->rd_ind++] = data;
+ }
+ }
+ } else {
+ // last byte is about to be read - end of transaction.
+ // Stop should be set before reading last byte.
+ u8 data;
+ enum smb_state_ind ind = SMB_MASTER_DONE_IND;
+
+ npcm_smb_eob_int(bus, true);
+
+ npcm_smb_master_stop(bus);
+
+ npcm_smb_rd_byte(bus, &data);
+
+ if (bus->rd_size == block_zero_bytes && bus->read_block_use) {
+ ind = SMB_BLOCK_BYTES_ERR_IND;
+ } else {
+ bus->rd_buf[bus->rd_ind++] = data;
+ if (npcm_smb_get_PEC(bus) != 0)
+ ind = SMB_MASTER_PEC_ERR_IND;
+ }
+
+ bus->state = SMB_STOP_PENDING;
+ bus->stop_ind = ind;
+ }
+}
+
+static void npcm_smb_int_master_handler(struct npcm_i2c *bus)
+{
+ // A negative acknowledge has occurred
+ if (FIELD_GET(NPCM_SMBST_NEGACK, ioread8(bus->reg + NPCM_SMBST))) {
+ NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_NACK);
+ if (bus->fifo_use) {
+ // if there are still untransmitted bytes in TX FIFO
+ // reduce them from wr_ind
+
+ if (bus->operation == SMB_WRITE_OPER)
+ bus->wr_ind -= npcm_smb_get_fifo_fullness(bus);
+ // clear the FIFO
+ iowrite8(NPCM_SMBFIF_CTS_CLR_FIFO,
+ bus->reg + NPCM_SMBFIF_CTS);
+ }
+
+ // In master write operation, NACK is a problem
+ // number of bytes sent to master less than required
+ npcm_smb_master_abort(bus);
+ bus->state = SMB_IDLE;
+
+ // In Master mode, NEGACK should be cleared only after
+ // generating STOP.
+ // In such case, the bus is released from stall only after the
+ // software clears NEGACK bit.
+ // Then a Stop condition is sent.
+ iowrite8(NPCM_SMBST_NEGACK, bus->reg + NPCM_SMBST);
+ npcm_smb_callback(bus, SMB_NACK_IND, bus->wr_ind);
+ return;
+ }
+
+ // Master mode: a Bus Error has been identified
+ if (FIELD_GET(NPCM_SMBST_BER, ioread8(bus->reg + NPCM_SMBST))) {
+ // Check whether bus arbitration or Start or Stop during data
+ // xfer bus arbitration problem should not result in recovery
+ if (npcm_smb_is_master(bus)) {
+ // Only current master is allowed to issue stop
+ npcm_smb_master_abort(bus);
+ } else {
+ // Bus arbitration loss
+ if (bus->retry_count-- > 0) {
+ // Perform a retry (generate a start condition)
+ // as soon as the SMBus is free
+ iowrite8(NPCM_SMBST_BER, bus->reg + NPCM_SMBST);
+ npcm_smb_master_start(bus);
+ return;
+ }
+ }
+ iowrite8(NPCM_SMBST_BER, bus->reg + NPCM_SMBST);
+ bus->state = SMB_IDLE;
+ npcm_smb_callback(bus, SMB_BUS_ERR_IND,
+ npcm_smb_get_index(bus));
+ return;
+ }
+
+ // A Master End of Busy (meaning Stop Condition happened)
+ // End of Busy int is on and End of Busy is set
+ if ((FIELD_GET(NPCM_SMBCTL1_EOBINTE,
+ ioread8(bus->reg + NPCM_SMBCTL1)) == 1) &&
+ (FIELD_GET(NPCM_SMBCST3_EO_BUSY,
+ ioread8(bus->reg + NPCM_SMBCST3)))) {
+ NPCM_I2C_EVENT_LOG(NPCM_I2C_EVENT_EOB);
+ npcm_smb_eob_int(bus, false);
+ bus->state = SMB_IDLE;
+ if (npcm_smb_is_quick(bus))
+ npcm_smb_callback(bus, bus->stop_ind, 0);
+ else
+ npcm_smb_callback(bus, bus->stop_ind, bus->rd_ind);
+ return;
+ }
+
+ // Address sent and requested stall occurred (Master mode)
+ if (FIELD_GET(NPCM_SMBST_STASTR, ioread8(bus->reg + NPCM_SMBST))) {
+ // Check for Quick Command SMBus protocol
+ if (npcm_smb_is_quick(bus)) {
+ npcm_smb_eob_int(bus, true);
+ npcm_smb_master_stop(bus);
+
+ // Update status
+ bus->state = SMB_STOP_PENDING;
+ bus->stop_ind = SMB_MASTER_DONE_IND;
+
+ } else if (bus->rd_size == 1) {
+ // Receiving one byte only - set NACK after ensuring
+ // slave ACKed the address byte
+ npcm_smb_nack(bus);
+ }
+
+ // Reset stall-after-address-byte
+ npcm_smb_stall_after_start(bus, false);
+
+ // Clear stall only after setting STOP
+ iowrite8(NPCM_SMBST_STASTR, bus->reg + NPCM_SMBST);
+
+ return;
+ }
+
+ // SDA status is set - transmit or receive, master
+ if (FIELD_GET(NPCM_SMBST_SDAST, ioread8(bus->reg + NPCM_SMBST)) ||
+ (bus->fifo_use &&
+ (npcm_smb_tx_fifo_full(bus) || npcm_smb_rx_fifo_full(bus)))) {
+ // Status Bit is cleared by writing to or reading from SDA
+ // (depending on current direction)
+ switch (bus->state) {
+ // Handle unsuccessful bus mastership
+ case SMB_IDLE:
+ npcm_smb_master_abort(bus);
+ return;
+
+ case SMB_MASTER_START:
+ if (npcm_smb_is_master(bus)) {
+ u8 addr_byte = bus->dest_addr;
+
+ bus->crc_data = 0;
+ if (npcm_smb_is_quick(bus)) {
+ // Need to stall after successful
+ // completion of sending address byte
+ npcm_smb_stall_after_start(bus, true);
+ } else if (bus->wr_size == 0) {
+ // Set direction to Read
+ addr_byte |= (u8)0x1;
+ bus->operation = SMB_READ_OPER;
+ } else {
+ bus->operation = SMB_WRITE_OPER;
+ }
+
+ // Receiving one byte only - stall after successful completion of
+ // sending address byte. If we NACK here, and slave doesn't ACK the
+ // address, we might unintentionally NACK the next multi-byte read
+ if (bus->wr_size == 0 && bus->rd_size == 1)
+ npcm_smb_stall_after_start(bus, true);
+
+ // Write the address to the bus
+ bus->state = SMB_OPER_STARTED;
+ npcm_smb_wr_byte(bus, addr_byte);
+ } else {
+ dev_err(bus->dev,
+ "SDA, bus%d is not master, wr %d 0x%x...\n",
+ bus->num, bus->wr_size,
+ bus->wr_buf[0]);
+ }
+ break;
+
+ // SDA status is set - transmit or receive: Handle master mode
+ case SMB_OPER_STARTED:
+ if (bus->operation == SMB_WRITE_OPER)
+ npcm_smb_int_master_handler_write(bus);
+ else if (bus->operation == SMB_READ_OPER)
+ npcm_smb_int_master_handler_read(bus);
+ else
+ pr_err("I2C%d: unknown operation\n", bus->num);
+ break;
+ default:
+ dev_err(bus->dev, "i2c%d master sda err on state machine\n",
+ bus->num);
+ }
+ }
+}
+
+static int npcm_smb_recovery(struct i2c_adapter *_adap)
+{
+ u8 iter = 27; // Allow one byte to be sent by the Slave
+ u16 timeout;
+ bool done = false;
+ struct npcm_i2c *bus = container_of(_adap, struct npcm_i2c, adap);
+
+ dev_info(bus->dev, "recovery bus%d\n", bus->num);
+
+ might_sleep();
+
+ // Disable int
+ npcm_smb_int_enable(bus, false);
+
+ // Check If the SDA line is active (low)
+ if (FIELD_GET(NPCM_SMBCST_TSDA, ioread8(bus->reg + NPCM_SMBCST)) == 0) {
+ // Repeat the following sequence until SDA is released
+ do {
+ // Issue a single SCL cycle
+ iowrite8(NPCM_SMBCST_TGSCL, bus->reg + NPCM_SMBCST);
+ timeout = ABORT_TIMEOUT;
+ while (timeout != 0 &&
+ FIELD_GET(NPCM_SMBCST_TGSCL,
+ ioread8(bus->reg + NPCM_SMBCST) == 0))
+ timeout--;
+
+ // If SDA line is inactive (high), stop
+ if (FIELD_GET(NPCM_SMBCST_TSDA,
+ ioread8(bus->reg + NPCM_SMBCST)) == 1)
+ done = true;
+ } while ((!done) && (--iter != 0));
+
+ // If SDA line is released (high)
+ if (done) {
+ // Clear BB (BUS BUSY) bit
+ iowrite8(NPCM_SMBCST_BB, bus->reg + NPCM_SMBCST);
+
+ // Generate a START, to synchronize Master and Slave
+ npcm_smb_master_start(bus);
+
+ // Wait until START condition is sent, or timeout
+ timeout = ABORT_TIMEOUT;
+ while (timeout != 0 && !npcm_smb_is_master(bus))
+ timeout--;
+
+ // If START condition was sent
+ if (timeout > 0) {
+ // Send an address byte
+ npcm_smb_wr_byte(bus, bus->dest_addr);
+
+ // Generate a STOP condition
+ npcm_smb_master_stop(bus);
+ }
+ return 0;
+ }
+ }
+
+ // check if success:
+ if (npcm_smb_get_SCL(_adap) == 1 && npcm_smb_get_SDA(_adap) == 1)
+ goto npcm_smb_recovery_done;
+
+ // hold clock low for 35ms: 25 and some spair:
+ npcm_smb_set_SCL(_adap, 0);
+ usleep_range(35000, 40000);
+ npcm_smb_set_SCL(_adap, 1);
+ usleep_range(1000, 2000);
+
+ // check if success:
+ if (npcm_smb_get_SCL(_adap) == 1 && npcm_smb_get_SDA(_adap) == 1)
+ goto npcm_smb_recovery_done;
+
+ return 0;
+
+npcm_smb_recovery_done:
+
+ npcm_smb_int_enable(bus, true);
+ return -(ENOTRECOVERABLE);
+}
+
+static bool npcm_smb_init_clk(struct npcm_i2c *bus, enum smb_mode mode,
+ u32 bus_freq)
+{
+ u32 k1 = 0;
+ u32 k2 = 0;
+ u8 dbnct = 0;
+ u32 sclfrq = 0;
+ u8 hldt = 7;
+ bool fast_mode = false;
+ u32 src_clk_freq; // in KHz
+
+ src_clk_freq = bus->apb_clk / 1000;
+
+ if (bus_freq <= SMBUS_FREQ_100KHZ) {
+ sclfrq = src_clk_freq / (bus_freq * 4);
+
+ if (sclfrq < SCLFRQ_MIN || sclfrq > SCLFRQ_MAX)
+ return false;
+
+ if (src_clk_freq >= 40000)
+ hldt = 17;
+ else if (src_clk_freq >= 12500)
+ hldt = 15;
+ else
+ hldt = 7;
+ }
+
+ else if (bus_freq == SMBUS_FREQ_400KHZ) {
+ sclfrq = 0;
+ fast_mode = true;
+
+ if ((mode == SMB_MASTER && src_clk_freq < 7500) ||
+ (mode == SMB_SLAVE && src_clk_freq < 10000))
+ // 400KHZ cannot be supported for master core clock < 7.5 MHZ
+ // or slave core clock < 10 MHZ
+ return false;
+
+ // Master or Slave with frequency > 25 MHZ
+ if (mode == SMB_MASTER || src_clk_freq > 25000) {
+ hldt = (u8)__KERNEL_DIV_ROUND_UP(src_clk_freq * 300,
+ 1000000) + 7;
+ if (mode == SMB_MASTER) {
+ k1 = __KERNEL_DIV_ROUND_UP(src_clk_freq * 1600,
+ 1000000);
+ k2 = __KERNEL_DIV_ROUND_UP(src_clk_freq * 900,
+ 1000000);
+ k1 = round_up(k1, 2);
+ k2 = round_up(k2 + 1, 2);
+ if (k1 < SCLFRQ_MIN || k1 > SCLFRQ_MAX ||
+ k2 < SCLFRQ_MIN || k2 > SCLFRQ_MAX)
+ return false;
+ }
+ } else { // Slave with frequency 10-25 MHZ
+ hldt = 7;
+ dbnct = 2;
+ }
+ }
+
+ else if (bus_freq == SMBUS_FREQ_1MHZ) {
+ sclfrq = 0;
+ fast_mode = true;
+
+ if ((mode == SMB_MASTER && src_clk_freq < 15000) ||
+ (mode == SMB_SLAVE && src_clk_freq < 24000))
+ // 1MHZ cannot be supported for master core clock < 15 MHZ
+ // or slave core clock < 24 MHZ
+ return false;
+
+ if (mode == SMB_MASTER) {
+ k1 = round_up((__KERNEL_DIV_ROUND_UP(src_clk_freq * 620,
+ 1000000)), 2);
+ k2 = round_up((__KERNEL_DIV_ROUND_UP(src_clk_freq * 380,
+ 1000000) + 1), 2);
+ if (k1 < SCLFRQ_MIN || k1 > SCLFRQ_MAX ||
+ k2 < SCLFRQ_MIN || k2 > SCLFRQ_MAX) {
+ return false;
+ }
+ }
+
+ // Master or Slave with frequency > 40 MHZ
+ if (mode == SMB_MASTER || src_clk_freq > 40000) {
+ // Set HLDT:
+ // SDA hold time: (HLDT-7) * T(CLK) >= 120
+ // HLDT = 120/T(CLK) + 7 = 120 * FREQ(CLK) + 7
+ hldt = (u8)__KERNEL_DIV_ROUND_UP(src_clk_freq * 120,
+ 1000000) + 7;
+
+ // Slave with frequency 24-40 MHZ
+ } else {
+ hldt = 7;
+ dbnct = 2;
+ }
+ }
+
+ // Frequency larger than 1 MHZ
+ else
+ return false;
+
+ // After clock parameters calculation update the reg
+ iowrite8((ioread8(bus->reg + NPCM_SMBCTL2)
+ & ~SMBCTL2_SCLFRQ6_0) | FIELD_PREP(SMBCTL2_SCLFRQ6_0,
+ sclfrq & 0x7F), bus->reg + NPCM_SMBCTL2);
+
+ iowrite8((ioread8(bus->reg + NPCM_SMBCTL3) & ~SMBCTL3_SCLFRQ8_7) |
+ FIELD_PREP(SMBCTL3_SCLFRQ8_7, (sclfrq >> 7) & 0x3),
+ bus->reg + NPCM_SMBCTL3);
+
+ iowrite8((ioread8(bus->reg + NPCM_SMBCTL3) & ~SMBCTL3_400K_MODE) |
+ FIELD_PREP(SMBCTL3_400K_MODE, fast_mode),
+ bus->reg + NPCM_SMBCTL3);
+
+ // Select Bank 0 to access NPCM_SMBCTL4/NPCM_SMBCTL5
+ npcm_smb_select_bank(bus, SMB_BANK_0);
+
+ if (bus_freq >= SMBUS_FREQ_400KHZ) {
+ // k1 and k2 are relevant for master mode only
+ if (mode == SMB_MASTER) {
+ // Set SCL Low/High Time:
+ // k1 = 2 * SCLLT7-0 -> Low Time = k1 / 2
+ // k2 = 2 * SCLLT7-0 -> High Time = k2 / 2
+ iowrite8((u8)k1 / 2, bus->reg + NPCM_SMBSCLLT);
+ iowrite8((u8)k2 / 2, bus->reg + NPCM_SMBSCLHT);
+ } else { // DBNCT is relevant for slave mode only
+ iowrite8((ioread8(bus->reg + NPCM_SMBCTL5) &
+ ~SMBCTL5_DBNCT) |
+ FIELD_PREP(SMBCTL5_DBNCT, dbnct),
+ bus->reg + NPCM_SMBCTL5);
+ }
+ }
+
+ iowrite8((ioread8(bus->reg + NPCM_SMBCTL4) & ~SMBCTL4_HLDT)
+ | FIELD_PREP(SMBCTL4_HLDT, hldt), bus->reg + NPCM_SMBCTL4);
+
+ // Return to Bank 1, and stay there by default:
+ npcm_smb_select_bank(bus, SMB_BANK_1);
+
+ dev_dbg(bus->dev, "k1 = %d k2 = %d dbnct = %d sclfrq = %d hldt = %d src_clk_freq %d fast_mode %d\n",
+ k1, k2, dbnct, sclfrq, hldt, src_clk_freq, fast_mode);
+
+ return true;
+}
+
+static bool npcm_smb_init_module(struct npcm_i2c *bus, enum smb_mode mode,
+ u32 bus_freq)
+{
+ // Check whether module already enabled or frequency is out of bounds
+ if ((bus->state != SMB_DISABLE && bus->state != SMB_IDLE) ||
+ bus_freq < SMBUS_FREQ_MIN || bus_freq > SMBUS_FREQ_MAX)
+ return false;
+ // Configure FIFO disabled mode so slave will not use fifo
+ // (maste will set it on if supported)
+ bus->threshold_fifo = SMBUS_FIFO_SIZE;
+ iowrite8(ioread8(bus->reg + NPCM_SMBFIF_CTL) & ~NPCM_SMBFIF_CTL_FIFO_EN,
+ bus->reg + NPCM_SMBFIF_CTL);
+
+ bus->fifo_use = false;
+
+ // Configure SMB module clock frequency
+ if (!npcm_smb_init_clk(bus, mode, bus_freq)) {
+ pr_err("npcm_smb_init_clk failed\n");
+ return false;
+ }
+ npcm_smb_disable(bus);
+
+ // Enable module (before configuring CTL1)
+ npcm_smb_enable(bus);
+ bus->state = SMB_IDLE;
+
+ // Enable SMB int and New Address Match int source
+ iowrite8((ioread8(bus->reg + NPCM_SMBCTL1) | NPCM_SMBCTL1_NMINTE) &
+ ~NPCM_SMBCTL1_RWS_FIELDS,
+ bus->reg + NPCM_SMBCTL1);
+
+ npcm_smb_int_enable(bus, true);
+ return true;
+}
+
+static int __npcm_i2c_init(struct npcm_i2c *bus, struct platform_device *pdev)
+{
+ u32 clk_freq;
+ int ret;
+
+ // Initialize the internal data structures
+ bus->state = SMB_DISABLE;
+ bus->master_or_slave = SMB_SLAVE;
+
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "bus-frequency", &clk_freq);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "Could not read bus-frequency property\n");
+ clk_freq = 100000;
+ }
+ ret = npcm_smb_init_module(bus, SMB_MASTER, clk_freq / 1000);
+ if (!ret) {
+ dev_err(&pdev->dev,
+ "npcm_smb_init_module() failed\n");
+ return -1;
+ }
+
+ crc8_populate_lsb(npcm7xx_crc8, 0x07);
+ crc8_populate_msb(npcm7xx_crc8, 0x07);
+ return 0;
+}
+
+static irqreturn_t npcm_i2c_bus_irq(int irq, void *dev_id)
+{
+ struct npcm_i2c *bus = dev_id;
+
+ bus->int_cnt++;
+ _npcm7xx_get_time_stamp(&bus->int_time_stamp[0],
+ &bus->int_time_stamp[1]);
+ if (bus->master_or_slave == SMB_MASTER) {
+ npcm_smb_int_master_handler(bus);
+ return IRQ_HANDLED;
+ }
+
+ dev_err(bus->dev, "int unknown on bus%d\n", bus->num);
+ return IRQ_NONE;
+}
+
+static bool npcm_smb_master_start_xmit(struct npcm_i2c *bus,
+ u8 slave_addr, u16 nwrite, u16 nread,
+ u8 *write_data, u8 *read_data,
+ bool use_PEC)
+{
+ //
+ // Allow only if bus is not busy
+ //
+ if (bus->state != SMB_IDLE) {
+ dev_info(bus->dev, "\tbus%d->state != SMB_IDLE\n", bus->num);
+ return false;
+ }
+
+ // Configure FIFO mode :
+ if (FIELD_GET(SMB_VER_FIFO_EN, ioread8(bus->reg + SMB_VER))) {
+ bus->fifo_use = true;
+ iowrite8(ioread8(bus->reg + NPCM_SMBFIF_CTL) |
+ NPCM_SMBFIF_CTL_FIFO_EN, bus->reg + NPCM_SMBFIF_CTL);
+ } else {
+ bus->fifo_use = false;
+ }
+
+ // Update driver state
+ bus->master_or_slave = SMB_MASTER;
+ bus->state = SMB_MASTER_START;
+ if (nwrite > 0)
+ bus->operation = SMB_WRITE_OPER;
+ else
+ bus->operation = SMB_READ_OPER;
+
+ if (npcm_smb_is_quick(bus))
+ bus->operation = SMB_WRITE_OPER; // send the address with W bit.
+
+ bus->dest_addr = (u8)(slave_addr << 1);// Translate 7bit to 8bit format
+ bus->wr_buf = write_data;
+ bus->wr_size = nwrite;
+ bus->wr_ind = 0;
+ bus->rd_buf = read_data;
+ bus->rd_size = nread;
+ bus->rd_ind = 0;
+ bus->PEC_use = use_PEC;
+ bus->retry_count = SMB_RETRY_MAX_COUNT;
+
+ // clear BER just in case it is set due to a previous transaction
+ iowrite8(NPCM_SMBST_BER, bus->reg + NPCM_SMBST);
+
+ // Initiate SMBus master transaction
+ // Generate a Start condition on the SMBus
+ if (bus->fifo_use) {
+ // select bank 1 for FIFO regs
+ npcm_smb_select_bank(bus, SMB_BANK_1);
+
+ // clear FIFO and relevant status bits.
+ iowrite8(ioread8(bus->reg + NPCM_SMBFIF_CTS) |
+ NPCM_SMBFIF_CTS_SLVRSTR |
+ NPCM_SMBFIF_CTS_CLR_FIFO |
+ NPCM_SMBFIF_CTS_RXF_TXE, bus->reg + NPCM_SMBFIF_CTS);
+
+ if (bus->operation == SMB_READ_OPER) {
+ //This is a read only operation. Configure the FIFO
+ //threshold according to the needed # of bytes to read.
+ npcm_smb_set_fifo(bus, nread, -1);
+ } else if (bus->operation == SMB_WRITE_OPER) {
+ npcm_smb_set_fifo(bus, -1, nwrite);
+ }
+ }
+
+ bus->int_cnt = 0;
+ bus->event_log = 0;
+ npcm_smb_master_start(bus);
+
+ return true;
+}
+
+static int npcm_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ struct npcm_i2c *bus = adap->algo_data;
+ struct i2c_msg *msg0, *msg1;
+ unsigned long time_left, flags;
+ u16 nwrite, nread;
+ u8 *write_data, *read_data;
+ u8 slave_addr;
+ int ret = 0;
+
+ spin_lock_irqsave(&bus->lock, flags);
+ bus->cmd_err = -EPERM;
+ bus->int_cnt = 0;
+ bus->stop_ind = SMB_NO_STATUS_IND;
+ bus->read_block_use = false;
+
+ iowrite8(0xFF, bus->reg + NPCM_SMBST);
+
+ if (num > 2 || num < 1) {
+ pr_err("I2C command not supported, num of msgs = %d\n", num);
+ spin_unlock_irqrestore(&bus->lock, flags);
+ return -EINVAL;
+ }
+
+ msg0 = &msgs[0];
+ slave_addr = msg0->addr;
+ if (msg0->flags & I2C_M_RD) { // read
+ if (num == 2) {
+ pr_err(" num = 2 but first msg is rd instead of wr\n");
+ spin_unlock_irqrestore(&bus->lock, flags);
+ return -EINVAL;
+ }
+ nwrite = 0;
+ write_data = NULL;
+ if (msg0->flags & I2C_M_RECV_LEN) {
+ nread = 1;
+ bus->read_block_use = true;
+
+ } else {
+ nread = msg0->len;
+ }
+ read_data = msg0->buf;
+
+ } else { // write
+ nwrite = msg0->len;
+ write_data = msg0->buf;
+ nread = 0;
+ read_data = NULL;
+ if (num == 2) {
+ msg1 = &msgs[1];
+ if (slave_addr != msg1->addr) {
+ pr_err("SA==%02x but msg1->addr == %02x\n",
+ slave_addr, msg1->addr);
+ spin_unlock_irqrestore(&bus->lock, flags);
+ return -EINVAL;
+ }
+ if ((msg1->flags & I2C_M_RD) == 0) {
+ pr_err("num = 2 but both msg are write.\n");
+ spin_unlock_irqrestore(&bus->lock, flags);
+ return -EINVAL;
+ }
+ if (msg1->flags & I2C_M_RECV_LEN) {
+ nread = 1;
+ bus->read_block_use = true;
+ } else {
+ nread = msg1->len;
+ bus->read_block_use = false;
+ }
+
+ read_data = msg1->buf;
+ }
+ }
+
+ bus->msgs = msgs;
+ bus->msgs_num = num;
+
+ if (nwrite >= 32 * 1024 || nread >= 32 * 1024) {
+ pr_err("i2c%d buffer too big\n", bus->num);
+ return -EINVAL;
+ }
+
+ reinit_completion(&bus->cmd_complete);
+
+ if (npcm_smb_master_start_xmit(bus, slave_addr, nwrite, nread,
+ write_data, read_data, 0) == false)
+ ret = -(EBUSY);
+
+ if (ret != -(EBUSY)) {
+ time_left = wait_for_completion_timeout(&bus->cmd_complete,
+ bus->adap.timeout);
+
+ if (time_left == 0 && bus->cmd_err == -EPERM) {
+ npcm_smb_master_abort(bus);
+ ret = -ETIMEDOUT;
+ } else {
+ ret = bus->cmd_err;
+ }
+ }
+
+ bus->msgs = NULL;
+ bus->msgs_num = 0;
+ spin_unlock_irqrestore(&bus->lock, flags);
+
+ // If nothing went wrong, return number of messages xferred.
+ if (ret >= 0)
+ return num;
+ else
+ return ret;
+}
+
+static u32 npcm_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static const struct i2c_algorithm npcm_i2c_algo = {
+ .master_xfer = npcm_i2c_master_xfer,
+ .functionality = npcm_i2c_functionality,
+};
+
+static struct i2c_bus_recovery_info npcm_i2c_recovery = {
+ .recover_bus = npcm_smb_recovery,
+ .get_scl = npcm_smb_get_SCL,
+ .set_scl = npcm_smb_set_SCL,
+ .get_sda = npcm_smb_get_SDA,
+};
+
+static int npcm_i2c_probe_bus(struct platform_device *pdev)
+{
+ struct npcm_i2c *bus;
+ struct resource *res;
+ struct clk *i2c_clk;
+ int ret;
+ int num;
+
+ bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
+ if (!bus)
+ return -ENOMEM;
+
+#ifdef CONFIG_OF
+ num = of_alias_get_id(pdev->dev.of_node, "i2c");
+ bus->num = num;
+ i2c_clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(i2c_clk)) {
+ pr_err(" I2C probe failed: can't read clk.\n");
+ return -EPROBE_DEFER;
+ }
+ bus->apb_clk = clk_get_rate(i2c_clk);
+ dev_dbg(bus->dev, "I2C APB clock is %d\n", bus->apb_clk);
+#endif // CONFIG_OF
+
+ gcr_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-gcr");
+ if (IS_ERR(gcr_regmap)) {
+ pr_err("%s: failed to find nuvoton,npcm750-gcr\n", __func__);
+ return IS_ERR(gcr_regmap);
+ }
+ regmap_write(gcr_regmap, NPCM_I2CSEGCTL, I2CSEGCTL_VAL);
+ dev_dbg(bus->dev, "I2C%d: gcr mapped\n", bus->num);
+
+ clk_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-clk");
+ if (IS_ERR(clk_regmap)) {
+ pr_err("%s: failed to find nuvoton,npcm750-clk\n", __func__);
+ return IS_ERR(clk_regmap);
+ }
+ dev_dbg(bus->dev, "I2C%d: clk mapped\n", bus->num);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dev_dbg(bus->dev, "resource: %pR\n", res);
+ bus->reg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR((bus)->reg))
+ return PTR_ERR((bus)->reg);
+ dev_dbg(bus->dev, "base = %p\n", bus->reg);
+
+ // Initialize the I2C adapter
+ spin_lock_init(&bus->lock);
+ init_completion(&bus->cmd_complete);
+ bus->adap.owner = THIS_MODULE;
+ bus->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ bus->adap.retries = 0;
+ bus->adap.timeout = 500 * HZ / 1000;
+ bus->adap.algo = &npcm_i2c_algo;
+ bus->adap.algo_data = bus;
+ bus->adap.dev.parent = &pdev->dev;
+ bus->adap.dev.of_node = pdev->dev.of_node;
+ bus->adap.bus_recovery_info = &npcm_i2c_recovery;
+
+ snprintf(bus->adap.name, sizeof(bus->adap.name), "Nuvoton i2c");
+
+ bus->dev = &pdev->dev;
+
+ ret = __npcm_i2c_init(bus, pdev);
+ if (ret < 0)
+ return ret;
+
+ bus->irq = platform_get_irq(pdev, 0);
+ if (bus->irq < 0) {
+ pr_err("I2C platform_get_irq error.");
+ return -ENODEV;
+ }
+ dev_dbg(bus->dev, "irq = %d\n", bus->irq);
+
+ ret = request_irq(bus->irq, npcm_i2c_bus_irq, 0,
+ dev_name(&pdev->dev), (void *)bus);
+ if (ret) {
+ dev_err(&pdev->dev, "I2C%d: request_irq fail\n", bus->num);
+ return ret;
+ }
+
+ ret = i2c_add_adapter(&bus->adap);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "I2C%d: i2c_add_adapter fail\n", bus->num);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, bus);
+ pr_info("i2c bus %d registered\n", bus->adap.nr);
+
+ return 0;
+}
+
+static int npcm_i2c_remove_bus(struct platform_device *pdev)
+{
+ unsigned long lock_flags;
+ struct npcm_i2c *bus = platform_get_drvdata(pdev);
+
+ spin_lock_irqsave(&bus->lock, lock_flags);
+ npcm_smb_disable(bus);
+ spin_unlock_irqrestore(&bus->lock, lock_flags);
+ i2c_del_adapter(&bus->adap);
+
+ return 0;
+}
+
+static const struct of_device_id npcm_i2c_bus_of_table[] = {
+ { .compatible = "nuvoton,npcm750-i2c", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, npcm_i2c_bus_of_table);
+
+static struct platform_driver npcm_i2c_bus_driver = {
+ .probe = npcm_i2c_probe_bus,
+ .remove = npcm_i2c_remove_bus,
+ .driver = {
+ .name = "nuvoton-i2c",
+ .of_match_table = npcm_i2c_bus_of_table,
+ }
+};
+module_platform_driver(npcm_i2c_bus_driver);
+
+MODULE_AUTHOR("Avi Fishman <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] 19+ messages in thread
* [linux dev-4.19 06/15] dt-binding: bmc: Add NPCM7xx LPC BPC documentation
2019-01-14 13:06 [linux dev-4.19 00/15] dd NPCM7xx patches to dev-4.19 Tomer Maimon
` (4 preceding siblings ...)
2019-01-14 13:07 ` [linux dev-4.19 05/15] i2c: npcm: driver for Poleg " Tomer Maimon
@ 2019-01-14 13:07 ` Tomer Maimon
2019-01-14 13:07 ` [linux dev-4.19 07/15] misc: npcm7xx-lpc-bpc: add NPCM7xx BIOS post code driver Tomer Maimon
` (9 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Tomer Maimon @ 2019-01-14 13:07 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] 19+ messages in thread
* [linux dev-4.19 07/15] misc: npcm7xx-lpc-bpc: add NPCM7xx BIOS post code driver
2019-01-14 13:06 [linux dev-4.19 00/15] dd NPCM7xx patches to dev-4.19 Tomer Maimon
` (5 preceding siblings ...)
2019-01-14 13:07 ` [linux dev-4.19 06/15] dt-binding: bmc: Add NPCM7xx LPC BPC documentation Tomer Maimon
@ 2019-01-14 13:07 ` Tomer Maimon
2019-01-14 13:07 ` [linux dev-4.19 08/15] dt-binding: bmc: add npcm7xx pci mailbox document Tomer Maimon
` (8 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Tomer Maimon @ 2019-01-14 13:07 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 | 394 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 403 insertions(+)
create mode 100644 drivers/misc/npcm7xx-lpc-bpc.c
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 689d07ea7ded..c0cbd68238cf 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -520,6 +520,14 @@ config MISC_RTSX
tristate
default MISC_RTSX_PCI || MISC_RTSX_USB
+config NPCM7XX_LPC_BPC
+ tristate "NPCM7xx LPC BIOS Post Code support"
+ depends on (ARCH_NPCM7XX || COMPILE_TEST)
+ help
+ Provides a NPCM7xx driver to control the LPC BIOS Post Code
+ interface which allows the BMC to monitoring and save
+ the data written by the host to an arbitrary LPC I/O port.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index e4170f62ab98..2dc74726e3b0 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -59,3 +59,4 @@ obj-$(CONFIG_ASPEED_LPC_MBOX) += aspeed-lpc-mbox.o
obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o
obj-$(CONFIG_OCXL) += ocxl/
obj-$(CONFIG_MISC_RTSX) += cardreader/
+obj-$(CONFIG_NPCM7XX_LPC_BPC) += npcm7xx-lpc-bpc.o
diff --git a/drivers/misc/npcm7xx-lpc-bpc.c b/drivers/misc/npcm7xx-lpc-bpc.c
new file mode 100644
index 000000000000..e014e07cd4a4
--- /dev/null
+++ b/drivers/misc/npcm7xx-lpc-bpc.c
@@ -0,0 +1,394 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2014-2018 Nuvoton Technology corporation.
+
+#include <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 = 0;
+ bool isr_flag = false;
+
+ fifo_st = ioread8(lpc_bpc->base + NPCM7XX_BPCFSTAT_REG);
+ while (FIFO_DATA_VALID & fifo_st) {
+ /* If dwcapture enabled only channel 0 (FIFO 0) used */
+ if (!lpc_bpc->en_dwcap)
+ addr_index = fifo_st & FIFO_ADDR_DECODE;
+ else
+ last_addr_bit = fifo_st & FIFO_ADDR_DECODE;
+
+ /*Read data from FIFO to clear interrupt*/
+ Data = ioread8(lpc_bpc->base + NPCM7XX_BPCFDATA_REG);
+ if (kfifo_is_full(&lpc_bpc->ch[addr_index].fifo))
+ kfifo_skip(&lpc_bpc->ch[addr_index].fifo);
+ kfifo_put(&lpc_bpc->ch[addr_index].fifo, Data);
+ if (fifo_st & FIFO_OVERFLOW)
+ pr_info("BIOS Post Codes FIFO Overflow!!!\n");
+
+ fifo_st = ioread8(lpc_bpc->base + NPCM7XX_BPCFSTAT_REG);
+ if (lpc_bpc->en_dwcap && last_addr_bit) {
+ if ((fifo_st & FIFO_ADDR_DECODE) ||
+ ((FIFO_DATA_VALID & fifo_st) == 0)) {
+ while (kfifo_avail(&lpc_bpc->ch[addr_index].fifo) < DW_PAD_SIZE)
+ kfifo_skip(&lpc_bpc->ch[addr_index].fifo);
+ kfifo_in(&lpc_bpc->ch[addr_index].fifo,
+ padzero, DW_PAD_SIZE);
+ }
+ }
+ isr_flag = true;
+ }
+
+ host_st = ioread8(lpc_bpc->base + NPCM7XX_BPCFMSTAT_REG);
+ if (host_st & HOST_RESET_CHANGED) {
+ iowrite8(HOST_RESET_CHANGED,
+ lpc_bpc->base + NPCM7XX_BPCFMSTAT_REG);
+ lpc_bpc->ch[addr_index].host_reset = true;
+ isr_flag = true;
+ }
+
+ if (isr_flag) {
+ wake_up_interruptible(&lpc_bpc->ch[addr_index].wq);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int npcm7xx_bpc_config_irq(struct npcm7xx_bpc *lpc_bpc,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int rc;
+
+ lpc_bpc->irq = platform_get_irq(pdev, 0);
+ if (lpc_bpc->irq < 0) {
+ dev_err(dev, "get IRQ failed\n");
+ return lpc_bpc->irq;
+ }
+
+ rc = devm_request_irq(dev, lpc_bpc->irq,
+ npcm7xx_bpc_irq, IRQF_SHARED,
+ DEVICE_NAME, lpc_bpc);
+ if (rc < 0) {
+ dev_warn(dev, "Unable to request IRQ %d\n", lpc_bpc->irq);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int npcm7xx_enable_bpc(struct npcm7xx_bpc *lpc_bpc, struct device *dev,
+ int channel, u16 lpc_port)
+{
+ int rc;
+ u8 addr_en, reg_en;
+
+ init_waitqueue_head(&lpc_bpc->ch[channel].wq);
+
+ rc = kfifo_alloc(&lpc_bpc->ch[channel].fifo,
+ BPC_KFIFO_SIZE, GFP_KERNEL);
+ if (rc)
+ return rc;
+
+ lpc_bpc->ch[channel].miscdev.minor = MISC_DYNAMIC_MINOR;
+ lpc_bpc->ch[channel].miscdev.name =
+ devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel);
+ lpc_bpc->ch[channel].miscdev.fops = &npcm7xx_bpc_fops;
+ lpc_bpc->ch[channel].miscdev.parent = dev;
+ rc = misc_register(&lpc_bpc->ch[channel].miscdev);
+ if (rc)
+ return rc;
+
+ lpc_bpc->ch[channel].data = lpc_bpc;
+ lpc_bpc->ch[channel].host_reset = false;
+
+ /* Enable LPC snoop channel at requested port */
+ switch (channel) {
+ case 0:
+ addr_en = FIFO_IOADDR1_ENABLE;
+ iowrite8((u8)lpc_port & 0xFF,
+ lpc_bpc->base + NPCM7XX_BPCFA1L_REG);
+ iowrite8((u8)(lpc_port >> 8),
+ lpc_bpc->base + NPCM7XX_BPCFA1M_REG);
+ break;
+ case 1:
+ addr_en = FIFO_IOADDR2_ENABLE;
+ iowrite8((u8)lpc_port & 0xFF,
+ lpc_bpc->base + NPCM7XX_BPCFA2L_REG);
+ iowrite8((u8)(lpc_port >> 8),
+ lpc_bpc->base + NPCM7XX_BPCFA2M_REG);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (lpc_bpc->en_dwcap)
+ addr_en = FIFO_DWCAPTURE;
+
+ /*
+ * Enable FIFO Ready Interrupt, FIFO Capture of I/O addr,
+ * and Host Reset
+ */
+ reg_en = ioread8(lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+ iowrite8(reg_en | addr_en | FIFO_READY_INT_ENABLE |
+ HOST_RESET_INT_ENABLE, lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+
+ return 0;
+}
+
+static void npcm7xx_disable_bpc(struct npcm7xx_bpc *lpc_bpc, int channel)
+{
+ u8 reg_en;
+
+ switch (channel) {
+ case 0:
+ reg_en = ioread8(lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+ if (lpc_bpc->en_dwcap)
+ iowrite8(reg_en & ~FIFO_DWCAPTURE,
+ lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+ else
+ iowrite8(reg_en & ~FIFO_IOADDR1_ENABLE,
+ lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+ break;
+ case 1:
+ reg_en = ioread8(lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+ iowrite8(reg_en & ~FIFO_IOADDR2_ENABLE,
+ lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+ break;
+ default:
+ return;
+ }
+
+ if (!(reg_en & (FIFO_IOADDR1_ENABLE | FIFO_IOADDR2_ENABLE)))
+ iowrite8(reg_en &
+ ~(FIFO_READY_INT_ENABLE | HOST_RESET_INT_ENABLE),
+ lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+
+ kfifo_free(&lpc_bpc->ch[channel].fifo);
+ misc_deregister(&lpc_bpc->ch[channel].miscdev);
+}
+
+static int npcm7xx_bpc_probe(struct platform_device *pdev)
+{
+ struct npcm7xx_bpc *lpc_bpc;
+ struct resource *res;
+ struct device *dev;
+ u32 port;
+ int rc;
+
+ dev = &pdev->dev;
+
+ lpc_bpc = devm_kzalloc(dev, sizeof(*lpc_bpc), GFP_KERNEL);
+ if (!lpc_bpc)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "BIOS post code reg resource not found\n");
+ return -ENODEV;
+ }
+
+ dev_dbg(dev, "BIOS post code base resource is %pR\n", res);
+ lpc_bpc->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(lpc_bpc->base))
+ return PTR_ERR(lpc_bpc->base);
+
+ dev_set_drvdata(&pdev->dev, lpc_bpc);
+
+ rc = of_property_read_u32_index(dev->of_node, "monitor-ports", 0,
+ &port);
+ if (rc) {
+ dev_err(dev, "no monitor ports configured\n");
+ return -ENODEV;
+ }
+
+ lpc_bpc->en_dwcap =
+ of_property_read_bool(dev->of_node, "bpc-en-dwcapture");
+
+ rc = npcm7xx_bpc_config_irq(lpc_bpc, pdev);
+ if (rc)
+ return rc;
+
+ rc = npcm7xx_enable_bpc(lpc_bpc, dev, 0, port);
+ if (rc) {
+ dev_err(dev, "Enable BIOS post code I/O port 0 failed\n");
+ return rc;
+ }
+
+ /*
+ * Configuration of second BPC channel port is optional
+ * Double-Word Capture ignoring address 2
+ */
+ if (!lpc_bpc->en_dwcap) {
+ if (of_property_read_u32_index(dev->of_node, "monitor-ports",
+ 1, &port) == 0) {
+ rc = npcm7xx_enable_bpc(lpc_bpc, dev, 1, port);
+ if (rc) {
+ dev_err(dev, "Enable BIOS post code I/O port 1 failed, disable I/O port 0\n");
+ npcm7xx_disable_bpc(lpc_bpc, 0);
+ return rc;
+ }
+ }
+ }
+
+ pr_info("npcm7xx BIOS post code probe\n");
+
+ return rc;
+}
+
+static int npcm7xx_bpc_remove(struct platform_device *pdev)
+{
+ struct npcm7xx_bpc *lpc_bpc = dev_get_drvdata(&pdev->dev);
+ u8 reg_en;
+
+ reg_en = ioread8(lpc_bpc->base + NPCM7XX_BPCFEN_REG);
+
+ if (reg_en & FIFO_IOADDR1_ENABLE)
+ npcm7xx_disable_bpc(lpc_bpc, 0);
+ if (reg_en & FIFO_IOADDR2_ENABLE)
+ npcm7xx_disable_bpc(lpc_bpc, 1);
+
+ return 0;
+}
+
+static const struct of_device_id npcm7xx_bpc_match[] = {
+ { .compatible = "nuvoton,npcm750-lpc-bpc" },
+ { },
+};
+
+static struct platform_driver npcm7xx_bpc_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = npcm7xx_bpc_match,
+ },
+ .probe = npcm7xx_bpc_probe,
+ .remove = npcm7xx_bpc_remove,
+};
+
+module_platform_driver(npcm7xx_bpc_driver);
+
+MODULE_DEVICE_TABLE(of, npcm7xx_bpc_match);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tomer Maimon <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] 19+ messages in thread
* [linux dev-4.19 08/15] dt-binding: bmc: add npcm7xx pci mailbox document
2019-01-14 13:06 [linux dev-4.19 00/15] dd NPCM7xx patches to dev-4.19 Tomer Maimon
` (6 preceding siblings ...)
2019-01-14 13:07 ` [linux dev-4.19 07/15] misc: npcm7xx-lpc-bpc: add NPCM7xx BIOS post code driver Tomer Maimon
@ 2019-01-14 13:07 ` Tomer Maimon
2019-01-14 13:07 ` [linux dev-4.19 09/15] misc: mbox: add npcm7xx pci mailbox driver Tomer Maimon
` (7 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Tomer Maimon @ 2019-01-14 13:07 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] 19+ messages in thread
* [linux dev-4.19 09/15] misc: mbox: add npcm7xx pci mailbox driver
2019-01-14 13:06 [linux dev-4.19 00/15] dd NPCM7xx patches to dev-4.19 Tomer Maimon
` (7 preceding siblings ...)
2019-01-14 13:07 ` [linux dev-4.19 08/15] dt-binding: bmc: add npcm7xx pci mailbox document Tomer Maimon
@ 2019-01-14 13:07 ` Tomer Maimon
2019-01-14 13:07 ` [linux dev-4.19 10/15] dt-binding: net: document NPCM7xx EMC DT bindings Tomer Maimon
` (6 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Tomer Maimon @ 2019-01-14 13:07 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..00d1c547ece7 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -528,6 +528,13 @@ config NPCM7XX_LPC_BPC
interface which allows the BMC to monitoring and save
the data written by the host to an arbitrary LPC I/O port.
+config NPCM7XX_PCI_MBOX
+ tristate "NPCM7xx PCI Mailbox Controller"
+ depends on (ARCH_NPCM7XX || COMPILE_TEST) && REGMAP && MFD_SYSCON
+ help
+ Expose the NPCM750/730/715/705 PCI MBOX registers found on
+ Nuvoton SOCs to userspace.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 2dc74726e3b0..768278b059c3 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -60,3 +60,4 @@ obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o
obj-$(CONFIG_OCXL) += ocxl/
obj-$(CONFIG_MISC_RTSX) += cardreader/
obj-$(CONFIG_NPCM7XX_LPC_BPC) += npcm7xx-lpc-bpc.o
+obj-$(CONFIG_NPCM7XX_PCI_MBOX) += npcm7xx-pci-mbox.o
diff --git a/drivers/misc/npcm7xx-pci-mbox.c b/drivers/misc/npcm7xx-pci-mbox.c
new file mode 100644
index 000000000000..df7f18fef1e4
--- /dev/null
+++ b/drivers/misc/npcm7xx-pci-mbox.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2014-2018 Nuvoton Technology corporation.
+
+#include <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 ((*ppos + count) > mbox->max_buf_size)
+ return -EINVAL;
+
+ if (file->f_flags & O_NONBLOCK) {
+ if (!mbox->cif0)
+ return -EAGAIN;
+ } else if (wait_event_interruptible(mbox->queue, mbox->cif0)) {
+ return -ERESTARTSYS;
+ }
+
+ spin_lock_irqsave(&mbox->lock, flags);
+
+ if (copy_to_user((void __user *)buf,
+ (const void *)(mbox->memory + *ppos), count)) {
+ spin_unlock_irqrestore(&mbox->lock, flags);
+ return -EFAULT;
+ }
+
+ mbox->cif0 = false;
+ spin_unlock_irqrestore(&mbox->lock, flags);
+ return count;
+}
+
+static ssize_t npcm7xx_mbox_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct npcm7xx_mbox *mbox = file_mbox(file);
+ unsigned long flags;
+
+ if (!access_ok(VERIFY_READ, buf, count))
+ return -EFAULT;
+
+ if ((*ppos + count) > mbox->max_buf_size)
+ return -EINVAL;
+
+ spin_lock_irqsave(&mbox->lock, flags);
+
+ if (copy_from_user((void *)(mbox->memory + *ppos),
+ (void __user *)buf, count)) {
+ spin_unlock_irqrestore(&mbox->lock, flags);
+ return -EFAULT;
+ }
+
+ regmap_update_bits(mbox->regmap, NPCM7XX_MBOX_BMBXCMD,
+ NPCM7XX_MBOX_ALL_HIF, NPCM7XX_MBOX_HIF_0);
+
+ spin_unlock_irqrestore(&mbox->lock, flags);
+ return count;
+}
+
+static unsigned int npcm7xx_mbox_poll(struct file *file, poll_table *wait)
+{
+ struct npcm7xx_mbox *mbox = file_mbox(file);
+ unsigned int mask = 0;
+
+ poll_wait(file, &mbox->queue, wait);
+ if (mbox->cif0)
+ mask |= POLLIN;
+
+ return mask;
+}
+
+static int npcm7xx_mbox_release(struct inode *inode, struct file *file)
+{
+ atomic_dec(&npcm7xx_mbox_open_count);
+ return 0;
+}
+
+static const struct file_operations npcm7xx_mbox_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_seek_end_llseek,
+ .read = npcm7xx_mbox_read,
+ .write = npcm7xx_mbox_write,
+ .open = npcm7xx_mbox_open,
+ .release = npcm7xx_mbox_release,
+ .poll = npcm7xx_mbox_poll,
+};
+
+static irqreturn_t npcm7xx_mbox_irq(int irq, void *arg)
+{
+ struct npcm7xx_mbox *mbox = arg;
+ u32 val;
+
+ regmap_read(mbox->regmap, NPCM7XX_MBOX_BMBXSTAT, &val);
+ if ((val & NPCM7XX_MBOX_CIF_0) != NPCM7XX_MBOX_CIF_0)
+ return IRQ_NONE;
+
+ /*
+ * Leave the status bit set so that we know the data is for us,
+ * clear it once it has been read.
+ */
+ mbox->cif0 = true;
+
+ /* Mask it off, we'll clear it when we the data gets read */
+ regmap_write_bits(mbox->regmap, NPCM7XX_MBOX_BMBXSTAT,
+ NPCM7XX_MBOX_ALL_CIF, NPCM7XX_MBOX_CIF_0);
+
+ wake_up(&mbox->queue);
+
+ return IRQ_HANDLED;
+}
+
+static int npcm7xx_mbox_config_irq(struct npcm7xx_mbox *mbox,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int rc, irq;
+ u32 val;
+
+ /* Disable all register based interrupts */
+ regmap_update_bits(mbox->regmap, NPCM7XX_MBOX_BMBXCTL,
+ NPCM7XX_MBOX_ALL_CIE, 0);
+/*
+ * These registers are write one to clear. Clear them.
+ * Per spec, cleared bits should not be re-cleared.
+ * Need to read and clear needed bits only, instead of blindly clearing all.
+ */
+ regmap_read(mbox->regmap, NPCM7XX_MBOX_BMBXSTAT, &val);
+ val &= NPCM7XX_MBOX_ALL_CIF;
+
+ /* If any bit is set, write back to clear */
+ if (val)
+ regmap_write_bits(mbox->regmap, NPCM7XX_MBOX_BMBXSTAT,
+ NPCM7XX_MBOX_ALL_CIF, val);
+
+ irq = irq_of_parse_and_map(dev->of_node, 0);
+ if (!irq)
+ return -ENODEV;
+
+ rc = devm_request_irq(dev, irq, npcm7xx_mbox_irq, 0, DEVICE_NAME, mbox);
+ if (rc < 0) {
+ dev_err(dev, "Unable to request IRQ %d\n", irq);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int npcm7xx_mbox_probe(struct platform_device *pdev)
+{
+ struct npcm7xx_mbox *mbox;
+ struct device *dev;
+ struct resource *res;
+ int rc;
+
+ dev = &pdev->dev;
+
+ mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
+ if (!mbox)
+ return -ENOMEM;
+
+ dev_set_drvdata(&pdev->dev, mbox);
+
+ mbox->regmap = syscon_node_to_regmap(dev->of_node);
+ if (IS_ERR(mbox->regmap)) {
+ dev_err(dev, "Couldn't get regmap\n");
+ return -ENODEV;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ mbox->memory = devm_ioremap_resource(dev, res);
+ if (IS_ERR(mbox->memory))
+ return PTR_ERR(mbox->memory);
+ mbox->max_buf_size = resource_size(res);
+
+ spin_lock_init(&mbox->lock);
+ init_waitqueue_head(&mbox->queue);
+
+ mbox->miscdev.minor = MISC_DYNAMIC_MINOR;
+ mbox->miscdev.name = DEVICE_NAME;
+ mbox->miscdev.fops = &npcm7xx_mbox_fops;
+ mbox->miscdev.parent = dev;
+ mbox->cif0 = false;
+ rc = misc_register(&mbox->miscdev);
+ if (rc) {
+ dev_err(dev, "Unable to register device\n");
+ return rc;
+ }
+
+ rc = npcm7xx_mbox_config_irq(mbox, pdev);
+ if (rc) {
+ dev_err(dev, "Failed to configure IRQ\n");
+ misc_deregister(&mbox->miscdev);
+ return rc;
+ }
+
+ pr_info("NPCM7xx PCI Mailbox probed\n");
+
+ return 0;
+}
+
+static int npcm7xx_mbox_remove(struct platform_device *pdev)
+{
+ struct npcm7xx_mbox *mbox = dev_get_drvdata(&pdev->dev);
+
+ misc_deregister(&mbox->miscdev);
+
+ return 0;
+}
+
+static const struct of_device_id npcm7xx_mbox_match[] = {
+ { .compatible = "nuvoton,npcm750-pci-mbox" },
+ { },
+};
+
+static struct platform_driver npcm7xx_mbox_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = npcm7xx_mbox_match,
+ },
+ .probe = npcm7xx_mbox_probe,
+ .remove = npcm7xx_mbox_remove,
+};
+
+module_platform_driver(npcm7xx_mbox_driver);
+
+MODULE_DEVICE_TABLE(of, npcm7xx_mbox_match);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
+MODULE_DESCRIPTION("NPCM7XX mailbox device driver");
--
2.14.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [linux dev-4.19 10/15] dt-binding: net: document NPCM7xx EMC DT bindings
2019-01-14 13:06 [linux dev-4.19 00/15] dd NPCM7xx patches to dev-4.19 Tomer Maimon
` (8 preceding siblings ...)
2019-01-14 13:07 ` [linux dev-4.19 09/15] misc: mbox: add npcm7xx pci mailbox driver Tomer Maimon
@ 2019-01-14 13:07 ` Tomer Maimon
2019-01-14 13:07 ` [linux dev-4.19 11/15] net: npcm: add NPCM7xx Ethernet MAC controller Tomer Maimon
` (5 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Tomer Maimon @ 2019-01-14 13:07 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>
Signed-off-by: Tomer Maimon <tmaimon77@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] 19+ messages in thread
* [linux dev-4.19 11/15] net: npcm: add NPCM7xx Ethernet MAC controller
2019-01-14 13:06 [linux dev-4.19 00/15] dd NPCM7xx patches to dev-4.19 Tomer Maimon
` (9 preceding siblings ...)
2019-01-14 13:07 ` [linux dev-4.19 10/15] dt-binding: net: document NPCM7xx EMC DT bindings Tomer Maimon
@ 2019-01-14 13:07 ` Tomer Maimon
2019-01-14 13:07 ` [linux dev-4.19 12/15] dt-binding: iio: add NPCM ADC documentation Tomer Maimon
` (4 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Tomer Maimon @ 2019-01-14 13:07 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>
Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
---
drivers/net/ethernet/nuvoton/Kconfig | 17 +-
drivers/net/ethernet/nuvoton/Makefile | 2 +
drivers/net/ethernet/nuvoton/npcm7xx_emc.c | 2091 ++++++++++++++++++++++++++++
3 files changed, 2109 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/nuvoton/npcm7xx_emc.c
diff --git a/drivers/net/ethernet/nuvoton/Kconfig b/drivers/net/ethernet/nuvoton/Kconfig
index 71c973f8e50f..b12997687cb1 100644
--- a/drivers/net/ethernet/nuvoton/Kconfig
+++ b/drivers/net/ethernet/nuvoton/Kconfig
@@ -5,7 +5,7 @@
config NET_VENDOR_NUVOTON
bool "Nuvoton devices"
default y
- depends on ARM && ARCH_W90X900
+ depends on ARM && (ARCH_W90X900 || ARCH_NPCM7XX)
---help---
If you have a network (Ethernet) card belonging to this class, say Y.
@@ -25,4 +25,19 @@ config W90P910_ETH
Say Y here if you want to use built-in Ethernet ports
on w90p910 processor.
+config NPCM7XX_EMC_ETH
+ bool "Nuvoton NPCM7XX Ethernet EMC"
+ depends on ARM && ARCH_NPCM7XX
+ select PHYLIB
+ select MII
+ help
+ Say Y here if you want to use built-in Ethernet MAC
+ on NPCM750 MCU.
+
+config NPCM7XX_EMC_ETH_DEBUG
+ bool "Nuvoton NPCM7XX Ethernet EMC debug"
+ depends on NPCM7XX_EMC_ETH
+ help
+ Say Y here if you want debug info via /proc/driver/npcm7xx_emc.x
+
endif # NET_VENDOR_NUVOTON
diff --git a/drivers/net/ethernet/nuvoton/Makefile b/drivers/net/ethernet/nuvoton/Makefile
index 171aa044bd3b..513a60647c55 100644
--- a/drivers/net/ethernet/nuvoton/Makefile
+++ b/drivers/net/ethernet/nuvoton/Makefile
@@ -2,4 +2,6 @@
# Makefile for the Nuvoton network device drivers.
#
+#Eternet 10/100 EMC
obj-$(CONFIG_W90P910_ETH) += w90p910_ether.o
+obj-$(CONFIG_NPCM7XX_EMC_ETH) += npcm7xx_emc.o
diff --git a/drivers/net/ethernet/nuvoton/npcm7xx_emc.c b/drivers/net/ethernet/nuvoton/npcm7xx_emc.c
new file mode 100644
index 000000000000..3615e227545e
--- /dev/null
+++ b/drivers/net/ethernet/nuvoton/npcm7xx_emc.c
@@ -0,0 +1,2091 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2014-2019 Nuvoton Technology corporation.
+
+#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG
+#define DEBUG
+#endif
+
+#include <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;
+#endif
+
+#define MFSEL1_OFFSET 0x00C
+#define MFSEL3_OFFSET 0x064
+#define INTCR_OFFSET 0x03C
+
+#define IPSRST1_OFFSET 0x020
+
+#define DRV_MODULE_NAME "npcm7xx-emc"
+#define DRV_MODULE_VERSION "3.90"
+
+/* Ethernet MAC Registers */
+#define REG_CAMCMR 0x00
+#define REG_CAMEN 0x04
+#define REG_CAMM_BASE 0x08
+#define REG_CAML_BASE 0x0c
+#define REG_TXDLSA 0x88
+#define REG_RXDLSA 0x8C
+#define REG_MCMDR 0x90
+#define REG_MIID 0x94
+#define REG_MIIDA 0x98
+#define REG_FFTCR 0x9C
+#define REG_TSDR 0xa0
+#define REG_RSDR 0xa4
+#define REG_DMARFC 0xa8
+#define REG_MIEN 0xac
+#define REG_MISTA 0xb0
+#define REG_MGSTA 0xb4
+#define REG_MPCNT 0xb8
+#define REG_MRPC 0xbc
+#define REG_MRPCC 0xc0
+#define REG_MREPC 0xc4
+#define REG_DMARFS 0xc8
+#define REG_CTXDSA 0xcc
+#define REG_CTXBSA 0xd0
+#define REG_CRXDSA 0xd4
+#define REG_CRXBSA 0xd8
+
+/* EMC Diagnostic Registers */
+#define REG_RXFSM 0x200
+#define REG_TXFSM 0x204
+#define REG_FSM0 0x208
+#define REG_FSM1 0x20c
+#define REG_DCR 0x210
+#define REG_DMMIR 0x214
+#define REG_BISTR 0x300
+
+/* mac controller bit */
+#define MCMDR_RXON BIT(0)
+#define MCMDR_ALP BIT(1)
+#define MCMDR_ACP BIT(3)
+#define MCMDR_SPCRC BIT(5)
+#define MCMDR_TXON BIT(8)
+#define MCMDR_NDEF BIT(9)
+#define MCMDR_FDUP BIT(18)
+#define MCMDR_ENMDC BIT(19)
+#define MCMDR_OPMOD BIT(20)
+#define SWR BIT(24)
+
+/* cam command regiser */
+#define CAMCMR_AUP BIT(0)
+#define CAMCMR_AMP BIT(1)
+#define CAMCMR_ABP BIT(2)
+#define CAMCMR_CCAM BIT(3)
+#define CAMCMR_ECMP BIT(4)
+
+/* cam enable regiser */
+#define CAM0EN BIT(0)
+
+/* mac mii controller bit */
+#define PHYAD BIT(8)
+#define PHYWR BIT(16)
+#define PHYBUSY BIT(17)
+#define PHYPRESP BIT(18)
+#define MDCON BIT(19)
+#define CAM_ENTRY_SIZE 0x08
+
+/* rx and tx status */
+#define TXDS_TXCP BIT(19)
+#define RXDS_CRCE BIT(17)
+#define RXDS_PTLE BIT(19)
+#define RXDS_RXGD BIT(20)
+#define RXDS_ALIE BIT(21)
+#define RXDS_RP BIT(22)
+
+/* mac interrupt status*/
+#define MISTA_RXINTR BIT(0)
+#define MISTA_CRCE BIT(1)
+#define MISTA_RXOV BIT(2)
+#define MISTA_PTLE BIT(3)
+#define MISTA_RXGD BIT(4)
+#define MISTA_ALIE BIT(5)
+#define MISTA_RP BIT(6)
+#define MISTA_MMP BIT(7)
+#define MISTA_DFOI BIT(8)
+#define MISTA_DENI BIT(9)
+#define MISTA_RDU BIT(10)
+#define MISTA_RXBERR BIT(11)
+#define MISTA_CFR BIT(14)
+#define MISTA_TXINTR BIT(16)
+#define MISTA_TXEMP BIT(17)
+#define MISTA_TXCP BIT(18)
+#define MISTA_EXDEF BIT(19)
+#define MISTA_NCS BIT(20)
+#define MISTA_TXABT BIT(21)
+#define MISTA_LC BIT(22)
+#define MISTA_TDU BIT(23)
+#define MISTA_TXBERR BIT(24)
+
+/* Transmit/Receive Start Demand Register */
+#define ENSTART BIT(0)
+
+#define ENRXINTR BIT(0)
+#define ENCRCE BIT(1)
+#define EMRXOV BIT(2)
+#define ENPTLE BIT(3)
+#define ENRXGD BIT(4)
+#define ENALIE BIT(5)
+#define ENRP BIT(6)
+#define ENMMP BIT(7)
+#define ENDFO BIT(8)
+#define ENDENI BIT(9)
+#define ENRDU BIT(10)
+#define ENRXBERR BIT(11)
+#define ENCFR BIT(14)
+#define ENTXINTR BIT(16)
+#define ENTXEMP BIT(17)
+#define ENTXCP BIT(18)
+#define ENTXDEF BIT(19)
+#define ENNCS BIT(20)
+#define ENTXABT BIT(21)
+#define ENLC BIT(22)
+#define ENTDU BIT(23)
+#define ENTXBERR BIT(24)
+
+/* rx and tx owner bit */
+#define RX_OWN_DMA BIT(31)
+#define TX_OWN_DMA BIT(31)
+
+/* tx frame desc controller bit */
+#define MACTXINTEN BIT(2)
+#define CRCMODE BIT(1)
+#define PADDINGMODE BIT(0)
+
+/* fftcr controller bit */
+#define RXTHD (0x03 << 0)
+#define TXTHD (0x02 << 8)
+#define BLENGTH (0x02 << 20)
+
+/* global setting for driver */
+#define RX_QUEUE_LEN 128
+#define TX_QUEUE_LEN 64
+#define MAX_RBUFF_SZ 0x600
+#define MAX_TBUFF_SZ 0x600
+#define TX_TIMEOUT 50
+#define DELAY 1000
+#define CAM0 0x0
+#define RX_POLL_SIZE 16
+
+#ifdef CONFIG_VLAN_8021Q
+#define IS_VLAN 1
+#else
+#define IS_VLAN 0
+#endif
+
+#define MAX_PACKET_SIZE (1514 + (IS_VLAN * 4))
+#define MAX_PACKET_SIZE_W_CRC (MAX_PACKET_SIZE + 4) /* 1518 */
+
+#define MHZ (1000 * 1000)
+#define MII_TIMEOUT 100
+
+struct plat_npcm7xx_emc_data {
+ char *phy_bus_name;
+ int phy_addr;
+ unsigned char mac_addr[ETH_ALEN];
+};
+
+struct npcm7xx_rxbd {
+ __le32 sl;
+ __le32 buffer;
+ __le32 reserved;
+ __le32 next;
+};
+
+struct npcm7xx_txbd {
+ __le32 mode; /* Ownership bit and some other bits */
+ __le32 buffer; /* Transmit Buffer Starting Address */
+ __le32 sl; /* Transmit Byte Count and status bits */
+ __le32 next; /* Next Tx Descriptor Starting Address */
+};
+
+struct npcm7xx_ether {
+ struct sk_buff *rx_skb[RX_QUEUE_LEN];
+ struct sk_buff *tx_skb[TX_QUEUE_LEN];
+ spinlock_t lock; /* lock sk */
+ struct npcm7xx_rxbd *rdesc;
+ struct npcm7xx_txbd *tdesc;
+ dma_addr_t rdesc_phys;
+ dma_addr_t tdesc_phys;
+ struct net_device_stats stats;
+ struct platform_device *pdev;
+ struct net_device *ndev;
+ struct resource *res;
+ unsigned int msg_enable;
+ struct mii_bus *mii_bus;
+ struct phy_device *phy_dev;
+ struct napi_struct napi;
+ struct ncsi_dev *ncsidev;
+ bool use_ncsi;
+ void __iomem *reg;
+ int rxirq;
+ int txirq;
+ unsigned int cur_tx;
+ unsigned int cur_rx;
+ unsigned int finish_tx;
+ unsigned int pending_tx;
+ __le32 start_tx_ptr;
+ __le32 start_rx_ptr;
+ unsigned int rx_berr;
+ unsigned int rx_err;
+ unsigned int rdu;
+ unsigned int rxov;
+ __le32 camcmr;
+ unsigned int rx_stuck;
+ int link;
+ int speed;
+ int duplex;
+ int need_reset;
+ char *dump_buf;
+ struct regmap *rst_regmap;
+
+ /* debug counters */
+ unsigned int max_waiting_rx;
+ unsigned int rx_count_pool;
+ unsigned int count_xmit;
+ unsigned int rx_int_count;
+ unsigned int rx_err_count;
+ unsigned int tx_int_count;
+ unsigned int tx_tdu;
+ unsigned int tx_tdu_i;
+ unsigned int tx_cp_i;
+ unsigned int count_finish;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dbgfs_dir;
+ struct dentry *dbgfs_status;
+ struct dentry *dbgfs_dma_cap;
+#endif
+};
+
+#if defined CONFIG_NPCM7XX_EMC_ETH_DEBUG || defined CONFIG_DEBUG_FS
+#define REG_PRINT(reg_name) {t = scnprintf(next, size, "%-10s = %08X\n", \
+ #reg_name, readl(ether->reg + reg_name)); size -= t; next += t; }
+#define DUMP_PRINT(f, x...) {t = scnprintf(next, size, f, ## x); size -= t; \
+ next += t; }
+
+static int npcm7xx_info_dump(char *buf, int count, struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct npcm7xx_txbd *txbd;
+ struct npcm7xx_rxbd *rxbd;
+ unsigned long flags;
+ unsigned int i, cur, txd_offset, rxd_offset;
+ char *next = buf;
+ unsigned int size = count;
+ int t;
+ int is_locked = spin_is_locked(ðer->lock);
+
+ if (!is_locked)
+ spin_lock_irqsave(ðer->lock, flags);
+
+ /* ------basic driver information ---- */
+ DUMP_PRINT("NPCM7XX EMC %s driver version: %s\n", dev->name,
+ DRV_MODULE_VERSION);
+
+ REG_PRINT(REG_CAMCMR);
+ REG_PRINT(REG_CAMEN);
+ REG_PRINT(REG_CAMM_BASE);
+ REG_PRINT(REG_CAML_BASE);
+ REG_PRINT(REG_TXDLSA);
+ REG_PRINT(REG_RXDLSA);
+ REG_PRINT(REG_MCMDR);
+ REG_PRINT(REG_MIID);
+ REG_PRINT(REG_MIIDA);
+ REG_PRINT(REG_FFTCR);
+ REG_PRINT(REG_TSDR);
+ REG_PRINT(REG_RSDR);
+ REG_PRINT(REG_DMARFC);
+ REG_PRINT(REG_MIEN);
+ REG_PRINT(REG_MISTA);
+ REG_PRINT(REG_MGSTA);
+ REG_PRINT(REG_MPCNT);
+ writel(0x7FFF, (ether->reg + REG_MPCNT));
+ REG_PRINT(REG_MRPC);
+ REG_PRINT(REG_MRPCC);
+ REG_PRINT(REG_MREPC);
+ REG_PRINT(REG_DMARFS);
+ REG_PRINT(REG_CTXDSA);
+ REG_PRINT(REG_CTXBSA);
+ REG_PRINT(REG_CRXDSA);
+ REG_PRINT(REG_CRXBSA);
+ REG_PRINT(REG_RXFSM);
+ REG_PRINT(REG_TXFSM);
+ REG_PRINT(REG_FSM0);
+ REG_PRINT(REG_FSM1);
+ REG_PRINT(REG_DCR);
+ REG_PRINT(REG_DMMIR);
+ REG_PRINT(REG_BISTR);
+ DUMP_PRINT("\n");
+
+ DUMP_PRINT("netif_queue %s\n\n", netif_queue_stopped(dev) ?
+ "Stopped" : "Running");
+ if (ether->rdesc)
+ DUMP_PRINT("napi is %s\n\n", test_bit(NAPI_STATE_SCHED,
+ ðer->napi.state) ?
+ "scheduled" :
+ "not scheduled");
+
+ txd_offset = (readl((ether->reg + REG_CTXDSA)) -
+ readl((ether->reg + REG_TXDLSA))) /
+ sizeof(struct npcm7xx_txbd);
+ DUMP_PRINT("TXD offset %6d\n", txd_offset);
+ DUMP_PRINT("cur_tx %6d\n", ether->cur_tx);
+ DUMP_PRINT("finish_tx %6d\n", ether->finish_tx);
+ DUMP_PRINT("pending_tx %6d\n", ether->pending_tx);
+ /* debug counters */
+ DUMP_PRINT("tx_tdu %6d\n", ether->tx_tdu);
+ ether->tx_tdu = 0;
+ DUMP_PRINT("tx_tdu_i %6d\n", ether->tx_tdu_i);
+ ether->tx_tdu_i = 0;
+ DUMP_PRINT("tx_cp_i %6d\n", ether->tx_cp_i);
+ ether->tx_cp_i = 0;
+ DUMP_PRINT("tx_int_count %6d\n", ether->tx_int_count);
+ ether->tx_int_count = 0;
+ DUMP_PRINT("count_xmit tx %6d\n", ether->count_xmit);
+ ether->count_xmit = 0;
+ DUMP_PRINT("count_finish %6d\n", ether->count_finish);
+ ether->count_finish = 0;
+ DUMP_PRINT("\n");
+
+ rxd_offset = (readl((ether->reg + REG_CRXDSA)) -
+ readl((ether->reg + REG_RXDLSA)))
+ / sizeof(struct npcm7xx_txbd);
+ DUMP_PRINT("RXD offset %6d\n", rxd_offset);
+ DUMP_PRINT("cur_rx %6d\n", ether->cur_rx);
+ DUMP_PRINT("rx_err %6d\n", ether->rx_err);
+ ether->rx_err = 0;
+ DUMP_PRINT("rx_berr %6d\n", ether->rx_berr);
+ ether->rx_berr = 0;
+ DUMP_PRINT("rx_stuck %6d\n", ether->rx_stuck);
+ ether->rx_stuck = 0;
+ DUMP_PRINT("rdu %6d\n", ether->rdu);
+ ether->rdu = 0;
+ DUMP_PRINT("rxov rx %6d\n", ether->rxov);
+ ether->rxov = 0;
+ /* debug counters */
+ DUMP_PRINT("rx_int_count %6d\n", ether->rx_int_count);
+ ether->rx_int_count = 0;
+ DUMP_PRINT("rx_err_count %6d\n", ether->rx_err_count);
+ ether->rx_err_count = 0;
+ DUMP_PRINT("rx_count_pool %6d\n", ether->rx_count_pool);
+ ether->rx_count_pool = 0;
+ DUMP_PRINT("max_waiting_rx %5d\n", ether->max_waiting_rx);
+ ether->max_waiting_rx = 0;
+ DUMP_PRINT("\n");
+ DUMP_PRINT("need_reset %5d\n", ether->need_reset);
+
+ if (ether->tdesc && ether->rdesc) {
+ cur = ether->finish_tx - 2;
+ for (i = 0; i < 3; i++) {
+ cur = (cur + 1) % TX_QUEUE_LEN;
+ txbd = (ether->tdesc + cur);
+ DUMP_PRINT("finish %3d txbd mode %08X buffer %08X sl %08X next %08X tx_skb %p\n",
+ cur, txbd->mode, txbd->buffer,
+ txbd->sl, txbd->next, ether->tx_skb[cur]);
+ }
+ DUMP_PRINT("\n");
+
+ cur = txd_offset - 2;
+ for (i = 0; i < 3; i++) {
+ cur = (cur + 1) % TX_QUEUE_LEN;
+ txbd = (ether->tdesc + cur);
+ DUMP_PRINT("txd_of %3d txbd mode %08X buffer %08X sl %08X next %08X\n",
+ cur, txbd->mode, txbd->buffer,
+ txbd->sl, txbd->next);
+ }
+ DUMP_PRINT("\n");
+
+ cur = ether->cur_tx - 63;
+ for (i = 0; i < 64; i++) {
+ cur = (cur + 1) % TX_QUEUE_LEN;
+ txbd = (ether->tdesc + cur);
+ DUMP_PRINT("cur_tx %3d txbd mode %08X buffer %08X sl %08X next %08X\n",
+ cur, txbd->mode, txbd->buffer,
+ txbd->sl, txbd->next);
+ }
+ DUMP_PRINT("\n");
+
+ cur = ether->cur_rx - 63;
+ for (i = 0; i < 64; i++) {
+ cur = (cur + 1) % RX_QUEUE_LEN;
+ rxbd = (ether->rdesc + cur);
+ DUMP_PRINT("cur_rx %3d rxbd sl %08X buffer %08X sl %08X next %08X\n",
+ cur, rxbd->sl, rxbd->buffer,
+ rxbd->reserved, rxbd->next);
+ }
+ DUMP_PRINT("\n");
+
+ cur = rxd_offset - 2;
+ for (i = 0; i < 3; i++) {
+ cur = (cur + 1) % RX_QUEUE_LEN;
+ rxbd = (ether->rdesc + cur);
+ DUMP_PRINT("rxd_of %3d rxbd sl %08X buffer %08X sl %08X next %08X\n",
+ cur, rxbd->sl, rxbd->buffer,
+ rxbd->reserved, rxbd->next);
+ }
+ DUMP_PRINT("\n");
+ }
+
+ if (!is_locked)
+ spin_unlock_irqrestore(ðer->lock, flags);
+
+ return count - size;
+}
+#endif
+
+#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG
+static void npcm7xx_info_print(struct net_device *dev)
+{
+ char *emc_dump_buf;
+ int count;
+ struct npcm7xx_ether *ether;
+ struct platform_device *pdev;
+ const size_t print_size = 5 * PAGE_SIZE;
+
+ ether = netdev_priv(dev);
+ pdev = ether->pdev;
+
+ emc_dump_buf = kmalloc(print_size, GFP_KERNEL);
+ if (!emc_dump_buf) {
+ dev_err(&pdev->dev, "kmalloc failed\n");
+ } else {
+ char c;
+ char *tmp_buf = emc_dump_buf;
+
+ count = npcm7xx_info_dump(emc_dump_buf, print_size, dev);
+ while (count > 512) {
+ c = tmp_buf[512];
+ tmp_buf[512] = 0;
+ dev_info(&pdev->dev, "%s", tmp_buf);
+ tmp_buf += 512;
+ tmp_buf[0] = c;
+ count -= 512;
+ }
+ dev_info(&pdev->dev, "%s", tmp_buf);
+ kfree(emc_dump_buf);
+ }
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+#include <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) {
+ ether->dump_buf = kmalloc(print_size, GFP_KERNEL);
+ if (!ether->dump_buf)
+ return -1;
+ npcm7xx_info_dump(ether->dump_buf, print_size, dev);
+ }
+
+ seq_printf(sf, "%s", ether->dump_buf);
+ if (sf->count < sf->size) {
+ kfree(ether->dump_buf);
+ ether->dump_buf = NULL;
+ }
+
+ return 0;
+}
+
+static int npcm7xx_debug_show_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, npcm7xx_debug_show, inode->i_private);
+}
+
+static const struct file_operations npcm7xx_debug_show_fops = {
+ .open = npcm7xx_debug_show_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int npcm7xx_debug_reset(struct seq_file *sf, void *v)
+{
+ struct net_device *dev = (struct net_device *)sf->private;
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ unsigned long flags;
+
+ seq_puts(sf, "Ask to reset the module\n");
+ spin_lock_irqsave(ðer->lock, flags);
+ writel(0, (ether->reg + REG_MIEN));
+ spin_unlock_irqrestore(ðer->lock, flags);
+ ether->need_reset = 1;
+ napi_schedule(ðer->napi);
+
+ return 0;
+}
+
+static int npcm7xx_debug_reset_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, npcm7xx_debug_reset, inode->i_private);
+}
+
+static const struct file_operations npcm7xx_debug_reset_fops = {
+ .owner = THIS_MODULE,
+ .open = npcm7xx_debug_reset_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int npcm7xx_debug_fs(struct npcm7xx_ether *ether)
+{
+ /* Create debugfs main directory if it doesn't exist yet */
+ if (!npcm7xx_fs_dir) {
+ npcm7xx_fs_dir = debugfs_create_dir(DRV_MODULE_NAME, NULL);
+
+ if (!npcm7xx_fs_dir || IS_ERR(npcm7xx_fs_dir)) {
+ dev_err(ðer->pdev->dev, "ERROR %s, debugfs create directory failed\n",
+ DRV_MODULE_NAME);
+ return -ENOMEM;
+ }
+ }
+
+ /* Create per netdev entries */
+ ether->dbgfs_dir = debugfs_create_dir(ether->ndev->name,
+ npcm7xx_fs_dir);
+ if (!ether->dbgfs_dir || IS_ERR(ether->dbgfs_dir)) {
+ dev_err(ðer->pdev->dev, "ERROR failed to create %s directory\n", ether->ndev->name);
+ return -ENOMEM;
+ }
+
+ /* Entry to report DMA RX/TX rings */
+ ether->dbgfs_status =
+ debugfs_create_file("status", 0444,
+ ether->dbgfs_dir, ether->ndev,
+ &npcm7xx_debug_show_fops);
+
+ if (!ether->dbgfs_status || IS_ERR(ether->dbgfs_status)) {
+ dev_err(ðer->pdev->dev, "ERROR creating \'status\' debugfs file\n");
+ debugfs_remove_recursive(ether->dbgfs_dir);
+
+ return -ENOMEM;
+ }
+
+ /* Entry to report the DMA HW features */
+ ether->dbgfs_dma_cap = debugfs_create_file("do_reset", 0444,
+ ether->dbgfs_dir,
+ ether->ndev,
+ &npcm7xx_debug_reset_fops);
+
+ if (!ether->dbgfs_dma_cap || IS_ERR(ether->dbgfs_dma_cap)) {
+ dev_err(ðer->pdev->dev, "ERROR creating stmmac \'do_reset\' debugfs file\n");
+ debugfs_remove_recursive(ether->dbgfs_dir);
+
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+#endif
+
+static void npcm7xx_opmode(struct net_device *dev, int speed, int duplex)
+{
+ __le32 val;
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+
+ val = readl((ether->reg + REG_MCMDR));
+ if (speed == 100)
+ val |= MCMDR_OPMOD;
+ else
+ val &= ~MCMDR_OPMOD;
+
+ if (duplex == DUPLEX_FULL)
+ val |= MCMDR_FDUP;
+ else
+ val &= ~MCMDR_FDUP;
+
+ writel(val, (ether->reg + REG_MCMDR));
+}
+
+static void adjust_link(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct phy_device *phydev = ether->phy_dev;
+ bool status_change = false;
+ unsigned long flags;
+
+ /* clear GPIO interrupt status whihc indicates PHY statu change? */
+ spin_lock_irqsave(ðer->lock, flags);
+
+ if (phydev->link) {
+ if (ether->speed != phydev->speed ||
+ ether->duplex != phydev->duplex) {
+ ether->speed = phydev->speed;
+ ether->duplex = phydev->duplex;
+ status_change = true;
+ }
+ } else {
+ ether->speed = 0;
+ ether->duplex = -1;
+ }
+
+ if (phydev->link != ether->link) {
+ ether->link = phydev->link;
+ status_change = true;
+ }
+
+ spin_unlock_irqrestore(ðer->lock, flags);
+
+ if (status_change)
+ npcm7xx_opmode(dev, ether->speed, ether->duplex);
+}
+
+static void npcm7xx_write_cam(struct net_device *dev,
+ unsigned int x, unsigned char *pval)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ __le32 msw, lsw;
+
+ msw = (pval[0] << 24) | (pval[1] << 16) | (pval[2] << 8) | pval[3];
+
+ lsw = (pval[4] << 24) | (pval[5] << 16);
+
+ writel(lsw, (ether->reg + REG_CAML_BASE) + x * CAM_ENTRY_SIZE);
+ writel(msw, (ether->reg + REG_CAMM_BASE) + x * CAM_ENTRY_SIZE);
+ dev_dbg(ðer->pdev->dev, "REG_CAML_BASE = 0x%08X REG_CAMM_BASE = 0x%08X", lsw, msw);
+}
+
+static struct sk_buff *get_new_skb(struct net_device *dev, u32 i)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct sk_buff *skb = dev_alloc_skb(roundup(MAX_PACKET_SIZE_W_CRC, 4));
+
+ if (!skb)
+ return NULL;
+
+ /* Do not unmark the following skb_reserve() Receive Buffer Starting
+ * Address must be aligned to 4 bytes and the following line
+ * if unmarked will make it align to 2 and this likely will
+ * hult the RX and crash the linux skb_reserve(skb, NET_IP_ALIGN);
+ */
+ skb->dev = dev;
+ (ether->rdesc + i)->buffer =
+ dma_map_single(&dev->dev, skb->data,
+ roundup(MAX_PACKET_SIZE_W_CRC, 4),
+ DMA_FROM_DEVICE);
+ ether->rx_skb[i] = skb;
+
+ return skb;
+}
+
+static int npcm7xx_init_desc(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether;
+ struct npcm7xx_txbd *tdesc;
+ struct npcm7xx_rxbd *rdesc;
+ struct platform_device *pdev;
+ unsigned int i;
+
+ ether = netdev_priv(dev);
+ pdev = ether->pdev;
+
+ if (!ether->tdesc) {
+ ether->tdesc = (struct npcm7xx_txbd *)
+ dma_alloc_coherent(&pdev->dev,
+ sizeof(struct npcm7xx_txbd) *
+ TX_QUEUE_LEN,
+ ðer->tdesc_phys,
+ GFP_KERNEL);
+
+ if (!ether->tdesc) {
+ dev_err(&pdev->dev, "Failed to allocate memory for tx desc\n");
+ return -ENOMEM;
+ }
+ }
+
+ if (!ether->rdesc) {
+ ether->rdesc = (struct npcm7xx_rxbd *)
+ dma_alloc_coherent(&pdev->dev,
+ sizeof(struct npcm7xx_rxbd) *
+ RX_QUEUE_LEN,
+ ðer->rdesc_phys,
+ GFP_KERNEL);
+
+ if (!ether->rdesc) {
+ dev_err(&pdev->dev, "Failed to allocate memory for rx desc\n");
+ dma_free_coherent(&pdev->dev,
+ sizeof(struct npcm7xx_txbd) *
+ TX_QUEUE_LEN, ether->tdesc,
+ ether->tdesc_phys);
+ ether->tdesc = NULL;
+ return -ENOMEM;
+ }
+ }
+
+ for (i = 0; i < TX_QUEUE_LEN; i++) {
+ unsigned int offset;
+
+ tdesc = (ether->tdesc + i);
+
+ if (i == TX_QUEUE_LEN - 1)
+ offset = 0;
+ else
+ offset = sizeof(struct npcm7xx_txbd) * (i + 1);
+
+ tdesc->next = ether->tdesc_phys + offset;
+ tdesc->buffer = (__le32)NULL;
+ tdesc->sl = 0;
+ tdesc->mode = 0;
+ }
+
+ ether->start_tx_ptr = ether->tdesc_phys;
+
+ for (i = 0; i < RX_QUEUE_LEN; i++) {
+ unsigned int offset;
+
+ rdesc = (ether->rdesc + i);
+
+ if (i == RX_QUEUE_LEN - 1)
+ offset = 0;
+ else
+ offset = sizeof(struct npcm7xx_rxbd) * (i + 1);
+
+ rdesc->next = ether->rdesc_phys + offset;
+ rdesc->sl = RX_OWN_DMA;
+
+ if (!get_new_skb(dev, i)) {
+ dev_err(&pdev->dev, "get_new_skb() failed\n");
+
+ for (; i != 0; i--) {
+ dma_unmap_single(&dev->dev, (dma_addr_t)
+ ((ether->rdesc + i)->buffer),
+ roundup(MAX_PACKET_SIZE_W_CRC,
+ 4), DMA_FROM_DEVICE);
+ dev_kfree_skb_any(ether->rx_skb[i]);
+ ether->rx_skb[i] = NULL;
+ }
+
+ dma_free_coherent(&pdev->dev,
+ sizeof(struct npcm7xx_txbd) *
+ TX_QUEUE_LEN,
+ ether->tdesc, ether->tdesc_phys);
+ dma_free_coherent(&pdev->dev,
+ sizeof(struct npcm7xx_rxbd) *
+ RX_QUEUE_LEN,
+ ether->rdesc, ether->rdesc_phys);
+
+ return -ENOMEM;
+ }
+ }
+
+ ether->start_rx_ptr = ether->rdesc_phys;
+ wmb();
+ for (i = 0; i < TX_QUEUE_LEN; i++)
+ ether->tx_skb[i] = NULL;
+
+ return 0;
+}
+
+/* This API must call with Tx/Rx stopped */
+static void npcm7xx_free_desc(struct net_device *dev,
+ bool free_also_descriptors)
+{
+ struct sk_buff *skb;
+ u32 i;
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct platform_device *pdev = ether->pdev;
+
+ for (i = 0; i < TX_QUEUE_LEN; i++) {
+ skb = ether->tx_skb[i];
+ if (skb) {
+ dma_unmap_single(&dev->dev, (dma_addr_t)((ether->tdesc +
+ i)->buffer),
+ skb->len, DMA_TO_DEVICE);
+ dev_kfree_skb_any(skb);
+ ether->tx_skb[i] = NULL;
+ }
+ }
+
+ for (i = 0; i < RX_QUEUE_LEN; i++) {
+ skb = ether->rx_skb[i];
+ if (skb) {
+ dma_unmap_single(&dev->dev, (dma_addr_t)((ether->rdesc +
+ i)->buffer),
+ roundup(MAX_PACKET_SIZE_W_CRC, 4),
+ DMA_FROM_DEVICE);
+ dev_kfree_skb_any(skb);
+ ether->rx_skb[i] = NULL;
+ }
+ }
+
+ if (free_also_descriptors) {
+ if (ether->tdesc)
+ dma_free_coherent(&pdev->dev,
+ sizeof(struct npcm7xx_txbd) *
+ TX_QUEUE_LEN,
+ ether->tdesc, ether->tdesc_phys);
+ ether->tdesc = NULL;
+
+ if (ether->rdesc)
+ dma_free_coherent(&pdev->dev,
+ sizeof(struct npcm7xx_rxbd) *
+ RX_QUEUE_LEN,
+ ether->rdesc, ether->rdesc_phys);
+ ether->rdesc = NULL;
+ }
+}
+
+static void npcm7xx_set_fifo_threshold(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ __le32 val;
+
+ val = RXTHD | TXTHD | BLENGTH;
+ writel(val, (ether->reg + REG_FFTCR));
+}
+
+static void npcm7xx_return_default_idle(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ __le32 val;
+ __le32 saved_bits;
+
+ val = readl((ether->reg + REG_MCMDR));
+ saved_bits = val & (MCMDR_FDUP | MCMDR_OPMOD);
+ val |= SWR;
+ writel(val, (ether->reg + REG_MCMDR));
+
+ /* During the EMC reset the AHB will read 0 from all registers,
+ * so in order to see if the reset finished we can't count on
+ * (ether->reg + REG_MCMDR).SWR to become 0, instead we read another
+ * register that its reset value is not 0,
+ * we choose (ether->reg + REG_FFTCR).
+ */
+ do {
+ val = readl((ether->reg + REG_FFTCR));
+ } while (val == 0);
+
+ /*
+ * Now we can verify if (ether->reg + REG_MCMDR).SWR became
+ * 0 (probably it will be 0 on the first read).
+ */
+ do {
+ val = readl((ether->reg + REG_MCMDR));
+ } while (val & SWR);
+
+ /* restore values */
+ writel(saved_bits, (ether->reg + REG_MCMDR));
+}
+
+static void npcm7xx_enable_mac_interrupt(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ __le32 val;
+
+ val = ENRXINTR | /* Start of RX interrupts */
+ ENCRCE |
+ EMRXOV |
+ (ENPTLE * (!IS_VLAN)) | /* If we don't support VLAN we want interrupt on long packets */
+ ENRXGD |
+ ENALIE |
+ ENRP |
+ ENMMP |
+ ENDFO |
+ /* ENDENI | */ /* We don't need interrupt on DMA Early Notification */
+ ENRDU | /* We don't need interrupt on Receive Descriptor Unavailable Interrupt */
+ ENRXBERR |
+ /* ENCFR | */
+ ENTXINTR | /* Start of TX interrupts */
+ ENTXEMP |
+ ENTXCP |
+ ENTXDEF |
+ ENNCS |
+ ENTXABT |
+ ENLC |
+ /* ENTDU | */ /* We don't need interrupt on Transmit Descriptor Unavailable at start of operation */
+ ENTXBERR;
+ writel(val, (ether->reg + REG_MIEN));
+}
+
+static void npcm7xx_get_and_clear_int(struct net_device *dev,
+ __le32 *val, __le32 mask)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+
+ *val = readl((ether->reg + REG_MISTA)) & mask;
+ writel(*val, (ether->reg + REG_MISTA));
+}
+
+static void npcm7xx_set_global_maccmd(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ __le32 val;
+
+ val = readl((ether->reg + REG_MCMDR));
+
+ val |= MCMDR_SPCRC | MCMDR_ENMDC | MCMDR_ACP | MCMDR_NDEF;
+ if (IS_VLAN) {
+ /*
+ * we set ALP accept long packets since VLAN packets
+ * are 4 bytes longer than 1518
+ */
+ val |= MCMDR_ALP;
+ /* limit receive length to 1522 bytes due to VLAN */
+ writel(MAX_PACKET_SIZE_W_CRC, (ether->reg + REG_DMARFC));
+ }
+ writel(val, (ether->reg + REG_MCMDR));
+}
+
+static void npcm7xx_enable_cam(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ __le32 val;
+
+ npcm7xx_write_cam(dev, CAM0, dev->dev_addr);
+
+ val = readl((ether->reg + REG_CAMEN));
+ val |= CAM0EN;
+ writel(val, (ether->reg + REG_CAMEN));
+}
+
+static void npcm7xx_set_curdest(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+
+ writel(ether->start_rx_ptr, (ether->reg + REG_RXDLSA));
+ writel(ether->start_tx_ptr, (ether->reg + REG_TXDLSA));
+}
+
+static void npcm7xx_ether_set_rx_mode(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether;
+ __le32 rx_mode;
+
+ ether = netdev_priv(dev);
+
+ dev_dbg(ðer->pdev->dev, "%s CAMCMR_AUP\n",
+ (dev->flags & IFF_PROMISC) ? "Set" : "Clear");
+ if (dev->flags & IFF_PROMISC)
+ rx_mode = CAMCMR_AUP | CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP;
+ else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev))
+ rx_mode = CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP;
+ else
+ rx_mode = CAMCMR_ECMP | CAMCMR_ABP;
+ writel(rx_mode, (ether->reg + REG_CAMCMR));
+ ether->camcmr = rx_mode;
+}
+
+static void npcm7xx_reset_mac(struct net_device *dev, int need_free)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+
+ netif_tx_lock(dev);
+
+ /* disable RX and TX */
+ writel(readl((ether->reg + REG_MCMDR)) & ~(MCMDR_TXON | MCMDR_RXON),
+ (ether->reg + REG_MCMDR));
+
+ npcm7xx_return_default_idle(dev);
+ npcm7xx_set_fifo_threshold(dev);
+
+ if (need_free)
+ npcm7xx_free_desc(dev, false);
+
+ npcm7xx_init_desc(dev);
+
+ ether->cur_tx = 0x0;
+ ether->finish_tx = 0x0;
+ ether->pending_tx = 0x0;
+ ether->cur_rx = 0x0;
+ ether->tx_tdu = 0;
+ ether->tx_tdu_i = 0;
+ ether->tx_cp_i = 0;
+
+ npcm7xx_set_curdest(dev);
+ npcm7xx_enable_cam(dev);
+ npcm7xx_ether_set_rx_mode(dev);
+ npcm7xx_enable_mac_interrupt(dev);
+ npcm7xx_set_global_maccmd(dev);
+
+ /* enable RX and TX */
+ writel(readl((ether->reg + REG_MCMDR)) | MCMDR_TXON | MCMDR_RXON,
+ (ether->reg + REG_MCMDR));
+
+ /* trigger RX */
+ writel(ENSTART, (ether->reg + REG_RSDR));
+
+ ether->need_reset = 0;
+
+ netif_wake_queue(dev);
+ netif_tx_unlock(dev);
+}
+
+static int npcm7xx_mdio_write(struct mii_bus *bus, int phy_id, int regnum,
+ u16 value)
+{
+ struct npcm7xx_ether *ether = bus->priv;
+ unsigned long timeout = jiffies + msecs_to_jiffies(MII_TIMEOUT * 100);
+
+ writel(value, (ether->reg + REG_MIID));
+ writel((phy_id << 0x08) | regnum | PHYBUSY | PHYWR,
+ (ether->reg + REG_MIIDA));
+
+ /* Wait for completion */
+ while (readl((ether->reg + REG_MIIDA)) & PHYBUSY) {
+ if (time_after(jiffies, timeout)) {
+ dev_dbg(ðer->pdev->dev, "mdio read timed out\n ether->reg = 0x%x phy_id=0x%x REG_MIIDA=0x%x\n",
+ (unsigned int)ether->reg, phy_id
+ , readl((ether->reg + REG_MIIDA)));
+ return -ETIMEDOUT;
+ }
+ cpu_relax();
+ }
+
+ return 0;
+}
+
+static int npcm7xx_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
+{
+ struct npcm7xx_ether *ether = bus->priv;
+ unsigned long timeout = jiffies + msecs_to_jiffies(MII_TIMEOUT * 100);
+
+ writel((phy_id << 0x08) | regnum | PHYBUSY, (ether->reg + REG_MIIDA));
+
+ /* Wait for completion */
+ while (readl((ether->reg + REG_MIIDA)) & PHYBUSY) {
+ if (time_after(jiffies, timeout)) {
+ dev_dbg(ðer->pdev->dev, "mdio read timed out\n ether->reg = 0x%x phy_id=0x%x REG_MIIDA=0x%x\n",
+ (unsigned int)ether->reg, phy_id
+ , readl((ether->reg + REG_MIIDA)));
+ return -ETIMEDOUT;
+ }
+ cpu_relax();
+ }
+
+ return readl((ether->reg + REG_MIID));
+}
+
+static int npcm7xx_mdio_reset(struct mii_bus *bus)
+{
+ /* reset EMAC engine?? */
+ return 0;
+}
+
+static int npcm7xx_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *address = addr;
+
+ if (!is_valid_ether_addr((u8 *)address->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(dev->dev_addr, address->sa_data, dev->addr_len);
+ npcm7xx_write_cam(dev, CAM0, dev->dev_addr);
+
+ return 0;
+}
+
+static int npcm7xx_ether_close(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+
+ npcm7xx_return_default_idle(dev);
+
+ if (ether->phy_dev)
+ phy_stop(ether->phy_dev);
+ else if (ether->use_ncsi)
+ ncsi_stop_dev(ether->ncsidev);
+
+ msleep(20);
+
+ free_irq(ether->txirq, dev);
+ free_irq(ether->rxirq, dev);
+
+ netif_stop_queue(dev);
+ napi_disable(ðer->napi);
+
+ npcm7xx_free_desc(dev, true);
+
+ kfree(ether->dump_buf);
+ ether->dump_buf = NULL;
+
+ return 0;
+}
+
+static struct net_device_stats *npcm7xx_ether_stats(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether;
+
+ ether = netdev_priv(dev);
+ return ðer->stats;
+}
+
+static int npcm7xx_clean_tx(struct net_device *dev, bool from_xmit)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct npcm7xx_txbd *txbd;
+ struct sk_buff *s;
+ dma_addr_t cur_entry, entry;
+ __le32 sl;
+
+ if (ether->pending_tx == 0)
+ return (0);
+
+ cur_entry = readl((ether->reg + REG_CTXDSA));
+
+ /* Release old used buffers */
+ entry = ether->tdesc_phys + sizeof(struct npcm7xx_txbd) *
+ (ether->finish_tx);
+
+ while (entry != cur_entry) {
+ txbd = (ether->tdesc + ether->finish_tx);
+ s = ether->tx_skb[ether->finish_tx];
+ if (!s)
+ break;
+
+ ether->count_finish++;
+
+ dma_unmap_single(&dev->dev, txbd->buffer, s->len,
+ DMA_TO_DEVICE);
+ consume_skb(s);
+ ether->tx_skb[ether->finish_tx] = NULL;
+
+ if (++ether->finish_tx >= TX_QUEUE_LEN)
+ ether->finish_tx = 0;
+ ether->pending_tx--;
+
+ sl = txbd->sl;
+ if (sl & TXDS_TXCP) {
+ ether->stats.tx_packets++;
+ ether->stats.tx_bytes += (sl & 0xFFFF);
+ } else {
+ ether->stats.tx_errors++;
+ }
+
+ entry = ether->tdesc_phys + sizeof(struct npcm7xx_txbd) *
+ (ether->finish_tx);
+ }
+
+ if (!from_xmit && unlikely(netif_queue_stopped(dev) &&
+ (TX_QUEUE_LEN - ether->pending_tx) > 1)) {
+ netif_tx_lock(dev);
+ if (netif_queue_stopped(dev) &&
+ (TX_QUEUE_LEN - ether->pending_tx) > 1) {
+ netif_wake_queue(dev);
+ }
+ netif_tx_unlock(dev);
+ }
+
+ return(0);
+}
+
+static int npcm7xx_ether_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct npcm7xx_txbd *txbd;
+ unsigned long flags;
+
+ ether->count_xmit++;
+
+ /* Insert new buffer */
+ txbd = (ether->tdesc + ether->cur_tx);
+ txbd->buffer = dma_map_single(&dev->dev, skb->data, skb->len,
+ DMA_TO_DEVICE);
+ ether->tx_skb[ether->cur_tx] = skb;
+ if (skb->len > MAX_PACKET_SIZE)
+ dev_err(ðer->pdev->dev, "skb->len (= %d) > MAX_PACKET_SIZE (= %d)\n",
+ skb->len, MAX_PACKET_SIZE);
+
+ txbd->sl = skb->len > MAX_PACKET_SIZE ? MAX_PACKET_SIZE : skb->len;
+ dma_wmb();
+
+ txbd->mode = TX_OWN_DMA | PADDINGMODE | CRCMODE;
+ wmb();
+
+ /* trigger TX */
+ writel(ENSTART, (ether->reg + REG_TSDR));
+
+ if (++ether->cur_tx >= TX_QUEUE_LEN)
+ ether->cur_tx = 0;
+
+ spin_lock_irqsave(ðer->lock, flags);
+ ether->pending_tx++;
+
+ npcm7xx_clean_tx(dev, true);
+
+ if (ether->pending_tx >= TX_QUEUE_LEN - 1) {
+ __le32 reg_mien;
+ unsigned int index_to_wake = ether->cur_tx +
+ ((TX_QUEUE_LEN * 3) / 4);
+
+ if (index_to_wake >= TX_QUEUE_LEN)
+ index_to_wake -= TX_QUEUE_LEN;
+
+ txbd = (ether->tdesc + index_to_wake);
+ txbd->mode = TX_OWN_DMA | PADDINGMODE | CRCMODE | MACTXINTEN;
+ wmb();
+
+ writel(MISTA_TDU, (ether->reg + REG_MISTA));
+ /* Clear TDU interrupt */
+ reg_mien = readl((ether->reg + REG_MIEN));
+
+ if (reg_mien != 0)
+ /* Enable TDU interrupt */
+ writel(reg_mien | ENTDU, (ether->reg + REG_MIEN));
+
+ ether->tx_tdu++;
+ netif_stop_queue(dev);
+ }
+
+ spin_unlock_irqrestore(ðer->lock, flags);
+
+ return 0;
+}
+
+static irqreturn_t npcm7xx_tx_interrupt(int irq, void *dev_id)
+{
+ struct npcm7xx_ether *ether;
+ struct platform_device *pdev;
+ struct net_device *dev;
+ __le32 status;
+ unsigned long flags;
+
+ dev = dev_id;
+ ether = netdev_priv(dev);
+ pdev = ether->pdev;
+
+ npcm7xx_get_and_clear_int(dev, &status, 0xFFFF0000);
+
+ ether->tx_int_count++;
+
+ if (status & MISTA_EXDEF)
+ dev_err(&pdev->dev, "emc defer exceed interrupt status=0x%08X\n"
+ , status);
+ else if (status & MISTA_TXBERR) {
+ dev_err(&pdev->dev, "emc bus error interrupt status=0x%08X\n",
+ status);
+#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG
+ npcm7xx_info_print(dev);
+#endif
+ spin_lock_irqsave(ðer->lock, flags);
+ writel(0, (ether->reg + REG_MIEN)); /* disable any interrupt */
+ spin_unlock_irqrestore(ðer->lock, flags);
+ ether->need_reset = 1;
+ } else if (status & ~(MISTA_TXINTR | MISTA_TXCP | MISTA_TDU))
+ dev_err(&pdev->dev, "emc other error interrupt status=0x%08X\n",
+ status);
+
+ /* if we got MISTA_TXCP | MISTA_TDU remove those interrupt and call napi */
+ if (status & (MISTA_TXCP | MISTA_TDU) &
+ readl((ether->reg + REG_MIEN))) {
+ __le32 reg_mien;
+
+ spin_lock_irqsave(ðer->lock, flags);
+ reg_mien = readl((ether->reg + REG_MIEN));
+ if (reg_mien & ENTDU)
+ /* Disable TDU interrupt */
+ writel(reg_mien & (~ENTDU), (ether->reg + REG_MIEN));
+
+ spin_unlock_irqrestore(ðer->lock, flags);
+
+ if (status & MISTA_TXCP)
+ ether->tx_cp_i++;
+ if (status & MISTA_TDU)
+ ether->tx_tdu_i++;
+ } else {
+ dev_dbg(&pdev->dev, "status=0x%08X\n", status);
+ }
+
+ napi_schedule(ðer->napi);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t npcm7xx_rx_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct platform_device *pdev = ether->pdev;
+ __le32 status;
+ unsigned long flags;
+ unsigned int any_err = 0;
+ __le32 rxfsm;
+
+ npcm7xx_get_and_clear_int(dev, &status, 0xFFFF);
+ ether->rx_int_count++;
+
+ if (unlikely(status & MISTA_RXBERR)) {
+ ether->rx_berr++;
+ dev_err(&pdev->dev, "emc rx bus error status=0x%08X\n", status);
+#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG
+ npcm7xx_info_print(dev);
+#endif
+ spin_lock_irqsave(ðer->lock, flags);
+ writel(0, (ether->reg + REG_MIEN)); /* disable any interrupt */
+ spin_unlock_irqrestore(ðer->lock, flags);
+ ether->need_reset = 1;
+ napi_schedule(ðer->napi);
+ return IRQ_HANDLED;
+ }
+
+ if (unlikely(status & (MISTA_RXOV | MISTA_RDU))) {
+ /*
+ * filter out all received packets until we have
+ * enough available buffer descriptors
+ */
+ writel(0, (ether->reg + REG_CAMCMR));
+ any_err = 1;
+ if (status & (MISTA_RXOV))
+ ether->rxov++;
+ if (status & (MISTA_RDU))
+ ether->rdu++;
+
+ /*
+ * workaround Errata 1.36: EMC Hangs on receiving 253-256
+ * byte packet
+ */
+ rxfsm = readl((ether->reg + REG_RXFSM));
+
+ if ((rxfsm & 0xFFFFF000) == 0x08044000) {
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ rxfsm = readl((ether->reg + REG_RXFSM));
+ if ((rxfsm & 0xFFFFF000) != 0x08044000)
+ break;
+ }
+ if (i == 32) {
+ ether->rx_stuck++;
+ spin_lock_irqsave(ðer->lock, flags);
+#ifdef CONFIG_NPCM7XX_EMC_ETH_DEBUG
+ npcm7xx_info_print(dev);
+#endif
+ writel(0, (ether->reg + REG_MIEN));
+ spin_unlock_irqrestore(ðer->lock, flags);
+ ether->need_reset = 1;
+ napi_schedule(ðer->napi);
+ dev_err(&pdev->dev, "stuck on REG_RXFSM = 0x%08X status=%08X doing reset!\n", rxfsm, status);
+ return IRQ_HANDLED;
+ }
+ }
+ }
+
+ /* echo MISTA status on unexpected flags although we don't do anithing with them */
+ if (unlikely(status &
+ (/* MISTA_RXINTR | */ /* Receive - all RX interrupt set this */
+ MISTA_CRCE | /* CRC Error */
+ /* MISTA_RXOV | */ /* Receive FIFO Overflow - we alread handled it */
+ (MISTA_PTLE * !IS_VLAN) | /* Packet Too Long is needed if VLAN is not supported */
+ /* MISTA_RXGD | */ /* Receive Good - this is the common good case */
+ MISTA_ALIE | /* Alignment Error */
+ MISTA_RP | /* Runt Packet */
+ MISTA_MMP | /* More Missed Packet */
+ MISTA_DFOI | /* Maximum Frame Length */
+ /* MISTA_DENI | */ /* DMA Early Notification - every packet get this */
+ /* MISTA_RDU | */ /* Receive Descriptor Unavailable */
+ /* MISTA_RXBERR | */ /* Receive Bus Error Interrupt - we alread handled it */
+ /* MISTA_CFR | */ /* Control Frame Receive - not an error */
+ 0))) {
+ dev_dbg(&pdev->dev, "emc rx MISTA status=0x%08X\n", status);
+ any_err = 1;
+ ether->rx_err++;
+ }
+
+ if (!any_err && ((status & MISTA_RXGD) == 0))
+ dev_err(&pdev->dev, "emc rx MISTA status=0x%08X\n", status);
+
+ spin_lock_irqsave(ðer->lock, flags);
+ writel(readl((ether->reg + REG_MIEN)) & ~ENRXGD,
+ (ether->reg + REG_MIEN));
+ spin_unlock_irqrestore(ðer->lock, flags);
+ napi_schedule(ðer->napi);
+
+ return IRQ_HANDLED;
+}
+
+static int npcm7xx_poll(struct napi_struct *napi, int budget)
+{
+ struct npcm7xx_ether *ether =
+ container_of(napi, struct npcm7xx_ether, napi);
+ struct npcm7xx_rxbd *rxbd;
+ struct net_device *dev = ether->ndev;
+ struct platform_device *pdev = ether->pdev;
+ struct sk_buff *skb, *s;
+ unsigned int length;
+ __le32 status;
+ unsigned long flags;
+ int rx_cnt = 0;
+ int complete = 0;
+ unsigned int rx_offset = (readl((ether->reg + REG_CRXDSA)) -
+ ether->start_rx_ptr) /
+ sizeof(struct npcm7xx_txbd);
+ unsigned int local_count = (rx_offset >= ether->cur_rx) ?
+ rx_offset - ether->cur_rx : rx_offset +
+ RX_QUEUE_LEN - ether->cur_rx;
+
+ if (local_count > ether->max_waiting_rx)
+ ether->max_waiting_rx = local_count;
+
+ if (local_count > (4 * RX_POLL_SIZE))
+ /*
+ * we are porbably in a storm of short packets and we don't
+ * want to get into RDU since short packets in RDU cause
+ * many RXOV which may cause EMC halt, so we filter out all
+ * coming packets
+ */
+ writel(0, (ether->reg + REG_CAMCMR));
+
+ if (local_count <= budget)
+ /* we can restore accepting of packets */
+ writel(ether->camcmr, (ether->reg + REG_CAMCMR));
+
+ spin_lock_irqsave(ðer->lock, flags);
+ npcm7xx_clean_tx(dev, false);
+ spin_unlock_irqrestore(ðer->lock, flags);
+
+ rxbd = (ether->rdesc + ether->cur_rx);
+
+ while (rx_cnt < budget) {
+ status = rxbd->sl;
+ if ((status & RX_OWN_DMA) == RX_OWN_DMA) {
+ complete = 1;
+ break;
+ }
+ /* for debug puposes we save the previous value */
+ rxbd->reserved = status;
+ s = ether->rx_skb[ether->cur_rx];
+ length = status & 0xFFFF;
+
+ /*
+ * If VLAN is not supporte RXDS_PTLE (packet too long) is also
+ * an error
+ */
+ if (likely((status & (RXDS_RXGD | RXDS_CRCE | RXDS_ALIE |
+ RXDS_RP | (IS_VLAN ? 0 : RXDS_PTLE))) ==
+ RXDS_RXGD) && likely(length <= MAX_PACKET_SIZE)) {
+ dma_unmap_single(&dev->dev, (dma_addr_t)rxbd->buffer,
+ roundup(MAX_PACKET_SIZE_W_CRC, 4),
+ DMA_FROM_DEVICE);
+
+ skb_put(s, length);
+ s->protocol = eth_type_trans(s, dev);
+ netif_receive_skb(s);
+ ether->stats.rx_packets++;
+ ether->stats.rx_bytes += length;
+ rx_cnt++;
+ ether->rx_count_pool++;
+
+ /* now we allocate new skb instead if the used one. */
+ skb = dev_alloc_skb(roundup(MAX_PACKET_SIZE_W_CRC, 4));
+ if (!skb) {
+ dev_err(&pdev->dev, "get skb buffer error\n");
+ ether->stats.rx_dropped++;
+ goto rx_out;
+ }
+
+ /* Do not unmark the following skb_reserve() Receive
+ * Buffer Starting Address must be aligned
+ * to 4 bytes and the following line if unmarked
+ * will make it align to 2 and this likely
+ * will hult the RX and crash the linux
+ * skb_reserve(skb, NET_IP_ALIGN);
+ */
+ skb->dev = dev;
+
+ rxbd->buffer = dma_map_single(&dev->dev, skb->data,
+ roundup(MAX_PACKET_SIZE_W_CRC, 4),
+ DMA_FROM_DEVICE);
+ ether->rx_skb[ether->cur_rx] = skb;
+ } else {
+ ether->rx_err_count++;
+ ether->stats.rx_errors++;
+ dev_dbg(&pdev->dev, "rx_errors = %lu status = 0x%08X\n",
+ ether->stats.rx_errors, status);
+
+ if (status & RXDS_RP) {
+ ether->stats.rx_length_errors++;
+ dev_dbg(&pdev->dev, "rx_length_errors = %lu\n",
+ ether->stats.rx_length_errors);
+ } else if (status & RXDS_CRCE) {
+ ether->stats.rx_crc_errors++;
+ dev_dbg(&pdev->dev, "rx_crc_errors = %lu\n",
+ ether->stats.rx_crc_errors);
+ } else if (status & RXDS_ALIE) {
+ ether->stats.rx_frame_errors++;
+ dev_dbg(&pdev->dev, "rx_frame_errors = %lu\n",
+ ether->stats.rx_frame_errors);
+ } else if (((!IS_VLAN) && (status & RXDS_PTLE)) ||
+ length > MAX_PACKET_SIZE) {
+ ether->stats.rx_length_errors++;
+ dev_dbg(&pdev->dev, "rx_length_errors = %lu\n",
+ ether->stats.rx_length_errors);
+ }
+ }
+
+ wmb();
+ rxbd->sl = RX_OWN_DMA;
+ wmb();
+
+ if (++ether->cur_rx >= RX_QUEUE_LEN)
+ ether->cur_rx = 0;
+
+ rxbd = (ether->rdesc + ether->cur_rx);
+ }
+
+ if (complete) {
+ napi_complete(napi);
+
+ if (ether->need_reset) {
+ dev_dbg(&pdev->dev, "Reset\n");
+ npcm7xx_reset_mac(dev, 1);
+ }
+
+ spin_lock_irqsave(ðer->lock, flags);
+ writel(readl((ether->reg + REG_MIEN)) | ENRXGD, (ether->reg +
+ REG_MIEN));
+ spin_unlock_irqrestore(ðer->lock, flags);
+ } else {
+ rx_offset = (readl((ether->reg + REG_CRXDSA)) -
+ ether->start_rx_ptr) / sizeof(struct npcm7xx_txbd);
+ local_count = (rx_offset >= ether->cur_rx) ? rx_offset -
+ ether->cur_rx : rx_offset + RX_QUEUE_LEN -
+ ether->cur_rx;
+
+ if (local_count > ether->max_waiting_rx)
+ ether->max_waiting_rx = local_count;
+
+ if (local_count > (3 * RX_POLL_SIZE))
+ /*
+ * we are porbably in a storm of short packets and
+ * we don't want to get into RDU since short packets in
+ * RDU cause many RXOV which may cause
+ * EMC halt, so we filter out all coming packets
+ */
+ writel(0, (ether->reg + REG_CAMCMR));
+ if (local_count <= RX_POLL_SIZE)
+ /* we can restore accepting of packets */
+ writel(ether->camcmr, (ether->reg + REG_CAMCMR));
+ }
+rx_out:
+
+ /* trigger RX */
+ writel(ENSTART, (ether->reg + REG_RSDR));
+ return rx_cnt;
+}
+
+static int npcm7xx_ether_open(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether;
+ struct platform_device *pdev;
+
+ ether = netdev_priv(dev);
+ pdev = ether->pdev;
+
+ if (ether->use_ncsi) {
+ ether->speed = 100;
+ ether->duplex = DUPLEX_FULL;
+ npcm7xx_opmode(dev, 100, DUPLEX_FULL);
+ }
+ npcm7xx_reset_mac(dev, 0);
+
+ if (request_irq(ether->txirq, npcm7xx_tx_interrupt, 0x0, pdev->name,
+ dev)) {
+ dev_err(&pdev->dev, "register irq tx failed\n");
+ npcm7xx_ether_close(dev);
+ return -EAGAIN;
+ }
+
+ if (request_irq(ether->rxirq, npcm7xx_rx_interrupt, 0x0, pdev->name,
+ dev)) {
+ dev_err(&pdev->dev, "register irq rx failed\n");
+ npcm7xx_ether_close(dev);
+ return -EAGAIN;
+ }
+
+ if (ether->phy_dev)
+ phy_start(ether->phy_dev);
+ else if (ether->use_ncsi)
+ netif_carrier_on(dev);
+
+ netif_start_queue(dev);
+ napi_enable(ðer->napi);
+
+ /* trigger RX */
+ writel(ENSTART, (ether->reg + REG_RSDR));
+
+ /* Start the NCSI device */
+ if (ether->use_ncsi) {
+ int err = ncsi_start_dev(ether->ncsidev);
+
+ if (err) {
+ npcm7xx_ether_close(dev);
+ return err;
+ }
+ }
+
+ dev_info(&pdev->dev, "%s is OPENED\n", dev->name);
+
+ return 0;
+}
+
+static int npcm7xx_ether_ioctl(struct net_device *dev,
+ struct ifreq *ifr, int cmd)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct phy_device *phydev = ether->phy_dev;
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ if (!phydev)
+ return -ENODEV;
+
+ return phy_mii_ioctl(phydev, ifr, cmd);
+}
+
+static void npcm7xx_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver));
+ strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version));
+ strlcpy(info->fw_version, "N/A", sizeof(info->fw_version));
+ strlcpy(info->bus_info, "N/A", sizeof(info->bus_info));
+}
+
+static int npcm7xx_get_settings(struct net_device *dev,
+ struct ethtool_link_ksettings *cmd)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct phy_device *phydev = ether->phy_dev;
+
+ if (!phydev)
+ return -ENODEV;
+
+ dev_info(ðer->pdev->dev, "\n\nnpcm7xx_get_settings\n");
+ phy_ethtool_ksettings_get(phydev, cmd);
+
+ return 0;
+}
+
+static int npcm7xx_set_settings(struct net_device *dev,
+ const struct ethtool_link_ksettings *cmd)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct phy_device *phydev = ether->phy_dev;
+ int ret;
+
+ if (!phydev)
+ return -ENODEV;
+
+ dev_info(ðer->pdev->dev, "\n\nnpcm7xx_set_settings\n");
+ ret = phy_ethtool_ksettings_set(phydev, cmd);
+
+ return ret;
+}
+
+static u32 npcm7xx_get_msglevel(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+
+ return ether->msg_enable;
+}
+
+static void npcm7xx_set_msglevel(struct net_device *dev, u32 level)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+
+ ether->msg_enable = level;
+}
+
+static const struct ethtool_ops npcm7xx_ether_ethtool_ops = {
+ .get_link_ksettings = npcm7xx_get_settings,
+ .set_link_ksettings = npcm7xx_set_settings,
+ .get_drvinfo = npcm7xx_get_drvinfo,
+ .get_msglevel = npcm7xx_get_msglevel,
+ .set_msglevel = npcm7xx_set_msglevel,
+ .get_link = ethtool_op_get_link,
+};
+
+static const struct net_device_ops npcm7xx_ether_netdev_ops = {
+ .ndo_open = npcm7xx_ether_open,
+ .ndo_stop = npcm7xx_ether_close,
+ .ndo_start_xmit = npcm7xx_ether_start_xmit,
+ .ndo_get_stats = npcm7xx_ether_stats,
+ .ndo_set_rx_mode = npcm7xx_ether_set_rx_mode,
+ .ndo_set_mac_address = npcm7xx_set_mac_address,
+ .ndo_do_ioctl = npcm7xx_ether_ioctl,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+};
+
+static void get_mac_address(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct platform_device *pdev = ether->pdev;
+ struct device_node *np = ether->pdev->dev.of_node;
+ const u8 *mac_address = NULL;
+
+ mac_address = of_get_mac_address(np);
+
+ if (mac_address != 0)
+ ether_addr_copy(dev->dev_addr, mac_address);
+
+ if (is_valid_ether_addr(dev->dev_addr)) {
+ dev_info(&pdev->dev, "%s: device MAC address : %pM\n",
+ pdev->name, dev->dev_addr);
+ } else {
+ eth_hw_addr_random(dev);
+ dev_info(&pdev->dev, "%s: device MAC address (random generator) %pM\n",
+ dev->name, dev->dev_addr);
+ }
+}
+
+static int npcm7xx_mii_setup(struct net_device *dev)
+{
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+ struct platform_device *pdev;
+ struct phy_device *phydev = NULL;
+ int i, err = 0;
+
+ pdev = ether->pdev;
+
+ ether->mii_bus = mdiobus_alloc();
+ if (!ether->mii_bus) {
+ err = -ENOMEM;
+ dev_err(&pdev->dev, "mdiobus_alloc() failed\n");
+ goto out0;
+ }
+
+ ether->mii_bus->name = "npcm7xx_rmii";
+ ether->mii_bus->read = &npcm7xx_mdio_read;
+ ether->mii_bus->write = &npcm7xx_mdio_write;
+ ether->mii_bus->reset = &npcm7xx_mdio_reset;
+ snprintf(ether->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
+ ether->pdev->name, ether->pdev->id);
+ dev_dbg(&pdev->dev, "%s ether->mii_bus->id=%s\n", __func__,
+ ether->mii_bus->id);
+ ether->mii_bus->priv = ether;
+ ether->mii_bus->parent = ðer->pdev->dev;
+
+ for (i = 0; i < PHY_MAX_ADDR; i++)
+ ether->mii_bus->irq[i] = PHY_POLL;
+
+ platform_set_drvdata(ether->pdev, ether->mii_bus);
+
+ /* Enable MDIO Clock */
+ writel(readl((ether->reg + REG_MCMDR)) | MCMDR_ENMDC,
+ (ether->reg + REG_MCMDR));
+
+ if (mdiobus_register(ether->mii_bus)) {
+ dev_err(&pdev->dev, "mdiobus_register() failed\n");
+ goto out2;
+ }
+
+ phydev = phy_find_first(ether->mii_bus);
+ if (!phydev) {
+ dev_err(&pdev->dev, "phy_find_first() failed\n");
+ goto out3;
+ }
+
+ dev_info(&pdev->dev, " name = %s ETH-Phy-Id = 0x%x\n",
+ phydev_name(phydev), phydev->phy_id);
+
+ phydev = phy_connect(dev, phydev_name(phydev),
+ &adjust_link,
+ PHY_INTERFACE_MODE_RMII);
+
+ dev_info(&pdev->dev, " ETH-Phy-Id = 0x%x name = %s\n",
+ phydev->phy_id, phydev->drv->name);
+
+ if (IS_ERR(phydev)) {
+ err = PTR_ERR(phydev);
+ dev_err(&pdev->dev, "phy_connect() failed - %d\n", err);
+ goto out3;
+ }
+
+ phydev->supported &= PHY_BASIC_FEATURES;
+ phydev->advertising = phydev->supported;
+ ether->phy_dev = phydev;
+
+ return 0;
+
+out3:
+ mdiobus_unregister(ether->mii_bus);
+out2:
+ kfree(ether->mii_bus->irq);
+ mdiobus_free(ether->mii_bus);
+out0:
+
+ return err;
+}
+
+static const struct of_device_id emc_dt_id[] = {
+ { .compatible = "nuvoton,npcm750-emc", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, emc_dt_id);
+
+static void npcm7xx_ncsi_handler(struct ncsi_dev *nd)
+{
+ if (unlikely(nd->state != ncsi_dev_state_functional))
+ return;
+
+ netdev_info(nd->dev, "NCSI interface %s\n",
+ nd->link_up ? "up" : "down");
+}
+
+static int npcm7xx_ether_probe(struct platform_device *pdev)
+{
+ struct npcm7xx_ether *ether;
+ struct net_device *dev;
+ int error;
+
+ struct clk *emc_clk = NULL;
+ struct device_node *np = pdev->dev.of_node;
+
+ pdev->id = of_alias_get_id(np, "ethernet");
+ if (pdev->id < 0)
+ pdev->id = 0;
+
+ emc_clk = devm_clk_get(&pdev->dev, NULL);
+
+ if (IS_ERR(emc_clk))
+ return PTR_ERR(emc_clk);
+
+ /* Enable Clock */
+ clk_prepare_enable(emc_clk);
+
+ error = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (error)
+ return -ENODEV;
+
+ dev = alloc_etherdev(sizeof(struct npcm7xx_ether));
+ if (!dev)
+ return -ENOMEM;
+
+ ether = netdev_priv(dev);
+
+ ether->rst_regmap =
+ syscon_regmap_lookup_by_compatible("nuvoton,npcm750-rst");
+ if (IS_ERR(ether->rst_regmap)) {
+ dev_err(&pdev->dev, "%s: failed to find nuvoton,npcm750-rst\n", __func__);
+ return IS_ERR(ether->rst_regmap);
+ }
+
+ /* Reset EMC module */
+ if (pdev->id == 0) {
+ regmap_update_bits(ether->rst_regmap, IPSRST1_OFFSET,
+ (0x1 << 6), (0x1 << 6));
+ regmap_update_bits(ether->rst_regmap, IPSRST1_OFFSET,
+ (0x1 << 6), 0);
+ }
+ if (pdev->id == 1) {
+ regmap_update_bits(ether->rst_regmap, IPSRST1_OFFSET,
+ (0x1 << 21), (0x1 << 21));
+ regmap_update_bits(ether->rst_regmap, IPSRST1_OFFSET,
+ (0x1 << 21), 0);
+ }
+
+ ether->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!ether->res) {
+ dev_err(&pdev->dev, "failed to get I/O memory\n");
+ error = -ENXIO;
+ goto failed_free;
+ }
+
+ if (!request_mem_region(ether->res->start,
+ resource_size(ether->res), pdev->name)) {
+ dev_err(&pdev->dev, "failed to request I/O memory\n");
+ error = -EBUSY;
+ goto failed_free;
+ }
+
+ ether->reg = ioremap(ether->res->start, resource_size(ether->res));
+ dev_dbg(&pdev->dev, "%s ether->reg = 0x%x\n", __func__,
+ (unsigned int)ether->reg);
+
+ if (!ether->reg) {
+ dev_err(&pdev->dev, "failed to remap I/O memory\n");
+ error = -ENXIO;
+ goto failed_free_mem;
+ }
+
+ ether->txirq = platform_get_irq(pdev, 0);
+ if (ether->txirq < 0) {
+ dev_err(&pdev->dev, "failed to get ether tx irq\n");
+ error = -ENXIO;
+ goto failed_free_io;
+ }
+
+ ether->rxirq = platform_get_irq(pdev, 1);
+ if (ether->rxirq < 0) {
+ dev_err(&pdev->dev, "failed to get ether rx irq\n");
+ error = -ENXIO;
+ goto failed_free_io;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+ platform_set_drvdata(pdev, dev);
+ ether->ndev = dev;
+
+ ether->pdev = pdev;
+ ether->msg_enable = NETIF_MSG_LINK;
+
+ dev->netdev_ops = &npcm7xx_ether_netdev_ops;
+ dev->ethtool_ops = &npcm7xx_ether_ethtool_ops;
+
+ dev->tx_queue_len = TX_QUEUE_LEN;
+ dev->dma = 0x0;
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ get_mac_address(dev);
+
+ ether->cur_tx = 0x0;
+ ether->cur_rx = 0x0;
+ ether->finish_tx = 0x0;
+ ether->pending_tx = 0x0;
+ ether->link = 0;
+ ether->speed = 100;
+ ether->duplex = DUPLEX_FULL;
+ ether->need_reset = 0;
+ ether->dump_buf = NULL;
+ ether->rx_berr = 0;
+ ether->rx_err = 0;
+ ether->rdu = 0;
+ ether->rxov = 0;
+ ether->rx_stuck = 0;
+ /* debug counters */
+ ether->max_waiting_rx = 0;
+ ether->rx_count_pool = 0;
+ ether->count_xmit = 0;
+ ether->rx_int_count = 0;
+ ether->rx_err_count = 0;
+ ether->tx_int_count = 0;
+ ether->count_finish = 0;
+ ether->tx_tdu = 0;
+ ether->tx_tdu_i = 0;
+ ether->tx_cp_i = 0;
+
+ spin_lock_init(ðer->lock);
+
+ netif_napi_add(dev, ðer->napi, npcm7xx_poll, RX_POLL_SIZE);
+
+ if (pdev->dev.of_node &&
+ of_get_property(pdev->dev.of_node, "use-ncsi", NULL)) {
+ if (!IS_ENABLED(CONFIG_NET_NCSI)) {
+ dev_err(&pdev->dev, "CONFIG_NET_NCSI not enabled\n");
+ error = -ENODEV;
+ goto failed_free_napi;
+ }
+ dev_info(&pdev->dev, "Using NCSI interface\n");
+ ether->use_ncsi = true;
+ ether->ncsidev = ncsi_register_dev(dev, npcm7xx_ncsi_handler);
+ if (!ether->ncsidev) {
+ error = -ENODEV;
+ goto failed_free_napi;
+ }
+ } else {
+ ether->use_ncsi = false;
+ error = npcm7xx_mii_setup(dev);
+ if (error < 0) {
+ dev_err(&pdev->dev, "npcm7xx_mii_setup err\n");
+ goto failed_free_napi;
+ }
+ }
+
+ error = register_netdev(dev);
+ if (error != 0) {
+ dev_err(&pdev->dev, "register_netdev() failed\n");
+ error = -ENODEV;
+ goto failed_free_napi;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ npcm7xx_debug_fs(ether);
+#endif
+
+ return 0;
+
+failed_free_napi:
+ netif_napi_del(ðer->napi);
+ platform_set_drvdata(pdev, NULL);
+failed_free_io:
+ iounmap(ether->reg);
+failed_free_mem:
+ release_mem_region(ether->res->start, resource_size(ether->res));
+failed_free:
+ free_netdev(dev);
+
+ return error;
+}
+
+static int npcm7xx_ether_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct npcm7xx_ether *ether = netdev_priv(dev);
+
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove_recursive(ether->dbgfs_dir);
+#endif
+
+ unregister_netdev(dev);
+
+ free_irq(ether->txirq, dev);
+ free_irq(ether->rxirq, dev);
+
+ if (ether->phy_dev)
+ phy_disconnect(ether->phy_dev);
+
+ mdiobus_unregister(ether->mii_bus);
+ kfree(ether->mii_bus->irq);
+ mdiobus_free(ether->mii_bus);
+
+ platform_set_drvdata(pdev, NULL);
+
+ free_netdev(dev);
+ return 0;
+}
+
+static struct platform_driver npcm7xx_ether_driver = {
+ .probe = npcm7xx_ether_probe,
+ .remove = npcm7xx_ether_remove,
+ .driver = {
+ .name = DRV_MODULE_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(emc_dt_id),
+ },
+};
+
+module_platform_driver(npcm7xx_ether_driver);
+
+MODULE_AUTHOR("Nuvoton Technology Corp.");
+MODULE_DESCRIPTION("NPCM750 EMC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:npcm750-emc");
+MODULE_VERSION(DRV_MODULE_VERSION);
--
2.14.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [linux dev-4.19 12/15] dt-binding: iio: add NPCM ADC documentation
2019-01-14 13:06 [linux dev-4.19 00/15] dd NPCM7xx patches to dev-4.19 Tomer Maimon
` (10 preceding siblings ...)
2019-01-14 13:07 ` [linux dev-4.19 11/15] net: npcm: add NPCM7xx Ethernet MAC controller Tomer Maimon
@ 2019-01-14 13:07 ` Tomer Maimon
2019-01-14 13:07 ` [linux dev-4.19 13/15] iio: adc: add NPCM ADC driver Tomer Maimon
` (3 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Tomer Maimon @ 2019-01-14 13:07 UTC (permalink / raw)
To: joel; +Cc: openbmc, Tomer Maimon
Added device tree binding documentation for Nuvoton BMC
NPCM Analog-to-Digital Converter(ADC).
Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
---
.../bindings/iio/adc/nuvoton,npcm-adc.txt | 35 ++++++++++++++++++++++
1 file changed, 35 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt
diff --git a/Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt b/Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt
new file mode 100644
index 000000000000..6f0843d837cc
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt
@@ -0,0 +1,35 @@
+Nuvoton NPCM Analog to Digital Converter (ADC)
+
+The NPCM ADC is a 10-bit converter for eight channel inputs.
+
+Required properties:
+- compatible : "nuvoton,npcm750-adc" for the NPCM7XX BMC.
+- reg : specifies physical base address and size of the registers.
+- interrupts : Contain the ADC interrupt with flags for falling edge.
+
+Optional properties:
+- clocks : phandle of ADC reference clock, in case the clock is not
+ added the ADC will use the default ADC sample rate.
+- vref-supply : The regulator supply ADC reference voltage, in case the
+ vref-supply is not added the ADC will use internal voltage
+ reference.
+
+Required Node in the NPCM7xx BMC:
+An additional register is present in the NPCM7xx SOC which is
+assumed to be in the same device tree, with and marked as
+compatible with "nuvoton,npcm750-rst".
+
+Example:
+
+adc: adc@f000c000 {
+ compatible = "nuvoton,npcm750-adc";
+ reg = <0xf000c000 0x8>;
+ interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk NPCM7XX_CLK_ADC>;
+};
+
+rst: rst@f0801000 {
+ compatible = "nuvoton,npcm750-rst", "syscon",
+ "simple-mfd";
+ reg = <0xf0801000 0x6C>;
+};
--
2.14.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [linux dev-4.19 13/15] iio: adc: add NPCM ADC driver
2019-01-14 13:06 [linux dev-4.19 00/15] dd NPCM7xx patches to dev-4.19 Tomer Maimon
` (11 preceding siblings ...)
2019-01-14 13:07 ` [linux dev-4.19 12/15] dt-binding: iio: add NPCM ADC documentation Tomer Maimon
@ 2019-01-14 13:07 ` Tomer Maimon
2019-01-14 13:07 ` [linux dev-4.19 14/15] arm: npcm: add defconfig for Nuvoton NPCM7xx BMC Tomer Maimon
` (2 subsequent siblings)
15 siblings, 0 replies; 19+ messages in thread
From: Tomer Maimon @ 2019-01-14 13:07 UTC (permalink / raw)
To: joel; +Cc: openbmc, Tomer Maimon
Add Nuvoton NPCM BMC Analog-to-Digital Converter(ADC) driver.
The NPCM ADC is a 10-bit converter for eight channel inputs.
Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
---
drivers/iio/adc/Kconfig | 10 ++
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/npcm_adc.c | 336 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 347 insertions(+)
create mode 100644 drivers/iio/adc/npcm_adc.c
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 4a754921fb6f..c261e12bdc01 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -545,6 +545,16 @@ config NAU7802
To compile this driver as a module, choose M here: the
module will be called nau7802.
+config NPCM_ADC
+ tristate "Nuvoton NPCM ADC driver"
+ depends on ARCH_NPCM || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Say yes here to build support for Nuvoton NPCM ADC.
+
+ This driver can also be built as a module. If so, the module
+ will be called npcm_adc.
+
config PALMAS_GPADC
tristate "TI Palmas General Purpose ADC"
depends on MFD_PALMAS
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 03db7b578f9c..68c7aa898c62 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
obj-$(CONFIG_MESON_SARADC) += meson_saradc.o
obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
obj-$(CONFIG_NAU7802) += nau7802.o
+obj-$(CONFIG_NPCM_ADC) += npcm_adc.o
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o
diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c
new file mode 100644
index 000000000000..4f7851472997
--- /dev/null
+++ b/drivers/iio/adc/npcm_adc.c
@@ -0,0 +1,336 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2016-2018 Nuvoton Technology corporation.
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/io.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+
+struct npcm_adc {
+ u32 vref_mv;
+ bool int_status;
+ u32 adc_sample_hz;
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *adc_clk;
+ wait_queue_head_t wq;
+ struct regulator *vref;
+ struct regmap *rst_regmap;
+};
+
+/* NPCM7xx reset module */
+#define IPSRST1_OFFSET 0x020
+#define IPSRST1_ADC_RST BIT(27)
+
+/* ADC registers */
+#define NPCM_ADCCON 0x00
+#define NPCM_ADCDATA 0x04
+
+/* ADCCON Register Bits */
+#define NPCM_ADCCON_ADC_INT_EN BIT(21)
+#define NPCM_ADCCON_REFSEL BIT(19)
+#define NPCM_ADCCON_ADC_INT_ST BIT(18)
+#define NPCM_ADCCON_ADC_EN BIT(17)
+#define NPCM_ADCCON_ADC_RST BIT(16)
+#define NPCM_ADCCON_ADC_CONV BIT(13)
+
+#define NPCM_ADCCON_CH_MASK GENMASK(27, 24)
+#define NPCM_ADCCON_CH(x) ((x) << 24)
+#define NPCM_ADCCON_DIV_SHIFT 1
+#define NPCM_ADCCON_DIV_MASK GENMASK(8, 1)
+#define NPCM_ADC_DATA_MASK(x) ((x) & GENMASK(9, 0))
+
+#define NPCM_ADC_ENABLE (NPCM_ADCCON_ADC_EN | NPCM_ADCCON_ADC_INT_EN)
+
+/* ADC General Definition */
+#define NPCM_RESOLUTION_BITS 10
+#define ADC_DEFAULT_SAMPLE_RATE 12500000
+#define INT_VREF_MV 2000
+
+#define NPCM_ADC_CHAN(ch) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = ch, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec npcm_adc_iio_channels[] = {
+ NPCM_ADC_CHAN(0),
+ NPCM_ADC_CHAN(1),
+ NPCM_ADC_CHAN(2),
+ NPCM_ADC_CHAN(3),
+ NPCM_ADC_CHAN(4),
+ NPCM_ADC_CHAN(5),
+ NPCM_ADC_CHAN(6),
+ NPCM_ADC_CHAN(7),
+};
+
+static irqreturn_t npcm_adc_isr(int irq, void *data)
+{
+ u32 regtemp = 0;
+ struct iio_dev *indio_dev = data;
+ struct npcm_adc *info = iio_priv(indio_dev);
+
+ regtemp = ioread32(info->regs + NPCM_ADCCON);
+ if (regtemp & NPCM_ADCCON_ADC_INT_ST) {
+ iowrite32(regtemp, info->regs + NPCM_ADCCON);
+ wake_up_interruptible(&info->wq);
+ info->int_status = true;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int npcm_adc_read(struct npcm_adc *info, int *val, u8 channel)
+{
+ int ret;
+ u32 regtemp = 0;
+
+ /* Select ADC channal */
+ regtemp = ioread32(info->regs + NPCM_ADCCON);
+ regtemp &= ~NPCM_ADCCON_CH_MASK;
+ iowrite32(regtemp | NPCM_ADCCON_CH(channel) |
+ NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON);
+
+ info->int_status = false;
+ ret = wait_event_interruptible_timeout(info->wq, info->int_status,
+ msecs_to_jiffies(10));
+ if (ret == 0) {
+ regtemp = ioread32(info->regs + NPCM_ADCCON);
+ if ((regtemp & NPCM_ADCCON_ADC_CONV) && info->rst_regmap) {
+ /* if conversion failed - reset ADC module */
+ regmap_write(info->rst_regmap, IPSRST1_OFFSET,
+ IPSRST1_ADC_RST);
+ msleep(100);
+ regmap_write(info->rst_regmap, IPSRST1_OFFSET, 0x0);
+ msleep(100);
+
+ /* Enable ADC and start conversion Module */
+ iowrite32(NPCM_ADC_ENABLE | NPCM_ADCCON_ADC_CONV,
+ info->regs + NPCM_ADCCON);
+ dev_err(info->dev, "RESET ADC Complete\n");
+ }
+ return -ETIMEDOUT;
+ }
+ if (ret < 0)
+ return ret;
+
+ *val = NPCM_ADC_DATA_MASK(ioread32(info->regs + NPCM_ADCDATA));
+
+ return 0;
+}
+
+static int npcm_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ int ret;
+ struct npcm_adc *info = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ ret = npcm_adc_read(info, val, chan->channel);
+ if (ret) {
+ dev_err(info->dev, "NPCM ADC read failed\n");
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+ mutex_unlock(&indio_dev->mlock);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = info->vref_mv;
+ *val2 = NPCM_RESOLUTION_BITS;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = info->adc_sample_hz;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct iio_info npcm_adc_iio_info = {
+ .read_raw = &npcm_adc_read_raw,
+};
+
+static const struct of_device_id npcm_adc_match[] = {
+ { .compatible = "nuvoton,npcm750-adc", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, npcm_adc_match);
+
+static int npcm_adc_probe(struct platform_device *pdev)
+{
+ int ret;
+ int irq;
+ u32 div;
+ u32 reg_con;
+ struct resource *res;
+ struct npcm_adc *info;
+ struct iio_dev *indio_dev;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
+ if (!indio_dev)
+ return -ENOMEM;
+ info = iio_priv(indio_dev);
+
+ info->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ info->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(info->regs))
+ return PTR_ERR(info->regs);
+
+ info->adc_clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(info->adc_clk)) {
+ dev_warn(&pdev->dev, "ADC clock failed: can't read clk, Assuming ADC clock Rate 12.5MHz\n");
+ info->adc_sample_hz = ADC_DEFAULT_SAMPLE_RATE;
+ } else {
+ /* calculate ADC clock sample rate */
+ reg_con = ioread32(info->regs + NPCM_ADCCON);
+ div = reg_con & NPCM_ADCCON_DIV_MASK;
+ div = div >> NPCM_ADCCON_DIV_SHIFT;
+ info->adc_sample_hz = clk_get_rate(info->adc_clk) /
+ ((div + 1) * 2);
+ }
+
+ if (of_device_is_compatible(np, "nuvoton,npcm750-adc")) {
+ info->rst_regmap = syscon_regmap_lookup_by_compatible
+ ("nuvoton,npcm750-rst");
+ if (IS_ERR(info->rst_regmap)) {
+ dev_err(&pdev->dev, "Failed to find nuvoton,npcm750-rst\n");
+ ret = PTR_ERR(info->rst_regmap);
+ goto err_disable_clk;
+ }
+ }
+
+ reg_con = ioread32(info->regs + NPCM_ADCCON);
+ info->vref = devm_regulator_get_optional(&pdev->dev, "vref");
+ if (!IS_ERR(info->vref)) {
+ ret = regulator_enable(info->vref);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't enable ADC reference voltage\n");
+ goto err_disable_clk;
+ }
+
+ ret = regulator_get_voltage(info->vref);
+ if (ret < 0)
+ goto err_disable_vref_reg;
+
+ info->vref_mv = ret / 1000;
+ iowrite32(reg_con & ~NPCM_ADCCON_REFSEL,
+ info->regs + NPCM_ADCCON);
+ } else {
+ /* Any other error indicates that the regulator does exist */
+ if (PTR_ERR(info->vref) != -ENODEV) {
+ goto err_disable_clk;
+ return PTR_ERR(info->vref);
+ }
+
+ /* Use internal reference */
+ info->vref_mv = INT_VREF_MV;
+ iowrite32(reg_con | NPCM_ADCCON_REFSEL,
+ info->regs + NPCM_ADCCON);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(dev, "failed getting interrupt resource\n");
+ ret = -EINVAL;
+ goto err_disable_vref_reg;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, npcm_adc_isr, 0,
+ "NPCM_ADC", indio_dev);
+ if (ret < 0) {
+ dev_err(dev, "failed requesting interrupt\n");
+ goto err_disable_vref_reg;
+ }
+
+ init_waitqueue_head(&info->wq);
+
+ platform_set_drvdata(pdev, indio_dev);
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &npcm_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = npcm_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(npcm_adc_iio_channels);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Couldn't register the device.\n");
+ goto err_disable_vref_reg;
+ }
+
+ reg_con = ioread32(info->regs + NPCM_ADCCON);
+ reg_con |= NPCM_ADC_ENABLE;
+
+ /* Enable the ADC Module */
+ iowrite32(reg_con, info->regs + NPCM_ADCCON);
+
+ /* Start ADC conversion */
+ iowrite32(reg_con | NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON);
+
+ pr_info("NPCM ADC driver probed, sample Rate %dHz\n",
+ info->adc_sample_hz);
+
+ return 0;
+
+err_disable_vref_reg:
+ if (!IS_ERR(info->vref))
+ regulator_disable(info->vref);
+err_disable_clk:
+ clk_disable_unprepare(info->adc_clk);
+
+ return ret;
+}
+
+static int npcm_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct npcm_adc *info = iio_priv(indio_dev);
+ u32 regtemp = 0;
+
+ regtemp = ioread32(info->regs + NPCM_ADCCON);
+
+ /* Disable the ADC Module */
+ iowrite32(regtemp & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON);
+
+ iio_device_unregister(indio_dev);
+ clk_disable_unprepare(info->adc_clk);
+ if (!IS_ERR(info->vref))
+ regulator_disable(info->vref);
+
+ return 0;
+}
+
+static struct platform_driver npcm_adc_driver = {
+ .probe = npcm_adc_probe,
+ .remove = npcm_adc_remove,
+ .driver = {
+ .name = "npcm_adc",
+ .of_match_table = npcm_adc_match,
+ },
+};
+
+module_platform_driver(npcm_adc_driver);
+
+MODULE_DESCRIPTION("Nuvoton NPCM ADC Sensor Driver");
+MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
--
2.14.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [linux dev-4.19 14/15] arm: npcm: add defconfig for Nuvoton NPCM7xx BMC
2019-01-14 13:06 [linux dev-4.19 00/15] dd NPCM7xx patches to dev-4.19 Tomer Maimon
` (12 preceding siblings ...)
2019-01-14 13:07 ` [linux dev-4.19 13/15] iio: adc: add NPCM ADC driver Tomer Maimon
@ 2019-01-14 13:07 ` Tomer Maimon
2019-01-14 13:07 ` [linux dev-4.19 15/15] dts: npcm7xx: Modify NPCM7xx device tree Tomer Maimon
2019-01-15 23:45 ` [linux dev-4.19 00/15] dd NPCM7xx patches to dev-4.19 Joel Stanley
15 siblings, 0 replies; 19+ messages in thread
From: Tomer Maimon @ 2019-01-14 13:07 UTC (permalink / raw)
To: joel; +Cc: openbmc, Tomer Maimon
Add configuration definition for Nuvoton
NPCM7xx (Poleg) BMC.
Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
---
arch/arm/configs/npcm7xx_defconfig | 122 +++++++++++++++++++++++++++++++++++++
1 file changed, 122 insertions(+)
create mode 100644 arch/arm/configs/npcm7xx_defconfig
diff --git a/arch/arm/configs/npcm7xx_defconfig b/arch/arm/configs/npcm7xx_defconfig
new file mode 100644
index 000000000000..afddb4e88300
--- /dev/null
+++ b/arch/arm/configs/npcm7xx_defconfig
@@ -0,0 +1,122 @@
+CONFIG_KERNEL_XZ=y
+CONFIG_SYSVIPC=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_LOG_BUF_SHIFT=21
+CONFIG_CGROUPS=y
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_EMBEDDED=y
+CONFIG_SLAB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_DEFAULT_DEADLINE=y
+CONFIG_ARCH_NPCM=y
+CONFIG_ARCH_NPCM7XX=y
+CONFIG_SMP=y
+CONFIG_VMSPLIT_3G_OPT=y
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_BINFMT_MISC=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_MTD=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_RAM=y
+CONFIG_MTD_COMPLEX_MAPPINGS=y
+CONFIG_MTD_M25P80=y
+CONFIG_MTD_SPI_NOR=y
+CONFIG_SPI_NPCM_FIU=y
+CONFIG_OF_OVERLAY=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_NBD=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=1
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_NPCM7XX_LPC_BPC=y
+CONFIG_NPCM7XX_PCI_MBOX=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_NETDEVICES=y
+CONFIG_NPCM7XX_EMC_ETH=y
+CONFIG_STMMAC_ETH=y
+CONFIG_BROADCOM_PHY=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_NPCM750_OTP=y
+CONFIG_NPCM750_OTP_WRITE_ENABLE=y
+CONFIG_NPCM7XX_KCS_IPMI_BMC=y
+CONFIG_HW_RANDOM=y
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_NPCM7XX=y
+CONFIG_I2C_SLAVE_EEPROM=y
+CONFIG_SPI=y
+CONFIG_SPI_NPCM_PSPI=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_GENERIC_PLATFORM=y
+CONFIG_SENSORS_LM75=y
+CONFIG_SENSORS_NPCM7XX=y
+CONFIG_SENSORS_TMP102=y
+CONFIG_WATCHDOG=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_NPCM750_VCD=y
+CONFIG_NPCM750_ECE=y
+CONFIG_USB_HIDDEV=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_CHIPIDEA=y
+CONFIG_USB_CHIPIDEA_UDC=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_NPCMX50_USB2=y
+CONFIG_USB_CONFIGFS=y
+CONFIG_USB_CONFIGFS_F_HID=y
+CONFIG_USB_MASS_STORAGE=m
+CONFIG_USB_EDM_KBD_MOUSE=m
+CONFIG_MMC=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_NPCM750=y
+CONFIG_IIO=y
+CONFIG_NPCM_ADC=y
+CONFIG_IIO_MUX=y
+CONFIG_MUX_MMIO=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_ROMFS_FS=y
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+CONFIG_CIFS=y
+CONFIG_CIFS_XATTR=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_INFO_REDUCED=y
+CONFIG_READABLE_ASM=y
+CONFIG_DEBUG_SECTION_MISMATCH=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_FUNCTION_TRACER=y
+CONFIG_CRYPTO_CBC=y
+CONFIG_CRYPTO_CRC32C=y
+CONFIG_CRYPTO_DEFLATE=y
+CONFIG_CRYPTO_LZO=y
+CONFIG_CRYPTO_USER_API_SKCIPHER=y
+CONFIG_CRYPTO_DEV_NPCMX50=y
+CONFIG_ARM_CRYPTO=y
--
2.14.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [linux dev-4.19 15/15] dts: npcm7xx: Modify NPCM7xx device tree
2019-01-14 13:06 [linux dev-4.19 00/15] dd NPCM7xx patches to dev-4.19 Tomer Maimon
` (13 preceding siblings ...)
2019-01-14 13:07 ` [linux dev-4.19 14/15] arm: npcm: add defconfig for Nuvoton NPCM7xx BMC Tomer Maimon
@ 2019-01-14 13:07 ` Tomer Maimon
2019-01-15 23:45 ` [linux dev-4.19 00/15] dd NPCM7xx patches to dev-4.19 Joel Stanley
15 siblings, 0 replies; 19+ messages in thread
From: Tomer Maimon @ 2019-01-14 13:07 UTC (permalink / raw)
To: joel; +Cc: openbmc, Tomer Maimon
Modify NPCM7xx device tree FIU, ADC, RST, VCD and SPI nodes
Add regulator and HGPIO pins nodes.
Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
---
arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi | 199 +++++++++++++++-----------
arch/arm/boot/dts/nuvoton-npcm750-evb.dts | 165 +++++++++++++++------
arch/arm/boot/dts/nuvoton-npcm750-gpio.dtsi | 18 +--
arch/arm/boot/dts/nuvoton-npcm750.dtsi | 10 +-
4 files changed, 249 insertions(+), 143 deletions(-)
diff --git a/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi b/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi
index afe0d3cb516d..a24a06170660 100644
--- a/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi
+++ b/arch/arm/boot/dts/nuvoton-common-npcm7xx.dtsi
@@ -5,6 +5,7 @@
#include "skeleton.dtsi"
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/nuvoton,npcm7xx-clock.h>
+#include <dt-bindings/gpio/gpio.h>
/ {
#address-cells = <1>;
@@ -74,7 +75,7 @@
rst: rst@f0801000 {
compatible = "nuvoton,npcm750-rst", "syscon",
"simple-mfd";
- reg = <0x801000 0x1000>;
+ reg = <0x801000 0x6C>;
};
scu: scu@3fe000 {
@@ -128,7 +129,7 @@
clock-names = "stmmaceth", "clk_gmac";
pinctrl-names = "default";
pinctrl-0 = <&rg1_pins
- &rg1mdio_pins>;
+ &rg1mdio_pins>;
status = "disabled";
};
@@ -142,8 +143,9 @@
clock-names = "clk_emc";
pinctrl-names = "default";
pinctrl-0 = <&r1_pins
- &r1err_pins
- &r1md_pins>;
+ &r1err_pins
+ &r1md_pins>;
+ status = "disabled";
};
ehci1:usb@f0806000 {
@@ -213,52 +215,38 @@
interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
};
- spi0: spi@fb000000 {
- compatible = "nuvoton,npcm750-spi";
+ fiu0: fiu@fb000000 {
+ compatible = "nuvoton,npcm750-fiu";
#address-cells = <1>;
#size-cells = <0>;
reg = <0xfb000000 0x1000>, <0x80000000 0x10000000>;
reg-names = "control", "memory";
- chip-max-address-map = <0x8000000>;
clocks = <&clk NPCM7XX_CLK_AHB>;
clock-names = "clk_ahb";
- spi-nor@0 {
- compatible = "jedec,spi-nor";
- #address-cells = <1>;
- #size-cells = <1>;
- reg = <0>;
- };
+ status = "disabled";
};
- spi3: spi@c0000000 {
- compatible = "nuvoton,npcm750-spi";
+
+ fiu3: fiu@c0000000 {
+ compatible = "nuvoton,npcm750-fiu";
#address-cells = <1>;
#size-cells = <0>;
reg = <0xc0000000 0x1000>, <0xA0000000 0x20000000>;
reg-names = "control", "memory";
- chip-max-address-map = <0x8000000>;
clocks = <&clk NPCM7XX_CLK_AHB>;
clock-names = "clk_ahb";
pinctrl-names = "default";
- pinctrl-0 = <&spi3_pins &spi3quad_pins>;
- spi-nor@0 {
- compatible = "jedec,spi-nor";
- #address-cells = <1>;
- #size-cells = <1>;
- reg = <0>;
- };
+ pinctrl-0 = <&spi3_pins>;
+ status = "disabled";
};
- pci_rc: axi-pcie@e1000000 {
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- compatible = "nuvoton,npcm750-pcirc";
- reg = <0xe1000000 0x1000>;
- device_type = "pci";
- interrupts = <GIC_SPI 127 IRQ_TYPE_LEVEL_HIGH>;
- bus-range = <0x00 0xff>;
- ranges = <0x02000000 0 0xea000000
- 0xea000000 0 0x02000000>;
+ fiux: fiu@fb001000 {
+ compatible = "nuvoton,npcm750-fiu";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0xfb001000 0x1000>, <0xf8000000 0x2000000>;
+ reg-names = "control", "memory";
+ clocks = <&clk NPCM7XX_CLK_AHB>;
+ clock-names = "clk_ahb";
status = "disabled";
};
@@ -271,17 +259,18 @@
vcd: vcd@f0810000 {
compatible = "nuvoton,npcm750-vcd";
reg = <0xf0810000 0x10000>;
- phy-memory = <0x3e200000 0x600000>;
- de-mode = /bits/ 8 <1>;
- interrupts = <0 22 4>;
+ mem-addr = <0x3e200000>;
+ mem-size = <0x600000>;
+ interrupts = <GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
};
ece: ece@f0820000 {
compatible = "nuvoton,npcm750-ece";
reg = <0xf0820000 0x2000>;
- phy-memory = <0x3e800000 0x600000>;
- interrupts = <0 24 4>;
+ mem-addr = <0x3e800000>;
+ mem-size = <0x600000>;
+ interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
};
@@ -352,13 +341,30 @@
};
};
- pspi: pspi@0 {
+ spi0: spi@200000 {
compatible = "nuvoton,npcm750-pspi";
- reg = <0x200000 0x2000>;
- interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
+ reg = <0x200000 0x1000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pspi1_pins>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk NPCM7XX_CLK_APB5>;
clock-names = "clk_apb5";
+ status = "disabled";
+ };
+
+ spi1: spi@201000 {
+ compatible = "nuvoton,npcm750-pspi";
+ reg = <0x201000 0x1000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pspi2_pins>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk NPCM7XX_CLK_APB5>;
+ clock-names = "clk_apb5";
+ status = "disabled";
};
timer0: timer@8000 {
@@ -438,10 +444,10 @@
adc: adc@c000 {
compatible = "nuvoton,npcm750-adc";
- reg = <0xc000 0x1000>;
+ reg = <0xc000 0x8>;
+ interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk NPCM7XX_CLK_ADC>;
- clock-names = "clk_adc";
- vref = <2048>;
+ status = "disabled";
};
otp:otp@189000 {
@@ -453,7 +459,7 @@
clock-names = "clk_apb4";
};
- pwm_fan:pwm-fan-controller@103000 {
+ pwm_fan:pwm-fan-controller@103000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "nuvoton,npcm750-pwm-fan";
@@ -487,9 +493,9 @@
status = "disabled";
};
- i2c0: i2c-bus@80000 {
+ i2c0: i2c@80000 {
reg = <0x80000 0x1000>;
- compatible = "nuvoton,npcm750-i2c-bus";
+ compatible = "nuvoton,npcm750-i2c";
clocks = <&clk NPCM7XX_CLK_APB2>;
bus-frequency = <100000>;
interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>;
@@ -498,9 +504,9 @@
status = "disabled";
};
- i2c1: i2c-bus@81000 {
+ i2c1: i2c@81000 {
reg = <0x81000 0x1000>;
- compatible = "nuvoton,npcm750-i2c-bus";
+ compatible = "nuvoton,npcm750-i2c";
clocks = <&clk NPCM7XX_CLK_APB2>;
bus-frequency = <100000>;
interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
@@ -509,9 +515,9 @@
status = "disabled";
};
- i2c2: i2c-bus@82000 {
+ i2c2: i2c@82000 {
reg = <0x82000 0x1000>;
- compatible = "nuvoton,npcm750-i2c-bus";
+ compatible = "nuvoton,npcm750-i2c";
clocks = <&clk NPCM7XX_CLK_APB2>;
bus-frequency = <100000>;
interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>;
@@ -520,9 +526,9 @@
status = "disabled";
};
- i2c3: i2c-bus@83000 {
+ i2c3: i2c@83000 {
reg = <0x83000 0x1000>;
- compatible = "nuvoton,npcm750-i2c-bus";
+ compatible = "nuvoton,npcm750-i2c";
clocks = <&clk NPCM7XX_CLK_APB2>;
bus-frequency = <100000>;
interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
@@ -531,9 +537,9 @@
status = "disabled";
};
- i2c4: i2c-bus@84000 {
+ i2c4: i2c@84000 {
reg = <0x84000 0x1000>;
- compatible = "nuvoton,npcm750-i2c-bus";
+ compatible = "nuvoton,npcm750-i2c";
clocks = <&clk NPCM7XX_CLK_APB2>;
bus-frequency = <100000>;
interrupts = <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>;
@@ -542,9 +548,9 @@
status = "disabled";
};
- i2c5: i2c-bus@85000 {
+ i2c5: i2c@85000 {
reg = <0x85000 0x1000>;
- compatible = "nuvoton,npcm750-i2c-bus";
+ compatible = "nuvoton,npcm750-i2c";
clocks = <&clk NPCM7XX_CLK_APB2>;
bus-frequency = <100000>;
interrupts = <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>;
@@ -553,9 +559,9 @@
status = "disabled";
};
- i2c6: i2c-bus@86000 {
+ i2c6: i2c@86000 {
reg = <0x86000 0x1000>;
- compatible = "nuvoton,npcm750-i2c-bus";
+ compatible = "nuvoton,npcm750-i2c";
clocks = <&clk NPCM7XX_CLK_APB2>;
bus-frequency = <100000>;
interrupts = <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
@@ -564,9 +570,9 @@
status = "disabled";
};
- i2c7: i2c-bus@87000 {
+ i2c7: i2c@87000 {
reg = <0x87000 0x1000>;
- compatible = "nuvoton,npcm750-i2c-bus";
+ compatible = "nuvoton,npcm750-i2c";
clocks = <&clk NPCM7XX_CLK_APB2>;
bus-frequency = <100000>;
interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
@@ -575,9 +581,9 @@
status = "disabled";
};
- i2c8: i2c-bus@88000 {
+ i2c8: i2c@88000 {
reg = <0x88000 0x1000>;
- compatible = "nuvoton,npcm750-i2c-bus";
+ compatible = "nuvoton,npcm750-i2c";
clocks = <&clk NPCM7XX_CLK_APB2>;
bus-frequency = <100000>;
interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
@@ -586,9 +592,9 @@
status = "disabled";
};
- i2c9: i2c-bus@89000 {
+ i2c9: i2c@89000 {
reg = <0x89000 0x1000>;
- compatible = "nuvoton,npcm750-i2c-bus";
+ compatible = "nuvoton,npcm750-i2c";
clocks = <&clk NPCM7XX_CLK_APB2>;
bus-frequency = <100000>;
interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
@@ -597,9 +603,9 @@
status = "disabled";
};
- i2c10: i2c-bus@8a000 {
+ i2c10: i2c@8a000 {
reg = <0x8a000 0x1000>;
- compatible = "nuvoton,npcm750-i2c-bus";
+ compatible = "nuvoton,npcm750-i2c";
clocks = <&clk NPCM7XX_CLK_APB2>;
bus-frequency = <100000>;
interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
@@ -608,9 +614,9 @@
status = "disabled";
};
- i2c11: i2c-bus@8b000 {
+ i2c11: i2c@8b000 {
reg = <0x8b000 0x1000>;
- compatible = "nuvoton,npcm750-i2c-bus";
+ compatible = "nuvoton,npcm750-i2c";
clocks = <&clk NPCM7XX_CLK_APB2>;
bus-frequency = <100000>;
interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
@@ -619,9 +625,9 @@
status = "disabled";
};
- i2c12: i2c-bus@8c000 {
+ i2c12: i2c@8c000 {
reg = <0x8c000 0x1000>;
- compatible = "nuvoton,npcm750-i2c-bus";
+ compatible = "nuvoton,npcm750-i2c";
clocks = <&clk NPCM7XX_CLK_APB2>;
bus-frequency = <100000>;
interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
@@ -630,9 +636,9 @@
status = "disabled";
};
- i2c13: i2c-bus@8d000 {
+ i2c13: i2c@8d000 {
reg = <0x8d000 0x1000>;
- compatible = "nuvoton,npcm750-i2c-bus";
+ compatible = "nuvoton,npcm750-i2c";
clocks = <&clk NPCM7XX_CLK_APB2>;
bus-frequency = <100000>;
interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>;
@@ -641,9 +647,9 @@
status = "disabled";
};
- i2c14: i2c-bus@8e000 {
+ i2c14: i2c@8e000 {
reg = <0x8e000 0x1000>;
- compatible = "nuvoton,npcm750-i2c-bus";
+ compatible = "nuvoton,npcm750-i2c";
clocks = <&clk NPCM7XX_CLK_APB2>;
bus-frequency = <100000>;
interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
@@ -652,9 +658,9 @@
status = "disabled";
};
- i2c15: i2c-bus@8f000 {
+ i2c15: i2c@8f000 {
reg = <0x8f000 0x1000>;
- compatible = "nuvoton,npcm750-i2c-bus";
+ compatible = "nuvoton,npcm750-i2c";
clocks = <&clk NPCM7XX_CLK_APB2>;
bus-frequency = <100000>;
interrupts = <GIC_SPI 79 IRQ_TYPE_LEVEL_HIGH>;
@@ -664,7 +670,8 @@
};
gfxi: gfxi@f000e000 {
- compatible = "nuvoton,npcm750-gfxi", "syscon", "simple-mfd";
+ compatible = "nuvoton,npcm750-gfxi", "syscon",
+ "simple-mfd";
reg = <0xf000e000 0x100>;
};
@@ -1202,5 +1209,37 @@
groups = "clkreq";
function = "clkreq";
};
+ hgpio0_pins: hgpio0-pins {
+ groups = "hgpio0";
+ function = "hgpio0";
+ };
+ hgpio1_pins: hgpio1-pins {
+ groups = "hgpio1";
+ function = "hgpio1";
+ };
+ hgpio2_pins: hgpio2-pins {
+ groups = "hgpio2";
+ function = "hgpio2";
+ };
+ hgpio3_pins: hgpio3-pins {
+ groups = "hgpio3";
+ function = "hgpio3";
+ };
+ hgpio4_pins: hgpio4-pins {
+ groups = "hgpio4";
+ function = "hgpio4";
+ };
+ hgpio5_pins: hgpio5-pins {
+ groups = "hgpio5";
+ function = "hgpio5";
+ };
+ hgpio6_pins: hgpio6-pins {
+ groups = "hgpio6";
+ function = "hgpio6";
+ };
+ hgpio7_pins: hgpio7-pins {
+ groups = "hgpio7";
+ function = "hgpio7";
+ };
};
};
diff --git a/arch/arm/boot/dts/nuvoton-npcm750-evb.dts b/arch/arm/boot/dts/nuvoton-npcm750-evb.dts
index 8c568b380706..d951ac997872 100644
--- a/arch/arm/boot/dts/nuvoton-npcm750-evb.dts
+++ b/arch/arm/boot/dts/nuvoton-npcm750-evb.dts
@@ -46,6 +46,11 @@
i2c13 = &i2c13;
i2c14 = &i2c14;
i2c15 = &i2c15;
+ spi0 = &spi0;
+ spi1 = &spi1;
+ fiu0 = &fiu0;
+ fiu1 = &fiu3;
+ fiu2 = &fiux;
};
chosen {
@@ -56,8 +61,29 @@
reg = <0 0x40000000>;
};
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_vref1_2: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "vref_1_2v";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ };
+ reg_vref3_3: regulator@1 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "vref_3_3v";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+ };
+
ahb {
- gmac0: eth@f0802000 {
+ gmac0: eth@f0802000 {
phy-mode = "rgmii-id";
status = "okay";
};
@@ -135,8 +161,14 @@
status = "okay";
};
- spi0: spi@fb000000 {
+ fiu0: fiu@fb000000 {
+ status = "okay";
spi-nor@0 {
+ compatible = "jedec,spi-nor";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ spi-rx-bus-width = <2>;
+ reg = <0>;
partitions@80000000 {
compatible = "fixed-partitions";
#address-cells = <1>;
@@ -188,24 +220,31 @@
};
};
- spi3: spi@c0000000 {
- spi-nor@0 {
+ fiu3: fiu@c0000000 {
+ pinctrl-0 = <&spi3_pins>, <&spi3quad_pins>;
+ status = "okay";
+ spi-nor@0 {
+ compatible = "jedec,spi-nor";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ spi-rx-bus-width = <2>;
+ reg = <0>;
partitions@A0000000 {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
system1@0 {
label = "spi3-system1";
- reg = <0x0 0x800000>;
- };
- system2@800000 {
- label = "spi3-system2";
- reg = <0x800000 0x0>;
+ reg = <0x0 0x0>;
};
};
};
};
+ fiux: fiu@fb001000 {
+ spix-mode;
+ };
+
sdhci0: sdhci@f0842000 {
status = "okay";
};
@@ -252,6 +291,12 @@
status = "okay";
};
+ adc: adc@c000 {
+ /* enable external vref */
+ /*vref-supply = <®_vref1_2>;*/
+ status = "okay";
+ };
+
otp:otp@189000 {
status = "okay";
};
@@ -278,7 +323,7 @@
};
/* lm75 on SVB */
- i2c0: i2c-bus@80000 {
+ i2c0: i2c@80000 {
#address-cells = <1>;
#size-cells = <0>;
bus-frequency = <100000>;
@@ -292,7 +337,7 @@
};
/* lm75 on EB */
- i2c1: i2c-bus@81000 {
+ i2c1: i2c@81000 {
#address-cells = <1>;
#size-cells = <0>;
bus-frequency = <100000>;
@@ -306,7 +351,7 @@
};
/* tmp100 on EB */
- i2c2: i2c-bus@82000 {
+ i2c2: i2c@82000 {
#address-cells = <1>;
#size-cells = <0>;
bus-frequency = <100000>;
@@ -320,7 +365,7 @@
};
/* tmp100 on SVB */
- i2c6: i2c-bus@86000 {
+ i2c6: i2c@86000 {
#address-cells = <1>;
#size-cells = <0>;
bus-frequency = <100000>;
@@ -332,74 +377,75 @@
status = "okay";
};
};
- i2c3: i2c-bus@83000 {
+ i2c3: i2c@83000 {
#address-cells = <1>;
#size-cells = <0>;
bus-frequency = <100000>;
status = "okay";
};
- i2c4: i2c-bus@84000 {
+ i2c4: i2c@84000 {
#address-cells = <1>;
#size-cells = <0>;
bus-frequency = <100000>;
status = "disabled";
};
- i2c5: i2c-bus@85000 {
+ i2c5: i2c@85000 {
#address-cells = <1>;
#size-cells = <0>;
bus-frequency = <100000>;
- status = "disabled";
+ status = "okay";
};
- i2c7: i2c-bus@87000 {
+ i2c7: i2c@87000 {
#address-cells = <1>;
#size-cells = <0>;
bus-frequency = <100000>;
- status = "disabled";
+ status = "okay";
};
- i2c8: i2c-bus@88000 {
+ i2c8: i2c@88000 {
#address-cells = <1>;
#size-cells = <0>;
bus-frequency = <100000>;
- status = "disabled";
+ status = "okay";
};
- i2c9: i2c-bus@89000 {
+ i2c9: i2c@89000 {
#address-cells = <1>;
#size-cells = <0>;
bus-frequency = <100000>;
- status = "disabled";
+ status = "okay";
};
- i2c10: i2c-bus@8a000 {
+ i2c10: i2c@8a000 {
#address-cells = <1>;
#size-cells = <0>;
bus-frequency = <100000>;
- status = "disabled";
+ status = "okay";
};
- i2c11: i2c-bus@8b000 {
+ i2c11: i2c@8b000 {
#address-cells = <1>;
#size-cells = <0>;
bus-frequency = <100000>;
- status = "disabled";
+ status = "okay";
};
- i2c14: i2c-bus@8e000 {
+ i2c14: i2c@8e000 {
#address-cells = <1>;
#size-cells = <0>;
bus-frequency = <100000>;
- status = "disabled";
+ status = "okay";
};
- i2c15: i2c-bus@8f000 {
+ i2c15: i2c@8f000 {
#address-cells = <1>;
#size-cells = <0>;
bus-frequency = <100000>;
- status = "disabled"; /* SVB conflict with pspi2 cs gpio20o_pins */
+ /* SVB conflict with pspi2 cs gpio20o_pins */
+ status = "disabled";
};
pwm_fan:pwm-fan-controller@103000 {
@@ -446,16 +492,47 @@
};
};
- /* example for future pspi binding */
- /*
- pspi: pspi@0 {
- pinctrl-names = "default";
- pinctrl-0 = <&pspi1_pins &pspi2_pins
- &gpio20o_pins &gpio203o_pins>;
- cs-gpios = <&gpio 20 1>, <&gpio 203 1>;
+ spi0: spi@200000 {
+ cs-gpios = <&gpio6 11 GPIO_ACTIVE_LOW>;
status = "okay";
+ Flash@0 {
+ compatible = "winbond,w25q128",
+ "jedec,spi-nor";
+ reg = <0x0>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ spi-max-frequency = <5000000>;
+ partition@0 {
+ label = "spi_spare1";
+ reg = <0x0000000 0x800000>;
+ };
+ partition@1 {
+ label = "spi_spare2";
+ reg = <0x800000 0x0>;
+ };
+ };
+ };
+
+ spi1: spi@201000 {
+ cs-gpios = <&gpio0 20 GPIO_ACTIVE_LOW>;
+ status = "okay";
+ Flash@0 {
+ compatible = "winbond,w25q128fw",
+ "jedec,spi-nor";
+ reg = <0x0>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ spi-max-frequency = <5000000>;
+ partition@0 {
+ label = "spi_spare1";
+ reg = <0x0000000 0x800000>;
+ };
+ partition@1 {
+ label = "spi_spare2";
+ reg = <0x800000 0x0>;
+ };
+ };
};
- */
};
};
@@ -480,7 +557,7 @@
&gpio82_pins
&gpio83_pins
&lpc_pins
- &gpio132o_pins
+ &gpio132_pins
&gpio133_pins
&gpio134_pins
&gpio135_pins
@@ -514,10 +591,10 @@
&gcr {
serial_port_mux: mux-controller {
- compatible = "mmio-mux";
- #mux-control-cells = <1>;
+ compatible = "mmio-mux";
+ #mux-control-cells = <1>;
- mux-reg-masks = <0x38 0x07>;
- idle-states = <2>; /* Serial port mode 3 (takeover) */
+ mux-reg-masks = <0x38 0x07>;
+ idle-states = <2>; /* Serial port mode 3 (takeover) */
};
};
diff --git a/arch/arm/boot/dts/nuvoton-npcm750-gpio.dtsi b/arch/arm/boot/dts/nuvoton-npcm750-gpio.dtsi
index b9675fbce4e6..a912910bc7ec 100644
--- a/arch/arm/boot/dts/nuvoton-npcm750-gpio.dtsi
+++ b/arch/arm/boot/dts/nuvoton-npcm750-gpio.dtsi
@@ -1,15 +1,5 @@
-/*
- * DTSi file for the NPCM750 pin controller
- *
- * Copyright (c) 2014-2017 Nuvoton Technology corporation.
- *
- * Released under the GPLv2 only.
- * SPDX-License-Identifier: GPL-2.0
- *
- * http://www.opensource.org/licenses/gpl-license.html
- * http://www.gnu.org/copyleft/gpl.html
- */
-
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018 Nuvoton Technology tomer.maimon@nuvoton.com
/ {
pinctrl: pinctrl@f0800000 {
@@ -1107,10 +1097,10 @@
bias-disable;
input-enable;
};
- gpio132o_pins: gpio132o-pins {
+ gpio132_pins: gpio132-pins {
pins = "GPIO132/SMB10SCL";
bias-disable;
- output-high;
+ input-enable;
};
gpio133_pins: gpio133-pins {
pins = "GPIO133/SMB10SDA";
diff --git a/arch/arm/boot/dts/nuvoton-npcm750.dtsi b/arch/arm/boot/dts/nuvoton-npcm750.dtsi
index 421a4ed54bad..14b3d5b1206f 100644
--- a/arch/arm/boot/dts/nuvoton-npcm750.dtsi
+++ b/arch/arm/boot/dts/nuvoton-npcm750.dtsi
@@ -44,7 +44,6 @@
};
};
-
ahb {
gmac1: eth@f0804000 {
device_type = "network";
@@ -56,8 +55,8 @@
clocks = <&clk_rg2refck>, <&clk NPCM7XX_CLK_AHB>;
clock-names = "stmmaceth", "clk_gmac";
pinctrl-names = "default";
- /*pinctrl-0 = <&rg2_pins
- &rg2mdio_pins>;*/
+ pinctrl-0 = <&rg2_pins
+ &rg2mdio_pins>;
status = "disabled";
};
@@ -71,8 +70,9 @@
clock-names = "clk_emc";
pinctrl-names = "default";
pinctrl-0 = <&r2_pins
- &r2err_pins
- &r2md_pins>;
+ &r2err_pins
+ &r2md_pins>;
+ status = "disabled";
};
udc0:udc@f0830000 {
--
2.14.1
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [linux dev-4.19 00/15] dd NPCM7xx patches to dev-4.19
2019-01-14 13:06 [linux dev-4.19 00/15] dd NPCM7xx patches to dev-4.19 Tomer Maimon
` (14 preceding siblings ...)
2019-01-14 13:07 ` [linux dev-4.19 15/15] dts: npcm7xx: Modify NPCM7xx device tree Tomer Maimon
@ 2019-01-15 23:45 ` Joel Stanley
2019-01-16 10:26 ` Tomer Maimon
15 siblings, 1 reply; 19+ messages in thread
From: Joel Stanley @ 2019-01-15 23:45 UTC (permalink / raw)
To: Tomer Maimon; +Cc: OpenBMC Maillist
Hi Tomer,
On Tue, 15 Jan 2019 at 00:07, Tomer Maimon <tmaimon77@gmail.com> wrote:
>
> Hi Joel,
>
> Please add the following NPCM7xx patches to dev-4.19:
I applied your series and it does not compile. I am using the
npcm7xx_defconfig you included:
arm-linux-gnueabi-ld: drivers/i2c/busses/i2c-npcm7xx.o: in function
`npcm_smb_calc_crc8':
drivers/i2c/busses/i2c-npcm7xx.c:423: undefined reference to `crc8'
arm-linux-gnueabi-ld: drivers/i2c/busses/i2c-npcm7xx.o: in function
`__npcm_i2c_init':
drivers/i2c/busses/i2c-npcm7xx.c:1668: undefined reference to
`crc8_populate_lsb'
drivers/i2c/busses/i2c-npcm7xx.c:1669: undefined reference to
`crc8_populate_msb'
Please test the patchset exactly as you intend to send before sending it.
In this case you needed to add `selects CRC8` to drivers/i2c/busses/Kconfig.
You also have some warnings. Please send follow up patches ASAP to
resolve these:
../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;
^~~
arch/arm/boot/dts/nuvoton-npcm750-evb.dtb: Warning (pci_bridge):
/ahb/axi-pcie@e1000000: node name is not "pci" or "pcie"
arch/arm/boot/dts/nuvoton-npcm750-evb.dtb: Warning
(pci_device_bus_num): Failed prerequisite 'pci_bridge'
I have done this for you and the tree now builds. I've merged the
patches into dev-4.19 and will push out once I've finished testing.
Cheers,
Joel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [linux dev-4.19 00/15] dd NPCM7xx patches to dev-4.19
2019-01-15 23:45 ` [linux dev-4.19 00/15] dd NPCM7xx patches to dev-4.19 Joel Stanley
@ 2019-01-16 10:26 ` Tomer Maimon
0 siblings, 0 replies; 19+ messages in thread
From: Tomer Maimon @ 2019-01-16 10:26 UTC (permalink / raw)
To: Joel Stanley; +Cc: OpenBMC Maillist
[-- Attachment #1: Type: text/plain, Size: 2331 bytes --]
Hi Joel,
Thanks a lot for applying the patches
On Wed, 16 Jan 2019 at 01:46, Joel Stanley <joel@jms.id.au> wrote:
> Hi Tomer,
>
> On Tue, 15 Jan 2019 at 00:07, Tomer Maimon <tmaimon77@gmail.com> wrote:
> >
> > Hi Joel,
> >
> > Please add the following NPCM7xx patches to dev-4.19:
>
> I applied your series and it does not compile. I am using the
> npcm7xx_defconfig you included:
>
> arm-linux-gnueabi-ld: drivers/i2c/busses/i2c-npcm7xx.o: in function
> `npcm_smb_calc_crc8':
> drivers/i2c/busses/i2c-npcm7xx.c:423: undefined reference to `crc8'
> arm-linux-gnueabi-ld: drivers/i2c/busses/i2c-npcm7xx.o: in function
> `__npcm_i2c_init':
> drivers/i2c/busses/i2c-npcm7xx.c:1668: undefined reference to
> `crc8_populate_lsb'
> drivers/i2c/busses/i2c-npcm7xx.c:1669: undefined reference to
> `crc8_populate_msb'
>
> Please test the patchset exactly as you intend to send before sending it.
>
>
I did test the patches with
https://developer.arm.com/open-source/gnu-toolchain/gnu-a/downloads/8-2-2018-08
(gcc-arm-8.2-2018.08-x86_64-arm-linux-gnueabi)
So I do not understand why you get this failures
> In this case you needed to add `selects CRC8` to
> drivers/i2c/busses/Kconfig.
>
> You also have some warnings. Please send follow up patches ASAP to
> resolve these:
>
> ../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;
> ^~~
>
> About the ADC patch you are right, somehow I didn't send the latest code
of the ADC driver. I will send it today.
> arch/arm/boot/dts/nuvoton-npcm750-evb.dtb: Warning (pci_bridge):
> /ahb/axi-pcie@e1000000: node name is not "pci" or "pcie"
> arch/arm/boot/dts/nuvoton-npcm750-evb.dtb: Warning
> (pci_device_bus_num): Failed prerequisite 'pci_bridge'
>
> In the patch set you can find patch
[linux dev-4.19 15/15] dts: npcm7xx: Modify NPCM7xx device tree
It's fix the above warnings and modify other node.
please apply it.
> I have done this for you and the tree now builds. I've merged the
> patches into dev-4.19 and will push out once I've finished testing.
>
> Cheers,
>
> Joel
>
I will make sure to send all the fixes today.
Thanks a lot,
Tomer
[-- Attachment #2: Type: text/html, Size: 3995 bytes --]
^ permalink raw reply [flat|nested] 19+ messages in thread