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