r2178 - trunk/src/target/kernel/patches
laforge at sita.openmoko.org
laforge at sita.openmoko.org
Wed Jun 6 20:11:09 CEST 2007
Author: laforge
Date: 2007-06-06 20:08:19 +0200 (Wed, 06 Jun 2007)
New Revision: 2178
Added:
trunk/src/target/kernel/patches/pcf50633.patch
Modified:
trunk/src/target/kernel/patches/series
Log:
Add first, untested version of PCF50633 kernel driver
Added: trunk/src/target/kernel/patches/pcf50633.patch
===================================================================
--- trunk/src/target/kernel/patches/pcf50633.patch 2007-06-06 14:54:34 UTC (rev 2177)
+++ trunk/src/target/kernel/patches/pcf50633.patch 2007-06-06 18:08:19 UTC (rev 2178)
@@ -0,0 +1,2255 @@
+Index: linux-2.6.21.3-moko/drivers/i2c/chips/Kconfig
+===================================================================
+--- linux-2.6.21.3-moko.orig/drivers/i2c/chips/Kconfig
++++ linux-2.6.21.3-moko/drivers/i2c/chips/Kconfig
+@@ -46,6 +46,15 @@
+ This driver can also be built as a module. If so, the module
+ will be called pcf50606.
+
++config SENSORS_PCF50633
++ tristate "Philips PCF50633"
++ depends on I2C
++ help
++ If you say yes here you get support for Philips PCF50633
++ PMU (Power Management Unit) chips.
++
++ This driver can also be built as a module. If so, the module
++ will be called pcf50633.
+
+ config SENSORS_PCF8574
+ tristate "Philips PCF8574 and PCF8574A"
+Index: linux-2.6.21.3-moko/drivers/i2c/chips/Makefile
+===================================================================
+--- linux-2.6.21.3-moko.orig/drivers/i2c/chips/Makefile
++++ linux-2.6.21.3-moko/drivers/i2c/chips/Makefile
+@@ -9,6 +9,7 @@
+ obj-$(CONFIG_SENSORS_M41T00) += m41t00.o
+ obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o
+ obj-$(CONFIG_SENSORS_PCF50606) += pcf50606.o
++obj-$(CONFIG_SENSORS_PCF50633) += pcf50633.o
+ obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o
+ obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
+ obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
+Index: linux-2.6.21.3-moko/drivers/i2c/chips/pcf50633.c
+===================================================================
+--- /dev/null
++++ linux-2.6.21.3-moko/drivers/i2c/chips/pcf50633.c
+@@ -0,0 +1,1691 @@
++/* Philips PCF50633 Power Management Unit (PMU) driver
++ *
++ * (C) 2006-2007 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * 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; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * 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
++ *
++ * This driver is a monster ;) It provides the following features
++ * - voltage control for a dozen different voltage domains
++ * - charging control for main and backup battery
++ * - rtc / alarm
++ * - adc driver (hw_sensors like)
++ * - backlight
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/i2c.h>
++#include <linux/types.h>
++#include <linux/interrupt.h>
++#include <linux/irq.h>
++#include <linux/workqueue.h>
++#include <linux/rtc.h>
++#include <linux/bcd.h>
++#include <linux/watchdog.h>
++#include <linux/miscdevice.h>
++#include <linux/input.h>
++#include <linux/fb.h>
++#include <linux/backlight.h>
++#include <linux/sched.h>
++#include <linux/platform_device.h>
++#include <linux/pcf50633.h>
++#include <linux/apm-emulation.h>
++
++#include <asm/mach-types.h>
++#include <asm/arch/gta01.h>
++
++#include "pcf50633.h"
++
++#if 1
++#define DEBUGP(x, args ...) printk("%s: " x, __FUNCTION__, ## args)
++#define DEBUGPC(x, args ...) printk(x, ## args)
++#else
++#define DEBUGP(x, args ...)
++#define DEBUGPC(x, args ...)
++#endif
++
++/***********************************************************************
++ * Static data / structures
++ ***********************************************************************/
++
++static unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
++
++I2C_CLIENT_INSMOD_1(pcf50633);
++
++#define PCF50633_F_CHG_FAST 0x00000001 /* Charger Fast allowed */
++#define PCF50633_F_CHG_PRESENT 0x00000002 /* Charger present */
++#define PCF50633_F_CHG_FOK 0x00000004 /* Fast OK for battery */
++#define PCF50633_F_CHG_ERR 0x00000008 /* Charger Error */
++#define PCF50633_F_CHG_PROT 0x00000010 /* Charger Protection */
++#define PCF50633_F_CHG_READY 0x00000020 /* Charging completed */
++#define PCF50633_F_CHG_MASK 0x000000fc
++
++#define PCF50633_F_PWR_PRESSED 0x00000100
++#define PCF50633_F_RTC_SECOND 0x00000200
++#define PCF50633_F_USB_PRESENT 0x00000400
++
++enum close_state {
++ CLOSE_STATE_NOT,
++ CLOSE_STATE_ALLOW = 0x2342,
++};
++
++struct pcf50633_data {
++ struct i2c_client client;
++ struct pcf50633_platform_data *pdata;
++ struct backlight_device *backlight;
++ struct mutex lock;
++ unsigned int flags;
++ unsigned int working;
++ struct work_struct work;
++ struct rtc_device *rtc;
++ struct input_dev *input_dev;
++ int allow_close;
++ int onkey_seconds;
++ int irq;
++#ifdef CONFIG_PM
++ struct {
++ u_int8_t int1m, int2m, int3m, int4m, int5m;
++ u_int8_t ooctim2;
++ u_int8_t autoout, autoena, automxc;
++ u_int8_t down1out, down1mxc;
++ u_int8_t down2out, down2ena;
++ u_int8_t memldoout, memldoena;
++ u_int8_t ledout, ledena, leddim;
++ struct {
++ u_int8_t out;
++ u_int8_t ena;
++ } ldo[__NUM_PCF50633_REGS];
++ } standby_regs;
++#endif
++};
++
++static struct i2c_driver pcf50633_driver;
++
++struct pcf50633_data *pcf50633_global;
++EXPORT_SYMBOL(pcf50633_global);
++
++static struct platform_device *pcf50633_pdev;
++
++/* This is a mitsubishi TN11-3H103J T,B NTC Thermistor -10..79 centigrade */
++static const u_int16_t ntc_table_tn11_3h103j[] = {
++ /* -10 */
++ 40260, 38560, 36950, 35410, 33950, 32550, 31220, 29960, 28750, 27590,
++ 26490, 25440, 24440, 23480, 22560, 21680, 20830, 20020, 19240, 18500,
++ 17780, 17710, 16440, 15810, 15210, 14630, 14070, 13540, 13030, 12540,
++ 12070, 11620, 11190, 10780, 10380, 10000, 9635, 9286, 8950, 8629,
++ 8320, 8024, 7740, 7467, 7205, 6954, 6713, 6481, 6258, 6044,
++ 5839, 5641, 5451, 5269, 5093, 4924, 4762, 4605, 4455, 4310,
++ 4171, 4037, 3908, 3784, 3664, 3549, 3438, 3313, 3227, 3128,
++ 3032, 2939, 2850, 2763, 2680, 2600, 2522, 2448, 2375, 2306,
++ 2239, 2174, 2111, 2050, 1922, 1935, 1881, 1828, 1776, 1727,
++};
++
++
++/***********************************************************************
++ * Low-Level routines
++ ***********************************************************************/
++
++static inline int __reg_write(struct pcf50633_data *pcf, u_int8_t reg, u_int8_t val)
++{
++ return i2c_smbus_write_byte_data(&pcf->client, reg, val);
++}
++
++static int reg_write(struct pcf50633_data *pcf, u_int8_t reg, u_int8_t val)
++{
++ int ret;
++
++ mutex_lock(&pcf->lock);
++ ret = __reg_write(pcf, reg, val);
++ mutex_unlock(&pcf->lock);
++
++ return ret;
++}
++
++static inline int32_t __reg_read(struct pcf50633_data *pcf, u_int8_t reg)
++{
++ int32_t ret;
++
++ ret = i2c_smbus_read_byte_data(&pcf->client, reg);
++
++ return ret;
++}
++
++static u_int8_t reg_read(struct pcf50633_data *pcf, u_int8_t reg)
++{
++ int32_t ret;
++
++ mutex_lock(&pcf->lock);
++ ret = __reg_read(pcf, reg);
++ mutex_unlock(&pcf->lock);
++
++ return ret & 0xff;
++}
++
++static int reg_set_bit_mask(struct pcf50633_data *pcf,
++ u_int8_t reg, u_int8_t mask, u_int8_t val)
++{
++ int ret;
++ u_int8_t tmp;
++
++ val &= mask;
++
++ mutex_lock(&pcf->lock);
++
++ tmp = __reg_read(pcf, reg);
++ tmp &= ~mask;
++ tmp |= val;
++ ret = __reg_write(pcf, reg, tmp);
++
++ mutex_unlock(&pcf->lock);
++
++ return ret;
++}
++
++static int reg_clear_bits(struct pcf50633_data *pcf, u_int8_t reg, u_int8_t val)
++{
++ int ret;
++ u_int8_t tmp;
++
++ mutex_lock(&pcf->lock);
++
++ tmp = __reg_read(pcf, reg);
++ tmp &= ~val;
++ ret = __reg_write(pcf, reg, tmp);
++
++ mutex_unlock(&pcf->lock);
++
++ return ret;
++}
++
++/* synchronously read one ADC channel (busy-wait for result to be complete) */
++static u_int16_t adc_read(struct pcf50633_data *pcf, int channel, int avg,
++ u_int16_t *data2)
++{
++ u_int8_t adcs3, adcs2, adcs1;
++ u_int16_t ret;
++
++ DEBUGP("entering (pcf=%p, channel=%u, data2=%p)\n",
++ pcf, channel, data2);
++
++ channel &= PCF50633_ADCC1_ADCMUX_MASK;
++
++ mutex_lock(&pcf->lock);
++
++ /* start ADC conversion of selected channel */
++ __reg_write(pcf, PCF50633_REG_ADCC1, channel | avg |
++ PCF50633_ADCC1_ADCSTART | PCF50633_ADCC1_RES_10BIT);
++
++ do {
++ adcs3 = __reg_read(pcf, PCF50633_REG_ADCS3);
++ } while (!(adcs3 & PCF50633_ADCS3_ADCRDY));
++
++ adcs1 = __reg_read(pcf, PCF50633_REG_ADCS1);
++ ret = (adcs1 << 2) | (adcs3 & PCF50633_ADCS3_ADCDAT1L_MASK);
++
++ if (data2) {
++ adcs2 = __reg_read(pcf, PCF50633_REG_ADCS2);
++ *data2 = (adcs2 << 2) | (adcs3 & PCF50633_ADCS3_ADCDAT2L_MASK)
++ >> PCF50633_ADCS3_ADCDAT2L_SHIFT;
++ }
++
++ mutex_unlock(&pcf->lock);
++
++ DEBUGP("returning %u %u\n", ret, data2 ? *data2 : 0);
++
++ return ret;
++}
++
++/***********************************************************************
++ * Voltage / ADC
++ ***********************************************************************/
++
++static u_int8_t auto_voltage(unsigned int millivolts)
++{
++ if (millivolts < 1800)
++ return 0;
++ if (millivolts > 3800)
++ return 0xff;
++
++ millivolts -= 625;
++ return millivolts/25;
++}
++
++static unsigned int auto_2voltage(u_int8_t bits)
++{
++ if (bits < 0x2f)
++ return 0;
++ return 625 + (bits * 25);
++}
++
++static u_int8_t down_voltage(unsigned int millivolts)
++{
++ if (millivolts < 625)
++ return 0;
++ else if (millivolts > 3000)
++ return 0xff;
++
++ millivolts -= 625;
++ return millivolts/25;
++}
++
++static unsigned int down_2voltage(u_int8_t bits)
++{
++ return 625 + (bits*25);
++}
++
++static u_int8_t ldo_voltage(unsigned int millivolts)
++{
++ if (millivolts < 900)
++ return 0;
++ else if (millivolts > 3600)
++ return 0x1f;
++
++ millivolts -= 900;
++ return millivolts/100;
++}
++
++static unsigned int ldo_2voltage(u_int8_t bits)
++{
++ bits &= 0x1f;
++ return 900 + (bits * 100);
++}
++
++static const u_int8_t regulator_registers[__NUM_PCF50633_REGULATORS] = {
++ [PCF50633_REGULATOR_AUTO] = PCF50633_REG_AUTOOUT,
++ [PCF50633_REGULATOR_DOWN1] = PCF50633_REG_DOWN1OUT,
++ [PCF50633_REGULATOR_DOWN2] = PCF50633_REG_DOWN2OUT,
++ [PCF50633_REGULATOR_MEMLDO] = PCF50633_REG_MEMLDOOUT,
++ [PCF50633_REGULATOR_LDO1] = PCF50633_REG_LDO1OUT,
++ [PCF50633_REGULATOR_LDO2] = PCF50633_REG_LDO2OUT,
++ [PCF50633_REGULATOR_LDO3] = PCF50633_REG_LDO3OUT,
++ [PCF50633_REGULATOR_LDO4] = PCF50633_REG_LDO4OUT,
++ [PCF50633_REGULATOR_LDO5] = PCF50633_REG_LDO5OUT,
++ [PCF50633_REGULATOR_LDO6] = PCF50633_REG_LDO6OUT,
++ [PCF50633_REGULATOR_HCLDO] = PCF50633_REG_HCLDOOUT,
++};
++
++int pcf50633_onoff_set(struct pcf50633_data *pcf,
++ enum pcf50633_regulator_id reg, int on)
++{
++ u_int8_t addr;
++
++ if (reg >= __NUM_PCF50633_REGULATORS)
++ return -EINVAL;
++
++ /* the *ENA register is always one after the *OUT register */
++ addr = regulator_registers[reg] + 1;
++
++ if (on == 0)
++ reg_set_bit_mask(pcf, addr, PCF50633_REGULATOR_ON, 0);
++ else
++ reg_set_bit_mask(pcf, addr, PCF50633_REGULATOR_ON,
++ PCF50633_REGULATOR_ON);
++
++ return 0;
++}
++EXPORT_SYMBOL(pcf50633_onoff_set);
++
++int pcf50633_onoff_get(struct pcf50633_data *pcf,
++ enum pcf50633_regulator_id reg)
++{
++ u_int8_t val, addr;
++
++ if (reg >= __NUM_PCF50633_REGULATORS)
++ return -EINVAL;
++
++ /* the *ENA register is always one after the *OUT register */
++ addr = regulator_registers[reg] + 1;
++ val = reg_read(pcf, addr) & PCF50633_REGULATOR_ON;
++
++ return val;
++}
++EXPORT_SYMBOL(pcf50633_onoff_get);
++
++int pcf50633_voltage_set(struct pcf50633_data *pcf,
++ enum pcf50633_regulator_id reg,
++ unsigned int millivolts)
++{
++ u_int8_t volt_bits;
++ u_int8_t regnr;
++
++ DEBUGP("pcf=%p, reg=%d, mvolts=%d\n", pcf, reg, millivolts);
++
++ if (reg >= __NUM_PCF50633_REGULATORS)
++ return -EINVAL;
++
++ regnr = regulator_registers[reg];
++
++ if (millivolts > pcf->pdata->rails[reg].voltage.max)
++ return -EINVAL;
++
++ switch (reg) {
++ case PCF50633_REGULATOR_AUTO:
++ volt_bits = auto_voltage(millivolts);
++ break;
++ case PCF50633_REGULATOR_DOWN1:
++ volt_bits = down_voltage(millivolts);
++ break;
++ case PCF50633_REGULATOR_DOWN2:
++ volt_bits = down_voltage(millivolts);
++ 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:
++ volt_bits = ldo_voltage(millivolts);
++ DEBUGP("ldo_voltage(0x%x)=%u\n", millivolts, volt_bits);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return reg_write(pcf, regnr, volt_bits);
++}
++EXPORT_SYMBOL(pcf50633_voltage_set);
++
++unsigned int pcf50633_voltage_get(struct pcf50633_data *pcf,
++ enum pcf50633_regulator_id reg)
++{
++ u_int8_t volt_bits;
++ u_int8_t regnr;
++ unsigned int rc = 0;
++
++ if (reg >= __NUM_PCF50633_REGULATORS)
++ return -EINVAL;
++
++ regnr = regulator_registers[reg];
++ volt_bits = reg_read(pcf, regnr);
++
++ switch (reg) {
++ case PCF50633_REGULATOR_AUTO:
++ rc = auto_2voltage(volt_bits);
++ break;
++ case PCF50633_REGULATOR_DOWN1:
++ rc = down_2voltage(volt_bits);
++ break;
++ case PCF50633_REGULATOR_DOWN2:
++ rc = down_2voltage(volt_bits);
++ 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:
++ rc = ldo_2voltage(volt_bits);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return rc;
++}
++EXPORT_SYMBOL(pcf50633_voltage_get);
++
++/* go into 'STANDBY' mode, i.e. power off the main CPU and peripherals */
++void pcf50633_go_standby(void)
++{
++ reg_write(pcf50633_global, PCF50633_REG_OOCSHDWN,
++ PCF50633_OOCSHDWN_GOSTDBY);
++}
++EXPORT_SYMBOL(pcf50633_go_standby);
++
++void pcf50633_gpo0_set(struct pcf50633_data *pcf, int on)
++{
++ u_int8_t val;
++
++ if (on)
++ val = PCF50633_GPOCFG_GPOSEL_1;
++ else
++ val = PCF50633_GPOCFG_GPOSEL_0;
++
++ reg_set_bit_mask(pcf, PCF50633_REG_GPOCFG, 0x0f, val);
++}
++EXPORT_SYMBOL(pcf50633_gpo0_set);
++
++int pcf50633_gpo0_get(struct pcf50633_data *pcf)
++{
++ u_int8_t reg = reg_read(pcf, PCF50633_REG_GPOCFG) & 0x0f;
++
++ if (reg == PCF50633_GPOCFG_GPOSEL_1 ||
++ reg == (PCF50633_GPOCFG_GPOSEL_0|PCF50633_GPOCFG_GPOSEL_INVERSE))
++ return 1;
++
++ return 0;
++}
++EXPORT_SYMBOL(pcf50633_gpo0_get);
++
++static void pcf50633_work(struct work_struct *work)
++{
++ struct pcf50633_data *pcf =
++ container_of(work, struct pcf50633_data, work);
++ u_int8_t int1, int2, int3, int4, int5;
++
++ pcf->working = 1;
++
++ /* FIXME: read in one i2c transaction */
++ int1 = __reg_read(pcf, PCF50633_REG_INT1);
++ int2 = __reg_read(pcf, PCF50633_REG_INT2);
++ int3 = __reg_read(pcf, PCF50633_REG_INT3);
++ int4 = __reg_read(pcf, PCF50633_REG_INT4);
++ int5 = __reg_read(pcf, PCF50633_REG_INT5);
++
++ DEBUGP("INT1=0x%02x INT2=0x%02x INT3=0x%02x INT4=0x%02x INT5=0x%02x\n",
++ int1, int2, int3, int4, int5);
++
++ if (int1 & PCF50633_INT1_ADPINS) {
++ /* Charger inserted */
++ DEBUGPC("ADPINS ");
++ input_report_key(pcf->input_dev, KEY_BATTERY, 1);
++ apm_queue_event(APM_POWER_STATUS_CHANGE);
++ pcf->flags |= PCF50633_F_CHG_PRESENT;
++ if (pcf->pdata->cb)
++ pcf->pdata->cb(&pcf->client.dev,
++ PCF50633_FEAT_MBC, PMU_EVT_INSERT);
++ /* FIXME: signal this to userspace */
++ //kobject_uevent( ,KOBJ_ADD);
++ }
++ if (int1 & PCF50633_INT1_ADPREM) {
++ /* Charger removed */
++ DEBUGPC("ADPREM ");
++ input_report_key(pcf->input_dev, KEY_BATTERY, 0);
++ apm_queue_event(APM_POWER_STATUS_CHANGE);
++ pcf->flags &= ~PCF50633_F_CHG_PRESENT;
++ if (pcf->pdata->cb)
++ pcf->pdata->cb(&pcf->client.dev,
++ PCF50633_FEAT_MBC, PMU_EVT_REMOVE);
++ /* FIXME: signal this to userspace */
++ //kobject_uevent( ,KOBJ_ADD);
++ }
++ if (int1 & PCF50633_INT1_USBINS) {
++ DEBUGPC("USBINS ");
++ input_report_key(pcf->input_dev, KEY_POWER2, 1);
++ apm_queue_event(APM_POWER_STATUS_CHANGE);
++ pcf->flags |= PCF50633_F_USB_PRESENT;
++ if (pcf->pdata->cb)
++ pcf->pdata->cb(&pcf->client.dev,
++ PCF50633_FEAT_MBC, PMU_EVT_USB_INSERT);
++
++ }
++ if (int1 & PCF50633_INT1_USBREM) {
++ DEBUGPC("USBREM ");
++ input_report_key(pcf->input_dev, KEY_POWER2, 0);
++ apm_queue_event(APM_POWER_STATUS_CHANGE);
++ pcf->flags &= ~PCF50633_F_USB_PRESENT;
++ if (pcf->pdata->cb)
++ pcf->pdata->cb(&pcf->client.dev,
++ PCF50633_FEAT_MBC, PMU_EVT_USB_REMOVE);
++ }
++ if (int1 & PCF50633_INT1_ALARM) {
++ DEBUGPC("ALARM ");
++ if (pcf->pdata->used_features & PCF50633_FEAT_RTC)
++ rtc_update_irq(&pcf->rtc->class_dev, 1,
++ RTC_AF | RTC_IRQF);
++ }
++ if (int1 & PCF50633_INT1_SECOND) {
++ DEBUGPC("SECOND ");
++ if (pcf->flags & PCF50633_F_RTC_SECOND)
++ rtc_update_irq(&pcf->rtc->class_dev, 1,
++ RTC_PF | RTC_IRQF);
++
++ if (pcf->onkey_seconds >= 0 &&
++ pcf->flags & PCF50633_F_PWR_PRESSED) {
++ DEBUGP("ONKEY_SECONDS(%u, OOCSTAT=0x%02x) ",
++ pcf->onkey_seconds,
++ reg_read(pcf, PCF50633_REG_OOCSTAT));
++ pcf->onkey_seconds++;
++ if (pcf->onkey_seconds >=
++ pcf->pdata->onkey_seconds_required) {
++ /* Ask init to do 'ctrlaltdel' */
++ DEBUGPC("SIGINT(init) ");
++ kill_proc(1, SIGINT, 1);
++ /* FIXME: what if userspace doesn't shut down? */
++ }
++ }
++ }
++
++ if (int2 & PCF50633_INT2_ONKEYF) {
++ /* ONKEY falling edge (start of button press) */
++ DEBUGPC("ONKEYF ");
++ pcf->flags |= PCF50633_F_PWR_PRESSED;
++ input_report_key(pcf->input_dev, KEY_POWER, 1);
++ }
++ if (int2 & PCF50633_INT2_ONKEYR) {
++ /* ONKEY rising edge (end of button press) */
++ DEBUGPC("ONKEYR ");
++ pcf->flags &= ~PCF50633_F_PWR_PRESSED;
++ pcf->onkey_seconds = -1;
++ input_report_key(pcf->input_dev, KEY_POWER, 0);
++ /* disable SECOND interrupt in case RTC didn't
++ * request it */
++ if (!(pcf->flags & PCF50633_F_RTC_SECOND))
++ reg_set_bit_mask(pcf, PCF50633_REG_INT1M,
++ PCF50633_INT1_SECOND,
++ PCF50633_INT1_SECOND);
++ }
++ /* FIXME: we don't use EXTON1/2/3. thats why we skip it */
++
++ if (int3 & PCF50633_INT3_BATFULL) {
++ DEBUGPC("BATFULL ");
++ /* FIXME: signal this to userspace */
++ }
++ if (int3 & PCF50633_INT3_CHGHALT) {
++ DEBUGPC("CHGHALT ");
++ /* FIXME: signal this to userspace */
++ }
++ if (int3 & PCF50633_INT3_THLIMON) {
++ DEBUGPC("THLIMON ");
++ /* FIXME: signal this to userspace */
++ }
++ if (int3 & PCF50633_INT3_THLIMOFF) {
++ DEBUGPC("THLIMOFF ");
++ /* FIXME: signal this to userspace */
++ }
++ if (int3 & PCF50633_INT3_USBLIMON) {
++ DEBUGPC("USBLIMON ");
++ /* FIXME: signal this to userspace */
++ }
++ if (int3 & PCF50633_INT3_USBLIMOFF) {
++ DEBUGPC("USBLIMOFF ");
++ /* FIXME: signal this to userspace */
++ }
++ if (int3 & PCF50633_INT3_ADCRDY) {
++ /* ADC result ready */
++ DEBUGPC("ADCRDY ");
++ }
++ if (int3 & PCF50633_INT3_ONKEY1S) {
++ /* ONKEY pressed for more than 1 second */
++ pcf->onkey_seconds = 0;
++ DEBUGPC("ONKEY1S ");
++ /* Tell PMU we are taking care of this */
++ reg_set_bit_mask(pcf, PCF50633_REG_OOCSHDWN,
++ PCF50633_OOCSHDWN_TOTRST,
++ PCF50633_OOCSHDWN_TOTRST);
++ /* enable SECOND interrupt (hz tick) */
++ reg_clear_bits(pcf, PCF50633_REG_INT1M, PCF50633_INT1_SECOND);
++ }
++
++ if (int4 & (PCF50633_INT4_LOWBAT|PCF50633_INT4_LOWSYS)) {
++ /* Really low battery voltage, we have 8 seconds left */
++ DEBUGPC("LOWBAT ");
++ apm_queue_event(APM_LOW_BATTERY);
++ DEBUGPC("SIGPWR(init) ");
++ kill_proc(1, SIGPWR, 1);
++ /* Tell PMU we are taking care of this */
++ reg_set_bit_mask(pcf, PCF50633_REG_OOCSHDWN,
++ PCF50633_OOCSHDWN_TOTRST,
++ PCF50633_OOCSHDWN_TOTRST);
++ }
++ if (int4 & PCF50633_INT4_HIGHTMP) {
++ /* High temperature */
++ DEBUGPC("HIGHTMP ");
++ apm_queue_event(APM_CRITICAL_SUSPEND);
++ }
++ if (int4 & (PCF50633_INT4_AUTOPWRFAIL|PCF50633_INT4_DWN1PWRFAIL|
++ PCF50633_INT4_DWN2PWRFAIL|PCF50633_INT4_LEDPWRFAIL|
++ PCF50633_INT4_LEDOVP)) {
++ /* Some regulator failed */
++ DEBUGPC("REGULATOR_FAIL ");
++ /* FIXME: deal with this */
++ }
++
++ DEBUGPC("\n");
++
++ pcf->working = 0;
++ input_sync(pcf->input_dev);
++ put_device(&pcf->client.dev);
++
++ enable_irq(pcf->irq);
++}
++
++static void pcf50633_schedule_work(struct pcf50633_data *pcf)
++{
++ int status;
++
++ get_device(&pcf->client.dev);
++ status = schedule_work(&pcf->work);
++ if (!status && !pcf->working)
++ dev_dbg(&pcf->client.dev, "work item may be lost\n");
++}
++
++
++static irqreturn_t pcf50633_irq(int irq, void *_pcf)
++{
++ struct pcf50633_data *pcf = _pcf;
++
++ DEBUGP("entering(irq=%u, pcf=%p): scheduling work\n",
++ irq, _pcf);
++ pcf50633_schedule_work(pcf);
++
++ /* Disable any further interrupts until we have processed
++ * the current one */
++ disable_irq(irq);
++
++ return IRQ_HANDLED;
++}
++
++static u_int16_t adc_to_batt_millivolts(u_int16_t adc)
++{
++ u_int16_t mvolts;
++
++ mvolts = (adc * 6000) / 1024;
++
++ return mvolts;
++}
++
++#define BATTVOLT_SCALE_START 2800
++#define BATTVOLT_SCALE_END 4200
++#define BATTVOLT_SCALE_DIVIDER ((BATTVOLT_SCALE_END - BATTVOLT_SCALE_START)/100)
++
++static u_int8_t battvolt_scale(u_int16_t battvolt)
++{
++ /* FIXME: this linear scale is completely bogus */
++ u_int16_t battvolt_relative = battvolt - BATTVOLT_SCALE_START;
++ unsigned int percent = battvolt_relative / BATTVOLT_SCALE_DIVIDER;
++
++ return percent;
++}
++
++u_int16_t pcf50633_battvolt(struct pcf50633_data *pcf)
++{
++ u_int16_t adc;
++ adc = adc_read(pcf, PCF50633_ADCC1_MUX_BATSNS_RES, 0, NULL);
++
++ return adc_to_batt_millivolts(adc);
++}
++EXPORT_SYMBOL(pcf50633_battvolt);
++
++static ssize_t show_battvolt(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++
++ return sprintf(buf, "%u\n", pcf50633_battvolt(pcf));
++}
++static DEVICE_ATTR(battvolt, S_IRUGO | S_IWUSR, show_battvolt, NULL);
++
++static int reg_id_by_name(const char *name)
++{
++ int reg_id;
++
++ if (!strcmp(name, "voltage_auto"))
++ reg_id = PCF50633_REGULATOR_AUTO;
++ else if (!strcmp(name, "voltage_down1"))
++ reg_id = PCF50633_REGULATOR_DOWN1;
++ else if (!strcmp(name, "voltage_down2"))
++ reg_id = PCF50633_REGULATOR_DOWN2;
++ else if (!strcmp(name, "voltage_memldo"))
++ reg_id = PCF50633_REGULATOR_MEMLDO;
++ else if (!strcmp(name, "voltage_ldo1"))
++ reg_id = PCF50633_REGULATOR_LDO1;
++ else if (!strcmp(name, "voltage_ldo2"))
++ reg_id = PCF50633_REGULATOR_LDO2;
++ else if (!strcmp(name, "voltage_ldo3"))
++ reg_id = PCF50633_REGULATOR_LDO3;
++ else if (!strcmp(name, "voltage_ldo4"))
++ reg_id = PCF50633_REGULATOR_LDO4;
++ else if (!strcmp(name, "voltage_ldo5"))
++ reg_id = PCF50633_REGULATOR_LDO5;
++ else if (!strcmp(name, "voltage_ldo6"))
++ reg_id = PCF50633_REGULATOR_LDO6;
++ else if (!strcmp(name, "voltage_hcldo"))
++ reg_id = PCF50633_REGULATOR_HCLDO;
++ else
++ reg_id = -1;
++
++ return reg_id;
++}
++
++static ssize_t show_vreg(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++ unsigned int reg_id;
++
++ reg_id = reg_id_by_name(attr->attr.name);
++ if (reg_id < 0)
++ return 0;
++
++ if (pcf50633_onoff_get(pcf, reg_id) > 0)
++ return sprintf(buf, "%u\n", pcf50633_voltage_get(pcf, reg_id));
++ else
++ return strlcpy(buf, "0\n", PAGE_SIZE);
++}
++
++static ssize_t set_vreg(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++ unsigned long mvolts = simple_strtoul(buf, NULL, 10);
++ unsigned int reg_id;
++
++ reg_id = reg_id_by_name(attr->attr.name);
++ if (reg_id < 0)
++ return -EIO;
++
++ DEBUGP("attempting to set %s(%d) to %lu mvolts\n", attr->attr.name,
++ reg_id, mvolts);
++
++ if (mvolts == 0) {
++ pcf50633_onoff_set(pcf, reg_id, 0);
++ } else {
++ if (pcf50633_voltage_set(pcf, reg_id, mvolts) < 0) {
++ dev_warn(dev, "refusing to set %s(%d) to %lu mvolts "
++ "(max=%u)\n", attr->attr.name, reg_id, mvolts,
++ pcf->pdata->rails[reg_id].voltage.max);
++ return -EINVAL;
++ }
++ pcf50633_onoff_set(pcf, reg_id, 1);
++ }
++
++ return count;
++}
++
++static DEVICE_ATTR(voltage_auto, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_down1, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_down2, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_memldo, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_ldo1, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_ldo2, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_ldo3, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_ldo4, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_ldo5, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_ldo6, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_hcldo, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++
++/***********************************************************************
++ * Charger Control
++ ***********************************************************************/
++
++/* Enable/disable fast charging (500mA in the GTA01) */
++void pcf50633_charge_fast(struct pcf50633_data *pcf, int on)
++{
++#if 0
++ if (!(pcf->pdata->used_features & PCF50606_FEAT_MBC))
++ return;
++
++ if (on) {
++ /* We can allow PCF to automatically charge
++ * using Ifast */
++ pcf->flags |= PCF50606_F_CHG_FAST;
++ reg_set_bit_mask(pcf, PCF50606_REG_MBCC1,
++ PCF50606_MBCC1_AUTOFST,
++ PCF50606_MBCC1_AUTOFST);
++ } else {
++ pcf->flags &= ~PCF50606_F_CHG_FAST;
++ /* disable automatic fast-charge */
++ reg_clear_bits(pcf, PCF50606_REG_MBCC1,
++ PCF50606_MBCC1_AUTOFST);
++ /* switch to idle mode to abort existing charge
++ * process */
++ reg_set_bit_mask(pcf, PCF50606_REG_MBCC1,
++ PCF50606_MBCC1_CHGMOD_MASK,
++ PCF50606_MBCC1_CHGMOD_IDLE);
++ }
++#endif
++}
++EXPORT_SYMBOL(pcf50633_charge_fast);
++
++#define ONE 1000000
++static inline u_int16_t adc_to_rntc(struct pcf50633_data *pcf, u_int16_t adc)
++{
++ u_int32_t r_batt = (adc * pcf->pdata->r_fix_batt) / (1023 - adc);
++ u_int16_t r_ntc;
++
++ /* The battery NTC has a parallell 10kOhms resistor */
++ r_ntc = ONE / ((ONE/r_batt) - (ONE/pcf->pdata->r_fix_batt_par));
++
++ return r_ntc;
++}
++
++static inline int16_t rntc_to_temp(u_int16_t rntc)
++{
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(ntc_table_tn11_3h103j); i++) {
++ if (rntc > ntc_table_tn11_3h103j[i])
++ return i - 10;
++ }
++ return 2342;
++}
++
++static ssize_t show_battemp(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++#if 0
++ u_int16_t adc;
++
++ adc = adc_read(pcf, PCF50633_ADCC1_MUX_BATTEMP, 0, NULL);
++
++ return sprintf(buf, "%d\n", rntc_to_temp(adc_to_rntc(pcf, adc)));
++#endif
++ return sprintf(buf, "\n");
++}
++static DEVICE_ATTR(battemp, S_IRUGO | S_IWUSR, show_battemp, NULL);
++
++static inline u_int16_t adc_to_chg_milliamps(struct pcf50633_data *pcf,
++ u_int16_t adc_adcin1,
++ u_int16_t adc_batvolt)
++{
++ u_int32_t res = ((adc_adcin1 - adc_batvolt) * 6000);
++ return res / (pcf->pdata->r_sense_milli * 1024 / 1000);
++}
++
++static ssize_t show_chgcur(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++#if 0
++ u_int16_t adc_batvolt, adc_adcin1;
++ u_int16_t ma;
++
++ adc_batvolt = adc_read(pcf, PCF50606_ADCMUX_BATVOLT_ADCIN1,
++ &adc_adcin1);
++ ma = adc_to_chg_milliamps(pcf, adc_adcin1, adc_batvolt);
++
++ return sprintf(buf, "%u\n", ma);
++#endif
++ return sprintf(buf, "\n");
++}
++static DEVICE_ATTR(chgcur, S_IRUGO | S_IWUSR, show_chgcur, NULL);
++
++static const char *chgmode_names[] = {
++ [PCF50633_MBCS2_MBC_PLAY] = "play-only",
++ [PCF50633_MBCS2_MBC_USB_PRE] = "pre",
++ [PCF50633_MBCS2_MBC_ADP_PRE] = "pre",
++ [PCF50633_MBCS2_MBC_USB_PRE_WAIT] = "pre-wait",
++ [PCF50633_MBCS2_MBC_ADP_PRE_WAIT] = "pre-wait",
++ [PCF50633_MBCS2_MBC_USB_FAST] = "fast",
++ [PCF50633_MBCS2_MBC_ADP_FAST] = "fast",
++ [PCF50633_MBCS2_MBC_USB_FAST_WAIT] = "fast-wait",
++ [PCF50633_MBCS2_MBC_ADP_FAST_WAIT] = "fast-wait",
++ [PCF50633_MBCS2_MBC_ADP_FAST_WAIT] = "bat-full",
++};
++
++static ssize_t show_chgmode(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++ u_int8_t mbcs2 = reg_read(pcf, PCF50633_REG_MBCS2);
++ u_int8_t chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
++
++ return sprintf(buf, "%s\n", chgmode_names[chgmod]);
++}
++
++static ssize_t set_chgmode(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++#if 0
++ u_int8_t mbcc1 = reg_read(pcf, PCF50606_REG_MBCC1);
++
++ mbcc1 &= ~PCF50606_MBCC1_CHGMOD_MASK;
++
++ if (!strcmp(buf, "qualification"))
++ mbcc1 |= PCF50606_MBCC1_CHGMOD_QUAL;
++ else if (!strcmp(buf, "pre"))
++ mbcc1 |= PCF50606_MBCC1_CHGMOD_PRE;
++ else if (!strcmp(buf, "trickle"))
++ mbcc1 |= PCF50606_MBCC1_CHGMOD_TRICKLE;
++ else if (!strcmp(buf, "fast_cccv"))
++ mbcc1 |= PCF50606_MBCC1_CHGMOD_FAST_CCCV;
++ /* We don't allow the other fast modes for security reasons */
++ else if (!strcmp(buf, "idle"))
++ mbcc1 |= PCF50606_MBCC1_CHGMOD_IDLE;
++ else
++ return -EINVAL;
++
++ reg_write(pcf, PCF50606_REG_MBCC1, mbcc1);
++#endif
++ return count;
++}
++
++static DEVICE_ATTR(chgmode, S_IRUGO | S_IWUSR, show_chgmode, set_chgmode);
++
++static const char *chgstate_names[] = {
++ [PCF50633_F_CHG_FAST] = "fast_enabled",
++ [PCF50633_F_CHG_PRESENT] = "present",
++ [PCF50633_F_CHG_FOK] = "fast_ok",
++ [PCF50633_F_CHG_ERR] = "error",
++ [PCF50633_F_CHG_PROT] = "protection",
++ [PCF50633_F_CHG_READY] = "ready",
++};
++
++static ssize_t show_chgstate(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++#if 0
++ char *b = buf;
++ int i;
++
++ for (i = 0; i < 32; i++)
++ if (pcf->flags & (1 << i) && i < ARRAY_SIZE(chgstate_names))
++ b += sprintf(b, "%s ", chgstate_names[i]);
++
++ if (b > buf)
++ b += sprintf(b, "\n");
++
++ return b - buf;
++#endif
++ return 0;
++}
++static DEVICE_ATTR(chgstate, S_IRUGO | S_IWUSR, show_chgstate, NULL);
++
++/***********************************************************************
++ * APM emulation
++ ***********************************************************************/
++
++extern void (*apm_get_power_status)(struct apm_power_info *);
++
++static void pcf50633_get_power_status(struct apm_power_info *info)
++{
++ struct pcf50633_data *pcf = pcf50633_global;
++ u_int8_t chgmod = reg_read(pcf, PCF50633_REG_MBCS2) &
++ PCF50633_MBCS2_MBC_MASK;
++ u_int16_t battvolt = 0; /* FIXME */
++ //u_int16_t battvolt = pcf50606_battvolt(pcf);
++
++ if (reg_read(pcf, PCF50633_REG_MBCS1) &
++ (PCF50633_MBCS1_USBPRES|PCF50633_MBCS1_ADAPTPRES))
++ info->ac_line_status = APM_AC_ONLINE;
++ else
++ info->ac_line_status = APM_AC_OFFLINE;
++
++ switch (chgmod) {
++ case PCF50633_MBCS2_MBC_USB_PRE:
++ case PCF50633_MBCS2_MBC_USB_PRE_WAIT:
++ case PCF50633_MBCS2_MBC_USB_FAST_WAIT:
++ case PCF50633_MBCS2_MBC_ADP_PRE:
++ case PCF50633_MBCS2_MBC_ADP_PRE_WAIT:
++ case PCF50633_MBCS2_MBC_ADP_FAST_WAIT:
++ case PCF50633_MBCS2_MBC_BAT_FULL:
++ case PCF50633_MBCS2_MBC_HALT:
++ info->battery_life = battvolt_scale(battvolt);
++ break;
++ case PCF50633_MBCS2_MBC_USB_FAST:
++ case PCF50633_MBCS2_MBC_ADP_FAST:
++ info->battery_status = APM_BATTERY_STATUS_CHARGING;
++ info->battery_flag = APM_BATTERY_FLAG_CHARGING;
++ default:
++ break;
++ }
++}
++
++/***********************************************************************
++ * RTC
++ ***********************************************************************/
++
++struct pcf50633_time {
++ u_int8_t sec;
++ u_int8_t min;
++ u_int8_t hour;
++ u_int8_t wkday;
++ u_int8_t day;
++ u_int8_t month;
++ u_int8_t year;
++};
++
++static void pcf2rtc_time(struct rtc_time *rtc, struct pcf50633_time *pcf)
++{
++ rtc->tm_sec = BCD2BIN(pcf->sec);
++ rtc->tm_min = BCD2BIN(pcf->min);
++ rtc->tm_hour = BCD2BIN(pcf->hour);
++ rtc->tm_wday = BCD2BIN(pcf->wkday);
++ rtc->tm_mday = BCD2BIN(pcf->day);
++ rtc->tm_mon = BCD2BIN(pcf->month);
++ rtc->tm_year = BCD2BIN(pcf->year) + 100;
++}
++
++static void rtc2pcf_time(struct pcf50633_time *pcf, struct rtc_time *rtc)
++{
++ pcf->sec = BIN2BCD(rtc->tm_sec);
++ pcf->min = BIN2BCD(rtc->tm_min);
++ pcf->hour = BIN2BCD(rtc->tm_hour);
++ pcf->wkday = BIN2BCD(rtc->tm_wday);
++ pcf->day = BIN2BCD(rtc->tm_mday);
++ pcf->month = BIN2BCD(rtc->tm_mon);
++ pcf->year = BIN2BCD(rtc->tm_year - 100);
++}
++
++static int pcf50633_rtc_ioctl(struct device *dev, unsigned int cmd,
++ unsigned long arg)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++ switch (cmd) {
++ case RTC_PIE_OFF:
++ /* disable periodic interrupt (hz tick) */
++ pcf->flags &= ~PCF50633_F_RTC_SECOND;
++ reg_set_bit_mask(pcf, PCF50633_REG_INT1M,
++ PCF50633_INT1_SECOND, PCF50633_INT1_SECOND);
++ return 0;
++ case RTC_PIE_ON:
++ /* ensable periodic interrupt (hz tick) */
++ pcf->flags |= PCF50633_F_RTC_SECOND;
++ reg_clear_bits(pcf, PCF50633_REG_INT1M, PCF50633_INT1_SECOND);
++ return 0;
++ }
++ return -ENOIOCTLCMD;
++}
++
++static int pcf50633_rtc_read_time(struct device *dev, struct rtc_time *tm)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++ struct pcf50633_time pcf_tm;
++
++ mutex_lock(&pcf->lock);
++ pcf_tm.sec = __reg_read(pcf, PCF50633_REG_RTCSC);
++ pcf_tm.min = __reg_read(pcf, PCF50633_REG_RTCMN);
++ pcf_tm.hour = __reg_read(pcf, PCF50633_REG_RTCHR);
++ pcf_tm.wkday = __reg_read(pcf, PCF50633_REG_RTCWD);
++ pcf_tm.day = __reg_read(pcf, PCF50633_REG_RTCDT);
++ pcf_tm.month = __reg_read(pcf, PCF50633_REG_RTCMT);
++ pcf_tm.year = __reg_read(pcf, PCF50633_REG_RTCYR);
++ mutex_unlock(&pcf->lock);
++
++ DEBUGP("PCF_TIME: %02x.%02x.%02x %02x:%02x:%02x\n",
++ pcf_tm.day, pcf_tm.month, pcf_tm.year,
++ pcf_tm.hour, pcf_tm.min, pcf_tm.sec);
++
++ pcf2rtc_time(tm, &pcf_tm);
++
++ DEBUGP("RTC_TIME: %u.%u.%u %u:%u:%u\n",
++ tm->tm_mday, tm->tm_mon, tm->tm_year,
++ tm->tm_hour, tm->tm_min, tm->tm_sec);
++
++ return 0;
++}
++
++static int pcf50633_rtc_set_time(struct device *dev, struct rtc_time *tm)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++ struct pcf50633_time pcf_tm;
++
++ DEBUGP("RTC_TIME: %u.%u.%u %u:%u:%u\n",
++ tm->tm_mday, tm->tm_mon, tm->tm_year,
++ tm->tm_hour, tm->tm_min, tm->tm_sec);
++ rtc2pcf_time(&pcf_tm, tm);
++ DEBUGP("PCF_TIME: %02x.%02x.%02x %02x:%02x:%02x\n",
++ pcf_tm.day, pcf_tm.month, pcf_tm.year,
++ pcf_tm.hour, pcf_tm.min, pcf_tm.sec);
++
++ mutex_lock(&pcf->lock);
++ /* FIXME: disable second interrupt */
++ __reg_write(pcf, PCF50633_REG_RTCSC, pcf_tm.sec);
++ __reg_write(pcf, PCF50633_REG_RTCMN, pcf_tm.min);
++ __reg_write(pcf, PCF50633_REG_RTCHR, pcf_tm.hour);
++ __reg_write(pcf, PCF50633_REG_RTCWD, pcf_tm.wkday);
++ __reg_write(pcf, PCF50633_REG_RTCDT, pcf_tm.day);
++ __reg_write(pcf, PCF50633_REG_RTCMT, pcf_tm.month);
++ __reg_write(pcf, PCF50633_REG_RTCYR, pcf_tm.year);
++ /* FIXME: re-enable second interrupt */
++ mutex_unlock(&pcf->lock);
++
++ return 0;
++}
++
++static int pcf50633_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++ struct pcf50633_time pcf_tm;
++
++ mutex_lock(&pcf->lock);
++ pcf_tm.sec = __reg_read(pcf, PCF50633_REG_RTCSCA);
++ pcf_tm.min = __reg_read(pcf, PCF50633_REG_RTCMNA);
++ pcf_tm.hour = __reg_read(pcf, PCF50633_REG_RTCHRA);
++ pcf_tm.wkday = __reg_read(pcf, PCF50633_REG_RTCWDA);
++ pcf_tm.day = __reg_read(pcf, PCF50633_REG_RTCDTA);
++ pcf_tm.month = __reg_read(pcf, PCF50633_REG_RTCMTA);
++ pcf_tm.year = __reg_read(pcf, PCF50633_REG_RTCYRA);
++ mutex_unlock(&pcf->lock);
++
++ pcf2rtc_time(&alrm->time, &pcf_tm);
++
++ return 0;
++}
++
++static int pcf50633_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++ struct pcf50633_time pcf_tm;
++ u_int8_t irqmask;
++
++ rtc2pcf_time(&pcf_tm, &alrm->time);
++
++ mutex_lock(&pcf->lock);
++
++ /* disable alarm interrupt */
++ irqmask = __reg_read(pcf, PCF50633_REG_INT1M);
++ irqmask |= PCF50633_INT1_ALARM;
++ __reg_write(pcf, PCF50633_REG_INT1M, irqmask);
++
++ __reg_write(pcf, PCF50633_REG_RTCSCA, pcf_tm.sec);
++ __reg_write(pcf, PCF50633_REG_RTCMNA, pcf_tm.min);
++ __reg_write(pcf, PCF50633_REG_RTCHRA, pcf_tm.hour);
++ __reg_write(pcf, PCF50633_REG_RTCWDA, pcf_tm.wkday);
++ __reg_write(pcf, PCF50633_REG_RTCDTA, pcf_tm.day);
++ __reg_write(pcf, PCF50633_REG_RTCMTA, pcf_tm.month);
++ __reg_write(pcf, PCF50633_REG_RTCYRA, pcf_tm.year);
++
++ if (alrm->enabled) {
++ /* (re-)enaable alarm interrupt */
++ irqmask = __reg_read(pcf, PCF50633_REG_INT1M);
++ irqmask &= ~PCF50633_INT1_ALARM;
++ __reg_write(pcf, PCF50633_REG_INT1M, irqmask);
++ }
++
++ mutex_unlock(&pcf->lock);
++
++ /* FIXME */
++ return 0;
++}
++
++static struct rtc_class_ops pcf50633_rtc_ops = {
++ .ioctl = pcf50633_rtc_ioctl,
++ .read_time = pcf50633_rtc_read_time,
++ .set_time = pcf50633_rtc_set_time,
++ .read_alarm = pcf50633_rtc_read_alarm,
++ .set_alarm = pcf50633_rtc_set_alarm,
++};
++
++/***********************************************************************
++ * Backlight device
++ ***********************************************************************/
++
++static int pcf50633bl_get_intensity(struct backlight_device *bd)
++{
++ struct pcf50633_data *pcf = class_get_devdata(&bd->class_dev);
++ int intensity = reg_read(pcf, PCF50633_REG_LEDOUT);
++
++ return intensity & 0x3f;
++}
++
++static int pcf50633bl_set_intensity(struct backlight_device *bd)
++{
++ struct pcf50633_data *pcf = class_get_devdata(&bd->class_dev);
++ int intensity = bd->props.brightness;
++
++ if (bd->props.power != FB_BLANK_UNBLANK)
++ intensity = 0;
++ if (bd->props.fb_blank != FB_BLANK_UNBLANK)
++ intensity = 0;
++
++ return reg_set_bit_mask(pcf, PCF50633_REG_LEDOUT, 0x3f,
++ intensity);
++}
++
++static struct backlight_ops pcf50633bl_ops = {
++ .get_brightness = pcf50633bl_get_intensity,
++ .update_status = pcf50633bl_set_intensity,
++};
++
++/***********************************************************************
++ * Driver initialization
++ ***********************************************************************/
++
++#ifdef CONFIG_MACH_NEO1973_GTA02
++/* We currently place those platform devices here to make sure the device
++ * suspend/resume order is correct */
++static struct platform_device gta01_pm_gps_dev = {
++ .name ="gta01-pm-gps",
++};
++
++static struct platform_device gta01_pm_bt_dev = {
++ .name ="gta01-pm-bt",
++};
++#endif
++
++static struct attribute *pcf_sysfs_entries[17] = {
++ &dev_attr_voltage_auto.attr,
++ &dev_attr_voltage_down1.attr,
++ &dev_attr_voltage_down2.attr,
++ &dev_attr_voltage_memldo.attr,
++ &dev_attr_voltage_ldo1.attr,
++ &dev_attr_voltage_ldo2.attr,
++ &dev_attr_voltage_ldo3.attr,
++ &dev_attr_voltage_ldo4.attr,
++ &dev_attr_voltage_ldo5.attr,
++ &dev_attr_voltage_ldo6.attr,
++ &dev_attr_voltage_hcldo.attr,
++ NULL
++};
++
++static struct attribute_group pcf_attr_group = {
++ .name = NULL, /* put in device directory */
++ .attrs = pcf_sysfs_entries,
++};
++
++static void populate_sysfs_group(struct pcf50633_data *pcf)
++{
++ int i = 0;
++ struct attribute **attr;
++
++ for (attr = pcf_sysfs_entries; *attr; attr++)
++ i++;
++
++ if (pcf->pdata->used_features & PCF50633_FEAT_MBC) {
++ pcf_sysfs_entries[i++] = &dev_attr_chgstate.attr;
++ pcf_sysfs_entries[i++] = &dev_attr_chgmode.attr;
++ }
++
++ if (pcf->pdata->used_features & PCF50633_FEAT_CHGCUR)
++ pcf_sysfs_entries[i++] = &dev_attr_chgcur.attr;
++
++ if (pcf->pdata->used_features & PCF50633_FEAT_BATVOLT)
++ pcf_sysfs_entries[i++] = &dev_attr_battvolt.attr;
++
++ if (pcf->pdata->used_features & PCF50633_FEAT_BATTEMP)
++ pcf_sysfs_entries[i++] = &dev_attr_battemp.attr;
++
++}
++
++static int pcf50633_detect(struct i2c_adapter *adapter, int address, int kind)
++{
++ struct i2c_client *new_client;
++ struct pcf50633_data *data;
++ int err = 0;
++ int irq;
++
++ DEBUGP("entering\n");
++ if (!pcf50633_pdev) {
++ printk(KERN_ERR "pcf50633: driver needs a platform_device!\n");
++ return -EIO;
++ }
++
++ irq = platform_get_irq(pcf50633_pdev, 0);
++ if (irq < 0) {
++ dev_err(&pcf50633_pdev->dev, "no irq in platform resources!\n");
++ return -EIO;
++ }
++
++ /* At the moment, we only support one PCF50633 in a system */
++ if (pcf50633_global) {
++ dev_err(&pcf50633_pdev->dev,
++ "currently only one chip supported\n");
++ return -EBUSY;
++ }
++
++ if (!(data = kzalloc(sizeof(*data), GFP_KERNEL)))
++ return -ENOMEM;
++
++ mutex_init(&data->lock);
++ INIT_WORK(&data->work, pcf50633_work);
++ data->irq = irq;
++ data->working = 0;
++ data->onkey_seconds = -1;
++ data->pdata = pcf50633_pdev->dev.platform_data;
++
++ new_client = &data->client;
++ i2c_set_clientdata(new_client, data);
++ new_client->addr = address;
++ new_client->adapter = adapter;
++ new_client->driver = &pcf50633_driver;
++ new_client->flags = 0;
++ strlcpy(new_client->name, "pcf50633", I2C_NAME_SIZE);
++
++ /* now we try to detect the chip */
++
++ /* register with i2c core */
++ if ((err = i2c_attach_client(new_client))) {
++ dev_err(&new_client->dev,
++ "error during i2c_attach_client()\n");
++ goto exit_free;
++ }
++
++ pcf50633_global = data;
++
++ populate_sysfs_group(data);
++
++ err = sysfs_create_group(&new_client->dev.kobj, &pcf_attr_group);
++ if (err) {
++ dev_err(&new_client->dev, "error creating sysfs group\n");
++ goto exit_detach;
++ }
++
++ /* create virtual charger 'device' */
++
++ /* register power off handler with core power management */
++ pm_power_off = &pcf50633_go_standby;
++
++ /* configure interrupt mask */
++ reg_write(data, PCF50633_REG_INT1M, PCF50633_INT1_SECOND);
++ reg_write(data, PCF50633_REG_INT2M, 0x00);
++ reg_write(data, PCF50633_REG_INT3M, 0x00);
++ reg_write(data, PCF50633_REG_INT4M, 0x00);
++ reg_write(data, PCF50633_REG_INT5M, 0x00);
++
++ err = request_irq(irq, pcf50633_irq, SA_INTERRUPT,
++ "pcf50633", data);
++ if (err < 0)
++ goto exit_sysfs;
++
++ set_irq_type(irq, IRQT_FALLING);
++
++ if (enable_irq_wake(irq) < 0)
++ dev_err(&new_client->dev, "IRQ %u cannot be enabled as wake-up"
++ "source in this hardware revision!", irq);
++
++ if (data->pdata->used_features & PCF50633_FEAT_RTC) {
++ data->rtc = rtc_device_register("pcf50633", &new_client->dev,
++ &pcf50633_rtc_ops, THIS_MODULE);
++ if (IS_ERR(data->rtc)) {
++ err = PTR_ERR(data->rtc);
++ goto exit_irq;
++ }
++ }
++
++ if (data->pdata->used_features & PCF50633_FEAT_PWM_BL) {
++ data->backlight = backlight_device_register("pcf50633-bl",
++ &new_client->dev,
++ data,
++ &pcf50633bl_ops);
++ if (!data->backlight)
++ goto exit_rtc;
++ /* FIXME: are we sure we want default == off? */
++ data->backlight->props.max_brightness = 16;
++ data->backlight->props.power = FB_BLANK_UNBLANK;
++ data->backlight->props.brightness = 0;
++ backlight_update_status(data->backlight);
++ }
++
++ data->input_dev = input_allocate_device();
++ if (!data->input_dev)
++ goto exit_pwm;
++
++ data->input_dev->name = "FIC Neo1973 PMU events";
++ data->input_dev->phys = "FIXME";
++ data->input_dev->id.bustype = BUS_I2C;
++ data->input_dev->cdev.dev = &new_client->dev;
++
++ data->input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_PWR);
++ set_bit(KEY_POWER, data->input_dev->keybit);
++ set_bit(KEY_POWER2, data->input_dev->keybit);
++ set_bit(KEY_BATTERY, data->input_dev->keybit);
++
++ input_register_device(data->input_dev);
++
++ apm_get_power_status = pcf50633_get_power_status;
++
++#ifdef CONFIG_MACH_NEO1973_GTA02
++ if (machine_is_neo1973_gta02()) {
++ gta01_pm_gps_dev.dev.parent = &new_client->dev;
++ switch (system_rev) {
++ case GTA02v1_SYSTEM_REV:
++ gta01_pm_bt_dev.dev.parent = &new_client->dev;
++ platform_device_register(>a01_pm_bt_dev);
++ break;
++ }
++ platform_device_register(>a01_pm_gps_dev);
++ }
++#endif
++
++ return 0;
++exit_pwm:
++ if (data->pdata->used_features & PCF50633_FEAT_PWM_BL)
++ backlight_device_unregister(data->backlight);
++exit_rtc:
++ if (data->pdata->used_features & PCF50633_FEAT_RTC)
++ rtc_device_unregister(pcf50633_global->rtc);
++exit_irq:
++ free_irq(pcf50633_global->irq, pcf50633_global);
++exit_sysfs:
++ pm_power_off = NULL;
++ sysfs_remove_group(&new_client->dev.kobj, &pcf_attr_group);
++exit_detach:
++ i2c_detach_client(new_client);
++exit_free:
++ kfree(data);
++ pcf50633_global = NULL;
++ return err;
++}
++
++static int pcf50633_attach_adapter(struct i2c_adapter *adapter)
++{
++ DEBUGP("entering, calling i2c_probe\n");
++ return i2c_probe(adapter, &addr_data, &pcf50633_detect);
++}
++
++static int pcf50633_detach_client(struct i2c_client *client)
++{
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++
++ DEBUGP("entering\n");
++
++ apm_get_power_status = NULL;
++ input_unregister_device(pcf->input_dev);
++
++ if (pcf->pdata->used_features & PCF50633_FEAT_PWM_BL)
++ backlight_device_unregister(pcf->backlight);
++
++ if (pcf->pdata->used_features & PCF50633_FEAT_RTC)
++ rtc_device_unregister(pcf->rtc);
++
++ free_irq(pcf->irq, pcf);
++
++ sysfs_remove_group(&client->dev.kobj, &pcf_attr_group);
++
++ pm_power_off = NULL;
++
++ kfree(pcf);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++#define INT1M_RESUMERS (PCF50633_INT1_ADPINS | \
++ PCF50633_INT1_ADPREM | \
++ PCF50633_INT1_USBINS | \
++ PCF50633_INT1_USBREM | \
++ PCF50633_INT1_ALARM)
++#define INT2M_RESUMERS (PCF50633_INT2_ONKEYF)
++#define INT3M_RESUMERS (PCF50633_INT3_BATFULL | \
++ PCF50633_INT3_CHGHALT | \
++ PCF50633_INT3_THLIMON | \
++ PCF50633_INT3_THLIMOFF | \
++ PCF50633_INT3_USBLIMON | \
++ PCF50633_INT3_USBLIMOFF | \
++ PCF50633_INT3_ONKEY1S)
++#define INT4M_RESUMERS (PCF50633_INT4_LOWSYS | \
++ PCF50633_INT4_LOWBAT | \
++ PCF50633_INT4_HIGHTMP)
++#define INT5M_RESUMERS (0)
++
++static int pcf50633_suspend(struct device *dev, pm_message_t state)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++ int i;
++
++ /* The general idea is to power down all unused power supplies,
++ * and then mask all PCF50606 interrup sources but EXTONR, ONKEYF
++ * and ALARM */
++
++ mutex_lock(&pcf->lock);
++
++ /* Save all registers that don't "survive" standby state */
++ pcf->standby_regs.ooctim2 = __reg_read(pcf, PCF50633_REG_OOCTIM2);
++ pcf->standby_regs.autoout = __reg_read(pcf, PCF50633_REG_AUTOOUT);
++ pcf->standby_regs.autoena = __reg_read(pcf, PCF50633_REG_AUTOENA);
++ pcf->standby_regs.automxc = __reg_read(pcf, PCF50633_REG_AUTOMXC);
++ pcf->standby_regs.down1out = __reg_read(pcf, PCF50633_REG_DOWN1OUT);
++ pcf->standby_regs.down1mxc = __reg_read(pcf, PCF50633_REG_DOWN1MXC);
++ pcf->standby_regs.down2out = __reg_read(pcf, PCF50633_REG_DOWN2OUT);
++ pcf->standby_regs.down2ena = __reg_read(pcf, PCF50633_REG_DOWN2ENA);
++ pcf->standby_regs.memldoout = __reg_read(pcf, PCF50633_REG_MEMLDOOUT);
++ pcf->standby_regs.memldoena = __reg_read(pcf, PCF50633_REG_MEMLDOENA);
++ pcf->standby_regs.ledout = __reg_read(pcf, PCF50633_REG_LEDOUT);
++ pcf->standby_regs.ledena = __reg_read(pcf, PCF50633_REG_LEDENA);
++ pcf->standby_regs.leddim = __reg_read(pcf, PCF50633_REG_LEDDIM);
++ /* FIXME: one big read? */
++ for (i = 0; i < 7; i++) {
++ u_int8_t reg_out = PCF50633_REG_LDO1OUT + 2*i;
++ pcf->standby_regs.ldo[i].out = __reg_read(pcf, reg_out);
++ pcf->standby_regs.ldo[i].ena = __reg_read(pcf, reg_out+1);
++ }
++
++ /* switch off power supplies that are not needed during suspend */
++ for (i = 0; i < __NUM_PCF50633_REGULATORS; i++) {
++ if (!(pcf->pdata->rails[i].flags & PMU_VRAIL_F_SUSPEND_ON)) {
++ u_int8_t tmp;
++
++ DEBUGP("disabling pcf50633 regulator %u\n", i);
++ /* we cannot use pcf50633_onoff_set() because we're
++ * already under the mutex */
++ tmp = __reg_read(pcf, regulator_registers[i]+1);
++ tmp &= 0xfe;
++ __reg_write(pcf, regulator_registers[i]+1, tmp);
++ }
++ }
++
++ pcf->standby_regs.int1m = __reg_read(pcf, PCF50633_REG_INT1M);
++ pcf->standby_regs.int2m = __reg_read(pcf, PCF50633_REG_INT2M);
++ pcf->standby_regs.int3m = __reg_read(pcf, PCF50633_REG_INT3M);
++ pcf->standby_regs.int4m = __reg_read(pcf, PCF50633_REG_INT4M);
++ pcf->standby_regs.int5m = __reg_read(pcf, PCF50633_REG_INT5M);
++ __reg_write(pcf, PCF50633_REG_INT1M, ~INT1M_RESUMERS & 0xff);
++ __reg_write(pcf, PCF50633_REG_INT2M, ~INT2M_RESUMERS & 0xff);
++ __reg_write(pcf, PCF50633_REG_INT3M, ~INT3M_RESUMERS & 0xff);
++ __reg_write(pcf, PCF50633_REG_INT4M, ~INT4M_RESUMERS & 0xff);
++ __reg_write(pcf, PCF50633_REG_INT5M, ~INT5M_RESUMERS & 0xff);
++
++ mutex_unlock(&pcf->lock);
++
++ return 0;
++}
++
++static int pcf50633_resume(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++ int i;
++
++ mutex_lock(&pcf->lock);
++
++ /* Resume all saved registers that don't "survive" standby state */
++ __reg_write(pcf, PCF50633_REG_INT1M, pcf->standby_regs.int1m);
++ __reg_write(pcf, PCF50633_REG_INT2M, pcf->standby_regs.int2m);
++ __reg_write(pcf, PCF50633_REG_INT3M, pcf->standby_regs.int3m);
++ __reg_write(pcf, PCF50633_REG_INT4M, pcf->standby_regs.int4m);
++ __reg_write(pcf, PCF50633_REG_INT5M, pcf->standby_regs.int5m);
++
++ __reg_write(pcf, PCF50633_REG_OOCTIM2, pcf->standby_regs.ooctim2);
++ __reg_write(pcf, PCF50633_REG_AUTOOUT, pcf->standby_regs.autoout);
++ __reg_write(pcf, PCF50633_REG_AUTOMXC, pcf->standby_regs.automxc);
++ __reg_write(pcf, PCF50633_REG_DOWN1OUT, pcf->standby_regs.down1out);
++ __reg_write(pcf, PCF50633_REG_DOWN1MXC, pcf->standby_regs.down1mxc);
++ __reg_write(pcf, PCF50633_REG_DOWN2OUT, pcf->standby_regs.down2out);
++ __reg_write(pcf, PCF50633_REG_DOWN2ENA, pcf->standby_regs.down2ena);
++ __reg_write(pcf, PCF50633_REG_MEMLDOOUT, pcf->standby_regs.memldoout);
++ __reg_write(pcf, PCF50633_REG_MEMLDOENA, pcf->standby_regs.memldoena);
++ __reg_write(pcf, PCF50633_REG_LEDOUT, pcf->standby_regs.ledout);
++ __reg_write(pcf, PCF50633_REG_LEDENA, pcf->standby_regs.ledena);
++ __reg_write(pcf, PCF50633_REG_LEDDIM, pcf->standby_regs.leddim);
++ /* FIXME: one big read? */
++ for (i = 0; i < 7; i++) {
++ u_int8_t reg_out = PCF50633_REG_LDO1OUT + 2*i;
++ __reg_write(pcf, reg_out, pcf->standby_regs.ldo[i].out);
++ __reg_write(pcf, reg_out+1, pcf->standby_regs.ldo[i].ena);
++ }
++
++ mutex_unlock(&pcf->lock);
++
++ return 0;
++}
++#else
++#define pcf50633_suspend NULL
++#define pcf50633_resume NULL
++#endif
++
++static struct i2c_driver pcf50633_driver = {
++ .driver = {
++ .name = "pcf50633",
++ .suspend= &pcf50633_suspend,
++ .resume = &pcf50633_resume,
++ },
++ .id = I2C_DRIVERID_PCF50633,
++ .attach_adapter = &pcf50633_attach_adapter,
++ .detach_client = &pcf50633_detach_client,
++};
++
++/* platform driver, since i2c devices don't have platform_data */
++static int __init pcf50633_plat_probe(struct platform_device *pdev)
++{
++ struct pcf50633_platform_data *pdata = pdev->dev.platform_data;
++
++ if (!pdata)
++ return -ENODEV;
++
++ pcf50633_pdev = pdev;
++
++ return 0;
++}
++
++static int pcf50633_plat_remove(struct platform_device *pdev)
++{
++ return 0;
++}
++
++static struct platform_driver pcf50633_plat_driver = {
++ .probe = pcf50633_plat_probe,
++ .remove = pcf50633_plat_remove,
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = "pcf50633",
++ },
++};
++
++static int __init pcf50633_init(void)
++{
++ int rc;
++
++ if (!(rc = platform_driver_register(&pcf50633_plat_driver)))
++ rc = i2c_add_driver(&pcf50633_driver);
++
++ return rc;
++}
++
++static void pcf50633_exit(void)
++{
++ i2c_del_driver(&pcf50633_driver);
++ platform_driver_unregister(&pcf50633_plat_driver);
++}
++
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
++MODULE_LICENSE("GPL");
++
++module_init(pcf50633_init);
++module_exit(pcf50633_exit);
+Index: linux-2.6.21.3-moko/include/linux/pcf50633.h
+===================================================================
+--- /dev/null
++++ linux-2.6.21.3-moko/include/linux/pcf50633.h
+@@ -0,0 +1,103 @@
++#ifndef _LINUX_PCF50633_H
++#define _LINUX_PCF50633_H
++
++/* public in-kernel pcf50633 api */
++enum pcf50633_regulator_id {
++ PCF50633_REGULATOR_AUTO,
++ PCF50633_REGULATOR_DOWN1,
++ PCF50633_REGULATOR_DOWN2,
++ PCF50633_REGULATOR_MEMLDO,
++ PCF50633_REGULATOR_LDO1,
++ PCF50633_REGULATOR_LDO2,
++ PCF50633_REGULATOR_LDO3,
++ PCF50633_REGULATOR_LDO4,
++ PCF50633_REGULATOR_LDO5,
++ PCF50633_REGULATOR_LDO6,
++ PCF50633_REGULATOR_HCLDO,
++ __NUM_PCF50633_REGULATORS
++};
++
++struct pcf50633_data;
++extern struct pcf50633_data *pcf50633_global;
++
++extern void
++pcf50633_go_standby(void);
++
++extern void
++pcf50633_gpo0_set(struct pcf50633_data *pcf, int on);
++
++extern int
++pcf50633_gpo0_get(struct pcf50633_data *pcf);
++
++extern int
++pcf50633_voltage_set(struct pcf50633_data *pcf,
++ enum pcf50633_regulator_id reg,
++ unsigned int millivolts);
++extern unsigned int
++pcf50633_voltage_get(struct pcf50633_data *pcf,
++ enum pcf50633_regulator_id reg);
++extern int
++pcf50633_onoff_get(struct pcf50633_data *pcf,
++ enum pcf50633_regulator_id reg);
++
++extern int
++pcf50633_onoff_set(struct pcf50633_data *pcf,
++ enum pcf50633_regulator_id reg, int on);
++
++extern void
++pcf50633_charge_fast(struct pcf50633_data *pcf, int on);
++
++/* FIXME: sharded with pcf50606 */
++#define PMU_VRAIL_F_SUSPEND_ON 0x00000001 /* Remains on during suspend */
++#define PMU_VRAIL_F_UNUSED 0x00000002 /* This rail is not used */
++struct pmu_voltage_rail {
++ char *name;
++ unsigned int flags;
++ struct {
++ unsigned int init;
++ unsigned int max;
++ } voltage;
++};
++
++enum pmu_event {
++ PMU_EVT_NONE,
++ PMU_EVT_INSERT,
++ PMU_EVT_REMOVE,
++ PMU_EVT_USB_INSERT,
++ PMU_EVT_USB_REMOVE,
++ __NUM_PMU_EVTS
++};
++
++typedef int pmu_cb(struct device *dev, unsigned int feature,
++ enum pmu_event event);
++
++#define PCF50633_FEAT_EXTON 0x00000001 /* not yet supported */
++#define PCF50633_FEAT_MBC 0x00000002
++#define PCF50633_FEAT_BBC 0x00000004 /* not yet supported */
++#define PCF50633_FEAT_RTC 0x00000040
++#define PCF50633_FEAT_CHGCUR 0x00000100
++#define PCF50633_FEAT_BATVOLT 0x00000200
++#define PCF50633_FEAT_BATTEMP 0x00000400
++#define PCF50633_FEAT_PWM_BL 0x00000800
++
++struct pcf50633_platform_data {
++ /* general */
++ unsigned int used_features;
++ unsigned int onkey_seconds_required;
++
++ /* voltage regulator related */
++ struct pmu_voltage_rail rails[__NUM_PCF50633_REGULATORS];
++ unsigned int used_regulators;
++
++ /* charger related */
++ unsigned int r_fix_batt;
++ unsigned int r_fix_batt_par;
++ unsigned int r_sense_milli;
++
++ struct {
++ u_int8_t mbcc3; /* charger voltage / current */
++ } charger;
++ pmu_cb *cb;
++};
++
++#endif /* _PCF50633_H */
+Index: linux-2.6.21.3-moko/include/linux/i2c-id.h
+===================================================================
+--- linux-2.6.21.3-moko.orig/include/linux/i2c-id.h
++++ linux-2.6.21.3-moko/include/linux/i2c-id.h
+@@ -161,6 +161,7 @@
+ #define I2C_DRIVERID_OV7670 1048 /* Omnivision 7670 camera */
+ #define I2C_DRIVERID_PCF50606 1049
+ #define I2C_DRIVERID_TSL256X 1050
++#define I2C_DRIVERID_PCF50633 1051
+
+ /*
+ * ---- Adapter types ----------------------------------------------------
+Index: linux-2.6.21.3-moko/drivers/i2c/chips/pcf50633.h
+===================================================================
+--- /dev/null
++++ linux-2.6.21.3-moko/drivers/i2c/chips/pcf50633.h
+@@ -0,0 +1,402 @@
++#ifndef _PCF50633_H
++#define _PCF50633_H
++
++/* Philips PCF50633 Power Managemnt Unit (PMU) driver
++ * (C) 2006-2007 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ *
++ */
++
++enum pfc50633_regs {
++ PCF50633_REG_VERSION = 0x00,
++ PCF50633_REG_VARIANT = 0x01,
++ PCF50633_REG_INT1 = 0x02, /* Interrupt Status */
++ PCF50633_REG_INT2 = 0x03, /* Interrupt Status */
++ PCF50633_REG_INT3 = 0x04, /* Interrupt Status */
++ PCF50633_REG_INT4 = 0x05, /* Interrupt Status */
++ PCF50633_REG_INT5 = 0x06, /* Interrupt Status */
++ PCF50633_REG_INT1M = 0x07, /* Interrupt Mask */
++ PCF50633_REG_INT2M = 0x08, /* Interrupt Mask */
++ PCF50633_REG_INT3M = 0x09, /* Interrupt Mask */
++ PCF50633_REG_INT4M = 0x0a, /* Interrupt Mask */
++ PCF50633_REG_INT5M = 0x0b, /* Interrupt Mask */
++ PCF50633_REG_OOCSHDWN = 0x0c,
++ PCF50633_REG_OOCWAKE = 0x0d,
++ PCF50633_REG_OOCTIM1 = 0x0e,
++ PCF50633_REG_OOCTIM2 = 0x0f,
++ PCF50633_REG_OOCMODE = 0x10,
++ PCF50633_REG_OOCCTL = 0x11,
++ PCF50633_REG_OOCSTAT = 0x12,
++ PCF50633_REG_GPIOCTL = 0x13,
++ PCF50633_REG_GPIO1CFG = 0x14,
++ PCF50633_REG_GPIO2CFG = 0x15,
++ PCF50633_REG_GPIO3CFG = 0x16,
++ PCF50633_REG_GPOCFG = 0x17,
++ PCF50633_REG_BVMCTL = 0x18,
++ PCF50633_REG_SVMCTL = 0x19,
++ PCF50633_REG_AUTOOUT = 0x1a,
++ PCF50633_REG_AUTOENA = 0x1b,
++ PCF50633_REG_AUTOCTL = 0x1c,
++ PCF50633_REG_AUTOMXC = 0x1d,
++ PCF50633_REG_DOWN1OUT = 0x1e,
++ PCF50633_REG_DOWN1ENA = 0x1f,
++ PCF50633_REG_DOWN1CTL = 0x20,
++ PCF50633_REG_DOWN1MXC = 0x21,
++ PCF50633_REG_DOWN2OUT = 0x22,
++ PCF50633_REG_DOWN2ENA = 0x23,
++ PCF50633_REG_DOWN2CTL = 0x24,
++ PCF50633_REG_DOWN2MXC = 0x25,
++ PCF50633_REG_MEMLDOOUT = 0x26,
++ PCF50633_REG_MEMLDOENA = 0x27,
++ PCF50633_REG_LEDOUT = 0x28,
++ PCF50633_REG_LEDENA = 0x29,
++ PCF50633_REG_LEDCTL = 0x2a,
++ PCF50633_REG_LEDDIM = 0x2b,
++ /* reserved */
++ PCF50633_REG_LDO1OUT = 0x2d,
++ PCF50633_REG_LDO1ENA = 0x2e,
++ PCF50633_REG_LDO2OUT = 0x2f,
++ PCF50633_REG_LDO2ENA = 0x30,
++ PCF50633_REG_LDO3OUT = 0x31,
++ PCF50633_REG_LDO3ENA = 0x32,
++ PCF50633_REG_LDO4OUT = 0x33,
++ PCF50633_REG_LDO4ENA = 0x34,
++ PCF50633_REG_LDO5OUT = 0x35,
++ PCF50633_REG_LDO5ENA = 0x36,
++ PCF50633_REG_LDO6OUT = 0x37,
++ PCF50633_REG_LDO6ENA = 0x38,
++ PCF50633_REG_HCLDOOUT = 0x39,
++ PCF50633_REG_HCLDOENA = 0x3a,
++ PCF50633_REG_STBYCTL1 = 0x3b,
++ PCF50633_REG_STBYCTL2 = 0x3c,
++ PCF50633_REG_DEBPF1 = 0x3d,
++ PCF50633_REG_DEBPF2 = 0x3e,
++ PCF50633_REG_DEBPF3 = 0x3f,
++ PCF50633_REG_HCLDOOVL = 0x40,
++ PCF50633_REG_DCDCSTAT = 0x41,
++ PCF50633_REG_LDOSTAT = 0x42,
++ PCF50633_REG_MBCC1 = 0x43,
++ PCF50633_REG_MBCC2 = 0x44,
++ PCF50633_REG_MBCC3 = 0x45,
++ PCF50633_REG_MBCC4 = 0x46,
++ PCF50633_REG_MBCC5 = 0x47,
++ PCF50633_REG_MBCC6 = 0x48,
++ PCF50633_REG_MBCC7 = 0x49,
++ PCF50633_REG_MBCC8 = 0x4a,
++ PCF50633_REG_MBCS1 = 0x4b,
++ PCF50633_REG_MBCS2 = 0x4c,
++ PCF50633_REG_MBCS3 = 0x4d,
++ PCF50633_REG_BBCCTL = 0x4e,
++ PCF50633_REG_ALMGAIN = 0x4f,
++ PCF50633_REG_ALMDATA = 0x50,
++ /* reserved */
++ PCF50633_REG_ADCC3 = 0x52,
++ PCF50633_REG_ADCC2 = 0x53,
++ PCF50633_REG_ADCC1 = 0x54,
++ PCF50633_REG_ADCS1 = 0x55,
++ PCF50633_REG_ADCS2 = 0x56,
++ PCF50633_REG_ADCS3 = 0x57,
++ /* reserved */
++ PCF50633_REG_RTCSC = 0x59, /* Second */
++ PCF50633_REG_RTCMN = 0x5a, /* Minute */
++ PCF50633_REG_RTCHR = 0x5b, /* Hour */
++ PCF50633_REG_RTCWD = 0x5c, /* Weekday */
++ PCF50633_REG_RTCDT = 0x5d, /* Day */
++ PCF50633_REG_RTCMT = 0x5e, /* Month */
++ PCF50633_REG_RTCYR = 0x5f, /* Year */
++ PCF50633_REG_RTCSCA = 0x60, /* Alarm Second */
++ PCF50633_REG_RTCMNA = 0x61, /* Alarm Minute */
++ PCF50633_REG_RTCHRA = 0x62, /* Alarm Hour */
++ PCF50633_REG_RTCWDA = 0x63, /* Alarm Weekday */
++ PCF50633_REG_RTCDTA = 0x64, /* Alarm Day */
++ PCF50633_REG_RTCMTA = 0x65, /* Alarm Month */
++ PCF50633_REG_RTCYRA = 0x66, /* Alarm Year */
++
++ PCF50633_REG_MEMBYTE0 = 0x67,
++ PCF50633_REG_MEMBYTE1 = 0x68,
++ PCF50633_REG_MEMBYTE2 = 0x69,
++ PCF50633_REG_MEMBYTE3 = 0x6a,
++ PCF50633_REG_MEMBYTE4 = 0x6b,
++ PCF50633_REG_MEMBYTE5 = 0x6c,
++ PCF50633_REG_MEMBYTE6 = 0x6d,
++ PCF50633_REG_MEMBYTE7 = 0x6e,
++ /* reserved */
++ PCF50633_REG_DCDCPFM = 0x84,
++ __NUM_PCF50633_REGS
++};
++
++enum pcf50633_reg_int1 {
++ PCF50633_INT1_ADPINS = 0x01, /* Adapter inserted */
++ PCF50633_INT1_ADPREM = 0x02, /* Adapter removed */
++ PCF50633_INT1_USBINS = 0x04, /* USB inserted */
++ PCF50633_INT1_USBREM = 0x08, /* USB removed */
++ /* reserved */
++ PCF50633_INT1_ALARM = 0x40, /* RTC alarm time is reached */
++ PCF50633_INT1_SECOND = 0x80, /* RTC periodic second interrupt */
++};
++
++enum pcf50633_reg_int2 {
++ PCF50633_INT2_ONKEYR = 0x01, /* ONKEY rising edge */
++ PCF50633_INT2_ONKEYF = 0x02, /* ONKEY falling edge */
++ PCF50633_INT2_EXTON1R = 0x04, /* EXTON1 rising edge */
++ PCF50633_INT2_EXTON1F = 0x08, /* EXTON1 falling edge */
++ PCF50633_INT2_EXTON2R = 0x10, /* EXTON2 rising edge */
++ PCF50633_INT2_EXTON2F = 0x20, /* EXTON2 falling edge */
++ PCF50633_INT2_EXTON3R = 0x40, /* EXTON3 rising edge */
++ PCF50633_INT2_EXTON3F = 0x80, /* EXTON3 falling edge */
++};
++
++enum pcf50633_reg_int3 {
++ PCF50633_INT3_BATFULL = 0x01, /* Battery full */
++ PCF50633_INT3_CHGHALT = 0x02, /* Charger halt */
++ PCF50633_INT3_THLIMON = 0x04,
++ PCF50633_INT3_THLIMOFF = 0x08,
++ PCF50633_INT3_USBLIMON = 0x10,
++ PCF50633_INT3_USBLIMOFF = 0x20,
++ PCF50633_INT3_ADCRDY = 0x40, /* ADC conversion finished */
++ PCF50633_INT3_ONKEY1S = 0x80, /* ONKEY pressed 1 second */
++};
++
++enum pcf50633_reg_int4 {
++ PCF50633_INT4_LOWSYS = 0x01,
++ PCF50633_INT4_LOWBAT = 0x02,
++ PCF50633_INT4_HIGHTMP = 0x04,
++ PCF50633_INT4_AUTOPWRFAIL = 0x08,
++ PCF50633_INT4_DWN1PWRFAIL = 0x10,
++ PCF50633_INT4_DWN2PWRFAIL = 0x20,
++ PCF50633_INT4_LEDPWRFAIL = 0x40,
++ PCF50633_INT4_LEDOVP = 0x80,
++};
++
++enum pcf50633_reg_int5 {
++ PCF50633_INT5_LDO1PWRFAIL = 0x01,
++ PCF50633_INT5_LDO2PWRFAIL = 0x02,
++ PCF50633_INT5_LDO3PWRFAIL = 0x04,
++ PCF50633_INT5_LDO4PWRFAIL = 0x08,
++ PCF50633_INT5_LDO5PWRFAIL = 0x10,
++ PCF50633_INT5_LDO6PWRFAIL = 0x20,
++ PCF50633_INT5_HCLDOPWRFAIL = 0x40,
++ PCF50633_INT5_HCLDOOVL = 0x80,
++};
++
++enum pcf50633_reg_oocshdwn {
++ PCF50633_OOCSHDWN_GOSTDBY = 0x01,
++ PCF50633_OOCSHDWN_TOTRST = 0x04,
++ PCF50633_OOCSHDWN_COLDBOOT = 0x08,
++};
++
++enum pcf50633_reg_oocwake {
++ PCF50633_OOCWAKE_ONKEY = 0x01,
++ PCF50633_OOCWAKE_EXTON1 = 0x02,
++ PCF50633_OOCWAKE_EXTON2 = 0x04,
++ PCF50633_OOCWAKE_EXTON3 = 0x08,
++ PCF50633_OOCWAKE_RTC = 0x10,
++ /* reserved */
++ PCF50633_OOCWAKE_USB = 0x40,
++ PCF50633_OOCWAKE_ADP = 0x80,
++};
++
++enum pcf50633_reg_mbcc1 {
++ PCF50633_MBCC1_CHGENA = 0x01, /* Charger enable */
++ PCF50633_MBCC1_AUTOSTOP = 0x02,
++ PCF50633_MBCC1_AUTORES = 0x04, /* automatic resume */
++ PCF50633_MBCC1_RESUME = 0x08, /* explicit resume cmd */
++ PCF50633_MBCC1_RESTART = 0x10, /* restart charging */
++ PCF50633_MBCC1_PREWDTIME_60M = 0x20, /* max. precharging time */
++ PCF50633_MBCC1_WDTIME_1H = 0x00,
++ PCF50633_MBCC1_WDTIME_2H = 0x40,
++ PCF50633_MBCC1_WDTIME_4H = 0x80,
++ PCF50633_MBCC1_WDTIME_6H = 0xc0,
++};
++#define PCF50633_MBCC1_WDTIME_MASK 0xc0
++
++enum pcf50633_reg_mbcc2 {
++ PCF50633_MBCC2_VBATCOND_2V7 = 0x00,
++ PCF50633_MBCC2_VBATCOND_2V85 = 0x01,
++ PCF50633_MBCC2_VBATCOND_3V0 = 0x02,
++ PCF50633_MBCC2_VBATCOND_3V15 = 0x03,
++ PCF50633_MBCC2_VMAX_4V = 0x00,
++ PCF50633_MBCC2_VMAX_4V20 = 0x28,
++ PCF50633_MBCC2_VRESDEBTIME_64S = 0x80, /* debounce time (32/64sec) */
++};
++#define PCF50633_MBCC2_VBATCOND_MASK 0x03
++#define PCF50633_MBCC2_VMAX_MASK 0x3c
++
++enum pcf50633_reg_adcc1 {
++ PCF50633_ADCC1_ADCSTART = 0x01,
++ PCF50633_ADCC1_RES_10BIT = 0x02,
++ PCF50633_ADCC1_AVERAGE_NO = 0x00,
++ PCF50633_ADCC1_AVERAGE_4 = 0x04,
++ PCF50633_ADCC1_AVERAGE_8 = 0x08,
++ PCF50633_ADCC1_AVERAGE_16 = 0x0c,
++
++ PCF50633_ADCC1_MUX_BATSNS_RES = 0x00,
++ PCF50633_ADCC1_MUX_BATSNS_SUBTR = 0x10,
++ PCF50633_ADCC1_MUX_ADCIN2_RES = 0x20,
++ PCF50633_ADCC1_MUX_ADCIN2_SUBTR = 0x30,
++ PCF50633_ADCC1_MUX_BATTEMP = 0x60,
++ PCF50633_ADCC1_MUX_ADCIN1 = 0x70,
++};
++#define PCF50633_ADCC1_AVERAGE_MASK 0x0c
++#define PCF50633_ADCC1_ADCMUX_MASK 0xf0
++
++enum pcf50633_reg_adcc2 {
++ PCF50633_ADCC2_RATIO_NONE = 0x00,
++ PCF50633_ADCC2_RATIO_BATTEMP = 0x01,
++ PCF50633_ADCC2_RATIO_ADCIN1 = 0x02,
++ PCF50633_ADCC2_RATIO_BOTH = 0x03,
++ PCF50633_ADCC2_RATIOSETTL_100US = 0x04,
++};
++#define PCF50633_ADCC2_RATIO_MASK 0x03
++
++enum pcf50633_reg_adcc3 {
++ PCF50633_ADCC3_ACCSW_EN = 0x01,
++ PCF50633_ADCC3_NTCSW_EN = 0x04,
++ PCF50633_ADCC3_RES_DIV_TWO = 0x10,
++ PCF50633_ADCC3_RES_DIV_THREE = 0x00,
++};
++
++enum pcf50633_reg_adcs3 {
++ PCF50633_ADCS3_REF_NTCSW = 0x00,
++ PCF50633_ADCS3_REF_ACCSW = 0x10,
++ PCF50633_ADCS3_REF_2V0 = 0x20,
++ PCF50633_ADCS3_REF_VISA = 0x30,
++ PCF50633_ADCS3_REF_2V0_2 = 0x70,
++ PCF50633_ADCS3_ADCRDY = 0x80,
++};
++#define PCF50633_ADCS3_ADCDAT1L_MASK 0x03
++#define PCF50633_ADCS3_ADCDAT2L_MASK 0x0c
++#define PCF50633_ADCS3_ADCDAT2L_SHIFT 2
++#define PCF50633_ASCS3_REF_MASK 0x70
++
++enum pcf50633_regulator_enable {
++ PCF50633_REGULATOR_ON = 0x01,
++ PCF50633_REGULATOR_ON_GPIO1 = 0x02,
++ PCF50633_REGULATOR_ON_GPIO2 = 0x04,
++ PCF50633_REGULATOR_ON_GPIO3 = 0x08,
++};
++#define PCF50633_REGULATOR_ON_MASK 0x0f
++
++enum pcf50633_regulator_phase {
++ PCF50633_REGULATOR_ACTPH1 = 0x00,
++ PCF50633_REGULATOR_ACTPH2 = 0x10,
++ PCF50633_REGULATOR_ACTPH3 = 0x20,
++ PCF50633_REGULATOR_ACTPH4 = 0x30,
++};
++#define PCF50633_REGULATOR_ACTPH_MASK 0x30
++
++enum pcf50633_reg_gpocfg {
++ PCF50633_GPOCFG_GPOSEL_0 = 0x00,
++ PCF50633_GPOCFG_GPOSEL_LED_NFET = 0x01,
++ PCF50633_GPOCFG_GPOSEL_SYSxOK = 0x02,
++ PCF50633_GPOCFG_GPOSEL_CLK32K = 0x03,
++ PCF50633_GPOCFG_GPOSEL_ADAPUSB = 0x04,
++ PCF50633_GPOCFG_GPOSEL_USBxOK = 0x05,
++ PCF50633_GPOCFG_GPOSEL_ACTPH4 = 0x06,
++ PCF50633_GPOCFG_GPOSEL_1 = 0x07,
++ PCF50633_GPOCFG_GPOSEL_INVERSE = 0x08,
++};
++#define PCF50633_GPOCFG_GPOSEL_MASK 0x07
++
++#if 0
++enum pcf50633_reg_mbcc1 {
++ PCF50633_MBCC1_CHGENA = 0x01,
++ PCF50633_MBCC1_AUTOSTOP = 0x02,
++ PCF50633_MBCC1_AUTORES = 0x04,
++ PCF50633_MBCC1_RESUME = 0x08,
++ PCF50633_MBCC1_RESTART = 0x10,
++ PCF50633_MBCC1_PREWDTIME_30MIN = 0x00,
++ PCF50633_MBCC1_PREWDTIME_60MIN = 0x20,
++ PCF50633_MBCC1_WDTIME_2HRS = 0x40,
++ PCF50633_MBCC1_WDTIME_4HRS = 0x80,
++ PCF50633_MBCC1_WDTIME_6HRS = 0xc0,
++};
++
++enum pcf50633_reg_mbcc2 {
++ PCF50633_MBCC2_VBATCOND_2V7 = 0x00,
++ PCF50633_MBCC2_VBATCOND_2V85 = 0x01,
++ PCF50633_MBCC2_VBATCOND_3V0 = 0x02,
++ PCF50633_MBCC2_VBATCOND_3V15 = 0x03,
++ PCF50633_MBCC2_VRESDEBTIME_64S = 0x80,
++};
++#define PCF50633_MBCC2_VMAX_MASK 0x3c
++#endif
++
++enum pcf50633_reg_mbcc7 {
++ PCF50633_MBCC7_USB_100mA = 0x00,
++ PCF50633_MBCC7_USB_500mA = 0x01,
++ PCF50633_MBCC7_USB_1000mA = 0x02,
++ PCF50633_MBCC7_USB_SUSPEND = 0x03,
++ PCF50633_MBCC7_BATTEMP_EN = 0x04,
++ PCF50633_MBCC7_BATSYSIMAX_1A6 = 0x00,
++ PCF50633_MBCC7_BATSYSIMAX_1A8 = 0x40,
++ PCF50633_MBCC7_BATSYSIMAX_2A0 = 0x80,
++ PCF50633_MBCC7_BATSYSIMAX_2A2 = 0xc0,
++};
++#define PCF56033_MBCC7_USB_MASK 0x03
++
++enum pcf50633_reg_mbcc8 {
++ PCF50633_MBCC8_USBENASUS = 0x10,
++};
++
++enum pcf50633_reg_mbcs1 {
++ PCF50633_MBCS1_USBPRES = 0x01,
++ PCF50633_MBCS1_USBOK = 0x02,
++ PCF50633_MBCS1_ADAPTPRES = 0x04,
++ PCF50633_MBCS1_ADAPTOK = 0x08,
++ PCF50633_MBCS1_TBAT_OK = 0x00,
++ PCF50633_MBCS1_TBAT_ABOVE = 0x10,
++ PCF50633_MBCS1_TBAT_BELOW = 0x20,
++ PCF50633_MBCS1_TBAT_UNDEF = 0x30,
++ PCF50633_MBCS1_PREWDTEXP = 0x40,
++ PCF50633_MBCS1_WDTEXP = 0x80,
++};
++
++enum pcf50633_reg_mbcs2_mbcmod {
++ PCF50633_MBCS2_MBC_PLAY = 0x00,
++ PCF50633_MBCS2_MBC_USB_PRE = 0x01,
++ PCF50633_MBCS2_MBC_USB_PRE_WAIT = 0x02,
++ PCF50633_MBCS2_MBC_USB_FAST = 0x03,
++ PCF50633_MBCS2_MBC_USB_FAST_WAIT= 0x04,
++ PCF50633_MBCS2_MBC_USB_SUSPEND = 0x05,
++ PCF50633_MBCS2_MBC_ADP_PRE = 0x06,
++ PCF50633_MBCS2_MBC_ADP_PRE_WAIT = 0x07,
++ PCF50633_MBCS2_MBC_ADP_FAST = 0x08,
++ PCF50633_MBCS2_MBC_ADP_FAST_WAIT= 0x09,
++ PCF50633_MBCS2_MBC_BAT_FULL = 0x0a,
++ PCF50633_MBCS2_MBC_HALT = 0x0b,
++};
++#define PCF50633_MBCS2_MBC_MASK 0x0f
++enum pcf50633_reg_mbcs2_chgstat {
++ PCF50633_MBCS2_CHGS_NONE = 0x00,
++ PCF50633_MBCS2_CHGS_ADAPTER = 0x10,
++ PCF50633_MBCS2_CHGS_USB = 0x20,
++ PCF50633_MBCS2_CHGS_BOTH = 0x30,
++};
++#define PCF50633_MBCS2_RESSTAT_AUTO 0x40
++
++enum pcf50633_reg_mbcs3 {
++ PCF50633_MBCS3_USBLIM_PLAY = 0x01,
++ PCF50633_MBCS3_USBLIM_CGH = 0x02,
++ PCF50633_MBCS3_TLIM_PLAY = 0x04,
++ PCF50633_MBCS3_TLIM_CHG = 0x08,
++ PCF50633_MBCS3_ILIM = 0x10, /* 1: Ibat > Icutoff */
++ PCF50633_MBCS3_VLIM = 0x20, /* 1: Vbat == Vmax */
++ PCF50633_MBCS3_VBATSTAT = 0x40, /* 1: Vbat > Vbatcond */
++ PCF50633_MBCS3_VRES = 0x80, /* 1: Vbat > Vth(RES) */
++};
++
++/* this is to be provided by the board implementation */
++extern const u_int8_t pcf50633_initial_regs[__NUM_PCF50633_REGS];
++
++void pcf50633_reg_write(u_int8_t reg, u_int8_t val);
++
++u_int8_t pcf50633_reg_read(u_int8_t reg);
++
++void pcf50633_reg_set_bit_mask(u_int8_t reg, u_int8_t mask, u_int8_t val);
++void pcf50633_reg_clear_bits(u_int8_t reg, u_int8_t bits);
++
++void pcf50633_charge_autofast(int on);
++
++#endif /* _PCF50606_H */
++
Modified: trunk/src/target/kernel/patches/series
===================================================================
--- trunk/src/target/kernel/patches/series 2007-06-06 14:54:34 UTC (rev 2177)
+++ trunk/src/target/kernel/patches/series 2007-06-06 18:08:19 UTC (rev 2178)
@@ -36,3 +36,4 @@
s3c2410_fb-truecolor.patch
s3c2440-nand-disable-hwecc.patch
hxd8-tsl256x.patch
+pcf50633.patch
More information about the commitlog
mailing list