162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Imagination Technologies Pulse Width Modulator driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2014-2015, Imagination Technologies 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Based on drivers/pwm/pwm-tegra.c, Copyright (c) 2010, NVIDIA Corporation 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/err.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/of_device.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1962306a36Sopenharmony_ci#include <linux/pwm.h> 2062306a36Sopenharmony_ci#include <linux/regmap.h> 2162306a36Sopenharmony_ci#include <linux/slab.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* PWM registers */ 2462306a36Sopenharmony_ci#define PWM_CTRL_CFG 0x0000 2562306a36Sopenharmony_ci#define PWM_CTRL_CFG_NO_SUB_DIV 0 2662306a36Sopenharmony_ci#define PWM_CTRL_CFG_SUB_DIV0 1 2762306a36Sopenharmony_ci#define PWM_CTRL_CFG_SUB_DIV1 2 2862306a36Sopenharmony_ci#define PWM_CTRL_CFG_SUB_DIV0_DIV1 3 2962306a36Sopenharmony_ci#define PWM_CTRL_CFG_DIV_SHIFT(ch) ((ch) * 2 + 4) 3062306a36Sopenharmony_ci#define PWM_CTRL_CFG_DIV_MASK 0x3 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define PWM_CH_CFG(ch) (0x4 + (ch) * 4) 3362306a36Sopenharmony_ci#define PWM_CH_CFG_TMBASE_SHIFT 0 3462306a36Sopenharmony_ci#define PWM_CH_CFG_DUTY_SHIFT 16 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define PERIP_PWM_PDM_CONTROL 0x0140 3762306a36Sopenharmony_ci#define PERIP_PWM_PDM_CONTROL_CH_MASK 0x1 3862306a36Sopenharmony_ci#define PERIP_PWM_PDM_CONTROL_CH_SHIFT(ch) ((ch) * 4) 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define IMG_PWM_PM_TIMEOUT 1000 /* ms */ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * PWM period is specified with a timebase register, 4462306a36Sopenharmony_ci * in number of step periods. The PWM duty cycle is also 4562306a36Sopenharmony_ci * specified in step periods, in the [0, $timebase] range. 4662306a36Sopenharmony_ci * In other words, the timebase imposes the duty cycle 4762306a36Sopenharmony_ci * resolution. Therefore, let's constraint the timebase to 4862306a36Sopenharmony_ci * a minimum value to allow a sane range of duty cycle values. 4962306a36Sopenharmony_ci * Imposing a minimum timebase, will impose a maximum PWM frequency. 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * The value chosen is completely arbitrary. 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_ci#define MIN_TMBASE_STEPS 16 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define IMG_PWM_NPWM 4 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistruct img_pwm_soc_data { 5862306a36Sopenharmony_ci u32 max_timebase; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct img_pwm_chip { 6262306a36Sopenharmony_ci struct device *dev; 6362306a36Sopenharmony_ci struct pwm_chip chip; 6462306a36Sopenharmony_ci struct clk *pwm_clk; 6562306a36Sopenharmony_ci struct clk *sys_clk; 6662306a36Sopenharmony_ci void __iomem *base; 6762306a36Sopenharmony_ci struct regmap *periph_regs; 6862306a36Sopenharmony_ci int max_period_ns; 6962306a36Sopenharmony_ci int min_period_ns; 7062306a36Sopenharmony_ci const struct img_pwm_soc_data *data; 7162306a36Sopenharmony_ci u32 suspend_ctrl_cfg; 7262306a36Sopenharmony_ci u32 suspend_ch_cfg[IMG_PWM_NPWM]; 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic inline struct img_pwm_chip *to_img_pwm_chip(struct pwm_chip *chip) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci return container_of(chip, struct img_pwm_chip, chip); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic inline void img_pwm_writel(struct img_pwm_chip *imgchip, 8162306a36Sopenharmony_ci u32 reg, u32 val) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci writel(val, imgchip->base + reg); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic inline u32 img_pwm_readl(struct img_pwm_chip *imgchip, u32 reg) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci return readl(imgchip->base + reg); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic int img_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, 9262306a36Sopenharmony_ci int duty_ns, int period_ns) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci u32 val, div, duty, timebase; 9562306a36Sopenharmony_ci unsigned long mul, output_clk_hz, input_clk_hz; 9662306a36Sopenharmony_ci struct img_pwm_chip *imgchip = to_img_pwm_chip(chip); 9762306a36Sopenharmony_ci unsigned int max_timebase = imgchip->data->max_timebase; 9862306a36Sopenharmony_ci int ret; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (period_ns < imgchip->min_period_ns || 10162306a36Sopenharmony_ci period_ns > imgchip->max_period_ns) { 10262306a36Sopenharmony_ci dev_err(chip->dev, "configured period not in range\n"); 10362306a36Sopenharmony_ci return -ERANGE; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci input_clk_hz = clk_get_rate(imgchip->pwm_clk); 10762306a36Sopenharmony_ci output_clk_hz = DIV_ROUND_UP(NSEC_PER_SEC, period_ns); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci mul = DIV_ROUND_UP(input_clk_hz, output_clk_hz); 11062306a36Sopenharmony_ci if (mul <= max_timebase) { 11162306a36Sopenharmony_ci div = PWM_CTRL_CFG_NO_SUB_DIV; 11262306a36Sopenharmony_ci timebase = DIV_ROUND_UP(mul, 1); 11362306a36Sopenharmony_ci } else if (mul <= max_timebase * 8) { 11462306a36Sopenharmony_ci div = PWM_CTRL_CFG_SUB_DIV0; 11562306a36Sopenharmony_ci timebase = DIV_ROUND_UP(mul, 8); 11662306a36Sopenharmony_ci } else if (mul <= max_timebase * 64) { 11762306a36Sopenharmony_ci div = PWM_CTRL_CFG_SUB_DIV1; 11862306a36Sopenharmony_ci timebase = DIV_ROUND_UP(mul, 64); 11962306a36Sopenharmony_ci } else if (mul <= max_timebase * 512) { 12062306a36Sopenharmony_ci div = PWM_CTRL_CFG_SUB_DIV0_DIV1; 12162306a36Sopenharmony_ci timebase = DIV_ROUND_UP(mul, 512); 12262306a36Sopenharmony_ci } else { 12362306a36Sopenharmony_ci dev_err(chip->dev, 12462306a36Sopenharmony_ci "failed to configure timebase steps/divider value\n"); 12562306a36Sopenharmony_ci return -EINVAL; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci duty = DIV_ROUND_UP(timebase * duty_ns, period_ns); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(chip->dev); 13162306a36Sopenharmony_ci if (ret < 0) 13262306a36Sopenharmony_ci return ret; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci val = img_pwm_readl(imgchip, PWM_CTRL_CFG); 13562306a36Sopenharmony_ci val &= ~(PWM_CTRL_CFG_DIV_MASK << PWM_CTRL_CFG_DIV_SHIFT(pwm->hwpwm)); 13662306a36Sopenharmony_ci val |= (div & PWM_CTRL_CFG_DIV_MASK) << 13762306a36Sopenharmony_ci PWM_CTRL_CFG_DIV_SHIFT(pwm->hwpwm); 13862306a36Sopenharmony_ci img_pwm_writel(imgchip, PWM_CTRL_CFG, val); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci val = (duty << PWM_CH_CFG_DUTY_SHIFT) | 14162306a36Sopenharmony_ci (timebase << PWM_CH_CFG_TMBASE_SHIFT); 14262306a36Sopenharmony_ci img_pwm_writel(imgchip, PWM_CH_CFG(pwm->hwpwm), val); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci pm_runtime_mark_last_busy(chip->dev); 14562306a36Sopenharmony_ci pm_runtime_put_autosuspend(chip->dev); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic int img_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci u32 val; 15362306a36Sopenharmony_ci struct img_pwm_chip *imgchip = to_img_pwm_chip(chip); 15462306a36Sopenharmony_ci int ret; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(chip->dev); 15762306a36Sopenharmony_ci if (ret < 0) 15862306a36Sopenharmony_ci return ret; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci val = img_pwm_readl(imgchip, PWM_CTRL_CFG); 16162306a36Sopenharmony_ci val |= BIT(pwm->hwpwm); 16262306a36Sopenharmony_ci img_pwm_writel(imgchip, PWM_CTRL_CFG, val); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci regmap_clear_bits(imgchip->periph_regs, PERIP_PWM_PDM_CONTROL, 16562306a36Sopenharmony_ci PERIP_PWM_PDM_CONTROL_CH_MASK << 16662306a36Sopenharmony_ci PERIP_PWM_PDM_CONTROL_CH_SHIFT(pwm->hwpwm)); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void img_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci u32 val; 17462306a36Sopenharmony_ci struct img_pwm_chip *imgchip = to_img_pwm_chip(chip); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci val = img_pwm_readl(imgchip, PWM_CTRL_CFG); 17762306a36Sopenharmony_ci val &= ~BIT(pwm->hwpwm); 17862306a36Sopenharmony_ci img_pwm_writel(imgchip, PWM_CTRL_CFG, val); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci pm_runtime_mark_last_busy(chip->dev); 18162306a36Sopenharmony_ci pm_runtime_put_autosuspend(chip->dev); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic int img_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, 18562306a36Sopenharmony_ci const struct pwm_state *state) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci int err; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (state->polarity != PWM_POLARITY_NORMAL) 19062306a36Sopenharmony_ci return -EINVAL; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (!state->enabled) { 19362306a36Sopenharmony_ci if (pwm->state.enabled) 19462306a36Sopenharmony_ci img_pwm_disable(chip, pwm); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return 0; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci err = img_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period); 20062306a36Sopenharmony_ci if (err) 20162306a36Sopenharmony_ci return err; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (!pwm->state.enabled) 20462306a36Sopenharmony_ci err = img_pwm_enable(chip, pwm); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci return err; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic const struct pwm_ops img_pwm_ops = { 21062306a36Sopenharmony_ci .apply = img_pwm_apply, 21162306a36Sopenharmony_ci .owner = THIS_MODULE, 21262306a36Sopenharmony_ci}; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic const struct img_pwm_soc_data pistachio_pwm = { 21562306a36Sopenharmony_ci .max_timebase = 255, 21662306a36Sopenharmony_ci}; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic const struct of_device_id img_pwm_of_match[] = { 21962306a36Sopenharmony_ci { 22062306a36Sopenharmony_ci .compatible = "img,pistachio-pwm", 22162306a36Sopenharmony_ci .data = &pistachio_pwm, 22262306a36Sopenharmony_ci }, 22362306a36Sopenharmony_ci { } 22462306a36Sopenharmony_ci}; 22562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, img_pwm_of_match); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic int img_pwm_runtime_suspend(struct device *dev) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct img_pwm_chip *imgchip = dev_get_drvdata(dev); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci clk_disable_unprepare(imgchip->pwm_clk); 23262306a36Sopenharmony_ci clk_disable_unprepare(imgchip->sys_clk); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return 0; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic int img_pwm_runtime_resume(struct device *dev) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct img_pwm_chip *imgchip = dev_get_drvdata(dev); 24062306a36Sopenharmony_ci int ret; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci ret = clk_prepare_enable(imgchip->sys_clk); 24362306a36Sopenharmony_ci if (ret < 0) { 24462306a36Sopenharmony_ci dev_err(dev, "could not prepare or enable sys clock\n"); 24562306a36Sopenharmony_ci return ret; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci ret = clk_prepare_enable(imgchip->pwm_clk); 24962306a36Sopenharmony_ci if (ret < 0) { 25062306a36Sopenharmony_ci dev_err(dev, "could not prepare or enable pwm clock\n"); 25162306a36Sopenharmony_ci clk_disable_unprepare(imgchip->sys_clk); 25262306a36Sopenharmony_ci return ret; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci return 0; 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic int img_pwm_probe(struct platform_device *pdev) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci int ret; 26162306a36Sopenharmony_ci u64 val; 26262306a36Sopenharmony_ci unsigned long clk_rate; 26362306a36Sopenharmony_ci struct img_pwm_chip *imgchip; 26462306a36Sopenharmony_ci const struct of_device_id *of_dev_id; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci imgchip = devm_kzalloc(&pdev->dev, sizeof(*imgchip), GFP_KERNEL); 26762306a36Sopenharmony_ci if (!imgchip) 26862306a36Sopenharmony_ci return -ENOMEM; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci imgchip->dev = &pdev->dev; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci imgchip->base = devm_platform_ioremap_resource(pdev, 0); 27362306a36Sopenharmony_ci if (IS_ERR(imgchip->base)) 27462306a36Sopenharmony_ci return PTR_ERR(imgchip->base); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci of_dev_id = of_match_device(img_pwm_of_match, &pdev->dev); 27762306a36Sopenharmony_ci if (!of_dev_id) 27862306a36Sopenharmony_ci return -ENODEV; 27962306a36Sopenharmony_ci imgchip->data = of_dev_id->data; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci imgchip->periph_regs = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, 28262306a36Sopenharmony_ci "img,cr-periph"); 28362306a36Sopenharmony_ci if (IS_ERR(imgchip->periph_regs)) 28462306a36Sopenharmony_ci return PTR_ERR(imgchip->periph_regs); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci imgchip->sys_clk = devm_clk_get(&pdev->dev, "sys"); 28762306a36Sopenharmony_ci if (IS_ERR(imgchip->sys_clk)) { 28862306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get system clock\n"); 28962306a36Sopenharmony_ci return PTR_ERR(imgchip->sys_clk); 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci imgchip->pwm_clk = devm_clk_get(&pdev->dev, "imgchip"); 29362306a36Sopenharmony_ci if (IS_ERR(imgchip->pwm_clk)) { 29462306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get imgchip clock\n"); 29562306a36Sopenharmony_ci return PTR_ERR(imgchip->pwm_clk); 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci platform_set_drvdata(pdev, imgchip); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, IMG_PWM_PM_TIMEOUT); 30162306a36Sopenharmony_ci pm_runtime_use_autosuspend(&pdev->dev); 30262306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 30362306a36Sopenharmony_ci if (!pm_runtime_enabled(&pdev->dev)) { 30462306a36Sopenharmony_ci ret = img_pwm_runtime_resume(&pdev->dev); 30562306a36Sopenharmony_ci if (ret) 30662306a36Sopenharmony_ci goto err_pm_disable; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci clk_rate = clk_get_rate(imgchip->pwm_clk); 31062306a36Sopenharmony_ci if (!clk_rate) { 31162306a36Sopenharmony_ci dev_err(&pdev->dev, "imgchip clock has no frequency\n"); 31262306a36Sopenharmony_ci ret = -EINVAL; 31362306a36Sopenharmony_ci goto err_suspend; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* The maximum input clock divider is 512 */ 31762306a36Sopenharmony_ci val = (u64)NSEC_PER_SEC * 512 * imgchip->data->max_timebase; 31862306a36Sopenharmony_ci do_div(val, clk_rate); 31962306a36Sopenharmony_ci imgchip->max_period_ns = val; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci val = (u64)NSEC_PER_SEC * MIN_TMBASE_STEPS; 32262306a36Sopenharmony_ci do_div(val, clk_rate); 32362306a36Sopenharmony_ci imgchip->min_period_ns = val; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci imgchip->chip.dev = &pdev->dev; 32662306a36Sopenharmony_ci imgchip->chip.ops = &img_pwm_ops; 32762306a36Sopenharmony_ci imgchip->chip.npwm = IMG_PWM_NPWM; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci ret = pwmchip_add(&imgchip->chip); 33062306a36Sopenharmony_ci if (ret < 0) { 33162306a36Sopenharmony_ci dev_err(&pdev->dev, "pwmchip_add failed: %d\n", ret); 33262306a36Sopenharmony_ci goto err_suspend; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci return 0; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cierr_suspend: 33862306a36Sopenharmony_ci if (!pm_runtime_enabled(&pdev->dev)) 33962306a36Sopenharmony_ci img_pwm_runtime_suspend(&pdev->dev); 34062306a36Sopenharmony_cierr_pm_disable: 34162306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 34262306a36Sopenharmony_ci pm_runtime_dont_use_autosuspend(&pdev->dev); 34362306a36Sopenharmony_ci return ret; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic void img_pwm_remove(struct platform_device *pdev) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci struct img_pwm_chip *imgchip = platform_get_drvdata(pdev); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 35162306a36Sopenharmony_ci if (!pm_runtime_status_suspended(&pdev->dev)) 35262306a36Sopenharmony_ci img_pwm_runtime_suspend(&pdev->dev); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci pwmchip_remove(&imgchip->chip); 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 35862306a36Sopenharmony_cistatic int img_pwm_suspend(struct device *dev) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct img_pwm_chip *imgchip = dev_get_drvdata(dev); 36162306a36Sopenharmony_ci int i, ret; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (pm_runtime_status_suspended(dev)) { 36462306a36Sopenharmony_ci ret = img_pwm_runtime_resume(dev); 36562306a36Sopenharmony_ci if (ret) 36662306a36Sopenharmony_ci return ret; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci for (i = 0; i < imgchip->chip.npwm; i++) 37062306a36Sopenharmony_ci imgchip->suspend_ch_cfg[i] = img_pwm_readl(imgchip, 37162306a36Sopenharmony_ci PWM_CH_CFG(i)); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci imgchip->suspend_ctrl_cfg = img_pwm_readl(imgchip, PWM_CTRL_CFG); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci img_pwm_runtime_suspend(dev); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci return 0; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic int img_pwm_resume(struct device *dev) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci struct img_pwm_chip *imgchip = dev_get_drvdata(dev); 38362306a36Sopenharmony_ci int ret; 38462306a36Sopenharmony_ci int i; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci ret = img_pwm_runtime_resume(dev); 38762306a36Sopenharmony_ci if (ret) 38862306a36Sopenharmony_ci return ret; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci for (i = 0; i < imgchip->chip.npwm; i++) 39162306a36Sopenharmony_ci img_pwm_writel(imgchip, PWM_CH_CFG(i), 39262306a36Sopenharmony_ci imgchip->suspend_ch_cfg[i]); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci img_pwm_writel(imgchip, PWM_CTRL_CFG, imgchip->suspend_ctrl_cfg); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci for (i = 0; i < imgchip->chip.npwm; i++) 39762306a36Sopenharmony_ci if (imgchip->suspend_ctrl_cfg & BIT(i)) 39862306a36Sopenharmony_ci regmap_clear_bits(imgchip->periph_regs, 39962306a36Sopenharmony_ci PERIP_PWM_PDM_CONTROL, 40062306a36Sopenharmony_ci PERIP_PWM_PDM_CONTROL_CH_MASK << 40162306a36Sopenharmony_ci PERIP_PWM_PDM_CONTROL_CH_SHIFT(i)); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (pm_runtime_status_suspended(dev)) 40462306a36Sopenharmony_ci img_pwm_runtime_suspend(dev); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci return 0; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci#endif /* CONFIG_PM */ 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic const struct dev_pm_ops img_pwm_pm_ops = { 41162306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(img_pwm_runtime_suspend, 41262306a36Sopenharmony_ci img_pwm_runtime_resume, 41362306a36Sopenharmony_ci NULL) 41462306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(img_pwm_suspend, img_pwm_resume) 41562306a36Sopenharmony_ci}; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic struct platform_driver img_pwm_driver = { 41862306a36Sopenharmony_ci .driver = { 41962306a36Sopenharmony_ci .name = "img-pwm", 42062306a36Sopenharmony_ci .pm = &img_pwm_pm_ops, 42162306a36Sopenharmony_ci .of_match_table = img_pwm_of_match, 42262306a36Sopenharmony_ci }, 42362306a36Sopenharmony_ci .probe = img_pwm_probe, 42462306a36Sopenharmony_ci .remove_new = img_pwm_remove, 42562306a36Sopenharmony_ci}; 42662306a36Sopenharmony_cimodule_platform_driver(img_pwm_driver); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ciMODULE_AUTHOR("Sai Masarapu <Sai.Masarapu@imgtec.com>"); 42962306a36Sopenharmony_ciMODULE_DESCRIPTION("Imagination Technologies PWM DAC driver"); 43062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 431