162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2015 Neil Armstrong <narmstrong@baylibre.com> 462306a36Sopenharmony_ci * Copyright (c) 2014 Joachim Eastwood <manabian@gmail.com> 562306a36Sopenharmony_ci * Copyright (c) 2012 NeilBrown <neilb@suse.de> 662306a36Sopenharmony_ci * Heavily based on earlier code which is: 762306a36Sopenharmony_ci * Copyright (c) 2010 Grant Erickson <marathon96@gmail.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Also based on pwm-samsung.c 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Description: 1262306a36Sopenharmony_ci * This file is the core OMAP support for the generic, Linux 1362306a36Sopenharmony_ci * PWM driver / controller, using the OMAP's dual-mode timers 1462306a36Sopenharmony_ci * with a timer counter that goes up. When it overflows it gets 1562306a36Sopenharmony_ci * reloaded with the load value and the pwm output goes up. 1662306a36Sopenharmony_ci * When counter matches with match register, the output goes down. 1762306a36Sopenharmony_ci * Reference Manual: https://www.ti.com/lit/ug/spruh73q/spruh73q.pdf 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * Limitations: 2062306a36Sopenharmony_ci * - When PWM is stopped, timer counter gets stopped immediately. This 2162306a36Sopenharmony_ci * doesn't allow the current PWM period to complete and stops abruptly. 2262306a36Sopenharmony_ci * - When PWM is running and changing both duty cycle and period, 2362306a36Sopenharmony_ci * we cannot prevent in software that the output might produce 2462306a36Sopenharmony_ci * a period with mixed settings. Especially when period/duty_cyle 2562306a36Sopenharmony_ci * is updated while the pwm pin is high, current pwm period/duty_cycle 2662306a36Sopenharmony_ci * can get updated as below based on the current timer counter: 2762306a36Sopenharmony_ci * - period for current cycle = current_period + new period 2862306a36Sopenharmony_ci * - duty_cycle for current period = current period + new duty_cycle. 2962306a36Sopenharmony_ci * - PWM OMAP DM timer cannot change the polarity when pwm is active. When 3062306a36Sopenharmony_ci * user requests a change in polarity when in active state: 3162306a36Sopenharmony_ci * - PWM is stopped abruptly(without completing the current cycle) 3262306a36Sopenharmony_ci * - Polarity is changed 3362306a36Sopenharmony_ci * - A fresh cycle is started. 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include <linux/clk.h> 3762306a36Sopenharmony_ci#include <linux/err.h> 3862306a36Sopenharmony_ci#include <linux/kernel.h> 3962306a36Sopenharmony_ci#include <linux/module.h> 4062306a36Sopenharmony_ci#include <linux/mutex.h> 4162306a36Sopenharmony_ci#include <linux/of.h> 4262306a36Sopenharmony_ci#include <linux/of_platform.h> 4362306a36Sopenharmony_ci#include <clocksource/timer-ti-dm.h> 4462306a36Sopenharmony_ci#include <linux/platform_data/dmtimer-omap.h> 4562306a36Sopenharmony_ci#include <linux/platform_device.h> 4662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 4762306a36Sopenharmony_ci#include <linux/pwm.h> 4862306a36Sopenharmony_ci#include <linux/slab.h> 4962306a36Sopenharmony_ci#include <linux/time.h> 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define DM_TIMER_LOAD_MIN 0xfffffffe 5262306a36Sopenharmony_ci#define DM_TIMER_MAX 0xffffffff 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/** 5562306a36Sopenharmony_ci * struct pwm_omap_dmtimer_chip - Structure representing a pwm chip 5662306a36Sopenharmony_ci * corresponding to omap dmtimer. 5762306a36Sopenharmony_ci * @chip: PWM chip structure representing PWM controller 5862306a36Sopenharmony_ci * @mutex: Mutex to protect pwm apply state 5962306a36Sopenharmony_ci * @dm_timer: Pointer to omap dm timer. 6062306a36Sopenharmony_ci * @pdata: Pointer to omap dm timer ops. 6162306a36Sopenharmony_ci * @dm_timer_pdev: Pointer to omap dm timer platform device 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_cistruct pwm_omap_dmtimer_chip { 6462306a36Sopenharmony_ci struct pwm_chip chip; 6562306a36Sopenharmony_ci /* Mutex to protect pwm apply state */ 6662306a36Sopenharmony_ci struct mutex mutex; 6762306a36Sopenharmony_ci struct omap_dm_timer *dm_timer; 6862306a36Sopenharmony_ci const struct omap_dm_timer_ops *pdata; 6962306a36Sopenharmony_ci struct platform_device *dm_timer_pdev; 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic inline struct pwm_omap_dmtimer_chip * 7362306a36Sopenharmony_cito_pwm_omap_dmtimer_chip(struct pwm_chip *chip) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci return container_of(chip, struct pwm_omap_dmtimer_chip, chip); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/** 7962306a36Sopenharmony_ci * pwm_omap_dmtimer_get_clock_cycles() - Get clock cycles in a time frame 8062306a36Sopenharmony_ci * @clk_rate: pwm timer clock rate 8162306a36Sopenharmony_ci * @ns: time frame in nano seconds. 8262306a36Sopenharmony_ci * 8362306a36Sopenharmony_ci * Return number of clock cycles in a given period(ins ns). 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_cistatic u32 pwm_omap_dmtimer_get_clock_cycles(unsigned long clk_rate, int ns) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci return DIV_ROUND_CLOSEST_ULL((u64)clk_rate * ns, NSEC_PER_SEC); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/** 9162306a36Sopenharmony_ci * pwm_omap_dmtimer_start() - Start the pwm omap dm timer in pwm mode 9262306a36Sopenharmony_ci * @omap: Pointer to pwm omap dm timer chip 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_cistatic void pwm_omap_dmtimer_start(struct pwm_omap_dmtimer_chip *omap) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci /* 9762306a36Sopenharmony_ci * According to OMAP 4 TRM section 22.2.4.10 the counter should be 9862306a36Sopenharmony_ci * started at 0xFFFFFFFE when overflow and match is used to ensure 9962306a36Sopenharmony_ci * that the PWM line is toggled on the first event. 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * Note that omap_dm_timer_enable/disable is for register access and 10262306a36Sopenharmony_ci * not the timer counter itself. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ci omap->pdata->enable(omap->dm_timer); 10562306a36Sopenharmony_ci omap->pdata->write_counter(omap->dm_timer, DM_TIMER_LOAD_MIN); 10662306a36Sopenharmony_ci omap->pdata->disable(omap->dm_timer); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci omap->pdata->start(omap->dm_timer); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/** 11262306a36Sopenharmony_ci * pwm_omap_dmtimer_is_enabled() - Detect if the pwm is enabled. 11362306a36Sopenharmony_ci * @omap: Pointer to pwm omap dm timer chip 11462306a36Sopenharmony_ci * 11562306a36Sopenharmony_ci * Return true if pwm is enabled else false. 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_cistatic bool pwm_omap_dmtimer_is_enabled(struct pwm_omap_dmtimer_chip *omap) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci u32 status; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci status = omap->pdata->get_pwm_status(omap->dm_timer); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return !!(status & OMAP_TIMER_CTRL_ST); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/** 12762306a36Sopenharmony_ci * pwm_omap_dmtimer_polarity() - Detect the polarity of pwm. 12862306a36Sopenharmony_ci * @omap: Pointer to pwm omap dm timer chip 12962306a36Sopenharmony_ci * 13062306a36Sopenharmony_ci * Return the polarity of pwm. 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_cistatic int pwm_omap_dmtimer_polarity(struct pwm_omap_dmtimer_chip *omap) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci u32 status; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci status = omap->pdata->get_pwm_status(omap->dm_timer); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return !!(status & OMAP_TIMER_CTRL_SCPWM); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/** 14262306a36Sopenharmony_ci * pwm_omap_dmtimer_config() - Update the configuration of pwm omap dm timer 14362306a36Sopenharmony_ci * @chip: Pointer to PWM controller 14462306a36Sopenharmony_ci * @pwm: Pointer to PWM channel 14562306a36Sopenharmony_ci * @duty_ns: New duty cycle in nano seconds 14662306a36Sopenharmony_ci * @period_ns: New period in nano seconds 14762306a36Sopenharmony_ci * 14862306a36Sopenharmony_ci * Return 0 if successfully changed the period/duty_cycle else appropriate 14962306a36Sopenharmony_ci * error. 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_cistatic int pwm_omap_dmtimer_config(struct pwm_chip *chip, 15262306a36Sopenharmony_ci struct pwm_device *pwm, 15362306a36Sopenharmony_ci int duty_ns, int period_ns) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip); 15662306a36Sopenharmony_ci u32 period_cycles, duty_cycles; 15762306a36Sopenharmony_ci u32 load_value, match_value; 15862306a36Sopenharmony_ci unsigned long clk_rate; 15962306a36Sopenharmony_ci struct clk *fclk; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci dev_dbg(chip->dev, "requested duty cycle: %d ns, period: %d ns\n", 16262306a36Sopenharmony_ci duty_ns, period_ns); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (duty_ns == pwm_get_duty_cycle(pwm) && 16562306a36Sopenharmony_ci period_ns == pwm_get_period(pwm)) 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci fclk = omap->pdata->get_fclk(omap->dm_timer); 16962306a36Sopenharmony_ci if (!fclk) { 17062306a36Sopenharmony_ci dev_err(chip->dev, "invalid pmtimer fclk\n"); 17162306a36Sopenharmony_ci return -EINVAL; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci clk_rate = clk_get_rate(fclk); 17562306a36Sopenharmony_ci if (!clk_rate) { 17662306a36Sopenharmony_ci dev_err(chip->dev, "invalid pmtimer fclk rate\n"); 17762306a36Sopenharmony_ci return -EINVAL; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci dev_dbg(chip->dev, "clk rate: %luHz\n", clk_rate); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* 18362306a36Sopenharmony_ci * Calculate the appropriate load and match values based on the 18462306a36Sopenharmony_ci * specified period and duty cycle. The load value determines the 18562306a36Sopenharmony_ci * period time and the match value determines the duty time. 18662306a36Sopenharmony_ci * 18762306a36Sopenharmony_ci * The period lasts for (DM_TIMER_MAX-load_value+1) clock cycles. 18862306a36Sopenharmony_ci * Similarly, the active time lasts (match_value-load_value+1) cycles. 18962306a36Sopenharmony_ci * The non-active time is the remainder: (DM_TIMER_MAX-match_value) 19062306a36Sopenharmony_ci * clock cycles. 19162306a36Sopenharmony_ci * 19262306a36Sopenharmony_ci * NOTE: It is required that: load_value <= match_value < DM_TIMER_MAX 19362306a36Sopenharmony_ci * 19462306a36Sopenharmony_ci * References: 19562306a36Sopenharmony_ci * OMAP4430/60/70 TRM sections 22.2.4.10 and 22.2.4.11 19662306a36Sopenharmony_ci * AM335x Sitara TRM sections 20.1.3.5 and 20.1.3.6 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_ci period_cycles = pwm_omap_dmtimer_get_clock_cycles(clk_rate, period_ns); 19962306a36Sopenharmony_ci duty_cycles = pwm_omap_dmtimer_get_clock_cycles(clk_rate, duty_ns); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (period_cycles < 2) { 20262306a36Sopenharmony_ci dev_info(chip->dev, 20362306a36Sopenharmony_ci "period %d ns too short for clock rate %lu Hz\n", 20462306a36Sopenharmony_ci period_ns, clk_rate); 20562306a36Sopenharmony_ci return -EINVAL; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (duty_cycles < 1) { 20962306a36Sopenharmony_ci dev_dbg(chip->dev, 21062306a36Sopenharmony_ci "duty cycle %d ns is too short for clock rate %lu Hz\n", 21162306a36Sopenharmony_ci duty_ns, clk_rate); 21262306a36Sopenharmony_ci dev_dbg(chip->dev, "using minimum of 1 clock cycle\n"); 21362306a36Sopenharmony_ci duty_cycles = 1; 21462306a36Sopenharmony_ci } else if (duty_cycles >= period_cycles) { 21562306a36Sopenharmony_ci dev_dbg(chip->dev, 21662306a36Sopenharmony_ci "duty cycle %d ns is too long for period %d ns at clock rate %lu Hz\n", 21762306a36Sopenharmony_ci duty_ns, period_ns, clk_rate); 21862306a36Sopenharmony_ci dev_dbg(chip->dev, "using maximum of 1 clock cycle less than period\n"); 21962306a36Sopenharmony_ci duty_cycles = period_cycles - 1; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci dev_dbg(chip->dev, "effective duty cycle: %lld ns, period: %lld ns\n", 22362306a36Sopenharmony_ci DIV_ROUND_CLOSEST_ULL((u64)NSEC_PER_SEC * duty_cycles, 22462306a36Sopenharmony_ci clk_rate), 22562306a36Sopenharmony_ci DIV_ROUND_CLOSEST_ULL((u64)NSEC_PER_SEC * period_cycles, 22662306a36Sopenharmony_ci clk_rate)); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci load_value = (DM_TIMER_MAX - period_cycles) + 1; 22962306a36Sopenharmony_ci match_value = load_value + duty_cycles - 1; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci omap->pdata->set_load(omap->dm_timer, load_value); 23262306a36Sopenharmony_ci omap->pdata->set_match(omap->dm_timer, true, match_value); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci dev_dbg(chip->dev, "load value: %#08x (%d), match value: %#08x (%d)\n", 23562306a36Sopenharmony_ci load_value, load_value, match_value, match_value); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci return 0; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci/** 24162306a36Sopenharmony_ci * pwm_omap_dmtimer_set_polarity() - Changes the polarity of the pwm dm timer. 24262306a36Sopenharmony_ci * @chip: Pointer to PWM controller 24362306a36Sopenharmony_ci * @pwm: Pointer to PWM channel 24462306a36Sopenharmony_ci * @polarity: New pwm polarity to be set 24562306a36Sopenharmony_ci */ 24662306a36Sopenharmony_cistatic void pwm_omap_dmtimer_set_polarity(struct pwm_chip *chip, 24762306a36Sopenharmony_ci struct pwm_device *pwm, 24862306a36Sopenharmony_ci enum pwm_polarity polarity) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip); 25162306a36Sopenharmony_ci bool enabled; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* Disable the PWM before changing the polarity. */ 25462306a36Sopenharmony_ci enabled = pwm_omap_dmtimer_is_enabled(omap); 25562306a36Sopenharmony_ci if (enabled) 25662306a36Sopenharmony_ci omap->pdata->stop(omap->dm_timer); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci omap->pdata->set_pwm(omap->dm_timer, 25962306a36Sopenharmony_ci polarity == PWM_POLARITY_INVERSED, 26062306a36Sopenharmony_ci true, OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE, 26162306a36Sopenharmony_ci true); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (enabled) 26462306a36Sopenharmony_ci pwm_omap_dmtimer_start(omap); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci/** 26862306a36Sopenharmony_ci * pwm_omap_dmtimer_apply() - Changes the state of the pwm omap dm timer. 26962306a36Sopenharmony_ci * @chip: Pointer to PWM controller 27062306a36Sopenharmony_ci * @pwm: Pointer to PWM channel 27162306a36Sopenharmony_ci * @state: New state to apply 27262306a36Sopenharmony_ci * 27362306a36Sopenharmony_ci * Return 0 if successfully changed the state else appropriate error. 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_cistatic int pwm_omap_dmtimer_apply(struct pwm_chip *chip, 27662306a36Sopenharmony_ci struct pwm_device *pwm, 27762306a36Sopenharmony_ci const struct pwm_state *state) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip); 28062306a36Sopenharmony_ci int ret = 0; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci mutex_lock(&omap->mutex); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (pwm_omap_dmtimer_is_enabled(omap) && !state->enabled) { 28562306a36Sopenharmony_ci omap->pdata->stop(omap->dm_timer); 28662306a36Sopenharmony_ci goto unlock_mutex; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (pwm_omap_dmtimer_polarity(omap) != state->polarity) 29062306a36Sopenharmony_ci pwm_omap_dmtimer_set_polarity(chip, pwm, state->polarity); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci ret = pwm_omap_dmtimer_config(chip, pwm, state->duty_cycle, 29362306a36Sopenharmony_ci state->period); 29462306a36Sopenharmony_ci if (ret) 29562306a36Sopenharmony_ci goto unlock_mutex; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (!pwm_omap_dmtimer_is_enabled(omap) && state->enabled) { 29862306a36Sopenharmony_ci omap->pdata->set_pwm(omap->dm_timer, 29962306a36Sopenharmony_ci state->polarity == PWM_POLARITY_INVERSED, 30062306a36Sopenharmony_ci true, 30162306a36Sopenharmony_ci OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE, 30262306a36Sopenharmony_ci true); 30362306a36Sopenharmony_ci pwm_omap_dmtimer_start(omap); 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ciunlock_mutex: 30762306a36Sopenharmony_ci mutex_unlock(&omap->mutex); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci return ret; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic const struct pwm_ops pwm_omap_dmtimer_ops = { 31362306a36Sopenharmony_ci .apply = pwm_omap_dmtimer_apply, 31462306a36Sopenharmony_ci .owner = THIS_MODULE, 31562306a36Sopenharmony_ci}; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic int pwm_omap_dmtimer_probe(struct platform_device *pdev) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 32062306a36Sopenharmony_ci struct dmtimer_platform_data *timer_pdata; 32162306a36Sopenharmony_ci const struct omap_dm_timer_ops *pdata; 32262306a36Sopenharmony_ci struct platform_device *timer_pdev; 32362306a36Sopenharmony_ci struct pwm_omap_dmtimer_chip *omap; 32462306a36Sopenharmony_ci struct omap_dm_timer *dm_timer; 32562306a36Sopenharmony_ci struct device_node *timer; 32662306a36Sopenharmony_ci int ret = 0; 32762306a36Sopenharmony_ci u32 v; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci timer = of_parse_phandle(np, "ti,timers", 0); 33062306a36Sopenharmony_ci if (!timer) 33162306a36Sopenharmony_ci return -ENODEV; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci timer_pdev = of_find_device_by_node(timer); 33462306a36Sopenharmony_ci if (!timer_pdev) { 33562306a36Sopenharmony_ci dev_err(&pdev->dev, "Unable to find Timer pdev\n"); 33662306a36Sopenharmony_ci ret = -ENODEV; 33762306a36Sopenharmony_ci goto err_find_timer_pdev; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci timer_pdata = dev_get_platdata(&timer_pdev->dev); 34162306a36Sopenharmony_ci if (!timer_pdata) { 34262306a36Sopenharmony_ci dev_dbg(&pdev->dev, 34362306a36Sopenharmony_ci "dmtimer pdata structure NULL, deferring probe\n"); 34462306a36Sopenharmony_ci ret = -EPROBE_DEFER; 34562306a36Sopenharmony_ci goto err_platdata; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci pdata = timer_pdata->timer_ops; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (!pdata || !pdata->request_by_node || 35162306a36Sopenharmony_ci !pdata->free || 35262306a36Sopenharmony_ci !pdata->enable || 35362306a36Sopenharmony_ci !pdata->disable || 35462306a36Sopenharmony_ci !pdata->get_fclk || 35562306a36Sopenharmony_ci !pdata->start || 35662306a36Sopenharmony_ci !pdata->stop || 35762306a36Sopenharmony_ci !pdata->set_load || 35862306a36Sopenharmony_ci !pdata->set_match || 35962306a36Sopenharmony_ci !pdata->set_pwm || 36062306a36Sopenharmony_ci !pdata->get_pwm_status || 36162306a36Sopenharmony_ci !pdata->set_prescaler || 36262306a36Sopenharmony_ci !pdata->write_counter) { 36362306a36Sopenharmony_ci dev_err(&pdev->dev, "Incomplete dmtimer pdata structure\n"); 36462306a36Sopenharmony_ci ret = -EINVAL; 36562306a36Sopenharmony_ci goto err_platdata; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (!of_get_property(timer, "ti,timer-pwm", NULL)) { 36962306a36Sopenharmony_ci dev_err(&pdev->dev, "Missing ti,timer-pwm capability\n"); 37062306a36Sopenharmony_ci ret = -ENODEV; 37162306a36Sopenharmony_ci goto err_timer_property; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci dm_timer = pdata->request_by_node(timer); 37562306a36Sopenharmony_ci if (!dm_timer) { 37662306a36Sopenharmony_ci ret = -EPROBE_DEFER; 37762306a36Sopenharmony_ci goto err_request_timer; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci omap = devm_kzalloc(&pdev->dev, sizeof(*omap), GFP_KERNEL); 38162306a36Sopenharmony_ci if (!omap) { 38262306a36Sopenharmony_ci ret = -ENOMEM; 38362306a36Sopenharmony_ci goto err_alloc_omap; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci omap->pdata = pdata; 38762306a36Sopenharmony_ci omap->dm_timer = dm_timer; 38862306a36Sopenharmony_ci omap->dm_timer_pdev = timer_pdev; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* 39162306a36Sopenharmony_ci * Ensure that the timer is stopped before we allow PWM core to call 39262306a36Sopenharmony_ci * pwm_enable. 39362306a36Sopenharmony_ci */ 39462306a36Sopenharmony_ci if (pm_runtime_active(&omap->dm_timer_pdev->dev)) 39562306a36Sopenharmony_ci omap->pdata->stop(omap->dm_timer); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (!of_property_read_u32(pdev->dev.of_node, "ti,prescaler", &v)) 39862306a36Sopenharmony_ci omap->pdata->set_prescaler(omap->dm_timer, v); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* setup dmtimer clock source */ 40162306a36Sopenharmony_ci if (!of_property_read_u32(pdev->dev.of_node, "ti,clock-source", &v)) 40262306a36Sopenharmony_ci omap->pdata->set_source(omap->dm_timer, v); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci omap->chip.dev = &pdev->dev; 40562306a36Sopenharmony_ci omap->chip.ops = &pwm_omap_dmtimer_ops; 40662306a36Sopenharmony_ci omap->chip.npwm = 1; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci mutex_init(&omap->mutex); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci ret = pwmchip_add(&omap->chip); 41162306a36Sopenharmony_ci if (ret < 0) { 41262306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register PWM\n"); 41362306a36Sopenharmony_ci goto err_pwmchip_add; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci of_node_put(timer); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci platform_set_drvdata(pdev, omap); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cierr_pwmchip_add: 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* 42562306a36Sopenharmony_ci * *omap is allocated using devm_kzalloc, 42662306a36Sopenharmony_ci * so no free necessary here 42762306a36Sopenharmony_ci */ 42862306a36Sopenharmony_cierr_alloc_omap: 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci pdata->free(dm_timer); 43162306a36Sopenharmony_cierr_request_timer: 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cierr_timer_property: 43462306a36Sopenharmony_cierr_platdata: 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci put_device(&timer_pdev->dev); 43762306a36Sopenharmony_cierr_find_timer_pdev: 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci of_node_put(timer); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci return ret; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic void pwm_omap_dmtimer_remove(struct platform_device *pdev) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct pwm_omap_dmtimer_chip *omap = platform_get_drvdata(pdev); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci pwmchip_remove(&omap->chip); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (pm_runtime_active(&omap->dm_timer_pdev->dev)) 45162306a36Sopenharmony_ci omap->pdata->stop(omap->dm_timer); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci omap->pdata->free(omap->dm_timer); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci put_device(&omap->dm_timer_pdev->dev); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci mutex_destroy(&omap->mutex); 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic const struct of_device_id pwm_omap_dmtimer_of_match[] = { 46162306a36Sopenharmony_ci {.compatible = "ti,omap-dmtimer-pwm"}, 46262306a36Sopenharmony_ci {} 46362306a36Sopenharmony_ci}; 46462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, pwm_omap_dmtimer_of_match); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic struct platform_driver pwm_omap_dmtimer_driver = { 46762306a36Sopenharmony_ci .driver = { 46862306a36Sopenharmony_ci .name = "omap-dmtimer-pwm", 46962306a36Sopenharmony_ci .of_match_table = of_match_ptr(pwm_omap_dmtimer_of_match), 47062306a36Sopenharmony_ci }, 47162306a36Sopenharmony_ci .probe = pwm_omap_dmtimer_probe, 47262306a36Sopenharmony_ci .remove_new = pwm_omap_dmtimer_remove, 47362306a36Sopenharmony_ci}; 47462306a36Sopenharmony_cimodule_platform_driver(pwm_omap_dmtimer_driver); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ciMODULE_AUTHOR("Grant Erickson <marathon96@gmail.com>"); 47762306a36Sopenharmony_ciMODULE_AUTHOR("NeilBrown <neilb@suse.de>"); 47862306a36Sopenharmony_ciMODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 47962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 48062306a36Sopenharmony_ciMODULE_DESCRIPTION("OMAP PWM Driver using Dual-mode Timers"); 481