brintos

brintos / linux-shallow public Read only

0
0
Text · 14.1 KiB · 5ee4254 Raw
537 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) Overkiz SAS 20124 *5 * Author: Boris BREZILLON <b.brezillon@overkiz.com>6 */7 8#include <linux/module.h>9#include <linux/init.h>10#include <linux/clocksource.h>11#include <linux/clockchips.h>12#include <linux/interrupt.h>13#include <linux/irq.h>14 15#include <linux/clk.h>16#include <linux/err.h>17#include <linux/ioport.h>18#include <linux/io.h>19#include <linux/mfd/syscon.h>20#include <linux/platform_device.h>21#include <linux/pwm.h>22#include <linux/of.h>23#include <linux/regmap.h>24#include <linux/slab.h>25#include <soc/at91/atmel_tcb.h>26 27#define NPWM	228 29#define ATMEL_TC_ACMR_MASK	(ATMEL_TC_ACPA | ATMEL_TC_ACPC |	\30				 ATMEL_TC_AEEVT | ATMEL_TC_ASWTRG)31 32#define ATMEL_TC_BCMR_MASK	(ATMEL_TC_BCPB | ATMEL_TC_BCPC |	\33				 ATMEL_TC_BEEVT | ATMEL_TC_BSWTRG)34 35struct atmel_tcb_pwm_device {36	unsigned div;			/* PWM clock divider */37	unsigned duty;			/* PWM duty expressed in clk cycles */38	unsigned period;		/* PWM period expressed in clk cycles */39};40 41struct atmel_tcb_channel {42	u32 enabled;43	u32 cmr;44	u32 ra;45	u32 rb;46	u32 rc;47};48 49struct atmel_tcb_pwm_chip {50	spinlock_t lock;51	u8 channel;52	u8 width;53	struct regmap *regmap;54	struct clk *clk;55	struct clk *gclk;56	struct clk *slow_clk;57	struct atmel_tcb_pwm_device pwms[NPWM];58	struct atmel_tcb_channel bkup;59};60 61static const u8 atmel_tcb_divisors[] = { 2, 8, 32, 128, 0, };62 63static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip)64{65	return pwmchip_get_drvdata(chip);66}67 68static int atmel_tcb_pwm_request(struct pwm_chip *chip,69				 struct pwm_device *pwm)70{71	struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);72	struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm];73	unsigned cmr;74	int ret;75 76	ret = clk_prepare_enable(tcbpwmc->clk);77	if (ret)78		return ret;79 80	tcbpwm->duty = 0;81	tcbpwm->period = 0;82	tcbpwm->div = 0;83 84	guard(spinlock)(&tcbpwmc->lock);85 86	regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr);87	/*88	 * Get init config from Timer Counter registers if89	 * Timer Counter is already configured as a PWM generator.90	 */91	if (cmr & ATMEL_TC_WAVE) {92		if (pwm->hwpwm == 0)93			regmap_read(tcbpwmc->regmap,94				    ATMEL_TC_REG(tcbpwmc->channel, RA),95				    &tcbpwm->duty);96		else97			regmap_read(tcbpwmc->regmap,98				    ATMEL_TC_REG(tcbpwmc->channel, RB),99				    &tcbpwm->duty);100 101		tcbpwm->div = cmr & ATMEL_TC_TCCLKS;102		regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, RC),103			    &tcbpwm->period);104		cmr &= (ATMEL_TC_TCCLKS | ATMEL_TC_ACMR_MASK |105			ATMEL_TC_BCMR_MASK);106	} else107		cmr = 0;108 109	cmr |= ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO | ATMEL_TC_EEVT_XC0;110	regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), cmr);111 112	return 0;113}114 115static void atmel_tcb_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)116{117	struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);118 119	clk_disable_unprepare(tcbpwmc->clk);120}121 122static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm,123				  enum pwm_polarity polarity)124{125	struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);126	struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm];127	unsigned cmr;128 129	/*130	 * If duty is 0 the timer will be stopped and we have to131	 * configure the output correctly on software trigger:132	 *  - set output to high if PWM_POLARITY_INVERSED133	 *  - set output to low if PWM_POLARITY_NORMAL134	 *135	 * This is why we're reverting polarity in this case.136	 */137	if (tcbpwm->duty == 0)138		polarity = !polarity;139 140	regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr);141 142	/* flush old setting and set the new one */143	if (pwm->hwpwm == 0) {144		cmr &= ~ATMEL_TC_ACMR_MASK;145		if (polarity == PWM_POLARITY_INVERSED)146			cmr |= ATMEL_TC_ASWTRG_CLEAR;147		else148			cmr |= ATMEL_TC_ASWTRG_SET;149	} else {150		cmr &= ~ATMEL_TC_BCMR_MASK;151		if (polarity == PWM_POLARITY_INVERSED)152			cmr |= ATMEL_TC_BSWTRG_CLEAR;153		else154			cmr |= ATMEL_TC_BSWTRG_SET;155	}156 157	regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), cmr);158 159	/*160	 * Use software trigger to apply the new setting.161	 * If both PWM devices in this group are disabled we stop the clock.162	 */163	if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC))) {164		regmap_write(tcbpwmc->regmap,165			     ATMEL_TC_REG(tcbpwmc->channel, CCR),166			     ATMEL_TC_SWTRG | ATMEL_TC_CLKDIS);167		tcbpwmc->bkup.enabled = 1;168	} else {169		regmap_write(tcbpwmc->regmap,170			     ATMEL_TC_REG(tcbpwmc->channel, CCR),171			     ATMEL_TC_SWTRG);172		tcbpwmc->bkup.enabled = 0;173	}174}175 176static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm,177				enum pwm_polarity polarity)178{179	struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);180	struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm];181	u32 cmr;182 183	/*184	 * If duty is 0 the timer will be stopped and we have to185	 * configure the output correctly on software trigger:186	 *  - set output to high if PWM_POLARITY_INVERSED187	 *  - set output to low if PWM_POLARITY_NORMAL188	 *189	 * This is why we're reverting polarity in this case.190	 */191	if (tcbpwm->duty == 0)192		polarity = !polarity;193 194	regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr);195 196	/* flush old setting and set the new one */197	cmr &= ~ATMEL_TC_TCCLKS;198 199	if (pwm->hwpwm == 0) {200		cmr &= ~ATMEL_TC_ACMR_MASK;201 202		/* Set CMR flags according to given polarity */203		if (polarity == PWM_POLARITY_INVERSED)204			cmr |= ATMEL_TC_ASWTRG_CLEAR;205		else206			cmr |= ATMEL_TC_ASWTRG_SET;207	} else {208		cmr &= ~ATMEL_TC_BCMR_MASK;209		if (polarity == PWM_POLARITY_INVERSED)210			cmr |= ATMEL_TC_BSWTRG_CLEAR;211		else212			cmr |= ATMEL_TC_BSWTRG_SET;213	}214 215	/*216	 * If duty is 0 or equal to period there's no need to register217	 * a specific action on RA/RB and RC compare.218	 * The output will be configured on software trigger and keep219	 * this config till next config call.220	 */221	if (tcbpwm->duty != tcbpwm->period && tcbpwm->duty > 0) {222		if (pwm->hwpwm == 0) {223			if (polarity == PWM_POLARITY_INVERSED)224				cmr |= ATMEL_TC_ACPA_SET | ATMEL_TC_ACPC_CLEAR;225			else226				cmr |= ATMEL_TC_ACPA_CLEAR | ATMEL_TC_ACPC_SET;227		} else {228			if (polarity == PWM_POLARITY_INVERSED)229				cmr |= ATMEL_TC_BCPB_SET | ATMEL_TC_BCPC_CLEAR;230			else231				cmr |= ATMEL_TC_BCPB_CLEAR | ATMEL_TC_BCPC_SET;232		}233	}234 235	cmr |= (tcbpwm->div & ATMEL_TC_TCCLKS);236 237	regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), cmr);238 239	if (pwm->hwpwm == 0)240		regmap_write(tcbpwmc->regmap,241			     ATMEL_TC_REG(tcbpwmc->channel, RA),242			     tcbpwm->duty);243	else244		regmap_write(tcbpwmc->regmap,245			     ATMEL_TC_REG(tcbpwmc->channel, RB),246			     tcbpwm->duty);247 248	regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, RC),249		     tcbpwm->period);250 251	/* Use software trigger to apply the new setting */252	regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CCR),253		     ATMEL_TC_SWTRG | ATMEL_TC_CLKEN);254	tcbpwmc->bkup.enabled = 1;255	return 0;256}257 258static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,259				int duty_ns, int period_ns)260{261	struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);262	struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm];263	/* companion PWM sharing register values period and div */264	struct atmel_tcb_pwm_device *atcbpwm = &tcbpwmc->pwms[pwm->hwpwm ^ 1];265	int i = 0;266	int slowclk = 0;267	unsigned period;268	unsigned duty;269	unsigned rate = clk_get_rate(tcbpwmc->clk);270	unsigned long long min;271	unsigned long long max;272 273	/*274	 * Find best clk divisor:275	 * the smallest divisor which can fulfill the period_ns requirements.276	 * If there is a gclk, the first divisor is actually the gclk selector277	 */278	if (tcbpwmc->gclk)279		i = 1;280	for (; i < ARRAY_SIZE(atmel_tcb_divisors); ++i) {281		if (atmel_tcb_divisors[i] == 0) {282			slowclk = i;283			continue;284		}285		min = div_u64((u64)NSEC_PER_SEC * atmel_tcb_divisors[i], rate);286		max = min << tcbpwmc->width;287		if (max >= period_ns)288			break;289	}290 291	/*292	 * If none of the divisor are small enough to represent period_ns293	 * take slow clock (32KHz).294	 */295	if (i == ARRAY_SIZE(atmel_tcb_divisors)) {296		i = slowclk;297		rate = clk_get_rate(tcbpwmc->slow_clk);298		min = div_u64(NSEC_PER_SEC, rate);299		max = min << tcbpwmc->width;300 301		/* If period is too big return ERANGE error */302		if (max < period_ns)303			return -ERANGE;304	}305 306	duty = div_u64(duty_ns, min);307	period = div_u64(period_ns, min);308 309	/*310	 * PWM devices provided by the TCB driver are grouped by 2.311	 * PWM devices in a given group must be configured with the312	 * same period_ns.313	 *314	 * We're checking the period value of the second PWM device315	 * in this group before applying the new config.316	 */317	if ((atcbpwm->duty > 0 && atcbpwm->duty != atcbpwm->period) &&318		(atcbpwm->div != i || atcbpwm->period != period)) {319		dev_err(pwmchip_parent(chip),320			"failed to configure period_ns: PWM group already configured with a different value\n");321		return -EINVAL;322	}323 324	tcbpwm->period = period;325	tcbpwm->div = i;326	tcbpwm->duty = duty;327 328	return 0;329}330 331static int atmel_tcb_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,332			       const struct pwm_state *state)333{334	struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);335	int duty_cycle, period;336	int ret;337 338	guard(spinlock)(&tcbpwmc->lock);339 340	if (!state->enabled) {341		atmel_tcb_pwm_disable(chip, pwm, state->polarity);342		return 0;343	}344 345	period = state->period < INT_MAX ? state->period : INT_MAX;346	duty_cycle = state->duty_cycle < INT_MAX ? state->duty_cycle : INT_MAX;347 348	ret = atmel_tcb_pwm_config(chip, pwm, duty_cycle, period);349	if (ret)350		return ret;351 352	return atmel_tcb_pwm_enable(chip, pwm, state->polarity);353}354 355static const struct pwm_ops atmel_tcb_pwm_ops = {356	.request = atmel_tcb_pwm_request,357	.free = atmel_tcb_pwm_free,358	.apply = atmel_tcb_pwm_apply,359};360 361static struct atmel_tcb_config tcb_rm9200_config = {362	.counter_width = 16,363};364 365static struct atmel_tcb_config tcb_sam9x5_config = {366	.counter_width = 32,367};368 369static struct atmel_tcb_config tcb_sama5d2_config = {370	.counter_width = 32,371	.has_gclk = 1,372};373 374static const struct of_device_id atmel_tcb_of_match[] = {375	{ .compatible = "atmel,at91rm9200-tcb", .data = &tcb_rm9200_config, },376	{ .compatible = "atmel,at91sam9x5-tcb", .data = &tcb_sam9x5_config, },377	{ .compatible = "atmel,sama5d2-tcb", .data = &tcb_sama5d2_config, },378	{ /* sentinel */ }379};380 381static int atmel_tcb_pwm_probe(struct platform_device *pdev)382{383	struct pwm_chip *chip;384	const struct of_device_id *match;385	struct atmel_tcb_pwm_chip *tcbpwmc;386	const struct atmel_tcb_config *config;387	struct device_node *np = pdev->dev.of_node;388	char clk_name[] = "t0_clk";389	int err;390	int channel;391 392	chip = devm_pwmchip_alloc(&pdev->dev, NPWM, sizeof(*tcbpwmc));393	if (IS_ERR(chip))394		return PTR_ERR(chip);395	tcbpwmc = to_tcb_chip(chip);396 397	err = of_property_read_u32(np, "reg", &channel);398	if (err < 0) {399		dev_err(&pdev->dev,400			"failed to get Timer Counter Block channel from device tree (error: %d)\n",401			err);402		return err;403	}404 405	tcbpwmc->regmap = syscon_node_to_regmap(np->parent);406	if (IS_ERR(tcbpwmc->regmap))407		return PTR_ERR(tcbpwmc->regmap);408 409	tcbpwmc->slow_clk = of_clk_get_by_name(np->parent, "slow_clk");410	if (IS_ERR(tcbpwmc->slow_clk))411		return PTR_ERR(tcbpwmc->slow_clk);412 413	clk_name[1] += channel;414	tcbpwmc->clk = of_clk_get_by_name(np->parent, clk_name);415	if (IS_ERR(tcbpwmc->clk))416		tcbpwmc->clk = of_clk_get_by_name(np->parent, "t0_clk");417	if (IS_ERR(tcbpwmc->clk)) {418		err = PTR_ERR(tcbpwmc->clk);419		goto err_slow_clk;420	}421 422	match = of_match_node(atmel_tcb_of_match, np->parent);423	config = match->data;424 425	if (config->has_gclk) {426		tcbpwmc->gclk = of_clk_get_by_name(np->parent, "gclk");427		if (IS_ERR(tcbpwmc->gclk)) {428			err = PTR_ERR(tcbpwmc->gclk);429			goto err_clk;430		}431	}432 433	chip->ops = &atmel_tcb_pwm_ops;434	tcbpwmc->channel = channel;435	tcbpwmc->width = config->counter_width;436 437	err = clk_prepare_enable(tcbpwmc->slow_clk);438	if (err)439		goto err_gclk;440 441	spin_lock_init(&tcbpwmc->lock);442 443	err = pwmchip_add(chip);444	if (err < 0)445		goto err_disable_clk;446 447	platform_set_drvdata(pdev, chip);448 449	return 0;450 451err_disable_clk:452	clk_disable_unprepare(tcbpwmc->slow_clk);453 454err_gclk:455	clk_put(tcbpwmc->gclk);456 457err_clk:458	clk_put(tcbpwmc->clk);459 460err_slow_clk:461	clk_put(tcbpwmc->slow_clk);462 463	return err;464}465 466static void atmel_tcb_pwm_remove(struct platform_device *pdev)467{468	struct pwm_chip *chip = platform_get_drvdata(pdev);469	struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);470 471	pwmchip_remove(chip);472 473	clk_disable_unprepare(tcbpwmc->slow_clk);474	clk_put(tcbpwmc->gclk);475	clk_put(tcbpwmc->clk);476	clk_put(tcbpwmc->slow_clk);477}478 479static const struct of_device_id atmel_tcb_pwm_dt_ids[] = {480	{ .compatible = "atmel,tcb-pwm", },481	{ /* sentinel */ }482};483MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids);484 485static int atmel_tcb_pwm_suspend(struct device *dev)486{487	struct pwm_chip *chip = dev_get_drvdata(dev);488	struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);489	struct atmel_tcb_channel *chan = &tcbpwmc->bkup;490	unsigned int channel = tcbpwmc->channel;491 492	regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, CMR), &chan->cmr);493	regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, RA), &chan->ra);494	regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, RB), &chan->rb);495	regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, RC), &chan->rc);496 497	return 0;498}499 500static int atmel_tcb_pwm_resume(struct device *dev)501{502	struct pwm_chip *chip = dev_get_drvdata(dev);503	struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);504	struct atmel_tcb_channel *chan = &tcbpwmc->bkup;505	unsigned int channel = tcbpwmc->channel;506 507	regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, CMR), chan->cmr);508	regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, RA), chan->ra);509	regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, RB), chan->rb);510	regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, RC), chan->rc);511 512	if (chan->enabled)513		regmap_write(tcbpwmc->regmap,514			     ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,515			     ATMEL_TC_REG(channel, CCR));516 517	return 0;518}519 520static DEFINE_SIMPLE_DEV_PM_OPS(atmel_tcb_pwm_pm_ops, atmel_tcb_pwm_suspend,521				atmel_tcb_pwm_resume);522 523static struct platform_driver atmel_tcb_pwm_driver = {524	.driver = {525		.name = "atmel-tcb-pwm",526		.of_match_table = atmel_tcb_pwm_dt_ids,527		.pm = pm_ptr(&atmel_tcb_pwm_pm_ops),528	},529	.probe = atmel_tcb_pwm_probe,530	.remove = atmel_tcb_pwm_remove,531};532module_platform_driver(atmel_tcb_pwm_driver);533 534MODULE_AUTHOR("Boris BREZILLON <b.brezillon@overkiz.com>");535MODULE_DESCRIPTION("Atmel Timer Counter Pulse Width Modulation Driver");536MODULE_LICENSE("GPL v2");537