diff options
author | Faiz Abbas <faiz_abbas@ti.com> | 2018-01-16 06:37:13 -0500 |
---|---|---|
committer | Marc Kleine-Budde <mkl@pengutronix.de> | 2018-01-16 09:11:34 -0500 |
commit | cdf8259d65739a04871513d4d17bb342ba1d4a46 (patch) | |
tree | a875f7ef76231766a17ead391c11c858a02397cf | |
parent | 63dffc1c699d2b808e7d192ffd67d443be136a56 (diff) |
can: m_can: Add PM Support
Add support for CONFIG_PM which is the new way to handle managing clocks.
Move the clock management to pm_runtime_resume() and pm_runtime_suspend()
callbacks for the driver.
CONFIG_PM is required by OMAP based devices to handle clock management.
Therefore, this allows future Texas Instruments SoCs that have the MCAN IP
to work with this driver.
Signed-off-by: Faiz Abbas <faiz_abbas@ti.com>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
-rw-r--r-- | drivers/net/can/m_can/m_can.c | 100 |
1 files changed, 62 insertions, 38 deletions
diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index f882cd5df4fa..2f0e56d4c332 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/of.h> | 23 | #include <linux/of.h> |
24 | #include <linux/of_device.h> | 24 | #include <linux/of_device.h> |
25 | #include <linux/platform_device.h> | 25 | #include <linux/platform_device.h> |
26 | #include <linux/pm_runtime.h> | ||
26 | #include <linux/iopoll.h> | 27 | #include <linux/iopoll.h> |
27 | #include <linux/can/dev.h> | 28 | #include <linux/can/dev.h> |
28 | 29 | ||
@@ -631,21 +632,16 @@ static int m_can_clk_start(struct m_can_priv *priv) | |||
631 | { | 632 | { |
632 | int err; | 633 | int err; |
633 | 634 | ||
634 | err = clk_prepare_enable(priv->hclk); | 635 | err = pm_runtime_get_sync(priv->device); |
635 | if (err) | 636 | if (err) |
636 | return err; | 637 | pm_runtime_put_noidle(priv->device); |
637 | |||
638 | err = clk_prepare_enable(priv->cclk); | ||
639 | if (err) | ||
640 | clk_disable_unprepare(priv->hclk); | ||
641 | 638 | ||
642 | return err; | 639 | return err; |
643 | } | 640 | } |
644 | 641 | ||
645 | static void m_can_clk_stop(struct m_can_priv *priv) | 642 | static void m_can_clk_stop(struct m_can_priv *priv) |
646 | { | 643 | { |
647 | clk_disable_unprepare(priv->cclk); | 644 | pm_runtime_put_sync(priv->device); |
648 | clk_disable_unprepare(priv->hclk); | ||
649 | } | 645 | } |
650 | 646 | ||
651 | static int m_can_get_berr_counter(const struct net_device *dev, | 647 | static int m_can_get_berr_counter(const struct net_device *dev, |
@@ -1594,37 +1590,26 @@ static int m_can_plat_probe(struct platform_device *pdev) | |||
1594 | goto failed_ret; | 1590 | goto failed_ret; |
1595 | } | 1591 | } |
1596 | 1592 | ||
1597 | /* Enable clocks. Necessary to read Core Release in order to determine | ||
1598 | * M_CAN version | ||
1599 | */ | ||
1600 | ret = clk_prepare_enable(hclk); | ||
1601 | if (ret) | ||
1602 | goto disable_hclk_ret; | ||
1603 | |||
1604 | ret = clk_prepare_enable(cclk); | ||
1605 | if (ret) | ||
1606 | goto disable_cclk_ret; | ||
1607 | |||
1608 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can"); | 1593 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can"); |
1609 | addr = devm_ioremap_resource(&pdev->dev, res); | 1594 | addr = devm_ioremap_resource(&pdev->dev, res); |
1610 | irq = platform_get_irq_byname(pdev, "int0"); | 1595 | irq = platform_get_irq_byname(pdev, "int0"); |
1611 | 1596 | ||
1612 | if (IS_ERR(addr) || irq < 0) { | 1597 | if (IS_ERR(addr) || irq < 0) { |
1613 | ret = -EINVAL; | 1598 | ret = -EINVAL; |
1614 | goto disable_cclk_ret; | 1599 | goto failed_ret; |
1615 | } | 1600 | } |
1616 | 1601 | ||
1617 | /* message ram could be shared */ | 1602 | /* message ram could be shared */ |
1618 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram"); | 1603 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram"); |
1619 | if (!res) { | 1604 | if (!res) { |
1620 | ret = -ENODEV; | 1605 | ret = -ENODEV; |
1621 | goto disable_cclk_ret; | 1606 | goto failed_ret; |
1622 | } | 1607 | } |
1623 | 1608 | ||
1624 | mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res)); | 1609 | mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res)); |
1625 | if (!mram_addr) { | 1610 | if (!mram_addr) { |
1626 | ret = -ENOMEM; | 1611 | ret = -ENOMEM; |
1627 | goto disable_cclk_ret; | 1612 | goto failed_ret; |
1628 | } | 1613 | } |
1629 | 1614 | ||
1630 | /* get message ram configuration */ | 1615 | /* get message ram configuration */ |
@@ -1633,7 +1618,7 @@ static int m_can_plat_probe(struct platform_device *pdev) | |||
1633 | sizeof(mram_config_vals) / 4); | 1618 | sizeof(mram_config_vals) / 4); |
1634 | if (ret) { | 1619 | if (ret) { |
1635 | dev_err(&pdev->dev, "Could not get Message RAM configuration."); | 1620 | dev_err(&pdev->dev, "Could not get Message RAM configuration."); |
1636 | goto disable_cclk_ret; | 1621 | goto failed_ret; |
1637 | } | 1622 | } |
1638 | 1623 | ||
1639 | /* Get TX FIFO size | 1624 | /* Get TX FIFO size |
@@ -1645,13 +1630,9 @@ static int m_can_plat_probe(struct platform_device *pdev) | |||
1645 | dev = alloc_candev(sizeof(*priv), tx_fifo_size); | 1630 | dev = alloc_candev(sizeof(*priv), tx_fifo_size); |
1646 | if (!dev) { | 1631 | if (!dev) { |
1647 | ret = -ENOMEM; | 1632 | ret = -ENOMEM; |
1648 | goto disable_cclk_ret; | 1633 | goto failed_ret; |
1649 | } | 1634 | } |
1650 | 1635 | ||
1651 | ret = m_can_dev_setup(pdev, dev, addr); | ||
1652 | if (ret) | ||
1653 | goto failed_free_dev; | ||
1654 | |||
1655 | priv = netdev_priv(dev); | 1636 | priv = netdev_priv(dev); |
1656 | dev->irq = irq; | 1637 | dev->irq = irq; |
1657 | priv->device = &pdev->dev; | 1638 | priv->device = &pdev->dev; |
@@ -1665,11 +1646,23 @@ static int m_can_plat_probe(struct platform_device *pdev) | |||
1665 | platform_set_drvdata(pdev, dev); | 1646 | platform_set_drvdata(pdev, dev); |
1666 | SET_NETDEV_DEV(dev, &pdev->dev); | 1647 | SET_NETDEV_DEV(dev, &pdev->dev); |
1667 | 1648 | ||
1649 | /* Enable clocks. Necessary to read Core Release in order to determine | ||
1650 | * M_CAN version | ||
1651 | */ | ||
1652 | pm_runtime_enable(&pdev->dev); | ||
1653 | ret = m_can_clk_start(priv); | ||
1654 | if (ret) | ||
1655 | goto pm_runtime_fail; | ||
1656 | |||
1657 | ret = m_can_dev_setup(pdev, dev, addr); | ||
1658 | if (ret) | ||
1659 | goto clk_disable; | ||
1660 | |||
1668 | ret = register_m_can_dev(dev); | 1661 | ret = register_m_can_dev(dev); |
1669 | if (ret) { | 1662 | if (ret) { |
1670 | dev_err(&pdev->dev, "registering %s failed (err=%d)\n", | 1663 | dev_err(&pdev->dev, "registering %s failed (err=%d)\n", |
1671 | KBUILD_MODNAME, ret); | 1664 | KBUILD_MODNAME, ret); |
1672 | goto failed_free_dev; | 1665 | goto clk_disable; |
1673 | } | 1666 | } |
1674 | 1667 | ||
1675 | devm_can_led_init(dev); | 1668 | devm_can_led_init(dev); |
@@ -1680,15 +1673,13 @@ static int m_can_plat_probe(struct platform_device *pdev) | |||
1680 | /* Probe finished | 1673 | /* Probe finished |
1681 | * Stop clocks. They will be reactivated once the M_CAN device is opened | 1674 | * Stop clocks. They will be reactivated once the M_CAN device is opened |
1682 | */ | 1675 | */ |
1683 | 1676 | clk_disable: | |
1684 | goto disable_cclk_ret; | 1677 | m_can_clk_stop(priv); |
1685 | 1678 | pm_runtime_fail: | |
1686 | failed_free_dev: | 1679 | if (ret) { |
1687 | free_candev(dev); | 1680 | pm_runtime_disable(&pdev->dev); |
1688 | disable_cclk_ret: | 1681 | free_candev(dev); |
1689 | clk_disable_unprepare(cclk); | 1682 | } |
1690 | disable_hclk_ret: | ||
1691 | clk_disable_unprepare(hclk); | ||
1692 | failed_ret: | 1683 | failed_ret: |
1693 | return ret; | 1684 | return ret; |
1694 | } | 1685 | } |
@@ -1746,6 +1737,9 @@ static int m_can_plat_remove(struct platform_device *pdev) | |||
1746 | struct net_device *dev = platform_get_drvdata(pdev); | 1737 | struct net_device *dev = platform_get_drvdata(pdev); |
1747 | 1738 | ||
1748 | unregister_m_can_dev(dev); | 1739 | unregister_m_can_dev(dev); |
1740 | |||
1741 | pm_runtime_disable(&pdev->dev); | ||
1742 | |||
1749 | platform_set_drvdata(pdev, NULL); | 1743 | platform_set_drvdata(pdev, NULL); |
1750 | 1744 | ||
1751 | free_candev(dev); | 1745 | free_candev(dev); |
@@ -1753,7 +1747,37 @@ static int m_can_plat_remove(struct platform_device *pdev) | |||
1753 | return 0; | 1747 | return 0; |
1754 | } | 1748 | } |
1755 | 1749 | ||
1750 | static int m_can_runtime_suspend(struct device *dev) | ||
1751 | { | ||
1752 | struct net_device *ndev = dev_get_drvdata(dev); | ||
1753 | struct m_can_priv *priv = netdev_priv(ndev); | ||
1754 | |||
1755 | clk_disable_unprepare(priv->cclk); | ||
1756 | clk_disable_unprepare(priv->hclk); | ||
1757 | |||
1758 | return 0; | ||
1759 | } | ||
1760 | |||
1761 | static int m_can_runtime_resume(struct device *dev) | ||
1762 | { | ||
1763 | struct net_device *ndev = dev_get_drvdata(dev); | ||
1764 | struct m_can_priv *priv = netdev_priv(ndev); | ||
1765 | int err; | ||
1766 | |||
1767 | err = clk_prepare_enable(priv->hclk); | ||
1768 | if (err) | ||
1769 | return err; | ||
1770 | |||
1771 | err = clk_prepare_enable(priv->cclk); | ||
1772 | if (err) | ||
1773 | clk_disable_unprepare(priv->hclk); | ||
1774 | |||
1775 | return err; | ||
1776 | } | ||
1777 | |||
1756 | static const struct dev_pm_ops m_can_pmops = { | 1778 | static const struct dev_pm_ops m_can_pmops = { |
1779 | SET_RUNTIME_PM_OPS(m_can_runtime_suspend, | ||
1780 | m_can_runtime_resume, NULL) | ||
1757 | SET_SYSTEM_SLEEP_PM_OPS(m_can_suspend, m_can_resume) | 1781 | SET_SYSTEM_SLEEP_PM_OPS(m_can_suspend, m_can_resume) |
1758 | }; | 1782 | }; |
1759 | 1783 | ||