EM/BCU: Enabling BCU for CTP Plus Platform
authorShravan <shravan.k.b.m@intel.com>
Fri, 18 May 2012 10:35:01 +0000 (16:05 +0530)
committerbuildbot <buildbot@intel.com>
Tue, 22 May 2012 18:11:14 +0000 (11:11 -0700)
BZ: 35622

This patch is for implementing BCU driver to detect
voltage drop in the system and perform action
required for those interrupts. Configuration
files updated for all CTP platforms.

Change-Id: Ib2fa25f166190fcb3acac512ded46085eeca0bc0
Signed-off-by: Rapaka, Naveen <naveen.rapaka@intel.com>
Reviewed-on: http://android.intel.com:8080/49352
Reviewed-by: Pallala, Ramakrishna <ramakrishna.pallala@intel.com>
Reviewed-by: R, Durgadoss <durgadoss.r@intel.com>
Tested-by: Kallappa Manjanna, MadhukumarX <madhukumarx.kallappa.manjanna@intel.com>
Reviewed-by: Tardy, Pierre <pierre.tardy@intel.com>
Reviewed-by: Gross, Mark <mark.gross@intel.com>
Reviewed-by: buildbot <buildbot@intel.com>
Tested-by: buildbot <buildbot@intel.com>
arch/x86/platform/intel-mid/board-ctp.c
arch/x86/platform/intel-mid/device_libs/Makefile
arch/x86/platform/intel-mid/device_libs/platform_msic_vdd.c [new file with mode: 0644]
arch/x86/platform/intel-mid/device_libs/platform_msic_vdd.h [new file with mode: 0644]
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/intel_mid_vdd.c [new file with mode: 0644]

index bdbcc54..e61dff9 100644 (file)
@@ -37,8 +37,7 @@
 #include "device_libs/platform_msic_gpio.h"
 #include "device_libs/platform_clvs_audio.h"
 #include "device_libs/platform_msic_power_btn.h"
-#include "device_libs/platform_msic_ocd.h"
-
+#include "device_libs/platform_msic_vdd.h"
 /*
  * I2C devices
  */
@@ -105,7 +104,7 @@ struct devs_id __initconst device_ids[] = {
                                        &ipc_device_handler},
        {"msic_power_btn", SFI_DEV_TYPE_IPC, 1, &msic_power_btn_platform_data,
                                        &ipc_device_handler},
-       {"msic_ocd", SFI_DEV_TYPE_IPC, 1, &msic_ocd_platform_data,
+       {"msic_ocd", SFI_DEV_TYPE_IPC, 1, &msic_vdd_platform_data,
                                        &ipc_device_handler},
        {"msic_gpio", SFI_DEV_TYPE_IPC, 1, &msic_gpio_platform_data,
                                        &ipc_device_handler},
index f7d5ef9..0d4e3af 100644 (file)
@@ -4,6 +4,7 @@ obj-y                                                   += platform_msic_audio.o
 obj-y                                                  += platform_clvs_audio.o
 obj-y                                                  += platform_msic_gpio.o
 obj-y                                                  += platform_msic_ocd.o
+obj-$(CONFIG_SENSORS_MID_VDD)                          += platform_msic_vdd.o
 obj-$(CONFIG_MSIC_GPADC)                               += platform_msic_adc.o
 obj-$(CONFIG_BATTERY_INTEL_MDF)                                += platform_msic_battery.o
 obj-$(CONFIG_INTEL_MID_POWER_BUTTON)                   += platform_msic_power_btn.o
diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic_vdd.c b/arch/x86/platform/intel-mid/device_libs/platform_msic_vdd.c
new file mode 100644 (file)
index 0000000..38c9f2a
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * platform_msic_vdd.c: MSIC VDD platform data initilization file
+ *
+ * (C) Copyright 2012 Intel Corporation
+ * Author: Chaurasia, Avinash K <avinash.k.chaurasia@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/scatterlist.h>
+#include <linux/sfi.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/mfd/intel_msic.h>
+#include <asm/intel-mid.h>
+#include "platform_ipc.h"
+#include "platform_msic_vdd.h"
+
+#define BCUIRQ 0x24
+
+static struct resource msic_vdd_resources[] __initdata = {
+       {
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct intel_ipc_dev_res ipc_msic_vdd_res[] __initdata = {
+       {
+               .name                   = MSIC_VDD_DEV_NAME,
+               .num_resources          = ARRAY_SIZE(msic_vdd_resources),
+               .resources              = msic_vdd_resources,
+       },
+
+};
+
+void __init *msic_vdd_platform_data(void *info)
+{
+       struct io_apic_irq_attr irq_attr;
+       static struct intel_msic_ocd_pdata msic_vdd_pdata;
+       /* IAFW support for MSIC VDD is not available,
+       Its Workaround - Should be reverted, Refer BZ 34411 */
+       int ioapic = mp_find_ioapic(BCUIRQ);
+
+       irq_attr.ioapic = ioapic;
+       irq_attr.ioapic_pin = BCUIRQ;
+       irq_attr.trigger = 1;
+       irq_attr.polarity = 0;
+       io_apic_set_pci_routing(NULL, BCUIRQ, &irq_attr);
+
+       msic_vdd_pdata.gpio = BCUIRQ;
+
+       handle_ipc_irq_res(BCUIRQ, ipc_msic_vdd_res);
+
+       return &msic_vdd_pdata;
+}
diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic_vdd.h b/arch/x86/platform/intel-mid/device_libs/platform_msic_vdd.h
new file mode 100644 (file)
index 0000000..d1588c6
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * platform_msic_vdd.h: MSIC VDD platform data header file
+ *
+ * (C) Copyright 2012 Intel Corporation
+ * Author: Chaurasia, Avinash K <avinash.k.chaurasia@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+#ifndef _PLATFORM_MSIC_VDD_H_
+#define _PLATFORM_MSIC_VDD_H_
+
+/* BZ 37319- Change the driver name from msic_ocd to msic_vdd */
+#define MSIC_VDD_DEV_NAME      "msic_ocd"
+
+extern void __init *msic_vdd_platform_data(void *info) __attribute__((weak));
+#endif
index 6b7967d..6b2ad7f 100644 (file)
@@ -1475,6 +1475,13 @@ config SENSORS_MID_CURRENT
          Say Y here to enable Over Current Detection driver on Intel Medfield
          Platform.
 
+config SENSORS_MID_VDD
+       tristate "Voltage drop detection driver for Intel Clovertrail plus platform"
+        depends on INTEL_SCU_IPC
+        help
+         Say Y here to enable Voltage drop detection driver on Intel Clovertrail plus
+         Platform.
+
 if ACPI
 
 comment "ACPI drivers"
index 9c5823a..1edb94b 100644 (file)
@@ -129,6 +129,7 @@ obj-$(CONFIG_MSIC_GPADC)    += intel_mid_gpadc.o
 obj-$(CONFIG_BASINCOVE_GPADC)   += intel_basincove_gpadc.o
 obj-$(CONFIG_MID_PWM)          += intel_mid_pwm.o
 obj-$(CONFIG_SENSORS_MID_CURRENT)      += intel_mid_ocd.o
+obj-$(CONFIG_SENSORS_MID_VDD)  += intel_mid_vdd.o
 obj-$(CONFIG_SENSORS_LSM303_MAG)   += lsm303dlhc_compass.o
 
 # PMBus drivers
diff --git a/drivers/hwmon/intel_mid_vdd.c b/drivers/hwmon/intel_mid_vdd.c
new file mode 100644 (file)
index 0000000..cf47298
--- /dev/null
@@ -0,0 +1,759 @@
+/*
+ * intel_mid_vdd.c - Intel Clovertrail Platform Voltage Drop Detection Driver
+ *
+ *
+ * Copyright (C) 2012 Intel Corporation
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.        See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Author: Durgadoss R <durgadoss.r@intel.com>
+ *         Chaurasia, Avinash K <avinash.k.chaurasia@intel.com>
+ *
+ * This driver monitors the voltage level of the system. When the voltage
+ * drops below a programmed threshold, it notifies the CPU of the drop.
+ * Also, the driver configures the HW to take some actions to prevent
+ * system crash due to sudden drop in voltage. The HW unit that does all
+ * these, is named as Burst Control Unit(BCU).
+ */
+
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ipc_device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/timer.h>
+#include <asm/intel_scu_ipc.h>
+#include <linux/kfifo.h>
+
+/* BZ 37319- Change the driver name from msic_ocd to msic_vdd
+once IAFW support is available */
+#define DRIVER_NAME "msic_ocd"
+#define DEVICE_NAME "msic_ocd"
+
+/* BCU registers that control the behaviour of output signals */
+#define BCUDISA_BEH            0x10C
+#define BCUDISB_BEH            0x10D
+#define BCUDISCRIT_BEH         0x10E
+#define BCUPROCHOT_BEH         0x10F
+#define BCU_STICKY             (1 << 2) /* BCU output signal stickiness */
+#define BCU_POLARITY           (1 << 1) /* BCU output signal polarity */
+#define BCU_STICKY_BIT         0x04
+
+/* BCU Status registers */
+#define SBCUIRQ                        0x110
+#define SBCUCTRL               0x111
+#define SBCUCTRL_CLEAR_ASSERT  0x02
+#define SBCUCTRL_STICKY_WARNB  0x04
+#define SBCUCTRL_STICKY_WARNA  0x08
+
+#define IRQLVL1MSK             0x021
+
+/* Voltage limits (in mV) */
+#define MAX_VOLTAGE            3300
+#define MIN_VOLTAGE            2600
+
+/* Number of voltage levels: WarnA, WarnB, Crit */
+#define NUM_VOLT_LEVELS                3
+
+/* Number of BCU Behavior registers: 0x10C -> 0x10F */
+#define NUM_BEH_REGS           4
+
+/* BCU Interrupt registers */
+#define BCUIRQ                 0x00A
+#define MBCUIRQ                        0x019
+/* BCUIRQ register settings */
+#define VCRIT_IRQ              (1 << 2)
+#define VWARNA_IRQ             (1 << 1)
+#define VWARNB_IRQ             (1 << 0)
+
+/* BCU registers that govern voltage monitoring */
+#define VWARNA_CFG             0x109
+#define VWARNB_CFG             0x10A
+#define VCRIT_CFG              0x10B
+
+#define VCOMP_ENABLE           (1 << 3) /* Enable BCU voltage comparator */
+#define BCU_OUT_EN             (1 << 0) /* Enable BCU output signal */
+
+/* BCU real time status flags for corresponding signals */
+#define SVWARNB                        (1<<0)
+#define SVWARNA                        (1<<1)
+#define SVCRIT                 (1<<2)
+
+/* curently 20 fast clock is set */
+#define DEBOUNCE               (0x70)
+
+/* voltage threshold to be set for different theshold points */
+#define VWARNA_VOLT_THRES      0x04 /* 2.9V */
+#define VWARNB_VOLT_THRES      0x04 /* 2.9V */
+#define VCRIT_VOLT_THRES       0x01 /* 3.2V */
+
+/* this flag has been used in lot of places to update the proper bit
+* in registers */
+#define FLAGS_THRES_REGS       0xF7
+
+/* unmask the 2nd level interrupts as by default they are masked */
+#define UNMASK_MBCUIRQ         0x00
+
+/* check whether bit is sticky or not by checking 3rd bit */
+#define IS_STICKY(data)                (data & 0x04)
+
+/* clear assert by writing 1 to appropiate bit of SBCUCTRL */
+#define CLEAR_ASSERT(bit)      (1 << bit)
+
+/* masking/clearing the bit */
+#define MASK_BIT(bit)          (1 << bit)
+#define CLEAR_BIT(bit)         (~(1 << bit))
+
+/* defines timer values in msec for spurious interrupts */
+#define FIXED_TIME             6
+#define RESETTING_VALUE                6
+
+#define BCU_STATUS(data)       ((data ^ 1) << 3)
+
+#define SET_ACTION_MASK(data, value, bit) (data | (value << bit))
+
+/* This macro is used in hardcoding of gpio */
+#define MSIC_VDD               0x24
+
+/* defines reading BCU registers from SRAM */
+#define MSIC_BCU_STAT          0xFFFF7FC8
+#define        MSIC_BCU_LEN            1
+#define IRQ_FIFO_MAX           16
+#define IRQ_KFIFO_ELEMENT      1
+
+/* Defined to match the correponding bit positions of the interrupt */
+enum { VWARNB_EVENT = 1, VWARNA_EVENT = 2, VCRIT_EVENT = 4};
+
+static DEFINE_MUTEX(vdd_update_lock);
+/* defining the fifo to store the interrupt value */
+static DEFINE_KFIFO(irq_fifo, u8, IRQ_FIFO_MAX);
+
+static struct timer_list masking_timer, spurious_timer;
+
+static int debouncing_time_after_timer_expired = RESETTING_VALUE;
+
+struct vdd_info {
+       unsigned int irq;
+       struct device *dev;
+       struct ipc_device *pdev;
+       /* Cached value of the status register, since the last interrupt */
+       unsigned int intrpt_status;
+       /* mapping SRAM address for BCU interrupts */
+       void __iomem *bcu_intr_addr;
+};
+
+static inline int vdd_set_bits(u16 addr, u8 mask)
+{
+       return intel_scu_ipc_update_register(addr, 0xff, mask);
+}
+
+static ssize_t store_action_mask(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       int ret;
+       long assert_pin;
+       if (strict_strtol(buf, 10, &assert_pin))
+               return -EINVAL;
+       /* 0x1F represents five output mask bit for 4 different registers
+       * when value is 0x1F then all the output pins will be masked and
+       * and when its 0x00 then it means all the output pins are unmasked
+       * and if interrupt occurs then all the output pins will be trigerred
+       * according to settings */
+       if (assert_pin < 0x00 || assert_pin > 0x1F)
+               return -EINVAL;
+
+       mutex_lock(&vdd_update_lock);
+       ret = intel_scu_ipc_update_register(BCUDISA_BEH,
+               ((assert_pin >> 0) & 1), 0x01);
+       if (ret)
+               goto mask_ipc_fail;
+       ret = intel_scu_ipc_update_register(BCUDISB_BEH,
+               ((assert_pin >> 1) & 1), 0x01);
+       if (ret)
+               goto mask_ipc_fail;
+       ret = intel_scu_ipc_update_register(BCUDISCRIT_BEH,
+               ((assert_pin >> 2) & 1), 0x01);
+       if (ret)
+               goto mask_ipc_fail;
+       ret = intel_scu_ipc_update_register(BCUPROCHOT_BEH,
+               ((assert_pin >> 3) & 1)<<1, 0x02);
+       if (ret)
+               goto mask_ipc_fail;
+       ret = intel_scu_ipc_update_register(BCUPROCHOT_BEH,
+               ((assert_pin >> 4) & 1), 0x01);
+       if (ret)
+               goto mask_ipc_fail;
+       ret = count;
+mask_ipc_fail:
+       mutex_unlock(&vdd_update_lock);
+       return ret;
+
+}
+
+static ssize_t show_action_mask(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int ret;
+       uint8_t data;
+       long assert_pin = 0;
+
+       mutex_lock(&vdd_update_lock);
+       ret = intel_scu_ipc_ioread8(BCUDISA_BEH, &data);
+       if (ret)
+               goto mask_ipc_fail;
+       assert_pin = SET_ACTION_MASK(assert_pin, (data & 1), 0);
+
+       ret = intel_scu_ipc_ioread8(BCUDISB_BEH, &data);
+       if (ret)
+               goto mask_ipc_fail;
+       assert_pin = SET_ACTION_MASK(assert_pin, (data & 1), 1);
+
+       ret = intel_scu_ipc_ioread8(BCUDISCRIT_BEH, &data);
+       if (ret)
+               goto mask_ipc_fail;
+       assert_pin = SET_ACTION_MASK(assert_pin, (data & 1), 2);
+
+       ret = intel_scu_ipc_ioread8(BCUPROCHOT_BEH, &data);
+       if (ret)
+               goto mask_ipc_fail;
+       assert_pin = SET_ACTION_MASK(assert_pin, ((data>>1) & 1), 3);
+
+       ret = intel_scu_ipc_ioread8(BCUPROCHOT_BEH, &data);
+       if (ret)
+               goto mask_ipc_fail;
+       assert_pin = SET_ACTION_MASK(assert_pin, (data & 1), 4);
+mask_ipc_fail:
+       mutex_unlock(&vdd_update_lock);
+       return sprintf(buf, "%x\n", assert_pin);
+}
+
+static ssize_t change_bcu_status(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       int ret;
+       long bcu_enable;
+
+       if (strict_strtol(buf, 10, &bcu_enable))
+               return -EINVAL;
+       if (bcu_enable != 0 &&  bcu_enable != 1)
+               return -EINVAL;
+
+       mutex_lock(&vdd_update_lock);
+       /* bcu_enable = 0 means enable bcu */
+       ret = intel_scu_ipc_update_register(VWARNA_CFG,
+                       BCU_STATUS(bcu_enable), 0x08);
+       if (ret)
+               goto bcu_ipc_fail;
+       ret = intel_scu_ipc_update_register(VWARNB_CFG,
+                       BCU_STATUS(bcu_enable), 0x08);
+       if (ret)
+               goto bcu_ipc_fail;
+       ret = intel_scu_ipc_update_register(VCRIT_CFG,
+                       BCU_STATUS(bcu_enable), 0x08);
+       if (ret)
+               goto bcu_ipc_fail;
+
+       ret = count;
+bcu_ipc_fail:
+       mutex_unlock(&vdd_update_lock);
+       return ret;
+}
+
+static ssize_t show_bcu_status(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int ret, bcu_enable;
+       uint8_t data;
+
+       ret = intel_scu_ipc_ioread8(VWARNA_CFG, &data);
+       if (ret)
+               return ret;
+       /* if BCU is enabled set it to 0 else 1 */
+       bcu_enable = ((data >> 3) & 0x01) ^ 1;
+       return sprintf(buf, "%d\n", bcu_enable);
+}
+
+static ssize_t store_volt_thres(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       int ret;
+       uint8_t data;
+       long volt;
+       struct sensor_device_attribute_2 *s_attr =
+                                       to_sensor_dev_attr_2(attr);
+
+       if (strict_strtol(buf, 10, &volt))
+               return -EINVAL;
+
+       if (volt > MAX_VOLTAGE || volt < MIN_VOLTAGE)
+               return -EINVAL;
+
+       mutex_lock(&vdd_update_lock);
+
+       ret = intel_scu_ipc_ioread8(VWARNA_CFG + s_attr->nr, &data);
+       if (ret)
+               goto ipc_fail;
+
+       /*
+        * The VWARN*_CFG registers hold 7 voltage values, in [0-2]
+        * bits. 3.3v corresponds to 0 and 2.6v corresponds to 7. So,
+        * find the difference(diff) from MAX_VOLTAGE and divide it by
+        * 100(since the values are entered as mV). Then, set bits
+        * [0-2] to 'diff'
+        */
+       data = (data & 0xF8) | ((MAX_VOLTAGE - volt)/100);
+
+       ret = intel_scu_ipc_iowrite8(VWARNA_CFG + s_attr->nr, data);
+       if (ret)
+               goto ipc_fail;
+
+       ret = count;
+
+ipc_fail:
+       mutex_unlock(&vdd_update_lock);
+       return ret;
+}
+
+static ssize_t show_volt_thres(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int ret, volt;
+       uint8_t data;
+       struct sensor_device_attribute_2 *s_attr =
+                                       to_sensor_dev_attr_2(attr);
+
+       ret = intel_scu_ipc_ioread8(VWARNA_CFG + s_attr->nr, &data);
+       if (ret)
+               return ret;
+
+       /* Read bits [0-2] of data and multiply by 100(for mV) */
+       volt = (data & 0x07) * 100;
+
+       return sprintf(buf, "%d\n", MAX_VOLTAGE - volt);
+}
+
+static ssize_t show_irq_status(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       int ret;
+       uint8_t irq_status;
+
+       ret = intel_scu_ipc_ioread8(SBCUIRQ, &irq_status);
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "%x\n", irq_status);
+}
+
+static ssize_t show_action_status(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       uint8_t action_status;
+       int ret;
+
+       ret = intel_scu_ipc_ioread8(SBCUCTRL, &action_status);
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "%x\n", action_status);
+}
+
+static void timer_platform_ready_tohandle(unsigned long data)
+{
+       int ret;
+       ret = intel_scu_ipc_update_register(MBCUIRQ, CLEAR_BIT(2), 0x04);
+       if (!timer_pending(&spurious_timer)) {
+               spurious_timer.expires = msecs_to_jiffies(RESETTING_VALUE);
+               add_timer(&spurious_timer);
+       }
+}
+
+static void timer_spurious_cleared(unsigned long data)
+{
+       int ret;
+       uint8_t irq_data, sticky_data;
+       ret = intel_scu_ipc_ioread8(SBCUIRQ, &irq_data);
+       if (!(irq_data & 4)) {
+               ret = intel_scu_ipc_ioread8(BCUDISCRIT_BEH, &sticky_data);
+               if (IS_STICKY(sticky_data)) {
+                       mutex_lock(&vdd_update_lock);
+                       ret = intel_scu_ipc_update_register(SBCUCTRL,
+                               CLEAR_ASSERT(1), 0x02);
+                       mutex_unlock(&vdd_update_lock);
+               }
+       }
+}
+
+
+static void handle_events(int flag, void *dev_data)
+{
+       uint8_t irq_data, sticky_data;
+       struct vdd_info *vinfo = (struct vdd_info *)dev_data;
+       int ret;
+
+       ret = intel_scu_ipc_ioread8(SBCUIRQ, &irq_data);
+       if (ret)
+               goto handle_ipc_fail;
+
+       if (flag & VCRIT_EVENT) {
+               dev_dbg(&vinfo->pdev->dev, "vdd VCRIT hits\n");
+               if (irq_data & SVCRIT) {
+                       /* interrupt up for VCRIT */
+                       if (!timer_pending(&spurious_timer)) {
+                               /* increase the masking time for spurious
+                               * interrupts and mask it for that time */
+                               masking_timer.expires =
+                                       msecs_to_jiffies(FIXED_TIME +
+                               debouncing_time_after_timer_expired);
+                               debouncing_time_after_timer_expired +=
+                                       RESETTING_VALUE;
+                       } else {
+                               debouncing_time_after_timer_expired =
+                                       RESETTING_VALUE;
+                               masking_timer.expires =
+                                       msecs_to_jiffies(FIXED_TIME);
+                       }
+                       ret = intel_scu_ipc_update_register(MBCUIRQ,
+                               MASK_BIT(2), 0x04);
+                       add_timer(&masking_timer);
+               } else {
+               /* interrupt cleared */
+                       ret = intel_scu_ipc_ioread8(BCUDISCRIT_BEH,
+                               &sticky_data);
+                       if (ret)
+                               goto handle_ipc_fail;
+                       if (IS_STICKY(sticky_data)) {
+                               ret = intel_scu_ipc_update_register(SBCUCTRL,
+                                       CLEAR_ASSERT(1), SBCUCTRL_CLEAR_ASSERT);
+                               if (ret)
+                                       goto handle_ipc_fail;
+                       }
+               }
+       }
+       if (flag & VWARNB_EVENT) {
+               dev_dbg(&vinfo->pdev->dev, "vdd VWARNB hits\n");
+               if (irq_data & SVWARNB) {
+                       /*interrupt up for WARNB*/
+                       ret = intel_scu_ipc_update_register(BCUDISB_BEH,
+                               BCU_STICKY, BCU_STICKY_BIT);
+                       if (ret)
+                               goto handle_ipc_fail;
+               } else {
+                       /*interrupt cleared*/
+                       ret = intel_scu_ipc_update_register(BCUDISB_BEH,
+                               ~BCU_STICKY, BCU_STICKY_BIT);
+                       if (ret)
+                               goto handle_ipc_fail;
+                       if (IS_STICKY(sticky_data)) {
+                               ret = intel_scu_ipc_update_register(SBCUCTRL,
+                                       CLEAR_ASSERT(2), SBCUCTRL_STICKY_WARNB);
+                               if (ret)
+                                       goto handle_ipc_fail;
+                       }
+               }
+       }
+       if (flag & VWARNA_EVENT) {
+               dev_dbg(&vinfo->pdev->dev, "vdd VWARNA hits\n");
+               if (irq_data & SVWARNA) {
+                       /*interrupt up for WARNA*/
+                       ret = intel_scu_ipc_update_register(BCUDISA_BEH,
+                               BCU_STICKY, BCU_STICKY_BIT);
+                       if (ret)
+                               goto handle_ipc_fail;
+               } else {
+               /*interrupt cleared*/
+                       ret = intel_scu_ipc_update_register(BCUDISA_BEH,
+                               ~BCU_STICKY, BCU_STICKY_BIT);
+                       if (ret)
+                               goto handle_ipc_fail;
+                       if (IS_STICKY(sticky_data)) {
+                               ret = intel_scu_ipc_update_register(SBCUCTRL,
+                                       CLEAR_ASSERT(3), SBCUCTRL_STICKY_WARNA);
+                               if (ret)
+                                       goto handle_ipc_fail;
+                       }
+               }
+       }
+       kobject_uevent(&vinfo->pdev->dev.kobj, KOBJ_CHANGE);
+       return;
+handle_ipc_fail:
+       kobject_uevent(&vinfo->pdev->dev.kobj, KOBJ_CHANGE);
+       dev_warn(&vinfo->pdev->dev, "ipc read/write failed\n");
+       return;
+}
+
+static irqreturn_t vdd_intrpt_handler(int id, void *dev)
+{
+       struct vdd_info *vinfo = (struct vdd_info *)dev;
+       uint8_t irq_data;
+       if (unlikely(kfifo_is_full(&irq_fifo)))
+               return IRQ_WAKE_THREAD;
+
+       irq_data = readb(vinfo->bcu_intr_addr);
+       /* Interrupt Queuing */
+       kfifo_in(&irq_fifo, &irq_data, IRQ_KFIFO_ELEMENT);
+       return IRQ_WAKE_THREAD;
+};
+
+static irqreturn_t vdd_interrupt_thread_handler(int irq, void *dev_data)
+{
+       int ret;
+       uint8_t irq_data, event;
+       struct vdd_info *vinfo = (struct vdd_info *)dev_data;
+       dev_dbg(&vinfo->pdev->dev, "vdd_handle_intrp\n");
+
+       if (!vinfo)
+               return IRQ_NONE;
+
+       if (unlikely(kfifo_is_empty(&irq_fifo))) {
+               dev_dbg(&vinfo->pdev->dev, "vdd IRQ empty fifo\n");
+               return IRQ_NONE;
+       }
+       ret = kfifo_out(&irq_fifo, &irq_data, IRQ_KFIFO_ELEMENT);
+       if (ret != IRQ_KFIFO_ELEMENT)   {
+               dev_dbg(&vinfo->pdev->dev, "vdd KFIFO underflow\n");
+               return IRQ_NONE;
+       }
+
+       mutex_lock(&vdd_update_lock);
+       if (irq_data & VCRIT_IRQ)               /* BCU VCRIT Interrupt */
+               event = VCRIT_EVENT;
+       else if (irq_data & VWARNA_IRQ)         /* BCU WARNA Interrupt */
+               event = VWARNA_EVENT;
+       else                            /* BCU WARNB Interrupt */
+               event = VWARNB_EVENT;
+
+       handle_events(event, dev_data);
+
+       mutex_unlock(&vdd_update_lock);
+       return IRQ_HANDLED;
+}
+
+
+static SENSOR_DEVICE_ATTR_2(voltage_warnA, S_IRUGO | S_IWUSR,
+                               show_volt_thres, store_volt_thres, 0, 0);
+static SENSOR_DEVICE_ATTR_2(voltage_warnB, S_IRUGO | S_IWUSR,
+                               show_volt_thres, store_volt_thres, 1, 0);
+static SENSOR_DEVICE_ATTR_2(voltage_warn_crit, S_IRUGO | S_IWUSR,
+                               show_volt_thres, store_volt_thres, 2, 0);
+static SENSOR_DEVICE_ATTR_2(action_mask, S_IRUGO | S_IWUSR, show_action_mask,
+                               store_action_mask, 0, 0);
+static SENSOR_DEVICE_ATTR_2(bcu_status, S_IRUGO | S_IWUSR, show_bcu_status,
+                               change_bcu_status, 0, 0);
+static SENSOR_DEVICE_ATTR_2(irq_status, S_IRUGO, show_irq_status,
+                               NULL, 0, 0);
+static SENSOR_DEVICE_ATTR_2(action_status, S_IRUGO, show_action_status,
+                               NULL, 0, 0);
+
+static struct attribute *mid_vdd_attrs[] = {
+       &sensor_dev_attr_voltage_warnA.dev_attr.attr,
+       &sensor_dev_attr_voltage_warnB.dev_attr.attr,
+       &sensor_dev_attr_voltage_warn_crit.dev_attr.attr,
+       &sensor_dev_attr_action_mask.dev_attr.attr,
+       &sensor_dev_attr_bcu_status.dev_attr.attr,
+       &sensor_dev_attr_irq_status.dev_attr.attr,
+       &sensor_dev_attr_action_status.dev_attr.attr,
+       NULL
+};
+
+static struct attribute_group mid_vdd_gr = {
+       .name = "msic_voltage",
+       .attrs = mid_vdd_attrs
+};
+
+static int mid_vdd_probe(struct ipc_device *pdev)
+{
+       int i, ret;
+       struct vdd_info *vinfo = devm_kzalloc(&pdev->dev,
+                               sizeof(struct vdd_info), GFP_KERNEL);
+       if (!vinfo) {
+               dev_dbg(&pdev->dev, "kzalloc failed\n");
+               return -ENOMEM;
+       }
+
+       vinfo->pdev = pdev;
+       vinfo->irq = ipc_get_irq(pdev, 0);
+
+       /* Revert me:
+        * Using hard coding here, will revert after
+        * IAFW support for BCU is available
+        */
+       vinfo->irq = MSIC_VDD;
+
+       ipc_set_drvdata(pdev, vinfo);
+
+       vinfo->bcu_intr_addr = ioremap(MSIC_BCU_STAT, MSIC_BCU_LEN);
+       if (!vinfo->bcu_intr_addr) {
+               dev_warn(&pdev->dev,
+                       "mid_vdd_probe bcu_intr_addr failed\n");
+               return -ENOMEM;
+       }
+       /* Enable Voltage Comparator logic for all warning levels */
+       for (i = 0; i < NUM_VOLT_LEVELS; i++)
+               vdd_set_bits(VWARNA_CFG + i, VCOMP_ENABLE);
+
+       /* Enable BCU output signals for all warning levels */
+       for (i = 0; i < NUM_BEH_REGS; i++)
+               vdd_set_bits(BCUDISA_BEH + i, BCU_OUT_EN);
+
+       /* Creating a sysfs group with mid_vdd_gr attributes */
+       ret = sysfs_create_group(&pdev->dev.kobj, &mid_vdd_gr);
+       if (ret) {
+               dev_dbg(&pdev->dev, "vdd sysfs_create_group failed\n");
+               goto vdd_error1;
+       }
+
+       /*registering with hwmon class*/
+       vinfo->dev = hwmon_device_register(&pdev->dev);
+       if (IS_ERR(vinfo->dev)) {
+               ret = PTR_ERR(vinfo->dev);
+               vinfo->dev = NULL;
+               dev_dbg(&pdev->dev, "vdd hwmon_dev_regs failed\n");
+               goto vdd_error2;
+       }
+       ret = request_threaded_irq(vinfo->irq, vdd_intrpt_handler,
+                                               vdd_interrupt_thread_handler,
+                                               IRQF_TRIGGER_FALLING,
+                                               DRIVER_NAME, vinfo);
+       if (ret) {
+               dev_dbg(&pdev->dev, "vdd request_threaded_irq failed:%d\n",
+                       ret);
+               goto vdd_error3;
+       }
+
+       /*initialize the timers */
+       init_timer(&masking_timer);
+       masking_timer.function  = timer_platform_ready_tohandle;
+       init_timer(&spurious_timer);
+       spurious_timer.function = timer_spurious_cleared;
+
+       /* unmasking all the interrupts */
+       ret = intel_scu_ipc_iowrite8(MBCUIRQ, UNMASK_MBCUIRQ);
+       if (ret) {
+               dev_dbg(&pdev->dev, "vdd unmasking%d\n", ret);
+               goto vdd_error4;
+       }
+
+       /* setting debounce and voltage threshold for WARNA */
+       ret = intel_scu_ipc_update_register(VWARNA_CFG,
+               DEBOUNCE | VWARNA_VOLT_THRES, FLAGS_THRES_REGS);
+       if (ret) {
+               dev_dbg(&pdev->dev,
+               "vdd update VWARNA_CFG debounce time failed:%d\n", ret);
+               goto vdd_ok;
+       }
+
+       /* setting debounce and voltage threshold for WARNB */
+       ret = intel_scu_ipc_update_register(VWARNB_CFG,
+               DEBOUNCE | VWARNB_VOLT_THRES, FLAGS_THRES_REGS);
+       if (ret) {
+               dev_dbg(&pdev->dev,
+               "vdd updating VWARNB_CFG debounce time failed:%d\n", ret);
+               goto vdd_ok;
+       }
+
+       /* setting debounce and voltage threshold for CRIT */
+       ret = intel_scu_ipc_update_register(VCRIT_CFG,
+               DEBOUNCE | VCRIT_VOLT_THRES, FLAGS_THRES_REGS);
+       if (ret) {
+               dev_dbg(&pdev->dev,
+               "vdd updating VCRIT_CFG debounce time failed:%d\n", ret);
+               goto vdd_ok;
+       }
+vdd_ok:
+       dev_dbg(&pdev->dev, "vdd Probe: returning SUCCESS\n");
+       return 0;
+vdd_error4:
+       free_irq(vinfo->irq, vinfo);
+vdd_error3:
+       hwmon_device_unregister(vinfo->dev);
+vdd_error2:
+       sysfs_remove_group(&pdev->dev.kobj, &mid_vdd_gr);
+vdd_error1:
+       iounmap(vinfo->bcu_intr_addr);
+       return ret;
+}
+
+static int mid_vdd_resume(struct device *dev)
+{
+       return 0;
+}
+
+static int mid_vdd_suspend(struct device *dev)
+{
+       return 0;
+}
+
+static int mid_vdd_remove(struct ipc_device *pdev)
+{
+       struct vdd_info *vinfo = ipc_get_drvdata(pdev);
+
+       if (vinfo) {
+               free_irq(vinfo->irq, vinfo);
+               hwmon_device_unregister(vinfo->dev);
+               sysfs_remove_group(&pdev->dev.kobj, &mid_vdd_gr);
+               iounmap(vinfo->bcu_intr_addr);
+       }
+       return 0;
+}
+
+/*********************************************************************
+ *             Driver initialisation and finalization
+ *********************************************************************/
+static const struct ipc_device_id vdd_id_table[] = {
+       { DEVICE_NAME, 1 },
+       { },
+};
+
+static const struct dev_pm_ops msic_vdd_pm_ops = {
+       .suspend = mid_vdd_suspend,
+       .resume = mid_vdd_resume,
+};
+
+static struct ipc_driver mid_volt_drop_detect_driver = {
+       .driver = {
+               .name = DRIVER_NAME,
+               .owner = THIS_MODULE,
+               .pm = &msic_vdd_pm_ops,
+               },
+       .probe = mid_vdd_probe,
+       .remove = __devexit_p(mid_vdd_remove),
+       .id_table = vdd_id_table,
+};
+
+static int __init mid_vdd_module_init(void)
+{
+       return ipc_driver_register(&mid_volt_drop_detect_driver);
+}
+
+static void __exit mid_vdd_module_exit(void)
+{
+       ipc_driver_unregister(&mid_volt_drop_detect_driver);
+}
+
+module_init(mid_vdd_module_init);
+module_exit(mid_vdd_module_exit);
+
+MODULE_AUTHOR("Durgadoss R <durgadoss.r@intel.com>");
+MODULE_AUTHOR("Chaurasia, Avinash K <avinash.k.chaurasia@intel.com>");
+MODULE_DESCRIPTION("Intel CloverView Voltage Drop Detection Driver");
+MODULE_LICENSE("GPL");