mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-23 15:36:10 +07:00
e09db3d241
On some BYT/CHT systems the SoC's P-Unit shares the I2C bus with the kernel. The P-Unit has a semaphore for the PMIC bus which we can take to block it from accessing the shared bus while the kernel wants to access it. Currently we have the I2C-controller driver acquiring and releasing the semaphore around each I2C transfer. There are 2 problems with this: 1) PMIC accesses often come in the form of a read-modify-write on one of the PMIC registers, we currently release the P-Unit's PMIC bus semaphore between the read and the write. If the P-Unit modifies the register during this window?, then we end up overwriting the P-Unit's changes. I believe that this is mostly an academic problem, but I'm not sure. 2) To safely access the shared I2C bus, we need to do 3 things: a) Notify the GPU driver that we are starting a window in which it may not access the P-Unit, since the P-Unit seems to ignore the semaphore for explicit power-level requests made by the GPU driver b) Make a pm_qos request to force all CPU cores out of C6/C7 since entering C6/C7 while we hold the semaphore hangs the SoC c) Finally take the P-Unit's PMIC bus semaphore All 3 these steps together are somewhat expensive, so ideally if we have a bunch of i2c transfers grouped together we only do this once for the entire group. Taking the read-modify-write on a PMIC register as example then ideally we would only do all 3 steps once at the beginning and undo all 3 steps once at the end. For this we need to be able to take the semaphore from within e.g. the PMIC opregion driver, yet we do not want to remove the taking of the semaphore from the I2C-controller driver, as that is still necessary to protect many other code-paths leading to accessing the shared I2C bus. This means that we first have the PMIC driver acquire the semaphore and then have the I2C controller driver trying to acquire it again. To make this possible this commit does the following: 1) Move the semaphore code from being private to the I2C controller driver into the generic iosf_mbi code, which already has other code to deal with the shared bus so that it can be accessed outside of the I2C bus driver. 2) Rework the code so that it can be called multiple times nested, while still blocking I2C accesses while e.g. the GPU driver has indicated the P-Unit needs the bus through a iosf_mbi_punit_acquire() call. Signed-off-by: Hans de Goede <hdegoede@redhat.com> Acked-by: Jarkko Nikula <jarkko.nikula@linux.intel.com> Tested-by: Jarkko Nikula <jarkko.nikula@linux.intel.com> Acked-by: Wolfram Sang <wsa@the-dreams.de> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
59 lines
1.1 KiB
C
59 lines
1.1 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Intel BayTrail PMIC I2C bus semaphore implementaion
|
|
* Copyright (c) 2014, Intel Corporation.
|
|
*/
|
|
#include <linux/device.h>
|
|
#include <linux/acpi.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <asm/iosf_mbi.h>
|
|
|
|
#include "i2c-designware-core.h"
|
|
|
|
static int baytrail_i2c_acquire(struct dw_i2c_dev *dev)
|
|
{
|
|
return iosf_mbi_block_punit_i2c_access();
|
|
}
|
|
|
|
static void baytrail_i2c_release(struct dw_i2c_dev *dev)
|
|
{
|
|
iosf_mbi_unblock_punit_i2c_access();
|
|
}
|
|
|
|
int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev)
|
|
{
|
|
acpi_status status;
|
|
unsigned long long shared_host = 0;
|
|
acpi_handle handle;
|
|
|
|
if (!dev || !dev->dev)
|
|
return 0;
|
|
|
|
handle = ACPI_HANDLE(dev->dev);
|
|
if (!handle)
|
|
return 0;
|
|
|
|
status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host);
|
|
if (ACPI_FAILURE(status))
|
|
return 0;
|
|
|
|
if (!shared_host)
|
|
return 0;
|
|
|
|
if (!iosf_mbi_available())
|
|
return -EPROBE_DEFER;
|
|
|
|
dev_info(dev->dev, "I2C bus managed by PUNIT\n");
|
|
dev->acquire_lock = baytrail_i2c_acquire;
|
|
dev->release_lock = baytrail_i2c_release;
|
|
dev->pm_disabled = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void i2c_dw_remove_lock_support(struct dw_i2c_dev *dev)
|
|
{
|
|
}
|