/** * mei_me_reg_read - Reads 32bit data from the mei device * * @hw: the me hardware structure * @offset: offset from which to read the data * * Return: register value (u32)
*/ staticinline u32 mei_me_reg_read(conststruct mei_me_hw *hw, unsignedlong offset)
{ return ioread32(hw->mem_addr + offset);
}
/** * mei_me_reg_write - Writes 32bit data to the mei device * * @hw: the me hardware structure * @offset: offset from which to write the data * @value: register value to write (u32)
*/ staticinlinevoid mei_me_reg_write(conststruct mei_me_hw *hw, unsignedlong offset, u32 value)
{
iowrite32(value, hw->mem_addr + offset);
}
/** * mei_me_mecbrw_read - Reads 32bit data from ME circular buffer * read window register * * @dev: the device structure * * Return: ME_CB_RW register value (u32)
*/ staticinline u32 mei_me_mecbrw_read(conststruct mei_device *dev)
{ return mei_me_reg_read(to_me_hw(dev), ME_CB_RW);
}
/** * mei_me_hcbww_write - write 32bit data to the host circular buffer * * @dev: the device structure * @data: 32bit data to be written to the host circular buffer
*/ staticinlinevoid mei_me_hcbww_write(struct mei_device *dev, u32 data)
{
mei_me_reg_write(to_me_hw(dev), H_CB_WW, data);
}
/** * mei_me_mecsr_read - Reads 32bit data from the ME CSR * * @dev: the device structure * * Return: ME_CSR_HA register value (u32)
*/ staticinline u32 mei_me_mecsr_read(conststruct mei_device *dev)
{
u32 reg;
/** * mei_hcsr_write - writes H_CSR register to the mei device * * @dev: the device structure * @reg: new register value
*/ staticinlinevoid mei_hcsr_write(struct mei_device *dev, u32 reg)
{
trace_mei_reg_write(dev->dev, "H_CSR", H_CSR, reg);
mei_me_reg_write(to_me_hw(dev), H_CSR, reg);
}
/** * mei_hcsr_set - writes H_CSR register to the mei device, * and ignores the H_IS bit for it is write-one-to-zero. * * @dev: the device structure * @reg: new register value
*/ staticinlinevoid mei_hcsr_set(struct mei_device *dev, u32 reg)
{
reg &= ~H_CSR_IS_MASK;
mei_hcsr_write(dev, reg);
}
/** * mei_me_fw_status - read fw status register from pci config space * * @dev: mei device * @fw_status: fw status register values * * Return: 0 on success, error otherwise
*/ staticint mei_me_fw_status(struct mei_device *dev, struct mei_fw_status *fw_status)
{ struct mei_me_hw *hw = to_me_hw(dev); conststruct mei_fw_status *fw_src = &hw->cfg->fw_status; int ret; int i;
if (!fw_status || !hw->read_fws) return -EINVAL;
fw_status->count = fw_src->count; for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) {
ret = hw->read_fws(dev, fw_src->status[i],
&fw_status->status[i]);
trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HFS_X",
fw_src->status[i],
fw_status->status[i]); if (ret) return ret;
}
return 0;
}
/** * mei_me_hw_config - configure hw dependent settings * * @dev: mei device * * Return: * * -EINVAL when read_fws is not set * * 0 on success *
*/ staticint mei_me_hw_config(struct mei_device *dev)
{ struct mei_me_hw *hw = to_me_hw(dev);
u32 hcsr, reg;
hw->pg_state = MEI_PG_OFF; if (hw->d0i3_supported) {
reg = mei_me_d0i3c_read(dev); if (reg & H_D0I3C_I3)
hw->pg_state = MEI_PG_ON;
}
return 0;
}
/** * mei_me_pg_state - translate internal pg state * to the mei power gating state * * @dev: mei device * * Return: MEI_PG_OFF if aliveness is on and MEI_PG_ON otherwise
*/ staticinlineenum mei_pg_state mei_me_pg_state(struct mei_device *dev)
{ struct mei_me_hw *hw = to_me_hw(dev);
/** * mei_me_d0i3_enter_sync - perform d0i3 entry procedure * * @dev: the device structure * * Return: 0 on success an error code otherwise
*/ staticint mei_me_d0i3_enter_sync(struct mei_device *dev)
{ struct mei_me_hw *hw = to_me_hw(dev); int ret;
u32 reg;
reg = mei_me_d0i3c_read(dev); if (reg & H_D0I3C_I3) { /* we are in d0i3, nothing to do */
dev_dbg(dev->dev, "d0i3 set not needed\n");
ret = 0; goto on;
}
if (dev->pg_event != MEI_PG_EVENT_INTR_RECEIVED) {
reg = mei_me_d0i3c_read(dev); if (!(reg & H_D0I3C_I3)) {
ret = -ETIME; goto out;
}
}
ret = 0;
on:
hw->pg_state = MEI_PG_ON;
out:
dev->pg_event = MEI_PG_EVENT_IDLE;
dev_dbg(dev->dev, "d0i3 enter ret = %d\n", ret); return ret;
}
/** * mei_me_d0i3_enter - perform d0i3 entry procedure * no hbm PG handshake * no waiting for confirmation; runs with interrupts * disabled * * @dev: the device structure * * Return: 0 on success an error code otherwise
*/ staticint mei_me_d0i3_enter(struct mei_device *dev)
{ struct mei_me_hw *hw = to_me_hw(dev);
u32 reg;
reg = mei_me_d0i3c_read(dev); if (reg & H_D0I3C_I3) { /* we are in d0i3, nothing to do */
dev_dbg(dev->dev, "already d0i3 : set not needed\n"); goto on;
}
/** * mei_me_d0i3_exit_sync - perform d0i3 exit procedure * * @dev: the device structure * * Return: 0 on success an error code otherwise
*/ staticint mei_me_d0i3_exit_sync(struct mei_device *dev)
{ struct mei_me_hw *hw = to_me_hw(dev); int ret;
u32 reg;
dev->pg_event = MEI_PG_EVENT_INTR_WAIT;
reg = mei_me_d0i3c_read(dev); if (!(reg & H_D0I3C_I3)) { /* we are not in d0i3, nothing to do */
dev_dbg(dev->dev, "d0i3 exit not needed\n");
ret = 0; goto off;
}
reg = mei_me_d0i3_unset(dev); if (!(reg & H_D0I3C_CIP)) {
dev_dbg(dev->dev, "d0i3 exit wait not needed\n");
ret = 0; goto off;
}
if (dev->pg_event == MEI_PG_EVENT_INTR_WAIT &&
(intr_source & H_D0I3C_IS)) {
dev->pg_event = MEI_PG_EVENT_INTR_RECEIVED; if (hw->pg_state == MEI_PG_ON) {
hw->pg_state = MEI_PG_OFF; if (dev->hbm_state != MEI_HBM_IDLE) { /* * force H_RDY because it could be * wiped off during PG
*/
dev_dbg(dev->dev, "d0i3 set host ready\n");
mei_me_host_set_ready(dev);
}
} else {
hw->pg_state = MEI_PG_ON;
}
wake_up(&dev->wait_pg);
}
if (hw->pg_state == MEI_PG_ON && (intr_source & H_IS)) { /* * HW sent some data and we are in D0i3, so * we got here because of HW initiated exit from D0i3. * Start runtime pm resume sequence to exit low power state.
*/
dev_dbg(dev->dev, "d0i3 want resume\n");
mei_hbm_pg_resume(dev);
}
}
if (hw->d0i3_supported)
mei_me_d0i3_intr(dev, intr_source); else
mei_me_pg_legacy_intr(dev);
}
/** * mei_me_pg_enter_sync - perform runtime pm entry procedure * * @dev: the device structure * * Return: 0 on success an error code otherwise
*/ int mei_me_pg_enter_sync(struct mei_device *dev)
{ struct mei_me_hw *hw = to_me_hw(dev);
if (hw->d0i3_supported) return mei_me_d0i3_enter_sync(dev); else return mei_me_pg_legacy_enter_sync(dev);
}
/** * mei_me_pg_exit_sync - perform runtime pm exit procedure * * @dev: the device structure * * Return: 0 on success an error code otherwise
*/ int mei_me_pg_exit_sync(struct mei_device *dev)
{ struct mei_me_hw *hw = to_me_hw(dev);
if (hw->d0i3_supported) return mei_me_d0i3_exit_sync(dev); else return mei_me_pg_legacy_exit_sync(dev);
}
/** * mei_me_hw_reset - resets fw via mei csr register. * * @dev: the device structure * @intr_enable: if interrupt should be enabled after reset. * * Return: 0 on success an error code otherwise
*/ staticint mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
{ struct mei_me_hw *hw = to_me_hw(dev); int ret;
u32 hcsr;
if (intr_enable) {
mei_me_intr_enable(dev); if (hw->d0i3_supported) {
ret = mei_me_d0i3_exit_sync(dev); if (ret) return ret;
} else {
hw->pg_state = MEI_PG_OFF;
}
}
pm_runtime_set_active(dev->dev);
hcsr = mei_hcsr_read(dev); /* H_RST may be found lit before reset is started, * for example if preceding reset flow hasn't completed. * In that case asserting H_RST will be ignored, therefore * we need to clean H_RST bit to start a successful reset sequence.
*/ if ((hcsr & H_RST) == H_RST) {
dev_warn(dev->dev, "H_RST is set = 0x%08X", hcsr);
hcsr &= ~H_RST;
mei_hcsr_set(dev, hcsr);
hcsr = mei_hcsr_read(dev);
}
hcsr |= H_RST | H_IG | H_CSR_IS_MASK;
if (!intr_enable || mei_me_hw_use_polling(to_me_hw(dev)))
hcsr &= ~H_CSR_IE_MASK;
/** * mei_me_irq_thread_handler - function called after ISR to handle the interrupt * processing. * * @irq: The irq number * @dev_id: pointer to the device structure * * Return: irqreturn_t *
*/
irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
{ struct mei_device *dev = (struct mei_device *) dev_id; struct list_head cmpl_list;
s32 slots;
u32 hcsr; int rets = 0;
dev_dbg(dev->dev, "function called after ISR to handle the interrupt processing.\n"); /* initialize our complete list */
mutex_lock(&dev->device_lock);
/* check if ME wants a reset */ if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) { if (kind_is_gsc(dev) || kind_is_gscfi(dev)) {
dev_dbg(dev->dev, "FW not ready: resetting: dev_state = %d\n",
dev->dev_state);
} else {
dev_warn(dev->dev, "FW not ready: resetting: dev_state = %d\n",
dev->dev_state);
} if (dev->dev_state == MEI_DEV_POWERING_DOWN ||
dev->dev_state == MEI_DEV_POWER_DOWN)
mei_cl_all_disconnect(dev); elseif (dev->dev_state != MEI_DEV_DISABLED)
schedule_work(&dev->reset_work); goto end;
}
if (mei_me_hw_is_resetting(dev))
mei_hcsr_set_hig(dev);
mei_me_pg_intr(dev, me_intr_src(hcsr));
/* check if we need to start the dev */ if (!mei_host_is_ready(dev)) { if (mei_hw_is_ready(dev)) {
dev_dbg(dev->dev, "we need to start the dev.\n");
dev->recvd_hw_ready = true;
wake_up(&dev->wait_hw_ready);
} else {
dev_dbg(dev->dev, "Spurious Interrupt\n");
} goto end;
} /* check slots available for reading */
slots = mei_count_full_read_slots(dev); while (slots > 0) {
dev_dbg(dev->dev, "slots to read = %08x\n", slots);
rets = mei_irq_read_handler(dev, &cmpl_list, &slots); /* There is a race between ME write and interrupt delivery: * Not all data is always available immediately after the * interrupt, so try to read again on the next interrupt.
*/ if (rets == -ENODATA) break;
if (rets) {
dev_err(dev->dev, "mei_irq_read_handler ret = %d, state = %d.\n",
rets, dev->dev_state); if (dev->dev_state != MEI_DEV_RESETTING &&
dev->dev_state != MEI_DEV_DISABLED &&
dev->dev_state != MEI_DEV_POWERING_DOWN &&
dev->dev_state != MEI_DEV_POWER_DOWN)
schedule_work(&dev->reset_work); goto end;
}
}
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
/* * During PG handshake only allowed write is the replay to the * PG exit message, so block calling write function * if the pg event is in PG handshake
*/ if (dev->pg_event != MEI_PG_EVENT_WAIT &&
dev->pg_event != MEI_PG_EVENT_RECEIVED) {
rets = mei_irq_write_handler(dev, &cmpl_list);
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
}
mei_irq_compl_handler(dev, &cmpl_list);
end:
dev_dbg(dev->dev, "interrupt thread end ret = %d\n", rets);
mei_me_intr_enable(dev);
mutex_unlock(&dev->device_lock); return IRQ_HANDLED;
}
EXPORT_SYMBOL_GPL(mei_me_irq_thread_handler);
/** * mei_me_polling_thread - interrupt register polling thread * * @_dev: mei device * * The thread monitors the interrupt source register and calls * mei_me_irq_thread_handler() to handle the firmware * input. * * The function polls in MEI_POLLING_TIMEOUT_ACTIVE timeout * in case there was an event, in idle case the polling * time increases yet again by MEI_POLLING_TIMEOUT_ACTIVE * up to MEI_POLLING_TIMEOUT_IDLE. * * Return: always 0
*/ int mei_me_polling_thread(void *_dev)
{ struct mei_device *dev = _dev;
irqreturn_t irq_ret; long polling_timeout = MEI_POLLING_TIMEOUT_ACTIVE;
dev_dbg(dev->dev, "kernel thread is running\n"); while (!kthread_should_stop()) { struct mei_me_hw *hw = to_me_hw(dev);
u32 hcsr;
/** * mei_me_fw_type_nm() - check for nm sku * * @pdev: pci device * * Read ME FW Status register to check for the Node Manager (NM) Firmware. * The NM FW is only signaled in PCI function 0. * __Note__: Deprecated by PCH8 and newer. * * Return: true in case of NM firmware
*/ staticbool mei_me_fw_type_nm(conststruct pci_dev *pdev)
{
u32 reg; unsignedint devfn;
devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0);
pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_2, ®);
trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_2", PCI_CFG_HFS_2, reg); /* make sure that bit 9 (NM) is up and bit 10 (DM) is down */ return (reg & 0x600) == 0x200;
}
/** * mei_me_fw_type_sps_4() - check for sps 4.0 sku * * @pdev: pci device * * Read ME FW Status register to check for SPS Firmware. * The SPS FW is only signaled in the PCI function 0. * __Note__: Deprecated by SPS 5.0 and newer. * * Return: true in case of SPS firmware
*/ staticbool mei_me_fw_type_sps_4(conststruct pci_dev *pdev)
{
u32 reg; unsignedint devfn;
/** * mei_me_fw_type_sps_ign() - check for sps or ign sku * * @pdev: pci device * * Read ME FW Status register to check for SPS or IGN Firmware. * The SPS/IGN FW is only signaled in pci function 0 * * Return: true in case of SPS/IGN firmware
*/ staticbool mei_me_fw_type_sps_ign(conststruct pci_dev *pdev)
{
u32 reg;
u32 fw_type; unsignedint devfn;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.