162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2018-2019 NXP. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Limitations: 662306a36Sopenharmony_ci * - The TPM counter and period counter are shared between 762306a36Sopenharmony_ci * multiple channels, so all channels should use same period 862306a36Sopenharmony_ci * settings. 962306a36Sopenharmony_ci * - Changes to polarity cannot be latched at the time of the 1062306a36Sopenharmony_ci * next period start. 1162306a36Sopenharmony_ci * - Changing period and duty cycle together isn't atomic, 1262306a36Sopenharmony_ci * with the wrong timing it might happen that a period is 1362306a36Sopenharmony_ci * produced with old duty cycle but new period settings. 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/bitfield.h> 1762306a36Sopenharmony_ci#include <linux/bitops.h> 1862306a36Sopenharmony_ci#include <linux/clk.h> 1962306a36Sopenharmony_ci#include <linux/err.h> 2062306a36Sopenharmony_ci#include <linux/io.h> 2162306a36Sopenharmony_ci#include <linux/module.h> 2262306a36Sopenharmony_ci#include <linux/of.h> 2362306a36Sopenharmony_ci#include <linux/platform_device.h> 2462306a36Sopenharmony_ci#include <linux/pwm.h> 2562306a36Sopenharmony_ci#include <linux/slab.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define PWM_IMX_TPM_PARAM 0x4 2862306a36Sopenharmony_ci#define PWM_IMX_TPM_GLOBAL 0x8 2962306a36Sopenharmony_ci#define PWM_IMX_TPM_SC 0x10 3062306a36Sopenharmony_ci#define PWM_IMX_TPM_CNT 0x14 3162306a36Sopenharmony_ci#define PWM_IMX_TPM_MOD 0x18 3262306a36Sopenharmony_ci#define PWM_IMX_TPM_CnSC(n) (0x20 + (n) * 0x8) 3362306a36Sopenharmony_ci#define PWM_IMX_TPM_CnV(n) (0x24 + (n) * 0x8) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define PWM_IMX_TPM_PARAM_CHAN GENMASK(7, 0) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define PWM_IMX_TPM_SC_PS GENMASK(2, 0) 3862306a36Sopenharmony_ci#define PWM_IMX_TPM_SC_CMOD GENMASK(4, 3) 3962306a36Sopenharmony_ci#define PWM_IMX_TPM_SC_CMOD_INC_EVERY_CLK FIELD_PREP(PWM_IMX_TPM_SC_CMOD, 1) 4062306a36Sopenharmony_ci#define PWM_IMX_TPM_SC_CPWMS BIT(5) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define PWM_IMX_TPM_CnSC_CHF BIT(7) 4362306a36Sopenharmony_ci#define PWM_IMX_TPM_CnSC_MSB BIT(5) 4462306a36Sopenharmony_ci#define PWM_IMX_TPM_CnSC_MSA BIT(4) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* 4762306a36Sopenharmony_ci * The reference manual describes this field as two separate bits. The 4862306a36Sopenharmony_ci * semantic of the two bits isn't orthogonal though, so they are treated 4962306a36Sopenharmony_ci * together as a 2-bit field here. 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci#define PWM_IMX_TPM_CnSC_ELS GENMASK(3, 2) 5262306a36Sopenharmony_ci#define PWM_IMX_TPM_CnSC_ELS_INVERSED FIELD_PREP(PWM_IMX_TPM_CnSC_ELS, 1) 5362306a36Sopenharmony_ci#define PWM_IMX_TPM_CnSC_ELS_NORMAL FIELD_PREP(PWM_IMX_TPM_CnSC_ELS, 2) 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define PWM_IMX_TPM_MOD_WIDTH 16 5762306a36Sopenharmony_ci#define PWM_IMX_TPM_MOD_MOD GENMASK(PWM_IMX_TPM_MOD_WIDTH - 1, 0) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistruct imx_tpm_pwm_chip { 6062306a36Sopenharmony_ci struct pwm_chip chip; 6162306a36Sopenharmony_ci struct clk *clk; 6262306a36Sopenharmony_ci void __iomem *base; 6362306a36Sopenharmony_ci struct mutex lock; 6462306a36Sopenharmony_ci u32 user_count; 6562306a36Sopenharmony_ci u32 enable_count; 6662306a36Sopenharmony_ci u32 real_period; 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistruct imx_tpm_pwm_param { 7062306a36Sopenharmony_ci u8 prescale; 7162306a36Sopenharmony_ci u32 mod; 7262306a36Sopenharmony_ci u32 val; 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic inline struct imx_tpm_pwm_chip * 7662306a36Sopenharmony_cito_imx_tpm_pwm_chip(struct pwm_chip *chip) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci return container_of(chip, struct imx_tpm_pwm_chip, chip); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* 8262306a36Sopenharmony_ci * This function determines for a given pwm_state *state that a consumer 8362306a36Sopenharmony_ci * might request the pwm_state *real_state that eventually is implemented 8462306a36Sopenharmony_ci * by the hardware and the necessary register values (in *p) to achieve 8562306a36Sopenharmony_ci * this. 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_cistatic int pwm_imx_tpm_round_state(struct pwm_chip *chip, 8862306a36Sopenharmony_ci struct imx_tpm_pwm_param *p, 8962306a36Sopenharmony_ci struct pwm_state *real_state, 9062306a36Sopenharmony_ci const struct pwm_state *state) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip); 9362306a36Sopenharmony_ci u32 rate, prescale, period_count, clock_unit; 9462306a36Sopenharmony_ci u64 tmp; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci rate = clk_get_rate(tpm->clk); 9762306a36Sopenharmony_ci tmp = (u64)state->period * rate; 9862306a36Sopenharmony_ci clock_unit = DIV_ROUND_CLOSEST_ULL(tmp, NSEC_PER_SEC); 9962306a36Sopenharmony_ci if (clock_unit <= PWM_IMX_TPM_MOD_MOD) 10062306a36Sopenharmony_ci prescale = 0; 10162306a36Sopenharmony_ci else 10262306a36Sopenharmony_ci prescale = ilog2(clock_unit) + 1 - PWM_IMX_TPM_MOD_WIDTH; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if ((!FIELD_FIT(PWM_IMX_TPM_SC_PS, prescale))) 10562306a36Sopenharmony_ci return -ERANGE; 10662306a36Sopenharmony_ci p->prescale = prescale; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci period_count = (clock_unit + ((1 << prescale) >> 1)) >> prescale; 10962306a36Sopenharmony_ci p->mod = period_count; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* calculate real period HW can support */ 11262306a36Sopenharmony_ci tmp = (u64)period_count << prescale; 11362306a36Sopenharmony_ci tmp *= NSEC_PER_SEC; 11462306a36Sopenharmony_ci real_state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* 11762306a36Sopenharmony_ci * if eventually the PWM output is inactive, either 11862306a36Sopenharmony_ci * duty cycle is 0 or status is disabled, need to 11962306a36Sopenharmony_ci * make sure the output pin is inactive. 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci if (!state->enabled) 12262306a36Sopenharmony_ci real_state->duty_cycle = 0; 12362306a36Sopenharmony_ci else 12462306a36Sopenharmony_ci real_state->duty_cycle = state->duty_cycle; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci tmp = (u64)p->mod * real_state->duty_cycle; 12762306a36Sopenharmony_ci p->val = DIV64_U64_ROUND_CLOSEST(tmp, real_state->period); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci real_state->polarity = state->polarity; 13062306a36Sopenharmony_ci real_state->enabled = state->enabled; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int pwm_imx_tpm_get_state(struct pwm_chip *chip, 13662306a36Sopenharmony_ci struct pwm_device *pwm, 13762306a36Sopenharmony_ci struct pwm_state *state) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip); 14062306a36Sopenharmony_ci u32 rate, val, prescale; 14162306a36Sopenharmony_ci u64 tmp; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* get period */ 14462306a36Sopenharmony_ci state->period = tpm->real_period; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* get duty cycle */ 14762306a36Sopenharmony_ci rate = clk_get_rate(tpm->clk); 14862306a36Sopenharmony_ci val = readl(tpm->base + PWM_IMX_TPM_SC); 14962306a36Sopenharmony_ci prescale = FIELD_GET(PWM_IMX_TPM_SC_PS, val); 15062306a36Sopenharmony_ci tmp = readl(tpm->base + PWM_IMX_TPM_CnV(pwm->hwpwm)); 15162306a36Sopenharmony_ci tmp = (tmp << prescale) * NSEC_PER_SEC; 15262306a36Sopenharmony_ci state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, rate); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* get polarity */ 15562306a36Sopenharmony_ci val = readl(tpm->base + PWM_IMX_TPM_CnSC(pwm->hwpwm)); 15662306a36Sopenharmony_ci if ((val & PWM_IMX_TPM_CnSC_ELS) == PWM_IMX_TPM_CnSC_ELS_INVERSED) 15762306a36Sopenharmony_ci state->polarity = PWM_POLARITY_INVERSED; 15862306a36Sopenharmony_ci else 15962306a36Sopenharmony_ci /* 16062306a36Sopenharmony_ci * Assume reserved values (2b00 and 2b11) to yield 16162306a36Sopenharmony_ci * normal polarity. 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_ci state->polarity = PWM_POLARITY_NORMAL; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* get channel status */ 16662306a36Sopenharmony_ci state->enabled = FIELD_GET(PWM_IMX_TPM_CnSC_ELS, val) ? true : false; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci/* this function is supposed to be called with mutex hold */ 17262306a36Sopenharmony_cistatic int pwm_imx_tpm_apply_hw(struct pwm_chip *chip, 17362306a36Sopenharmony_ci struct imx_tpm_pwm_param *p, 17462306a36Sopenharmony_ci struct pwm_state *state, 17562306a36Sopenharmony_ci struct pwm_device *pwm) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip); 17862306a36Sopenharmony_ci bool period_update = false; 17962306a36Sopenharmony_ci bool duty_update = false; 18062306a36Sopenharmony_ci u32 val, cmod, cur_prescale; 18162306a36Sopenharmony_ci unsigned long timeout; 18262306a36Sopenharmony_ci struct pwm_state c; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (state->period != tpm->real_period) { 18562306a36Sopenharmony_ci /* 18662306a36Sopenharmony_ci * TPM counter is shared by multiple channels, so 18762306a36Sopenharmony_ci * prescale and period can NOT be modified when 18862306a36Sopenharmony_ci * there are multiple channels in use with different 18962306a36Sopenharmony_ci * period settings. 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_ci if (tpm->user_count > 1) 19262306a36Sopenharmony_ci return -EBUSY; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci val = readl(tpm->base + PWM_IMX_TPM_SC); 19562306a36Sopenharmony_ci cmod = FIELD_GET(PWM_IMX_TPM_SC_CMOD, val); 19662306a36Sopenharmony_ci cur_prescale = FIELD_GET(PWM_IMX_TPM_SC_PS, val); 19762306a36Sopenharmony_ci if (cmod && cur_prescale != p->prescale) 19862306a36Sopenharmony_ci return -EBUSY; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* set TPM counter prescale */ 20162306a36Sopenharmony_ci val &= ~PWM_IMX_TPM_SC_PS; 20262306a36Sopenharmony_ci val |= FIELD_PREP(PWM_IMX_TPM_SC_PS, p->prescale); 20362306a36Sopenharmony_ci writel(val, tpm->base + PWM_IMX_TPM_SC); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* 20662306a36Sopenharmony_ci * set period count: 20762306a36Sopenharmony_ci * if the PWM is disabled (CMOD[1:0] = 2b00), then MOD register 20862306a36Sopenharmony_ci * is updated when MOD register is written. 20962306a36Sopenharmony_ci * 21062306a36Sopenharmony_ci * if the PWM is enabled (CMOD[1:0] ≠ 2b00), the period length 21162306a36Sopenharmony_ci * is latched into hardware when the next period starts. 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_ci writel(p->mod, tpm->base + PWM_IMX_TPM_MOD); 21462306a36Sopenharmony_ci tpm->real_period = state->period; 21562306a36Sopenharmony_ci period_update = true; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci pwm_imx_tpm_get_state(chip, pwm, &c); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* polarity is NOT allowed to be changed if PWM is active */ 22162306a36Sopenharmony_ci if (c.enabled && c.polarity != state->polarity) 22262306a36Sopenharmony_ci return -EBUSY; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (state->duty_cycle != c.duty_cycle) { 22562306a36Sopenharmony_ci /* 22662306a36Sopenharmony_ci * set channel value: 22762306a36Sopenharmony_ci * if the PWM is disabled (CMOD[1:0] = 2b00), then CnV register 22862306a36Sopenharmony_ci * is updated when CnV register is written. 22962306a36Sopenharmony_ci * 23062306a36Sopenharmony_ci * if the PWM is enabled (CMOD[1:0] ≠ 2b00), the duty length 23162306a36Sopenharmony_ci * is latched into hardware when the next period starts. 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_ci writel(p->val, tpm->base + PWM_IMX_TPM_CnV(pwm->hwpwm)); 23462306a36Sopenharmony_ci duty_update = true; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* make sure MOD & CnV registers are updated */ 23862306a36Sopenharmony_ci if (period_update || duty_update) { 23962306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(tpm->real_period / 24062306a36Sopenharmony_ci NSEC_PER_MSEC + 1); 24162306a36Sopenharmony_ci while (readl(tpm->base + PWM_IMX_TPM_MOD) != p->mod 24262306a36Sopenharmony_ci || readl(tpm->base + PWM_IMX_TPM_CnV(pwm->hwpwm)) 24362306a36Sopenharmony_ci != p->val) { 24462306a36Sopenharmony_ci if (time_after(jiffies, timeout)) 24562306a36Sopenharmony_ci return -ETIME; 24662306a36Sopenharmony_ci cpu_relax(); 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* 25162306a36Sopenharmony_ci * polarity settings will enabled/disable output status 25262306a36Sopenharmony_ci * immediately, so if the channel is disabled, need to 25362306a36Sopenharmony_ci * make sure MSA/MSB/ELS are set to 0 which means channel 25462306a36Sopenharmony_ci * disabled. 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_ci val = readl(tpm->base + PWM_IMX_TPM_CnSC(pwm->hwpwm)); 25762306a36Sopenharmony_ci val &= ~(PWM_IMX_TPM_CnSC_ELS | PWM_IMX_TPM_CnSC_MSA | 25862306a36Sopenharmony_ci PWM_IMX_TPM_CnSC_MSB); 25962306a36Sopenharmony_ci if (state->enabled) { 26062306a36Sopenharmony_ci /* 26162306a36Sopenharmony_ci * set polarity (for edge-aligned PWM modes) 26262306a36Sopenharmony_ci * 26362306a36Sopenharmony_ci * ELS[1:0] = 2b10 yields normal polarity behaviour, 26462306a36Sopenharmony_ci * ELS[1:0] = 2b01 yields inversed polarity. 26562306a36Sopenharmony_ci * The other values are reserved. 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ci val |= PWM_IMX_TPM_CnSC_MSB; 26862306a36Sopenharmony_ci val |= (state->polarity == PWM_POLARITY_NORMAL) ? 26962306a36Sopenharmony_ci PWM_IMX_TPM_CnSC_ELS_NORMAL : 27062306a36Sopenharmony_ci PWM_IMX_TPM_CnSC_ELS_INVERSED; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci writel(val, tpm->base + PWM_IMX_TPM_CnSC(pwm->hwpwm)); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* control the counter status */ 27562306a36Sopenharmony_ci if (state->enabled != c.enabled) { 27662306a36Sopenharmony_ci val = readl(tpm->base + PWM_IMX_TPM_SC); 27762306a36Sopenharmony_ci if (state->enabled) { 27862306a36Sopenharmony_ci if (++tpm->enable_count == 1) 27962306a36Sopenharmony_ci val |= PWM_IMX_TPM_SC_CMOD_INC_EVERY_CLK; 28062306a36Sopenharmony_ci } else { 28162306a36Sopenharmony_ci if (--tpm->enable_count == 0) 28262306a36Sopenharmony_ci val &= ~PWM_IMX_TPM_SC_CMOD; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci writel(val, tpm->base + PWM_IMX_TPM_SC); 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci return 0; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic int pwm_imx_tpm_apply(struct pwm_chip *chip, 29162306a36Sopenharmony_ci struct pwm_device *pwm, 29262306a36Sopenharmony_ci const struct pwm_state *state) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip); 29562306a36Sopenharmony_ci struct imx_tpm_pwm_param param; 29662306a36Sopenharmony_ci struct pwm_state real_state; 29762306a36Sopenharmony_ci int ret; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci ret = pwm_imx_tpm_round_state(chip, ¶m, &real_state, state); 30062306a36Sopenharmony_ci if (ret) 30162306a36Sopenharmony_ci return ret; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci mutex_lock(&tpm->lock); 30462306a36Sopenharmony_ci ret = pwm_imx_tpm_apply_hw(chip, ¶m, &real_state, pwm); 30562306a36Sopenharmony_ci mutex_unlock(&tpm->lock); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return ret; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic int pwm_imx_tpm_request(struct pwm_chip *chip, struct pwm_device *pwm) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci mutex_lock(&tpm->lock); 31562306a36Sopenharmony_ci tpm->user_count++; 31662306a36Sopenharmony_ci mutex_unlock(&tpm->lock); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return 0; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic void pwm_imx_tpm_free(struct pwm_chip *chip, struct pwm_device *pwm) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci mutex_lock(&tpm->lock); 32662306a36Sopenharmony_ci tpm->user_count--; 32762306a36Sopenharmony_ci mutex_unlock(&tpm->lock); 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic const struct pwm_ops imx_tpm_pwm_ops = { 33162306a36Sopenharmony_ci .request = pwm_imx_tpm_request, 33262306a36Sopenharmony_ci .free = pwm_imx_tpm_free, 33362306a36Sopenharmony_ci .get_state = pwm_imx_tpm_get_state, 33462306a36Sopenharmony_ci .apply = pwm_imx_tpm_apply, 33562306a36Sopenharmony_ci .owner = THIS_MODULE, 33662306a36Sopenharmony_ci}; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic int pwm_imx_tpm_probe(struct platform_device *pdev) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct imx_tpm_pwm_chip *tpm; 34162306a36Sopenharmony_ci int ret; 34262306a36Sopenharmony_ci u32 val; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci tpm = devm_kzalloc(&pdev->dev, sizeof(*tpm), GFP_KERNEL); 34562306a36Sopenharmony_ci if (!tpm) 34662306a36Sopenharmony_ci return -ENOMEM; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci platform_set_drvdata(pdev, tpm); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci tpm->base = devm_platform_ioremap_resource(pdev, 0); 35162306a36Sopenharmony_ci if (IS_ERR(tpm->base)) 35262306a36Sopenharmony_ci return PTR_ERR(tpm->base); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci tpm->clk = devm_clk_get(&pdev->dev, NULL); 35562306a36Sopenharmony_ci if (IS_ERR(tpm->clk)) 35662306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, PTR_ERR(tpm->clk), 35762306a36Sopenharmony_ci "failed to get PWM clock\n"); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci ret = clk_prepare_enable(tpm->clk); 36062306a36Sopenharmony_ci if (ret) { 36162306a36Sopenharmony_ci dev_err(&pdev->dev, 36262306a36Sopenharmony_ci "failed to prepare or enable clock: %d\n", ret); 36362306a36Sopenharmony_ci return ret; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci tpm->chip.dev = &pdev->dev; 36762306a36Sopenharmony_ci tpm->chip.ops = &imx_tpm_pwm_ops; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* get number of channels */ 37062306a36Sopenharmony_ci val = readl(tpm->base + PWM_IMX_TPM_PARAM); 37162306a36Sopenharmony_ci tpm->chip.npwm = FIELD_GET(PWM_IMX_TPM_PARAM_CHAN, val); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci mutex_init(&tpm->lock); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci ret = pwmchip_add(&tpm->chip); 37662306a36Sopenharmony_ci if (ret) { 37762306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); 37862306a36Sopenharmony_ci clk_disable_unprepare(tpm->clk); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return ret; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic void pwm_imx_tpm_remove(struct platform_device *pdev) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct imx_tpm_pwm_chip *tpm = platform_get_drvdata(pdev); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci pwmchip_remove(&tpm->chip); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci clk_disable_unprepare(tpm->clk); 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic int __maybe_unused pwm_imx_tpm_suspend(struct device *dev) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct imx_tpm_pwm_chip *tpm = dev_get_drvdata(dev); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (tpm->enable_count > 0) 39862306a36Sopenharmony_ci return -EBUSY; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* 40162306a36Sopenharmony_ci * Force 'real_period' to be zero to force period update code 40262306a36Sopenharmony_ci * can be executed after system resume back, since suspend causes 40362306a36Sopenharmony_ci * the period related registers to become their reset values. 40462306a36Sopenharmony_ci */ 40562306a36Sopenharmony_ci tpm->real_period = 0; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci clk_disable_unprepare(tpm->clk); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic int __maybe_unused pwm_imx_tpm_resume(struct device *dev) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct imx_tpm_pwm_chip *tpm = dev_get_drvdata(dev); 41562306a36Sopenharmony_ci int ret = 0; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci ret = clk_prepare_enable(tpm->clk); 41862306a36Sopenharmony_ci if (ret) 41962306a36Sopenharmony_ci dev_err(dev, "failed to prepare or enable clock: %d\n", ret); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return ret; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(imx_tpm_pwm_pm, 42562306a36Sopenharmony_ci pwm_imx_tpm_suspend, pwm_imx_tpm_resume); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic const struct of_device_id imx_tpm_pwm_dt_ids[] = { 42862306a36Sopenharmony_ci { .compatible = "fsl,imx7ulp-pwm", }, 42962306a36Sopenharmony_ci { /* sentinel */ } 43062306a36Sopenharmony_ci}; 43162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, imx_tpm_pwm_dt_ids); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic struct platform_driver imx_tpm_pwm_driver = { 43462306a36Sopenharmony_ci .driver = { 43562306a36Sopenharmony_ci .name = "imx7ulp-tpm-pwm", 43662306a36Sopenharmony_ci .of_match_table = imx_tpm_pwm_dt_ids, 43762306a36Sopenharmony_ci .pm = &imx_tpm_pwm_pm, 43862306a36Sopenharmony_ci }, 43962306a36Sopenharmony_ci .probe = pwm_imx_tpm_probe, 44062306a36Sopenharmony_ci .remove_new = pwm_imx_tpm_remove, 44162306a36Sopenharmony_ci}; 44262306a36Sopenharmony_cimodule_platform_driver(imx_tpm_pwm_driver); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ciMODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>"); 44562306a36Sopenharmony_ciMODULE_DESCRIPTION("i.MX TPM PWM Driver"); 44662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 447