RE: [PATCH] Add OMAP Support for Generic PWM Devices using Dual-mode Timers

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



> -----Original Message-----
> From: linux-omap-owner@xxxxxxxxxxxxxxx 
> [mailto:linux-omap-owner@xxxxxxxxxxxxxxx] On Behalf Of Grant Erickson
> Sent: Monday, November 15, 2010 12:59 AM
> To: linux-omap@xxxxxxxxxxxxxxx
> Cc: Tony Lindgren
> Subject: [PATCH] Add OMAP Support for Generic PWM Devices 
> using Dual-mode Timers
> 
> This patch adds support to request and use one or more of the OMAP
> dual-mode timers as a generic PWM device compatible with other generic
> PWM drivers such as the PWM backlight or PWM beeper driver.
> 
> Boards can register such devices using platform data such as 
> in the following
> example:
> 
> 	static struct omap2_pwm_platform_config pwm_config = {
> 	    .timer_id           = 10,   // GPT10_PWM_EVT
> 	    .polarity           = 1     // Active-high
> 	};
> 
> 	static struct platform_device pwm_device = {
> 	    .name               = "omap-pwm",
> 	    .id                 = 0,
> 	    .dev                = {
> 	        .platform_data  = &pwm_config
> 	    }
> 	};
> 
> Signed-off-by: Grant Erickson <marathon96@xxxxxxxxx>

[snip]...[snip]

[sp] Few additional comments that should be considered while submitting
     next patch revision.

> +/* Preprocessor Definitions */
> +
> +#undef OMAP_PWM_DEBUG
> +
> +#if defined(OMAP_PWM_DEBUG)
> +#define DBG(args...)			\
> +	do {						\
> +		pr_info(args);			\
> +	} while (0)
> +#define DEV_DBG(dev, args...)	\
> +	do {						\
> +		dev_info(dev, args);	\
> +	} while (0)
> +#else
> +#define DBG(args...)			\
> +	do { } while (0)
> +#define DEV_DBG(dev, args...)	\
> +	do { } while (0)
> +#endif /* defined(OMAP_PWM_DEBUG) */

[sp] pr_info(), dev_info() are already macros - conditional to DEBUG.
     We should use them instead of defining another layer.

> +
> +#define DM_TIMER_LOAD_MIN		0xFFFFFFFE
> +
> +/* Type Definitions */
> +
> +/**
> + * struct pwm_device - opaque internal PWM device instance state
> + * @head: list head for all PWMs managed by this driver.
> + * @pdev: corresponding platform device associated with this 
> device instance.
> + * @dm_timer: corresponding dual-mode timer associated with 
> this device
> + *  instance.
> + * @config: platform-specific configuration data.
> + * @label: description label.
> + * @use_count: use count.
> + * @pwm_id: generic PWM ID requested for this device instance.
> + *
> + * As far as clients of the PWM driver are concerned, PWM devices are
> + * opaque abstract objects. Consequently, this structure is used for
> + * tracking internal device instance state but is otherwise just a
> + * instance reference externally.
> + */
> +
> +struct pwm_device {
> +	struct list_head				   head;
> +	struct platform_device			  *pdev;
> +	struct omap_dm_timer			  *dm_timer;
> +	struct omap2_pwm_platform_config   config;
> +	const char						
>   *label;
> +	unsigned int					   use_count;
> +	unsigned int					   pwm_id;
> +};

[sp] This block has mix of tabs and white spaces. We should be consistent.
     There are similar instances below as well...

     You may also want to line up the field names - they start at different columns.

> +
> +/* Function Prototypes */
> +
> +static int __devinit omap_pwm_probe(struct platform_device *pdev);
> +static int __devexit omap_pwm_remove(struct platform_device *pdev);
> +
> +/* Global Variables */
> +
> +static struct platform_driver omap_pwm_driver = {
> +	.driver		= {
> +		.name	= "omap-pwm",
> +		.owner	= THIS_MODULE,
> +	},
> +	.probe		= omap_pwm_probe,
> +	.remove		= __devexit_p(omap_pwm_remove)
> +};
> +
> +/* List and associated lock for managing generic PWM devices bound to
> + * this driver.
> + */
> +
[sp] White space not needed. Similar instances below as well.

> +static DEFINE_MUTEX(pwm_lock);
> +static LIST_HEAD(pwm_list);
> +

[snip]...[snip]

> +/**
> + * pwm_config - configures the generic PWM device to the 
> specified parameters.
> + * @pwm: A pointer to the PWM device to configure.
> + * @duty_ns: The duty period of the PWM, in nanoseconds.
> + * @period_ns: The overall period of the PWM, in nanoseconds.
> + *
> + * Returns 0 if the generic PWM device was successfully configured;
> + * otherwise, < 0 on error.
> + */
> +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
> +{
> +	int status = 0;
> +	const bool enable = true;
> +	const bool autoreload = true;
> +	const bool toggle = true;
> +	const int trigger = OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE;

[sp] Based on code abobe, I believe that OMAP...COMPARE is macro;
     shouldn't we use it as is - where trigger is used in code below?

> +	int load_value, match_value;
> +	unsigned long clk_rate;
> +
> +	DEV_DBG(&pwm->pdev->dev,
> +			"duty cycle: %d, period %d\n",
> +			duty_ns, period_ns);
> +
> +	clk_rate = clk_get_rate(omap_dm_timer_get_fclk(pwm->dm_timer));
> +
> +	/* Calculate the appropriate load and match values based on the
> +	 * specified period and duty cycle. The load value determines the
> +	 * cycle time and the match value determines the duty cycle.
> +	 */
> +
> +	load_value = pwm_calc_value(clk_rate, period_ns);
> +	match_value = pwm_calc_value(clk_rate, period_ns - duty_ns);
> +
> +	/* We MUST enable yet stop the associated dual-mode timer before
> +	 * attempting to write its registers.
> +	 */
> +
> +	omap_dm_timer_enable(pwm->dm_timer);
> +	omap_dm_timer_stop(pwm->dm_timer);
> +
> +	omap_dm_timer_set_load(pwm->dm_timer, autoreload, load_value);
> +	omap_dm_timer_set_match(pwm->dm_timer, enable, match_value);
> +
> +	DEV_DBG(&pwm->pdev->dev,
> +			"load value: %#08x (%d), "
> +			"match value: %#08x (%d)\n",
> +			load_value, load_value,
> +			match_value, match_value);
> +
> +	omap_dm_timer_set_pwm(pwm->dm_timer,
> +						  !pwm->config.polarity,
> +						  toggle,
> +						  trigger);
> +
> +	/* Set the counter to generate an overflow event immediately. */
> +
> +	omap_dm_timer_write_counter(pwm->dm_timer, DM_TIMER_LOAD_MIN);

[sp] I am assuming that call to this function would be followed by
     another call to pwm_enable().

     omap_dm_timer_write_counter() is being called in pwm_enable()
     with exactly same arguments. Is is necessary duplication?
     If no, call here should be removed.

> +
> +	/* Now that we're done configuring the dual-mode timer, disable it
> +	 * again. We'll enable and start it later, when requested.
> +	 */
> +
> +	omap_dm_timer_disable(pwm->dm_timer);
> +
> +	return status;
> +}
> +EXPORT_SYMBOL(pwm_config);
> +
> +/**
> + * pwm_enable - enable the generic PWM device.
> + * @pwm: A pointer to the generic PWM device to enable.
> + *
> + * Returns 0 if the generic PWM device was successfully enabled;
> + * otherwise, < 0 on error.
> + */
> +int pwm_enable(struct pwm_device *pwm)
> +{
> +	int status = 0;

[sp] This variable isn't assigned/modified below. Not required.
     function can simply return 0.

> +
> +	/* Enable the counter--always--before attempting to write its
> +	 * registers and then set the timer to its minimum load value to
> +	 * ensure we get an overflow event right away once we start it.
> +	 */
> +
> +	omap_dm_timer_enable(pwm->dm_timer);
> +	omap_dm_timer_write_counter(pwm->dm_timer, DM_TIMER_LOAD_MIN);
> +	omap_dm_timer_start(pwm->dm_timer);
> +
> +	return status;
> +}
> +EXPORT_SYMBOL(pwm_enable);
> +
> +/**
> + * pwm_disable - disable the generic PWM device.
> + * @pwm: A pointer to the generic PWM device to disable.
> + */
> +void pwm_disable(struct pwm_device *pwm)
> +{
> +	omap_dm_timer_enable(pwm->dm_timer);
> +	omap_dm_timer_stop(pwm->dm_timer);
> +	omap_dm_timer_disable(pwm->dm_timer);
> +}
> +EXPORT_SYMBOL(pwm_disable);
> +
> +/**
> + * omap_pwm_probe - check for the PWM and bind it to the driver.
> + * @pdev: A pointer to the platform device node associated with the
> + *        PWM instance to be probed for driver binding.
> + *
> + * Returns 0 if the PWM instance was successfully bound to 
> the driver;
> + * otherwise, < 0 on error.
> + */
> +static int __devinit omap_pwm_probe(struct platform_device *pdev)
> +{
> +	struct pwm_device *pwm = NULL;
> +	struct omap2_pwm_platform_config *pdata = NULL;
> +	int status = 0;
> +
> +	pdata = ((struct omap2_pwm_platform_config 
> *)(pdev->dev.platform_data));
> +
> +	BUG_ON(pdata == NULL);
> +
> +	if (pdata == NULL) {
> +		dev_err(&pdev->dev, "Could not find required 
> platform data.\n");
> +		status = -ENOENT;
> +		goto done;

[sp] We can simply return status from here. goto and label can be avoided.

> +	}
> +
> +	/* Allocate memory for the driver-private PWM data and state */
> +
> +	pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
> +
> +	if (pwm == NULL) {
> +		dev_err(&pdev->dev, "Could not allocate memory.\n");
> +		status = -ENOMEM;
> +		goto done;

[sp] We can simply return status from here. goto and label can be avoided.

> +	}
> +
> +	/* Request the OMAP dual-mode timer that will be bound to and
> +	 * associated with this generic PWM.
> +	 */
> +
> +	pwm->dm_timer = omap_dm_timer_request_specific(pdata->timer_id);
> +
> +	if (pwm->dm_timer == NULL) {
> +		status = -ENOENT;
> +		goto err_free;
> +	}
> +
> +	/* Configure the source for the dual-mode timer backing this
> +	 * generic PWM device. The clock source will ultimately 
> determine
> +	 * how small or large the PWM frequency can be.
> +	 *
> +	 * At some point, it's probably worth revisiting moving this to
> +	 * the configure method and choosing either the slow- or
> +	 * system-clock source as appropriate for the desired 
> PWM period.
> +	 */
> +
> +	omap_dm_timer_set_source(pwm->dm_timer, OMAP_TIMER_SRC_SYS_CLK);
> +
> +	/* Cache away other miscellaneous driver-private data and state
> +	 * information and add the driver-private data to the platform
> +	 * device.
> +	 */
> +
> +	pwm->pdev = pdev;
> +	pwm->pwm_id = pdev->id;
> +	pwm->config = *pdata;
> +
> +	platform_set_drvdata(pdev, pwm);
> +
> +	/* Finally, push the added generic PWM device to the end of the
> +	 * list of available generic PWM devices.
> +	 */
> +
> +	mutex_lock(&pwm_lock);
> +	list_add_tail(&pwm->head, &pwm_list);
> +	mutex_unlock(&pwm_lock);
> +
> +	status = 0;
> +	goto done;
> +
> + err_free:
> +	kfree(pwm);
> +
> + done:
> +	return status;
> +}
> +
> +/**
> + * omap_pwm_remove - unbind the specified PWM platform 
> device from the driver.
> + * @pdev: A pointer to the platform device node associated with the
> + *        PWM instance to be unbound/removed.
> + *
> + * Returns 0 if the PWM was successfully removed as a 
> platform device;
> + * otherwise, < 0 on error.
> + */
> +static int __devexit omap_pwm_remove(struct platform_device *pdev)
> +{
> +	struct pwm_device *pwm = NULL;
> +	int status = 0;
> +
> +	/* Attempt to get the driver-private data from the 
> platform device
> +	 * node.
> +	 */
> +
> +	pwm = platform_get_drvdata(pdev);
> +
> +	if (pwm == NULL) {
> +		status = -ENODEV;
> +		goto done;

[sp] We can simply return status from here. goto and label can be avoided.

> +	}
> +
> +	/* Remove the generic PWM device from the list of available
> +	 * generic PWM devices.
> +	 */
> +
> +	mutex_lock(&pwm_lock);
> +	list_del(&pwm->head);
> +	mutex_unlock(&pwm_lock);
> +
> +	/* Unbind the OMAP dual-mode timer associated with the 
> generic PWM
> +	 * device.
> +	 */
> +
> +	omap_dm_timer_free(pwm->dm_timer);
> +
> +	/* Finally, release the memory associated with the 
> driver-private
> +	 * data and state.
> +	 */
> +
> +	kfree(pwm);
> +
> + done:
> +	return status;
> +}

[snip]...[snip]
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Arm (vger)]     [ARM Kernel]     [ARM MSM]     [Linux Tegra]     [Linux WPAN Networking]     [Linux Wireless Networking]     [Maemo Users]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux