806-flextimer-support-layerscape.patch 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. From 76cd2ef6b69b67c09480a3248f7b910897f0bb2f Mon Sep 17 00:00:00 2001
  2. From: Yangbo Lu <[email protected]>
  3. Date: Mon, 25 Sep 2017 12:13:12 +0800
  4. Subject: [PATCH] flextimer: support layerscape
  5. This is a integrated patch for layerscape flextimer support.
  6. Signed-off-by: Wang Dongsheng <[email protected]>
  7. Signed-off-by: Meng Yi <[email protected]>
  8. Signed-off-by: Yangbo Lu <[email protected]>
  9. ---
  10. drivers/clocksource/fsl_ftm_timer.c | 8 +-
  11. drivers/soc/fsl/layerscape/ftm_alarm.c | 367 +++++++++++++++++++++++++++++++++
  12. 2 files changed, 371 insertions(+), 4 deletions(-)
  13. create mode 100644 drivers/soc/fsl/layerscape/ftm_alarm.c
  14. diff --git a/drivers/clocksource/fsl_ftm_timer.c b/drivers/clocksource/fsl_ftm_timer.c
  15. index 738515b8..770bbbca 100644
  16. --- a/drivers/clocksource/fsl_ftm_timer.c
  17. +++ b/drivers/clocksource/fsl_ftm_timer.c
  18. @@ -83,11 +83,11 @@ static inline void ftm_counter_disable(void __iomem *base)
  19. static inline void ftm_irq_acknowledge(void __iomem *base)
  20. {
  21. - u32 val;
  22. + unsigned int timeout = 100;
  23. - val = ftm_readl(base + FTM_SC);
  24. - val &= ~FTM_SC_TOF;
  25. - ftm_writel(val, base + FTM_SC);
  26. + while ((FTM_SC_TOF & ftm_readl(base + FTM_SC)) && timeout--)
  27. + ftm_writel(ftm_readl(base + FTM_SC) & (~FTM_SC_TOF),
  28. + base + FTM_SC);
  29. }
  30. static inline void ftm_irq_enable(void __iomem *base)
  31. diff --git a/drivers/soc/fsl/layerscape/ftm_alarm.c b/drivers/soc/fsl/layerscape/ftm_alarm.c
  32. new file mode 100644
  33. index 00000000..49865b0b
  34. --- /dev/null
  35. +++ b/drivers/soc/fsl/layerscape/ftm_alarm.c
  36. @@ -0,0 +1,367 @@
  37. +/*
  38. + * Freescale FlexTimer Module (FTM) Alarm driver.
  39. + *
  40. + * Copyright 2014 Freescale Semiconductor, Inc.
  41. + *
  42. + * This program is free software; you can redistribute it and/or
  43. + * modify it under the terms of the GNU General Public License
  44. + * as published by the Free Software Foundation; either version 2
  45. + * of the License, or (at your option) any later version.
  46. + */
  47. +
  48. +#include <linux/device.h>
  49. +#include <linux/err.h>
  50. +#include <linux/interrupt.h>
  51. +#include <linux/io.h>
  52. +#include <linux/of_address.h>
  53. +#include <linux/of_irq.h>
  54. +#include <linux/platform_device.h>
  55. +#include <linux/of.h>
  56. +#include <linux/of_device.h>
  57. +#include <linux/libata.h>
  58. +#include <linux/module.h>
  59. +
  60. +#define FTM_SC 0x00
  61. +#define FTM_SC_CLK_SHIFT 3
  62. +#define FTM_SC_CLK_MASK (0x3 << FTM_SC_CLK_SHIFT)
  63. +#define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_SHIFT)
  64. +#define FTM_SC_PS_MASK 0x7
  65. +#define FTM_SC_TOIE BIT(6)
  66. +#define FTM_SC_TOF BIT(7)
  67. +
  68. +#define FTM_SC_CLKS_FIXED_FREQ 0x02
  69. +
  70. +#define FTM_CNT 0x04
  71. +#define FTM_MOD 0x08
  72. +#define FTM_CNTIN 0x4C
  73. +
  74. +#define FIXED_FREQ_CLK 32000
  75. +#define MAX_FREQ_DIV (1 << FTM_SC_PS_MASK)
  76. +#define MAX_COUNT_VAL 0xffff
  77. +
  78. +static void __iomem *ftm1_base;
  79. +static void __iomem *rcpm_ftm_addr;
  80. +static u32 alarm_freq;
  81. +static bool big_endian;
  82. +
  83. +enum pmu_endian_type {
  84. + BIG_ENDIAN,
  85. + LITTLE_ENDIAN,
  86. +};
  87. +
  88. +struct rcpm_cfg {
  89. + enum pmu_endian_type big_endian; /* Big/Little endian of PMU module */
  90. + u32 flextimer_set_bit; /* FlexTimer1 is not powerdown during device LPM20 */
  91. +};
  92. +
  93. +static struct rcpm_cfg ls1012a_rcpm_cfg = {
  94. + .big_endian = BIG_ENDIAN,
  95. + .flextimer_set_bit = 0x20000,
  96. +};
  97. +
  98. +static struct rcpm_cfg ls1021a_rcpm_cfg = {
  99. + .big_endian = BIG_ENDIAN,
  100. + .flextimer_set_bit = 0x20000,
  101. +};
  102. +
  103. +static struct rcpm_cfg ls1043a_rcpm_cfg = {
  104. + .big_endian = BIG_ENDIAN,
  105. + .flextimer_set_bit = 0x20000,
  106. +};
  107. +
  108. +static struct rcpm_cfg ls1046a_rcpm_cfg = {
  109. + .big_endian = BIG_ENDIAN,
  110. + .flextimer_set_bit = 0x20000,
  111. +};
  112. +
  113. +static struct rcpm_cfg ls1088a_rcpm_cfg = {
  114. + .big_endian = LITTLE_ENDIAN,
  115. + .flextimer_set_bit = 0x4000,
  116. +};
  117. +
  118. +static struct rcpm_cfg ls208xa_rcpm_cfg = {
  119. + .big_endian = LITTLE_ENDIAN,
  120. + .flextimer_set_bit = 0x4000,
  121. +};
  122. +
  123. +static const struct of_device_id ippdexpcr_of_match[] = {
  124. + { .compatible = "fsl,ls1012a-ftm", .data = &ls1012a_rcpm_cfg},
  125. + { .compatible = "fsl,ls1021a-ftm", .data = &ls1021a_rcpm_cfg},
  126. + { .compatible = "fsl,ls1043a-ftm", .data = &ls1043a_rcpm_cfg},
  127. + { .compatible = "fsl,ls1046a-ftm", .data = &ls1046a_rcpm_cfg},
  128. + { .compatible = "fsl,ls1088a-ftm", .data = &ls1088a_rcpm_cfg},
  129. + { .compatible = "fsl,ls208xa-ftm", .data = &ls208xa_rcpm_cfg},
  130. + {},
  131. +};
  132. +MODULE_DEVICE_TABLE(of, ippdexpcr_of_match);
  133. +
  134. +static inline u32 ftm_readl(void __iomem *addr)
  135. +{
  136. + if (big_endian)
  137. + return ioread32be(addr);
  138. +
  139. + return ioread32(addr);
  140. +}
  141. +
  142. +static inline void ftm_writel(u32 val, void __iomem *addr)
  143. +{
  144. + if (big_endian)
  145. + iowrite32be(val, addr);
  146. + else
  147. + iowrite32(val, addr);
  148. +}
  149. +
  150. +static inline void ftm_counter_enable(void __iomem *base)
  151. +{
  152. + u32 val;
  153. +
  154. + /* select and enable counter clock source */
  155. + val = ftm_readl(base + FTM_SC);
  156. + val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
  157. + val |= (FTM_SC_PS_MASK | FTM_SC_CLK(FTM_SC_CLKS_FIXED_FREQ));
  158. + ftm_writel(val, base + FTM_SC);
  159. +}
  160. +
  161. +static inline void ftm_counter_disable(void __iomem *base)
  162. +{
  163. + u32 val;
  164. +
  165. + /* disable counter clock source */
  166. + val = ftm_readl(base + FTM_SC);
  167. + val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
  168. + ftm_writel(val, base + FTM_SC);
  169. +}
  170. +
  171. +static inline void ftm_irq_acknowledge(void __iomem *base)
  172. +{
  173. + unsigned int timeout = 100;
  174. +
  175. + while ((FTM_SC_TOF & ftm_readl(base + FTM_SC)) && timeout--)
  176. + ftm_writel(ftm_readl(base + FTM_SC) & (~FTM_SC_TOF),
  177. + base + FTM_SC);
  178. +}
  179. +
  180. +static inline void ftm_irq_enable(void __iomem *base)
  181. +{
  182. + u32 val;
  183. +
  184. + val = ftm_readl(base + FTM_SC);
  185. + val |= FTM_SC_TOIE;
  186. + ftm_writel(val, base + FTM_SC);
  187. +}
  188. +
  189. +static inline void ftm_irq_disable(void __iomem *base)
  190. +{
  191. + u32 val;
  192. +
  193. + val = ftm_readl(base + FTM_SC);
  194. + val &= ~FTM_SC_TOIE;
  195. + ftm_writel(val, base + FTM_SC);
  196. +}
  197. +
  198. +static inline void ftm_reset_counter(void __iomem *base)
  199. +{
  200. + /*
  201. + * The CNT register contains the FTM counter value.
  202. + * Reset clears the CNT register. Writing any value to COUNT
  203. + * updates the counter with its initial value, CNTIN.
  204. + */
  205. + ftm_writel(0x00, base + FTM_CNT);
  206. +}
  207. +
  208. +static u32 time_to_cycle(unsigned long time)
  209. +{
  210. + u32 cycle;
  211. +
  212. + cycle = time * alarm_freq;
  213. + if (cycle > MAX_COUNT_VAL) {
  214. + pr_err("Out of alarm range.\n");
  215. + cycle = 0;
  216. + }
  217. +
  218. + return cycle;
  219. +}
  220. +
  221. +static u32 cycle_to_time(u32 cycle)
  222. +{
  223. + return cycle / alarm_freq + 1;
  224. +}
  225. +
  226. +static void ftm_clean_alarm(void)
  227. +{
  228. + ftm_counter_disable(ftm1_base);
  229. +
  230. + ftm_writel(0x00, ftm1_base + FTM_CNTIN);
  231. + ftm_writel(~0U, ftm1_base + FTM_MOD);
  232. +
  233. + ftm_reset_counter(ftm1_base);
  234. +}
  235. +
  236. +static int ftm_set_alarm(u64 cycle)
  237. +{
  238. + ftm_irq_disable(ftm1_base);
  239. +
  240. + /*
  241. + * The counter increments until the value of MOD is reached,
  242. + * at which point the counter is reloaded with the value of CNTIN.
  243. + * The TOF (the overflow flag) bit is set when the FTM counter
  244. + * changes from MOD to CNTIN. So we should using the cycle - 1.
  245. + */
  246. + ftm_writel(cycle - 1, ftm1_base + FTM_MOD);
  247. +
  248. + ftm_counter_enable(ftm1_base);
  249. +
  250. + ftm_irq_enable(ftm1_base);
  251. +
  252. + return 0;
  253. +}
  254. +
  255. +static irqreturn_t ftm_alarm_interrupt(int irq, void *dev_id)
  256. +{
  257. + ftm_irq_acknowledge(ftm1_base);
  258. + ftm_irq_disable(ftm1_base);
  259. + ftm_clean_alarm();
  260. +
  261. + return IRQ_HANDLED;
  262. +}
  263. +
  264. +static ssize_t ftm_alarm_show(struct device *dev,
  265. + struct device_attribute *attr,
  266. + char *buf)
  267. +{
  268. + u32 count, val;
  269. +
  270. + count = ftm_readl(ftm1_base + FTM_MOD);
  271. + val = ftm_readl(ftm1_base + FTM_CNT);
  272. + val = (count & MAX_COUNT_VAL) - val;
  273. + val = cycle_to_time(val);
  274. +
  275. + return sprintf(buf, "%u\n", val);
  276. +}
  277. +
  278. +static ssize_t ftm_alarm_store(struct device *dev,
  279. + struct device_attribute *attr,
  280. + const char *buf, size_t count)
  281. +{
  282. + u32 cycle;
  283. + unsigned long time;
  284. +
  285. + if (kstrtoul(buf, 0, &time))
  286. + return -EINVAL;
  287. +
  288. + ftm_clean_alarm();
  289. +
  290. + cycle = time_to_cycle(time);
  291. + if (!cycle)
  292. + return -EINVAL;
  293. +
  294. + ftm_set_alarm(cycle);
  295. +
  296. + return count;
  297. +}
  298. +
  299. +static struct device_attribute ftm_alarm_attributes = __ATTR(ftm_alarm, 0644,
  300. + ftm_alarm_show, ftm_alarm_store);
  301. +
  302. +static int ftm_alarm_probe(struct platform_device *pdev)
  303. +{
  304. + struct device_node *np = pdev->dev.of_node;
  305. + struct resource *r;
  306. + int irq;
  307. + int ret;
  308. + struct rcpm_cfg *rcpm_cfg;
  309. + u32 ippdexpcr, flextimer;
  310. + const struct of_device_id *of_id;
  311. + enum pmu_endian_type endian;
  312. +
  313. + r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  314. + if (!r)
  315. + return -ENODEV;
  316. +
  317. + ftm1_base = devm_ioremap_resource(&pdev->dev, r);
  318. + if (IS_ERR(ftm1_base))
  319. + return PTR_ERR(ftm1_base);
  320. +
  321. + of_id = of_match_node(ippdexpcr_of_match, np);
  322. + if (!of_id)
  323. + return -ENODEV;
  324. +
  325. + rcpm_cfg = devm_kzalloc(&pdev->dev, sizeof(*rcpm_cfg), GFP_KERNEL);
  326. + if (!rcpm_cfg)
  327. + return -ENOMEM;
  328. +
  329. + rcpm_cfg = (struct rcpm_cfg*)of_id->data;
  330. + endian = rcpm_cfg->big_endian;
  331. + flextimer = rcpm_cfg->flextimer_set_bit;
  332. +
  333. + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "FlexTimer1");
  334. + if (r) {
  335. + rcpm_ftm_addr = devm_ioremap_resource(&pdev->dev, r);
  336. + if (IS_ERR(rcpm_ftm_addr))
  337. + return PTR_ERR(rcpm_ftm_addr);
  338. + if (endian == BIG_ENDIAN)
  339. + ippdexpcr = ioread32be(rcpm_ftm_addr);
  340. + else
  341. + ippdexpcr = ioread32(rcpm_ftm_addr);
  342. + ippdexpcr |= flextimer;
  343. + if (endian == BIG_ENDIAN)
  344. + iowrite32be(ippdexpcr, rcpm_ftm_addr);
  345. + else
  346. + iowrite32(ippdexpcr, rcpm_ftm_addr);
  347. + }
  348. +
  349. + irq = irq_of_parse_and_map(np, 0);
  350. + if (irq <= 0) {
  351. + pr_err("ftm: unable to get IRQ from DT, %d\n", irq);
  352. + return -EINVAL;
  353. + }
  354. +
  355. + big_endian = of_property_read_bool(np, "big-endian");
  356. +
  357. + ret = devm_request_irq(&pdev->dev, irq, ftm_alarm_interrupt,
  358. + IRQF_NO_SUSPEND, dev_name(&pdev->dev), NULL);
  359. + if (ret < 0) {
  360. + dev_err(&pdev->dev, "failed to request irq\n");
  361. + return ret;
  362. + }
  363. +
  364. + ret = device_create_file(&pdev->dev, &ftm_alarm_attributes);
  365. + if (ret) {
  366. + dev_err(&pdev->dev, "create sysfs fail.\n");
  367. + return ret;
  368. + }
  369. +
  370. + alarm_freq = (u32)FIXED_FREQ_CLK / (u32)MAX_FREQ_DIV;
  371. +
  372. + ftm_clean_alarm();
  373. +
  374. + device_init_wakeup(&pdev->dev, true);
  375. +
  376. + return ret;
  377. +}
  378. +
  379. +static const struct of_device_id ftm_alarm_match[] = {
  380. + { .compatible = "fsl,ls1012a-ftm", },
  381. + { .compatible = "fsl,ls1021a-ftm", },
  382. + { .compatible = "fsl,ls1043a-ftm", },
  383. + { .compatible = "fsl,ls1046a-ftm", },
  384. + { .compatible = "fsl,ls1088a-ftm", },
  385. + { .compatible = "fsl,ls208xa-ftm", },
  386. + { .compatible = "fsl,ftm-timer", },
  387. + { },
  388. +};
  389. +
  390. +static struct platform_driver ftm_alarm_driver = {
  391. + .probe = ftm_alarm_probe,
  392. + .driver = {
  393. + .name = "ftm-alarm",
  394. + .owner = THIS_MODULE,
  395. + .of_match_table = ftm_alarm_match,
  396. + },
  397. +};
  398. +
  399. +static int __init ftm_alarm_init(void)
  400. +{
  401. + return platform_driver_register(&ftm_alarm_driver);
  402. +}
  403. +device_initcall(ftm_alarm_init);
  404. --
  405. 2.14.1