Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b44003bf authored by Sagar Dharia's avatar Sagar Dharia
Browse files

i2c: qcom: geni: Clock and GPIO management for GENI I2C controller



Runtime power management framework is used to turn controller
resources on and off.
Since I2C has clients who need the bus while suspending/resuming the
kernel, _noirq callbacks are used to suspend the controller as late
as practically feasible.

Change-Id: I8b86cffbc4efbeb05cf2d8b4936b913ffc7ce1a8
Signed-off-by: default avatarSagar Dharia <sdharia@codeaurora.org>
parent defea4e8
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -6,6 +6,10 @@ Required properties:
   * "qcom,i2c-geni.
 - reg: Should contain QUP register address and length.
 - interrupts: Should contain I2C interrupt.
 - clocks: Serial engine core clock, and AHB clocks needed by the device.
 - pinctrl-names/pinctrl-0/1: The GPIOs assigned to this core. The names
   should be "active" and "sleep" for the pin confuguration when core is active
   or when entering sleep state.
 - #address-cells: Should be <1> Address cells for i2c device address
 - #size-cells: Should be <0> as i2c addresses have no size component

@@ -17,6 +21,13 @@ i2c@a94000 {
	compatible = "qcom,i2c-geni";
	reg = <0xa94000 0x4000>;
	interrupts = <GIC_SPI 358 0>;
	clock-names = "se-clk", "m-ahb", "s-ahb";
	clocks = <&clock_gcc GCC_QUPV3_WRAP0_S5_CLK>,
		<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
		<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
	pinctrl-names = "default", "sleep";
	pinctrl-0 = <&qup_1_i2c_5_active>;
	pinctrl-1 = <&qup_1_i2c_5_sleep>;
	#address-cells = <1>;
	#size-cells = <0>;
};
+105 −10
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/qcom-geni-se.h>

#define SE_I2C_TX_TRANS_LEN		(0x26C)
@@ -57,6 +58,7 @@ struct geni_i2c_dev {
	struct i2c_adapter adap;
	struct completion xfer;
	struct i2c_msg *cur;
	struct se_geni_rsc i2c_rsc;
	int cur_wr;
	int cur_rd;
};
@@ -153,7 +155,15 @@ static int geni_i2c_xfer(struct i2c_adapter *adap,
	gi2c->err = 0;
	gi2c->cur = &msgs[0];
	reinit_completion(&gi2c->xfer);
	enable_irq(gi2c->irq);
	ret = pm_runtime_get_sync(gi2c->dev);
	if (ret < 0) {
		dev_err(gi2c->dev, "error turning SE resources:%d\n", ret);
		pm_runtime_put_noidle(gi2c->dev);
		/* Set device in suspended since resume failed */
		pm_runtime_set_suspended(gi2c->dev);
		return ret;
	}
	geni_se_init(gi2c->base, FIFO_MODE, 0xF, 0x10);
	qcom_geni_i2c_conf(gi2c->base, 0, 2);
	se_config_packing(gi2c->base, 8, 4, true);
	dev_dbg(gi2c->dev, "i2c xfer:num:%d, msgs:len:%d,flg:%d\n",
@@ -206,7 +216,7 @@ static int geni_i2c_xfer(struct i2c_adapter *adap,
	}
	if (ret == 0)
		ret = i;
	disable_irq(gi2c->irq);
	pm_runtime_put_sync(gi2c->dev);
	gi2c->cur = NULL;
	gi2c->err = 0;
	dev_dbg(gi2c->dev, "i2c txn ret:%d\n", ret);
@@ -239,10 +249,54 @@ static int geni_i2c_probe(struct platform_device *pdev)
	if (!res)
		return -EINVAL;

	gi2c->i2c_rsc.se_clk = devm_clk_get(&pdev->dev, "se-clk");
	if (IS_ERR(gi2c->i2c_rsc.se_clk)) {
		ret = PTR_ERR(gi2c->i2c_rsc.se_clk);
		dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
		return ret;
	}

	gi2c->i2c_rsc.m_ahb_clk = devm_clk_get(&pdev->dev, "m-ahb");
	if (IS_ERR(gi2c->i2c_rsc.m_ahb_clk)) {
		ret = PTR_ERR(gi2c->i2c_rsc.m_ahb_clk);
		dev_err(&pdev->dev, "Err getting M AHB clk %d\n", ret);
		return ret;
	}

	gi2c->i2c_rsc.s_ahb_clk = devm_clk_get(&pdev->dev, "s-ahb");
	if (IS_ERR(gi2c->i2c_rsc.s_ahb_clk)) {
		ret = PTR_ERR(gi2c->i2c_rsc.s_ahb_clk);
		dev_err(&pdev->dev, "Err getting S AHB clk %d\n", ret);
		return ret;
	}

	gi2c->base = devm_ioremap_resource(gi2c->dev, res);
	if (IS_ERR(gi2c->base))
		return PTR_ERR(gi2c->base);

	gi2c->i2c_rsc.geni_pinctrl = devm_pinctrl_get(&pdev->dev);
	if (IS_ERR_OR_NULL(gi2c->i2c_rsc.geni_pinctrl)) {
		dev_err(&pdev->dev, "No pinctrl config specified\n");
		ret = PTR_ERR(gi2c->i2c_rsc.geni_pinctrl);
		return ret;
	}
	gi2c->i2c_rsc.geni_gpio_active =
		pinctrl_lookup_state(gi2c->i2c_rsc.geni_pinctrl,
							PINCTRL_DEFAULT);
	if (IS_ERR_OR_NULL(gi2c->i2c_rsc.geni_gpio_active)) {
		dev_err(&pdev->dev, "No default config specified\n");
		ret = PTR_ERR(gi2c->i2c_rsc.geni_gpio_active);
		return ret;
	}
	gi2c->i2c_rsc.geni_gpio_sleep =
		pinctrl_lookup_state(gi2c->i2c_rsc.geni_pinctrl,
							PINCTRL_SLEEP);
	if (IS_ERR_OR_NULL(gi2c->i2c_rsc.geni_gpio_sleep)) {
		dev_err(&pdev->dev, "No sleep config specified\n");
		ret = PTR_ERR(gi2c->i2c_rsc.geni_gpio_sleep);
		return ret;
	}

	gi2c->irq = platform_get_irq(pdev, 0);
	if (gi2c->irq < 0) {
		dev_err(gi2c->dev, "IRQ error for i2c-geni\n");
@@ -266,8 +320,9 @@ static int geni_i2c_probe(struct platform_device *pdev)

	strlcpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name));

	pm_runtime_set_suspended(gi2c->dev);
	pm_runtime_enable(gi2c->dev);
	i2c_add_adapter(&gi2c->adap);
	geni_se_init(gi2c->base, FIFO_MODE, 0xF, 0x10);

	return 0;
}
@@ -276,27 +331,67 @@ static int geni_i2c_remove(struct platform_device *pdev)
{
	struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev);

	disable_irq(gi2c->irq);
	pm_runtime_disable(gi2c->dev);
	i2c_del_adapter(&gi2c->adap);
	return 0;
}

#ifdef CONFIG_PM_SLEEP
static int geni_i2c_suspend(struct device *device)
static int geni_i2c_resume_noirq(struct device *device)
{
	return 0;
}

#ifdef CONFIG_PM
static int geni_i2c_runtime_suspend(struct device *dev)
{
	struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);

	disable_irq(gi2c->irq);
	se_geni_resources_off(&gi2c->i2c_rsc);
	return 0;
}

static int geni_i2c_runtime_resume(struct device *dev)
{
	int ret;
	struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);

	ret = se_geni_resources_on(&gi2c->i2c_rsc);
	if (ret)
		return ret;

	enable_irq(gi2c->irq);
	return 0;
}

static int geni_i2c_suspend_noirq(struct device *device)
{
	if (!pm_runtime_status_suspended(device))
		return -EBUSY;
	return 0;
}
#else
static int geni_i2c_runtime_suspend(struct device *dev)
{
	return 0;
}

static int geni_i2c_runtime_resume(struct device *dev)
{
	return 0;
}

static int geni_i2c_resume(struct device *device)
static int geni_i2c_suspend_noirq(struct device *device)
{
	return 0;
}
#endif

static const struct dev_pm_ops geni_i2c_pm_ops = {
	SET_SYSTEM_SLEEP_PM_OPS(
		geni_i2c_suspend,
		geni_i2c_resume)
	.suspend_noirq		= geni_i2c_suspend_noirq,
	.resume_noirq		= geni_i2c_resume_noirq,
	.runtime_suspend	= geni_i2c_runtime_suspend,
	.runtime_resume		= geni_i2c_runtime_resume,
};

static const struct of_device_id geni_i2c_dt_match[] = {