// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2015 Neil Armstrong <[email protected]> * Copyright (c) 2014 Joachim Eastwood <[email protected]> * Copyright (c) 2012 NeilBrown <[email protected]> * Heavily based on earlier code which is: * Copyright (c) 2010 Grant Erickson <[email protected]> * * Also based on pwm-samsung.c * * Description: * This file is the core OMAP support for the generic, Linux * PWM driver / controller, using the OMAP's dual-mode timers * with a timer counter that goes up. When it overflows it gets * reloaded with the load value and the pwm output goes up. * When counter matches with match register, the output goes down. * Reference Manual: https://www.ti.com/lit/ug/spruh73q/spruh73q.pdf * * Limitations: * - When PWM is stopped, timer counter gets stopped immediately. This * doesn't allow the current PWM period to complete and stops abruptly. * - When PWM is running and changing both duty cycle and period, * we cannot prevent in software that the output might produce * a period with mixed settings. Especially when period/duty_cyle * is updated while the pwm pin is high, current pwm period/duty_cycle * can get updated as below based on the current timer counter: * - period for current cycle = current_period + new period * - duty_cycle for current period = current period + new duty_cycle. * - PWM OMAP DM timer cannot change the polarity when pwm is active. When * user requests a change in polarity when in active state: * - PWM is stopped abruptly(without completing the current cycle) * - Polarity is changed * - A fresh cycle is started. */ #include <linux/clk.h> #include <linux/err.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_platform.h> #include <clocksource/timer-ti-dm.h> #include <linux/platform_data/dmtimer-omap.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/pwm.h> #include <linux/slab.h> #include <linux/time.h> #define DM_TIMER_LOAD_MIN … #define DM_TIMER_MAX … /** * struct pwm_omap_dmtimer_chip - Structure representing a pwm chip * corresponding to omap dmtimer. * @dm_timer: Pointer to omap dm timer. * @pdata: Pointer to omap dm timer ops. * @dm_timer_pdev: Pointer to omap dm timer platform device */ struct pwm_omap_dmtimer_chip { … }; static inline struct pwm_omap_dmtimer_chip * to_pwm_omap_dmtimer_chip(struct pwm_chip *chip) { … } /** * pwm_omap_dmtimer_get_clock_cycles() - Get clock cycles in a time frame * @clk_rate: pwm timer clock rate * @ns: time frame in nano seconds. * * Return number of clock cycles in a given period(ins ns). */ static u32 pwm_omap_dmtimer_get_clock_cycles(unsigned long clk_rate, int ns) { … } /** * pwm_omap_dmtimer_start() - Start the pwm omap dm timer in pwm mode * @omap: Pointer to pwm omap dm timer chip */ static void pwm_omap_dmtimer_start(struct pwm_omap_dmtimer_chip *omap) { … } /** * pwm_omap_dmtimer_is_enabled() - Detect if the pwm is enabled. * @omap: Pointer to pwm omap dm timer chip * * Return true if pwm is enabled else false. */ static bool pwm_omap_dmtimer_is_enabled(struct pwm_omap_dmtimer_chip *omap) { … } /** * pwm_omap_dmtimer_polarity() - Detect the polarity of pwm. * @omap: Pointer to pwm omap dm timer chip * * Return the polarity of pwm. */ static int pwm_omap_dmtimer_polarity(struct pwm_omap_dmtimer_chip *omap) { … } /** * pwm_omap_dmtimer_config() - Update the configuration of pwm omap dm timer * @chip: Pointer to PWM controller * @pwm: Pointer to PWM channel * @duty_ns: New duty cycle in nano seconds * @period_ns: New period in nano seconds * * Return 0 if successfully changed the period/duty_cycle else appropriate * error. */ static int pwm_omap_dmtimer_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { … } /** * pwm_omap_dmtimer_set_polarity() - Changes the polarity of the pwm dm timer. * @chip: Pointer to PWM controller * @pwm: Pointer to PWM channel * @polarity: New pwm polarity to be set */ static void pwm_omap_dmtimer_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, enum pwm_polarity polarity) { … } /** * pwm_omap_dmtimer_apply() - Changes the state of the pwm omap dm timer. * @chip: Pointer to PWM controller * @pwm: Pointer to PWM channel * @state: New state to apply * * Return 0 if successfully changed the state else appropriate error. */ static int pwm_omap_dmtimer_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { … } static const struct pwm_ops pwm_omap_dmtimer_ops = …; static int pwm_omap_dmtimer_probe(struct platform_device *pdev) { … } static void pwm_omap_dmtimer_remove(struct platform_device *pdev) { … } static const struct of_device_id pwm_omap_dmtimer_of_match[] = …; MODULE_DEVICE_TABLE(of, pwm_omap_dmtimer_of_match); static struct platform_driver pwm_omap_dmtimer_driver = …; module_platform_driver(…) …; MODULE_AUTHOR(…) …; MODULE_AUTHOR(…) …; MODULE_AUTHOR(…) …; MODULE_LICENSE(…) …; MODULE_DESCRIPTION(…) …;