static void switch_init(void) { /* Set up memory-mapped switch positions */ memmap_switches = host_get_memmap(EC_MEMMAP_SWITCHES); *memmap_switches = 0; switch_update(); /* Switch data is now present */ *host_get_memmap(EC_MEMMAP_SWITCHES_VERSION) = 1; #ifdef CONFIG_SWITCH_DEDICATED_RECOVERY /* Enable interrupts, now that we've initialized */ gpio_enable_interrupt(GPIO_RECOVERY_L); #endif /* * TODO(crosbug.com/p/23793): It's weird that flash_common.c owns * reading the write protect signal, but we enable the interrupt for it * here. Take ownership of WP back, or refactor it to its own module. */ #ifdef CONFIG_WP_ACTIVE_HIGH gpio_enable_interrupt(GPIO_WP); #else gpio_enable_interrupt(GPIO_WP_L); #endif }
static void host_command_init(void) { /* Initialize memory map ID area */ host_get_memmap(EC_MEMMAP_ID)[0] = 'E'; host_get_memmap(EC_MEMMAP_ID)[1] = 'C'; *host_get_memmap(EC_MEMMAP_ID_VERSION) = 1; *host_get_memmap(EC_MEMMAP_EVENTS_VERSION) = 1; host_set_single_event(EC_HOST_EVENT_INTERFACE_READY); CPRINTS("hostcmd init 0x%x", host_get_events()); }
int virtual_battery_read(uint8_t batt_param, uint8_t *dest, int read_len) { int val; switch (batt_param) { case SB_SERIAL_NUMBER: val = strtoi(host_get_memmap(EC_MEMMAP_BATT_SERIAL), NULL, 16); memcpy(dest, &val, read_len); break; case SB_VOLTAGE: memcpy(dest, &curr.batt.voltage, read_len); break; case SB_RELATIVE_STATE_OF_CHARGE: memcpy(dest, &curr.batt.state_of_charge, read_len); break; case SB_TEMPERATURE: memcpy(dest, &curr.batt.temperature, read_len); break; case SB_CURRENT: memcpy(dest, &curr.batt.current, read_len); break; case SB_FULL_CHARGE_CAPACITY: memcpy(dest, &curr.batt.full_capacity, read_len); break; case SB_BATTERY_STATUS: memcpy(dest, &curr.batt.status, read_len); break; case SB_CYCLE_COUNT: memcpy(dest, (int *)host_get_memmap(EC_MEMMAP_BATT_CCNT), read_len); break; case SB_DESIGN_CAPACITY: memcpy(dest, (int *)host_get_memmap(EC_MEMMAP_BATT_DCAP), read_len); break; case SB_DESIGN_VOLTAGE: memcpy(dest, (int *)host_get_memmap(EC_MEMMAP_BATT_DVLT), read_len); break; default: return EC_ERROR_INVAL; } return EC_SUCCESS; }
static void charge_init(void) { struct charge_state_context *ctx = &task_ctx; ctx->prev.state = PWR_STATE_INIT; ctx->curr.state = PWR_STATE_INIT; ctx->trickle_charging_time.val = 0; ctx->battery = battery_get_info(); ctx->charger = charger_get_info(); /* Assume the battery is responsive until proven otherwise */ ctx->battery_responsive = 1; /* Set up LPC direct memmap */ ctx->memmap_batt_volt = (uint32_t *)host_get_memmap(EC_MEMMAP_BATT_VOLT); ctx->memmap_batt_rate = (uint32_t *)host_get_memmap(EC_MEMMAP_BATT_RATE); ctx->memmap_batt_cap = (uint32_t *)host_get_memmap(EC_MEMMAP_BATT_CAP); ctx->memmap_batt_flags = host_get_memmap(EC_MEMMAP_BATT_FLAG); }
static void extpower_init(void) { uint8_t *memmap_batt_flags = host_get_memmap(EC_MEMMAP_BATT_FLAG); debounced_extpower_presence = gpio_get_level(GPIO_AC_PRESENT); /* Initialize the memory-mapped AC_PRESENT flag */ if (debounced_extpower_presence) *memmap_batt_flags |= EC_BATT_FLAG_AC_PRESENT; else *memmap_batt_flags &= ~EC_BATT_FLAG_AC_PRESENT; /* Enable interrupts, now that we've initialized */ gpio_enable_interrupt(GPIO_AC_PRESENT); }
/** * Update memory-mapped battery information, used by ACPI _BIF and/or _BIX. */ static void update_battery_info(void) { char *batt_str; int batt_serial; /* Design Capacity of Full */ battery_design_capacity((int *)host_get_memmap(EC_MEMMAP_BATT_DCAP)); /* Design Voltage */ battery_design_voltage((int *)host_get_memmap(EC_MEMMAP_BATT_DVLT)); /* Last Full Charge Capacity */ battery_full_charge_capacity( (int *)host_get_memmap(EC_MEMMAP_BATT_LFCC)); /* Cycle Count */ battery_cycle_count((int *)host_get_memmap(EC_MEMMAP_BATT_CCNT)); /* Battery Manufacturer string */ batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_MFGR); memset(batt_str, 0, EC_MEMMAP_TEXT_MAX); battery_manufacturer_name(batt_str, EC_MEMMAP_TEXT_MAX); /* Battery Model string */ batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_MODEL); memset(batt_str, 0, EC_MEMMAP_TEXT_MAX); battery_device_name(batt_str, EC_MEMMAP_TEXT_MAX); /* Battery Type string */ batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_TYPE); battery_device_chemistry(batt_str, EC_MEMMAP_TEXT_MAX); /* Smart battery serial number is 16 bits */ batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_SERIAL); memset(batt_str, 0, EC_MEMMAP_TEXT_MAX); if (battery_serial_number(&batt_serial) == 0) snprintf(batt_str, EC_MEMMAP_TEXT_MAX, "%04X", batt_serial); /* Battery data is now present */ *host_get_memmap(EC_MEMMAP_BATTERY_VERSION) = 1; }
void als_task(void) { int i, val; uint16_t *mapped = (uint16_t *)host_get_memmap(EC_MEMMAP_ALS); uint16_t als_data; while (1) { for (i = 0; i < EC_ALS_ENTRIES && i < ALS_COUNT; i++) { als_data = als_read(i, &val) == EC_SUCCESS ? val : 0; mapped[i] = als_data; } task_wait_event(SECOND); } }
/* * Host command to read memory map is not needed on LPC, because LPC can * directly map the data to the host's memory space. */ static int host_command_read_memmap(struct host_cmd_handler_args *args) { const struct ec_params_read_memmap *p = args->params; /* Copy params out of data before we overwrite it with output */ uint8_t offset = p->offset; uint8_t size = p->size; if (size > EC_MEMMAP_SIZE || offset > EC_MEMMAP_SIZE || offset + size > EC_MEMMAP_SIZE) return EC_RES_INVALID_PARAM; memcpy(args->response, host_get_memmap(offset), size); args->response_size = size; return EC_RES_SUCCESS; }
void host_clear_events(uint32_t mask) { /* Only print if something's about to change */ if (events & mask) CPRINTS("event clear 0x%08x", mask); atomic_clear(&events, mask); #ifdef CONFIG_LPC lpc_set_host_event_state(events); #else *(uint32_t *)host_get_memmap(EC_MEMMAP_HOST_EVENTS) = events; #ifdef CONFIG_MKBP_EVENT mkbp_send_event(EC_MKBP_EVENT_HOST_EVENT); #endif #endif /* !CONFIG_LPC */ }
static void update_host_event_status(void) { int need_sci = 0; int need_smi = 0; if (!init_done) return; /* Disable PMC1 interrupt while updating status register */ task_disable_irq(IT83XX_IRQ_PMC_IN); if (host_events & event_mask[LPC_HOST_EVENT_SMI]) { /* Only generate SMI for first event */ if (!(pm_get_status(LPC_ACPI_CMD) & EC_LPC_STATUS_SMI_PENDING)) need_smi = 1; pm_set_status(LPC_ACPI_CMD, EC_LPC_STATUS_SMI_PENDING, 1); } else { pm_set_status(LPC_ACPI_CMD, EC_LPC_STATUS_SMI_PENDING, 0); } if (host_events & event_mask[LPC_HOST_EVENT_SCI]) { /* Generate SCI for every event */ need_sci = 1; pm_set_status(LPC_ACPI_CMD, EC_LPC_STATUS_SCI_PENDING, 1); } else { pm_set_status(LPC_ACPI_CMD, EC_LPC_STATUS_SCI_PENDING, 0); } /* Copy host events to mapped memory */ *(uint32_t *)host_get_memmap(EC_MEMMAP_HOST_EVENTS) = host_events; task_enable_irq(IT83XX_IRQ_PMC_IN); /* Process the wake events. */ lpc_update_wake(host_events & event_mask[LPC_HOST_EVENT_WAKE]); /* Send pulse on SMI signal if needed */ if (need_smi) lpc_generate_smi(); /* ACPI 5.0-12.6.1: Generate SCI for SCI_EVT=1. */ if (need_sci) lpc_generate_sci(); }
/** * Update the host event status. * * Sends a pulse if masked event status becomes non-zero: * - SMI pulse via EC_SMI_L GPIO * - SCI pulse via LPC0SCI */ static void update_host_event_status(void) { int need_sci = 0; int need_smi = 0; if (!init_done) return; /* Disable LPC interrupt while updating status register */ task_disable_irq(LM4_IRQ_LPC); if (host_events & event_mask[LPC_HOST_EVENT_SMI]) { /* Only generate SMI for first event */ if (!(LM4_LPC_ST(LPC_CH_ACPI) & LM4_LPC_ST_SMI)) need_smi = 1; LM4_LPC_ST(LPC_CH_ACPI) |= LM4_LPC_ST_SMI; } else LM4_LPC_ST(LPC_CH_ACPI) &= ~LM4_LPC_ST_SMI; if (host_events & event_mask[LPC_HOST_EVENT_SCI]) { /* Generate SCI for every event */ need_sci = 1; LM4_LPC_ST(LPC_CH_ACPI) |= LM4_LPC_ST_SCI; } else LM4_LPC_ST(LPC_CH_ACPI) &= ~LM4_LPC_ST_SCI; /* Copy host events to mapped memory */ *(uint32_t *)host_get_memmap(EC_MEMMAP_HOST_EVENTS) = host_events; task_enable_irq(LM4_IRQ_LPC); /* Process the wake events. */ lpc_update_wake(host_events & event_mask[LPC_HOST_EVENT_WAKE]); /* Send pulse on SMI signal if needed */ if (need_smi) lpc_generate_smi(); /* ACPI 5.0-12.6.1: Generate SCI for SCI_EVT=1. */ if (need_sci) lpc_generate_sci(); }
static int command_mmapinfo(int argc, char **argv) { uint8_t *memmap_switches = host_get_memmap(EC_MEMMAP_SWITCHES); uint8_t val = *memmap_switches; int i; const char *explanation[] = { "lid_open", "powerbtn", "wp_off", "kbd_rec", "gpio_rec", "fake_dev", }; ccprintf("memmap switches = 0x%x\n", val); for (i = 0; i < ARRAY_SIZE(explanation); i++) if (val & (1 << i)) ccprintf(" %s\n", explanation[i]); return EC_SUCCESS; }
/** * Update the host event status. * * Sends a pulse if masked event status becomes non-zero: * - SMI pulse via EC_SMI_L GPIO * - SCI pulse via LPC0SCI */ static void update_host_event_status(void) { int need_sci = 0; int need_smi = 0; if (!init_done) return; /* Disable LPC interrupt while updating status register */ lpc_task_disable_irq(); if (host_events & event_mask[LPC_HOST_EVENT_SMI]) { /* Only generate SMI for first event */ if (!(NPCX_HIPMIE(PMC_ACPI) & NPCX_HIPMIE_SMIE)) need_smi = 1; SET_BIT(NPCX_HIPMIE(PMC_ACPI), NPCX_HIPMIE_SMIE); } else CLEAR_BIT(NPCX_HIPMIE(PMC_ACPI), NPCX_HIPMIE_SMIE); if (host_events & event_mask[LPC_HOST_EVENT_SCI]) { /* Generate SCI for every event */ need_sci = 1; SET_BIT(NPCX_HIPMIE(PMC_ACPI), NPCX_HIPMIE_SCIE); } else CLEAR_BIT(NPCX_HIPMIE(PMC_ACPI), NPCX_HIPMIE_SCIE); /* Copy host events to mapped memory */ *(uint32_t *)host_get_memmap(EC_MEMMAP_HOST_EVENTS) = host_events; lpc_task_enable_irq(); /* Process the wake events. */ lpc_update_wake(host_events & event_mask[LPC_HOST_EVENT_WAKE]); /* Send pulse on SMI signal if needed */ if (need_smi) lpc_generate_smi(); /* ACPI 5.0-12.6.1: Generate SCI for SCI_EVT=1. */ if (need_sci) lpc_generate_sci(); }
void host_set_events(uint32_t mask) { /* Only print if something's about to change */ if ((events & mask) != mask || (events_copy_b & mask) != mask) CPRINTS("event set 0x%08x", mask); atomic_or(&events, mask); atomic_or(&events_copy_b, mask); #ifdef CONFIG_LPC lpc_set_host_event_state(events); #else *(uint32_t *)host_get_memmap(EC_MEMMAP_HOST_EVENTS) = events; #ifdef CONFIG_MKBP_EVENT #ifdef CONFIG_MKBP_USE_HOST_EVENT #error "Config error: MKBP must not be on top of host event" #endif mkbp_send_event(EC_MKBP_EVENT_HOST_EVENT); #endif /* CONFIG_MKBP_EVENT */ #endif /* !CONFIG_LPC */ }
void mkbp_send_event(uint8_t event_type) { set_event(event_type); #ifdef CONFIG_MKBP_WAKEUP_MASK /* checking the event if AP suspended */ if (chipset_in_state(CHIPSET_STATE_SUSPEND)) { uint32_t events; events = *(uint32_t *)host_get_memmap(EC_MEMMAP_HOST_EVENTS); /* * interrupt the AP if it is a wakeup event * which is defined in the white list. */ if ((events & CONFIG_MKBP_WAKEUP_MASK) || (event_type == EC_MKBP_EVENT_KEY_MATRIX)) set_host_interrupt(1); return; } #endif set_host_interrupt(1); }
static void update_dynamic_battery_info(void) { /* The memmap address is constant. We should fix these calls somehow. */ int *memmap_volt = (int *)host_get_memmap(EC_MEMMAP_BATT_VOLT); int *memmap_rate = (int *)host_get_memmap(EC_MEMMAP_BATT_RATE); int *memmap_cap = (int *)host_get_memmap(EC_MEMMAP_BATT_CAP); int *memmap_lfcc = (int *)host_get_memmap(EC_MEMMAP_BATT_LFCC); uint8_t *memmap_flags = host_get_memmap(EC_MEMMAP_BATT_FLAG); uint8_t tmp; int send_batt_status_event = 0; int send_batt_info_event = 0; static int __bss_slow batt_present; tmp = 0; if (curr.ac) tmp |= EC_BATT_FLAG_AC_PRESENT; if (curr.batt.is_present == BP_YES) { tmp |= EC_BATT_FLAG_BATT_PRESENT; batt_present = 1; /* Tell the AP to read battery info if it is newly present. */ if (!(*memmap_flags & EC_BATT_FLAG_BATT_PRESENT)) send_batt_info_event++; } else { /* * Require two consecutive updates with BP_NOT_SURE * before reporting it gone to the host. */ if (batt_present) tmp |= EC_BATT_FLAG_BATT_PRESENT; else if (*memmap_flags & EC_BATT_FLAG_BATT_PRESENT) send_batt_info_event++; batt_present = 0; } if (!(curr.batt.flags & BATT_FLAG_BAD_VOLTAGE)) *memmap_volt = curr.batt.voltage; if (!(curr.batt.flags & BATT_FLAG_BAD_CURRENT)) *memmap_rate = ABS(curr.batt.current); if (!(curr.batt.flags & BATT_FLAG_BAD_REMAINING_CAPACITY)) { /* * If we're running off the battery, it must have some charge. * Don't report zero charge, as that has special meaning * to Chrome OS powerd. */ if (curr.batt.remaining_capacity == 0 && !curr.batt_is_charging) *memmap_cap = 1; else *memmap_cap = curr.batt.remaining_capacity; } if (!(curr.batt.flags & BATT_FLAG_BAD_FULL_CAPACITY) && (curr.batt.full_capacity <= (*memmap_lfcc - LFCC_EVENT_THRESH) || curr.batt.full_capacity >= (*memmap_lfcc + LFCC_EVENT_THRESH))) { *memmap_lfcc = curr.batt.full_capacity; /* Poke the AP if the full_capacity changes. */ send_batt_info_event++; } if (curr.batt.is_present == BP_YES && !(curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE) && curr.batt.state_of_charge <= BATTERY_LEVEL_CRITICAL) tmp |= EC_BATT_FLAG_LEVEL_CRITICAL; tmp |= curr.batt_is_charging ? EC_BATT_FLAG_CHARGING : EC_BATT_FLAG_DISCHARGING; /* Tell the AP to re-read battery status if charge state changes */ if (*memmap_flags != tmp) send_batt_status_event++; /* Update flags before sending host events. */ *memmap_flags = tmp; if (send_batt_info_event) host_set_single_event(EC_HOST_EVENT_BATTERY); if (send_batt_status_event) host_set_single_event(EC_HOST_EVENT_BATTERY_STATUS); }
/* Returns zero if every item was updated. */ static int update_static_battery_info(void) { char *batt_str; int batt_serial; /* * The return values have type enum ec_error_list, but EC_SUCCESS is * zero. We'll just look for any failures so we can try them all again. */ int rv; /* Smart battery serial number is 16 bits */ batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_SERIAL); memset(batt_str, 0, EC_MEMMAP_TEXT_MAX); rv = battery_serial_number(&batt_serial); if (!rv) snprintf(batt_str, EC_MEMMAP_TEXT_MAX, "%04X", batt_serial); /* Design Capacity of Full */ rv |= battery_design_capacity( (int *)host_get_memmap(EC_MEMMAP_BATT_DCAP)); /* Design Voltage */ rv |= battery_design_voltage( (int *)host_get_memmap(EC_MEMMAP_BATT_DVLT)); /* Last Full Charge Capacity (this is only mostly static) */ rv |= battery_full_charge_capacity( (int *)host_get_memmap(EC_MEMMAP_BATT_LFCC)); /* Cycle Count */ rv |= battery_cycle_count((int *)host_get_memmap(EC_MEMMAP_BATT_CCNT)); /* Battery Manufacturer string */ batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_MFGR); memset(batt_str, 0, EC_MEMMAP_TEXT_MAX); rv |= battery_manufacturer_name(batt_str, EC_MEMMAP_TEXT_MAX); /* Battery Model string */ batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_MODEL); memset(batt_str, 0, EC_MEMMAP_TEXT_MAX); rv |= battery_device_name(batt_str, EC_MEMMAP_TEXT_MAX); /* Battery Type string */ batt_str = (char *)host_get_memmap(EC_MEMMAP_BATT_TYPE); rv |= battery_device_chemistry(batt_str, EC_MEMMAP_TEXT_MAX); /* Zero the dynamic entries. They'll come next. */ *(int *)host_get_memmap(EC_MEMMAP_BATT_VOLT) = 0; *(int *)host_get_memmap(EC_MEMMAP_BATT_RATE) = 0; *(int *)host_get_memmap(EC_MEMMAP_BATT_CAP) = 0; *(int *)host_get_memmap(EC_MEMMAP_BATT_LFCC) = 0; *host_get_memmap(EC_MEMMAP_BATT_FLAG) = 0; if (rv) problem(PR_STATIC_UPDATE, rv); else /* No errors seen. Battery data is now present */ *host_get_memmap(EC_MEMMAP_BATTERY_VERSION) = 1; return rv; }
/* * Motion Sense Task * Requirement: motion_sensors[] are defined in board.c file. * Two (minimium) Accelerometers: * 1 in the A/B(lid, display) and 1 in the C/D(base, keyboard) * Gyro Sensor (optional) */ void motion_sense_task(void) { int i, ret, wait_us, fifo_flush_needed = 0; timestamp_t ts_begin_task, ts_end_task; uint32_t event = 0; uint16_t ready_status; struct motion_sensor_t *sensor; #ifdef CONFIG_LID_ANGLE const uint16_t lid_angle_sensors = ((1 << CONFIG_LID_ANGLE_SENSOR_BASE)| (1 << CONFIG_LID_ANGLE_SENSOR_LID)); #endif #ifdef CONFIG_ACCEL_FIFO timestamp_t ts_last_int; #endif #ifdef CONFIG_LPC int sample_id = 0; uint8_t *lpc_status; uint16_t *lpc_data; lpc_status = host_get_memmap(EC_MEMMAP_ACC_STATUS); lpc_data = (uint16_t *)host_get_memmap(EC_MEMMAP_ACC_DATA); set_present(lpc_status); #endif #ifdef CONFIG_ACCEL_FIFO ts_last_int = get_time(); #endif do { ts_begin_task = get_time(); ready_status = 0; for (i = 0; i < motion_sensor_count; ++i) { sensor = &motion_sensors[i]; /* if the sensor is active in the current power state */ if (SENSOR_ACTIVE(sensor)) { if (sensor->state != SENSOR_INITIALIZED) { continue; } ts_begin_task = get_time(); ret = motion_sense_process(sensor, event, &ts_begin_task, &fifo_flush_needed); if (ret != EC_SUCCESS) continue; ready_status |= (1 << i); } } #ifdef CONFIG_GESTURE_DETECTION /* Run gesture recognition engine */ gesture_calc(); #endif #ifdef CONFIG_LID_ANGLE /* * Check to see that the sensors required for lid angle * calculation are ready. */ ready_status &= lid_angle_sensors; if (ready_status == lid_angle_sensors) motion_lid_calc(); #endif #ifdef CONFIG_CMD_ACCEL_INFO if (accel_disp) { CPRINTF("[%T event 0x%08x ", event); for (i = 0; i < motion_sensor_count; ++i) { sensor = &motion_sensors[i]; CPRINTF("%s=%-5d, %-5d, %-5d ", sensor->name, sensor->xyz[X], sensor->xyz[Y], sensor->xyz[Z]); } #ifdef CONFIG_LID_ANGLE CPRINTF("a=%-4d", motion_lid_get_angle()); #endif CPRINTF("]\n"); } #endif #ifdef CONFIG_LPC update_sense_data(lpc_status, lpc_data, &sample_id); #endif ts_end_task = get_time(); #ifdef CONFIG_ACCEL_FIFO /* * Ask the host to flush the queue if * - a flush event has been queued. * - the queue is almost full, * - we haven't done it for a while. */ if (fifo_flush_needed || event & TASK_EVENT_MOTION_ODR_CHANGE || queue_space(&motion_sense_fifo) < CONFIG_ACCEL_FIFO_THRES || (accel_interval > 0 && (ts_end_task.val - ts_last_int.val) > accel_interval)) { if (!fifo_flush_needed) motion_sense_insert_timestamp(); fifo_flush_needed = 0; ts_last_int = ts_end_task; #ifdef CONFIG_MKBP_EVENT /* * We don't currently support wake up sensor. * When we do, add per sensor test to know * when sending the event. */ if (sensor_active == SENSOR_ACTIVE_S0) mkbp_send_event(EC_MKBP_EVENT_SENSOR_FIFO); #endif } #endif if (accel_interval > 0) { /* * Delay appropriately to keep sampling time * consistent. */ wait_us = accel_interval - (ts_end_task.val - ts_begin_task.val); /* * Guarantee some minimum delay to allow other lower * priority tasks to run. */ if (wait_us < MIN_MOTION_SENSE_WAIT_TIME) wait_us = MIN_MOTION_SENSE_WAIT_TIME; } else { wait_us = -1; } } while ((event = task_wait_event(wait_us))); }
/* Test utilities */ static int test_lid_angle(void) { uint8_t *lpc_status = host_get_memmap(EC_MEMMAP_ACC_STATUS); uint8_t sample; struct motion_sensor_t *base = &motion_sensors[0]; struct motion_sensor_t *lid = &motion_sensors[1]; /* Go to S3 state */ hook_notify(HOOK_CHIPSET_STARTUP); /* Go to S0 state */ hook_notify(HOOK_CHIPSET_RESUME); /* * Set the base accelerometer as if it were sitting flat on a desk * and set the lid to closed. */ base->xyz[X] = 0; base->xyz[Y] = 0; base->xyz[Z] = 1000; lid->xyz[X] = 0; lid->xyz[Y] = 0; lid->xyz[Z] = 1000; sample = *lpc_status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK; task_wake(TASK_ID_MOTIONSENSE); while ((*lpc_status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK) == sample) msleep(5); TEST_ASSERT(motion_lid_get_angle() == 0); /* Set lid open to 90 degrees. */ lid->xyz[X] = -1000; lid->xyz[Y] = 0; lid->xyz[Z] = 0; sample = *lpc_status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK; task_wake(TASK_ID_MOTIONSENSE); while ((*lpc_status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK) == sample) msleep(5); TEST_ASSERT(motion_lid_get_angle() == 90); /* Set lid open to 225. */ lid->xyz[X] = 500; lid->xyz[Y] = 0; lid->xyz[Z] = -500; sample = *lpc_status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK; task_wake(TASK_ID_MOTIONSENSE); while ((*lpc_status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK) == sample) msleep(5); TEST_ASSERT(motion_lid_get_angle() == 225); /* * Align base with hinge and make sure it returns unreliable for angle. * In this test it doesn't matter what the lid acceleration vector is. */ base->xyz[X] = 0; base->xyz[Y] = 1000; base->xyz[Z] = 0; sample = *lpc_status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK; task_wake(TASK_ID_MOTIONSENSE); while ((*lpc_status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK) == sample) msleep(5); TEST_ASSERT(motion_lid_get_angle() == LID_ANGLE_UNRELIABLE); /* * Use all three axes and set lid to negative base and make sure * angle is 180. */ base->xyz[X] = 500; base->xyz[Y] = 400; base->xyz[Z] = 300; lid->xyz[X] = -500; lid->xyz[Y] = -400; lid->xyz[Z] = -300; sample = *lpc_status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK; task_wake(TASK_ID_MOTIONSENSE); while ((*lpc_status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK) == sample) msleep(5); TEST_ASSERT(motion_lid_get_angle() == 180); return EC_SUCCESS; }
static int host_cmd_motion_sense(struct host_cmd_handler_args *args) { const struct ec_params_motion_sense *in = args->params; struct ec_response_motion_sense *out = args->response; struct motion_sensor_t *sensor; int i, ret = EC_RES_INVALID_PARAM, reported; switch (in->cmd) { case MOTIONSENSE_CMD_DUMP: out->dump.module_flags = (*(host_get_memmap(EC_MEMMAP_ACC_STATUS)) & EC_MEMMAP_ACC_STATUS_PRESENCE_BIT) ? MOTIONSENSE_MODULE_FLAG_ACTIVE : 0; out->dump.sensor_count = motion_sensor_count; args->response_size = sizeof(out->dump); reported = MIN(motion_sensor_count, in->dump.max_sensor_count); mutex_lock(&g_sensor_mutex); for (i = 0; i < reported; i++) { sensor = &motion_sensors[i]; out->dump.sensor[i].flags = MOTIONSENSE_SENSOR_FLAG_PRESENT; /* casting from int to s16 */ out->dump.sensor[i].data[X] = sensor->xyz[X]; out->dump.sensor[i].data[Y] = sensor->xyz[Y]; out->dump.sensor[i].data[Z] = sensor->xyz[Z]; } mutex_unlock(&g_sensor_mutex); args->response_size += reported * sizeof(struct ec_response_motion_sensor_data); break; case MOTIONSENSE_CMD_DATA: sensor = host_sensor_id_to_motion_sensor( in->sensor_odr.sensor_num); if (sensor == NULL) return EC_RES_INVALID_PARAM; out->data.flags = 0; mutex_lock(&g_sensor_mutex); out->data.data[X] = sensor->xyz[X]; out->data.data[Y] = sensor->xyz[Y]; out->data.data[Z] = sensor->xyz[Z]; mutex_unlock(&g_sensor_mutex); args->response_size = sizeof(out->data); break; case MOTIONSENSE_CMD_INFO: sensor = host_sensor_id_to_motion_sensor( in->sensor_odr.sensor_num); if (sensor == NULL) return EC_RES_INVALID_PARAM; out->info.type = sensor->type; out->info.location = sensor->location; out->info.chip = sensor->chip; args->response_size = sizeof(out->info); break; case MOTIONSENSE_CMD_EC_RATE: sensor = host_sensor_id_to_motion_sensor( in->sensor_odr.sensor_num); if (sensor == NULL) return EC_RES_INVALID_PARAM; /* * Set new sensor sampling rate when AP is on, if the data arg * has a value. */ if (in->ec_rate.data != EC_MOTION_SENSE_NO_VALUE) { if (in->ec_rate.data == 0) sensor->config[SENSOR_CONFIG_AP].ec_rate = 0; else sensor->config[SENSOR_CONFIG_AP].ec_rate = MAX(in->ec_rate.data, MIN_MOTION_SENSE_WAIT_TIME / MSEC); /* Bound the new sampling rate. */ motion_sense_set_accel_interval(); } out->ec_rate.ret = motion_sense_ec_rate(sensor) / MSEC; args->response_size = sizeof(out->ec_rate); break; case MOTIONSENSE_CMD_SENSOR_ODR: /* Verify sensor number is valid. */ sensor = host_sensor_id_to_motion_sensor( in->sensor_odr.sensor_num); if (sensor == NULL) return EC_RES_INVALID_PARAM; /* Set new data rate if the data arg has a value. */ if (in->sensor_odr.data != EC_MOTION_SENSE_NO_VALUE) { sensor->config[SENSOR_CONFIG_AP].odr = in->sensor_odr.data | (in->sensor_odr.roundup ? ROUND_UP_FLAG : 0); ret = motion_sense_set_data_rate(sensor); if (ret != EC_SUCCESS) return EC_RES_INVALID_PARAM; /* * To be sure timestamps are calculated properly, * Send an event to have a timestamp inserted in the * FIFO. */ task_set_event(TASK_ID_MOTIONSENSE, TASK_EVENT_MOTION_ODR_CHANGE, 0); /* * If the sensor was suspended before, or now * suspended, we have to recalculate the EC sampling * rate */ motion_sense_set_accel_interval(); } out->sensor_odr.ret = sensor->drv->get_data_rate(sensor); args->response_size = sizeof(out->sensor_odr); break; case MOTIONSENSE_CMD_SENSOR_RANGE: /* Verify sensor number is valid. */ sensor = host_sensor_id_to_motion_sensor( in->sensor_range.sensor_num); if (sensor == NULL) return EC_RES_INVALID_PARAM; /* Set new range if the data arg has a value. */ if (in->sensor_range.data != EC_MOTION_SENSE_NO_VALUE) { if (sensor->drv->set_range(sensor, in->sensor_range.data, in->sensor_range.roundup) != EC_SUCCESS) { return EC_RES_INVALID_PARAM; } } out->sensor_range.ret = sensor->drv->get_range(sensor); args->response_size = sizeof(out->sensor_range); break; case MOTIONSENSE_CMD_SENSOR_OFFSET: /* Verify sensor number is valid. */ sensor = host_sensor_id_to_motion_sensor( in->sensor_offset.sensor_num); if (sensor == NULL) return EC_RES_INVALID_PARAM; /* Set new range if the data arg has a value. */ if (in->sensor_offset.flags & MOTION_SENSE_SET_OFFSET) { ret = sensor->drv->set_offset(sensor, in->sensor_offset.offset, in->sensor_offset.temp); if (ret != EC_SUCCESS) return ret; } ret = sensor->drv->get_offset(sensor, out->sensor_offset.offset, &out->sensor_offset.temp); if (ret != EC_SUCCESS) return ret; args->response_size = sizeof(out->sensor_offset); break; case MOTIONSENSE_CMD_PERFORM_CALIB: /* Verify sensor number is valid. */ sensor = host_sensor_id_to_motion_sensor( in->sensor_offset.sensor_num); if (sensor == NULL) return EC_RES_INVALID_PARAM; if (!sensor->drv->perform_calib) return EC_RES_INVALID_COMMAND; ret = sensor->drv->perform_calib(sensor); if (ret != EC_SUCCESS) return ret; ret = sensor->drv->get_offset(sensor, out->sensor_offset.offset, &out->sensor_offset.temp); if (ret != EC_SUCCESS) return ret; args->response_size = sizeof(out->sensor_offset); break; #ifdef CONFIG_ACCEL_FIFO case MOTIONSENSE_CMD_FIFO_FLUSH: sensor = host_sensor_id_to_motion_sensor( in->sensor_odr.sensor_num); if (sensor == NULL) return EC_RES_INVALID_PARAM; atomic_add(&sensor->flush_pending, 1); task_set_event(TASK_ID_MOTIONSENSE, TASK_EVENT_MOTION_FLUSH_PENDING, 0); /* passthrough */ case MOTIONSENSE_CMD_FIFO_INFO: motion_sense_get_fifo_info(&out->fifo_info); for (i = 0; i < motion_sensor_count; i++) { out->fifo_info.lost[i] = motion_sensors[i].lost; motion_sensors[i].lost = 0; } motion_sense_fifo_lost = 0; args->response_size = sizeof(out->fifo_info) + sizeof(uint16_t) * motion_sensor_count; break; case MOTIONSENSE_CMD_FIFO_READ: mutex_lock(&g_sensor_mutex); reported = MIN((args->response_max - sizeof(out->fifo_read)) / motion_sense_fifo.unit_bytes, MIN(queue_count(&motion_sense_fifo), in->fifo_read.max_data_vector)); reported = queue_remove_units(&motion_sense_fifo, out->fifo_read.data, reported); mutex_unlock(&g_sensor_mutex); out->fifo_read.number_data = reported; args->response_size = sizeof(out->fifo_read) + reported * motion_sense_fifo.unit_bytes; break; #else case MOTIONSENSE_CMD_FIFO_INFO: /* Only support the INFO command, to tell there is no FIFO. */ memset(&out->fifo_info, 0, sizeof(out->fifo_info)); args->response_size = sizeof(out->fifo_info); break; #endif default: /* Call other users of the motion task */ #ifdef CONFIG_LID_ANGLE if (ret == EC_RES_INVALID_PARAM) ret = host_cmd_motion_lid(args); #endif return ret; } return EC_RES_SUCCESS; }
/** * Common handler for charging states. * * This handler gets battery charging parameters, charger state, ac state, and * timestamp. It also fills memory map and issues power events on state change. */ static int state_common(struct charge_state_context *ctx) { int rv, d; struct charge_state_data *curr = &ctx->curr; struct charge_state_data *prev = &ctx->prev; struct batt_params *batt = &ctx->curr.batt; uint8_t *batt_flags = ctx->memmap_batt_flags; /* Copy previous state and init new state */ ctx->prev = ctx->curr; curr->ts = get_time(); curr->error = 0; /* Detect AC change */ curr->ac = charge_get_flags() & CHARGE_FLAG_EXTERNAL_POWER; if (curr->ac != prev->ac) { if (curr->ac) { /* AC on * Initialize charger to power on reset mode */ rv = charger_post_init(); if (rv) curr->error |= F_CHARGER_INIT; } } if (curr->ac) { *batt_flags |= EC_BATT_FLAG_AC_PRESENT; if (charger_get_voltage(&curr->charging_voltage)) { charge_request(0, 0); curr->error |= F_CHARGER_VOLTAGE; } if (charger_get_current(&curr->charging_current)) { charge_request(0, 0); curr->error |= F_CHARGER_CURRENT; } #ifdef CONFIG_CHARGER_EN_GPIO if (!charge_get_charger_en_gpio()) { curr->charging_voltage = 0; curr->charging_current = 0; } #endif } else { *batt_flags &= ~EC_BATT_FLAG_AC_PRESENT; /* AC disconnected should get us out of force idle mode. */ state_machine_force_idle = 0; } #if defined(CONFIG_BATTERY_PRESENT_CUSTOM) || \ defined(CONFIG_BATTERY_PRESENT_GPIO) if (battery_is_present() == BP_NO) { curr->error |= F_BATTERY_NOT_CONNECTED; return curr->error; } #endif /* Read params and see if battery is responsive */ battery_get_params(batt); if (!(batt->flags & BATT_FLAG_RESPONSIVE)) { /* Check low battery condition and retry */ if (curr->ac && ctx->battery_responsive && !(curr->error & F_CHARGER_MASK)) { ctx->battery_responsive = 0; /* * Try to revive ultra low voltage pack. Charge * battery pack with minimum current and maximum * voltage for 30 seconds. */ charge_request(ctx->battery->voltage_max, ctx->battery->precharge_current); for (d = 0; d < PRECHARGE_TIMEOUT; d++) { sleep(1); battery_get_params(batt); if (batt->flags & BATT_FLAG_RESPONSIVE) { ctx->battery_responsive = 1; break; } } } /* Set error if battery is still unresponsive */ if (!(batt->flags & BATT_FLAG_RESPONSIVE)) { curr->error |= F_BATTERY_UNRESPONSIVE; return curr->error; } } else { ctx->battery_responsive = 1; } /* Translate flags */ if (batt->flags & BATT_FLAG_BAD_ANY) curr->error |= F_BATTERY_GET_PARAMS; if (batt->flags & BATT_FLAG_BAD_VOLTAGE) curr->error |= F_BATTERY_VOLTAGE; if (batt->flags & BATT_FLAG_BAD_STATE_OF_CHARGE) curr->error |= F_BATTERY_STATE_OF_CHARGE; *ctx->memmap_batt_volt = batt->voltage; /* Memory mapped value: discharge rate */ *ctx->memmap_batt_rate = batt->current < 0 ? -batt->current : batt->current; /* Fake state of charge if necessary */ if (fake_state_of_charge >= 0) { batt->state_of_charge = fake_state_of_charge; curr->error &= ~F_BATTERY_STATE_OF_CHARGE; } if (batt->state_of_charge != prev->batt.state_of_charge) { rv = battery_full_charge_capacity(&d); if (!rv && d != *(int *)host_get_memmap(EC_MEMMAP_BATT_LFCC)) { *(int *)host_get_memmap(EC_MEMMAP_BATT_LFCC) = d; /* Notify host to re-read battery information */ host_set_single_event(EC_HOST_EVENT_BATTERY); } } /* Prevent deep discharging */ if (!curr->ac) { if ((batt->state_of_charge < BATTERY_LEVEL_SHUTDOWN && !(curr->error & F_BATTERY_STATE_OF_CHARGE)) || (batt->voltage <= ctx->battery->voltage_min && !(curr->error & F_BATTERY_VOLTAGE))) low_battery_shutdown(ctx); } /* Check battery presence */ if (curr->error & F_BATTERY_MASK) { *ctx->memmap_batt_flags &= ~EC_BATT_FLAG_BATT_PRESENT; return curr->error; } *ctx->memmap_batt_flags |= EC_BATT_FLAG_BATT_PRESENT; /* Battery charge level low */ if (batt->state_of_charge <= BATTERY_LEVEL_LOW && prev->batt.state_of_charge > BATTERY_LEVEL_LOW) host_set_single_event(EC_HOST_EVENT_BATTERY_LOW); /* Battery charge level critical */ if (batt->state_of_charge <= BATTERY_LEVEL_CRITICAL) { *ctx->memmap_batt_flags |= EC_BATT_FLAG_LEVEL_CRITICAL; /* Send battery critical host event */ if (prev->batt.state_of_charge > BATTERY_LEVEL_CRITICAL) host_set_single_event(EC_HOST_EVENT_BATTERY_CRITICAL); } else { *ctx->memmap_batt_flags &= ~EC_BATT_FLAG_LEVEL_CRITICAL; } #ifdef CONFIG_BATTERY_OVERRIDE_PARAMS /* Apply battery pack vendor charging method */ battery_override_params(batt); #endif #ifdef CONFIG_CHARGER_CURRENT_LIMIT if (batt->desired_current > CONFIG_CHARGER_CURRENT_LIMIT) batt->desired_current = CONFIG_CHARGER_CURRENT_LIMIT; #endif if (batt->desired_current > user_current_limit) batt->desired_current = user_current_limit; if (fake_state_of_charge >= 0) *ctx->memmap_batt_cap = fake_state_of_charge * *(int *)host_get_memmap(EC_MEMMAP_BATT_LFCC) / 100; else if (battery_remaining_capacity(&d)) ctx->curr.error |= F_BATTERY_CAPACITY; else *ctx->memmap_batt_cap = d; return ctx->curr.error; }