static acpi_status acpi_hw_read_multiple(u32 *value, struct acpi_generic_address *register_a, struct acpi_generic_address *register_b) { u32 value_a = 0; u32 value_b = 0; acpi_status status; /* The first register is always required */ status = acpi_read(&value_a, register_a); if (ACPI_FAILURE(status)) { return (status); } /* Second register is optional */ if (register_b->address) { status = acpi_read(&value_b, register_b); if (ACPI_FAILURE(status)) { return (status); } } /* * OR the two return values together. No shifting or masking is necessary, * because of how the PM1 registers are defined in the ACPI specification: * * "Although the bits can be split between the two register blocks (each * register block has a unique pointer within the FADT), the bit positions * are maintained. The register block with unimplemented bits (that is, * those implemented in the other register block) always returns zeros, * and writes have no side effects" */ *value = (value_a | value_b); return (AE_OK); }
/** * pcc_send_data - Called from Mailbox Controller code. Used * here only to ring the channel doorbell. The PCC client * specific read/write is done in the client driver in * order to maintain atomicity over PCC channel once * OS has control over it. See above for flow of operations. * @chan: Pointer to Mailbox channel over which to send data. * @data: Client specific data written over channel. Used here * only for debug after PCC transaction completes. * * Return: Err if something failed else 0 for success. */ static int pcc_send_data(struct mbox_chan *chan, void *data) { struct acpi_pcct_hw_reduced *pcct_ss = chan->con_priv; struct acpi_generic_address doorbell; u64 doorbell_preserve; u64 doorbell_val; u64 doorbell_write; doorbell = pcct_ss->doorbell_register; doorbell_preserve = pcct_ss->preserve_mask; doorbell_write = pcct_ss->write_mask; /* Sync notification from OS to Platform. */ acpi_read(&doorbell_val, &doorbell); acpi_write((doorbell_val & doorbell_preserve) | doorbell_write, &doorbell); return 0; }
/** * pcc_send_data - Called from Mailbox Controller code. Used * here only to ring the channel doorbell. The PCC client * specific read/write is done in the client driver in * order to maintain atomicity over PCC channel once * OS has control over it. See above for flow of operations. * @chan: Pointer to Mailbox channel over which to send data. * @data: Client specific data written over channel. Used here * only for debug after PCC transaction completes. * * Return: Err if something failed else 0 for success. */ static int pcc_send_data(struct mbox_chan *chan, void *data) { struct acpi_pcct_hw_reduced *pcct_ss = chan->con_priv; struct acpi_generic_address *doorbell; u64 doorbell_preserve; u64 doorbell_val; u64 doorbell_write; u32 id = chan - pcc_mbox_channels; int ret = 0; if (id >= pcc_mbox_ctrl.num_chans) { pr_debug("pcc_send_data: Invalid mbox_chan passed\n"); return -ENOENT; } doorbell = &pcct_ss->doorbell_register; doorbell_preserve = pcct_ss->preserve_mask; doorbell_write = pcct_ss->write_mask; /* Sync notification from OS to Platform. */ if (pcc_doorbell_vaddr[id]) { ret = read_register(pcc_doorbell_vaddr[id], &doorbell_val, doorbell->bit_width); if (ret) return ret; ret = write_register(pcc_doorbell_vaddr[id], (doorbell_val & doorbell_preserve) | doorbell_write, doorbell->bit_width); } else { ret = acpi_read(&doorbell_val, doorbell); if (ret) return ret; ret = acpi_write((doorbell_val & doorbell_preserve) | doorbell_write, doorbell); } return ret; }
acpi_status acpi_hw_extended_sleep(u8 sleep_state) { acpi_status status; u8 sleep_control; u64 sleep_status; ACPI_FUNCTION_TRACE(hw_extended_sleep); /* Extended sleep registers must be valid */ if (!acpi_gbl_FADT.sleep_control.address || !acpi_gbl_FADT.sleep_status.address) { return_ACPI_STATUS(AE_NOT_EXIST); } /* Clear wake status (WAK_STS) */ status = acpi_write((u64)ACPI_X_WAKE_STATUS, &acpi_gbl_FADT.sleep_status); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } acpi_gbl_system_awake_and_running = FALSE; /* * Set the SLP_TYP and SLP_EN bits. * * Note: We only use the first value returned by the \_Sx method * (acpi_gbl_sleep_type_a) - As per ACPI specification. */ ACPI_DEBUG_PRINT((ACPI_DB_INIT, "Entering sleep state [S%u]\n", sleep_state)); sleep_control = ((acpi_gbl_sleep_type_a << ACPI_X_SLEEP_TYPE_POSITION) & ACPI_X_SLEEP_TYPE_MASK) | ACPI_X_SLEEP_ENABLE; /* Flush caches, as per ACPI specification */ ACPI_FLUSH_CPU_CACHE(); status = acpi_os_enter_sleep(sleep_state, sleep_control, 0); if (status == AE_CTRL_TERMINATE) { return_ACPI_STATUS(AE_OK); } if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } status = acpi_write((u64)sleep_control, &acpi_gbl_FADT.sleep_control); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } /* Wait for transition back to Working State */ do { status = acpi_read(&sleep_status, &acpi_gbl_FADT.sleep_status); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } } while (!(((u8)sleep_status) & ACPI_X_WAKE_STATUS)); return_ACPI_STATUS(AE_OK); }
acpi_status acpi_hw_register_write(u32 register_id, u32 value) { acpi_status status; u32 read_value; ACPI_FUNCTION_TRACE(hw_register_write); switch (register_id) { case ACPI_REGISTER_PM1_STATUS: /* PM1 A/B: 16-bit access each */ /* * Handle the "ignored" bit in PM1 Status. According to the ACPI * specification, ignored bits are to be preserved when writing. * Normally, this would mean a read/modify/write sequence. However, * preserving a bit in the status register is different. Writing a * one clears the status, and writing a zero preserves the status. * Therefore, we must always write zero to the ignored bit. * * This behavior is clarified in the ACPI 4.0 specification. */ value &= ~ACPI_PM1_STATUS_PRESERVED_BITS; status = acpi_hw_write_multiple(value, &acpi_gbl_xpm1a_status, &acpi_gbl_xpm1b_status); break; case ACPI_REGISTER_PM1_ENABLE: /* PM1 A/B: 16-bit access */ status = acpi_hw_write_multiple(value, &acpi_gbl_xpm1a_enable, &acpi_gbl_xpm1b_enable); break; case ACPI_REGISTER_PM1_CONTROL: /* PM1 A/B: 16-bit access each */ /* * Perform a read first to preserve certain bits (per ACPI spec) * Note: This includes SCI_EN, we never want to change this bit */ status = acpi_hw_read_multiple(&read_value, &acpi_gbl_FADT. xpm1a_control_block, &acpi_gbl_FADT. xpm1b_control_block); if (ACPI_FAILURE(status)) { goto exit; } /* Insert the bits to be preserved */ ACPI_INSERT_BITS(value, ACPI_PM1_CONTROL_PRESERVED_BITS, read_value); /* Now we can write the data */ status = acpi_hw_write_multiple(value, &acpi_gbl_FADT. xpm1a_control_block, &acpi_gbl_FADT. xpm1b_control_block); break; case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */ /* * For control registers, all reserved bits must be preserved, * as per the ACPI spec. */ status = acpi_read(&read_value, &acpi_gbl_FADT.xpm2_control_block); if (ACPI_FAILURE(status)) { goto exit; } /* Insert the bits to be preserved */ ACPI_INSERT_BITS(value, ACPI_PM2_CONTROL_PRESERVED_BITS, read_value); status = acpi_write(value, &acpi_gbl_FADT.xpm2_control_block); break; case ACPI_REGISTER_PM_TIMER: /* 32-bit access */ status = acpi_write(value, &acpi_gbl_FADT.xpm_timer_block); break; case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */ /* SMI_CMD is currently always in IO space */ status = acpi_hw_write_port(acpi_gbl_FADT.smi_command, value, 8); break; default: ACPI_ERROR((AE_INFO, "Unknown Register ID: %X", register_id)); status = AE_BAD_PARAMETER; break; } exit: return_ACPI_STATUS(status); }
/****************************************************************************** * * FUNCTION: acpi_hw_register_read * * PARAMETERS: register_id - ACPI Register ID * return_value - Where the register value is returned * * RETURN: Status and the value read. * * DESCRIPTION: Read from the specified ACPI register * ******************************************************************************/ acpi_status acpi_hw_register_read(u32 register_id, u32 * return_value) { u32 value = 0; acpi_status status; ACPI_FUNCTION_TRACE(hw_register_read); switch (register_id) { case ACPI_REGISTER_PM1_STATUS: /* PM1 A/B: 16-bit access each */ status = acpi_hw_read_multiple(&value, &acpi_gbl_xpm1a_status, &acpi_gbl_xpm1b_status); break; case ACPI_REGISTER_PM1_ENABLE: /* PM1 A/B: 16-bit access each */ status = acpi_hw_read_multiple(&value, &acpi_gbl_xpm1a_enable, &acpi_gbl_xpm1b_enable); break; case ACPI_REGISTER_PM1_CONTROL: /* PM1 A/B: 16-bit access each */ status = acpi_hw_read_multiple(&value, &acpi_gbl_FADT. xpm1a_control_block, &acpi_gbl_FADT. xpm1b_control_block); /* * Zero the write-only bits. From the ACPI specification, "Hardware * Write-Only Bits": "Upon reads to registers with write-only bits, * software masks out all write-only bits." */ value &= ~ACPI_PM1_CONTROL_WRITEONLY_BITS; break; case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */ status = acpi_read(&value, &acpi_gbl_FADT.xpm2_control_block); break; case ACPI_REGISTER_PM_TIMER: /* 32-bit access */ status = acpi_read(&value, &acpi_gbl_FADT.xpm_timer_block); break; case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */ status = acpi_hw_read_port(acpi_gbl_FADT.smi_command, &value, 8); break; default: ACPI_ERROR((AE_INFO, "Unknown Register ID: %X", register_id)); status = AE_BAD_PARAMETER; break; } if (ACPI_SUCCESS(status)) { *return_value = value; } return_ACPI_STATUS(status); }
u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info * gpe_xrupt_list) { acpi_status status; struct acpi_gpe_block_info *gpe_block; struct acpi_gpe_register_info *gpe_register_info; u32 int_status = ACPI_INTERRUPT_NOT_HANDLED; u8 enabled_status_byte; u32 status_reg; u32 enable_reg; acpi_cpu_flags flags; u32 i; u32 j; ACPI_FUNCTION_NAME(ev_gpe_detect); /* Check for the case where there are no GPEs */ if (!gpe_xrupt_list) { return (int_status); } /* * We need to obtain the GPE lock for both the data structs and registers * Note: Not necessary to obtain the hardware lock, since the GPE * registers are owned by the gpe_lock. */ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); /* Examine all GPE blocks attached to this interrupt level */ gpe_block = gpe_xrupt_list->gpe_block_list_head; while (gpe_block) { /* * Read all of the 8-bit GPE status and enable registers in this GPE * block, saving all of them. Find all currently active GP events. */ for (i = 0; i < gpe_block->register_count; i++) { /* Get the next status/enable pair */ gpe_register_info = &gpe_block->register_info[i]; /* Read the Status Register */ status = acpi_read(&status_reg, &gpe_register_info->status_address); if (ACPI_FAILURE(status)) { goto unlock_and_exit; } /* Read the Enable Register */ status = acpi_read(&enable_reg, &gpe_register_info->enable_address); if (ACPI_FAILURE(status)) { goto unlock_and_exit; } ACPI_DEBUG_PRINT((ACPI_DB_INTERRUPTS, "Read GPE Register at GPE%X: Status=%02X, Enable=%02X\n", gpe_register_info->base_gpe_number, status_reg, enable_reg)); /* Check if there is anything active at all in this register */ enabled_status_byte = (u8) (status_reg & enable_reg); if (!enabled_status_byte) { /* No active GPEs in this register, move on */ continue; } /* Now look at the individual GPEs in this byte register */ for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) { /* Examine one GPE bit */ if (enabled_status_byte & (1 << j)) { /* * Found an active GPE. Dispatch the event to a handler * or method. */ int_status |= acpi_ev_gpe_dispatch(&gpe_block-> event_info[((acpi_size) i * ACPI_GPE_REGISTER_WIDTH) + j], j + gpe_register_info->base_gpe_number); } } } gpe_block = gpe_block->next; } unlock_and_exit: acpi_os_release_lock(acpi_gbl_gpe_lock, flags); return (int_status); }
void main_loop (void) { int activity=0, sleep_now=0, total_unused=0; int sleep_battery=0; int prev_ac_line_status=0; time_t nowtime, oldtime=0; apm_info ai; double loadavg[1]; if (use_events) { pthread_t emthread; pthread_create(&emthread, NULL, eventMonitor, NULL); } while (1) { activity=0; if (use_events) { pthread_mutex_lock(&condition_mutex); pthread_cond_signal(&condition_cond); pthread_mutex_unlock(&condition_mutex); } if (use_acpi) { acpi_read(1, &ai); } #ifdef HAL else if (use_simplehal) { simplehal_read(1, &ai); } #endif else { apm_read(&ai); } if (min_batt != -1 && ai.ac_line_status != 1 && ai.battery_percentage < min_batt && ai.battery_status != BATTERY_STATUS_ABSENT) { sleep_battery = 1; } if (sleep_battery && ! require_unused_and_battery) { syslog(LOG_NOTICE, "battery level %d%% is below %d%%; forcing hibernation", ai.battery_percentage, min_batt); if (system(hibernate_command) != 0) syslog(LOG_ERR, "%s failed", hibernate_command); /* This counts as activity; to prevent double sleeps. */ if (debug) printf("sleepd: activity: just woke up\n"); activity=1; oldtime=0; sleep_battery=0; } /* Rest is only needed if sleeping on inactivity. */ if (! max_unused && ! ac_max_unused) { sleep(sleep_time); continue; } if (autoprobe || have_irqs) { activity=check_irqs(activity, autoprobe); } if (use_net) { activity=check_net(activity); } if ((max_loadavg != 0) && (getloadavg(loadavg, 1) == 1) && (loadavg[0] >= max_loadavg)) { /* If the load average is too high */ if (debug) printf("sleepd: activity: load average %f\n", loadavg[0]); activity=1; } if (use_utmp == 1) { total_unused=check_utmp(total_unused); } if (ai.ac_line_status != prev_ac_line_status) { /* AC plug/unplug counts as activity. */ if (debug) printf("sleepd: activity: AC status change\n"); activity=1; } prev_ac_line_status=ai.ac_line_status; sleep(sleep_time); if (use_events) { pthread_mutex_lock(&activity_mutex); if (eventData.emactivity == 1) { if (debug) printf("sleepd: activity: keyboard/mouse events\n"); activity=1; } pthread_mutex_unlock(&activity_mutex); } if (activity) { total_unused = 0; } else { total_unused += sleep_time; if (ai.ac_line_status == 1) { /* On wall power. */ if (ac_max_unused > 0) { sleep_now = total_unused >= ac_max_unused; } } else if (max_unused > 0) { sleep_now = total_unused >= max_unused; } if (sleep_now && ! no_sleep && ! require_unused_and_battery) { syslog(LOG_NOTICE, "system inactive for %ds; forcing sleep", total_unused); if (system(sleep_command) != 0) syslog(LOG_ERR, "%s failed", sleep_command); total_unused=0; oldtime=0; sleep_now=0; } else if (sleep_now && ! no_sleep && sleep_battery) { syslog(LOG_NOTICE, "system inactive for %ds and battery level %d%% is below %d%%; forcing hibernaton", total_unused, ai.battery_percentage, min_batt); if (system(hibernate_command) != 0) syslog(LOG_ERR, "%s failed", hibernate_command); total_unused=0; oldtime=0; sleep_now=0; sleep_battery=0; } } /* * Keep track of how long it's been since we were last * here. If it was much longer than sleep_time, the system * was probably suspended, or this program was, (or the * kernel is thrashing :-), so clear idle counter. */ nowtime=time(NULL); /* The 1 is a necessary fudge factor. */ if (oldtime && nowtime - sleep_time > oldtime + 1) { no_sleep=0; /* reset, since they must have put it to sleep */ writecontrol(no_sleep); syslog(LOG_NOTICE, "%i sec sleep; resetting timer", (int)(nowtime - oldtime)); total_unused=0; } oldtime=nowtime; } }