diff options
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.patch | 487 |
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 + |