Commit 4fe05d21 authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

Merge tag 'sunxi-drivers-for-5.12' of...

Merge tag 'sunxi-drivers-for-5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux into arm/drivers

Some new drivers changes for the Allwinner SoCs, but mostly runtime_pm and
suspend / resume  support for our RSB bus, and support for the H616

* tag 'sunxi-drivers-for-5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux:
  dt-bindings: bus: rsb: Add H616 compatible string
  soc: sunxi: sram: Add support for more than one EMAC clock
  dt-bindings: sram: sunxi-sram: Add H616 compatible string
  mfd/bus: sunxi-rsb: Make .remove() callback return void
  bus: sunxi-rsb: Implement runtime power management
  bus: sunxi-rsb: Implement suspend/resume/shutdown callbacks
  bus: sunxi-rsb: Split out controller init/exit functions
  bus: sunxi-rsb: Move OF match table

Link: https://lore.kernel.org/r/91f2980f-266f-41f2-ba10-5a395625498c.lettre@localhost


Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 4940b991 308e7894
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -21,7 +21,9 @@ properties:
    oneOf:
      - const: allwinner,sun8i-a23-rsb
      - items:
          - const: allwinner,sun8i-a83t-rsb
          - enum:
              - allwinner,sun8i-a83t-rsb
              - allwinner,sun50i-h616-rsb
          - const: allwinner,sun8i-a23-rsb

  reg:
+1 −0
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ properties:
      - items:
          - const: allwinner,suniv-f1c100s-system-control
          - const: allwinner,sun4i-a10-system-control
      - const: allwinner,sun50i-h616-system-control

  reg:
    maxItems: 1
+153 −62
Original line number Diff line number Diff line
@@ -45,6 +45,8 @@
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/slab.h>
@@ -126,6 +128,7 @@ struct sunxi_rsb {
	struct completion complete;
	struct mutex lock;
	unsigned int status;
	u32 clk_freq;
};

/* bus / slave device related functions */
@@ -170,7 +173,9 @@ static int sunxi_rsb_device_remove(struct device *dev)
{
	const struct sunxi_rsb_driver *drv = to_sunxi_rsb_driver(dev->driver);

	return drv->remove(to_sunxi_rsb_device(dev));
	drv->remove(to_sunxi_rsb_device(dev));

	return 0;
}

static struct bus_type sunxi_rsb_bus = {
@@ -335,6 +340,10 @@ static int sunxi_rsb_read(struct sunxi_rsb *rsb, u8 rtaddr, u8 addr,
		return -EINVAL;
	}

	ret = pm_runtime_resume_and_get(rsb->dev);
	if (ret)
		return ret;

	mutex_lock(&rsb->lock);

	writel(addr, rsb->regs + RSB_ADDR);
@@ -350,6 +359,9 @@ static int sunxi_rsb_read(struct sunxi_rsb *rsb, u8 rtaddr, u8 addr,
unlock:
	mutex_unlock(&rsb->lock);

	pm_runtime_mark_last_busy(rsb->dev);
	pm_runtime_put_autosuspend(rsb->dev);

	return ret;
}

@@ -377,6 +389,10 @@ static int sunxi_rsb_write(struct sunxi_rsb *rsb, u8 rtaddr, u8 addr,
		return -EINVAL;
	}

	ret = pm_runtime_resume_and_get(rsb->dev);
	if (ret)
		return ret;

	mutex_lock(&rsb->lock);

	writel(addr, rsb->regs + RSB_ADDR);
@@ -387,6 +403,9 @@ static int sunxi_rsb_write(struct sunxi_rsb *rsb, u8 rtaddr, u8 addr,

	mutex_unlock(&rsb->lock);

	pm_runtime_mark_last_busy(rsb->dev);
	pm_runtime_put_autosuspend(rsb->dev);

	return ret;
}

@@ -614,11 +633,100 @@ static int of_rsb_register_devices(struct sunxi_rsb *rsb)
	return 0;
}

static const struct of_device_id sunxi_rsb_of_match_table[] = {
	{ .compatible = "allwinner,sun8i-a23-rsb" },
	{}
};
MODULE_DEVICE_TABLE(of, sunxi_rsb_of_match_table);
static int sunxi_rsb_hw_init(struct sunxi_rsb *rsb)
{
	struct device *dev = rsb->dev;
	unsigned long p_clk_freq;
	u32 clk_delay, reg;
	int clk_div, ret;

	ret = clk_prepare_enable(rsb->clk);
	if (ret) {
		dev_err(dev, "failed to enable clk: %d\n", ret);
		return ret;
	}

	ret = reset_control_deassert(rsb->rstc);
	if (ret) {
		dev_err(dev, "failed to deassert reset line: %d\n", ret);
		goto err_clk_disable;
	}

	/* reset the controller */
	writel(RSB_CTRL_SOFT_RST, rsb->regs + RSB_CTRL);
	readl_poll_timeout(rsb->regs + RSB_CTRL, reg,
			   !(reg & RSB_CTRL_SOFT_RST), 1000, 100000);

	/*
	 * Clock frequency and delay calculation code is from
	 * Allwinner U-boot sources.
	 *
	 * From A83 user manual:
	 * bus clock frequency = parent clock frequency / (2 * (divider + 1))
	 */
	p_clk_freq = clk_get_rate(rsb->clk);
	clk_div = p_clk_freq / rsb->clk_freq / 2;
	if (!clk_div)
		clk_div = 1;
	else if (clk_div > RSB_CCR_MAX_CLK_DIV + 1)
		clk_div = RSB_CCR_MAX_CLK_DIV + 1;

	clk_delay = clk_div >> 1;
	if (!clk_delay)
		clk_delay = 1;

	dev_info(dev, "RSB running at %lu Hz\n", p_clk_freq / clk_div / 2);
	writel(RSB_CCR_SDA_OUT_DELAY(clk_delay) | RSB_CCR_CLK_DIV(clk_div - 1),
	       rsb->regs + RSB_CCR);

	return 0;

err_clk_disable:
	clk_disable_unprepare(rsb->clk);

	return ret;
}

static void sunxi_rsb_hw_exit(struct sunxi_rsb *rsb)
{
	/* Keep the clock and PM reference counts consistent. */
	if (pm_runtime_status_suspended(rsb->dev))
		pm_runtime_resume(rsb->dev);
	reset_control_assert(rsb->rstc);
	clk_disable_unprepare(rsb->clk);
}

static int __maybe_unused sunxi_rsb_runtime_suspend(struct device *dev)
{
	struct sunxi_rsb *rsb = dev_get_drvdata(dev);

	clk_disable_unprepare(rsb->clk);

	return 0;
}

static int __maybe_unused sunxi_rsb_runtime_resume(struct device *dev)
{
	struct sunxi_rsb *rsb = dev_get_drvdata(dev);

	return clk_prepare_enable(rsb->clk);
}

static int __maybe_unused sunxi_rsb_suspend(struct device *dev)
{
	struct sunxi_rsb *rsb = dev_get_drvdata(dev);

	sunxi_rsb_hw_exit(rsb);

	return 0;
}

static int __maybe_unused sunxi_rsb_resume(struct device *dev)
{
	struct sunxi_rsb *rsb = dev_get_drvdata(dev);

	return sunxi_rsb_hw_init(rsb);
}

static int sunxi_rsb_probe(struct platform_device *pdev)
{
@@ -626,10 +734,8 @@ static int sunxi_rsb_probe(struct platform_device *pdev)
	struct device_node *np = dev->of_node;
	struct resource *r;
	struct sunxi_rsb *rsb;
	unsigned long p_clk_freq;
	u32 clk_delay, clk_freq = 3000000;
	int clk_div, irq, ret;
	u32 reg;
	u32 clk_freq = 3000000;
	int irq, ret;

	of_property_read_u32(np, "clock-frequency", &clk_freq);
	if (clk_freq > RSB_MAX_FREQ) {
@@ -644,6 +750,7 @@ static int sunxi_rsb_probe(struct platform_device *pdev)
		return -ENOMEM;

	rsb->dev = dev;
	rsb->clk_freq = clk_freq;
	platform_set_drvdata(pdev, rsb);
	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	rsb->regs = devm_ioremap_resource(dev, r);
@@ -661,79 +768,41 @@ static int sunxi_rsb_probe(struct platform_device *pdev)
		return ret;
	}

	ret = clk_prepare_enable(rsb->clk);
	if (ret) {
		dev_err(dev, "failed to enable clk: %d\n", ret);
		return ret;
	}

	p_clk_freq = clk_get_rate(rsb->clk);

	rsb->rstc = devm_reset_control_get(dev, NULL);
	if (IS_ERR(rsb->rstc)) {
		ret = PTR_ERR(rsb->rstc);
		dev_err(dev, "failed to retrieve reset controller: %d\n", ret);
		goto err_clk_disable;
	}

	ret = reset_control_deassert(rsb->rstc);
	if (ret) {
		dev_err(dev, "failed to deassert reset line: %d\n", ret);
		goto err_clk_disable;
		return ret;
	}

	init_completion(&rsb->complete);
	mutex_init(&rsb->lock);

	/* reset the controller */
	writel(RSB_CTRL_SOFT_RST, rsb->regs + RSB_CTRL);
	readl_poll_timeout(rsb->regs + RSB_CTRL, reg,
			   !(reg & RSB_CTRL_SOFT_RST), 1000, 100000);

	/*
	 * Clock frequency and delay calculation code is from
	 * Allwinner U-boot sources.
	 *
	 * From A83 user manual:
	 * bus clock frequency = parent clock frequency / (2 * (divider + 1))
	 */
	clk_div = p_clk_freq / clk_freq / 2;
	if (!clk_div)
		clk_div = 1;
	else if (clk_div > RSB_CCR_MAX_CLK_DIV + 1)
		clk_div = RSB_CCR_MAX_CLK_DIV + 1;

	clk_delay = clk_div >> 1;
	if (!clk_delay)
		clk_delay = 1;

	dev_info(dev, "RSB running at %lu Hz\n", p_clk_freq / clk_div / 2);
	writel(RSB_CCR_SDA_OUT_DELAY(clk_delay) | RSB_CCR_CLK_DIV(clk_div - 1),
	       rsb->regs + RSB_CCR);

	ret = devm_request_irq(dev, irq, sunxi_rsb_irq, 0, RSB_CTRL_NAME, rsb);
	if (ret) {
		dev_err(dev, "can't register interrupt handler irq %d: %d\n",
			irq, ret);
		goto err_reset_assert;
		return ret;
	}

	ret = sunxi_rsb_hw_init(rsb);
	if (ret)
		return ret;

	/* initialize all devices on the bus into RSB mode */
	ret = sunxi_rsb_init_device_mode(rsb);
	if (ret)
		dev_warn(dev, "Initialize device mode failed: %d\n", ret);

	pm_suspend_ignore_children(dev, true);
	pm_runtime_set_active(dev);
	pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC);
	pm_runtime_use_autosuspend(dev);
	pm_runtime_enable(dev);

	of_rsb_register_devices(rsb);

	return 0;

err_reset_assert:
	reset_control_assert(rsb->rstc);

err_clk_disable:
	clk_disable_unprepare(rsb->clk);

	return ret;
}

static int sunxi_rsb_remove(struct platform_device *pdev)
@@ -741,18 +810,40 @@ static int sunxi_rsb_remove(struct platform_device *pdev)
	struct sunxi_rsb *rsb = platform_get_drvdata(pdev);

	device_for_each_child(rsb->dev, NULL, sunxi_rsb_remove_devices);
	reset_control_assert(rsb->rstc);
	clk_disable_unprepare(rsb->clk);
	pm_runtime_disable(&pdev->dev);
	sunxi_rsb_hw_exit(rsb);

	return 0;
}

static void sunxi_rsb_shutdown(struct platform_device *pdev)
{
	struct sunxi_rsb *rsb = platform_get_drvdata(pdev);

	pm_runtime_disable(&pdev->dev);
	sunxi_rsb_hw_exit(rsb);
}

static const struct dev_pm_ops sunxi_rsb_dev_pm_ops = {
	SET_RUNTIME_PM_OPS(sunxi_rsb_runtime_suspend,
			   sunxi_rsb_runtime_resume, NULL)
	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sunxi_rsb_suspend, sunxi_rsb_resume)
};

static const struct of_device_id sunxi_rsb_of_match_table[] = {
	{ .compatible = "allwinner,sun8i-a23-rsb" },
	{}
};
MODULE_DEVICE_TABLE(of, sunxi_rsb_of_match_table);

static struct platform_driver sunxi_rsb_driver = {
	.probe = sunxi_rsb_probe,
	.remove	= sunxi_rsb_remove,
	.shutdown = sunxi_rsb_shutdown,
	.driver	= {
		.name = RSB_CTRL_NAME,
		.of_match_table = sunxi_rsb_of_match_table,
		.pm = &sunxi_rsb_dev_pm_ops,
	},
};

+3 −1
Original line number Diff line number Diff line
@@ -54,7 +54,9 @@ static int axp20x_i2c_remove(struct i2c_client *i2c)
{
	struct axp20x_dev *axp20x = i2c_get_clientdata(i2c);

	return axp20x_device_remove(axp20x);
	axp20x_device_remove(axp20x);

	return 0;
}

#ifdef CONFIG_OF
+2 −2
Original line number Diff line number Diff line
@@ -49,11 +49,11 @@ static int axp20x_rsb_probe(struct sunxi_rsb_device *rdev)
	return axp20x_device_probe(axp20x);
}

static int axp20x_rsb_remove(struct sunxi_rsb_device *rdev)
static void axp20x_rsb_remove(struct sunxi_rsb_device *rdev)
{
	struct axp20x_dev *axp20x = sunxi_rsb_device_get_drvdata(rdev);

	return axp20x_device_remove(axp20x);
	axp20x_device_remove(axp20x);
}

static const struct of_device_id axp20x_rsb_of_match[] = {
Loading