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
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
