// SPDX-License-Identifier: GPL-2.0-only /* * IOSF-SB MailBox Interface Driver * Copyright (c) 2013, Intel Corporation. * * The IOSF-SB is a fabric bus available on Atom based SOC's that uses a * mailbox interface (MBI) to communicate with multiple devices. This * driver implements access to this interface for those platforms that can * enumerate the device using PCI.
*/
/* Read current mdr value */
ret = iosf_mbi_pci_read_mdr(mcrx, mcr & MBI_RD_MASK, &value); if (ret < 0) {
spin_unlock_irqrestore(&iosf_mbi_lock, flags); return ret;
}
/* Apply mask */
value &= ~mask;
mdr &= mask;
value |= mdr;
/* Write back */
ret = iosf_mbi_pci_write_mdr(mcrx, mcr | MBI_WR_MASK, value);
spin_unlock_irqrestore(&iosf_mbi_lock, flags);
return ret;
}
EXPORT_SYMBOL(iosf_mbi_modify);
bool iosf_mbi_available(void)
{ /* Mbi isn't hot-pluggable. No remove routine is provided */ return mbi_pdev;
}
EXPORT_SYMBOL(iosf_mbi_available);
/* **************** P-Unit/kernel shared I2C bus arbitration **************** * * Some Bay Trail and Cherry Trail devices have the P-Unit and us (the kernel) * share a single I2C bus to the PMIC. Below are helpers to arbitrate the * accesses between the kernel and the P-Unit. * * See arch/x86/include/asm/iosf_mbi.h for kernel-doc text for each function.
*/
void iosf_mbi_punit_acquire(void)
{ /* Wait for any I2C PMIC accesses from in kernel drivers to finish. */
mutex_lock(&iosf_mbi_pmic_access_mutex); while (iosf_mbi_pmic_i2c_access_count != 0) {
mutex_unlock(&iosf_mbi_pmic_access_mutex);
wait_event(iosf_mbi_pmic_access_waitq,
iosf_mbi_pmic_i2c_access_count == 0);
mutex_lock(&iosf_mbi_pmic_access_mutex);
} /* * We do not need to do anything to allow the PUNIT to safely access * the PMIC, other then block in kernel accesses to the PMIC.
*/
iosf_mbi_pmic_punit_access_count++;
mutex_unlock(&iosf_mbi_pmic_access_mutex);
}
EXPORT_SYMBOL(iosf_mbi_punit_acquire);
/* * This function blocks P-Unit accesses to the PMIC I2C bus, so that kernel * I2C code, such as e.g. a fuel-gauge driver, can access it safely. * * This function may be called by I2C controller code while an I2C driver has * already blocked P-Unit accesses because it wants them blocked over multiple * i2c-transfers, for e.g. read-modify-write of an I2C client register. * * To allow safe PMIC i2c bus accesses this function takes the following steps: * * 1) Some code sends request to the P-Unit which make it access the PMIC * I2C bus. Testing has shown that the P-Unit does not check its internal * PMIC bus semaphore for these requests. Callers of these requests call * iosf_mbi_punit_acquire()/_release() around their P-Unit accesses, these * functions increase/decrease iosf_mbi_pmic_punit_access_count, so first * we wait for iosf_mbi_pmic_punit_access_count to become 0. * * 2) Check iosf_mbi_pmic_i2c_access_count, if access has already * been blocked by another caller, we only need to increment * iosf_mbi_pmic_i2c_access_count and we can skip the other steps. * * 3) Some code makes such P-Unit requests from atomic contexts where it * cannot call iosf_mbi_punit_acquire() as that may sleep. * As the second step we call a notifier chain which allows any code * needing P-Unit resources from atomic context to acquire them before * we take control over the PMIC I2C bus. * * 4) When CPU cores enter C6 or C7 the P-Unit needs to talk to the PMIC * if this happens while the kernel itself is accessing the PMIC I2C bus * the SoC hangs. * As the third step we call cpu_latency_qos_update_request() to disallow the * CPU to enter C6 or C7. * * 5) The P-Unit has a PMIC bus semaphore which we can request to stop * autonomous P-Unit tasks from accessing the PMIC I2C bus while we hold it. * As the fourth and final step we request this semaphore and wait for our * request to be acknowledged.
*/ int iosf_mbi_block_punit_i2c_access(void)
{ unsignedlong start, end; int ret = 0;
u32 sem;
if (WARN_ON(!mbi_pdev || !iosf_mbi_sem_address)) return -ENXIO;
/* * Disallow the CPU to enter C6 or C7 state, entering these states * requires the P-Unit to talk to the PMIC and if this happens while * we're holding the semaphore, the SoC hangs.
*/
cpu_latency_qos_update_request(&iosf_mbi_pm_qos, 0);
/* host driver writes to side band semaphore register */
ret = iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
iosf_mbi_sem_address, PUNIT_SEMAPHORE_ACQUIRE); if (ret) {
dev_err(&mbi_pdev->dev, "Error P-Unit semaphore request failed\n"); goto error;
}
/* host driver waits for bit 0 to be set in semaphore register */
start = jiffies;
end = start + msecs_to_jiffies(SEMAPHORE_TIMEOUT); do {
ret = iosf_mbi_get_sem(&sem); if (!ret && sem) {
iosf_mbi_sem_acquired = jiffies;
dev_dbg(&mbi_pdev->dev, "P-Unit semaphore acquired after %ums\n",
jiffies_to_msecs(jiffies - start)); goto success;
}
usleep_range(1000, 2000);
} while (time_before(jiffies, end));
ret = -ETIMEDOUT;
dev_err(&mbi_pdev->dev, "Error P-Unit semaphore timed out, resetting\n");
error:
iosf_mbi_reset_semaphore(); if (!iosf_mbi_get_sem(&sem))
dev_err(&mbi_pdev->dev, "P-Unit semaphore: %d\n", sem);
success: if (!WARN_ON(ret))
iosf_mbi_pmic_i2c_access_count++;
mutex_lock(&iosf_mbi_pmic_access_mutex);
iosf_mbi_pmic_i2c_access_count--; if (iosf_mbi_pmic_i2c_access_count == 0) {
iosf_mbi_reset_semaphore();
dev_dbg(&mbi_pdev->dev, "punit semaphore held for %ums\n",
jiffies_to_msecs(jiffies - iosf_mbi_sem_acquired));
do_wakeup = true;
}
mutex_unlock(&iosf_mbi_pmic_access_mutex);
if (do_wakeup)
wake_up(&iosf_mbi_pmic_access_waitq);
}
EXPORT_SYMBOL(iosf_mbi_unblock_punit_i2c_access);
int iosf_mbi_register_pmic_bus_access_notifier(struct notifier_block *nb)
{ int ret;
/* Wait for the bus to go inactive before registering */
iosf_mbi_punit_acquire();
ret = blocking_notifier_chain_register(
&iosf_mbi_pmic_bus_access_notifier, nb);
iosf_mbi_punit_release();
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.