18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Freescale FlexTimer Module (FTM) PWM Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2012-2013 Freescale Semiconductor, Inc. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/err.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/mutex.h> 148c2ecf20Sopenharmony_ci#include <linux/of_address.h> 158c2ecf20Sopenharmony_ci#include <linux/of_device.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/pm.h> 188c2ecf20Sopenharmony_ci#include <linux/pwm.h> 198c2ecf20Sopenharmony_ci#include <linux/regmap.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/fsl/ftm.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define FTM_SC_CLK(c) (((c) + 1) << FTM_SC_CLK_MASK_SHIFT) 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cienum fsl_pwm_clk { 268c2ecf20Sopenharmony_ci FSL_PWM_CLK_SYS, 278c2ecf20Sopenharmony_ci FSL_PWM_CLK_FIX, 288c2ecf20Sopenharmony_ci FSL_PWM_CLK_EXT, 298c2ecf20Sopenharmony_ci FSL_PWM_CLK_CNTEN, 308c2ecf20Sopenharmony_ci FSL_PWM_CLK_MAX 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct fsl_ftm_soc { 348c2ecf20Sopenharmony_ci bool has_enable_bits; 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistruct fsl_pwm_periodcfg { 388c2ecf20Sopenharmony_ci enum fsl_pwm_clk clk_select; 398c2ecf20Sopenharmony_ci unsigned int clk_ps; 408c2ecf20Sopenharmony_ci unsigned int mod_period; 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistruct fsl_pwm_chip { 448c2ecf20Sopenharmony_ci struct pwm_chip chip; 458c2ecf20Sopenharmony_ci struct mutex lock; 468c2ecf20Sopenharmony_ci struct regmap *regmap; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci /* This value is valid iff a pwm is running */ 498c2ecf20Sopenharmony_ci struct fsl_pwm_periodcfg period; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci struct clk *ipg_clk; 528c2ecf20Sopenharmony_ci struct clk *clk[FSL_PWM_CLK_MAX]; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci const struct fsl_ftm_soc *soc; 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic inline struct fsl_pwm_chip *to_fsl_chip(struct pwm_chip *chip) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci return container_of(chip, struct fsl_pwm_chip, chip); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic void ftm_clear_write_protection(struct fsl_pwm_chip *fpc) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci u32 val; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci regmap_read(fpc->regmap, FTM_FMS, &val); 678c2ecf20Sopenharmony_ci if (val & FTM_FMS_WPEN) 688c2ecf20Sopenharmony_ci regmap_update_bits(fpc->regmap, FTM_MODE, FTM_MODE_WPDIS, 698c2ecf20Sopenharmony_ci FTM_MODE_WPDIS); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic void ftm_set_write_protection(struct fsl_pwm_chip *fpc) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci regmap_update_bits(fpc->regmap, FTM_FMS, FTM_FMS_WPEN, FTM_FMS_WPEN); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic bool fsl_pwm_periodcfg_are_equal(const struct fsl_pwm_periodcfg *a, 788c2ecf20Sopenharmony_ci const struct fsl_pwm_periodcfg *b) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci if (a->clk_select != b->clk_select) 818c2ecf20Sopenharmony_ci return false; 828c2ecf20Sopenharmony_ci if (a->clk_ps != b->clk_ps) 838c2ecf20Sopenharmony_ci return false; 848c2ecf20Sopenharmony_ci if (a->mod_period != b->mod_period) 858c2ecf20Sopenharmony_ci return false; 868c2ecf20Sopenharmony_ci return true; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic int fsl_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci int ret; 928c2ecf20Sopenharmony_ci struct fsl_pwm_chip *fpc = to_fsl_chip(chip); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci ret = clk_prepare_enable(fpc->ipg_clk); 958c2ecf20Sopenharmony_ci if (!ret && fpc->soc->has_enable_bits) { 968c2ecf20Sopenharmony_ci mutex_lock(&fpc->lock); 978c2ecf20Sopenharmony_ci regmap_update_bits(fpc->regmap, FTM_SC, BIT(pwm->hwpwm + 16), 988c2ecf20Sopenharmony_ci BIT(pwm->hwpwm + 16)); 998c2ecf20Sopenharmony_ci mutex_unlock(&fpc->lock); 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return ret; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic void fsl_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct fsl_pwm_chip *fpc = to_fsl_chip(chip); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (fpc->soc->has_enable_bits) { 1108c2ecf20Sopenharmony_ci mutex_lock(&fpc->lock); 1118c2ecf20Sopenharmony_ci regmap_update_bits(fpc->regmap, FTM_SC, BIT(pwm->hwpwm + 16), 1128c2ecf20Sopenharmony_ci 0); 1138c2ecf20Sopenharmony_ci mutex_unlock(&fpc->lock); 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci clk_disable_unprepare(fpc->ipg_clk); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic unsigned int fsl_pwm_ticks_to_ns(struct fsl_pwm_chip *fpc, 1208c2ecf20Sopenharmony_ci unsigned int ticks) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci unsigned long rate; 1238c2ecf20Sopenharmony_ci unsigned long long exval; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci rate = clk_get_rate(fpc->clk[fpc->period.clk_select]); 1268c2ecf20Sopenharmony_ci exval = ticks; 1278c2ecf20Sopenharmony_ci exval *= 1000000000UL; 1288c2ecf20Sopenharmony_ci do_div(exval, rate >> fpc->period.clk_ps); 1298c2ecf20Sopenharmony_ci return exval; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic bool fsl_pwm_calculate_period_clk(struct fsl_pwm_chip *fpc, 1338c2ecf20Sopenharmony_ci unsigned int period_ns, 1348c2ecf20Sopenharmony_ci enum fsl_pwm_clk index, 1358c2ecf20Sopenharmony_ci struct fsl_pwm_periodcfg *periodcfg 1368c2ecf20Sopenharmony_ci ) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci unsigned long long c; 1398c2ecf20Sopenharmony_ci unsigned int ps; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci c = clk_get_rate(fpc->clk[index]); 1428c2ecf20Sopenharmony_ci c = c * period_ns; 1438c2ecf20Sopenharmony_ci do_div(c, 1000000000UL); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (c == 0) 1468c2ecf20Sopenharmony_ci return false; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci for (ps = 0; ps < 8 ; ++ps, c >>= 1) { 1498c2ecf20Sopenharmony_ci if (c <= 0x10000) { 1508c2ecf20Sopenharmony_ci periodcfg->clk_select = index; 1518c2ecf20Sopenharmony_ci periodcfg->clk_ps = ps; 1528c2ecf20Sopenharmony_ci periodcfg->mod_period = c - 1; 1538c2ecf20Sopenharmony_ci return true; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci return false; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic bool fsl_pwm_calculate_period(struct fsl_pwm_chip *fpc, 1608c2ecf20Sopenharmony_ci unsigned int period_ns, 1618c2ecf20Sopenharmony_ci struct fsl_pwm_periodcfg *periodcfg) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci enum fsl_pwm_clk m0, m1; 1648c2ecf20Sopenharmony_ci unsigned long fix_rate, ext_rate; 1658c2ecf20Sopenharmony_ci bool ret; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci ret = fsl_pwm_calculate_period_clk(fpc, period_ns, FSL_PWM_CLK_SYS, 1688c2ecf20Sopenharmony_ci periodcfg); 1698c2ecf20Sopenharmony_ci if (ret) 1708c2ecf20Sopenharmony_ci return true; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci fix_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_FIX]); 1738c2ecf20Sopenharmony_ci ext_rate = clk_get_rate(fpc->clk[FSL_PWM_CLK_EXT]); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (fix_rate > ext_rate) { 1768c2ecf20Sopenharmony_ci m0 = FSL_PWM_CLK_FIX; 1778c2ecf20Sopenharmony_ci m1 = FSL_PWM_CLK_EXT; 1788c2ecf20Sopenharmony_ci } else { 1798c2ecf20Sopenharmony_ci m0 = FSL_PWM_CLK_EXT; 1808c2ecf20Sopenharmony_ci m1 = FSL_PWM_CLK_FIX; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci ret = fsl_pwm_calculate_period_clk(fpc, period_ns, m0, periodcfg); 1848c2ecf20Sopenharmony_ci if (ret) 1858c2ecf20Sopenharmony_ci return true; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return fsl_pwm_calculate_period_clk(fpc, period_ns, m1, periodcfg); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic unsigned int fsl_pwm_calculate_duty(struct fsl_pwm_chip *fpc, 1918c2ecf20Sopenharmony_ci unsigned int duty_ns) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci unsigned long long duty; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci unsigned int period = fpc->period.mod_period + 1; 1968c2ecf20Sopenharmony_ci unsigned int period_ns = fsl_pwm_ticks_to_ns(fpc, period); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci duty = (unsigned long long)duty_ns * period; 1998c2ecf20Sopenharmony_ci do_div(duty, period_ns); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return (unsigned int)duty; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic bool fsl_pwm_is_any_pwm_enabled(struct fsl_pwm_chip *fpc, 2058c2ecf20Sopenharmony_ci struct pwm_device *pwm) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci u32 val; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci regmap_read(fpc->regmap, FTM_OUTMASK, &val); 2108c2ecf20Sopenharmony_ci if (~val & 0xFF) 2118c2ecf20Sopenharmony_ci return true; 2128c2ecf20Sopenharmony_ci else 2138c2ecf20Sopenharmony_ci return false; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic bool fsl_pwm_is_other_pwm_enabled(struct fsl_pwm_chip *fpc, 2178c2ecf20Sopenharmony_ci struct pwm_device *pwm) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci u32 val; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci regmap_read(fpc->regmap, FTM_OUTMASK, &val); 2228c2ecf20Sopenharmony_ci if (~(val | BIT(pwm->hwpwm)) & 0xFF) 2238c2ecf20Sopenharmony_ci return true; 2248c2ecf20Sopenharmony_ci else 2258c2ecf20Sopenharmony_ci return false; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic int fsl_pwm_apply_config(struct fsl_pwm_chip *fpc, 2298c2ecf20Sopenharmony_ci struct pwm_device *pwm, 2308c2ecf20Sopenharmony_ci const struct pwm_state *newstate) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci unsigned int duty; 2338c2ecf20Sopenharmony_ci u32 reg_polarity; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci struct fsl_pwm_periodcfg periodcfg; 2368c2ecf20Sopenharmony_ci bool do_write_period = false; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (!fsl_pwm_calculate_period(fpc, newstate->period, &periodcfg)) { 2398c2ecf20Sopenharmony_ci dev_err(fpc->chip.dev, "failed to calculate new period\n"); 2408c2ecf20Sopenharmony_ci return -EINVAL; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (!fsl_pwm_is_any_pwm_enabled(fpc, pwm)) 2448c2ecf20Sopenharmony_ci do_write_period = true; 2458c2ecf20Sopenharmony_ci /* 2468c2ecf20Sopenharmony_ci * The Freescale FTM controller supports only a single period for 2478c2ecf20Sopenharmony_ci * all PWM channels, therefore verify if the newly computed period 2488c2ecf20Sopenharmony_ci * is different than the current period being used. In such case 2498c2ecf20Sopenharmony_ci * we allow to change the period only if no other pwm is running. 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_ci else if (!fsl_pwm_periodcfg_are_equal(&fpc->period, &periodcfg)) { 2528c2ecf20Sopenharmony_ci if (fsl_pwm_is_other_pwm_enabled(fpc, pwm)) { 2538c2ecf20Sopenharmony_ci dev_err(fpc->chip.dev, 2548c2ecf20Sopenharmony_ci "Cannot change period for PWM %u, disable other PWMs first\n", 2558c2ecf20Sopenharmony_ci pwm->hwpwm); 2568c2ecf20Sopenharmony_ci return -EBUSY; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci if (fpc->period.clk_select != periodcfg.clk_select) { 2598c2ecf20Sopenharmony_ci int ret; 2608c2ecf20Sopenharmony_ci enum fsl_pwm_clk oldclk = fpc->period.clk_select; 2618c2ecf20Sopenharmony_ci enum fsl_pwm_clk newclk = periodcfg.clk_select; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci ret = clk_prepare_enable(fpc->clk[newclk]); 2648c2ecf20Sopenharmony_ci if (ret) 2658c2ecf20Sopenharmony_ci return ret; 2668c2ecf20Sopenharmony_ci clk_disable_unprepare(fpc->clk[oldclk]); 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci do_write_period = true; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci ftm_clear_write_protection(fpc); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (do_write_period) { 2748c2ecf20Sopenharmony_ci regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK, 2758c2ecf20Sopenharmony_ci FTM_SC_CLK(periodcfg.clk_select)); 2768c2ecf20Sopenharmony_ci regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_PS_MASK, 2778c2ecf20Sopenharmony_ci periodcfg.clk_ps); 2788c2ecf20Sopenharmony_ci regmap_write(fpc->regmap, FTM_MOD, periodcfg.mod_period); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci fpc->period = periodcfg; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci duty = fsl_pwm_calculate_duty(fpc, newstate->duty_cycle); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci regmap_write(fpc->regmap, FTM_CSC(pwm->hwpwm), 2868c2ecf20Sopenharmony_ci FTM_CSC_MSB | FTM_CSC_ELSB); 2878c2ecf20Sopenharmony_ci regmap_write(fpc->regmap, FTM_CV(pwm->hwpwm), duty); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci reg_polarity = 0; 2908c2ecf20Sopenharmony_ci if (newstate->polarity == PWM_POLARITY_INVERSED) 2918c2ecf20Sopenharmony_ci reg_polarity = BIT(pwm->hwpwm); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci regmap_update_bits(fpc->regmap, FTM_POL, BIT(pwm->hwpwm), reg_polarity); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci ftm_set_write_protection(fpc); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic int fsl_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, 3018c2ecf20Sopenharmony_ci const struct pwm_state *newstate) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci struct fsl_pwm_chip *fpc = to_fsl_chip(chip); 3048c2ecf20Sopenharmony_ci struct pwm_state *oldstate = &pwm->state; 3058c2ecf20Sopenharmony_ci int ret = 0; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci /* 3088c2ecf20Sopenharmony_ci * oldstate to newstate : action 3098c2ecf20Sopenharmony_ci * 3108c2ecf20Sopenharmony_ci * disabled to disabled : ignore 3118c2ecf20Sopenharmony_ci * enabled to disabled : disable 3128c2ecf20Sopenharmony_ci * enabled to enabled : update settings 3138c2ecf20Sopenharmony_ci * disabled to enabled : update settings + enable 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci mutex_lock(&fpc->lock); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (!newstate->enabled) { 3198c2ecf20Sopenharmony_ci if (oldstate->enabled) { 3208c2ecf20Sopenharmony_ci regmap_update_bits(fpc->regmap, FTM_OUTMASK, 3218c2ecf20Sopenharmony_ci BIT(pwm->hwpwm), BIT(pwm->hwpwm)); 3228c2ecf20Sopenharmony_ci clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); 3238c2ecf20Sopenharmony_ci clk_disable_unprepare(fpc->clk[fpc->period.clk_select]); 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci goto end_mutex; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci ret = fsl_pwm_apply_config(fpc, pwm, newstate); 3308c2ecf20Sopenharmony_ci if (ret) 3318c2ecf20Sopenharmony_ci goto end_mutex; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci /* check if need to enable */ 3348c2ecf20Sopenharmony_ci if (!oldstate->enabled) { 3358c2ecf20Sopenharmony_ci ret = clk_prepare_enable(fpc->clk[fpc->period.clk_select]); 3368c2ecf20Sopenharmony_ci if (ret) 3378c2ecf20Sopenharmony_ci goto end_mutex; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci ret = clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]); 3408c2ecf20Sopenharmony_ci if (ret) { 3418c2ecf20Sopenharmony_ci clk_disable_unprepare(fpc->clk[fpc->period.clk_select]); 3428c2ecf20Sopenharmony_ci goto end_mutex; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm), 3468c2ecf20Sopenharmony_ci 0); 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ciend_mutex: 3508c2ecf20Sopenharmony_ci mutex_unlock(&fpc->lock); 3518c2ecf20Sopenharmony_ci return ret; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic const struct pwm_ops fsl_pwm_ops = { 3558c2ecf20Sopenharmony_ci .request = fsl_pwm_request, 3568c2ecf20Sopenharmony_ci .free = fsl_pwm_free, 3578c2ecf20Sopenharmony_ci .apply = fsl_pwm_apply, 3588c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3598c2ecf20Sopenharmony_ci}; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic int fsl_pwm_init(struct fsl_pwm_chip *fpc) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci int ret; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci ret = clk_prepare_enable(fpc->ipg_clk); 3668c2ecf20Sopenharmony_ci if (ret) 3678c2ecf20Sopenharmony_ci return ret; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci regmap_write(fpc->regmap, FTM_CNTIN, 0x00); 3708c2ecf20Sopenharmony_ci regmap_write(fpc->regmap, FTM_OUTINIT, 0x00); 3718c2ecf20Sopenharmony_ci regmap_write(fpc->regmap, FTM_OUTMASK, 0xFF); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci clk_disable_unprepare(fpc->ipg_clk); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci return 0; 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic bool fsl_pwm_volatile_reg(struct device *dev, unsigned int reg) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci switch (reg) { 3818c2ecf20Sopenharmony_ci case FTM_FMS: 3828c2ecf20Sopenharmony_ci case FTM_MODE: 3838c2ecf20Sopenharmony_ci case FTM_CNT: 3848c2ecf20Sopenharmony_ci return true; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci return false; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic const struct regmap_config fsl_pwm_regmap_config = { 3908c2ecf20Sopenharmony_ci .reg_bits = 32, 3918c2ecf20Sopenharmony_ci .reg_stride = 4, 3928c2ecf20Sopenharmony_ci .val_bits = 32, 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci .max_register = FTM_PWMLOAD, 3958c2ecf20Sopenharmony_ci .volatile_reg = fsl_pwm_volatile_reg, 3968c2ecf20Sopenharmony_ci .cache_type = REGCACHE_FLAT, 3978c2ecf20Sopenharmony_ci}; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic int fsl_pwm_probe(struct platform_device *pdev) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct fsl_pwm_chip *fpc; 4028c2ecf20Sopenharmony_ci struct resource *res; 4038c2ecf20Sopenharmony_ci void __iomem *base; 4048c2ecf20Sopenharmony_ci int ret; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci fpc = devm_kzalloc(&pdev->dev, sizeof(*fpc), GFP_KERNEL); 4078c2ecf20Sopenharmony_ci if (!fpc) 4088c2ecf20Sopenharmony_ci return -ENOMEM; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci mutex_init(&fpc->lock); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci fpc->soc = of_device_get_match_data(&pdev->dev); 4138c2ecf20Sopenharmony_ci fpc->chip.dev = &pdev->dev; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 4168c2ecf20Sopenharmony_ci base = devm_ioremap_resource(&pdev->dev, res); 4178c2ecf20Sopenharmony_ci if (IS_ERR(base)) 4188c2ecf20Sopenharmony_ci return PTR_ERR(base); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci fpc->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "ftm_sys", base, 4218c2ecf20Sopenharmony_ci &fsl_pwm_regmap_config); 4228c2ecf20Sopenharmony_ci if (IS_ERR(fpc->regmap)) { 4238c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "regmap init failed\n"); 4248c2ecf20Sopenharmony_ci return PTR_ERR(fpc->regmap); 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci fpc->clk[FSL_PWM_CLK_SYS] = devm_clk_get(&pdev->dev, "ftm_sys"); 4288c2ecf20Sopenharmony_ci if (IS_ERR(fpc->clk[FSL_PWM_CLK_SYS])) { 4298c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get \"ftm_sys\" clock\n"); 4308c2ecf20Sopenharmony_ci return PTR_ERR(fpc->clk[FSL_PWM_CLK_SYS]); 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci fpc->clk[FSL_PWM_CLK_FIX] = devm_clk_get(fpc->chip.dev, "ftm_fix"); 4348c2ecf20Sopenharmony_ci if (IS_ERR(fpc->clk[FSL_PWM_CLK_FIX])) 4358c2ecf20Sopenharmony_ci return PTR_ERR(fpc->clk[FSL_PWM_CLK_FIX]); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci fpc->clk[FSL_PWM_CLK_EXT] = devm_clk_get(fpc->chip.dev, "ftm_ext"); 4388c2ecf20Sopenharmony_ci if (IS_ERR(fpc->clk[FSL_PWM_CLK_EXT])) 4398c2ecf20Sopenharmony_ci return PTR_ERR(fpc->clk[FSL_PWM_CLK_EXT]); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci fpc->clk[FSL_PWM_CLK_CNTEN] = 4428c2ecf20Sopenharmony_ci devm_clk_get(fpc->chip.dev, "ftm_cnt_clk_en"); 4438c2ecf20Sopenharmony_ci if (IS_ERR(fpc->clk[FSL_PWM_CLK_CNTEN])) 4448c2ecf20Sopenharmony_ci return PTR_ERR(fpc->clk[FSL_PWM_CLK_CNTEN]); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* 4478c2ecf20Sopenharmony_ci * ipg_clk is the interface clock for the IP. If not provided, use the 4488c2ecf20Sopenharmony_ci * ftm_sys clock as the default. 4498c2ecf20Sopenharmony_ci */ 4508c2ecf20Sopenharmony_ci fpc->ipg_clk = devm_clk_get(&pdev->dev, "ipg"); 4518c2ecf20Sopenharmony_ci if (IS_ERR(fpc->ipg_clk)) 4528c2ecf20Sopenharmony_ci fpc->ipg_clk = fpc->clk[FSL_PWM_CLK_SYS]; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci fpc->chip.ops = &fsl_pwm_ops; 4568c2ecf20Sopenharmony_ci fpc->chip.of_xlate = of_pwm_xlate_with_flags; 4578c2ecf20Sopenharmony_ci fpc->chip.of_pwm_n_cells = 3; 4588c2ecf20Sopenharmony_ci fpc->chip.base = -1; 4598c2ecf20Sopenharmony_ci fpc->chip.npwm = 8; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci ret = pwmchip_add(&fpc->chip); 4628c2ecf20Sopenharmony_ci if (ret < 0) { 4638c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); 4648c2ecf20Sopenharmony_ci return ret; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, fpc); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci return fsl_pwm_init(fpc); 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic int fsl_pwm_remove(struct platform_device *pdev) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci struct fsl_pwm_chip *fpc = platform_get_drvdata(pdev); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci return pwmchip_remove(&fpc->chip); 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4808c2ecf20Sopenharmony_cistatic int fsl_pwm_suspend(struct device *dev) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct fsl_pwm_chip *fpc = dev_get_drvdata(dev); 4838c2ecf20Sopenharmony_ci int i; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci regcache_cache_only(fpc->regmap, true); 4868c2ecf20Sopenharmony_ci regcache_mark_dirty(fpc->regmap); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci for (i = 0; i < fpc->chip.npwm; i++) { 4898c2ecf20Sopenharmony_ci struct pwm_device *pwm = &fpc->chip.pwms[i]; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if (!test_bit(PWMF_REQUESTED, &pwm->flags)) 4928c2ecf20Sopenharmony_ci continue; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci clk_disable_unprepare(fpc->ipg_clk); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (!pwm_is_enabled(pwm)) 4978c2ecf20Sopenharmony_ci continue; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); 5008c2ecf20Sopenharmony_ci clk_disable_unprepare(fpc->clk[fpc->period.clk_select]); 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci return 0; 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic int fsl_pwm_resume(struct device *dev) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci struct fsl_pwm_chip *fpc = dev_get_drvdata(dev); 5098c2ecf20Sopenharmony_ci int i; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci for (i = 0; i < fpc->chip.npwm; i++) { 5128c2ecf20Sopenharmony_ci struct pwm_device *pwm = &fpc->chip.pwms[i]; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci if (!test_bit(PWMF_REQUESTED, &pwm->flags)) 5158c2ecf20Sopenharmony_ci continue; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci clk_prepare_enable(fpc->ipg_clk); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci if (!pwm_is_enabled(pwm)) 5208c2ecf20Sopenharmony_ci continue; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci clk_prepare_enable(fpc->clk[fpc->period.clk_select]); 5238c2ecf20Sopenharmony_ci clk_prepare_enable(fpc->clk[FSL_PWM_CLK_CNTEN]); 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* restore all registers from cache */ 5278c2ecf20Sopenharmony_ci regcache_cache_only(fpc->regmap, false); 5288c2ecf20Sopenharmony_ci regcache_sync(fpc->regmap); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci return 0; 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci#endif 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic const struct dev_pm_ops fsl_pwm_pm_ops = { 5358c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(fsl_pwm_suspend, fsl_pwm_resume) 5368c2ecf20Sopenharmony_ci}; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistatic const struct fsl_ftm_soc vf610_ftm_pwm = { 5398c2ecf20Sopenharmony_ci .has_enable_bits = false, 5408c2ecf20Sopenharmony_ci}; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic const struct fsl_ftm_soc imx8qm_ftm_pwm = { 5438c2ecf20Sopenharmony_ci .has_enable_bits = true, 5448c2ecf20Sopenharmony_ci}; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_cistatic const struct of_device_id fsl_pwm_dt_ids[] = { 5478c2ecf20Sopenharmony_ci { .compatible = "fsl,vf610-ftm-pwm", .data = &vf610_ftm_pwm }, 5488c2ecf20Sopenharmony_ci { .compatible = "fsl,imx8qm-ftm-pwm", .data = &imx8qm_ftm_pwm }, 5498c2ecf20Sopenharmony_ci { /* sentinel */ } 5508c2ecf20Sopenharmony_ci}; 5518c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, fsl_pwm_dt_ids); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic struct platform_driver fsl_pwm_driver = { 5548c2ecf20Sopenharmony_ci .driver = { 5558c2ecf20Sopenharmony_ci .name = "fsl-ftm-pwm", 5568c2ecf20Sopenharmony_ci .of_match_table = fsl_pwm_dt_ids, 5578c2ecf20Sopenharmony_ci .pm = &fsl_pwm_pm_ops, 5588c2ecf20Sopenharmony_ci }, 5598c2ecf20Sopenharmony_ci .probe = fsl_pwm_probe, 5608c2ecf20Sopenharmony_ci .remove = fsl_pwm_remove, 5618c2ecf20Sopenharmony_ci}; 5628c2ecf20Sopenharmony_cimodule_platform_driver(fsl_pwm_driver); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Freescale FlexTimer Module PWM Driver"); 5658c2ecf20Sopenharmony_ciMODULE_AUTHOR("Xiubo Li <Li.Xiubo@freescale.com>"); 5668c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:fsl-ftm-pwm"); 5678c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 568