All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/6] Introduce Juniper CBC FPGA
@ 2016-10-07 15:20 ` Pantelis Antoniou
  0 siblings, 0 replies; 24+ messages in thread
From: Pantelis Antoniou @ 2016-10-07 15:20 UTC (permalink / raw)
  To: Lee Jones
  Cc: Mark Rutland, Alexandre Courbot, devel, devicetree,
	Greg Kroah-Hartman, Linus Walleij, Pantelis Antoniou,
	linux-kernel, JawaharBalaji Thirumalaisamy, linux-gpio,
	Rob Herring, Debjit Ghosh, Mohammad Kamil, Georgi Vlaev,
	Frank Rowand, Guenter Roeck

Add Juniper's PTX1K CBC FPGA driver. Those FPGAs
are present in Juniper's PTX series of routers.

The MFD driver provices a gpio device and a special
driver for Juniper's board infrastucture.
The FPGA infrastucture driver is providing an interface
for user-space handling of the FPGA in those platforms.

There are full device tree binding documents for the
master mfd driver and for the slave driver.

This patchset is against mainline as of today: v4.8-9431-g3477d16
and is dependent on the "Juniper prerequisites" and
"Juniper infrastructure" patchsets sent earlier.

Georgi Vlaev (5):
  mfd: Add support for the PTX1K CBC FPGA
  gpio: Add support for PTX1K CBC FPGA spare GPIOs
  gpio: gpio-cbc: Document bindings of CBC FPGA GPIO block
  gpio: cbc-presence: Add CBC presence detect as GPIO driver
  gpio: gpio-cbc-presense: Document bindings of CBC FPGA presence

Tom Kavanagh (1):
  staging: jnx: CBD-FPGA infrastructure

 .../bindings/gpio/jnx,gpio-cbc-presense.txt        |  31 +
 .../devicetree/bindings/gpio/jnx,gpio-cbc.txt      |  30 +
 drivers/gpio/Kconfig                               |  23 +
 drivers/gpio/Makefile                              |   2 +
 drivers/gpio/gpio-cbc-presence.c                   | 460 ++++++++++
 drivers/gpio/gpio-cbc.c                            | 236 +++++
 drivers/mfd/Kconfig                                |  16 +
 drivers/mfd/Makefile                               |   1 +
 drivers/mfd/cbc-core.c                             | 971 +++++++++++++++++++++
 drivers/staging/jnx/Kconfig                        |  34 +
 drivers/staging/jnx/Makefile                       |   5 +
 drivers/staging/jnx/jnx-cbc-ptx1k.c                | 242 +++++
 drivers/staging/jnx/jnx-cbd-fpga-common.c          | 332 +++++++
 drivers/staging/jnx/jnx-cbd-fpga-common.h          |  27 +
 drivers/staging/jnx/jnx-cbd-fpga-core.c            | 540 ++++++++++++
 drivers/staging/jnx/jnx-cbd-fpga-core.h            |  68 ++
 drivers/staging/jnx/jnx-cbd-fpga-platdata.h        |  51 ++
 drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c           | 134 +++
 drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c          | 143 +++
 drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c           | 111 +++
 drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c           | 107 +++
 include/linux/mfd/cbc-core.h                       | 181 ++++
 22 files changed, 3745 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/jnx,gpio-cbc-presense.txt
 create mode 100644 Documentation/devicetree/bindings/gpio/jnx,gpio-cbc.txt
 create mode 100644 drivers/gpio/gpio-cbc-presence.c
 create mode 100644 drivers/gpio/gpio-cbc.c
 create mode 100644 drivers/mfd/cbc-core.c
 create mode 100644 drivers/staging/jnx/jnx-cbc-ptx1k.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-common.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-common.h
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-core.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-core.h
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-platdata.h
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c
 create mode 100644 include/linux/mfd/cbc-core.h

-- 
1.9.1

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

* [PATCH 0/6] Introduce Juniper CBC FPGA
@ 2016-10-07 15:20 ` Pantelis Antoniou
  0 siblings, 0 replies; 24+ messages in thread
From: Pantelis Antoniou @ 2016-10-07 15:20 UTC (permalink / raw)
  To: Lee Jones
  Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
	Frank Rowand, Greg Kroah-Hartman, Debjit Ghosh, Georgi Vlaev,
	Guenter Roeck, JawaharBalaji Thirumalaisamy, Mohammad Kamil,
	Pantelis Antoniou, devicetree, linux-kernel, linux-gpio, devel

Add Juniper's PTX1K CBC FPGA driver. Those FPGAs
are present in Juniper's PTX series of routers.

The MFD driver provices a gpio device and a special
driver for Juniper's board infrastucture.
The FPGA infrastucture driver is providing an interface
for user-space handling of the FPGA in those platforms.

There are full device tree binding documents for the
master mfd driver and for the slave driver.

This patchset is against mainline as of today: v4.8-9431-g3477d16
and is dependent on the "Juniper prerequisites" and
"Juniper infrastructure" patchsets sent earlier.

Georgi Vlaev (5):
  mfd: Add support for the PTX1K CBC FPGA
  gpio: Add support for PTX1K CBC FPGA spare GPIOs
  gpio: gpio-cbc: Document bindings of CBC FPGA GPIO block
  gpio: cbc-presence: Add CBC presence detect as GPIO driver
  gpio: gpio-cbc-presense: Document bindings of CBC FPGA presence

Tom Kavanagh (1):
  staging: jnx: CBD-FPGA infrastructure

 .../bindings/gpio/jnx,gpio-cbc-presense.txt        |  31 +
 .../devicetree/bindings/gpio/jnx,gpio-cbc.txt      |  30 +
 drivers/gpio/Kconfig                               |  23 +
 drivers/gpio/Makefile                              |   2 +
 drivers/gpio/gpio-cbc-presence.c                   | 460 ++++++++++
 drivers/gpio/gpio-cbc.c                            | 236 +++++
 drivers/mfd/Kconfig                                |  16 +
 drivers/mfd/Makefile                               |   1 +
 drivers/mfd/cbc-core.c                             | 971 +++++++++++++++++++++
 drivers/staging/jnx/Kconfig                        |  34 +
 drivers/staging/jnx/Makefile                       |   5 +
 drivers/staging/jnx/jnx-cbc-ptx1k.c                | 242 +++++
 drivers/staging/jnx/jnx-cbd-fpga-common.c          | 332 +++++++
 drivers/staging/jnx/jnx-cbd-fpga-common.h          |  27 +
 drivers/staging/jnx/jnx-cbd-fpga-core.c            | 540 ++++++++++++
 drivers/staging/jnx/jnx-cbd-fpga-core.h            |  68 ++
 drivers/staging/jnx/jnx-cbd-fpga-platdata.h        |  51 ++
 drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c           | 134 +++
 drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c          | 143 +++
 drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c           | 111 +++
 drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c           | 107 +++
 include/linux/mfd/cbc-core.h                       | 181 ++++
 22 files changed, 3745 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/jnx,gpio-cbc-presense.txt
 create mode 100644 Documentation/devicetree/bindings/gpio/jnx,gpio-cbc.txt
 create mode 100644 drivers/gpio/gpio-cbc-presence.c
 create mode 100644 drivers/gpio/gpio-cbc.c
 create mode 100644 drivers/mfd/cbc-core.c
 create mode 100644 drivers/staging/jnx/jnx-cbc-ptx1k.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-common.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-common.h
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-core.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-core.h
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-platdata.h
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c
 create mode 100644 include/linux/mfd/cbc-core.h

-- 
1.9.1

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

* [PATCH 1/6] mfd: Add support for the PTX1K CBC FPGA
  2016-10-07 15:20 ` Pantelis Antoniou
@ 2016-10-07 15:20   ` Pantelis Antoniou
  -1 siblings, 0 replies; 24+ messages in thread
From: Pantelis Antoniou @ 2016-10-07 15:20 UTC (permalink / raw)
  To: Lee Jones
  Cc: Mark Rutland, Alexandre Courbot, devel, devicetree,
	Greg Kroah-Hartman, Linus Walleij, Pantelis Antoniou,
	linux-kernel, JawaharBalaji Thirumalaisamy, linux-gpio,
	Rob Herring, Debjit Ghosh, Mohammad Kamil, Georgi Vlaev,
	Frank Rowand, Guenter Roeck

From: Georgi Vlaev <gvlaev@juniper.net>

The CBC intergrates CB and SAM on single FPGA. This is a PCI MFD driver
and provides support for the following functions as subdrivers:

* SAM I2C accelerator
* SAM MTD flash
* CBC spare GPIOs
* CBC JNX infrastructure

Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
Signed-off-by: Guenter Roeck <groeck@juniper.net>
Signed-off-by: Rajat Jain <rajatjain@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
 drivers/mfd/Kconfig          |  16 +
 drivers/mfd/Makefile         |   1 +
 drivers/mfd/cbc-core.c       | 971 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/cbc-core.h | 181 ++++++++
 4 files changed, 1169 insertions(+)
 create mode 100644 drivers/mfd/cbc-core.c
 create mode 100644 include/linux/mfd/cbc-core.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 7e1fa14..6107f7a 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1384,6 +1384,22 @@ config MFD_JUNIPER_EXT_CPLD
 	  called "ptxpmb-ext-cpld"
 
 
+config MFD_JUNIPER_CBC
+	tristate "Juniper PTX1K CBC FPGA"
+	depends on JNX_PTX1K_RCB
+	default y if JNX_PTX1K_RCB
+	select MFD_CORE
+	select MTD
+	select MTD_SAM_FLASH
+	select I2C_SAM
+	select GPIO_CBC_PRESENCE if JNX_CONNECTOR
+	help
+	  Select this to enable the CBC FPGA multi-function kernel driver.
+	  This FPGA is present on the PTX1K RCB Juniper platform.
+
+	  This driver can be built as a module. If built as a module it will be
+	  called "cbc-core"
+
 config MFD_TWL4030_AUDIO
 	bool "TI TWL4030 Audio"
 	depends on TWL4030_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index da94482..0ea6dc6 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -151,6 +151,7 @@ obj-$(CONFIG_AB8500_GPADC)	+= ab8500-gpadc.o
 obj-$(CONFIG_MFD_JUNIPER_CPLD)	+= ptxpmb-cpld-core.o
 obj-$(CONFIG_MFD_JUNIPER_SAM)	+= sam-core.o
 obj-$(CONFIG_MFD_JUNIPER_EXT_CPLD) += ptxpmb-ext-cpld-core.o
+obj-$(CONFIG_MFD_JUNIPER_CBC)	+= cbc-core.o
 obj-$(CONFIG_MFD_DB8500_PRCMU)	+= db8500-prcmu.o
 # ab8500-core need to come after db8500-prcmu (which provides the channel)
 obj-$(CONFIG_AB8500_CORE)	+= ab8500-core.o ab8500-sysctrl.o
diff --git a/drivers/mfd/cbc-core.c b/drivers/mfd/cbc-core.c
new file mode 100644
index 0000000..1e6c95b
--- /dev/null
+++ b/drivers/mfd/cbc-core.c
@@ -0,0 +1,971 @@
+/*
+ * drivers/mfd/cbc-core.c
+ *
+ * Copyright (c) 2014, Juniper Networks
+ * Author: Georgi Vlaev <gvlaev@juniper.net>
+ * Based on: sam-core.c
+ *
+ * The CBC FPGA intergrates the PTX1K CB and some functions of
+ * the SAM FPGA on single device. We're reusing the SAM I2C,
+ * MTD flash drivers. The JNX CB logic is implemented in
+ * drivers/jnx/jnx-cbc-ptx1k.c and drivers/jnx/jnx-cbd-fpga-ptx1k.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/irqdomain.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/sam.h>
+#include <linux/jnx/pci_ids.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/cbc-core.h>
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/string.h>
+#include <linux/ctype.h>
+#endif
+
+enum {
+	CBC_CELL_SAM_I2C,       /* SAM I2C accelerator */
+	CBC_CELL_SAM_MTD,       /* SAM EPCS64 config flash */
+	CBC_CELL_GPIO,          /* CBC spare GPIO */
+	CBC_CELL_JNX_CBD,       /* CBC CB JNX driver */
+	CBC_CELL_GPIO_PRS,      /* CBC presence as GPIO (CH_PRS) */
+	CBC_CELL_GPIO_PRS_OTHER,/* CBC presence ss GPIO (OTHER_CH_PRS) */
+	CBC_CELL_GPIO_PRS_SIB,  /* CBC presence as GPIO (CH_PRS_SIB) */
+	CBC_NUM_MFD_CELLS
+};
+
+#define CBC_IRQ_NR			31
+#define CBC_RES_NR			2 /* iomem, irq */
+#define CBC_RES_NR_NOIRQ		1 /* iomem */
+
+struct cbc_fpga_data {
+	void __iomem *membase;
+	struct pci_dev *pdev;
+	u32 fpga_rev;	/* CBC revision */
+	u32 fpga_date;	/* CBC revision date */
+	int i2c_mstr_count; /* CBC/I2C SAM master count */
+	struct irq_domain *irq_domain;
+	u32 int_src; /* interrupt src reg (MSI, legacy) */
+	u32 int_en; /* interrupt en reg (MSI, legacy) */
+	spinlock_t irq_lock;
+	struct mfd_cell mfd_cells[CBC_NUM_MFD_CELLS];
+	struct resource mfd_i2c_resources[CBC_RES_NR];
+	struct resource mfd_mtd_resources[CBC_RES_NR_NOIRQ];
+	struct resource mfd_gpio_resources[CBC_RES_NR_NOIRQ];
+	struct resource mfd_jnx_resources[CBC_RES_NR_NOIRQ];
+	struct resource mfd_gpio_prs_resources[CBC_RES_NR_NOIRQ];
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *cbc_debugfs_dir;
+	u32 debug_address; /* any register offsset */
+	struct debugfs_regset32 *regset; /* all regs */
+	u32 test_int_cnt; /* TEST_INT counter */
+	u32 i2c_accel_cnt; /* INT_I2C_ACCEL cnt */
+	u32 i2c_accel_empty_cnt; /* INT_I2C_ACCEL cnt && !CBC_STATUS_IRQ_EN */
+#endif
+};
+
+/* debugfs */
+/* TODO: split into a separate file */
+#ifdef CONFIG_DEBUG_FS
+
+#define dump_register(n)		\
+{					\
+	.name	= __stringify(n),	\
+	.offset = n,			\
+}
+
+static const struct debugfs_reg32 cbc_regs[] = {
+	dump_register(CBC_TOP_REGS_INT_SRC),
+	dump_register(CBC_TOP_REGS_INT_EN),
+	dump_register(CBC_TOP_REGS_I2C_SEL),
+	dump_register(CBC_TOP_REGS_CH_PRS),
+	dump_register(CBC_TOP_REGS_RTL_REVISION),
+	dump_register(CBC_TOP_REGS_PWR_STATUS),
+	dump_register(CBC_TOP_REGS_I2CS_INT),
+	dump_register(CBC_TOP_REGS_I2CS_INT_EN),
+	dump_register(CBC_TOP_REGS_MSTR_CONFIG),
+	dump_register(CBC_TOP_REGS_MSTR_ALIVE),
+	dump_register(CBC_TOP_REGS_MSTR_ALIVE_CNT),
+	dump_register(CBC_TOP_REGS_FPGA_REV),
+	dump_register(CBC_TOP_REGS_FPGA_DATE),
+	dump_register(CBC_TOP_REGS_DEVICE_CTRL),
+	dump_register(CBC_TOP_REGS_SLOT_ID),
+	dump_register(CBC_TOP_REGS_SCRATCH),
+	dump_register(CBC_TOP_REGS_RE_HALT),
+	dump_register(CBC_TOP_REGS_OTHER_CH_PRS_CHANGE),
+	dump_register(CBC_TOP_REGS_FPC_SPARE_A),
+	dump_register(CBC_TOP_REGS_FT_SPARE),
+	dump_register(CBC_TOP_REGS_CB_SPARE),
+	dump_register(CBC_TOP_REGS_MTTR_0),
+	dump_register(CBC_TOP_REGS_MTTR_1),
+	dump_register(CBC_TOP_REGS_MTTR_2),
+	dump_register(CBC_TOP_REGS_MSTR_FRC),
+	dump_register(CBC_TOP_REGS_MSTR_RSN),
+	dump_register(CBC_TOP_REGS_ME_WRITE_0),
+	dump_register(CBC_TOP_REGS_ME_WRITE_1),
+	dump_register(CBC_TOP_REGS_ME_WRITE_2),
+	dump_register(CBC_TOP_REGS_ME_WRITE_3),
+	dump_register(CBC_TOP_REGS_ME_WRITE_4),
+	dump_register(CBC_TOP_REGS_ME_WRITE_5),
+	dump_register(CBC_TOP_REGS_ME_READ_0),
+	dump_register(CBC_TOP_REGS_ME_READ_1),
+	dump_register(CBC_TOP_REGS_ME_READ_2),
+	dump_register(CBC_TOP_REGS_ME_READ_3),
+	dump_register(CBC_TOP_REGS_ME_READ_4),
+	dump_register(CBC_TOP_REGS_ME_READ_5),
+	dump_register(CBC_TOP_REGS_ME_STATUS),
+	dump_register(CBC_TOP_REGS_LIU_CONFIG),
+	dump_register(CBC_TOP_REGS_CBC_8112_8614_RST),
+	dump_register(CBC_TOP_REGS_CCG_CONFIG),
+	dump_register(CBC_TOP_REGS_SFPP_CONTROL),
+	dump_register(CBC_TOP_REGS_SFPP_PCIE_FT_STATUS),
+	dump_register(CBC_TOP_REGS_GPIO_CTRL),
+	dump_register(CBC_TOP_REGS_GPIO_OUTPUT_DATA),
+	dump_register(CBC_TOP_REGS_GPIO_INPUT_DATA),
+	dump_register(CBC_TOP_REGS_CCG_STATUS_RT),
+	dump_register(CBC_TOP_REGS_CCG_STATUS_LATCHED),
+	dump_register(CBC_TOP_REGS_CCG_STATUS_INTERRUPT_ENABLE),
+	dump_register(CBC_TOP_REGS_OTHER_LED),
+	dump_register(CBC_TOP_REGS_SFPP_I2C_RW_CONTROL),
+	dump_register(CBC_TOP_REGS_SFPP_I2C_REG_ADDRESS),
+	dump_register(CBC_TOP_REGS_SFPP_I2C_WDATA),
+	dump_register(CBC_TOP_REGS_SFPP_I2C_RDATA),
+	dump_register(CBC_TOP_REGS_SFPP_I2C_STATUS),
+	dump_register(CBC_TOP_REGS_SFPP_I2C_DEV_ADDRESS),
+	dump_register(CBC_TOP_REGS_SIB_SPARE_OUTPUTENABLE),
+	dump_register(CBC_TOP_REGS_SIB_SPARE_OUTPUT),
+	dump_register(CBC_TOP_REGS_SIB_SPARE_INPUT),
+	dump_register(CBC_TOP_REGS_FPC_SPARE_OUTPUTENABLE),
+	dump_register(CBC_TOP_REGS_FPC_SPARE_OUTPUT),
+	dump_register(CBC_TOP_REGS_FPC_SPARE_INPUT),
+	dump_register(CBC_TOP_REGS_OTHER_SPARE_OUTPUTENABLE),
+	dump_register(CBC_TOP_REGS_OTHER_SPARE_OUTPUT),
+	dump_register(CBC_TOP_REGS_OTHER_SPARE_INPUT),
+	dump_register(CBC_TOP_REGS_MSI_INT_SRC),
+	dump_register(CBC_TOP_REGS_MSI_INT_EN),
+	dump_register(CBC_TOP_REGS_TOD_LOCK),
+	dump_register(CBC_TOP_REGS_TOD_TIME_79_48),
+	dump_register(CBC_TOP_REGS_TOD_TIME_47_16),
+	dump_register(CBC_TOP_REGS_TOD_TIME_15_0),
+	dump_register(CBC_TOP_REGS_TOD_CLKACC_CRC),
+	dump_register(CBC_TOP_REGS_APS_COMMAND_REGISTER),
+	dump_register(CBC_TOP_REGS_APS_STATUS_REGISTER),
+	dump_register(CBC_TOP_REGS_APS_FRAME_DATA0),
+	dump_register(CBC_TOP_REGS_APS_FRAME_DATA1),
+	dump_register(CBC_TOP_REGS_APS_FRAME_DATA2),
+	dump_register(CBC_TOP_REGS_APS_FRAME_DATA3),
+	dump_register(CBC_TOP_REGS_APS_FRAME_DATA4),
+	dump_register(CBC_TOP_REGS_APS_FRAME_DATA5),
+	dump_register(CBC_TOP_REGS_APS_FRAME_DATA6),
+	dump_register(CBC_TOP_REGS_APS_FRAME_DATA7),
+	dump_register(CBC_TOP_REGS_APS_APS_REV_REG),
+	dump_register(CBC_TOP_REGS_APS_APS_DEBUG0),
+	dump_register(CBC_TOP_REGS_APS_COUNTER_GOOD_FRAMES),
+	dump_register(CBC_TOP_REGS_APS_COUNTER_BAD_FRAMES),
+	dump_register(CBC_TOP_REGS_APS_APS_LINK_STATUS),
+	dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG0),
+	dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG1),
+	dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG2),
+	dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG3),
+	dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG4),
+	dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG5),
+	dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG6),
+	dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG7),
+	dump_register(CBC_INFO_I2C_MASTER_COUNT),
+	dump_register(CBC_INFO_FAST_I2C_CONFIG_A),
+	dump_register(CBC_INFO_FAST_I2C_CONFIG_B),
+	dump_register(CBC_INFO_FEATURES),
+	dump_register(CBC_INFO_PMA_CONTROL),
+	dump_register(CBC_INFO_PMA_STATUS),
+	dump_register(CBC_STATUS_IRQ_EN),
+	dump_register(CBC_STATUS_IRQ_ST),
+	dump_register(CBC_REMOTE_UPGRADE_CONTROL),
+	dump_register(CBC_REMOTE_UPGRADE_STATUS),
+	dump_register(CBC_FLASH_IF_ADDRESS),
+	dump_register(CBC_FLASH_IF_BYTE_COUNT),
+	dump_register(CBC_FLASH_IF_CONTROL),
+	dump_register(CBC_FLASH_IF_STATUS),
+	dump_register(CBC_FLASH_IF_WRITE_DATA),
+	dump_register(CBC_FLASH_IF_READ_DATA),
+};
+
+/*
+ * Usage:
+ * #echo ADDR > <debugfs>/cbc-core/register-address
+ * #cat <debugfs>/cbc-core/register-value
+ *
+ */
+static int cbc_debugfs_addr_print(struct seq_file *s, void *p)
+{
+	struct cbc_fpga_data *cbc = (struct cbc_fpga_data *)s->private;
+
+	seq_printf(s, "0x%08X\n", cbc->debug_address);
+
+	return 0;
+}
+
+static int cbc_debugfs_addr_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, cbc_debugfs_addr_print, inode->i_private);
+}
+
+static ssize_t cbc_debugfs_addr_write(struct file *file,
+	const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	struct cbc_fpga_data *cbc =
+		((struct seq_file *)(file->private_data))->private;
+	unsigned long user_address;
+	int err;
+
+	err = kstrtoul_from_user(user_buf, count, 0, &user_address);
+	if (err)
+		return err;
+
+	if (user_address > 0xffffff) {
+		dev_err(&cbc->pdev->dev, "debugfs error input > 0xffffff\n");
+		return -EINVAL;
+	}
+	cbc->debug_address = user_address;
+
+	return count;
+}
+
+static const struct file_operations cbc_debugfs_addr_fops = {
+	.open = cbc_debugfs_addr_open,
+	.write = cbc_debugfs_addr_write,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
+static int cbc_debugfs_val_print(struct seq_file *s, void *p)
+{
+	struct cbc_fpga_data *cbc = (struct cbc_fpga_data *)s->private;
+
+	seq_printf(s, "0x%08X\n", ioread32(cbc->membase + cbc->debug_address));
+
+	return 0;
+}
+
+static int cbc_debugfs_val_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, cbc_debugfs_val_print, inode->i_private);
+}
+
+static ssize_t cbc_debugfs_val_write(struct file *file,
+	const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	struct cbc_fpga_data *cbc =
+		((struct seq_file *)(file->private_data))->private;
+	unsigned long user_val;
+	int err;
+
+	err = kstrtoul_from_user(user_buf, count, 0, &user_val);
+	if (err)
+		return err;
+
+	iowrite32(user_val, cbc->membase + cbc->debug_address);
+	ioread32(cbc->membase + cbc->debug_address);
+
+	return count;
+}
+
+static const struct file_operations cbc_debugfs_val_fops = {
+	.open = cbc_debugfs_val_open,
+	.write = cbc_debugfs_val_write,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
+/*
+ * Usage:
+ * #echo 1 > <debugfs>/cbc-core/test-int
+ * #cat <debugfs>/cbc-core/test-int
+ *
+ */
+static void cbc_fire_test_int(struct cbc_fpga_data *cbc)
+{
+	u32 int_en;
+
+	/* Disable and enable the TEST_INT bit  */
+	int_en = ioread32(cbc->membase + cbc->int_en);
+	iowrite32(int_en & ~BIT(INT_TEST), cbc->membase + cbc->int_en);
+	ioread32(cbc->membase + cbc->int_en);
+	iowrite32(int_en | BIT(INT_TEST), cbc->membase + cbc->int_en);
+	ioread32(cbc->membase + cbc->int_en);
+}
+
+static int cbc_debugfs_test_int_print(struct seq_file *s, void *p)
+{
+	struct cbc_fpga_data *cbc = (struct cbc_fpga_data *)s->private;
+
+	seq_printf(s, "0x%08X\n", cbc->test_int_cnt);
+	return 0;
+}
+
+static int cbc_debugfs_test_int_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, cbc_debugfs_test_int_print, inode->i_private);
+}
+
+/* write address triggers a page read */
+static ssize_t cbc_debugfs_test_int_write(struct file *file,
+	const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	struct cbc_fpga_data *cbc =
+		((struct seq_file *)(file->private_data))->private;
+	unsigned long val;
+	int err;
+
+	err = kstrtoul_from_user(user_buf, count, 0, &val);
+	if (err)
+		return err;
+
+	if (val)
+		cbc_fire_test_int(cbc);
+
+	return count;
+}
+
+static const struct file_operations cbc_debugfs_test_int_fops = {
+	.open = cbc_debugfs_test_int_open,
+	.write = cbc_debugfs_test_int_write,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
+
+static int cbc_debugfs_i2c_accel_int_print(struct seq_file *s, void *p)
+{
+	struct cbc_fpga_data *cbc = (struct cbc_fpga_data *)s->private;
+
+	seq_printf(s, "%d:%d\n", cbc->i2c_accel_cnt,
+					cbc->i2c_accel_empty_cnt);
+
+	return 0;
+}
+
+static int cbc_debugfs_i2c_accel_int_open(struct inode *inode,
+				struct file *file)
+{
+	return single_open(file, cbc_debugfs_i2c_accel_int_print,
+				inode->i_private);
+}
+
+static const struct file_operations cbc_debugfs_i2c_accel_int_fops = {
+	.open = cbc_debugfs_i2c_accel_int_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
+static int cbc_debugfs_init(struct cbc_fpga_data *cbc)
+{
+	struct dentry *file;
+	struct device *dev = &cbc->pdev->dev;
+
+	cbc->cbc_debugfs_dir = debugfs_create_dir("cbc-core", NULL);
+	if (!cbc->cbc_debugfs_dir)
+		return -ENOMEM;
+/* regset dump */
+	cbc->regset = devm_kzalloc(dev, sizeof(*cbc->regset), GFP_KERNEL);
+	if (!cbc->regset)
+		goto err;
+
+	cbc->regset->regs = cbc_regs;
+	cbc->regset->nregs = ARRAY_SIZE(cbc_regs);
+	cbc->regset->base = cbc->membase;
+
+	file = debugfs_create_regset32("regdump", S_IRUGO,
+		cbc->cbc_debugfs_dir, cbc->regset);
+	if (!file)
+		goto err;
+
+/* Any register read/write */
+	file = debugfs_create_file("register-address",
+		(S_IRUGO | S_IWUSR | S_IWGRP),
+		cbc->cbc_debugfs_dir, cbc, &cbc_debugfs_addr_fops);
+	if (!file)
+		goto err;
+
+	file = debugfs_create_file("register-value",
+		(S_IRUGO | S_IWUSR | S_IWGRP),
+		cbc->cbc_debugfs_dir, cbc, &cbc_debugfs_val_fops);
+
+	if (!file)
+		goto err;
+
+/* TEST_INT */
+	file = debugfs_create_file("test-int",
+		(S_IRUGO | S_IWUSR | S_IWGRP),
+		cbc->cbc_debugfs_dir, cbc, &cbc_debugfs_test_int_fops);
+	if (!file)
+		goto err;
+
+/* I2C_ACCEL_INT */
+	file = debugfs_create_file("i2c-accel-int",
+		(S_IRUGO | S_IWUSR | S_IWGRP),
+		cbc->cbc_debugfs_dir, cbc, &cbc_debugfs_i2c_accel_int_fops);
+	if (!file)
+		goto err;
+
+	return 0;
+err:
+	debugfs_remove_recursive(cbc->cbc_debugfs_dir);
+	dev_err(dev, "failed to create debugfs entries.\n");
+
+	return -ENOMEM;
+}
+
+static void cbc_debugfs_remove(struct cbc_fpga_data *cbc)
+{
+	debugfs_remove_recursive(cbc->cbc_debugfs_dir);
+}
+#endif
+
+/*
+ * CBC/SAM interrupt handling
+ * The CBC_STATUS_IRQ_EN register is not used in the CBC
+ * The CBC_STATUS_IRQ_ST register just reports the pending
+ * interrupts for each master.
+ */
+static void sam_enable_irq(struct device *dev, enum sam_irq_type type,
+			   int irq, u32 mask)
+{
+}
+
+static void sam_disable_irq(struct device *dev, enum sam_irq_type type,
+			    int irq, u32 mask)
+{
+}
+
+static u32 sam_irq_status(struct device *dev, enum sam_irq_type type, int irq)
+{
+	struct cbc_fpga_data *cbc = dev_get_drvdata(dev);
+
+	return ioread32(cbc->membase + CBC_STATUS_IRQ_ST);
+}
+
+static void sam_irq_status_clear(struct device *dev, enum sam_irq_type type,
+				 int irq, u32 mask)
+{
+	struct cbc_fpga_data *cbc = dev_get_drvdata(dev);
+
+	iowrite32(mask, cbc->membase + CBC_STATUS_IRQ_ST);
+	ioread32(cbc->membase + CBC_STATUS_IRQ_ST);
+
+	/*
+	 * Clear the MSI INT_I2C_ACCEL interrupt.
+	 * This might cause additional interrupt, but we
+	 * won't miss the MSI in the windows from clearing the
+	 * CBC_STATUS_IRQ_ST and INT_I2C_ACCEL.
+	 */
+	iowrite32(BIT(INT_I2C_ACCEL), cbc->membase + cbc->int_src);
+	ioread32(cbc->membase + cbc->int_src);
+}
+
+static irqreturn_t cbc_irq_handler(int irq, void *data)
+{
+	struct cbc_fpga_data *cbc = data;
+	u32 int_src;
+	int i, iirq, ret = IRQ_NONE;
+
+	int_src = ioread32(cbc->membase + cbc->int_src);
+
+	/* (CBC) Test interrupt - just count */
+	if (int_src & BIT(INT_TEST)) {
+		cbc->test_int_cnt++;
+		iowrite32(BIT(INT_TEST), cbc->membase + cbc->int_src);
+		ioread32(cbc->membase + cbc->int_src);
+		ret = IRQ_HANDLED;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	if (int_src & BIT(INT_I2C_ACCEL)) {
+		u32 irq_st;
+
+		cbc->i2c_accel_cnt++;
+		irq_st = ioread32(cbc->membase + CBC_STATUS_IRQ_ST);
+		if (!irq_st)
+			cbc->i2c_accel_empty_cnt++;
+	}
+#endif
+
+	for (i = 0; i < CBC_IRQ_NR; i++) {
+		if (int_src & BIT(i)) {
+			iirq = irq_find_mapping(cbc->irq_domain, i);
+			if (iirq) {
+				generic_handle_irq(iirq);
+				ret = IRQ_HANDLED;
+			}
+		}
+	}
+
+	return ret;
+}
+
+/* irq_chip */
+static void cbc_irq_unmask(struct irq_data *data)
+{
+	struct cbc_fpga_data *cbc = irq_data_get_irq_chip_data(data);
+	unsigned long flags;
+	int irq = data->hwirq & 0x3f;
+	u32 int_en;
+
+	spin_lock_irqsave(&cbc->irq_lock, flags);
+
+	int_en = ioread32(cbc->membase + cbc->int_en);
+	iowrite32(int_en | BIT(irq), cbc->membase + cbc->int_en);
+	ioread32(cbc->membase + cbc->int_en);
+
+	spin_unlock_irqrestore(&cbc->irq_lock, flags);
+}
+
+static void cbc_irq_mask(struct irq_data *data)
+{
+	struct cbc_fpga_data *cbc = irq_data_get_irq_chip_data(data);
+	unsigned long flags;
+	int irq = data->hwirq & 0x3f;
+	u32 int_en;
+
+	spin_lock_irqsave(&cbc->irq_lock, flags);
+
+	int_en = ioread32(cbc->membase + cbc->int_en);
+	iowrite32(int_en & ~BIT(irq), cbc->membase + cbc->int_en);
+	ioread32(cbc->membase + cbc->int_en);
+
+	spin_unlock_irqrestore(&cbc->irq_lock, flags);
+}
+
+static void cbc_irq_ack(struct irq_data *data)
+{
+	struct cbc_fpga_data *cbc = irq_data_get_irq_chip_data(data);
+	int irq = data->hwirq & 0x3f;
+
+	iowrite32(BIT(irq), cbc->membase + cbc->int_src);
+	ioread32(cbc->membase + cbc->int_src);
+}
+
+struct irq_chip cbc_irq_chip = {
+	.name		= "cbc-core",
+	.irq_ack	= cbc_irq_ack,
+	.irq_mask	= cbc_irq_mask,
+	.irq_unmask	= cbc_irq_unmask,
+};
+
+/* irq_domain */
+static int cbc_irq_map(struct irq_domain *h, unsigned int virq,
+			irq_hw_number_t hw)
+{
+	irq_set_chip_data(virq, h->host_data);
+	irq_set_chip_and_handler(virq, &cbc_irq_chip, handle_level_irq);
+	irq_set_noprobe(virq);
+
+	return 0;
+}
+
+static struct irq_domain_ops cbc_irq_domain_ops = {
+	.map	= cbc_irq_map,
+	.xlate	= irq_domain_xlate_twocell,
+};
+
+static int cbc_request_interrupt(struct cbc_fpga_data *cbc)
+{
+	int err;
+	struct pci_dev *pdev = cbc->pdev;
+	struct device *dev = &pdev->dev;
+
+	if (!pdev->irq)
+		return -ENODEV;
+
+	if (!pci_enable_msi(pdev)) {
+		cbc->int_src = CBC_TOP_REGS_MSI_INT_SRC;
+		cbc->int_en = CBC_TOP_REGS_MSI_INT_EN;
+	} else {
+		cbc->int_src = CBC_TOP_REGS_INT_SRC;
+		cbc->int_en = CBC_TOP_REGS_INT_EN;
+	}
+
+	cbc->irq_domain = irq_domain_add_linear(dev->of_node, CBC_IRQ_NR,
+						&cbc_irq_domain_ops, cbc);
+	if (!cbc->irq_domain) {
+		dev_err(dev, "could not create irq domain\n");
+		return -ENOMEM;
+	}
+
+	err = devm_request_irq(dev, pdev->irq, cbc_irq_handler,
+				0, dev_driver_string(dev), cbc);
+
+	if (err) {
+		dev_err(dev, "failed to request irq %d\n", pdev->irq);
+		irq_domain_remove(cbc->irq_domain);
+		pci_disable_msi(pdev);
+		return err;
+	}
+
+	return 0;
+}
+
+static void cbc_free_interrupt(struct cbc_fpga_data *cbc)
+{
+	struct pci_dev *pdev = cbc->pdev;
+
+	devm_free_irq(&pdev->dev, pdev->irq, cbc);
+	if (cbc->irq_domain)
+		irq_domain_remove(cbc->irq_domain);
+	pci_disable_msi(pdev);
+}
+
+/* scratch access test */
+static int cbc_test_scratch(struct cbc_fpga_data *cbc)
+{
+	struct pci_dev *pdev = cbc->pdev;
+	struct device *dev = &pdev->dev;
+	u32 acc, i;
+
+	/*
+	 * Check rw register access -> use the scratch reg.
+	 * Earlier revisions fail on scratch rw test
+	 */
+	iowrite32(0xA5A5A5A5, cbc->membase + CBC_TOP_REGS_SCRATCH);
+	acc = ioread32(cbc->membase + CBC_TOP_REGS_SCRATCH);
+	if (acc != 0xA5A5A5A5) {
+		dev_err(dev,
+			"CBC scratch write failed: %08x -> %08x",
+			0xA5A5A5A5, acc);
+		return -EIO;
+	}
+
+	for (i = 0; i < 0xf0000000; i += 0x01010101) {
+		iowrite32(i, cbc->membase + CBC_TOP_REGS_SCRATCH);
+		acc = ioread32(cbc->membase + CBC_TOP_REGS_SCRATCH);
+		if (acc != i) {
+			dev_err(dev, "CBC scratch write failed: %08x -> %08x",
+				i, acc);
+			return -EIO;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Check if the CBC is capable of generating interrupts.
+ * Use the test interrupt bit. The counter is incremented
+ * from the interrupt handler. We want it > 0
+ */
+static int cbc_test_interrupts(struct cbc_fpga_data *cbc)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(100);
+
+	cbc->test_int_cnt = 0;
+	cbc_fire_test_int(cbc);
+
+	while (!cbc->test_int_cnt) {
+		if (time_after(jiffies, timeout))
+			break;
+		schedule_timeout_interruptible(msecs_to_jiffies(10));
+	}
+
+	return cbc->test_int_cnt;
+}
+
+/* sysfs entries */
+static ssize_t version_show(struct device *dev, struct device_attribute *attr,
+				char *buf)
+{
+	struct cbc_fpga_data *cbc = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", cbc->fpga_rev);
+}
+
+static ssize_t cbc_date_show(struct device *dev, struct device_attribute *attr,
+				char *buf)
+{
+	struct cbc_fpga_data *cbc = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%08x\n", cbc->fpga_date);
+}
+
+static DEVICE_ATTR(version, S_IRUGO, version_show, NULL);
+static DEVICE_ATTR(cbc_date, S_IRUGO, cbc_date_show, NULL);
+
+static struct attribute *cbc_attrs[] = {
+	&dev_attr_version.attr,
+	&dev_attr_cbc_date.attr,
+	NULL,
+};
+
+static struct attribute_group cbc_attr_group = {
+	.attrs  = cbc_attrs,
+};
+
+/* SAM drivers interrupt handling */
+static struct sam_platform_data cbc_sam_plat_data = {
+	.enable_irq = sam_enable_irq,
+	.disable_irq = sam_disable_irq,
+	.irq_status = sam_irq_status,
+	.irq_status_clear = sam_irq_status_clear,
+	.i2c_mux_channels = 4,
+};
+
+/*
+ * List of modules to be loaded. Should only be necessary if devicetree
+ * is not configured, but doesn't hurt otherwise.
+ */
+const char *cbc_modules[] = {
+	"i2c-sam",
+	"sam-flash",
+	"gpio-cbc",
+	NULL
+};
+
+static void cbc_request_modules(bool wait)
+{
+	struct module *m;
+	int i;
+
+	for (i = 0; cbc_modules[i]; i++) {
+		mutex_lock(&module_mutex);
+		m = find_module(cbc_modules[i]);
+		mutex_unlock(&module_mutex);
+		if (!m) {
+			if (wait)
+				request_module(cbc_modules[i]);
+			else
+				request_module_nowait(cbc_modules[i]);
+		}
+	}
+}
+
+static struct cbc_fpga_platdata cbc_fpga_platdata[] = {
+	[JNX_CBD_FPGA_PTX1K] = {
+		.board_type = JNX_CBD_FPGA_PTX1K,
+	},
+	[JNX_CBD_FPGA_PTX1K_P2] = {
+		.board_type = JNX_CBD_FPGA_PTX1K_P2,
+	},
+	[JNX_CBD_FPGA_PTX21K] = {
+		.board_type = JNX_CBD_FPGA_PTX21K,
+	},
+};
+
+static int cbc_fpga_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	int err;
+	struct cbc_fpga_data *cbc;
+	struct device *dev = &pdev->dev;
+
+	/* Request modules, but keep going */
+	cbc_request_modules(false);
+
+	err = pcim_enable_device(pdev);
+	if (err)
+		return err;
+
+	err = pcim_iomap_regions(pdev, 1 << 0, "cbc-core");
+	if (err)
+		return err;
+
+	cbc = devm_kzalloc(dev, sizeof(*cbc), GFP_KERNEL);
+	if (cbc == NULL)
+		return -ENOMEM;
+
+	cbc->membase = pcim_iomap_table(pdev)[0];
+	cbc->pdev = pdev;
+	pci_set_drvdata(pdev, cbc);
+
+/* Check IO */
+	err = cbc_test_scratch(cbc);
+	if (err)
+		return err;
+
+/* CBC Revision ID */
+	cbc->fpga_rev = ioread32(cbc->membase + CBC_TOP_REGS_RTL_REVISION);
+	cbc->fpga_date = ioread32(cbc->membase + CBC_TOP_REGS_FPGA_DATE);
+	cbc->i2c_mstr_count =
+		ioread32(cbc->membase + CBC_INFO_I2C_MASTER_COUNT) & 0xff;
+
+	spin_lock_init(&cbc->irq_lock);
+
+	/* i2c accel */
+	cbc->mfd_cells[CBC_CELL_SAM_I2C].name = "i2c-sam";
+	cbc->mfd_cells[CBC_CELL_SAM_I2C].num_resources = CBC_RES_NR;
+	cbc->mfd_cells[CBC_CELL_SAM_I2C].resources = cbc->mfd_i2c_resources;
+	cbc->mfd_cells[CBC_CELL_SAM_I2C].of_compatible = "jnx,i2c-sam";
+	cbc->mfd_cells[CBC_CELL_SAM_I2C].platform_data = &cbc_sam_plat_data;
+	cbc->mfd_cells[CBC_CELL_SAM_I2C].pdata_size =
+				sizeof(struct sam_platform_data);
+
+	cbc->mfd_i2c_resources[0].start = CBC_I2C_ACCEL_IF_MEM_START;
+	cbc->mfd_i2c_resources[0].end = CBC_I2C_ACCEL_IF_MEM_END;
+	cbc->mfd_i2c_resources[0].flags = IORESOURCE_MEM;
+	cbc->mfd_i2c_resources[1].start =
+			cbc->mfd_i2c_resources[1].end = INT_I2C_ACCEL;
+	cbc->mfd_i2c_resources[1].flags = IORESOURCE_IRQ;
+
+	/* epcs64,128 mtd flash */
+	cbc->mfd_cells[CBC_CELL_SAM_MTD].name = "flash-sam";
+	cbc->mfd_cells[CBC_CELL_SAM_MTD].num_resources = CBC_RES_NR_NOIRQ;
+	cbc->mfd_cells[CBC_CELL_SAM_MTD].resources = cbc->mfd_mtd_resources;
+	cbc->mfd_cells[CBC_CELL_SAM_MTD].of_compatible = "jnx,flash-sam";
+
+	cbc->mfd_mtd_resources[0].start = CBC_FLASH_IF_MEM_START;
+	cbc->mfd_mtd_resources[0].end = CBC_FLASH_IF_MEM_END;
+	cbc->mfd_mtd_resources[0].flags = IORESOURCE_MEM;
+
+	/* CBC GPIO  */
+	cbc->mfd_cells[CBC_CELL_GPIO].name = "gpio-cbc";
+	cbc->mfd_cells[CBC_CELL_GPIO].num_resources = CBC_RES_NR_NOIRQ;
+	cbc->mfd_cells[CBC_CELL_GPIO].resources = cbc->mfd_gpio_resources;
+	cbc->mfd_cells[CBC_CELL_GPIO].of_compatible = "jnx,gpio-cbc";
+	cbc->mfd_gpio_resources[0].flags = IORESOURCE_MEM;
+
+	/* CBC JNX  */
+	cbc->mfd_cells[CBC_CELL_JNX_CBD].name = "jnx-cbd-fpga";
+	cbc->mfd_cells[CBC_CELL_JNX_CBD].num_resources = CBC_RES_NR_NOIRQ;
+	cbc->mfd_cells[CBC_CELL_JNX_CBD].resources = cbc->mfd_jnx_resources;
+	cbc->mfd_cells[CBC_CELL_JNX_CBD].of_compatible = "jnx,jnx-cbd-fpga";
+	cbc->mfd_cells[CBC_CELL_JNX_CBD].platform_data =
+					&cbc_fpga_platdata[id->driver_data];
+	cbc->mfd_cells[CBC_CELL_JNX_CBD].pdata_size =
+					sizeof(struct cbc_fpga_platdata);
+
+	cbc->mfd_jnx_resources[0].flags = IORESOURCE_MEM;
+
+	/* CBC presence detect as GPIO  (CH_PRS) */
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS].name = "gpio-cbc-presence";
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS].id = 0;
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS].num_resources =
+					CBC_RES_NR_NOIRQ;
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS].resources =
+					cbc->mfd_gpio_prs_resources;
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS].of_compatible =
+					"jnx,gpio-cbc-presence";
+	cbc->mfd_gpio_prs_resources[0].flags = IORESOURCE_MEM;
+
+	/* CBC presence detect as GPIO  (OTHER_CH_PRS) */
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS_OTHER].name = "gpio-cbc-presence";
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS_OTHER].id = 1;
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS_OTHER].num_resources =
+					CBC_RES_NR_NOIRQ;
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS_OTHER].resources =
+						cbc->mfd_gpio_prs_resources;
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS_OTHER].of_compatible =
+						"jnx,gpio-cbc-presence-other";
+	cbc->mfd_gpio_prs_resources[0].flags = IORESOURCE_MEM;
+
+       /* CBC presence detect as GPIO  (CH_PRS_SIB) */
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS_SIB].name = "gpio-cbc-presence";
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS_SIB].id = 2;
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS_SIB].num_resources =
+						CBC_RES_NR_NOIRQ;
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS_SIB].resources =
+						cbc->mfd_gpio_prs_resources;
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS_SIB].of_compatible =
+						"jnx,gpio-cbc-presence-sib";
+	cbc->mfd_gpio_prs_resources[0].flags = IORESOURCE_MEM;
+
+	/* CBC is using MSI, make sure bus master is enabled */
+	pci_set_master(pdev);
+
+	/* Setup interrupts - MSI/INTx */
+	err = cbc_request_interrupt(cbc);
+	if (err < 0)
+		return err;
+
+	err = cbc_test_interrupts(cbc);
+	if (!err) {
+		dev_warn(dev, "Interrupt test failed, using poll mode");
+		cbc->mfd_cells[CBC_CELL_SAM_I2C].num_resources =
+						CBC_RES_NR_NOIRQ;
+	}
+
+	/* Request modules for good. */
+	cbc_request_modules(true);
+
+	err = mfd_add_devices(dev, pdev->bus->number, cbc->mfd_cells,
+			      ARRAY_SIZE(cbc->mfd_cells), &pdev->resource[0],
+			      0, cbc->irq_domain);
+	if (err < 0)
+		goto err_int;
+
+	err = sysfs_create_group(&pdev->dev.kobj, &cbc_attr_group);
+	if (err) {
+		sysfs_remove_group(&pdev->dev.kobj, &cbc_attr_group);
+		dev_err(&pdev->dev, "Failed to create attr group\n");
+		goto err_mfd;
+	}
+
+	dev_dbg(dev, "CBC/SAM I2C: Master count: %u\n", cbc->i2c_mstr_count);
+	dev_info(dev, "CBC FPGA Revision #%u (%02X/%02X/%04X)\n",
+		cbc->fpga_rev & 0xffff, (cbc->fpga_date >> 24) & 0xff,
+		(cbc->fpga_date >> 16) & 0xff, cbc->fpga_date & 0xffff);
+
+#ifdef CONFIG_DEBUG_FS
+	cbc_debugfs_init(cbc);
+#endif
+	return 0;
+
+err_mfd:
+	mfd_remove_devices(dev);
+
+err_int:
+	cbc_free_interrupt(cbc);
+
+	return err;
+}
+
+static void cbc_fpga_remove(struct pci_dev *pdev)
+{
+	struct cbc_fpga_data *cbc = pci_get_drvdata(pdev);
+
+#ifdef CONFIG_DEBUG_FS
+	cbc_debugfs_remove(cbc);
+#endif
+	sysfs_remove_group(&pdev->dev.kobj, &cbc_attr_group);
+	mfd_remove_devices(&pdev->dev);
+	cbc_free_interrupt(cbc);
+}
+
+static const struct pci_device_id cbc_fpga_id_tbl[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_CBC),
+	    .driver_data = JNX_CBD_FPGA_PTX1K },
+	{ PCI_DEVICE(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_CBC_P2),
+	    .driver_data = JNX_CBD_FPGA_PTX1K_P2 },
+	{ PCI_DEVICE(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_OMG_CBC),
+	    .driver_data = JNX_CBD_FPGA_PTX21K },
+	{ }
+};
+MODULE_DEVICE_TABLE(pci, cbc_fpga_id_tbl);
+
+static struct pci_driver cbc_fpga_driver = {
+	.name = "cbc-core",
+	.id_table = cbc_fpga_id_tbl,
+	.probe = cbc_fpga_probe,
+	.remove = cbc_fpga_remove,
+};
+
+module_pci_driver(cbc_fpga_driver);
+
+MODULE_DESCRIPTION("Juniper PTX1K RCB CBC MFD core driver");
+MODULE_AUTHOR("Georgi Vlaev <gvlaev@juniper.net>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/cbc-core.h b/include/linux/mfd/cbc-core.h
new file mode 100644
index 0000000..dc510a7
--- /dev/null
+++ b/include/linux/mfd/cbc-core.h
@@ -0,0 +1,181 @@
+/*
+ * PTX1K CBC FPGA registers
+ *
+ * Copyright (C) 2014 Juniper Networks
+ * Author: Georgi Vlaev <gvlaev@juniper.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __CBC_CORE_H__
+#define __CBC_CORE_H__
+
+enum jnx_cbd_fpga_board_types {
+	JNX_CBD_FPGA_PTX1K, JNX_CBD_FPGA_PTX1K_P2, JNX_CBD_FPGA_PTX21K };
+
+struct cbc_fpga_platdata {
+	enum jnx_cbd_fpga_board_types board_type;
+};
+
+#define CBC_TOP_REGS_INT_SRC			0x000
+#define CBC_TOP_REGS_INT_EN			0x004
+#define CBC_TOP_REGS_I2C_SEL			0x010
+#define CBC_TOP_REGS_CH_PRS			0x014
+#define CBC_TOP_REGS_RTL_REVISION		0x018
+#define CBC_TOP_REGS_PWR_STATUS			0x01c
+#define CBC_TOP_REGS_I2CS_INT			0x020
+#define CBC_TOP_REGS_I2CS_INT_EN		0x024
+#define CBC_TOP_REGS_MSTR_CONFIG		0x03c
+#define CBC_TOP_REGS_MSTR_ALIVE			0x040
+#define CBC_TOP_REGS_MSTR_ALIVE_CNT		0x044
+#define CBC_TOP_REGS_FPGA_REV			0x060
+#define CBC_TOP_REGS_FPGA_DATE			0x064
+#define CBC_TOP_REGS_DEVICE_CTRL		0x0c0
+#define CBC_TOP_REGS_SLOT_ID			0x0c4
+#define CBC_TOP_REGS_SCRATCH			0x0c8
+#define CBC_TOP_REGS_RE_HALT			0x0cc
+#define CBC_TOP_REGS_OTHER_CH_PRS_CHANGE	0x0d4
+#define CBC_TOP_REGS_FPC_SPARE_A		0x0dc
+#define CBC_TOP_REGS_FT_SPARE			0x0e4
+#define CBC_TOP_REGS_CB_SPARE			0x0e8
+#define CBC_TOP_REGS_MTTR_0			0x0ec
+#define CBC_TOP_REGS_MTTR_1			0x0f0
+#define CBC_TOP_REGS_MTTR_2			0x0f4
+#define CBC_TOP_REGS_MSTR_FRC			0x0f8
+#define CBC_TOP_REGS_MSTR_RSN			0x0fc
+#define CBC_TOP_REGS_ME_WRITE_0			0x100
+#define CBC_TOP_REGS_ME_WRITE_1			0x104
+#define CBC_TOP_REGS_ME_WRITE_2			0x108
+#define CBC_TOP_REGS_ME_WRITE_3			0x10c
+#define CBC_TOP_REGS_ME_WRITE_4			0x110
+#define CBC_TOP_REGS_ME_WRITE_5			0x114
+#define CBC_TOP_REGS_ME_READ_0			0x120
+#define CBC_TOP_REGS_ME_READ_1			0x124
+#define CBC_TOP_REGS_ME_READ_2			0x128
+#define CBC_TOP_REGS_ME_READ_3			0x12c
+#define CBC_TOP_REGS_ME_READ_4			0x130
+#define CBC_TOP_REGS_ME_READ_5			0x134
+#define CBC_TOP_REGS_ME_STATUS			0x13c
+#define CBC_TOP_REGS_LIU_CONFIG			0x140
+#define CBC_TOP_REGS_CBC_8112_8614_RST		0x144
+#define CBC_TOP_REGS_CCG_CONFIG			0x148
+#define CBC_TOP_REGS_SFPP_CONTROL		0x14c
+#define CBC_TOP_REGS_SFPP_PCIE_FT_STATUS	0x150
+#define CBC_TOP_REGS_GPIO_CTRL			0x168
+#define CBC_TOP_REGS_GPIO_OUTPUT_DATA		0x16c
+#define CBC_TOP_REGS_GPIO_INPUT_DATA		0x170
+#define CBC_TOP_REGS_CCG_STATUS_RT		0x174
+#define CBC_TOP_REGS_CCG_STATUS_LATCHED		0x178
+#define CBC_TOP_REGS_CCG_STATUS_INTERRUPT_ENABLE	0x17c
+#define CBC_TOP_REGS_OTHER_LED			0x180
+#define CBC_TOP_REGS_SFPP_I2C_RW_CONTROL	0x190
+#define CBC_TOP_REGS_SFPP_I2C_REG_ADDRESS	0x194
+#define CBC_TOP_REGS_SFPP_I2C_WDATA		0x198
+#define CBC_TOP_REGS_SFPP_I2C_RDATA		0x19c
+#define CBC_TOP_REGS_SFPP_I2C_STATUS		0x1a0
+#define CBC_TOP_REGS_SFPP_I2C_DEV_ADDRESS	0x1a4
+#define CBC_TOP_REGS_SIB_SPARE_OUTPUTENABLE	0x1a8
+#define CBC_TOP_REGS_SIB_SPARE_OUTPUT		0x1ac
+#define CBC_TOP_REGS_SIB_SPARE_INPUT		0x1c0
+#define CBC_TOP_REGS_FPC_SPARE_OUTPUTENABLE	0x1c4
+#define CBC_TOP_REGS_FPC_SPARE_OUTPUT		0x1c8
+#define CBC_TOP_REGS_FPC_SPARE_INPUT		0x1cc
+#define CBC_TOP_REGS_OTHER_SPARE_OUTPUTENABLE	0x1d0
+#define CBC_TOP_REGS_OTHER_SPARE_OUTPUT		0x1d4
+#define CBC_TOP_REGS_OTHER_SPARE_INPUT		0x1d8
+#define CBC_TOP_REGS_MSI_INT_SRC		0x1e0
+#define CBC_TOP_REGS_MSI_INT_EN			0x1e4
+#define CBC_TOP_REGS_TOD_LOCK			0x1f0
+#define CBC_TOP_REGS_TOD_TIME_79_48		0x1f4
+#define CBC_TOP_REGS_TOD_TIME_47_16		0x1f8
+#define CBC_TOP_REGS_TOD_TIME_15_0		0x1fc
+#define CBC_TOP_REGS_TOD_CLKACC_CRC		0x200
+#define CBC_TOP_REGS_APS_COMMAND_REGISTER	0x400
+#define CBC_TOP_REGS_APS_STATUS_REGISTER	0x404
+#define CBC_TOP_REGS_APS_FRAME_DATA0		0x420
+#define CBC_TOP_REGS_APS_FRAME_DATA1		0x424
+#define CBC_TOP_REGS_APS_FRAME_DATA2		0x428
+#define CBC_TOP_REGS_APS_FRAME_DATA3		0x42c
+#define CBC_TOP_REGS_APS_FRAME_DATA4		0x430
+#define CBC_TOP_REGS_APS_FRAME_DATA5		0x434
+#define CBC_TOP_REGS_APS_FRAME_DATA6		0x438
+#define CBC_TOP_REGS_APS_FRAME_DATA7		0x43c
+#define CBC_TOP_REGS_APS_APS_REV_REG		0x440
+#define CBC_TOP_REGS_APS_APS_DEBUG0		0x444
+#define CBC_TOP_REGS_APS_COUNTER_GOOD_FRAMES	0x448
+#define CBC_TOP_REGS_APS_COUNTER_BAD_FRAMES	0x44c
+#define CBC_TOP_REGS_APS_APS_LINK_STATUS	0x450
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG0		0x454
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG1		0x458
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG2		0x45c
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG3		0x460
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG4		0x464
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG5		0x468
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG6		0x46c
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG7		0x470
+
+/* CBC: SAM register base is @0x1000 */
+#define CBC_SAM_BASE				0x1000
+#define CBC_INFO_I2C_MASTER_COUNT		(CBC_SAM_BASE + 0x00c)
+#define CBC_INFO_FAST_I2C_CONFIG_A		(CBC_SAM_BASE + 0x018)
+#define CBC_INFO_FAST_I2C_CONFIG_B		(CBC_SAM_BASE + 0x01c)
+#define CBC_INFO_FEATURES			(CBC_SAM_BASE + 0x020)
+#define CBC_INFO_PMA_CONTROL			(CBC_SAM_BASE + 0x040)
+#define CBC_INFO_PMA_STATUS			(CBC_SAM_BASE + 0x044)
+#define CBC_STATUS_IRQ_EN			(CBC_SAM_BASE + 0x104)
+#define CBC_STATUS_IRQ_ST			(CBC_SAM_BASE + 0x108)
+/* remote upgrade_if */
+#define CBC_REMOTE_UPGRADE_CONTROL		(CBC_SAM_BASE + 0x200)
+#define CBC_REMOTE_UPGRADE_STATUS		(CBC_SAM_BASE + 0x204)
+/* flash_if */
+#define CBC_FLASH_IF_ADDRESS			(CBC_SAM_BASE + 0x300)
+#define CBC_FLASH_IF_BYTE_COUNT			(CBC_SAM_BASE + 0x304)
+#define CBC_FLASH_IF_CONTROL			(CBC_SAM_BASE + 0x308)
+#define CBC_FLASH_IF_STATUS			(CBC_SAM_BASE + 0x30c)
+#define CBC_FLASH_IF_WRITE_DATA			(CBC_SAM_BASE + 0x400)
+#define CBC_FLASH_IF_READ_DATA			(CBC_SAM_BASE + 0x500)
+
+/* Constants used for FPGA upgrades */
+#define SAM_FPGA_FLASH_VALID_BIT		0xA5A5A5A5
+#define SAM_FPGA_FLASH_VALID_BIT_ADDR		0x7F0000 /* EPCS64 */
+
+/* FPGA remote upgrade registers */
+#define SAM_FPGA_REMOTE_UPGRADE_TRIG_BIT	0x08000000
+#define SAM_FPGA_REMOTE_UPGRADE_STATUS_BUSY	0x01000000
+#define SAM_FPGA_REMOTE_UPGRADE_READ_PARAM	0x80000000
+#define SAM_FPGA_REMOTE_UPGRADE_WRITE_PARAM	0x40000000
+#define SAM_FPGA_REMOTE_UPGRADE_CONTROL_RESET	0x10000000
+
+#define SAM_FPGA_REMOTE_UPGRADE_PAGE_SEL	(0x04 << 24)
+#define SAM_FPGA_REMOTE_UPGRADE_ANF		(0x05 << 24)
+#define SAM_FPGA_USER_IMAGE_BASE		0x400000
+
+/* CBC/SAM flash */
+#define FLASH_STATUS_BUSY			0x01
+#define SAM_FLASH_IF_CONTROL_READ		0x01
+#define SAM_FLASH_IF_CONTROL_READ_SID		0x80
+#define ECS64_PAGE_SIZE				0x100
+
+/* MFD resources */
+/* CBC/SAM flash - mtd/devices/flash-sam.c */
+#define CBC_FLASH_IF_MEM_START			(CBC_SAM_BASE)
+#define CBC_FLASH_IF_MEM_END			(CBC_SAM_BASE + 0x05ff)
+/* CBC/SAM I2C Accel - i2c/busses/i2c-sam.c */
+#define CBC_I2C_ACCEL_IF_MEM_START		(CBC_SAM_BASE)
+#define CBC_I2C_ACCEL_IF_MEM_END		(CBC_SAM_BASE + 0x1ffff)
+
+/* MSI & legacy nterrupts [W1C] */
+#define INT_TEST			31 /* Test interrupt */
+#define INT_MSTRSHIP_LOSS		30 /* Mastership loss */
+#define INT_I2C_ACCEL			25 /* Cascade I2C accel int */
+#define INT_CASI2CS			24 /* casI2CS_INT */
+#define INT_SFPP_PCIE_STATUS_CHANGE	23 /* SFPP status, PCIe status */
+#define INT_CCG				22 /* CCG INT */
+#define INT_PSM_STATUS_CHANGE		21 /* PSMSatusChange */
+#define INT_OTHERCH_PRS_CHANGE		18 /* OTHER_CH_PRS change */
+#define INT_CH_PRS_CHANGE		16 /* CH_PRS_change */
+#define INT_2C_CTRL			14 /* I2C_CTRL_INT */
+
+#endif /*__CBC_CORE_H__*/
-- 
1.9.1

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

* [PATCH 1/6] mfd: Add support for the PTX1K CBC FPGA
@ 2016-10-07 15:20   ` Pantelis Antoniou
  0 siblings, 0 replies; 24+ messages in thread
From: Pantelis Antoniou @ 2016-10-07 15:20 UTC (permalink / raw)
  To: Lee Jones
  Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
	Frank Rowand, Greg Kroah-Hartman, Debjit Ghosh, Georgi Vlaev,
	Guenter Roeck, JawaharBalaji Thirumalaisamy, Mohammad Kamil,
	Pantelis Antoniou, devicetree, linux-kernel, linux-gpio, devel

From: Georgi Vlaev <gvlaev@juniper.net>

The CBC intergrates CB and SAM on single FPGA. This is a PCI MFD driver
and provides support for the following functions as subdrivers:

* SAM I2C accelerator
* SAM MTD flash
* CBC spare GPIOs
* CBC JNX infrastructure

Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
Signed-off-by: Guenter Roeck <groeck@juniper.net>
Signed-off-by: Rajat Jain <rajatjain@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
 drivers/mfd/Kconfig          |  16 +
 drivers/mfd/Makefile         |   1 +
 drivers/mfd/cbc-core.c       | 971 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/cbc-core.h | 181 ++++++++
 4 files changed, 1169 insertions(+)
 create mode 100644 drivers/mfd/cbc-core.c
 create mode 100644 include/linux/mfd/cbc-core.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 7e1fa14..6107f7a 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1384,6 +1384,22 @@ config MFD_JUNIPER_EXT_CPLD
 	  called "ptxpmb-ext-cpld"
 
 
+config MFD_JUNIPER_CBC
+	tristate "Juniper PTX1K CBC FPGA"
+	depends on JNX_PTX1K_RCB
+	default y if JNX_PTX1K_RCB
+	select MFD_CORE
+	select MTD
+	select MTD_SAM_FLASH
+	select I2C_SAM
+	select GPIO_CBC_PRESENCE if JNX_CONNECTOR
+	help
+	  Select this to enable the CBC FPGA multi-function kernel driver.
+	  This FPGA is present on the PTX1K RCB Juniper platform.
+
+	  This driver can be built as a module. If built as a module it will be
+	  called "cbc-core"
+
 config MFD_TWL4030_AUDIO
 	bool "TI TWL4030 Audio"
 	depends on TWL4030_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index da94482..0ea6dc6 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -151,6 +151,7 @@ obj-$(CONFIG_AB8500_GPADC)	+= ab8500-gpadc.o
 obj-$(CONFIG_MFD_JUNIPER_CPLD)	+= ptxpmb-cpld-core.o
 obj-$(CONFIG_MFD_JUNIPER_SAM)	+= sam-core.o
 obj-$(CONFIG_MFD_JUNIPER_EXT_CPLD) += ptxpmb-ext-cpld-core.o
+obj-$(CONFIG_MFD_JUNIPER_CBC)	+= cbc-core.o
 obj-$(CONFIG_MFD_DB8500_PRCMU)	+= db8500-prcmu.o
 # ab8500-core need to come after db8500-prcmu (which provides the channel)
 obj-$(CONFIG_AB8500_CORE)	+= ab8500-core.o ab8500-sysctrl.o
diff --git a/drivers/mfd/cbc-core.c b/drivers/mfd/cbc-core.c
new file mode 100644
index 0000000..1e6c95b
--- /dev/null
+++ b/drivers/mfd/cbc-core.c
@@ -0,0 +1,971 @@
+/*
+ * drivers/mfd/cbc-core.c
+ *
+ * Copyright (c) 2014, Juniper Networks
+ * Author: Georgi Vlaev <gvlaev@juniper.net>
+ * Based on: sam-core.c
+ *
+ * The CBC FPGA intergrates the PTX1K CB and some functions of
+ * the SAM FPGA on single device. We're reusing the SAM I2C,
+ * MTD flash drivers. The JNX CB logic is implemented in
+ * drivers/jnx/jnx-cbc-ptx1k.c and drivers/jnx/jnx-cbd-fpga-ptx1k.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/irqdomain.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/sam.h>
+#include <linux/jnx/pci_ids.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/cbc-core.h>
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/string.h>
+#include <linux/ctype.h>
+#endif
+
+enum {
+	CBC_CELL_SAM_I2C,       /* SAM I2C accelerator */
+	CBC_CELL_SAM_MTD,       /* SAM EPCS64 config flash */
+	CBC_CELL_GPIO,          /* CBC spare GPIO */
+	CBC_CELL_JNX_CBD,       /* CBC CB JNX driver */
+	CBC_CELL_GPIO_PRS,      /* CBC presence as GPIO (CH_PRS) */
+	CBC_CELL_GPIO_PRS_OTHER,/* CBC presence ss GPIO (OTHER_CH_PRS) */
+	CBC_CELL_GPIO_PRS_SIB,  /* CBC presence as GPIO (CH_PRS_SIB) */
+	CBC_NUM_MFD_CELLS
+};
+
+#define CBC_IRQ_NR			31
+#define CBC_RES_NR			2 /* iomem, irq */
+#define CBC_RES_NR_NOIRQ		1 /* iomem */
+
+struct cbc_fpga_data {
+	void __iomem *membase;
+	struct pci_dev *pdev;
+	u32 fpga_rev;	/* CBC revision */
+	u32 fpga_date;	/* CBC revision date */
+	int i2c_mstr_count; /* CBC/I2C SAM master count */
+	struct irq_domain *irq_domain;
+	u32 int_src; /* interrupt src reg (MSI, legacy) */
+	u32 int_en; /* interrupt en reg (MSI, legacy) */
+	spinlock_t irq_lock;
+	struct mfd_cell mfd_cells[CBC_NUM_MFD_CELLS];
+	struct resource mfd_i2c_resources[CBC_RES_NR];
+	struct resource mfd_mtd_resources[CBC_RES_NR_NOIRQ];
+	struct resource mfd_gpio_resources[CBC_RES_NR_NOIRQ];
+	struct resource mfd_jnx_resources[CBC_RES_NR_NOIRQ];
+	struct resource mfd_gpio_prs_resources[CBC_RES_NR_NOIRQ];
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *cbc_debugfs_dir;
+	u32 debug_address; /* any register offsset */
+	struct debugfs_regset32 *regset; /* all regs */
+	u32 test_int_cnt; /* TEST_INT counter */
+	u32 i2c_accel_cnt; /* INT_I2C_ACCEL cnt */
+	u32 i2c_accel_empty_cnt; /* INT_I2C_ACCEL cnt && !CBC_STATUS_IRQ_EN */
+#endif
+};
+
+/* debugfs */
+/* TODO: split into a separate file */
+#ifdef CONFIG_DEBUG_FS
+
+#define dump_register(n)		\
+{					\
+	.name	= __stringify(n),	\
+	.offset = n,			\
+}
+
+static const struct debugfs_reg32 cbc_regs[] = {
+	dump_register(CBC_TOP_REGS_INT_SRC),
+	dump_register(CBC_TOP_REGS_INT_EN),
+	dump_register(CBC_TOP_REGS_I2C_SEL),
+	dump_register(CBC_TOP_REGS_CH_PRS),
+	dump_register(CBC_TOP_REGS_RTL_REVISION),
+	dump_register(CBC_TOP_REGS_PWR_STATUS),
+	dump_register(CBC_TOP_REGS_I2CS_INT),
+	dump_register(CBC_TOP_REGS_I2CS_INT_EN),
+	dump_register(CBC_TOP_REGS_MSTR_CONFIG),
+	dump_register(CBC_TOP_REGS_MSTR_ALIVE),
+	dump_register(CBC_TOP_REGS_MSTR_ALIVE_CNT),
+	dump_register(CBC_TOP_REGS_FPGA_REV),
+	dump_register(CBC_TOP_REGS_FPGA_DATE),
+	dump_register(CBC_TOP_REGS_DEVICE_CTRL),
+	dump_register(CBC_TOP_REGS_SLOT_ID),
+	dump_register(CBC_TOP_REGS_SCRATCH),
+	dump_register(CBC_TOP_REGS_RE_HALT),
+	dump_register(CBC_TOP_REGS_OTHER_CH_PRS_CHANGE),
+	dump_register(CBC_TOP_REGS_FPC_SPARE_A),
+	dump_register(CBC_TOP_REGS_FT_SPARE),
+	dump_register(CBC_TOP_REGS_CB_SPARE),
+	dump_register(CBC_TOP_REGS_MTTR_0),
+	dump_register(CBC_TOP_REGS_MTTR_1),
+	dump_register(CBC_TOP_REGS_MTTR_2),
+	dump_register(CBC_TOP_REGS_MSTR_FRC),
+	dump_register(CBC_TOP_REGS_MSTR_RSN),
+	dump_register(CBC_TOP_REGS_ME_WRITE_0),
+	dump_register(CBC_TOP_REGS_ME_WRITE_1),
+	dump_register(CBC_TOP_REGS_ME_WRITE_2),
+	dump_register(CBC_TOP_REGS_ME_WRITE_3),
+	dump_register(CBC_TOP_REGS_ME_WRITE_4),
+	dump_register(CBC_TOP_REGS_ME_WRITE_5),
+	dump_register(CBC_TOP_REGS_ME_READ_0),
+	dump_register(CBC_TOP_REGS_ME_READ_1),
+	dump_register(CBC_TOP_REGS_ME_READ_2),
+	dump_register(CBC_TOP_REGS_ME_READ_3),
+	dump_register(CBC_TOP_REGS_ME_READ_4),
+	dump_register(CBC_TOP_REGS_ME_READ_5),
+	dump_register(CBC_TOP_REGS_ME_STATUS),
+	dump_register(CBC_TOP_REGS_LIU_CONFIG),
+	dump_register(CBC_TOP_REGS_CBC_8112_8614_RST),
+	dump_register(CBC_TOP_REGS_CCG_CONFIG),
+	dump_register(CBC_TOP_REGS_SFPP_CONTROL),
+	dump_register(CBC_TOP_REGS_SFPP_PCIE_FT_STATUS),
+	dump_register(CBC_TOP_REGS_GPIO_CTRL),
+	dump_register(CBC_TOP_REGS_GPIO_OUTPUT_DATA),
+	dump_register(CBC_TOP_REGS_GPIO_INPUT_DATA),
+	dump_register(CBC_TOP_REGS_CCG_STATUS_RT),
+	dump_register(CBC_TOP_REGS_CCG_STATUS_LATCHED),
+	dump_register(CBC_TOP_REGS_CCG_STATUS_INTERRUPT_ENABLE),
+	dump_register(CBC_TOP_REGS_OTHER_LED),
+	dump_register(CBC_TOP_REGS_SFPP_I2C_RW_CONTROL),
+	dump_register(CBC_TOP_REGS_SFPP_I2C_REG_ADDRESS),
+	dump_register(CBC_TOP_REGS_SFPP_I2C_WDATA),
+	dump_register(CBC_TOP_REGS_SFPP_I2C_RDATA),
+	dump_register(CBC_TOP_REGS_SFPP_I2C_STATUS),
+	dump_register(CBC_TOP_REGS_SFPP_I2C_DEV_ADDRESS),
+	dump_register(CBC_TOP_REGS_SIB_SPARE_OUTPUTENABLE),
+	dump_register(CBC_TOP_REGS_SIB_SPARE_OUTPUT),
+	dump_register(CBC_TOP_REGS_SIB_SPARE_INPUT),
+	dump_register(CBC_TOP_REGS_FPC_SPARE_OUTPUTENABLE),
+	dump_register(CBC_TOP_REGS_FPC_SPARE_OUTPUT),
+	dump_register(CBC_TOP_REGS_FPC_SPARE_INPUT),
+	dump_register(CBC_TOP_REGS_OTHER_SPARE_OUTPUTENABLE),
+	dump_register(CBC_TOP_REGS_OTHER_SPARE_OUTPUT),
+	dump_register(CBC_TOP_REGS_OTHER_SPARE_INPUT),
+	dump_register(CBC_TOP_REGS_MSI_INT_SRC),
+	dump_register(CBC_TOP_REGS_MSI_INT_EN),
+	dump_register(CBC_TOP_REGS_TOD_LOCK),
+	dump_register(CBC_TOP_REGS_TOD_TIME_79_48),
+	dump_register(CBC_TOP_REGS_TOD_TIME_47_16),
+	dump_register(CBC_TOP_REGS_TOD_TIME_15_0),
+	dump_register(CBC_TOP_REGS_TOD_CLKACC_CRC),
+	dump_register(CBC_TOP_REGS_APS_COMMAND_REGISTER),
+	dump_register(CBC_TOP_REGS_APS_STATUS_REGISTER),
+	dump_register(CBC_TOP_REGS_APS_FRAME_DATA0),
+	dump_register(CBC_TOP_REGS_APS_FRAME_DATA1),
+	dump_register(CBC_TOP_REGS_APS_FRAME_DATA2),
+	dump_register(CBC_TOP_REGS_APS_FRAME_DATA3),
+	dump_register(CBC_TOP_REGS_APS_FRAME_DATA4),
+	dump_register(CBC_TOP_REGS_APS_FRAME_DATA5),
+	dump_register(CBC_TOP_REGS_APS_FRAME_DATA6),
+	dump_register(CBC_TOP_REGS_APS_FRAME_DATA7),
+	dump_register(CBC_TOP_REGS_APS_APS_REV_REG),
+	dump_register(CBC_TOP_REGS_APS_APS_DEBUG0),
+	dump_register(CBC_TOP_REGS_APS_COUNTER_GOOD_FRAMES),
+	dump_register(CBC_TOP_REGS_APS_COUNTER_BAD_FRAMES),
+	dump_register(CBC_TOP_REGS_APS_APS_LINK_STATUS),
+	dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG0),
+	dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG1),
+	dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG2),
+	dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG3),
+	dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG4),
+	dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG5),
+	dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG6),
+	dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG7),
+	dump_register(CBC_INFO_I2C_MASTER_COUNT),
+	dump_register(CBC_INFO_FAST_I2C_CONFIG_A),
+	dump_register(CBC_INFO_FAST_I2C_CONFIG_B),
+	dump_register(CBC_INFO_FEATURES),
+	dump_register(CBC_INFO_PMA_CONTROL),
+	dump_register(CBC_INFO_PMA_STATUS),
+	dump_register(CBC_STATUS_IRQ_EN),
+	dump_register(CBC_STATUS_IRQ_ST),
+	dump_register(CBC_REMOTE_UPGRADE_CONTROL),
+	dump_register(CBC_REMOTE_UPGRADE_STATUS),
+	dump_register(CBC_FLASH_IF_ADDRESS),
+	dump_register(CBC_FLASH_IF_BYTE_COUNT),
+	dump_register(CBC_FLASH_IF_CONTROL),
+	dump_register(CBC_FLASH_IF_STATUS),
+	dump_register(CBC_FLASH_IF_WRITE_DATA),
+	dump_register(CBC_FLASH_IF_READ_DATA),
+};
+
+/*
+ * Usage:
+ * #echo ADDR > <debugfs>/cbc-core/register-address
+ * #cat <debugfs>/cbc-core/register-value
+ *
+ */
+static int cbc_debugfs_addr_print(struct seq_file *s, void *p)
+{
+	struct cbc_fpga_data *cbc = (struct cbc_fpga_data *)s->private;
+
+	seq_printf(s, "0x%08X\n", cbc->debug_address);
+
+	return 0;
+}
+
+static int cbc_debugfs_addr_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, cbc_debugfs_addr_print, inode->i_private);
+}
+
+static ssize_t cbc_debugfs_addr_write(struct file *file,
+	const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	struct cbc_fpga_data *cbc =
+		((struct seq_file *)(file->private_data))->private;
+	unsigned long user_address;
+	int err;
+
+	err = kstrtoul_from_user(user_buf, count, 0, &user_address);
+	if (err)
+		return err;
+
+	if (user_address > 0xffffff) {
+		dev_err(&cbc->pdev->dev, "debugfs error input > 0xffffff\n");
+		return -EINVAL;
+	}
+	cbc->debug_address = user_address;
+
+	return count;
+}
+
+static const struct file_operations cbc_debugfs_addr_fops = {
+	.open = cbc_debugfs_addr_open,
+	.write = cbc_debugfs_addr_write,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
+static int cbc_debugfs_val_print(struct seq_file *s, void *p)
+{
+	struct cbc_fpga_data *cbc = (struct cbc_fpga_data *)s->private;
+
+	seq_printf(s, "0x%08X\n", ioread32(cbc->membase + cbc->debug_address));
+
+	return 0;
+}
+
+static int cbc_debugfs_val_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, cbc_debugfs_val_print, inode->i_private);
+}
+
+static ssize_t cbc_debugfs_val_write(struct file *file,
+	const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	struct cbc_fpga_data *cbc =
+		((struct seq_file *)(file->private_data))->private;
+	unsigned long user_val;
+	int err;
+
+	err = kstrtoul_from_user(user_buf, count, 0, &user_val);
+	if (err)
+		return err;
+
+	iowrite32(user_val, cbc->membase + cbc->debug_address);
+	ioread32(cbc->membase + cbc->debug_address);
+
+	return count;
+}
+
+static const struct file_operations cbc_debugfs_val_fops = {
+	.open = cbc_debugfs_val_open,
+	.write = cbc_debugfs_val_write,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
+/*
+ * Usage:
+ * #echo 1 > <debugfs>/cbc-core/test-int
+ * #cat <debugfs>/cbc-core/test-int
+ *
+ */
+static void cbc_fire_test_int(struct cbc_fpga_data *cbc)
+{
+	u32 int_en;
+
+	/* Disable and enable the TEST_INT bit  */
+	int_en = ioread32(cbc->membase + cbc->int_en);
+	iowrite32(int_en & ~BIT(INT_TEST), cbc->membase + cbc->int_en);
+	ioread32(cbc->membase + cbc->int_en);
+	iowrite32(int_en | BIT(INT_TEST), cbc->membase + cbc->int_en);
+	ioread32(cbc->membase + cbc->int_en);
+}
+
+static int cbc_debugfs_test_int_print(struct seq_file *s, void *p)
+{
+	struct cbc_fpga_data *cbc = (struct cbc_fpga_data *)s->private;
+
+	seq_printf(s, "0x%08X\n", cbc->test_int_cnt);
+	return 0;
+}
+
+static int cbc_debugfs_test_int_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, cbc_debugfs_test_int_print, inode->i_private);
+}
+
+/* write address triggers a page read */
+static ssize_t cbc_debugfs_test_int_write(struct file *file,
+	const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	struct cbc_fpga_data *cbc =
+		((struct seq_file *)(file->private_data))->private;
+	unsigned long val;
+	int err;
+
+	err = kstrtoul_from_user(user_buf, count, 0, &val);
+	if (err)
+		return err;
+
+	if (val)
+		cbc_fire_test_int(cbc);
+
+	return count;
+}
+
+static const struct file_operations cbc_debugfs_test_int_fops = {
+	.open = cbc_debugfs_test_int_open,
+	.write = cbc_debugfs_test_int_write,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
+
+static int cbc_debugfs_i2c_accel_int_print(struct seq_file *s, void *p)
+{
+	struct cbc_fpga_data *cbc = (struct cbc_fpga_data *)s->private;
+
+	seq_printf(s, "%d:%d\n", cbc->i2c_accel_cnt,
+					cbc->i2c_accel_empty_cnt);
+
+	return 0;
+}
+
+static int cbc_debugfs_i2c_accel_int_open(struct inode *inode,
+				struct file *file)
+{
+	return single_open(file, cbc_debugfs_i2c_accel_int_print,
+				inode->i_private);
+}
+
+static const struct file_operations cbc_debugfs_i2c_accel_int_fops = {
+	.open = cbc_debugfs_i2c_accel_int_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
+static int cbc_debugfs_init(struct cbc_fpga_data *cbc)
+{
+	struct dentry *file;
+	struct device *dev = &cbc->pdev->dev;
+
+	cbc->cbc_debugfs_dir = debugfs_create_dir("cbc-core", NULL);
+	if (!cbc->cbc_debugfs_dir)
+		return -ENOMEM;
+/* regset dump */
+	cbc->regset = devm_kzalloc(dev, sizeof(*cbc->regset), GFP_KERNEL);
+	if (!cbc->regset)
+		goto err;
+
+	cbc->regset->regs = cbc_regs;
+	cbc->regset->nregs = ARRAY_SIZE(cbc_regs);
+	cbc->regset->base = cbc->membase;
+
+	file = debugfs_create_regset32("regdump", S_IRUGO,
+		cbc->cbc_debugfs_dir, cbc->regset);
+	if (!file)
+		goto err;
+
+/* Any register read/write */
+	file = debugfs_create_file("register-address",
+		(S_IRUGO | S_IWUSR | S_IWGRP),
+		cbc->cbc_debugfs_dir, cbc, &cbc_debugfs_addr_fops);
+	if (!file)
+		goto err;
+
+	file = debugfs_create_file("register-value",
+		(S_IRUGO | S_IWUSR | S_IWGRP),
+		cbc->cbc_debugfs_dir, cbc, &cbc_debugfs_val_fops);
+
+	if (!file)
+		goto err;
+
+/* TEST_INT */
+	file = debugfs_create_file("test-int",
+		(S_IRUGO | S_IWUSR | S_IWGRP),
+		cbc->cbc_debugfs_dir, cbc, &cbc_debugfs_test_int_fops);
+	if (!file)
+		goto err;
+
+/* I2C_ACCEL_INT */
+	file = debugfs_create_file("i2c-accel-int",
+		(S_IRUGO | S_IWUSR | S_IWGRP),
+		cbc->cbc_debugfs_dir, cbc, &cbc_debugfs_i2c_accel_int_fops);
+	if (!file)
+		goto err;
+
+	return 0;
+err:
+	debugfs_remove_recursive(cbc->cbc_debugfs_dir);
+	dev_err(dev, "failed to create debugfs entries.\n");
+
+	return -ENOMEM;
+}
+
+static void cbc_debugfs_remove(struct cbc_fpga_data *cbc)
+{
+	debugfs_remove_recursive(cbc->cbc_debugfs_dir);
+}
+#endif
+
+/*
+ * CBC/SAM interrupt handling
+ * The CBC_STATUS_IRQ_EN register is not used in the CBC
+ * The CBC_STATUS_IRQ_ST register just reports the pending
+ * interrupts for each master.
+ */
+static void sam_enable_irq(struct device *dev, enum sam_irq_type type,
+			   int irq, u32 mask)
+{
+}
+
+static void sam_disable_irq(struct device *dev, enum sam_irq_type type,
+			    int irq, u32 mask)
+{
+}
+
+static u32 sam_irq_status(struct device *dev, enum sam_irq_type type, int irq)
+{
+	struct cbc_fpga_data *cbc = dev_get_drvdata(dev);
+
+	return ioread32(cbc->membase + CBC_STATUS_IRQ_ST);
+}
+
+static void sam_irq_status_clear(struct device *dev, enum sam_irq_type type,
+				 int irq, u32 mask)
+{
+	struct cbc_fpga_data *cbc = dev_get_drvdata(dev);
+
+	iowrite32(mask, cbc->membase + CBC_STATUS_IRQ_ST);
+	ioread32(cbc->membase + CBC_STATUS_IRQ_ST);
+
+	/*
+	 * Clear the MSI INT_I2C_ACCEL interrupt.
+	 * This might cause additional interrupt, but we
+	 * won't miss the MSI in the windows from clearing the
+	 * CBC_STATUS_IRQ_ST and INT_I2C_ACCEL.
+	 */
+	iowrite32(BIT(INT_I2C_ACCEL), cbc->membase + cbc->int_src);
+	ioread32(cbc->membase + cbc->int_src);
+}
+
+static irqreturn_t cbc_irq_handler(int irq, void *data)
+{
+	struct cbc_fpga_data *cbc = data;
+	u32 int_src;
+	int i, iirq, ret = IRQ_NONE;
+
+	int_src = ioread32(cbc->membase + cbc->int_src);
+
+	/* (CBC) Test interrupt - just count */
+	if (int_src & BIT(INT_TEST)) {
+		cbc->test_int_cnt++;
+		iowrite32(BIT(INT_TEST), cbc->membase + cbc->int_src);
+		ioread32(cbc->membase + cbc->int_src);
+		ret = IRQ_HANDLED;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	if (int_src & BIT(INT_I2C_ACCEL)) {
+		u32 irq_st;
+
+		cbc->i2c_accel_cnt++;
+		irq_st = ioread32(cbc->membase + CBC_STATUS_IRQ_ST);
+		if (!irq_st)
+			cbc->i2c_accel_empty_cnt++;
+	}
+#endif
+
+	for (i = 0; i < CBC_IRQ_NR; i++) {
+		if (int_src & BIT(i)) {
+			iirq = irq_find_mapping(cbc->irq_domain, i);
+			if (iirq) {
+				generic_handle_irq(iirq);
+				ret = IRQ_HANDLED;
+			}
+		}
+	}
+
+	return ret;
+}
+
+/* irq_chip */
+static void cbc_irq_unmask(struct irq_data *data)
+{
+	struct cbc_fpga_data *cbc = irq_data_get_irq_chip_data(data);
+	unsigned long flags;
+	int irq = data->hwirq & 0x3f;
+	u32 int_en;
+
+	spin_lock_irqsave(&cbc->irq_lock, flags);
+
+	int_en = ioread32(cbc->membase + cbc->int_en);
+	iowrite32(int_en | BIT(irq), cbc->membase + cbc->int_en);
+	ioread32(cbc->membase + cbc->int_en);
+
+	spin_unlock_irqrestore(&cbc->irq_lock, flags);
+}
+
+static void cbc_irq_mask(struct irq_data *data)
+{
+	struct cbc_fpga_data *cbc = irq_data_get_irq_chip_data(data);
+	unsigned long flags;
+	int irq = data->hwirq & 0x3f;
+	u32 int_en;
+
+	spin_lock_irqsave(&cbc->irq_lock, flags);
+
+	int_en = ioread32(cbc->membase + cbc->int_en);
+	iowrite32(int_en & ~BIT(irq), cbc->membase + cbc->int_en);
+	ioread32(cbc->membase + cbc->int_en);
+
+	spin_unlock_irqrestore(&cbc->irq_lock, flags);
+}
+
+static void cbc_irq_ack(struct irq_data *data)
+{
+	struct cbc_fpga_data *cbc = irq_data_get_irq_chip_data(data);
+	int irq = data->hwirq & 0x3f;
+
+	iowrite32(BIT(irq), cbc->membase + cbc->int_src);
+	ioread32(cbc->membase + cbc->int_src);
+}
+
+struct irq_chip cbc_irq_chip = {
+	.name		= "cbc-core",
+	.irq_ack	= cbc_irq_ack,
+	.irq_mask	= cbc_irq_mask,
+	.irq_unmask	= cbc_irq_unmask,
+};
+
+/* irq_domain */
+static int cbc_irq_map(struct irq_domain *h, unsigned int virq,
+			irq_hw_number_t hw)
+{
+	irq_set_chip_data(virq, h->host_data);
+	irq_set_chip_and_handler(virq, &cbc_irq_chip, handle_level_irq);
+	irq_set_noprobe(virq);
+
+	return 0;
+}
+
+static struct irq_domain_ops cbc_irq_domain_ops = {
+	.map	= cbc_irq_map,
+	.xlate	= irq_domain_xlate_twocell,
+};
+
+static int cbc_request_interrupt(struct cbc_fpga_data *cbc)
+{
+	int err;
+	struct pci_dev *pdev = cbc->pdev;
+	struct device *dev = &pdev->dev;
+
+	if (!pdev->irq)
+		return -ENODEV;
+
+	if (!pci_enable_msi(pdev)) {
+		cbc->int_src = CBC_TOP_REGS_MSI_INT_SRC;
+		cbc->int_en = CBC_TOP_REGS_MSI_INT_EN;
+	} else {
+		cbc->int_src = CBC_TOP_REGS_INT_SRC;
+		cbc->int_en = CBC_TOP_REGS_INT_EN;
+	}
+
+	cbc->irq_domain = irq_domain_add_linear(dev->of_node, CBC_IRQ_NR,
+						&cbc_irq_domain_ops, cbc);
+	if (!cbc->irq_domain) {
+		dev_err(dev, "could not create irq domain\n");
+		return -ENOMEM;
+	}
+
+	err = devm_request_irq(dev, pdev->irq, cbc_irq_handler,
+				0, dev_driver_string(dev), cbc);
+
+	if (err) {
+		dev_err(dev, "failed to request irq %d\n", pdev->irq);
+		irq_domain_remove(cbc->irq_domain);
+		pci_disable_msi(pdev);
+		return err;
+	}
+
+	return 0;
+}
+
+static void cbc_free_interrupt(struct cbc_fpga_data *cbc)
+{
+	struct pci_dev *pdev = cbc->pdev;
+
+	devm_free_irq(&pdev->dev, pdev->irq, cbc);
+	if (cbc->irq_domain)
+		irq_domain_remove(cbc->irq_domain);
+	pci_disable_msi(pdev);
+}
+
+/* scratch access test */
+static int cbc_test_scratch(struct cbc_fpga_data *cbc)
+{
+	struct pci_dev *pdev = cbc->pdev;
+	struct device *dev = &pdev->dev;
+	u32 acc, i;
+
+	/*
+	 * Check rw register access -> use the scratch reg.
+	 * Earlier revisions fail on scratch rw test
+	 */
+	iowrite32(0xA5A5A5A5, cbc->membase + CBC_TOP_REGS_SCRATCH);
+	acc = ioread32(cbc->membase + CBC_TOP_REGS_SCRATCH);
+	if (acc != 0xA5A5A5A5) {
+		dev_err(dev,
+			"CBC scratch write failed: %08x -> %08x",
+			0xA5A5A5A5, acc);
+		return -EIO;
+	}
+
+	for (i = 0; i < 0xf0000000; i += 0x01010101) {
+		iowrite32(i, cbc->membase + CBC_TOP_REGS_SCRATCH);
+		acc = ioread32(cbc->membase + CBC_TOP_REGS_SCRATCH);
+		if (acc != i) {
+			dev_err(dev, "CBC scratch write failed: %08x -> %08x",
+				i, acc);
+			return -EIO;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Check if the CBC is capable of generating interrupts.
+ * Use the test interrupt bit. The counter is incremented
+ * from the interrupt handler. We want it > 0
+ */
+static int cbc_test_interrupts(struct cbc_fpga_data *cbc)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(100);
+
+	cbc->test_int_cnt = 0;
+	cbc_fire_test_int(cbc);
+
+	while (!cbc->test_int_cnt) {
+		if (time_after(jiffies, timeout))
+			break;
+		schedule_timeout_interruptible(msecs_to_jiffies(10));
+	}
+
+	return cbc->test_int_cnt;
+}
+
+/* sysfs entries */
+static ssize_t version_show(struct device *dev, struct device_attribute *attr,
+				char *buf)
+{
+	struct cbc_fpga_data *cbc = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", cbc->fpga_rev);
+}
+
+static ssize_t cbc_date_show(struct device *dev, struct device_attribute *attr,
+				char *buf)
+{
+	struct cbc_fpga_data *cbc = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%08x\n", cbc->fpga_date);
+}
+
+static DEVICE_ATTR(version, S_IRUGO, version_show, NULL);
+static DEVICE_ATTR(cbc_date, S_IRUGO, cbc_date_show, NULL);
+
+static struct attribute *cbc_attrs[] = {
+	&dev_attr_version.attr,
+	&dev_attr_cbc_date.attr,
+	NULL,
+};
+
+static struct attribute_group cbc_attr_group = {
+	.attrs  = cbc_attrs,
+};
+
+/* SAM drivers interrupt handling */
+static struct sam_platform_data cbc_sam_plat_data = {
+	.enable_irq = sam_enable_irq,
+	.disable_irq = sam_disable_irq,
+	.irq_status = sam_irq_status,
+	.irq_status_clear = sam_irq_status_clear,
+	.i2c_mux_channels = 4,
+};
+
+/*
+ * List of modules to be loaded. Should only be necessary if devicetree
+ * is not configured, but doesn't hurt otherwise.
+ */
+const char *cbc_modules[] = {
+	"i2c-sam",
+	"sam-flash",
+	"gpio-cbc",
+	NULL
+};
+
+static void cbc_request_modules(bool wait)
+{
+	struct module *m;
+	int i;
+
+	for (i = 0; cbc_modules[i]; i++) {
+		mutex_lock(&module_mutex);
+		m = find_module(cbc_modules[i]);
+		mutex_unlock(&module_mutex);
+		if (!m) {
+			if (wait)
+				request_module(cbc_modules[i]);
+			else
+				request_module_nowait(cbc_modules[i]);
+		}
+	}
+}
+
+static struct cbc_fpga_platdata cbc_fpga_platdata[] = {
+	[JNX_CBD_FPGA_PTX1K] = {
+		.board_type = JNX_CBD_FPGA_PTX1K,
+	},
+	[JNX_CBD_FPGA_PTX1K_P2] = {
+		.board_type = JNX_CBD_FPGA_PTX1K_P2,
+	},
+	[JNX_CBD_FPGA_PTX21K] = {
+		.board_type = JNX_CBD_FPGA_PTX21K,
+	},
+};
+
+static int cbc_fpga_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	int err;
+	struct cbc_fpga_data *cbc;
+	struct device *dev = &pdev->dev;
+
+	/* Request modules, but keep going */
+	cbc_request_modules(false);
+
+	err = pcim_enable_device(pdev);
+	if (err)
+		return err;
+
+	err = pcim_iomap_regions(pdev, 1 << 0, "cbc-core");
+	if (err)
+		return err;
+
+	cbc = devm_kzalloc(dev, sizeof(*cbc), GFP_KERNEL);
+	if (cbc == NULL)
+		return -ENOMEM;
+
+	cbc->membase = pcim_iomap_table(pdev)[0];
+	cbc->pdev = pdev;
+	pci_set_drvdata(pdev, cbc);
+
+/* Check IO */
+	err = cbc_test_scratch(cbc);
+	if (err)
+		return err;
+
+/* CBC Revision ID */
+	cbc->fpga_rev = ioread32(cbc->membase + CBC_TOP_REGS_RTL_REVISION);
+	cbc->fpga_date = ioread32(cbc->membase + CBC_TOP_REGS_FPGA_DATE);
+	cbc->i2c_mstr_count =
+		ioread32(cbc->membase + CBC_INFO_I2C_MASTER_COUNT) & 0xff;
+
+	spin_lock_init(&cbc->irq_lock);
+
+	/* i2c accel */
+	cbc->mfd_cells[CBC_CELL_SAM_I2C].name = "i2c-sam";
+	cbc->mfd_cells[CBC_CELL_SAM_I2C].num_resources = CBC_RES_NR;
+	cbc->mfd_cells[CBC_CELL_SAM_I2C].resources = cbc->mfd_i2c_resources;
+	cbc->mfd_cells[CBC_CELL_SAM_I2C].of_compatible = "jnx,i2c-sam";
+	cbc->mfd_cells[CBC_CELL_SAM_I2C].platform_data = &cbc_sam_plat_data;
+	cbc->mfd_cells[CBC_CELL_SAM_I2C].pdata_size =
+				sizeof(struct sam_platform_data);
+
+	cbc->mfd_i2c_resources[0].start = CBC_I2C_ACCEL_IF_MEM_START;
+	cbc->mfd_i2c_resources[0].end = CBC_I2C_ACCEL_IF_MEM_END;
+	cbc->mfd_i2c_resources[0].flags = IORESOURCE_MEM;
+	cbc->mfd_i2c_resources[1].start =
+			cbc->mfd_i2c_resources[1].end = INT_I2C_ACCEL;
+	cbc->mfd_i2c_resources[1].flags = IORESOURCE_IRQ;
+
+	/* epcs64,128 mtd flash */
+	cbc->mfd_cells[CBC_CELL_SAM_MTD].name = "flash-sam";
+	cbc->mfd_cells[CBC_CELL_SAM_MTD].num_resources = CBC_RES_NR_NOIRQ;
+	cbc->mfd_cells[CBC_CELL_SAM_MTD].resources = cbc->mfd_mtd_resources;
+	cbc->mfd_cells[CBC_CELL_SAM_MTD].of_compatible = "jnx,flash-sam";
+
+	cbc->mfd_mtd_resources[0].start = CBC_FLASH_IF_MEM_START;
+	cbc->mfd_mtd_resources[0].end = CBC_FLASH_IF_MEM_END;
+	cbc->mfd_mtd_resources[0].flags = IORESOURCE_MEM;
+
+	/* CBC GPIO  */
+	cbc->mfd_cells[CBC_CELL_GPIO].name = "gpio-cbc";
+	cbc->mfd_cells[CBC_CELL_GPIO].num_resources = CBC_RES_NR_NOIRQ;
+	cbc->mfd_cells[CBC_CELL_GPIO].resources = cbc->mfd_gpio_resources;
+	cbc->mfd_cells[CBC_CELL_GPIO].of_compatible = "jnx,gpio-cbc";
+	cbc->mfd_gpio_resources[0].flags = IORESOURCE_MEM;
+
+	/* CBC JNX  */
+	cbc->mfd_cells[CBC_CELL_JNX_CBD].name = "jnx-cbd-fpga";
+	cbc->mfd_cells[CBC_CELL_JNX_CBD].num_resources = CBC_RES_NR_NOIRQ;
+	cbc->mfd_cells[CBC_CELL_JNX_CBD].resources = cbc->mfd_jnx_resources;
+	cbc->mfd_cells[CBC_CELL_JNX_CBD].of_compatible = "jnx,jnx-cbd-fpga";
+	cbc->mfd_cells[CBC_CELL_JNX_CBD].platform_data =
+					&cbc_fpga_platdata[id->driver_data];
+	cbc->mfd_cells[CBC_CELL_JNX_CBD].pdata_size =
+					sizeof(struct cbc_fpga_platdata);
+
+	cbc->mfd_jnx_resources[0].flags = IORESOURCE_MEM;
+
+	/* CBC presence detect as GPIO  (CH_PRS) */
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS].name = "gpio-cbc-presence";
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS].id = 0;
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS].num_resources =
+					CBC_RES_NR_NOIRQ;
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS].resources =
+					cbc->mfd_gpio_prs_resources;
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS].of_compatible =
+					"jnx,gpio-cbc-presence";
+	cbc->mfd_gpio_prs_resources[0].flags = IORESOURCE_MEM;
+
+	/* CBC presence detect as GPIO  (OTHER_CH_PRS) */
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS_OTHER].name = "gpio-cbc-presence";
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS_OTHER].id = 1;
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS_OTHER].num_resources =
+					CBC_RES_NR_NOIRQ;
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS_OTHER].resources =
+						cbc->mfd_gpio_prs_resources;
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS_OTHER].of_compatible =
+						"jnx,gpio-cbc-presence-other";
+	cbc->mfd_gpio_prs_resources[0].flags = IORESOURCE_MEM;
+
+       /* CBC presence detect as GPIO  (CH_PRS_SIB) */
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS_SIB].name = "gpio-cbc-presence";
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS_SIB].id = 2;
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS_SIB].num_resources =
+						CBC_RES_NR_NOIRQ;
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS_SIB].resources =
+						cbc->mfd_gpio_prs_resources;
+	cbc->mfd_cells[CBC_CELL_GPIO_PRS_SIB].of_compatible =
+						"jnx,gpio-cbc-presence-sib";
+	cbc->mfd_gpio_prs_resources[0].flags = IORESOURCE_MEM;
+
+	/* CBC is using MSI, make sure bus master is enabled */
+	pci_set_master(pdev);
+
+	/* Setup interrupts - MSI/INTx */
+	err = cbc_request_interrupt(cbc);
+	if (err < 0)
+		return err;
+
+	err = cbc_test_interrupts(cbc);
+	if (!err) {
+		dev_warn(dev, "Interrupt test failed, using poll mode");
+		cbc->mfd_cells[CBC_CELL_SAM_I2C].num_resources =
+						CBC_RES_NR_NOIRQ;
+	}
+
+	/* Request modules for good. */
+	cbc_request_modules(true);
+
+	err = mfd_add_devices(dev, pdev->bus->number, cbc->mfd_cells,
+			      ARRAY_SIZE(cbc->mfd_cells), &pdev->resource[0],
+			      0, cbc->irq_domain);
+	if (err < 0)
+		goto err_int;
+
+	err = sysfs_create_group(&pdev->dev.kobj, &cbc_attr_group);
+	if (err) {
+		sysfs_remove_group(&pdev->dev.kobj, &cbc_attr_group);
+		dev_err(&pdev->dev, "Failed to create attr group\n");
+		goto err_mfd;
+	}
+
+	dev_dbg(dev, "CBC/SAM I2C: Master count: %u\n", cbc->i2c_mstr_count);
+	dev_info(dev, "CBC FPGA Revision #%u (%02X/%02X/%04X)\n",
+		cbc->fpga_rev & 0xffff, (cbc->fpga_date >> 24) & 0xff,
+		(cbc->fpga_date >> 16) & 0xff, cbc->fpga_date & 0xffff);
+
+#ifdef CONFIG_DEBUG_FS
+	cbc_debugfs_init(cbc);
+#endif
+	return 0;
+
+err_mfd:
+	mfd_remove_devices(dev);
+
+err_int:
+	cbc_free_interrupt(cbc);
+
+	return err;
+}
+
+static void cbc_fpga_remove(struct pci_dev *pdev)
+{
+	struct cbc_fpga_data *cbc = pci_get_drvdata(pdev);
+
+#ifdef CONFIG_DEBUG_FS
+	cbc_debugfs_remove(cbc);
+#endif
+	sysfs_remove_group(&pdev->dev.kobj, &cbc_attr_group);
+	mfd_remove_devices(&pdev->dev);
+	cbc_free_interrupt(cbc);
+}
+
+static const struct pci_device_id cbc_fpga_id_tbl[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_CBC),
+	    .driver_data = JNX_CBD_FPGA_PTX1K },
+	{ PCI_DEVICE(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_CBC_P2),
+	    .driver_data = JNX_CBD_FPGA_PTX1K_P2 },
+	{ PCI_DEVICE(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_OMG_CBC),
+	    .driver_data = JNX_CBD_FPGA_PTX21K },
+	{ }
+};
+MODULE_DEVICE_TABLE(pci, cbc_fpga_id_tbl);
+
+static struct pci_driver cbc_fpga_driver = {
+	.name = "cbc-core",
+	.id_table = cbc_fpga_id_tbl,
+	.probe = cbc_fpga_probe,
+	.remove = cbc_fpga_remove,
+};
+
+module_pci_driver(cbc_fpga_driver);
+
+MODULE_DESCRIPTION("Juniper PTX1K RCB CBC MFD core driver");
+MODULE_AUTHOR("Georgi Vlaev <gvlaev@juniper.net>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/cbc-core.h b/include/linux/mfd/cbc-core.h
new file mode 100644
index 0000000..dc510a7
--- /dev/null
+++ b/include/linux/mfd/cbc-core.h
@@ -0,0 +1,181 @@
+/*
+ * PTX1K CBC FPGA registers
+ *
+ * Copyright (C) 2014 Juniper Networks
+ * Author: Georgi Vlaev <gvlaev@juniper.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __CBC_CORE_H__
+#define __CBC_CORE_H__
+
+enum jnx_cbd_fpga_board_types {
+	JNX_CBD_FPGA_PTX1K, JNX_CBD_FPGA_PTX1K_P2, JNX_CBD_FPGA_PTX21K };
+
+struct cbc_fpga_platdata {
+	enum jnx_cbd_fpga_board_types board_type;
+};
+
+#define CBC_TOP_REGS_INT_SRC			0x000
+#define CBC_TOP_REGS_INT_EN			0x004
+#define CBC_TOP_REGS_I2C_SEL			0x010
+#define CBC_TOP_REGS_CH_PRS			0x014
+#define CBC_TOP_REGS_RTL_REVISION		0x018
+#define CBC_TOP_REGS_PWR_STATUS			0x01c
+#define CBC_TOP_REGS_I2CS_INT			0x020
+#define CBC_TOP_REGS_I2CS_INT_EN		0x024
+#define CBC_TOP_REGS_MSTR_CONFIG		0x03c
+#define CBC_TOP_REGS_MSTR_ALIVE			0x040
+#define CBC_TOP_REGS_MSTR_ALIVE_CNT		0x044
+#define CBC_TOP_REGS_FPGA_REV			0x060
+#define CBC_TOP_REGS_FPGA_DATE			0x064
+#define CBC_TOP_REGS_DEVICE_CTRL		0x0c0
+#define CBC_TOP_REGS_SLOT_ID			0x0c4
+#define CBC_TOP_REGS_SCRATCH			0x0c8
+#define CBC_TOP_REGS_RE_HALT			0x0cc
+#define CBC_TOP_REGS_OTHER_CH_PRS_CHANGE	0x0d4
+#define CBC_TOP_REGS_FPC_SPARE_A		0x0dc
+#define CBC_TOP_REGS_FT_SPARE			0x0e4
+#define CBC_TOP_REGS_CB_SPARE			0x0e8
+#define CBC_TOP_REGS_MTTR_0			0x0ec
+#define CBC_TOP_REGS_MTTR_1			0x0f0
+#define CBC_TOP_REGS_MTTR_2			0x0f4
+#define CBC_TOP_REGS_MSTR_FRC			0x0f8
+#define CBC_TOP_REGS_MSTR_RSN			0x0fc
+#define CBC_TOP_REGS_ME_WRITE_0			0x100
+#define CBC_TOP_REGS_ME_WRITE_1			0x104
+#define CBC_TOP_REGS_ME_WRITE_2			0x108
+#define CBC_TOP_REGS_ME_WRITE_3			0x10c
+#define CBC_TOP_REGS_ME_WRITE_4			0x110
+#define CBC_TOP_REGS_ME_WRITE_5			0x114
+#define CBC_TOP_REGS_ME_READ_0			0x120
+#define CBC_TOP_REGS_ME_READ_1			0x124
+#define CBC_TOP_REGS_ME_READ_2			0x128
+#define CBC_TOP_REGS_ME_READ_3			0x12c
+#define CBC_TOP_REGS_ME_READ_4			0x130
+#define CBC_TOP_REGS_ME_READ_5			0x134
+#define CBC_TOP_REGS_ME_STATUS			0x13c
+#define CBC_TOP_REGS_LIU_CONFIG			0x140
+#define CBC_TOP_REGS_CBC_8112_8614_RST		0x144
+#define CBC_TOP_REGS_CCG_CONFIG			0x148
+#define CBC_TOP_REGS_SFPP_CONTROL		0x14c
+#define CBC_TOP_REGS_SFPP_PCIE_FT_STATUS	0x150
+#define CBC_TOP_REGS_GPIO_CTRL			0x168
+#define CBC_TOP_REGS_GPIO_OUTPUT_DATA		0x16c
+#define CBC_TOP_REGS_GPIO_INPUT_DATA		0x170
+#define CBC_TOP_REGS_CCG_STATUS_RT		0x174
+#define CBC_TOP_REGS_CCG_STATUS_LATCHED		0x178
+#define CBC_TOP_REGS_CCG_STATUS_INTERRUPT_ENABLE	0x17c
+#define CBC_TOP_REGS_OTHER_LED			0x180
+#define CBC_TOP_REGS_SFPP_I2C_RW_CONTROL	0x190
+#define CBC_TOP_REGS_SFPP_I2C_REG_ADDRESS	0x194
+#define CBC_TOP_REGS_SFPP_I2C_WDATA		0x198
+#define CBC_TOP_REGS_SFPP_I2C_RDATA		0x19c
+#define CBC_TOP_REGS_SFPP_I2C_STATUS		0x1a0
+#define CBC_TOP_REGS_SFPP_I2C_DEV_ADDRESS	0x1a4
+#define CBC_TOP_REGS_SIB_SPARE_OUTPUTENABLE	0x1a8
+#define CBC_TOP_REGS_SIB_SPARE_OUTPUT		0x1ac
+#define CBC_TOP_REGS_SIB_SPARE_INPUT		0x1c0
+#define CBC_TOP_REGS_FPC_SPARE_OUTPUTENABLE	0x1c4
+#define CBC_TOP_REGS_FPC_SPARE_OUTPUT		0x1c8
+#define CBC_TOP_REGS_FPC_SPARE_INPUT		0x1cc
+#define CBC_TOP_REGS_OTHER_SPARE_OUTPUTENABLE	0x1d0
+#define CBC_TOP_REGS_OTHER_SPARE_OUTPUT		0x1d4
+#define CBC_TOP_REGS_OTHER_SPARE_INPUT		0x1d8
+#define CBC_TOP_REGS_MSI_INT_SRC		0x1e0
+#define CBC_TOP_REGS_MSI_INT_EN			0x1e4
+#define CBC_TOP_REGS_TOD_LOCK			0x1f0
+#define CBC_TOP_REGS_TOD_TIME_79_48		0x1f4
+#define CBC_TOP_REGS_TOD_TIME_47_16		0x1f8
+#define CBC_TOP_REGS_TOD_TIME_15_0		0x1fc
+#define CBC_TOP_REGS_TOD_CLKACC_CRC		0x200
+#define CBC_TOP_REGS_APS_COMMAND_REGISTER	0x400
+#define CBC_TOP_REGS_APS_STATUS_REGISTER	0x404
+#define CBC_TOP_REGS_APS_FRAME_DATA0		0x420
+#define CBC_TOP_REGS_APS_FRAME_DATA1		0x424
+#define CBC_TOP_REGS_APS_FRAME_DATA2		0x428
+#define CBC_TOP_REGS_APS_FRAME_DATA3		0x42c
+#define CBC_TOP_REGS_APS_FRAME_DATA4		0x430
+#define CBC_TOP_REGS_APS_FRAME_DATA5		0x434
+#define CBC_TOP_REGS_APS_FRAME_DATA6		0x438
+#define CBC_TOP_REGS_APS_FRAME_DATA7		0x43c
+#define CBC_TOP_REGS_APS_APS_REV_REG		0x440
+#define CBC_TOP_REGS_APS_APS_DEBUG0		0x444
+#define CBC_TOP_REGS_APS_COUNTER_GOOD_FRAMES	0x448
+#define CBC_TOP_REGS_APS_COUNTER_BAD_FRAMES	0x44c
+#define CBC_TOP_REGS_APS_APS_LINK_STATUS	0x450
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG0		0x454
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG1		0x458
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG2		0x45c
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG3		0x460
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG4		0x464
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG5		0x468
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG6		0x46c
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG7		0x470
+
+/* CBC: SAM register base is @0x1000 */
+#define CBC_SAM_BASE				0x1000
+#define CBC_INFO_I2C_MASTER_COUNT		(CBC_SAM_BASE + 0x00c)
+#define CBC_INFO_FAST_I2C_CONFIG_A		(CBC_SAM_BASE + 0x018)
+#define CBC_INFO_FAST_I2C_CONFIG_B		(CBC_SAM_BASE + 0x01c)
+#define CBC_INFO_FEATURES			(CBC_SAM_BASE + 0x020)
+#define CBC_INFO_PMA_CONTROL			(CBC_SAM_BASE + 0x040)
+#define CBC_INFO_PMA_STATUS			(CBC_SAM_BASE + 0x044)
+#define CBC_STATUS_IRQ_EN			(CBC_SAM_BASE + 0x104)
+#define CBC_STATUS_IRQ_ST			(CBC_SAM_BASE + 0x108)
+/* remote upgrade_if */
+#define CBC_REMOTE_UPGRADE_CONTROL		(CBC_SAM_BASE + 0x200)
+#define CBC_REMOTE_UPGRADE_STATUS		(CBC_SAM_BASE + 0x204)
+/* flash_if */
+#define CBC_FLASH_IF_ADDRESS			(CBC_SAM_BASE + 0x300)
+#define CBC_FLASH_IF_BYTE_COUNT			(CBC_SAM_BASE + 0x304)
+#define CBC_FLASH_IF_CONTROL			(CBC_SAM_BASE + 0x308)
+#define CBC_FLASH_IF_STATUS			(CBC_SAM_BASE + 0x30c)
+#define CBC_FLASH_IF_WRITE_DATA			(CBC_SAM_BASE + 0x400)
+#define CBC_FLASH_IF_READ_DATA			(CBC_SAM_BASE + 0x500)
+
+/* Constants used for FPGA upgrades */
+#define SAM_FPGA_FLASH_VALID_BIT		0xA5A5A5A5
+#define SAM_FPGA_FLASH_VALID_BIT_ADDR		0x7F0000 /* EPCS64 */
+
+/* FPGA remote upgrade registers */
+#define SAM_FPGA_REMOTE_UPGRADE_TRIG_BIT	0x08000000
+#define SAM_FPGA_REMOTE_UPGRADE_STATUS_BUSY	0x01000000
+#define SAM_FPGA_REMOTE_UPGRADE_READ_PARAM	0x80000000
+#define SAM_FPGA_REMOTE_UPGRADE_WRITE_PARAM	0x40000000
+#define SAM_FPGA_REMOTE_UPGRADE_CONTROL_RESET	0x10000000
+
+#define SAM_FPGA_REMOTE_UPGRADE_PAGE_SEL	(0x04 << 24)
+#define SAM_FPGA_REMOTE_UPGRADE_ANF		(0x05 << 24)
+#define SAM_FPGA_USER_IMAGE_BASE		0x400000
+
+/* CBC/SAM flash */
+#define FLASH_STATUS_BUSY			0x01
+#define SAM_FLASH_IF_CONTROL_READ		0x01
+#define SAM_FLASH_IF_CONTROL_READ_SID		0x80
+#define ECS64_PAGE_SIZE				0x100
+
+/* MFD resources */
+/* CBC/SAM flash - mtd/devices/flash-sam.c */
+#define CBC_FLASH_IF_MEM_START			(CBC_SAM_BASE)
+#define CBC_FLASH_IF_MEM_END			(CBC_SAM_BASE + 0x05ff)
+/* CBC/SAM I2C Accel - i2c/busses/i2c-sam.c */
+#define CBC_I2C_ACCEL_IF_MEM_START		(CBC_SAM_BASE)
+#define CBC_I2C_ACCEL_IF_MEM_END		(CBC_SAM_BASE + 0x1ffff)
+
+/* MSI & legacy nterrupts [W1C] */
+#define INT_TEST			31 /* Test interrupt */
+#define INT_MSTRSHIP_LOSS		30 /* Mastership loss */
+#define INT_I2C_ACCEL			25 /* Cascade I2C accel int */
+#define INT_CASI2CS			24 /* casI2CS_INT */
+#define INT_SFPP_PCIE_STATUS_CHANGE	23 /* SFPP status, PCIe status */
+#define INT_CCG				22 /* CCG INT */
+#define INT_PSM_STATUS_CHANGE		21 /* PSMSatusChange */
+#define INT_OTHERCH_PRS_CHANGE		18 /* OTHER_CH_PRS change */
+#define INT_CH_PRS_CHANGE		16 /* CH_PRS_change */
+#define INT_2C_CTRL			14 /* I2C_CTRL_INT */
+
+#endif /*__CBC_CORE_H__*/
-- 
1.9.1

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

* [PATCH 2/6] gpio: Add support for PTX1K CBC FPGA spare GPIOs
  2016-10-07 15:20 ` Pantelis Antoniou
@ 2016-10-07 15:20   ` Pantelis Antoniou
  -1 siblings, 0 replies; 24+ messages in thread
From: Pantelis Antoniou @ 2016-10-07 15:20 UTC (permalink / raw)
  To: Lee Jones
  Cc: Mark Rutland, Alexandre Courbot, devel, devicetree,
	Greg Kroah-Hartman, Linus Walleij, Pantelis Antoniou,
	linux-kernel, JawaharBalaji Thirumalaisamy, linux-gpio,
	Rob Herring, Debjit Ghosh, Mohammad Kamil, Georgi Vlaev,
	Frank Rowand, Guenter Roeck

From: Georgi Vlaev <gvlaev@juniper.net>

Add support for the GPIO block in Juniper's CBC FPGA.

A number of GPIOs exported by different kind of boards
is supported.

Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
Signed-off-by: Guenter Roeck <groeck@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
 drivers/gpio/Kconfig    |  11 +++
 drivers/gpio/Makefile   |   1 +
 drivers/gpio/gpio-cbc.c | 236 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 248 insertions(+)
 create mode 100644 drivers/gpio/gpio-cbc.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 281029b..b29f521 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -147,6 +147,17 @@ config GPIO_BRCMSTB
 	help
 	  Say yes here to enable GPIO support for Broadcom STB (BCM7XXX) SoCs.
 
+config GPIO_CBC
+	tristate "Juniper Networks PTX1K CBC GPIO support"
+	depends on MFD_JUNIPER_CBC
+	default y if MFD_JUNIPER_CBC
+	help
+	  This driver supports the spare GPIO interfaces on the Juniper
+	  PTX1K CBC.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called gpio-cbc.
+
 config GPIO_CLPS711X
 	tristate "CLPS711X GPIO support"
 	depends on ARCH_CLPS711X || COMPILE_TEST
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index ec890c7..78dd0d4 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_GPIO_AXP209)	+= gpio-axp209.o
 obj-$(CONFIG_GPIO_BCM_KONA)	+= gpio-bcm-kona.o
 obj-$(CONFIG_GPIO_BRCMSTB)	+= gpio-brcmstb.o
 obj-$(CONFIG_GPIO_BT8XX)	+= gpio-bt8xx.o
+obj-$(CONFIG_GPIO_CBC)		+= gpio-cbc.o
 obj-$(CONFIG_GPIO_CLPS711X)	+= gpio-clps711x.o
 obj-$(CONFIG_GPIO_CS5535)	+= gpio-cs5535.o
 obj-$(CONFIG_GPIO_CRYSTAL_COVE)	+= gpio-crystalcove.o
diff --git a/drivers/gpio/gpio-cbc.c b/drivers/gpio/gpio-cbc.c
new file mode 100644
index 0000000..d698f00
--- /dev/null
+++ b/drivers/gpio/gpio-cbc.c
@@ -0,0 +1,236 @@
+/*
+ * Polaris CBC 8614, 8112, SIB, FPC, FTC Spare GPIO driver
+ *
+ * Copyright 2014 Juniper Networks
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/mfd/cbc-core.h>
+
+#define CBC_GPIO_DIR		0x00
+#define CBC_GPIO_OUTPUT		0x04
+#define CBC_GPIO_INPUT		0x08
+
+struct cbc_gpio {
+	u32 reg;	/* start register offset */
+	u32 ngpio;	/* number of GPIOs */
+	u32 offset;	/* start offset of the fisrt GPIO */
+};
+
+#define CBC_GPIO(r, n, o) { .reg = r, .ngpio = n, .offset = o }
+
+static struct cbc_gpio cbc_gpios[] = {
+	CBC_GPIO(CBC_TOP_REGS_GPIO_CTRL, 12, 0), /* GPIO_8614-GPIO_8112 */
+	CBC_GPIO(CBC_TOP_REGS_SIB_SPARE_OUTPUTENABLE, 18, 0), /* SIB_SPARE */
+	CBC_GPIO(CBC_TOP_REGS_FPC_SPARE_OUTPUTENABLE, 32, 0), /* FPC_SPARE */
+	CBC_GPIO(CBC_TOP_REGS_OTHER_SPARE_OUTPUTENABLE, 10, 1) /* OTHER_SPARE */
+};
+
+/*
+ * struct cbc_gpio_chip
+ */
+struct cbc_gpio_chip {
+	void __iomem *base;
+	struct device *dev;
+	struct gpio_chip chip;
+	u32 gpio_state;
+	u32 gpio_dir;
+	u32 offset;
+	spinlock_t gpio_lock;
+};
+
+/*
+ * cbc_gpio_get - Read the specified signal of the GPIO device.
+ */
+static int cbc_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+	struct cbc_gpio_chip *chip =
+	    container_of(gc, struct cbc_gpio_chip, chip);
+
+	return !!(ioread32(chip->base + CBC_GPIO_INPUT) &
+		BIT(gpio));
+}
+
+/*
+ * cbc_gpio_set - Write the specified signal of the GPIO device.
+ */
+static void cbc_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+	unsigned long flags;
+	struct cbc_gpio_chip *chip =
+	    container_of(gc, struct cbc_gpio_chip, chip);
+
+	spin_lock_irqsave(&chip->gpio_lock, flags);
+
+	/* Write to GPIO signal and set its direction to output */
+	if (val)
+		chip->gpio_state |= BIT(gpio);
+	else
+		chip->gpio_state &= ~BIT(gpio);
+
+	iowrite32(chip->gpio_state, chip->base + CBC_GPIO_OUTPUT);
+
+	spin_unlock_irqrestore(&chip->gpio_lock, flags);
+}
+
+/*
+ * cbc_gpio_dir_in - Set the direction of the specified GPIO signal as input.
+ */
+static int cbc_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+	unsigned long flags;
+	struct cbc_gpio_chip *chip =
+	    container_of(gc, struct cbc_gpio_chip, chip);
+
+	spin_lock_irqsave(&chip->gpio_lock, flags);
+
+	chip->gpio_dir &= ~BIT(gpio);
+	iowrite32(chip->gpio_dir, chip->base + CBC_GPIO_DIR);
+
+	spin_unlock_irqrestore(&chip->gpio_lock, flags);
+
+	return 0;
+}
+
+/*
+ * cbc_gpio_dir_out - Set the direction of the specified GPIO signal as output.
+ */
+static int cbc_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+	unsigned long flags;
+	struct cbc_gpio_chip *chip =
+	    container_of(gc, struct cbc_gpio_chip, chip);
+
+	spin_lock_irqsave(&chip->gpio_lock, flags);
+
+	chip->gpio_dir |= BIT(gpio);
+	iowrite32(chip->gpio_dir, chip->base +  CBC_GPIO_DIR);
+
+	if (val)
+		chip->gpio_state |= BIT(gpio);
+	else
+		chip->gpio_state &= ~BIT(gpio);
+	iowrite32(chip->gpio_state, chip->base + CBC_GPIO_OUTPUT);
+
+	spin_unlock_irqrestore(&chip->gpio_lock, flags);
+
+	return 0;
+}
+
+/*
+ * cbc_gpio_setup_one - Setup single bank as gpio_chip
+ */
+static void cbc_gpio_setup_one(struct cbc_gpio_chip *cgc, int ngpio)
+{
+	struct gpio_chip *gpio = &cgc->chip;
+
+	gpio->label = dev_name(cgc->dev);
+	gpio->owner = THIS_MODULE;
+	gpio->direction_input = cbc_gpio_dir_in;
+	gpio->get = cbc_gpio_get;
+	gpio->direction_output = cbc_gpio_dir_out;
+	gpio->set = cbc_gpio_set;
+	gpio->dbg_show = NULL;
+	gpio->base = -1;
+	gpio->ngpio = ngpio;
+	gpio->can_sleep = 0;
+#ifdef CONFIG_OF_GPIO
+	gpio->of_node = cgc->dev->of_node;
+#endif
+}
+
+static int cbc_gpio_probe(struct platform_device *pdev)
+{
+	int i, ret;
+	struct cbc_gpio_chip *chips, *c;
+	struct resource *res;
+	void __iomem *base;
+	struct device *dev = &pdev->dev;
+
+	chips = devm_kzalloc(dev, sizeof(struct cbc_gpio_chip) *
+		ARRAY_SIZE(cbc_gpios), GFP_KERNEL);
+	if (chips == NULL)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	base = devm_ioremap(dev, res->start, resource_size(res));
+	if (!base)
+		return -ENOMEM;
+
+
+	platform_set_drvdata(pdev, chips);
+
+	/* For each GPIO bank, register a GPIO chip. */
+	for (i = 0; i < ARRAY_SIZE(cbc_gpios); i++) {
+		c = &chips[i];
+
+		c->dev = dev;
+		c->base = base;
+		spin_lock_init(&c->gpio_lock);
+		c->offset = cbc_gpios[i].offset;
+		cbc_gpio_setup_one(c, cbc_gpios[i].ngpio);
+
+		ret = gpiochip_add(&c->chip);
+		if (ret) {
+			dev_err(&pdev->dev,
+				"Failed to register CBC gpiochip %d: %d\n",
+				i, ret);
+			goto err_gpiochip;
+		}
+	}
+	return 0;
+
+err_gpiochip:
+	for (i = i - 1; i >= 0; i--)
+		gpiochip_remove(&chips[i].chip);
+
+	return ret;
+}
+
+static int cbc_gpio_remove(struct platform_device *pdev)
+{
+	int i;
+	struct cbc_gpio_chip *chips = platform_get_drvdata(pdev);
+
+	for (i = 0; i < ARRAY_SIZE(cbc_gpios); i++)
+		gpiochip_remove(&chips[i].chip);
+
+	return 0;
+}
+
+static const struct of_device_id cbc_gpio_ids[] = {
+	{ .compatible = "jnx,gpio-cbc", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, cbc_gpio_ids);
+
+static struct platform_driver cbc_gpio_driver = {
+	.driver = {
+		.name = "gpio-cbc",
+		.owner  = THIS_MODULE,
+		.of_match_table = cbc_gpio_ids,
+	},
+	.probe = cbc_gpio_probe,
+	.remove = cbc_gpio_remove,
+};
+module_platform_driver(cbc_gpio_driver);
+
+MODULE_DESCRIPTION("Juniper Networks CBC GPIO Driver");
+MODULE_AUTHOR("Georgi Vlaev <gvlaev@juniper.net>");
+MODULE_LICENSE("GPL");
-- 
1.9.1

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

* [PATCH 2/6] gpio: Add support for PTX1K CBC FPGA spare GPIOs
@ 2016-10-07 15:20   ` Pantelis Antoniou
  0 siblings, 0 replies; 24+ messages in thread
From: Pantelis Antoniou @ 2016-10-07 15:20 UTC (permalink / raw)
  To: Lee Jones
  Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
	Frank Rowand, Greg Kroah-Hartman, Debjit Ghosh, Georgi Vlaev,
	Guenter Roeck, JawaharBalaji Thirumalaisamy, Mohammad Kamil,
	Pantelis Antoniou, devicetree, linux-kernel, linux-gpio, devel

From: Georgi Vlaev <gvlaev@juniper.net>

Add support for the GPIO block in Juniper's CBC FPGA.

A number of GPIOs exported by different kind of boards
is supported.

Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
Signed-off-by: Guenter Roeck <groeck@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
 drivers/gpio/Kconfig    |  11 +++
 drivers/gpio/Makefile   |   1 +
 drivers/gpio/gpio-cbc.c | 236 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 248 insertions(+)
 create mode 100644 drivers/gpio/gpio-cbc.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 281029b..b29f521 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -147,6 +147,17 @@ config GPIO_BRCMSTB
 	help
 	  Say yes here to enable GPIO support for Broadcom STB (BCM7XXX) SoCs.
 
+config GPIO_CBC
+	tristate "Juniper Networks PTX1K CBC GPIO support"
+	depends on MFD_JUNIPER_CBC
+	default y if MFD_JUNIPER_CBC
+	help
+	  This driver supports the spare GPIO interfaces on the Juniper
+	  PTX1K CBC.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called gpio-cbc.
+
 config GPIO_CLPS711X
 	tristate "CLPS711X GPIO support"
 	depends on ARCH_CLPS711X || COMPILE_TEST
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index ec890c7..78dd0d4 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_GPIO_AXP209)	+= gpio-axp209.o
 obj-$(CONFIG_GPIO_BCM_KONA)	+= gpio-bcm-kona.o
 obj-$(CONFIG_GPIO_BRCMSTB)	+= gpio-brcmstb.o
 obj-$(CONFIG_GPIO_BT8XX)	+= gpio-bt8xx.o
+obj-$(CONFIG_GPIO_CBC)		+= gpio-cbc.o
 obj-$(CONFIG_GPIO_CLPS711X)	+= gpio-clps711x.o
 obj-$(CONFIG_GPIO_CS5535)	+= gpio-cs5535.o
 obj-$(CONFIG_GPIO_CRYSTAL_COVE)	+= gpio-crystalcove.o
diff --git a/drivers/gpio/gpio-cbc.c b/drivers/gpio/gpio-cbc.c
new file mode 100644
index 0000000..d698f00
--- /dev/null
+++ b/drivers/gpio/gpio-cbc.c
@@ -0,0 +1,236 @@
+/*
+ * Polaris CBC 8614, 8112, SIB, FPC, FTC Spare GPIO driver
+ *
+ * Copyright 2014 Juniper Networks
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/mfd/cbc-core.h>
+
+#define CBC_GPIO_DIR		0x00
+#define CBC_GPIO_OUTPUT		0x04
+#define CBC_GPIO_INPUT		0x08
+
+struct cbc_gpio {
+	u32 reg;	/* start register offset */
+	u32 ngpio;	/* number of GPIOs */
+	u32 offset;	/* start offset of the fisrt GPIO */
+};
+
+#define CBC_GPIO(r, n, o) { .reg = r, .ngpio = n, .offset = o }
+
+static struct cbc_gpio cbc_gpios[] = {
+	CBC_GPIO(CBC_TOP_REGS_GPIO_CTRL, 12, 0), /* GPIO_8614-GPIO_8112 */
+	CBC_GPIO(CBC_TOP_REGS_SIB_SPARE_OUTPUTENABLE, 18, 0), /* SIB_SPARE */
+	CBC_GPIO(CBC_TOP_REGS_FPC_SPARE_OUTPUTENABLE, 32, 0), /* FPC_SPARE */
+	CBC_GPIO(CBC_TOP_REGS_OTHER_SPARE_OUTPUTENABLE, 10, 1) /* OTHER_SPARE */
+};
+
+/*
+ * struct cbc_gpio_chip
+ */
+struct cbc_gpio_chip {
+	void __iomem *base;
+	struct device *dev;
+	struct gpio_chip chip;
+	u32 gpio_state;
+	u32 gpio_dir;
+	u32 offset;
+	spinlock_t gpio_lock;
+};
+
+/*
+ * cbc_gpio_get - Read the specified signal of the GPIO device.
+ */
+static int cbc_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+	struct cbc_gpio_chip *chip =
+	    container_of(gc, struct cbc_gpio_chip, chip);
+
+	return !!(ioread32(chip->base + CBC_GPIO_INPUT) &
+		BIT(gpio));
+}
+
+/*
+ * cbc_gpio_set - Write the specified signal of the GPIO device.
+ */
+static void cbc_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+	unsigned long flags;
+	struct cbc_gpio_chip *chip =
+	    container_of(gc, struct cbc_gpio_chip, chip);
+
+	spin_lock_irqsave(&chip->gpio_lock, flags);
+
+	/* Write to GPIO signal and set its direction to output */
+	if (val)
+		chip->gpio_state |= BIT(gpio);
+	else
+		chip->gpio_state &= ~BIT(gpio);
+
+	iowrite32(chip->gpio_state, chip->base + CBC_GPIO_OUTPUT);
+
+	spin_unlock_irqrestore(&chip->gpio_lock, flags);
+}
+
+/*
+ * cbc_gpio_dir_in - Set the direction of the specified GPIO signal as input.
+ */
+static int cbc_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
+{
+	unsigned long flags;
+	struct cbc_gpio_chip *chip =
+	    container_of(gc, struct cbc_gpio_chip, chip);
+
+	spin_lock_irqsave(&chip->gpio_lock, flags);
+
+	chip->gpio_dir &= ~BIT(gpio);
+	iowrite32(chip->gpio_dir, chip->base + CBC_GPIO_DIR);
+
+	spin_unlock_irqrestore(&chip->gpio_lock, flags);
+
+	return 0;
+}
+
+/*
+ * cbc_gpio_dir_out - Set the direction of the specified GPIO signal as output.
+ */
+static int cbc_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+{
+	unsigned long flags;
+	struct cbc_gpio_chip *chip =
+	    container_of(gc, struct cbc_gpio_chip, chip);
+
+	spin_lock_irqsave(&chip->gpio_lock, flags);
+
+	chip->gpio_dir |= BIT(gpio);
+	iowrite32(chip->gpio_dir, chip->base +  CBC_GPIO_DIR);
+
+	if (val)
+		chip->gpio_state |= BIT(gpio);
+	else
+		chip->gpio_state &= ~BIT(gpio);
+	iowrite32(chip->gpio_state, chip->base + CBC_GPIO_OUTPUT);
+
+	spin_unlock_irqrestore(&chip->gpio_lock, flags);
+
+	return 0;
+}
+
+/*
+ * cbc_gpio_setup_one - Setup single bank as gpio_chip
+ */
+static void cbc_gpio_setup_one(struct cbc_gpio_chip *cgc, int ngpio)
+{
+	struct gpio_chip *gpio = &cgc->chip;
+
+	gpio->label = dev_name(cgc->dev);
+	gpio->owner = THIS_MODULE;
+	gpio->direction_input = cbc_gpio_dir_in;
+	gpio->get = cbc_gpio_get;
+	gpio->direction_output = cbc_gpio_dir_out;
+	gpio->set = cbc_gpio_set;
+	gpio->dbg_show = NULL;
+	gpio->base = -1;
+	gpio->ngpio = ngpio;
+	gpio->can_sleep = 0;
+#ifdef CONFIG_OF_GPIO
+	gpio->of_node = cgc->dev->of_node;
+#endif
+}
+
+static int cbc_gpio_probe(struct platform_device *pdev)
+{
+	int i, ret;
+	struct cbc_gpio_chip *chips, *c;
+	struct resource *res;
+	void __iomem *base;
+	struct device *dev = &pdev->dev;
+
+	chips = devm_kzalloc(dev, sizeof(struct cbc_gpio_chip) *
+		ARRAY_SIZE(cbc_gpios), GFP_KERNEL);
+	if (chips == NULL)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	base = devm_ioremap(dev, res->start, resource_size(res));
+	if (!base)
+		return -ENOMEM;
+
+
+	platform_set_drvdata(pdev, chips);
+
+	/* For each GPIO bank, register a GPIO chip. */
+	for (i = 0; i < ARRAY_SIZE(cbc_gpios); i++) {
+		c = &chips[i];
+
+		c->dev = dev;
+		c->base = base;
+		spin_lock_init(&c->gpio_lock);
+		c->offset = cbc_gpios[i].offset;
+		cbc_gpio_setup_one(c, cbc_gpios[i].ngpio);
+
+		ret = gpiochip_add(&c->chip);
+		if (ret) {
+			dev_err(&pdev->dev,
+				"Failed to register CBC gpiochip %d: %d\n",
+				i, ret);
+			goto err_gpiochip;
+		}
+	}
+	return 0;
+
+err_gpiochip:
+	for (i = i - 1; i >= 0; i--)
+		gpiochip_remove(&chips[i].chip);
+
+	return ret;
+}
+
+static int cbc_gpio_remove(struct platform_device *pdev)
+{
+	int i;
+	struct cbc_gpio_chip *chips = platform_get_drvdata(pdev);
+
+	for (i = 0; i < ARRAY_SIZE(cbc_gpios); i++)
+		gpiochip_remove(&chips[i].chip);
+
+	return 0;
+}
+
+static const struct of_device_id cbc_gpio_ids[] = {
+	{ .compatible = "jnx,gpio-cbc", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, cbc_gpio_ids);
+
+static struct platform_driver cbc_gpio_driver = {
+	.driver = {
+		.name = "gpio-cbc",
+		.owner  = THIS_MODULE,
+		.of_match_table = cbc_gpio_ids,
+	},
+	.probe = cbc_gpio_probe,
+	.remove = cbc_gpio_remove,
+};
+module_platform_driver(cbc_gpio_driver);
+
+MODULE_DESCRIPTION("Juniper Networks CBC GPIO Driver");
+MODULE_AUTHOR("Georgi Vlaev <gvlaev@juniper.net>");
+MODULE_LICENSE("GPL");
-- 
1.9.1

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

* [PATCH 3/6] gpio: gpio-cbc: Document bindings of CBC FPGA GPIO block
  2016-10-07 15:20 ` Pantelis Antoniou
@ 2016-10-07 15:20   ` Pantelis Antoniou
  -1 siblings, 0 replies; 24+ messages in thread
From: Pantelis Antoniou @ 2016-10-07 15:20 UTC (permalink / raw)
  To: Lee Jones
  Cc: Mark Rutland, Alexandre Courbot, devel, devicetree,
	Greg Kroah-Hartman, Linus Walleij, Pantelis Antoniou,
	linux-kernel, JawaharBalaji Thirumalaisamy, linux-gpio,
	Rob Herring, Debjit Ghosh, Mohammad Kamil, Georgi Vlaev,
	Frank Rowand, Guenter Roeck

From: Georgi Vlaev <gvlaev@juniper.net>

Add device tree bindings document for the GPIO driver of
Juniper's CBC FPGA.

Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
 .../devicetree/bindings/gpio/jnx,gpio-cbc.txt      | 30 ++++++++++++++++++++++
 1 file changed, 30 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/jnx,gpio-cbc.txt

diff --git a/Documentation/devicetree/bindings/gpio/jnx,gpio-cbc.txt b/Documentation/devicetree/bindings/gpio/jnx,gpio-cbc.txt
new file mode 100644
index 0000000..d205d0b
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/jnx,gpio-cbc.txt
@@ -0,0 +1,30 @@
+Juniper CBC FPGA GPIO block
+
+Required properties:
+
+- compatible:
+    Must be "jnx,cbc-gpio"
+
+Optional properties:
+
+- reg:
+    Address and length of the register set for the device. This is optional since
+    usually the parent MFD driver fills it in.
+
+- #gpio-cells:
+    Should be <2>.  The first cell is the pin number (within the controller's
+    pin space), and the second is used for the following flags:
+	bit[0]: direction (0 = out, 1 = in)
+	bit[1]: init high
+	bit[2]: active low
+
+- gpio-controller:
+    Specifies that the node is a GPIO controller.
+
+Example:
+
+gpio_cbc {
+	compatible = "jnx,gpio-cbc";
+	#gpio-cells = <2>;
+	gpio-controller;
+};
-- 
1.9.1

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

* [PATCH 3/6] gpio: gpio-cbc: Document bindings of CBC FPGA GPIO block
@ 2016-10-07 15:20   ` Pantelis Antoniou
  0 siblings, 0 replies; 24+ messages in thread
From: Pantelis Antoniou @ 2016-10-07 15:20 UTC (permalink / raw)
  To: Lee Jones
  Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
	Frank Rowand, Greg Kroah-Hartman, Debjit Ghosh, Georgi Vlaev,
	Guenter Roeck, JawaharBalaji Thirumalaisamy, Mohammad Kamil,
	Pantelis Antoniou, devicetree, linux-kernel, linux-gpio, devel

From: Georgi Vlaev <gvlaev@juniper.net>

Add device tree bindings document for the GPIO driver of
Juniper's CBC FPGA.

Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
 .../devicetree/bindings/gpio/jnx,gpio-cbc.txt      | 30 ++++++++++++++++++++++
 1 file changed, 30 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/jnx,gpio-cbc.txt

diff --git a/Documentation/devicetree/bindings/gpio/jnx,gpio-cbc.txt b/Documentation/devicetree/bindings/gpio/jnx,gpio-cbc.txt
new file mode 100644
index 0000000..d205d0b
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/jnx,gpio-cbc.txt
@@ -0,0 +1,30 @@
+Juniper CBC FPGA GPIO block
+
+Required properties:
+
+- compatible:
+    Must be "jnx,cbc-gpio"
+
+Optional properties:
+
+- reg:
+    Address and length of the register set for the device. This is optional since
+    usually the parent MFD driver fills it in.
+
+- #gpio-cells:
+    Should be <2>.  The first cell is the pin number (within the controller's
+    pin space), and the second is used for the following flags:
+	bit[0]: direction (0 = out, 1 = in)
+	bit[1]: init high
+	bit[2]: active low
+
+- gpio-controller:
+    Specifies that the node is a GPIO controller.
+
+Example:
+
+gpio_cbc {
+	compatible = "jnx,gpio-cbc";
+	#gpio-cells = <2>;
+	gpio-controller;
+};
-- 
1.9.1

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

* [PATCH 4/6] gpio: cbc-presence: Add CBC presence detect as GPIO driver
  2016-10-07 15:20 ` Pantelis Antoniou
@ 2016-10-07 15:20   ` Pantelis Antoniou
  -1 siblings, 0 replies; 24+ messages in thread
From: Pantelis Antoniou @ 2016-10-07 15:20 UTC (permalink / raw)
  To: Lee Jones
  Cc: Mark Rutland, Alexandre Courbot, devel, devicetree,
	Greg Kroah-Hartman, Linus Walleij, Pantelis Antoniou,
	linux-kernel, JawaharBalaji Thirumalaisamy, linux-gpio,
	Rob Herring, Debjit Ghosh, Mohammad Kamil, Georgi Vlaev,
	Frank Rowand, Guenter Roeck

From: Georgi Vlaev <gvlaev@juniper.net>

This driver exports the CB FPGA presence detect bits from a
single 32bit CB register as GPIOs.

Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
Signed-off-by: Guenter Roeck <groeck@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
 drivers/gpio/Kconfig             |  12 +
 drivers/gpio/Makefile            |   1 +
 drivers/gpio/gpio-cbc-presence.c | 460 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 473 insertions(+)
 create mode 100644 drivers/gpio/gpio-cbc-presence.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index b29f521..ef8f408 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -158,6 +158,18 @@ config GPIO_CBC
 	  This driver can also be built as a module.  If so, the module
 	  will be called gpio-cbc.
 
+config GPIO_CBC_PRESENCE
+	tristate "Juniper Networks PTX1K CBC presence detect as GPIO"
+	depends on MFD_JUNIPER_CBC && OF
+	default y if JNX_CONNECTOR
+	help
+	  This driver exports the CH_PRS and OTHER_CH_PRS presence detect
+	  bits of the PTX1K RCB CBC FPGA as GPIOs on the relevant Juniper
+	  platforms. Select if JNX_CONNECTOR is selected.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called gpio-cbc-presence.
+
 config GPIO_CLPS711X
 	tristate "CLPS711X GPIO support"
 	depends on ARCH_CLPS711X || COMPILE_TEST
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 78dd0d4..825c2636 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_GPIO_BCM_KONA)	+= gpio-bcm-kona.o
 obj-$(CONFIG_GPIO_BRCMSTB)	+= gpio-brcmstb.o
 obj-$(CONFIG_GPIO_BT8XX)	+= gpio-bt8xx.o
 obj-$(CONFIG_GPIO_CBC)		+= gpio-cbc.o
+obj-$(CONFIG_GPIO_CBC_PRESENCE)	+= gpio-cbc-presence.o
 obj-$(CONFIG_GPIO_CLPS711X)	+= gpio-clps711x.o
 obj-$(CONFIG_GPIO_CS5535)	+= gpio-cs5535.o
 obj-$(CONFIG_GPIO_CRYSTAL_COVE)	+= gpio-crystalcove.o
diff --git a/drivers/gpio/gpio-cbc-presence.c b/drivers/gpio/gpio-cbc-presence.c
new file mode 100644
index 0000000..ab16c0b
--- /dev/null
+++ b/drivers/gpio/gpio-cbc-presence.c
@@ -0,0 +1,460 @@
+/*
+ * Juniper Networks PTX1K CBC presence detect as GPIO driver
+ *
+ * Copyright (c) 2014, Juniper Networks
+ * Author: Georgi Vlaev <gvlaev@juniper.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/mfd/cbc-core.h>
+
+/*
+ * PTX1K RCB CBC:
+ * We have 26 presence bits in 2 regs.
+ * Interrupts are raised per bit change in a reg (2 ints)
+ *
+ * CBC_TOP_REGS_CH_PRS:
+ * FPC[7:0] -> FPC[7:0]
+ * FAN[16] -> FAN[0]
+ * CB[22:21] -> CB[1:0]
+ * FPD[23]
+ * OTHER_RE[26]
+ *
+ * CBC_TOP_REGS_OTHER_CH_PRS:
+ * PSM[4:0]
+ * SIB[10:5] -> SIB[5:0]
+ * SFPP[21:20] -> SFPP[1:0]
+ */
+
+/*
+ * struct cbc_presence_gpio - GPIO private data structure.
+ * @base: PCI base address of Memory mapped I/O register.
+ * @dev: Pointer to device structure.
+ * @gpio: Data for GPIO infrastructure.
+ */
+struct cbc_presence_gpio {
+	void __iomem *base;
+	struct device *dev;
+	struct gpio_chip gpio;
+	struct mutex irq_lock;
+	struct mutex work_lock;
+	struct irq_domain *domain;
+	int irq;
+	u32 reg;
+	unsigned long presence_cache;
+	unsigned long presence_irq_enabled;
+	unsigned long mask;
+	unsigned long always_present;
+	unsigned long poll_interval;
+	u8 irq_type[32];
+	struct delayed_work work;
+};
+
+static u32 cbc_presence_read(struct cbc_presence_gpio *cpg)
+{
+	return ioread32(cpg->base + cpg->reg) | cpg->always_present;
+}
+
+static int cbc_presence_gpio_get(struct gpio_chip *gc, unsigned int nr)
+{
+	struct cbc_presence_gpio *cpg =
+			container_of(gc, struct cbc_presence_gpio, gpio);
+	unsigned long data, pos, ord = 0;
+
+	data = cbc_presence_read(cpg);
+	for_each_set_bit(pos, &cpg->mask, fls(cpg->mask)) {
+		if (ord == nr)
+			return !!test_bit(ord, &data);
+		ord++;
+	}
+	return 0;
+}
+
+static int cbc_presence_gpio_direction_input(struct gpio_chip *gc,
+					     unsigned int nr)
+{
+	/* all pins are input pins */
+	return 0;
+}
+
+static int cbc_presence_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
+{
+	struct cbc_presence_gpio *cpg = container_of(gc,
+						struct cbc_presence_gpio, gpio);
+
+	return irq_create_mapping(cpg->domain, offset);
+}
+
+static void cbc_presence_irq_mask(struct irq_data *data)
+{
+	struct cbc_presence_gpio *cpg = irq_data_get_irq_chip_data(data);
+
+	clear_bit(data->hwirq, &cpg->presence_irq_enabled);
+}
+
+static void cbc_presence_irq_unmask(struct irq_data *data)
+{
+	struct cbc_presence_gpio *cpg = irq_data_get_irq_chip_data(data);
+
+	set_bit(data->hwirq, &cpg->presence_irq_enabled);
+}
+
+static int cbc_presence_irq_set_type(struct irq_data *data, unsigned int type)
+{
+	struct cbc_presence_gpio *cpg = irq_data_get_irq_chip_data(data);
+
+	cpg->irq_type[data->hwirq] = type & 0x0f;
+
+	return 0;
+}
+
+static void cbc_presence_irq_bus_lock(struct irq_data *data)
+{
+	struct cbc_presence_gpio *cpg = irq_data_get_irq_chip_data(data);
+
+	mutex_lock(&cpg->irq_lock);
+}
+
+static void cbc_presence_irq_bus_unlock(struct irq_data *data)
+{
+	struct cbc_presence_gpio *cpg = irq_data_get_irq_chip_data(data);
+
+	/* Synchronize interrupts to chip */
+
+	mutex_unlock(&cpg->irq_lock);
+}
+
+static struct irq_chip cbc_presence_irq_chip = {
+	.name = "gpio-cbc-presence",
+	.irq_mask = cbc_presence_irq_mask,
+	.irq_unmask = cbc_presence_irq_unmask,
+	.irq_set_type = cbc_presence_irq_set_type,
+	.irq_bus_lock = cbc_presence_irq_bus_lock,
+	.irq_bus_sync_unlock = cbc_presence_irq_bus_unlock,
+};
+
+static int cbc_presence_gpio_irq_map(struct irq_domain *domain,
+				unsigned int irq, irq_hw_number_t hwirq)
+{
+	irq_set_chip_data(irq, domain->host_data);
+	irq_set_chip(irq, &cbc_presence_irq_chip);
+	irq_set_nested_thread(irq, true);
+	irq_set_noprobe(irq);
+
+	return 0;
+}
+
+static const struct irq_domain_ops cbc_presence_gpio_irq_domain_ops = {
+	.map = cbc_presence_gpio_irq_map,
+	.xlate = irq_domain_xlate_twocell,
+};
+
+static void cbc_presence_gpio_irq_work(struct cbc_presence_gpio *cpg)
+{
+	unsigned long data, pos, ord = 0;
+
+	data = cbc_presence_read(cpg);
+
+	mutex_lock(&cpg->work_lock);
+	for_each_set_bit(pos, &cpg->mask, fls(cpg->mask)) {
+		int type, bit;
+
+		if (!test_bit(ord, &cpg->presence_irq_enabled)) {
+			ord++;
+			continue;
+		}
+
+		bit = test_bit(pos, &data);
+		if (bit == test_bit(pos, &cpg->presence_cache)) {
+			ord++;
+			continue;
+		}
+
+		type = cpg->irq_type[ord];
+		/*
+		 * check irq->type for match. Only handle edge triggered
+		 * interrupts; anything else doesn't make sense here.
+		 */
+		if (((type & IRQ_TYPE_EDGE_RISING) && bit) ||
+		    ((type & IRQ_TYPE_EDGE_FALLING) && !bit)) {
+			int virq = irq_find_mapping(cpg->domain, ord);
+
+			handle_nested_irq(virq);
+		}
+		ord++;
+	}
+	cpg->presence_cache = data;
+	mutex_unlock(&cpg->work_lock);
+}
+
+static irqreturn_t cbc_presence_gpio_irq_handler(int irq, void *data)
+{
+	struct cbc_presence_gpio *cpg = data;
+
+	cbc_presence_gpio_irq_work(cpg);
+
+	return IRQ_HANDLED;
+}
+
+static void cbc_presence_gpio_worker(struct work_struct *work)
+{
+	struct cbc_presence_gpio *cpg =
+		container_of(work, struct cbc_presence_gpio, work.work);
+
+	cbc_presence_gpio_irq_work(cpg);
+	schedule_delayed_work(&cpg->work,
+				msecs_to_jiffies(cpg->poll_interval));
+}
+
+static int cbc_presence_gpio_irq_setup(struct device *dev,
+					struct cbc_presence_gpio *cpg)
+{
+	int ret;
+
+	cpg->domain = irq_domain_add_linear(dev->of_node,
+				hweight_long(cpg->mask),
+				&cbc_presence_gpio_irq_domain_ops,
+				cpg);
+
+	if (cpg->domain == NULL)
+		return -ENOMEM;
+
+	INIT_DELAYED_WORK(&cpg->work, cbc_presence_gpio_worker);
+
+	if (cpg->irq) {
+		dev_info(dev, "Setting up interrupt %d\n", cpg->irq);
+		ret = devm_request_threaded_irq(dev, cpg->irq, NULL,
+						cbc_presence_gpio_irq_handler,
+						IRQF_ONESHOT,
+						dev_name(dev), cpg);
+		if (ret)
+			goto out_remove_domain;
+	} else {
+		schedule_delayed_work(&cpg->work, 1);
+	}
+
+	cpg->gpio.to_irq = cbc_presence_gpio_to_irq;
+
+	return 0;
+
+out_remove_domain:
+	irq_domain_remove(cpg->domain);
+	return ret;
+}
+
+static void cbc_presence_gpio_irq_teardown(struct device *dev,
+					struct cbc_presence_gpio *cpg)
+{
+	int i;
+
+	if (!cpg->irq)
+		cancel_delayed_work_sync(&cpg->work);
+
+	for (i = 0; i < cpg->gpio.ngpio; i++) {
+		int irq = irq_find_mapping(cpg->domain, i);
+
+		if (irq > 0)
+			irq_dispose_mapping(irq);
+	}
+	irq_domain_remove(cpg->domain);
+}
+
+static int cbc_presence_gpio_of_xlate(struct gpio_chip *gpio,
+				  const struct of_phandle_args *gpiospec,
+				  u32 *flags)
+{
+	if (WARN_ON(gpio->of_gpio_n_cells < 2))
+		return -EINVAL;
+
+	if (WARN_ON(gpiospec->args_count < gpio->of_gpio_n_cells))
+		return -EINVAL;
+
+	if (gpiospec->args[0] > gpio->ngpio)
+		return -EINVAL;
+
+	if (flags)
+		*flags = gpiospec->args[1] >> 16;
+
+	return gpiospec->args[0];
+}
+
+static void cbc_presence_gpio_setup(struct cbc_presence_gpio *cpg)
+{
+	struct gpio_chip *gpio = &cpg->gpio;
+
+	gpio->label = dev_name(cpg->dev);
+	gpio->owner = THIS_MODULE;
+	gpio->get = cbc_presence_gpio_get;
+	gpio->direction_input = cbc_presence_gpio_direction_input;
+	gpio->dbg_show = NULL;
+	gpio->base = -1;
+	gpio->ngpio = hweight_long(cpg->mask);
+	gpio->can_sleep = 0;
+	gpio->of_node = cpg->dev->of_node;
+	gpio->of_xlate = cbc_presence_gpio_of_xlate;
+	gpio->of_gpio_n_cells = 2;
+}
+
+static int cbc_presence_of_setup(struct cbc_presence_gpio *cpg)
+{
+	struct device *dev = cpg->dev;
+	struct device_node *node = dev->of_node;
+	u32 value;
+	int ret;
+
+	/* reg := CBC_TOP_REGS_CH_PRS | CBC_TOP_REGS_OTHER_CH_PRS */
+	ret = of_property_read_u32(node, "reg", &value);
+	if (ret) {
+		dev_err(dev, "failed to read the <reg> property.\n");
+		return ret;
+	}
+	cpg->reg = value;
+
+	/* mask := valid presence bits in <reg> */
+	ret = of_property_read_u32(node, "mask", &value);
+	if (ret)
+		value = 0xffffffff;
+	cpg->mask = value;
+
+	/*
+	 * always-present := some frus are always present, but not reported
+	 * e.g MP/BP on PTX1K & PTX3K
+	 */
+	ret = of_property_read_u32(node, "always-present", &value);
+	if (ret)
+		value = 0;
+	cpg->always_present = value;
+
+	/*
+	 * poll-interval := some CBC releses don't support interrupts.
+	 * Default poll interval is 1000 msec
+	 */
+	ret = of_property_read_u32(node, "poll-interval", &value);
+	if (ret)
+		value = 1000;
+	cpg->poll_interval = value;
+
+	return 0;
+}
+
+static int cbc_presence_gpio_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct cbc_presence_gpio *cpg;
+	struct resource *res;
+	int ret;
+
+	cpg = devm_kzalloc(dev, sizeof(struct cbc_presence_gpio),
+			GFP_KERNEL);
+	if (cpg == NULL)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	cpg->base = devm_ioremap_nocache(dev, res->start, resource_size(res));
+	if (IS_ERR(cpg->base))
+		return -ENOMEM;
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret > 0)
+		cpg->irq = ret;
+
+	cpg->dev = dev;
+	mutex_init(&cpg->irq_lock);
+	mutex_init(&cpg->work_lock);
+
+	ret = cbc_presence_of_setup(cpg);
+	if (ret)
+		return -ENODEV;
+
+	cbc_presence_gpio_setup(cpg);
+
+	/* cache the current presence */
+	cpg->presence_cache = cbc_presence_read(cpg);
+
+	ret = cbc_presence_gpio_irq_setup(dev, cpg);
+	if (ret < 0) {
+		dev_err(dev, "Failed to setup CBC presence irqs\n");
+		return ret;
+	}
+
+	ret = gpiochip_add(&cpg->gpio);
+	if (ret) {
+		dev_err(dev, "Failed to register CBC presence gpio\n");
+		goto err;
+	}
+
+	platform_set_drvdata(pdev, cpg);
+
+	return 0;
+err:
+	gpiochip_remove(&cpg->gpio);
+
+	if (cpg->domain)
+		cbc_presence_gpio_irq_teardown(dev, cpg);
+
+	return ret;
+}
+
+static int cbc_presence_gpio_remove(struct platform_device *pdev)
+{
+	struct cbc_presence_gpio *cpg = platform_get_drvdata(pdev);
+
+	cancel_delayed_work_sync(&cpg->work);
+	if (cpg->domain)
+		cbc_presence_gpio_irq_teardown(&pdev->dev, cpg);
+
+	gpiochip_remove(&cpg->gpio);
+
+	return 0;
+}
+
+static const struct of_device_id cbc_presence_gpio_ids[] = {
+	{ .compatible = "jnx,gpio-cbc-presence", },
+	/*
+	 * These are the same devices ... MFD OF hackery to
+	 * get around the single of_node compatible match
+	 * mfd_add_device() for the OTHER_CH_PRESENCE
+	 */
+	{ .compatible = "jnx,gpio-cbc-presence-other", },
+	{ .compatible = "jnx,gpio-cbc-presence-sib", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, cbc_presence_gpio_ids);
+
+static struct platform_driver cbc_presence_gpio_driver = {
+	.driver = {
+		.name = "gpio-cbc-presence",
+		.owner  = THIS_MODULE,
+		.of_match_table = cbc_presence_gpio_ids,
+	},
+	.probe = cbc_presence_gpio_probe,
+	.remove = cbc_presence_gpio_remove,
+};
+module_platform_driver(cbc_presence_gpio_driver);
+
+MODULE_DESCRIPTION("Juniper Networks CB presence detect as GPIO driver");
+MODULE_AUTHOR("Georgi Vlaev <gvlaev@juniper.net>");
+MODULE_LICENSE("GPL");
-- 
1.9.1

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

* [PATCH 4/6] gpio: cbc-presence: Add CBC presence detect as GPIO driver
@ 2016-10-07 15:20   ` Pantelis Antoniou
  0 siblings, 0 replies; 24+ messages in thread
From: Pantelis Antoniou @ 2016-10-07 15:20 UTC (permalink / raw)
  To: Lee Jones
  Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
	Frank Rowand, Greg Kroah-Hartman, Debjit Ghosh, Georgi Vlaev,
	Guenter Roeck, JawaharBalaji Thirumalaisamy, Mohammad Kamil,
	Pantelis Antoniou, devicetree, linux-kernel, linux-gpio, devel

From: Georgi Vlaev <gvlaev@juniper.net>

This driver exports the CB FPGA presence detect bits from a
single 32bit CB register as GPIOs.

Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
Signed-off-by: Guenter Roeck <groeck@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
 drivers/gpio/Kconfig             |  12 +
 drivers/gpio/Makefile            |   1 +
 drivers/gpio/gpio-cbc-presence.c | 460 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 473 insertions(+)
 create mode 100644 drivers/gpio/gpio-cbc-presence.c

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index b29f521..ef8f408 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -158,6 +158,18 @@ config GPIO_CBC
 	  This driver can also be built as a module.  If so, the module
 	  will be called gpio-cbc.
 
+config GPIO_CBC_PRESENCE
+	tristate "Juniper Networks PTX1K CBC presence detect as GPIO"
+	depends on MFD_JUNIPER_CBC && OF
+	default y if JNX_CONNECTOR
+	help
+	  This driver exports the CH_PRS and OTHER_CH_PRS presence detect
+	  bits of the PTX1K RCB CBC FPGA as GPIOs on the relevant Juniper
+	  platforms. Select if JNX_CONNECTOR is selected.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called gpio-cbc-presence.
+
 config GPIO_CLPS711X
 	tristate "CLPS711X GPIO support"
 	depends on ARCH_CLPS711X || COMPILE_TEST
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 78dd0d4..825c2636 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_GPIO_BCM_KONA)	+= gpio-bcm-kona.o
 obj-$(CONFIG_GPIO_BRCMSTB)	+= gpio-brcmstb.o
 obj-$(CONFIG_GPIO_BT8XX)	+= gpio-bt8xx.o
 obj-$(CONFIG_GPIO_CBC)		+= gpio-cbc.o
+obj-$(CONFIG_GPIO_CBC_PRESENCE)	+= gpio-cbc-presence.o
 obj-$(CONFIG_GPIO_CLPS711X)	+= gpio-clps711x.o
 obj-$(CONFIG_GPIO_CS5535)	+= gpio-cs5535.o
 obj-$(CONFIG_GPIO_CRYSTAL_COVE)	+= gpio-crystalcove.o
diff --git a/drivers/gpio/gpio-cbc-presence.c b/drivers/gpio/gpio-cbc-presence.c
new file mode 100644
index 0000000..ab16c0b
--- /dev/null
+++ b/drivers/gpio/gpio-cbc-presence.c
@@ -0,0 +1,460 @@
+/*
+ * Juniper Networks PTX1K CBC presence detect as GPIO driver
+ *
+ * Copyright (c) 2014, Juniper Networks
+ * Author: Georgi Vlaev <gvlaev@juniper.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/mfd/cbc-core.h>
+
+/*
+ * PTX1K RCB CBC:
+ * We have 26 presence bits in 2 regs.
+ * Interrupts are raised per bit change in a reg (2 ints)
+ *
+ * CBC_TOP_REGS_CH_PRS:
+ * FPC[7:0] -> FPC[7:0]
+ * FAN[16] -> FAN[0]
+ * CB[22:21] -> CB[1:0]
+ * FPD[23]
+ * OTHER_RE[26]
+ *
+ * CBC_TOP_REGS_OTHER_CH_PRS:
+ * PSM[4:0]
+ * SIB[10:5] -> SIB[5:0]
+ * SFPP[21:20] -> SFPP[1:0]
+ */
+
+/*
+ * struct cbc_presence_gpio - GPIO private data structure.
+ * @base: PCI base address of Memory mapped I/O register.
+ * @dev: Pointer to device structure.
+ * @gpio: Data for GPIO infrastructure.
+ */
+struct cbc_presence_gpio {
+	void __iomem *base;
+	struct device *dev;
+	struct gpio_chip gpio;
+	struct mutex irq_lock;
+	struct mutex work_lock;
+	struct irq_domain *domain;
+	int irq;
+	u32 reg;
+	unsigned long presence_cache;
+	unsigned long presence_irq_enabled;
+	unsigned long mask;
+	unsigned long always_present;
+	unsigned long poll_interval;
+	u8 irq_type[32];
+	struct delayed_work work;
+};
+
+static u32 cbc_presence_read(struct cbc_presence_gpio *cpg)
+{
+	return ioread32(cpg->base + cpg->reg) | cpg->always_present;
+}
+
+static int cbc_presence_gpio_get(struct gpio_chip *gc, unsigned int nr)
+{
+	struct cbc_presence_gpio *cpg =
+			container_of(gc, struct cbc_presence_gpio, gpio);
+	unsigned long data, pos, ord = 0;
+
+	data = cbc_presence_read(cpg);
+	for_each_set_bit(pos, &cpg->mask, fls(cpg->mask)) {
+		if (ord == nr)
+			return !!test_bit(ord, &data);
+		ord++;
+	}
+	return 0;
+}
+
+static int cbc_presence_gpio_direction_input(struct gpio_chip *gc,
+					     unsigned int nr)
+{
+	/* all pins are input pins */
+	return 0;
+}
+
+static int cbc_presence_gpio_to_irq(struct gpio_chip *gc, unsigned int offset)
+{
+	struct cbc_presence_gpio *cpg = container_of(gc,
+						struct cbc_presence_gpio, gpio);
+
+	return irq_create_mapping(cpg->domain, offset);
+}
+
+static void cbc_presence_irq_mask(struct irq_data *data)
+{
+	struct cbc_presence_gpio *cpg = irq_data_get_irq_chip_data(data);
+
+	clear_bit(data->hwirq, &cpg->presence_irq_enabled);
+}
+
+static void cbc_presence_irq_unmask(struct irq_data *data)
+{
+	struct cbc_presence_gpio *cpg = irq_data_get_irq_chip_data(data);
+
+	set_bit(data->hwirq, &cpg->presence_irq_enabled);
+}
+
+static int cbc_presence_irq_set_type(struct irq_data *data, unsigned int type)
+{
+	struct cbc_presence_gpio *cpg = irq_data_get_irq_chip_data(data);
+
+	cpg->irq_type[data->hwirq] = type & 0x0f;
+
+	return 0;
+}
+
+static void cbc_presence_irq_bus_lock(struct irq_data *data)
+{
+	struct cbc_presence_gpio *cpg = irq_data_get_irq_chip_data(data);
+
+	mutex_lock(&cpg->irq_lock);
+}
+
+static void cbc_presence_irq_bus_unlock(struct irq_data *data)
+{
+	struct cbc_presence_gpio *cpg = irq_data_get_irq_chip_data(data);
+
+	/* Synchronize interrupts to chip */
+
+	mutex_unlock(&cpg->irq_lock);
+}
+
+static struct irq_chip cbc_presence_irq_chip = {
+	.name = "gpio-cbc-presence",
+	.irq_mask = cbc_presence_irq_mask,
+	.irq_unmask = cbc_presence_irq_unmask,
+	.irq_set_type = cbc_presence_irq_set_type,
+	.irq_bus_lock = cbc_presence_irq_bus_lock,
+	.irq_bus_sync_unlock = cbc_presence_irq_bus_unlock,
+};
+
+static int cbc_presence_gpio_irq_map(struct irq_domain *domain,
+				unsigned int irq, irq_hw_number_t hwirq)
+{
+	irq_set_chip_data(irq, domain->host_data);
+	irq_set_chip(irq, &cbc_presence_irq_chip);
+	irq_set_nested_thread(irq, true);
+	irq_set_noprobe(irq);
+
+	return 0;
+}
+
+static const struct irq_domain_ops cbc_presence_gpio_irq_domain_ops = {
+	.map = cbc_presence_gpio_irq_map,
+	.xlate = irq_domain_xlate_twocell,
+};
+
+static void cbc_presence_gpio_irq_work(struct cbc_presence_gpio *cpg)
+{
+	unsigned long data, pos, ord = 0;
+
+	data = cbc_presence_read(cpg);
+
+	mutex_lock(&cpg->work_lock);
+	for_each_set_bit(pos, &cpg->mask, fls(cpg->mask)) {
+		int type, bit;
+
+		if (!test_bit(ord, &cpg->presence_irq_enabled)) {
+			ord++;
+			continue;
+		}
+
+		bit = test_bit(pos, &data);
+		if (bit == test_bit(pos, &cpg->presence_cache)) {
+			ord++;
+			continue;
+		}
+
+		type = cpg->irq_type[ord];
+		/*
+		 * check irq->type for match. Only handle edge triggered
+		 * interrupts; anything else doesn't make sense here.
+		 */
+		if (((type & IRQ_TYPE_EDGE_RISING) && bit) ||
+		    ((type & IRQ_TYPE_EDGE_FALLING) && !bit)) {
+			int virq = irq_find_mapping(cpg->domain, ord);
+
+			handle_nested_irq(virq);
+		}
+		ord++;
+	}
+	cpg->presence_cache = data;
+	mutex_unlock(&cpg->work_lock);
+}
+
+static irqreturn_t cbc_presence_gpio_irq_handler(int irq, void *data)
+{
+	struct cbc_presence_gpio *cpg = data;
+
+	cbc_presence_gpio_irq_work(cpg);
+
+	return IRQ_HANDLED;
+}
+
+static void cbc_presence_gpio_worker(struct work_struct *work)
+{
+	struct cbc_presence_gpio *cpg =
+		container_of(work, struct cbc_presence_gpio, work.work);
+
+	cbc_presence_gpio_irq_work(cpg);
+	schedule_delayed_work(&cpg->work,
+				msecs_to_jiffies(cpg->poll_interval));
+}
+
+static int cbc_presence_gpio_irq_setup(struct device *dev,
+					struct cbc_presence_gpio *cpg)
+{
+	int ret;
+
+	cpg->domain = irq_domain_add_linear(dev->of_node,
+				hweight_long(cpg->mask),
+				&cbc_presence_gpio_irq_domain_ops,
+				cpg);
+
+	if (cpg->domain == NULL)
+		return -ENOMEM;
+
+	INIT_DELAYED_WORK(&cpg->work, cbc_presence_gpio_worker);
+
+	if (cpg->irq) {
+		dev_info(dev, "Setting up interrupt %d\n", cpg->irq);
+		ret = devm_request_threaded_irq(dev, cpg->irq, NULL,
+						cbc_presence_gpio_irq_handler,
+						IRQF_ONESHOT,
+						dev_name(dev), cpg);
+		if (ret)
+			goto out_remove_domain;
+	} else {
+		schedule_delayed_work(&cpg->work, 1);
+	}
+
+	cpg->gpio.to_irq = cbc_presence_gpio_to_irq;
+
+	return 0;
+
+out_remove_domain:
+	irq_domain_remove(cpg->domain);
+	return ret;
+}
+
+static void cbc_presence_gpio_irq_teardown(struct device *dev,
+					struct cbc_presence_gpio *cpg)
+{
+	int i;
+
+	if (!cpg->irq)
+		cancel_delayed_work_sync(&cpg->work);
+
+	for (i = 0; i < cpg->gpio.ngpio; i++) {
+		int irq = irq_find_mapping(cpg->domain, i);
+
+		if (irq > 0)
+			irq_dispose_mapping(irq);
+	}
+	irq_domain_remove(cpg->domain);
+}
+
+static int cbc_presence_gpio_of_xlate(struct gpio_chip *gpio,
+				  const struct of_phandle_args *gpiospec,
+				  u32 *flags)
+{
+	if (WARN_ON(gpio->of_gpio_n_cells < 2))
+		return -EINVAL;
+
+	if (WARN_ON(gpiospec->args_count < gpio->of_gpio_n_cells))
+		return -EINVAL;
+
+	if (gpiospec->args[0] > gpio->ngpio)
+		return -EINVAL;
+
+	if (flags)
+		*flags = gpiospec->args[1] >> 16;
+
+	return gpiospec->args[0];
+}
+
+static void cbc_presence_gpio_setup(struct cbc_presence_gpio *cpg)
+{
+	struct gpio_chip *gpio = &cpg->gpio;
+
+	gpio->label = dev_name(cpg->dev);
+	gpio->owner = THIS_MODULE;
+	gpio->get = cbc_presence_gpio_get;
+	gpio->direction_input = cbc_presence_gpio_direction_input;
+	gpio->dbg_show = NULL;
+	gpio->base = -1;
+	gpio->ngpio = hweight_long(cpg->mask);
+	gpio->can_sleep = 0;
+	gpio->of_node = cpg->dev->of_node;
+	gpio->of_xlate = cbc_presence_gpio_of_xlate;
+	gpio->of_gpio_n_cells = 2;
+}
+
+static int cbc_presence_of_setup(struct cbc_presence_gpio *cpg)
+{
+	struct device *dev = cpg->dev;
+	struct device_node *node = dev->of_node;
+	u32 value;
+	int ret;
+
+	/* reg := CBC_TOP_REGS_CH_PRS | CBC_TOP_REGS_OTHER_CH_PRS */
+	ret = of_property_read_u32(node, "reg", &value);
+	if (ret) {
+		dev_err(dev, "failed to read the <reg> property.\n");
+		return ret;
+	}
+	cpg->reg = value;
+
+	/* mask := valid presence bits in <reg> */
+	ret = of_property_read_u32(node, "mask", &value);
+	if (ret)
+		value = 0xffffffff;
+	cpg->mask = value;
+
+	/*
+	 * always-present := some frus are always present, but not reported
+	 * e.g MP/BP on PTX1K & PTX3K
+	 */
+	ret = of_property_read_u32(node, "always-present", &value);
+	if (ret)
+		value = 0;
+	cpg->always_present = value;
+
+	/*
+	 * poll-interval := some CBC releses don't support interrupts.
+	 * Default poll interval is 1000 msec
+	 */
+	ret = of_property_read_u32(node, "poll-interval", &value);
+	if (ret)
+		value = 1000;
+	cpg->poll_interval = value;
+
+	return 0;
+}
+
+static int cbc_presence_gpio_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct cbc_presence_gpio *cpg;
+	struct resource *res;
+	int ret;
+
+	cpg = devm_kzalloc(dev, sizeof(struct cbc_presence_gpio),
+			GFP_KERNEL);
+	if (cpg == NULL)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	cpg->base = devm_ioremap_nocache(dev, res->start, resource_size(res));
+	if (IS_ERR(cpg->base))
+		return -ENOMEM;
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret > 0)
+		cpg->irq = ret;
+
+	cpg->dev = dev;
+	mutex_init(&cpg->irq_lock);
+	mutex_init(&cpg->work_lock);
+
+	ret = cbc_presence_of_setup(cpg);
+	if (ret)
+		return -ENODEV;
+
+	cbc_presence_gpio_setup(cpg);
+
+	/* cache the current presence */
+	cpg->presence_cache = cbc_presence_read(cpg);
+
+	ret = cbc_presence_gpio_irq_setup(dev, cpg);
+	if (ret < 0) {
+		dev_err(dev, "Failed to setup CBC presence irqs\n");
+		return ret;
+	}
+
+	ret = gpiochip_add(&cpg->gpio);
+	if (ret) {
+		dev_err(dev, "Failed to register CBC presence gpio\n");
+		goto err;
+	}
+
+	platform_set_drvdata(pdev, cpg);
+
+	return 0;
+err:
+	gpiochip_remove(&cpg->gpio);
+
+	if (cpg->domain)
+		cbc_presence_gpio_irq_teardown(dev, cpg);
+
+	return ret;
+}
+
+static int cbc_presence_gpio_remove(struct platform_device *pdev)
+{
+	struct cbc_presence_gpio *cpg = platform_get_drvdata(pdev);
+
+	cancel_delayed_work_sync(&cpg->work);
+	if (cpg->domain)
+		cbc_presence_gpio_irq_teardown(&pdev->dev, cpg);
+
+	gpiochip_remove(&cpg->gpio);
+
+	return 0;
+}
+
+static const struct of_device_id cbc_presence_gpio_ids[] = {
+	{ .compatible = "jnx,gpio-cbc-presence", },
+	/*
+	 * These are the same devices ... MFD OF hackery to
+	 * get around the single of_node compatible match
+	 * mfd_add_device() for the OTHER_CH_PRESENCE
+	 */
+	{ .compatible = "jnx,gpio-cbc-presence-other", },
+	{ .compatible = "jnx,gpio-cbc-presence-sib", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, cbc_presence_gpio_ids);
+
+static struct platform_driver cbc_presence_gpio_driver = {
+	.driver = {
+		.name = "gpio-cbc-presence",
+		.owner  = THIS_MODULE,
+		.of_match_table = cbc_presence_gpio_ids,
+	},
+	.probe = cbc_presence_gpio_probe,
+	.remove = cbc_presence_gpio_remove,
+};
+module_platform_driver(cbc_presence_gpio_driver);
+
+MODULE_DESCRIPTION("Juniper Networks CB presence detect as GPIO driver");
+MODULE_AUTHOR("Georgi Vlaev <gvlaev@juniper.net>");
+MODULE_LICENSE("GPL");
-- 
1.9.1

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

* [PATCH 5/6] gpio: gpio-cbc-presense: Document bindings of CBC FPGA presence
  2016-10-07 15:20 ` Pantelis Antoniou
@ 2016-10-07 15:20   ` Pantelis Antoniou
  -1 siblings, 0 replies; 24+ messages in thread
From: Pantelis Antoniou @ 2016-10-07 15:20 UTC (permalink / raw)
  To: Lee Jones
  Cc: Mark Rutland, Alexandre Courbot, devel, devicetree,
	Greg Kroah-Hartman, Linus Walleij, Pantelis Antoniou,
	linux-kernel, JawaharBalaji Thirumalaisamy, linux-gpio,
	Rob Herring, Debjit Ghosh, Mohammad Kamil, Georgi Vlaev,
	Frank Rowand, Guenter Roeck

From: Georgi Vlaev <gvlaev@juniper.net>

Add device tree bindings document for the presence virtual GPIOs
on Juniper's CBC FPGA.

Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
 .../bindings/gpio/jnx,gpio-cbc-presense.txt        | 31 ++++++++++++++++++++++
 1 file changed, 31 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/jnx,gpio-cbc-presense.txt

diff --git a/Documentation/devicetree/bindings/gpio/jnx,gpio-cbc-presense.txt b/Documentation/devicetree/bindings/gpio/jnx,gpio-cbc-presense.txt
new file mode 100644
index 0000000..f44e5a0
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/jnx,gpio-cbc-presense.txt
@@ -0,0 +1,31 @@
+Juniper CBC FPGA GPIO presence block
+
+Required properties:
+
+- compatible:
+    Must be one of "jnx,gpio-cbc-presence", "jnx,gpio-cbc-presence-other",
+    "jnx,gpio-cbc-presence-sib"
+
+Optional properties:
+
+- reg:
+    Address and length of the register set for the device. This is optional since
+    usually the parent MFD driver fills it in.
+
+- #gpio-cells:
+    Should be <2>.  The first cell is the pin number (within the controller's
+    pin space), and the second is used for the following flags:
+	bit[0]: direction (0 = out, 1 = in)
+	bit[1]: init high
+	bit[2]: active low
+
+- gpio-controller:
+    Specifies that the node is a GPIO controller.
+
+Example:
+
+gpio_cbc_presense {
+	compatible = "jnx,gpio-cbc-presense";
+	#gpio-cells = <2>;
+	gpio-controller;
+};
-- 
1.9.1

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

* [PATCH 5/6] gpio: gpio-cbc-presense: Document bindings of CBC FPGA presence
@ 2016-10-07 15:20   ` Pantelis Antoniou
  0 siblings, 0 replies; 24+ messages in thread
From: Pantelis Antoniou @ 2016-10-07 15:20 UTC (permalink / raw)
  To: Lee Jones
  Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
	Frank Rowand, Greg Kroah-Hartman, Debjit Ghosh, Georgi Vlaev,
	Guenter Roeck, JawaharBalaji Thirumalaisamy, Mohammad Kamil,
	Pantelis Antoniou, devicetree, linux-kernel, linux-gpio, devel

From: Georgi Vlaev <gvlaev@juniper.net>

Add device tree bindings document for the presence virtual GPIOs
on Juniper's CBC FPGA.

Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
 .../bindings/gpio/jnx,gpio-cbc-presense.txt        | 31 ++++++++++++++++++++++
 1 file changed, 31 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/jnx,gpio-cbc-presense.txt

diff --git a/Documentation/devicetree/bindings/gpio/jnx,gpio-cbc-presense.txt b/Documentation/devicetree/bindings/gpio/jnx,gpio-cbc-presense.txt
new file mode 100644
index 0000000..f44e5a0
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/jnx,gpio-cbc-presense.txt
@@ -0,0 +1,31 @@
+Juniper CBC FPGA GPIO presence block
+
+Required properties:
+
+- compatible:
+    Must be one of "jnx,gpio-cbc-presence", "jnx,gpio-cbc-presence-other",
+    "jnx,gpio-cbc-presence-sib"
+
+Optional properties:
+
+- reg:
+    Address and length of the register set for the device. This is optional since
+    usually the parent MFD driver fills it in.
+
+- #gpio-cells:
+    Should be <2>.  The first cell is the pin number (within the controller's
+    pin space), and the second is used for the following flags:
+	bit[0]: direction (0 = out, 1 = in)
+	bit[1]: init high
+	bit[2]: active low
+
+- gpio-controller:
+    Specifies that the node is a GPIO controller.
+
+Example:
+
+gpio_cbc_presense {
+	compatible = "jnx,gpio-cbc-presense";
+	#gpio-cells = <2>;
+	gpio-controller;
+};
-- 
1.9.1

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

* [PATCH 6/6] staging: jnx: CBD-FPGA infrastructure
  2016-10-07 15:20 ` Pantelis Antoniou
@ 2016-10-07 15:20   ` Pantelis Antoniou
  -1 siblings, 0 replies; 24+ messages in thread
From: Pantelis Antoniou @ 2016-10-07 15:20 UTC (permalink / raw)
  To: Lee Jones
  Cc: Mark Rutland, Alexandre Courbot, devel, devicetree,
	Greg Kroah-Hartman, Linus Walleij, Pantelis Antoniou,
	linux-kernel, JawaharBalaji Thirumalaisamy, linux-gpio,
	Rob Herring, Debjit Ghosh, Mohammad Kamil, Georgi Vlaev,
	Frank Rowand, Guenter Roeck

From: Tom Kavanagh <tkavanagh@juniper.net>

Every Juniper platform contains a CBD (Control Board) FPGA.

While each CBD FPGA is different, a common abstact API makes
handling them common for every platform and the same parts
they have can be factored out.

The supported CBDs are PTX1K, PTX21K, PTX3K & PTX5K.

Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
Signed-off-by: Guenter Roeck <groeck@juniper.net>
Signed-off-by: JawaharBalaji Thirumalaisamy <jawaharb@juniper.net>
Signed-off-by: Mohammad Kamil <mkamil@juniper.net>
Signed-off-by: Tom Kavanagh <tkavanagh@juniper.net>
Signed-off-by: Debjit Ghosh <dghosh@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
 drivers/staging/jnx/Kconfig                 |  34 ++
 drivers/staging/jnx/Makefile                |   5 +
 drivers/staging/jnx/jnx-cbc-ptx1k.c         | 242 +++++++++++++
 drivers/staging/jnx/jnx-cbd-fpga-common.c   | 332 +++++++++++++++++
 drivers/staging/jnx/jnx-cbd-fpga-common.h   |  27 ++
 drivers/staging/jnx/jnx-cbd-fpga-core.c     | 540 ++++++++++++++++++++++++++++
 drivers/staging/jnx/jnx-cbd-fpga-core.h     |  68 ++++
 drivers/staging/jnx/jnx-cbd-fpga-platdata.h |  51 +++
 drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c    | 134 +++++++
 drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c   | 143 ++++++++
 drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c    | 111 ++++++
 drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c    | 107 ++++++
 12 files changed, 1794 insertions(+)
 create mode 100644 drivers/staging/jnx/jnx-cbc-ptx1k.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-common.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-common.h
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-core.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-core.h
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-platdata.h
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c

diff --git a/drivers/staging/jnx/Kconfig b/drivers/staging/jnx/Kconfig
index 4c38fc2..cd29276 100644
--- a/drivers/staging/jnx/Kconfig
+++ b/drivers/staging/jnx/Kconfig
@@ -34,6 +34,40 @@ config JNX_CONNECTOR
 	  This driver can also be built as a module.  If so, the module
 	  will be called jnx-connector.
 
+config JNX_CBD_FPGA
+	tristate "Juniper Generic CBD FPGA"
+	select I2C_MUX
+	help
+	  Driver to support the Juniper Control Board (CBD) FPGA.  Provides all
+	  common functionality for device across all supported juniper platforms.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called jnx-cbd-fpga.
+
+config JNX_CBD_FPGA_PTX21K
+	tristate "Juniper PTX21K CBC FPGA"
+	depends on JNX_SYSTEM && MFD_JUNIPER_CBC && JNX_PTX21K_BOARDS
+	depends on JNX_PTX21K_RCB
+	default m
+	help
+	  Driver to support the Juniper Control Board (CBC) FPGA in PTX21K.
+	  Provides hooks for the common fpga handling core for these
+	  particular Juniper Boards.
+
+	  When compiled as a module it is included in jnx-cbd-fpga.
+
+config JNX_CBD_FPGA_PTX1K
+	tristate "Juniper PTX1K CBC FPGA"
+	depends on JNX_SYSTEM && MFD_JUNIPER_CBC && JNX_PTX1K_BOARDS
+	depends on JNX_CBD_FPGA_PTX21K
+	help
+	  Driver to support the Juniper Control Board (CBC) FPGA in PTX1K.
+	  Provides all common functionality for device across platforms and
+	  hooks for the common fpga handling core for these particular
+	  Juniper Boards.
+
+	  When compiled as a module it is included in jnx-cbd-fpga.
+
 endmenu
 
 config JNX_COMMON_PCI
diff --git a/drivers/staging/jnx/Makefile b/drivers/staging/jnx/Makefile
index c89e701..b937896 100644
--- a/drivers/staging/jnx/Makefile
+++ b/drivers/staging/jnx/Makefile
@@ -7,3 +7,8 @@ obj-$(CONFIG_JNX_CHIP_PCI_QUIRKS)+= jnx-chip-pci-quirks.o
 obj-$(CONFIG_JNX_COMMON_PCI)	+= jnx_common_pci.o
 obj-$(CONFIG_JNX_PEX8XXX_I2C)	+= pex8xxx_i2c.o
 obj-$(CONFIG_JNX_CONNECTOR)	+= jnx-connector.o
+obj-$(CONFIG_JNX_CBD_FPGA)	+= jnx-cbd-fpga.o jnx-cbd-fpga-common.o
+obj-$(CONFIG_JNX_CBD_FPGA_PTX1K)+= jnx-cbd-ptx1k.o
+obj-$(CONFIG_JNX_CBD_FPGA_PTX21K)+= jnx-cbd-fpga-ptx21k.o
+jnx-cbd-fpga-y := jnx-cbd-fpga-core.o jnx-cbd-fpga-ptx5k.o jnx-cbd-fpga-ptx3k.o
+jnx-cbd-ptx1k-y := jnx-cbc-ptx1k.o jnx-cbd-fpga-ptx1k.o
diff --git a/drivers/staging/jnx/jnx-cbc-ptx1k.c b/drivers/staging/jnx/jnx-cbc-ptx1k.c
new file mode 100644
index 0000000..7eff5ce
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbc-ptx1k.c
@@ -0,0 +1,242 @@
+/*
+ * Juniper Generic Control Board (CB) FPGA Driver
+ *
+ * Copyright (C) 2012, 2013, 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/jnx/jnx-subsys.h>
+#include <linux/jnx/jnx-board-core.h>
+#include <linux/jnx/board_ids.h>
+#include <linux/mfd/cbc-core.h>
+#include <linux/of.h>
+#include <linux/dmi.h>
+#include "jnx-cbd-fpga-core.h"
+#include "jnx-cbd-fpga-common.h"
+#include "jnx-cbd-fpga-platdata.h"
+#include "jnx-subsys-private.h"
+
+/*
+ * Polaris notes (current state):
+ *
+ * I2C: We dont have I2C mux and I2C slow bus. The I2C is handled
+ *   by the I2C accellerator in the CBC FPGA (40 buses -> 10 i2c masters
+ *   internaly muxed x4), supported by the SAM i2c driver.
+ * Presence detect: (CH_PRS and OTHER_CH_PRS) - these bits are exported as
+ *   GPIOs by the gpio-cbc-presence driver
+ * DEVICE_CRTL is not valid for Polaris - we don't have to reset anything a mux
+ */
+
+#define PTX1K_SAM_CHANNELS	4
+static struct i2c_adapter *jnx_find_sam_adapter(int chan)
+{
+	char name[JNX_BRD_I2C_NAME_LEN];
+	struct i2c_adapter *adap;
+	int base, mux, chan_id;
+
+	/*
+	 * Find base adapter index first. Base is first SAM adapter,
+	 * and the name starts with 'i2c-sam.'. We can not assume this
+	 * to be constant.
+	 */
+	adap = jnx_i2c_find_adapter("i2c-sam");
+	if (!adap)
+		return NULL;
+	base = adap->nr;
+	i2c_put_adapter(adap);
+
+	/*
+	 * (zero based)  channel -> sam i2c_adapter
+	 * Example:
+	 *	bus #11 = i2c-10-mux (chan_id 3)
+	 *	bus #4  = i2c-5-mux (chan_id 0)
+	 */
+	chan_id = chan % PTX1K_SAM_CHANNELS; /* 0..3 */
+	mux = chan / PTX1K_SAM_CHANNELS;
+	mux *= (PTX1K_SAM_CHANNELS + 1); /* 0, 5, 10, ... */
+	mux += base;
+
+	sprintf(name, "i2c-%d-mux (chan_id %d)", mux, chan_id);
+
+	return jnx_i2c_find_adapter(name);
+}
+
+/*
+ * Check if we're on OF enabled setup and if the jnx_connector is defined
+ * in boot dtb. If present we'll assume that board insertion/removal will
+ * be handled by the connector and the card overlays and not the jnx-*
+ * platform drivers. This allows a temp fallback util the ptx1k/x86 code
+ * is fully DT capable.
+ */
+static int jnx_connector_present(void)
+{
+	return of_have_populated_dt();
+}
+
+const struct jnx_cbd_fpga_info * const jnx_cbc_cfinfo[] = {
+	[JNX_CBD_FPGA_PTX1K] = &jnx_cbd_fpga_ptx1k_info,
+	[JNX_CBD_FPGA_PTX1K_P2] = &jnx_cbd_fpga_ptx1k_p2_info,
+	[JNX_CBD_FPGA_PTX21K] = &jnx_cbd_fpga_ptx21k_info,
+};
+
+static int jnx_cbc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct cbc_fpga_platdata *pdata = dev_get_platdata(dev);
+	const struct jnx_cbd_fpga_info *cfinfo;
+	struct jnx_cbd_fpga_data *fdata;
+	struct jnx_chassis_info chinfo;
+	struct jnx_card_info cinfo;
+	struct resource *res;
+	u32 ch_prs;
+	int err;
+
+	if (!pdata || pdata->board_type >= ARRAY_SIZE(jnx_cbc_cfinfo))
+		return -ENODEV;
+
+	cfinfo = jnx_cbc_cfinfo[pdata->board_type];
+
+	fdata = devm_kzalloc(dev, sizeof(*fdata), GFP_KERNEL);
+	if (!fdata)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	fdata->region0 = devm_ioremap(dev, res->start, resource_size(res));
+	if (!fdata->region0)
+		return -ENOMEM;
+
+	fdata->cfinfo = cfinfo;
+	fdata->find_mux_adapter = jnx_find_sam_adapter;
+	fdata->default_chan = cfinfo->i2cmux_default_chan;
+	fdata->mux_channels = cfinfo->mux_channels;
+	if (fdata->mux_channels > JNX_CBD_FPGA_NUM_MUXES)
+		fdata->mux_channels = JNX_CBD_FPGA_NUM_MUXES;
+
+	mutex_init(&fdata->lock);
+
+	platform_set_drvdata(pdev, fdata);
+
+	err = jnx_cbd_fpga_sysfs_init(dev);
+	if (err)
+		return err;
+
+	/*
+	 * Register the chassis with the system so userspace is able to
+	 * determine platform type, etc.
+	 */
+	chinfo.platform = cfinfo->platform;
+	chinfo.chassis_no = 0;
+	chinfo.multichassis = 0;
+	chinfo.master_data = fdata;
+	chinfo.get_master = jnx_cbd_fpga_get_master;
+	chinfo.mastership_get = jnx_cbd_fpga_mastership_get;
+	chinfo.mastership_set = jnx_cbd_fpga_mastership_set;
+	chinfo.mastership_ping = jnx_cbd_fpga_mastership_ping;
+	chinfo.mastership_count_get = jnx_cbd_fpga_mastership_count_get;
+	chinfo.mastership_count_set = jnx_cbd_fpga_mastership_count_set;
+
+	jnx_register_chassis(&chinfo);
+
+	/*
+	 * Register the local card with the system so userspace is able to
+	 * determine board type etc.
+	 */
+	ch_prs = ioread32(fdata->region0 + JNX_CBD_FPGA_CH_PRS_REG);
+
+	fdata->slot = (ch_prs & cfinfo->ch_prs_cb0_bit) ? 0 : 1;
+
+	cinfo.assembly_id = cfinfo->assembly_id;
+	cinfo.slot = fdata->slot;
+	cinfo.type = JNX_BOARD_TYPE_RE;
+	cinfo.adap = NULL;
+
+	jnx_register_local_card(&cinfo);
+
+	/* The jnx-connector & gpio-cbc-presence will handle the cards */
+	fdata->dt_enabled = jnx_connector_present();
+	if (fdata->dt_enabled) {
+		dev_info(dev, "line cards will handled by the jnx-connector\n");
+		return 0;
+	}
+
+	fdata->workqueue = create_singlethread_workqueue("jnx-cbd-poller");
+	if (!fdata->workqueue) {
+		err = -ENOMEM;
+		goto err_unregister;
+	}
+
+	INIT_DELAYED_WORK(&fdata->work, jnx_cbd_fpga_ch_prs_handler);
+
+	return 0;
+
+err_unregister:
+	jnx_unregister_local_card();
+	jnx_unregister_chassis();
+	jnx_cbd_fpga_sysfs_remove(dev);
+
+	return err;
+}
+
+static int jnx_cbc_remove(struct platform_device *pdev)
+{
+	struct jnx_cbd_fpga_data *fdata = platform_get_drvdata(pdev);
+	int i;
+
+	if (!fdata->dt_enabled) {
+		cancel_delayed_work_sync(&fdata->work);
+		flush_workqueue(fdata->workqueue);
+		destroy_workqueue(fdata->workqueue);
+
+		for (i = 0; i < fdata->mux_channels; i++) {
+			/*
+			 * jnx_i2c_find_adapter acquires a hold on
+			 * the  adapter
+			 */
+			i2c_put_adapter(fdata->mux[i]);
+		}
+	}
+
+	jnx_unregister_local_card();
+	jnx_unregister_chassis();
+
+	jnx_cbd_fpga_sysfs_remove(&pdev->dev);
+
+	return 0;
+}
+
+static struct platform_driver jnx_cbc_driver = {
+	.driver = {
+		.name = "jnx-cbd-fpga",
+		.owner  = THIS_MODULE,
+	},
+	.probe = jnx_cbc_probe,
+	.remove = jnx_cbc_remove,
+};
+module_platform_driver(jnx_cbc_driver);
+
+MODULE_DESCRIPTION("JNX Polaris CBC Driver");
+MODULE_AUTHOR("Georgi Vlaev <gvlaev@juniper.net>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:jnx-cbd-fpga");
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-common.c b/drivers/staging/jnx/jnx-cbd-fpga-common.c
new file mode 100644
index 0000000..718fb37
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-common.c
@@ -0,0 +1,332 @@
+/*
+ * Juniper Generic Control Board (CB) FPGA Driver common code
+ *
+ * Copyright (C) 2012, 2013, 2014, 2015 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/jnx/jnx-subsys.h>
+#include <linux/jnx/jnx-board-core.h>
+#include <linux/jnx/board_ids.h>
+#include <linux/of.h>
+#include <linux/dmi.h>
+#include "jnx-cbd-fpga-common.h"
+#include "jnx-cbd-fpga-core.h"
+#include "jnx-cbd-fpga-platdata.h"
+#include "jnx-subsys-private.h"
+
+/**********************************************************************
+ *
+ * JNX CBD FPGA Board insertion/removal handling
+ *
+ **********************************************************************/
+
+void jnx_cbd_fpga_board_insert(struct jnx_cbd_fpga_data *fdata,
+			       int slot, int chan)
+{
+	int mindex = chan / 64;
+	int mchan = chan % 64;
+
+	if (WARN(chan >= ARRAY_SIZE(fdata->client), "bad chan=%d\n", chan))
+		return;
+	fdata->client[chan] =
+	  jnx_board_inserted(fdata->mux[chan], slot,
+			     fdata->cfinfo->cpld_mux_bitmask[mindex] &
+			       BIT_ULL(mchan));
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_board_insert);
+
+void jnx_cbd_fpga_board_remove(struct jnx_cbd_fpga_data *fdata, int chan)
+{
+	jnx_board_removed(fdata->mux[chan], fdata->client[chan]);
+	fdata->client[chan] = NULL;
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_board_remove);
+
+static void jnx_cbd_fpga_board_remove_all(struct jnx_cbd_fpga_data *fdata)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(fdata->client); i++)
+		jnx_cbd_fpga_board_remove(fdata, i);
+}
+
+/**********************************************************************
+ *
+ * JNX CBD FPGA Mastership attribute support
+ *
+ ***********************************************************************/
+#define JNX_MSTR_CFG_REG		0x003C
+#define JNX_MSTR_ALIVE_REG		0x0040
+#define JNX_MSTR_ALIVE_CNT_REG		0x0044
+
+#define JNX_MSTR_BOOTED			BIT(0)
+#define JNX_MSTR_RELINQUISH		BIT(1)
+#define JNX_MSTR_IM_RDY			BIT(3)
+#define JNX_MSTR_IM_MASTER		BIT(4)
+#define JNX_MSTR_HE_RDY			BIT(5)
+#define JNX_MSTR_HE_MASTER		BIT(6)
+
+#define JNX_MSTR	(JNX_MSTR_BOOTED | JNX_MSTR_IM_RDY | JNX_MSTR_IM_MASTER)
+#define JNX_ANY_MSTR	(JNX_MSTR_IM_MASTER | JNX_MSTR_HE_MASTER)
+#define JNX_HE_MSTR	(JNX_MSTR_HE_RDY | JNX_MSTR_HE_MASTER)
+
+#define MSTR_ALIVE_CNT			0x01FF
+#define JNX_MSTR_ALIVE			0x0001
+
+#define JNX_MSTR_REFRESH_TIMEOUT	21
+#define JNX_MAX_MSTR_ALIVE_CNT		0xFF
+
+static bool jnx_cbd_fpga_am_master(struct jnx_cbd_fpga_data *fdata)
+{
+	u32 mstr_cfg = ioread32(fdata->region0 + JNX_MSTR_CFG_REG);
+
+	return (mstr_cfg & JNX_MSTR) == JNX_MSTR;
+}
+
+static bool jnx_cbd_fpga_he_master(struct jnx_cbd_fpga_data *fdata)
+{
+	u32 mstr_cfg = ioread32(fdata->region0 + JNX_MSTR_CFG_REG);
+
+	return (mstr_cfg & JNX_HE_MSTR) == JNX_HE_MSTR;
+}
+
+static bool jnx_cbd_fpga_become_master(struct jnx_cbd_fpga_data *fdata)
+{
+	bool am_master;
+	int  i;
+
+	iowrite32(JNX_MSTR_REFRESH_TIMEOUT | (1 << 8),
+		  fdata->region0 + JNX_MSTR_ALIVE_CNT_REG);
+	iowrite32(1, fdata->region0 + JNX_MSTR_ALIVE_REG);
+	iowrite32(JNX_MSTR_BOOTED, fdata->region0 + JNX_MSTR_CFG_REG);
+
+	for (i = 0; i < 20; i++) {
+		am_master = jnx_cbd_fpga_am_master(fdata);
+		if (am_master)
+			break;
+		usleep_range(10, 20);
+	}
+
+	if (am_master && !fdata->dt_enabled)	/* FIXME dt_enabled is odd */
+		queue_delayed_work(fdata->workqueue, &fdata->work, 0);
+
+	return am_master;
+}
+
+static void jnx_cbd_fpga_relinquish_master(struct jnx_cbd_fpga_data *fdata)
+{
+	u32 mstr_cfg;
+	int i;
+
+	cancel_delayed_work_sync(&fdata->work);
+
+	jnx_cbd_fpga_board_remove_all(fdata);
+
+	mstr_cfg = ioread32(fdata->region0 + JNX_MSTR_CFG_REG);
+	mstr_cfg &= ~JNX_MSTR_BOOTED;
+	iowrite32(mstr_cfg, fdata->region0 + JNX_MSTR_CFG_REG);
+
+	for (i = 0; i < ARRAY_SIZE(fdata->prev_ch_prs); i++)
+		fdata->prev_ch_prs[i] = 0;
+}
+
+int jnx_cbd_fpga_get_master(void *data)
+{
+	struct jnx_cbd_fpga_data *fdata = data;
+
+	if (jnx_cbd_fpga_am_master(fdata))
+		return fdata->slot;
+
+	if (jnx_cbd_fpga_he_master(fdata))
+		return fdata->slot ^ 1;
+
+	return -1;
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_get_master);
+
+bool jnx_cbd_fpga_mastership_get(void *data)
+{
+	struct jnx_cbd_fpga_data *fdata = data;
+
+	return jnx_cbd_fpga_am_master(fdata);
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_mastership_get);
+
+void jnx_cbd_fpga_mastership_set(void *data, bool master)
+{
+	struct jnx_cbd_fpga_data *fdata = data;
+
+	mutex_lock(&fdata->lock);
+	if (jnx_cbd_fpga_am_master(fdata)) {
+		if (!master)
+			jnx_cbd_fpga_relinquish_master(fdata);
+	} else {
+		if (master)
+			jnx_cbd_fpga_become_master(fdata);
+	}
+	mutex_unlock(&fdata->lock);
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_mastership_set);
+
+void jnx_cbd_fpga_mastership_ping(void *data)
+{
+	struct jnx_cbd_fpga_data *fdata = data;
+
+	iowrite32(1, fdata->region0 + JNX_MSTR_ALIVE_REG);
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_mastership_ping);
+
+int jnx_cbd_fpga_mastership_count_get(void *data)
+{
+	struct jnx_cbd_fpga_data *fdata = data;
+
+	return ioread32(fdata->region0 + JNX_MSTR_ALIVE_CNT_REG) &
+		JNX_MAX_MSTR_ALIVE_CNT;
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_mastership_count_get);
+
+int jnx_cbd_fpga_mastership_count_set(void *data, int val)
+{
+	struct jnx_cbd_fpga_data *fdata = data;
+
+	if (val < 0 || val > JNX_MAX_MSTR_ALIVE_CNT)
+		return -EINVAL;
+
+	iowrite32(val | (1 << 8), fdata->region0 + JNX_MSTR_ALIVE_CNT_REG);
+
+	return 0;
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_mastership_count_set);
+
+#define FPGA_MEM_SIZE		0x1000
+
+static struct jnx_cbd_fpga_data *cbd_sysfs_get_dev_info(struct kobject *kobj)
+{
+	struct platform_device *pdev =
+		to_platform_device(container_of(kobj, struct device, kobj));
+
+	return platform_get_drvdata(pdev);
+}
+
+static int cbd_fpga_validate_access_parameters(loff_t offset, size_t count)
+{
+	/*
+	 * Need to make sure the count is a multiple of JNX_CBD_FPGA_REG_SIZE
+	 * bytes and that the offset plus the number of registers being read
+	 * is less than the size of the fpga.
+	 */
+	if ((count % JNX_CBD_FPGA_REG_SIZE) == 0)
+		if ((offset + (count / JNX_CBD_FPGA_REG_SIZE)) < FPGA_MEM_SIZE)
+			return 0;
+
+	return -EINVAL;
+}
+
+static ssize_t cbd_fpga_read(struct file *filp, struct kobject *kobj,
+			     struct bin_attribute *bin_attr,
+			     char *buffer, loff_t offset, size_t count)
+{
+	struct jnx_cbd_fpga_data *cfpga = cbd_sysfs_get_dev_info(kobj);
+	void __iomem *addr;
+	ssize_t err;
+	int nregs;
+	u32 *buf;
+	int i;
+
+	err = cbd_fpga_validate_access_parameters(offset, count);
+	if (err)
+		return err;
+
+	nregs = count / JNX_CBD_FPGA_REG_SIZE;
+	buf = (u32 *)buffer;
+	addr = cfpga->region0 + offset;
+
+	for (i = 0; i < nregs; i++)
+		buf[i] = ioread32(addr + i * JNX_CBD_FPGA_REG_SIZE);
+
+	return count;
+}
+
+static ssize_t cbd_fpga_write(struct file *filp, struct kobject *kobj,
+			      struct bin_attribute *bin_attr,
+			      char *buffer, loff_t offset, size_t count)
+{
+	struct jnx_cbd_fpga_data *cfpga = cbd_sysfs_get_dev_info(kobj);
+	void __iomem *addr;
+	ssize_t err;
+	int nregs;
+	u32 *buf;
+	int i;
+
+	err = cbd_fpga_validate_access_parameters(offset, count);
+	if (err)
+		return err;
+
+	nregs = count / JNX_CBD_FPGA_REG_SIZE;
+	buf = (u32 *)buffer;
+	addr = cfpga->region0 + offset;
+
+	for (i = 0; i < nregs; i++)
+		iowrite32(buf[i], addr + i * JNX_CBD_FPGA_REG_SIZE);
+
+	return count;
+}
+
+static struct bin_attribute cbd_fpga_attr = {
+	.attr = {
+		.name = "fpga-data",
+		.mode = S_IRUGO | S_IWUSR,
+	},
+
+	.size = FPGA_MEM_SIZE,
+	.read = cbd_fpga_read,
+	.write = cbd_fpga_write,
+};
+
+int jnx_cbd_fpga_sysfs_init(struct device *dev)
+{
+	int err;
+
+	err = sysfs_create_bin_file(&dev->kobj, &cbd_fpga_attr);
+	if (err)
+		return err;
+
+	err = sysfs_create_link(&dev->parent->parent->parent->kobj,
+				&dev->kobj, "cbfpga");
+	if (err)
+		goto err_bin_file;
+	return 0;
+
+err_bin_file:
+	sysfs_remove_bin_file(&dev->kobj, &cbd_fpga_attr);
+	return err;
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_sysfs_init);
+
+void jnx_cbd_fpga_sysfs_remove(struct device *dev)
+{
+	sysfs_remove_link(&dev->parent->parent->parent->kobj,
+			  "cbfpga");
+	sysfs_remove_bin_file(&dev->kobj, &cbd_fpga_attr);
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_sysfs_remove);
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-common.h b/drivers/staging/jnx/jnx-cbd-fpga-common.h
new file mode 100644
index 0000000..9bf4949
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-common.h
@@ -0,0 +1,27 @@
+/*
+ * jnx-cbd-fpga-common.h
+ *
+ * Copyright (C) 2015 Juniper Networks. All rights reserved.
+ *
+ */
+
+#ifndef JNX_CBD_FPGA_COMMON_H
+#define JNX_CBD_FPGA_COMMON_H
+
+struct jnx_cbd_fpga_data;
+
+void jnx_cbd_fpga_board_insert(struct jnx_cbd_fpga_data *fdata,
+			       int slot, int chan);
+void jnx_cbd_fpga_board_remove(struct jnx_cbd_fpga_data *fdata, int chan);
+
+int jnx_cbd_fpga_get_master(void *data);
+bool jnx_cbd_fpga_mastership_get(void *data);
+void jnx_cbd_fpga_mastership_set(void *data, bool master);
+void jnx_cbd_fpga_mastership_ping(void *data);
+int jnx_cbd_fpga_mastership_count_get(void *data);
+int jnx_cbd_fpga_mastership_count_set(void *data, int val);
+
+int jnx_cbd_fpga_sysfs_init(struct device *dev);
+void jnx_cbd_fpga_sysfs_remove(struct device *dev);
+
+#endif /* JNX_CBD_FPGA_COMMON_H */
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-core.c b/drivers/staging/jnx/jnx-cbd-fpga-core.c
new file mode 100644
index 0000000..b665f46
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-core.c
@@ -0,0 +1,540 @@
+/*
+ * Juniper Generic Control Board (CB) FPGA Driver
+ *
+ * Copyright (C) 2012, 2013, 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/i2c-mux.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+#include <linux/workqueue.h>
+#include <linux/jnx/jnx-subsys.h>
+#include <linux/jnx/jnx-board-core.h>
+#include <linux/jnx/board_ids.h>
+#include <linux/jnx/pci_ids.h>
+#include <linux/platform_data/i2c-pca-jnx.h>
+#include <linux/of.h>
+#include "jnx-cbd-fpga-core.h"
+#include "jnx-cbd-fpga-common.h"
+#include "jnx-cbd-fpga-platdata.h"
+#include "jnx-subsys-private.h"
+
+#define DRIVER_VERSION  "0.01.0"
+#define DRIVER_DESC     "JNX CB FPGA Driver"
+
+#define JNX_CBD_FPGA_MODULE_NAME	"jnx-cbd-fpga"
+
+static const struct pci_device_id jnx_cbd_fpga_id_table[] = {
+	{PCI_VDEVICE(JUNIPER, JNX_CBD_FPGA_DID_09B3), JNX_CBD_FPGA_PTX5K},
+	{PCI_VDEVICE(JUNIPER, JNX_CBD_FPGA_DID_0BA8), JNX_CBD_FPGA_PTX3K},
+	{0,}
+};
+MODULE_DEVICE_TABLE(pci, jnx_cbd_fpga_id_table);
+
+static const struct jnx_cbd_fpga_info *jnx_cbd_fpga_info_tbl[] = {
+	[JNX_CBD_FPGA_PTX5K] = &jnx_cbd_fpga_ptx5k_info,
+	[JNX_CBD_FPGA_PTX3K] = &jnx_cbd_fpga_ptx3k_info,
+};
+
+/**********************************************************************
+ *
+ * JNX CBD FPGA I2C controller APIs
+ *
+ **********************************************************************/
+#define JNX_CBD_FPGA_WAIT_FOR_IDLE	10000
+
+#define JNX_CBD_FPGA_I2C_CTRL_READ	BIT(0)
+#define JNX_CBD_FPGA_I2C_CTRL_WRITE	0
+#define JNX_CBD_FPGA_I2C_CTRL_START	BIT(1)
+#define JNX_CBD_FPGA_I2C_CTRL_SEL_I2C	BIT(2)
+
+static int jnx_cbd_fpga_i2c_wait_bus_inactive(struct jnx_cbd_fpga_data *fdata)
+{
+	int i = 0;
+
+	while (ioread32(fdata->ctrl_reg) & JNX_CBD_FPGA_I2C_CTRL_START &&
+	       i++ < JNX_CBD_FPGA_WAIT_FOR_IDLE) {
+		udelay(1);
+	}
+
+	if (i >= JNX_CBD_FPGA_WAIT_FOR_IDLE)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static void jnx_cbd_fpga_iowrite32_sync(u32 val, void __iomem *reg)
+{
+	iowrite32(val, reg);
+	ioread32(reg);
+}
+
+static void jnx_cbd_fpga_i2c_writebyte(struct device *dev, int reg, int value)
+{
+	struct jnx_cbd_fpga_data *fdata = dev_get_drvdata(dev);
+	int rc;
+
+	rc = jnx_cbd_fpga_i2c_wait_bus_inactive(fdata);
+	if (rc < 0)
+		return;
+
+	iowrite32(value, fdata->data_reg);
+
+	iowrite32(reg, fdata->addr_reg);
+
+	jnx_cbd_fpga_iowrite32_sync(JNX_CBD_FPGA_I2C_CTRL_SEL_I2C |
+				    JNX_CBD_FPGA_I2C_CTRL_WRITE |
+				    JNX_CBD_FPGA_I2C_CTRL_START,
+				    fdata->ctrl_reg);
+}
+
+static int jnx_cbd_fpga_i2c_readbyte(struct device *dev, int reg)
+{
+	struct jnx_cbd_fpga_data *fdata = dev_get_drvdata(dev);
+	int rc;
+
+	rc = jnx_cbd_fpga_i2c_wait_bus_inactive(fdata);
+	if (rc)
+		return 0;
+
+	iowrite32(reg, fdata->addr_reg);
+
+	iowrite32(JNX_CBD_FPGA_I2C_CTRL_SEL_I2C | JNX_CBD_FPGA_I2C_CTRL_READ |
+		  JNX_CBD_FPGA_I2C_CTRL_START, fdata->ctrl_reg);
+
+	rc = jnx_cbd_fpga_i2c_wait_bus_inactive(fdata);
+	if (rc)
+		return 0;
+
+	return (u8)ioread32(fdata->data_reg);
+}
+
+/**********************************************************************
+ *
+ * End JNX CBD FPGA I2C controller APIs
+ *
+ **********************************************************************/
+
+/**********************************************************************
+ *
+ * JNX CBD FPGA I2C Selector Mux APIs
+ *
+ **********************************************************************/
+
+static int cbd_i2c_select_chan(struct i2c_mux_core *muxc, u32 chan)
+{
+	struct jnx_cbd_fpga_data *fdata = i2c_mux_priv(muxc);
+	u32 select;
+
+	select = ioread32(fdata->select_reg);
+	if (select != chan)
+		jnx_cbd_fpga_iowrite32_sync(chan, fdata->select_reg);
+
+	return 0;
+}
+
+static int cbd_i2c_deselect_chan(struct i2c_mux_core *muxc, u32 chan)
+{
+	struct jnx_cbd_fpga_data *fdata = i2c_mux_priv(muxc);
+
+	return cbd_i2c_select_chan(muxc, fdata->default_chan);
+}
+
+/**********************************************************************
+ * JNX CBD FPGA I2C Selector Mux APIs
+ **********************************************************************/
+
+void jnx_cbd_fpga_ch_prs_handler(struct work_struct *work)
+{
+	struct jnx_cbd_fpga_data *fdata = container_of(to_delayed_work(work),
+						       struct jnx_cbd_fpga_data,
+						       work);
+	const struct jnx_cbd_fpga_info *cfinfo;
+	unsigned long ch_prs, prs_change;
+	int slot, chan;
+	int i, bit;
+
+	cfinfo = fdata->cfinfo;
+
+	mutex_lock(&fdata->lock);
+
+	for (i = 0; i < ARRAY_SIZE(cfinfo->cpld_prs_reg); i++) {
+		if (!cfinfo->cpld_prs_reg[i])
+			continue;
+
+		ch_prs = ioread32(fdata->region0 + cfinfo->cpld_prs_reg[i]);
+		ch_prs |= cfinfo->cpld_prs_set_bitmask[i];
+		if (cfinfo->cpld_prs_bitmask[i])
+			ch_prs &= cfinfo->cpld_prs_bitmask[i];
+		prs_change = ch_prs ^ fdata->prev_ch_prs[i];
+		fdata->prev_ch_prs[i] = ch_prs;
+
+		for_each_set_bit(bit, &prs_change, sizeof(u32) * 8) {
+			chan = cfinfo->get_channel(bit + i * 32);
+			if (chan < 0)
+				continue;
+
+			if (!fdata->mux[chan] && fdata->find_mux_adapter)
+				fdata->mux[chan] =
+					fdata->find_mux_adapter(chan);
+
+			if (!fdata->mux[chan])
+				continue;
+
+			slot = cfinfo->get_slot_from_chan(chan);
+			if (slot < 0)
+				continue;
+
+			pr_info("CBC: presence bit %d -> chan: %d, adap %p\n",
+				bit + i * 32, chan, fdata->mux[chan]);
+
+			if (test_bit(bit, &ch_prs))
+				jnx_cbd_fpga_board_insert(fdata, slot, chan);
+			else
+				jnx_cbd_fpga_board_remove(fdata, chan);
+		}
+	}
+
+	mutex_unlock(&fdata->lock);
+
+	queue_delayed_work(fdata->workqueue, &fdata->work, 10 * HZ);
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_ch_prs_handler);
+
+/**********************************************************************
+ * End JNX CBD FPGA Board insertion/removal handling
+ **********************************************************************/
+
+static void jnx_cbd_fpga_device_reset(void __iomem *reg_addr, u32 mask)
+{
+	u32 device_ctrl = 0;
+
+	device_ctrl = ioread32(reg_addr);
+	device_ctrl |= mask;
+	iowrite32(device_ctrl, reg_addr);
+
+	usleep_range(100, 500);	/* was udelay(100) */
+
+	device_ctrl = ioread32(reg_addr);
+	device_ctrl &= ~mask;
+	iowrite32(device_ctrl, reg_addr);
+}
+
+static int jnx_cbd_fpga_ptx5k_get_master(void *data)
+{
+	struct jnx_cbd_fpga_data *fdata = data;
+	int slot;
+	u32 rev;
+
+	slot = jnx_cbd_fpga_get_master(data);
+	if (slot < 0) {
+		rev = ioread32(fdata->region0 + JNX_CBD_FPGA_REV_REG);
+		if (rev < JNX_CBD_FPGA_REV_GOOD) {
+			WARN_ONCE(1,
+				  "FPGA ver. 0x%x can't support master detect\n",
+				  rev);
+			/*
+			 * We can not determine who is master. It is not us,
+			 * so assume that the other RE is master.
+			 */
+			slot = fdata->slot ^ 1;
+		}
+	}
+
+	return slot;
+}
+
+/*
+ * Check if we're on OF enabled setup. If so, we'll assume that board
+ * insertion/removal is handled by the jnx-connector driver + overlays
+ * and not the jnx-* platform drivers. This allows a temporary workaround
+ * until all the ptx-x86 code is OF capable.
+ */
+static int jnx_connector_present(void)
+{
+	return of_have_populated_dt();
+}
+
+/*
+ * jnx_of_create_device()
+ *	Hack - create the presence driver and associate with dt node
+ *	This vaguely simulates what the mfd_core does.
+ */
+static char * const of_presence_dev_names[] = {
+	"jnx,gpio-cbc-presence",
+	"jnx,gpio-cbc-presence-other",
+};
+
+static struct platform_device *jnx_of_create_device(struct pci_dev *pcidev,
+						    int id, const char *name,
+						    const char *of_compatible)
+{
+	struct device *parent = &pcidev->dev;
+	struct platform_device *pdev = NULL;
+	struct device_node *np = NULL;
+	struct resource res;
+
+	if (!parent->of_node || !of_compatible)
+		return NULL;
+
+	memset(&res, 0, sizeof(res));
+	res.start = pci_resource_start(pcidev, 0);
+	res.end = pci_resource_end(pcidev, 0);
+	res.flags = IORESOURCE_MEM;
+	res.parent = &pcidev->resource[0];
+
+	pdev = platform_device_register_resndata(parent, name, id,
+						 &res, 1, NULL, 0);
+
+	if (IS_ERR(pdev))
+		return NULL;
+
+	for_each_child_of_node(parent->of_node, np) {
+		if (of_device_is_compatible(np, of_compatible)) {
+			pdev->dev.of_node = np;
+			return pdev;
+		}
+	}
+
+	dev_err(parent, "Failed to create %s device\n", of_compatible);
+	/* of_compatible node missing */
+	platform_device_put(pdev);
+
+	return NULL;
+}
+
+static int
+jnx_cbd_fpga_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	const struct jnx_cbd_fpga_info *cfinfo =
+		jnx_cbd_fpga_info_tbl[id->driver_data];
+	struct jnx_i2c_pca_platform_data idata;
+	struct device *dev = &pdev->dev;
+	struct jnx_cbd_fpga_data *fdata;
+	struct jnx_chassis_info chinfo;
+	struct jnx_card_info cinfo;
+	void __iomem *reset_reg;
+	u32 *bitmasks;
+	u32 ch_prs;
+	int err;
+	int i;
+
+	fdata = devm_kzalloc(dev, sizeof(*fdata), GFP_KERNEL);
+	if (!fdata)
+		return -ENOMEM;
+
+	dev_dbg(dev, "%s.%s(): irq pin %d\n", JNX_CBD_FPGA_MODULE_NAME,
+		__func__, pdev->irq);
+
+	err = pcim_enable_device(pdev);
+	if (err)
+		return err;
+
+	err = pcim_iomap_regions(pdev, 1 << 0, JNX_CBD_FPGA_MODULE_NAME);
+	if (err)
+		return err;
+
+	/*
+	 * Need to remap the io regions for the slow bus registers and
+	 * the i2c select register.  These are needed for the associated
+	 * i2c adapter and mux drivers.
+	 */
+	fdata->region0 = pcim_iomap_table(pdev)[0];
+
+	fdata->cfinfo = cfinfo;
+	fdata->default_chan = cfinfo->i2cmux_default_chan;
+	fdata->mux_channels = cfinfo->mux_channels;
+	if (fdata->mux_channels > JNX_CBD_FPGA_NUM_MUXES)
+		fdata->mux_channels = JNX_CBD_FPGA_NUM_MUXES;
+
+	mutex_init(&fdata->lock);
+
+	/*
+	 * Set up the slow bus address registers
+	 */
+	fdata->addr_reg = fdata->region0 + JNX_CBD_FPGA_SLOW_BUS_REG;
+	fdata->data_reg = fdata->addr_reg + JNX_CBD_FPGA_REG_SIZE;
+	fdata->ctrl_reg = fdata->data_reg + JNX_CBD_FPGA_REG_SIZE;
+
+	fdata->select_reg = fdata->region0 + JNX_CBD_FPGA_I2C_SELECT_REG;
+
+	pci_set_drvdata(pdev, fdata);
+
+	err = jnx_cbd_fpga_sysfs_init(dev);
+	if (err)
+		return err;
+
+	/*
+	 * Run any devices resets that are required before instantiating
+	 * any other devices.
+	 */
+	reset_reg = fdata->region0 + cfinfo->reset->reg_offset;
+	bitmasks = cfinfo->reset->bitmasks;
+	i = 0;
+
+	while (bitmasks[i])
+		jnx_cbd_fpga_device_reset(reset_reg, bitmasks[i++]);
+
+	/*
+	 * Register the chassis with the system so userspace is able to
+	 * determine platform type, etc.
+	 */
+	chinfo.platform = cfinfo->platform;
+	chinfo.chassis_no = 0;
+	chinfo.multichassis = 0;
+	chinfo.master_data = fdata;
+	chinfo.get_master = jnx_cbd_fpga_ptx5k_get_master;
+	chinfo.mastership_get = jnx_cbd_fpga_mastership_get;
+	chinfo.mastership_set = jnx_cbd_fpga_mastership_set;
+	chinfo.mastership_ping = jnx_cbd_fpga_mastership_ping;
+	chinfo.mastership_count_get = jnx_cbd_fpga_mastership_count_get;
+	chinfo.mastership_count_set = jnx_cbd_fpga_mastership_count_set;
+
+	jnx_register_chassis(&chinfo);
+
+	/*
+	 * Register the local card with the system so userspace is able to
+	 * determine board type etc.
+	 */
+	ch_prs = ioread32(fdata->region0 + JNX_CBD_FPGA_CH_PRS_REG);
+
+	fdata->slot = (ch_prs & cfinfo->ch_prs_cb0_bit) ? 0 : 1;
+
+	cinfo.assembly_id = cfinfo->assembly_id;
+	cinfo.slot = fdata->slot;
+	cinfo.type = JNX_BOARD_TYPE_RE;
+	cinfo.adap = NULL;
+
+	jnx_register_local_card(&cinfo);
+
+	/*
+	 * Set up and create the i2c controller
+	 */
+	idata.dev = dev;
+	idata.adap = &fdata->adap;
+	idata.clock = 400000;
+	idata.read_byte = jnx_cbd_fpga_i2c_readbyte;
+	idata.write_byte = jnx_cbd_fpga_i2c_writebyte;
+
+	fdata->i2c_ctlr = platform_device_alloc(cfinfo->i2c_ctlr_name, -1);
+	if (!fdata->i2c_ctlr) {
+		err = -ENOMEM;
+		goto err_unregister;
+	}
+
+	err = platform_device_add_data(fdata->i2c_ctlr, &idata, sizeof(idata));
+	if (err)
+		goto err_pdev;
+
+	err = platform_device_add(fdata->i2c_ctlr);
+	if (err)
+		goto err_pdev;
+
+	/* Now create i2c muxes on top of the i2c adapter */
+	fdata->muxc = i2c_mux_alloc(&fdata->adap, &pdev->dev,
+				    fdata->mux_channels, 0, 0,
+				    cbd_i2c_select_chan,
+				    cbd_i2c_deselect_chan);
+	if (!fdata->muxc) {
+		err = -ENOMEM;
+		goto err_pdev;
+	}
+	fdata->muxc->priv = fdata;
+
+	for (i = 0; i < fdata->mux_channels; i++) {
+		err = i2c_mux_add_adapter(fdata->muxc, 0, i, 0);
+		if (err)
+			goto err_mux;
+	}
+
+	/* The jnx-connector & gpio-cbc-presence will handle the cards */
+	fdata->dt_enabled = jnx_connector_present();
+	if (fdata->dt_enabled) {
+		dev_info(dev, "line cards will handled by the jnx-connector\n");
+		for (i = 0; i < ARRAY_SIZE(fdata->presence_devices); i++) {
+			fdata->presence_devices[i] =
+				jnx_of_create_device(pdev, i,
+						     "gpio-cbc-presence",
+						     of_presence_dev_names[i]);
+			if (!fdata->presence_devices[i])
+				dev_err(dev, "Failed to create %s device\n",
+					of_presence_dev_names[i]);
+		}
+		return 0;
+	}
+
+	fdata->workqueue = create_singlethread_workqueue("jnx-cbd-poller");
+	if (!fdata->workqueue) {
+		err = -ENOMEM;
+		goto err_mux;
+	}
+
+	INIT_DELAYED_WORK(&fdata->work, jnx_cbd_fpga_ch_prs_handler);
+
+	return 0;
+
+err_mux:
+	i2c_mux_del_adapters(fdata->muxc);
+
+err_pdev:
+	platform_device_put(fdata->i2c_ctlr);
+
+err_unregister:
+	jnx_unregister_chassis();
+	jnx_cbd_fpga_sysfs_remove(dev);
+
+	return err;
+}
+
+static void jnx_cbd_fpga_remove(struct pci_dev *pdev)
+{
+	struct jnx_cbd_fpga_data *fdata = pci_get_drvdata(pdev);
+	int i;
+
+	if (!fdata->dt_enabled) {
+		cancel_delayed_work_sync(&fdata->work);
+		flush_workqueue(fdata->workqueue);
+		destroy_workqueue(fdata->workqueue);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(fdata->presence_devices); i++) {
+		if (fdata->presence_devices[i])
+			platform_device_put(fdata->presence_devices[i]);
+	}
+
+	i2c_mux_del_adapters(fdata->muxc);
+	platform_device_put(fdata->i2c_ctlr);
+	jnx_unregister_local_card();
+	jnx_unregister_chassis();
+	jnx_cbd_fpga_sysfs_remove(&pdev->dev);
+}
+
+static struct pci_driver jnx_cbd_fpga_driver = {
+	.name = JNX_CBD_FPGA_MODULE_NAME,
+	.id_table = jnx_cbd_fpga_id_table,
+	.probe = jnx_cbd_fpga_probe,
+	.remove = jnx_cbd_fpga_remove,
+};
+
+module_pci_driver(jnx_cbd_fpga_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-core.h b/drivers/staging/jnx/jnx-cbd-fpga-core.h
new file mode 100644
index 0000000..ac7a4cf
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-core.h
@@ -0,0 +1,68 @@
+/*
+ * Juniper Generic Control Board (CB) FPGA Driver
+ *
+ * Copyright (C) 2012, 2013, 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _JNX_CBD_FPGA_CORE_H
+#define _JNX_CBD_FPGA_CORE_H
+
+#include <linux/bitops.h>
+
+#define JNX_CBD_FPGA_REG_SIZE		4
+
+#define JNX_CBD_FPGA_INTR_SRC		0x0000
+#define JNX_CBD_FPGA_INTR_EN		0x0004
+#define JNX_CBD_FPGA_I2C_SELECT_REG	0x0010
+#define JNX_CBD_FPGA_CH_PRS_REG		0x0014
+#define JNX_CBD_FPGA_SLOW_BUS_REG	0x0054
+#define JNX_CBD_FPGA_REV_REG		0x0060
+#define JNX_CBD_FPGA_DUMMY_READ_REG	0x0064
+#define JNX_CBD_FPGA_CH_OTHER_PRS_REG	0x00D4
+
+#define JNX_CBD_FPGA_REV_GOOD		0x011F
+
+#define JNX_CBD_FPGA_NUM_MUXES		96
+/*
+ * JNX CBD CH Presence definitions
+ */
+#define JNX_CBD_FPGA_CH_PRS_CB0_PIN	BIT(27)
+
+struct jnx_cbd_fpga_data {
+	u32 slot;
+	const struct jnx_cbd_fpga_info *cfinfo;
+	u32 default_chan;
+	u32 prev_ch_prs[4];
+	struct i2c_adapter * (*find_mux_adapter)(int chan);
+	int mux_channels;
+	struct i2c_adapter *mux[JNX_CBD_FPGA_NUM_MUXES];
+	struct i2c_client *client[JNX_CBD_FPGA_NUM_MUXES];
+	struct i2c_mux_core *muxc;
+	bool standalone;
+	int irq;
+	struct i2c_adapter adap;
+	struct mutex lock;			/* safe access lock */
+	struct platform_device *i2c_ctlr;
+	struct workqueue_struct *workqueue;
+	struct delayed_work work;
+	int dt_enabled;
+	struct platform_device *presence_devices[2];
+	void __iomem *region0;
+	void __iomem *addr_reg;
+	void __iomem *data_reg;
+	void __iomem *ctrl_reg;
+	void __iomem *select_reg;
+};
+
+void jnx_cbd_fpga_ch_prs_handler(struct work_struct *work);
+
+#endif /* _JNX_CBD_FPGA_CORE_H */
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-platdata.h b/drivers/staging/jnx/jnx-cbd-fpga-platdata.h
new file mode 100644
index 0000000..a91e38d
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-platdata.h
@@ -0,0 +1,51 @@
+/*
+ * Juniper CBD FPGA Platform Data
+ *
+ * Copyright (C) 2012, 2013, 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __JNX_CBD_FPGA_H
+#define __JNX_CBD_FPGA_H
+
+enum jnx_cbd_fpga_boards {
+	JNX_CBD_FPGA_PTX5K,
+	JNX_CBD_FPGA_PTX3K,
+};
+
+struct jnx_cbd_fpga_dev_reset_data {
+	u16 reg_offset;
+	u32 *bitmasks;
+};
+
+struct jnx_cbd_fpga_info {
+	u32 platform;
+	u32 assembly_id;
+	u64 cpld_mux_bitmask[2];
+	u32 cpld_prs_reg[4];
+	u32 cpld_prs_set_bitmask[4];
+	u32 cpld_prs_bitmask[4];
+	u32 ch_prs_cb0_bit;
+	u32 mux_channels;
+	u32 i2cmux_default_chan;
+	const char *i2c_ctlr_name;
+	struct jnx_cbd_fpga_dev_reset_data *reset;
+	int (*get_channel)(u32 bit);
+	int (*get_slot_from_chan)(int chan);
+};
+
+extern struct jnx_cbd_fpga_info jnx_cbd_fpga_ptx5k_info,
+				jnx_cbd_fpga_ptx3k_info,
+				jnx_cbd_fpga_ptx1k_info,
+				jnx_cbd_fpga_ptx1k_p2_info,
+				jnx_cbd_fpga_ptx21k_info;
+
+#endif /* __JNX_CBD_FPGA_H */
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c b/drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c
new file mode 100644
index 0000000..3314569
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c
@@ -0,0 +1,134 @@
+/*
+ * Juniper PTX 1000 Control Board (CB) FPGA
+ *
+ * Copyright (C) 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/jnx/jnx-subsys.h>
+#include <linux/jnx/board_ids.h>
+#include "jnx-cbd-fpga-core.h"
+#include "jnx-cbd-fpga-platdata.h"
+#include "jnx-subsys-private.h"
+
+#define JNX_CBD_FPGA_PTX1K_DEVICE_CTRL_REG	0x00C0
+#define JNX_CBD_FPGA_PTX1K_DEFAULT_CHAN		26 /* LOCAL_I2C */
+
+#define JNX_CBD_FPGA_PTX1K_CPLD_MUX		0xFF00 /* FPC[8] 8..15 */
+
+static u32 jnx_cbd_fpga_ptx1k_bitmask[] = { 0 };
+
+/* DEVICE_CTRL is not valid for Polaris */
+static struct jnx_cbd_fpga_dev_reset_data jnx_cbd_fpga_ptx1k_device_reset = {
+	.reg_offset = JNX_CBD_FPGA_PTX1K_DEVICE_CTRL_REG,
+	.bitmasks = jnx_cbd_fpga_ptx1k_bitmask,
+};
+
+/* Table to map from mux channel number to slot */
+
+static s8 ptx1k_mux_slot_map[40] = {
+	-1, -1, -1, -1, -1, -1, -1, -1, /*  0.. 7: Unused + RSVD[2] */
+	0, 1, 2, 3, 4, 5, 6, 7,		/*  8..15: FPC[8] */
+	0, 1, 2, 3, 4, 5,		/* 16..21: SIB[6] */
+	-1, -1, -1,			/* 22..24: Unused */
+	0,				/* 25:	   MP */
+	1,				/* 26:     Local */
+	-1,				/* 27:     Unused */
+	0, 1, 2, 3, 4,			/* 28..32: PSM[5] */
+	-1,				/* 33:     Unused */
+	0,				/* 34:     FT0 */
+	0,				/* 35:     FPD */
+	0, 1,				/* 36..37: CB[2] */
+	-1, -1				/* 38..39: SFPP[2] */
+};
+
+static int jnx_cbd_fpga_ptx1k_get_slot_from_channel(int chan)
+{
+	if (chan < 0 || chan >= ARRAY_SIZE(ptx1k_mux_slot_map) ||
+	    ptx1k_mux_slot_map[chan] < 0)
+		return -EINVAL;
+
+	return ptx1k_mux_slot_map[chan];
+}
+
+/* Table to map from presence bit to mux channel */
+static s8 ptx1k_mux_channel_map[64] = {
+	8, 9, 10, 11, 12, 13, 14, 15,	/* CH_PRS FPC[7:0] */
+	-1, -1, -1, -1, -1, -1, -1, -1,	/* CH_PRS Reserved [15:8] */
+	34,				/* CH_PRS FT[16] */
+	-1, -1, -1, -1,			/* CH_PRS Reserved [20:17] */
+	36, 37,				/* CH_PRS CB[22:21] */
+	35,				/* CH_PRS FPD[23] */
+	-1, -1, -1, -1, -1, -1,		/* CH_PRS Reserved[29:24] */
+	25,				/* CH_PRS MP[30] */
+	-1,				/* CH_PRS Reserved[31]*/
+	28, 29, 30, 31, 32,		/* OTHER_CH_PRS PSM[4:0] */
+	16, 17, 18, 19, 20, 21,		/* OTHER_CH_PRS SIB[10:5] */
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1		/* OTHER_CH_PRS Reserved[31:11] */
+};
+
+static int jnx_cbd_fpga_ptx1k_get_channel(u32 bit)
+{
+	if (bit > 63 || ptx1k_mux_channel_map[bit] < 0)
+		return -EINVAL;
+
+	return ptx1k_mux_channel_map[bit];
+}
+
+struct jnx_cbd_fpga_info jnx_cbd_fpga_ptx1k_info = {
+	.platform = JNX_PRODUCT_POLARIS,
+	.assembly_id = JNX_ID_POLARIS_RCB,
+	.cpld_mux_bitmask = {
+		JNX_CBD_FPGA_PTX1K_CPLD_MUX,
+	},
+	.cpld_prs_set_bitmask = {
+		BIT(30),	/* MP */
+	},
+	.cpld_prs_reg = {
+		JNX_CBD_FPGA_CH_PRS_REG,
+		JNX_CBD_FPGA_CH_OTHER_PRS_REG,
+	},
+	.ch_prs_cb0_bit = JNX_CBD_FPGA_CH_PRS_CB0_PIN,
+	.mux_channels = ARRAY_SIZE(ptx1k_mux_slot_map),
+	.i2cmux_default_chan = JNX_CBD_FPGA_PTX1K_DEFAULT_CHAN,
+	.i2c_ctlr_name = "",
+	.reset = &jnx_cbd_fpga_ptx1k_device_reset,
+	.get_channel = jnx_cbd_fpga_ptx1k_get_channel,
+	.get_slot_from_chan = jnx_cbd_fpga_ptx1k_get_slot_from_channel,
+};
+
+struct jnx_cbd_fpga_info jnx_cbd_fpga_ptx1k_p2_info = {
+	.platform = JNX_PRODUCT_POLARIS,
+	.assembly_id = JNX_ID_POLARIS_RCB_P2,
+	.cpld_mux_bitmask = {
+		JNX_CBD_FPGA_PTX1K_CPLD_MUX,
+	},
+	.cpld_prs_set_bitmask = {
+		BIT(30),	/* MP */
+	},
+	.cpld_prs_reg = {
+		JNX_CBD_FPGA_CH_PRS_REG,
+		JNX_CBD_FPGA_CH_OTHER_PRS_REG,
+	},
+	.ch_prs_cb0_bit = JNX_CBD_FPGA_CH_PRS_CB0_PIN,
+	.mux_channels = ARRAY_SIZE(ptx1k_mux_slot_map),
+	.i2cmux_default_chan = JNX_CBD_FPGA_PTX1K_DEFAULT_CHAN,
+	.i2c_ctlr_name = "",
+	.reset = &jnx_cbd_fpga_ptx1k_device_reset,
+	.get_channel = jnx_cbd_fpga_ptx1k_get_channel,
+	.get_slot_from_chan = jnx_cbd_fpga_ptx1k_get_slot_from_channel,
+};
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c b/drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c
new file mode 100644
index 0000000..d9a4227
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c
@@ -0,0 +1,143 @@
+/*
+ * Juniper PTX 21000 Control Board (CB) FPGA
+ *
+ * Copyright (C) 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/jnx/jnx-subsys.h>
+#include <linux/jnx/board_ids.h>
+#include <linux/bitops.h>
+
+#include "jnx-cbd-fpga-platdata.h"
+#include "jnx-cbd-fpga-core.h"
+#include "jnx-subsys-private.h"
+
+#define JNX_CBD_FPGA_PTX21K_DEVICE_CTRL_REG	0x00C0
+#define JNX_CBD_FPGA_PTX21K_DEFAULT_CHAN	57 /* LOCAL_I2C */
+
+#define JNX_CBD_FPGA_PTX21K_CPLD_MUX		0x00000000
+#define JNX_CBD_FPGA_PTX21K_CPLD_MUX_HI		0x00000000
+
+/* PTX21k has a different bit for the CB slot */
+#define JNX_CBD_FPGA_21K_CH_PRS_CB0_PIN		BIT(30)
+
+#define JNX_CBD_FPGA_21K_CH_PRS_SIB_REG		0x0028
+#define JNX_CBD_FPGA_21K_CH_PRS_SIB_MASK	0x1ff
+
+static u32 jnx_cbd_fpga_ptx21k_bitmask[] = { 0 };
+
+static struct jnx_cbd_fpga_dev_reset_data jnx_cbd_fpga_ptx21k_device_reset = {
+	.reg_offset = JNX_CBD_FPGA_PTX21K_DEVICE_CTRL_REG,
+	.bitmasks = jnx_cbd_fpga_ptx21k_bitmask,
+};
+
+/* Table to map from mux channel number to slot */
+
+static s8 ptx21k_mux_slot_map[72] = {
+	0, 1, 2, 3,			/* 0..3: FPC */
+	4, 5, 6, 7,			/* 4..7: FPC */
+	8, 9, 10, 11,			/* 8..11: FPC */
+	12, 13, 14, 15,			/* 12..15: FPC */
+	16, 17, 18, 19,			/* 16..19: FPC */
+	0, 1, 2, 3,			/* 20..23: PSM */
+	4, 5, 6, 7,			/* 24..27: PSM */
+	8, 9, 10, 11,			/* 28..31: PSM */
+	12, 13, 14, 15,			/* 32..35: PSM */
+	16, 17, 18, 19,			/* 36..39: PSM */
+	20, 21, 22, 23,			/* 40..43: PSM */
+	0, 1, 2, 3, 4, 5,		/* 44..49: FT */
+	0, 1,				/* 50..51: CB */
+	0, 1, 2, 3, 4,			/* 52..56: BP */
+	0,				/* 57:     Local */
+	-1,				/* 58:     QSFP */
+	0,				/* 59:     FPD */
+	0, 1, 2, 3, 4,			/* 60 ..64: SIB */
+	5, 6, 7, 8,			/* 65 ..68: SIB */
+	0,				/* 69:     CB PCIeSW */
+};
+
+static int jnx_cbd_fpga_ptx21k_get_slot_from_channel(int chan)
+{
+	if (chan < 0 || chan >= ARRAY_SIZE(ptx21k_mux_slot_map) ||
+	    ptx21k_mux_slot_map[chan] < 0)
+		return -EINVAL;
+
+	return ptx21k_mux_slot_map[chan];
+}
+
+static s8 ptx21k_mux_channel_map[96] = {
+	0, 1, 2, 3, 4, 5, 6, 7,		/* CH_PRS[7:0]   = FPC[7:0] */
+	8, 9, 10, 11, 12, 13, 14, 15,	/* CH_PRS[15:8]  = FPC[15:8] */
+	16, 17, 18, 19,			/* CH_PRS[19:16] = FPC[19:16] */
+	44, 45, 46, 47,			/* CH_PRS[23:20] = FT[3:0] */
+	50, 51,				/* CH_PRS[25:24] = CB[0:1] */
+	59,				/* CH_PRS[26]    = FPD */
+	-1, -1, -1, -1,			/* CH_PRS[30:27] = Reserved */
+	-1,				/* CH_PRS MP[31] There are 5 MPs*/
+	20, 21, 22, 23, 24, 25, 26, 27,	/* OTHER_CH_PRS[7:0]   = PSM[7:0] */
+	28, 29, 30, 31, 32, 33, 34, 35,	/* OTHER_CH_PRS[15:8]  = PSM[15:8] */
+	36, 37, 38, 39, 40, 41, 42, 43,	/* OTHER_CH_PRS[23:15] = PSM[23:15] */
+	48, 49,				/* OTHER_CH_PRS[25:24] = FT[5:4] */
+	-1, -1, -1, -1, -1, -1,		/* OTHER_CH_PRS[31:26]  QSFP, SFP */
+	60, 61, 62, 63, 64, 65, 66, 67, 68,	/* CH_PRS_SIB[8:0] = SIB[8:0] */
+	-1, -1, -1, -1, -1, -1, -1, -1,	/* CH_PRS_SIB[16:9] = NA */
+	-1, -1, -1, -1, -1, -1, -1, -1,	/* CH_PRS_SIB[17:24] = NA */
+	-1, -1, -1, -1, -1, -1, -1	/* CH_PRS_SIB[25:31] = NA */
+};
+
+static int jnx_cbd_fpga_ptx21k_get_channel(u32 bit)
+{
+	if (bit >= ARRAY_SIZE(ptx21k_mux_channel_map) ||
+	    ptx21k_mux_channel_map[bit] < 0)
+		return -EINVAL;
+
+	return ptx21k_mux_channel_map[bit];
+}
+
+struct jnx_cbd_fpga_info jnx_cbd_fpga_ptx21k_info = {
+	.platform = JNX_PRODUCT_OMEGA,
+	.assembly_id = JNX_ID_OMEGA_RCB,
+	.cpld_mux_bitmask = {
+		JNX_CBD_FPGA_PTX21K_CPLD_MUX,
+		JNX_CBD_FPGA_PTX21K_CPLD_MUX_HI,
+	},
+	.ch_prs_cb0_bit = JNX_CBD_FPGA_21K_CH_PRS_CB0_PIN,
+	.cpld_prs_set_bitmask = {
+		BIT(31),	/* MP */
+	},
+	.cpld_prs_reg = {
+		JNX_CBD_FPGA_CH_PRS_REG,
+		JNX_CBD_FPGA_CH_OTHER_PRS_REG,
+		JNX_CBD_FPGA_21K_CH_PRS_SIB_REG,
+		0
+	},
+	.cpld_prs_bitmask = {
+		0,
+		0,
+		JNX_CBD_FPGA_21K_CH_PRS_SIB_MASK,
+		0
+	},
+	.mux_channels = ARRAY_SIZE(ptx21k_mux_slot_map),
+	.i2cmux_default_chan = JNX_CBD_FPGA_PTX21K_DEFAULT_CHAN,
+	.i2c_ctlr_name = "",
+	.reset = &jnx_cbd_fpga_ptx21k_device_reset,
+	.get_channel = jnx_cbd_fpga_ptx21k_get_channel,
+	.get_slot_from_chan = jnx_cbd_fpga_ptx21k_get_slot_from_channel,
+};
+EXPORT_SYMBOL_GPL(jnx_cbd_fpga_ptx21k_info);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c b/drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c
new file mode 100644
index 0000000..ad575c0
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c
@@ -0,0 +1,111 @@
+/*
+ * Juniper PTX 3000 Control Board (CB) FPGA
+ *
+ * Copyright (C) 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/jnx/jnx-subsys.h>
+#include <linux/jnx/board_ids.h>
+#include <linux/platform_data/i2c-pca-jnx.h>
+#include "jnx-cbd-fpga-core.h"
+#include "jnx-cbd-fpga-platdata.h"
+#include "jnx-subsys-private.h"
+
+#define JNX_CBD_FPGA_PTX3K_DEVICE_CTRL_REG	0x00C0
+#define JNX_CBD_FPGA_PTX3K_I2C_CTRL_RESET	BIT(24)
+#define JNX_CBD_FPGA_PTX3K_DEFAULT_CHAN		0x10
+
+#define JNX_CBD_FPGA_PTX3K_CPLD_MUX		0xFF
+
+static u32 jnx_cbd_fpga_ptx3k_bitmask[] = {
+	JNX_CBD_FPGA_PTX3K_I2C_CTRL_RESET,
+	0
+};
+
+static struct jnx_cbd_fpga_dev_reset_data jnx_cbd_fpga_ptx3k_device_reset = {
+	.reg_offset = JNX_CBD_FPGA_PTX3K_DEVICE_CTRL_REG,
+	.bitmasks = jnx_cbd_fpga_ptx3k_bitmask,
+};
+
+/* Table to map from mux channel number to slot */
+
+static s8 ptx3k_mux_slot_map[42] = {
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,	/* FPC */
+	1,						/* 0x10, local */
+	0, 1,						/* 0x11, CB */
+	-1, -1,
+	0,						/* 0x15, FPD */
+	0,						/* 0x16, MP */
+	0, 1,						/* 0x17, fans */
+	-1, -1, -1,
+	0, 1, 2, 3, 4,					/* 0x1c, PSM */
+	0, 1, 2, 3, 4, 5, 6, 7, 8,			/* 0x21, SIB */
+};
+
+static int jnx_cbd_fpga_ptx3k_get_slot_from_channel(int chan)
+{
+	if (chan < 0 || chan >= ARRAY_SIZE(ptx3k_mux_slot_map) ||
+	    ptx3k_mux_slot_map[chan] < 0)
+		return -EINVAL;
+
+	return ptx3k_mux_slot_map[chan];
+}
+
+/* Table to map from presence bit to mux channel */
+static s8 ptx3k_mux_channel_map[64] = {
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,	/* FPC */
+	0x17, 0x18,						/* fans */
+	-1, -1, -1,
+	0x11, 0x12,						/* CB */
+	0x15,							/* FPD */
+	-1, -1, -1, -1, -1, -1,
+	0x16,							/* MP */
+	-1,
+	0x1c, 0x1d, 0x1e, 0x1f, 0x20,				/* PSM */
+	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,	/* SIB */
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1,
+};
+
+static int jnx_cbd_fpga_ptx3k_get_channel(u32 bit)
+{
+	if (bit > 63 || ptx3k_mux_channel_map[bit] < 0)
+		return -EINVAL;
+
+	return ptx3k_mux_channel_map[bit];
+}
+
+struct jnx_cbd_fpga_info jnx_cbd_fpga_ptx3k_info = {
+	.platform = JNX_PRODUCT_HENDRICKS,
+	.assembly_id = JNX_ID_HENDRICKS_CB,
+	.cpld_mux_bitmask = {
+		JNX_CBD_FPGA_PTX3K_CPLD_MUX,
+	},
+	.cpld_prs_set_bitmask = {
+		BIT(30),
+	},
+	.cpld_prs_reg = {
+		JNX_CBD_FPGA_CH_PRS_REG,
+		JNX_CBD_FPGA_CH_OTHER_PRS_REG,
+	},
+	.ch_prs_cb0_bit = JNX_CBD_FPGA_CH_PRS_CB0_PIN,
+	.mux_channels = 42,
+	.i2cmux_default_chan = JNX_CBD_FPGA_PTX3K_DEFAULT_CHAN,
+	.i2c_ctlr_name = I2C_PCA_JNX_CTLR_NAME,
+	.reset = &jnx_cbd_fpga_ptx3k_device_reset,
+	.get_channel = jnx_cbd_fpga_ptx3k_get_channel,
+	.get_slot_from_chan = jnx_cbd_fpga_ptx3k_get_slot_from_channel,
+};
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c b/drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c
new file mode 100644
index 0000000..11adc8f
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c
@@ -0,0 +1,107 @@
+/*
+ * Juniper PTX 5000 Control Board (CB) FPGA
+ *
+ * Copyright (C) 2012, 2013, 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/jnx/board_ids.h>
+#include <linux/jnx/jnx-subsys.h>
+#include <linux/platform_data/i2c-pca-jnx.h>
+#include "jnx-cbd-fpga-core.h"
+#include "jnx-cbd-fpga-platdata.h"
+#include "jnx-subsys-private.h"
+
+#define JNX_CBD_FPGA_PTX5K_DEVICE_CTRL_REG	0x00C0
+#define JNX_CBD_FPGA_PTX5K_I2C_CTRL_RESET	BIT(24)
+#define JNX_CBD_FPGA_PTX5K_DEFAULT_CHAN		0x10
+
+#define JNX_CBD_FPGA_PTX5K_CPLD_MUX		0xFF
+
+static u32 jnx_cbd_fpga_ptx5k_bitmask[] = {
+	JNX_CBD_FPGA_PTX5K_I2C_CTRL_RESET,
+	0
+};
+
+static struct jnx_cbd_fpga_dev_reset_data jnx_cbd_fpga_ptx5k_device_reset = {
+	.reg_offset = JNX_CBD_FPGA_PTX5K_DEVICE_CTRL_REG,
+	.bitmasks = jnx_cbd_fpga_ptx5k_bitmask,
+};
+
+/* Table to map from mux channel number to slot */
+
+static s8 mux_slot_map[30] = {
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,	/* FPC */
+	1,						/* 0x10, local */
+	0, 1,						/* 0x11, CB */
+	0, 1,						/* 0x13, CCG */
+	0,						/* 0x15, FPD */
+	0,						/* 0x16, MP */
+	0, 1, 2, 3, 4,					/* 0x17, fans */
+	0, 1,						/* 0x1c, PDU */
+};
+
+static int jnx_cbd_fpga_ptx5k_get_slot_from_channel(int chan)
+{
+	if (chan < 0 || chan >= ARRAY_SIZE(mux_slot_map))
+		return -EINVAL;
+
+	return mux_slot_map[chan];
+}
+
+/* Table to map from presence bit to mux channel */
+static s8 mux_channel_map[64] = {
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,	/* FPC */
+	0x17, 0x18, 0x19, 0x1a, 0x1b,				/* fans */
+	0x11, 0x12,						/* CB */
+	0x15,							/* FPD */
+	-1, -1, -1, -1,
+	0x13, 0x14,						/* CCG */
+	0x16,							/* MP */
+	-1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	0x1c, 0x1d,						/* PDU */
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
+static int jnx_cbd_fpga_ptx5k_get_channel(u32 bit)
+{
+	if (bit > 63)
+		return -EINVAL;
+
+	return mux_channel_map[bit];
+}
+
+struct jnx_cbd_fpga_info jnx_cbd_fpga_ptx5k_info = {
+	.platform = JNX_PRODUCT_SANGRIA,
+	.assembly_id = JNX_ID_SNG_CB,
+	.cpld_mux_bitmask = {
+		JNX_CBD_FPGA_PTX5K_CPLD_MUX,
+	},
+	.cpld_prs_set_bitmask = {
+		BIT(30),
+	},
+	.cpld_prs_reg = {
+		JNX_CBD_FPGA_CH_PRS_REG,
+		JNX_CBD_FPGA_CH_OTHER_PRS_REG,
+	},
+	.ch_prs_cb0_bit = JNX_CBD_FPGA_CH_PRS_CB0_PIN,
+	.mux_channels = 30,
+	.i2cmux_default_chan = JNX_CBD_FPGA_PTX5K_DEFAULT_CHAN,
+	.i2c_ctlr_name = I2C_PCA_JNX_CTLR_NAME,
+	.reset = &jnx_cbd_fpga_ptx5k_device_reset,
+	.get_channel = jnx_cbd_fpga_ptx5k_get_channel,
+	.get_slot_from_chan = jnx_cbd_fpga_ptx5k_get_slot_from_channel,
+};
-- 
1.9.1

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

* [PATCH 6/6] staging: jnx: CBD-FPGA infrastructure
@ 2016-10-07 15:20   ` Pantelis Antoniou
  0 siblings, 0 replies; 24+ messages in thread
From: Pantelis Antoniou @ 2016-10-07 15:20 UTC (permalink / raw)
  To: Lee Jones
  Cc: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
	Frank Rowand, Greg Kroah-Hartman, Debjit Ghosh, Georgi Vlaev,
	Guenter Roeck, JawaharBalaji Thirumalaisamy, Mohammad Kamil,
	Pantelis Antoniou, devicetree, linux-kernel, linux-gpio, devel

From: Tom Kavanagh <tkavanagh@juniper.net>

Every Juniper platform contains a CBD (Control Board) FPGA.

While each CBD FPGA is different, a common abstact API makes
handling them common for every platform and the same parts
they have can be factored out.

The supported CBDs are PTX1K, PTX21K, PTX3K & PTX5K.

Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
Signed-off-by: Guenter Roeck <groeck@juniper.net>
Signed-off-by: JawaharBalaji Thirumalaisamy <jawaharb@juniper.net>
Signed-off-by: Mohammad Kamil <mkamil@juniper.net>
Signed-off-by: Tom Kavanagh <tkavanagh@juniper.net>
Signed-off-by: Debjit Ghosh <dghosh@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
 drivers/staging/jnx/Kconfig                 |  34 ++
 drivers/staging/jnx/Makefile                |   5 +
 drivers/staging/jnx/jnx-cbc-ptx1k.c         | 242 +++++++++++++
 drivers/staging/jnx/jnx-cbd-fpga-common.c   | 332 +++++++++++++++++
 drivers/staging/jnx/jnx-cbd-fpga-common.h   |  27 ++
 drivers/staging/jnx/jnx-cbd-fpga-core.c     | 540 ++++++++++++++++++++++++++++
 drivers/staging/jnx/jnx-cbd-fpga-core.h     |  68 ++++
 drivers/staging/jnx/jnx-cbd-fpga-platdata.h |  51 +++
 drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c    | 134 +++++++
 drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c   | 143 ++++++++
 drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c    | 111 ++++++
 drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c    | 107 ++++++
 12 files changed, 1794 insertions(+)
 create mode 100644 drivers/staging/jnx/jnx-cbc-ptx1k.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-common.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-common.h
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-core.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-core.h
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-platdata.h
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c
 create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c

diff --git a/drivers/staging/jnx/Kconfig b/drivers/staging/jnx/Kconfig
index 4c38fc2..cd29276 100644
--- a/drivers/staging/jnx/Kconfig
+++ b/drivers/staging/jnx/Kconfig
@@ -34,6 +34,40 @@ config JNX_CONNECTOR
 	  This driver can also be built as a module.  If so, the module
 	  will be called jnx-connector.
 
+config JNX_CBD_FPGA
+	tristate "Juniper Generic CBD FPGA"
+	select I2C_MUX
+	help
+	  Driver to support the Juniper Control Board (CBD) FPGA.  Provides all
+	  common functionality for device across all supported juniper platforms.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called jnx-cbd-fpga.
+
+config JNX_CBD_FPGA_PTX21K
+	tristate "Juniper PTX21K CBC FPGA"
+	depends on JNX_SYSTEM && MFD_JUNIPER_CBC && JNX_PTX21K_BOARDS
+	depends on JNX_PTX21K_RCB
+	default m
+	help
+	  Driver to support the Juniper Control Board (CBC) FPGA in PTX21K.
+	  Provides hooks for the common fpga handling core for these
+	  particular Juniper Boards.
+
+	  When compiled as a module it is included in jnx-cbd-fpga.
+
+config JNX_CBD_FPGA_PTX1K
+	tristate "Juniper PTX1K CBC FPGA"
+	depends on JNX_SYSTEM && MFD_JUNIPER_CBC && JNX_PTX1K_BOARDS
+	depends on JNX_CBD_FPGA_PTX21K
+	help
+	  Driver to support the Juniper Control Board (CBC) FPGA in PTX1K.
+	  Provides all common functionality for device across platforms and
+	  hooks for the common fpga handling core for these particular
+	  Juniper Boards.
+
+	  When compiled as a module it is included in jnx-cbd-fpga.
+
 endmenu
 
 config JNX_COMMON_PCI
diff --git a/drivers/staging/jnx/Makefile b/drivers/staging/jnx/Makefile
index c89e701..b937896 100644
--- a/drivers/staging/jnx/Makefile
+++ b/drivers/staging/jnx/Makefile
@@ -7,3 +7,8 @@ obj-$(CONFIG_JNX_CHIP_PCI_QUIRKS)+= jnx-chip-pci-quirks.o
 obj-$(CONFIG_JNX_COMMON_PCI)	+= jnx_common_pci.o
 obj-$(CONFIG_JNX_PEX8XXX_I2C)	+= pex8xxx_i2c.o
 obj-$(CONFIG_JNX_CONNECTOR)	+= jnx-connector.o
+obj-$(CONFIG_JNX_CBD_FPGA)	+= jnx-cbd-fpga.o jnx-cbd-fpga-common.o
+obj-$(CONFIG_JNX_CBD_FPGA_PTX1K)+= jnx-cbd-ptx1k.o
+obj-$(CONFIG_JNX_CBD_FPGA_PTX21K)+= jnx-cbd-fpga-ptx21k.o
+jnx-cbd-fpga-y := jnx-cbd-fpga-core.o jnx-cbd-fpga-ptx5k.o jnx-cbd-fpga-ptx3k.o
+jnx-cbd-ptx1k-y := jnx-cbc-ptx1k.o jnx-cbd-fpga-ptx1k.o
diff --git a/drivers/staging/jnx/jnx-cbc-ptx1k.c b/drivers/staging/jnx/jnx-cbc-ptx1k.c
new file mode 100644
index 0000000..7eff5ce
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbc-ptx1k.c
@@ -0,0 +1,242 @@
+/*
+ * Juniper Generic Control Board (CB) FPGA Driver
+ *
+ * Copyright (C) 2012, 2013, 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/jnx/jnx-subsys.h>
+#include <linux/jnx/jnx-board-core.h>
+#include <linux/jnx/board_ids.h>
+#include <linux/mfd/cbc-core.h>
+#include <linux/of.h>
+#include <linux/dmi.h>
+#include "jnx-cbd-fpga-core.h"
+#include "jnx-cbd-fpga-common.h"
+#include "jnx-cbd-fpga-platdata.h"
+#include "jnx-subsys-private.h"
+
+/*
+ * Polaris notes (current state):
+ *
+ * I2C: We dont have I2C mux and I2C slow bus. The I2C is handled
+ *   by the I2C accellerator in the CBC FPGA (40 buses -> 10 i2c masters
+ *   internaly muxed x4), supported by the SAM i2c driver.
+ * Presence detect: (CH_PRS and OTHER_CH_PRS) - these bits are exported as
+ *   GPIOs by the gpio-cbc-presence driver
+ * DEVICE_CRTL is not valid for Polaris - we don't have to reset anything a mux
+ */
+
+#define PTX1K_SAM_CHANNELS	4
+static struct i2c_adapter *jnx_find_sam_adapter(int chan)
+{
+	char name[JNX_BRD_I2C_NAME_LEN];
+	struct i2c_adapter *adap;
+	int base, mux, chan_id;
+
+	/*
+	 * Find base adapter index first. Base is first SAM adapter,
+	 * and the name starts with 'i2c-sam.'. We can not assume this
+	 * to be constant.
+	 */
+	adap = jnx_i2c_find_adapter("i2c-sam");
+	if (!adap)
+		return NULL;
+	base = adap->nr;
+	i2c_put_adapter(adap);
+
+	/*
+	 * (zero based)  channel -> sam i2c_adapter
+	 * Example:
+	 *	bus #11 = i2c-10-mux (chan_id 3)
+	 *	bus #4  = i2c-5-mux (chan_id 0)
+	 */
+	chan_id = chan % PTX1K_SAM_CHANNELS; /* 0..3 */
+	mux = chan / PTX1K_SAM_CHANNELS;
+	mux *= (PTX1K_SAM_CHANNELS + 1); /* 0, 5, 10, ... */
+	mux += base;
+
+	sprintf(name, "i2c-%d-mux (chan_id %d)", mux, chan_id);
+
+	return jnx_i2c_find_adapter(name);
+}
+
+/*
+ * Check if we're on OF enabled setup and if the jnx_connector is defined
+ * in boot dtb. If present we'll assume that board insertion/removal will
+ * be handled by the connector and the card overlays and not the jnx-*
+ * platform drivers. This allows a temp fallback util the ptx1k/x86 code
+ * is fully DT capable.
+ */
+static int jnx_connector_present(void)
+{
+	return of_have_populated_dt();
+}
+
+const struct jnx_cbd_fpga_info * const jnx_cbc_cfinfo[] = {
+	[JNX_CBD_FPGA_PTX1K] = &jnx_cbd_fpga_ptx1k_info,
+	[JNX_CBD_FPGA_PTX1K_P2] = &jnx_cbd_fpga_ptx1k_p2_info,
+	[JNX_CBD_FPGA_PTX21K] = &jnx_cbd_fpga_ptx21k_info,
+};
+
+static int jnx_cbc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct cbc_fpga_platdata *pdata = dev_get_platdata(dev);
+	const struct jnx_cbd_fpga_info *cfinfo;
+	struct jnx_cbd_fpga_data *fdata;
+	struct jnx_chassis_info chinfo;
+	struct jnx_card_info cinfo;
+	struct resource *res;
+	u32 ch_prs;
+	int err;
+
+	if (!pdata || pdata->board_type >= ARRAY_SIZE(jnx_cbc_cfinfo))
+		return -ENODEV;
+
+	cfinfo = jnx_cbc_cfinfo[pdata->board_type];
+
+	fdata = devm_kzalloc(dev, sizeof(*fdata), GFP_KERNEL);
+	if (!fdata)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	fdata->region0 = devm_ioremap(dev, res->start, resource_size(res));
+	if (!fdata->region0)
+		return -ENOMEM;
+
+	fdata->cfinfo = cfinfo;
+	fdata->find_mux_adapter = jnx_find_sam_adapter;
+	fdata->default_chan = cfinfo->i2cmux_default_chan;
+	fdata->mux_channels = cfinfo->mux_channels;
+	if (fdata->mux_channels > JNX_CBD_FPGA_NUM_MUXES)
+		fdata->mux_channels = JNX_CBD_FPGA_NUM_MUXES;
+
+	mutex_init(&fdata->lock);
+
+	platform_set_drvdata(pdev, fdata);
+
+	err = jnx_cbd_fpga_sysfs_init(dev);
+	if (err)
+		return err;
+
+	/*
+	 * Register the chassis with the system so userspace is able to
+	 * determine platform type, etc.
+	 */
+	chinfo.platform = cfinfo->platform;
+	chinfo.chassis_no = 0;
+	chinfo.multichassis = 0;
+	chinfo.master_data = fdata;
+	chinfo.get_master = jnx_cbd_fpga_get_master;
+	chinfo.mastership_get = jnx_cbd_fpga_mastership_get;
+	chinfo.mastership_set = jnx_cbd_fpga_mastership_set;
+	chinfo.mastership_ping = jnx_cbd_fpga_mastership_ping;
+	chinfo.mastership_count_get = jnx_cbd_fpga_mastership_count_get;
+	chinfo.mastership_count_set = jnx_cbd_fpga_mastership_count_set;
+
+	jnx_register_chassis(&chinfo);
+
+	/*
+	 * Register the local card with the system so userspace is able to
+	 * determine board type etc.
+	 */
+	ch_prs = ioread32(fdata->region0 + JNX_CBD_FPGA_CH_PRS_REG);
+
+	fdata->slot = (ch_prs & cfinfo->ch_prs_cb0_bit) ? 0 : 1;
+
+	cinfo.assembly_id = cfinfo->assembly_id;
+	cinfo.slot = fdata->slot;
+	cinfo.type = JNX_BOARD_TYPE_RE;
+	cinfo.adap = NULL;
+
+	jnx_register_local_card(&cinfo);
+
+	/* The jnx-connector & gpio-cbc-presence will handle the cards */
+	fdata->dt_enabled = jnx_connector_present();
+	if (fdata->dt_enabled) {
+		dev_info(dev, "line cards will handled by the jnx-connector\n");
+		return 0;
+	}
+
+	fdata->workqueue = create_singlethread_workqueue("jnx-cbd-poller");
+	if (!fdata->workqueue) {
+		err = -ENOMEM;
+		goto err_unregister;
+	}
+
+	INIT_DELAYED_WORK(&fdata->work, jnx_cbd_fpga_ch_prs_handler);
+
+	return 0;
+
+err_unregister:
+	jnx_unregister_local_card();
+	jnx_unregister_chassis();
+	jnx_cbd_fpga_sysfs_remove(dev);
+
+	return err;
+}
+
+static int jnx_cbc_remove(struct platform_device *pdev)
+{
+	struct jnx_cbd_fpga_data *fdata = platform_get_drvdata(pdev);
+	int i;
+
+	if (!fdata->dt_enabled) {
+		cancel_delayed_work_sync(&fdata->work);
+		flush_workqueue(fdata->workqueue);
+		destroy_workqueue(fdata->workqueue);
+
+		for (i = 0; i < fdata->mux_channels; i++) {
+			/*
+			 * jnx_i2c_find_adapter acquires a hold on
+			 * the  adapter
+			 */
+			i2c_put_adapter(fdata->mux[i]);
+		}
+	}
+
+	jnx_unregister_local_card();
+	jnx_unregister_chassis();
+
+	jnx_cbd_fpga_sysfs_remove(&pdev->dev);
+
+	return 0;
+}
+
+static struct platform_driver jnx_cbc_driver = {
+	.driver = {
+		.name = "jnx-cbd-fpga",
+		.owner  = THIS_MODULE,
+	},
+	.probe = jnx_cbc_probe,
+	.remove = jnx_cbc_remove,
+};
+module_platform_driver(jnx_cbc_driver);
+
+MODULE_DESCRIPTION("JNX Polaris CBC Driver");
+MODULE_AUTHOR("Georgi Vlaev <gvlaev@juniper.net>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:jnx-cbd-fpga");
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-common.c b/drivers/staging/jnx/jnx-cbd-fpga-common.c
new file mode 100644
index 0000000..718fb37
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-common.c
@@ -0,0 +1,332 @@
+/*
+ * Juniper Generic Control Board (CB) FPGA Driver common code
+ *
+ * Copyright (C) 2012, 2013, 2014, 2015 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/jnx/jnx-subsys.h>
+#include <linux/jnx/jnx-board-core.h>
+#include <linux/jnx/board_ids.h>
+#include <linux/of.h>
+#include <linux/dmi.h>
+#include "jnx-cbd-fpga-common.h"
+#include "jnx-cbd-fpga-core.h"
+#include "jnx-cbd-fpga-platdata.h"
+#include "jnx-subsys-private.h"
+
+/**********************************************************************
+ *
+ * JNX CBD FPGA Board insertion/removal handling
+ *
+ **********************************************************************/
+
+void jnx_cbd_fpga_board_insert(struct jnx_cbd_fpga_data *fdata,
+			       int slot, int chan)
+{
+	int mindex = chan / 64;
+	int mchan = chan % 64;
+
+	if (WARN(chan >= ARRAY_SIZE(fdata->client), "bad chan=%d\n", chan))
+		return;
+	fdata->client[chan] =
+	  jnx_board_inserted(fdata->mux[chan], slot,
+			     fdata->cfinfo->cpld_mux_bitmask[mindex] &
+			       BIT_ULL(mchan));
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_board_insert);
+
+void jnx_cbd_fpga_board_remove(struct jnx_cbd_fpga_data *fdata, int chan)
+{
+	jnx_board_removed(fdata->mux[chan], fdata->client[chan]);
+	fdata->client[chan] = NULL;
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_board_remove);
+
+static void jnx_cbd_fpga_board_remove_all(struct jnx_cbd_fpga_data *fdata)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(fdata->client); i++)
+		jnx_cbd_fpga_board_remove(fdata, i);
+}
+
+/**********************************************************************
+ *
+ * JNX CBD FPGA Mastership attribute support
+ *
+ ***********************************************************************/
+#define JNX_MSTR_CFG_REG		0x003C
+#define JNX_MSTR_ALIVE_REG		0x0040
+#define JNX_MSTR_ALIVE_CNT_REG		0x0044
+
+#define JNX_MSTR_BOOTED			BIT(0)
+#define JNX_MSTR_RELINQUISH		BIT(1)
+#define JNX_MSTR_IM_RDY			BIT(3)
+#define JNX_MSTR_IM_MASTER		BIT(4)
+#define JNX_MSTR_HE_RDY			BIT(5)
+#define JNX_MSTR_HE_MASTER		BIT(6)
+
+#define JNX_MSTR	(JNX_MSTR_BOOTED | JNX_MSTR_IM_RDY | JNX_MSTR_IM_MASTER)
+#define JNX_ANY_MSTR	(JNX_MSTR_IM_MASTER | JNX_MSTR_HE_MASTER)
+#define JNX_HE_MSTR	(JNX_MSTR_HE_RDY | JNX_MSTR_HE_MASTER)
+
+#define MSTR_ALIVE_CNT			0x01FF
+#define JNX_MSTR_ALIVE			0x0001
+
+#define JNX_MSTR_REFRESH_TIMEOUT	21
+#define JNX_MAX_MSTR_ALIVE_CNT		0xFF
+
+static bool jnx_cbd_fpga_am_master(struct jnx_cbd_fpga_data *fdata)
+{
+	u32 mstr_cfg = ioread32(fdata->region0 + JNX_MSTR_CFG_REG);
+
+	return (mstr_cfg & JNX_MSTR) == JNX_MSTR;
+}
+
+static bool jnx_cbd_fpga_he_master(struct jnx_cbd_fpga_data *fdata)
+{
+	u32 mstr_cfg = ioread32(fdata->region0 + JNX_MSTR_CFG_REG);
+
+	return (mstr_cfg & JNX_HE_MSTR) == JNX_HE_MSTR;
+}
+
+static bool jnx_cbd_fpga_become_master(struct jnx_cbd_fpga_data *fdata)
+{
+	bool am_master;
+	int  i;
+
+	iowrite32(JNX_MSTR_REFRESH_TIMEOUT | (1 << 8),
+		  fdata->region0 + JNX_MSTR_ALIVE_CNT_REG);
+	iowrite32(1, fdata->region0 + JNX_MSTR_ALIVE_REG);
+	iowrite32(JNX_MSTR_BOOTED, fdata->region0 + JNX_MSTR_CFG_REG);
+
+	for (i = 0; i < 20; i++) {
+		am_master = jnx_cbd_fpga_am_master(fdata);
+		if (am_master)
+			break;
+		usleep_range(10, 20);
+	}
+
+	if (am_master && !fdata->dt_enabled)	/* FIXME dt_enabled is odd */
+		queue_delayed_work(fdata->workqueue, &fdata->work, 0);
+
+	return am_master;
+}
+
+static void jnx_cbd_fpga_relinquish_master(struct jnx_cbd_fpga_data *fdata)
+{
+	u32 mstr_cfg;
+	int i;
+
+	cancel_delayed_work_sync(&fdata->work);
+
+	jnx_cbd_fpga_board_remove_all(fdata);
+
+	mstr_cfg = ioread32(fdata->region0 + JNX_MSTR_CFG_REG);
+	mstr_cfg &= ~JNX_MSTR_BOOTED;
+	iowrite32(mstr_cfg, fdata->region0 + JNX_MSTR_CFG_REG);
+
+	for (i = 0; i < ARRAY_SIZE(fdata->prev_ch_prs); i++)
+		fdata->prev_ch_prs[i] = 0;
+}
+
+int jnx_cbd_fpga_get_master(void *data)
+{
+	struct jnx_cbd_fpga_data *fdata = data;
+
+	if (jnx_cbd_fpga_am_master(fdata))
+		return fdata->slot;
+
+	if (jnx_cbd_fpga_he_master(fdata))
+		return fdata->slot ^ 1;
+
+	return -1;
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_get_master);
+
+bool jnx_cbd_fpga_mastership_get(void *data)
+{
+	struct jnx_cbd_fpga_data *fdata = data;
+
+	return jnx_cbd_fpga_am_master(fdata);
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_mastership_get);
+
+void jnx_cbd_fpga_mastership_set(void *data, bool master)
+{
+	struct jnx_cbd_fpga_data *fdata = data;
+
+	mutex_lock(&fdata->lock);
+	if (jnx_cbd_fpga_am_master(fdata)) {
+		if (!master)
+			jnx_cbd_fpga_relinquish_master(fdata);
+	} else {
+		if (master)
+			jnx_cbd_fpga_become_master(fdata);
+	}
+	mutex_unlock(&fdata->lock);
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_mastership_set);
+
+void jnx_cbd_fpga_mastership_ping(void *data)
+{
+	struct jnx_cbd_fpga_data *fdata = data;
+
+	iowrite32(1, fdata->region0 + JNX_MSTR_ALIVE_REG);
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_mastership_ping);
+
+int jnx_cbd_fpga_mastership_count_get(void *data)
+{
+	struct jnx_cbd_fpga_data *fdata = data;
+
+	return ioread32(fdata->region0 + JNX_MSTR_ALIVE_CNT_REG) &
+		JNX_MAX_MSTR_ALIVE_CNT;
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_mastership_count_get);
+
+int jnx_cbd_fpga_mastership_count_set(void *data, int val)
+{
+	struct jnx_cbd_fpga_data *fdata = data;
+
+	if (val < 0 || val > JNX_MAX_MSTR_ALIVE_CNT)
+		return -EINVAL;
+
+	iowrite32(val | (1 << 8), fdata->region0 + JNX_MSTR_ALIVE_CNT_REG);
+
+	return 0;
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_mastership_count_set);
+
+#define FPGA_MEM_SIZE		0x1000
+
+static struct jnx_cbd_fpga_data *cbd_sysfs_get_dev_info(struct kobject *kobj)
+{
+	struct platform_device *pdev =
+		to_platform_device(container_of(kobj, struct device, kobj));
+
+	return platform_get_drvdata(pdev);
+}
+
+static int cbd_fpga_validate_access_parameters(loff_t offset, size_t count)
+{
+	/*
+	 * Need to make sure the count is a multiple of JNX_CBD_FPGA_REG_SIZE
+	 * bytes and that the offset plus the number of registers being read
+	 * is less than the size of the fpga.
+	 */
+	if ((count % JNX_CBD_FPGA_REG_SIZE) == 0)
+		if ((offset + (count / JNX_CBD_FPGA_REG_SIZE)) < FPGA_MEM_SIZE)
+			return 0;
+
+	return -EINVAL;
+}
+
+static ssize_t cbd_fpga_read(struct file *filp, struct kobject *kobj,
+			     struct bin_attribute *bin_attr,
+			     char *buffer, loff_t offset, size_t count)
+{
+	struct jnx_cbd_fpga_data *cfpga = cbd_sysfs_get_dev_info(kobj);
+	void __iomem *addr;
+	ssize_t err;
+	int nregs;
+	u32 *buf;
+	int i;
+
+	err = cbd_fpga_validate_access_parameters(offset, count);
+	if (err)
+		return err;
+
+	nregs = count / JNX_CBD_FPGA_REG_SIZE;
+	buf = (u32 *)buffer;
+	addr = cfpga->region0 + offset;
+
+	for (i = 0; i < nregs; i++)
+		buf[i] = ioread32(addr + i * JNX_CBD_FPGA_REG_SIZE);
+
+	return count;
+}
+
+static ssize_t cbd_fpga_write(struct file *filp, struct kobject *kobj,
+			      struct bin_attribute *bin_attr,
+			      char *buffer, loff_t offset, size_t count)
+{
+	struct jnx_cbd_fpga_data *cfpga = cbd_sysfs_get_dev_info(kobj);
+	void __iomem *addr;
+	ssize_t err;
+	int nregs;
+	u32 *buf;
+	int i;
+
+	err = cbd_fpga_validate_access_parameters(offset, count);
+	if (err)
+		return err;
+
+	nregs = count / JNX_CBD_FPGA_REG_SIZE;
+	buf = (u32 *)buffer;
+	addr = cfpga->region0 + offset;
+
+	for (i = 0; i < nregs; i++)
+		iowrite32(buf[i], addr + i * JNX_CBD_FPGA_REG_SIZE);
+
+	return count;
+}
+
+static struct bin_attribute cbd_fpga_attr = {
+	.attr = {
+		.name = "fpga-data",
+		.mode = S_IRUGO | S_IWUSR,
+	},
+
+	.size = FPGA_MEM_SIZE,
+	.read = cbd_fpga_read,
+	.write = cbd_fpga_write,
+};
+
+int jnx_cbd_fpga_sysfs_init(struct device *dev)
+{
+	int err;
+
+	err = sysfs_create_bin_file(&dev->kobj, &cbd_fpga_attr);
+	if (err)
+		return err;
+
+	err = sysfs_create_link(&dev->parent->parent->parent->kobj,
+				&dev->kobj, "cbfpga");
+	if (err)
+		goto err_bin_file;
+	return 0;
+
+err_bin_file:
+	sysfs_remove_bin_file(&dev->kobj, &cbd_fpga_attr);
+	return err;
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_sysfs_init);
+
+void jnx_cbd_fpga_sysfs_remove(struct device *dev)
+{
+	sysfs_remove_link(&dev->parent->parent->parent->kobj,
+			  "cbfpga");
+	sysfs_remove_bin_file(&dev->kobj, &cbd_fpga_attr);
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_sysfs_remove);
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-common.h b/drivers/staging/jnx/jnx-cbd-fpga-common.h
new file mode 100644
index 0000000..9bf4949
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-common.h
@@ -0,0 +1,27 @@
+/*
+ * jnx-cbd-fpga-common.h
+ *
+ * Copyright (C) 2015 Juniper Networks. All rights reserved.
+ *
+ */
+
+#ifndef JNX_CBD_FPGA_COMMON_H
+#define JNX_CBD_FPGA_COMMON_H
+
+struct jnx_cbd_fpga_data;
+
+void jnx_cbd_fpga_board_insert(struct jnx_cbd_fpga_data *fdata,
+			       int slot, int chan);
+void jnx_cbd_fpga_board_remove(struct jnx_cbd_fpga_data *fdata, int chan);
+
+int jnx_cbd_fpga_get_master(void *data);
+bool jnx_cbd_fpga_mastership_get(void *data);
+void jnx_cbd_fpga_mastership_set(void *data, bool master);
+void jnx_cbd_fpga_mastership_ping(void *data);
+int jnx_cbd_fpga_mastership_count_get(void *data);
+int jnx_cbd_fpga_mastership_count_set(void *data, int val);
+
+int jnx_cbd_fpga_sysfs_init(struct device *dev);
+void jnx_cbd_fpga_sysfs_remove(struct device *dev);
+
+#endif /* JNX_CBD_FPGA_COMMON_H */
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-core.c b/drivers/staging/jnx/jnx-cbd-fpga-core.c
new file mode 100644
index 0000000..b665f46
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-core.c
@@ -0,0 +1,540 @@
+/*
+ * Juniper Generic Control Board (CB) FPGA Driver
+ *
+ * Copyright (C) 2012, 2013, 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/i2c-mux.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+#include <linux/workqueue.h>
+#include <linux/jnx/jnx-subsys.h>
+#include <linux/jnx/jnx-board-core.h>
+#include <linux/jnx/board_ids.h>
+#include <linux/jnx/pci_ids.h>
+#include <linux/platform_data/i2c-pca-jnx.h>
+#include <linux/of.h>
+#include "jnx-cbd-fpga-core.h"
+#include "jnx-cbd-fpga-common.h"
+#include "jnx-cbd-fpga-platdata.h"
+#include "jnx-subsys-private.h"
+
+#define DRIVER_VERSION  "0.01.0"
+#define DRIVER_DESC     "JNX CB FPGA Driver"
+
+#define JNX_CBD_FPGA_MODULE_NAME	"jnx-cbd-fpga"
+
+static const struct pci_device_id jnx_cbd_fpga_id_table[] = {
+	{PCI_VDEVICE(JUNIPER, JNX_CBD_FPGA_DID_09B3), JNX_CBD_FPGA_PTX5K},
+	{PCI_VDEVICE(JUNIPER, JNX_CBD_FPGA_DID_0BA8), JNX_CBD_FPGA_PTX3K},
+	{0,}
+};
+MODULE_DEVICE_TABLE(pci, jnx_cbd_fpga_id_table);
+
+static const struct jnx_cbd_fpga_info *jnx_cbd_fpga_info_tbl[] = {
+	[JNX_CBD_FPGA_PTX5K] = &jnx_cbd_fpga_ptx5k_info,
+	[JNX_CBD_FPGA_PTX3K] = &jnx_cbd_fpga_ptx3k_info,
+};
+
+/**********************************************************************
+ *
+ * JNX CBD FPGA I2C controller APIs
+ *
+ **********************************************************************/
+#define JNX_CBD_FPGA_WAIT_FOR_IDLE	10000
+
+#define JNX_CBD_FPGA_I2C_CTRL_READ	BIT(0)
+#define JNX_CBD_FPGA_I2C_CTRL_WRITE	0
+#define JNX_CBD_FPGA_I2C_CTRL_START	BIT(1)
+#define JNX_CBD_FPGA_I2C_CTRL_SEL_I2C	BIT(2)
+
+static int jnx_cbd_fpga_i2c_wait_bus_inactive(struct jnx_cbd_fpga_data *fdata)
+{
+	int i = 0;
+
+	while (ioread32(fdata->ctrl_reg) & JNX_CBD_FPGA_I2C_CTRL_START &&
+	       i++ < JNX_CBD_FPGA_WAIT_FOR_IDLE) {
+		udelay(1);
+	}
+
+	if (i >= JNX_CBD_FPGA_WAIT_FOR_IDLE)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static void jnx_cbd_fpga_iowrite32_sync(u32 val, void __iomem *reg)
+{
+	iowrite32(val, reg);
+	ioread32(reg);
+}
+
+static void jnx_cbd_fpga_i2c_writebyte(struct device *dev, int reg, int value)
+{
+	struct jnx_cbd_fpga_data *fdata = dev_get_drvdata(dev);
+	int rc;
+
+	rc = jnx_cbd_fpga_i2c_wait_bus_inactive(fdata);
+	if (rc < 0)
+		return;
+
+	iowrite32(value, fdata->data_reg);
+
+	iowrite32(reg, fdata->addr_reg);
+
+	jnx_cbd_fpga_iowrite32_sync(JNX_CBD_FPGA_I2C_CTRL_SEL_I2C |
+				    JNX_CBD_FPGA_I2C_CTRL_WRITE |
+				    JNX_CBD_FPGA_I2C_CTRL_START,
+				    fdata->ctrl_reg);
+}
+
+static int jnx_cbd_fpga_i2c_readbyte(struct device *dev, int reg)
+{
+	struct jnx_cbd_fpga_data *fdata = dev_get_drvdata(dev);
+	int rc;
+
+	rc = jnx_cbd_fpga_i2c_wait_bus_inactive(fdata);
+	if (rc)
+		return 0;
+
+	iowrite32(reg, fdata->addr_reg);
+
+	iowrite32(JNX_CBD_FPGA_I2C_CTRL_SEL_I2C | JNX_CBD_FPGA_I2C_CTRL_READ |
+		  JNX_CBD_FPGA_I2C_CTRL_START, fdata->ctrl_reg);
+
+	rc = jnx_cbd_fpga_i2c_wait_bus_inactive(fdata);
+	if (rc)
+		return 0;
+
+	return (u8)ioread32(fdata->data_reg);
+}
+
+/**********************************************************************
+ *
+ * End JNX CBD FPGA I2C controller APIs
+ *
+ **********************************************************************/
+
+/**********************************************************************
+ *
+ * JNX CBD FPGA I2C Selector Mux APIs
+ *
+ **********************************************************************/
+
+static int cbd_i2c_select_chan(struct i2c_mux_core *muxc, u32 chan)
+{
+	struct jnx_cbd_fpga_data *fdata = i2c_mux_priv(muxc);
+	u32 select;
+
+	select = ioread32(fdata->select_reg);
+	if (select != chan)
+		jnx_cbd_fpga_iowrite32_sync(chan, fdata->select_reg);
+
+	return 0;
+}
+
+static int cbd_i2c_deselect_chan(struct i2c_mux_core *muxc, u32 chan)
+{
+	struct jnx_cbd_fpga_data *fdata = i2c_mux_priv(muxc);
+
+	return cbd_i2c_select_chan(muxc, fdata->default_chan);
+}
+
+/**********************************************************************
+ * JNX CBD FPGA I2C Selector Mux APIs
+ **********************************************************************/
+
+void jnx_cbd_fpga_ch_prs_handler(struct work_struct *work)
+{
+	struct jnx_cbd_fpga_data *fdata = container_of(to_delayed_work(work),
+						       struct jnx_cbd_fpga_data,
+						       work);
+	const struct jnx_cbd_fpga_info *cfinfo;
+	unsigned long ch_prs, prs_change;
+	int slot, chan;
+	int i, bit;
+
+	cfinfo = fdata->cfinfo;
+
+	mutex_lock(&fdata->lock);
+
+	for (i = 0; i < ARRAY_SIZE(cfinfo->cpld_prs_reg); i++) {
+		if (!cfinfo->cpld_prs_reg[i])
+			continue;
+
+		ch_prs = ioread32(fdata->region0 + cfinfo->cpld_prs_reg[i]);
+		ch_prs |= cfinfo->cpld_prs_set_bitmask[i];
+		if (cfinfo->cpld_prs_bitmask[i])
+			ch_prs &= cfinfo->cpld_prs_bitmask[i];
+		prs_change = ch_prs ^ fdata->prev_ch_prs[i];
+		fdata->prev_ch_prs[i] = ch_prs;
+
+		for_each_set_bit(bit, &prs_change, sizeof(u32) * 8) {
+			chan = cfinfo->get_channel(bit + i * 32);
+			if (chan < 0)
+				continue;
+
+			if (!fdata->mux[chan] && fdata->find_mux_adapter)
+				fdata->mux[chan] =
+					fdata->find_mux_adapter(chan);
+
+			if (!fdata->mux[chan])
+				continue;
+
+			slot = cfinfo->get_slot_from_chan(chan);
+			if (slot < 0)
+				continue;
+
+			pr_info("CBC: presence bit %d -> chan: %d, adap %p\n",
+				bit + i * 32, chan, fdata->mux[chan]);
+
+			if (test_bit(bit, &ch_prs))
+				jnx_cbd_fpga_board_insert(fdata, slot, chan);
+			else
+				jnx_cbd_fpga_board_remove(fdata, chan);
+		}
+	}
+
+	mutex_unlock(&fdata->lock);
+
+	queue_delayed_work(fdata->workqueue, &fdata->work, 10 * HZ);
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_ch_prs_handler);
+
+/**********************************************************************
+ * End JNX CBD FPGA Board insertion/removal handling
+ **********************************************************************/
+
+static void jnx_cbd_fpga_device_reset(void __iomem *reg_addr, u32 mask)
+{
+	u32 device_ctrl = 0;
+
+	device_ctrl = ioread32(reg_addr);
+	device_ctrl |= mask;
+	iowrite32(device_ctrl, reg_addr);
+
+	usleep_range(100, 500);	/* was udelay(100) */
+
+	device_ctrl = ioread32(reg_addr);
+	device_ctrl &= ~mask;
+	iowrite32(device_ctrl, reg_addr);
+}
+
+static int jnx_cbd_fpga_ptx5k_get_master(void *data)
+{
+	struct jnx_cbd_fpga_data *fdata = data;
+	int slot;
+	u32 rev;
+
+	slot = jnx_cbd_fpga_get_master(data);
+	if (slot < 0) {
+		rev = ioread32(fdata->region0 + JNX_CBD_FPGA_REV_REG);
+		if (rev < JNX_CBD_FPGA_REV_GOOD) {
+			WARN_ONCE(1,
+				  "FPGA ver. 0x%x can't support master detect\n",
+				  rev);
+			/*
+			 * We can not determine who is master. It is not us,
+			 * so assume that the other RE is master.
+			 */
+			slot = fdata->slot ^ 1;
+		}
+	}
+
+	return slot;
+}
+
+/*
+ * Check if we're on OF enabled setup. If so, we'll assume that board
+ * insertion/removal is handled by the jnx-connector driver + overlays
+ * and not the jnx-* platform drivers. This allows a temporary workaround
+ * until all the ptx-x86 code is OF capable.
+ */
+static int jnx_connector_present(void)
+{
+	return of_have_populated_dt();
+}
+
+/*
+ * jnx_of_create_device()
+ *	Hack - create the presence driver and associate with dt node
+ *	This vaguely simulates what the mfd_core does.
+ */
+static char * const of_presence_dev_names[] = {
+	"jnx,gpio-cbc-presence",
+	"jnx,gpio-cbc-presence-other",
+};
+
+static struct platform_device *jnx_of_create_device(struct pci_dev *pcidev,
+						    int id, const char *name,
+						    const char *of_compatible)
+{
+	struct device *parent = &pcidev->dev;
+	struct platform_device *pdev = NULL;
+	struct device_node *np = NULL;
+	struct resource res;
+
+	if (!parent->of_node || !of_compatible)
+		return NULL;
+
+	memset(&res, 0, sizeof(res));
+	res.start = pci_resource_start(pcidev, 0);
+	res.end = pci_resource_end(pcidev, 0);
+	res.flags = IORESOURCE_MEM;
+	res.parent = &pcidev->resource[0];
+
+	pdev = platform_device_register_resndata(parent, name, id,
+						 &res, 1, NULL, 0);
+
+	if (IS_ERR(pdev))
+		return NULL;
+
+	for_each_child_of_node(parent->of_node, np) {
+		if (of_device_is_compatible(np, of_compatible)) {
+			pdev->dev.of_node = np;
+			return pdev;
+		}
+	}
+
+	dev_err(parent, "Failed to create %s device\n", of_compatible);
+	/* of_compatible node missing */
+	platform_device_put(pdev);
+
+	return NULL;
+}
+
+static int
+jnx_cbd_fpga_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	const struct jnx_cbd_fpga_info *cfinfo =
+		jnx_cbd_fpga_info_tbl[id->driver_data];
+	struct jnx_i2c_pca_platform_data idata;
+	struct device *dev = &pdev->dev;
+	struct jnx_cbd_fpga_data *fdata;
+	struct jnx_chassis_info chinfo;
+	struct jnx_card_info cinfo;
+	void __iomem *reset_reg;
+	u32 *bitmasks;
+	u32 ch_prs;
+	int err;
+	int i;
+
+	fdata = devm_kzalloc(dev, sizeof(*fdata), GFP_KERNEL);
+	if (!fdata)
+		return -ENOMEM;
+
+	dev_dbg(dev, "%s.%s(): irq pin %d\n", JNX_CBD_FPGA_MODULE_NAME,
+		__func__, pdev->irq);
+
+	err = pcim_enable_device(pdev);
+	if (err)
+		return err;
+
+	err = pcim_iomap_regions(pdev, 1 << 0, JNX_CBD_FPGA_MODULE_NAME);
+	if (err)
+		return err;
+
+	/*
+	 * Need to remap the io regions for the slow bus registers and
+	 * the i2c select register.  These are needed for the associated
+	 * i2c adapter and mux drivers.
+	 */
+	fdata->region0 = pcim_iomap_table(pdev)[0];
+
+	fdata->cfinfo = cfinfo;
+	fdata->default_chan = cfinfo->i2cmux_default_chan;
+	fdata->mux_channels = cfinfo->mux_channels;
+	if (fdata->mux_channels > JNX_CBD_FPGA_NUM_MUXES)
+		fdata->mux_channels = JNX_CBD_FPGA_NUM_MUXES;
+
+	mutex_init(&fdata->lock);
+
+	/*
+	 * Set up the slow bus address registers
+	 */
+	fdata->addr_reg = fdata->region0 + JNX_CBD_FPGA_SLOW_BUS_REG;
+	fdata->data_reg = fdata->addr_reg + JNX_CBD_FPGA_REG_SIZE;
+	fdata->ctrl_reg = fdata->data_reg + JNX_CBD_FPGA_REG_SIZE;
+
+	fdata->select_reg = fdata->region0 + JNX_CBD_FPGA_I2C_SELECT_REG;
+
+	pci_set_drvdata(pdev, fdata);
+
+	err = jnx_cbd_fpga_sysfs_init(dev);
+	if (err)
+		return err;
+
+	/*
+	 * Run any devices resets that are required before instantiating
+	 * any other devices.
+	 */
+	reset_reg = fdata->region0 + cfinfo->reset->reg_offset;
+	bitmasks = cfinfo->reset->bitmasks;
+	i = 0;
+
+	while (bitmasks[i])
+		jnx_cbd_fpga_device_reset(reset_reg, bitmasks[i++]);
+
+	/*
+	 * Register the chassis with the system so userspace is able to
+	 * determine platform type, etc.
+	 */
+	chinfo.platform = cfinfo->platform;
+	chinfo.chassis_no = 0;
+	chinfo.multichassis = 0;
+	chinfo.master_data = fdata;
+	chinfo.get_master = jnx_cbd_fpga_ptx5k_get_master;
+	chinfo.mastership_get = jnx_cbd_fpga_mastership_get;
+	chinfo.mastership_set = jnx_cbd_fpga_mastership_set;
+	chinfo.mastership_ping = jnx_cbd_fpga_mastership_ping;
+	chinfo.mastership_count_get = jnx_cbd_fpga_mastership_count_get;
+	chinfo.mastership_count_set = jnx_cbd_fpga_mastership_count_set;
+
+	jnx_register_chassis(&chinfo);
+
+	/*
+	 * Register the local card with the system so userspace is able to
+	 * determine board type etc.
+	 */
+	ch_prs = ioread32(fdata->region0 + JNX_CBD_FPGA_CH_PRS_REG);
+
+	fdata->slot = (ch_prs & cfinfo->ch_prs_cb0_bit) ? 0 : 1;
+
+	cinfo.assembly_id = cfinfo->assembly_id;
+	cinfo.slot = fdata->slot;
+	cinfo.type = JNX_BOARD_TYPE_RE;
+	cinfo.adap = NULL;
+
+	jnx_register_local_card(&cinfo);
+
+	/*
+	 * Set up and create the i2c controller
+	 */
+	idata.dev = dev;
+	idata.adap = &fdata->adap;
+	idata.clock = 400000;
+	idata.read_byte = jnx_cbd_fpga_i2c_readbyte;
+	idata.write_byte = jnx_cbd_fpga_i2c_writebyte;
+
+	fdata->i2c_ctlr = platform_device_alloc(cfinfo->i2c_ctlr_name, -1);
+	if (!fdata->i2c_ctlr) {
+		err = -ENOMEM;
+		goto err_unregister;
+	}
+
+	err = platform_device_add_data(fdata->i2c_ctlr, &idata, sizeof(idata));
+	if (err)
+		goto err_pdev;
+
+	err = platform_device_add(fdata->i2c_ctlr);
+	if (err)
+		goto err_pdev;
+
+	/* Now create i2c muxes on top of the i2c adapter */
+	fdata->muxc = i2c_mux_alloc(&fdata->adap, &pdev->dev,
+				    fdata->mux_channels, 0, 0,
+				    cbd_i2c_select_chan,
+				    cbd_i2c_deselect_chan);
+	if (!fdata->muxc) {
+		err = -ENOMEM;
+		goto err_pdev;
+	}
+	fdata->muxc->priv = fdata;
+
+	for (i = 0; i < fdata->mux_channels; i++) {
+		err = i2c_mux_add_adapter(fdata->muxc, 0, i, 0);
+		if (err)
+			goto err_mux;
+	}
+
+	/* The jnx-connector & gpio-cbc-presence will handle the cards */
+	fdata->dt_enabled = jnx_connector_present();
+	if (fdata->dt_enabled) {
+		dev_info(dev, "line cards will handled by the jnx-connector\n");
+		for (i = 0; i < ARRAY_SIZE(fdata->presence_devices); i++) {
+			fdata->presence_devices[i] =
+				jnx_of_create_device(pdev, i,
+						     "gpio-cbc-presence",
+						     of_presence_dev_names[i]);
+			if (!fdata->presence_devices[i])
+				dev_err(dev, "Failed to create %s device\n",
+					of_presence_dev_names[i]);
+		}
+		return 0;
+	}
+
+	fdata->workqueue = create_singlethread_workqueue("jnx-cbd-poller");
+	if (!fdata->workqueue) {
+		err = -ENOMEM;
+		goto err_mux;
+	}
+
+	INIT_DELAYED_WORK(&fdata->work, jnx_cbd_fpga_ch_prs_handler);
+
+	return 0;
+
+err_mux:
+	i2c_mux_del_adapters(fdata->muxc);
+
+err_pdev:
+	platform_device_put(fdata->i2c_ctlr);
+
+err_unregister:
+	jnx_unregister_chassis();
+	jnx_cbd_fpga_sysfs_remove(dev);
+
+	return err;
+}
+
+static void jnx_cbd_fpga_remove(struct pci_dev *pdev)
+{
+	struct jnx_cbd_fpga_data *fdata = pci_get_drvdata(pdev);
+	int i;
+
+	if (!fdata->dt_enabled) {
+		cancel_delayed_work_sync(&fdata->work);
+		flush_workqueue(fdata->workqueue);
+		destroy_workqueue(fdata->workqueue);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(fdata->presence_devices); i++) {
+		if (fdata->presence_devices[i])
+			platform_device_put(fdata->presence_devices[i]);
+	}
+
+	i2c_mux_del_adapters(fdata->muxc);
+	platform_device_put(fdata->i2c_ctlr);
+	jnx_unregister_local_card();
+	jnx_unregister_chassis();
+	jnx_cbd_fpga_sysfs_remove(&pdev->dev);
+}
+
+static struct pci_driver jnx_cbd_fpga_driver = {
+	.name = JNX_CBD_FPGA_MODULE_NAME,
+	.id_table = jnx_cbd_fpga_id_table,
+	.probe = jnx_cbd_fpga_probe,
+	.remove = jnx_cbd_fpga_remove,
+};
+
+module_pci_driver(jnx_cbd_fpga_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-core.h b/drivers/staging/jnx/jnx-cbd-fpga-core.h
new file mode 100644
index 0000000..ac7a4cf
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-core.h
@@ -0,0 +1,68 @@
+/*
+ * Juniper Generic Control Board (CB) FPGA Driver
+ *
+ * Copyright (C) 2012, 2013, 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _JNX_CBD_FPGA_CORE_H
+#define _JNX_CBD_FPGA_CORE_H
+
+#include <linux/bitops.h>
+
+#define JNX_CBD_FPGA_REG_SIZE		4
+
+#define JNX_CBD_FPGA_INTR_SRC		0x0000
+#define JNX_CBD_FPGA_INTR_EN		0x0004
+#define JNX_CBD_FPGA_I2C_SELECT_REG	0x0010
+#define JNX_CBD_FPGA_CH_PRS_REG		0x0014
+#define JNX_CBD_FPGA_SLOW_BUS_REG	0x0054
+#define JNX_CBD_FPGA_REV_REG		0x0060
+#define JNX_CBD_FPGA_DUMMY_READ_REG	0x0064
+#define JNX_CBD_FPGA_CH_OTHER_PRS_REG	0x00D4
+
+#define JNX_CBD_FPGA_REV_GOOD		0x011F
+
+#define JNX_CBD_FPGA_NUM_MUXES		96
+/*
+ * JNX CBD CH Presence definitions
+ */
+#define JNX_CBD_FPGA_CH_PRS_CB0_PIN	BIT(27)
+
+struct jnx_cbd_fpga_data {
+	u32 slot;
+	const struct jnx_cbd_fpga_info *cfinfo;
+	u32 default_chan;
+	u32 prev_ch_prs[4];
+	struct i2c_adapter * (*find_mux_adapter)(int chan);
+	int mux_channels;
+	struct i2c_adapter *mux[JNX_CBD_FPGA_NUM_MUXES];
+	struct i2c_client *client[JNX_CBD_FPGA_NUM_MUXES];
+	struct i2c_mux_core *muxc;
+	bool standalone;
+	int irq;
+	struct i2c_adapter adap;
+	struct mutex lock;			/* safe access lock */
+	struct platform_device *i2c_ctlr;
+	struct workqueue_struct *workqueue;
+	struct delayed_work work;
+	int dt_enabled;
+	struct platform_device *presence_devices[2];
+	void __iomem *region0;
+	void __iomem *addr_reg;
+	void __iomem *data_reg;
+	void __iomem *ctrl_reg;
+	void __iomem *select_reg;
+};
+
+void jnx_cbd_fpga_ch_prs_handler(struct work_struct *work);
+
+#endif /* _JNX_CBD_FPGA_CORE_H */
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-platdata.h b/drivers/staging/jnx/jnx-cbd-fpga-platdata.h
new file mode 100644
index 0000000..a91e38d
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-platdata.h
@@ -0,0 +1,51 @@
+/*
+ * Juniper CBD FPGA Platform Data
+ *
+ * Copyright (C) 2012, 2013, 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __JNX_CBD_FPGA_H
+#define __JNX_CBD_FPGA_H
+
+enum jnx_cbd_fpga_boards {
+	JNX_CBD_FPGA_PTX5K,
+	JNX_CBD_FPGA_PTX3K,
+};
+
+struct jnx_cbd_fpga_dev_reset_data {
+	u16 reg_offset;
+	u32 *bitmasks;
+};
+
+struct jnx_cbd_fpga_info {
+	u32 platform;
+	u32 assembly_id;
+	u64 cpld_mux_bitmask[2];
+	u32 cpld_prs_reg[4];
+	u32 cpld_prs_set_bitmask[4];
+	u32 cpld_prs_bitmask[4];
+	u32 ch_prs_cb0_bit;
+	u32 mux_channels;
+	u32 i2cmux_default_chan;
+	const char *i2c_ctlr_name;
+	struct jnx_cbd_fpga_dev_reset_data *reset;
+	int (*get_channel)(u32 bit);
+	int (*get_slot_from_chan)(int chan);
+};
+
+extern struct jnx_cbd_fpga_info jnx_cbd_fpga_ptx5k_info,
+				jnx_cbd_fpga_ptx3k_info,
+				jnx_cbd_fpga_ptx1k_info,
+				jnx_cbd_fpga_ptx1k_p2_info,
+				jnx_cbd_fpga_ptx21k_info;
+
+#endif /* __JNX_CBD_FPGA_H */
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c b/drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c
new file mode 100644
index 0000000..3314569
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c
@@ -0,0 +1,134 @@
+/*
+ * Juniper PTX 1000 Control Board (CB) FPGA
+ *
+ * Copyright (C) 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/jnx/jnx-subsys.h>
+#include <linux/jnx/board_ids.h>
+#include "jnx-cbd-fpga-core.h"
+#include "jnx-cbd-fpga-platdata.h"
+#include "jnx-subsys-private.h"
+
+#define JNX_CBD_FPGA_PTX1K_DEVICE_CTRL_REG	0x00C0
+#define JNX_CBD_FPGA_PTX1K_DEFAULT_CHAN		26 /* LOCAL_I2C */
+
+#define JNX_CBD_FPGA_PTX1K_CPLD_MUX		0xFF00 /* FPC[8] 8..15 */
+
+static u32 jnx_cbd_fpga_ptx1k_bitmask[] = { 0 };
+
+/* DEVICE_CTRL is not valid for Polaris */
+static struct jnx_cbd_fpga_dev_reset_data jnx_cbd_fpga_ptx1k_device_reset = {
+	.reg_offset = JNX_CBD_FPGA_PTX1K_DEVICE_CTRL_REG,
+	.bitmasks = jnx_cbd_fpga_ptx1k_bitmask,
+};
+
+/* Table to map from mux channel number to slot */
+
+static s8 ptx1k_mux_slot_map[40] = {
+	-1, -1, -1, -1, -1, -1, -1, -1, /*  0.. 7: Unused + RSVD[2] */
+	0, 1, 2, 3, 4, 5, 6, 7,		/*  8..15: FPC[8] */
+	0, 1, 2, 3, 4, 5,		/* 16..21: SIB[6] */
+	-1, -1, -1,			/* 22..24: Unused */
+	0,				/* 25:	   MP */
+	1,				/* 26:     Local */
+	-1,				/* 27:     Unused */
+	0, 1, 2, 3, 4,			/* 28..32: PSM[5] */
+	-1,				/* 33:     Unused */
+	0,				/* 34:     FT0 */
+	0,				/* 35:     FPD */
+	0, 1,				/* 36..37: CB[2] */
+	-1, -1				/* 38..39: SFPP[2] */
+};
+
+static int jnx_cbd_fpga_ptx1k_get_slot_from_channel(int chan)
+{
+	if (chan < 0 || chan >= ARRAY_SIZE(ptx1k_mux_slot_map) ||
+	    ptx1k_mux_slot_map[chan] < 0)
+		return -EINVAL;
+
+	return ptx1k_mux_slot_map[chan];
+}
+
+/* Table to map from presence bit to mux channel */
+static s8 ptx1k_mux_channel_map[64] = {
+	8, 9, 10, 11, 12, 13, 14, 15,	/* CH_PRS FPC[7:0] */
+	-1, -1, -1, -1, -1, -1, -1, -1,	/* CH_PRS Reserved [15:8] */
+	34,				/* CH_PRS FT[16] */
+	-1, -1, -1, -1,			/* CH_PRS Reserved [20:17] */
+	36, 37,				/* CH_PRS CB[22:21] */
+	35,				/* CH_PRS FPD[23] */
+	-1, -1, -1, -1, -1, -1,		/* CH_PRS Reserved[29:24] */
+	25,				/* CH_PRS MP[30] */
+	-1,				/* CH_PRS Reserved[31]*/
+	28, 29, 30, 31, 32,		/* OTHER_CH_PRS PSM[4:0] */
+	16, 17, 18, 19, 20, 21,		/* OTHER_CH_PRS SIB[10:5] */
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1		/* OTHER_CH_PRS Reserved[31:11] */
+};
+
+static int jnx_cbd_fpga_ptx1k_get_channel(u32 bit)
+{
+	if (bit > 63 || ptx1k_mux_channel_map[bit] < 0)
+		return -EINVAL;
+
+	return ptx1k_mux_channel_map[bit];
+}
+
+struct jnx_cbd_fpga_info jnx_cbd_fpga_ptx1k_info = {
+	.platform = JNX_PRODUCT_POLARIS,
+	.assembly_id = JNX_ID_POLARIS_RCB,
+	.cpld_mux_bitmask = {
+		JNX_CBD_FPGA_PTX1K_CPLD_MUX,
+	},
+	.cpld_prs_set_bitmask = {
+		BIT(30),	/* MP */
+	},
+	.cpld_prs_reg = {
+		JNX_CBD_FPGA_CH_PRS_REG,
+		JNX_CBD_FPGA_CH_OTHER_PRS_REG,
+	},
+	.ch_prs_cb0_bit = JNX_CBD_FPGA_CH_PRS_CB0_PIN,
+	.mux_channels = ARRAY_SIZE(ptx1k_mux_slot_map),
+	.i2cmux_default_chan = JNX_CBD_FPGA_PTX1K_DEFAULT_CHAN,
+	.i2c_ctlr_name = "",
+	.reset = &jnx_cbd_fpga_ptx1k_device_reset,
+	.get_channel = jnx_cbd_fpga_ptx1k_get_channel,
+	.get_slot_from_chan = jnx_cbd_fpga_ptx1k_get_slot_from_channel,
+};
+
+struct jnx_cbd_fpga_info jnx_cbd_fpga_ptx1k_p2_info = {
+	.platform = JNX_PRODUCT_POLARIS,
+	.assembly_id = JNX_ID_POLARIS_RCB_P2,
+	.cpld_mux_bitmask = {
+		JNX_CBD_FPGA_PTX1K_CPLD_MUX,
+	},
+	.cpld_prs_set_bitmask = {
+		BIT(30),	/* MP */
+	},
+	.cpld_prs_reg = {
+		JNX_CBD_FPGA_CH_PRS_REG,
+		JNX_CBD_FPGA_CH_OTHER_PRS_REG,
+	},
+	.ch_prs_cb0_bit = JNX_CBD_FPGA_CH_PRS_CB0_PIN,
+	.mux_channels = ARRAY_SIZE(ptx1k_mux_slot_map),
+	.i2cmux_default_chan = JNX_CBD_FPGA_PTX1K_DEFAULT_CHAN,
+	.i2c_ctlr_name = "",
+	.reset = &jnx_cbd_fpga_ptx1k_device_reset,
+	.get_channel = jnx_cbd_fpga_ptx1k_get_channel,
+	.get_slot_from_chan = jnx_cbd_fpga_ptx1k_get_slot_from_channel,
+};
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c b/drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c
new file mode 100644
index 0000000..d9a4227
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c
@@ -0,0 +1,143 @@
+/*
+ * Juniper PTX 21000 Control Board (CB) FPGA
+ *
+ * Copyright (C) 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/jnx/jnx-subsys.h>
+#include <linux/jnx/board_ids.h>
+#include <linux/bitops.h>
+
+#include "jnx-cbd-fpga-platdata.h"
+#include "jnx-cbd-fpga-core.h"
+#include "jnx-subsys-private.h"
+
+#define JNX_CBD_FPGA_PTX21K_DEVICE_CTRL_REG	0x00C0
+#define JNX_CBD_FPGA_PTX21K_DEFAULT_CHAN	57 /* LOCAL_I2C */
+
+#define JNX_CBD_FPGA_PTX21K_CPLD_MUX		0x00000000
+#define JNX_CBD_FPGA_PTX21K_CPLD_MUX_HI		0x00000000
+
+/* PTX21k has a different bit for the CB slot */
+#define JNX_CBD_FPGA_21K_CH_PRS_CB0_PIN		BIT(30)
+
+#define JNX_CBD_FPGA_21K_CH_PRS_SIB_REG		0x0028
+#define JNX_CBD_FPGA_21K_CH_PRS_SIB_MASK	0x1ff
+
+static u32 jnx_cbd_fpga_ptx21k_bitmask[] = { 0 };
+
+static struct jnx_cbd_fpga_dev_reset_data jnx_cbd_fpga_ptx21k_device_reset = {
+	.reg_offset = JNX_CBD_FPGA_PTX21K_DEVICE_CTRL_REG,
+	.bitmasks = jnx_cbd_fpga_ptx21k_bitmask,
+};
+
+/* Table to map from mux channel number to slot */
+
+static s8 ptx21k_mux_slot_map[72] = {
+	0, 1, 2, 3,			/* 0..3: FPC */
+	4, 5, 6, 7,			/* 4..7: FPC */
+	8, 9, 10, 11,			/* 8..11: FPC */
+	12, 13, 14, 15,			/* 12..15: FPC */
+	16, 17, 18, 19,			/* 16..19: FPC */
+	0, 1, 2, 3,			/* 20..23: PSM */
+	4, 5, 6, 7,			/* 24..27: PSM */
+	8, 9, 10, 11,			/* 28..31: PSM */
+	12, 13, 14, 15,			/* 32..35: PSM */
+	16, 17, 18, 19,			/* 36..39: PSM */
+	20, 21, 22, 23,			/* 40..43: PSM */
+	0, 1, 2, 3, 4, 5,		/* 44..49: FT */
+	0, 1,				/* 50..51: CB */
+	0, 1, 2, 3, 4,			/* 52..56: BP */
+	0,				/* 57:     Local */
+	-1,				/* 58:     QSFP */
+	0,				/* 59:     FPD */
+	0, 1, 2, 3, 4,			/* 60 ..64: SIB */
+	5, 6, 7, 8,			/* 65 ..68: SIB */
+	0,				/* 69:     CB PCIeSW */
+};
+
+static int jnx_cbd_fpga_ptx21k_get_slot_from_channel(int chan)
+{
+	if (chan < 0 || chan >= ARRAY_SIZE(ptx21k_mux_slot_map) ||
+	    ptx21k_mux_slot_map[chan] < 0)
+		return -EINVAL;
+
+	return ptx21k_mux_slot_map[chan];
+}
+
+static s8 ptx21k_mux_channel_map[96] = {
+	0, 1, 2, 3, 4, 5, 6, 7,		/* CH_PRS[7:0]   = FPC[7:0] */
+	8, 9, 10, 11, 12, 13, 14, 15,	/* CH_PRS[15:8]  = FPC[15:8] */
+	16, 17, 18, 19,			/* CH_PRS[19:16] = FPC[19:16] */
+	44, 45, 46, 47,			/* CH_PRS[23:20] = FT[3:0] */
+	50, 51,				/* CH_PRS[25:24] = CB[0:1] */
+	59,				/* CH_PRS[26]    = FPD */
+	-1, -1, -1, -1,			/* CH_PRS[30:27] = Reserved */
+	-1,				/* CH_PRS MP[31] There are 5 MPs*/
+	20, 21, 22, 23, 24, 25, 26, 27,	/* OTHER_CH_PRS[7:0]   = PSM[7:0] */
+	28, 29, 30, 31, 32, 33, 34, 35,	/* OTHER_CH_PRS[15:8]  = PSM[15:8] */
+	36, 37, 38, 39, 40, 41, 42, 43,	/* OTHER_CH_PRS[23:15] = PSM[23:15] */
+	48, 49,				/* OTHER_CH_PRS[25:24] = FT[5:4] */
+	-1, -1, -1, -1, -1, -1,		/* OTHER_CH_PRS[31:26]  QSFP, SFP */
+	60, 61, 62, 63, 64, 65, 66, 67, 68,	/* CH_PRS_SIB[8:0] = SIB[8:0] */
+	-1, -1, -1, -1, -1, -1, -1, -1,	/* CH_PRS_SIB[16:9] = NA */
+	-1, -1, -1, -1, -1, -1, -1, -1,	/* CH_PRS_SIB[17:24] = NA */
+	-1, -1, -1, -1, -1, -1, -1	/* CH_PRS_SIB[25:31] = NA */
+};
+
+static int jnx_cbd_fpga_ptx21k_get_channel(u32 bit)
+{
+	if (bit >= ARRAY_SIZE(ptx21k_mux_channel_map) ||
+	    ptx21k_mux_channel_map[bit] < 0)
+		return -EINVAL;
+
+	return ptx21k_mux_channel_map[bit];
+}
+
+struct jnx_cbd_fpga_info jnx_cbd_fpga_ptx21k_info = {
+	.platform = JNX_PRODUCT_OMEGA,
+	.assembly_id = JNX_ID_OMEGA_RCB,
+	.cpld_mux_bitmask = {
+		JNX_CBD_FPGA_PTX21K_CPLD_MUX,
+		JNX_CBD_FPGA_PTX21K_CPLD_MUX_HI,
+	},
+	.ch_prs_cb0_bit = JNX_CBD_FPGA_21K_CH_PRS_CB0_PIN,
+	.cpld_prs_set_bitmask = {
+		BIT(31),	/* MP */
+	},
+	.cpld_prs_reg = {
+		JNX_CBD_FPGA_CH_PRS_REG,
+		JNX_CBD_FPGA_CH_OTHER_PRS_REG,
+		JNX_CBD_FPGA_21K_CH_PRS_SIB_REG,
+		0
+	},
+	.cpld_prs_bitmask = {
+		0,
+		0,
+		JNX_CBD_FPGA_21K_CH_PRS_SIB_MASK,
+		0
+	},
+	.mux_channels = ARRAY_SIZE(ptx21k_mux_slot_map),
+	.i2cmux_default_chan = JNX_CBD_FPGA_PTX21K_DEFAULT_CHAN,
+	.i2c_ctlr_name = "",
+	.reset = &jnx_cbd_fpga_ptx21k_device_reset,
+	.get_channel = jnx_cbd_fpga_ptx21k_get_channel,
+	.get_slot_from_chan = jnx_cbd_fpga_ptx21k_get_slot_from_channel,
+};
+EXPORT_SYMBOL_GPL(jnx_cbd_fpga_ptx21k_info);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c b/drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c
new file mode 100644
index 0000000..ad575c0
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c
@@ -0,0 +1,111 @@
+/*
+ * Juniper PTX 3000 Control Board (CB) FPGA
+ *
+ * Copyright (C) 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/jnx/jnx-subsys.h>
+#include <linux/jnx/board_ids.h>
+#include <linux/platform_data/i2c-pca-jnx.h>
+#include "jnx-cbd-fpga-core.h"
+#include "jnx-cbd-fpga-platdata.h"
+#include "jnx-subsys-private.h"
+
+#define JNX_CBD_FPGA_PTX3K_DEVICE_CTRL_REG	0x00C0
+#define JNX_CBD_FPGA_PTX3K_I2C_CTRL_RESET	BIT(24)
+#define JNX_CBD_FPGA_PTX3K_DEFAULT_CHAN		0x10
+
+#define JNX_CBD_FPGA_PTX3K_CPLD_MUX		0xFF
+
+static u32 jnx_cbd_fpga_ptx3k_bitmask[] = {
+	JNX_CBD_FPGA_PTX3K_I2C_CTRL_RESET,
+	0
+};
+
+static struct jnx_cbd_fpga_dev_reset_data jnx_cbd_fpga_ptx3k_device_reset = {
+	.reg_offset = JNX_CBD_FPGA_PTX3K_DEVICE_CTRL_REG,
+	.bitmasks = jnx_cbd_fpga_ptx3k_bitmask,
+};
+
+/* Table to map from mux channel number to slot */
+
+static s8 ptx3k_mux_slot_map[42] = {
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,	/* FPC */
+	1,						/* 0x10, local */
+	0, 1,						/* 0x11, CB */
+	-1, -1,
+	0,						/* 0x15, FPD */
+	0,						/* 0x16, MP */
+	0, 1,						/* 0x17, fans */
+	-1, -1, -1,
+	0, 1, 2, 3, 4,					/* 0x1c, PSM */
+	0, 1, 2, 3, 4, 5, 6, 7, 8,			/* 0x21, SIB */
+};
+
+static int jnx_cbd_fpga_ptx3k_get_slot_from_channel(int chan)
+{
+	if (chan < 0 || chan >= ARRAY_SIZE(ptx3k_mux_slot_map) ||
+	    ptx3k_mux_slot_map[chan] < 0)
+		return -EINVAL;
+
+	return ptx3k_mux_slot_map[chan];
+}
+
+/* Table to map from presence bit to mux channel */
+static s8 ptx3k_mux_channel_map[64] = {
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,	/* FPC */
+	0x17, 0x18,						/* fans */
+	-1, -1, -1,
+	0x11, 0x12,						/* CB */
+	0x15,							/* FPD */
+	-1, -1, -1, -1, -1, -1,
+	0x16,							/* MP */
+	-1,
+	0x1c, 0x1d, 0x1e, 0x1f, 0x20,				/* PSM */
+	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,	/* SIB */
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1,
+};
+
+static int jnx_cbd_fpga_ptx3k_get_channel(u32 bit)
+{
+	if (bit > 63 || ptx3k_mux_channel_map[bit] < 0)
+		return -EINVAL;
+
+	return ptx3k_mux_channel_map[bit];
+}
+
+struct jnx_cbd_fpga_info jnx_cbd_fpga_ptx3k_info = {
+	.platform = JNX_PRODUCT_HENDRICKS,
+	.assembly_id = JNX_ID_HENDRICKS_CB,
+	.cpld_mux_bitmask = {
+		JNX_CBD_FPGA_PTX3K_CPLD_MUX,
+	},
+	.cpld_prs_set_bitmask = {
+		BIT(30),
+	},
+	.cpld_prs_reg = {
+		JNX_CBD_FPGA_CH_PRS_REG,
+		JNX_CBD_FPGA_CH_OTHER_PRS_REG,
+	},
+	.ch_prs_cb0_bit = JNX_CBD_FPGA_CH_PRS_CB0_PIN,
+	.mux_channels = 42,
+	.i2cmux_default_chan = JNX_CBD_FPGA_PTX3K_DEFAULT_CHAN,
+	.i2c_ctlr_name = I2C_PCA_JNX_CTLR_NAME,
+	.reset = &jnx_cbd_fpga_ptx3k_device_reset,
+	.get_channel = jnx_cbd_fpga_ptx3k_get_channel,
+	.get_slot_from_chan = jnx_cbd_fpga_ptx3k_get_slot_from_channel,
+};
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c b/drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c
new file mode 100644
index 0000000..11adc8f
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c
@@ -0,0 +1,107 @@
+/*
+ * Juniper PTX 5000 Control Board (CB) FPGA
+ *
+ * Copyright (C) 2012, 2013, 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/jnx/board_ids.h>
+#include <linux/jnx/jnx-subsys.h>
+#include <linux/platform_data/i2c-pca-jnx.h>
+#include "jnx-cbd-fpga-core.h"
+#include "jnx-cbd-fpga-platdata.h"
+#include "jnx-subsys-private.h"
+
+#define JNX_CBD_FPGA_PTX5K_DEVICE_CTRL_REG	0x00C0
+#define JNX_CBD_FPGA_PTX5K_I2C_CTRL_RESET	BIT(24)
+#define JNX_CBD_FPGA_PTX5K_DEFAULT_CHAN		0x10
+
+#define JNX_CBD_FPGA_PTX5K_CPLD_MUX		0xFF
+
+static u32 jnx_cbd_fpga_ptx5k_bitmask[] = {
+	JNX_CBD_FPGA_PTX5K_I2C_CTRL_RESET,
+	0
+};
+
+static struct jnx_cbd_fpga_dev_reset_data jnx_cbd_fpga_ptx5k_device_reset = {
+	.reg_offset = JNX_CBD_FPGA_PTX5K_DEVICE_CTRL_REG,
+	.bitmasks = jnx_cbd_fpga_ptx5k_bitmask,
+};
+
+/* Table to map from mux channel number to slot */
+
+static s8 mux_slot_map[30] = {
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,	/* FPC */
+	1,						/* 0x10, local */
+	0, 1,						/* 0x11, CB */
+	0, 1,						/* 0x13, CCG */
+	0,						/* 0x15, FPD */
+	0,						/* 0x16, MP */
+	0, 1, 2, 3, 4,					/* 0x17, fans */
+	0, 1,						/* 0x1c, PDU */
+};
+
+static int jnx_cbd_fpga_ptx5k_get_slot_from_channel(int chan)
+{
+	if (chan < 0 || chan >= ARRAY_SIZE(mux_slot_map))
+		return -EINVAL;
+
+	return mux_slot_map[chan];
+}
+
+/* Table to map from presence bit to mux channel */
+static s8 mux_channel_map[64] = {
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,	/* FPC */
+	0x17, 0x18, 0x19, 0x1a, 0x1b,				/* fans */
+	0x11, 0x12,						/* CB */
+	0x15,							/* FPD */
+	-1, -1, -1, -1,
+	0x13, 0x14,						/* CCG */
+	0x16,							/* MP */
+	-1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	0x1c, 0x1d,						/* PDU */
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
+static int jnx_cbd_fpga_ptx5k_get_channel(u32 bit)
+{
+	if (bit > 63)
+		return -EINVAL;
+
+	return mux_channel_map[bit];
+}
+
+struct jnx_cbd_fpga_info jnx_cbd_fpga_ptx5k_info = {
+	.platform = JNX_PRODUCT_SANGRIA,
+	.assembly_id = JNX_ID_SNG_CB,
+	.cpld_mux_bitmask = {
+		JNX_CBD_FPGA_PTX5K_CPLD_MUX,
+	},
+	.cpld_prs_set_bitmask = {
+		BIT(30),
+	},
+	.cpld_prs_reg = {
+		JNX_CBD_FPGA_CH_PRS_REG,
+		JNX_CBD_FPGA_CH_OTHER_PRS_REG,
+	},
+	.ch_prs_cb0_bit = JNX_CBD_FPGA_CH_PRS_CB0_PIN,
+	.mux_channels = 30,
+	.i2cmux_default_chan = JNX_CBD_FPGA_PTX5K_DEFAULT_CHAN,
+	.i2c_ctlr_name = I2C_PCA_JNX_CTLR_NAME,
+	.reset = &jnx_cbd_fpga_ptx5k_device_reset,
+	.get_channel = jnx_cbd_fpga_ptx5k_get_channel,
+	.get_slot_from_chan = jnx_cbd_fpga_ptx5k_get_slot_from_channel,
+};
-- 
1.9.1

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

* Re: [PATCH 0/6] Introduce Juniper CBC FPGA
  2016-10-07 15:20 ` Pantelis Antoniou
@ 2016-10-07 15:39   ` Greg Kroah-Hartman
  -1 siblings, 0 replies; 24+ messages in thread
From: Greg Kroah-Hartman @ 2016-10-07 15:39 UTC (permalink / raw)
  To: Pantelis Antoniou
  Cc: Mark Rutland, Alexandre Courbot, devel, devicetree, Frank Rowand,
	Linus Walleij, linux-kernel, JawaharBalaji Thirumalaisamy,
	linux-gpio, Rob Herring, Debjit Ghosh, Mohammad Kamil,
	Georgi Vlaev, Lee Jones, Guenter Roeck

On Fri, Oct 07, 2016 at 06:20:08PM +0300, Pantelis Antoniou wrote:
> Add Juniper's PTX1K CBC FPGA driver. Those FPGAs
> are present in Juniper's PTX series of routers.
> 
> The MFD driver provices a gpio device and a special
> driver for Juniper's board infrastucture.
> The FPGA infrastucture driver is providing an interface
> for user-space handling of the FPGA in those platforms.
> 
> There are full device tree binding documents for the
> master mfd driver and for the slave driver.
> 
> This patchset is against mainline as of today: v4.8-9431-g3477d16
> and is dependent on the "Juniper prerequisites" and
> "Juniper infrastructure" patchsets sent earlier.
> 
> Georgi Vlaev (5):
>   mfd: Add support for the PTX1K CBC FPGA
>   gpio: Add support for PTX1K CBC FPGA spare GPIOs
>   gpio: gpio-cbc: Document bindings of CBC FPGA GPIO block
>   gpio: cbc-presence: Add CBC presence detect as GPIO driver
>   gpio: gpio-cbc-presense: Document bindings of CBC FPGA presence
> 
> Tom Kavanagh (1):
>   staging: jnx: CBD-FPGA infrastructure
> 
>  .../bindings/gpio/jnx,gpio-cbc-presense.txt        |  31 +
>  .../devicetree/bindings/gpio/jnx,gpio-cbc.txt      |  30 +
>  drivers/gpio/Kconfig                               |  23 +
>  drivers/gpio/Makefile                              |   2 +
>  drivers/gpio/gpio-cbc-presence.c                   | 460 ++++++++++
>  drivers/gpio/gpio-cbc.c                            | 236 +++++
>  drivers/mfd/Kconfig                                |  16 +
>  drivers/mfd/Makefile                               |   1 +
>  drivers/mfd/cbc-core.c                             | 971 +++++++++++++++++++++
>  drivers/staging/jnx/Kconfig                        |  34 +
>  drivers/staging/jnx/Makefile                       |   5 +
>  drivers/staging/jnx/jnx-cbc-ptx1k.c                | 242 +++++
>  drivers/staging/jnx/jnx-cbd-fpga-common.c          | 332 +++++++
>  drivers/staging/jnx/jnx-cbd-fpga-common.h          |  27 +
>  drivers/staging/jnx/jnx-cbd-fpga-core.c            | 540 ++++++++++++
>  drivers/staging/jnx/jnx-cbd-fpga-core.h            |  68 ++
>  drivers/staging/jnx/jnx-cbd-fpga-platdata.h        |  51 ++
>  drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c           | 134 +++
>  drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c          | 143 +++
>  drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c           | 111 +++
>  drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c           | 107 +++
>  include/linux/mfd/cbc-core.h                       | 181 ++++
>  22 files changed, 3745 insertions(+)

Please don't mix driver submissions like this.  Staging stuff needs to
go to the staging maintainer, gpio to that one, mfd to that one, and so
on.

there's almost nothing anyone can do with this series as-is, sorry.

please split it up.

thanks,

greg k-h

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

* Re: [PATCH 0/6] Introduce Juniper CBC FPGA
@ 2016-10-07 15:39   ` Greg Kroah-Hartman
  0 siblings, 0 replies; 24+ messages in thread
From: Greg Kroah-Hartman @ 2016-10-07 15:39 UTC (permalink / raw)
  To: Pantelis Antoniou
  Cc: Lee Jones, Mark Rutland, Alexandre Courbot, devel, devicetree,
	Linus Walleij, linux-kernel, JawaharBalaji Thirumalaisamy,
	linux-gpio, Rob Herring, Debjit Ghosh, Mohammad Kamil,
	Georgi Vlaev, Frank Rowand, Guenter Roeck

On Fri, Oct 07, 2016 at 06:20:08PM +0300, Pantelis Antoniou wrote:
> Add Juniper's PTX1K CBC FPGA driver. Those FPGAs
> are present in Juniper's PTX series of routers.
> 
> The MFD driver provices a gpio device and a special
> driver for Juniper's board infrastucture.
> The FPGA infrastucture driver is providing an interface
> for user-space handling of the FPGA in those platforms.
> 
> There are full device tree binding documents for the
> master mfd driver and for the slave driver.
> 
> This patchset is against mainline as of today: v4.8-9431-g3477d16
> and is dependent on the "Juniper prerequisites" and
> "Juniper infrastructure" patchsets sent earlier.
> 
> Georgi Vlaev (5):
>   mfd: Add support for the PTX1K CBC FPGA
>   gpio: Add support for PTX1K CBC FPGA spare GPIOs
>   gpio: gpio-cbc: Document bindings of CBC FPGA GPIO block
>   gpio: cbc-presence: Add CBC presence detect as GPIO driver
>   gpio: gpio-cbc-presense: Document bindings of CBC FPGA presence
> 
> Tom Kavanagh (1):
>   staging: jnx: CBD-FPGA infrastructure
> 
>  .../bindings/gpio/jnx,gpio-cbc-presense.txt        |  31 +
>  .../devicetree/bindings/gpio/jnx,gpio-cbc.txt      |  30 +
>  drivers/gpio/Kconfig                               |  23 +
>  drivers/gpio/Makefile                              |   2 +
>  drivers/gpio/gpio-cbc-presence.c                   | 460 ++++++++++
>  drivers/gpio/gpio-cbc.c                            | 236 +++++
>  drivers/mfd/Kconfig                                |  16 +
>  drivers/mfd/Makefile                               |   1 +
>  drivers/mfd/cbc-core.c                             | 971 +++++++++++++++++++++
>  drivers/staging/jnx/Kconfig                        |  34 +
>  drivers/staging/jnx/Makefile                       |   5 +
>  drivers/staging/jnx/jnx-cbc-ptx1k.c                | 242 +++++
>  drivers/staging/jnx/jnx-cbd-fpga-common.c          | 332 +++++++
>  drivers/staging/jnx/jnx-cbd-fpga-common.h          |  27 +
>  drivers/staging/jnx/jnx-cbd-fpga-core.c            | 540 ++++++++++++
>  drivers/staging/jnx/jnx-cbd-fpga-core.h            |  68 ++
>  drivers/staging/jnx/jnx-cbd-fpga-platdata.h        |  51 ++
>  drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c           | 134 +++
>  drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c          | 143 +++
>  drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c           | 111 +++
>  drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c           | 107 +++
>  include/linux/mfd/cbc-core.h                       | 181 ++++
>  22 files changed, 3745 insertions(+)

Please don't mix driver submissions like this.  Staging stuff needs to
go to the staging maintainer, gpio to that one, mfd to that one, and so
on.

there's almost nothing anyone can do with this series as-is, sorry.

please split it up.

thanks,

greg k-h

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

* Re: [PATCH 0/6] Introduce Juniper CBC FPGA
  2016-10-07 15:39   ` Greg Kroah-Hartman
@ 2016-10-07 15:44       ` Pantelis Antoniou
  -1 siblings, 0 replies; 24+ messages in thread
From: Pantelis Antoniou @ 2016-10-07 15:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Lee Jones, Mark Rutland, Alexandre Courbot,
	devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b, devicetree,
	Linus Walleij,
	<linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>,
	JawaharBalaji Thirumalaisamy, linux-gpio-u79uwXL29TY76Z2rM5mHXA,
	Rob Herring, Debjit Ghosh, Mohammad Kamil, Georgi Vlaev,
	Frank Rowand, Guenter Roeck


> On Oct 7, 2016, at 18:39 , Greg Kroah-Hartman <gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org> wrote:
> 
> On Fri, Oct 07, 2016 at 06:20:08PM +0300, Pantelis Antoniou wrote:
>> Add Juniper's PTX1K CBC FPGA driver. Those FPGAs
>> are present in Juniper's PTX series of routers.
>> 
>> The MFD driver provices a gpio device and a special
>> driver for Juniper's board infrastucture.
>> The FPGA infrastucture driver is providing an interface
>> for user-space handling of the FPGA in those platforms.
>> 
>> There are full device tree binding documents for the
>> master mfd driver and for the slave driver.
>> 
>> This patchset is against mainline as of today: v4.8-9431-g3477d16
>> and is dependent on the "Juniper prerequisites" and
>> "Juniper infrastructure" patchsets sent earlier.
>> 
>> Georgi Vlaev (5):
>>  mfd: Add support for the PTX1K CBC FPGA
>>  gpio: Add support for PTX1K CBC FPGA spare GPIOs
>>  gpio: gpio-cbc: Document bindings of CBC FPGA GPIO block
>>  gpio: cbc-presence: Add CBC presence detect as GPIO driver
>>  gpio: gpio-cbc-presense: Document bindings of CBC FPGA presence
>> 
>> Tom Kavanagh (1):
>>  staging: jnx: CBD-FPGA infrastructure
>> 
>> .../bindings/gpio/jnx,gpio-cbc-presense.txt        |  31 +
>> .../devicetree/bindings/gpio/jnx,gpio-cbc.txt      |  30 +
>> drivers/gpio/Kconfig                               |  23 +
>> drivers/gpio/Makefile                              |   2 +
>> drivers/gpio/gpio-cbc-presence.c                   | 460 ++++++++++
>> drivers/gpio/gpio-cbc.c                            | 236 +++++
>> drivers/mfd/Kconfig                                |  16 +
>> drivers/mfd/Makefile                               |   1 +
>> drivers/mfd/cbc-core.c                             | 971 +++++++++++++++++++++
>> drivers/staging/jnx/Kconfig                        |  34 +
>> drivers/staging/jnx/Makefile                       |   5 +
>> drivers/staging/jnx/jnx-cbc-ptx1k.c                | 242 +++++
>> drivers/staging/jnx/jnx-cbd-fpga-common.c          | 332 +++++++
>> drivers/staging/jnx/jnx-cbd-fpga-common.h          |  27 +
>> drivers/staging/jnx/jnx-cbd-fpga-core.c            | 540 ++++++++++++
>> drivers/staging/jnx/jnx-cbd-fpga-core.h            |  68 ++
>> drivers/staging/jnx/jnx-cbd-fpga-platdata.h        |  51 ++
>> drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c           | 134 +++
>> drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c          | 143 +++
>> drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c           | 111 +++
>> drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c           | 107 +++
>> include/linux/mfd/cbc-core.h                       | 181 ++++
>> 22 files changed, 3745 insertions(+)
> 
> Please don't mix driver submissions like this.  Staging stuff needs to
> go to the staging maintainer, gpio to that one, mfd to that one, and so
> on.
> 
> there's almost nothing anyone can do with this series as-is, sorry.
> 
> please split it up.
> 

Well, it’s an MFD driver, there is dependence; will split up.
> thanks,
> 
> greg k-h

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH 0/6] Introduce Juniper CBC FPGA
@ 2016-10-07 15:44       ` Pantelis Antoniou
  0 siblings, 0 replies; 24+ messages in thread
From: Pantelis Antoniou @ 2016-10-07 15:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Lee Jones, Mark Rutland, Alexandre Courbot, devel, devicetree,
	Linus Walleij, <linux-kernel@vger.kernel.org>,
	JawaharBalaji Thirumalaisamy, linux-gpio, Rob Herring,
	Debjit Ghosh, Mohammad Kamil, Georgi Vlaev, Frank Rowand,
	Guenter Roeck


> On Oct 7, 2016, at 18:39 , Greg Kroah-Hartman <gregkh@linuxfoundation.org> wrote:
> 
> On Fri, Oct 07, 2016 at 06:20:08PM +0300, Pantelis Antoniou wrote:
>> Add Juniper's PTX1K CBC FPGA driver. Those FPGAs
>> are present in Juniper's PTX series of routers.
>> 
>> The MFD driver provices a gpio device and a special
>> driver for Juniper's board infrastucture.
>> The FPGA infrastucture driver is providing an interface
>> for user-space handling of the FPGA in those platforms.
>> 
>> There are full device tree binding documents for the
>> master mfd driver and for the slave driver.
>> 
>> This patchset is against mainline as of today: v4.8-9431-g3477d16
>> and is dependent on the "Juniper prerequisites" and
>> "Juniper infrastructure" patchsets sent earlier.
>> 
>> Georgi Vlaev (5):
>>  mfd: Add support for the PTX1K CBC FPGA
>>  gpio: Add support for PTX1K CBC FPGA spare GPIOs
>>  gpio: gpio-cbc: Document bindings of CBC FPGA GPIO block
>>  gpio: cbc-presence: Add CBC presence detect as GPIO driver
>>  gpio: gpio-cbc-presense: Document bindings of CBC FPGA presence
>> 
>> Tom Kavanagh (1):
>>  staging: jnx: CBD-FPGA infrastructure
>> 
>> .../bindings/gpio/jnx,gpio-cbc-presense.txt        |  31 +
>> .../devicetree/bindings/gpio/jnx,gpio-cbc.txt      |  30 +
>> drivers/gpio/Kconfig                               |  23 +
>> drivers/gpio/Makefile                              |   2 +
>> drivers/gpio/gpio-cbc-presence.c                   | 460 ++++++++++
>> drivers/gpio/gpio-cbc.c                            | 236 +++++
>> drivers/mfd/Kconfig                                |  16 +
>> drivers/mfd/Makefile                               |   1 +
>> drivers/mfd/cbc-core.c                             | 971 +++++++++++++++++++++
>> drivers/staging/jnx/Kconfig                        |  34 +
>> drivers/staging/jnx/Makefile                       |   5 +
>> drivers/staging/jnx/jnx-cbc-ptx1k.c                | 242 +++++
>> drivers/staging/jnx/jnx-cbd-fpga-common.c          | 332 +++++++
>> drivers/staging/jnx/jnx-cbd-fpga-common.h          |  27 +
>> drivers/staging/jnx/jnx-cbd-fpga-core.c            | 540 ++++++++++++
>> drivers/staging/jnx/jnx-cbd-fpga-core.h            |  68 ++
>> drivers/staging/jnx/jnx-cbd-fpga-platdata.h        |  51 ++
>> drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c           | 134 +++
>> drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c          | 143 +++
>> drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c           | 111 +++
>> drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c           | 107 +++
>> include/linux/mfd/cbc-core.h                       | 181 ++++
>> 22 files changed, 3745 insertions(+)
> 
> Please don't mix driver submissions like this.  Staging stuff needs to
> go to the staging maintainer, gpio to that one, mfd to that one, and so
> on.
> 
> there's almost nothing anyone can do with this series as-is, sorry.
> 
> please split it up.
> 

Well, it’s an MFD driver, there is dependence; will split up.
> thanks,
> 
> greg k-h

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

* Re: [PATCH 0/6] Introduce Juniper CBC FPGA
  2016-10-07 15:39   ` Greg Kroah-Hartman
  (?)
  (?)
@ 2016-10-07 18:53   ` Pantelis Antoniou
  2016-10-07 20:37     ` Greg Kroah-Hartman
  -1 siblings, 1 reply; 24+ messages in thread
From: Pantelis Antoniou @ 2016-10-07 18:53 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Lee Jones, Mark Rutland, Alexandre Courbot, devel, devicetree,
	Linus Walleij, <linux-kernel@vger.kernel.org>,
	JawaharBalaji Thirumalaisamy, linux-gpio, Rob Herring,
	Debjit Ghosh, Mohammad Kamil, Georgi Vlaev, Frank Rowand,
	Guenter Roeck

Hi Greg,

> On Oct 7, 2016, at 18:39 , Greg Kroah-Hartman <gregkh@linuxfoundation.org> wrote:
> 
> On Fri, Oct 07, 2016 at 06:20:08PM +0300, Pantelis Antoniou wrote:
>> Add Juniper's PTX1K CBC FPGA driver. Those FPGAs
>> are present in Juniper's PTX series of routers.
>> 
>> The MFD driver provices a gpio device and a special
>> driver for Juniper's board infrastucture.
>> The FPGA infrastucture driver is providing an interface
>> for user-space handling of the FPGA in those platforms.
>> 
>> There are full device tree binding documents for the
>> master mfd driver and for the slave driver.
>> 
>> This patchset is against mainline as of today: v4.8-9431-g3477d16
>> and is dependent on the "Juniper prerequisites" and
>> "Juniper infrastructure" patchsets sent earlier.
>> 
>> Georgi Vlaev (5):
>>  mfd: Add support for the PTX1K CBC FPGA
>>  gpio: Add support for PTX1K CBC FPGA spare GPIOs
>>  gpio: gpio-cbc: Document bindings of CBC FPGA GPIO block
>>  gpio: cbc-presence: Add CBC presence detect as GPIO driver
>>  gpio: gpio-cbc-presense: Document bindings of CBC FPGA presence
>> 
>> Tom Kavanagh (1):
>>  staging: jnx: CBD-FPGA infrastructure
>> 
>> .../bindings/gpio/jnx,gpio-cbc-presense.txt        |  31 +
>> .../devicetree/bindings/gpio/jnx,gpio-cbc.txt      |  30 +
>> drivers/gpio/Kconfig                               |  23 +
>> drivers/gpio/Makefile                              |   2 +
>> drivers/gpio/gpio-cbc-presence.c                   | 460 ++++++++++
>> drivers/gpio/gpio-cbc.c                            | 236 +++++
>> drivers/mfd/Kconfig                                |  16 +
>> drivers/mfd/Makefile                               |   1 +
>> drivers/mfd/cbc-core.c                             | 971 +++++++++++++++++++++
>> drivers/staging/jnx/Kconfig                        |  34 +
>> drivers/staging/jnx/Makefile                       |   5 +
>> drivers/staging/jnx/jnx-cbc-ptx1k.c                | 242 +++++
>> drivers/staging/jnx/jnx-cbd-fpga-common.c          | 332 +++++++
>> drivers/staging/jnx/jnx-cbd-fpga-common.h          |  27 +
>> drivers/staging/jnx/jnx-cbd-fpga-core.c            | 540 ++++++++++++
>> drivers/staging/jnx/jnx-cbd-fpga-core.h            |  68 ++
>> drivers/staging/jnx/jnx-cbd-fpga-platdata.h        |  51 ++
>> drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c           | 134 +++
>> drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c          | 143 +++
>> drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c           | 111 +++
>> drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c           | 107 +++
>> include/linux/mfd/cbc-core.h                       | 181 ++++
>> 22 files changed, 3745 insertions(+)
> 
> Please don't mix driver submissions like this.  Staging stuff needs to
> go to the staging maintainer, gpio to that one, mfd to that one, and so
> on.
> 
> there's almost nothing anyone can do with this series as-is, sorry.
> 
> please split it up.
> 

Now I’m confused, this is a typical MFD submission:

https://lwn.net/Articles/587032/

Looks like it’s normal for a single patchset against multiple subsystems.

Do we have a definitive form for this?

> thanks,
> 
> greg k-h

Regards

— Pantelis


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

* Re: [PATCH 0/6] Introduce Juniper CBC FPGA
  2016-10-07 18:53   ` Pantelis Antoniou
@ 2016-10-07 20:37     ` Greg Kroah-Hartman
  0 siblings, 0 replies; 24+ messages in thread
From: Greg Kroah-Hartman @ 2016-10-07 20:37 UTC (permalink / raw)
  To: Pantelis Antoniou
  Cc: Lee Jones, Mark Rutland, Alexandre Courbot, devel, devicetree,
	Linus Walleij, <linux-kernel@vger.kernel.org>,
	JawaharBalaji Thirumalaisamy, linux-gpio, Rob Herring,
	Debjit Ghosh, Mohammad Kamil, Georgi Vlaev, Frank Rowand,
	Guenter Roeck

On Fri, Oct 07, 2016 at 09:53:29PM +0300, Pantelis Antoniou wrote:
> Hi Greg,
> 
> > On Oct 7, 2016, at 18:39 , Greg Kroah-Hartman <gregkh@linuxfoundation.org> wrote:
> > 
> > On Fri, Oct 07, 2016 at 06:20:08PM +0300, Pantelis Antoniou wrote:
> >> Add Juniper's PTX1K CBC FPGA driver. Those FPGAs
> >> are present in Juniper's PTX series of routers.
> >> 
> >> The MFD driver provices a gpio device and a special
> >> driver for Juniper's board infrastucture.
> >> The FPGA infrastucture driver is providing an interface
> >> for user-space handling of the FPGA in those platforms.
> >> 
> >> There are full device tree binding documents for the
> >> master mfd driver and for the slave driver.
> >> 
> >> This patchset is against mainline as of today: v4.8-9431-g3477d16
> >> and is dependent on the "Juniper prerequisites" and
> >> "Juniper infrastructure" patchsets sent earlier.
> >> 
> >> Georgi Vlaev (5):
> >>  mfd: Add support for the PTX1K CBC FPGA
> >>  gpio: Add support for PTX1K CBC FPGA spare GPIOs
> >>  gpio: gpio-cbc: Document bindings of CBC FPGA GPIO block
> >>  gpio: cbc-presence: Add CBC presence detect as GPIO driver
> >>  gpio: gpio-cbc-presense: Document bindings of CBC FPGA presence
> >> 
> >> Tom Kavanagh (1):
> >>  staging: jnx: CBD-FPGA infrastructure
> >> 
> >> .../bindings/gpio/jnx,gpio-cbc-presense.txt        |  31 +
> >> .../devicetree/bindings/gpio/jnx,gpio-cbc.txt      |  30 +
> >> drivers/gpio/Kconfig                               |  23 +
> >> drivers/gpio/Makefile                              |   2 +
> >> drivers/gpio/gpio-cbc-presence.c                   | 460 ++++++++++
> >> drivers/gpio/gpio-cbc.c                            | 236 +++++
> >> drivers/mfd/Kconfig                                |  16 +
> >> drivers/mfd/Makefile                               |   1 +
> >> drivers/mfd/cbc-core.c                             | 971 +++++++++++++++++++++
> >> drivers/staging/jnx/Kconfig                        |  34 +
> >> drivers/staging/jnx/Makefile                       |   5 +
> >> drivers/staging/jnx/jnx-cbc-ptx1k.c                | 242 +++++
> >> drivers/staging/jnx/jnx-cbd-fpga-common.c          | 332 +++++++
> >> drivers/staging/jnx/jnx-cbd-fpga-common.h          |  27 +
> >> drivers/staging/jnx/jnx-cbd-fpga-core.c            | 540 ++++++++++++
> >> drivers/staging/jnx/jnx-cbd-fpga-core.h            |  68 ++
> >> drivers/staging/jnx/jnx-cbd-fpga-platdata.h        |  51 ++
> >> drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c           | 134 +++
> >> drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c          | 143 +++
> >> drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c           | 111 +++
> >> drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c           | 107 +++
> >> include/linux/mfd/cbc-core.h                       | 181 ++++
> >> 22 files changed, 3745 insertions(+)
> > 
> > Please don't mix driver submissions like this.  Staging stuff needs to
> > go to the staging maintainer, gpio to that one, mfd to that one, and so
> > on.
> > 
> > there's almost nothing anyone can do with this series as-is, sorry.
> > 
> > please split it up.
> > 
> 
> Now I’m confused, this is a typical MFD submission:
> 
> https://lwn.net/Articles/587032/
> 
> Looks like it’s normal for a single patchset against multiple subsystems.

Not when it crosses the drivers/staging/ boundry.

> Do we have a definitive form for this?

Either submit all of this stuff "properly", or put it in staging, don't
cross the boundry if at all possible, it just causes a lot of confusion
and headache as the staging stuff could be deleted at any time.

You still haven't explained why you feel drivers/staging/ is the right
place for this codebase.  Again, why not just submit it "properly"?

thanks,

greg k-h

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

* Re: [PATCH 2/6] gpio: Add support for PTX1K CBC FPGA spare GPIOs
  2016-10-07 15:20   ` Pantelis Antoniou
  (?)
@ 2016-10-21  8:44   ` Linus Walleij
  -1 siblings, 0 replies; 24+ messages in thread
From: Linus Walleij @ 2016-10-21  8:44 UTC (permalink / raw)
  To: Pantelis Antoniou
  Cc: Lee Jones, Alexandre Courbot, Rob Herring, Mark Rutland,
	Frank Rowand, Greg Kroah-Hartman, Debjit Ghosh, Georgi Vlaev,
	Guenter Roeck, JawaharBalaji Thirumalaisamy, Mohammad Kamil,
	devicetree, linux-kernel, linux-gpio, devel

On Fri, Oct 7, 2016 at 5:20 PM, Pantelis Antoniou
<pantelis.antoniou@konsulko.com> wrote:

> From: Georgi Vlaev <gvlaev@juniper.net>
>
> Add support for the GPIO block in Juniper's CBC FPGA.
>
> A number of GPIOs exported by different kind of boards
> is supported.
>
> Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
> Signed-off-by: Guenter Roeck <groeck@juniper.net>
> [Ported from Juniper kernel]
> Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>

Again pretty much the same comments.

The drivers not supporting IRQ will be pretty quick and
easy to fix up I guess, the IRQ drivers will require some
effort. An approach is to split these in two: one for the basic
GPIO and a separate add-on patch for the IRQ functionality.

Yours,
Linus Walleij

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

* Re: [PATCH 3/6] gpio: gpio-cbc: Document bindings of CBC FPGA GPIO block
  2016-10-07 15:20   ` Pantelis Antoniou
@ 2016-10-21  8:45     ` Linus Walleij
  -1 siblings, 0 replies; 24+ messages in thread
From: Linus Walleij @ 2016-10-21  8:45 UTC (permalink / raw)
  To: Pantelis Antoniou
  Cc: Mark Rutland, Alexandre Courbot, devel, devicetree, Frank Rowand,
	linux-kernel, JawaharBalaji Thirumalaisamy, linux-gpio,
	Rob Herring, Debjit Ghosh, Greg Kroah-Hartman, Mohammad Kamil,
	Georgi Vlaev, Lee Jones, Guenter Roeck

On Fri, Oct 7, 2016 at 5:20 PM, Pantelis Antoniou
<pantelis.antoniou@konsulko.com> wrote:

> From: Georgi Vlaev <gvlaev@juniper.net>
>
> Add device tree bindings document for the GPIO driver of
> Juniper's CBC FPGA.
>
> Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
> [Ported from Juniper kernel]
> Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
> ---
>  .../devicetree/bindings/gpio/jnx,gpio-cbc.txt      | 30 ++++++++++++++++++++++
>  1 file changed, 30 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/gpio/jnx,gpio-cbc.txt
>
> diff --git a/Documentation/devicetree/bindings/gpio/jnx,gpio-cbc.txt b/Documentation/devicetree/bindings/gpio/jnx,gpio-cbc.txt
> new file mode 100644
> index 0000000..d205d0b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/gpio/jnx,gpio-cbc.txt
> @@ -0,0 +1,30 @@
> +Juniper CBC FPGA GPIO block
> +
> +Required properties:
> +
> +- compatible:
> +    Must be "jnx,cbc-gpio"
> +
> +Optional properties:
> +
> +- reg:
> +    Address and length of the register set for the device. This is optional since
> +    usually the parent MFD driver fills it in.
> +
> +- #gpio-cells:
> +    Should be <2>.  The first cell is the pin number (within the controller's
> +    pin space), and the second is used for the following flags:
> +       bit[0]: direction (0 = out, 1 = in)
> +       bit[1]: init high
> +       bit[2]: active low

Can't you just refer to the generic bindings?

Apart from that it looks fine.

Yours,
Linus Walleij

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

* Re: [PATCH 3/6] gpio: gpio-cbc: Document bindings of CBC FPGA GPIO block
@ 2016-10-21  8:45     ` Linus Walleij
  0 siblings, 0 replies; 24+ messages in thread
From: Linus Walleij @ 2016-10-21  8:45 UTC (permalink / raw)
  To: Pantelis Antoniou
  Cc: Lee Jones, Alexandre Courbot, Rob Herring, Mark Rutland,
	Frank Rowand, Greg Kroah-Hartman, Debjit Ghosh, Georgi Vlaev,
	Guenter Roeck, JawaharBalaji Thirumalaisamy, Mohammad Kamil,
	devicetree, linux-kernel, linux-gpio, devel

On Fri, Oct 7, 2016 at 5:20 PM, Pantelis Antoniou
<pantelis.antoniou@konsulko.com> wrote:

> From: Georgi Vlaev <gvlaev@juniper.net>
>
> Add device tree bindings document for the GPIO driver of
> Juniper's CBC FPGA.
>
> Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
> [Ported from Juniper kernel]
> Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
> ---
>  .../devicetree/bindings/gpio/jnx,gpio-cbc.txt      | 30 ++++++++++++++++++++++
>  1 file changed, 30 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/gpio/jnx,gpio-cbc.txt
>
> diff --git a/Documentation/devicetree/bindings/gpio/jnx,gpio-cbc.txt b/Documentation/devicetree/bindings/gpio/jnx,gpio-cbc.txt
> new file mode 100644
> index 0000000..d205d0b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/gpio/jnx,gpio-cbc.txt
> @@ -0,0 +1,30 @@
> +Juniper CBC FPGA GPIO block
> +
> +Required properties:
> +
> +- compatible:
> +    Must be "jnx,cbc-gpio"
> +
> +Optional properties:
> +
> +- reg:
> +    Address and length of the register set for the device. This is optional since
> +    usually the parent MFD driver fills it in.
> +
> +- #gpio-cells:
> +    Should be <2>.  The first cell is the pin number (within the controller's
> +    pin space), and the second is used for the following flags:
> +       bit[0]: direction (0 = out, 1 = in)
> +       bit[1]: init high
> +       bit[2]: active low

Can't you just refer to the generic bindings?

Apart from that it looks fine.

Yours,
Linus Walleij

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

* Re: [PATCH 4/6] gpio: cbc-presence: Add CBC presence detect as GPIO driver
  2016-10-07 15:20   ` Pantelis Antoniou
  (?)
@ 2016-10-21  8:49   ` Linus Walleij
  -1 siblings, 0 replies; 24+ messages in thread
From: Linus Walleij @ 2016-10-21  8:49 UTC (permalink / raw)
  To: Pantelis Antoniou
  Cc: Lee Jones, Alexandre Courbot, Rob Herring, Mark Rutland,
	Frank Rowand, Greg Kroah-Hartman, Debjit Ghosh, Georgi Vlaev,
	Guenter Roeck, JawaharBalaji Thirumalaisamy, Mohammad Kamil,
	devicetree, linux-kernel, linux-gpio, devel

On Fri, Oct 7, 2016 at 5:20 PM, Pantelis Antoniou
<pantelis.antoniou@konsulko.com> wrote:

> From: Georgi Vlaev <gvlaev@juniper.net>
>
> This driver exports the CB FPGA presence detect bits from a
> single 32bit CB register as GPIOs.
>
> Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
> Signed-off-by: Guenter Roeck <groeck@juniper.net>
> [Ported from Juniper kernel]
> Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>

This needs some more verbose commit message and explanation.

Note: GPIO = General Purpose Input/Output.

This doesn't really sound like general purpose, more like special
purpose.

Consider drivers/input and drivers/connector classes for example.

> +config GPIO_CBC_PRESENCE
> +       tristate "Juniper Networks PTX1K CBC presence detect as GPIO"
> +       depends on MFD_JUNIPER_CBC && OF
> +       default y if JNX_CONNECTOR
> +       help
> +         This driver exports the CH_PRS and OTHER_CH_PRS presence detect
> +         bits of the PTX1K RCB CBC FPGA as GPIOs on the relevant Juniper
> +         platforms. Select if JNX_CONNECTOR is selected.
> +
> +         This driver can also be built as a module.  If so, the module
> +         will be called gpio-cbc-presence.

At least tell us *what* it is detecting the presence of.

Apart from this it has some of the same issues pointed out in the
other drivers, correct these as well.

Yours,
Linus Walleij

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

end of thread, other threads:[~2016-10-21  8:49 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-10-07 15:20 [PATCH 0/6] Introduce Juniper CBC FPGA Pantelis Antoniou
2016-10-07 15:20 ` Pantelis Antoniou
2016-10-07 15:20 ` [PATCH 1/6] mfd: Add support for the PTX1K " Pantelis Antoniou
2016-10-07 15:20   ` Pantelis Antoniou
2016-10-07 15:20 ` [PATCH 2/6] gpio: Add support for PTX1K CBC FPGA spare GPIOs Pantelis Antoniou
2016-10-07 15:20   ` Pantelis Antoniou
2016-10-21  8:44   ` Linus Walleij
2016-10-07 15:20 ` [PATCH 3/6] gpio: gpio-cbc: Document bindings of CBC FPGA GPIO block Pantelis Antoniou
2016-10-07 15:20   ` Pantelis Antoniou
2016-10-21  8:45   ` Linus Walleij
2016-10-21  8:45     ` Linus Walleij
2016-10-07 15:20 ` [PATCH 4/6] gpio: cbc-presence: Add CBC presence detect as GPIO driver Pantelis Antoniou
2016-10-07 15:20   ` Pantelis Antoniou
2016-10-21  8:49   ` Linus Walleij
2016-10-07 15:20 ` [PATCH 5/6] gpio: gpio-cbc-presense: Document bindings of CBC FPGA presence Pantelis Antoniou
2016-10-07 15:20   ` Pantelis Antoniou
2016-10-07 15:20 ` [PATCH 6/6] staging: jnx: CBD-FPGA infrastructure Pantelis Antoniou
2016-10-07 15:20   ` Pantelis Antoniou
2016-10-07 15:39 ` [PATCH 0/6] Introduce Juniper CBC FPGA Greg Kroah-Hartman
2016-10-07 15:39   ` Greg Kroah-Hartman
     [not found]   ` <20161007153944.GA13519-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
2016-10-07 15:44     ` Pantelis Antoniou
2016-10-07 15:44       ` Pantelis Antoniou
2016-10-07 18:53   ` Pantelis Antoniou
2016-10-07 20:37     ` Greg Kroah-Hartman

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