Index: linux-2.6.17/arch/arm/mach-pxa/Makefile =================================================================== --- linux-2.6.17.orig/arch/arm/mach-pxa/Makefile 2006-09-19 20:51:33.984424500 +0200 +++ linux-2.6.17/arch/arm/mach-pxa/Makefile 2006-09-19 21:08:04.922354250 +0200 @@ -16,7 +16,7 @@ obj-$(CONFIG_PXA_SHARP_C7xx) += corgi.o obj-$(CONFIG_PXA_SHARP_Cxx00) += spitz.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o spitz_pm.o obj-$(CONFIG_MACH_AKITA) += akita-ioexp.o obj-$(CONFIG_MACH_POODLE) += poodle.o corgi_ssp.o sharpsl_pm.o poodle_pm.o -obj-$(CONFIG_MACH_TOSA) += tosa.o +obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o obj-$(CONFIG_MACH_EM_X270) += em-x270.o obj-$(CONFIG_MACH_HX2750) += hx2750.o hx2750_test.o Index: linux-2.6.17/arch/arm/mach-pxa/tosa_pm.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17/arch/arm/mach-pxa/tosa_pm.c 2006-09-19 21:08:34.476201250 +0200 @@ -0,0 +1,661 @@ +/* + * Battery and Power Management code for the Sharp SL-6000x + * + * Copyright (c) 2005 Dirk Opfer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "sharpsl.h" + +extern int tosa_bl_intensity(void); +volatile static int ad_polling; +static int tosa_pm_driver_probe(struct device *dev); +static int tosa_pm_driver_remove(struct device *dev); +static struct wm97xx *wm9712; + +/************************************************************ + * AC97 functions + ************************************************************/ +#define AC97_TIMEOUT_VAL 0x1000000 + +#define AC97_MISC_MODEM_STAT 0x0056 +#define AC97_GPIO_CONFIG 0x004C + +static u16 tosa_ac97_read(unsigned short reg) +{ + volatile u32 *reg_addr; + volatile int timeout; + unsigned short data; + + if (CAR & CAR_CAIP) { + printk(KERN_CRIT ": CAR_CAIP already set\n"); + return 0; + } + + if (reg == AC97_GPIO_STATUS) + reg_addr = &PMC_REG_BASE; + else + reg_addr = &PAC_REG_BASE; + + reg_addr += (reg >> 1); + + data=0; + GSR = GSR_CDONE | GSR_SDONE; + + data = *reg_addr; + timeout = 0; + + while(((GSR & GSR_SDONE)) == 0 && (timeout++ < AC97_TIMEOUT_VAL)); + + if ((timeout >= AC97_TIMEOUT_VAL)) { + GSR = GSR; + printk(KERN_CRIT ": AC97 is busy1.\n"); + return data; + } + + // actual read + GSR = GSR_CDONE | GSR_SDONE; + data = *reg_addr; + + timeout = 0; + while(((GSR & GSR_SDONE) == 0) && (timeout++= AC97_TIMEOUT_VAL)) { + GSR = GSR; + printk(KERN_CRIT ": AC97 is busy2.\n"); + return data; + } + + return data; +} + +static void tosa_ac97_write(unsigned short reg, unsigned short val) +{ + volatile u32 *reg_addr; + volatile int timeout=0; + + if (CAR & CAR_CAIP) { + printk(KERN_CRIT ": CAR_CAIP already set\n"); + return; + } + + GSR = GSR_CDONE | GSR_SDONE; + if (reg == AC97_GPIO_STATUS) + reg_addr = &PMC_REG_BASE; + else + reg_addr = &PAC_REG_BASE; + + reg_addr += (reg >> 1); + + *reg_addr = val; + while(((GSR & GSR_CDONE) == 0) && (timeout++ < AC97_TIMEOUT_VAL)); + if (timeout >= AC97_TIMEOUT_VAL) { + printk(KERN_CRIT ": AC97 is busy.\n"); + } +} + +static void tosa_ac97_bit_clear(u8 reg, u16 val) +{ + unsigned short dat = tosa_ac97_read(reg); + dat &= ~val; + tosa_ac97_write(reg, dat); +} + +static void tosa_ac97_bit_set(u8 reg, u16 val) +{ + unsigned short dat = tosa_ac97_read(reg); + dat |= val; + tosa_ac97_write(reg, dat); +} + + +static int tosa_ac97_init(void) +{ + int timeo; + + pxa_gpio_mode(GPIO31_SYNC_AC97_MD); + pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); + pxa_gpio_mode(GPIO28_BITCLK_AC97_MD); + pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); + pxa_gpio_mode(GPIO20_DREQ0_MD); + + pxa_set_cken(CKEN2_AC97, 1); + /* AC97 power on sequense */ + while ( 1 ) { + GCR = 0; + udelay(100); + GCR |= GCR_COLD_RST; + udelay(5); + GCR |= GCR_WARM_RST; + udelay(5); + for ( timeo = 0x10000; timeo > 0; timeo-- ) { + if ( GSR & GSR_PCR ) break; + mdelay(5); + } + if( timeo > 0 ) break; + printk(KERN_WARNING "AC97 power on retry!!\n"); + } + + tosa_ac97_write(AC97_EXTENDED_STATUS, 1); + /* + * Setting AC97 GPIO + * i/o function + * GPIO1: input EAR_IN signal + * GPIO2: output IRQ signal + * GPIO3: output PENDOWN signal + * GPIO4: input MASK signal + * GPIO5: input DETECT MIC signal + */ + // AC97_GPIO_FUNC AC97_MISC_MODEM_STAT + + tosa_ac97_bit_clear(AC97_MISC_MODEM_STAT, + ((1<<2)|(1<<3)|(1<<4)|(1<<5))); + tosa_ac97_bit_clear(AC97_GPIO_CONFIG,(1<<2)|(1<<3)); + tosa_ac97_bit_set(AC97_GPIO_CONFIG, (1<<1)|(1<<4)|(1<<5)); + + tosa_ac97_write(AC97_WM97XX_DIGITISER2, 0xc009); + tosa_ac97_write(AC97_WM97XX_DIGITISER1, 0x0030 | WM97XX_DELAY(4)); + + pxa_gpio_mode(GPIO32_SDATA_IN1_AC97_MD); + ad_polling = 1; + printk("tosa_ac97_init\n"); +} + +void tosa_ac97_exit(void) +{ + if (!(CKEN & CKEN2_AC97)) + return; + + // power down the whole chip + tosa_ac97_write(AC97_POWERDOWN, 0x7fff); + +// GCR &= ~(GCR_CDONE_IE | GCR_SDONE_IE | GCR_SECRDY_IEN | GCR_PRIRDY_IEN | GCR_SECRES_IEN | GCR_PRIRES_IEN); +// GSR = GSR; +// GCR = GCR_ACLINK_OFF; + pxa_set_cken(CKEN2_AC97, 0); + /* switch back to irq driver */ + ad_polling = 0; + printk("tosa_ac97_exit\n"); +} + +int ac97_ad_input(u16 adcsel) +{ + unsigned short val = 0; + unsigned long timeout; + + // prepare + tosa_ac97_read(AC97_WM97XX_DIGITISER_RD); + + if (adcsel & 0x8000) + adcsel = ((adcsel & 0x7fff) + 3) << 12; + + /* Conversion start */ + tosa_ac97_write(AC97_WM97XX_DIGITISER1, (adcsel | WM97XX_POLL | WM97XX_DELAY(4))); + timeout = 0x1000; + /* wait for POLL to go low */ + while ((tosa_ac97_read(AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { + udelay(100); + timeout--; + } + + val = tosa_ac97_read(AC97_WM97XX_DIGITISER_RD); + + val &= 0xFFF ; + + return val; +} + + +int tosa_read_aux_adc(u16 adcsel) +{ + if (ad_polling) + return (ac97_ad_input(adcsel)); + else + return (wm97xx_read_aux_adc(wm9712, adcsel)); +} + +static struct device_driver tosa_pm_driver = { + .name = "wm97xx-battery", + .bus = &wm97xx_bus_type, + .owner = THIS_MODULE, + .probe = tosa_pm_driver_probe, + .remove = tosa_pm_driver_remove, +}; + +#if 0 +#define TOSA_TEMP_READ_WAIT_TIME (5) // 5msec [Fix] +int tosa_read_battery(struct wm97xx* wm, int channel) +{ + //return sprintf(buf, "%d\n", wm97xx_read_aux_adc(wm, input)); + + int wm_aux,i; + int value = 0; + int clear_mux; + + switch(channel) { + + case 0: // Main + + set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON); + reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_V_ON | TOSA_TC6393_BAT_SW_ON); + wm_aux = WM97XX_AUX_ID3; + break; + + case 1: // Jacket + + set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_V_ON); + reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON | TOSA_TC6393_BAT_SW_ON); + wm_aux = WM97XX_AUX_ID3; + break; + + case 2: // BU + set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BU_CHRG_ON); + wm_aux = WM97XX_AUX_ID4; + break; + + case 3: // Main Temp + set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_TH_ON); + reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_TH_ON); + wm_aux = WM97XX_AUX_ID2; + break; + + case 4: // Jacket Temp + set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_TH_ON); + reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_TH_ON); + wm_aux = WM97XX_AUX_ID2; + break; + + default: + return -1; + } + + mdelay(TOSA_TEMP_READ_WAIT_TIME); + for(i=0;i<4;i++) + { + if (wm9712) + value += wm97xx_read_aux_adc(wm, wm_aux); + else + value += ac97_ad_input(wm, wm_aux); + } + + value>>=2; + // reset the multiplexer + clear_mux = TOSA_TC6393_BAT0_V_ON | TOSA_TC6393_BAT1_V_ON | TOSA_TC6393_BAT_SW_ON | TOSA_TC6393_BAT0_TH_ON | TOSA_TC6393_BAT1_TH_ON | TOSA_TC6393_BU_CHRG_ON; + + return value; + +} +#endif + +/* BL 5-3 */ +struct battery_thresh tosa_battery_levels_bl[] = { + { 1663, 100 }, + { 1605, 75 }, + { 1564, 50 }, + { 1510, 25 }, + { 1435, 5 }, + { 0, 0 }, +}; + +/* BL 2-0 */ +struct battery_thresh tosa_battery_levels[] = { + { 1679, 100 }, + { 1617, 75 }, + { 1576, 50 }, + { 1530, 25 }, + { 1448, 5 }, + { 0, 0 }, +}; + +struct pm_devices { + const char * name; + struct bus_type *bus; + struct device_driver *driver; + struct device * dev; + int (*resume)(struct device *dev, void * data); // int (*resume)(struct device *dev); + int (*suspend)(struct device *dev, void * data);// int (*suspend)(struct device *dev, pm_message_t state); +}; + +/* Ugly + We need the following devices to measure the battery and control the charger: + Also we need access to these before we sleep and immediatly after we resume so we can't + control their pm via the kernel device manager. To access their pm functions we will backup + the suspend and resume handler and clear these pointers. + After that we can suspend and resume these devices. + + Don't change the order of this table!!!!! +*/ +static struct pm_devices dev_table[] = { + [0] = { + .name = TMIO_SOC_NAME, + .bus = &platform_bus_type, + }, +}; + +static int tosa_pm_driver_probe(struct device *dev) +{ + wm9712 = dev->driver_data; + ad_polling = 0; + return 0; +} + +static int tosa_pm_driver_remove(struct device *dev) +{ + wm9712 = NULL; + return 0; +} + + +static void tosa_charger_init(void) +{ + int i; + + /* If this driver doesn't register, bad things will happen, Tosa won't boot, + and the world will possibly explode */ + i = driver_register(&tosa_pm_driver); + if (i < 0) + panic("Cannot register the tosa_pm driver on the wm97xx bus. Halting."); + + for(i=0;iresume; + dev_table[i].suspend = dev_table[i].driver->suspend; + dev_table[i].driver->resume = NULL; + dev_table[i].driver->suspend = NULL; + } + } + + pxa_gpio_mode(TOSA_GPIO_AC_IN | GPIO_IN); + pxa_gpio_mode(TOSA_GPIO_BAT0_CRG | GPIO_IN); + pxa_gpio_mode(TOSA_GPIO_BAT1_CRG | GPIO_IN); + pxa_gpio_mode(TOSA_GPIO_BAT0_LOW | GPIO_IN); + pxa_gpio_mode(TOSA_GPIO_BAT1_LOW | GPIO_IN); + + pxa_gpio_mode(TOSA_GPIO_JACKET_DETECT | GPIO_IN); + pxa_gpio_mode(TOSA_GPIO_POWERON | GPIO_IN); + sharpsl_pm_pxa_init(); +} + + +static void tosa_measure_temp(int on) +{ + reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_TH_ON | TOSA_TC6393_BAT1_TH_ON); + + if (on) + set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_TH_ON); + +} + +static void tosa_charge(int on) +{ + if(on) + reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_CHARGE_OFF); + else + set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_CHARGE_OFF); +} + +static void tosa_discharge1(int on) +{ +} + +static void tosa_discharge(int on) +{ +} + + +static void tosa_presuspend(void) +{ + int i; + unsigned long wakeup_mask; + + // put remaining devices into sleep + for(i=0;iread_devdata(SHARPSL_STATUS_ACIN)) { + /* charge on */ + dev_dbg(sharpsl_pm.dev, "ac insert\n"); + sharpsl_pm.flags |= SHARPSL_DO_OFFLINE_CHRG; + } else { + /* charge off */ + dev_dbg(sharpsl_pm.dev, "ac remove\n"); + sharpsl_pm_led(SHARPSL_LED_OFF); + sharpsl_pm.machinfo->charge(0); + sharpsl_pm.charge_mode = CHRG_OFF; + } + } + + if ((PEDR & GPIO_bit(GPIO_bit(TOSA_GPIO_BAT0_CRG)))) + dev_dbg(sharpsl_pm.dev, "Charge full interrupt\n"); + + if (PEDR & GPIO_bit(TOSA_GPIO_POWERON)) + is_resume |= GPIO_bit(TOSA_GPIO_POWERON); + + if (PEDR & GPIO_bit(TOSA_GPIO_ON_KEY)) + is_resume |= GPIO_bit(TOSA_GPIO_ON_KEY); + + if (resume_on_alarm && (PEDR & PWER_RTC)) + is_resume |= PWER_RTC; + + return is_resume; +} + +static unsigned long tosa_charger_wakeup(void) +{ +// return ~GPLR0 & ( GPIO_bit(TOSA_GPIO_AC_IN) | GPIO_bit(TOSA_GPIO_POWERON) | GPIO_bit(TOSA_GPIO_ON_KEY) ); + return (~GPLR0 & ( GPIO_bit(TOSA_GPIO_POWERON) | GPIO_bit(TOSA_GPIO_ON_KEY) )) | (GPLR0 & GPIO_bit(TOSA_GPIO_AC_IN)); +} + +unsigned long tosa_read_bat(void) +{ + unsigned long value; + reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON | TOSA_TC6393_BAT1_V_ON | TOSA_TC6393_BAT_SW_ON); + mdelay(5); + value = tosa_read_aux_adc(WM97XX_AUX_ID3); + set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON); + return value; +} + +unsigned long tosapm_read_devdata(int type) +{ + switch(type) { + case SHARPSL_STATUS_ACIN: + return ((GPLR(TOSA_GPIO_AC_IN) & GPIO_bit(TOSA_GPIO_AC_IN)) == 0); + case SHARPSL_STATUS_LOCK: + return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_batlock); + case SHARPSL_STATUS_CHRGFULL: + return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_batfull); + case SHARPSL_STATUS_FATAL: + return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_fatal); + case SHARPSL_ACIN_VOLT: + return 1000; // not used on tosa + case SHARPSL_BATT_TEMP: + return tosa_read_aux_adc(WM97XX_AUX_ID2); + case SHARPSL_BATT_VOLT: + return tosa_read_bat(); + default: + return tosa_read_bat(); + } +} + +static struct sharpsl_charger_machinfo tosa_pm_machinfo = { + .init = tosa_charger_init, + .exit = sharpsl_pm_pxa_remove, + .gpio_batlock = TOSA_GPIO_BAT_LOCKED, + .gpio_acin = TOSA_GPIO_AC_IN, + .gpio_batfull = TOSA_GPIO_BAT0_CRG, + .batfull_irq = 0, + .discharge = tosa_discharge, + .discharge1 = tosa_discharge1, + .charge = tosa_charge, + .measure_temp = tosa_measure_temp, + .presuspend = tosa_presuspend, + .postsuspend = tosa_postsuspend, + .postresume = tosa_postresume, + .read_devdata = tosapm_read_devdata, + .charger_wakeup = tosa_charger_wakeup, + .should_wakeup = tosa_should_wakeup, + .backlight_limit = corgibl_limit_intensity, + .backlight_get_status= tosa_bl_intensity, + .bat_levels = 6, + .bat_levels_noac = tosa_battery_levels, + .bat_levels_acin = tosa_battery_levels, + .bat_levels_noac_bl = tosa_battery_levels_bl, + .bat_levels_acin_bl = tosa_battery_levels_bl, + .charge_on_volt = 1200, // 2.9V TOSA_MAIN_BATTERY_ERR_THRESH voltage < 1200 -> error + .charge_on_temp = 3600, // -- TOSA_MAIN_BATTERY_EXIST_THRESH temp > 3600 -> error, no battery + .charge_acin_high = 1500, // not used default value + .charge_acin_low = 500, // not used default value + .fatal_acin_volt = 1572, // 3.8V + .fatal_noacin_volt= 1551, // 3.75V + .status_high_acin = 1564, // 3.78V + .status_low_acin = 1510, // 3.65V + .status_high_noac = 1564, // 3.78V + .status_low_noac = 1510, // 3.65V +}; + +static struct platform_device *tosapm_device; + +static int __devinit tosapm_init(void) +{ + int ret; + + tosapm_device = platform_device_alloc("sharpsl-pm", -1); + if (!tosapm_device) + return -ENOMEM; + + tosapm_device->dev.platform_data = &tosa_pm_machinfo; + ret = platform_device_add(tosapm_device); + + if (ret) + platform_device_put(tosapm_device); + + return ret; +} + +static void tosapm_exit(void) +{ + int i; + + // restore the resume / suspend handler + for(i=0;iresume = dev_table[i].resume; + dev_table[i].driver->suspend = dev_table[i].suspend; + dev_table[i].resume = NULL; + dev_table[i].suspend = NULL; + put_driver(dev_table[i].driver); + } + } + + if (wm9712) + driver_unregister(&tosa_pm_driver); + + platform_device_unregister(tosapm_device); +} + +module_init(tosapm_init); +module_exit(tosapm_exit); Index: linux-2.6.17/arch/arm/mach-pxa/Kconfig =================================================================== --- linux-2.6.17.orig/arch/arm/mach-pxa/Kconfig 2006-09-19 20:51:40.160810500 +0200 +++ linux-2.6.17/arch/arm/mach-pxa/Kconfig 2006-09-19 21:08:04.926354500 +0200 @@ -110,6 +110,7 @@ config MACH_TOSA bool "Enable Sharp SL-6000x (Tosa) Support" depends PXA_SHARPSL_25x select TOSHIBA_TC6393XB + select SHARPSL_PM config PXA25x bool Index: linux-2.6.17/arch/arm/mach-pxa/Makefile =================================================================== --- linux-2.6.17.orig/arch/arm/mach-pxa/Makefile 2006-09-19 20:51:33.984424500 +0200 +++ linux-2.6.17/arch/arm/mach-pxa/Makefile 2006-09-19 21:08:04.922354250 +0200 @@ -16,7 +16,7 @@ obj-$(CONFIG_PXA_SHARP_C7xx) += corgi.o obj-$(CONFIG_PXA_SHARP_Cxx00) += spitz.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o spitz_pm.o obj-$(CONFIG_MACH_AKITA) += akita-ioexp.o obj-$(CONFIG_MACH_POODLE) += poodle.o corgi_ssp.o sharpsl_pm.o poodle_pm.o -obj-$(CONFIG_MACH_TOSA) += tosa.o +obj-$(CONFIG_MACH_TOSA) += tosa.o sharpsl_pm.o tosa_pm.o obj-$(CONFIG_MACH_EM_X270) += em-x270.o obj-$(CONFIG_MACH_HX2750) += hx2750.o hx2750_test.o Index: linux-2.6.17/arch/arm/mach-pxa/tosa_pm.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17/arch/arm/mach-pxa/tosa_pm.c 2006-09-19 21:08:34.476201250 +0200 @@ -0,0 +1,661 @@ +/* + * Battery and Power Management code for the Sharp SL-6000x + * + * Copyright (c) 2005 Dirk Opfer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "sharpsl.h" + +extern int tosa_bl_intensity(void); +volatile static int ad_polling; +static int tosa_pm_driver_probe(struct device *dev); +static int tosa_pm_driver_remove(struct device *dev); +static struct wm97xx *wm9712; + +/************************************************************ + * AC97 functions + ************************************************************/ +#define AC97_TIMEOUT_VAL 0x1000000 + +#define AC97_MISC_MODEM_STAT 0x0056 +#define AC97_GPIO_CONFIG 0x004C + +static u16 tosa_ac97_read(unsigned short reg) +{ + volatile u32 *reg_addr; + volatile int timeout; + unsigned short data; + + if (CAR & CAR_CAIP) { + printk(KERN_CRIT ": CAR_CAIP already set\n"); + return 0; + } + + if (reg == AC97_GPIO_STATUS) + reg_addr = &PMC_REG_BASE; + else + reg_addr = &PAC_REG_BASE; + + reg_addr += (reg >> 1); + + data=0; + GSR = GSR_CDONE | GSR_SDONE; + + data = *reg_addr; + timeout = 0; + + while(((GSR & GSR_SDONE)) == 0 && (timeout++ < AC97_TIMEOUT_VAL)); + + if ((timeout >= AC97_TIMEOUT_VAL)) { + GSR = GSR; + printk(KERN_CRIT ": AC97 is busy1.\n"); + return data; + } + + // actual read + GSR = GSR_CDONE | GSR_SDONE; + data = *reg_addr; + + timeout = 0; + while(((GSR & GSR_SDONE) == 0) && (timeout++= AC97_TIMEOUT_VAL)) { + GSR = GSR; + printk(KERN_CRIT ": AC97 is busy2.\n"); + return data; + } + + return data; +} + +static void tosa_ac97_write(unsigned short reg, unsigned short val) +{ + volatile u32 *reg_addr; + volatile int timeout=0; + + if (CAR & CAR_CAIP) { + printk(KERN_CRIT ": CAR_CAIP already set\n"); + return; + } + + GSR = GSR_CDONE | GSR_SDONE; + if (reg == AC97_GPIO_STATUS) + reg_addr = &PMC_REG_BASE; + else + reg_addr = &PAC_REG_BASE; + + reg_addr += (reg >> 1); + + *reg_addr = val; + while(((GSR & GSR_CDONE) == 0) && (timeout++ < AC97_TIMEOUT_VAL)); + if (timeout >= AC97_TIMEOUT_VAL) { + printk(KERN_CRIT ": AC97 is busy.\n"); + } +} + +static void tosa_ac97_bit_clear(u8 reg, u16 val) +{ + unsigned short dat = tosa_ac97_read(reg); + dat &= ~val; + tosa_ac97_write(reg, dat); +} + +static void tosa_ac97_bit_set(u8 reg, u16 val) +{ + unsigned short dat = tosa_ac97_read(reg); + dat |= val; + tosa_ac97_write(reg, dat); +} + + +static int tosa_ac97_init(void) +{ + int timeo; + + pxa_gpio_mode(GPIO31_SYNC_AC97_MD); + pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); + pxa_gpio_mode(GPIO28_BITCLK_AC97_MD); + pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); + pxa_gpio_mode(GPIO20_DREQ0_MD); + + pxa_set_cken(CKEN2_AC97, 1); + /* AC97 power on sequense */ + while ( 1 ) { + GCR = 0; + udelay(100); + GCR |= GCR_COLD_RST; + udelay(5); + GCR |= GCR_WARM_RST; + udelay(5); + for ( timeo = 0x10000; timeo > 0; timeo-- ) { + if ( GSR & GSR_PCR ) break; + mdelay(5); + } + if( timeo > 0 ) break; + printk(KERN_WARNING "AC97 power on retry!!\n"); + } + + tosa_ac97_write(AC97_EXTENDED_STATUS, 1); + /* + * Setting AC97 GPIO + * i/o function + * GPIO1: input EAR_IN signal + * GPIO2: output IRQ signal + * GPIO3: output PENDOWN signal + * GPIO4: input MASK signal + * GPIO5: input DETECT MIC signal + */ + // AC97_GPIO_FUNC AC97_MISC_MODEM_STAT + + tosa_ac97_bit_clear(AC97_MISC_MODEM_STAT, + ((1<<2)|(1<<3)|(1<<4)|(1<<5))); + tosa_ac97_bit_clear(AC97_GPIO_CONFIG,(1<<2)|(1<<3)); + tosa_ac97_bit_set(AC97_GPIO_CONFIG, (1<<1)|(1<<4)|(1<<5)); + + tosa_ac97_write(AC97_WM97XX_DIGITISER2, 0xc009); + tosa_ac97_write(AC97_WM97XX_DIGITISER1, 0x0030 | WM97XX_DELAY(4)); + + pxa_gpio_mode(GPIO32_SDATA_IN1_AC97_MD); + ad_polling = 1; + printk("tosa_ac97_init\n"); +} + +void tosa_ac97_exit(void) +{ + if (!(CKEN & CKEN2_AC97)) + return; + + // power down the whole chip + tosa_ac97_write(AC97_POWERDOWN, 0x7fff); + +// GCR &= ~(GCR_CDONE_IE | GCR_SDONE_IE | GCR_SECRDY_IEN | GCR_PRIRDY_IEN | GCR_SECRES_IEN | GCR_PRIRES_IEN); +// GSR = GSR; +// GCR = GCR_ACLINK_OFF; + pxa_set_cken(CKEN2_AC97, 0); + /* switch back to irq driver */ + ad_polling = 0; + printk("tosa_ac97_exit\n"); +} + +int ac97_ad_input(u16 adcsel) +{ + unsigned short val = 0; + unsigned long timeout; + + // prepare + tosa_ac97_read(AC97_WM97XX_DIGITISER_RD); + + if (adcsel & 0x8000) + adcsel = ((adcsel & 0x7fff) + 3) << 12; + + /* Conversion start */ + tosa_ac97_write(AC97_WM97XX_DIGITISER1, (adcsel | WM97XX_POLL | WM97XX_DELAY(4))); + timeout = 0x1000; + /* wait for POLL to go low */ + while ((tosa_ac97_read(AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { + udelay(100); + timeout--; + } + + val = tosa_ac97_read(AC97_WM97XX_DIGITISER_RD); + + val &= 0xFFF ; + + return val; +} + + +int tosa_read_aux_adc(u16 adcsel) +{ + if (ad_polling) + return (ac97_ad_input(adcsel)); + else + return (wm97xx_read_aux_adc(wm9712, adcsel)); +} + +static struct device_driver tosa_pm_driver = { + .name = "wm97xx-battery", + .bus = &wm97xx_bus_type, + .owner = THIS_MODULE, + .probe = tosa_pm_driver_probe, + .remove = tosa_pm_driver_remove, +}; + +#if 0 +#define TOSA_TEMP_READ_WAIT_TIME (5) // 5msec [Fix] +int tosa_read_battery(struct wm97xx* wm, int channel) +{ + //return sprintf(buf, "%d\n", wm97xx_read_aux_adc(wm, input)); + + int wm_aux,i; + int value = 0; + int clear_mux; + + switch(channel) { + + case 0: // Main + + set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON); + reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_V_ON | TOSA_TC6393_BAT_SW_ON); + wm_aux = WM97XX_AUX_ID3; + break; + + case 1: // Jacket + + set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_V_ON); + reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON | TOSA_TC6393_BAT_SW_ON); + wm_aux = WM97XX_AUX_ID3; + break; + + case 2: // BU + set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BU_CHRG_ON); + wm_aux = WM97XX_AUX_ID4; + break; + + case 3: // Main Temp + set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_TH_ON); + reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_TH_ON); + wm_aux = WM97XX_AUX_ID2; + break; + + case 4: // Jacket Temp + set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_TH_ON); + reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_TH_ON); + wm_aux = WM97XX_AUX_ID2; + break; + + default: + return -1; + } + + mdelay(TOSA_TEMP_READ_WAIT_TIME); + for(i=0;i<4;i++) + { + if (wm9712) + value += wm97xx_read_aux_adc(wm, wm_aux); + else + value += ac97_ad_input(wm, wm_aux); + } + + value>>=2; + // reset the multiplexer + clear_mux = TOSA_TC6393_BAT0_V_ON | TOSA_TC6393_BAT1_V_ON | TOSA_TC6393_BAT_SW_ON | TOSA_TC6393_BAT0_TH_ON | TOSA_TC6393_BAT1_TH_ON | TOSA_TC6393_BU_CHRG_ON; + + return value; + +} +#endif + +/* BL 5-3 */ +struct battery_thresh tosa_battery_levels_bl[] = { + { 1663, 100 }, + { 1605, 75 }, + { 1564, 50 }, + { 1510, 25 }, + { 1435, 5 }, + { 0, 0 }, +}; + +/* BL 2-0 */ +struct battery_thresh tosa_battery_levels[] = { + { 1679, 100 }, + { 1617, 75 }, + { 1576, 50 }, + { 1530, 25 }, + { 1448, 5 }, + { 0, 0 }, +}; + +struct pm_devices { + const char * name; + struct bus_type *bus; + struct device_driver *driver; + struct device * dev; + int (*resume)(struct device *dev, void * data); // int (*resume)(struct device *dev); + int (*suspend)(struct device *dev, void * data);// int (*suspend)(struct device *dev, pm_message_t state); +}; + +/* Ugly + We need the following devices to measure the battery and control the charger: + Also we need access to these before we sleep and immediatly after we resume so we can't + control their pm via the kernel device manager. To access their pm functions we will backup + the suspend and resume handler and clear these pointers. + After that we can suspend and resume these devices. + + Don't change the order of this table!!!!! +*/ +static struct pm_devices dev_table[] = { + [0] = { + .name = TMIO_SOC_NAME, + .bus = &platform_bus_type, + }, +}; + +static int tosa_pm_driver_probe(struct device *dev) +{ + wm9712 = dev->driver_data; + ad_polling = 0; + return 0; +} + +static int tosa_pm_driver_remove(struct device *dev) +{ + wm9712 = NULL; + return 0; +} + + +static void tosa_charger_init(void) +{ + int i; + + /* If this driver doesn't register, bad things will happen, Tosa won't boot, + and the world will possibly explode */ + i = driver_register(&tosa_pm_driver); + if (i < 0) + panic("Cannot register the tosa_pm driver on the wm97xx bus. Halting."); + + for(i=0;iresume; + dev_table[i].suspend = dev_table[i].driver->suspend; + dev_table[i].driver->resume = NULL; + dev_table[i].driver->suspend = NULL; + } + } + + pxa_gpio_mode(TOSA_GPIO_AC_IN | GPIO_IN); + pxa_gpio_mode(TOSA_GPIO_BAT0_CRG | GPIO_IN); + pxa_gpio_mode(TOSA_GPIO_BAT1_CRG | GPIO_IN); + pxa_gpio_mode(TOSA_GPIO_BAT0_LOW | GPIO_IN); + pxa_gpio_mode(TOSA_GPIO_BAT1_LOW | GPIO_IN); + + pxa_gpio_mode(TOSA_GPIO_JACKET_DETECT | GPIO_IN); + pxa_gpio_mode(TOSA_GPIO_POWERON | GPIO_IN); + sharpsl_pm_pxa_init(); +} + + +static void tosa_measure_temp(int on) +{ + reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_TH_ON | TOSA_TC6393_BAT1_TH_ON); + + if (on) + set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT1_TH_ON); + +} + +static void tosa_charge(int on) +{ + if(on) + reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_CHARGE_OFF); + else + set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_CHARGE_OFF); +} + +static void tosa_discharge1(int on) +{ +} + +static void tosa_discharge(int on) +{ +} + + +static void tosa_presuspend(void) +{ + int i; + unsigned long wakeup_mask; + + // put remaining devices into sleep + for(i=0;iread_devdata(SHARPSL_STATUS_ACIN)) { + /* charge on */ + dev_dbg(sharpsl_pm.dev, "ac insert\n"); + sharpsl_pm.flags |= SHARPSL_DO_OFFLINE_CHRG; + } else { + /* charge off */ + dev_dbg(sharpsl_pm.dev, "ac remove\n"); + sharpsl_pm_led(SHARPSL_LED_OFF); + sharpsl_pm.machinfo->charge(0); + sharpsl_pm.charge_mode = CHRG_OFF; + } + } + + if ((PEDR & GPIO_bit(GPIO_bit(TOSA_GPIO_BAT0_CRG)))) + dev_dbg(sharpsl_pm.dev, "Charge full interrupt\n"); + + if (PEDR & GPIO_bit(TOSA_GPIO_POWERON)) + is_resume |= GPIO_bit(TOSA_GPIO_POWERON); + + if (PEDR & GPIO_bit(TOSA_GPIO_ON_KEY)) + is_resume |= GPIO_bit(TOSA_GPIO_ON_KEY); + + if (resume_on_alarm && (PEDR & PWER_RTC)) + is_resume |= PWER_RTC; + + return is_resume; +} + +static unsigned long tosa_charger_wakeup(void) +{ +// return ~GPLR0 & ( GPIO_bit(TOSA_GPIO_AC_IN) | GPIO_bit(TOSA_GPIO_POWERON) | GPIO_bit(TOSA_GPIO_ON_KEY) ); + return (~GPLR0 & ( GPIO_bit(TOSA_GPIO_POWERON) | GPIO_bit(TOSA_GPIO_ON_KEY) )) | (GPLR0 & GPIO_bit(TOSA_GPIO_AC_IN)); +} + +unsigned long tosa_read_bat(void) +{ + unsigned long value; + reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON | TOSA_TC6393_BAT1_V_ON | TOSA_TC6393_BAT_SW_ON); + mdelay(5); + value = tosa_read_aux_adc(WM97XX_AUX_ID3); + set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_BAT0_V_ON); + return value; +} + +unsigned long tosapm_read_devdata(int type) +{ + switch(type) { + case SHARPSL_STATUS_ACIN: + return ((GPLR(TOSA_GPIO_AC_IN) & GPIO_bit(TOSA_GPIO_AC_IN)) == 0); + case SHARPSL_STATUS_LOCK: + return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_batlock); + case SHARPSL_STATUS_CHRGFULL: + return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_batfull); + case SHARPSL_STATUS_FATAL: + return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_fatal); + case SHARPSL_ACIN_VOLT: + return 1000; // not used on tosa + case SHARPSL_BATT_TEMP: + return tosa_read_aux_adc(WM97XX_AUX_ID2); + case SHARPSL_BATT_VOLT: + return tosa_read_bat(); + default: + return tosa_read_bat(); + } +} + +static struct sharpsl_charger_machinfo tosa_pm_machinfo = { + .init = tosa_charger_init, + .exit = sharpsl_pm_pxa_remove, + .gpio_batlock = TOSA_GPIO_BAT_LOCKED, + .gpio_acin = TOSA_GPIO_AC_IN, + .gpio_batfull = TOSA_GPIO_BAT0_CRG, + .batfull_irq = 0, + .discharge = tosa_discharge, + .discharge1 = tosa_discharge1, + .charge = tosa_charge, + .measure_temp = tosa_measure_temp, + .presuspend = tosa_presuspend, + .postsuspend = tosa_postsuspend, + .postresume = tosa_postresume, + .read_devdata = tosapm_read_devdata, + .charger_wakeup = tosa_charger_wakeup, + .should_wakeup = tosa_should_wakeup, + .backlight_limit = corgibl_limit_intensity, + .backlight_get_status= tosa_bl_intensity, + .bat_levels = 6, + .bat_levels_noac = tosa_battery_levels, + .bat_levels_acin = tosa_battery_levels, + .bat_levels_noac_bl = tosa_battery_levels_bl, + .bat_levels_acin_bl = tosa_battery_levels_bl, + .charge_on_volt = 1200, // 2.9V TOSA_MAIN_BATTERY_ERR_THRESH voltage < 1200 -> error + .charge_on_temp = 3600, // -- TOSA_MAIN_BATTERY_EXIST_THRESH temp > 3600 -> error, no battery + .charge_acin_high = 1500, // not used default value + .charge_acin_low = 500, // not used default value + .fatal_acin_volt = 1572, // 3.8V + .fatal_noacin_volt= 1551, // 3.75V + .status_high_acin = 1564, // 3.78V + .status_low_acin = 1510, // 3.65V + .status_high_noac = 1564, // 3.78V + .status_low_noac = 1510, // 3.65V +}; + +static struct platform_device *tosapm_device; + +static int __devinit tosapm_init(void) +{ + int ret; + + tosapm_device = platform_device_alloc("sharpsl-pm", -1); + if (!tosapm_device) + return -ENOMEM; + + tosapm_device->dev.platform_data = &tosa_pm_machinfo; + ret = platform_device_add(tosapm_device); + + if (ret) + platform_device_put(tosapm_device); + + return ret; +} + +static void tosapm_exit(void) +{ + int i; + + // restore the resume / suspend handler + for(i=0;iresume = dev_table[i].resume; + dev_table[i].driver->suspend = dev_table[i].suspend; + dev_table[i].resume = NULL; + dev_table[i].suspend = NULL; + put_driver(dev_table[i].driver); + } + } + + if (wm9712) + driver_unregister(&tosa_pm_driver); + + platform_device_unregister(tosapm_device); +} + +module_init(tosapm_init); +module_exit(tosapm_exit); Index: linux-2.6.17/arch/arm/mach-pxa/Kconfig =================================================================== --- linux-2.6.17.orig/arch/arm/mach-pxa/Kconfig 2006-09-19 20:51:40.160810500 +0200 +++ linux-2.6.17/arch/arm/mach-pxa/Kconfig 2006-09-19 21:08:04.926354500 +0200 @@ -110,6 +110,7 @@ config MACH_TOSA bool "Enable Sharp SL-6000x (Tosa) Support" depends PXA_SHARPSL_25x select TOSHIBA_TC6393XB + select SHARPSL_PM config PXA25x bool