[PATCH v8 1/2] PWM: atmel-pwm: add PWM controller driver

Bo Shen voice.shen at atmel.com
Mon Dec 2 22:09:12 EST 2013


Hi Thierry,

On 12/02/2013 06:59 PM, Thierry Reding wrote:
> On Mon, Nov 18, 2013 at 05:13:21PM +0800, Bo Shen wrote:
> [...]
>> diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c
> [...]
>> +/* Max value for duty and period
>
> Block comments should be of this form:
>
> 	/*
> 	 * Max value ...
> 	 * ...
> 	 */

OK, I will use this style.

>> +static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
>> +		int duty_ns, int period_ns)
>> +{
>> +	struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
>> +	unsigned long clk_rate, prd, dty;
>> +	unsigned long long div;
>> +	int ret, pres = 0;
>> +
>> +	clk_rate = clk_get_rate(atmel_pwm->clk);
>> +	div = clk_rate;
>> +
>> +	/* Calculate the period cycles */
>> +	while (div > PWM_MAX_PRD) {
>> +		div = clk_rate / (1 << pres);
>> +		div = div * period_ns;
>> +		/* 1/Hz = 100000000 ns */
>
> I don't think that comment is needed.

This is asked to be added.
And, I think keep it and it won't hurt, what do you think?

>> +		do_div(div, 1000000000);
>> +
>> +		if (pres++ > PRD_MAX_PRES) {
>> +			dev_err(chip->dev, "pres exceed the maximum value\n");
>
> "exceeds"

Thanks for correct it.

>> +			return -EINVAL;
>> +		}
>> +	}
>> +
>> +	/* Calculate the duty cycles */
>> +	prd = div;
>> +	div *= duty_ns;
>> +	do_div(div, period_ns);
>> +	dty = div;
>> +
>> +	ret = clk_enable(atmel_pwm->clk);
>> +	if (ret) {
>> +		dev_err(chip->dev, "failed to enable pwm clock\n");
>
> "PWM clock"

OK, I will change all low case pwm to upper case PWM.

>> +static void atmel_pwm_config_v1(struct pwm_chip *chip, struct pwm_device *pwm,
>> +				int dty, int prd)
>> +{
>> +	struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
>> +	unsigned int val;
>> +
>> +	/*
>> +	 * If the PWM channel is disabled, write value to duty and period
>> +	 * registers directly.
>> +	 * If the PWM channel is enabled, using the update register, it needs
>> +	 * to set bit 10 of CMR to 0
>> +	 */
>
> I think it would make sense to split this comment and move each part
> into the respective conditional branch.

OK, I will split them.

>> +	if (test_bit(PWMF_ENABLED, &pwm->flags)) {
>> +		atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV1_CUPD, dty);
>> +
>> +		val = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR);
>> +		val &= ~PWM_CMR_UPD_CDTY;
>> +		atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val);
>> +	} else {
>> +		atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV1_CDTY, dty);
>> +		atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV1_CPRD, prd);
>> +	}
>> +}
>> +
>> +static void atmel_pwm_config_v2(struct pwm_chip *chip, struct pwm_device *pwm,
>> +				int dty, int prd)
>> +{
>> +	struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
>> +
>> +	/*
>> +	 * If the PWM channel is disabled, write value to duty and period
>> +	 * registers directly.
>> +	 * If the PWM channel is enabled, using the duty update register to
>> +	 * update the value.
>> +	 */
>
> Same here.
>
>> +	if (test_bit(PWMF_ENABLED, &pwm->flags)) {
>> +		atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV2_CDTYUPD, dty);
>> +	} else {
>> +		atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV2_CDTY, dty);
>> +		atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV2_CPRD, prd);
>> +	}
>> +}
>
> Neither version 1 nor version 2 seem to be able to change the period
> while the channel is enabled. Perhaps that should be checked for in
> atmel_pwm_config() and an error (-EBUSY) returned?

The period is configured in dt in device tree, or platform data in non 
device tree. Nowhere will update period. So, not code to update period.
Am I right? If not, please figure out.

>> +
>> +static int atmel_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
>> +		enum pwm_polarity polarity)
>> +{
>> +	struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
>> +	u32 val = 0;
>> +	int ret;
>> +
>> +	if (polarity == PWM_POLARITY_NORMAL)
>> +		val &= ~PWM_CMR_CPOL;
>> +	else
>> +		val |= PWM_CMR_CPOL;
>
> I think I've mentioned this before, but val is always assigned to 0, so
> clearing a bit is a superfluous. Perhaps you need to readl the CMR
> register first before toggling the bit here?

Thanks, we should read CMR, and set the CPOL accordingly.

>> +
>> +	ret = clk_enable(atmel_pwm->clk);
>> +	if (ret) {
>> +		dev_err(chip->dev, "failed to enable pwm clock\n");
>
> "PWM clock"
>
>> +#ifdef CONFIG_OF
>> +static const struct of_device_id atmel_pwm_dt_ids[] = {
>> +	{
>> +		.compatible = "atmel,at91sam9rl-pwm",
>> +		.data = &atmel_pwm_data_v1,
>> +	}, {
>> +		.compatible = "atmel,sama5d3-pwm",
>> +		.data = &atmel_pwm_data_v2,
>> +	}, {
>> +		/* sentinel */
>> +	},
>> +};
>> +MODULE_DEVICE_TABLE(of, atmel_pwm_dt_ids);
>> +#endif
>
> I don't think you can do this. You use this table in a call to
> of_match_device() later on, in code which isn't protected by a
> corresponding #ifdef.

I will remove #ifdef.

>> +static inline const struct atmel_pwm_data * __init
>> +	atmel_pwm_get_driver_data(struct platform_device *pdev)
>
> I don't think __init is warranted here. In fact I think this will give
> you a build warning, because this code is called from atmel_pwm_probe(),
> which in turn isn't marked __init.

OK, I will remove __init.

> Also it's probably not worth marking this inline explicitly. It isn't
> all that short, and the compiler will likely inline it anyway since it's
> only called once.

It only called one, so, it can be inline.

>> +{
>> +	if (pdev->dev.of_node) {
>> +		const struct of_device_id *match;
>> +		match = of_match_device(atmel_pwm_dt_ids, &pdev->dev);
>
> Blank line between the above two for readability.

OK, I will add one blank line.

>> +		if (match == NULL)
>> +			return NULL;
>> +		return match->data;
>
> Same here. And "if (!match)" rather than "if (match == NULL)".

OK, I will change like this.

>> +	}
>> +
>> +	return (struct atmel_pwm_data *)
>> +		platform_get_device_id(pdev)->driver_data;
>
> Please use a temporary variable here to make this more readable, like
> so:
>
> 	struct platform_device_id *id = platform_get_device_id(pdev);
>
> 	...
>
> 	return (struct atmel_pwm_data *)id->driver;
>
>> +}

OK, I will change like this.

>> +static int atmel_pwm_probe(struct platform_device *pdev)
>> +{
>> +	const struct atmel_pwm_data *data;
>> +	struct atmel_pwm_chip *atmel_pwm;
>> +	struct resource *res;
>> +	int ret;
>> +
>> +	atmel_pwm = devm_kzalloc(&pdev->dev, sizeof(*atmel_pwm), GFP_KERNEL);
>> +	if (!atmel_pwm)
>> +		return -ENOMEM;
>
> You could move this further down, so that memory doesn't get allocated
> if atmel_pwm_get_driver_data() or platform_get_resource() fails.

OK, I will move this down.

>> +
>> +	data = atmel_pwm_get_driver_data(pdev);
>> +	if (!data)
>> +		return -ENODEV;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res)
>> +		return -ENODEV;
>
> No need to check the return value here. devm_ioremap_resource() checks
> it for you.

OK, I will remove this check.

>> +
>> +	atmel_pwm->base = devm_ioremap_resource(&pdev->dev, res);
>> +	if (IS_ERR(atmel_pwm->base))
>> +		return PTR_ERR(atmel_pwm->base);
>> +
>> +	atmel_pwm->clk = devm_clk_get(&pdev->dev, NULL);
>> +	if (IS_ERR(atmel_pwm->clk))
>> +		return PTR_ERR(atmel_pwm->clk);
>> +
>> +	ret = clk_prepare(atmel_pwm->clk);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "failed to prepare pwm clock\n");
>
> "PWM clock"
>
>> +		return ret;
>> +	}
>> +
>> +	atmel_pwm->chip.dev = &pdev->dev;
>> +	atmel_pwm->chip.ops = &atmel_pwm_ops;
>> +	if (pdev->dev.of_node) {
>
> Blank line between the above two for readability.

OK, I will add blank line.

>> +		atmel_pwm->chip.of_xlate = of_pwm_xlate_with_flags;
>> +		atmel_pwm->chip.of_pwm_n_cells = 3;
>> +		atmel_pwm->chip.base = -1;
>> +	} else {
>> +		atmel_pwm->chip.base = pdev->id;
>
> That's not correct. The chip cannot be tied to pdev->id, because that ID
> is the instance number of the device. So typically you would have
> devices name like this:
>
> 	atmel-pwm.0
> 	atmel-pwm.1
> 	...
>
> Now, if you have that, then you won't be able to register the second
> instance because the first instance will already have requested PWMs
> 0-3, and setting .base to 1 will cause PWMs 1-4 to be requested, which
> intersects with the range of the first instance.
>
> The same applies of course if you have other PWM controllers in the
> system which have similar instance names.
>
> So the right thing to do here is to provide that number via platform
> data so that platform code can define it, knowing in advance all ranges
> for all other PWM controllers and thereby make sure there's no
> intersection.

OK, I will fix this.

>> +	}
>> +	atmel_pwm->chip.npwm = 4;
>
> Blank line between the above two for readability.

OK, I will add blank line.

>> +	atmel_pwm->config = data->config;
>> +
>> +	ret = pwmchip_add(&atmel_pwm->chip);
>> +	if (ret < 0) {
>> +		dev_err(&pdev->dev, "failed to add pwm chip %d\n", ret);
>
> "PWM chip"
>
> Thierry

Best Regards,
Bo Shen




More information about the linux-arm-kernel mailing list