aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDurgadoss R <durgadoss.r@intel.com>2010-12-13 09:44:36 +0000
committerAlan Cox <alan@linux.intel.com>2010-12-13 09:44:36 +0000
commitd051ad45ef13793159797873161ea07e4a10efbc (patch)
tree951cf01f50618acc2ac621938ab1e17de47e4033 /drivers
parent2cf5b7ad760b1699d9060ea0ab5601375a5ad43f (diff)
downloadmrst-s0i3-test-d051ad45ef13793159797873161ea07e4a10efbc.tar.gz
mrst-s0i3-test-d051ad45ef13793159797873161ea07e4a10efbc.tar.xz
mrst-s0i3-test-d051ad45ef13793159797873161ea07e4a10efbc.zip
x86,mrst: Intel Medfield over-current detection patch
This patch enables over current detection on the Intel Medfield Platform. This driver provides sysfs interfaces to configure current thresholds. When the current being drawn on the platform exceeds the configured threshold, an interrupt is generated. This interrupt can be used to bring the current consumption down by taking necessary hardware or software actions. Signed-off-by: Durgadoss R <durgadoss.r@intel.com> Signed-off-by: Alan Cox <alan@linux.intel.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/platform/x86/Kconfig7
-rw-r--r--drivers/platform/x86/Makefile1
-rw-r--r--drivers/platform/x86/intel_mid_ocd.c640
3 files changed, 648 insertions, 0 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 65ba62086ca..2b4038ab600 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -592,6 +592,13 @@ config GPIO_INTEL_PMIC
Say Y here to support GPIO via the SCU IPC interface
on Intel MID platforms.
+config INTEL_MID_OCD
+ tristate "Intel MID platform over-current detector"
+ depends on INTEL_SCU_IPC
+ ---help---
+ Say Y here to support over-current detection and handling on
+ Intel Medfield MID platforms.
+
config RAR_REGISTER
bool "Restricted Access Region Register Driver"
depends on PCI && X86_MRST
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 4ec4ff8f918..7ff60e603ec 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
obj-$(CONFIG_INTEL_SCU_IPC_UTIL)+= intel_scu_ipcutil.o
+obj-$(CONFIG_INTEL_MID_OCD) += intel_mid_ocd.o
obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o
obj-$(CONFIG_INTEL_IPS) += intel_ips.o
obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o
diff --git a/drivers/platform/x86/intel_mid_ocd.c b/drivers/platform/x86/intel_mid_ocd.c
new file mode 100644
index 00000000000..a1284a06793
--- /dev/null
+++ b/drivers/platform/x86/intel_mid_ocd.c
@@ -0,0 +1,640 @@
+/*
+ * intel_mid_ocd.c - Intel Medfield Platform Over Current Detection Driver
+ *
+ *
+ * Copyright (C) 2010 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>
+ */
+
+#define pr_fmt(fmt) "intel_mid_ocd: " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+
+#include <asm/intel_scu_ipc.h>
+
+#define DRIVER_NAME "intel_mid_ocd"
+#define FIRMWARE_NAME "msic_cmd"
+
+/* Registers that govern current monitoring */
+#define BATTCURRENTLIMIT12 0x102
+#define BATTTIMELIMIT12 0x103
+#define BATTTIMELIMIT3 0x104
+#define BRSTCONFIGOUTPUTS 0x106
+#define BRSTCONFIGACTIONS 0x107
+#define BRSTCONTROLSTATUS 0x108
+
+#define BCUSTATUS (1 << 7)
+
+/* Bits that enable sys burst input & output */
+#define SYSACTEN (1 << 3)
+#define SYSOUTEN (1 << 3)
+
+/* Status register */
+#define CAMSTAT (1 << 4)
+#define SYSSTAT (0x0F << 0)
+#define OVER_TIMER1 (1 << 5)
+#define OVER_TIMER2 (1 << 6)
+
+#define NUM_CURR_LIMITS 8
+#define NUM_TIME_LIMITS 15
+
+/* Base and offset for every time limit */
+#define TIME_LIMIT12_BASE 200
+#define TIME_LIMIT12_OFFSET 500
+#define TIME_LIMIT12_MAX (TIME_LIMIT12_BASE + \
+ (TIME_LIMIT12_OFFSET * NUM_TIME_LIMITS))
+
+#define TIME_LIMIT3_BASE 200
+#define TIME_LIMIT3_OFFSET 1000
+#define TIME_LIMIT3_MAX (TIME_LIMIT3_OFFSET * NUM_TIME_LIMITS)
+
+#define MAX_COUNT 0xFFFFFFFF
+
+static DEFINE_MUTEX(ocd_update_lock);
+
+/* stores the current thresholds(in mA) at which
+ * row 0: warning is generated
+ * row 1: system shut down is initiated
+ */
+static const int curr_thresholds[][NUM_CURR_LIMITS] = {
+ {1400, 1800, 2200, 2800, 3000, 3400, 3800, 4800},
+ {1800, 2200, 2800, 3000, 3800, 4800, 5800, 5800} };
+
+struct ocd_info {
+ unsigned long timer1_count;
+ unsigned long timer2_count;
+ unsigned long acc_time;
+ unsigned int irq;
+ uint8_t intrpt_status;
+ struct device *dev;
+ struct platform_device *pdev;
+};
+
+static int configure_bcu(int flag)
+{
+ int ret;
+ uint8_t data;
+
+ mutex_lock(&ocd_update_lock);
+
+ ret = intel_scu_ipc_ioread8(BRSTCONFIGACTIONS, &data);
+ if (ret)
+ goto ipc_fail;
+
+ /* Zero enables BCU and non-zero disables BCU */
+ if (!flag)
+ data &= (~BCUSTATUS); /* enable bcu */
+ else
+ data |= BCUSTATUS; /* disable bcu */
+
+ ret = intel_scu_ipc_iowrite8(BRSTCONFIGACTIONS, data);
+
+ipc_fail:
+ mutex_unlock(&ocd_update_lock);
+ return ret;
+}
+
+static ssize_t store_bcu_status(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned long val;
+
+ if (strict_strtoul(buf, 10, &val))
+ return -EINVAL;
+
+ if (val != 0 && val != 1)
+ return -EINVAL;
+
+ return configure_bcu(val) ? -EINVAL : count;
+}
+
+static ssize_t show_bcu_status(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ uint8_t data;
+
+ ret = intel_scu_ipc_ioread8(BRSTCONFIGACTIONS, &data);
+ if (ret)
+ return ret;
+
+ ret = (data & BCUSTATUS) ? 1 : 0;
+
+ return sprintf(buf, "%d\n", ret);
+}
+
+static ssize_t store_action_mask(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int ret;
+ unsigned long data;
+
+ if (strict_strtoul(buf, 16, &data))
+ return -EINVAL;
+
+ ret = intel_scu_ipc_iowrite8(BRSTCONFIGOUTPUTS, (uint8_t)data);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static ssize_t show_action_mask(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ uint8_t data;
+
+ ret = intel_scu_ipc_ioread8(BRSTCONFIGOUTPUTS, &data);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%.2x\n", data);
+}
+
+static ssize_t show_action_status(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ocd_info *cinfo = dev_get_drvdata(dev);
+
+ /* This shows the status of actions taken since the last interrupt */
+ return sprintf(buf, "%.2x\n", cinfo->intrpt_status);
+}
+
+static int get_current_value(int index, int value)
+{
+ int pos = 0;
+
+ if (index != 0 && index != 1)
+ return -EINVAL;
+
+ if (value < curr_thresholds[index][0] ||
+ value > curr_thresholds[index][NUM_CURR_LIMITS-1])
+ return -EINVAL;
+
+ /* Find the index of 'value' in the thresholds array */
+ while (pos < NUM_CURR_LIMITS && value >= curr_thresholds[index][pos])
+ ++pos;
+
+ return pos - 1;
+}
+
+static ssize_t store_curr_thres(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int ret;
+ uint8_t data;
+ long curnt;
+ int pos;
+ struct sensor_device_attribute_2 *s_attr =
+ to_sensor_dev_attr_2(attr);
+
+ if (strict_strtol(buf, 10, &curnt))
+ return -EINVAL;
+
+ mutex_lock(&ocd_update_lock);
+
+ pos = get_current_value(s_attr->nr, (int)curnt);
+ if (pos < 0) {
+ ret = pos;
+ goto ipc_fail;
+ }
+
+ ret = intel_scu_ipc_ioread8(BATTCURRENTLIMIT12, &data);
+ if (ret)
+ goto ipc_fail;
+
+ if (s_attr->nr == 0)
+ /* set bits [0-2] to value of pos */
+ data = (data & 0xF8) | pos;
+ else
+ /* set bits [3-5] to value of pos */
+ data = (data & 0xC7) | (pos << 3);
+
+ ret = intel_scu_ipc_iowrite8(BATTCURRENTLIMIT12, data);
+ if (ret)
+ goto ipc_fail;
+
+ ret = count;
+
+ipc_fail:
+ mutex_unlock(&ocd_update_lock);
+ return ret;
+}
+
+static ssize_t show_curr_thres(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret, indx;
+ uint8_t data;
+ struct sensor_device_attribute_2 *s_attr =
+ to_sensor_dev_attr_2(attr);
+
+ WARN_ON(s_attr->nr != 0 && s_attr->nr != 1);
+
+ ret = intel_scu_ipc_ioread8(BATTCURRENTLIMIT12, &data);
+ if (ret)
+ return ret;
+
+ /* read bits [0-2] or [3-5] of data */
+ indx = (data >> (3 * s_attr->nr)) & 0x07;
+
+ return sprintf(buf, "%d\n", curr_thresholds[s_attr->nr][indx]);
+}
+
+static int get_timer_threshold(unsigned long time, int index)
+{
+ if (index == 0 || index == 1) {
+ if (time < TIME_LIMIT12_BASE || time > TIME_LIMIT12_MAX)
+ return -EINVAL;
+ return (time - TIME_LIMIT12_BASE) / TIME_LIMIT12_OFFSET;
+ } else {
+ if (time < TIME_LIMIT3_BASE || time > TIME_LIMIT3_MAX)
+ return -EINVAL;
+ return time / TIME_LIMIT3_OFFSET;
+ }
+}
+
+static ssize_t store_timer_thres(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned long time;
+ uint8_t data;
+ int ret, val;
+ struct sensor_device_attribute_2 *s_attr =
+ to_sensor_dev_attr_2(attr);
+
+ if (strict_strtoul(buf, 10, &time))
+ return -EINVAL;
+
+ val = get_timer_threshold(time, s_attr->nr);
+ if (val < 0)
+ return -EINVAL;
+
+ mutex_lock(&ocd_update_lock);
+
+ if (s_attr->nr == 2) {
+ ret = intel_scu_ipc_ioread8(BATTTIMELIMIT3, &data);
+ if (ret)
+ goto ipc_fail;
+ /* set bits [0-3] to val */
+ data = (data & 0xF0) | val;
+
+ ret = intel_scu_ipc_iowrite8(BATTTIMELIMIT3, data);
+ if (ret)
+ goto ipc_fail;
+ } else {
+ ret = intel_scu_ipc_ioread8(BATTTIMELIMIT12, &data);
+ if (ret)
+ goto ipc_fail;
+
+ if (s_attr->nr == 0)
+ /* set bits [0-3] to val */
+ data = (data & 0xF0) | val;
+ else
+ /* set bits [4-7] to val */
+ data = (data & 0x0F) | (val << 4);
+
+ ret = intel_scu_ipc_iowrite8(BATTTIMELIMIT12, data);
+ if (ret)
+ goto ipc_fail;
+ }
+ ret = count;
+
+ipc_fail:
+ mutex_unlock(&ocd_update_lock);
+ return ret;
+}
+
+static ssize_t show_timer_thres(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret, val;
+ uint8_t data;
+ int time;
+ struct sensor_device_attribute_2 *s_attr =
+ to_sensor_dev_attr_2(attr);
+
+ if (s_attr->nr == 0 || s_attr->nr == 1) {
+ ret = intel_scu_ipc_ioread8(BATTTIMELIMIT12, &data);
+ if (ret)
+ return ret;
+
+ val = s_attr->nr ? ((data >> 4) & 0x0F) : (data & 0x0F);
+ time = TIME_LIMIT12_BASE + val * TIME_LIMIT12_OFFSET;
+
+ } else if (s_attr->nr == 2) {
+ ret = intel_scu_ipc_ioread8(BATTTIMELIMIT3, &data);
+ if (ret)
+ return ret;
+
+ val = data & 0x0F;
+ time = (val) ? (val * TIME_LIMIT3_OFFSET) : TIME_LIMIT3_BASE;
+ } else
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", time);
+}
+
+static ssize_t show_warn_count(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ocd_info *cinfo = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%ld %ld\n", cinfo->timer1_count,
+ cinfo->timer2_count);
+}
+
+static ssize_t store_acc_time(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct ocd_info *cinfo = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (strict_strtoul(buf, 10, &time))
+ return -EINVAL;
+
+ /* Only 0 can be written to clear, otherwise return*/
+ if (time)
+ return -EINVAL;
+
+ /* Set the acc_time to 'now' */
+ cinfo->acc_time = jiffies;
+
+ /* Clear warning counters */
+ cinfo->timer1_count = cinfo->timer2_count = 0;
+
+ return count;
+}
+static ssize_t show_acc_time(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ocd_info *cinfo = dev_get_drvdata(dev);
+ long time_gap;
+
+ if (cinfo->acc_time == 0)
+ return sprintf(buf, "0\n");
+
+ /* Calculate the time gap in jiffies */
+ time_gap = jiffies - cinfo->acc_time;
+
+ /* Convert to milli secs and print */
+ return sprintf(buf, "%u\n", jiffies_to_msecs(time_gap));
+}
+
+static irqreturn_t ocd_handle_intrpt(int irq, void *dev_data)
+{
+ int ret;
+ uint8_t data;
+ struct ocd_info *cinfo = (struct ocd_info *)dev_data;
+
+ if (!cinfo)
+ return IRQ_NONE;
+
+ mutex_lock(&ocd_update_lock);
+
+ /* Interrupt came now */
+ cinfo->acc_time = jiffies;
+
+ /* Read the interrupt status register */
+ ret = intel_scu_ipc_ioread8(BRSTCONTROLSTATUS, &data);
+ if (ret)
+ goto ipc_fail;
+
+ /* Cache the interrupt status register */
+ cinfo->intrpt_status = data;
+
+ /* It's a timer1 interrupt. Increment the counter.
+ * Reset timer1 and camera status bits */
+ if (data & OVER_TIMER1) {
+ cinfo->timer1_count++;
+ data &= (~(OVER_TIMER1 | CAMSTAT));
+ }
+
+ /* It's a timer2 interrupt. Increment the counter.
+ * Reset timer2 and sys burst status bits */
+ if (data & OVER_TIMER2) {
+ cinfo->timer2_count++;
+ data &= (~(OVER_TIMER2 | SYSSTAT));
+ }
+
+ if (cinfo->timer1_count == MAX_COUNT ||
+ cinfo->timer2_count == MAX_COUNT) {
+ cinfo->timer1_count = cinfo->timer2_count = 0;
+ cinfo->acc_time = jiffies;
+ }
+
+ /* Write the masked data */
+ ret = intel_scu_ipc_iowrite8(BRSTCONTROLSTATUS, data);
+ if (ret)
+ goto ipc_fail;
+
+ mutex_unlock(&ocd_update_lock);
+ return IRQ_HANDLED;
+
+ipc_fail:
+ mutex_unlock(&ocd_update_lock);
+ dev_err(cinfo->dev, "ipc read/write failed");
+ return ret;
+}
+
+static int initialize_hw(struct ocd_info *cinfo)
+{
+ int ret;
+ uint8_t data;
+
+ ret = intel_scu_ipc_ioread8(BRSTCONFIGOUTPUTS, &data);
+ if (ret)
+ return ret;
+
+ /* Enable sys burst output signal */
+ ret = intel_scu_ipc_iowrite8(BRSTCONFIGOUTPUTS, (data | SYSOUTEN));
+ if (ret)
+ return ret;
+
+ ret = intel_scu_ipc_ioread8(BRSTCONFIGACTIONS, &data);
+ if (ret)
+ return ret;
+
+ /* Enable sys burst action signal*/
+ return intel_scu_ipc_iowrite8(BRSTCONFIGACTIONS, (data | SYSACTEN));
+}
+
+static SENSOR_DEVICE_ATTR_2(bcu_status, S_IRUGO | S_IWUSR,
+ show_bcu_status, store_bcu_status, 0, 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(current_warning, S_IRUGO | S_IWUSR,
+ show_curr_thres, store_curr_thres, 0, 0);
+static SENSOR_DEVICE_ATTR_2(current_shutdown, S_IRUGO | S_IWUSR,
+ show_curr_thres, store_curr_thres, 1, 0);
+
+static SENSOR_DEVICE_ATTR_2(timer_warning, S_IRUGO | S_IWUSR,
+ show_timer_thres, store_timer_thres, 0, 0);
+static SENSOR_DEVICE_ATTR_2(timer_hw_action, S_IRUGO | S_IWUSR,
+ show_timer_thres, store_timer_thres, 1, 0);
+static SENSOR_DEVICE_ATTR_2(timer_shutdown, S_IRUGO | S_IWUSR,
+ show_timer_thres, store_timer_thres, 2, 0);
+
+static SENSOR_DEVICE_ATTR_2(accumulation_time, S_IRUGO | S_IWUSR,
+ show_acc_time, store_acc_time, 0, 0);
+
+static SENSOR_DEVICE_ATTR_2(warning_count, S_IRUGO, show_warn_count,
+ NULL, 0, 0);
+static SENSOR_DEVICE_ATTR_2(action_status, S_IRUGO, show_action_status,
+ NULL, 0, 0);
+
+static struct attribute *mid_ocd_attrs[] = {
+ &sensor_dev_attr_bcu_status.dev_attr.attr,
+ &sensor_dev_attr_action_mask.dev_attr.attr,
+ &sensor_dev_attr_current_warning.dev_attr.attr,
+ &sensor_dev_attr_current_shutdown.dev_attr.attr,
+ &sensor_dev_attr_timer_warning.dev_attr.attr,
+ &sensor_dev_attr_timer_hw_action.dev_attr.attr,
+ &sensor_dev_attr_timer_shutdown.dev_attr.attr,
+ &sensor_dev_attr_warning_count.dev_attr.attr,
+ &sensor_dev_attr_accumulation_time.dev_attr.attr,
+ &sensor_dev_attr_action_status.dev_attr.attr,
+ NULL
+};
+
+static struct attribute_group mid_ocd_gr = {
+ .name = "msic_current",
+ .attrs = mid_ocd_attrs
+};
+
+static int mid_ocd_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct ocd_info *cinfo = kzalloc(sizeof(struct ocd_info), GFP_KERNEL);
+
+ if (!cinfo) {
+ dev_err(&pdev->dev, "kzalloc failed\n");
+ return -ENOMEM;
+ }
+
+ cinfo->pdev = pdev;
+ cinfo->irq = platform_get_irq(pdev, 0);
+ platform_set_drvdata(pdev, cinfo);
+
+ /* Creating a sysfs group with mid_ocd_gr attributes */
+ ret = sysfs_create_group(&pdev->dev.kobj, &mid_ocd_gr);
+ if (ret) {
+ dev_err(&pdev->dev, "sysfs create group failed\n");
+ goto ocd_error1;
+ }
+
+ /* Enable interrupt */
+ ret = request_threaded_irq(cinfo->irq, NULL, ocd_handle_intrpt,
+ 0, DRIVER_NAME, cinfo);
+ if (ret) {
+ dev_err(cinfo->dev, "request_threaded_irq failed:%d\n", ret);
+ goto ocd_error2;
+ }
+
+ ret = initialize_hw(cinfo);
+ if (ret)
+ goto ocd_error2;
+
+ ret = configure_bcu(1);
+ if (ret)
+ goto ocd_error2;
+
+ return 0;
+
+ocd_error2:
+ sysfs_remove_group(&pdev->dev.kobj, &mid_ocd_gr);
+ocd_error1:
+ kfree(cinfo);
+ return ret;
+}
+
+static int mid_ocd_resume(struct platform_device *pdev)
+{
+ int ret;
+ struct ocd_info *cinfo = platform_get_drvdata(pdev);
+
+ ret = initialize_hw(cinfo);
+ if (ret)
+ return ret;
+
+ return configure_bcu(0);
+}
+
+static int mid_ocd_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ return configure_bcu(1);
+}
+
+static int mid_ocd_remove(struct platform_device *pdev)
+{
+ struct ocd_info *cinfo = platform_get_drvdata(pdev);
+
+ if (cinfo) {
+ sysfs_remove_group(&pdev->dev.kobj, &mid_ocd_gr);
+ kfree(cinfo);
+ }
+ return 0;
+}
+
+/*********************************************************************
+ * Driver initialisation and finalization
+ *********************************************************************/
+static const struct platform_device_id ocd_id_table[] = {
+ { FIRMWARE_NAME, 1 },
+};
+
+static struct platform_driver mid_over_curr_detect_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = mid_ocd_probe,
+ .suspend = mid_ocd_suspend,
+ .resume = mid_ocd_resume,
+ .remove = __devexit_p(mid_ocd_remove),
+ .id_table = ocd_id_table,
+};
+
+static int __init mid_ocd_module_init(void)
+{
+ return platform_driver_register(&mid_over_curr_detect_driver);
+}
+
+static void __exit mid_ocd_module_exit(void)
+{
+ platform_driver_unregister(&mid_over_curr_detect_driver);
+}
+
+module_init(mid_ocd_module_init);
+module_exit(mid_ocd_module_exit);
+
+MODULE_AUTHOR("Durgadoss R <durgadoss.r@intel.com>");
+MODULE_DESCRIPTION("Intel Medfield Over Current Detection Driver");
+MODULE_LICENSE("GPL");