18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2015 Neil Armstrong <narmstrong@baylibre.com> 48c2ecf20Sopenharmony_ci * Copyright (c) 2014 Joachim Eastwood <manabian@gmail.com> 58c2ecf20Sopenharmony_ci * Copyright (c) 2012 NeilBrown <neilb@suse.de> 68c2ecf20Sopenharmony_ci * Heavily based on earlier code which is: 78c2ecf20Sopenharmony_ci * Copyright (c) 2010 Grant Erickson <marathon96@gmail.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Also based on pwm-samsung.c 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Description: 128c2ecf20Sopenharmony_ci * This file is the core OMAP support for the generic, Linux 138c2ecf20Sopenharmony_ci * PWM driver / controller, using the OMAP's dual-mode timers 148c2ecf20Sopenharmony_ci * with a timer counter that goes up. When it overflows it gets 158c2ecf20Sopenharmony_ci * reloaded with the load value and the pwm output goes up. 168c2ecf20Sopenharmony_ci * When counter matches with match register, the output goes down. 178c2ecf20Sopenharmony_ci * Reference Manual: https://www.ti.com/lit/ug/spruh73q/spruh73q.pdf 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * Limitations: 208c2ecf20Sopenharmony_ci * - When PWM is stopped, timer counter gets stopped immediately. This 218c2ecf20Sopenharmony_ci * doesn't allow the current PWM period to complete and stops abruptly. 228c2ecf20Sopenharmony_ci * - When PWM is running and changing both duty cycle and period, 238c2ecf20Sopenharmony_ci * we cannot prevent in software that the output might produce 248c2ecf20Sopenharmony_ci * a period with mixed settings. Especially when period/duty_cyle 258c2ecf20Sopenharmony_ci * is updated while the pwm pin is high, current pwm period/duty_cycle 268c2ecf20Sopenharmony_ci * can get updated as below based on the current timer counter: 278c2ecf20Sopenharmony_ci * - period for current cycle = current_period + new period 288c2ecf20Sopenharmony_ci * - duty_cycle for current period = current period + new duty_cycle. 298c2ecf20Sopenharmony_ci * - PWM OMAP DM timer cannot change the polarity when pwm is active. When 308c2ecf20Sopenharmony_ci * user requests a change in polarity when in active state: 318c2ecf20Sopenharmony_ci * - PWM is stopped abruptly(without completing the current cycle) 328c2ecf20Sopenharmony_ci * - Polarity is changed 338c2ecf20Sopenharmony_ci * - A fresh cycle is started. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include <linux/clk.h> 378c2ecf20Sopenharmony_ci#include <linux/err.h> 388c2ecf20Sopenharmony_ci#include <linux/kernel.h> 398c2ecf20Sopenharmony_ci#include <linux/module.h> 408c2ecf20Sopenharmony_ci#include <linux/mutex.h> 418c2ecf20Sopenharmony_ci#include <linux/of.h> 428c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 438c2ecf20Sopenharmony_ci#include <clocksource/timer-ti-dm.h> 448c2ecf20Sopenharmony_ci#include <linux/platform_data/dmtimer-omap.h> 458c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 468c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 478c2ecf20Sopenharmony_ci#include <linux/pwm.h> 488c2ecf20Sopenharmony_ci#include <linux/slab.h> 498c2ecf20Sopenharmony_ci#include <linux/time.h> 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define DM_TIMER_LOAD_MIN 0xfffffffe 528c2ecf20Sopenharmony_ci#define DM_TIMER_MAX 0xffffffff 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/** 558c2ecf20Sopenharmony_ci * struct pwm_omap_dmtimer_chip - Structure representing a pwm chip 568c2ecf20Sopenharmony_ci * corresponding to omap dmtimer. 578c2ecf20Sopenharmony_ci * @chip: PWM chip structure representing PWM controller 588c2ecf20Sopenharmony_ci * @mutex: Mutex to protect pwm apply state 598c2ecf20Sopenharmony_ci * @dm_timer: Pointer to omap dm timer. 608c2ecf20Sopenharmony_ci * @pdata: Pointer to omap dm timer ops. 618c2ecf20Sopenharmony_ci * @dm_timer_pdev: Pointer to omap dm timer platform device 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_cistruct pwm_omap_dmtimer_chip { 648c2ecf20Sopenharmony_ci struct pwm_chip chip; 658c2ecf20Sopenharmony_ci /* Mutex to protect pwm apply state */ 668c2ecf20Sopenharmony_ci struct mutex mutex; 678c2ecf20Sopenharmony_ci struct omap_dm_timer *dm_timer; 688c2ecf20Sopenharmony_ci const struct omap_dm_timer_ops *pdata; 698c2ecf20Sopenharmony_ci struct platform_device *dm_timer_pdev; 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic inline struct pwm_omap_dmtimer_chip * 738c2ecf20Sopenharmony_cito_pwm_omap_dmtimer_chip(struct pwm_chip *chip) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci return container_of(chip, struct pwm_omap_dmtimer_chip, chip); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/** 798c2ecf20Sopenharmony_ci * pwm_omap_dmtimer_get_clock_cycles() - Get clock cycles in a time frame 808c2ecf20Sopenharmony_ci * @clk_rate: pwm timer clock rate 818c2ecf20Sopenharmony_ci * @ns: time frame in nano seconds. 828c2ecf20Sopenharmony_ci * 838c2ecf20Sopenharmony_ci * Return number of clock cycles in a given period(ins ns). 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_cistatic u32 pwm_omap_dmtimer_get_clock_cycles(unsigned long clk_rate, int ns) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci return DIV_ROUND_CLOSEST_ULL((u64)clk_rate * ns, NSEC_PER_SEC); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/** 918c2ecf20Sopenharmony_ci * pwm_omap_dmtimer_start() - Start the pwm omap dm timer in pwm mode 928c2ecf20Sopenharmony_ci * @omap: Pointer to pwm omap dm timer chip 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_cistatic void pwm_omap_dmtimer_start(struct pwm_omap_dmtimer_chip *omap) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci /* 978c2ecf20Sopenharmony_ci * According to OMAP 4 TRM section 22.2.4.10 the counter should be 988c2ecf20Sopenharmony_ci * started at 0xFFFFFFFE when overflow and match is used to ensure 998c2ecf20Sopenharmony_ci * that the PWM line is toggled on the first event. 1008c2ecf20Sopenharmony_ci * 1018c2ecf20Sopenharmony_ci * Note that omap_dm_timer_enable/disable is for register access and 1028c2ecf20Sopenharmony_ci * not the timer counter itself. 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_ci omap->pdata->enable(omap->dm_timer); 1058c2ecf20Sopenharmony_ci omap->pdata->write_counter(omap->dm_timer, DM_TIMER_LOAD_MIN); 1068c2ecf20Sopenharmony_ci omap->pdata->disable(omap->dm_timer); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci omap->pdata->start(omap->dm_timer); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/** 1128c2ecf20Sopenharmony_ci * pwm_omap_dmtimer_is_enabled() - Detect if the pwm is enabled. 1138c2ecf20Sopenharmony_ci * @omap: Pointer to pwm omap dm timer chip 1148c2ecf20Sopenharmony_ci * 1158c2ecf20Sopenharmony_ci * Return true if pwm is enabled else false. 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_cistatic bool pwm_omap_dmtimer_is_enabled(struct pwm_omap_dmtimer_chip *omap) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci u32 status; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci status = omap->pdata->get_pwm_status(omap->dm_timer); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return !!(status & OMAP_TIMER_CTRL_ST); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/** 1278c2ecf20Sopenharmony_ci * pwm_omap_dmtimer_polarity() - Detect the polarity of pwm. 1288c2ecf20Sopenharmony_ci * @omap: Pointer to pwm omap dm timer chip 1298c2ecf20Sopenharmony_ci * 1308c2ecf20Sopenharmony_ci * Return the polarity of pwm. 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_cistatic int pwm_omap_dmtimer_polarity(struct pwm_omap_dmtimer_chip *omap) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci u32 status; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci status = omap->pdata->get_pwm_status(omap->dm_timer); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return !!(status & OMAP_TIMER_CTRL_SCPWM); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci/** 1428c2ecf20Sopenharmony_ci * pwm_omap_dmtimer_config() - Update the configuration of pwm omap dm timer 1438c2ecf20Sopenharmony_ci * @chip: Pointer to PWM controller 1448c2ecf20Sopenharmony_ci * @pwm: Pointer to PWM channel 1458c2ecf20Sopenharmony_ci * @duty_ns: New duty cycle in nano seconds 1468c2ecf20Sopenharmony_ci * @period_ns: New period in nano seconds 1478c2ecf20Sopenharmony_ci * 1488c2ecf20Sopenharmony_ci * Return 0 if successfully changed the period/duty_cycle else appropriate 1498c2ecf20Sopenharmony_ci * error. 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_cistatic int pwm_omap_dmtimer_config(struct pwm_chip *chip, 1528c2ecf20Sopenharmony_ci struct pwm_device *pwm, 1538c2ecf20Sopenharmony_ci int duty_ns, int period_ns) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip); 1568c2ecf20Sopenharmony_ci u32 period_cycles, duty_cycles; 1578c2ecf20Sopenharmony_ci u32 load_value, match_value; 1588c2ecf20Sopenharmony_ci unsigned long clk_rate; 1598c2ecf20Sopenharmony_ci struct clk *fclk; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci dev_dbg(chip->dev, "requested duty cycle: %d ns, period: %d ns\n", 1628c2ecf20Sopenharmony_ci duty_ns, period_ns); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (duty_ns == pwm_get_duty_cycle(pwm) && 1658c2ecf20Sopenharmony_ci period_ns == pwm_get_period(pwm)) 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci fclk = omap->pdata->get_fclk(omap->dm_timer); 1698c2ecf20Sopenharmony_ci if (!fclk) { 1708c2ecf20Sopenharmony_ci dev_err(chip->dev, "invalid pmtimer fclk\n"); 1718c2ecf20Sopenharmony_ci return -EINVAL; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci clk_rate = clk_get_rate(fclk); 1758c2ecf20Sopenharmony_ci if (!clk_rate) { 1768c2ecf20Sopenharmony_ci dev_err(chip->dev, "invalid pmtimer fclk rate\n"); 1778c2ecf20Sopenharmony_ci return -EINVAL; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci dev_dbg(chip->dev, "clk rate: %luHz\n", clk_rate); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* 1838c2ecf20Sopenharmony_ci * Calculate the appropriate load and match values based on the 1848c2ecf20Sopenharmony_ci * specified period and duty cycle. The load value determines the 1858c2ecf20Sopenharmony_ci * period time and the match value determines the duty time. 1868c2ecf20Sopenharmony_ci * 1878c2ecf20Sopenharmony_ci * The period lasts for (DM_TIMER_MAX-load_value+1) clock cycles. 1888c2ecf20Sopenharmony_ci * Similarly, the active time lasts (match_value-load_value+1) cycles. 1898c2ecf20Sopenharmony_ci * The non-active time is the remainder: (DM_TIMER_MAX-match_value) 1908c2ecf20Sopenharmony_ci * clock cycles. 1918c2ecf20Sopenharmony_ci * 1928c2ecf20Sopenharmony_ci * NOTE: It is required that: load_value <= match_value < DM_TIMER_MAX 1938c2ecf20Sopenharmony_ci * 1948c2ecf20Sopenharmony_ci * References: 1958c2ecf20Sopenharmony_ci * OMAP4430/60/70 TRM sections 22.2.4.10 and 22.2.4.11 1968c2ecf20Sopenharmony_ci * AM335x Sitara TRM sections 20.1.3.5 and 20.1.3.6 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_ci period_cycles = pwm_omap_dmtimer_get_clock_cycles(clk_rate, period_ns); 1998c2ecf20Sopenharmony_ci duty_cycles = pwm_omap_dmtimer_get_clock_cycles(clk_rate, duty_ns); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (period_cycles < 2) { 2028c2ecf20Sopenharmony_ci dev_info(chip->dev, 2038c2ecf20Sopenharmony_ci "period %d ns too short for clock rate %lu Hz\n", 2048c2ecf20Sopenharmony_ci period_ns, clk_rate); 2058c2ecf20Sopenharmony_ci return -EINVAL; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (duty_cycles < 1) { 2098c2ecf20Sopenharmony_ci dev_dbg(chip->dev, 2108c2ecf20Sopenharmony_ci "duty cycle %d ns is too short for clock rate %lu Hz\n", 2118c2ecf20Sopenharmony_ci duty_ns, clk_rate); 2128c2ecf20Sopenharmony_ci dev_dbg(chip->dev, "using minimum of 1 clock cycle\n"); 2138c2ecf20Sopenharmony_ci duty_cycles = 1; 2148c2ecf20Sopenharmony_ci } else if (duty_cycles >= period_cycles) { 2158c2ecf20Sopenharmony_ci dev_dbg(chip->dev, 2168c2ecf20Sopenharmony_ci "duty cycle %d ns is too long for period %d ns at clock rate %lu Hz\n", 2178c2ecf20Sopenharmony_ci duty_ns, period_ns, clk_rate); 2188c2ecf20Sopenharmony_ci dev_dbg(chip->dev, "using maximum of 1 clock cycle less than period\n"); 2198c2ecf20Sopenharmony_ci duty_cycles = period_cycles - 1; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci dev_dbg(chip->dev, "effective duty cycle: %lld ns, period: %lld ns\n", 2238c2ecf20Sopenharmony_ci DIV_ROUND_CLOSEST_ULL((u64)NSEC_PER_SEC * duty_cycles, 2248c2ecf20Sopenharmony_ci clk_rate), 2258c2ecf20Sopenharmony_ci DIV_ROUND_CLOSEST_ULL((u64)NSEC_PER_SEC * period_cycles, 2268c2ecf20Sopenharmony_ci clk_rate)); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci load_value = (DM_TIMER_MAX - period_cycles) + 1; 2298c2ecf20Sopenharmony_ci match_value = load_value + duty_cycles - 1; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci omap->pdata->set_load(omap->dm_timer, load_value); 2328c2ecf20Sopenharmony_ci omap->pdata->set_match(omap->dm_timer, true, match_value); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci dev_dbg(chip->dev, "load value: %#08x (%d), match value: %#08x (%d)\n", 2358c2ecf20Sopenharmony_ci load_value, load_value, match_value, match_value); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci return 0; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci/** 2418c2ecf20Sopenharmony_ci * pwm_omap_dmtimer_set_polarity() - Changes the polarity of the pwm dm timer. 2428c2ecf20Sopenharmony_ci * @chip: Pointer to PWM controller 2438c2ecf20Sopenharmony_ci * @pwm: Pointer to PWM channel 2448c2ecf20Sopenharmony_ci * @polarity: New pwm polarity to be set 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_cistatic void pwm_omap_dmtimer_set_polarity(struct pwm_chip *chip, 2478c2ecf20Sopenharmony_ci struct pwm_device *pwm, 2488c2ecf20Sopenharmony_ci enum pwm_polarity polarity) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip); 2518c2ecf20Sopenharmony_ci bool enabled; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* Disable the PWM before changing the polarity. */ 2548c2ecf20Sopenharmony_ci enabled = pwm_omap_dmtimer_is_enabled(omap); 2558c2ecf20Sopenharmony_ci if (enabled) 2568c2ecf20Sopenharmony_ci omap->pdata->stop(omap->dm_timer); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci omap->pdata->set_pwm(omap->dm_timer, 2598c2ecf20Sopenharmony_ci polarity == PWM_POLARITY_INVERSED, 2608c2ecf20Sopenharmony_ci true, OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE, 2618c2ecf20Sopenharmony_ci true); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (enabled) 2648c2ecf20Sopenharmony_ci pwm_omap_dmtimer_start(omap); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci/** 2688c2ecf20Sopenharmony_ci * pwm_omap_dmtimer_apply() - Changes the state of the pwm omap dm timer. 2698c2ecf20Sopenharmony_ci * @chip: Pointer to PWM controller 2708c2ecf20Sopenharmony_ci * @pwm: Pointer to PWM channel 2718c2ecf20Sopenharmony_ci * @state: New state to apply 2728c2ecf20Sopenharmony_ci * 2738c2ecf20Sopenharmony_ci * Return 0 if successfully changed the state else appropriate error. 2748c2ecf20Sopenharmony_ci */ 2758c2ecf20Sopenharmony_cistatic int pwm_omap_dmtimer_apply(struct pwm_chip *chip, 2768c2ecf20Sopenharmony_ci struct pwm_device *pwm, 2778c2ecf20Sopenharmony_ci const struct pwm_state *state) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip); 2808c2ecf20Sopenharmony_ci int ret = 0; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci mutex_lock(&omap->mutex); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (pwm_omap_dmtimer_is_enabled(omap) && !state->enabled) { 2858c2ecf20Sopenharmony_ci omap->pdata->stop(omap->dm_timer); 2868c2ecf20Sopenharmony_ci goto unlock_mutex; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (pwm_omap_dmtimer_polarity(omap) != state->polarity) 2908c2ecf20Sopenharmony_ci pwm_omap_dmtimer_set_polarity(chip, pwm, state->polarity); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci ret = pwm_omap_dmtimer_config(chip, pwm, state->duty_cycle, 2938c2ecf20Sopenharmony_ci state->period); 2948c2ecf20Sopenharmony_ci if (ret) 2958c2ecf20Sopenharmony_ci goto unlock_mutex; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (!pwm_omap_dmtimer_is_enabled(omap) && state->enabled) { 2988c2ecf20Sopenharmony_ci omap->pdata->set_pwm(omap->dm_timer, 2998c2ecf20Sopenharmony_ci state->polarity == PWM_POLARITY_INVERSED, 3008c2ecf20Sopenharmony_ci true, 3018c2ecf20Sopenharmony_ci OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE, 3028c2ecf20Sopenharmony_ci true); 3038c2ecf20Sopenharmony_ci pwm_omap_dmtimer_start(omap); 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ciunlock_mutex: 3078c2ecf20Sopenharmony_ci mutex_unlock(&omap->mutex); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci return ret; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic const struct pwm_ops pwm_omap_dmtimer_ops = { 3138c2ecf20Sopenharmony_ci .apply = pwm_omap_dmtimer_apply, 3148c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3158c2ecf20Sopenharmony_ci}; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic int pwm_omap_dmtimer_probe(struct platform_device *pdev) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 3208c2ecf20Sopenharmony_ci struct dmtimer_platform_data *timer_pdata; 3218c2ecf20Sopenharmony_ci const struct omap_dm_timer_ops *pdata; 3228c2ecf20Sopenharmony_ci struct platform_device *timer_pdev; 3238c2ecf20Sopenharmony_ci struct pwm_omap_dmtimer_chip *omap; 3248c2ecf20Sopenharmony_ci struct omap_dm_timer *dm_timer; 3258c2ecf20Sopenharmony_ci struct device_node *timer; 3268c2ecf20Sopenharmony_ci int ret = 0; 3278c2ecf20Sopenharmony_ci u32 v; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci timer = of_parse_phandle(np, "ti,timers", 0); 3308c2ecf20Sopenharmony_ci if (!timer) 3318c2ecf20Sopenharmony_ci return -ENODEV; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci timer_pdev = of_find_device_by_node(timer); 3348c2ecf20Sopenharmony_ci if (!timer_pdev) { 3358c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to find Timer pdev\n"); 3368c2ecf20Sopenharmony_ci ret = -ENODEV; 3378c2ecf20Sopenharmony_ci goto err_find_timer_pdev; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci timer_pdata = dev_get_platdata(&timer_pdev->dev); 3418c2ecf20Sopenharmony_ci if (!timer_pdata) { 3428c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, 3438c2ecf20Sopenharmony_ci "dmtimer pdata structure NULL, deferring probe\n"); 3448c2ecf20Sopenharmony_ci ret = -EPROBE_DEFER; 3458c2ecf20Sopenharmony_ci goto err_platdata; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci pdata = timer_pdata->timer_ops; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (!pdata || !pdata->request_by_node || 3518c2ecf20Sopenharmony_ci !pdata->free || 3528c2ecf20Sopenharmony_ci !pdata->enable || 3538c2ecf20Sopenharmony_ci !pdata->disable || 3548c2ecf20Sopenharmony_ci !pdata->get_fclk || 3558c2ecf20Sopenharmony_ci !pdata->start || 3568c2ecf20Sopenharmony_ci !pdata->stop || 3578c2ecf20Sopenharmony_ci !pdata->set_load || 3588c2ecf20Sopenharmony_ci !pdata->set_match || 3598c2ecf20Sopenharmony_ci !pdata->set_pwm || 3608c2ecf20Sopenharmony_ci !pdata->get_pwm_status || 3618c2ecf20Sopenharmony_ci !pdata->set_prescaler || 3628c2ecf20Sopenharmony_ci !pdata->write_counter) { 3638c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Incomplete dmtimer pdata structure\n"); 3648c2ecf20Sopenharmony_ci ret = -EINVAL; 3658c2ecf20Sopenharmony_ci goto err_platdata; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (!of_get_property(timer, "ti,timer-pwm", NULL)) { 3698c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Missing ti,timer-pwm capability\n"); 3708c2ecf20Sopenharmony_ci ret = -ENODEV; 3718c2ecf20Sopenharmony_ci goto err_timer_property; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci dm_timer = pdata->request_by_node(timer); 3758c2ecf20Sopenharmony_ci if (!dm_timer) { 3768c2ecf20Sopenharmony_ci ret = -EPROBE_DEFER; 3778c2ecf20Sopenharmony_ci goto err_request_timer; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci omap = devm_kzalloc(&pdev->dev, sizeof(*omap), GFP_KERNEL); 3818c2ecf20Sopenharmony_ci if (!omap) { 3828c2ecf20Sopenharmony_ci ret = -ENOMEM; 3838c2ecf20Sopenharmony_ci goto err_alloc_omap; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci omap->pdata = pdata; 3878c2ecf20Sopenharmony_ci omap->dm_timer = dm_timer; 3888c2ecf20Sopenharmony_ci omap->dm_timer_pdev = timer_pdev; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* 3918c2ecf20Sopenharmony_ci * Ensure that the timer is stopped before we allow PWM core to call 3928c2ecf20Sopenharmony_ci * pwm_enable. 3938c2ecf20Sopenharmony_ci */ 3948c2ecf20Sopenharmony_ci if (pm_runtime_active(&omap->dm_timer_pdev->dev)) 3958c2ecf20Sopenharmony_ci omap->pdata->stop(omap->dm_timer); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (!of_property_read_u32(pdev->dev.of_node, "ti,prescaler", &v)) 3988c2ecf20Sopenharmony_ci omap->pdata->set_prescaler(omap->dm_timer, v); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* setup dmtimer clock source */ 4018c2ecf20Sopenharmony_ci if (!of_property_read_u32(pdev->dev.of_node, "ti,clock-source", &v)) 4028c2ecf20Sopenharmony_ci omap->pdata->set_source(omap->dm_timer, v); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci omap->chip.dev = &pdev->dev; 4058c2ecf20Sopenharmony_ci omap->chip.ops = &pwm_omap_dmtimer_ops; 4068c2ecf20Sopenharmony_ci omap->chip.base = -1; 4078c2ecf20Sopenharmony_ci omap->chip.npwm = 1; 4088c2ecf20Sopenharmony_ci omap->chip.of_xlate = of_pwm_xlate_with_flags; 4098c2ecf20Sopenharmony_ci omap->chip.of_pwm_n_cells = 3; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci mutex_init(&omap->mutex); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci ret = pwmchip_add(&omap->chip); 4148c2ecf20Sopenharmony_ci if (ret < 0) { 4158c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register PWM\n"); 4168c2ecf20Sopenharmony_ci goto err_pwmchip_add; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci of_node_put(timer); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, omap); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci return 0; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cierr_pwmchip_add: 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* 4288c2ecf20Sopenharmony_ci * *omap is allocated using devm_kzalloc, 4298c2ecf20Sopenharmony_ci * so no free necessary here 4308c2ecf20Sopenharmony_ci */ 4318c2ecf20Sopenharmony_cierr_alloc_omap: 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci pdata->free(dm_timer); 4348c2ecf20Sopenharmony_cierr_request_timer: 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cierr_timer_property: 4378c2ecf20Sopenharmony_cierr_platdata: 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci put_device(&timer_pdev->dev); 4408c2ecf20Sopenharmony_cierr_find_timer_pdev: 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci of_node_put(timer); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci return ret; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic int pwm_omap_dmtimer_remove(struct platform_device *pdev) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci struct pwm_omap_dmtimer_chip *omap = platform_get_drvdata(pdev); 4508c2ecf20Sopenharmony_ci int ret; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci ret = pwmchip_remove(&omap->chip); 4538c2ecf20Sopenharmony_ci if (ret) 4548c2ecf20Sopenharmony_ci return ret; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (pm_runtime_active(&omap->dm_timer_pdev->dev)) 4578c2ecf20Sopenharmony_ci omap->pdata->stop(omap->dm_timer); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci omap->pdata->free(omap->dm_timer); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci put_device(&omap->dm_timer_pdev->dev); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci mutex_destroy(&omap->mutex); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci return 0; 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic const struct of_device_id pwm_omap_dmtimer_of_match[] = { 4698c2ecf20Sopenharmony_ci {.compatible = "ti,omap-dmtimer-pwm"}, 4708c2ecf20Sopenharmony_ci {} 4718c2ecf20Sopenharmony_ci}; 4728c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, pwm_omap_dmtimer_of_match); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic struct platform_driver pwm_omap_dmtimer_driver = { 4758c2ecf20Sopenharmony_ci .driver = { 4768c2ecf20Sopenharmony_ci .name = "omap-dmtimer-pwm", 4778c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(pwm_omap_dmtimer_of_match), 4788c2ecf20Sopenharmony_ci }, 4798c2ecf20Sopenharmony_ci .probe = pwm_omap_dmtimer_probe, 4808c2ecf20Sopenharmony_ci .remove = pwm_omap_dmtimer_remove, 4818c2ecf20Sopenharmony_ci}; 4828c2ecf20Sopenharmony_cimodule_platform_driver(pwm_omap_dmtimer_driver); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ciMODULE_AUTHOR("Grant Erickson <marathon96@gmail.com>"); 4858c2ecf20Sopenharmony_ciMODULE_AUTHOR("NeilBrown <neilb@suse.de>"); 4868c2ecf20Sopenharmony_ciMODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 4878c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 4888c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OMAP PWM Driver using Dual-mode Timers"); 489