Loading Documentation/devicetree/bindings/i2c/i2c-qup.txt +5 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,10 @@ Optional property: callbacks. - qcom,master-id : Master endpoint number used for voting on clocks using bus-scaling driver. - qcom,clk-ctl-xfer : When present, the clocks's state (prepare_enable/ unprepare_disable) is controlled by i2c-transaction's begining and ending. When missing, the clock's state is controlled by runtime-pm events. Example: i2c_3: i2c@f9966000 { Loading @@ -47,6 +51,7 @@ Example: interrupt-names = "qup_err_intr"; qcom,i2c-bus-freq = <100000>; qcom,i2c-src-freq = <24000000>; qcom,clk-ctl-xfer; qcom,scl-gpio = <&msmgpio 7 0>; qcom,sda-gpio = <&msmgpio 6 0>; }; arch/arm/mach-msm/include/mach/board.h +5 −0 Original line number Diff line number Diff line Loading @@ -522,6 +522,10 @@ struct msm_mhl_platform_data { /** * msm_i2c_platform_data: i2c-qup driver configuration data * * @clk_ctl_xfer : When true, the clocks's state (prepare_enable/ * unprepare_disable) is controlled by i2c-transaction's begining and * ending. When false, the clock's state is controlled by runtime-pm * events. * @active_only when set, votes when system active and removes the vote when * system goes idle (optimises for performance). When unset, voting using * runtime pm (optimizes for power). Loading @@ -530,6 +534,7 @@ struct msm_mhl_platform_data { */ struct msm_i2c_platform_data { int clk_freq; bool clk_ctl_xfer; uint32_t rmutex; const char *rsl_id; uint32_t pm_lat; Loading drivers/i2c/busses/i2c-qup.c +155 −119 Original line number Diff line number Diff line /* Copyright (c) 2009-2013, The Linux Foundation. All rights reserved. /* Copyright (c) 2009-2014, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -85,10 +85,11 @@ enum { QUP_MX_INPUT_DONE = 1U << 11, }; /* I2C mini core related values */ /* QUP_CONFIG values and flags */ enum { I2C_MINI_CORE = 2U << 8, I2C_N_VAL = 0xF, I2C_CORE_CLK_ON_EN = BIT(13), }; Loading Loading @@ -129,6 +130,12 @@ enum { I2C_CLK_FORCED_LOW_STATE = 5, }; enum msm_i2c_state { MSM_I2C_PM_ACTIVE, MSM_I2C_PM_SUSPENDED, MSM_I2C_SYS_SUSPENDING, MSM_I2C_SYS_SUSPENDED, }; #define QUP_MAX_CLK_STATE_RETRIES 300 #define DEFAULT_CLK_RATE (19200000) #define I2C_STATUS_CLK_STATE 13 Loading Loading @@ -184,8 +191,7 @@ struct qup_i2c_dev { int in_blk_sz; int wr_sz; struct msm_i2c_platform_data *pdata; int suspended; int pwr_state; enum msm_i2c_state pwr_state; struct mutex mlock; void *complete; int i2c_gpios[ARRAY_SIZE(i2c_rsrcs)]; Loading Loading @@ -339,22 +345,6 @@ qup_update_state(struct qup_i2c_dev *dev, uint32_t state) return 0; } /* * Before calling qup_config_core_on_en(), please make * sure that QuPE core is in RESET state. */ static void qup_config_core_on_en(struct qup_i2c_dev *dev) { uint32_t status; status = readl_relaxed(dev->base + QUP_CONFIG); status |= BIT(13); writel_relaxed(status, dev->base + QUP_CONFIG); /* making sure that write has really gone through */ mb(); } #define MSM_I2C_CLK_PATH_SUSPEND (0) #define MSM_I2C_CLK_PATH_RESUME (1) #define MSM_I2C_CLK_PATH_MAX_BW(dev) ((dev->pdata->src_clk_rate * 8) / 1000) Loading Loading @@ -500,27 +490,97 @@ static void i2c_qup_clk_path_postponed_register(struct qup_i2c_dev *dev) } } static void qup_i2c_pwr_mgmt(struct qup_i2c_dev *dev, unsigned int state) static int i2c_qup_gpio_request(struct qup_i2c_dev *dev) { dev->pwr_state = state; if (state != 0) { i2c_qup_clk_path_postponed_register(dev); if (!dev->pdata->active_only) i2c_qup_clk_path_vote(dev); int i; int result = 0; clk_prepare_enable(dev->clk); if (!dev->pdata->keep_ahb_clk_on) clk_prepare_enable(dev->pclk); } else { for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) { if (dev->i2c_gpios[i] >= 0) { result = gpio_request(dev->i2c_gpios[i], i2c_rsrcs[i]); if (result) { dev_err(dev->dev, "gpio_request for pin %d failed with error %d\n", dev->i2c_gpios[i], result); goto error; } } } return 0; error: for (; --i >= 0;) { if (dev->i2c_gpios[i] >= 0) gpio_free(dev->i2c_gpios[i]); } return result; } static void i2c_qup_gpio_free(struct qup_i2c_dev *dev) { int i; for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) { if (dev->i2c_gpios[i] >= 0) gpio_free(dev->i2c_gpios[i]); } } static void i2c_qup_pm_suspend_clk(struct qup_i2c_dev *dev) { uint32_t status; /* reset core and enable conditional dynamic clock gating */ qup_update_state(dev, QUP_RESET_STATE); status = readl_relaxed(dev->base + QUP_CONFIG); status |= I2C_CORE_CLK_ON_EN; writel_relaxed(status, dev->base + QUP_CONFIG); /* ensure that write has really gone through */ mb(); clk_disable_unprepare(dev->clk); qup_config_core_on_en(dev); if (!dev->pdata->keep_ahb_clk_on) clk_disable_unprepare(dev->pclk); } static void i2c_qup_pm_resume_clk(struct qup_i2c_dev *dev) { clk_prepare_enable(dev->clk); if (!dev->pdata->keep_ahb_clk_on) clk_prepare_enable(dev->pclk); } static void i2c_qup_pm_suspend(struct qup_i2c_dev *dev) { if (dev->pwr_state == MSM_I2C_PM_SUSPENDED) { dev_err(dev->dev, "attempt to suspend when suspended\n"); return; } if (!dev->pdata->clk_ctl_xfer) i2c_qup_pm_suspend_clk(dev); if (!dev->pdata->active_only) i2c_qup_clk_path_unvote(dev); i2c_qup_gpio_free(dev); dev->pwr_state = MSM_I2C_PM_SUSPENDED; } static void i2c_qup_pm_resume(struct qup_i2c_dev *dev) { if (dev->pwr_state == MSM_I2C_PM_ACTIVE) return; i2c_qup_gpio_request(dev); i2c_qup_clk_path_postponed_register(dev); if (!dev->pdata->active_only) i2c_qup_clk_path_vote(dev); if (!dev->pdata->clk_ctl_xfer) i2c_qup_pm_resume_clk(dev); dev->pwr_state = MSM_I2C_PM_ACTIVE; } static int Loading Loading @@ -595,43 +655,6 @@ static int qup_i2c_poll_clock_ready(struct qup_i2c_dev *dev) return -ETIMEDOUT; } static inline int qup_i2c_request_gpios(struct qup_i2c_dev *dev) { int i; int result = 0; for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) { if (dev->i2c_gpios[i] >= 0) { result = gpio_request(dev->i2c_gpios[i], i2c_rsrcs[i]); if (result) { dev_err(dev->dev, "gpio_request for pin %d failed\ with error %d\n", dev->i2c_gpios[i], result); goto error; } } } return 0; error: for (; --i >= 0;) { if (dev->i2c_gpios[i] >= 0) gpio_free(dev->i2c_gpios[i]); } return result; } static inline void qup_i2c_free_gpios(struct qup_i2c_dev *dev) { int i; for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) { if (dev->i2c_gpios[i] >= 0) gpio_free(dev->i2c_gpios[i]); } } #ifdef DEBUG static void qup_verify_fifo(struct qup_i2c_dev *dev, uint32_t val, uint32_t addr, int rdwr) Loading Loading @@ -948,19 +971,35 @@ qup_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) long timeout; int err; /* Alternate if runtime power management is disabled */ /* * If all slaves of this controller behave as expected, they will * implement suspend and won't call any transaction if they are * suspended. Since controller is its parent, controller's suspend * will be called only AFTER alls slaves are suspended. * However reality is differe and some slave don't implement suspend * If a slave tries to initiate transfer when we are suspended, * pm_runtime_enabled is set to false by system-pm. * Make sure we return error when transaction is initiated while * we are in suspended state */ mutex_lock(&dev->mlock); if (dev->pwr_state >= MSM_I2C_SYS_SUSPENDING) { dev_err(dev->dev, "xfer not allowed when ctrl is suspended addr:0x%x\n", msgs->addr); mutex_unlock(&dev->mlock); return -EIO; } if (!pm_runtime_enabled(dev->dev)) { dev_dbg(dev->dev, "Runtime PM is disabled\n"); i2c_qup_pm_resume_runtime(dev->dev); dev_dbg(dev->dev, "Runtime PM FEATURE is disabled\n"); i2c_qup_pm_resume(dev); } else { pm_runtime_get_sync(dev->dev); } mutex_lock(&dev->mlock); if (dev->suspended) { mutex_unlock(&dev->mlock); return -EIO; } if (dev->pdata->clk_ctl_xfer) i2c_qup_pm_resume_clk(dev); /* Initialize QUP registers during first transfer */ if (dev->clk_ctl == 0) { Loading Loading @@ -1263,6 +1302,8 @@ timeout_err: dev->pos = 0; dev->err = 0; dev->cnt = 0; if (dev->pdata->clk_ctl_xfer) i2c_qup_pm_suspend_clk(dev); mutex_unlock(&dev->mlock); pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); Loading Loading @@ -1302,6 +1343,7 @@ int msm_i2c_rsrcs_dt_to_pdata_map(struct platform_device *pdev, {"qcom,master-id", &pdata->master_id, DT_SUGGESTED, DT_U32, 0}, {"qcom,scl-gpio", gpios, DT_OPTIONAL, DT_GPIO, -1}, {"qcom,sda-gpio", gpios + 1, DT_OPTIONAL, DT_GPIO, -1}, {"qcom,clk-ctl-xfer", &pdata->clk_ctl_xfer, DT_OPTIONAL, DT_BOOL, -1}, {"qcom,active-only", &pdata->active_only, DT_OPTIONAL, DT_BOOL, 0}, {NULL, NULL, 0, 0, 0}, }; Loading Loading @@ -1611,7 +1653,7 @@ blsp_core_init: pdata->msm_i2c_config_gpio(dev->adapter.nr, 1); mutex_init(&dev->mlock); dev->pwr_state = 0; dev->pwr_state = MSM_I2C_PM_SUSPENDED; /* If the same AHB clock is used on Modem side * switch it on here itself and don't switch it * on and off during suspend and resume. Loading Loading @@ -1685,13 +1727,11 @@ qup_i2c_remove(struct platform_device *pdev) /* Grab mutex to ensure ongoing transaction is over */ mutex_lock(&dev->mlock); dev->suspended = 1; dev->pwr_state = MSM_I2C_SYS_SUSPENDING; mutex_unlock(&dev->mlock); i2c_qup_pm_suspend(dev); dev->pwr_state = MSM_I2C_SYS_SUSPENDED; mutex_destroy(&dev->mlock); if (dev->pwr_state != 0) { qup_i2c_pwr_mgmt(dev, 0); qup_i2c_free_gpios(dev); } platform_set_drvdata(pdev, NULL); if (dev->num_irqs == 3) { free_irq(dev->out_irq, dev); Loading Loading @@ -1732,14 +1772,7 @@ static int i2c_qup_pm_suspend_runtime(struct device *device) struct platform_device *pdev = to_platform_device(device); struct qup_i2c_dev *dev = platform_get_drvdata(pdev); dev_dbg(device, "pm_runtime: suspending...\n"); /* Grab mutex to ensure ongoing transaction is over */ mutex_lock(&dev->mlock); dev->suspended = 1; mutex_unlock(&dev->mlock); if (dev->pwr_state != 0) { qup_i2c_pwr_mgmt(dev, 0); qup_i2c_free_gpios(dev); } i2c_qup_pm_suspend(dev); return 0; } Loading @@ -1747,23 +1780,22 @@ static int i2c_qup_pm_resume_runtime(struct device *device) { struct platform_device *pdev = to_platform_device(device); struct qup_i2c_dev *dev = platform_get_drvdata(pdev); int ret = 0; dev_dbg(device, "pm_runtime: resuming...\n"); if (dev->pwr_state == 0) { ret = qup_i2c_request_gpios(dev); if (ret != 0) return ret; qup_i2c_pwr_mgmt(dev, 1); } dev->suspended = 0; i2c_qup_pm_resume(dev); return 0; } static int qup_i2c_suspend(struct device *device) static int i2c_qup_pm_suspend_sys(struct device *device) { struct platform_device *pdev = to_platform_device(device); struct qup_i2c_dev *dev = platform_get_drvdata(pdev); /* Acquire mutex to ensure current transaction is over */ mutex_lock(&dev->mlock); dev->pwr_state = MSM_I2C_SYS_SUSPENDING; mutex_unlock(&dev->mlock); if (!pm_runtime_enabled(device) || !pm_runtime_suspended(device)) { dev_dbg(device, "system suspend"); i2c_qup_pm_suspend_runtime(device); dev_dbg(device, "system suspend\n"); i2c_qup_pm_suspend(dev); /* * set the device's runtime PM status to 'suspended' */ Loading @@ -1771,25 +1803,29 @@ static int qup_i2c_suspend(struct device *device) pm_runtime_set_suspended(device); pm_runtime_enable(device); } dev->pwr_state = MSM_I2C_SYS_SUSPENDED; return 0; } static int qup_i2c_resume(struct device *device) static int i2c_qup_pm_resume_sys(struct device *device) { struct platform_device *pdev = to_platform_device(device); struct qup_i2c_dev *dev = platform_get_drvdata(pdev); /* * Rely on runtime-PM to call resume in case it is enabled * Even if it's not enabled, rely on 1st client transaction to do * clock ON and gpio configuration */ dev_dbg(device, "system resume"); dev_dbg(device, "system resume\n"); dev->pwr_state = MSM_I2C_PM_SUSPENDED; return 0; } #endif /* CONFIG_PM */ static const struct dev_pm_ops i2c_qup_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS( qup_i2c_suspend, qup_i2c_resume i2c_qup_pm_suspend_sys, i2c_qup_pm_resume_sys ) SET_RUNTIME_PM_OPS( i2c_qup_pm_suspend_runtime, Loading Loading
Documentation/devicetree/bindings/i2c/i2c-qup.txt +5 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,10 @@ Optional property: callbacks. - qcom,master-id : Master endpoint number used for voting on clocks using bus-scaling driver. - qcom,clk-ctl-xfer : When present, the clocks's state (prepare_enable/ unprepare_disable) is controlled by i2c-transaction's begining and ending. When missing, the clock's state is controlled by runtime-pm events. Example: i2c_3: i2c@f9966000 { Loading @@ -47,6 +51,7 @@ Example: interrupt-names = "qup_err_intr"; qcom,i2c-bus-freq = <100000>; qcom,i2c-src-freq = <24000000>; qcom,clk-ctl-xfer; qcom,scl-gpio = <&msmgpio 7 0>; qcom,sda-gpio = <&msmgpio 6 0>; };
arch/arm/mach-msm/include/mach/board.h +5 −0 Original line number Diff line number Diff line Loading @@ -522,6 +522,10 @@ struct msm_mhl_platform_data { /** * msm_i2c_platform_data: i2c-qup driver configuration data * * @clk_ctl_xfer : When true, the clocks's state (prepare_enable/ * unprepare_disable) is controlled by i2c-transaction's begining and * ending. When false, the clock's state is controlled by runtime-pm * events. * @active_only when set, votes when system active and removes the vote when * system goes idle (optimises for performance). When unset, voting using * runtime pm (optimizes for power). Loading @@ -530,6 +534,7 @@ struct msm_mhl_platform_data { */ struct msm_i2c_platform_data { int clk_freq; bool clk_ctl_xfer; uint32_t rmutex; const char *rsl_id; uint32_t pm_lat; Loading
drivers/i2c/busses/i2c-qup.c +155 −119 Original line number Diff line number Diff line /* Copyright (c) 2009-2013, The Linux Foundation. All rights reserved. /* Copyright (c) 2009-2014, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -85,10 +85,11 @@ enum { QUP_MX_INPUT_DONE = 1U << 11, }; /* I2C mini core related values */ /* QUP_CONFIG values and flags */ enum { I2C_MINI_CORE = 2U << 8, I2C_N_VAL = 0xF, I2C_CORE_CLK_ON_EN = BIT(13), }; Loading Loading @@ -129,6 +130,12 @@ enum { I2C_CLK_FORCED_LOW_STATE = 5, }; enum msm_i2c_state { MSM_I2C_PM_ACTIVE, MSM_I2C_PM_SUSPENDED, MSM_I2C_SYS_SUSPENDING, MSM_I2C_SYS_SUSPENDED, }; #define QUP_MAX_CLK_STATE_RETRIES 300 #define DEFAULT_CLK_RATE (19200000) #define I2C_STATUS_CLK_STATE 13 Loading Loading @@ -184,8 +191,7 @@ struct qup_i2c_dev { int in_blk_sz; int wr_sz; struct msm_i2c_platform_data *pdata; int suspended; int pwr_state; enum msm_i2c_state pwr_state; struct mutex mlock; void *complete; int i2c_gpios[ARRAY_SIZE(i2c_rsrcs)]; Loading Loading @@ -339,22 +345,6 @@ qup_update_state(struct qup_i2c_dev *dev, uint32_t state) return 0; } /* * Before calling qup_config_core_on_en(), please make * sure that QuPE core is in RESET state. */ static void qup_config_core_on_en(struct qup_i2c_dev *dev) { uint32_t status; status = readl_relaxed(dev->base + QUP_CONFIG); status |= BIT(13); writel_relaxed(status, dev->base + QUP_CONFIG); /* making sure that write has really gone through */ mb(); } #define MSM_I2C_CLK_PATH_SUSPEND (0) #define MSM_I2C_CLK_PATH_RESUME (1) #define MSM_I2C_CLK_PATH_MAX_BW(dev) ((dev->pdata->src_clk_rate * 8) / 1000) Loading Loading @@ -500,27 +490,97 @@ static void i2c_qup_clk_path_postponed_register(struct qup_i2c_dev *dev) } } static void qup_i2c_pwr_mgmt(struct qup_i2c_dev *dev, unsigned int state) static int i2c_qup_gpio_request(struct qup_i2c_dev *dev) { dev->pwr_state = state; if (state != 0) { i2c_qup_clk_path_postponed_register(dev); if (!dev->pdata->active_only) i2c_qup_clk_path_vote(dev); int i; int result = 0; clk_prepare_enable(dev->clk); if (!dev->pdata->keep_ahb_clk_on) clk_prepare_enable(dev->pclk); } else { for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) { if (dev->i2c_gpios[i] >= 0) { result = gpio_request(dev->i2c_gpios[i], i2c_rsrcs[i]); if (result) { dev_err(dev->dev, "gpio_request for pin %d failed with error %d\n", dev->i2c_gpios[i], result); goto error; } } } return 0; error: for (; --i >= 0;) { if (dev->i2c_gpios[i] >= 0) gpio_free(dev->i2c_gpios[i]); } return result; } static void i2c_qup_gpio_free(struct qup_i2c_dev *dev) { int i; for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) { if (dev->i2c_gpios[i] >= 0) gpio_free(dev->i2c_gpios[i]); } } static void i2c_qup_pm_suspend_clk(struct qup_i2c_dev *dev) { uint32_t status; /* reset core and enable conditional dynamic clock gating */ qup_update_state(dev, QUP_RESET_STATE); status = readl_relaxed(dev->base + QUP_CONFIG); status |= I2C_CORE_CLK_ON_EN; writel_relaxed(status, dev->base + QUP_CONFIG); /* ensure that write has really gone through */ mb(); clk_disable_unprepare(dev->clk); qup_config_core_on_en(dev); if (!dev->pdata->keep_ahb_clk_on) clk_disable_unprepare(dev->pclk); } static void i2c_qup_pm_resume_clk(struct qup_i2c_dev *dev) { clk_prepare_enable(dev->clk); if (!dev->pdata->keep_ahb_clk_on) clk_prepare_enable(dev->pclk); } static void i2c_qup_pm_suspend(struct qup_i2c_dev *dev) { if (dev->pwr_state == MSM_I2C_PM_SUSPENDED) { dev_err(dev->dev, "attempt to suspend when suspended\n"); return; } if (!dev->pdata->clk_ctl_xfer) i2c_qup_pm_suspend_clk(dev); if (!dev->pdata->active_only) i2c_qup_clk_path_unvote(dev); i2c_qup_gpio_free(dev); dev->pwr_state = MSM_I2C_PM_SUSPENDED; } static void i2c_qup_pm_resume(struct qup_i2c_dev *dev) { if (dev->pwr_state == MSM_I2C_PM_ACTIVE) return; i2c_qup_gpio_request(dev); i2c_qup_clk_path_postponed_register(dev); if (!dev->pdata->active_only) i2c_qup_clk_path_vote(dev); if (!dev->pdata->clk_ctl_xfer) i2c_qup_pm_resume_clk(dev); dev->pwr_state = MSM_I2C_PM_ACTIVE; } static int Loading Loading @@ -595,43 +655,6 @@ static int qup_i2c_poll_clock_ready(struct qup_i2c_dev *dev) return -ETIMEDOUT; } static inline int qup_i2c_request_gpios(struct qup_i2c_dev *dev) { int i; int result = 0; for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) { if (dev->i2c_gpios[i] >= 0) { result = gpio_request(dev->i2c_gpios[i], i2c_rsrcs[i]); if (result) { dev_err(dev->dev, "gpio_request for pin %d failed\ with error %d\n", dev->i2c_gpios[i], result); goto error; } } } return 0; error: for (; --i >= 0;) { if (dev->i2c_gpios[i] >= 0) gpio_free(dev->i2c_gpios[i]); } return result; } static inline void qup_i2c_free_gpios(struct qup_i2c_dev *dev) { int i; for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) { if (dev->i2c_gpios[i] >= 0) gpio_free(dev->i2c_gpios[i]); } } #ifdef DEBUG static void qup_verify_fifo(struct qup_i2c_dev *dev, uint32_t val, uint32_t addr, int rdwr) Loading Loading @@ -948,19 +971,35 @@ qup_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) long timeout; int err; /* Alternate if runtime power management is disabled */ /* * If all slaves of this controller behave as expected, they will * implement suspend and won't call any transaction if they are * suspended. Since controller is its parent, controller's suspend * will be called only AFTER alls slaves are suspended. * However reality is differe and some slave don't implement suspend * If a slave tries to initiate transfer when we are suspended, * pm_runtime_enabled is set to false by system-pm. * Make sure we return error when transaction is initiated while * we are in suspended state */ mutex_lock(&dev->mlock); if (dev->pwr_state >= MSM_I2C_SYS_SUSPENDING) { dev_err(dev->dev, "xfer not allowed when ctrl is suspended addr:0x%x\n", msgs->addr); mutex_unlock(&dev->mlock); return -EIO; } if (!pm_runtime_enabled(dev->dev)) { dev_dbg(dev->dev, "Runtime PM is disabled\n"); i2c_qup_pm_resume_runtime(dev->dev); dev_dbg(dev->dev, "Runtime PM FEATURE is disabled\n"); i2c_qup_pm_resume(dev); } else { pm_runtime_get_sync(dev->dev); } mutex_lock(&dev->mlock); if (dev->suspended) { mutex_unlock(&dev->mlock); return -EIO; } if (dev->pdata->clk_ctl_xfer) i2c_qup_pm_resume_clk(dev); /* Initialize QUP registers during first transfer */ if (dev->clk_ctl == 0) { Loading Loading @@ -1263,6 +1302,8 @@ timeout_err: dev->pos = 0; dev->err = 0; dev->cnt = 0; if (dev->pdata->clk_ctl_xfer) i2c_qup_pm_suspend_clk(dev); mutex_unlock(&dev->mlock); pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); Loading Loading @@ -1302,6 +1343,7 @@ int msm_i2c_rsrcs_dt_to_pdata_map(struct platform_device *pdev, {"qcom,master-id", &pdata->master_id, DT_SUGGESTED, DT_U32, 0}, {"qcom,scl-gpio", gpios, DT_OPTIONAL, DT_GPIO, -1}, {"qcom,sda-gpio", gpios + 1, DT_OPTIONAL, DT_GPIO, -1}, {"qcom,clk-ctl-xfer", &pdata->clk_ctl_xfer, DT_OPTIONAL, DT_BOOL, -1}, {"qcom,active-only", &pdata->active_only, DT_OPTIONAL, DT_BOOL, 0}, {NULL, NULL, 0, 0, 0}, }; Loading Loading @@ -1611,7 +1653,7 @@ blsp_core_init: pdata->msm_i2c_config_gpio(dev->adapter.nr, 1); mutex_init(&dev->mlock); dev->pwr_state = 0; dev->pwr_state = MSM_I2C_PM_SUSPENDED; /* If the same AHB clock is used on Modem side * switch it on here itself and don't switch it * on and off during suspend and resume. Loading Loading @@ -1685,13 +1727,11 @@ qup_i2c_remove(struct platform_device *pdev) /* Grab mutex to ensure ongoing transaction is over */ mutex_lock(&dev->mlock); dev->suspended = 1; dev->pwr_state = MSM_I2C_SYS_SUSPENDING; mutex_unlock(&dev->mlock); i2c_qup_pm_suspend(dev); dev->pwr_state = MSM_I2C_SYS_SUSPENDED; mutex_destroy(&dev->mlock); if (dev->pwr_state != 0) { qup_i2c_pwr_mgmt(dev, 0); qup_i2c_free_gpios(dev); } platform_set_drvdata(pdev, NULL); if (dev->num_irqs == 3) { free_irq(dev->out_irq, dev); Loading Loading @@ -1732,14 +1772,7 @@ static int i2c_qup_pm_suspend_runtime(struct device *device) struct platform_device *pdev = to_platform_device(device); struct qup_i2c_dev *dev = platform_get_drvdata(pdev); dev_dbg(device, "pm_runtime: suspending...\n"); /* Grab mutex to ensure ongoing transaction is over */ mutex_lock(&dev->mlock); dev->suspended = 1; mutex_unlock(&dev->mlock); if (dev->pwr_state != 0) { qup_i2c_pwr_mgmt(dev, 0); qup_i2c_free_gpios(dev); } i2c_qup_pm_suspend(dev); return 0; } Loading @@ -1747,23 +1780,22 @@ static int i2c_qup_pm_resume_runtime(struct device *device) { struct platform_device *pdev = to_platform_device(device); struct qup_i2c_dev *dev = platform_get_drvdata(pdev); int ret = 0; dev_dbg(device, "pm_runtime: resuming...\n"); if (dev->pwr_state == 0) { ret = qup_i2c_request_gpios(dev); if (ret != 0) return ret; qup_i2c_pwr_mgmt(dev, 1); } dev->suspended = 0; i2c_qup_pm_resume(dev); return 0; } static int qup_i2c_suspend(struct device *device) static int i2c_qup_pm_suspend_sys(struct device *device) { struct platform_device *pdev = to_platform_device(device); struct qup_i2c_dev *dev = platform_get_drvdata(pdev); /* Acquire mutex to ensure current transaction is over */ mutex_lock(&dev->mlock); dev->pwr_state = MSM_I2C_SYS_SUSPENDING; mutex_unlock(&dev->mlock); if (!pm_runtime_enabled(device) || !pm_runtime_suspended(device)) { dev_dbg(device, "system suspend"); i2c_qup_pm_suspend_runtime(device); dev_dbg(device, "system suspend\n"); i2c_qup_pm_suspend(dev); /* * set the device's runtime PM status to 'suspended' */ Loading @@ -1771,25 +1803,29 @@ static int qup_i2c_suspend(struct device *device) pm_runtime_set_suspended(device); pm_runtime_enable(device); } dev->pwr_state = MSM_I2C_SYS_SUSPENDED; return 0; } static int qup_i2c_resume(struct device *device) static int i2c_qup_pm_resume_sys(struct device *device) { struct platform_device *pdev = to_platform_device(device); struct qup_i2c_dev *dev = platform_get_drvdata(pdev); /* * Rely on runtime-PM to call resume in case it is enabled * Even if it's not enabled, rely on 1st client transaction to do * clock ON and gpio configuration */ dev_dbg(device, "system resume"); dev_dbg(device, "system resume\n"); dev->pwr_state = MSM_I2C_PM_SUSPENDED; return 0; } #endif /* CONFIG_PM */ static const struct dev_pm_ops i2c_qup_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS( qup_i2c_suspend, qup_i2c_resume i2c_qup_pm_suspend_sys, i2c_qup_pm_resume_sys ) SET_RUNTIME_PM_OPS( i2c_qup_pm_suspend_runtime, Loading