i2c: designware: Introduce semaphore reservation timer to AMDPSP driver
authorJan Dabros <jsd@semihalf.com>
Fri, 12 Aug 2022 07:15:26 +0000 (09:15 +0200)
committerWolfram Sang <wsa@kernel.org>
Sat, 20 Aug 2022 06:29:04 +0000 (08:29 +0200)
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>
Acked-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
[wsa: removed an unneeded empty line]
Signed-off-by: Wolfram Sang <wsa@kernel.org>
drivers/i2c/busses/i2c-designware-amdpsp.c

index b624356..8f36167 100644 (file)
@@ -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,41 @@ cleanup:
        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 +288,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 +319,6 @@ cleanup:
 
 static void psp_release_i2c_bus(void)
 {
-       int status;
-
        mutex_lock(&psp_i2c_access_mutex);
 
        /* Return early if mailbox was malfunctional */
@@ -295,13 +333,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);