Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) Overkiz SAS 2012
0004  *
0005  * Author: Boris BREZILLON <b.brezillon@overkiz.com>
0006  */
0007 
0008 #include <linux/module.h>
0009 #include <linux/init.h>
0010 #include <linux/clocksource.h>
0011 #include <linux/clockchips.h>
0012 #include <linux/interrupt.h>
0013 #include <linux/irq.h>
0014 
0015 #include <linux/clk.h>
0016 #include <linux/err.h>
0017 #include <linux/ioport.h>
0018 #include <linux/io.h>
0019 #include <linux/mfd/syscon.h>
0020 #include <linux/platform_device.h>
0021 #include <linux/pwm.h>
0022 #include <linux/of_device.h>
0023 #include <linux/of_irq.h>
0024 #include <linux/regmap.h>
0025 #include <linux/slab.h>
0026 #include <soc/at91/atmel_tcb.h>
0027 
0028 #define NPWM    2
0029 
0030 #define ATMEL_TC_ACMR_MASK  (ATMEL_TC_ACPA | ATMEL_TC_ACPC |    \
0031                  ATMEL_TC_AEEVT | ATMEL_TC_ASWTRG)
0032 
0033 #define ATMEL_TC_BCMR_MASK  (ATMEL_TC_BCPB | ATMEL_TC_BCPC |    \
0034                  ATMEL_TC_BEEVT | ATMEL_TC_BSWTRG)
0035 
0036 struct atmel_tcb_pwm_device {
0037     enum pwm_polarity polarity; /* PWM polarity */
0038     unsigned div;           /* PWM clock divider */
0039     unsigned duty;          /* PWM duty expressed in clk cycles */
0040     unsigned period;        /* PWM period expressed in clk cycles */
0041 };
0042 
0043 struct atmel_tcb_channel {
0044     u32 enabled;
0045     u32 cmr;
0046     u32 ra;
0047     u32 rb;
0048     u32 rc;
0049 };
0050 
0051 struct atmel_tcb_pwm_chip {
0052     struct pwm_chip chip;
0053     spinlock_t lock;
0054     u8 channel;
0055     u8 width;
0056     struct regmap *regmap;
0057     struct clk *clk;
0058     struct clk *gclk;
0059     struct clk *slow_clk;
0060     struct atmel_tcb_pwm_device *pwms[NPWM];
0061     struct atmel_tcb_channel bkup;
0062 };
0063 
0064 static const u8 atmel_tcb_divisors[] = { 2, 8, 32, 128, 0, };
0065 
0066 static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip)
0067 {
0068     return container_of(chip, struct atmel_tcb_pwm_chip, chip);
0069 }
0070 
0071 static int atmel_tcb_pwm_set_polarity(struct pwm_chip *chip,
0072                       struct pwm_device *pwm,
0073                       enum pwm_polarity polarity)
0074 {
0075     struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
0076     struct atmel_tcb_pwm_device *tcbpwm = tcbpwmc->pwms[pwm->hwpwm];
0077 
0078     tcbpwm->polarity = polarity;
0079 
0080     return 0;
0081 }
0082 
0083 static int atmel_tcb_pwm_request(struct pwm_chip *chip,
0084                  struct pwm_device *pwm)
0085 {
0086     struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
0087     struct atmel_tcb_pwm_device *tcbpwm;
0088     unsigned cmr;
0089     int ret;
0090 
0091     tcbpwm = devm_kzalloc(chip->dev, sizeof(*tcbpwm), GFP_KERNEL);
0092     if (!tcbpwm)
0093         return -ENOMEM;
0094 
0095     ret = clk_prepare_enable(tcbpwmc->clk);
0096     if (ret) {
0097         devm_kfree(chip->dev, tcbpwm);
0098         return ret;
0099     }
0100 
0101     tcbpwm->polarity = PWM_POLARITY_NORMAL;
0102     tcbpwm->duty = 0;
0103     tcbpwm->period = 0;
0104     tcbpwm->div = 0;
0105 
0106     spin_lock(&tcbpwmc->lock);
0107     regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr);
0108     /*
0109      * Get init config from Timer Counter registers if
0110      * Timer Counter is already configured as a PWM generator.
0111      */
0112     if (cmr & ATMEL_TC_WAVE) {
0113         if (pwm->hwpwm == 0)
0114             regmap_read(tcbpwmc->regmap,
0115                     ATMEL_TC_REG(tcbpwmc->channel, RA),
0116                     &tcbpwm->duty);
0117         else
0118             regmap_read(tcbpwmc->regmap,
0119                     ATMEL_TC_REG(tcbpwmc->channel, RB),
0120                     &tcbpwm->duty);
0121 
0122         tcbpwm->div = cmr & ATMEL_TC_TCCLKS;
0123         regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, RC),
0124                 &tcbpwm->period);
0125         cmr &= (ATMEL_TC_TCCLKS | ATMEL_TC_ACMR_MASK |
0126             ATMEL_TC_BCMR_MASK);
0127     } else
0128         cmr = 0;
0129 
0130     cmr |= ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO | ATMEL_TC_EEVT_XC0;
0131     regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), cmr);
0132     spin_unlock(&tcbpwmc->lock);
0133 
0134     tcbpwmc->pwms[pwm->hwpwm] = tcbpwm;
0135 
0136     return 0;
0137 }
0138 
0139 static void atmel_tcb_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
0140 {
0141     struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
0142     struct atmel_tcb_pwm_device *tcbpwm = tcbpwmc->pwms[pwm->hwpwm];
0143 
0144     clk_disable_unprepare(tcbpwmc->clk);
0145     tcbpwmc->pwms[pwm->hwpwm] = NULL;
0146     devm_kfree(chip->dev, tcbpwm);
0147 }
0148 
0149 static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
0150 {
0151     struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
0152     struct atmel_tcb_pwm_device *tcbpwm = tcbpwmc->pwms[pwm->hwpwm];
0153     unsigned cmr;
0154     enum pwm_polarity polarity = tcbpwm->polarity;
0155 
0156     /*
0157      * If duty is 0 the timer will be stopped and we have to
0158      * configure the output correctly on software trigger:
0159      *  - set output to high if PWM_POLARITY_INVERSED
0160      *  - set output to low if PWM_POLARITY_NORMAL
0161      *
0162      * This is why we're reverting polarity in this case.
0163      */
0164     if (tcbpwm->duty == 0)
0165         polarity = !polarity;
0166 
0167     spin_lock(&tcbpwmc->lock);
0168     regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr);
0169 
0170     /* flush old setting and set the new one */
0171     if (pwm->hwpwm == 0) {
0172         cmr &= ~ATMEL_TC_ACMR_MASK;
0173         if (polarity == PWM_POLARITY_INVERSED)
0174             cmr |= ATMEL_TC_ASWTRG_CLEAR;
0175         else
0176             cmr |= ATMEL_TC_ASWTRG_SET;
0177     } else {
0178         cmr &= ~ATMEL_TC_BCMR_MASK;
0179         if (polarity == PWM_POLARITY_INVERSED)
0180             cmr |= ATMEL_TC_BSWTRG_CLEAR;
0181         else
0182             cmr |= ATMEL_TC_BSWTRG_SET;
0183     }
0184 
0185     regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), cmr);
0186 
0187     /*
0188      * Use software trigger to apply the new setting.
0189      * If both PWM devices in this group are disabled we stop the clock.
0190      */
0191     if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC))) {
0192         regmap_write(tcbpwmc->regmap,
0193                  ATMEL_TC_REG(tcbpwmc->channel, CCR),
0194                  ATMEL_TC_SWTRG | ATMEL_TC_CLKDIS);
0195         tcbpwmc->bkup.enabled = 1;
0196     } else {
0197         regmap_write(tcbpwmc->regmap,
0198                  ATMEL_TC_REG(tcbpwmc->channel, CCR),
0199                  ATMEL_TC_SWTRG);
0200         tcbpwmc->bkup.enabled = 0;
0201     }
0202 
0203     spin_unlock(&tcbpwmc->lock);
0204 }
0205 
0206 static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
0207 {
0208     struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
0209     struct atmel_tcb_pwm_device *tcbpwm = tcbpwmc->pwms[pwm->hwpwm];
0210     u32 cmr;
0211     enum pwm_polarity polarity = tcbpwm->polarity;
0212 
0213     /*
0214      * If duty is 0 the timer will be stopped and we have to
0215      * configure the output correctly on software trigger:
0216      *  - set output to high if PWM_POLARITY_INVERSED
0217      *  - set output to low if PWM_POLARITY_NORMAL
0218      *
0219      * This is why we're reverting polarity in this case.
0220      */
0221     if (tcbpwm->duty == 0)
0222         polarity = !polarity;
0223 
0224     spin_lock(&tcbpwmc->lock);
0225     regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr);
0226 
0227     /* flush old setting and set the new one */
0228     cmr &= ~ATMEL_TC_TCCLKS;
0229 
0230     if (pwm->hwpwm == 0) {
0231         cmr &= ~ATMEL_TC_ACMR_MASK;
0232 
0233         /* Set CMR flags according to given polarity */
0234         if (polarity == PWM_POLARITY_INVERSED)
0235             cmr |= ATMEL_TC_ASWTRG_CLEAR;
0236         else
0237             cmr |= ATMEL_TC_ASWTRG_SET;
0238     } else {
0239         cmr &= ~ATMEL_TC_BCMR_MASK;
0240         if (polarity == PWM_POLARITY_INVERSED)
0241             cmr |= ATMEL_TC_BSWTRG_CLEAR;
0242         else
0243             cmr |= ATMEL_TC_BSWTRG_SET;
0244     }
0245 
0246     /*
0247      * If duty is 0 or equal to period there's no need to register
0248      * a specific action on RA/RB and RC compare.
0249      * The output will be configured on software trigger and keep
0250      * this config till next config call.
0251      */
0252     if (tcbpwm->duty != tcbpwm->period && tcbpwm->duty > 0) {
0253         if (pwm->hwpwm == 0) {
0254             if (polarity == PWM_POLARITY_INVERSED)
0255                 cmr |= ATMEL_TC_ACPA_SET | ATMEL_TC_ACPC_CLEAR;
0256             else
0257                 cmr |= ATMEL_TC_ACPA_CLEAR | ATMEL_TC_ACPC_SET;
0258         } else {
0259             if (polarity == PWM_POLARITY_INVERSED)
0260                 cmr |= ATMEL_TC_BCPB_SET | ATMEL_TC_BCPC_CLEAR;
0261             else
0262                 cmr |= ATMEL_TC_BCPB_CLEAR | ATMEL_TC_BCPC_SET;
0263         }
0264     }
0265 
0266     cmr |= (tcbpwm->div & ATMEL_TC_TCCLKS);
0267 
0268     regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), cmr);
0269 
0270     if (pwm->hwpwm == 0)
0271         regmap_write(tcbpwmc->regmap,
0272                  ATMEL_TC_REG(tcbpwmc->channel, RA),
0273                  tcbpwm->duty);
0274     else
0275         regmap_write(tcbpwmc->regmap,
0276                  ATMEL_TC_REG(tcbpwmc->channel, RB),
0277                  tcbpwm->duty);
0278 
0279     regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, RC),
0280              tcbpwm->period);
0281 
0282     /* Use software trigger to apply the new setting */
0283     regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CCR),
0284              ATMEL_TC_SWTRG | ATMEL_TC_CLKEN);
0285     tcbpwmc->bkup.enabled = 1;
0286     spin_unlock(&tcbpwmc->lock);
0287     return 0;
0288 }
0289 
0290 static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
0291                 int duty_ns, int period_ns)
0292 {
0293     struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
0294     struct atmel_tcb_pwm_device *tcbpwm = tcbpwmc->pwms[pwm->hwpwm];
0295     struct atmel_tcb_pwm_device *atcbpwm = NULL;
0296     int i = 0;
0297     int slowclk = 0;
0298     unsigned period;
0299     unsigned duty;
0300     unsigned rate = clk_get_rate(tcbpwmc->clk);
0301     unsigned long long min;
0302     unsigned long long max;
0303 
0304     /*
0305      * Find best clk divisor:
0306      * the smallest divisor which can fulfill the period_ns requirements.
0307      * If there is a gclk, the first divisor is actually the gclk selector
0308      */
0309     if (tcbpwmc->gclk)
0310         i = 1;
0311     for (; i < ARRAY_SIZE(atmel_tcb_divisors); ++i) {
0312         if (atmel_tcb_divisors[i] == 0) {
0313             slowclk = i;
0314             continue;
0315         }
0316         min = div_u64((u64)NSEC_PER_SEC * atmel_tcb_divisors[i], rate);
0317         max = min << tcbpwmc->width;
0318         if (max >= period_ns)
0319             break;
0320     }
0321 
0322     /*
0323      * If none of the divisor are small enough to represent period_ns
0324      * take slow clock (32KHz).
0325      */
0326     if (i == ARRAY_SIZE(atmel_tcb_divisors)) {
0327         i = slowclk;
0328         rate = clk_get_rate(tcbpwmc->slow_clk);
0329         min = div_u64(NSEC_PER_SEC, rate);
0330         max = min << tcbpwmc->width;
0331 
0332         /* If period is too big return ERANGE error */
0333         if (max < period_ns)
0334             return -ERANGE;
0335     }
0336 
0337     duty = div_u64(duty_ns, min);
0338     period = div_u64(period_ns, min);
0339 
0340     if (pwm->hwpwm == 0)
0341         atcbpwm = tcbpwmc->pwms[1];
0342     else
0343         atcbpwm = tcbpwmc->pwms[0];
0344 
0345     /*
0346      * PWM devices provided by the TCB driver are grouped by 2.
0347      * PWM devices in a given group must be configured with the
0348      * same period_ns.
0349      *
0350      * We're checking the period value of the second PWM device
0351      * in this group before applying the new config.
0352      */
0353     if ((atcbpwm && atcbpwm->duty > 0 &&
0354             atcbpwm->duty != atcbpwm->period) &&
0355         (atcbpwm->div != i || atcbpwm->period != period)) {
0356         dev_err(chip->dev,
0357             "failed to configure period_ns: PWM group already configured with a different value\n");
0358         return -EINVAL;
0359     }
0360 
0361     tcbpwm->period = period;
0362     tcbpwm->div = i;
0363     tcbpwm->duty = duty;
0364 
0365     return 0;
0366 }
0367 
0368 static int atmel_tcb_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
0369                    const struct pwm_state *state)
0370 {
0371     int duty_cycle, period;
0372     int ret;
0373 
0374     /* This function only sets a flag in driver data */
0375     atmel_tcb_pwm_set_polarity(chip, pwm, state->polarity);
0376 
0377     if (!state->enabled) {
0378         atmel_tcb_pwm_disable(chip, pwm);
0379         return 0;
0380     }
0381 
0382     period = state->period < INT_MAX ? state->period : INT_MAX;
0383     duty_cycle = state->duty_cycle < INT_MAX ? state->duty_cycle : INT_MAX;
0384 
0385     ret = atmel_tcb_pwm_config(chip, pwm, duty_cycle, period);
0386     if (ret)
0387         return ret;
0388 
0389     return atmel_tcb_pwm_enable(chip, pwm);
0390 }
0391 
0392 static const struct pwm_ops atmel_tcb_pwm_ops = {
0393     .request = atmel_tcb_pwm_request,
0394     .free = atmel_tcb_pwm_free,
0395     .apply = atmel_tcb_pwm_apply,
0396     .owner = THIS_MODULE,
0397 };
0398 
0399 static struct atmel_tcb_config tcb_rm9200_config = {
0400     .counter_width = 16,
0401 };
0402 
0403 static struct atmel_tcb_config tcb_sam9x5_config = {
0404     .counter_width = 32,
0405 };
0406 
0407 static struct atmel_tcb_config tcb_sama5d2_config = {
0408     .counter_width = 32,
0409     .has_gclk = 1,
0410 };
0411 
0412 static const struct of_device_id atmel_tcb_of_match[] = {
0413     { .compatible = "atmel,at91rm9200-tcb", .data = &tcb_rm9200_config, },
0414     { .compatible = "atmel,at91sam9x5-tcb", .data = &tcb_sam9x5_config, },
0415     { .compatible = "atmel,sama5d2-tcb", .data = &tcb_sama5d2_config, },
0416     { /* sentinel */ }
0417 };
0418 
0419 static int atmel_tcb_pwm_probe(struct platform_device *pdev)
0420 {
0421     const struct of_device_id *match;
0422     struct atmel_tcb_pwm_chip *tcbpwm;
0423     const struct atmel_tcb_config *config;
0424     struct device_node *np = pdev->dev.of_node;
0425     struct regmap *regmap;
0426     struct clk *clk, *gclk = NULL;
0427     struct clk *slow_clk;
0428     char clk_name[] = "t0_clk";
0429     int err;
0430     int channel;
0431 
0432     err = of_property_read_u32(np, "reg", &channel);
0433     if (err < 0) {
0434         dev_err(&pdev->dev,
0435             "failed to get Timer Counter Block channel from device tree (error: %d)\n",
0436             err);
0437         return err;
0438     }
0439 
0440     regmap = syscon_node_to_regmap(np->parent);
0441     if (IS_ERR(regmap))
0442         return PTR_ERR(regmap);
0443 
0444     slow_clk = of_clk_get_by_name(np->parent, "slow_clk");
0445     if (IS_ERR(slow_clk))
0446         return PTR_ERR(slow_clk);
0447 
0448     clk_name[1] += channel;
0449     clk = of_clk_get_by_name(np->parent, clk_name);
0450     if (IS_ERR(clk))
0451         clk = of_clk_get_by_name(np->parent, "t0_clk");
0452     if (IS_ERR(clk))
0453         return PTR_ERR(clk);
0454 
0455     match = of_match_node(atmel_tcb_of_match, np->parent);
0456     config = match->data;
0457 
0458     if (config->has_gclk) {
0459         gclk = of_clk_get_by_name(np->parent, "gclk");
0460         if (IS_ERR(gclk))
0461             return PTR_ERR(gclk);
0462     }
0463 
0464     tcbpwm = devm_kzalloc(&pdev->dev, sizeof(*tcbpwm), GFP_KERNEL);
0465     if (tcbpwm == NULL) {
0466         err = -ENOMEM;
0467         goto err_slow_clk;
0468     }
0469 
0470     tcbpwm->chip.dev = &pdev->dev;
0471     tcbpwm->chip.ops = &atmel_tcb_pwm_ops;
0472     tcbpwm->chip.npwm = NPWM;
0473     tcbpwm->channel = channel;
0474     tcbpwm->regmap = regmap;
0475     tcbpwm->clk = clk;
0476     tcbpwm->gclk = gclk;
0477     tcbpwm->slow_clk = slow_clk;
0478     tcbpwm->width = config->counter_width;
0479 
0480     err = clk_prepare_enable(slow_clk);
0481     if (err)
0482         goto err_slow_clk;
0483 
0484     spin_lock_init(&tcbpwm->lock);
0485 
0486     err = pwmchip_add(&tcbpwm->chip);
0487     if (err < 0)
0488         goto err_disable_clk;
0489 
0490     platform_set_drvdata(pdev, tcbpwm);
0491 
0492     return 0;
0493 
0494 err_disable_clk:
0495     clk_disable_unprepare(tcbpwm->slow_clk);
0496 
0497 err_slow_clk:
0498     clk_put(slow_clk);
0499 
0500     return err;
0501 }
0502 
0503 static int atmel_tcb_pwm_remove(struct platform_device *pdev)
0504 {
0505     struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev);
0506 
0507     pwmchip_remove(&tcbpwm->chip);
0508 
0509     clk_disable_unprepare(tcbpwm->slow_clk);
0510     clk_put(tcbpwm->slow_clk);
0511     clk_put(tcbpwm->clk);
0512 
0513     return 0;
0514 }
0515 
0516 static const struct of_device_id atmel_tcb_pwm_dt_ids[] = {
0517     { .compatible = "atmel,tcb-pwm", },
0518     { /* sentinel */ }
0519 };
0520 MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids);
0521 
0522 #ifdef CONFIG_PM_SLEEP
0523 static int atmel_tcb_pwm_suspend(struct device *dev)
0524 {
0525     struct atmel_tcb_pwm_chip *tcbpwm = dev_get_drvdata(dev);
0526     struct atmel_tcb_channel *chan = &tcbpwm->bkup;
0527     unsigned int channel = tcbpwm->channel;
0528 
0529     regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, CMR), &chan->cmr);
0530     regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, RA), &chan->ra);
0531     regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, RB), &chan->rb);
0532     regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, RC), &chan->rc);
0533 
0534     return 0;
0535 }
0536 
0537 static int atmel_tcb_pwm_resume(struct device *dev)
0538 {
0539     struct atmel_tcb_pwm_chip *tcbpwm = dev_get_drvdata(dev);
0540     struct atmel_tcb_channel *chan = &tcbpwm->bkup;
0541     unsigned int channel = tcbpwm->channel;
0542 
0543     regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, CMR), chan->cmr);
0544     regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, RA), chan->ra);
0545     regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, RB), chan->rb);
0546     regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, RC), chan->rc);
0547 
0548     if (chan->enabled)
0549         regmap_write(tcbpwm->regmap,
0550                  ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
0551                  ATMEL_TC_REG(channel, CCR));
0552 
0553     return 0;
0554 }
0555 #endif
0556 
0557 static SIMPLE_DEV_PM_OPS(atmel_tcb_pwm_pm_ops, atmel_tcb_pwm_suspend,
0558              atmel_tcb_pwm_resume);
0559 
0560 static struct platform_driver atmel_tcb_pwm_driver = {
0561     .driver = {
0562         .name = "atmel-tcb-pwm",
0563         .of_match_table = atmel_tcb_pwm_dt_ids,
0564         .pm = &atmel_tcb_pwm_pm_ops,
0565     },
0566     .probe = atmel_tcb_pwm_probe,
0567     .remove = atmel_tcb_pwm_remove,
0568 };
0569 module_platform_driver(atmel_tcb_pwm_driver);
0570 
0571 MODULE_AUTHOR("Boris BREZILLON <b.brezillon@overkiz.com>");
0572 MODULE_DESCRIPTION("Atmel Timer Counter Pulse Width Modulation Driver");
0573 MODULE_LICENSE("GPL v2");