#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/proc.h>
#include <sys/sensors.h>
#include <sys/signalvar.h>
#include <uvm/uvm_extern.h>
#include <machine/apmvar.h>
#include <machine/bus.h>
#include <machine/fdt.h>
#include <dev/clock_subr.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_gpio.h>
#include <dev/ofw/ofw_misc.h>
#include <dev/ofw/fdt.h>
#include <arm64/dev/aplmbox.h>
#include <arm64/dev/rtkit.h>
#include "apm.h"
extern int lid_action;
extern void (*simplefb_burn_hook)(u_int);
extern void (*cpuresetfn)(void);
extern void (*powerdownfn)(void);
extern int (*hw_battery_setchargemode)(int);
extern int (*hw_battery_setchargestart)(int);
extern int (*hw_battery_setchargestop)(int);
extern int hw_battery_chargemode;
extern int hw_battery_chargestart;
extern int hw_battery_chargestop;
#define SMC_EP 32
#define SMC_CMD(d) ((d) & 0xff)
#define SMC_READ_KEY 0x10
#define SMC_WRITE_KEY 0x11
#define SMC_GET_KEY_BY_INDEX 0x12
#define SMC_GET_KEY_INFO 0x13
#define SMC_GET_SRAM_ADDR 0x17
#define SMC_SRAM_SIZE 0x4000
#define SMC_NOTIFICATION 0x18
#define SMC_ERROR(d) ((d) & 0xff)
#define SMC_OK 0x00
#define SMC_KEYNOTFOUND 0x84
#define SMC_KEY(s) ((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3])
struct smc_key_info {
uint8_t size;
uint8_t type[4];
uint8_t flags;
};
#define SMC_GPIO_CMD_OUTPUT (0x01 << 24)
#define RTC_OFFSET_LEN 6
#define SMC_CLKM_LEN 6
struct aplsmc_sensor {
const char *key;
const char *key_type;
enum sensor_type type;
int scale;
const char *desc;
int flags;
};
#define SMC_EV_TYPE(d) (((d) >> 48) & 0xffff)
#define SMC_EV_TYPE_BTN 0x7201
#define SMC_EV_TYPE_LID 0x7203
#define SMC_EV_SUBTYPE(d) (((d) >> 40) & 0xff)
#define SMC_EV_DATA(d) (((d) >> 32) & 0xff)
#define SMC_PWRBTN_OFF 0x00
#define SMC_PWRBTN_SHORT 0x01
#define SMC_PWRBTN_TOUCHID 0x06
#define SMC_PWRBTN_LONG 0xfe
#define SMC_LID_OPEN 0x00
#define SMC_LID_CLOSE 0x01
#define APLSMC_BE (1 << 0)
#define APLSMC_HIDDEN (1 << 1)
#define APLSMC_MAX_SENSORS 19
struct aplsmc_softc {
struct device sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
bus_space_handle_t sc_sram_ioh;
void *sc_ih;
struct rtkit_state *sc_rs;
struct rtkit sc_rk;
uint8_t sc_msgid;
uint64_t sc_data;
struct gpio_controller sc_gc;
int sc_rtc_node;
struct todr_chip_handle sc_todr;
int sc_reboot_node;
struct aplsmc_sensor *sc_smcsensors[APLSMC_MAX_SENSORS];
struct ksensor sc_sensors[APLSMC_MAX_SENSORS];
int sc_nsensors;
struct ksensordev sc_sensordev;
uint32_t sc_suspend_pstr;
};
#define CH0I_DISCHARGE (1 << 0)
#define CH0C_INHIBIT (1 << 0)
#define CHLS_FORCE_DISCHARGE (1 << 8)
struct aplsmc_softc *aplsmc_sc;
#ifndef SMALL_KERNEL
struct aplsmc_sensor aplsmc_sensors[] = {
{ "ACDI", "ui16", SENSOR_INDICATOR, 1, "power supply" },
{ "B0RM", "ui16", SENSOR_AMPHOUR, 1000, "remaining battery capacity",
APLSMC_BE },
{ "B0FC", "ui16", SENSOR_AMPHOUR, 1000, "last full battery capacity" },
{ "B0DC", "ui16", SENSOR_AMPHOUR, 1000, "battery design capacity" },
{ "B0AV", "ui16", SENSOR_VOLTS_DC, 1000, "battery" },
{ "B0CT", "ui16", SENSOR_INTEGER, 1, "battery discharge cycles" },
{ "B0TF", "ui16", SENSOR_INTEGER, 1, "battery time-to-full",
APLSMC_HIDDEN },
{ "B0TE", "ui16", SENSOR_INTEGER, 1, "battery time-to-empty",
APLSMC_HIDDEN },
{ "F0Ac", "flt ", SENSOR_FANRPM, 1, "" },
{ "ID0R", "flt ", SENSOR_AMPS, 1000000, "input" },
{ "PDTR", "flt ", SENSOR_WATTS, 1000000, "input" },
{ "PSTR", "flt ", SENSOR_WATTS, 1000000, "system" },
{ "TB0T", "flt ", SENSOR_TEMP, 1000000, "battery" },
{ "TCHP", "flt ", SENSOR_TEMP, 1000000, "charger" },
{ "TW0P", "flt ", SENSOR_TEMP, 1000000, "wireless" },
{ "Ts0P", "flt ", SENSOR_TEMP, 1000000, "palm rest" },
{ "Ts1P", "flt ", SENSOR_TEMP, 1000000, "palm rest" },
{ "VD0R", "flt ", SENSOR_VOLTS_DC, 1000000, "input" },
};
#endif
int aplsmc_match(struct device *, void *, void *);
void aplsmc_attach(struct device *, struct device *, void *);
int aplsmc_activate(struct device *, int);
const struct cfattach aplsmc_ca = {
sizeof (struct aplsmc_softc), aplsmc_match, aplsmc_attach,
NULL, aplsmc_activate
};
struct cfdriver aplsmc_cd = {
NULL, "aplsmc", DV_DULL
};
paddr_t aplsmc_logmap(void *, bus_addr_t);
void aplsmc_callback(void *, uint64_t);
int aplsmc_send_cmd(struct aplsmc_softc *, uint16_t, uint32_t, uint16_t);
int aplsmc_wait_cmd(struct aplsmc_softc *sc);
int aplsmc_read_key(struct aplsmc_softc *, uint32_t, void *, size_t);
int aplsmc_write_key(struct aplsmc_softc *, uint32_t, void *, size_t);
int64_t aplsmc_convert_flt(uint32_t, int);
void aplsmc_refresh_sensors(void *);
int aplsmc_apminfo(struct apm_power_info *);
void aplsmc_set_pin(void *, uint32_t *, int);
int aplsmc_gettime(struct todr_chip_handle *, struct timeval *);
int aplsmc_settime(struct todr_chip_handle *, struct timeval *);
void aplsmc_reset(void);
void aplsmc_powerdown(void);
void aplsmc_reboot_attachhook(struct device *);
void aplsmc_battery_init(struct aplsmc_softc *);
int aplsmc_battery_setchargemode(int);
int aplsmc_battery_setchargestart(int);
int aplsmc_battery_chls_setchargestop(int);
int aplsmc_battery_chwa_setchargestop(int);
int
aplsmc_match(struct device *parent, void *match, void *aux)
{
struct fdt_attach_args *faa = aux;
return OF_is_compatible(faa->fa_node, "apple,smc") ||
OF_is_compatible(faa->fa_node, "apple,t8103-smc");
}
void
aplsmc_attach(struct device *parent, struct device *self, void *aux)
{
struct aplsmc_softc *sc = (struct aplsmc_softc *)self;
struct fdt_attach_args *faa = aux;
uint8_t data[SMC_CLKM_LEN];
uint8_t ntap;
int error, node;
#ifndef SMALL_KERNEL
int i;
#endif
if (faa->fa_nreg < 1) {
printf(": no registers\n");
return;
}
sc->sc_iot = faa->fa_iot;
if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
printf(": can't map registers\n");
return;
}
sc->sc_rk.rk_cookie = sc;
sc->sc_rk.rk_dmat = faa->fa_dmat;
sc->sc_rk.rk_logmap = aplsmc_logmap;
sc->sc_rs = rtkit_init(faa->fa_node, NULL, RK_WAKEUP, &sc->sc_rk);
if (sc->sc_rs == NULL) {
printf(": can't map mailbox channel\n");
return;
}
error = rtkit_boot(sc->sc_rs);
if (error) {
printf(": can't boot firmware\n");
return;
}
error = rtkit_start_endpoint(sc->sc_rs, SMC_EP, aplsmc_callback, sc);
if (error) {
printf(": can't start SMC endpoint\n");
return;
}
aplsmc_send_cmd(sc, SMC_GET_SRAM_ADDR, 0, 0);
error = aplsmc_wait_cmd(sc);
if (error) {
printf(": can't get SRAM address\n");
return;
}
if (bus_space_map(sc->sc_iot, sc->sc_data,
SMC_SRAM_SIZE, 0, &sc->sc_sram_ioh)) {
printf(": can't map SRAM\n");
return;
}
printf("\n");
aplsmc_sc = sc;
node = OF_getnodebyname(faa->fa_node, "gpio");
if (node) {
sc->sc_gc.gc_node = node;
sc->sc_gc.gc_cookie = sc;
sc->sc_gc.gc_set_pin = aplsmc_set_pin;
gpio_controller_register(&sc->sc_gc);
}
error = aplsmc_read_key(sc, SMC_KEY("CLKM"), &data, SMC_CLKM_LEN);
node = OF_getnodebyname(faa->fa_node, "rtc");
if (node && error == 0) {
sc->sc_rtc_node = node;
sc->sc_todr.cookie = sc;
sc->sc_todr.todr_gettime = aplsmc_gettime;
sc->sc_todr.todr_settime = aplsmc_settime;
sc->sc_todr.todr_quality = 1000;
todr_attach(&sc->sc_todr);
}
node = OF_getnodebyname(faa->fa_node, "reboot");
if (node) {
sc->sc_reboot_node = node;
cpuresetfn = aplsmc_reset;
powerdownfn = aplsmc_powerdown;
config_mountroot(self, aplsmc_reboot_attachhook);
}
ntap = 1;
aplsmc_write_key(sc, SMC_KEY("NTAP"), &ntap, sizeof(ntap));
#ifndef SMALL_KERNEL
for (i = 0; i < nitems(aplsmc_sensors); i++) {
struct smc_key_info info;
aplsmc_send_cmd(sc, SMC_GET_KEY_INFO,
SMC_KEY(aplsmc_sensors[i].key), 0);
error = aplsmc_wait_cmd(sc);
if (error || SMC_ERROR(sc->sc_data) != SMC_OK)
continue;
bus_space_read_region_1(sc->sc_iot, sc->sc_sram_ioh, 0,
(uint8_t *)&info, sizeof(info));
if (memcmp(aplsmc_sensors[i].key_type, info.type,
sizeof(info.type)) != 0)
continue;
if (sc->sc_nsensors >= APLSMC_MAX_SENSORS) {
printf("%s: maximum number of sensors exceeded\n",
sc->sc_dev.dv_xname);
break;
}
sc->sc_smcsensors[sc->sc_nsensors] = &aplsmc_sensors[i];
strlcpy(sc->sc_sensors[sc->sc_nsensors].desc,
aplsmc_sensors[i].desc, sizeof(sc->sc_sensors[0].desc));
sc->sc_sensors[sc->sc_nsensors].type = aplsmc_sensors[i].type;
if (!(aplsmc_sensors[i].flags & APLSMC_HIDDEN)) {
sensor_attach(&sc->sc_sensordev,
&sc->sc_sensors[sc->sc_nsensors]);
}
sc->sc_nsensors++;
}
aplsmc_refresh_sensors(sc);
strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
sizeof(sc->sc_sensordev.xname));
sensordev_install(&sc->sc_sensordev);
sensor_task_register(sc, aplsmc_refresh_sensors, 5);
#if NAPM > 0
apm_setinfohook(aplsmc_apminfo);
#endif
aplsmc_battery_init(sc);
#endif
#ifdef SUSPEND
device_register_wakeup(&sc->sc_dev);
#endif
}
int
aplsmc_activate(struct device *self, int act)
{
#ifdef SUSPEND
struct aplsmc_softc *sc = (struct aplsmc_softc *)self;
int64_t value;
switch (act) {
case DVACT_WAKEUP:
value = aplsmc_convert_flt(sc->sc_suspend_pstr, 100);
printf("%s: system %lld.%02lld W\n", sc->sc_dev.dv_xname,
value / 100, value % 100);
}
#endif
return 0;
}
paddr_t
aplsmc_logmap(void *cookie, bus_addr_t addr)
{
return addr | PMAP_DEVICE;
}
void
aplsmc_handle_notification(struct aplsmc_softc *sc, uint64_t data)
{
#ifdef SUSPEND
extern int cpu_suspended;
uint32_t flt = 0;
if (cpu_suspended) {
aplsmc_read_key(sc, 'PSTR', &flt, sizeof(flt));
sc->sc_suspend_pstr = flt;
switch (SMC_EV_TYPE(data)) {
case SMC_EV_TYPE_BTN:
switch (SMC_EV_SUBTYPE(data)) {
case SMC_PWRBTN_SHORT:
case SMC_PWRBTN_TOUCHID:
cpu_suspended = 0;
break;
}
break;
case SMC_EV_TYPE_LID:
switch (SMC_EV_SUBTYPE(data)) {
case SMC_LID_OPEN:
cpu_suspended = 0;
break;
}
break;
}
return;
}
#endif
switch (SMC_EV_TYPE(data)) {
case SMC_EV_TYPE_BTN:
switch (SMC_EV_SUBTYPE(data)) {
case SMC_PWRBTN_SHORT:
case SMC_PWRBTN_TOUCHID:
if (SMC_EV_DATA(data) == 1)
powerbutton_event();
break;
case SMC_PWRBTN_LONG:
break;
case SMC_PWRBTN_OFF:
break;
default:
printf("%s: SMV_EV_TYPE_BTN 0x%016llx\n",
sc->sc_dev.dv_xname, data);
break;
}
break;
case SMC_EV_TYPE_LID:
switch (lid_action) {
case 0:
switch (SMC_EV_SUBTYPE(data)) {
case SMC_LID_OPEN:
if (simplefb_burn_hook)
simplefb_burn_hook(1);
break;
case SMC_LID_CLOSE:
if (simplefb_burn_hook)
simplefb_burn_hook(0);
break;
default:
printf("%s: SMV_EV_TYPE_LID 0x%016llx\n",
sc->sc_dev.dv_xname, data);
break;
}
case 1:
#ifdef SUSPEND
request_sleep(SLEEP_SUSPEND);
#endif
break;
case 2:
break;
}
break;
default:
#ifdef APLSMC_DEBUG
printf("%s: unhandled event 0x%016llx\n",
sc->sc_dev.dv_xname, data);
#endif
break;
}
}
void
aplsmc_callback(void *arg, uint64_t data)
{
struct aplsmc_softc *sc = arg;
if (SMC_CMD(data) == SMC_NOTIFICATION) {
aplsmc_handle_notification(sc, data);
return;
}
sc->sc_data = data;
wakeup(&sc->sc_data);
}
int
aplsmc_send_cmd(struct aplsmc_softc *sc, uint16_t cmd, uint32_t key,
uint16_t len)
{
uint64_t data;
data = cmd;
data |= (uint64_t)len << 16;
data |= (uint64_t)key << 32;
data |= (sc->sc_msgid++ & 0xf) << 12;
return rtkit_send_endpoint(sc->sc_rs, SMC_EP, data);
}
int
aplsmc_wait_cmd(struct aplsmc_softc *sc)
{
if (cold) {
int error, timo;
for (timo = 1000; timo > 0; timo--) {
error = rtkit_poll(sc->sc_rs);
if (error == 0)
return 0;
delay(10);
}
return EWOULDBLOCK;
}
return tsleep_nsec(&sc->sc_data, PWAIT, "apsmc", 10000000);
}
int
aplsmc_read_key(struct aplsmc_softc *sc, uint32_t key, void *data, size_t len)
{
int error;
aplsmc_send_cmd(sc, SMC_READ_KEY, key, len);
error = aplsmc_wait_cmd(sc);
if (error)
return error;
switch (SMC_ERROR(sc->sc_data)) {
case SMC_OK:
break;
case SMC_KEYNOTFOUND:
return EINVAL;
break;
default:
return EIO;
break;
}
len = min(len, (sc->sc_data >> 16) & 0xffff);
if (len > sizeof(uint32_t)) {
bus_space_read_region_1(sc->sc_iot, sc->sc_sram_ioh, 0,
data, len);
} else {
uint32_t tmp = (sc->sc_data >> 32);
memcpy(data, &tmp, len);
}
return 0;
}
int
aplsmc_write_key(struct aplsmc_softc *sc, uint32_t key, void *data, size_t len)
{
bus_space_write_region_1(sc->sc_iot, sc->sc_sram_ioh, 0, data, len);
bus_space_barrier(sc->sc_iot, sc->sc_sram_ioh, 0, len,
BUS_SPACE_BARRIER_WRITE);
aplsmc_send_cmd(sc, SMC_WRITE_KEY, key, len);
return aplsmc_wait_cmd(sc);
}
#ifndef SMALL_KERNEL
int64_t
aplsmc_convert_flt(uint32_t flt, int scale)
{
int64_t mant;
int sign, exp;
sign = (flt >> 31) ? -1 : 1;
exp = ((flt >> 23) & 0xff) - 127;
mant = (flt & 0x7fffff) | 0x800000;
mant *= scale;
if (exp < 23)
return sign * (mant >> (23 - exp));
else
return sign * (mant << (exp - 23));
}
void
aplsmc_refresh_sensors(void *arg)
{
extern int hw_power;
struct aplsmc_softc *sc = arg;
struct aplsmc_sensor *sensor;
int64_t value;
uint32_t key;
int i, error;
for (i = 0; i < sc->sc_nsensors; i++) {
sensor = sc->sc_smcsensors[i];
key = SMC_KEY(sensor->key);
if (strcmp(sensor->key_type, "ui8 ") == 0) {
uint8_t ui8;
error = aplsmc_read_key(sc, key, &ui8, sizeof(ui8));
value = (int64_t)ui8 * sensor->scale;
} else if (strcmp(sensor->key_type, "ui16") == 0) {
uint16_t ui16;
error = aplsmc_read_key(sc, key, &ui16, sizeof(ui16));
if (sensor->flags & APLSMC_BE)
ui16 = betoh16(ui16);
value = (int64_t)ui16 * sensor->scale;
} else if (strcmp(sensor->key_type, "flt ") == 0) {
uint32_t flt;
error = aplsmc_read_key(sc, key, &flt, sizeof(flt));
if (sensor->flags & APLSMC_BE)
flt = betoh32(flt);
value = aplsmc_convert_flt(flt, sensor->scale);
}
if (sensor->type == SENSOR_TEMP)
value += 273150000;
if (error) {
sc->sc_sensors[i].flags |= SENSOR_FUNKNOWN;
} else {
sc->sc_sensors[i].flags &= ~SENSOR_FUNKNOWN;
sc->sc_sensors[i].value = value;
}
if (strcmp(sensor->key, "ACDI") == 0)
hw_power = (value > 0);
}
}
#if NAPM > 0
int
aplsmc_apminfo(struct apm_power_info *info)
{
struct aplsmc_sensor *sensor;
struct ksensor *ksensor;
struct aplsmc_softc *sc = aplsmc_sc;
int remaining = -1, capacity = -1, i;
info->battery_state = APM_BATT_UNKNOWN;
info->ac_state = APM_AC_UNKNOWN;
info->battery_life = 0;
info->minutes_left = -1;
for (i = 0; i < sc->sc_nsensors; i++) {
sensor = sc->sc_smcsensors[i];
ksensor = &sc->sc_sensors[i];
if (ksensor->flags & SENSOR_FUNKNOWN)
continue;
if (strcmp(sensor->key, "ACDI") == 0) {
info->ac_state = ksensor->value ?
APM_AC_ON : APM_AC_OFF;
} else if (strcmp(sensor->key, "B0RM") == 0)
remaining = ksensor->value;
else if (strcmp(sensor->key, "B0FC") == 0)
capacity = ksensor->value;
else if ((strcmp(sensor->key, "B0TE") == 0) &&
(ksensor->value != 0xffff))
info->minutes_left = ksensor->value;
else if ((strcmp(sensor->key, "B0TF") == 0) &&
(ksensor->value != 0xffff)) {
info->battery_state = APM_BATT_CHARGING;
info->minutes_left = ksensor->value;
}
}
if (remaining > -1 && capacity > 0) {
info->battery_life = ((remaining * 100) / capacity);
if (info->battery_state != APM_BATT_CHARGING) {
if (info->battery_life > 50)
info->battery_state = APM_BATT_HIGH;
else if (info->battery_life > 25)
info->battery_state = APM_BATT_LOW;
else
info->battery_state = APM_BATT_CRITICAL;
}
}
return 0;
}
#endif
#endif
void
aplsmc_set_pin(void *cookie, uint32_t *cells, int val)
{
struct aplsmc_softc *sc = cookie;
static char *digits = "0123456789abcdef";
uint32_t pin = cells[0];
uint32_t flags = cells[1];
uint32_t key = SMC_KEY("gP\0\0");
uint32_t data;
KASSERT(pin < 256);
key |= (digits[(pin >> 0) & 0xf] << 0);
key |= (digits[(pin >> 4) & 0xf] << 8);
if (flags & GPIO_ACTIVE_LOW)
val = !val;
data = SMC_GPIO_CMD_OUTPUT | !!val;
aplsmc_write_key(sc, key, &data, sizeof(data));
}
int
aplsmc_gettime(struct todr_chip_handle *handle, struct timeval *tv)
{
struct aplsmc_softc *sc = handle->cookie;
uint8_t data[8] = {};
uint64_t offset, time;
int error;
error = nvmem_read_cell(sc->sc_rtc_node, "rtc_offset", &data,
RTC_OFFSET_LEN);
if (error)
return error;
offset = lemtoh64(data);
error = aplsmc_read_key(sc, SMC_KEY("CLKM"), &data, SMC_CLKM_LEN);
if (error)
return error;
time = lemtoh64(data) + offset;
tv->tv_sec = (time >> 15);
tv->tv_usec = (((time & 0x7fff) * 1000000) >> 15);
return 0;
}
int
aplsmc_settime(struct todr_chip_handle *handle, struct timeval *tv)
{
struct aplsmc_softc *sc = handle->cookie;
uint8_t data[8] = {};
uint64_t offset, time;
int error;
error = aplsmc_read_key(sc, SMC_KEY("CLKM"), &data, SMC_CLKM_LEN);
if (error)
return error;
time = ((uint64_t)tv->tv_sec << 15);
time |= ((uint64_t)tv->tv_usec << 15) / 1000000;
offset = time - lemtoh64(data);
htolem64(data, offset);
return nvmem_write_cell(sc->sc_rtc_node, "rtc_offset", &data,
RTC_OFFSET_LEN);
}
void
aplsmc_reboot_attachhook(struct device *self)
{
struct aplsmc_softc *sc = (struct aplsmc_softc *)self;
uint8_t count = 0;
nvmem_write_cell(sc->sc_reboot_node, "boot_error_count",
&count, sizeof(count));
nvmem_write_cell(sc->sc_reboot_node, "panic_count",
&count, sizeof(count));
}
void
aplsmc_reset(void)
{
struct aplsmc_softc *sc = aplsmc_sc;
uint32_t key = SMC_KEY("MBSE");
uint32_t rest = SMC_KEY("rest");
uint32_t phra = SMC_KEY("phra");
uint8_t boot_stage = 0;
aplsmc_write_key(sc, key, &rest, sizeof(rest));
nvmem_write_cell(sc->sc_reboot_node, "boot_stage",
&boot_stage, sizeof(boot_stage));
aplsmc_write_key(sc, key, &phra, sizeof(phra));
}
void
aplsmc_powerdown(void)
{
struct aplsmc_softc *sc = aplsmc_sc;
uint32_t key = SMC_KEY("MBSE");
uint32_t offw = SMC_KEY("offw");
uint32_t off1 = SMC_KEY("off1");
uint8_t boot_stage = 0;
uint8_t shutdown_flag = 1;
aplsmc_write_key(sc, key, &offw, sizeof(offw));
nvmem_write_cell(sc->sc_reboot_node, "boot_stage",
&boot_stage, sizeof(boot_stage));
nvmem_write_cell(sc->sc_reboot_node, "shutdown_flag",
&shutdown_flag, sizeof(shutdown_flag));
aplsmc_write_key(sc, key, &off1, sizeof(off1));
}
#ifndef SMALL_KERNEL
void
aplsmc_battery_init(struct aplsmc_softc *sc)
{
uint8_t ch0i, ch0c, chwa;
int error, stop;
error = aplsmc_read_key(sc, SMC_KEY("CH0I"), &ch0i, sizeof(ch0i));
if (error)
return;
error = aplsmc_read_key(sc, SMC_KEY("CH0C"), &ch0c, sizeof(ch0c));
if (error)
return;
if (ch0i & CH0I_DISCHARGE)
hw_battery_chargemode = -1;
else if (ch0c & CH0C_INHIBIT)
hw_battery_chargemode = 0;
else
hw_battery_chargemode = 1;
hw_battery_setchargemode = aplsmc_battery_setchargemode;
error = aplsmc_read_key(sc, SMC_KEY("CHWA"), &chwa, sizeof(chwa));
if (error) {
uint16_t chls;
error = aplsmc_read_key(sc, SMC_KEY("CHLS"),
&chls, sizeof(chls));
if (error)
return;
stop = (chls & 0xff) ? (chls & 0xff) : 100;
hw_battery_setchargestop = aplsmc_battery_chls_setchargestop;
} else {
stop = chwa ? 80 : 100;
hw_battery_setchargestop = aplsmc_battery_chwa_setchargestop;
}
hw_battery_setchargestart = aplsmc_battery_setchargestart;
hw_battery_chargestart = stop - 5;
hw_battery_chargestop = stop;
}
int
aplsmc_battery_setchargemode(int mode)
{
struct aplsmc_softc *sc = aplsmc_sc;
uint8_t val;
int error;
switch (mode) {
case -1:
val = 0;
error = aplsmc_write_key(sc, SMC_KEY("CH0C"),
&val, sizeof(val));
if (error)
return error;
val = 1;
error = aplsmc_write_key(sc, SMC_KEY("CH0I"),
&val, sizeof(val));
if (error)
return error;
break;
case 0:
val = 0;
error = aplsmc_write_key(sc, SMC_KEY("CH0I"),
&val, sizeof(val));
if (error)
return error;
val = 1;
error = aplsmc_write_key(sc, SMC_KEY("CH0C"),
&val, sizeof(val));
if (error)
return error;
break;
case 1:
val = 0;
error = aplsmc_write_key(sc, SMC_KEY("CH0I"),
&val, sizeof(val));
if (error)
return error;
val = 0;
error = aplsmc_write_key(sc, SMC_KEY("CH0C"),
&val, sizeof(val));
if (error)
return error;
break;
default:
return EINVAL;
}
hw_battery_chargemode = mode;
return 0;
}
int
aplsmc_battery_setchargestart(int start)
{
return EOPNOTSUPP;
}
int
aplsmc_battery_chls_setchargestop(int stop)
{
struct aplsmc_softc *sc = aplsmc_sc;
uint16_t chls;
int error;
if (stop < 10)
stop = 10;
chls = (stop == 100 ? 0 : stop) | CHLS_FORCE_DISCHARGE;
error = aplsmc_write_key(sc, SMC_KEY("CHLS"), &chls, sizeof(chls));
if (error)
return error;
hw_battery_chargestart = stop - 5;
hw_battery_chargestop = stop;
return 0;
}
int
aplsmc_battery_chwa_setchargestop(int stop)
{
struct aplsmc_softc *sc = aplsmc_sc;
uint8_t chwa;
int error;
if (stop <= 80) {
stop = 80;
chwa = 1;
} else {
stop = 100;
chwa = 0;
}
error = aplsmc_write_key(sc, SMC_KEY("CHWA"), &chwa, sizeof(chwa));
if (error)
return error;
hw_battery_chargestart = stop - 5;
hw_battery_chargestop = stop;
return 0;
}
#endif