@@ -6,6 +6,7 @@
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/psp-sev.h>
#include <linux/types.h>
+#include <linux/workqueue.h>
#include <asm/msr.h>
@@ -15,6 +16,8 @@
#define PSP_MBOX_OFFSET 0x10570
#define PSP_CMD_TIMEOUT_US (500 * USEC_PER_MSEC)
+#define PSP_I2C_RESERVATION_TIME_MS 100
+
#define PSP_I2C_REQ_BUS_CMD 0x64
#define PSP_I2C_REQ_RETRY_CNT 400
#define PSP_I2C_REQ_RETRY_DELAY_US (25 * USEC_PER_MSEC)
@@ -240,6 +243,42 @@ static int psp_send_i2c_req(enum psp_i2c_req_type i2c_req_type)
return ret;
}
+static void release_bus(void)
+{
+ int status;
+
+ if (!psp_i2c_sem_acquired)
+ return;
+
+ status = psp_send_i2c_req(PSP_I2C_REQ_RELEASE);
+ if (status)
+ return;
+
+ dev_dbg(psp_i2c_dev, "PSP semaphore held for %ums\n",
+ jiffies_to_msecs(jiffies - psp_i2c_sem_acquired));
+
+ psp_i2c_sem_acquired = 0;
+}
+
+static void psp_release_i2c_bus_deferred(struct work_struct *work)
+{
+
+ mutex_lock(&psp_i2c_access_mutex);
+
+ /*
+ * If there is any pending transaction, cannot release the bus here.
+ * psp_release_i2c_bus will take care of this later.
+ */
+ if (psp_i2c_access_count)
+ goto cleanup;
+
+ release_bus();
+
+cleanup:
+ mutex_unlock(&psp_i2c_access_mutex);
+}
+static DECLARE_DELAYED_WORK(release_queue, psp_release_i2c_bus_deferred);
+
static int psp_acquire_i2c_bus(void)
{
int status;
@@ -250,21 +289,23 @@ static int psp_acquire_i2c_bus(void)
if (psp_i2c_mbox_fail)
goto cleanup;
+ psp_i2c_access_count++;
+
/*
- * Simply increment usage counter and return if PSP semaphore was
- * already taken by kernel.
+ * No need to request bus arbitration once we are inside semaphore
+ * reservation period.
*/
- if (psp_i2c_access_count) {
- psp_i2c_access_count++;
+ if (psp_i2c_sem_acquired)
goto cleanup;
- }
status = psp_send_i2c_req(PSP_I2C_REQ_ACQUIRE);
if (status)
goto cleanup;
psp_i2c_sem_acquired = jiffies;
- psp_i2c_access_count++;
+
+ schedule_delayed_work(&release_queue,
+ msecs_to_jiffies(PSP_I2C_RESERVATION_TIME_MS));
/*
* In case of errors with PSP arbitrator psp_i2c_mbox_fail variable is
@@ -279,8 +320,6 @@ static int psp_acquire_i2c_bus(void)
static void psp_release_i2c_bus(void)
{
- int status;
-
mutex_lock(&psp_i2c_access_mutex);
/* Return early if mailbox was malfunctional */
@@ -295,13 +334,12 @@ static void psp_release_i2c_bus(void)
if (psp_i2c_access_count)
goto cleanup;
- /* Send a release command to PSP */
- status = psp_send_i2c_req(PSP_I2C_REQ_RELEASE);
- if (status)
- goto cleanup;
-
- dev_dbg(psp_i2c_dev, "PSP semaphore held for %ums\n",
- jiffies_to_msecs(jiffies - psp_i2c_sem_acquired));
+ /*
+ * Send a release command to PSP if the semaphore reservation timeout
+ * elapsed but x86 still owns the controller.
+ */
+ if (!delayed_work_pending(&release_queue))
+ release_bus();
cleanup:
mutex_unlock(&psp_i2c_access_mutex);
In order to optimize performance, limit amount of back and forth transactions between x86 and PSP. This is done by introduction of semaphore reservation period - that is window in which x86 isn't releasing the bus immediately after each I2C transaction. In order to protect PSP from being starved while waiting for arbitration, after a programmed time bus is automatically released by a deferred function. Signed-off-by: Jan Dabros <jsd@semihalf.com> --- drivers/i2c/busses/i2c-designware-amdpsp.c | 68 +++++++++++++++++----- 1 file changed, 53 insertions(+), 15 deletions(-)