162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Battery and Power Management code for the Sharp SL-Cxx00 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2005 Richard Purdie 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/stat.h> 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/gpio.h> 1462306a36Sopenharmony_ci#include <linux/gpio-pxa.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/apm-emulation.h> 1862306a36Sopenharmony_ci#include <linux/spi/corgi_lcd.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <asm/irq.h> 2162306a36Sopenharmony_ci#include <asm/mach-types.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "spitz.h" 2462306a36Sopenharmony_ci#include "pxa27x.h" 2562306a36Sopenharmony_ci#include "sharpsl_pm.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "generic.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define SHARPSL_CHARGE_ON_VOLT 0x99 /* 2.9V */ 3062306a36Sopenharmony_ci#define SHARPSL_CHARGE_ON_TEMP 0xe0 /* 2.9V */ 3162306a36Sopenharmony_ci#define SHARPSL_CHARGE_ON_ACIN_HIGH 0x9b /* 6V */ 3262306a36Sopenharmony_ci#define SHARPSL_CHARGE_ON_ACIN_LOW 0x34 /* 2V */ 3362306a36Sopenharmony_ci#define SHARPSL_FATAL_ACIN_VOLT 182 /* 3.45V */ 3462306a36Sopenharmony_ci#define SHARPSL_FATAL_NOACIN_VOLT 170 /* 3.40V */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic int spitz_last_ac_status; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic struct gpio spitz_charger_gpios[] = { 3962306a36Sopenharmony_ci { SPITZ_GPIO_KEY_INT, GPIOF_IN, "Keyboard Interrupt" }, 4062306a36Sopenharmony_ci { SPITZ_GPIO_SYNC, GPIOF_IN, "Sync" }, 4162306a36Sopenharmony_ci { SPITZ_GPIO_AC_IN, GPIOF_IN, "Charger Detection" }, 4262306a36Sopenharmony_ci { SPITZ_GPIO_ADC_TEMP_ON, GPIOF_OUT_INIT_LOW, "ADC Temp On" }, 4362306a36Sopenharmony_ci { SPITZ_GPIO_JK_B, GPIOF_OUT_INIT_LOW, "JK B" }, 4462306a36Sopenharmony_ci { SPITZ_GPIO_CHRG_ON, GPIOF_OUT_INIT_LOW, "Charger On" }, 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic void spitz_charger_init(void) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci gpio_request_array(ARRAY_AND_SIZE(spitz_charger_gpios)); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic void spitz_measure_temp(int on) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci gpio_set_value(SPITZ_GPIO_ADC_TEMP_ON, on); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic void spitz_charge(int on) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci if (on) { 6062306a36Sopenharmony_ci if (sharpsl_pm.flags & SHARPSL_SUSPENDED) { 6162306a36Sopenharmony_ci gpio_set_value(SPITZ_GPIO_JK_B, 1); 6262306a36Sopenharmony_ci gpio_set_value(SPITZ_GPIO_CHRG_ON, 0); 6362306a36Sopenharmony_ci } else { 6462306a36Sopenharmony_ci gpio_set_value(SPITZ_GPIO_JK_B, 0); 6562306a36Sopenharmony_ci gpio_set_value(SPITZ_GPIO_CHRG_ON, 0); 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci } else { 6862306a36Sopenharmony_ci gpio_set_value(SPITZ_GPIO_JK_B, 0); 6962306a36Sopenharmony_ci gpio_set_value(SPITZ_GPIO_CHRG_ON, 1); 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic void spitz_discharge(int on) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci gpio_set_value(SPITZ_GPIO_JK_A, on); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* HACK - For unknown reasons, accurate voltage readings are only made with a load 7962306a36Sopenharmony_ci on the power bus which the green led on spitz provides */ 8062306a36Sopenharmony_cistatic void spitz_discharge1(int on) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci gpio_set_value(SPITZ_GPIO_LED_GREEN, on); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic unsigned long gpio18_config = GPIO18_GPIO; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic void spitz_presuspend(void) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci spitz_last_ac_status = sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* GPIO Sleep Register */ 9262306a36Sopenharmony_ci PGSR0 = 0x00144018; 9362306a36Sopenharmony_ci PGSR1 = 0x00EF0000; 9462306a36Sopenharmony_ci if (machine_is_akita()) { 9562306a36Sopenharmony_ci PGSR2 = 0x2121C000; 9662306a36Sopenharmony_ci PGSR3 = 0x00600400; 9762306a36Sopenharmony_ci } else { 9862306a36Sopenharmony_ci PGSR2 = 0x0121C000; 9962306a36Sopenharmony_ci PGSR3 = 0x00600000; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci PGSR0 &= ~SPITZ_GPIO_G0_STROBE_BIT; 10362306a36Sopenharmony_ci PGSR1 &= ~SPITZ_GPIO_G1_STROBE_BIT; 10462306a36Sopenharmony_ci PGSR2 &= ~SPITZ_GPIO_G2_STROBE_BIT; 10562306a36Sopenharmony_ci PGSR3 &= ~SPITZ_GPIO_G3_STROBE_BIT; 10662306a36Sopenharmony_ci PGSR2 |= GPIO_bit(SPITZ_GPIO_KEY_STROBE0); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci pxa2xx_mfp_config(&gpio18_config, 1); 10962306a36Sopenharmony_ci gpio_request_one(18, GPIOF_OUT_INIT_HIGH, "Unknown"); 11062306a36Sopenharmony_ci gpio_free(18); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci PRER = GPIO_bit(SPITZ_GPIO_KEY_INT); 11362306a36Sopenharmony_ci PFER = GPIO_bit(SPITZ_GPIO_KEY_INT) | GPIO_bit(SPITZ_GPIO_RESET); 11462306a36Sopenharmony_ci PWER = GPIO_bit(SPITZ_GPIO_KEY_INT) | GPIO_bit(SPITZ_GPIO_RESET) | PWER_RTC; 11562306a36Sopenharmony_ci PKWR = GPIO_bit(SPITZ_GPIO_SYNC) | GPIO_bit(SPITZ_GPIO_KEY_INT) | GPIO_bit(SPITZ_GPIO_RESET); 11662306a36Sopenharmony_ci PKSR = 0xffffffff; /* clear */ 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* nRESET_OUT Disable */ 11962306a36Sopenharmony_ci PSLR |= PSLR_SL_ROD; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* Stop 3.6MHz and drive HIGH to PCMCIA and CS */ 12262306a36Sopenharmony_ci PCFR = PCFR_GPR_EN | PCFR_OPDE; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic void spitz_postsuspend(void) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int spitz_should_wakeup(unsigned int resume_on_alarm) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci int is_resume = 0; 13262306a36Sopenharmony_ci int acin = sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (spitz_last_ac_status != acin) { 13562306a36Sopenharmony_ci if (acin) { 13662306a36Sopenharmony_ci /* charge on */ 13762306a36Sopenharmony_ci sharpsl_pm.flags |= SHARPSL_DO_OFFLINE_CHRG; 13862306a36Sopenharmony_ci dev_dbg(sharpsl_pm.dev, "AC Inserted\n"); 13962306a36Sopenharmony_ci } else { 14062306a36Sopenharmony_ci /* charge off */ 14162306a36Sopenharmony_ci dev_dbg(sharpsl_pm.dev, "AC Removed\n"); 14262306a36Sopenharmony_ci sharpsl_pm_led(SHARPSL_LED_OFF); 14362306a36Sopenharmony_ci sharpsl_pm.machinfo->charge(0); 14462306a36Sopenharmony_ci sharpsl_pm.charge_mode = CHRG_OFF; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci spitz_last_ac_status = acin; 14762306a36Sopenharmony_ci /* Return to suspend as this must be what we were woken for */ 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (PEDR & GPIO_bit(SPITZ_GPIO_KEY_INT)) 15262306a36Sopenharmony_ci is_resume |= GPIO_bit(SPITZ_GPIO_KEY_INT); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (PKSR & GPIO_bit(SPITZ_GPIO_SYNC)) 15562306a36Sopenharmony_ci is_resume |= GPIO_bit(SPITZ_GPIO_SYNC); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (resume_on_alarm && (PEDR & PWER_RTC)) 15862306a36Sopenharmony_ci is_resume |= PWER_RTC; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci dev_dbg(sharpsl_pm.dev, "is_resume: %x\n", is_resume); 16162306a36Sopenharmony_ci return is_resume; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic bool spitz_charger_wakeup(void) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci return !gpio_get_value(SPITZ_GPIO_KEY_INT) || 16762306a36Sopenharmony_ci gpio_get_value(SPITZ_GPIO_SYNC); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic unsigned long spitzpm_read_devdata(int type) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci switch (type) { 17362306a36Sopenharmony_ci case SHARPSL_STATUS_ACIN: 17462306a36Sopenharmony_ci return !gpio_get_value(SPITZ_GPIO_AC_IN); 17562306a36Sopenharmony_ci case SHARPSL_STATUS_LOCK: 17662306a36Sopenharmony_ci return gpio_get_value(sharpsl_pm.machinfo->gpio_batlock); 17762306a36Sopenharmony_ci case SHARPSL_STATUS_CHRGFULL: 17862306a36Sopenharmony_ci return gpio_get_value(sharpsl_pm.machinfo->gpio_batfull); 17962306a36Sopenharmony_ci case SHARPSL_STATUS_FATAL: 18062306a36Sopenharmony_ci return gpio_get_value(sharpsl_pm.machinfo->gpio_fatal); 18162306a36Sopenharmony_ci case SHARPSL_ACIN_VOLT: 18262306a36Sopenharmony_ci return sharpsl_pm_pxa_read_max1111(MAX1111_ACIN_VOLT); 18362306a36Sopenharmony_ci case SHARPSL_BATT_TEMP: 18462306a36Sopenharmony_ci return sharpsl_pm_pxa_read_max1111(MAX1111_BATT_TEMP); 18562306a36Sopenharmony_ci case SHARPSL_BATT_VOLT: 18662306a36Sopenharmony_ci default: 18762306a36Sopenharmony_ci return sharpsl_pm_pxa_read_max1111(MAX1111_BATT_VOLT); 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistruct sharpsl_charger_machinfo spitz_pm_machinfo = { 19262306a36Sopenharmony_ci .init = spitz_charger_init, 19362306a36Sopenharmony_ci .exit = NULL, 19462306a36Sopenharmony_ci .gpio_batlock = SPITZ_GPIO_BAT_COVER, 19562306a36Sopenharmony_ci .gpio_acin = SPITZ_GPIO_AC_IN, 19662306a36Sopenharmony_ci .gpio_batfull = SPITZ_GPIO_CHRG_FULL, 19762306a36Sopenharmony_ci .batfull_irq = 1, 19862306a36Sopenharmony_ci .gpio_fatal = SPITZ_GPIO_FATAL_BAT, 19962306a36Sopenharmony_ci .discharge = spitz_discharge, 20062306a36Sopenharmony_ci .discharge1 = spitz_discharge1, 20162306a36Sopenharmony_ci .charge = spitz_charge, 20262306a36Sopenharmony_ci .measure_temp = spitz_measure_temp, 20362306a36Sopenharmony_ci .presuspend = spitz_presuspend, 20462306a36Sopenharmony_ci .postsuspend = spitz_postsuspend, 20562306a36Sopenharmony_ci .read_devdata = spitzpm_read_devdata, 20662306a36Sopenharmony_ci .charger_wakeup = spitz_charger_wakeup, 20762306a36Sopenharmony_ci .should_wakeup = spitz_should_wakeup, 20862306a36Sopenharmony_ci#if defined(CONFIG_LCD_CORGI) 20962306a36Sopenharmony_ci .backlight_limit = corgi_lcd_limit_intensity, 21062306a36Sopenharmony_ci#endif 21162306a36Sopenharmony_ci .charge_on_volt = SHARPSL_CHARGE_ON_VOLT, 21262306a36Sopenharmony_ci .charge_on_temp = SHARPSL_CHARGE_ON_TEMP, 21362306a36Sopenharmony_ci .charge_acin_high = SHARPSL_CHARGE_ON_ACIN_HIGH, 21462306a36Sopenharmony_ci .charge_acin_low = SHARPSL_CHARGE_ON_ACIN_LOW, 21562306a36Sopenharmony_ci .fatal_acin_volt = SHARPSL_FATAL_ACIN_VOLT, 21662306a36Sopenharmony_ci .fatal_noacin_volt= SHARPSL_FATAL_NOACIN_VOLT, 21762306a36Sopenharmony_ci .bat_levels = 40, 21862306a36Sopenharmony_ci .bat_levels_noac = sharpsl_battery_levels_noac, 21962306a36Sopenharmony_ci .bat_levels_acin = sharpsl_battery_levels_acin, 22062306a36Sopenharmony_ci .status_high_acin = 188, 22162306a36Sopenharmony_ci .status_low_acin = 178, 22262306a36Sopenharmony_ci .status_high_noac = 185, 22362306a36Sopenharmony_ci .status_low_noac = 175, 22462306a36Sopenharmony_ci}; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic struct platform_device *spitzpm_device; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic int spitzpm_init(void) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci int ret; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (!machine_is_spitz() && !machine_is_akita() 23362306a36Sopenharmony_ci && !machine_is_borzoi()) 23462306a36Sopenharmony_ci return -ENODEV; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci spitzpm_device = platform_device_alloc("sharpsl-pm", -1); 23762306a36Sopenharmony_ci if (!spitzpm_device) 23862306a36Sopenharmony_ci return -ENOMEM; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci spitzpm_device->dev.platform_data = &spitz_pm_machinfo; 24162306a36Sopenharmony_ci ret = platform_device_add(spitzpm_device); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (ret) 24462306a36Sopenharmony_ci platform_device_put(spitzpm_device); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return ret; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic void spitzpm_exit(void) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci platform_device_unregister(spitzpm_device); 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cimodule_init(spitzpm_init); 25562306a36Sopenharmony_cimodule_exit(spitzpm_exit); 256