Skip to content

Commit 6077cfd

Browse files
Kartik RajputWolfram Sang
authored andcommitted
i2c: tegra: Add support for SW mutex register
Add support for SW mutex register introduced in Tegra264 to provide an option to share the interface between multiple firmwares and/or VMs. This involves following steps: - A firmware/OS writes its unique ID to the mutex REQUEST field. - Ownership is established when reading the GRANT field returns the same ID. - If GRANT shows a different non-zero ID, the firmware/OS retries until timeout. - After completing access, it releases the mutex by writing 0. However, the hardware does not ensure any protection based on the values. The driver/firmware should honor the peer who already holds the mutex. Signed-off-by: Kartik Rajput <kkartik@nvidia.com> Signed-off-by: Akhil R <akhilrajeev@nvidia.com> Reviewed-by: Jon Hunter <jonathanh@nvidia.com> Acked-by: Thierry Reding <treding@nvidia.com> Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
1 parent 978b3cc commit 6077cfd

1 file changed

Lines changed: 93 additions & 0 deletions

File tree

drivers/i2c/busses/i2c-tegra.c

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,14 @@
137137

138138
#define I2C_MASTER_RESET_CNTRL 0x0a8
139139

140+
#define I2C_SW_MUTEX 0x0ec
141+
#define I2C_SW_MUTEX_REQUEST GENMASK(3, 0)
142+
#define I2C_SW_MUTEX_GRANT GENMASK(7, 4)
143+
#define I2C_SW_MUTEX_ID_CCPLEX 9
144+
145+
/* SW mutex acquire timeout value in microseconds. */
146+
#define I2C_SW_MUTEX_TIMEOUT_US (25 * USEC_PER_MSEC)
147+
140148
/* configuration load timeout in microseconds */
141149
#define I2C_CONFIG_LOAD_TIMEOUT 1000000
142150

@@ -214,6 +222,7 @@ enum msg_end_type {
214222
* @has_interface_timing_reg: Has interface timing register to program the tuned
215223
* timing settings.
216224
* @enable_hs_mode_support: Enable support for high speed (HS) mode transfers.
225+
* @has_mutex: Has mutex register for mutual exclusion with other firmwares or VMs.
217226
*/
218227
struct tegra_i2c_hw_feature {
219228
bool has_continue_xfer_support;
@@ -244,6 +253,7 @@ struct tegra_i2c_hw_feature {
244253
u32 setup_hold_time_hs_mode;
245254
bool has_interface_timing_reg;
246255
bool enable_hs_mode_support;
256+
bool has_mutex;
247257
};
248258

249259
/**
@@ -388,6 +398,76 @@ static void i2c_readsl(struct tegra_i2c_dev *i2c_dev, void *data,
388398
readsl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg), data, len);
389399
}
390400

401+
static bool tegra_i2c_mutex_acquired(struct tegra_i2c_dev *i2c_dev)
402+
{
403+
unsigned int reg = tegra_i2c_reg_addr(i2c_dev, I2C_SW_MUTEX);
404+
u32 val, id;
405+
406+
val = readl(i2c_dev->base + reg);
407+
id = FIELD_GET(I2C_SW_MUTEX_GRANT, val);
408+
409+
return id == I2C_SW_MUTEX_ID_CCPLEX;
410+
}
411+
412+
static bool tegra_i2c_mutex_trylock(struct tegra_i2c_dev *i2c_dev)
413+
{
414+
unsigned int reg = tegra_i2c_reg_addr(i2c_dev, I2C_SW_MUTEX);
415+
u32 val, id;
416+
417+
val = readl(i2c_dev->base + reg);
418+
id = FIELD_GET(I2C_SW_MUTEX_GRANT, val);
419+
if (id != 0 && id != I2C_SW_MUTEX_ID_CCPLEX)
420+
return false;
421+
422+
val = FIELD_PREP(I2C_SW_MUTEX_REQUEST, I2C_SW_MUTEX_ID_CCPLEX);
423+
writel(val, i2c_dev->base + reg);
424+
425+
return tegra_i2c_mutex_acquired(i2c_dev);
426+
}
427+
428+
static int tegra_i2c_mutex_lock(struct tegra_i2c_dev *i2c_dev)
429+
{
430+
bool locked;
431+
int ret;
432+
433+
if (!i2c_dev->hw->has_mutex)
434+
return 0;
435+
436+
if (i2c_dev->atomic_mode)
437+
ret = read_poll_timeout_atomic(tegra_i2c_mutex_trylock, locked, locked,
438+
USEC_PER_MSEC, I2C_SW_MUTEX_TIMEOUT_US,
439+
false, i2c_dev);
440+
else
441+
ret = read_poll_timeout(tegra_i2c_mutex_trylock, locked, locked, USEC_PER_MSEC,
442+
I2C_SW_MUTEX_TIMEOUT_US, false, i2c_dev);
443+
444+
if (ret)
445+
dev_warn(i2c_dev->dev, "failed to acquire mutex\n");
446+
447+
return ret;
448+
}
449+
450+
static int tegra_i2c_mutex_unlock(struct tegra_i2c_dev *i2c_dev)
451+
{
452+
unsigned int reg = tegra_i2c_reg_addr(i2c_dev, I2C_SW_MUTEX);
453+
u32 val, id;
454+
455+
if (!i2c_dev->hw->has_mutex)
456+
return 0;
457+
458+
val = readl(i2c_dev->base + reg);
459+
460+
id = FIELD_GET(I2C_SW_MUTEX_GRANT, val);
461+
if (id && id != I2C_SW_MUTEX_ID_CCPLEX) {
462+
dev_warn(i2c_dev->dev, "unable to unlock mutex, mutex is owned by: %u\n", id);
463+
return -EPERM;
464+
}
465+
466+
writel(0, i2c_dev->base + reg);
467+
468+
return 0;
469+
}
470+
391471
static void tegra_i2c_mask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
392472
{
393473
u32 int_mask;
@@ -1443,6 +1523,10 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
14431523
return ret;
14441524
}
14451525

1526+
ret = tegra_i2c_mutex_lock(i2c_dev);
1527+
if (ret)
1528+
return ret;
1529+
14461530
for (i = 0; i < num; i++) {
14471531
enum msg_end_type end_type = MSG_END_STOP;
14481532

@@ -1472,6 +1556,7 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
14721556
break;
14731557
}
14741558

1559+
ret = tegra_i2c_mutex_unlock(i2c_dev);
14751560
pm_runtime_put(i2c_dev->dev);
14761561

14771562
return ret ?: i;
@@ -1551,6 +1636,7 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
15511636
.setup_hold_time_hs_mode = 0x0,
15521637
.has_interface_timing_reg = false,
15531638
.enable_hs_mode_support = false,
1639+
.has_mutex = false,
15541640
};
15551641

15561642
static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
@@ -1580,6 +1666,7 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
15801666
.setup_hold_time_hs_mode = 0x0,
15811667
.has_interface_timing_reg = false,
15821668
.enable_hs_mode_support = false,
1669+
.has_mutex = false,
15831670
};
15841671

15851672
static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
@@ -1609,6 +1696,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
16091696
.setup_hold_time_hs_mode = 0x0,
16101697
.has_interface_timing_reg = false,
16111698
.enable_hs_mode_support = false,
1699+
.has_mutex = false,
16121700
};
16131701

16141702
static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
@@ -1638,6 +1726,7 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
16381726
.setup_hold_time_hs_mode = 0x0,
16391727
.has_interface_timing_reg = true,
16401728
.enable_hs_mode_support = false,
1729+
.has_mutex = false,
16411730
};
16421731

16431732
static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
@@ -1667,6 +1756,7 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
16671756
.setup_hold_time_hs_mode = 0,
16681757
.has_interface_timing_reg = true,
16691758
.enable_hs_mode_support = false,
1759+
.has_mutex = false,
16701760
};
16711761

16721762
static const struct tegra_i2c_hw_feature tegra186_i2c_hw = {
@@ -1696,6 +1786,7 @@ static const struct tegra_i2c_hw_feature tegra186_i2c_hw = {
16961786
.setup_hold_time_hs_mode = 0,
16971787
.has_interface_timing_reg = true,
16981788
.enable_hs_mode_support = false,
1789+
.has_mutex = false,
16991790
};
17001791

17011792
static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
@@ -1727,6 +1818,7 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = {
17271818
.setup_hold_time_hs_mode = 0x090909,
17281819
.has_interface_timing_reg = true,
17291820
.enable_hs_mode_support = true,
1821+
.has_mutex = false,
17301822
};
17311823

17321824
static const struct tegra_i2c_hw_feature tegra256_i2c_hw = {
@@ -1758,6 +1850,7 @@ static const struct tegra_i2c_hw_feature tegra256_i2c_hw = {
17581850
.setup_hold_time_hs_mode = 0x030303,
17591851
.has_interface_timing_reg = true,
17601852
.enable_hs_mode_support = true,
1853+
.has_mutex = true,
17611854
};
17621855

17631856
static const struct of_device_id tegra_i2c_of_match[] = {

0 commit comments

Comments
 (0)