summaryrefslogtreecommitdiff
path: root/target/linux/s3c24xx/patches-2.6.31/057-pcf50633.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/s3c24xx/patches-2.6.31/057-pcf50633.patch')
-rw-r--r--target/linux/s3c24xx/patches-2.6.31/057-pcf50633.patch487
1 files changed, 487 insertions, 0 deletions
diff --git a/target/linux/s3c24xx/patches-2.6.31/057-pcf50633.patch b/target/linux/s3c24xx/patches-2.6.31/057-pcf50633.patch
new file mode 100644
index 0000000000..31432151ce
--- /dev/null
+++ b/target/linux/s3c24xx/patches-2.6.31/057-pcf50633.patch
@@ -0,0 +1,487 @@
+From 20fb4fd1e317dadaaaaeb9c153098b57d0fc86fe Mon Sep 17 00:00:00 2001
+From: Lars-Peter Clausen <lars@metafoo.de>
+Date: Tue, 21 Jul 2009 12:47:03 +0200
+Subject: [PATCH] 056-pcf50633.patch
+
+---
+ drivers/mfd/pcf50633-core.c | 16 +++--
+ drivers/power/pcf50633-charger.c | 121 ++++++++++++++++++++++++++++---
+ drivers/regulator/pcf50633-regulator.c | 60 +++++++++++++---
+ drivers/rtc/rtc-pcf50633.c | 12 +++-
+ include/linux/mfd/pcf50633/core.h | 5 +-
+ 5 files changed, 180 insertions(+), 34 deletions(-)
+
+diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c
+index 8d3c38b..e81c967 100644
+--- a/drivers/mfd/pcf50633-core.c
++++ b/drivers/mfd/pcf50633-core.c
+@@ -15,6 +15,7 @@
+ #include <linux/kernel.h>
+ #include <linux/device.h>
+ #include <linux/sysfs.h>
++#include <linux/device.h>
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/interrupt.h>
+@@ -345,6 +346,8 @@ static void pcf50633_irq_worker(struct work_struct *work)
+ goto out;
+ }
+
++ pcf50633_reg_write(pcf, PCF50633_REG_OOCSHDWN, 0x04 ); /* defeat 8s death from lowsys on A5 */
++
+ /* We immediately read the usb and adapter status. We thus make sure
+ * only of USBINS/USBREM IRQ handlers are called */
+ if (pcf_int[0] & (PCF50633_INT1_USBINS | PCF50633_INT1_USBREM)) {
+@@ -482,13 +485,13 @@ pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name,
+ }
+
+ #ifdef CONFIG_PM
+-static int pcf50633_suspend(struct device *dev, pm_message_t state)
++static int pcf50633_suspend(struct i2c_client *client, pm_message_t state)
+ {
+ struct pcf50633 *pcf;
+ int ret = 0, i;
+ u8 res[5];
+
+- pcf = dev_get_drvdata(dev);
++ pcf = i2c_get_clientdata(client);
+
+ /* Make sure our interrupt handlers are not called
+ * henceforth */
+@@ -523,12 +526,12 @@ out:
+ return ret;
+ }
+
+-static int pcf50633_resume(struct device *dev)
++static int pcf50633_resume(struct i2c_client *client)
+ {
+ struct pcf50633 *pcf;
+ int ret;
+
+- pcf = dev_get_drvdata(dev);
++ pcf = i2c_get_clientdata(client);
+
+ /* Write the saved mask registers */
+ ret = pcf50633_write_block(pcf, PCF50633_REG_INT1M,
+@@ -625,6 +628,7 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
+ }
+
+ if (client->irq) {
++ set_irq_handler(client->irq, handle_level_irq);
+ ret = request_irq(client->irq, pcf50633_irq,
+ IRQF_TRIGGER_LOW, "pcf50633", pcf);
+
+@@ -683,12 +687,12 @@ static struct i2c_device_id pcf50633_id_table[] = {
+ static struct i2c_driver pcf50633_driver = {
+ .driver = {
+ .name = "pcf50633",
+- .suspend = pcf50633_suspend,
+- .resume = pcf50633_resume,
+ },
+ .id_table = pcf50633_id_table,
+ .probe = pcf50633_probe,
+ .remove = __devexit_p(pcf50633_remove),
++ .suspend = pcf50633_suspend,
++ .resume = pcf50633_resume,
+ };
+
+ static int __init pcf50633_init(void)
+diff --git a/drivers/power/pcf50633-charger.c b/drivers/power/pcf50633-charger.c
+index e8b278f..41fa421 100644
+--- a/drivers/power/pcf50633-charger.c
++++ b/drivers/power/pcf50633-charger.c
+@@ -36,6 +36,7 @@ struct pcf50633_mbc {
+
+ struct power_supply usb;
+ struct power_supply adapter;
++ struct power_supply ac;
+
+ struct delayed_work charging_restart_work;
+ };
+@@ -47,16 +48,21 @@ int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma)
+ u8 bits;
+ int charging_start = 1;
+ u8 mbcs2, chgmod;
++ unsigned int mbcc5;
+
+- if (ma >= 1000)
++ if (ma >= 1000) {
+ bits = PCF50633_MBCC7_USB_1000mA;
+- else if (ma >= 500)
++ ma = 1000;
++ } else if (ma >= 500) {
+ bits = PCF50633_MBCC7_USB_500mA;
+- else if (ma >= 100)
++ ma = 500;
++ } else if (ma >= 100) {
+ bits = PCF50633_MBCC7_USB_100mA;
+- else {
++ ma = 100;
++ } else {
+ bits = PCF50633_MBCC7_USB_SUSPEND;
+ charging_start = 0;
++ ma = 0;
+ }
+
+ ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC7,
+@@ -66,7 +72,22 @@ int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma)
+ else
+ dev_info(pcf->dev, "usb curlim to %d mA\n", ma);
+
+- /* Manual charging start */
++ /*
++ * We limit the charging current to be the USB current limit.
++ * The reason is that on pcf50633, when it enters PMU Standby mode,
++ * which it does when the device goes "off", the USB current limit
++ * reverts to the variant default. In at least one common case, that
++ * default is 500mA. By setting the charging current to be the same
++ * as the USB limit we set here before PMU standby, we enforce it only
++ * using the correct amount of current even when the USB current limit
++ * gets reset to the wrong thing
++ */
++
++ mbcc5 = (ma << 8) / mbc->pcf->pdata->chg_ref_current_ma;
++ if (mbcc5 > 255)
++ mbcc5 = 255;
++ pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5);
++
+ mbcs2 = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
+ chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
+
+@@ -81,7 +102,7 @@ int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma)
+ PCF50633_MBCC1_RESUME, PCF50633_MBCC1_RESUME);
+
+ mbc->usb_active = charging_start;
+-
++
+ power_supply_changed(&mbc->usb);
+
+ return ret;
+@@ -156,9 +177,44 @@ static ssize_t set_usblim(struct device *dev,
+
+ static DEVICE_ATTR(usb_curlim, S_IRUGO | S_IWUSR, show_usblim, set_usblim);
+
++static ssize_t
++show_chglim(struct device *dev, struct device_attribute *attr, char *buf)
++{
++ struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
++ u8 mbcc5 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC5);
++ unsigned int ma;
++
++ ma = (mbc->pcf->pdata->chg_ref_current_ma * mbcc5) >> 8;
++
++ return sprintf(buf, "%u\n", ma);
++}
++
++static ssize_t set_chglim(struct device *dev,
++ struct device_attribute *attr, const char *buf, size_t count)
++{
++ struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
++ unsigned long ma;
++ unsigned int mbcc5;
++ int ret;
++
++ ret = strict_strtoul(buf, 10, &ma);
++ if (ret)
++ return -EINVAL;
++
++ mbcc5 = (ma << 8) / mbc->pcf->pdata->chg_ref_current_ma;
++ if (mbcc5 > 255)
++ mbcc5 = 255;
++ pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5);
++
++ return count;
++}
++
++static DEVICE_ATTR(chg_curlim, S_IRUGO | S_IWUSR, show_chglim, set_chglim);
++
+ static struct attribute *pcf50633_mbc_sysfs_entries[] = {
+ &dev_attr_chgmode.attr,
+ &dev_attr_usb_curlim.attr,
++ &dev_attr_chg_curlim.attr,
+ NULL,
+ };
+
+@@ -239,6 +295,7 @@ pcf50633_mbc_irq_handler(int irq, void *data)
+
+ power_supply_changed(&mbc->usb);
+ power_supply_changed(&mbc->adapter);
++ power_supply_changed(&mbc->ac);
+
+ if (mbc->pcf->pdata->mbc_event_callback)
+ mbc->pcf->pdata->mbc_event_callback(mbc->pcf, irq);
+@@ -248,8 +305,7 @@ static int adapter_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+ {
+- struct pcf50633_mbc *mbc = container_of(psy,
+- struct pcf50633_mbc, adapter);
++ struct pcf50633_mbc *mbc = container_of(psy, struct pcf50633_mbc, adapter);
+ int ret = 0;
+
+ switch (psp) {
+@@ -269,10 +325,34 @@ static int usb_get_property(struct power_supply *psy,
+ {
+ struct pcf50633_mbc *mbc = container_of(psy, struct pcf50633_mbc, usb);
+ int ret = 0;
++ u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
++ PCF50633_MBCC7_USB_MASK;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+- val->intval = mbc->usb_online;
++ val->intval = mbc->usb_online &&
++ (usblim <= PCF50633_MBCC7_USB_500mA);
++ break;
++ default:
++ ret = -EINVAL;
++ break;
++ }
++ return ret;
++}
++
++static int ac_get_property(struct power_supply *psy,
++ enum power_supply_property psp,
++ union power_supply_propval *val)
++{
++ struct pcf50633_mbc *mbc = container_of(psy, struct pcf50633_mbc, ac);
++ int ret = 0;
++ u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
++ PCF50633_MBCC7_USB_MASK;
++
++ switch (psp) {
++ case POWER_SUPPLY_PROP_ONLINE:
++ val->intval = mbc->usb_online &&
++ (usblim == PCF50633_MBCC7_USB_1000mA);
+ break;
+ default:
+ ret = -EINVAL;
+@@ -337,6 +417,17 @@ static int __devinit pcf50633_mbc_probe(struct platform_device *pdev)
+ mbc->usb.supplied_to = mbc->pcf->pdata->batteries;
+ mbc->usb.num_supplicants = mbc->pcf->pdata->num_batteries;
+
++ mbc->ac.name = "ac";
++ mbc->ac.type = POWER_SUPPLY_TYPE_MAINS;
++ mbc->ac.properties = power_props;
++ mbc->ac.num_properties = ARRAY_SIZE(power_props);
++ mbc->ac.get_property = ac_get_property;
++ mbc->ac.supplied_to = mbc->pcf->pdata->batteries;
++ mbc->ac.num_supplicants = mbc->pcf->pdata->num_batteries;
++
++ INIT_DELAYED_WORK(&mbc->charging_restart_work,
++ pcf50633_mbc_charging_restart);
++
+ ret = power_supply_register(&pdev->dev, &mbc->adapter);
+ if (ret) {
+ dev_err(mbc->pcf->dev, "failed to register adapter\n");
+@@ -352,9 +443,15 @@ static int __devinit pcf50633_mbc_probe(struct platform_device *pdev)
+ return ret;
+ }
+
+- INIT_DELAYED_WORK(&mbc->charging_restart_work,
+- pcf50633_mbc_charging_restart);
+-
++ ret = power_supply_register(&pdev->dev, &mbc->ac);
++ if (ret) {
++ dev_err(mbc->pcf->dev, "failed to register ac\n");
++ power_supply_unregister(&mbc->adapter);
++ power_supply_unregister(&mbc->usb);
++ kfree(mbc);
++ return ret;
++ }
++
+ ret = sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group);
+ if (ret)
+ dev_err(mbc->pcf->dev, "failed to create sysfs entries\n");
+diff --git a/drivers/regulator/pcf50633-regulator.c b/drivers/regulator/pcf50633-regulator.c
+index 8e14900..4809789 100644
+--- a/drivers/regulator/pcf50633-regulator.c
++++ b/drivers/regulator/pcf50633-regulator.c
+@@ -24,11 +24,12 @@
+ #include <linux/mfd/pcf50633/core.h>
+ #include <linux/mfd/pcf50633/pmic.h>
+
+-#define PCF50633_REGULATOR(_name, _id) \
++#define PCF50633_REGULATOR(_name, _id, _n) \
+ { \
+ .name = _name, \
+ .id = _id, \
+ .ops = &pcf50633_regulator_ops, \
++ .n_voltages = _n, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }
+@@ -193,6 +194,40 @@ static int pcf50633_regulator_get_voltage(struct regulator_dev *rdev)
+ return millivolts * 1000;
+ }
+
++static int pcf50633_regulator_list_voltage(struct regulator_dev *rdev,
++ unsigned int index)
++{
++ struct pcf50633 *pcf;
++ int regulator_id, millivolts;
++
++ pcf = rdev_get_drvdata(rdev);;
++
++ regulator_id = rdev_get_id(rdev);
++
++ switch (regulator_id) {
++ case PCF50633_REGULATOR_AUTO:
++ millivolts = auto_voltage_value(index + 0x2f);
++ break;
++ case PCF50633_REGULATOR_DOWN1:
++ case PCF50633_REGULATOR_DOWN2:
++ millivolts = down_voltage_value(index);
++ break;
++ case PCF50633_REGULATOR_LDO1:
++ case PCF50633_REGULATOR_LDO2:
++ case PCF50633_REGULATOR_LDO3:
++ case PCF50633_REGULATOR_LDO4:
++ case PCF50633_REGULATOR_LDO5:
++ case PCF50633_REGULATOR_LDO6:
++ case PCF50633_REGULATOR_HCLDO:
++ millivolts = ldo_voltage_value(index);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return millivolts * 1000;
++}
++
+ static int pcf50633_regulator_enable(struct regulator_dev *rdev)
+ {
+ struct pcf50633 *pcf = rdev_get_drvdata(rdev);
+@@ -246,6 +281,7 @@ static int pcf50633_regulator_is_enabled(struct regulator_dev *rdev)
+ static struct regulator_ops pcf50633_regulator_ops = {
+ .set_voltage = pcf50633_regulator_set_voltage,
+ .get_voltage = pcf50633_regulator_get_voltage,
++ .list_voltage = pcf50633_regulator_list_voltage,
+ .enable = pcf50633_regulator_enable,
+ .disable = pcf50633_regulator_disable,
+ .is_enabled = pcf50633_regulator_is_enabled,
+@@ -253,27 +289,27 @@ static struct regulator_ops pcf50633_regulator_ops = {
+
+ static struct regulator_desc regulators[] = {
+ [PCF50633_REGULATOR_AUTO] =
+- PCF50633_REGULATOR("auto", PCF50633_REGULATOR_AUTO),
++ PCF50633_REGULATOR("auto", PCF50633_REGULATOR_AUTO, 80),
+ [PCF50633_REGULATOR_DOWN1] =
+- PCF50633_REGULATOR("down1", PCF50633_REGULATOR_DOWN1),
++ PCF50633_REGULATOR("down1", PCF50633_REGULATOR_DOWN1, 95),
+ [PCF50633_REGULATOR_DOWN2] =
+- PCF50633_REGULATOR("down2", PCF50633_REGULATOR_DOWN2),
++ PCF50633_REGULATOR("down2", PCF50633_REGULATOR_DOWN2, 95),
+ [PCF50633_REGULATOR_LDO1] =
+- PCF50633_REGULATOR("ldo1", PCF50633_REGULATOR_LDO1),
++ PCF50633_REGULATOR("ldo1", PCF50633_REGULATOR_LDO1, 27),
+ [PCF50633_REGULATOR_LDO2] =
+- PCF50633_REGULATOR("ldo2", PCF50633_REGULATOR_LDO2),
++ PCF50633_REGULATOR("ldo2", PCF50633_REGULATOR_LDO2, 27),
+ [PCF50633_REGULATOR_LDO3] =
+- PCF50633_REGULATOR("ldo3", PCF50633_REGULATOR_LDO3),
++ PCF50633_REGULATOR("ldo3", PCF50633_REGULATOR_LDO3, 27),
+ [PCF50633_REGULATOR_LDO4] =
+- PCF50633_REGULATOR("ldo4", PCF50633_REGULATOR_LDO4),
++ PCF50633_REGULATOR("ldo4", PCF50633_REGULATOR_LDO4, 27),
+ [PCF50633_REGULATOR_LDO5] =
+- PCF50633_REGULATOR("ldo5", PCF50633_REGULATOR_LDO5),
++ PCF50633_REGULATOR("ldo5", PCF50633_REGULATOR_LDO5, 27),
+ [PCF50633_REGULATOR_LDO6] =
+- PCF50633_REGULATOR("ldo6", PCF50633_REGULATOR_LDO6),
++ PCF50633_REGULATOR("ldo6", PCF50633_REGULATOR_LDO6, 27),
+ [PCF50633_REGULATOR_HCLDO] =
+- PCF50633_REGULATOR("hcldo", PCF50633_REGULATOR_HCLDO),
++ PCF50633_REGULATOR("hcldo", PCF50633_REGULATOR_HCLDO, 26),
+ [PCF50633_REGULATOR_MEMLDO] =
+- PCF50633_REGULATOR("memldo", PCF50633_REGULATOR_MEMLDO),
++ PCF50633_REGULATOR("memldo", PCF50633_REGULATOR_MEMLDO, 27),
+ };
+
+ static int __devinit pcf50633_regulator_probe(struct platform_device *pdev)
+diff --git a/drivers/rtc/rtc-pcf50633.c b/drivers/rtc/rtc-pcf50633.c
+index f4dd87e..8669815 100644
+--- a/drivers/rtc/rtc-pcf50633.c
++++ b/drivers/rtc/rtc-pcf50633.c
+@@ -58,6 +58,7 @@ struct pcf50633_time {
+ struct pcf50633_rtc {
+ int alarm_enabled;
+ int second_enabled;
++ int alarm_pending;
+
+ struct pcf50633 *pcf;
+ struct rtc_device *rtc_dev;
+@@ -70,7 +71,7 @@ static void pcf2rtc_time(struct rtc_time *rtc, struct pcf50633_time *pcf)
+ rtc->tm_hour = bcd2bin(pcf->time[PCF50633_TI_HOUR]);
+ rtc->tm_wday = bcd2bin(pcf->time[PCF50633_TI_WKDAY]);
+ rtc->tm_mday = bcd2bin(pcf->time[PCF50633_TI_DAY]);
+- rtc->tm_mon = bcd2bin(pcf->time[PCF50633_TI_MONTH]);
++ rtc->tm_mon = bcd2bin(pcf->time[PCF50633_TI_MONTH]) - 1;
+ rtc->tm_year = bcd2bin(pcf->time[PCF50633_TI_YEAR]) + 100;
+ }
+
+@@ -81,7 +82,7 @@ static void rtc2pcf_time(struct pcf50633_time *pcf, struct rtc_time *rtc)
+ pcf->time[PCF50633_TI_HOUR] = bin2bcd(rtc->tm_hour);
+ pcf->time[PCF50633_TI_WKDAY] = bin2bcd(rtc->tm_wday);
+ pcf->time[PCF50633_TI_DAY] = bin2bcd(rtc->tm_mday);
+- pcf->time[PCF50633_TI_MONTH] = bin2bcd(rtc->tm_mon);
++ pcf->time[PCF50633_TI_MONTH] = bin2bcd(rtc->tm_mon + 1);
+ pcf->time[PCF50633_TI_YEAR] = bin2bcd(rtc->tm_year % 100);
+ }
+
+@@ -209,6 +210,7 @@ static int pcf50633_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+ rtc = dev_get_drvdata(dev);
+
+ alrm->enabled = rtc->alarm_enabled;
++ alrm->pending = rtc->alarm_pending;
+
+ ret = pcf50633_read_block(rtc->pcf, PCF50633_REG_RTCSCA,
+ PCF50633_TI_EXTENT, &pcf_tm.time[0]);
+@@ -244,9 +246,12 @@ static int pcf50633_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+ /* Returns 0 on success */
+ ret = pcf50633_write_block(rtc->pcf, PCF50633_REG_RTCSCA,
+ PCF50633_TI_EXTENT, &pcf_tm.time[0]);
++ if (!alrm->enabled)
++ rtc->alarm_pending = 0;
+
+- if (!alarm_masked)
++ if (!alarm_masked || alrm->enabled)
+ pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM);
++ rtc->alarm_enabled = alrm->enabled;
+
+ return ret;
+ }
+@@ -267,6 +272,7 @@ static void pcf50633_rtc_irq(int irq, void *data)
+ switch (irq) {
+ case PCF50633_IRQ_ALARM:
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
++ rtc->alarm_pending = 1;
+ break;
+ case PCF50633_IRQ_SECOND:
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF);
+diff --git a/include/linux/mfd/pcf50633/core.h b/include/linux/mfd/pcf50633/core.h
+index c8f51c3..af67b4e 100644
+--- a/include/linux/mfd/pcf50633/core.h
++++ b/include/linux/mfd/pcf50633/core.h
+@@ -31,6 +31,8 @@ struct pcf50633_platform_data {
+
+ int charging_restart_interval;
+
++ int chg_ref_current_ma;
++
+ /* Callbacks */
+ void (*probe_done)(struct pcf50633 *);
+ void (*mbc_event_callback)(struct pcf50633 *, int);
+@@ -208,7 +210,8 @@ enum pcf50633_reg_int5 {
+ };
+
+ /* misc. registers */
+-#define PCF50633_REG_OOCSHDWN 0x0c
++#define PCF50633_REG_OOCSHDWN 0x0c
++#define PCF50633_OOCSHDWN_GOSTDBY 0x01
+
+ /* LED registers */
+ #define PCF50633_REG_LEDOUT 0x28
+--
+1.5.6.5
+