162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * IOSF-SB MailBox Interface Driver
462306a36Sopenharmony_ci * Copyright (c) 2013, Intel Corporation.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * The IOSF-SB is a fabric bus available on Atom based SOC's that uses a
762306a36Sopenharmony_ci * mailbox interface (MBI) to communicate with multiple devices. This
862306a36Sopenharmony_ci * driver implements access to this interface for those platforms that can
962306a36Sopenharmony_ci * enumerate the device using PCI.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/delay.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci#include <linux/spinlock.h>
1662306a36Sopenharmony_ci#include <linux/pci.h>
1762306a36Sopenharmony_ci#include <linux/debugfs.h>
1862306a36Sopenharmony_ci#include <linux/capability.h>
1962306a36Sopenharmony_ci#include <linux/pm_qos.h>
2062306a36Sopenharmony_ci#include <linux/wait.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <asm/iosf_mbi.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_BAYTRAIL		0x0F00
2562306a36Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_BRASWELL		0x2280
2662306a36Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_QUARK_X1000		0x0958
2762306a36Sopenharmony_ci#define PCI_DEVICE_ID_INTEL_TANGIER		0x1170
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic struct pci_dev *mbi_pdev;
3062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(iosf_mbi_lock);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/**************** Generic iosf_mbi access helpers ****************/
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic inline u32 iosf_mbi_form_mcr(u8 op, u8 port, u8 offset)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	return (op << 24) | (port << 16) | (offset << 8) | MBI_ENABLE;
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic int iosf_mbi_pci_read_mdr(u32 mcrx, u32 mcr, u32 *mdr)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	int result;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	if (!mbi_pdev)
4462306a36Sopenharmony_ci		return -ENODEV;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if (mcrx) {
4762306a36Sopenharmony_ci		result = pci_write_config_dword(mbi_pdev, MBI_MCRX_OFFSET,
4862306a36Sopenharmony_ci						mcrx);
4962306a36Sopenharmony_ci		if (result < 0)
5062306a36Sopenharmony_ci			goto fail_read;
5162306a36Sopenharmony_ci	}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	result = pci_write_config_dword(mbi_pdev, MBI_MCR_OFFSET, mcr);
5462306a36Sopenharmony_ci	if (result < 0)
5562306a36Sopenharmony_ci		goto fail_read;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	result = pci_read_config_dword(mbi_pdev, MBI_MDR_OFFSET, mdr);
5862306a36Sopenharmony_ci	if (result < 0)
5962306a36Sopenharmony_ci		goto fail_read;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	return 0;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cifail_read:
6462306a36Sopenharmony_ci	dev_err(&mbi_pdev->dev, "PCI config access failed with %d\n", result);
6562306a36Sopenharmony_ci	return result;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic int iosf_mbi_pci_write_mdr(u32 mcrx, u32 mcr, u32 mdr)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	int result;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	if (!mbi_pdev)
7362306a36Sopenharmony_ci		return -ENODEV;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	result = pci_write_config_dword(mbi_pdev, MBI_MDR_OFFSET, mdr);
7662306a36Sopenharmony_ci	if (result < 0)
7762306a36Sopenharmony_ci		goto fail_write;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (mcrx) {
8062306a36Sopenharmony_ci		result = pci_write_config_dword(mbi_pdev, MBI_MCRX_OFFSET,
8162306a36Sopenharmony_ci						mcrx);
8262306a36Sopenharmony_ci		if (result < 0)
8362306a36Sopenharmony_ci			goto fail_write;
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	result = pci_write_config_dword(mbi_pdev, MBI_MCR_OFFSET, mcr);
8762306a36Sopenharmony_ci	if (result < 0)
8862306a36Sopenharmony_ci		goto fail_write;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	return 0;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cifail_write:
9362306a36Sopenharmony_ci	dev_err(&mbi_pdev->dev, "PCI config access failed with %d\n", result);
9462306a36Sopenharmony_ci	return result;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ciint iosf_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	u32 mcr, mcrx;
10062306a36Sopenharmony_ci	unsigned long flags;
10162306a36Sopenharmony_ci	int ret;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/* Access to the GFX unit is handled by GPU code */
10462306a36Sopenharmony_ci	if (port == BT_MBI_UNIT_GFX) {
10562306a36Sopenharmony_ci		WARN_ON(1);
10662306a36Sopenharmony_ci		return -EPERM;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	mcr = iosf_mbi_form_mcr(opcode, port, offset & MBI_MASK_LO);
11062306a36Sopenharmony_ci	mcrx = offset & MBI_MASK_HI;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	spin_lock_irqsave(&iosf_mbi_lock, flags);
11362306a36Sopenharmony_ci	ret = iosf_mbi_pci_read_mdr(mcrx, mcr, mdr);
11462306a36Sopenharmony_ci	spin_unlock_irqrestore(&iosf_mbi_lock, flags);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return ret;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ciEXPORT_SYMBOL(iosf_mbi_read);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ciint iosf_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	u32 mcr, mcrx;
12362306a36Sopenharmony_ci	unsigned long flags;
12462306a36Sopenharmony_ci	int ret;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/* Access to the GFX unit is handled by GPU code */
12762306a36Sopenharmony_ci	if (port == BT_MBI_UNIT_GFX) {
12862306a36Sopenharmony_ci		WARN_ON(1);
12962306a36Sopenharmony_ci		return -EPERM;
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	mcr = iosf_mbi_form_mcr(opcode, port, offset & MBI_MASK_LO);
13362306a36Sopenharmony_ci	mcrx = offset & MBI_MASK_HI;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	spin_lock_irqsave(&iosf_mbi_lock, flags);
13662306a36Sopenharmony_ci	ret = iosf_mbi_pci_write_mdr(mcrx, mcr, mdr);
13762306a36Sopenharmony_ci	spin_unlock_irqrestore(&iosf_mbi_lock, flags);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	return ret;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ciEXPORT_SYMBOL(iosf_mbi_write);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ciint iosf_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	u32 mcr, mcrx;
14662306a36Sopenharmony_ci	u32 value;
14762306a36Sopenharmony_ci	unsigned long flags;
14862306a36Sopenharmony_ci	int ret;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* Access to the GFX unit is handled by GPU code */
15162306a36Sopenharmony_ci	if (port == BT_MBI_UNIT_GFX) {
15262306a36Sopenharmony_ci		WARN_ON(1);
15362306a36Sopenharmony_ci		return -EPERM;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	mcr = iosf_mbi_form_mcr(opcode, port, offset & MBI_MASK_LO);
15762306a36Sopenharmony_ci	mcrx = offset & MBI_MASK_HI;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	spin_lock_irqsave(&iosf_mbi_lock, flags);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/* Read current mdr value */
16262306a36Sopenharmony_ci	ret = iosf_mbi_pci_read_mdr(mcrx, mcr & MBI_RD_MASK, &value);
16362306a36Sopenharmony_ci	if (ret < 0) {
16462306a36Sopenharmony_ci		spin_unlock_irqrestore(&iosf_mbi_lock, flags);
16562306a36Sopenharmony_ci		return ret;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/* Apply mask */
16962306a36Sopenharmony_ci	value &= ~mask;
17062306a36Sopenharmony_ci	mdr &= mask;
17162306a36Sopenharmony_ci	value |= mdr;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/* Write back */
17462306a36Sopenharmony_ci	ret = iosf_mbi_pci_write_mdr(mcrx, mcr | MBI_WR_MASK, value);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	spin_unlock_irqrestore(&iosf_mbi_lock, flags);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	return ret;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ciEXPORT_SYMBOL(iosf_mbi_modify);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cibool iosf_mbi_available(void)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	/* Mbi isn't hot-pluggable. No remove routine is provided */
18562306a36Sopenharmony_ci	return mbi_pdev;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ciEXPORT_SYMBOL(iosf_mbi_available);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci/*
19062306a36Sopenharmony_ci **************** P-Unit/kernel shared I2C bus arbitration ****************
19162306a36Sopenharmony_ci *
19262306a36Sopenharmony_ci * Some Bay Trail and Cherry Trail devices have the P-Unit and us (the kernel)
19362306a36Sopenharmony_ci * share a single I2C bus to the PMIC. Below are helpers to arbitrate the
19462306a36Sopenharmony_ci * accesses between the kernel and the P-Unit.
19562306a36Sopenharmony_ci *
19662306a36Sopenharmony_ci * See arch/x86/include/asm/iosf_mbi.h for kernel-doc text for each function.
19762306a36Sopenharmony_ci */
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci#define SEMAPHORE_TIMEOUT		500
20062306a36Sopenharmony_ci#define PUNIT_SEMAPHORE_BYT		0x7
20162306a36Sopenharmony_ci#define PUNIT_SEMAPHORE_CHT		0x10e
20262306a36Sopenharmony_ci#define PUNIT_SEMAPHORE_BIT		BIT(0)
20362306a36Sopenharmony_ci#define PUNIT_SEMAPHORE_ACQUIRE		BIT(1)
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic DEFINE_MUTEX(iosf_mbi_pmic_access_mutex);
20662306a36Sopenharmony_cistatic BLOCKING_NOTIFIER_HEAD(iosf_mbi_pmic_bus_access_notifier);
20762306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(iosf_mbi_pmic_access_waitq);
20862306a36Sopenharmony_cistatic u32 iosf_mbi_pmic_punit_access_count;
20962306a36Sopenharmony_cistatic u32 iosf_mbi_pmic_i2c_access_count;
21062306a36Sopenharmony_cistatic u32 iosf_mbi_sem_address;
21162306a36Sopenharmony_cistatic unsigned long iosf_mbi_sem_acquired;
21262306a36Sopenharmony_cistatic struct pm_qos_request iosf_mbi_pm_qos;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_civoid iosf_mbi_punit_acquire(void)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	/* Wait for any I2C PMIC accesses from in kernel drivers to finish. */
21762306a36Sopenharmony_ci	mutex_lock(&iosf_mbi_pmic_access_mutex);
21862306a36Sopenharmony_ci	while (iosf_mbi_pmic_i2c_access_count != 0) {
21962306a36Sopenharmony_ci		mutex_unlock(&iosf_mbi_pmic_access_mutex);
22062306a36Sopenharmony_ci		wait_event(iosf_mbi_pmic_access_waitq,
22162306a36Sopenharmony_ci			   iosf_mbi_pmic_i2c_access_count == 0);
22262306a36Sopenharmony_ci		mutex_lock(&iosf_mbi_pmic_access_mutex);
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci	/*
22562306a36Sopenharmony_ci	 * We do not need to do anything to allow the PUNIT to safely access
22662306a36Sopenharmony_ci	 * the PMIC, other then block in kernel accesses to the PMIC.
22762306a36Sopenharmony_ci	 */
22862306a36Sopenharmony_ci	iosf_mbi_pmic_punit_access_count++;
22962306a36Sopenharmony_ci	mutex_unlock(&iosf_mbi_pmic_access_mutex);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ciEXPORT_SYMBOL(iosf_mbi_punit_acquire);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_civoid iosf_mbi_punit_release(void)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	bool do_wakeup;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	mutex_lock(&iosf_mbi_pmic_access_mutex);
23862306a36Sopenharmony_ci	iosf_mbi_pmic_punit_access_count--;
23962306a36Sopenharmony_ci	do_wakeup = iosf_mbi_pmic_punit_access_count == 0;
24062306a36Sopenharmony_ci	mutex_unlock(&iosf_mbi_pmic_access_mutex);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if (do_wakeup)
24362306a36Sopenharmony_ci		wake_up(&iosf_mbi_pmic_access_waitq);
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ciEXPORT_SYMBOL(iosf_mbi_punit_release);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic int iosf_mbi_get_sem(u32 *sem)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	int ret;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	ret = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
25262306a36Sopenharmony_ci			    iosf_mbi_sem_address, sem);
25362306a36Sopenharmony_ci	if (ret) {
25462306a36Sopenharmony_ci		dev_err(&mbi_pdev->dev, "Error P-Unit semaphore read failed\n");
25562306a36Sopenharmony_ci		return ret;
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	*sem &= PUNIT_SEMAPHORE_BIT;
25962306a36Sopenharmony_ci	return 0;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic void iosf_mbi_reset_semaphore(void)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	if (iosf_mbi_modify(BT_MBI_UNIT_PMC, MBI_REG_READ,
26562306a36Sopenharmony_ci			    iosf_mbi_sem_address, 0, PUNIT_SEMAPHORE_BIT))
26662306a36Sopenharmony_ci		dev_err(&mbi_pdev->dev, "Error P-Unit semaphore reset failed\n");
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	cpu_latency_qos_update_request(&iosf_mbi_pm_qos, PM_QOS_DEFAULT_VALUE);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	blocking_notifier_call_chain(&iosf_mbi_pmic_bus_access_notifier,
27162306a36Sopenharmony_ci				     MBI_PMIC_BUS_ACCESS_END, NULL);
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci/*
27562306a36Sopenharmony_ci * This function blocks P-Unit accesses to the PMIC I2C bus, so that kernel
27662306a36Sopenharmony_ci * I2C code, such as e.g. a fuel-gauge driver, can access it safely.
27762306a36Sopenharmony_ci *
27862306a36Sopenharmony_ci * This function may be called by I2C controller code while an I2C driver has
27962306a36Sopenharmony_ci * already blocked P-Unit accesses because it wants them blocked over multiple
28062306a36Sopenharmony_ci * i2c-transfers, for e.g. read-modify-write of an I2C client register.
28162306a36Sopenharmony_ci *
28262306a36Sopenharmony_ci * To allow safe PMIC i2c bus accesses this function takes the following steps:
28362306a36Sopenharmony_ci *
28462306a36Sopenharmony_ci * 1) Some code sends request to the P-Unit which make it access the PMIC
28562306a36Sopenharmony_ci *    I2C bus. Testing has shown that the P-Unit does not check its internal
28662306a36Sopenharmony_ci *    PMIC bus semaphore for these requests. Callers of these requests call
28762306a36Sopenharmony_ci *    iosf_mbi_punit_acquire()/_release() around their P-Unit accesses, these
28862306a36Sopenharmony_ci *    functions increase/decrease iosf_mbi_pmic_punit_access_count, so first
28962306a36Sopenharmony_ci *    we wait for iosf_mbi_pmic_punit_access_count to become 0.
29062306a36Sopenharmony_ci *
29162306a36Sopenharmony_ci * 2) Check iosf_mbi_pmic_i2c_access_count, if access has already
29262306a36Sopenharmony_ci *    been blocked by another caller, we only need to increment
29362306a36Sopenharmony_ci *    iosf_mbi_pmic_i2c_access_count and we can skip the other steps.
29462306a36Sopenharmony_ci *
29562306a36Sopenharmony_ci * 3) Some code makes such P-Unit requests from atomic contexts where it
29662306a36Sopenharmony_ci *    cannot call iosf_mbi_punit_acquire() as that may sleep.
29762306a36Sopenharmony_ci *    As the second step we call a notifier chain which allows any code
29862306a36Sopenharmony_ci *    needing P-Unit resources from atomic context to acquire them before
29962306a36Sopenharmony_ci *    we take control over the PMIC I2C bus.
30062306a36Sopenharmony_ci *
30162306a36Sopenharmony_ci * 4) When CPU cores enter C6 or C7 the P-Unit needs to talk to the PMIC
30262306a36Sopenharmony_ci *    if this happens while the kernel itself is accessing the PMIC I2C bus
30362306a36Sopenharmony_ci *    the SoC hangs.
30462306a36Sopenharmony_ci *    As the third step we call cpu_latency_qos_update_request() to disallow the
30562306a36Sopenharmony_ci *    CPU to enter C6 or C7.
30662306a36Sopenharmony_ci *
30762306a36Sopenharmony_ci * 5) The P-Unit has a PMIC bus semaphore which we can request to stop
30862306a36Sopenharmony_ci *    autonomous P-Unit tasks from accessing the PMIC I2C bus while we hold it.
30962306a36Sopenharmony_ci *    As the fourth and final step we request this semaphore and wait for our
31062306a36Sopenharmony_ci *    request to be acknowledged.
31162306a36Sopenharmony_ci */
31262306a36Sopenharmony_ciint iosf_mbi_block_punit_i2c_access(void)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	unsigned long start, end;
31562306a36Sopenharmony_ci	int ret = 0;
31662306a36Sopenharmony_ci	u32 sem;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	if (WARN_ON(!mbi_pdev || !iosf_mbi_sem_address))
31962306a36Sopenharmony_ci		return -ENXIO;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	mutex_lock(&iosf_mbi_pmic_access_mutex);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	while (iosf_mbi_pmic_punit_access_count != 0) {
32462306a36Sopenharmony_ci		mutex_unlock(&iosf_mbi_pmic_access_mutex);
32562306a36Sopenharmony_ci		wait_event(iosf_mbi_pmic_access_waitq,
32662306a36Sopenharmony_ci			   iosf_mbi_pmic_punit_access_count == 0);
32762306a36Sopenharmony_ci		mutex_lock(&iosf_mbi_pmic_access_mutex);
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (iosf_mbi_pmic_i2c_access_count > 0)
33162306a36Sopenharmony_ci		goto success;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	blocking_notifier_call_chain(&iosf_mbi_pmic_bus_access_notifier,
33462306a36Sopenharmony_ci				     MBI_PMIC_BUS_ACCESS_BEGIN, NULL);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	/*
33762306a36Sopenharmony_ci	 * Disallow the CPU to enter C6 or C7 state, entering these states
33862306a36Sopenharmony_ci	 * requires the P-Unit to talk to the PMIC and if this happens while
33962306a36Sopenharmony_ci	 * we're holding the semaphore, the SoC hangs.
34062306a36Sopenharmony_ci	 */
34162306a36Sopenharmony_ci	cpu_latency_qos_update_request(&iosf_mbi_pm_qos, 0);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	/* host driver writes to side band semaphore register */
34462306a36Sopenharmony_ci	ret = iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
34562306a36Sopenharmony_ci			     iosf_mbi_sem_address, PUNIT_SEMAPHORE_ACQUIRE);
34662306a36Sopenharmony_ci	if (ret) {
34762306a36Sopenharmony_ci		dev_err(&mbi_pdev->dev, "Error P-Unit semaphore request failed\n");
34862306a36Sopenharmony_ci		goto error;
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	/* host driver waits for bit 0 to be set in semaphore register */
35262306a36Sopenharmony_ci	start = jiffies;
35362306a36Sopenharmony_ci	end = start + msecs_to_jiffies(SEMAPHORE_TIMEOUT);
35462306a36Sopenharmony_ci	do {
35562306a36Sopenharmony_ci		ret = iosf_mbi_get_sem(&sem);
35662306a36Sopenharmony_ci		if (!ret && sem) {
35762306a36Sopenharmony_ci			iosf_mbi_sem_acquired = jiffies;
35862306a36Sopenharmony_ci			dev_dbg(&mbi_pdev->dev, "P-Unit semaphore acquired after %ums\n",
35962306a36Sopenharmony_ci				jiffies_to_msecs(jiffies - start));
36062306a36Sopenharmony_ci			goto success;
36162306a36Sopenharmony_ci		}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci		usleep_range(1000, 2000);
36462306a36Sopenharmony_ci	} while (time_before(jiffies, end));
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	ret = -ETIMEDOUT;
36762306a36Sopenharmony_ci	dev_err(&mbi_pdev->dev, "Error P-Unit semaphore timed out, resetting\n");
36862306a36Sopenharmony_cierror:
36962306a36Sopenharmony_ci	iosf_mbi_reset_semaphore();
37062306a36Sopenharmony_ci	if (!iosf_mbi_get_sem(&sem))
37162306a36Sopenharmony_ci		dev_err(&mbi_pdev->dev, "P-Unit semaphore: %d\n", sem);
37262306a36Sopenharmony_cisuccess:
37362306a36Sopenharmony_ci	if (!WARN_ON(ret))
37462306a36Sopenharmony_ci		iosf_mbi_pmic_i2c_access_count++;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	mutex_unlock(&iosf_mbi_pmic_access_mutex);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	return ret;
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ciEXPORT_SYMBOL(iosf_mbi_block_punit_i2c_access);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_civoid iosf_mbi_unblock_punit_i2c_access(void)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	bool do_wakeup = false;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	mutex_lock(&iosf_mbi_pmic_access_mutex);
38762306a36Sopenharmony_ci	iosf_mbi_pmic_i2c_access_count--;
38862306a36Sopenharmony_ci	if (iosf_mbi_pmic_i2c_access_count == 0) {
38962306a36Sopenharmony_ci		iosf_mbi_reset_semaphore();
39062306a36Sopenharmony_ci		dev_dbg(&mbi_pdev->dev, "punit semaphore held for %ums\n",
39162306a36Sopenharmony_ci			jiffies_to_msecs(jiffies - iosf_mbi_sem_acquired));
39262306a36Sopenharmony_ci		do_wakeup = true;
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci	mutex_unlock(&iosf_mbi_pmic_access_mutex);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	if (do_wakeup)
39762306a36Sopenharmony_ci		wake_up(&iosf_mbi_pmic_access_waitq);
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ciEXPORT_SYMBOL(iosf_mbi_unblock_punit_i2c_access);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ciint iosf_mbi_register_pmic_bus_access_notifier(struct notifier_block *nb)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	int ret;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	/* Wait for the bus to go inactive before registering */
40662306a36Sopenharmony_ci	iosf_mbi_punit_acquire();
40762306a36Sopenharmony_ci	ret = blocking_notifier_chain_register(
40862306a36Sopenharmony_ci				&iosf_mbi_pmic_bus_access_notifier, nb);
40962306a36Sopenharmony_ci	iosf_mbi_punit_release();
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	return ret;
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ciEXPORT_SYMBOL(iosf_mbi_register_pmic_bus_access_notifier);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ciint iosf_mbi_unregister_pmic_bus_access_notifier_unlocked(
41662306a36Sopenharmony_ci	struct notifier_block *nb)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	iosf_mbi_assert_punit_acquired();
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	return blocking_notifier_chain_unregister(
42162306a36Sopenharmony_ci				&iosf_mbi_pmic_bus_access_notifier, nb);
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ciEXPORT_SYMBOL(iosf_mbi_unregister_pmic_bus_access_notifier_unlocked);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ciint iosf_mbi_unregister_pmic_bus_access_notifier(struct notifier_block *nb)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	int ret;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	/* Wait for the bus to go inactive before unregistering */
43062306a36Sopenharmony_ci	iosf_mbi_punit_acquire();
43162306a36Sopenharmony_ci	ret = iosf_mbi_unregister_pmic_bus_access_notifier_unlocked(nb);
43262306a36Sopenharmony_ci	iosf_mbi_punit_release();
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	return ret;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ciEXPORT_SYMBOL(iosf_mbi_unregister_pmic_bus_access_notifier);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_civoid iosf_mbi_assert_punit_acquired(void)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	WARN_ON(iosf_mbi_pmic_punit_access_count == 0);
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ciEXPORT_SYMBOL(iosf_mbi_assert_punit_acquired);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci/**************** iosf_mbi debug code ****************/
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci#ifdef CONFIG_IOSF_MBI_DEBUG
44762306a36Sopenharmony_cistatic u32	dbg_mdr;
44862306a36Sopenharmony_cistatic u32	dbg_mcr;
44962306a36Sopenharmony_cistatic u32	dbg_mcrx;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cistatic int mcr_get(void *data, u64 *val)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	*val = *(u32 *)data;
45462306a36Sopenharmony_ci	return 0;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic int mcr_set(void *data, u64 val)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	u8 command = ((u32)val & 0xFF000000) >> 24,
46062306a36Sopenharmony_ci	   port	   = ((u32)val & 0x00FF0000) >> 16,
46162306a36Sopenharmony_ci	   offset  = ((u32)val & 0x0000FF00) >> 8;
46262306a36Sopenharmony_ci	int err;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	*(u32 *)data = val;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	if (!capable(CAP_SYS_RAWIO))
46762306a36Sopenharmony_ci		return -EACCES;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	if (command & 1u)
47062306a36Sopenharmony_ci		err = iosf_mbi_write(port,
47162306a36Sopenharmony_ci			       command,
47262306a36Sopenharmony_ci			       dbg_mcrx | offset,
47362306a36Sopenharmony_ci			       dbg_mdr);
47462306a36Sopenharmony_ci	else
47562306a36Sopenharmony_ci		err = iosf_mbi_read(port,
47662306a36Sopenharmony_ci			      command,
47762306a36Sopenharmony_ci			      dbg_mcrx | offset,
47862306a36Sopenharmony_ci			      &dbg_mdr);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	return err;
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(iosf_mcr_fops, mcr_get, mcr_set , "%llx\n");
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic struct dentry *iosf_dbg;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_cistatic void iosf_sideband_debug_init(void)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	iosf_dbg = debugfs_create_dir("iosf_sb", NULL);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	/* mdr */
49162306a36Sopenharmony_ci	debugfs_create_x32("mdr", 0660, iosf_dbg, &dbg_mdr);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	/* mcrx */
49462306a36Sopenharmony_ci	debugfs_create_x32("mcrx", 0660, iosf_dbg, &dbg_mcrx);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	/* mcr - initiates mailbox transaction */
49762306a36Sopenharmony_ci	debugfs_create_file("mcr", 0660, iosf_dbg, &dbg_mcr, &iosf_mcr_fops);
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cistatic void iosf_debugfs_init(void)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	iosf_sideband_debug_init();
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistatic void iosf_debugfs_remove(void)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	debugfs_remove_recursive(iosf_dbg);
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci#else
51062306a36Sopenharmony_cistatic inline void iosf_debugfs_init(void) { }
51162306a36Sopenharmony_cistatic inline void iosf_debugfs_remove(void) { }
51262306a36Sopenharmony_ci#endif /* CONFIG_IOSF_MBI_DEBUG */
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistatic int iosf_mbi_probe(struct pci_dev *pdev,
51562306a36Sopenharmony_ci			  const struct pci_device_id *dev_id)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	int ret;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	ret = pci_enable_device(pdev);
52062306a36Sopenharmony_ci	if (ret < 0) {
52162306a36Sopenharmony_ci		dev_err(&pdev->dev, "error: could not enable device\n");
52262306a36Sopenharmony_ci		return ret;
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	mbi_pdev = pci_dev_get(pdev);
52662306a36Sopenharmony_ci	iosf_mbi_sem_address = dev_id->driver_data;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	return 0;
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic const struct pci_device_id iosf_mbi_pci_ids[] = {
53262306a36Sopenharmony_ci	{ PCI_DEVICE_DATA(INTEL, BAYTRAIL, PUNIT_SEMAPHORE_BYT) },
53362306a36Sopenharmony_ci	{ PCI_DEVICE_DATA(INTEL, BRASWELL, PUNIT_SEMAPHORE_CHT) },
53462306a36Sopenharmony_ci	{ PCI_DEVICE_DATA(INTEL, QUARK_X1000, 0) },
53562306a36Sopenharmony_ci	{ PCI_DEVICE_DATA(INTEL, TANGIER, 0) },
53662306a36Sopenharmony_ci	{ 0, },
53762306a36Sopenharmony_ci};
53862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, iosf_mbi_pci_ids);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_cistatic struct pci_driver iosf_mbi_pci_driver = {
54162306a36Sopenharmony_ci	.name		= "iosf_mbi_pci",
54262306a36Sopenharmony_ci	.probe		= iosf_mbi_probe,
54362306a36Sopenharmony_ci	.id_table	= iosf_mbi_pci_ids,
54462306a36Sopenharmony_ci};
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cistatic int __init iosf_mbi_init(void)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	iosf_debugfs_init();
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	cpu_latency_qos_add_request(&iosf_mbi_pm_qos, PM_QOS_DEFAULT_VALUE);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	return pci_register_driver(&iosf_mbi_pci_driver);
55362306a36Sopenharmony_ci}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_cistatic void __exit iosf_mbi_exit(void)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	iosf_debugfs_remove();
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	pci_unregister_driver(&iosf_mbi_pci_driver);
56062306a36Sopenharmony_ci	pci_dev_put(mbi_pdev);
56162306a36Sopenharmony_ci	mbi_pdev = NULL;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	cpu_latency_qos_remove_request(&iosf_mbi_pm_qos);
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cimodule_init(iosf_mbi_init);
56762306a36Sopenharmony_cimodule_exit(iosf_mbi_exit);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ciMODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
57062306a36Sopenharmony_ciMODULE_DESCRIPTION("IOSF Mailbox Interface accessor");
57162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
572