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
From: Tom Kavanagh <[email protected]>
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 <[email protected]>
Signed-off-by: Guenter Roeck <[email protected]>
Signed-off-by: JawaharBalaji Thirumalaisamy <[email protected]>
Signed-off-by: Mohammad Kamil <[email protected]>
Signed-off-by: Tom Kavanagh <[email protected]>
Signed-off-by: Debjit Ghosh <[email protected]>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <[email protected]>
---
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 <[email protected]>");
+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
From: Georgi Vlaev <[email protected]>
Add device tree bindings document for the GPIO driver of
Juniper's CBC FPGA.
Signed-off-by: Georgi Vlaev <[email protected]>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <[email protected]>
---
.../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
From: Georgi Vlaev <[email protected]>
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 <[email protected]>
Signed-off-by: Guenter Roeck <[email protected]>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <[email protected]>
---
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 <[email protected]>");
+MODULE_LICENSE("GPL");
--
1.9.1
From: Georgi Vlaev <[email protected]>
This driver exports the CB FPGA presence detect bits from a
single 32bit CB register as GPIOs.
Signed-off-by: Georgi Vlaev <[email protected]>
Signed-off-by: Guenter Roeck <[email protected]>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <[email protected]>
---
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 <[email protected]>
+ *
+ * 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 <[email protected]>");
+MODULE_LICENSE("GPL");
--
1.9.1
From: Georgi Vlaev <[email protected]>
Add device tree bindings document for the presence virtual GPIOs
on Juniper's CBC FPGA.
Signed-off-by: Georgi Vlaev <[email protected]>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <[email protected]>
---
.../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
From: Georgi Vlaev <[email protected]>
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 <[email protected]>
Signed-off-by: Guenter Roeck <[email protected]>
Signed-off-by: Rajat Jain <[email protected]>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <[email protected]>
---
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 <[email protected]>
+ * 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 <[email protected]>");
+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 <[email protected]>
+ *
+ * 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
> On Oct 7, 2016, at 18:39 , Greg Kroah-Hartman <[email protected]> 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
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
Hi Greg,
> On Oct 7, 2016, at 18:39 , Greg Kroah-Hartman <[email protected]> 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
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 <[email protected]> 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
On Fri, Oct 7, 2016 at 5:20 PM, Pantelis Antoniou
<[email protected]> wrote:
> From: Georgi Vlaev <[email protected]>
>
> 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 <[email protected]>
> Signed-off-by: Guenter Roeck <[email protected]>
> [Ported from Juniper kernel]
> Signed-off-by: Pantelis Antoniou <[email protected]>
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
On Fri, Oct 7, 2016 at 5:20 PM, Pantelis Antoniou
<[email protected]> wrote:
> From: Georgi Vlaev <[email protected]>
>
> Add device tree bindings document for the GPIO driver of
> Juniper's CBC FPGA.
>
> Signed-off-by: Georgi Vlaev <[email protected]>
> [Ported from Juniper kernel]
> Signed-off-by: Pantelis Antoniou <[email protected]>
> ---
> .../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
On Fri, Oct 7, 2016 at 5:20 PM, Pantelis Antoniou
<[email protected]> wrote:
> From: Georgi Vlaev <[email protected]>
>
> This driver exports the CB FPGA presence detect bits from a
> single 32bit CB register as GPIOs.
>
> Signed-off-by: Georgi Vlaev <[email protected]>
> Signed-off-by: Guenter Roeck <[email protected]>
> [Ported from Juniper kernel]
> Signed-off-by: Pantelis Antoniou <[email protected]>
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