aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/s3c24xx/patches-2.6.30/056-pcf50633.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/s3c24xx/patches-2.6.30/056-pcf50633.patch')
-rw-r--r--target/linux/s3c24xx/patches-2.6.30/056-pcf50633.patch375
1 files changed, 375 insertions, 0 deletions
diff --git a/target/linux/s3c24xx/patches-2.6.30/056-pcf50633.patch b/target/linux/s3c24xx/patches-2.6.30/056-pcf50633.patch
new file mode 100644
index 0000000000..e05e051830
--- /dev/null
+++ b/target/linux/s3c24xx/patches-2.6.30/056-pcf50633.patch
@@ -0,0 +1,375 @@
+Index: linux-2.6.30-rc6/drivers/mfd/pcf50633-core.c
+===================================================================
+--- linux-2.6.30-rc6.orig/drivers/mfd/pcf50633-core.c 2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/drivers/mfd/pcf50633-core.c 2009-05-18 19:08:33.000000000 +0200
+@@ -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 @@
+ 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)) {
+@@ -443,7 +446,8 @@
+ dev_dbg(pcf->dev, "pcf50633_irq\n");
+
+ get_device(pcf->dev);
+- disable_irq(pcf->irq);
++ disable_irq_nosync(pcf->irq);
++
+ schedule_work(&pcf->irq_work);
+
+ return IRQ_HANDLED;
+@@ -482,13 +486,13 @@
+ }
+
+ #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 +527,12 @@
+ 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 +629,7 @@
+ }
+
+ 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 +688,12 @@
+ 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)
+Index: linux-2.6.30-rc6/drivers/power/pcf50633-charger.c
+===================================================================
+--- linux-2.6.30-rc6.orig/drivers/power/pcf50633-charger.c 2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/drivers/power/pcf50633-charger.c 2009-05-18 19:08:33.000000000 +0200
+@@ -36,6 +36,7 @@
+
+ struct power_supply usb;
+ struct power_supply adapter;
++ struct power_supply ac;
+
+ struct delayed_work charging_restart_work;
+ };
+@@ -47,16 +48,21 @@
+ 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 @@
+ 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 @@
+ PCF50633_MBCC1_RESUME, PCF50633_MBCC1_RESUME);
+
+ mbc->usb_active = charging_start;
+-
++
+ power_supply_changed(&mbc->usb);
+
+ return ret;
+@@ -156,9 +177,44 @@
+
+ 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 @@
+
+ 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 @@
+ 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 @@
+ {
+ 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 @@
+ 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 @@
+ 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");
+Index: linux-2.6.30-rc6/drivers/rtc/rtc-pcf50633.c
+===================================================================
+--- linux-2.6.30-rc6.orig/drivers/rtc/rtc-pcf50633.c 2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/drivers/rtc/rtc-pcf50633.c 2009-05-18 19:08:33.000000000 +0200
+@@ -58,6 +58,7 @@
+ struct pcf50633_rtc {
+ int alarm_enabled;
+ int second_enabled;
++ int alarm_pending;
+
+ struct pcf50633 *pcf;
+ struct rtc_device *rtc_dev;
+@@ -70,7 +71,7 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ /* 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 @@
+ 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);
+Index: linux-2.6.30-rc6/include/linux/mfd/pcf50633/core.h
+===================================================================
+--- linux-2.6.30-rc6.orig/include/linux/mfd/pcf50633/core.h 2009-05-16 06:12:57.000000000 +0200
++++ linux-2.6.30-rc6/include/linux/mfd/pcf50633/core.h 2009-05-18 19:08:33.000000000 +0200
+@@ -31,6 +31,8 @@
+
+ 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 @@
+ };
+
+ /* misc. registers */
+-#define PCF50633_REG_OOCSHDWN 0x0c
++#define PCF50633_REG_OOCSHDWN 0x0c
++#define PCF50633_OOCSHDWN_GOSTDBY 0x01
+
+ /* LED registers */
+ #define PCF50633_REG_LEDOUT 0x28