Hi Arjan,

I am submitting the initial code for the Medfield Current Monitoring
Driver. This is a platform driver. It monitors the platform
current usage and handles interrupts when the configured current
thresholds are crossed.

I have also added detailed documentation for this in
Documentation/hwmon/current_monitor.

Kindly review and merge.
----------------------------------------------------------------
From: Durgadoss R <[email protected]>

Date: Thu, 18 Nov 2010 23:29:12 +0530
Subject: [PATCH] Medfield_Current_Monitoring_Driver

Signed-off-by: Durgadoss R <[email protected]>
---
 Documentation/hwmon/current_monitor |   85 +++++
 drivers/hwmon/intel_mid_cmd.c       |  660 +++++++++++++++++++++++++++++++++++
 2 files changed, 745 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/hwmon/current_monitor
 create mode 100644 drivers/hwmon/intel_mid_cmd.c

diff --git a/Documentation/hwmon/current_monitor 
b/Documentation/hwmon/current_monitor
new file mode 100644
index 0000000..e5950d7
--- /dev/null
+++ b/Documentation/hwmon/current_monitor
@@ -0,0 +1,85 @@
+Kernel driver current monitoring driver
+=======================================
+
+Supported systems:
+  * Intel Medfield Platform
+
+Author: Durgadoss R
+
+Description
+-----------
+
+This driver monitors the current drawn by the platform. When the current
+drawn is more than the configured limit for a period of time (which is
+specified by a timer), an interrupt is generated. The current limit and the
+timer value can be configured at run time.
+
+The current thresholds aka BATTCURRENTLIMITS(BC) are of two types:
+       1.warning threshold(BC1)
+               at which the system should take actions to bring the
+               current consumption down.
+       2.shutdown threshold(BC2)
+               at which the hardware does a COLDOFF.
+
+The timer thresholds are of three types:
+       1.warning threshold(T1)
+               This corresponds to the first flag for time that the battery
+               current has been above BC1. An interrupt is generated to allow
+               software to correct the situation based on use-case.
+       2.hardware action threshold(T2)
+               This corresponds to the second flag for time that the battery
+               current has been above BC1. It signifies that the system
+               should control high current subsystems through hardware.
+               Besides a general interrupt, audio_volume_crush, vibra_disable
+               signals are sent.
+       3.shutdown threshold(T3)
+               This final flag signifies that when the system current exceeds
+               the threshold for more than T3, system should shutdown.
+
+Following table summarizes the exported sysfs files:
+
+bcu_status(RW)      -  To enable/disable burst control unit.
+                       0 - enables bcu, 1 - disables bcu.
+accumulation_time(RW)-  Time since last clearing/overflow of warning_count
+                       in milli seconds. Writing 0 resets the acc_time and
+                       also clears both the warning counters.
+warning_count(RO)    -  Two space seperated values that indicate the number
+                       of times the current thresholds have been crossed.
+action_mask(RW)      -  A hex value to enable/disable specific
+                       actions taken when current violation happens.
+                       bits [b4 b3 b2 b1 b0] control [a4 a3 a2 a1 a0] actions
+                       respectively. a4 - camera output, a3 - sys burst,
+                       a2 - SOC burst enable, a1 - vibra, a0 - audio output.
+                       bits [b7 b6 b5] - Reserved.
+                       Default value:08 (sys burst output is enabled).
+action_status(RO)    -  A hex value showing the status of actions taken
+                       since the __last__ interrupt.
+                       b7 - T3 violation
+                       b6 - T2 violation
+                       b5 - T1 violation
+                       b4 - camera action taken status
+                       b3 - sys burst disable action taken status
+                       b2 - SOC burst disable action taken status
+                       b1 - vibra disable
+                       b0 - audio volume crush
+                       A '1' in a bit position indicates that particular
+                       action has been taken.
+current_warning(RW)  -  This gives the current(in mA) at which a warning is
+                       generated. Range: 1400 to 4800. Default:3000
+current_shutdown(RW) -  This gives the current(in mA) at which system shutdown
+                       is initiated. Range:1800 to 5800. Default:3800
+timer_warning(RW)    -  Time above current_warning limit at which interrupts
+                       are trigerred(so that software can bring the current
+                       consumption down). Values are in micro seconds.
+                       Range:200 to 7700 in steps of 500
+timer_hw_action(RW)  -  Time above current_warning limit at which the system
+                       is trigerred into hardware control to bring down the
+                       current consumption. Values are in micro seconds.
+                       Range:200 to 7700 in steps of 500
+timer_shutdown(RW)   -  Time above current_warning limit at which a hardware
+                       shutdown event is trigerred. The timer values are in
+                       micro seconds.
+                       Range:1000 to 15000 in steps of 1000
+For all timer interfaces, tolerance shall be 5% maximum.
+All timers start running concurrently once current_warning/current_shutdown
+is crossed.
diff --git a/drivers/hwmon/intel_mid_cmd.c b/drivers/hwmon/intel_mid_cmd.c
new file mode 100644
index 0000000..47426fc
--- /dev/null
+++ b/drivers/hwmon/intel_mid_cmd.c
@@ -0,0 +1,660 @@
+/*
+ * intel_mid_cmd.c - Intel Medfield Platform Current Monitoring 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 <[email protected]>
+ */
+
+#define pr_fmt(fmt)  "intel_mid_cmd: " 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/hwmon.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+
+#include <asm/intel_scu_ipc.h>
+
+#define DRIVER_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(cmd_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 cmd_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(&cmd_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(&cmd_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;
+
+       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 cmd_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;
+
+       /* Find the index of 'value' in the thresholds array */
+       while (value > curr_thresholds[index][pos++])
+               ;
+
+       if (value != curr_thresholds[index][--pos])
+               --pos;
+
+       return pos;
+}
+
+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;
+
+       if (curnt < curr_thresholds[s_attr->nr][0] ||
+                       curnt > curr_thresholds[s_attr->nr][NUM_CURR_LIMITS-1])
+               return -EINVAL;
+
+       mutex_lock(&cmd_update_lock);
+
+       pos = get_current_value(s_attr->nr, (int)curnt);
+
+       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(&cmd_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)
+               ret = -EINVAL;
+
+       mutex_lock(&cmd_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(&cmd_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 cmd_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 cmd_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 cmd_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 cmd_handle_intrpt(int irq, void *dev_data)
+{
+       int ret;
+       uint8_t data;
+       struct cmd_info *cinfo = (struct cmd_info *)dev_data;
+
+       if (!cinfo)
+               return IRQ_HANDLED;
+
+       mutex_lock(&cmd_update_lock);
+
+       /* Interrupt came now */
+       cinfo->acc_time = jiffies;
+
+       /* Read the interrupt status register */
+       ret = intel_scu_ipc_ioread8(BRSTCONTROLSTATUS, &data);
+       if (ret)
+               dev_err(cinfo->dev, "can't read status register");
+
+       /* 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)
+               dev_err(cinfo->dev, "status register update failed");
+
+       mutex_unlock(&cmd_update_lock);
+
+       return IRQ_HANDLED;
+}
+
+static int enable_interrupt(struct cmd_info *cinfo)
+{
+       int ret;
+
+       /* Workaround till an sfi table entry comes */
+       cinfo->irq = gpio_to_irq(30);
+
+       ret = request_threaded_irq(cinfo->irq, NULL, cmd_handle_intrpt,
+                                               0, DRIVER_NAME, cinfo);
+       if (ret)
+               dev_err(cinfo->dev, "request irq failed:%d", ret);
+       return ret;
+}
+
+static int initialize_hw(struct cmd_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_cmd_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_cmd_gr = {
+       .name = "msic_current",
+       .attrs = mid_cmd_attrs
+};
+
+static int mid_cmd_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct cmd_info *cinfo = kzalloc(sizeof(struct cmd_info), GFP_KERNEL);
+
+       if (!cinfo) {
+               dev_err(&pdev->dev, "kzalloc failed\n");
+               return -ENOMEM;
+       }
+       cinfo->pdev = pdev;
+       platform_set_drvdata(pdev, cinfo);
+
+       /* Creating a sysfs group with mid_cmd_gr attributes */
+       ret = sysfs_create_group(&pdev->dev.kobj, &mid_cmd_gr);
+       if (ret) {
+               dev_err(&pdev->dev, "sysfs create group failed\n");
+               goto cmd_error1;
+       }
+
+       /* Registering with hwmon class */
+       cinfo->dev = hwmon_device_register(&pdev->dev);
+       if (IS_ERR(cinfo->dev)) {
+               ret = PTR_ERR(cinfo->dev);
+               cinfo->dev = NULL;
+               dev_err(&pdev->dev, "hwmon_dev_regs failed\n");
+               goto cmd_error2;
+       }
+
+       ret = initialize_hw(cinfo);
+       if (ret)
+               return ret;
+
+       ret = configure_bcu(1);
+       if (ret)
+               return ret;
+
+       ret = enable_interrupt(cinfo);
+
+       return ret;
+
+cmd_error2:
+       sysfs_remove_group(&pdev->dev.kobj, &mid_cmd_gr);
+cmd_error1:
+       kfree(cinfo);
+       return ret;
+}
+
+static int mid_cmd_resume(struct platform_device *pdev)
+{
+       int ret;
+       struct cmd_info *cinfo = platform_get_drvdata(pdev);
+
+       ret = initialize_hw(cinfo);
+       if (ret)
+               return ret;
+
+       return configure_bcu(0);
+}
+
+static int mid_cmd_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+       return configure_bcu(1);
+}
+
+static int mid_cmd_remove(struct platform_device *pdev)
+{
+       struct cmd_info *cinfo = platform_get_drvdata(pdev);
+
+       if (cinfo) {
+               hwmon_device_unregister(cinfo->dev);
+               sysfs_remove_group(&pdev->dev.kobj, &mid_cmd_gr);
+               kfree(cinfo);
+       }
+       return 0;
+}
+
+/*********************************************************************
+ *             Driver initialisation and finalization
+ *********************************************************************/
+static const struct platform_device_id cmd_id_table[] = {
+       { DRIVER_NAME, 1 },
+};
+
+static struct platform_driver mid_curr_monitor_driver = {
+       .driver = {
+               .name = DRIVER_NAME,
+               .owner = THIS_MODULE,
+       },
+       .probe = mid_cmd_probe,
+       .suspend = mid_cmd_suspend,
+       .resume = mid_cmd_resume,
+       .remove = __devexit_p(mid_cmd_remove),
+       .id_table = cmd_id_table,
+};
+
+static int __init mid_cmd_module_init(void)
+{
+       int ret;
+       struct platform_device *pdev;
+
+       ret = platform_driver_register(&mid_curr_monitor_driver);
+       if (ret) {
+               pr_info("driver_register failed");
+               return ret;
+       }
+
+       /* Workaround till an sfi table entry comes */
+       pdev = platform_device_register_simple(DRIVER_NAME, 0, NULL, 0);
+       if (IS_ERR(pdev)) {
+               dev_err(&pdev->dev, "device_register failed");
+               return PTR_ERR(pdev);
+       }
+       return ret;
+}
+
+static void __exit mid_cmd_module_exit(void)
+{
+       platform_driver_unregister(&mid_curr_monitor_driver);
+}
+
+module_init(mid_cmd_module_init);
+module_exit(mid_cmd_module_exit);
+
+MODULE_AUTHOR("Durgadoss R <[email protected]>");
+MODULE_DESCRIPTION("Intel Medfield Current Monitoring Driver");
+MODULE_LICENSE("GPL");
--
1.6.5.2

Attachment: 0001-Medfield_Current_Monitoring_Driver.patch
Description: 0001-Medfield_Current_Monitoring_Driver.patch

_______________________________________________
MeeGo-kernel mailing list
[email protected]
http://lists.meego.com/listinfo/meego-kernel

Reply via email to