PM / devfreq: event: add support for rk3368 dfi
[firefly-linux-kernel-4.4.55.git] / drivers / devfreq / event / rockchip-dfi.c
1 /*
2  * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
3  * Author: Lin Huang <hl@rock-chips.com>
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms and conditions of the GNU General Public License,
7  * version 2, as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12  * more details.
13  */
14
15 #include <linux/clk.h>
16 #include <linux/devfreq-event.h>
17 #include <linux/kernel.h>
18 #include <linux/err.h>
19 #include <linux/init.h>
20 #include <linux/io.h>
21 #include <linux/mfd/syscon.h>
22 #include <linux/module.h>
23 #include <linux/platform_device.h>
24 #include <linux/regmap.h>
25 #include <linux/slab.h>
26 #include <linux/list.h>
27 #include <linux/of.h>
28
29 #define RK3368_GRF_DDRC0_CON0           0x600
30 #define RK3368_GRF_SOC_STATUS5          0x494
31 #define RK3368_GRF_SOC_STATUS6          0x498
32 #define RK3368_GRF_SOC_STATUS8          0x4a0
33 #define RK3368_GRF_SOC_STATUS9          0x4a4
34 #define RK3368_GRF_SOC_STATUS10         0x4a8
35 #define RK3368_DFI_EN                   (0x30003 << 5)
36 #define RK3368_DFI_DIS                  (0x30000 << 5)
37
38 #define RK3399_DMC_NUM_CH       2
39
40 /* DDRMON_CTRL */
41 #define DDRMON_CTRL     0x04
42 #define CLR_DDRMON_CTRL (0x1f0000 << 0)
43 #define LPDDR4_EN       (0x10001 << 4)
44 #define HARDWARE_EN     (0x10001 << 3)
45 #define LPDDR3_EN       (0x10001 << 2)
46 #define SOFTWARE_EN     (0x10001 << 1)
47 #define SOFTWARE_DIS    (0x10000 << 1)
48 #define TIME_CNT_EN     (0x10001 << 0)
49
50 #define DDRMON_CH0_COUNT_NUM            0x28
51 #define DDRMON_CH0_DFI_ACCESS_NUM       0x2c
52 #define DDRMON_CH1_COUNT_NUM            0x3c
53 #define DDRMON_CH1_DFI_ACCESS_NUM       0x40
54
55 /* pmu grf */
56 #define PMUGRF_OS_REG2  0x308
57 #define DDRTYPE_SHIFT   13
58 #define DDRTYPE_MASK    7
59
60 enum {
61         DDR3 = 3,
62         LPDDR3 = 6,
63         LPDDR4 = 7,
64         UNUSED = 0xFF
65 };
66
67 struct dmc_usage {
68         u32 access;
69         u32 total;
70 };
71
72 /*
73  * The dfi controller can monitor DDR load. It has an upper and lower threshold
74  * for the operating points. Whenever the usage leaves these bounds an event is
75  * generated to indicate the DDR frequency should be changed.
76  */
77 struct rockchip_dfi {
78         struct devfreq_event_dev *edev;
79         struct devfreq_event_desc *desc;
80         struct dmc_usage ch_usage[RK3399_DMC_NUM_CH];
81         struct device *dev;
82         void __iomem *regs;
83         struct regmap *regmap_pmu;
84         struct regmap *regmap_grf;
85         struct clk *clk;
86 };
87
88 static void rk3368_dfi_start_hardware_counter(struct devfreq_event_dev *edev)
89 {
90         struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
91
92         regmap_write(info->regmap_grf, RK3368_GRF_DDRC0_CON0, RK3368_DFI_EN);
93 }
94
95 static void rk3368_dfi_stop_hardware_counter(struct devfreq_event_dev *edev)
96 {
97         struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
98
99         regmap_write(info->regmap_grf, RK3368_GRF_DDRC0_CON0, RK3368_DFI_DIS);
100 }
101
102 static int rk3368_dfi_disable(struct devfreq_event_dev *edev)
103 {
104         rk3368_dfi_stop_hardware_counter(edev);
105
106         return 0;
107 }
108
109 static int rk3368_dfi_enable(struct devfreq_event_dev *edev)
110 {
111         rk3368_dfi_start_hardware_counter(edev);
112
113         return 0;
114 }
115
116 static int rk3368_dfi_set_event(struct devfreq_event_dev *edev)
117 {
118         return 0;
119 }
120
121 static int rk3368_dfi_get_event(struct devfreq_event_dev *edev,
122                                 struct devfreq_event_data *edata)
123 {
124         struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
125         unsigned long flags;
126         u32 dfi0_wr, dfi0_rd, dfi1_wr, dfi1_rd, dfi_timer;
127
128         local_irq_save(flags);
129
130         rk3368_dfi_stop_hardware_counter(edev);
131
132         regmap_read(info->regmap_grf, RK3368_GRF_SOC_STATUS5, &dfi0_wr);
133         regmap_read(info->regmap_grf, RK3368_GRF_SOC_STATUS6, &dfi0_rd);
134         regmap_read(info->regmap_grf, RK3368_GRF_SOC_STATUS9, &dfi1_wr);
135         regmap_read(info->regmap_grf, RK3368_GRF_SOC_STATUS10, &dfi1_rd);
136         regmap_read(info->regmap_grf, RK3368_GRF_SOC_STATUS8, &dfi_timer);
137
138         edata->load_count = (dfi0_wr + dfi0_rd + dfi1_wr + dfi1_rd) * 2;
139         edata->total_count = dfi_timer;
140
141         rk3368_dfi_start_hardware_counter(edev);
142
143         local_irq_restore(flags);
144
145         return 0;
146 }
147
148 static const struct devfreq_event_ops rk3368_dfi_ops = {
149         .disable = rk3368_dfi_disable,
150         .enable = rk3368_dfi_enable,
151         .get_event = rk3368_dfi_get_event,
152         .set_event = rk3368_dfi_set_event,
153 };
154
155 static void rockchip_dfi_start_hardware_counter(struct devfreq_event_dev *edev)
156 {
157         struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
158         void __iomem *dfi_regs = info->regs;
159         u32 val;
160         u32 ddr_type;
161
162         /* get ddr type */
163         regmap_read(info->regmap_pmu, PMUGRF_OS_REG2, &val);
164         ddr_type = (val >> DDRTYPE_SHIFT) & DDRTYPE_MASK;
165
166         /* clear DDRMON_CTRL setting */
167         writel_relaxed(CLR_DDRMON_CTRL, dfi_regs + DDRMON_CTRL);
168
169         /* set ddr type to dfi */
170         if (ddr_type == LPDDR3)
171                 writel_relaxed(LPDDR3_EN, dfi_regs + DDRMON_CTRL);
172         else if (ddr_type == LPDDR4)
173                 writel_relaxed(LPDDR4_EN, dfi_regs + DDRMON_CTRL);
174
175         /* enable count, use software mode */
176         writel_relaxed(SOFTWARE_EN, dfi_regs + DDRMON_CTRL);
177 }
178
179 static void rockchip_dfi_stop_hardware_counter(struct devfreq_event_dev *edev)
180 {
181         struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
182         void __iomem *dfi_regs = info->regs;
183
184         writel_relaxed(SOFTWARE_DIS, dfi_regs + DDRMON_CTRL);
185 }
186
187 static int rockchip_dfi_get_busier_ch(struct devfreq_event_dev *edev)
188 {
189         struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
190         u32 tmp, max = 0;
191         u32 i, busier_ch = 0;
192         void __iomem *dfi_regs = info->regs;
193
194         rockchip_dfi_stop_hardware_counter(edev);
195
196         /* Find out which channel is busier */
197         for (i = 0; i < RK3399_DMC_NUM_CH; i++) {
198                 info->ch_usage[i].access = readl_relaxed(dfi_regs +
199                                 DDRMON_CH0_DFI_ACCESS_NUM + i * 20) * 4;
200                 info->ch_usage[i].total = readl_relaxed(dfi_regs +
201                                 DDRMON_CH0_COUNT_NUM + i * 20);
202                 tmp = info->ch_usage[i].access;
203                 if (tmp > max) {
204                         busier_ch = i;
205                         max = tmp;
206                 }
207         }
208         rockchip_dfi_start_hardware_counter(edev);
209
210         return busier_ch;
211 }
212
213 static int rockchip_dfi_disable(struct devfreq_event_dev *edev)
214 {
215         struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
216
217         rockchip_dfi_stop_hardware_counter(edev);
218         clk_disable_unprepare(info->clk);
219
220         return 0;
221 }
222
223 static int rockchip_dfi_enable(struct devfreq_event_dev *edev)
224 {
225         struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
226         int ret;
227
228         ret = clk_prepare_enable(info->clk);
229         if (ret) {
230                 dev_err(&edev->dev, "failed to enable dfi clk: %d\n", ret);
231                 return ret;
232         }
233
234         rockchip_dfi_start_hardware_counter(edev);
235         return 0;
236 }
237
238 static int rockchip_dfi_set_event(struct devfreq_event_dev *edev)
239 {
240         return 0;
241 }
242
243 static int rockchip_dfi_get_event(struct devfreq_event_dev *edev,
244                                   struct devfreq_event_data *edata)
245 {
246         struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
247         int busier_ch;
248         unsigned long flags;
249
250         local_irq_save(flags);
251         busier_ch = rockchip_dfi_get_busier_ch(edev);
252         local_irq_restore(flags);
253
254         edata->load_count = info->ch_usage[busier_ch].access;
255         edata->total_count = info->ch_usage[busier_ch].total;
256
257         return 0;
258 }
259
260 static const struct devfreq_event_ops rockchip_dfi_ops = {
261         .disable = rockchip_dfi_disable,
262         .enable = rockchip_dfi_enable,
263         .get_event = rockchip_dfi_get_event,
264         .set_event = rockchip_dfi_set_event,
265 };
266
267 static __init int rk3368_dfi_init(struct platform_device *pdev,
268                                   struct rockchip_dfi *data,
269                                   struct devfreq_event_desc *desc)
270 {
271         struct device *dev = &pdev->dev;
272
273         if (!dev->parent || !dev->parent->of_node)
274                 return -EINVAL;
275
276         data->regmap_grf = syscon_node_to_regmap(dev->parent->of_node);
277         if (IS_ERR(data->regmap_grf))
278                 return PTR_ERR(data->regmap_grf);
279
280         desc->ops = &rk3368_dfi_ops;
281
282         return 0;
283 }
284
285 static __init int rockchip_dfi_init(struct platform_device *pdev,
286                                     struct rockchip_dfi *data,
287                                     struct devfreq_event_desc *desc)
288 {
289         struct device *dev = &pdev->dev;
290         struct resource *res;
291         struct device_node *np = pdev->dev.of_node, *node;
292
293         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
294         data->regs = devm_ioremap_resource(&pdev->dev, res);
295         if (IS_ERR(data->regs))
296                 return PTR_ERR(data->regs);
297
298         data->clk = devm_clk_get(dev, "pclk_ddr_mon");
299         if (IS_ERR(data->clk)) {
300                 dev_err(dev, "Cannot get the clk dmc_clk\n");
301                 return PTR_ERR(data->clk);
302         };
303
304         /* try to find the optional reference to the pmu syscon */
305         node = of_parse_phandle(np, "rockchip,pmu", 0);
306         if (node) {
307                 data->regmap_pmu = syscon_node_to_regmap(node);
308                 if (IS_ERR(data->regmap_pmu))
309                         return PTR_ERR(data->regmap_pmu);
310         }
311
312         desc->ops = &rockchip_dfi_ops;
313
314         return 0;
315 }
316
317 static const struct of_device_id rockchip_dfi_id_match[] = {
318         { .compatible = "rockchip,rk3368-dfi", .data = rk3368_dfi_init },
319         { .compatible = "rockchip,rk3399-dfi", .data = rockchip_dfi_init },
320         { },
321 };
322
323 static int rockchip_dfi_probe(struct platform_device *pdev)
324 {
325         struct device *dev = &pdev->dev;
326         struct rockchip_dfi *data;
327         struct devfreq_event_desc *desc;
328         struct device_node *np = pdev->dev.of_node;
329         const struct of_device_id *match;
330         int (*init)(struct platform_device *pdev, struct rockchip_dfi *data,
331                     struct devfreq_event_desc *desc);
332
333         data = devm_kzalloc(dev, sizeof(struct rockchip_dfi), GFP_KERNEL);
334         if (!data)
335                 return -ENOMEM;
336
337         desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
338         if (!desc)
339                 return -ENOMEM;
340
341         match = of_match_node(rockchip_dfi_id_match, pdev->dev.of_node);
342         if (match) {
343                 init = match->data;
344                 if (init) {
345                         if (init(pdev, data, desc))
346                                 return -EINVAL;
347                 } else {
348                         return 0;
349                 }
350         } else {
351                 return 0;
352         }
353
354         desc->driver_data = data;
355         desc->name = np->name;
356
357         data->edev = devm_devfreq_event_add_edev(dev, desc);
358         if (IS_ERR(data->edev)) {
359                 dev_err(dev, "failed to add devfreq-event device\n");
360                 return PTR_ERR(data->edev);
361         }
362         data->desc = desc;
363         data->dev = &pdev->dev;
364
365         platform_set_drvdata(pdev, data);
366
367         return 0;
368 }
369
370 static struct platform_driver rockchip_dfi_driver = {
371         .probe  = rockchip_dfi_probe,
372         .driver = {
373                 .name   = "rockchip-dfi",
374                 .of_match_table = rockchip_dfi_id_match,
375         },
376 };
377 module_platform_driver(rockchip_dfi_driver);
378
379 MODULE_LICENSE("GPL v2");
380 MODULE_AUTHOR("Lin Huang <hl@rock-chips.com>");
381 MODULE_DESCRIPTION("Rockchip DFI driver");