1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 26604c655SNeil Armstrong /* 36604c655SNeil Armstrong * Copyright (c) 2015 Neil Armstrong <narmstrong@baylibre.com> 46604c655SNeil Armstrong * Copyright (c) 2014 Joachim Eastwood <manabian@gmail.com> 56604c655SNeil Armstrong * Copyright (c) 2012 NeilBrown <neilb@suse.de> 66604c655SNeil Armstrong * Heavily based on earlier code which is: 76604c655SNeil Armstrong * Copyright (c) 2010 Grant Erickson <marathon96@gmail.com> 86604c655SNeil Armstrong * 96604c655SNeil Armstrong * Also based on pwm-samsung.c 106604c655SNeil Armstrong * 116604c655SNeil Armstrong * Description: 126604c655SNeil Armstrong * This file is the core OMAP support for the generic, Linux 13348fb6f7SLokesh Vutla * PWM driver / controller, using the OMAP's dual-mode timers 14348fb6f7SLokesh Vutla * with a timer counter that goes up. When it overflows it gets 15348fb6f7SLokesh Vutla * reloaded with the load value and the pwm output goes up. 16348fb6f7SLokesh Vutla * When counter matches with match register, the output goes down. 17216a094dSAlexander A. Klimov * Reference Manual: https://www.ti.com/lit/ug/spruh73q/spruh73q.pdf 18348fb6f7SLokesh Vutla * 19348fb6f7SLokesh Vutla * Limitations: 20348fb6f7SLokesh Vutla * - When PWM is stopped, timer counter gets stopped immediately. This 21348fb6f7SLokesh Vutla * doesn't allow the current PWM period to complete and stops abruptly. 22e793eef8SLokesh Vutla * - When PWM is running and changing both duty cycle and period, 23e793eef8SLokesh Vutla * we cannot prevent in software that the output might produce 24e793eef8SLokesh Vutla * a period with mixed settings. Especially when period/duty_cyle 25e793eef8SLokesh Vutla * is updated while the pwm pin is high, current pwm period/duty_cycle 26e793eef8SLokesh Vutla * can get updated as below based on the current timer counter: 27e793eef8SLokesh Vutla * - period for current cycle = current_period + new period 28e793eef8SLokesh Vutla * - duty_cycle for current period = current period + new duty_cycle. 296b28fb6fSLokesh Vutla * - PWM OMAP DM timer cannot change the polarity when pwm is active. When 306b28fb6fSLokesh Vutla * user requests a change in polarity when in active state: 316b28fb6fSLokesh Vutla * - PWM is stopped abruptly(without completing the current cycle) 326b28fb6fSLokesh Vutla * - Polarity is changed 336b28fb6fSLokesh Vutla * - A fresh cycle is started. 346604c655SNeil Armstrong */ 356604c655SNeil Armstrong 366604c655SNeil Armstrong #include <linux/clk.h> 376604c655SNeil Armstrong #include <linux/err.h> 386604c655SNeil Armstrong #include <linux/kernel.h> 396604c655SNeil Armstrong #include <linux/module.h> 406604c655SNeil Armstrong #include <linux/of.h> 416604c655SNeil Armstrong #include <linux/of_platform.h> 4254091b5fSLokesh Vutla #include <clocksource/timer-ti-dm.h> 43b7290cf6SKeerthy #include <linux/platform_data/dmtimer-omap.h> 446604c655SNeil Armstrong #include <linux/platform_device.h> 456604c655SNeil Armstrong #include <linux/pm_runtime.h> 466604c655SNeil Armstrong #include <linux/pwm.h> 476604c655SNeil Armstrong #include <linux/slab.h> 486604c655SNeil Armstrong #include <linux/time.h> 496604c655SNeil Armstrong 506604c655SNeil Armstrong #define DM_TIMER_LOAD_MIN 0xfffffffe 51f8caa792SDavid Rivshin #define DM_TIMER_MAX 0xffffffff 526604c655SNeil Armstrong 536b28fb6fSLokesh Vutla /** 546b28fb6fSLokesh Vutla * struct pwm_omap_dmtimer_chip - Structure representing a pwm chip 556b28fb6fSLokesh Vutla * corresponding to omap dmtimer. 566b28fb6fSLokesh Vutla * @chip: PWM chip structure representing PWM controller 576b28fb6fSLokesh Vutla * @dm_timer: Pointer to omap dm timer. 586b28fb6fSLokesh Vutla * @pdata: Pointer to omap dm timer ops. 59dfd9b615SLee Jones * @dm_timer_pdev: Pointer to omap dm timer platform device 606b28fb6fSLokesh Vutla */ 616604c655SNeil Armstrong struct pwm_omap_dmtimer_chip { 626604c655SNeil Armstrong struct pwm_chip chip; 636b28fb6fSLokesh Vutla /* Mutex to protect pwm apply state */ 6454091b5fSLokesh Vutla struct omap_dm_timer *dm_timer; 65b7290cf6SKeerthy const struct omap_dm_timer_ops *pdata; 666604c655SNeil Armstrong struct platform_device *dm_timer_pdev; 676604c655SNeil Armstrong }; 686604c655SNeil Armstrong 696604c655SNeil Armstrong static inline struct pwm_omap_dmtimer_chip * 706604c655SNeil Armstrong to_pwm_omap_dmtimer_chip(struct pwm_chip *chip) 716604c655SNeil Armstrong { 726604c655SNeil Armstrong return container_of(chip, struct pwm_omap_dmtimer_chip, chip); 736604c655SNeil Armstrong } 746604c655SNeil Armstrong 756b28fb6fSLokesh Vutla /** 766b28fb6fSLokesh Vutla * pwm_omap_dmtimer_get_clock_cycles() - Get clock cycles in a time frame 776b28fb6fSLokesh Vutla * @clk_rate: pwm timer clock rate 786b28fb6fSLokesh Vutla * @ns: time frame in nano seconds. 796b28fb6fSLokesh Vutla * 806b28fb6fSLokesh Vutla * Return number of clock cycles in a given period(ins ns). 816b28fb6fSLokesh Vutla */ 82f8caa792SDavid Rivshin static u32 pwm_omap_dmtimer_get_clock_cycles(unsigned long clk_rate, int ns) 836604c655SNeil Armstrong { 847b0883f3SDavid Rivshin return DIV_ROUND_CLOSEST_ULL((u64)clk_rate * ns, NSEC_PER_SEC); 856604c655SNeil Armstrong } 866604c655SNeil Armstrong 876b28fb6fSLokesh Vutla /** 886b28fb6fSLokesh Vutla * pwm_omap_dmtimer_start() - Start the pwm omap dm timer in pwm mode 896b28fb6fSLokesh Vutla * @omap: Pointer to pwm omap dm timer chip 906b28fb6fSLokesh Vutla */ 916604c655SNeil Armstrong static void pwm_omap_dmtimer_start(struct pwm_omap_dmtimer_chip *omap) 926604c655SNeil Armstrong { 936604c655SNeil Armstrong /* 946604c655SNeil Armstrong * According to OMAP 4 TRM section 22.2.4.10 the counter should be 956604c655SNeil Armstrong * started at 0xFFFFFFFE when overflow and match is used to ensure 966604c655SNeil Armstrong * that the PWM line is toggled on the first event. 976604c655SNeil Armstrong * 986604c655SNeil Armstrong * Note that omap_dm_timer_enable/disable is for register access and 996604c655SNeil Armstrong * not the timer counter itself. 1006604c655SNeil Armstrong */ 1016604c655SNeil Armstrong omap->pdata->enable(omap->dm_timer); 1026604c655SNeil Armstrong omap->pdata->write_counter(omap->dm_timer, DM_TIMER_LOAD_MIN); 1036604c655SNeil Armstrong omap->pdata->disable(omap->dm_timer); 1046604c655SNeil Armstrong 1056604c655SNeil Armstrong omap->pdata->start(omap->dm_timer); 1066604c655SNeil Armstrong } 1076604c655SNeil Armstrong 1086b28fb6fSLokesh Vutla /** 1096b28fb6fSLokesh Vutla * pwm_omap_dmtimer_is_enabled() - Detect if the pwm is enabled. 1106b28fb6fSLokesh Vutla * @omap: Pointer to pwm omap dm timer chip 1116b28fb6fSLokesh Vutla * 1126b28fb6fSLokesh Vutla * Return true if pwm is enabled else false. 1136b28fb6fSLokesh Vutla */ 1146b28fb6fSLokesh Vutla static bool pwm_omap_dmtimer_is_enabled(struct pwm_omap_dmtimer_chip *omap) 1156604c655SNeil Armstrong { 1166b28fb6fSLokesh Vutla u32 status; 1176604c655SNeil Armstrong 1186b28fb6fSLokesh Vutla status = omap->pdata->get_pwm_status(omap->dm_timer); 119867beb60SLokesh Vutla 1206b28fb6fSLokesh Vutla return !!(status & OMAP_TIMER_CTRL_ST); 1216604c655SNeil Armstrong } 1226604c655SNeil Armstrong 1236b28fb6fSLokesh Vutla /** 1246b28fb6fSLokesh Vutla * pwm_omap_dmtimer_polarity() - Detect the polarity of pwm. 1256b28fb6fSLokesh Vutla * @omap: Pointer to pwm omap dm timer chip 1266b28fb6fSLokesh Vutla * 1276b28fb6fSLokesh Vutla * Return the polarity of pwm. 1286b28fb6fSLokesh Vutla */ 1296b28fb6fSLokesh Vutla static int pwm_omap_dmtimer_polarity(struct pwm_omap_dmtimer_chip *omap) 1306604c655SNeil Armstrong { 1316b28fb6fSLokesh Vutla u32 status; 1326604c655SNeil Armstrong 1336b28fb6fSLokesh Vutla status = omap->pdata->get_pwm_status(omap->dm_timer); 1346b28fb6fSLokesh Vutla 1356b28fb6fSLokesh Vutla return !!(status & OMAP_TIMER_CTRL_SCPWM); 1366604c655SNeil Armstrong } 1376604c655SNeil Armstrong 1386b28fb6fSLokesh Vutla /** 1396b28fb6fSLokesh Vutla * pwm_omap_dmtimer_config() - Update the configuration of pwm omap dm timer 1406b28fb6fSLokesh Vutla * @chip: Pointer to PWM controller 1416b28fb6fSLokesh Vutla * @pwm: Pointer to PWM channel 1426b28fb6fSLokesh Vutla * @duty_ns: New duty cycle in nano seconds 1436b28fb6fSLokesh Vutla * @period_ns: New period in nano seconds 1446b28fb6fSLokesh Vutla * 1456b28fb6fSLokesh Vutla * Return 0 if successfully changed the period/duty_cycle else appropriate 1466b28fb6fSLokesh Vutla * error. 1476b28fb6fSLokesh Vutla */ 1486604c655SNeil Armstrong static int pwm_omap_dmtimer_config(struct pwm_chip *chip, 1496604c655SNeil Armstrong struct pwm_device *pwm, 1506604c655SNeil Armstrong int duty_ns, int period_ns) 1516604c655SNeil Armstrong { 1526604c655SNeil Armstrong struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip); 153f8caa792SDavid Rivshin u32 period_cycles, duty_cycles; 154f8caa792SDavid Rivshin u32 load_value, match_value; 1556604c655SNeil Armstrong unsigned long clk_rate; 1566b28fb6fSLokesh Vutla struct clk *fclk; 1576604c655SNeil Armstrong 158922201d1SDavid Rivshin dev_dbg(chip->dev, "requested duty cycle: %d ns, period: %d ns\n", 159922201d1SDavid Rivshin duty_ns, period_ns); 1606604c655SNeil Armstrong 1616604c655SNeil Armstrong if (duty_ns == pwm_get_duty_cycle(pwm) && 1626b28fb6fSLokesh Vutla period_ns == pwm_get_period(pwm)) 1636604c655SNeil Armstrong return 0; 1646604c655SNeil Armstrong 1656604c655SNeil Armstrong fclk = omap->pdata->get_fclk(omap->dm_timer); 1666604c655SNeil Armstrong if (!fclk) { 1676604c655SNeil Armstrong dev_err(chip->dev, "invalid pmtimer fclk\n"); 1686b28fb6fSLokesh Vutla return -EINVAL; 1696604c655SNeil Armstrong } 1706604c655SNeil Armstrong 1716604c655SNeil Armstrong clk_rate = clk_get_rate(fclk); 1726604c655SNeil Armstrong if (!clk_rate) { 1736604c655SNeil Armstrong dev_err(chip->dev, "invalid pmtimer fclk rate\n"); 1746b28fb6fSLokesh Vutla return -EINVAL; 1756604c655SNeil Armstrong } 1766604c655SNeil Armstrong 1776604c655SNeil Armstrong dev_dbg(chip->dev, "clk rate: %luHz\n", clk_rate); 1786604c655SNeil Armstrong 1796604c655SNeil Armstrong /* 1806604c655SNeil Armstrong * Calculate the appropriate load and match values based on the 1816604c655SNeil Armstrong * specified period and duty cycle. The load value determines the 182f8caa792SDavid Rivshin * period time and the match value determines the duty time. 183f8caa792SDavid Rivshin * 184f8caa792SDavid Rivshin * The period lasts for (DM_TIMER_MAX-load_value+1) clock cycles. 185f8caa792SDavid Rivshin * Similarly, the active time lasts (match_value-load_value+1) cycles. 186f8caa792SDavid Rivshin * The non-active time is the remainder: (DM_TIMER_MAX-match_value) 187f8caa792SDavid Rivshin * clock cycles. 188f8caa792SDavid Rivshin * 189cd378881SDavid Rivshin * NOTE: It is required that: load_value <= match_value < DM_TIMER_MAX 190cd378881SDavid Rivshin * 191f8caa792SDavid Rivshin * References: 192f8caa792SDavid Rivshin * OMAP4430/60/70 TRM sections 22.2.4.10 and 22.2.4.11 193f8caa792SDavid Rivshin * AM335x Sitara TRM sections 20.1.3.5 and 20.1.3.6 1946604c655SNeil Armstrong */ 195f8caa792SDavid Rivshin period_cycles = pwm_omap_dmtimer_get_clock_cycles(clk_rate, period_ns); 196f8caa792SDavid Rivshin duty_cycles = pwm_omap_dmtimer_get_clock_cycles(clk_rate, duty_ns); 197f8caa792SDavid Rivshin 198cd378881SDavid Rivshin if (period_cycles < 2) { 199cd378881SDavid Rivshin dev_info(chip->dev, 200cd378881SDavid Rivshin "period %d ns too short for clock rate %lu Hz\n", 201cd378881SDavid Rivshin period_ns, clk_rate); 2026b28fb6fSLokesh Vutla return -EINVAL; 203cd378881SDavid Rivshin } 204cd378881SDavid Rivshin 205cd378881SDavid Rivshin if (duty_cycles < 1) { 206cd378881SDavid Rivshin dev_dbg(chip->dev, 207cd378881SDavid Rivshin "duty cycle %d ns is too short for clock rate %lu Hz\n", 208cd378881SDavid Rivshin duty_ns, clk_rate); 209cd378881SDavid Rivshin dev_dbg(chip->dev, "using minimum of 1 clock cycle\n"); 210cd378881SDavid Rivshin duty_cycles = 1; 211cd378881SDavid Rivshin } else if (duty_cycles >= period_cycles) { 212cd378881SDavid Rivshin dev_dbg(chip->dev, 213cd378881SDavid Rivshin "duty cycle %d ns is too long for period %d ns at clock rate %lu Hz\n", 214cd378881SDavid Rivshin duty_ns, period_ns, clk_rate); 215cd378881SDavid Rivshin dev_dbg(chip->dev, "using maximum of 1 clock cycle less than period\n"); 216cd378881SDavid Rivshin duty_cycles = period_cycles - 1; 217cd378881SDavid Rivshin } 218cd378881SDavid Rivshin 219922201d1SDavid Rivshin dev_dbg(chip->dev, "effective duty cycle: %lld ns, period: %lld ns\n", 220922201d1SDavid Rivshin DIV_ROUND_CLOSEST_ULL((u64)NSEC_PER_SEC * duty_cycles, 221922201d1SDavid Rivshin clk_rate), 222922201d1SDavid Rivshin DIV_ROUND_CLOSEST_ULL((u64)NSEC_PER_SEC * period_cycles, 223922201d1SDavid Rivshin clk_rate)); 224922201d1SDavid Rivshin 225f8caa792SDavid Rivshin load_value = (DM_TIMER_MAX - period_cycles) + 1; 226f8caa792SDavid Rivshin match_value = load_value + duty_cycles - 1; 2276604c655SNeil Armstrong 22802e6d546SLokesh Vutla omap->pdata->set_load(omap->dm_timer, load_value); 2296604c655SNeil Armstrong omap->pdata->set_match(omap->dm_timer, true, match_value); 2306604c655SNeil Armstrong 2316604c655SNeil Armstrong dev_dbg(chip->dev, "load value: %#08x (%d), match value: %#08x (%d)\n", 2326604c655SNeil Armstrong load_value, load_value, match_value, match_value); 2336604c655SNeil Armstrong 2346604c655SNeil Armstrong return 0; 2356604c655SNeil Armstrong } 2366604c655SNeil Armstrong 2376b28fb6fSLokesh Vutla /** 2386b28fb6fSLokesh Vutla * pwm_omap_dmtimer_set_polarity() - Changes the polarity of the pwm dm timer. 2396b28fb6fSLokesh Vutla * @chip: Pointer to PWM controller 2406b28fb6fSLokesh Vutla * @pwm: Pointer to PWM channel 2416b28fb6fSLokesh Vutla * @polarity: New pwm polarity to be set 2426b28fb6fSLokesh Vutla */ 2436b28fb6fSLokesh Vutla static void pwm_omap_dmtimer_set_polarity(struct pwm_chip *chip, 2446604c655SNeil Armstrong struct pwm_device *pwm, 2456604c655SNeil Armstrong enum pwm_polarity polarity) 2466604c655SNeil Armstrong { 2476604c655SNeil Armstrong struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip); 2486b28fb6fSLokesh Vutla bool enabled; 2496604c655SNeil Armstrong 2506b28fb6fSLokesh Vutla /* Disable the PWM before changing the polarity. */ 2516b28fb6fSLokesh Vutla enabled = pwm_omap_dmtimer_is_enabled(omap); 2526b28fb6fSLokesh Vutla if (enabled) 2536b28fb6fSLokesh Vutla omap->pdata->stop(omap->dm_timer); 2546b28fb6fSLokesh Vutla 2556604c655SNeil Armstrong omap->pdata->set_pwm(omap->dm_timer, 2566604c655SNeil Armstrong polarity == PWM_POLARITY_INVERSED, 25754091b5fSLokesh Vutla true, OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE, 25802e6d546SLokesh Vutla true); 2596b28fb6fSLokesh Vutla 2606b28fb6fSLokesh Vutla if (enabled) 2616b28fb6fSLokesh Vutla pwm_omap_dmtimer_start(omap); 2626b28fb6fSLokesh Vutla } 2636b28fb6fSLokesh Vutla 2646b28fb6fSLokesh Vutla /** 2656b28fb6fSLokesh Vutla * pwm_omap_dmtimer_apply() - Changes the state of the pwm omap dm timer. 2666b28fb6fSLokesh Vutla * @chip: Pointer to PWM controller 2676b28fb6fSLokesh Vutla * @pwm: Pointer to PWM channel 2686b28fb6fSLokesh Vutla * @state: New state to apply 2696b28fb6fSLokesh Vutla * 2706b28fb6fSLokesh Vutla * Return 0 if successfully changed the state else appropriate error. 2716b28fb6fSLokesh Vutla */ 2726b28fb6fSLokesh Vutla static int pwm_omap_dmtimer_apply(struct pwm_chip *chip, 2736b28fb6fSLokesh Vutla struct pwm_device *pwm, 2746b28fb6fSLokesh Vutla const struct pwm_state *state) 2756b28fb6fSLokesh Vutla { 2766b28fb6fSLokesh Vutla struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip); 27701b571fbSUwe Kleine-König int ret; 2786b28fb6fSLokesh Vutla 2796b28fb6fSLokesh Vutla if (pwm_omap_dmtimer_is_enabled(omap) && !state->enabled) { 2806b28fb6fSLokesh Vutla omap->pdata->stop(omap->dm_timer); 28101b571fbSUwe Kleine-König return 0; 2826b28fb6fSLokesh Vutla } 2836b28fb6fSLokesh Vutla 2846b28fb6fSLokesh Vutla if (pwm_omap_dmtimer_polarity(omap) != state->polarity) 2856b28fb6fSLokesh Vutla pwm_omap_dmtimer_set_polarity(chip, pwm, state->polarity); 2866b28fb6fSLokesh Vutla 2876b28fb6fSLokesh Vutla ret = pwm_omap_dmtimer_config(chip, pwm, state->duty_cycle, 2886b28fb6fSLokesh Vutla state->period); 2896b28fb6fSLokesh Vutla if (ret) 29001b571fbSUwe Kleine-König return ret; 2916b28fb6fSLokesh Vutla 2926b28fb6fSLokesh Vutla if (!pwm_omap_dmtimer_is_enabled(omap) && state->enabled) { 2936b28fb6fSLokesh Vutla omap->pdata->set_pwm(omap->dm_timer, 2946b28fb6fSLokesh Vutla state->polarity == PWM_POLARITY_INVERSED, 2956b28fb6fSLokesh Vutla true, 2966b28fb6fSLokesh Vutla OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE, 2976b28fb6fSLokesh Vutla true); 2986b28fb6fSLokesh Vutla pwm_omap_dmtimer_start(omap); 2996b28fb6fSLokesh Vutla } 3006b28fb6fSLokesh Vutla 30101b571fbSUwe Kleine-König return 0; 3026604c655SNeil Armstrong } 3036604c655SNeil Armstrong 3046604c655SNeil Armstrong static const struct pwm_ops pwm_omap_dmtimer_ops = { 3056b28fb6fSLokesh Vutla .apply = pwm_omap_dmtimer_apply, 3066604c655SNeil Armstrong }; 3076604c655SNeil Armstrong 3086604c655SNeil Armstrong static int pwm_omap_dmtimer_probe(struct platform_device *pdev) 3096604c655SNeil Armstrong { 3106604c655SNeil Armstrong struct device_node *np = pdev->dev.of_node; 311b7290cf6SKeerthy struct dmtimer_platform_data *timer_pdata; 312b7290cf6SKeerthy const struct omap_dm_timer_ops *pdata; 3136b28fb6fSLokesh Vutla struct platform_device *timer_pdev; 3146b28fb6fSLokesh Vutla struct pwm_omap_dmtimer_chip *omap; 31554091b5fSLokesh Vutla struct omap_dm_timer *dm_timer; 3166b28fb6fSLokesh Vutla struct device_node *timer; 317b7290cf6SKeerthy int ret = 0; 3186b28fb6fSLokesh Vutla u32 v; 3196604c655SNeil Armstrong 320b7290cf6SKeerthy timer = of_parse_phandle(np, "ti,timers", 0); 321b7290cf6SKeerthy if (!timer) 322b7290cf6SKeerthy return -ENODEV; 323b7290cf6SKeerthy 324b7290cf6SKeerthy timer_pdev = of_find_device_by_node(timer); 325b7290cf6SKeerthy if (!timer_pdev) { 326b7290cf6SKeerthy dev_err(&pdev->dev, "Unable to find Timer pdev\n"); 327b7290cf6SKeerthy ret = -ENODEV; 328c7cb3a1dSUwe Kleine-König goto err_find_timer_pdev; 3296604c655SNeil Armstrong } 3306604c655SNeil Armstrong 331b7290cf6SKeerthy timer_pdata = dev_get_platdata(&timer_pdev->dev); 332b7290cf6SKeerthy if (!timer_pdata) { 33343725febSDavid Rivshin dev_dbg(&pdev->dev, 33443725febSDavid Rivshin "dmtimer pdata structure NULL, deferring probe\n"); 33543725febSDavid Rivshin ret = -EPROBE_DEFER; 336c7cb3a1dSUwe Kleine-König goto err_platdata; 337b7290cf6SKeerthy } 338b7290cf6SKeerthy 339b7290cf6SKeerthy pdata = timer_pdata->timer_ops; 340b7290cf6SKeerthy 341b7290cf6SKeerthy if (!pdata || !pdata->request_by_node || 3426604c655SNeil Armstrong !pdata->free || 3436604c655SNeil Armstrong !pdata->enable || 3446604c655SNeil Armstrong !pdata->disable || 3456604c655SNeil Armstrong !pdata->get_fclk || 3466604c655SNeil Armstrong !pdata->start || 3476604c655SNeil Armstrong !pdata->stop || 3486604c655SNeil Armstrong !pdata->set_load || 3496604c655SNeil Armstrong !pdata->set_match || 3506604c655SNeil Armstrong !pdata->set_pwm || 3516b28fb6fSLokesh Vutla !pdata->get_pwm_status || 3526604c655SNeil Armstrong !pdata->set_prescaler || 3536604c655SNeil Armstrong !pdata->write_counter) { 3546604c655SNeil Armstrong dev_err(&pdev->dev, "Incomplete dmtimer pdata structure\n"); 355b7290cf6SKeerthy ret = -EINVAL; 356c7cb3a1dSUwe Kleine-König goto err_platdata; 3576604c655SNeil Armstrong } 3586604c655SNeil Armstrong 3596604c655SNeil Armstrong if (!of_get_property(timer, "ti,timer-pwm", NULL)) { 3606604c655SNeil Armstrong dev_err(&pdev->dev, "Missing ti,timer-pwm capability\n"); 361b7290cf6SKeerthy ret = -ENODEV; 362c7cb3a1dSUwe Kleine-König goto err_timer_property; 3636604c655SNeil Armstrong } 3646604c655SNeil Armstrong 3656604c655SNeil Armstrong dm_timer = pdata->request_by_node(timer); 366b7290cf6SKeerthy if (!dm_timer) { 367b7290cf6SKeerthy ret = -EPROBE_DEFER; 368c7cb3a1dSUwe Kleine-König goto err_request_timer; 369b7290cf6SKeerthy } 370b7290cf6SKeerthy 3716604c655SNeil Armstrong omap = devm_kzalloc(&pdev->dev, sizeof(*omap), GFP_KERNEL); 3726604c655SNeil Armstrong if (!omap) { 373c4cf7aa5SUwe Kleine-König ret = -ENOMEM; 374c4cf7aa5SUwe Kleine-König goto err_alloc_omap; 3756604c655SNeil Armstrong } 3766604c655SNeil Armstrong 3776604c655SNeil Armstrong omap->pdata = pdata; 3786604c655SNeil Armstrong omap->dm_timer = dm_timer; 379b7290cf6SKeerthy omap->dm_timer_pdev = timer_pdev; 3806604c655SNeil Armstrong 3816604c655SNeil Armstrong /* 3826604c655SNeil Armstrong * Ensure that the timer is stopped before we allow PWM core to call 3836604c655SNeil Armstrong * pwm_enable. 3846604c655SNeil Armstrong */ 3856604c655SNeil Armstrong if (pm_runtime_active(&omap->dm_timer_pdev->dev)) 3866604c655SNeil Armstrong omap->pdata->stop(omap->dm_timer); 3876604c655SNeil Armstrong 388a74a1982SIvaylo Dimitrov if (!of_property_read_u32(pdev->dev.of_node, "ti,prescaler", &v)) 389a74a1982SIvaylo Dimitrov omap->pdata->set_prescaler(omap->dm_timer, v); 390a74a1982SIvaylo Dimitrov 391a74a1982SIvaylo Dimitrov /* setup dmtimer clock source */ 392a74a1982SIvaylo Dimitrov if (!of_property_read_u32(pdev->dev.of_node, "ti,clock-source", &v)) 393a74a1982SIvaylo Dimitrov omap->pdata->set_source(omap->dm_timer, v); 3946604c655SNeil Armstrong 3956604c655SNeil Armstrong omap->chip.dev = &pdev->dev; 3966604c655SNeil Armstrong omap->chip.ops = &pwm_omap_dmtimer_ops; 3976604c655SNeil Armstrong omap->chip.npwm = 1; 3986604c655SNeil Armstrong 399b7290cf6SKeerthy ret = pwmchip_add(&omap->chip); 400b7290cf6SKeerthy if (ret < 0) { 4016604c655SNeil Armstrong dev_err(&pdev->dev, "failed to register PWM\n"); 402c4cf7aa5SUwe Kleine-König goto err_pwmchip_add; 4036604c655SNeil Armstrong } 4046604c655SNeil Armstrong 405c4cf7aa5SUwe Kleine-König of_node_put(timer); 406c4cf7aa5SUwe Kleine-König 4076604c655SNeil Armstrong platform_set_drvdata(pdev, omap); 4086604c655SNeil Armstrong 4096604c655SNeil Armstrong return 0; 410c4cf7aa5SUwe Kleine-König 411c4cf7aa5SUwe Kleine-König err_pwmchip_add: 412c4cf7aa5SUwe Kleine-König 413c4cf7aa5SUwe Kleine-König /* 414c4cf7aa5SUwe Kleine-König * *omap is allocated using devm_kzalloc, 415c4cf7aa5SUwe Kleine-König * so no free necessary here 416c4cf7aa5SUwe Kleine-König */ 417c4cf7aa5SUwe Kleine-König err_alloc_omap: 418c4cf7aa5SUwe Kleine-König 419c4cf7aa5SUwe Kleine-König pdata->free(dm_timer); 420c7cb3a1dSUwe Kleine-König err_request_timer: 421c7cb3a1dSUwe Kleine-König 422c7cb3a1dSUwe Kleine-König err_timer_property: 423c7cb3a1dSUwe Kleine-König err_platdata: 424c7cb3a1dSUwe Kleine-König 425c7cb3a1dSUwe Kleine-König put_device(&timer_pdev->dev); 426c7cb3a1dSUwe Kleine-König err_find_timer_pdev: 427c7cb3a1dSUwe Kleine-König 428c4cf7aa5SUwe Kleine-König of_node_put(timer); 429c4cf7aa5SUwe Kleine-König 430c4cf7aa5SUwe Kleine-König return ret; 4316604c655SNeil Armstrong } 4326604c655SNeil Armstrong 433fed5d59dSUwe Kleine-König static void pwm_omap_dmtimer_remove(struct platform_device *pdev) 4346604c655SNeil Armstrong { 4356604c655SNeil Armstrong struct pwm_omap_dmtimer_chip *omap = platform_get_drvdata(pdev); 43643efdc8fSUwe Kleine-König 437faaa2222SUwe Kleine-König pwmchip_remove(&omap->chip); 4386604c655SNeil Armstrong 4396604c655SNeil Armstrong if (pm_runtime_active(&omap->dm_timer_pdev->dev)) 4406604c655SNeil Armstrong omap->pdata->stop(omap->dm_timer); 4416604c655SNeil Armstrong 4426604c655SNeil Armstrong omap->pdata->free(omap->dm_timer); 4436604c655SNeil Armstrong 444c7cb3a1dSUwe Kleine-König put_device(&omap->dm_timer_pdev->dev); 4456604c655SNeil Armstrong } 4466604c655SNeil Armstrong 4476604c655SNeil Armstrong static const struct of_device_id pwm_omap_dmtimer_of_match[] = { 4486604c655SNeil Armstrong {.compatible = "ti,omap-dmtimer-pwm"}, 4496604c655SNeil Armstrong {} 4506604c655SNeil Armstrong }; 4516604c655SNeil Armstrong MODULE_DEVICE_TABLE(of, pwm_omap_dmtimer_of_match); 4526604c655SNeil Armstrong 4536604c655SNeil Armstrong static struct platform_driver pwm_omap_dmtimer_driver = { 4546604c655SNeil Armstrong .driver = { 4556604c655SNeil Armstrong .name = "omap-dmtimer-pwm", 4567818f0bcSRuan Jinjie .of_match_table = pwm_omap_dmtimer_of_match, 4576604c655SNeil Armstrong }, 4586604c655SNeil Armstrong .probe = pwm_omap_dmtimer_probe, 459fed5d59dSUwe Kleine-König .remove_new = pwm_omap_dmtimer_remove, 4606604c655SNeil Armstrong }; 4616604c655SNeil Armstrong module_platform_driver(pwm_omap_dmtimer_driver); 4626604c655SNeil Armstrong 4636604c655SNeil Armstrong MODULE_AUTHOR("Grant Erickson <marathon96@gmail.com>"); 4646604c655SNeil Armstrong MODULE_AUTHOR("NeilBrown <neilb@suse.de>"); 4656604c655SNeil Armstrong MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 4666604c655SNeil Armstrong MODULE_LICENSE("GPL v2"); 4676604c655SNeil Armstrong MODULE_DESCRIPTION("OMAP PWM Driver using Dual-mode Timers"); 468