spi: rockchip-sfc: Support pm ops

Support system_sleep and runtime_pm ops.

Signed-off-by: Jon Lin <jon.lin@rock-chips.com>
Link: https://patch.msgid.link/20241208130311.1324024-1-jon.lin@rock-chips.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Jon Lin 2024-12-08 21:03:11 +08:00 committed by Mark Brown
parent 3f3b063e0c
commit 8011709906
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
1 changed files with 128 additions and 20 deletions

View File

@ -13,12 +13,14 @@
#include <linux/completion.h>
#include <linux/dma-mapping.h>
#include <linux/iopoll.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/spi/spi-mem.h>
/* System control */
@ -150,11 +152,9 @@
/* Data */
#define SFC_DATA 0x108
/* The controller and documentation reports that it supports up to 4 CS
* devices (0-3), however I have only been able to test a single CS (CS 0)
* due to the configuration of my device.
*/
#define SFC_MAX_CHIPSELECT_NUM 4
#define SFC_CS1_REG_OFFSET 0x200
#define SFC_MAX_CHIPSELECT_NUM 2
/* The SFC can transfer max 16KB - 1 at one time
* we set it to 15.5KB here for alignment.
@ -169,12 +169,14 @@
*/
#define SFC_MAX_SPEED (150 * 1000 * 1000)
#define ROCKCHIP_AUTOSUSPEND_DELAY 2000
struct rockchip_sfc {
struct device *dev;
void __iomem *regbase;
struct clk *hclk;
struct clk *clk;
u32 frequency;
u32 speed[SFC_MAX_CHIPSELECT_NUM];
/* virtual mapped addr for dma_buffer */
void *buffer;
dma_addr_t dma_buffer;
@ -301,6 +303,7 @@ static int rockchip_sfc_xfer_setup(struct rockchip_sfc *sfc,
u32 len)
{
u32 ctrl = 0, cmd = 0;
u8 cs = spi_get_chipselect(mem->spi, 0);
/* set CMD */
cmd = op->cmd.opcode;
@ -314,7 +317,8 @@ static int rockchip_sfc_xfer_setup(struct rockchip_sfc *sfc,
cmd |= SFC_CMD_ADDR_24BITS << SFC_CMD_ADDR_SHIFT;
} else {
cmd |= SFC_CMD_ADDR_XBITS << SFC_CMD_ADDR_SHIFT;
writel(op->addr.nbytes * 8 - 1, sfc->regbase + SFC_ABIT);
writel(op->addr.nbytes * 8 - 1,
sfc->regbase + cs * SFC_CS1_REG_OFFSET + SFC_ABIT);
}
ctrl |= ((op->addr.buswidth >> 1) << SFC_CTRL_ADDR_BITS_SHIFT);
@ -346,7 +350,7 @@ static int rockchip_sfc_xfer_setup(struct rockchip_sfc *sfc,
/* set the Controller */
ctrl |= SFC_CTRL_PHASE_SEL_NEGETIVE;
cmd |= spi_get_chipselect(mem->spi, 0) << SFC_CMD_CS_SHIFT;
cmd |= cs << SFC_CMD_CS_SHIFT;
dev_dbg(sfc->dev, "sfc addr.nbytes=%x(x%d) dummy.nbytes=%x(x%d)\n",
op->addr.nbytes, op->addr.buswidth,
@ -354,7 +358,7 @@ static int rockchip_sfc_xfer_setup(struct rockchip_sfc *sfc,
dev_dbg(sfc->dev, "sfc ctrl=%x cmd=%x addr=%llx len=%x\n",
ctrl, cmd, op->addr.val, len);
writel(ctrl, sfc->regbase + SFC_CTRL);
writel(ctrl, sfc->regbase + cs * SFC_CS1_REG_OFFSET + SFC_CTRL);
writel(cmd, sfc->regbase + SFC_CMD);
if (op->addr.nbytes)
writel(op->addr.val, sfc->regbase + SFC_ADDR);
@ -500,14 +504,22 @@ static int rockchip_sfc_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op
struct rockchip_sfc *sfc = spi_controller_get_devdata(mem->spi->controller);
u32 len = op->data.nbytes;
int ret;
u8 cs = spi_get_chipselect(mem->spi, 0);
if (unlikely(mem->spi->max_speed_hz != sfc->frequency) && !has_acpi_companion(sfc->dev)) {
ret = pm_runtime_get_sync(sfc->dev);
if (ret < 0) {
pm_runtime_put_noidle(sfc->dev);
return ret;
}
if (unlikely(mem->spi->max_speed_hz != sfc->speed[cs]) &&
!has_acpi_companion(sfc->dev)) {
ret = clk_set_rate(sfc->clk, mem->spi->max_speed_hz);
if (ret)
return ret;
sfc->frequency = mem->spi->max_speed_hz;
goto out;
sfc->speed[cs] = mem->spi->max_speed_hz;
dev_dbg(sfc->dev, "set_freq=%dHz real_freq=%ldHz\n",
sfc->frequency, clk_get_rate(sfc->clk));
sfc->speed[cs], clk_get_rate(sfc->clk));
}
rockchip_sfc_adjust_op_work((struct spi_mem_op *)op);
@ -524,11 +536,17 @@ static int rockchip_sfc_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op
if (ret != len) {
dev_err(sfc->dev, "xfer data failed ret %d dir %d\n", ret, op->data.dir);
return -EIO;
ret = -EIO;
goto out;
}
}
return rockchip_sfc_xfer_done(sfc, 100000);
ret = rockchip_sfc_xfer_done(sfc, 100000);
out:
pm_runtime_mark_last_busy(sfc->dev);
pm_runtime_put_autosuspend(sfc->dev);
return ret;
}
static int rockchip_sfc_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
@ -570,6 +588,7 @@ static int rockchip_sfc_probe(struct platform_device *pdev)
struct spi_controller *host;
struct rockchip_sfc *sfc;
int ret;
u32 i, val;
host = devm_spi_alloc_host(&pdev->dev, sizeof(*sfc));
if (!host)
@ -602,9 +621,12 @@ static int rockchip_sfc_probe(struct platform_device *pdev)
"Failed to get sfc ahb clk\n");
if (has_acpi_companion(&pdev->dev)) {
ret = device_property_read_u32(&pdev->dev, "clock-frequency", &sfc->frequency);
ret = device_property_read_u32(&pdev->dev, "clock-frequency", &val);
if (ret)
return dev_err_probe(&pdev->dev, ret, "Failed to find clock-frequency\n");
return dev_err_probe(&pdev->dev, ret,
"Failed to find clock-frequency in ACPI\n");
for (i = 0; i < SFC_MAX_CHIPSELECT_NUM; i++)
sfc->speed[i] = val;
}
sfc->use_dma = !of_property_read_bool(sfc->dev->of_node, "rockchip,sfc-no-dma");
@ -646,6 +668,8 @@ static int rockchip_sfc_probe(struct platform_device *pdev)
goto err_irq;
}
platform_set_drvdata(pdev, sfc);
ret = rockchip_sfc_init(sfc);
if (ret)
goto err_irq;
@ -653,12 +677,27 @@ static int rockchip_sfc_probe(struct platform_device *pdev)
sfc->max_iosize = rockchip_sfc_get_max_iosize(sfc);
sfc->version = rockchip_sfc_get_version(sfc);
ret = spi_register_controller(host);
pm_runtime_set_autosuspend_delay(dev, ROCKCHIP_AUTOSUSPEND_DELAY);
pm_runtime_use_autosuspend(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
pm_runtime_get_noresume(dev);
ret = devm_spi_register_controller(dev, host);
if (ret)
goto err_irq;
goto err_pm_runtime_free;
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
err_pm_runtime_free:
pm_runtime_get_sync(dev);
pm_runtime_put_noidle(dev);
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
pm_runtime_dont_use_autosuspend(dev);
err_irq:
clk_disable_unprepare(sfc->clk);
err_clk:
@ -678,6 +717,74 @@ static void rockchip_sfc_remove(struct platform_device *pdev)
clk_disable_unprepare(sfc->hclk);
}
#ifdef CONFIG_PM
static int rockchip_sfc_runtime_suspend(struct device *dev)
{
struct rockchip_sfc *sfc = dev_get_drvdata(dev);
clk_disable_unprepare(sfc->clk);
clk_disable_unprepare(sfc->hclk);
return 0;
}
static int rockchip_sfc_runtime_resume(struct device *dev)
{
struct rockchip_sfc *sfc = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(sfc->hclk);
if (ret < 0)
return ret;
ret = clk_prepare_enable(sfc->clk);
if (ret < 0)
clk_disable_unprepare(sfc->hclk);
return ret;
}
#endif /* CONFIG_PM */
#ifdef CONFIG_PM_SLEEP
static int rockchip_sfc_suspend(struct device *dev)
{
pinctrl_pm_select_sleep_state(dev);
return pm_runtime_force_suspend(dev);
}
static int rockchip_sfc_resume(struct device *dev)
{
struct rockchip_sfc *sfc = dev_get_drvdata(dev);
int ret;
ret = pm_runtime_force_resume(dev);
if (ret < 0)
return ret;
pinctrl_pm_select_default_state(dev);
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
pm_runtime_put_noidle(dev);
return ret;
}
rockchip_sfc_init(sfc);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
}
#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops rockchip_sfc_pm_ops = {
SET_RUNTIME_PM_OPS(rockchip_sfc_runtime_suspend,
rockchip_sfc_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(rockchip_sfc_suspend, rockchip_sfc_resume)
};
static const struct of_device_id rockchip_sfc_dt_ids[] = {
{ .compatible = "rockchip,sfc"},
{ /* sentinel */ }
@ -688,6 +795,7 @@ static struct platform_driver rockchip_sfc_driver = {
.driver = {
.name = "rockchip-sfc",
.of_match_table = rockchip_sfc_dt_ids,
.pm = &rockchip_sfc_pm_ops,
},
.probe = rockchip_sfc_probe,
.remove = rockchip_sfc_remove,