18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PWM device driver for ST SoCs 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013-2016 STMicroelectronics (R&D) Limited 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Ajit Pal Singh <ajitpal.singh@st.com> 88c2ecf20Sopenharmony_ci * Lee Jones <lee.jones@linaro.org> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/clk.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/math64.h> 148c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/pwm.h> 198c2ecf20Sopenharmony_ci#include <linux/regmap.h> 208c2ecf20Sopenharmony_ci#include <linux/sched.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/time.h> 238c2ecf20Sopenharmony_ci#include <linux/wait.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define PWM_OUT_VAL(x) (0x00 + (4 * (x))) /* Device's Duty Cycle register */ 268c2ecf20Sopenharmony_ci#define PWM_CPT_VAL(x) (0x10 + (4 * (x))) /* Capture value */ 278c2ecf20Sopenharmony_ci#define PWM_CPT_EDGE(x) (0x30 + (4 * (x))) /* Edge to capture on */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define STI_PWM_CTRL 0x50 /* Control/Config register */ 308c2ecf20Sopenharmony_ci#define STI_INT_EN 0x54 /* Interrupt Enable/Disable register */ 318c2ecf20Sopenharmony_ci#define STI_INT_STA 0x58 /* Interrupt Status register */ 328c2ecf20Sopenharmony_ci#define PWM_INT_ACK 0x5c 338c2ecf20Sopenharmony_ci#define PWM_PRESCALE_LOW_MASK 0x0f 348c2ecf20Sopenharmony_ci#define PWM_PRESCALE_HIGH_MASK 0xf0 358c2ecf20Sopenharmony_ci#define PWM_CPT_EDGE_MASK 0x03 368c2ecf20Sopenharmony_ci#define PWM_INT_ACK_MASK 0x1ff 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define STI_MAX_CPT_DEVS 4 398c2ecf20Sopenharmony_ci#define CPT_DC_MAX 0xff 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* Regfield IDs */ 428c2ecf20Sopenharmony_cienum { 438c2ecf20Sopenharmony_ci /* Bits in PWM_CTRL*/ 448c2ecf20Sopenharmony_ci PWMCLK_PRESCALE_LOW, 458c2ecf20Sopenharmony_ci PWMCLK_PRESCALE_HIGH, 468c2ecf20Sopenharmony_ci CPTCLK_PRESCALE, 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci PWM_OUT_EN, 498c2ecf20Sopenharmony_ci PWM_CPT_EN, 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci PWM_CPT_INT_EN, 528c2ecf20Sopenharmony_ci PWM_CPT_INT_STAT, 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* Keep last */ 558c2ecf20Sopenharmony_ci MAX_REGFIELDS 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * Each capture input can be programmed to detect rising-edge, falling-edge, 608c2ecf20Sopenharmony_ci * either edge or neither egde. 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_cienum sti_cpt_edge { 638c2ecf20Sopenharmony_ci CPT_EDGE_DISABLED, 648c2ecf20Sopenharmony_ci CPT_EDGE_RISING, 658c2ecf20Sopenharmony_ci CPT_EDGE_FALLING, 668c2ecf20Sopenharmony_ci CPT_EDGE_BOTH, 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistruct sti_cpt_ddata { 708c2ecf20Sopenharmony_ci u32 snapshot[3]; 718c2ecf20Sopenharmony_ci unsigned int index; 728c2ecf20Sopenharmony_ci struct mutex lock; 738c2ecf20Sopenharmony_ci wait_queue_head_t wait; 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistruct sti_pwm_compat_data { 778c2ecf20Sopenharmony_ci const struct reg_field *reg_fields; 788c2ecf20Sopenharmony_ci unsigned int pwm_num_devs; 798c2ecf20Sopenharmony_ci unsigned int cpt_num_devs; 808c2ecf20Sopenharmony_ci unsigned int max_pwm_cnt; 818c2ecf20Sopenharmony_ci unsigned int max_prescale; 828c2ecf20Sopenharmony_ci struct sti_cpt_ddata *ddata; 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistruct sti_pwm_chip { 868c2ecf20Sopenharmony_ci struct device *dev; 878c2ecf20Sopenharmony_ci struct clk *pwm_clk; 888c2ecf20Sopenharmony_ci struct clk *cpt_clk; 898c2ecf20Sopenharmony_ci struct regmap *regmap; 908c2ecf20Sopenharmony_ci struct sti_pwm_compat_data *cdata; 918c2ecf20Sopenharmony_ci struct regmap_field *prescale_low; 928c2ecf20Sopenharmony_ci struct regmap_field *prescale_high; 938c2ecf20Sopenharmony_ci struct regmap_field *pwm_out_en; 948c2ecf20Sopenharmony_ci struct regmap_field *pwm_cpt_en; 958c2ecf20Sopenharmony_ci struct regmap_field *pwm_cpt_int_en; 968c2ecf20Sopenharmony_ci struct regmap_field *pwm_cpt_int_stat; 978c2ecf20Sopenharmony_ci struct pwm_chip chip; 988c2ecf20Sopenharmony_ci struct pwm_device *cur; 998c2ecf20Sopenharmony_ci unsigned long configured; 1008c2ecf20Sopenharmony_ci unsigned int en_count; 1018c2ecf20Sopenharmony_ci struct mutex sti_pwm_lock; /* To sync between enable/disable calls */ 1028c2ecf20Sopenharmony_ci void __iomem *mmio; 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic const struct reg_field sti_pwm_regfields[MAX_REGFIELDS] = { 1068c2ecf20Sopenharmony_ci [PWMCLK_PRESCALE_LOW] = REG_FIELD(STI_PWM_CTRL, 0, 3), 1078c2ecf20Sopenharmony_ci [PWMCLK_PRESCALE_HIGH] = REG_FIELD(STI_PWM_CTRL, 11, 14), 1088c2ecf20Sopenharmony_ci [CPTCLK_PRESCALE] = REG_FIELD(STI_PWM_CTRL, 4, 8), 1098c2ecf20Sopenharmony_ci [PWM_OUT_EN] = REG_FIELD(STI_PWM_CTRL, 9, 9), 1108c2ecf20Sopenharmony_ci [PWM_CPT_EN] = REG_FIELD(STI_PWM_CTRL, 10, 10), 1118c2ecf20Sopenharmony_ci [PWM_CPT_INT_EN] = REG_FIELD(STI_INT_EN, 1, 4), 1128c2ecf20Sopenharmony_ci [PWM_CPT_INT_STAT] = REG_FIELD(STI_INT_STA, 1, 4), 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic inline struct sti_pwm_chip *to_sti_pwmchip(struct pwm_chip *chip) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci return container_of(chip, struct sti_pwm_chip, chip); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/* 1218c2ecf20Sopenharmony_ci * Calculate the prescaler value corresponding to the period. 1228c2ecf20Sopenharmony_ci */ 1238c2ecf20Sopenharmony_cistatic int sti_pwm_get_prescale(struct sti_pwm_chip *pc, unsigned long period, 1248c2ecf20Sopenharmony_ci unsigned int *prescale) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct sti_pwm_compat_data *cdata = pc->cdata; 1278c2ecf20Sopenharmony_ci unsigned long clk_rate; 1288c2ecf20Sopenharmony_ci unsigned long value; 1298c2ecf20Sopenharmony_ci unsigned int ps; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci clk_rate = clk_get_rate(pc->pwm_clk); 1328c2ecf20Sopenharmony_ci if (!clk_rate) { 1338c2ecf20Sopenharmony_ci dev_err(pc->dev, "failed to get clock rate\n"); 1348c2ecf20Sopenharmony_ci return -EINVAL; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* 1388c2ecf20Sopenharmony_ci * prescale = ((period_ns * clk_rate) / (10^9 * (max_pwm_cnt + 1)) - 1 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ci value = NSEC_PER_SEC / clk_rate; 1418c2ecf20Sopenharmony_ci value *= cdata->max_pwm_cnt + 1; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (period % value) 1448c2ecf20Sopenharmony_ci return -EINVAL; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci ps = period / value - 1; 1478c2ecf20Sopenharmony_ci if (ps > cdata->max_prescale) 1488c2ecf20Sopenharmony_ci return -EINVAL; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci *prescale = ps; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/* 1568c2ecf20Sopenharmony_ci * For STiH4xx PWM IP, the PWM period is fixed to 256 local clock cycles. The 1578c2ecf20Sopenharmony_ci * only way to change the period (apart from changing the PWM input clock) is 1588c2ecf20Sopenharmony_ci * to change the PWM clock prescaler. 1598c2ecf20Sopenharmony_ci * 1608c2ecf20Sopenharmony_ci * The prescaler is of 8 bits, so 256 prescaler values and hence 256 possible 1618c2ecf20Sopenharmony_ci * period values are supported (for a particular clock rate). The requested 1628c2ecf20Sopenharmony_ci * period will be applied only if it matches one of these 256 values. 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_cistatic int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, 1658c2ecf20Sopenharmony_ci int duty_ns, int period_ns) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct sti_pwm_chip *pc = to_sti_pwmchip(chip); 1688c2ecf20Sopenharmony_ci struct sti_pwm_compat_data *cdata = pc->cdata; 1698c2ecf20Sopenharmony_ci unsigned int ncfg, value, prescale = 0; 1708c2ecf20Sopenharmony_ci struct pwm_device *cur = pc->cur; 1718c2ecf20Sopenharmony_ci struct device *dev = pc->dev; 1728c2ecf20Sopenharmony_ci bool period_same = false; 1738c2ecf20Sopenharmony_ci int ret; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci ncfg = hweight_long(pc->configured); 1768c2ecf20Sopenharmony_ci if (ncfg) 1778c2ecf20Sopenharmony_ci period_same = (period_ns == pwm_get_period(cur)); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* 1808c2ecf20Sopenharmony_ci * Allow configuration changes if one of the following conditions 1818c2ecf20Sopenharmony_ci * satisfy. 1828c2ecf20Sopenharmony_ci * 1. No devices have been configured. 1838c2ecf20Sopenharmony_ci * 2. Only one device has been configured and the new request is for 1848c2ecf20Sopenharmony_ci * the same device. 1858c2ecf20Sopenharmony_ci * 3. Only one device has been configured and the new request is for 1868c2ecf20Sopenharmony_ci * a new device and period of the new device is same as the current 1878c2ecf20Sopenharmony_ci * configured period. 1888c2ecf20Sopenharmony_ci * 4. More than one devices are configured and period of the new 1898c2ecf20Sopenharmony_ci * requestis the same as the current period. 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_ci if (!ncfg || 1928c2ecf20Sopenharmony_ci ((ncfg == 1) && (pwm->hwpwm == cur->hwpwm)) || 1938c2ecf20Sopenharmony_ci ((ncfg == 1) && (pwm->hwpwm != cur->hwpwm) && period_same) || 1948c2ecf20Sopenharmony_ci ((ncfg > 1) && period_same)) { 1958c2ecf20Sopenharmony_ci /* Enable clock before writing to PWM registers. */ 1968c2ecf20Sopenharmony_ci ret = clk_enable(pc->pwm_clk); 1978c2ecf20Sopenharmony_ci if (ret) 1988c2ecf20Sopenharmony_ci return ret; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci ret = clk_enable(pc->cpt_clk); 2018c2ecf20Sopenharmony_ci if (ret) 2028c2ecf20Sopenharmony_ci return ret; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (!period_same) { 2058c2ecf20Sopenharmony_ci ret = sti_pwm_get_prescale(pc, period_ns, &prescale); 2068c2ecf20Sopenharmony_ci if (ret) 2078c2ecf20Sopenharmony_ci goto clk_dis; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci value = prescale & PWM_PRESCALE_LOW_MASK; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci ret = regmap_field_write(pc->prescale_low, value); 2128c2ecf20Sopenharmony_ci if (ret) 2138c2ecf20Sopenharmony_ci goto clk_dis; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci value = (prescale & PWM_PRESCALE_HIGH_MASK) >> 4; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci ret = regmap_field_write(pc->prescale_high, value); 2188c2ecf20Sopenharmony_ci if (ret) 2198c2ecf20Sopenharmony_ci goto clk_dis; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* 2238c2ecf20Sopenharmony_ci * When PWMVal == 0, PWM pulse = 1 local clock cycle. 2248c2ecf20Sopenharmony_ci * When PWMVal == max_pwm_count, 2258c2ecf20Sopenharmony_ci * PWM pulse = (max_pwm_count + 1) local cycles, 2268c2ecf20Sopenharmony_ci * that is continuous pulse: signal never goes low. 2278c2ecf20Sopenharmony_ci */ 2288c2ecf20Sopenharmony_ci value = cdata->max_pwm_cnt * duty_ns / period_ns; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci ret = regmap_write(pc->regmap, PWM_OUT_VAL(pwm->hwpwm), value); 2318c2ecf20Sopenharmony_ci if (ret) 2328c2ecf20Sopenharmony_ci goto clk_dis; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci ret = regmap_field_write(pc->pwm_cpt_int_en, 0); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci set_bit(pwm->hwpwm, &pc->configured); 2378c2ecf20Sopenharmony_ci pc->cur = pwm; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci dev_dbg(dev, "prescale:%u, period:%i, duty:%i, value:%u\n", 2408c2ecf20Sopenharmony_ci prescale, period_ns, duty_ns, value); 2418c2ecf20Sopenharmony_ci } else { 2428c2ecf20Sopenharmony_ci return -EINVAL; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ciclk_dis: 2468c2ecf20Sopenharmony_ci clk_disable(pc->pwm_clk); 2478c2ecf20Sopenharmony_ci clk_disable(pc->cpt_clk); 2488c2ecf20Sopenharmony_ci return ret; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic int sti_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct sti_pwm_chip *pc = to_sti_pwmchip(chip); 2548c2ecf20Sopenharmony_ci struct device *dev = pc->dev; 2558c2ecf20Sopenharmony_ci int ret = 0; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* 2588c2ecf20Sopenharmony_ci * Since we have a common enable for all PWM devices, do not enable if 2598c2ecf20Sopenharmony_ci * already enabled. 2608c2ecf20Sopenharmony_ci */ 2618c2ecf20Sopenharmony_ci mutex_lock(&pc->sti_pwm_lock); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (!pc->en_count) { 2648c2ecf20Sopenharmony_ci ret = clk_enable(pc->pwm_clk); 2658c2ecf20Sopenharmony_ci if (ret) 2668c2ecf20Sopenharmony_ci goto out; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci ret = clk_enable(pc->cpt_clk); 2698c2ecf20Sopenharmony_ci if (ret) 2708c2ecf20Sopenharmony_ci goto out; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci ret = regmap_field_write(pc->pwm_out_en, 1); 2738c2ecf20Sopenharmony_ci if (ret) { 2748c2ecf20Sopenharmony_ci dev_err(dev, "failed to enable PWM device %u: %d\n", 2758c2ecf20Sopenharmony_ci pwm->hwpwm, ret); 2768c2ecf20Sopenharmony_ci goto out; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci pc->en_count++; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ciout: 2838c2ecf20Sopenharmony_ci mutex_unlock(&pc->sti_pwm_lock); 2848c2ecf20Sopenharmony_ci return ret; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic void sti_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct sti_pwm_chip *pc = to_sti_pwmchip(chip); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci mutex_lock(&pc->sti_pwm_lock); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (--pc->en_count) { 2948c2ecf20Sopenharmony_ci mutex_unlock(&pc->sti_pwm_lock); 2958c2ecf20Sopenharmony_ci return; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci regmap_field_write(pc->pwm_out_en, 0); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci clk_disable(pc->pwm_clk); 3018c2ecf20Sopenharmony_ci clk_disable(pc->cpt_clk); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci mutex_unlock(&pc->sti_pwm_lock); 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic void sti_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct sti_pwm_chip *pc = to_sti_pwmchip(chip); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci clear_bit(pwm->hwpwm, &pc->configured); 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic int sti_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm, 3148c2ecf20Sopenharmony_ci struct pwm_capture *result, unsigned long timeout) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct sti_pwm_chip *pc = to_sti_pwmchip(chip); 3178c2ecf20Sopenharmony_ci struct sti_pwm_compat_data *cdata = pc->cdata; 3188c2ecf20Sopenharmony_ci struct sti_cpt_ddata *ddata = &cdata->ddata[pwm->hwpwm]; 3198c2ecf20Sopenharmony_ci struct device *dev = pc->dev; 3208c2ecf20Sopenharmony_ci unsigned int effective_ticks; 3218c2ecf20Sopenharmony_ci unsigned long long high, low; 3228c2ecf20Sopenharmony_ci int ret; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (pwm->hwpwm >= cdata->cpt_num_devs) { 3258c2ecf20Sopenharmony_ci dev_err(dev, "device %u is not valid\n", pwm->hwpwm); 3268c2ecf20Sopenharmony_ci return -EINVAL; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci mutex_lock(&ddata->lock); 3308c2ecf20Sopenharmony_ci ddata->index = 0; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* Prepare capture measurement */ 3338c2ecf20Sopenharmony_ci regmap_write(pc->regmap, PWM_CPT_EDGE(pwm->hwpwm), CPT_EDGE_RISING); 3348c2ecf20Sopenharmony_ci regmap_field_write(pc->pwm_cpt_int_en, BIT(pwm->hwpwm)); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* Enable capture */ 3378c2ecf20Sopenharmony_ci ret = regmap_field_write(pc->pwm_cpt_en, 1); 3388c2ecf20Sopenharmony_ci if (ret) { 3398c2ecf20Sopenharmony_ci dev_err(dev, "failed to enable PWM capture %u: %d\n", 3408c2ecf20Sopenharmony_ci pwm->hwpwm, ret); 3418c2ecf20Sopenharmony_ci goto out; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci ret = wait_event_interruptible_timeout(ddata->wait, ddata->index > 1, 3458c2ecf20Sopenharmony_ci msecs_to_jiffies(timeout)); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci regmap_write(pc->regmap, PWM_CPT_EDGE(pwm->hwpwm), CPT_EDGE_DISABLED); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (ret == -ERESTARTSYS) 3508c2ecf20Sopenharmony_ci goto out; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci switch (ddata->index) { 3538c2ecf20Sopenharmony_ci case 0: 3548c2ecf20Sopenharmony_ci case 1: 3558c2ecf20Sopenharmony_ci /* 3568c2ecf20Sopenharmony_ci * Getting here could mean: 3578c2ecf20Sopenharmony_ci * - input signal is constant of less than 1 Hz 3588c2ecf20Sopenharmony_ci * - there is no input signal at all 3598c2ecf20Sopenharmony_ci * 3608c2ecf20Sopenharmony_ci * In such case the frequency is rounded down to 0 3618c2ecf20Sopenharmony_ci */ 3628c2ecf20Sopenharmony_ci result->period = 0; 3638c2ecf20Sopenharmony_ci result->duty_cycle = 0; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci break; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci case 2: 3688c2ecf20Sopenharmony_ci /* We have everying we need */ 3698c2ecf20Sopenharmony_ci high = ddata->snapshot[1] - ddata->snapshot[0]; 3708c2ecf20Sopenharmony_ci low = ddata->snapshot[2] - ddata->snapshot[1]; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci effective_ticks = clk_get_rate(pc->cpt_clk); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci result->period = (high + low) * NSEC_PER_SEC; 3758c2ecf20Sopenharmony_ci result->period /= effective_ticks; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci result->duty_cycle = high * NSEC_PER_SEC; 3788c2ecf20Sopenharmony_ci result->duty_cycle /= effective_ticks; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci default: 3838c2ecf20Sopenharmony_ci dev_err(dev, "internal error\n"); 3848c2ecf20Sopenharmony_ci break; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ciout: 3888c2ecf20Sopenharmony_ci /* Disable capture */ 3898c2ecf20Sopenharmony_ci regmap_field_write(pc->pwm_cpt_en, 0); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci mutex_unlock(&ddata->lock); 3928c2ecf20Sopenharmony_ci return ret; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic const struct pwm_ops sti_pwm_ops = { 3968c2ecf20Sopenharmony_ci .capture = sti_pwm_capture, 3978c2ecf20Sopenharmony_ci .config = sti_pwm_config, 3988c2ecf20Sopenharmony_ci .enable = sti_pwm_enable, 3998c2ecf20Sopenharmony_ci .disable = sti_pwm_disable, 4008c2ecf20Sopenharmony_ci .free = sti_pwm_free, 4018c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4028c2ecf20Sopenharmony_ci}; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic irqreturn_t sti_pwm_interrupt(int irq, void *data) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci struct sti_pwm_chip *pc = data; 4078c2ecf20Sopenharmony_ci struct device *dev = pc->dev; 4088c2ecf20Sopenharmony_ci struct sti_cpt_ddata *ddata; 4098c2ecf20Sopenharmony_ci int devicenum; 4108c2ecf20Sopenharmony_ci unsigned int cpt_int_stat; 4118c2ecf20Sopenharmony_ci unsigned int reg; 4128c2ecf20Sopenharmony_ci int ret = IRQ_NONE; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci ret = regmap_field_read(pc->pwm_cpt_int_stat, &cpt_int_stat); 4158c2ecf20Sopenharmony_ci if (ret) 4168c2ecf20Sopenharmony_ci return ret; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci while (cpt_int_stat) { 4198c2ecf20Sopenharmony_ci devicenum = ffs(cpt_int_stat) - 1; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci ddata = &pc->cdata->ddata[devicenum]; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* 4248c2ecf20Sopenharmony_ci * Capture input: 4258c2ecf20Sopenharmony_ci * _______ _______ 4268c2ecf20Sopenharmony_ci * | | | | 4278c2ecf20Sopenharmony_ci * __| |_________________| |________ 4288c2ecf20Sopenharmony_ci * ^0 ^1 ^2 4298c2ecf20Sopenharmony_ci * 4308c2ecf20Sopenharmony_ci * Capture start by the first available rising edge. When a 4318c2ecf20Sopenharmony_ci * capture event occurs, capture value (CPT_VALx) is stored, 4328c2ecf20Sopenharmony_ci * index incremented, capture edge changed. 4338c2ecf20Sopenharmony_ci * 4348c2ecf20Sopenharmony_ci * After the capture, if the index > 1, we have collected the 4358c2ecf20Sopenharmony_ci * necessary data so we signal the thread waiting for it and 4368c2ecf20Sopenharmony_ci * disable the capture by setting capture edge to none 4378c2ecf20Sopenharmony_ci */ 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci regmap_read(pc->regmap, 4408c2ecf20Sopenharmony_ci PWM_CPT_VAL(devicenum), 4418c2ecf20Sopenharmony_ci &ddata->snapshot[ddata->index]); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci switch (ddata->index) { 4448c2ecf20Sopenharmony_ci case 0: 4458c2ecf20Sopenharmony_ci case 1: 4468c2ecf20Sopenharmony_ci regmap_read(pc->regmap, PWM_CPT_EDGE(devicenum), ®); 4478c2ecf20Sopenharmony_ci reg ^= PWM_CPT_EDGE_MASK; 4488c2ecf20Sopenharmony_ci regmap_write(pc->regmap, PWM_CPT_EDGE(devicenum), reg); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci ddata->index++; 4518c2ecf20Sopenharmony_ci break; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci case 2: 4548c2ecf20Sopenharmony_ci regmap_write(pc->regmap, 4558c2ecf20Sopenharmony_ci PWM_CPT_EDGE(devicenum), 4568c2ecf20Sopenharmony_ci CPT_EDGE_DISABLED); 4578c2ecf20Sopenharmony_ci wake_up(&ddata->wait); 4588c2ecf20Sopenharmony_ci break; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci default: 4618c2ecf20Sopenharmony_ci dev_err(dev, "Internal error\n"); 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci cpt_int_stat &= ~BIT_MASK(devicenum); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci /* Just ACK everything */ 4708c2ecf20Sopenharmony_ci regmap_write(pc->regmap, PWM_INT_ACK, PWM_INT_ACK_MASK); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci return ret; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic int sti_pwm_probe_dt(struct sti_pwm_chip *pc) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct device *dev = pc->dev; 4788c2ecf20Sopenharmony_ci const struct reg_field *reg_fields; 4798c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 4808c2ecf20Sopenharmony_ci struct sti_pwm_compat_data *cdata = pc->cdata; 4818c2ecf20Sopenharmony_ci u32 num_devs; 4828c2ecf20Sopenharmony_ci int ret; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "st,pwm-num-chan", &num_devs); 4858c2ecf20Sopenharmony_ci if (!ret) 4868c2ecf20Sopenharmony_ci cdata->pwm_num_devs = num_devs; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "st,capture-num-chan", &num_devs); 4898c2ecf20Sopenharmony_ci if (!ret) 4908c2ecf20Sopenharmony_ci cdata->cpt_num_devs = num_devs; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci if (!cdata->pwm_num_devs && !cdata->cpt_num_devs) { 4938c2ecf20Sopenharmony_ci dev_err(dev, "No channels configured\n"); 4948c2ecf20Sopenharmony_ci return -EINVAL; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci reg_fields = cdata->reg_fields; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci pc->prescale_low = devm_regmap_field_alloc(dev, pc->regmap, 5008c2ecf20Sopenharmony_ci reg_fields[PWMCLK_PRESCALE_LOW]); 5018c2ecf20Sopenharmony_ci if (IS_ERR(pc->prescale_low)) 5028c2ecf20Sopenharmony_ci return PTR_ERR(pc->prescale_low); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci pc->prescale_high = devm_regmap_field_alloc(dev, pc->regmap, 5058c2ecf20Sopenharmony_ci reg_fields[PWMCLK_PRESCALE_HIGH]); 5068c2ecf20Sopenharmony_ci if (IS_ERR(pc->prescale_high)) 5078c2ecf20Sopenharmony_ci return PTR_ERR(pc->prescale_high); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci pc->pwm_out_en = devm_regmap_field_alloc(dev, pc->regmap, 5118c2ecf20Sopenharmony_ci reg_fields[PWM_OUT_EN]); 5128c2ecf20Sopenharmony_ci if (IS_ERR(pc->pwm_out_en)) 5138c2ecf20Sopenharmony_ci return PTR_ERR(pc->pwm_out_en); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci pc->pwm_cpt_en = devm_regmap_field_alloc(dev, pc->regmap, 5168c2ecf20Sopenharmony_ci reg_fields[PWM_CPT_EN]); 5178c2ecf20Sopenharmony_ci if (IS_ERR(pc->pwm_cpt_en)) 5188c2ecf20Sopenharmony_ci return PTR_ERR(pc->pwm_cpt_en); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci pc->pwm_cpt_int_en = devm_regmap_field_alloc(dev, pc->regmap, 5218c2ecf20Sopenharmony_ci reg_fields[PWM_CPT_INT_EN]); 5228c2ecf20Sopenharmony_ci if (IS_ERR(pc->pwm_cpt_int_en)) 5238c2ecf20Sopenharmony_ci return PTR_ERR(pc->pwm_cpt_int_en); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci pc->pwm_cpt_int_stat = devm_regmap_field_alloc(dev, pc->regmap, 5268c2ecf20Sopenharmony_ci reg_fields[PWM_CPT_INT_STAT]); 5278c2ecf20Sopenharmony_ci if (PTR_ERR_OR_ZERO(pc->pwm_cpt_int_stat)) 5288c2ecf20Sopenharmony_ci return PTR_ERR(pc->pwm_cpt_int_stat); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci return 0; 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic const struct regmap_config sti_pwm_regmap_config = { 5348c2ecf20Sopenharmony_ci .reg_bits = 32, 5358c2ecf20Sopenharmony_ci .val_bits = 32, 5368c2ecf20Sopenharmony_ci .reg_stride = 4, 5378c2ecf20Sopenharmony_ci}; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic int sti_pwm_probe(struct platform_device *pdev) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 5428c2ecf20Sopenharmony_ci struct sti_pwm_compat_data *cdata; 5438c2ecf20Sopenharmony_ci struct sti_pwm_chip *pc; 5448c2ecf20Sopenharmony_ci struct resource *res; 5458c2ecf20Sopenharmony_ci unsigned int i; 5468c2ecf20Sopenharmony_ci int irq, ret; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL); 5498c2ecf20Sopenharmony_ci if (!pc) 5508c2ecf20Sopenharmony_ci return -ENOMEM; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci cdata = devm_kzalloc(dev, sizeof(*cdata), GFP_KERNEL); 5538c2ecf20Sopenharmony_ci if (!cdata) 5548c2ecf20Sopenharmony_ci return -ENOMEM; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci pc->mmio = devm_ioremap_resource(dev, res); 5598c2ecf20Sopenharmony_ci if (IS_ERR(pc->mmio)) 5608c2ecf20Sopenharmony_ci return PTR_ERR(pc->mmio); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci pc->regmap = devm_regmap_init_mmio(dev, pc->mmio, 5638c2ecf20Sopenharmony_ci &sti_pwm_regmap_config); 5648c2ecf20Sopenharmony_ci if (IS_ERR(pc->regmap)) 5658c2ecf20Sopenharmony_ci return PTR_ERR(pc->regmap); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 5688c2ecf20Sopenharmony_ci if (irq < 0) 5698c2ecf20Sopenharmony_ci return irq; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, sti_pwm_interrupt, 0, 5728c2ecf20Sopenharmony_ci pdev->name, pc); 5738c2ecf20Sopenharmony_ci if (ret < 0) { 5748c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to request IRQ\n"); 5758c2ecf20Sopenharmony_ci return ret; 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci /* 5798c2ecf20Sopenharmony_ci * Setup PWM data with default values: some values could be replaced 5808c2ecf20Sopenharmony_ci * with specific ones provided from Device Tree. 5818c2ecf20Sopenharmony_ci */ 5828c2ecf20Sopenharmony_ci cdata->reg_fields = sti_pwm_regfields; 5838c2ecf20Sopenharmony_ci cdata->max_prescale = 0xff; 5848c2ecf20Sopenharmony_ci cdata->max_pwm_cnt = 255; 5858c2ecf20Sopenharmony_ci cdata->pwm_num_devs = 0; 5868c2ecf20Sopenharmony_ci cdata->cpt_num_devs = 0; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci pc->cdata = cdata; 5898c2ecf20Sopenharmony_ci pc->dev = dev; 5908c2ecf20Sopenharmony_ci pc->en_count = 0; 5918c2ecf20Sopenharmony_ci mutex_init(&pc->sti_pwm_lock); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci ret = sti_pwm_probe_dt(pc); 5948c2ecf20Sopenharmony_ci if (ret) 5958c2ecf20Sopenharmony_ci return ret; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (cdata->pwm_num_devs) { 5988c2ecf20Sopenharmony_ci pc->pwm_clk = of_clk_get_by_name(dev->of_node, "pwm"); 5998c2ecf20Sopenharmony_ci if (IS_ERR(pc->pwm_clk)) { 6008c2ecf20Sopenharmony_ci dev_err(dev, "failed to get PWM clock\n"); 6018c2ecf20Sopenharmony_ci return PTR_ERR(pc->pwm_clk); 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci ret = clk_prepare(pc->pwm_clk); 6058c2ecf20Sopenharmony_ci if (ret) { 6068c2ecf20Sopenharmony_ci dev_err(dev, "failed to prepare clock\n"); 6078c2ecf20Sopenharmony_ci return ret; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (cdata->cpt_num_devs) { 6128c2ecf20Sopenharmony_ci pc->cpt_clk = of_clk_get_by_name(dev->of_node, "capture"); 6138c2ecf20Sopenharmony_ci if (IS_ERR(pc->cpt_clk)) { 6148c2ecf20Sopenharmony_ci dev_err(dev, "failed to get PWM capture clock\n"); 6158c2ecf20Sopenharmony_ci return PTR_ERR(pc->cpt_clk); 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci ret = clk_prepare(pc->cpt_clk); 6198c2ecf20Sopenharmony_ci if (ret) { 6208c2ecf20Sopenharmony_ci dev_err(dev, "failed to prepare clock\n"); 6218c2ecf20Sopenharmony_ci return ret; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci cdata->ddata = devm_kzalloc(dev, cdata->cpt_num_devs * sizeof(*cdata->ddata), GFP_KERNEL); 6258c2ecf20Sopenharmony_ci if (!cdata->ddata) 6268c2ecf20Sopenharmony_ci return -ENOMEM; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci pc->chip.dev = dev; 6308c2ecf20Sopenharmony_ci pc->chip.ops = &sti_pwm_ops; 6318c2ecf20Sopenharmony_ci pc->chip.base = -1; 6328c2ecf20Sopenharmony_ci pc->chip.npwm = pc->cdata->pwm_num_devs; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci for (i = 0; i < cdata->cpt_num_devs; i++) { 6358c2ecf20Sopenharmony_ci struct sti_cpt_ddata *ddata = &cdata->ddata[i]; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci init_waitqueue_head(&ddata->wait); 6388c2ecf20Sopenharmony_ci mutex_init(&ddata->lock); 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci ret = pwmchip_add(&pc->chip); 6428c2ecf20Sopenharmony_ci if (ret < 0) { 6438c2ecf20Sopenharmony_ci clk_unprepare(pc->pwm_clk); 6448c2ecf20Sopenharmony_ci clk_unprepare(pc->cpt_clk); 6458c2ecf20Sopenharmony_ci return ret; 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, pc); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci return 0; 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic int sti_pwm_remove(struct platform_device *pdev) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci struct sti_pwm_chip *pc = platform_get_drvdata(pdev); 6568c2ecf20Sopenharmony_ci unsigned int i; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci for (i = 0; i < pc->cdata->pwm_num_devs; i++) 6598c2ecf20Sopenharmony_ci pwm_disable(&pc->chip.pwms[i]); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci clk_unprepare(pc->pwm_clk); 6628c2ecf20Sopenharmony_ci clk_unprepare(pc->cpt_clk); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci return pwmchip_remove(&pc->chip); 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_cistatic const struct of_device_id sti_pwm_of_match[] = { 6688c2ecf20Sopenharmony_ci { .compatible = "st,sti-pwm", }, 6698c2ecf20Sopenharmony_ci { /* sentinel */ } 6708c2ecf20Sopenharmony_ci}; 6718c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, sti_pwm_of_match); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_cistatic struct platform_driver sti_pwm_driver = { 6748c2ecf20Sopenharmony_ci .driver = { 6758c2ecf20Sopenharmony_ci .name = "sti-pwm", 6768c2ecf20Sopenharmony_ci .of_match_table = sti_pwm_of_match, 6778c2ecf20Sopenharmony_ci }, 6788c2ecf20Sopenharmony_ci .probe = sti_pwm_probe, 6798c2ecf20Sopenharmony_ci .remove = sti_pwm_remove, 6808c2ecf20Sopenharmony_ci}; 6818c2ecf20Sopenharmony_cimodule_platform_driver(sti_pwm_driver); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ajit Pal Singh <ajitpal.singh@st.com>"); 6848c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("STMicroelectronics ST PWM driver"); 6858c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 686