47b943978c
SVN-Revision: 17182
462 lines
14 KiB
Diff
462 lines
14 KiB
Diff
--- 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 w
|
|
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 pcf5
|
|
}
|
|
|
|
#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(stru
|
|
}
|
|
|
|
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_
|
|
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)
|
|
--- 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 p
|
|
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 p
|
|
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 p
|
|
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
|
|
|
|
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 *
|
|
|
|
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 p
|
|
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
|
|
{
|
|
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(
|
|
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(
|
|
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");
|
|
--- 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->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
|
|
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(struc
|
|
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
|
|
/* 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, vo
|
|
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);
|
|
--- 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
|
|
--- 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_voltag
|
|
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
|
|
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_reg
|
|
|
|
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)
|