|
24 | 24 | #include <linux/slab.h>
|
25 | 25 | #include <linux/soc/ti/ti-msgmgr.h>
|
26 | 26 | #include <linux/soc/ti/ti_sci_protocol.h>
|
| 27 | +#include <linux/suspend.h> |
27 | 28 | #include <linux/sys_soc.h>
|
28 | 29 | #include <linux/reboot.h>
|
29 | 30 |
|
@@ -1654,6 +1655,68 @@ static int ti_sci_cmd_clk_get_freq(const struct ti_sci_handle *handle,
|
1654 | 1655 | return ret;
|
1655 | 1656 | }
|
1656 | 1657 |
|
| 1658 | +/** |
| 1659 | + * ti_sci_cmd_prepare_sleep() - Prepare system for system suspend |
| 1660 | + * @handle: pointer to TI SCI handle |
| 1661 | + * @mode: Device identifier |
| 1662 | + * @ctx_lo: Low part of address for context save |
| 1663 | + * @ctx_hi: High part of address for context save |
| 1664 | + * @debug_flags: Debug flags to pass to firmware |
| 1665 | + * |
| 1666 | + * Return: 0 if all went well, else returns appropriate error value. |
| 1667 | + */ |
| 1668 | +static int ti_sci_cmd_prepare_sleep(const struct ti_sci_handle *handle, u8 mode, |
| 1669 | + u32 ctx_lo, u32 ctx_hi, u32 debug_flags) |
| 1670 | +{ |
| 1671 | + struct ti_sci_info *info; |
| 1672 | + struct ti_sci_msg_req_prepare_sleep *req; |
| 1673 | + struct ti_sci_msg_hdr *resp; |
| 1674 | + struct ti_sci_xfer *xfer; |
| 1675 | + struct device *dev; |
| 1676 | + int ret = 0; |
| 1677 | + |
| 1678 | + if (IS_ERR(handle)) |
| 1679 | + return PTR_ERR(handle); |
| 1680 | + if (!handle) |
| 1681 | + return -EINVAL; |
| 1682 | + |
| 1683 | + info = handle_to_ti_sci_info(handle); |
| 1684 | + dev = info->dev; |
| 1685 | + |
| 1686 | + xfer = ti_sci_get_one_xfer(info, TI_SCI_MSG_PREPARE_SLEEP, |
| 1687 | + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, |
| 1688 | + sizeof(*req), sizeof(*resp)); |
| 1689 | + if (IS_ERR(xfer)) { |
| 1690 | + ret = PTR_ERR(xfer); |
| 1691 | + dev_err(dev, "Message alloc failed(%d)\n", ret); |
| 1692 | + return ret; |
| 1693 | + } |
| 1694 | + |
| 1695 | + req = (struct ti_sci_msg_req_prepare_sleep *)xfer->xfer_buf; |
| 1696 | + req->mode = mode; |
| 1697 | + req->ctx_lo = ctx_lo; |
| 1698 | + req->ctx_hi = ctx_hi; |
| 1699 | + req->debug_flags = debug_flags; |
| 1700 | + |
| 1701 | + ret = ti_sci_do_xfer(info, xfer); |
| 1702 | + if (ret) { |
| 1703 | + dev_err(dev, "Mbox send fail %d\n", ret); |
| 1704 | + goto fail; |
| 1705 | + } |
| 1706 | + |
| 1707 | + resp = (struct ti_sci_msg_hdr *)xfer->xfer_buf; |
| 1708 | + |
| 1709 | + if (!ti_sci_is_response_ack(resp)) { |
| 1710 | + dev_err(dev, "Failed to prepare sleep\n"); |
| 1711 | + ret = -ENODEV; |
| 1712 | + } |
| 1713 | + |
| 1714 | +fail: |
| 1715 | + ti_sci_put_one_xfer(&info->minfo, xfer); |
| 1716 | + |
| 1717 | + return ret; |
| 1718 | +} |
| 1719 | + |
1657 | 1720 | /**
|
1658 | 1721 | * ti_sci_msg_cmd_query_fw_caps() - Get the FW/SoC capabilities
|
1659 | 1722 | * @handle: Pointer to TI SCI handle
|
@@ -1715,6 +1778,61 @@ static int ti_sci_msg_cmd_query_fw_caps(const struct ti_sci_handle *handle,
|
1715 | 1778 | return ret;
|
1716 | 1779 | }
|
1717 | 1780 |
|
| 1781 | +/** |
| 1782 | + * ti_sci_cmd_set_io_isolation() - Enable IO isolation in LPM |
| 1783 | + * @handle: Pointer to TI SCI handle |
| 1784 | + * @state: The desired state of the IO isolation |
| 1785 | + * |
| 1786 | + * Return: 0 if all went well, else returns appropriate error value. |
| 1787 | + */ |
| 1788 | +static int ti_sci_cmd_set_io_isolation(const struct ti_sci_handle *handle, |
| 1789 | + u8 state) |
| 1790 | +{ |
| 1791 | + struct ti_sci_info *info; |
| 1792 | + struct ti_sci_msg_req_set_io_isolation *req; |
| 1793 | + struct ti_sci_msg_hdr *resp; |
| 1794 | + struct ti_sci_xfer *xfer; |
| 1795 | + struct device *dev; |
| 1796 | + int ret = 0; |
| 1797 | + |
| 1798 | + if (IS_ERR(handle)) |
| 1799 | + return PTR_ERR(handle); |
| 1800 | + if (!handle) |
| 1801 | + return -EINVAL; |
| 1802 | + |
| 1803 | + info = handle_to_ti_sci_info(handle); |
| 1804 | + dev = info->dev; |
| 1805 | + |
| 1806 | + xfer = ti_sci_get_one_xfer(info, TI_SCI_MSG_SET_IO_ISOLATION, |
| 1807 | + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, |
| 1808 | + sizeof(*req), sizeof(*resp)); |
| 1809 | + if (IS_ERR(xfer)) { |
| 1810 | + ret = PTR_ERR(xfer); |
| 1811 | + dev_err(dev, "Message alloc failed(%d)\n", ret); |
| 1812 | + return ret; |
| 1813 | + } |
| 1814 | + req = (struct ti_sci_msg_req_set_io_isolation *)xfer->xfer_buf; |
| 1815 | + req->state = state; |
| 1816 | + |
| 1817 | + ret = ti_sci_do_xfer(info, xfer); |
| 1818 | + if (ret) { |
| 1819 | + dev_err(dev, "Mbox send fail %d\n", ret); |
| 1820 | + goto fail; |
| 1821 | + } |
| 1822 | + |
| 1823 | + resp = (struct ti_sci_msg_hdr *)xfer->xfer_buf; |
| 1824 | + |
| 1825 | + if (!ti_sci_is_response_ack(resp)) { |
| 1826 | + dev_err(dev, "Failed to set IO isolation\n"); |
| 1827 | + ret = -ENODEV; |
| 1828 | + } |
| 1829 | + |
| 1830 | +fail: |
| 1831 | + ti_sci_put_one_xfer(&info->minfo, xfer); |
| 1832 | + |
| 1833 | + return ret; |
| 1834 | +} |
| 1835 | + |
1718 | 1836 | static int ti_sci_cmd_core_reboot(const struct ti_sci_handle *handle)
|
1719 | 1837 | {
|
1720 | 1838 | struct ti_sci_info *info;
|
@@ -3326,6 +3444,81 @@ static int tisci_reboot_handler(struct sys_off_data *data)
|
3326 | 3444 | return NOTIFY_BAD;
|
3327 | 3445 | }
|
3328 | 3446 |
|
| 3447 | +static int ti_sci_prepare_system_suspend(struct ti_sci_info *info) |
| 3448 | +{ |
| 3449 | + /* |
| 3450 | + * Map and validate the target Linux suspend state to TISCI LPM. |
| 3451 | + * Default is to let Device Manager select the low power mode. |
| 3452 | + */ |
| 3453 | + switch (pm_suspend_target_state) { |
| 3454 | + case PM_SUSPEND_MEM: |
| 3455 | + if (info->fw_caps & MSG_FLAG_CAPS_LPM_DM_MANAGED) { |
| 3456 | + /* |
| 3457 | + * For the DM_MANAGED mode the context is reserved for |
| 3458 | + * internal use and can be 0 |
| 3459 | + */ |
| 3460 | + return ti_sci_cmd_prepare_sleep(&info->handle, |
| 3461 | + TISCI_MSG_VALUE_SLEEP_MODE_DM_MANAGED, |
| 3462 | + 0, 0, 0); |
| 3463 | + } else { |
| 3464 | + /* DM Managed is not supported by the firmware. */ |
| 3465 | + dev_err(info->dev, "Suspend to memory is not supported by the firmware\n"); |
| 3466 | + return -EOPNOTSUPP; |
| 3467 | + } |
| 3468 | + break; |
| 3469 | + default: |
| 3470 | + /* |
| 3471 | + * Do not fail if we don't have action to take for a |
| 3472 | + * specific suspend mode. |
| 3473 | + */ |
| 3474 | + return 0; |
| 3475 | + } |
| 3476 | +} |
| 3477 | + |
| 3478 | +static int __maybe_unused ti_sci_suspend(struct device *dev) |
| 3479 | +{ |
| 3480 | + struct ti_sci_info *info = dev_get_drvdata(dev); |
| 3481 | + int ret; |
| 3482 | + |
| 3483 | + ret = ti_sci_prepare_system_suspend(info); |
| 3484 | + if (ret) |
| 3485 | + return ret; |
| 3486 | + |
| 3487 | + return 0; |
| 3488 | +} |
| 3489 | + |
| 3490 | +static int __maybe_unused ti_sci_suspend_noirq(struct device *dev) |
| 3491 | +{ |
| 3492 | + struct ti_sci_info *info = dev_get_drvdata(dev); |
| 3493 | + int ret = 0; |
| 3494 | + |
| 3495 | + ret = ti_sci_cmd_set_io_isolation(&info->handle, TISCI_MSG_VALUE_IO_ENABLE); |
| 3496 | + if (ret) |
| 3497 | + return ret; |
| 3498 | + |
| 3499 | + return 0; |
| 3500 | +} |
| 3501 | + |
| 3502 | +static int __maybe_unused ti_sci_resume_noirq(struct device *dev) |
| 3503 | +{ |
| 3504 | + struct ti_sci_info *info = dev_get_drvdata(dev); |
| 3505 | + int ret = 0; |
| 3506 | + |
| 3507 | + ret = ti_sci_cmd_set_io_isolation(&info->handle, TISCI_MSG_VALUE_IO_DISABLE); |
| 3508 | + if (ret) |
| 3509 | + return ret; |
| 3510 | + |
| 3511 | + return 0; |
| 3512 | +} |
| 3513 | + |
| 3514 | +static const struct dev_pm_ops ti_sci_pm_ops = { |
| 3515 | +#ifdef CONFIG_PM_SLEEP |
| 3516 | + .suspend = ti_sci_suspend, |
| 3517 | + .suspend_noirq = ti_sci_suspend_noirq, |
| 3518 | + .resume_noirq = ti_sci_resume_noirq, |
| 3519 | +#endif |
| 3520 | +}; |
| 3521 | + |
3329 | 3522 | /* Description for K2G */
|
3330 | 3523 | static const struct ti_sci_desc ti_sci_pmmc_k2g_desc = {
|
3331 | 3524 | .default_host_id = 2,
|
@@ -3494,6 +3687,7 @@ static struct platform_driver ti_sci_driver = {
|
3494 | 3687 | .name = "ti-sci",
|
3495 | 3688 | .of_match_table = of_match_ptr(ti_sci_of_match),
|
3496 | 3689 | .suppress_bind_attrs = true,
|
| 3690 | + .pm = &ti_sci_pm_ops, |
3497 | 3691 | },
|
3498 | 3692 | };
|
3499 | 3693 | module_platform_driver(ti_sci_driver);
|
|
0 commit comments