|
137 | 137 |
|
138 | 138 | #define I2C_MASTER_RESET_CNTRL 0x0a8 |
139 | 139 |
|
| 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 | + |
140 | 148 | /* configuration load timeout in microseconds */ |
141 | 149 | #define I2C_CONFIG_LOAD_TIMEOUT 1000000 |
142 | 150 |
|
@@ -214,6 +222,7 @@ enum msg_end_type { |
214 | 222 | * @has_interface_timing_reg: Has interface timing register to program the tuned |
215 | 223 | * timing settings. |
216 | 224 | * @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. |
217 | 226 | */ |
218 | 227 | struct tegra_i2c_hw_feature { |
219 | 228 | bool has_continue_xfer_support; |
@@ -244,6 +253,7 @@ struct tegra_i2c_hw_feature { |
244 | 253 | u32 setup_hold_time_hs_mode; |
245 | 254 | bool has_interface_timing_reg; |
246 | 255 | bool enable_hs_mode_support; |
| 256 | + bool has_mutex; |
247 | 257 | }; |
248 | 258 |
|
249 | 259 | /** |
@@ -388,6 +398,76 @@ static void i2c_readsl(struct tegra_i2c_dev *i2c_dev, void *data, |
388 | 398 | readsl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg), data, len); |
389 | 399 | } |
390 | 400 |
|
| 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 | + |
391 | 471 | static void tegra_i2c_mask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask) |
392 | 472 | { |
393 | 473 | u32 int_mask; |
@@ -1443,6 +1523,10 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], |
1443 | 1523 | return ret; |
1444 | 1524 | } |
1445 | 1525 |
|
| 1526 | + ret = tegra_i2c_mutex_lock(i2c_dev); |
| 1527 | + if (ret) |
| 1528 | + return ret; |
| 1529 | + |
1446 | 1530 | for (i = 0; i < num; i++) { |
1447 | 1531 | enum msg_end_type end_type = MSG_END_STOP; |
1448 | 1532 |
|
@@ -1472,6 +1556,7 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], |
1472 | 1556 | break; |
1473 | 1557 | } |
1474 | 1558 |
|
| 1559 | + ret = tegra_i2c_mutex_unlock(i2c_dev); |
1475 | 1560 | pm_runtime_put(i2c_dev->dev); |
1476 | 1561 |
|
1477 | 1562 | return ret ?: i; |
@@ -1551,6 +1636,7 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { |
1551 | 1636 | .setup_hold_time_hs_mode = 0x0, |
1552 | 1637 | .has_interface_timing_reg = false, |
1553 | 1638 | .enable_hs_mode_support = false, |
| 1639 | + .has_mutex = false, |
1554 | 1640 | }; |
1555 | 1641 |
|
1556 | 1642 | static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { |
@@ -1580,6 +1666,7 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { |
1580 | 1666 | .setup_hold_time_hs_mode = 0x0, |
1581 | 1667 | .has_interface_timing_reg = false, |
1582 | 1668 | .enable_hs_mode_support = false, |
| 1669 | + .has_mutex = false, |
1583 | 1670 | }; |
1584 | 1671 |
|
1585 | 1672 | static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { |
@@ -1609,6 +1696,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { |
1609 | 1696 | .setup_hold_time_hs_mode = 0x0, |
1610 | 1697 | .has_interface_timing_reg = false, |
1611 | 1698 | .enable_hs_mode_support = false, |
| 1699 | + .has_mutex = false, |
1612 | 1700 | }; |
1613 | 1701 |
|
1614 | 1702 | static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { |
@@ -1638,6 +1726,7 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { |
1638 | 1726 | .setup_hold_time_hs_mode = 0x0, |
1639 | 1727 | .has_interface_timing_reg = true, |
1640 | 1728 | .enable_hs_mode_support = false, |
| 1729 | + .has_mutex = false, |
1641 | 1730 | }; |
1642 | 1731 |
|
1643 | 1732 | static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { |
@@ -1667,6 +1756,7 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { |
1667 | 1756 | .setup_hold_time_hs_mode = 0, |
1668 | 1757 | .has_interface_timing_reg = true, |
1669 | 1758 | .enable_hs_mode_support = false, |
| 1759 | + .has_mutex = false, |
1670 | 1760 | }; |
1671 | 1761 |
|
1672 | 1762 | static const struct tegra_i2c_hw_feature tegra186_i2c_hw = { |
@@ -1696,6 +1786,7 @@ static const struct tegra_i2c_hw_feature tegra186_i2c_hw = { |
1696 | 1786 | .setup_hold_time_hs_mode = 0, |
1697 | 1787 | .has_interface_timing_reg = true, |
1698 | 1788 | .enable_hs_mode_support = false, |
| 1789 | + .has_mutex = false, |
1699 | 1790 | }; |
1700 | 1791 |
|
1701 | 1792 | static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { |
@@ -1727,6 +1818,7 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { |
1727 | 1818 | .setup_hold_time_hs_mode = 0x090909, |
1728 | 1819 | .has_interface_timing_reg = true, |
1729 | 1820 | .enable_hs_mode_support = true, |
| 1821 | + .has_mutex = false, |
1730 | 1822 | }; |
1731 | 1823 |
|
1732 | 1824 | static const struct tegra_i2c_hw_feature tegra256_i2c_hw = { |
@@ -1758,6 +1850,7 @@ static const struct tegra_i2c_hw_feature tegra256_i2c_hw = { |
1758 | 1850 | .setup_hold_time_hs_mode = 0x030303, |
1759 | 1851 | .has_interface_timing_reg = true, |
1760 | 1852 | .enable_hs_mode_support = true, |
| 1853 | + .has_mutex = true, |
1761 | 1854 | }; |
1762 | 1855 |
|
1763 | 1856 | static const struct of_device_id tegra_i2c_of_match[] = { |
|
0 commit comments