pwm-atmel-tcb.c 15 KB

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