* [PATCH 1/6] mfd: Add support for the PTX1K CBC FPGA
2016-10-07 15:20 [PATCH 0/6] Introduce Juniper CBC FPGA Pantelis Antoniou
@ 2016-10-07 15:20 ` Pantelis Antoniou
2016-10-07 15:20 ` [PATCH 2/6] gpio: Add support for PTX1K CBC FPGA spare GPIOs Pantelis Antoniou
` (5 subsequent siblings)
6 siblings, 0 replies; 14+ messages in thread
From: Pantelis Antoniou @ 2016-10-07 15:20 UTC (permalink / raw)
To: Lee Jones
Cc: Mark Rutland, Alexandre Courbot, devel, devicetree,
Greg Kroah-Hartman, Linus Walleij, Pantelis Antoniou,
linux-kernel, JawaharBalaji Thirumalaisamy, linux-gpio,
Rob Herring, Debjit Ghosh, Mohammad Kamil, Georgi Vlaev,
Frank Rowand, Guenter Roeck
From: Georgi Vlaev <gvlaev@juniper.net>
The CBC intergrates CB and SAM on single FPGA. This is a PCI MFD driver
and provides support for the following functions as subdrivers:
* SAM I2C accelerator
* SAM MTD flash
* CBC spare GPIOs
* CBC JNX infrastructure
Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
Signed-off-by: Guenter Roeck <groeck@juniper.net>
Signed-off-by: Rajat Jain <rajatjain@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
drivers/mfd/Kconfig | 16 +
drivers/mfd/Makefile | 1 +
drivers/mfd/cbc-core.c | 971 +++++++++++++++++++++++++++++++++++++++++++
include/linux/mfd/cbc-core.h | 181 ++++++++
4 files changed, 1169 insertions(+)
create mode 100644 drivers/mfd/cbc-core.c
create mode 100644 include/linux/mfd/cbc-core.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 7e1fa14..6107f7a 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1384,6 +1384,22 @@ config MFD_JUNIPER_EXT_CPLD
called "ptxpmb-ext-cpld"
+config MFD_JUNIPER_CBC
+ tristate "Juniper PTX1K CBC FPGA"
+ depends on JNX_PTX1K_RCB
+ default y if JNX_PTX1K_RCB
+ select MFD_CORE
+ select MTD
+ select MTD_SAM_FLASH
+ select I2C_SAM
+ select GPIO_CBC_PRESENCE if JNX_CONNECTOR
+ help
+ Select this to enable the CBC FPGA multi-function kernel driver.
+ This FPGA is present on the PTX1K RCB Juniper platform.
+
+ This driver can be built as a module. If built as a module it will be
+ called "cbc-core"
+
config MFD_TWL4030_AUDIO
bool "TI TWL4030 Audio"
depends on TWL4030_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index da94482..0ea6dc6 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -151,6 +151,7 @@ obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o
obj-$(CONFIG_MFD_JUNIPER_CPLD) += ptxpmb-cpld-core.o
obj-$(CONFIG_MFD_JUNIPER_SAM) += sam-core.o
obj-$(CONFIG_MFD_JUNIPER_EXT_CPLD) += ptxpmb-ext-cpld-core.o
+obj-$(CONFIG_MFD_JUNIPER_CBC) += cbc-core.o
obj-$(CONFIG_MFD_DB8500_PRCMU) += db8500-prcmu.o
# ab8500-core need to come after db8500-prcmu (which provides the channel)
obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o
diff --git a/drivers/mfd/cbc-core.c b/drivers/mfd/cbc-core.c
new file mode 100644
index 0000000..1e6c95b
--- /dev/null
+++ b/drivers/mfd/cbc-core.c
@@ -0,0 +1,971 @@
+/*
+ * drivers/mfd/cbc-core.c
+ *
+ * Copyright (c) 2014, Juniper Networks
+ * Author: Georgi Vlaev <gvlaev@juniper.net>
+ * Based on: sam-core.c
+ *
+ * The CBC FPGA intergrates the PTX1K CB and some functions of
+ * the SAM FPGA on single device. We're reusing the SAM I2C,
+ * MTD flash drivers. The JNX CB logic is implemented in
+ * drivers/jnx/jnx-cbc-ptx1k.c and drivers/jnx/jnx-cbd-fpga-ptx1k.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/irqdomain.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/sam.h>
+#include <linux/jnx/pci_ids.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/cbc-core.h>
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/string.h>
+#include <linux/ctype.h>
+#endif
+
+enum {
+ CBC_CELL_SAM_I2C, /* SAM I2C accelerator */
+ CBC_CELL_SAM_MTD, /* SAM EPCS64 config flash */
+ CBC_CELL_GPIO, /* CBC spare GPIO */
+ CBC_CELL_JNX_CBD, /* CBC CB JNX driver */
+ CBC_CELL_GPIO_PRS, /* CBC presence as GPIO (CH_PRS) */
+ CBC_CELL_GPIO_PRS_OTHER,/* CBC presence ss GPIO (OTHER_CH_PRS) */
+ CBC_CELL_GPIO_PRS_SIB, /* CBC presence as GPIO (CH_PRS_SIB) */
+ CBC_NUM_MFD_CELLS
+};
+
+#define CBC_IRQ_NR 31
+#define CBC_RES_NR 2 /* iomem, irq */
+#define CBC_RES_NR_NOIRQ 1 /* iomem */
+
+struct cbc_fpga_data {
+ void __iomem *membase;
+ struct pci_dev *pdev;
+ u32 fpga_rev; /* CBC revision */
+ u32 fpga_date; /* CBC revision date */
+ int i2c_mstr_count; /* CBC/I2C SAM master count */
+ struct irq_domain *irq_domain;
+ u32 int_src; /* interrupt src reg (MSI, legacy) */
+ u32 int_en; /* interrupt en reg (MSI, legacy) */
+ spinlock_t irq_lock;
+ struct mfd_cell mfd_cells[CBC_NUM_MFD_CELLS];
+ struct resource mfd_i2c_resources[CBC_RES_NR];
+ struct resource mfd_mtd_resources[CBC_RES_NR_NOIRQ];
+ struct resource mfd_gpio_resources[CBC_RES_NR_NOIRQ];
+ struct resource mfd_jnx_resources[CBC_RES_NR_NOIRQ];
+ struct resource mfd_gpio_prs_resources[CBC_RES_NR_NOIRQ];
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *cbc_debugfs_dir;
+ u32 debug_address; /* any register offsset */
+ struct debugfs_regset32 *regset; /* all regs */
+ u32 test_int_cnt; /* TEST_INT counter */
+ u32 i2c_accel_cnt; /* INT_I2C_ACCEL cnt */
+ u32 i2c_accel_empty_cnt; /* INT_I2C_ACCEL cnt && !CBC_STATUS_IRQ_EN */
+#endif
+};
+
+/* debugfs */
+/* TODO: split into a separate file */
+#ifdef CONFIG_DEBUG_FS
+
+#define dump_register(n) \
+{ \
+ .name = __stringify(n), \
+ .offset = n, \
+}
+
+static const struct debugfs_reg32 cbc_regs[] = {
+ dump_register(CBC_TOP_REGS_INT_SRC),
+ dump_register(CBC_TOP_REGS_INT_EN),
+ dump_register(CBC_TOP_REGS_I2C_SEL),
+ dump_register(CBC_TOP_REGS_CH_PRS),
+ dump_register(CBC_TOP_REGS_RTL_REVISION),
+ dump_register(CBC_TOP_REGS_PWR_STATUS),
+ dump_register(CBC_TOP_REGS_I2CS_INT),
+ dump_register(CBC_TOP_REGS_I2CS_INT_EN),
+ dump_register(CBC_TOP_REGS_MSTR_CONFIG),
+ dump_register(CBC_TOP_REGS_MSTR_ALIVE),
+ dump_register(CBC_TOP_REGS_MSTR_ALIVE_CNT),
+ dump_register(CBC_TOP_REGS_FPGA_REV),
+ dump_register(CBC_TOP_REGS_FPGA_DATE),
+ dump_register(CBC_TOP_REGS_DEVICE_CTRL),
+ dump_register(CBC_TOP_REGS_SLOT_ID),
+ dump_register(CBC_TOP_REGS_SCRATCH),
+ dump_register(CBC_TOP_REGS_RE_HALT),
+ dump_register(CBC_TOP_REGS_OTHER_CH_PRS_CHANGE),
+ dump_register(CBC_TOP_REGS_FPC_SPARE_A),
+ dump_register(CBC_TOP_REGS_FT_SPARE),
+ dump_register(CBC_TOP_REGS_CB_SPARE),
+ dump_register(CBC_TOP_REGS_MTTR_0),
+ dump_register(CBC_TOP_REGS_MTTR_1),
+ dump_register(CBC_TOP_REGS_MTTR_2),
+ dump_register(CBC_TOP_REGS_MSTR_FRC),
+ dump_register(CBC_TOP_REGS_MSTR_RSN),
+ dump_register(CBC_TOP_REGS_ME_WRITE_0),
+ dump_register(CBC_TOP_REGS_ME_WRITE_1),
+ dump_register(CBC_TOP_REGS_ME_WRITE_2),
+ dump_register(CBC_TOP_REGS_ME_WRITE_3),
+ dump_register(CBC_TOP_REGS_ME_WRITE_4),
+ dump_register(CBC_TOP_REGS_ME_WRITE_5),
+ dump_register(CBC_TOP_REGS_ME_READ_0),
+ dump_register(CBC_TOP_REGS_ME_READ_1),
+ dump_register(CBC_TOP_REGS_ME_READ_2),
+ dump_register(CBC_TOP_REGS_ME_READ_3),
+ dump_register(CBC_TOP_REGS_ME_READ_4),
+ dump_register(CBC_TOP_REGS_ME_READ_5),
+ dump_register(CBC_TOP_REGS_ME_STATUS),
+ dump_register(CBC_TOP_REGS_LIU_CONFIG),
+ dump_register(CBC_TOP_REGS_CBC_8112_8614_RST),
+ dump_register(CBC_TOP_REGS_CCG_CONFIG),
+ dump_register(CBC_TOP_REGS_SFPP_CONTROL),
+ dump_register(CBC_TOP_REGS_SFPP_PCIE_FT_STATUS),
+ dump_register(CBC_TOP_REGS_GPIO_CTRL),
+ dump_register(CBC_TOP_REGS_GPIO_OUTPUT_DATA),
+ dump_register(CBC_TOP_REGS_GPIO_INPUT_DATA),
+ dump_register(CBC_TOP_REGS_CCG_STATUS_RT),
+ dump_register(CBC_TOP_REGS_CCG_STATUS_LATCHED),
+ dump_register(CBC_TOP_REGS_CCG_STATUS_INTERRUPT_ENABLE),
+ dump_register(CBC_TOP_REGS_OTHER_LED),
+ dump_register(CBC_TOP_REGS_SFPP_I2C_RW_CONTROL),
+ dump_register(CBC_TOP_REGS_SFPP_I2C_REG_ADDRESS),
+ dump_register(CBC_TOP_REGS_SFPP_I2C_WDATA),
+ dump_register(CBC_TOP_REGS_SFPP_I2C_RDATA),
+ dump_register(CBC_TOP_REGS_SFPP_I2C_STATUS),
+ dump_register(CBC_TOP_REGS_SFPP_I2C_DEV_ADDRESS),
+ dump_register(CBC_TOP_REGS_SIB_SPARE_OUTPUTENABLE),
+ dump_register(CBC_TOP_REGS_SIB_SPARE_OUTPUT),
+ dump_register(CBC_TOP_REGS_SIB_SPARE_INPUT),
+ dump_register(CBC_TOP_REGS_FPC_SPARE_OUTPUTENABLE),
+ dump_register(CBC_TOP_REGS_FPC_SPARE_OUTPUT),
+ dump_register(CBC_TOP_REGS_FPC_SPARE_INPUT),
+ dump_register(CBC_TOP_REGS_OTHER_SPARE_OUTPUTENABLE),
+ dump_register(CBC_TOP_REGS_OTHER_SPARE_OUTPUT),
+ dump_register(CBC_TOP_REGS_OTHER_SPARE_INPUT),
+ dump_register(CBC_TOP_REGS_MSI_INT_SRC),
+ dump_register(CBC_TOP_REGS_MSI_INT_EN),
+ dump_register(CBC_TOP_REGS_TOD_LOCK),
+ dump_register(CBC_TOP_REGS_TOD_TIME_79_48),
+ dump_register(CBC_TOP_REGS_TOD_TIME_47_16),
+ dump_register(CBC_TOP_REGS_TOD_TIME_15_0),
+ dump_register(CBC_TOP_REGS_TOD_CLKACC_CRC),
+ dump_register(CBC_TOP_REGS_APS_COMMAND_REGISTER),
+ dump_register(CBC_TOP_REGS_APS_STATUS_REGISTER),
+ dump_register(CBC_TOP_REGS_APS_FRAME_DATA0),
+ dump_register(CBC_TOP_REGS_APS_FRAME_DATA1),
+ dump_register(CBC_TOP_REGS_APS_FRAME_DATA2),
+ dump_register(CBC_TOP_REGS_APS_FRAME_DATA3),
+ dump_register(CBC_TOP_REGS_APS_FRAME_DATA4),
+ dump_register(CBC_TOP_REGS_APS_FRAME_DATA5),
+ dump_register(CBC_TOP_REGS_APS_FRAME_DATA6),
+ dump_register(CBC_TOP_REGS_APS_FRAME_DATA7),
+ dump_register(CBC_TOP_REGS_APS_APS_REV_REG),
+ dump_register(CBC_TOP_REGS_APS_APS_DEBUG0),
+ dump_register(CBC_TOP_REGS_APS_COUNTER_GOOD_FRAMES),
+ dump_register(CBC_TOP_REGS_APS_COUNTER_BAD_FRAMES),
+ dump_register(CBC_TOP_REGS_APS_APS_LINK_STATUS),
+ dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG0),
+ dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG1),
+ dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG2),
+ dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG3),
+ dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG4),
+ dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG5),
+ dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG6),
+ dump_register(CBC_TOP_REGS_APS_INTERRUPT_LOG7),
+ dump_register(CBC_INFO_I2C_MASTER_COUNT),
+ dump_register(CBC_INFO_FAST_I2C_CONFIG_A),
+ dump_register(CBC_INFO_FAST_I2C_CONFIG_B),
+ dump_register(CBC_INFO_FEATURES),
+ dump_register(CBC_INFO_PMA_CONTROL),
+ dump_register(CBC_INFO_PMA_STATUS),
+ dump_register(CBC_STATUS_IRQ_EN),
+ dump_register(CBC_STATUS_IRQ_ST),
+ dump_register(CBC_REMOTE_UPGRADE_CONTROL),
+ dump_register(CBC_REMOTE_UPGRADE_STATUS),
+ dump_register(CBC_FLASH_IF_ADDRESS),
+ dump_register(CBC_FLASH_IF_BYTE_COUNT),
+ dump_register(CBC_FLASH_IF_CONTROL),
+ dump_register(CBC_FLASH_IF_STATUS),
+ dump_register(CBC_FLASH_IF_WRITE_DATA),
+ dump_register(CBC_FLASH_IF_READ_DATA),
+};
+
+/*
+ * Usage:
+ * #echo ADDR > <debugfs>/cbc-core/register-address
+ * #cat <debugfs>/cbc-core/register-value
+ *
+ */
+static int cbc_debugfs_addr_print(struct seq_file *s, void *p)
+{
+ struct cbc_fpga_data *cbc = (struct cbc_fpga_data *)s->private;
+
+ seq_printf(s, "0x%08X\n", cbc->debug_address);
+
+ return 0;
+}
+
+static int cbc_debugfs_addr_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cbc_debugfs_addr_print, inode->i_private);
+}
+
+static ssize_t cbc_debugfs_addr_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct cbc_fpga_data *cbc =
+ ((struct seq_file *)(file->private_data))->private;
+ unsigned long user_address;
+ int err;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &user_address);
+ if (err)
+ return err;
+
+ if (user_address > 0xffffff) {
+ dev_err(&cbc->pdev->dev, "debugfs error input > 0xffffff\n");
+ return -EINVAL;
+ }
+ cbc->debug_address = user_address;
+
+ return count;
+}
+
+static const struct file_operations cbc_debugfs_addr_fops = {
+ .open = cbc_debugfs_addr_open,
+ .write = cbc_debugfs_addr_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int cbc_debugfs_val_print(struct seq_file *s, void *p)
+{
+ struct cbc_fpga_data *cbc = (struct cbc_fpga_data *)s->private;
+
+ seq_printf(s, "0x%08X\n", ioread32(cbc->membase + cbc->debug_address));
+
+ return 0;
+}
+
+static int cbc_debugfs_val_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cbc_debugfs_val_print, inode->i_private);
+}
+
+static ssize_t cbc_debugfs_val_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct cbc_fpga_data *cbc =
+ ((struct seq_file *)(file->private_data))->private;
+ unsigned long user_val;
+ int err;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &user_val);
+ if (err)
+ return err;
+
+ iowrite32(user_val, cbc->membase + cbc->debug_address);
+ ioread32(cbc->membase + cbc->debug_address);
+
+ return count;
+}
+
+static const struct file_operations cbc_debugfs_val_fops = {
+ .open = cbc_debugfs_val_open,
+ .write = cbc_debugfs_val_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+/*
+ * Usage:
+ * #echo 1 > <debugfs>/cbc-core/test-int
+ * #cat <debugfs>/cbc-core/test-int
+ *
+ */
+static void cbc_fire_test_int(struct cbc_fpga_data *cbc)
+{
+ u32 int_en;
+
+ /* Disable and enable the TEST_INT bit */
+ int_en = ioread32(cbc->membase + cbc->int_en);
+ iowrite32(int_en & ~BIT(INT_TEST), cbc->membase + cbc->int_en);
+ ioread32(cbc->membase + cbc->int_en);
+ iowrite32(int_en | BIT(INT_TEST), cbc->membase + cbc->int_en);
+ ioread32(cbc->membase + cbc->int_en);
+}
+
+static int cbc_debugfs_test_int_print(struct seq_file *s, void *p)
+{
+ struct cbc_fpga_data *cbc = (struct cbc_fpga_data *)s->private;
+
+ seq_printf(s, "0x%08X\n", cbc->test_int_cnt);
+ return 0;
+}
+
+static int cbc_debugfs_test_int_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cbc_debugfs_test_int_print, inode->i_private);
+}
+
+/* write address triggers a page read */
+static ssize_t cbc_debugfs_test_int_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct cbc_fpga_data *cbc =
+ ((struct seq_file *)(file->private_data))->private;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &val);
+ if (err)
+ return err;
+
+ if (val)
+ cbc_fire_test_int(cbc);
+
+ return count;
+}
+
+static const struct file_operations cbc_debugfs_test_int_fops = {
+ .open = cbc_debugfs_test_int_open,
+ .write = cbc_debugfs_test_int_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+
+static int cbc_debugfs_i2c_accel_int_print(struct seq_file *s, void *p)
+{
+ struct cbc_fpga_data *cbc = (struct cbc_fpga_data *)s->private;
+
+ seq_printf(s, "%d:%d\n", cbc->i2c_accel_cnt,
+ cbc->i2c_accel_empty_cnt);
+
+ return 0;
+}
+
+static int cbc_debugfs_i2c_accel_int_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, cbc_debugfs_i2c_accel_int_print,
+ inode->i_private);
+}
+
+static const struct file_operations cbc_debugfs_i2c_accel_int_fops = {
+ .open = cbc_debugfs_i2c_accel_int_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int cbc_debugfs_init(struct cbc_fpga_data *cbc)
+{
+ struct dentry *file;
+ struct device *dev = &cbc->pdev->dev;
+
+ cbc->cbc_debugfs_dir = debugfs_create_dir("cbc-core", NULL);
+ if (!cbc->cbc_debugfs_dir)
+ return -ENOMEM;
+/* regset dump */
+ cbc->regset = devm_kzalloc(dev, sizeof(*cbc->regset), GFP_KERNEL);
+ if (!cbc->regset)
+ goto err;
+
+ cbc->regset->regs = cbc_regs;
+ cbc->regset->nregs = ARRAY_SIZE(cbc_regs);
+ cbc->regset->base = cbc->membase;
+
+ file = debugfs_create_regset32("regdump", S_IRUGO,
+ cbc->cbc_debugfs_dir, cbc->regset);
+ if (!file)
+ goto err;
+
+/* Any register read/write */
+ file = debugfs_create_file("register-address",
+ (S_IRUGO | S_IWUSR | S_IWGRP),
+ cbc->cbc_debugfs_dir, cbc, &cbc_debugfs_addr_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("register-value",
+ (S_IRUGO | S_IWUSR | S_IWGRP),
+ cbc->cbc_debugfs_dir, cbc, &cbc_debugfs_val_fops);
+
+ if (!file)
+ goto err;
+
+/* TEST_INT */
+ file = debugfs_create_file("test-int",
+ (S_IRUGO | S_IWUSR | S_IWGRP),
+ cbc->cbc_debugfs_dir, cbc, &cbc_debugfs_test_int_fops);
+ if (!file)
+ goto err;
+
+/* I2C_ACCEL_INT */
+ file = debugfs_create_file("i2c-accel-int",
+ (S_IRUGO | S_IWUSR | S_IWGRP),
+ cbc->cbc_debugfs_dir, cbc, &cbc_debugfs_i2c_accel_int_fops);
+ if (!file)
+ goto err;
+
+ return 0;
+err:
+ debugfs_remove_recursive(cbc->cbc_debugfs_dir);
+ dev_err(dev, "failed to create debugfs entries.\n");
+
+ return -ENOMEM;
+}
+
+static void cbc_debugfs_remove(struct cbc_fpga_data *cbc)
+{
+ debugfs_remove_recursive(cbc->cbc_debugfs_dir);
+}
+#endif
+
+/*
+ * CBC/SAM interrupt handling
+ * The CBC_STATUS_IRQ_EN register is not used in the CBC
+ * The CBC_STATUS_IRQ_ST register just reports the pending
+ * interrupts for each master.
+ */
+static void sam_enable_irq(struct device *dev, enum sam_irq_type type,
+ int irq, u32 mask)
+{
+}
+
+static void sam_disable_irq(struct device *dev, enum sam_irq_type type,
+ int irq, u32 mask)
+{
+}
+
+static u32 sam_irq_status(struct device *dev, enum sam_irq_type type, int irq)
+{
+ struct cbc_fpga_data *cbc = dev_get_drvdata(dev);
+
+ return ioread32(cbc->membase + CBC_STATUS_IRQ_ST);
+}
+
+static void sam_irq_status_clear(struct device *dev, enum sam_irq_type type,
+ int irq, u32 mask)
+{
+ struct cbc_fpga_data *cbc = dev_get_drvdata(dev);
+
+ iowrite32(mask, cbc->membase + CBC_STATUS_IRQ_ST);
+ ioread32(cbc->membase + CBC_STATUS_IRQ_ST);
+
+ /*
+ * Clear the MSI INT_I2C_ACCEL interrupt.
+ * This might cause additional interrupt, but we
+ * won't miss the MSI in the windows from clearing the
+ * CBC_STATUS_IRQ_ST and INT_I2C_ACCEL.
+ */
+ iowrite32(BIT(INT_I2C_ACCEL), cbc->membase + cbc->int_src);
+ ioread32(cbc->membase + cbc->int_src);
+}
+
+static irqreturn_t cbc_irq_handler(int irq, void *data)
+{
+ struct cbc_fpga_data *cbc = data;
+ u32 int_src;
+ int i, iirq, ret = IRQ_NONE;
+
+ int_src = ioread32(cbc->membase + cbc->int_src);
+
+ /* (CBC) Test interrupt - just count */
+ if (int_src & BIT(INT_TEST)) {
+ cbc->test_int_cnt++;
+ iowrite32(BIT(INT_TEST), cbc->membase + cbc->int_src);
+ ioread32(cbc->membase + cbc->int_src);
+ ret = IRQ_HANDLED;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ if (int_src & BIT(INT_I2C_ACCEL)) {
+ u32 irq_st;
+
+ cbc->i2c_accel_cnt++;
+ irq_st = ioread32(cbc->membase + CBC_STATUS_IRQ_ST);
+ if (!irq_st)
+ cbc->i2c_accel_empty_cnt++;
+ }
+#endif
+
+ for (i = 0; i < CBC_IRQ_NR; i++) {
+ if (int_src & BIT(i)) {
+ iirq = irq_find_mapping(cbc->irq_domain, i);
+ if (iirq) {
+ generic_handle_irq(iirq);
+ ret = IRQ_HANDLED;
+ }
+ }
+ }
+
+ return ret;
+}
+
+/* irq_chip */
+static void cbc_irq_unmask(struct irq_data *data)
+{
+ struct cbc_fpga_data *cbc = irq_data_get_irq_chip_data(data);
+ unsigned long flags;
+ int irq = data->hwirq & 0x3f;
+ u32 int_en;
+
+ spin_lock_irqsave(&cbc->irq_lock, flags);
+
+ int_en = ioread32(cbc->membase + cbc->int_en);
+ iowrite32(int_en | BIT(irq), cbc->membase + cbc->int_en);
+ ioread32(cbc->membase + cbc->int_en);
+
+ spin_unlock_irqrestore(&cbc->irq_lock, flags);
+}
+
+static void cbc_irq_mask(struct irq_data *data)
+{
+ struct cbc_fpga_data *cbc = irq_data_get_irq_chip_data(data);
+ unsigned long flags;
+ int irq = data->hwirq & 0x3f;
+ u32 int_en;
+
+ spin_lock_irqsave(&cbc->irq_lock, flags);
+
+ int_en = ioread32(cbc->membase + cbc->int_en);
+ iowrite32(int_en & ~BIT(irq), cbc->membase + cbc->int_en);
+ ioread32(cbc->membase + cbc->int_en);
+
+ spin_unlock_irqrestore(&cbc->irq_lock, flags);
+}
+
+static void cbc_irq_ack(struct irq_data *data)
+{
+ struct cbc_fpga_data *cbc = irq_data_get_irq_chip_data(data);
+ int irq = data->hwirq & 0x3f;
+
+ iowrite32(BIT(irq), cbc->membase + cbc->int_src);
+ ioread32(cbc->membase + cbc->int_src);
+}
+
+struct irq_chip cbc_irq_chip = {
+ .name = "cbc-core",
+ .irq_ack = cbc_irq_ack,
+ .irq_mask = cbc_irq_mask,
+ .irq_unmask = cbc_irq_unmask,
+};
+
+/* irq_domain */
+static int cbc_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ irq_set_chip_data(virq, h->host_data);
+ irq_set_chip_and_handler(virq, &cbc_irq_chip, handle_level_irq);
+ irq_set_noprobe(virq);
+
+ return 0;
+}
+
+static struct irq_domain_ops cbc_irq_domain_ops = {
+ .map = cbc_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static int cbc_request_interrupt(struct cbc_fpga_data *cbc)
+{
+ int err;
+ struct pci_dev *pdev = cbc->pdev;
+ struct device *dev = &pdev->dev;
+
+ if (!pdev->irq)
+ return -ENODEV;
+
+ if (!pci_enable_msi(pdev)) {
+ cbc->int_src = CBC_TOP_REGS_MSI_INT_SRC;
+ cbc->int_en = CBC_TOP_REGS_MSI_INT_EN;
+ } else {
+ cbc->int_src = CBC_TOP_REGS_INT_SRC;
+ cbc->int_en = CBC_TOP_REGS_INT_EN;
+ }
+
+ cbc->irq_domain = irq_domain_add_linear(dev->of_node, CBC_IRQ_NR,
+ &cbc_irq_domain_ops, cbc);
+ if (!cbc->irq_domain) {
+ dev_err(dev, "could not create irq domain\n");
+ return -ENOMEM;
+ }
+
+ err = devm_request_irq(dev, pdev->irq, cbc_irq_handler,
+ 0, dev_driver_string(dev), cbc);
+
+ if (err) {
+ dev_err(dev, "failed to request irq %d\n", pdev->irq);
+ irq_domain_remove(cbc->irq_domain);
+ pci_disable_msi(pdev);
+ return err;
+ }
+
+ return 0;
+}
+
+static void cbc_free_interrupt(struct cbc_fpga_data *cbc)
+{
+ struct pci_dev *pdev = cbc->pdev;
+
+ devm_free_irq(&pdev->dev, pdev->irq, cbc);
+ if (cbc->irq_domain)
+ irq_domain_remove(cbc->irq_domain);
+ pci_disable_msi(pdev);
+}
+
+/* scratch access test */
+static int cbc_test_scratch(struct cbc_fpga_data *cbc)
+{
+ struct pci_dev *pdev = cbc->pdev;
+ struct device *dev = &pdev->dev;
+ u32 acc, i;
+
+ /*
+ * Check rw register access -> use the scratch reg.
+ * Earlier revisions fail on scratch rw test
+ */
+ iowrite32(0xA5A5A5A5, cbc->membase + CBC_TOP_REGS_SCRATCH);
+ acc = ioread32(cbc->membase + CBC_TOP_REGS_SCRATCH);
+ if (acc != 0xA5A5A5A5) {
+ dev_err(dev,
+ "CBC scratch write failed: %08x -> %08x",
+ 0xA5A5A5A5, acc);
+ return -EIO;
+ }
+
+ for (i = 0; i < 0xf0000000; i += 0x01010101) {
+ iowrite32(i, cbc->membase + CBC_TOP_REGS_SCRATCH);
+ acc = ioread32(cbc->membase + CBC_TOP_REGS_SCRATCH);
+ if (acc != i) {
+ dev_err(dev, "CBC scratch write failed: %08x -> %08x",
+ i, acc);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Check if the CBC is capable of generating interrupts.
+ * Use the test interrupt bit. The counter is incremented
+ * from the interrupt handler. We want it > 0
+ */
+static int cbc_test_interrupts(struct cbc_fpga_data *cbc)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(100);
+
+ cbc->test_int_cnt = 0;
+ cbc_fire_test_int(cbc);
+
+ while (!cbc->test_int_cnt) {
+ if (time_after(jiffies, timeout))
+ break;
+ schedule_timeout_interruptible(msecs_to_jiffies(10));
+ }
+
+ return cbc->test_int_cnt;
+}
+
+/* sysfs entries */
+static ssize_t version_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct cbc_fpga_data *cbc = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", cbc->fpga_rev);
+}
+
+static ssize_t cbc_date_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct cbc_fpga_data *cbc = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%08x\n", cbc->fpga_date);
+}
+
+static DEVICE_ATTR(version, S_IRUGO, version_show, NULL);
+static DEVICE_ATTR(cbc_date, S_IRUGO, cbc_date_show, NULL);
+
+static struct attribute *cbc_attrs[] = {
+ &dev_attr_version.attr,
+ &dev_attr_cbc_date.attr,
+ NULL,
+};
+
+static struct attribute_group cbc_attr_group = {
+ .attrs = cbc_attrs,
+};
+
+/* SAM drivers interrupt handling */
+static struct sam_platform_data cbc_sam_plat_data = {
+ .enable_irq = sam_enable_irq,
+ .disable_irq = sam_disable_irq,
+ .irq_status = sam_irq_status,
+ .irq_status_clear = sam_irq_status_clear,
+ .i2c_mux_channels = 4,
+};
+
+/*
+ * List of modules to be loaded. Should only be necessary if devicetree
+ * is not configured, but doesn't hurt otherwise.
+ */
+const char *cbc_modules[] = {
+ "i2c-sam",
+ "sam-flash",
+ "gpio-cbc",
+ NULL
+};
+
+static void cbc_request_modules(bool wait)
+{
+ struct module *m;
+ int i;
+
+ for (i = 0; cbc_modules[i]; i++) {
+ mutex_lock(&module_mutex);
+ m = find_module(cbc_modules[i]);
+ mutex_unlock(&module_mutex);
+ if (!m) {
+ if (wait)
+ request_module(cbc_modules[i]);
+ else
+ request_module_nowait(cbc_modules[i]);
+ }
+ }
+}
+
+static struct cbc_fpga_platdata cbc_fpga_platdata[] = {
+ [JNX_CBD_FPGA_PTX1K] = {
+ .board_type = JNX_CBD_FPGA_PTX1K,
+ },
+ [JNX_CBD_FPGA_PTX1K_P2] = {
+ .board_type = JNX_CBD_FPGA_PTX1K_P2,
+ },
+ [JNX_CBD_FPGA_PTX21K] = {
+ .board_type = JNX_CBD_FPGA_PTX21K,
+ },
+};
+
+static int cbc_fpga_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ int err;
+ struct cbc_fpga_data *cbc;
+ struct device *dev = &pdev->dev;
+
+ /* Request modules, but keep going */
+ cbc_request_modules(false);
+
+ err = pcim_enable_device(pdev);
+ if (err)
+ return err;
+
+ err = pcim_iomap_regions(pdev, 1 << 0, "cbc-core");
+ if (err)
+ return err;
+
+ cbc = devm_kzalloc(dev, sizeof(*cbc), GFP_KERNEL);
+ if (cbc == NULL)
+ return -ENOMEM;
+
+ cbc->membase = pcim_iomap_table(pdev)[0];
+ cbc->pdev = pdev;
+ pci_set_drvdata(pdev, cbc);
+
+/* Check IO */
+ err = cbc_test_scratch(cbc);
+ if (err)
+ return err;
+
+/* CBC Revision ID */
+ cbc->fpga_rev = ioread32(cbc->membase + CBC_TOP_REGS_RTL_REVISION);
+ cbc->fpga_date = ioread32(cbc->membase + CBC_TOP_REGS_FPGA_DATE);
+ cbc->i2c_mstr_count =
+ ioread32(cbc->membase + CBC_INFO_I2C_MASTER_COUNT) & 0xff;
+
+ spin_lock_init(&cbc->irq_lock);
+
+ /* i2c accel */
+ cbc->mfd_cells[CBC_CELL_SAM_I2C].name = "i2c-sam";
+ cbc->mfd_cells[CBC_CELL_SAM_I2C].num_resources = CBC_RES_NR;
+ cbc->mfd_cells[CBC_CELL_SAM_I2C].resources = cbc->mfd_i2c_resources;
+ cbc->mfd_cells[CBC_CELL_SAM_I2C].of_compatible = "jnx,i2c-sam";
+ cbc->mfd_cells[CBC_CELL_SAM_I2C].platform_data = &cbc_sam_plat_data;
+ cbc->mfd_cells[CBC_CELL_SAM_I2C].pdata_size =
+ sizeof(struct sam_platform_data);
+
+ cbc->mfd_i2c_resources[0].start = CBC_I2C_ACCEL_IF_MEM_START;
+ cbc->mfd_i2c_resources[0].end = CBC_I2C_ACCEL_IF_MEM_END;
+ cbc->mfd_i2c_resources[0].flags = IORESOURCE_MEM;
+ cbc->mfd_i2c_resources[1].start =
+ cbc->mfd_i2c_resources[1].end = INT_I2C_ACCEL;
+ cbc->mfd_i2c_resources[1].flags = IORESOURCE_IRQ;
+
+ /* epcs64,128 mtd flash */
+ cbc->mfd_cells[CBC_CELL_SAM_MTD].name = "flash-sam";
+ cbc->mfd_cells[CBC_CELL_SAM_MTD].num_resources = CBC_RES_NR_NOIRQ;
+ cbc->mfd_cells[CBC_CELL_SAM_MTD].resources = cbc->mfd_mtd_resources;
+ cbc->mfd_cells[CBC_CELL_SAM_MTD].of_compatible = "jnx,flash-sam";
+
+ cbc->mfd_mtd_resources[0].start = CBC_FLASH_IF_MEM_START;
+ cbc->mfd_mtd_resources[0].end = CBC_FLASH_IF_MEM_END;
+ cbc->mfd_mtd_resources[0].flags = IORESOURCE_MEM;
+
+ /* CBC GPIO */
+ cbc->mfd_cells[CBC_CELL_GPIO].name = "gpio-cbc";
+ cbc->mfd_cells[CBC_CELL_GPIO].num_resources = CBC_RES_NR_NOIRQ;
+ cbc->mfd_cells[CBC_CELL_GPIO].resources = cbc->mfd_gpio_resources;
+ cbc->mfd_cells[CBC_CELL_GPIO].of_compatible = "jnx,gpio-cbc";
+ cbc->mfd_gpio_resources[0].flags = IORESOURCE_MEM;
+
+ /* CBC JNX */
+ cbc->mfd_cells[CBC_CELL_JNX_CBD].name = "jnx-cbd-fpga";
+ cbc->mfd_cells[CBC_CELL_JNX_CBD].num_resources = CBC_RES_NR_NOIRQ;
+ cbc->mfd_cells[CBC_CELL_JNX_CBD].resources = cbc->mfd_jnx_resources;
+ cbc->mfd_cells[CBC_CELL_JNX_CBD].of_compatible = "jnx,jnx-cbd-fpga";
+ cbc->mfd_cells[CBC_CELL_JNX_CBD].platform_data =
+ &cbc_fpga_platdata[id->driver_data];
+ cbc->mfd_cells[CBC_CELL_JNX_CBD].pdata_size =
+ sizeof(struct cbc_fpga_platdata);
+
+ cbc->mfd_jnx_resources[0].flags = IORESOURCE_MEM;
+
+ /* CBC presence detect as GPIO (CH_PRS) */
+ cbc->mfd_cells[CBC_CELL_GPIO_PRS].name = "gpio-cbc-presence";
+ cbc->mfd_cells[CBC_CELL_GPIO_PRS].id = 0;
+ cbc->mfd_cells[CBC_CELL_GPIO_PRS].num_resources =
+ CBC_RES_NR_NOIRQ;
+ cbc->mfd_cells[CBC_CELL_GPIO_PRS].resources =
+ cbc->mfd_gpio_prs_resources;
+ cbc->mfd_cells[CBC_CELL_GPIO_PRS].of_compatible =
+ "jnx,gpio-cbc-presence";
+ cbc->mfd_gpio_prs_resources[0].flags = IORESOURCE_MEM;
+
+ /* CBC presence detect as GPIO (OTHER_CH_PRS) */
+ cbc->mfd_cells[CBC_CELL_GPIO_PRS_OTHER].name = "gpio-cbc-presence";
+ cbc->mfd_cells[CBC_CELL_GPIO_PRS_OTHER].id = 1;
+ cbc->mfd_cells[CBC_CELL_GPIO_PRS_OTHER].num_resources =
+ CBC_RES_NR_NOIRQ;
+ cbc->mfd_cells[CBC_CELL_GPIO_PRS_OTHER].resources =
+ cbc->mfd_gpio_prs_resources;
+ cbc->mfd_cells[CBC_CELL_GPIO_PRS_OTHER].of_compatible =
+ "jnx,gpio-cbc-presence-other";
+ cbc->mfd_gpio_prs_resources[0].flags = IORESOURCE_MEM;
+
+ /* CBC presence detect as GPIO (CH_PRS_SIB) */
+ cbc->mfd_cells[CBC_CELL_GPIO_PRS_SIB].name = "gpio-cbc-presence";
+ cbc->mfd_cells[CBC_CELL_GPIO_PRS_SIB].id = 2;
+ cbc->mfd_cells[CBC_CELL_GPIO_PRS_SIB].num_resources =
+ CBC_RES_NR_NOIRQ;
+ cbc->mfd_cells[CBC_CELL_GPIO_PRS_SIB].resources =
+ cbc->mfd_gpio_prs_resources;
+ cbc->mfd_cells[CBC_CELL_GPIO_PRS_SIB].of_compatible =
+ "jnx,gpio-cbc-presence-sib";
+ cbc->mfd_gpio_prs_resources[0].flags = IORESOURCE_MEM;
+
+ /* CBC is using MSI, make sure bus master is enabled */
+ pci_set_master(pdev);
+
+ /* Setup interrupts - MSI/INTx */
+ err = cbc_request_interrupt(cbc);
+ if (err < 0)
+ return err;
+
+ err = cbc_test_interrupts(cbc);
+ if (!err) {
+ dev_warn(dev, "Interrupt test failed, using poll mode");
+ cbc->mfd_cells[CBC_CELL_SAM_I2C].num_resources =
+ CBC_RES_NR_NOIRQ;
+ }
+
+ /* Request modules for good. */
+ cbc_request_modules(true);
+
+ err = mfd_add_devices(dev, pdev->bus->number, cbc->mfd_cells,
+ ARRAY_SIZE(cbc->mfd_cells), &pdev->resource[0],
+ 0, cbc->irq_domain);
+ if (err < 0)
+ goto err_int;
+
+ err = sysfs_create_group(&pdev->dev.kobj, &cbc_attr_group);
+ if (err) {
+ sysfs_remove_group(&pdev->dev.kobj, &cbc_attr_group);
+ dev_err(&pdev->dev, "Failed to create attr group\n");
+ goto err_mfd;
+ }
+
+ dev_dbg(dev, "CBC/SAM I2C: Master count: %u\n", cbc->i2c_mstr_count);
+ dev_info(dev, "CBC FPGA Revision #%u (%02X/%02X/%04X)\n",
+ cbc->fpga_rev & 0xffff, (cbc->fpga_date >> 24) & 0xff,
+ (cbc->fpga_date >> 16) & 0xff, cbc->fpga_date & 0xffff);
+
+#ifdef CONFIG_DEBUG_FS
+ cbc_debugfs_init(cbc);
+#endif
+ return 0;
+
+err_mfd:
+ mfd_remove_devices(dev);
+
+err_int:
+ cbc_free_interrupt(cbc);
+
+ return err;
+}
+
+static void cbc_fpga_remove(struct pci_dev *pdev)
+{
+ struct cbc_fpga_data *cbc = pci_get_drvdata(pdev);
+
+#ifdef CONFIG_DEBUG_FS
+ cbc_debugfs_remove(cbc);
+#endif
+ sysfs_remove_group(&pdev->dev.kobj, &cbc_attr_group);
+ mfd_remove_devices(&pdev->dev);
+ cbc_free_interrupt(cbc);
+}
+
+static const struct pci_device_id cbc_fpga_id_tbl[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_CBC),
+ .driver_data = JNX_CBD_FPGA_PTX1K },
+ { PCI_DEVICE(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_CBC_P2),
+ .driver_data = JNX_CBD_FPGA_PTX1K_P2 },
+ { PCI_DEVICE(PCI_VENDOR_ID_JUNIPER, PCI_DEVICE_ID_JNX_OMG_CBC),
+ .driver_data = JNX_CBD_FPGA_PTX21K },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, cbc_fpga_id_tbl);
+
+static struct pci_driver cbc_fpga_driver = {
+ .name = "cbc-core",
+ .id_table = cbc_fpga_id_tbl,
+ .probe = cbc_fpga_probe,
+ .remove = cbc_fpga_remove,
+};
+
+module_pci_driver(cbc_fpga_driver);
+
+MODULE_DESCRIPTION("Juniper PTX1K RCB CBC MFD core driver");
+MODULE_AUTHOR("Georgi Vlaev <gvlaev@juniper.net>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/cbc-core.h b/include/linux/mfd/cbc-core.h
new file mode 100644
index 0000000..dc510a7
--- /dev/null
+++ b/include/linux/mfd/cbc-core.h
@@ -0,0 +1,181 @@
+/*
+ * PTX1K CBC FPGA registers
+ *
+ * Copyright (C) 2014 Juniper Networks
+ * Author: Georgi Vlaev <gvlaev@juniper.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __CBC_CORE_H__
+#define __CBC_CORE_H__
+
+enum jnx_cbd_fpga_board_types {
+ JNX_CBD_FPGA_PTX1K, JNX_CBD_FPGA_PTX1K_P2, JNX_CBD_FPGA_PTX21K };
+
+struct cbc_fpga_platdata {
+ enum jnx_cbd_fpga_board_types board_type;
+};
+
+#define CBC_TOP_REGS_INT_SRC 0x000
+#define CBC_TOP_REGS_INT_EN 0x004
+#define CBC_TOP_REGS_I2C_SEL 0x010
+#define CBC_TOP_REGS_CH_PRS 0x014
+#define CBC_TOP_REGS_RTL_REVISION 0x018
+#define CBC_TOP_REGS_PWR_STATUS 0x01c
+#define CBC_TOP_REGS_I2CS_INT 0x020
+#define CBC_TOP_REGS_I2CS_INT_EN 0x024
+#define CBC_TOP_REGS_MSTR_CONFIG 0x03c
+#define CBC_TOP_REGS_MSTR_ALIVE 0x040
+#define CBC_TOP_REGS_MSTR_ALIVE_CNT 0x044
+#define CBC_TOP_REGS_FPGA_REV 0x060
+#define CBC_TOP_REGS_FPGA_DATE 0x064
+#define CBC_TOP_REGS_DEVICE_CTRL 0x0c0
+#define CBC_TOP_REGS_SLOT_ID 0x0c4
+#define CBC_TOP_REGS_SCRATCH 0x0c8
+#define CBC_TOP_REGS_RE_HALT 0x0cc
+#define CBC_TOP_REGS_OTHER_CH_PRS_CHANGE 0x0d4
+#define CBC_TOP_REGS_FPC_SPARE_A 0x0dc
+#define CBC_TOP_REGS_FT_SPARE 0x0e4
+#define CBC_TOP_REGS_CB_SPARE 0x0e8
+#define CBC_TOP_REGS_MTTR_0 0x0ec
+#define CBC_TOP_REGS_MTTR_1 0x0f0
+#define CBC_TOP_REGS_MTTR_2 0x0f4
+#define CBC_TOP_REGS_MSTR_FRC 0x0f8
+#define CBC_TOP_REGS_MSTR_RSN 0x0fc
+#define CBC_TOP_REGS_ME_WRITE_0 0x100
+#define CBC_TOP_REGS_ME_WRITE_1 0x104
+#define CBC_TOP_REGS_ME_WRITE_2 0x108
+#define CBC_TOP_REGS_ME_WRITE_3 0x10c
+#define CBC_TOP_REGS_ME_WRITE_4 0x110
+#define CBC_TOP_REGS_ME_WRITE_5 0x114
+#define CBC_TOP_REGS_ME_READ_0 0x120
+#define CBC_TOP_REGS_ME_READ_1 0x124
+#define CBC_TOP_REGS_ME_READ_2 0x128
+#define CBC_TOP_REGS_ME_READ_3 0x12c
+#define CBC_TOP_REGS_ME_READ_4 0x130
+#define CBC_TOP_REGS_ME_READ_5 0x134
+#define CBC_TOP_REGS_ME_STATUS 0x13c
+#define CBC_TOP_REGS_LIU_CONFIG 0x140
+#define CBC_TOP_REGS_CBC_8112_8614_RST 0x144
+#define CBC_TOP_REGS_CCG_CONFIG 0x148
+#define CBC_TOP_REGS_SFPP_CONTROL 0x14c
+#define CBC_TOP_REGS_SFPP_PCIE_FT_STATUS 0x150
+#define CBC_TOP_REGS_GPIO_CTRL 0x168
+#define CBC_TOP_REGS_GPIO_OUTPUT_DATA 0x16c
+#define CBC_TOP_REGS_GPIO_INPUT_DATA 0x170
+#define CBC_TOP_REGS_CCG_STATUS_RT 0x174
+#define CBC_TOP_REGS_CCG_STATUS_LATCHED 0x178
+#define CBC_TOP_REGS_CCG_STATUS_INTERRUPT_ENABLE 0x17c
+#define CBC_TOP_REGS_OTHER_LED 0x180
+#define CBC_TOP_REGS_SFPP_I2C_RW_CONTROL 0x190
+#define CBC_TOP_REGS_SFPP_I2C_REG_ADDRESS 0x194
+#define CBC_TOP_REGS_SFPP_I2C_WDATA 0x198
+#define CBC_TOP_REGS_SFPP_I2C_RDATA 0x19c
+#define CBC_TOP_REGS_SFPP_I2C_STATUS 0x1a0
+#define CBC_TOP_REGS_SFPP_I2C_DEV_ADDRESS 0x1a4
+#define CBC_TOP_REGS_SIB_SPARE_OUTPUTENABLE 0x1a8
+#define CBC_TOP_REGS_SIB_SPARE_OUTPUT 0x1ac
+#define CBC_TOP_REGS_SIB_SPARE_INPUT 0x1c0
+#define CBC_TOP_REGS_FPC_SPARE_OUTPUTENABLE 0x1c4
+#define CBC_TOP_REGS_FPC_SPARE_OUTPUT 0x1c8
+#define CBC_TOP_REGS_FPC_SPARE_INPUT 0x1cc
+#define CBC_TOP_REGS_OTHER_SPARE_OUTPUTENABLE 0x1d0
+#define CBC_TOP_REGS_OTHER_SPARE_OUTPUT 0x1d4
+#define CBC_TOP_REGS_OTHER_SPARE_INPUT 0x1d8
+#define CBC_TOP_REGS_MSI_INT_SRC 0x1e0
+#define CBC_TOP_REGS_MSI_INT_EN 0x1e4
+#define CBC_TOP_REGS_TOD_LOCK 0x1f0
+#define CBC_TOP_REGS_TOD_TIME_79_48 0x1f4
+#define CBC_TOP_REGS_TOD_TIME_47_16 0x1f8
+#define CBC_TOP_REGS_TOD_TIME_15_0 0x1fc
+#define CBC_TOP_REGS_TOD_CLKACC_CRC 0x200
+#define CBC_TOP_REGS_APS_COMMAND_REGISTER 0x400
+#define CBC_TOP_REGS_APS_STATUS_REGISTER 0x404
+#define CBC_TOP_REGS_APS_FRAME_DATA0 0x420
+#define CBC_TOP_REGS_APS_FRAME_DATA1 0x424
+#define CBC_TOP_REGS_APS_FRAME_DATA2 0x428
+#define CBC_TOP_REGS_APS_FRAME_DATA3 0x42c
+#define CBC_TOP_REGS_APS_FRAME_DATA4 0x430
+#define CBC_TOP_REGS_APS_FRAME_DATA5 0x434
+#define CBC_TOP_REGS_APS_FRAME_DATA6 0x438
+#define CBC_TOP_REGS_APS_FRAME_DATA7 0x43c
+#define CBC_TOP_REGS_APS_APS_REV_REG 0x440
+#define CBC_TOP_REGS_APS_APS_DEBUG0 0x444
+#define CBC_TOP_REGS_APS_COUNTER_GOOD_FRAMES 0x448
+#define CBC_TOP_REGS_APS_COUNTER_BAD_FRAMES 0x44c
+#define CBC_TOP_REGS_APS_APS_LINK_STATUS 0x450
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG0 0x454
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG1 0x458
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG2 0x45c
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG3 0x460
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG4 0x464
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG5 0x468
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG6 0x46c
+#define CBC_TOP_REGS_APS_INTERRUPT_LOG7 0x470
+
+/* CBC: SAM register base is @0x1000 */
+#define CBC_SAM_BASE 0x1000
+#define CBC_INFO_I2C_MASTER_COUNT (CBC_SAM_BASE + 0x00c)
+#define CBC_INFO_FAST_I2C_CONFIG_A (CBC_SAM_BASE + 0x018)
+#define CBC_INFO_FAST_I2C_CONFIG_B (CBC_SAM_BASE + 0x01c)
+#define CBC_INFO_FEATURES (CBC_SAM_BASE + 0x020)
+#define CBC_INFO_PMA_CONTROL (CBC_SAM_BASE + 0x040)
+#define CBC_INFO_PMA_STATUS (CBC_SAM_BASE + 0x044)
+#define CBC_STATUS_IRQ_EN (CBC_SAM_BASE + 0x104)
+#define CBC_STATUS_IRQ_ST (CBC_SAM_BASE + 0x108)
+/* remote upgrade_if */
+#define CBC_REMOTE_UPGRADE_CONTROL (CBC_SAM_BASE + 0x200)
+#define CBC_REMOTE_UPGRADE_STATUS (CBC_SAM_BASE + 0x204)
+/* flash_if */
+#define CBC_FLASH_IF_ADDRESS (CBC_SAM_BASE + 0x300)
+#define CBC_FLASH_IF_BYTE_COUNT (CBC_SAM_BASE + 0x304)
+#define CBC_FLASH_IF_CONTROL (CBC_SAM_BASE + 0x308)
+#define CBC_FLASH_IF_STATUS (CBC_SAM_BASE + 0x30c)
+#define CBC_FLASH_IF_WRITE_DATA (CBC_SAM_BASE + 0x400)
+#define CBC_FLASH_IF_READ_DATA (CBC_SAM_BASE + 0x500)
+
+/* Constants used for FPGA upgrades */
+#define SAM_FPGA_FLASH_VALID_BIT 0xA5A5A5A5
+#define SAM_FPGA_FLASH_VALID_BIT_ADDR 0x7F0000 /* EPCS64 */
+
+/* FPGA remote upgrade registers */
+#define SAM_FPGA_REMOTE_UPGRADE_TRIG_BIT 0x08000000
+#define SAM_FPGA_REMOTE_UPGRADE_STATUS_BUSY 0x01000000
+#define SAM_FPGA_REMOTE_UPGRADE_READ_PARAM 0x80000000
+#define SAM_FPGA_REMOTE_UPGRADE_WRITE_PARAM 0x40000000
+#define SAM_FPGA_REMOTE_UPGRADE_CONTROL_RESET 0x10000000
+
+#define SAM_FPGA_REMOTE_UPGRADE_PAGE_SEL (0x04 << 24)
+#define SAM_FPGA_REMOTE_UPGRADE_ANF (0x05 << 24)
+#define SAM_FPGA_USER_IMAGE_BASE 0x400000
+
+/* CBC/SAM flash */
+#define FLASH_STATUS_BUSY 0x01
+#define SAM_FLASH_IF_CONTROL_READ 0x01
+#define SAM_FLASH_IF_CONTROL_READ_SID 0x80
+#define ECS64_PAGE_SIZE 0x100
+
+/* MFD resources */
+/* CBC/SAM flash - mtd/devices/flash-sam.c */
+#define CBC_FLASH_IF_MEM_START (CBC_SAM_BASE)
+#define CBC_FLASH_IF_MEM_END (CBC_SAM_BASE + 0x05ff)
+/* CBC/SAM I2C Accel - i2c/busses/i2c-sam.c */
+#define CBC_I2C_ACCEL_IF_MEM_START (CBC_SAM_BASE)
+#define CBC_I2C_ACCEL_IF_MEM_END (CBC_SAM_BASE + 0x1ffff)
+
+/* MSI & legacy nterrupts [W1C] */
+#define INT_TEST 31 /* Test interrupt */
+#define INT_MSTRSHIP_LOSS 30 /* Mastership loss */
+#define INT_I2C_ACCEL 25 /* Cascade I2C accel int */
+#define INT_CASI2CS 24 /* casI2CS_INT */
+#define INT_SFPP_PCIE_STATUS_CHANGE 23 /* SFPP status, PCIe status */
+#define INT_CCG 22 /* CCG INT */
+#define INT_PSM_STATUS_CHANGE 21 /* PSMSatusChange */
+#define INT_OTHERCH_PRS_CHANGE 18 /* OTHER_CH_PRS change */
+#define INT_CH_PRS_CHANGE 16 /* CH_PRS_change */
+#define INT_2C_CTRL 14 /* I2C_CTRL_INT */
+
+#endif /*__CBC_CORE_H__*/
--
1.9.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 6/6] staging: jnx: CBD-FPGA infrastructure
2016-10-07 15:20 [PATCH 0/6] Introduce Juniper CBC FPGA Pantelis Antoniou
` (4 preceding siblings ...)
2016-10-07 15:20 ` [PATCH 5/6] gpio: gpio-cbc-presense: Document bindings of CBC FPGA presence Pantelis Antoniou
@ 2016-10-07 15:20 ` Pantelis Antoniou
2016-10-07 15:39 ` [PATCH 0/6] Introduce Juniper CBC FPGA Greg Kroah-Hartman
6 siblings, 0 replies; 14+ messages in thread
From: Pantelis Antoniou @ 2016-10-07 15:20 UTC (permalink / raw)
To: Lee Jones
Cc: Mark Rutland, Alexandre Courbot, devel, devicetree,
Greg Kroah-Hartman, Linus Walleij, Pantelis Antoniou,
linux-kernel, JawaharBalaji Thirumalaisamy, linux-gpio,
Rob Herring, Debjit Ghosh, Mohammad Kamil, Georgi Vlaev,
Frank Rowand, Guenter Roeck
From: Tom Kavanagh <tkavanagh@juniper.net>
Every Juniper platform contains a CBD (Control Board) FPGA.
While each CBD FPGA is different, a common abstact API makes
handling them common for every platform and the same parts
they have can be factored out.
The supported CBDs are PTX1K, PTX21K, PTX3K & PTX5K.
Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
Signed-off-by: Guenter Roeck <groeck@juniper.net>
Signed-off-by: JawaharBalaji Thirumalaisamy <jawaharb@juniper.net>
Signed-off-by: Mohammad Kamil <mkamil@juniper.net>
Signed-off-by: Tom Kavanagh <tkavanagh@juniper.net>
Signed-off-by: Debjit Ghosh <dghosh@juniper.net>
[Ported from Juniper kernel]
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
drivers/staging/jnx/Kconfig | 34 ++
drivers/staging/jnx/Makefile | 5 +
drivers/staging/jnx/jnx-cbc-ptx1k.c | 242 +++++++++++++
drivers/staging/jnx/jnx-cbd-fpga-common.c | 332 +++++++++++++++++
drivers/staging/jnx/jnx-cbd-fpga-common.h | 27 ++
drivers/staging/jnx/jnx-cbd-fpga-core.c | 540 ++++++++++++++++++++++++++++
drivers/staging/jnx/jnx-cbd-fpga-core.h | 68 ++++
drivers/staging/jnx/jnx-cbd-fpga-platdata.h | 51 +++
drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c | 134 +++++++
drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c | 143 ++++++++
drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c | 111 ++++++
drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c | 107 ++++++
12 files changed, 1794 insertions(+)
create mode 100644 drivers/staging/jnx/jnx-cbc-ptx1k.c
create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-common.c
create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-common.h
create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-core.c
create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-core.h
create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-platdata.h
create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c
create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c
create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c
create mode 100644 drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c
diff --git a/drivers/staging/jnx/Kconfig b/drivers/staging/jnx/Kconfig
index 4c38fc2..cd29276 100644
--- a/drivers/staging/jnx/Kconfig
+++ b/drivers/staging/jnx/Kconfig
@@ -34,6 +34,40 @@ config JNX_CONNECTOR
This driver can also be built as a module. If so, the module
will be called jnx-connector.
+config JNX_CBD_FPGA
+ tristate "Juniper Generic CBD FPGA"
+ select I2C_MUX
+ help
+ Driver to support the Juniper Control Board (CBD) FPGA. Provides all
+ common functionality for device across all supported juniper platforms.
+
+ This driver can also be built as a module. If so, the module
+ will be called jnx-cbd-fpga.
+
+config JNX_CBD_FPGA_PTX21K
+ tristate "Juniper PTX21K CBC FPGA"
+ depends on JNX_SYSTEM && MFD_JUNIPER_CBC && JNX_PTX21K_BOARDS
+ depends on JNX_PTX21K_RCB
+ default m
+ help
+ Driver to support the Juniper Control Board (CBC) FPGA in PTX21K.
+ Provides hooks for the common fpga handling core for these
+ particular Juniper Boards.
+
+ When compiled as a module it is included in jnx-cbd-fpga.
+
+config JNX_CBD_FPGA_PTX1K
+ tristate "Juniper PTX1K CBC FPGA"
+ depends on JNX_SYSTEM && MFD_JUNIPER_CBC && JNX_PTX1K_BOARDS
+ depends on JNX_CBD_FPGA_PTX21K
+ help
+ Driver to support the Juniper Control Board (CBC) FPGA in PTX1K.
+ Provides all common functionality for device across platforms and
+ hooks for the common fpga handling core for these particular
+ Juniper Boards.
+
+ When compiled as a module it is included in jnx-cbd-fpga.
+
endmenu
config JNX_COMMON_PCI
diff --git a/drivers/staging/jnx/Makefile b/drivers/staging/jnx/Makefile
index c89e701..b937896 100644
--- a/drivers/staging/jnx/Makefile
+++ b/drivers/staging/jnx/Makefile
@@ -7,3 +7,8 @@ obj-$(CONFIG_JNX_CHIP_PCI_QUIRKS)+= jnx-chip-pci-quirks.o
obj-$(CONFIG_JNX_COMMON_PCI) += jnx_common_pci.o
obj-$(CONFIG_JNX_PEX8XXX_I2C) += pex8xxx_i2c.o
obj-$(CONFIG_JNX_CONNECTOR) += jnx-connector.o
+obj-$(CONFIG_JNX_CBD_FPGA) += jnx-cbd-fpga.o jnx-cbd-fpga-common.o
+obj-$(CONFIG_JNX_CBD_FPGA_PTX1K)+= jnx-cbd-ptx1k.o
+obj-$(CONFIG_JNX_CBD_FPGA_PTX21K)+= jnx-cbd-fpga-ptx21k.o
+jnx-cbd-fpga-y := jnx-cbd-fpga-core.o jnx-cbd-fpga-ptx5k.o jnx-cbd-fpga-ptx3k.o
+jnx-cbd-ptx1k-y := jnx-cbc-ptx1k.o jnx-cbd-fpga-ptx1k.o
diff --git a/drivers/staging/jnx/jnx-cbc-ptx1k.c b/drivers/staging/jnx/jnx-cbc-ptx1k.c
new file mode 100644
index 0000000..7eff5ce
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbc-ptx1k.c
@@ -0,0 +1,242 @@
+/*
+ * Juniper Generic Control Board (CB) FPGA Driver
+ *
+ * Copyright (C) 2012, 2013, 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/jnx/jnx-subsys.h>
+#include <linux/jnx/jnx-board-core.h>
+#include <linux/jnx/board_ids.h>
+#include <linux/mfd/cbc-core.h>
+#include <linux/of.h>
+#include <linux/dmi.h>
+#include "jnx-cbd-fpga-core.h"
+#include "jnx-cbd-fpga-common.h"
+#include "jnx-cbd-fpga-platdata.h"
+#include "jnx-subsys-private.h"
+
+/*
+ * Polaris notes (current state):
+ *
+ * I2C: We dont have I2C mux and I2C slow bus. The I2C is handled
+ * by the I2C accellerator in the CBC FPGA (40 buses -> 10 i2c masters
+ * internaly muxed x4), supported by the SAM i2c driver.
+ * Presence detect: (CH_PRS and OTHER_CH_PRS) - these bits are exported as
+ * GPIOs by the gpio-cbc-presence driver
+ * DEVICE_CRTL is not valid for Polaris - we don't have to reset anything a mux
+ */
+
+#define PTX1K_SAM_CHANNELS 4
+static struct i2c_adapter *jnx_find_sam_adapter(int chan)
+{
+ char name[JNX_BRD_I2C_NAME_LEN];
+ struct i2c_adapter *adap;
+ int base, mux, chan_id;
+
+ /*
+ * Find base adapter index first. Base is first SAM adapter,
+ * and the name starts with 'i2c-sam.'. We can not assume this
+ * to be constant.
+ */
+ adap = jnx_i2c_find_adapter("i2c-sam");
+ if (!adap)
+ return NULL;
+ base = adap->nr;
+ i2c_put_adapter(adap);
+
+ /*
+ * (zero based) channel -> sam i2c_adapter
+ * Example:
+ * bus #11 = i2c-10-mux (chan_id 3)
+ * bus #4 = i2c-5-mux (chan_id 0)
+ */
+ chan_id = chan % PTX1K_SAM_CHANNELS; /* 0..3 */
+ mux = chan / PTX1K_SAM_CHANNELS;
+ mux *= (PTX1K_SAM_CHANNELS + 1); /* 0, 5, 10, ... */
+ mux += base;
+
+ sprintf(name, "i2c-%d-mux (chan_id %d)", mux, chan_id);
+
+ return jnx_i2c_find_adapter(name);
+}
+
+/*
+ * Check if we're on OF enabled setup and if the jnx_connector is defined
+ * in boot dtb. If present we'll assume that board insertion/removal will
+ * be handled by the connector and the card overlays and not the jnx-*
+ * platform drivers. This allows a temp fallback util the ptx1k/x86 code
+ * is fully DT capable.
+ */
+static int jnx_connector_present(void)
+{
+ return of_have_populated_dt();
+}
+
+const struct jnx_cbd_fpga_info * const jnx_cbc_cfinfo[] = {
+ [JNX_CBD_FPGA_PTX1K] = &jnx_cbd_fpga_ptx1k_info,
+ [JNX_CBD_FPGA_PTX1K_P2] = &jnx_cbd_fpga_ptx1k_p2_info,
+ [JNX_CBD_FPGA_PTX21K] = &jnx_cbd_fpga_ptx21k_info,
+};
+
+static int jnx_cbc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cbc_fpga_platdata *pdata = dev_get_platdata(dev);
+ const struct jnx_cbd_fpga_info *cfinfo;
+ struct jnx_cbd_fpga_data *fdata;
+ struct jnx_chassis_info chinfo;
+ struct jnx_card_info cinfo;
+ struct resource *res;
+ u32 ch_prs;
+ int err;
+
+ if (!pdata || pdata->board_type >= ARRAY_SIZE(jnx_cbc_cfinfo))
+ return -ENODEV;
+
+ cfinfo = jnx_cbc_cfinfo[pdata->board_type];
+
+ fdata = devm_kzalloc(dev, sizeof(*fdata), GFP_KERNEL);
+ if (!fdata)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ fdata->region0 = devm_ioremap(dev, res->start, resource_size(res));
+ if (!fdata->region0)
+ return -ENOMEM;
+
+ fdata->cfinfo = cfinfo;
+ fdata->find_mux_adapter = jnx_find_sam_adapter;
+ fdata->default_chan = cfinfo->i2cmux_default_chan;
+ fdata->mux_channels = cfinfo->mux_channels;
+ if (fdata->mux_channels > JNX_CBD_FPGA_NUM_MUXES)
+ fdata->mux_channels = JNX_CBD_FPGA_NUM_MUXES;
+
+ mutex_init(&fdata->lock);
+
+ platform_set_drvdata(pdev, fdata);
+
+ err = jnx_cbd_fpga_sysfs_init(dev);
+ if (err)
+ return err;
+
+ /*
+ * Register the chassis with the system so userspace is able to
+ * determine platform type, etc.
+ */
+ chinfo.platform = cfinfo->platform;
+ chinfo.chassis_no = 0;
+ chinfo.multichassis = 0;
+ chinfo.master_data = fdata;
+ chinfo.get_master = jnx_cbd_fpga_get_master;
+ chinfo.mastership_get = jnx_cbd_fpga_mastership_get;
+ chinfo.mastership_set = jnx_cbd_fpga_mastership_set;
+ chinfo.mastership_ping = jnx_cbd_fpga_mastership_ping;
+ chinfo.mastership_count_get = jnx_cbd_fpga_mastership_count_get;
+ chinfo.mastership_count_set = jnx_cbd_fpga_mastership_count_set;
+
+ jnx_register_chassis(&chinfo);
+
+ /*
+ * Register the local card with the system so userspace is able to
+ * determine board type etc.
+ */
+ ch_prs = ioread32(fdata->region0 + JNX_CBD_FPGA_CH_PRS_REG);
+
+ fdata->slot = (ch_prs & cfinfo->ch_prs_cb0_bit) ? 0 : 1;
+
+ cinfo.assembly_id = cfinfo->assembly_id;
+ cinfo.slot = fdata->slot;
+ cinfo.type = JNX_BOARD_TYPE_RE;
+ cinfo.adap = NULL;
+
+ jnx_register_local_card(&cinfo);
+
+ /* The jnx-connector & gpio-cbc-presence will handle the cards */
+ fdata->dt_enabled = jnx_connector_present();
+ if (fdata->dt_enabled) {
+ dev_info(dev, "line cards will handled by the jnx-connector\n");
+ return 0;
+ }
+
+ fdata->workqueue = create_singlethread_workqueue("jnx-cbd-poller");
+ if (!fdata->workqueue) {
+ err = -ENOMEM;
+ goto err_unregister;
+ }
+
+ INIT_DELAYED_WORK(&fdata->work, jnx_cbd_fpga_ch_prs_handler);
+
+ return 0;
+
+err_unregister:
+ jnx_unregister_local_card();
+ jnx_unregister_chassis();
+ jnx_cbd_fpga_sysfs_remove(dev);
+
+ return err;
+}
+
+static int jnx_cbc_remove(struct platform_device *pdev)
+{
+ struct jnx_cbd_fpga_data *fdata = platform_get_drvdata(pdev);
+ int i;
+
+ if (!fdata->dt_enabled) {
+ cancel_delayed_work_sync(&fdata->work);
+ flush_workqueue(fdata->workqueue);
+ destroy_workqueue(fdata->workqueue);
+
+ for (i = 0; i < fdata->mux_channels; i++) {
+ /*
+ * jnx_i2c_find_adapter acquires a hold on
+ * the adapter
+ */
+ i2c_put_adapter(fdata->mux[i]);
+ }
+ }
+
+ jnx_unregister_local_card();
+ jnx_unregister_chassis();
+
+ jnx_cbd_fpga_sysfs_remove(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver jnx_cbc_driver = {
+ .driver = {
+ .name = "jnx-cbd-fpga",
+ .owner = THIS_MODULE,
+ },
+ .probe = jnx_cbc_probe,
+ .remove = jnx_cbc_remove,
+};
+module_platform_driver(jnx_cbc_driver);
+
+MODULE_DESCRIPTION("JNX Polaris CBC Driver");
+MODULE_AUTHOR("Georgi Vlaev <gvlaev@juniper.net>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:jnx-cbd-fpga");
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-common.c b/drivers/staging/jnx/jnx-cbd-fpga-common.c
new file mode 100644
index 0000000..718fb37
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-common.c
@@ -0,0 +1,332 @@
+/*
+ * Juniper Generic Control Board (CB) FPGA Driver common code
+ *
+ * Copyright (C) 2012, 2013, 2014, 2015 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/jnx/jnx-subsys.h>
+#include <linux/jnx/jnx-board-core.h>
+#include <linux/jnx/board_ids.h>
+#include <linux/of.h>
+#include <linux/dmi.h>
+#include "jnx-cbd-fpga-common.h"
+#include "jnx-cbd-fpga-core.h"
+#include "jnx-cbd-fpga-platdata.h"
+#include "jnx-subsys-private.h"
+
+/**********************************************************************
+ *
+ * JNX CBD FPGA Board insertion/removal handling
+ *
+ **********************************************************************/
+
+void jnx_cbd_fpga_board_insert(struct jnx_cbd_fpga_data *fdata,
+ int slot, int chan)
+{
+ int mindex = chan / 64;
+ int mchan = chan % 64;
+
+ if (WARN(chan >= ARRAY_SIZE(fdata->client), "bad chan=%d\n", chan))
+ return;
+ fdata->client[chan] =
+ jnx_board_inserted(fdata->mux[chan], slot,
+ fdata->cfinfo->cpld_mux_bitmask[mindex] &
+ BIT_ULL(mchan));
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_board_insert);
+
+void jnx_cbd_fpga_board_remove(struct jnx_cbd_fpga_data *fdata, int chan)
+{
+ jnx_board_removed(fdata->mux[chan], fdata->client[chan]);
+ fdata->client[chan] = NULL;
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_board_remove);
+
+static void jnx_cbd_fpga_board_remove_all(struct jnx_cbd_fpga_data *fdata)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fdata->client); i++)
+ jnx_cbd_fpga_board_remove(fdata, i);
+}
+
+/**********************************************************************
+ *
+ * JNX CBD FPGA Mastership attribute support
+ *
+ ***********************************************************************/
+#define JNX_MSTR_CFG_REG 0x003C
+#define JNX_MSTR_ALIVE_REG 0x0040
+#define JNX_MSTR_ALIVE_CNT_REG 0x0044
+
+#define JNX_MSTR_BOOTED BIT(0)
+#define JNX_MSTR_RELINQUISH BIT(1)
+#define JNX_MSTR_IM_RDY BIT(3)
+#define JNX_MSTR_IM_MASTER BIT(4)
+#define JNX_MSTR_HE_RDY BIT(5)
+#define JNX_MSTR_HE_MASTER BIT(6)
+
+#define JNX_MSTR (JNX_MSTR_BOOTED | JNX_MSTR_IM_RDY | JNX_MSTR_IM_MASTER)
+#define JNX_ANY_MSTR (JNX_MSTR_IM_MASTER | JNX_MSTR_HE_MASTER)
+#define JNX_HE_MSTR (JNX_MSTR_HE_RDY | JNX_MSTR_HE_MASTER)
+
+#define MSTR_ALIVE_CNT 0x01FF
+#define JNX_MSTR_ALIVE 0x0001
+
+#define JNX_MSTR_REFRESH_TIMEOUT 21
+#define JNX_MAX_MSTR_ALIVE_CNT 0xFF
+
+static bool jnx_cbd_fpga_am_master(struct jnx_cbd_fpga_data *fdata)
+{
+ u32 mstr_cfg = ioread32(fdata->region0 + JNX_MSTR_CFG_REG);
+
+ return (mstr_cfg & JNX_MSTR) == JNX_MSTR;
+}
+
+static bool jnx_cbd_fpga_he_master(struct jnx_cbd_fpga_data *fdata)
+{
+ u32 mstr_cfg = ioread32(fdata->region0 + JNX_MSTR_CFG_REG);
+
+ return (mstr_cfg & JNX_HE_MSTR) == JNX_HE_MSTR;
+}
+
+static bool jnx_cbd_fpga_become_master(struct jnx_cbd_fpga_data *fdata)
+{
+ bool am_master;
+ int i;
+
+ iowrite32(JNX_MSTR_REFRESH_TIMEOUT | (1 << 8),
+ fdata->region0 + JNX_MSTR_ALIVE_CNT_REG);
+ iowrite32(1, fdata->region0 + JNX_MSTR_ALIVE_REG);
+ iowrite32(JNX_MSTR_BOOTED, fdata->region0 + JNX_MSTR_CFG_REG);
+
+ for (i = 0; i < 20; i++) {
+ am_master = jnx_cbd_fpga_am_master(fdata);
+ if (am_master)
+ break;
+ usleep_range(10, 20);
+ }
+
+ if (am_master && !fdata->dt_enabled) /* FIXME dt_enabled is odd */
+ queue_delayed_work(fdata->workqueue, &fdata->work, 0);
+
+ return am_master;
+}
+
+static void jnx_cbd_fpga_relinquish_master(struct jnx_cbd_fpga_data *fdata)
+{
+ u32 mstr_cfg;
+ int i;
+
+ cancel_delayed_work_sync(&fdata->work);
+
+ jnx_cbd_fpga_board_remove_all(fdata);
+
+ mstr_cfg = ioread32(fdata->region0 + JNX_MSTR_CFG_REG);
+ mstr_cfg &= ~JNX_MSTR_BOOTED;
+ iowrite32(mstr_cfg, fdata->region0 + JNX_MSTR_CFG_REG);
+
+ for (i = 0; i < ARRAY_SIZE(fdata->prev_ch_prs); i++)
+ fdata->prev_ch_prs[i] = 0;
+}
+
+int jnx_cbd_fpga_get_master(void *data)
+{
+ struct jnx_cbd_fpga_data *fdata = data;
+
+ if (jnx_cbd_fpga_am_master(fdata))
+ return fdata->slot;
+
+ if (jnx_cbd_fpga_he_master(fdata))
+ return fdata->slot ^ 1;
+
+ return -1;
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_get_master);
+
+bool jnx_cbd_fpga_mastership_get(void *data)
+{
+ struct jnx_cbd_fpga_data *fdata = data;
+
+ return jnx_cbd_fpga_am_master(fdata);
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_mastership_get);
+
+void jnx_cbd_fpga_mastership_set(void *data, bool master)
+{
+ struct jnx_cbd_fpga_data *fdata = data;
+
+ mutex_lock(&fdata->lock);
+ if (jnx_cbd_fpga_am_master(fdata)) {
+ if (!master)
+ jnx_cbd_fpga_relinquish_master(fdata);
+ } else {
+ if (master)
+ jnx_cbd_fpga_become_master(fdata);
+ }
+ mutex_unlock(&fdata->lock);
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_mastership_set);
+
+void jnx_cbd_fpga_mastership_ping(void *data)
+{
+ struct jnx_cbd_fpga_data *fdata = data;
+
+ iowrite32(1, fdata->region0 + JNX_MSTR_ALIVE_REG);
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_mastership_ping);
+
+int jnx_cbd_fpga_mastership_count_get(void *data)
+{
+ struct jnx_cbd_fpga_data *fdata = data;
+
+ return ioread32(fdata->region0 + JNX_MSTR_ALIVE_CNT_REG) &
+ JNX_MAX_MSTR_ALIVE_CNT;
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_mastership_count_get);
+
+int jnx_cbd_fpga_mastership_count_set(void *data, int val)
+{
+ struct jnx_cbd_fpga_data *fdata = data;
+
+ if (val < 0 || val > JNX_MAX_MSTR_ALIVE_CNT)
+ return -EINVAL;
+
+ iowrite32(val | (1 << 8), fdata->region0 + JNX_MSTR_ALIVE_CNT_REG);
+
+ return 0;
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_mastership_count_set);
+
+#define FPGA_MEM_SIZE 0x1000
+
+static struct jnx_cbd_fpga_data *cbd_sysfs_get_dev_info(struct kobject *kobj)
+{
+ struct platform_device *pdev =
+ to_platform_device(container_of(kobj, struct device, kobj));
+
+ return platform_get_drvdata(pdev);
+}
+
+static int cbd_fpga_validate_access_parameters(loff_t offset, size_t count)
+{
+ /*
+ * Need to make sure the count is a multiple of JNX_CBD_FPGA_REG_SIZE
+ * bytes and that the offset plus the number of registers being read
+ * is less than the size of the fpga.
+ */
+ if ((count % JNX_CBD_FPGA_REG_SIZE) == 0)
+ if ((offset + (count / JNX_CBD_FPGA_REG_SIZE)) < FPGA_MEM_SIZE)
+ return 0;
+
+ return -EINVAL;
+}
+
+static ssize_t cbd_fpga_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buffer, loff_t offset, size_t count)
+{
+ struct jnx_cbd_fpga_data *cfpga = cbd_sysfs_get_dev_info(kobj);
+ void __iomem *addr;
+ ssize_t err;
+ int nregs;
+ u32 *buf;
+ int i;
+
+ err = cbd_fpga_validate_access_parameters(offset, count);
+ if (err)
+ return err;
+
+ nregs = count / JNX_CBD_FPGA_REG_SIZE;
+ buf = (u32 *)buffer;
+ addr = cfpga->region0 + offset;
+
+ for (i = 0; i < nregs; i++)
+ buf[i] = ioread32(addr + i * JNX_CBD_FPGA_REG_SIZE);
+
+ return count;
+}
+
+static ssize_t cbd_fpga_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buffer, loff_t offset, size_t count)
+{
+ struct jnx_cbd_fpga_data *cfpga = cbd_sysfs_get_dev_info(kobj);
+ void __iomem *addr;
+ ssize_t err;
+ int nregs;
+ u32 *buf;
+ int i;
+
+ err = cbd_fpga_validate_access_parameters(offset, count);
+ if (err)
+ return err;
+
+ nregs = count / JNX_CBD_FPGA_REG_SIZE;
+ buf = (u32 *)buffer;
+ addr = cfpga->region0 + offset;
+
+ for (i = 0; i < nregs; i++)
+ iowrite32(buf[i], addr + i * JNX_CBD_FPGA_REG_SIZE);
+
+ return count;
+}
+
+static struct bin_attribute cbd_fpga_attr = {
+ .attr = {
+ .name = "fpga-data",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+
+ .size = FPGA_MEM_SIZE,
+ .read = cbd_fpga_read,
+ .write = cbd_fpga_write,
+};
+
+int jnx_cbd_fpga_sysfs_init(struct device *dev)
+{
+ int err;
+
+ err = sysfs_create_bin_file(&dev->kobj, &cbd_fpga_attr);
+ if (err)
+ return err;
+
+ err = sysfs_create_link(&dev->parent->parent->parent->kobj,
+ &dev->kobj, "cbfpga");
+ if (err)
+ goto err_bin_file;
+ return 0;
+
+err_bin_file:
+ sysfs_remove_bin_file(&dev->kobj, &cbd_fpga_attr);
+ return err;
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_sysfs_init);
+
+void jnx_cbd_fpga_sysfs_remove(struct device *dev)
+{
+ sysfs_remove_link(&dev->parent->parent->parent->kobj,
+ "cbfpga");
+ sysfs_remove_bin_file(&dev->kobj, &cbd_fpga_attr);
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_sysfs_remove);
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-common.h b/drivers/staging/jnx/jnx-cbd-fpga-common.h
new file mode 100644
index 0000000..9bf4949
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-common.h
@@ -0,0 +1,27 @@
+/*
+ * jnx-cbd-fpga-common.h
+ *
+ * Copyright (C) 2015 Juniper Networks. All rights reserved.
+ *
+ */
+
+#ifndef JNX_CBD_FPGA_COMMON_H
+#define JNX_CBD_FPGA_COMMON_H
+
+struct jnx_cbd_fpga_data;
+
+void jnx_cbd_fpga_board_insert(struct jnx_cbd_fpga_data *fdata,
+ int slot, int chan);
+void jnx_cbd_fpga_board_remove(struct jnx_cbd_fpga_data *fdata, int chan);
+
+int jnx_cbd_fpga_get_master(void *data);
+bool jnx_cbd_fpga_mastership_get(void *data);
+void jnx_cbd_fpga_mastership_set(void *data, bool master);
+void jnx_cbd_fpga_mastership_ping(void *data);
+int jnx_cbd_fpga_mastership_count_get(void *data);
+int jnx_cbd_fpga_mastership_count_set(void *data, int val);
+
+int jnx_cbd_fpga_sysfs_init(struct device *dev);
+void jnx_cbd_fpga_sysfs_remove(struct device *dev);
+
+#endif /* JNX_CBD_FPGA_COMMON_H */
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-core.c b/drivers/staging/jnx/jnx-cbd-fpga-core.c
new file mode 100644
index 0000000..b665f46
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-core.c
@@ -0,0 +1,540 @@
+/*
+ * Juniper Generic Control Board (CB) FPGA Driver
+ *
+ * Copyright (C) 2012, 2013, 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/i2c-mux.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+#include <linux/workqueue.h>
+#include <linux/jnx/jnx-subsys.h>
+#include <linux/jnx/jnx-board-core.h>
+#include <linux/jnx/board_ids.h>
+#include <linux/jnx/pci_ids.h>
+#include <linux/platform_data/i2c-pca-jnx.h>
+#include <linux/of.h>
+#include "jnx-cbd-fpga-core.h"
+#include "jnx-cbd-fpga-common.h"
+#include "jnx-cbd-fpga-platdata.h"
+#include "jnx-subsys-private.h"
+
+#define DRIVER_VERSION "0.01.0"
+#define DRIVER_DESC "JNX CB FPGA Driver"
+
+#define JNX_CBD_FPGA_MODULE_NAME "jnx-cbd-fpga"
+
+static const struct pci_device_id jnx_cbd_fpga_id_table[] = {
+ {PCI_VDEVICE(JUNIPER, JNX_CBD_FPGA_DID_09B3), JNX_CBD_FPGA_PTX5K},
+ {PCI_VDEVICE(JUNIPER, JNX_CBD_FPGA_DID_0BA8), JNX_CBD_FPGA_PTX3K},
+ {0,}
+};
+MODULE_DEVICE_TABLE(pci, jnx_cbd_fpga_id_table);
+
+static const struct jnx_cbd_fpga_info *jnx_cbd_fpga_info_tbl[] = {
+ [JNX_CBD_FPGA_PTX5K] = &jnx_cbd_fpga_ptx5k_info,
+ [JNX_CBD_FPGA_PTX3K] = &jnx_cbd_fpga_ptx3k_info,
+};
+
+/**********************************************************************
+ *
+ * JNX CBD FPGA I2C controller APIs
+ *
+ **********************************************************************/
+#define JNX_CBD_FPGA_WAIT_FOR_IDLE 10000
+
+#define JNX_CBD_FPGA_I2C_CTRL_READ BIT(0)
+#define JNX_CBD_FPGA_I2C_CTRL_WRITE 0
+#define JNX_CBD_FPGA_I2C_CTRL_START BIT(1)
+#define JNX_CBD_FPGA_I2C_CTRL_SEL_I2C BIT(2)
+
+static int jnx_cbd_fpga_i2c_wait_bus_inactive(struct jnx_cbd_fpga_data *fdata)
+{
+ int i = 0;
+
+ while (ioread32(fdata->ctrl_reg) & JNX_CBD_FPGA_I2C_CTRL_START &&
+ i++ < JNX_CBD_FPGA_WAIT_FOR_IDLE) {
+ udelay(1);
+ }
+
+ if (i >= JNX_CBD_FPGA_WAIT_FOR_IDLE)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static void jnx_cbd_fpga_iowrite32_sync(u32 val, void __iomem *reg)
+{
+ iowrite32(val, reg);
+ ioread32(reg);
+}
+
+static void jnx_cbd_fpga_i2c_writebyte(struct device *dev, int reg, int value)
+{
+ struct jnx_cbd_fpga_data *fdata = dev_get_drvdata(dev);
+ int rc;
+
+ rc = jnx_cbd_fpga_i2c_wait_bus_inactive(fdata);
+ if (rc < 0)
+ return;
+
+ iowrite32(value, fdata->data_reg);
+
+ iowrite32(reg, fdata->addr_reg);
+
+ jnx_cbd_fpga_iowrite32_sync(JNX_CBD_FPGA_I2C_CTRL_SEL_I2C |
+ JNX_CBD_FPGA_I2C_CTRL_WRITE |
+ JNX_CBD_FPGA_I2C_CTRL_START,
+ fdata->ctrl_reg);
+}
+
+static int jnx_cbd_fpga_i2c_readbyte(struct device *dev, int reg)
+{
+ struct jnx_cbd_fpga_data *fdata = dev_get_drvdata(dev);
+ int rc;
+
+ rc = jnx_cbd_fpga_i2c_wait_bus_inactive(fdata);
+ if (rc)
+ return 0;
+
+ iowrite32(reg, fdata->addr_reg);
+
+ iowrite32(JNX_CBD_FPGA_I2C_CTRL_SEL_I2C | JNX_CBD_FPGA_I2C_CTRL_READ |
+ JNX_CBD_FPGA_I2C_CTRL_START, fdata->ctrl_reg);
+
+ rc = jnx_cbd_fpga_i2c_wait_bus_inactive(fdata);
+ if (rc)
+ return 0;
+
+ return (u8)ioread32(fdata->data_reg);
+}
+
+/**********************************************************************
+ *
+ * End JNX CBD FPGA I2C controller APIs
+ *
+ **********************************************************************/
+
+/**********************************************************************
+ *
+ * JNX CBD FPGA I2C Selector Mux APIs
+ *
+ **********************************************************************/
+
+static int cbd_i2c_select_chan(struct i2c_mux_core *muxc, u32 chan)
+{
+ struct jnx_cbd_fpga_data *fdata = i2c_mux_priv(muxc);
+ u32 select;
+
+ select = ioread32(fdata->select_reg);
+ if (select != chan)
+ jnx_cbd_fpga_iowrite32_sync(chan, fdata->select_reg);
+
+ return 0;
+}
+
+static int cbd_i2c_deselect_chan(struct i2c_mux_core *muxc, u32 chan)
+{
+ struct jnx_cbd_fpga_data *fdata = i2c_mux_priv(muxc);
+
+ return cbd_i2c_select_chan(muxc, fdata->default_chan);
+}
+
+/**********************************************************************
+ * JNX CBD FPGA I2C Selector Mux APIs
+ **********************************************************************/
+
+void jnx_cbd_fpga_ch_prs_handler(struct work_struct *work)
+{
+ struct jnx_cbd_fpga_data *fdata = container_of(to_delayed_work(work),
+ struct jnx_cbd_fpga_data,
+ work);
+ const struct jnx_cbd_fpga_info *cfinfo;
+ unsigned long ch_prs, prs_change;
+ int slot, chan;
+ int i, bit;
+
+ cfinfo = fdata->cfinfo;
+
+ mutex_lock(&fdata->lock);
+
+ for (i = 0; i < ARRAY_SIZE(cfinfo->cpld_prs_reg); i++) {
+ if (!cfinfo->cpld_prs_reg[i])
+ continue;
+
+ ch_prs = ioread32(fdata->region0 + cfinfo->cpld_prs_reg[i]);
+ ch_prs |= cfinfo->cpld_prs_set_bitmask[i];
+ if (cfinfo->cpld_prs_bitmask[i])
+ ch_prs &= cfinfo->cpld_prs_bitmask[i];
+ prs_change = ch_prs ^ fdata->prev_ch_prs[i];
+ fdata->prev_ch_prs[i] = ch_prs;
+
+ for_each_set_bit(bit, &prs_change, sizeof(u32) * 8) {
+ chan = cfinfo->get_channel(bit + i * 32);
+ if (chan < 0)
+ continue;
+
+ if (!fdata->mux[chan] && fdata->find_mux_adapter)
+ fdata->mux[chan] =
+ fdata->find_mux_adapter(chan);
+
+ if (!fdata->mux[chan])
+ continue;
+
+ slot = cfinfo->get_slot_from_chan(chan);
+ if (slot < 0)
+ continue;
+
+ pr_info("CBC: presence bit %d -> chan: %d, adap %p\n",
+ bit + i * 32, chan, fdata->mux[chan]);
+
+ if (test_bit(bit, &ch_prs))
+ jnx_cbd_fpga_board_insert(fdata, slot, chan);
+ else
+ jnx_cbd_fpga_board_remove(fdata, chan);
+ }
+ }
+
+ mutex_unlock(&fdata->lock);
+
+ queue_delayed_work(fdata->workqueue, &fdata->work, 10 * HZ);
+}
+EXPORT_SYMBOL(jnx_cbd_fpga_ch_prs_handler);
+
+/**********************************************************************
+ * End JNX CBD FPGA Board insertion/removal handling
+ **********************************************************************/
+
+static void jnx_cbd_fpga_device_reset(void __iomem *reg_addr, u32 mask)
+{
+ u32 device_ctrl = 0;
+
+ device_ctrl = ioread32(reg_addr);
+ device_ctrl |= mask;
+ iowrite32(device_ctrl, reg_addr);
+
+ usleep_range(100, 500); /* was udelay(100) */
+
+ device_ctrl = ioread32(reg_addr);
+ device_ctrl &= ~mask;
+ iowrite32(device_ctrl, reg_addr);
+}
+
+static int jnx_cbd_fpga_ptx5k_get_master(void *data)
+{
+ struct jnx_cbd_fpga_data *fdata = data;
+ int slot;
+ u32 rev;
+
+ slot = jnx_cbd_fpga_get_master(data);
+ if (slot < 0) {
+ rev = ioread32(fdata->region0 + JNX_CBD_FPGA_REV_REG);
+ if (rev < JNX_CBD_FPGA_REV_GOOD) {
+ WARN_ONCE(1,
+ "FPGA ver. 0x%x can't support master detect\n",
+ rev);
+ /*
+ * We can not determine who is master. It is not us,
+ * so assume that the other RE is master.
+ */
+ slot = fdata->slot ^ 1;
+ }
+ }
+
+ return slot;
+}
+
+/*
+ * Check if we're on OF enabled setup. If so, we'll assume that board
+ * insertion/removal is handled by the jnx-connector driver + overlays
+ * and not the jnx-* platform drivers. This allows a temporary workaround
+ * until all the ptx-x86 code is OF capable.
+ */
+static int jnx_connector_present(void)
+{
+ return of_have_populated_dt();
+}
+
+/*
+ * jnx_of_create_device()
+ * Hack - create the presence driver and associate with dt node
+ * This vaguely simulates what the mfd_core does.
+ */
+static char * const of_presence_dev_names[] = {
+ "jnx,gpio-cbc-presence",
+ "jnx,gpio-cbc-presence-other",
+};
+
+static struct platform_device *jnx_of_create_device(struct pci_dev *pcidev,
+ int id, const char *name,
+ const char *of_compatible)
+{
+ struct device *parent = &pcidev->dev;
+ struct platform_device *pdev = NULL;
+ struct device_node *np = NULL;
+ struct resource res;
+
+ if (!parent->of_node || !of_compatible)
+ return NULL;
+
+ memset(&res, 0, sizeof(res));
+ res.start = pci_resource_start(pcidev, 0);
+ res.end = pci_resource_end(pcidev, 0);
+ res.flags = IORESOURCE_MEM;
+ res.parent = &pcidev->resource[0];
+
+ pdev = platform_device_register_resndata(parent, name, id,
+ &res, 1, NULL, 0);
+
+ if (IS_ERR(pdev))
+ return NULL;
+
+ for_each_child_of_node(parent->of_node, np) {
+ if (of_device_is_compatible(np, of_compatible)) {
+ pdev->dev.of_node = np;
+ return pdev;
+ }
+ }
+
+ dev_err(parent, "Failed to create %s device\n", of_compatible);
+ /* of_compatible node missing */
+ platform_device_put(pdev);
+
+ return NULL;
+}
+
+static int
+jnx_cbd_fpga_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ const struct jnx_cbd_fpga_info *cfinfo =
+ jnx_cbd_fpga_info_tbl[id->driver_data];
+ struct jnx_i2c_pca_platform_data idata;
+ struct device *dev = &pdev->dev;
+ struct jnx_cbd_fpga_data *fdata;
+ struct jnx_chassis_info chinfo;
+ struct jnx_card_info cinfo;
+ void __iomem *reset_reg;
+ u32 *bitmasks;
+ u32 ch_prs;
+ int err;
+ int i;
+
+ fdata = devm_kzalloc(dev, sizeof(*fdata), GFP_KERNEL);
+ if (!fdata)
+ return -ENOMEM;
+
+ dev_dbg(dev, "%s.%s(): irq pin %d\n", JNX_CBD_FPGA_MODULE_NAME,
+ __func__, pdev->irq);
+
+ err = pcim_enable_device(pdev);
+ if (err)
+ return err;
+
+ err = pcim_iomap_regions(pdev, 1 << 0, JNX_CBD_FPGA_MODULE_NAME);
+ if (err)
+ return err;
+
+ /*
+ * Need to remap the io regions for the slow bus registers and
+ * the i2c select register. These are needed for the associated
+ * i2c adapter and mux drivers.
+ */
+ fdata->region0 = pcim_iomap_table(pdev)[0];
+
+ fdata->cfinfo = cfinfo;
+ fdata->default_chan = cfinfo->i2cmux_default_chan;
+ fdata->mux_channels = cfinfo->mux_channels;
+ if (fdata->mux_channels > JNX_CBD_FPGA_NUM_MUXES)
+ fdata->mux_channels = JNX_CBD_FPGA_NUM_MUXES;
+
+ mutex_init(&fdata->lock);
+
+ /*
+ * Set up the slow bus address registers
+ */
+ fdata->addr_reg = fdata->region0 + JNX_CBD_FPGA_SLOW_BUS_REG;
+ fdata->data_reg = fdata->addr_reg + JNX_CBD_FPGA_REG_SIZE;
+ fdata->ctrl_reg = fdata->data_reg + JNX_CBD_FPGA_REG_SIZE;
+
+ fdata->select_reg = fdata->region0 + JNX_CBD_FPGA_I2C_SELECT_REG;
+
+ pci_set_drvdata(pdev, fdata);
+
+ err = jnx_cbd_fpga_sysfs_init(dev);
+ if (err)
+ return err;
+
+ /*
+ * Run any devices resets that are required before instantiating
+ * any other devices.
+ */
+ reset_reg = fdata->region0 + cfinfo->reset->reg_offset;
+ bitmasks = cfinfo->reset->bitmasks;
+ i = 0;
+
+ while (bitmasks[i])
+ jnx_cbd_fpga_device_reset(reset_reg, bitmasks[i++]);
+
+ /*
+ * Register the chassis with the system so userspace is able to
+ * determine platform type, etc.
+ */
+ chinfo.platform = cfinfo->platform;
+ chinfo.chassis_no = 0;
+ chinfo.multichassis = 0;
+ chinfo.master_data = fdata;
+ chinfo.get_master = jnx_cbd_fpga_ptx5k_get_master;
+ chinfo.mastership_get = jnx_cbd_fpga_mastership_get;
+ chinfo.mastership_set = jnx_cbd_fpga_mastership_set;
+ chinfo.mastership_ping = jnx_cbd_fpga_mastership_ping;
+ chinfo.mastership_count_get = jnx_cbd_fpga_mastership_count_get;
+ chinfo.mastership_count_set = jnx_cbd_fpga_mastership_count_set;
+
+ jnx_register_chassis(&chinfo);
+
+ /*
+ * Register the local card with the system so userspace is able to
+ * determine board type etc.
+ */
+ ch_prs = ioread32(fdata->region0 + JNX_CBD_FPGA_CH_PRS_REG);
+
+ fdata->slot = (ch_prs & cfinfo->ch_prs_cb0_bit) ? 0 : 1;
+
+ cinfo.assembly_id = cfinfo->assembly_id;
+ cinfo.slot = fdata->slot;
+ cinfo.type = JNX_BOARD_TYPE_RE;
+ cinfo.adap = NULL;
+
+ jnx_register_local_card(&cinfo);
+
+ /*
+ * Set up and create the i2c controller
+ */
+ idata.dev = dev;
+ idata.adap = &fdata->adap;
+ idata.clock = 400000;
+ idata.read_byte = jnx_cbd_fpga_i2c_readbyte;
+ idata.write_byte = jnx_cbd_fpga_i2c_writebyte;
+
+ fdata->i2c_ctlr = platform_device_alloc(cfinfo->i2c_ctlr_name, -1);
+ if (!fdata->i2c_ctlr) {
+ err = -ENOMEM;
+ goto err_unregister;
+ }
+
+ err = platform_device_add_data(fdata->i2c_ctlr, &idata, sizeof(idata));
+ if (err)
+ goto err_pdev;
+
+ err = platform_device_add(fdata->i2c_ctlr);
+ if (err)
+ goto err_pdev;
+
+ /* Now create i2c muxes on top of the i2c adapter */
+ fdata->muxc = i2c_mux_alloc(&fdata->adap, &pdev->dev,
+ fdata->mux_channels, 0, 0,
+ cbd_i2c_select_chan,
+ cbd_i2c_deselect_chan);
+ if (!fdata->muxc) {
+ err = -ENOMEM;
+ goto err_pdev;
+ }
+ fdata->muxc->priv = fdata;
+
+ for (i = 0; i < fdata->mux_channels; i++) {
+ err = i2c_mux_add_adapter(fdata->muxc, 0, i, 0);
+ if (err)
+ goto err_mux;
+ }
+
+ /* The jnx-connector & gpio-cbc-presence will handle the cards */
+ fdata->dt_enabled = jnx_connector_present();
+ if (fdata->dt_enabled) {
+ dev_info(dev, "line cards will handled by the jnx-connector\n");
+ for (i = 0; i < ARRAY_SIZE(fdata->presence_devices); i++) {
+ fdata->presence_devices[i] =
+ jnx_of_create_device(pdev, i,
+ "gpio-cbc-presence",
+ of_presence_dev_names[i]);
+ if (!fdata->presence_devices[i])
+ dev_err(dev, "Failed to create %s device\n",
+ of_presence_dev_names[i]);
+ }
+ return 0;
+ }
+
+ fdata->workqueue = create_singlethread_workqueue("jnx-cbd-poller");
+ if (!fdata->workqueue) {
+ err = -ENOMEM;
+ goto err_mux;
+ }
+
+ INIT_DELAYED_WORK(&fdata->work, jnx_cbd_fpga_ch_prs_handler);
+
+ return 0;
+
+err_mux:
+ i2c_mux_del_adapters(fdata->muxc);
+
+err_pdev:
+ platform_device_put(fdata->i2c_ctlr);
+
+err_unregister:
+ jnx_unregister_chassis();
+ jnx_cbd_fpga_sysfs_remove(dev);
+
+ return err;
+}
+
+static void jnx_cbd_fpga_remove(struct pci_dev *pdev)
+{
+ struct jnx_cbd_fpga_data *fdata = pci_get_drvdata(pdev);
+ int i;
+
+ if (!fdata->dt_enabled) {
+ cancel_delayed_work_sync(&fdata->work);
+ flush_workqueue(fdata->workqueue);
+ destroy_workqueue(fdata->workqueue);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(fdata->presence_devices); i++) {
+ if (fdata->presence_devices[i])
+ platform_device_put(fdata->presence_devices[i]);
+ }
+
+ i2c_mux_del_adapters(fdata->muxc);
+ platform_device_put(fdata->i2c_ctlr);
+ jnx_unregister_local_card();
+ jnx_unregister_chassis();
+ jnx_cbd_fpga_sysfs_remove(&pdev->dev);
+}
+
+static struct pci_driver jnx_cbd_fpga_driver = {
+ .name = JNX_CBD_FPGA_MODULE_NAME,
+ .id_table = jnx_cbd_fpga_id_table,
+ .probe = jnx_cbd_fpga_probe,
+ .remove = jnx_cbd_fpga_remove,
+};
+
+module_pci_driver(jnx_cbd_fpga_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-core.h b/drivers/staging/jnx/jnx-cbd-fpga-core.h
new file mode 100644
index 0000000..ac7a4cf
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-core.h
@@ -0,0 +1,68 @@
+/*
+ * Juniper Generic Control Board (CB) FPGA Driver
+ *
+ * Copyright (C) 2012, 2013, 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _JNX_CBD_FPGA_CORE_H
+#define _JNX_CBD_FPGA_CORE_H
+
+#include <linux/bitops.h>
+
+#define JNX_CBD_FPGA_REG_SIZE 4
+
+#define JNX_CBD_FPGA_INTR_SRC 0x0000
+#define JNX_CBD_FPGA_INTR_EN 0x0004
+#define JNX_CBD_FPGA_I2C_SELECT_REG 0x0010
+#define JNX_CBD_FPGA_CH_PRS_REG 0x0014
+#define JNX_CBD_FPGA_SLOW_BUS_REG 0x0054
+#define JNX_CBD_FPGA_REV_REG 0x0060
+#define JNX_CBD_FPGA_DUMMY_READ_REG 0x0064
+#define JNX_CBD_FPGA_CH_OTHER_PRS_REG 0x00D4
+
+#define JNX_CBD_FPGA_REV_GOOD 0x011F
+
+#define JNX_CBD_FPGA_NUM_MUXES 96
+/*
+ * JNX CBD CH Presence definitions
+ */
+#define JNX_CBD_FPGA_CH_PRS_CB0_PIN BIT(27)
+
+struct jnx_cbd_fpga_data {
+ u32 slot;
+ const struct jnx_cbd_fpga_info *cfinfo;
+ u32 default_chan;
+ u32 prev_ch_prs[4];
+ struct i2c_adapter * (*find_mux_adapter)(int chan);
+ int mux_channels;
+ struct i2c_adapter *mux[JNX_CBD_FPGA_NUM_MUXES];
+ struct i2c_client *client[JNX_CBD_FPGA_NUM_MUXES];
+ struct i2c_mux_core *muxc;
+ bool standalone;
+ int irq;
+ struct i2c_adapter adap;
+ struct mutex lock; /* safe access lock */
+ struct platform_device *i2c_ctlr;
+ struct workqueue_struct *workqueue;
+ struct delayed_work work;
+ int dt_enabled;
+ struct platform_device *presence_devices[2];
+ void __iomem *region0;
+ void __iomem *addr_reg;
+ void __iomem *data_reg;
+ void __iomem *ctrl_reg;
+ void __iomem *select_reg;
+};
+
+void jnx_cbd_fpga_ch_prs_handler(struct work_struct *work);
+
+#endif /* _JNX_CBD_FPGA_CORE_H */
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-platdata.h b/drivers/staging/jnx/jnx-cbd-fpga-platdata.h
new file mode 100644
index 0000000..a91e38d
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-platdata.h
@@ -0,0 +1,51 @@
+/*
+ * Juniper CBD FPGA Platform Data
+ *
+ * Copyright (C) 2012, 2013, 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __JNX_CBD_FPGA_H
+#define __JNX_CBD_FPGA_H
+
+enum jnx_cbd_fpga_boards {
+ JNX_CBD_FPGA_PTX5K,
+ JNX_CBD_FPGA_PTX3K,
+};
+
+struct jnx_cbd_fpga_dev_reset_data {
+ u16 reg_offset;
+ u32 *bitmasks;
+};
+
+struct jnx_cbd_fpga_info {
+ u32 platform;
+ u32 assembly_id;
+ u64 cpld_mux_bitmask[2];
+ u32 cpld_prs_reg[4];
+ u32 cpld_prs_set_bitmask[4];
+ u32 cpld_prs_bitmask[4];
+ u32 ch_prs_cb0_bit;
+ u32 mux_channels;
+ u32 i2cmux_default_chan;
+ const char *i2c_ctlr_name;
+ struct jnx_cbd_fpga_dev_reset_data *reset;
+ int (*get_channel)(u32 bit);
+ int (*get_slot_from_chan)(int chan);
+};
+
+extern struct jnx_cbd_fpga_info jnx_cbd_fpga_ptx5k_info,
+ jnx_cbd_fpga_ptx3k_info,
+ jnx_cbd_fpga_ptx1k_info,
+ jnx_cbd_fpga_ptx1k_p2_info,
+ jnx_cbd_fpga_ptx21k_info;
+
+#endif /* __JNX_CBD_FPGA_H */
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c b/drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c
new file mode 100644
index 0000000..3314569
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-ptx1k.c
@@ -0,0 +1,134 @@
+/*
+ * Juniper PTX 1000 Control Board (CB) FPGA
+ *
+ * Copyright (C) 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/jnx/jnx-subsys.h>
+#include <linux/jnx/board_ids.h>
+#include "jnx-cbd-fpga-core.h"
+#include "jnx-cbd-fpga-platdata.h"
+#include "jnx-subsys-private.h"
+
+#define JNX_CBD_FPGA_PTX1K_DEVICE_CTRL_REG 0x00C0
+#define JNX_CBD_FPGA_PTX1K_DEFAULT_CHAN 26 /* LOCAL_I2C */
+
+#define JNX_CBD_FPGA_PTX1K_CPLD_MUX 0xFF00 /* FPC[8] 8..15 */
+
+static u32 jnx_cbd_fpga_ptx1k_bitmask[] = { 0 };
+
+/* DEVICE_CTRL is not valid for Polaris */
+static struct jnx_cbd_fpga_dev_reset_data jnx_cbd_fpga_ptx1k_device_reset = {
+ .reg_offset = JNX_CBD_FPGA_PTX1K_DEVICE_CTRL_REG,
+ .bitmasks = jnx_cbd_fpga_ptx1k_bitmask,
+};
+
+/* Table to map from mux channel number to slot */
+
+static s8 ptx1k_mux_slot_map[40] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, /* 0.. 7: Unused + RSVD[2] */
+ 0, 1, 2, 3, 4, 5, 6, 7, /* 8..15: FPC[8] */
+ 0, 1, 2, 3, 4, 5, /* 16..21: SIB[6] */
+ -1, -1, -1, /* 22..24: Unused */
+ 0, /* 25: MP */
+ 1, /* 26: Local */
+ -1, /* 27: Unused */
+ 0, 1, 2, 3, 4, /* 28..32: PSM[5] */
+ -1, /* 33: Unused */
+ 0, /* 34: FT0 */
+ 0, /* 35: FPD */
+ 0, 1, /* 36..37: CB[2] */
+ -1, -1 /* 38..39: SFPP[2] */
+};
+
+static int jnx_cbd_fpga_ptx1k_get_slot_from_channel(int chan)
+{
+ if (chan < 0 || chan >= ARRAY_SIZE(ptx1k_mux_slot_map) ||
+ ptx1k_mux_slot_map[chan] < 0)
+ return -EINVAL;
+
+ return ptx1k_mux_slot_map[chan];
+}
+
+/* Table to map from presence bit to mux channel */
+static s8 ptx1k_mux_channel_map[64] = {
+ 8, 9, 10, 11, 12, 13, 14, 15, /* CH_PRS FPC[7:0] */
+ -1, -1, -1, -1, -1, -1, -1, -1, /* CH_PRS Reserved [15:8] */
+ 34, /* CH_PRS FT[16] */
+ -1, -1, -1, -1, /* CH_PRS Reserved [20:17] */
+ 36, 37, /* CH_PRS CB[22:21] */
+ 35, /* CH_PRS FPD[23] */
+ -1, -1, -1, -1, -1, -1, /* CH_PRS Reserved[29:24] */
+ 25, /* CH_PRS MP[30] */
+ -1, /* CH_PRS Reserved[31]*/
+ 28, 29, 30, 31, 32, /* OTHER_CH_PRS PSM[4:0] */
+ 16, 17, 18, 19, 20, 21, /* OTHER_CH_PRS SIB[10:5] */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1 /* OTHER_CH_PRS Reserved[31:11] */
+};
+
+static int jnx_cbd_fpga_ptx1k_get_channel(u32 bit)
+{
+ if (bit > 63 || ptx1k_mux_channel_map[bit] < 0)
+ return -EINVAL;
+
+ return ptx1k_mux_channel_map[bit];
+}
+
+struct jnx_cbd_fpga_info jnx_cbd_fpga_ptx1k_info = {
+ .platform = JNX_PRODUCT_POLARIS,
+ .assembly_id = JNX_ID_POLARIS_RCB,
+ .cpld_mux_bitmask = {
+ JNX_CBD_FPGA_PTX1K_CPLD_MUX,
+ },
+ .cpld_prs_set_bitmask = {
+ BIT(30), /* MP */
+ },
+ .cpld_prs_reg = {
+ JNX_CBD_FPGA_CH_PRS_REG,
+ JNX_CBD_FPGA_CH_OTHER_PRS_REG,
+ },
+ .ch_prs_cb0_bit = JNX_CBD_FPGA_CH_PRS_CB0_PIN,
+ .mux_channels = ARRAY_SIZE(ptx1k_mux_slot_map),
+ .i2cmux_default_chan = JNX_CBD_FPGA_PTX1K_DEFAULT_CHAN,
+ .i2c_ctlr_name = "",
+ .reset = &jnx_cbd_fpga_ptx1k_device_reset,
+ .get_channel = jnx_cbd_fpga_ptx1k_get_channel,
+ .get_slot_from_chan = jnx_cbd_fpga_ptx1k_get_slot_from_channel,
+};
+
+struct jnx_cbd_fpga_info jnx_cbd_fpga_ptx1k_p2_info = {
+ .platform = JNX_PRODUCT_POLARIS,
+ .assembly_id = JNX_ID_POLARIS_RCB_P2,
+ .cpld_mux_bitmask = {
+ JNX_CBD_FPGA_PTX1K_CPLD_MUX,
+ },
+ .cpld_prs_set_bitmask = {
+ BIT(30), /* MP */
+ },
+ .cpld_prs_reg = {
+ JNX_CBD_FPGA_CH_PRS_REG,
+ JNX_CBD_FPGA_CH_OTHER_PRS_REG,
+ },
+ .ch_prs_cb0_bit = JNX_CBD_FPGA_CH_PRS_CB0_PIN,
+ .mux_channels = ARRAY_SIZE(ptx1k_mux_slot_map),
+ .i2cmux_default_chan = JNX_CBD_FPGA_PTX1K_DEFAULT_CHAN,
+ .i2c_ctlr_name = "",
+ .reset = &jnx_cbd_fpga_ptx1k_device_reset,
+ .get_channel = jnx_cbd_fpga_ptx1k_get_channel,
+ .get_slot_from_chan = jnx_cbd_fpga_ptx1k_get_slot_from_channel,
+};
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c b/drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c
new file mode 100644
index 0000000..d9a4227
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-ptx21k.c
@@ -0,0 +1,143 @@
+/*
+ * Juniper PTX 21000 Control Board (CB) FPGA
+ *
+ * Copyright (C) 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/jnx/jnx-subsys.h>
+#include <linux/jnx/board_ids.h>
+#include <linux/bitops.h>
+
+#include "jnx-cbd-fpga-platdata.h"
+#include "jnx-cbd-fpga-core.h"
+#include "jnx-subsys-private.h"
+
+#define JNX_CBD_FPGA_PTX21K_DEVICE_CTRL_REG 0x00C0
+#define JNX_CBD_FPGA_PTX21K_DEFAULT_CHAN 57 /* LOCAL_I2C */
+
+#define JNX_CBD_FPGA_PTX21K_CPLD_MUX 0x00000000
+#define JNX_CBD_FPGA_PTX21K_CPLD_MUX_HI 0x00000000
+
+/* PTX21k has a different bit for the CB slot */
+#define JNX_CBD_FPGA_21K_CH_PRS_CB0_PIN BIT(30)
+
+#define JNX_CBD_FPGA_21K_CH_PRS_SIB_REG 0x0028
+#define JNX_CBD_FPGA_21K_CH_PRS_SIB_MASK 0x1ff
+
+static u32 jnx_cbd_fpga_ptx21k_bitmask[] = { 0 };
+
+static struct jnx_cbd_fpga_dev_reset_data jnx_cbd_fpga_ptx21k_device_reset = {
+ .reg_offset = JNX_CBD_FPGA_PTX21K_DEVICE_CTRL_REG,
+ .bitmasks = jnx_cbd_fpga_ptx21k_bitmask,
+};
+
+/* Table to map from mux channel number to slot */
+
+static s8 ptx21k_mux_slot_map[72] = {
+ 0, 1, 2, 3, /* 0..3: FPC */
+ 4, 5, 6, 7, /* 4..7: FPC */
+ 8, 9, 10, 11, /* 8..11: FPC */
+ 12, 13, 14, 15, /* 12..15: FPC */
+ 16, 17, 18, 19, /* 16..19: FPC */
+ 0, 1, 2, 3, /* 20..23: PSM */
+ 4, 5, 6, 7, /* 24..27: PSM */
+ 8, 9, 10, 11, /* 28..31: PSM */
+ 12, 13, 14, 15, /* 32..35: PSM */
+ 16, 17, 18, 19, /* 36..39: PSM */
+ 20, 21, 22, 23, /* 40..43: PSM */
+ 0, 1, 2, 3, 4, 5, /* 44..49: FT */
+ 0, 1, /* 50..51: CB */
+ 0, 1, 2, 3, 4, /* 52..56: BP */
+ 0, /* 57: Local */
+ -1, /* 58: QSFP */
+ 0, /* 59: FPD */
+ 0, 1, 2, 3, 4, /* 60 ..64: SIB */
+ 5, 6, 7, 8, /* 65 ..68: SIB */
+ 0, /* 69: CB PCIeSW */
+};
+
+static int jnx_cbd_fpga_ptx21k_get_slot_from_channel(int chan)
+{
+ if (chan < 0 || chan >= ARRAY_SIZE(ptx21k_mux_slot_map) ||
+ ptx21k_mux_slot_map[chan] < 0)
+ return -EINVAL;
+
+ return ptx21k_mux_slot_map[chan];
+}
+
+static s8 ptx21k_mux_channel_map[96] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, /* CH_PRS[7:0] = FPC[7:0] */
+ 8, 9, 10, 11, 12, 13, 14, 15, /* CH_PRS[15:8] = FPC[15:8] */
+ 16, 17, 18, 19, /* CH_PRS[19:16] = FPC[19:16] */
+ 44, 45, 46, 47, /* CH_PRS[23:20] = FT[3:0] */
+ 50, 51, /* CH_PRS[25:24] = CB[0:1] */
+ 59, /* CH_PRS[26] = FPD */
+ -1, -1, -1, -1, /* CH_PRS[30:27] = Reserved */
+ -1, /* CH_PRS MP[31] There are 5 MPs*/
+ 20, 21, 22, 23, 24, 25, 26, 27, /* OTHER_CH_PRS[7:0] = PSM[7:0] */
+ 28, 29, 30, 31, 32, 33, 34, 35, /* OTHER_CH_PRS[15:8] = PSM[15:8] */
+ 36, 37, 38, 39, 40, 41, 42, 43, /* OTHER_CH_PRS[23:15] = PSM[23:15] */
+ 48, 49, /* OTHER_CH_PRS[25:24] = FT[5:4] */
+ -1, -1, -1, -1, -1, -1, /* OTHER_CH_PRS[31:26] QSFP, SFP */
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, /* CH_PRS_SIB[8:0] = SIB[8:0] */
+ -1, -1, -1, -1, -1, -1, -1, -1, /* CH_PRS_SIB[16:9] = NA */
+ -1, -1, -1, -1, -1, -1, -1, -1, /* CH_PRS_SIB[17:24] = NA */
+ -1, -1, -1, -1, -1, -1, -1 /* CH_PRS_SIB[25:31] = NA */
+};
+
+static int jnx_cbd_fpga_ptx21k_get_channel(u32 bit)
+{
+ if (bit >= ARRAY_SIZE(ptx21k_mux_channel_map) ||
+ ptx21k_mux_channel_map[bit] < 0)
+ return -EINVAL;
+
+ return ptx21k_mux_channel_map[bit];
+}
+
+struct jnx_cbd_fpga_info jnx_cbd_fpga_ptx21k_info = {
+ .platform = JNX_PRODUCT_OMEGA,
+ .assembly_id = JNX_ID_OMEGA_RCB,
+ .cpld_mux_bitmask = {
+ JNX_CBD_FPGA_PTX21K_CPLD_MUX,
+ JNX_CBD_FPGA_PTX21K_CPLD_MUX_HI,
+ },
+ .ch_prs_cb0_bit = JNX_CBD_FPGA_21K_CH_PRS_CB0_PIN,
+ .cpld_prs_set_bitmask = {
+ BIT(31), /* MP */
+ },
+ .cpld_prs_reg = {
+ JNX_CBD_FPGA_CH_PRS_REG,
+ JNX_CBD_FPGA_CH_OTHER_PRS_REG,
+ JNX_CBD_FPGA_21K_CH_PRS_SIB_REG,
+ 0
+ },
+ .cpld_prs_bitmask = {
+ 0,
+ 0,
+ JNX_CBD_FPGA_21K_CH_PRS_SIB_MASK,
+ 0
+ },
+ .mux_channels = ARRAY_SIZE(ptx21k_mux_slot_map),
+ .i2cmux_default_chan = JNX_CBD_FPGA_PTX21K_DEFAULT_CHAN,
+ .i2c_ctlr_name = "",
+ .reset = &jnx_cbd_fpga_ptx21k_device_reset,
+ .get_channel = jnx_cbd_fpga_ptx21k_get_channel,
+ .get_slot_from_chan = jnx_cbd_fpga_ptx21k_get_slot_from_channel,
+};
+EXPORT_SYMBOL_GPL(jnx_cbd_fpga_ptx21k_info);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c b/drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c
new file mode 100644
index 0000000..ad575c0
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-ptx3k.c
@@ -0,0 +1,111 @@
+/*
+ * Juniper PTX 3000 Control Board (CB) FPGA
+ *
+ * Copyright (C) 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/jnx/jnx-subsys.h>
+#include <linux/jnx/board_ids.h>
+#include <linux/platform_data/i2c-pca-jnx.h>
+#include "jnx-cbd-fpga-core.h"
+#include "jnx-cbd-fpga-platdata.h"
+#include "jnx-subsys-private.h"
+
+#define JNX_CBD_FPGA_PTX3K_DEVICE_CTRL_REG 0x00C0
+#define JNX_CBD_FPGA_PTX3K_I2C_CTRL_RESET BIT(24)
+#define JNX_CBD_FPGA_PTX3K_DEFAULT_CHAN 0x10
+
+#define JNX_CBD_FPGA_PTX3K_CPLD_MUX 0xFF
+
+static u32 jnx_cbd_fpga_ptx3k_bitmask[] = {
+ JNX_CBD_FPGA_PTX3K_I2C_CTRL_RESET,
+ 0
+};
+
+static struct jnx_cbd_fpga_dev_reset_data jnx_cbd_fpga_ptx3k_device_reset = {
+ .reg_offset = JNX_CBD_FPGA_PTX3K_DEVICE_CTRL_REG,
+ .bitmasks = jnx_cbd_fpga_ptx3k_bitmask,
+};
+
+/* Table to map from mux channel number to slot */
+
+static s8 ptx3k_mux_slot_map[42] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* FPC */
+ 1, /* 0x10, local */
+ 0, 1, /* 0x11, CB */
+ -1, -1,
+ 0, /* 0x15, FPD */
+ 0, /* 0x16, MP */
+ 0, 1, /* 0x17, fans */
+ -1, -1, -1,
+ 0, 1, 2, 3, 4, /* 0x1c, PSM */
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, /* 0x21, SIB */
+};
+
+static int jnx_cbd_fpga_ptx3k_get_slot_from_channel(int chan)
+{
+ if (chan < 0 || chan >= ARRAY_SIZE(ptx3k_mux_slot_map) ||
+ ptx3k_mux_slot_map[chan] < 0)
+ return -EINVAL;
+
+ return ptx3k_mux_slot_map[chan];
+}
+
+/* Table to map from presence bit to mux channel */
+static s8 ptx3k_mux_channel_map[64] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* FPC */
+ 0x17, 0x18, /* fans */
+ -1, -1, -1,
+ 0x11, 0x12, /* CB */
+ 0x15, /* FPD */
+ -1, -1, -1, -1, -1, -1,
+ 0x16, /* MP */
+ -1,
+ 0x1c, 0x1d, 0x1e, 0x1f, 0x20, /* PSM */
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, /* SIB */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1,
+};
+
+static int jnx_cbd_fpga_ptx3k_get_channel(u32 bit)
+{
+ if (bit > 63 || ptx3k_mux_channel_map[bit] < 0)
+ return -EINVAL;
+
+ return ptx3k_mux_channel_map[bit];
+}
+
+struct jnx_cbd_fpga_info jnx_cbd_fpga_ptx3k_info = {
+ .platform = JNX_PRODUCT_HENDRICKS,
+ .assembly_id = JNX_ID_HENDRICKS_CB,
+ .cpld_mux_bitmask = {
+ JNX_CBD_FPGA_PTX3K_CPLD_MUX,
+ },
+ .cpld_prs_set_bitmask = {
+ BIT(30),
+ },
+ .cpld_prs_reg = {
+ JNX_CBD_FPGA_CH_PRS_REG,
+ JNX_CBD_FPGA_CH_OTHER_PRS_REG,
+ },
+ .ch_prs_cb0_bit = JNX_CBD_FPGA_CH_PRS_CB0_PIN,
+ .mux_channels = 42,
+ .i2cmux_default_chan = JNX_CBD_FPGA_PTX3K_DEFAULT_CHAN,
+ .i2c_ctlr_name = I2C_PCA_JNX_CTLR_NAME,
+ .reset = &jnx_cbd_fpga_ptx3k_device_reset,
+ .get_channel = jnx_cbd_fpga_ptx3k_get_channel,
+ .get_slot_from_chan = jnx_cbd_fpga_ptx3k_get_slot_from_channel,
+};
diff --git a/drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c b/drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c
new file mode 100644
index 0000000..11adc8f
--- /dev/null
+++ b/drivers/staging/jnx/jnx-cbd-fpga-ptx5k.c
@@ -0,0 +1,107 @@
+/*
+ * Juniper PTX 5000 Control Board (CB) FPGA
+ *
+ * Copyright (C) 2012, 2013, 2014 Juniper Networks. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/jnx/board_ids.h>
+#include <linux/jnx/jnx-subsys.h>
+#include <linux/platform_data/i2c-pca-jnx.h>
+#include "jnx-cbd-fpga-core.h"
+#include "jnx-cbd-fpga-platdata.h"
+#include "jnx-subsys-private.h"
+
+#define JNX_CBD_FPGA_PTX5K_DEVICE_CTRL_REG 0x00C0
+#define JNX_CBD_FPGA_PTX5K_I2C_CTRL_RESET BIT(24)
+#define JNX_CBD_FPGA_PTX5K_DEFAULT_CHAN 0x10
+
+#define JNX_CBD_FPGA_PTX5K_CPLD_MUX 0xFF
+
+static u32 jnx_cbd_fpga_ptx5k_bitmask[] = {
+ JNX_CBD_FPGA_PTX5K_I2C_CTRL_RESET,
+ 0
+};
+
+static struct jnx_cbd_fpga_dev_reset_data jnx_cbd_fpga_ptx5k_device_reset = {
+ .reg_offset = JNX_CBD_FPGA_PTX5K_DEVICE_CTRL_REG,
+ .bitmasks = jnx_cbd_fpga_ptx5k_bitmask,
+};
+
+/* Table to map from mux channel number to slot */
+
+static s8 mux_slot_map[30] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* FPC */
+ 1, /* 0x10, local */
+ 0, 1, /* 0x11, CB */
+ 0, 1, /* 0x13, CCG */
+ 0, /* 0x15, FPD */
+ 0, /* 0x16, MP */
+ 0, 1, 2, 3, 4, /* 0x17, fans */
+ 0, 1, /* 0x1c, PDU */
+};
+
+static int jnx_cbd_fpga_ptx5k_get_slot_from_channel(int chan)
+{
+ if (chan < 0 || chan >= ARRAY_SIZE(mux_slot_map))
+ return -EINVAL;
+
+ return mux_slot_map[chan];
+}
+
+/* Table to map from presence bit to mux channel */
+static s8 mux_channel_map[64] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* FPC */
+ 0x17, 0x18, 0x19, 0x1a, 0x1b, /* fans */
+ 0x11, 0x12, /* CB */
+ 0x15, /* FPD */
+ -1, -1, -1, -1,
+ 0x13, 0x14, /* CCG */
+ 0x16, /* MP */
+ -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0x1c, 0x1d, /* PDU */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
+static int jnx_cbd_fpga_ptx5k_get_channel(u32 bit)
+{
+ if (bit > 63)
+ return -EINVAL;
+
+ return mux_channel_map[bit];
+}
+
+struct jnx_cbd_fpga_info jnx_cbd_fpga_ptx5k_info = {
+ .platform = JNX_PRODUCT_SANGRIA,
+ .assembly_id = JNX_ID_SNG_CB,
+ .cpld_mux_bitmask = {
+ JNX_CBD_FPGA_PTX5K_CPLD_MUX,
+ },
+ .cpld_prs_set_bitmask = {
+ BIT(30),
+ },
+ .cpld_prs_reg = {
+ JNX_CBD_FPGA_CH_PRS_REG,
+ JNX_CBD_FPGA_CH_OTHER_PRS_REG,
+ },
+ .ch_prs_cb0_bit = JNX_CBD_FPGA_CH_PRS_CB0_PIN,
+ .mux_channels = 30,
+ .i2cmux_default_chan = JNX_CBD_FPGA_PTX5K_DEFAULT_CHAN,
+ .i2c_ctlr_name = I2C_PCA_JNX_CTLR_NAME,
+ .reset = &jnx_cbd_fpga_ptx5k_device_reset,
+ .get_channel = jnx_cbd_fpga_ptx5k_get_channel,
+ .get_slot_from_chan = jnx_cbd_fpga_ptx5k_get_slot_from_channel,
+};
--
1.9.1
^ permalink raw reply related [flat|nested] 14+ messages in thread