162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) Overkiz SAS 2012
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author: Boris BREZILLON <b.brezillon@overkiz.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/init.h>
1062306a36Sopenharmony_ci#include <linux/clocksource.h>
1162306a36Sopenharmony_ci#include <linux/clockchips.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/irq.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/clk.h>
1662306a36Sopenharmony_ci#include <linux/err.h>
1762306a36Sopenharmony_ci#include <linux/ioport.h>
1862306a36Sopenharmony_ci#include <linux/io.h>
1962306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
2062306a36Sopenharmony_ci#include <linux/platform_device.h>
2162306a36Sopenharmony_ci#include <linux/pwm.h>
2262306a36Sopenharmony_ci#include <linux/of.h>
2362306a36Sopenharmony_ci#include <linux/regmap.h>
2462306a36Sopenharmony_ci#include <linux/slab.h>
2562306a36Sopenharmony_ci#include <soc/at91/atmel_tcb.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define NPWM	2
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define ATMEL_TC_ACMR_MASK	(ATMEL_TC_ACPA | ATMEL_TC_ACPC |	\
3062306a36Sopenharmony_ci				 ATMEL_TC_AEEVT | ATMEL_TC_ASWTRG)
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define ATMEL_TC_BCMR_MASK	(ATMEL_TC_BCPB | ATMEL_TC_BCPC |	\
3362306a36Sopenharmony_ci				 ATMEL_TC_BEEVT | ATMEL_TC_BSWTRG)
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistruct atmel_tcb_pwm_device {
3662306a36Sopenharmony_ci	unsigned div;			/* PWM clock divider */
3762306a36Sopenharmony_ci	unsigned duty;			/* PWM duty expressed in clk cycles */
3862306a36Sopenharmony_ci	unsigned period;		/* PWM period expressed in clk cycles */
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistruct atmel_tcb_channel {
4262306a36Sopenharmony_ci	u32 enabled;
4362306a36Sopenharmony_ci	u32 cmr;
4462306a36Sopenharmony_ci	u32 ra;
4562306a36Sopenharmony_ci	u32 rb;
4662306a36Sopenharmony_ci	u32 rc;
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistruct atmel_tcb_pwm_chip {
5062306a36Sopenharmony_ci	struct pwm_chip chip;
5162306a36Sopenharmony_ci	spinlock_t lock;
5262306a36Sopenharmony_ci	u8 channel;
5362306a36Sopenharmony_ci	u8 width;
5462306a36Sopenharmony_ci	struct regmap *regmap;
5562306a36Sopenharmony_ci	struct clk *clk;
5662306a36Sopenharmony_ci	struct clk *gclk;
5762306a36Sopenharmony_ci	struct clk *slow_clk;
5862306a36Sopenharmony_ci	struct atmel_tcb_pwm_device pwms[NPWM];
5962306a36Sopenharmony_ci	struct atmel_tcb_channel bkup;
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic const u8 atmel_tcb_divisors[] = { 2, 8, 32, 128, 0, };
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	return container_of(chip, struct atmel_tcb_pwm_chip, chip);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic int atmel_tcb_pwm_request(struct pwm_chip *chip,
7062306a36Sopenharmony_ci				 struct pwm_device *pwm)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
7362306a36Sopenharmony_ci	struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm];
7462306a36Sopenharmony_ci	unsigned cmr;
7562306a36Sopenharmony_ci	int ret;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	ret = clk_prepare_enable(tcbpwmc->clk);
7862306a36Sopenharmony_ci	if (ret)
7962306a36Sopenharmony_ci		return ret;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	tcbpwm->duty = 0;
8262306a36Sopenharmony_ci	tcbpwm->period = 0;
8362306a36Sopenharmony_ci	tcbpwm->div = 0;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	spin_lock(&tcbpwmc->lock);
8662306a36Sopenharmony_ci	regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr);
8762306a36Sopenharmony_ci	/*
8862306a36Sopenharmony_ci	 * Get init config from Timer Counter registers if
8962306a36Sopenharmony_ci	 * Timer Counter is already configured as a PWM generator.
9062306a36Sopenharmony_ci	 */
9162306a36Sopenharmony_ci	if (cmr & ATMEL_TC_WAVE) {
9262306a36Sopenharmony_ci		if (pwm->hwpwm == 0)
9362306a36Sopenharmony_ci			regmap_read(tcbpwmc->regmap,
9462306a36Sopenharmony_ci				    ATMEL_TC_REG(tcbpwmc->channel, RA),
9562306a36Sopenharmony_ci				    &tcbpwm->duty);
9662306a36Sopenharmony_ci		else
9762306a36Sopenharmony_ci			regmap_read(tcbpwmc->regmap,
9862306a36Sopenharmony_ci				    ATMEL_TC_REG(tcbpwmc->channel, RB),
9962306a36Sopenharmony_ci				    &tcbpwm->duty);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		tcbpwm->div = cmr & ATMEL_TC_TCCLKS;
10262306a36Sopenharmony_ci		regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, RC),
10362306a36Sopenharmony_ci			    &tcbpwm->period);
10462306a36Sopenharmony_ci		cmr &= (ATMEL_TC_TCCLKS | ATMEL_TC_ACMR_MASK |
10562306a36Sopenharmony_ci			ATMEL_TC_BCMR_MASK);
10662306a36Sopenharmony_ci	} else
10762306a36Sopenharmony_ci		cmr = 0;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	cmr |= ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO | ATMEL_TC_EEVT_XC0;
11062306a36Sopenharmony_ci	regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), cmr);
11162306a36Sopenharmony_ci	spin_unlock(&tcbpwmc->lock);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return 0;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic void atmel_tcb_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	clk_disable_unprepare(tcbpwmc->clk);
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm,
12462306a36Sopenharmony_ci				  enum pwm_polarity polarity)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
12762306a36Sopenharmony_ci	struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm];
12862306a36Sopenharmony_ci	unsigned cmr;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/*
13162306a36Sopenharmony_ci	 * If duty is 0 the timer will be stopped and we have to
13262306a36Sopenharmony_ci	 * configure the output correctly on software trigger:
13362306a36Sopenharmony_ci	 *  - set output to high if PWM_POLARITY_INVERSED
13462306a36Sopenharmony_ci	 *  - set output to low if PWM_POLARITY_NORMAL
13562306a36Sopenharmony_ci	 *
13662306a36Sopenharmony_ci	 * This is why we're reverting polarity in this case.
13762306a36Sopenharmony_ci	 */
13862306a36Sopenharmony_ci	if (tcbpwm->duty == 0)
13962306a36Sopenharmony_ci		polarity = !polarity;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	spin_lock(&tcbpwmc->lock);
14262306a36Sopenharmony_ci	regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/* flush old setting and set the new one */
14562306a36Sopenharmony_ci	if (pwm->hwpwm == 0) {
14662306a36Sopenharmony_ci		cmr &= ~ATMEL_TC_ACMR_MASK;
14762306a36Sopenharmony_ci		if (polarity == PWM_POLARITY_INVERSED)
14862306a36Sopenharmony_ci			cmr |= ATMEL_TC_ASWTRG_CLEAR;
14962306a36Sopenharmony_ci		else
15062306a36Sopenharmony_ci			cmr |= ATMEL_TC_ASWTRG_SET;
15162306a36Sopenharmony_ci	} else {
15262306a36Sopenharmony_ci		cmr &= ~ATMEL_TC_BCMR_MASK;
15362306a36Sopenharmony_ci		if (polarity == PWM_POLARITY_INVERSED)
15462306a36Sopenharmony_ci			cmr |= ATMEL_TC_BSWTRG_CLEAR;
15562306a36Sopenharmony_ci		else
15662306a36Sopenharmony_ci			cmr |= ATMEL_TC_BSWTRG_SET;
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), cmr);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/*
16262306a36Sopenharmony_ci	 * Use software trigger to apply the new setting.
16362306a36Sopenharmony_ci	 * If both PWM devices in this group are disabled we stop the clock.
16462306a36Sopenharmony_ci	 */
16562306a36Sopenharmony_ci	if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC))) {
16662306a36Sopenharmony_ci		regmap_write(tcbpwmc->regmap,
16762306a36Sopenharmony_ci			     ATMEL_TC_REG(tcbpwmc->channel, CCR),
16862306a36Sopenharmony_ci			     ATMEL_TC_SWTRG | ATMEL_TC_CLKDIS);
16962306a36Sopenharmony_ci		tcbpwmc->bkup.enabled = 1;
17062306a36Sopenharmony_ci	} else {
17162306a36Sopenharmony_ci		regmap_write(tcbpwmc->regmap,
17262306a36Sopenharmony_ci			     ATMEL_TC_REG(tcbpwmc->channel, CCR),
17362306a36Sopenharmony_ci			     ATMEL_TC_SWTRG);
17462306a36Sopenharmony_ci		tcbpwmc->bkup.enabled = 0;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	spin_unlock(&tcbpwmc->lock);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm,
18162306a36Sopenharmony_ci				enum pwm_polarity polarity)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
18462306a36Sopenharmony_ci	struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm];
18562306a36Sopenharmony_ci	u32 cmr;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	/*
18862306a36Sopenharmony_ci	 * If duty is 0 the timer will be stopped and we have to
18962306a36Sopenharmony_ci	 * configure the output correctly on software trigger:
19062306a36Sopenharmony_ci	 *  - set output to high if PWM_POLARITY_INVERSED
19162306a36Sopenharmony_ci	 *  - set output to low if PWM_POLARITY_NORMAL
19262306a36Sopenharmony_ci	 *
19362306a36Sopenharmony_ci	 * This is why we're reverting polarity in this case.
19462306a36Sopenharmony_ci	 */
19562306a36Sopenharmony_ci	if (tcbpwm->duty == 0)
19662306a36Sopenharmony_ci		polarity = !polarity;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	spin_lock(&tcbpwmc->lock);
19962306a36Sopenharmony_ci	regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	/* flush old setting and set the new one */
20262306a36Sopenharmony_ci	cmr &= ~ATMEL_TC_TCCLKS;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	if (pwm->hwpwm == 0) {
20562306a36Sopenharmony_ci		cmr &= ~ATMEL_TC_ACMR_MASK;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		/* Set CMR flags according to given polarity */
20862306a36Sopenharmony_ci		if (polarity == PWM_POLARITY_INVERSED)
20962306a36Sopenharmony_ci			cmr |= ATMEL_TC_ASWTRG_CLEAR;
21062306a36Sopenharmony_ci		else
21162306a36Sopenharmony_ci			cmr |= ATMEL_TC_ASWTRG_SET;
21262306a36Sopenharmony_ci	} else {
21362306a36Sopenharmony_ci		cmr &= ~ATMEL_TC_BCMR_MASK;
21462306a36Sopenharmony_ci		if (polarity == PWM_POLARITY_INVERSED)
21562306a36Sopenharmony_ci			cmr |= ATMEL_TC_BSWTRG_CLEAR;
21662306a36Sopenharmony_ci		else
21762306a36Sopenharmony_ci			cmr |= ATMEL_TC_BSWTRG_SET;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/*
22162306a36Sopenharmony_ci	 * If duty is 0 or equal to period there's no need to register
22262306a36Sopenharmony_ci	 * a specific action on RA/RB and RC compare.
22362306a36Sopenharmony_ci	 * The output will be configured on software trigger and keep
22462306a36Sopenharmony_ci	 * this config till next config call.
22562306a36Sopenharmony_ci	 */
22662306a36Sopenharmony_ci	if (tcbpwm->duty != tcbpwm->period && tcbpwm->duty > 0) {
22762306a36Sopenharmony_ci		if (pwm->hwpwm == 0) {
22862306a36Sopenharmony_ci			if (polarity == PWM_POLARITY_INVERSED)
22962306a36Sopenharmony_ci				cmr |= ATMEL_TC_ACPA_SET | ATMEL_TC_ACPC_CLEAR;
23062306a36Sopenharmony_ci			else
23162306a36Sopenharmony_ci				cmr |= ATMEL_TC_ACPA_CLEAR | ATMEL_TC_ACPC_SET;
23262306a36Sopenharmony_ci		} else {
23362306a36Sopenharmony_ci			if (polarity == PWM_POLARITY_INVERSED)
23462306a36Sopenharmony_ci				cmr |= ATMEL_TC_BCPB_SET | ATMEL_TC_BCPC_CLEAR;
23562306a36Sopenharmony_ci			else
23662306a36Sopenharmony_ci				cmr |= ATMEL_TC_BCPB_CLEAR | ATMEL_TC_BCPC_SET;
23762306a36Sopenharmony_ci		}
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	cmr |= (tcbpwm->div & ATMEL_TC_TCCLKS);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), cmr);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	if (pwm->hwpwm == 0)
24562306a36Sopenharmony_ci		regmap_write(tcbpwmc->regmap,
24662306a36Sopenharmony_ci			     ATMEL_TC_REG(tcbpwmc->channel, RA),
24762306a36Sopenharmony_ci			     tcbpwm->duty);
24862306a36Sopenharmony_ci	else
24962306a36Sopenharmony_ci		regmap_write(tcbpwmc->regmap,
25062306a36Sopenharmony_ci			     ATMEL_TC_REG(tcbpwmc->channel, RB),
25162306a36Sopenharmony_ci			     tcbpwm->duty);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, RC),
25462306a36Sopenharmony_ci		     tcbpwm->period);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/* Use software trigger to apply the new setting */
25762306a36Sopenharmony_ci	regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CCR),
25862306a36Sopenharmony_ci		     ATMEL_TC_SWTRG | ATMEL_TC_CLKEN);
25962306a36Sopenharmony_ci	tcbpwmc->bkup.enabled = 1;
26062306a36Sopenharmony_ci	spin_unlock(&tcbpwmc->lock);
26162306a36Sopenharmony_ci	return 0;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
26562306a36Sopenharmony_ci				int duty_ns, int period_ns)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
26862306a36Sopenharmony_ci	struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm];
26962306a36Sopenharmony_ci	struct atmel_tcb_pwm_device *atcbpwm = NULL;
27062306a36Sopenharmony_ci	int i = 0;
27162306a36Sopenharmony_ci	int slowclk = 0;
27262306a36Sopenharmony_ci	unsigned period;
27362306a36Sopenharmony_ci	unsigned duty;
27462306a36Sopenharmony_ci	unsigned rate = clk_get_rate(tcbpwmc->clk);
27562306a36Sopenharmony_ci	unsigned long long min;
27662306a36Sopenharmony_ci	unsigned long long max;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	/*
27962306a36Sopenharmony_ci	 * Find best clk divisor:
28062306a36Sopenharmony_ci	 * the smallest divisor which can fulfill the period_ns requirements.
28162306a36Sopenharmony_ci	 * If there is a gclk, the first divisor is actually the gclk selector
28262306a36Sopenharmony_ci	 */
28362306a36Sopenharmony_ci	if (tcbpwmc->gclk)
28462306a36Sopenharmony_ci		i = 1;
28562306a36Sopenharmony_ci	for (; i < ARRAY_SIZE(atmel_tcb_divisors); ++i) {
28662306a36Sopenharmony_ci		if (atmel_tcb_divisors[i] == 0) {
28762306a36Sopenharmony_ci			slowclk = i;
28862306a36Sopenharmony_ci			continue;
28962306a36Sopenharmony_ci		}
29062306a36Sopenharmony_ci		min = div_u64((u64)NSEC_PER_SEC * atmel_tcb_divisors[i], rate);
29162306a36Sopenharmony_ci		max = min << tcbpwmc->width;
29262306a36Sopenharmony_ci		if (max >= period_ns)
29362306a36Sopenharmony_ci			break;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	/*
29762306a36Sopenharmony_ci	 * If none of the divisor are small enough to represent period_ns
29862306a36Sopenharmony_ci	 * take slow clock (32KHz).
29962306a36Sopenharmony_ci	 */
30062306a36Sopenharmony_ci	if (i == ARRAY_SIZE(atmel_tcb_divisors)) {
30162306a36Sopenharmony_ci		i = slowclk;
30262306a36Sopenharmony_ci		rate = clk_get_rate(tcbpwmc->slow_clk);
30362306a36Sopenharmony_ci		min = div_u64(NSEC_PER_SEC, rate);
30462306a36Sopenharmony_ci		max = min << tcbpwmc->width;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci		/* If period is too big return ERANGE error */
30762306a36Sopenharmony_ci		if (max < period_ns)
30862306a36Sopenharmony_ci			return -ERANGE;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	duty = div_u64(duty_ns, min);
31262306a36Sopenharmony_ci	period = div_u64(period_ns, min);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	if (pwm->hwpwm == 0)
31562306a36Sopenharmony_ci		atcbpwm = &tcbpwmc->pwms[1];
31662306a36Sopenharmony_ci	else
31762306a36Sopenharmony_ci		atcbpwm = &tcbpwmc->pwms[0];
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	/*
32062306a36Sopenharmony_ci	 * PWM devices provided by the TCB driver are grouped by 2.
32162306a36Sopenharmony_ci	 * PWM devices in a given group must be configured with the
32262306a36Sopenharmony_ci	 * same period_ns.
32362306a36Sopenharmony_ci	 *
32462306a36Sopenharmony_ci	 * We're checking the period value of the second PWM device
32562306a36Sopenharmony_ci	 * in this group before applying the new config.
32662306a36Sopenharmony_ci	 */
32762306a36Sopenharmony_ci	if ((atcbpwm && atcbpwm->duty > 0 &&
32862306a36Sopenharmony_ci			atcbpwm->duty != atcbpwm->period) &&
32962306a36Sopenharmony_ci		(atcbpwm->div != i || atcbpwm->period != period)) {
33062306a36Sopenharmony_ci		dev_err(chip->dev,
33162306a36Sopenharmony_ci			"failed to configure period_ns: PWM group already configured with a different value\n");
33262306a36Sopenharmony_ci		return -EINVAL;
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	tcbpwm->period = period;
33662306a36Sopenharmony_ci	tcbpwm->div = i;
33762306a36Sopenharmony_ci	tcbpwm->duty = duty;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	return 0;
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic int atmel_tcb_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
34362306a36Sopenharmony_ci			       const struct pwm_state *state)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	int duty_cycle, period;
34662306a36Sopenharmony_ci	int ret;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	if (!state->enabled) {
34962306a36Sopenharmony_ci		atmel_tcb_pwm_disable(chip, pwm, state->polarity);
35062306a36Sopenharmony_ci		return 0;
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	period = state->period < INT_MAX ? state->period : INT_MAX;
35462306a36Sopenharmony_ci	duty_cycle = state->duty_cycle < INT_MAX ? state->duty_cycle : INT_MAX;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	ret = atmel_tcb_pwm_config(chip, pwm, duty_cycle, period);
35762306a36Sopenharmony_ci	if (ret)
35862306a36Sopenharmony_ci		return ret;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	return atmel_tcb_pwm_enable(chip, pwm, state->polarity);
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic const struct pwm_ops atmel_tcb_pwm_ops = {
36462306a36Sopenharmony_ci	.request = atmel_tcb_pwm_request,
36562306a36Sopenharmony_ci	.free = atmel_tcb_pwm_free,
36662306a36Sopenharmony_ci	.apply = atmel_tcb_pwm_apply,
36762306a36Sopenharmony_ci	.owner = THIS_MODULE,
36862306a36Sopenharmony_ci};
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic struct atmel_tcb_config tcb_rm9200_config = {
37162306a36Sopenharmony_ci	.counter_width = 16,
37262306a36Sopenharmony_ci};
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic struct atmel_tcb_config tcb_sam9x5_config = {
37562306a36Sopenharmony_ci	.counter_width = 32,
37662306a36Sopenharmony_ci};
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_cistatic struct atmel_tcb_config tcb_sama5d2_config = {
37962306a36Sopenharmony_ci	.counter_width = 32,
38062306a36Sopenharmony_ci	.has_gclk = 1,
38162306a36Sopenharmony_ci};
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic const struct of_device_id atmel_tcb_of_match[] = {
38462306a36Sopenharmony_ci	{ .compatible = "atmel,at91rm9200-tcb", .data = &tcb_rm9200_config, },
38562306a36Sopenharmony_ci	{ .compatible = "atmel,at91sam9x5-tcb", .data = &tcb_sam9x5_config, },
38662306a36Sopenharmony_ci	{ .compatible = "atmel,sama5d2-tcb", .data = &tcb_sama5d2_config, },
38762306a36Sopenharmony_ci	{ /* sentinel */ }
38862306a36Sopenharmony_ci};
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistatic int atmel_tcb_pwm_probe(struct platform_device *pdev)
39162306a36Sopenharmony_ci{
39262306a36Sopenharmony_ci	const struct of_device_id *match;
39362306a36Sopenharmony_ci	struct atmel_tcb_pwm_chip *tcbpwm;
39462306a36Sopenharmony_ci	const struct atmel_tcb_config *config;
39562306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
39662306a36Sopenharmony_ci	char clk_name[] = "t0_clk";
39762306a36Sopenharmony_ci	int err;
39862306a36Sopenharmony_ci	int channel;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	tcbpwm = devm_kzalloc(&pdev->dev, sizeof(*tcbpwm), GFP_KERNEL);
40162306a36Sopenharmony_ci	if (tcbpwm == NULL)
40262306a36Sopenharmony_ci		return -ENOMEM;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	err = of_property_read_u32(np, "reg", &channel);
40562306a36Sopenharmony_ci	if (err < 0) {
40662306a36Sopenharmony_ci		dev_err(&pdev->dev,
40762306a36Sopenharmony_ci			"failed to get Timer Counter Block channel from device tree (error: %d)\n",
40862306a36Sopenharmony_ci			err);
40962306a36Sopenharmony_ci		return err;
41062306a36Sopenharmony_ci	}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	tcbpwm->regmap = syscon_node_to_regmap(np->parent);
41362306a36Sopenharmony_ci	if (IS_ERR(tcbpwm->regmap))
41462306a36Sopenharmony_ci		return PTR_ERR(tcbpwm->regmap);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	tcbpwm->slow_clk = of_clk_get_by_name(np->parent, "slow_clk");
41762306a36Sopenharmony_ci	if (IS_ERR(tcbpwm->slow_clk))
41862306a36Sopenharmony_ci		return PTR_ERR(tcbpwm->slow_clk);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	clk_name[1] += channel;
42162306a36Sopenharmony_ci	tcbpwm->clk = of_clk_get_by_name(np->parent, clk_name);
42262306a36Sopenharmony_ci	if (IS_ERR(tcbpwm->clk))
42362306a36Sopenharmony_ci		tcbpwm->clk = of_clk_get_by_name(np->parent, "t0_clk");
42462306a36Sopenharmony_ci	if (IS_ERR(tcbpwm->clk)) {
42562306a36Sopenharmony_ci		err = PTR_ERR(tcbpwm->clk);
42662306a36Sopenharmony_ci		goto err_slow_clk;
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	match = of_match_node(atmel_tcb_of_match, np->parent);
43062306a36Sopenharmony_ci	config = match->data;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	if (config->has_gclk) {
43362306a36Sopenharmony_ci		tcbpwm->gclk = of_clk_get_by_name(np->parent, "gclk");
43462306a36Sopenharmony_ci		if (IS_ERR(tcbpwm->gclk)) {
43562306a36Sopenharmony_ci			err = PTR_ERR(tcbpwm->gclk);
43662306a36Sopenharmony_ci			goto err_clk;
43762306a36Sopenharmony_ci		}
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	tcbpwm->chip.dev = &pdev->dev;
44162306a36Sopenharmony_ci	tcbpwm->chip.ops = &atmel_tcb_pwm_ops;
44262306a36Sopenharmony_ci	tcbpwm->chip.npwm = NPWM;
44362306a36Sopenharmony_ci	tcbpwm->channel = channel;
44462306a36Sopenharmony_ci	tcbpwm->width = config->counter_width;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	err = clk_prepare_enable(tcbpwm->slow_clk);
44762306a36Sopenharmony_ci	if (err)
44862306a36Sopenharmony_ci		goto err_gclk;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	spin_lock_init(&tcbpwm->lock);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	err = pwmchip_add(&tcbpwm->chip);
45362306a36Sopenharmony_ci	if (err < 0)
45462306a36Sopenharmony_ci		goto err_disable_clk;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	platform_set_drvdata(pdev, tcbpwm);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	return 0;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cierr_disable_clk:
46162306a36Sopenharmony_ci	clk_disable_unprepare(tcbpwm->slow_clk);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cierr_gclk:
46462306a36Sopenharmony_ci	clk_put(tcbpwm->gclk);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cierr_clk:
46762306a36Sopenharmony_ci	clk_put(tcbpwm->clk);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cierr_slow_clk:
47062306a36Sopenharmony_ci	clk_put(tcbpwm->slow_clk);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	return err;
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_cistatic void atmel_tcb_pwm_remove(struct platform_device *pdev)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	pwmchip_remove(&tcbpwm->chip);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	clk_disable_unprepare(tcbpwm->slow_clk);
48262306a36Sopenharmony_ci	clk_put(tcbpwm->gclk);
48362306a36Sopenharmony_ci	clk_put(tcbpwm->clk);
48462306a36Sopenharmony_ci	clk_put(tcbpwm->slow_clk);
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic const struct of_device_id atmel_tcb_pwm_dt_ids[] = {
48862306a36Sopenharmony_ci	{ .compatible = "atmel,tcb-pwm", },
48962306a36Sopenharmony_ci	{ /* sentinel */ }
49062306a36Sopenharmony_ci};
49162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
49462306a36Sopenharmony_cistatic int atmel_tcb_pwm_suspend(struct device *dev)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	struct atmel_tcb_pwm_chip *tcbpwm = dev_get_drvdata(dev);
49762306a36Sopenharmony_ci	struct atmel_tcb_channel *chan = &tcbpwm->bkup;
49862306a36Sopenharmony_ci	unsigned int channel = tcbpwm->channel;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, CMR), &chan->cmr);
50162306a36Sopenharmony_ci	regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, RA), &chan->ra);
50262306a36Sopenharmony_ci	regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, RB), &chan->rb);
50362306a36Sopenharmony_ci	regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, RC), &chan->rc);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	return 0;
50662306a36Sopenharmony_ci}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_cistatic int atmel_tcb_pwm_resume(struct device *dev)
50962306a36Sopenharmony_ci{
51062306a36Sopenharmony_ci	struct atmel_tcb_pwm_chip *tcbpwm = dev_get_drvdata(dev);
51162306a36Sopenharmony_ci	struct atmel_tcb_channel *chan = &tcbpwm->bkup;
51262306a36Sopenharmony_ci	unsigned int channel = tcbpwm->channel;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, CMR), chan->cmr);
51562306a36Sopenharmony_ci	regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, RA), chan->ra);
51662306a36Sopenharmony_ci	regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, RB), chan->rb);
51762306a36Sopenharmony_ci	regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, RC), chan->rc);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	if (chan->enabled)
52062306a36Sopenharmony_ci		regmap_write(tcbpwm->regmap,
52162306a36Sopenharmony_ci			     ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
52262306a36Sopenharmony_ci			     ATMEL_TC_REG(channel, CCR));
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	return 0;
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci#endif
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(atmel_tcb_pwm_pm_ops, atmel_tcb_pwm_suspend,
52962306a36Sopenharmony_ci			 atmel_tcb_pwm_resume);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic struct platform_driver atmel_tcb_pwm_driver = {
53262306a36Sopenharmony_ci	.driver = {
53362306a36Sopenharmony_ci		.name = "atmel-tcb-pwm",
53462306a36Sopenharmony_ci		.of_match_table = atmel_tcb_pwm_dt_ids,
53562306a36Sopenharmony_ci		.pm = &atmel_tcb_pwm_pm_ops,
53662306a36Sopenharmony_ci	},
53762306a36Sopenharmony_ci	.probe = atmel_tcb_pwm_probe,
53862306a36Sopenharmony_ci	.remove_new = atmel_tcb_pwm_remove,
53962306a36Sopenharmony_ci};
54062306a36Sopenharmony_cimodule_platform_driver(atmel_tcb_pwm_driver);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ciMODULE_AUTHOR("Boris BREZILLON <b.brezillon@overkiz.com>");
54362306a36Sopenharmony_ciMODULE_DESCRIPTION("Atmel Timer Counter Pulse Width Modulation Driver");
54462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
545