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 #include <soc/rockchip/rk3399_grf.h> 30 31 #define RK3399_DMC_NUM_CH 2 32 33 /* DDRMON_CTRL */ 34 #define DDRMON_CTRL 0x04 35 #define CLR_DDRMON_CTRL (0x1f0000 << 0) 36 #define LPDDR4_EN (0x10001 << 4) 37 #define HARDWARE_EN (0x10001 << 3) 38 #define LPDDR3_EN (0x10001 << 2) 39 #define SOFTWARE_EN (0x10001 << 1) 40 #define SOFTWARE_DIS (0x10000 << 1) 41 #define TIME_CNT_EN (0x10001 << 0) 42 43 #define DDRMON_CH0_COUNT_NUM 0x28 44 #define DDRMON_CH0_DFI_ACCESS_NUM 0x2c 45 #define DDRMON_CH1_COUNT_NUM 0x3c 46 #define DDRMON_CH1_DFI_ACCESS_NUM 0x40 47 48 struct dmc_usage { 49 u32 access; 50 u32 total; 51 }; 52 53 /* 54 * The dfi controller can monitor DDR load. It has an upper and lower threshold 55 * for the operating points. Whenever the usage leaves these bounds an event is 56 * generated to indicate the DDR frequency should be changed. 57 */ 58 struct rockchip_dfi { 59 struct devfreq_event_dev *edev; 60 struct devfreq_event_desc *desc; 61 struct dmc_usage ch_usage[RK3399_DMC_NUM_CH]; 62 struct device *dev; 63 void __iomem *regs; 64 struct regmap *regmap_pmu; 65 struct clk *clk; 66 }; 67 68 static void rockchip_dfi_start_hardware_counter(struct devfreq_event_dev *edev) 69 { 70 struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); 71 void __iomem *dfi_regs = info->regs; 72 u32 val; 73 u32 ddr_type; 74 75 /* get ddr type */ 76 regmap_read(info->regmap_pmu, RK3399_PMUGRF_OS_REG2, &val); 77 ddr_type = (val >> RK3399_PMUGRF_DDRTYPE_SHIFT) & 78 RK3399_PMUGRF_DDRTYPE_MASK; 79 80 /* clear DDRMON_CTRL setting */ 81 writel_relaxed(CLR_DDRMON_CTRL, dfi_regs + DDRMON_CTRL); 82 83 /* set ddr type to dfi */ 84 if (ddr_type == RK3399_PMUGRF_DDRTYPE_LPDDR3) 85 writel_relaxed(LPDDR3_EN, dfi_regs + DDRMON_CTRL); 86 else if (ddr_type == RK3399_PMUGRF_DDRTYPE_LPDDR4) 87 writel_relaxed(LPDDR4_EN, dfi_regs + DDRMON_CTRL); 88 89 /* enable count, use software mode */ 90 writel_relaxed(SOFTWARE_EN, dfi_regs + DDRMON_CTRL); 91 } 92 93 static void rockchip_dfi_stop_hardware_counter(struct devfreq_event_dev *edev) 94 { 95 struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); 96 void __iomem *dfi_regs = info->regs; 97 98 writel_relaxed(SOFTWARE_DIS, dfi_regs + DDRMON_CTRL); 99 } 100 101 static int rockchip_dfi_get_busier_ch(struct devfreq_event_dev *edev) 102 { 103 struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); 104 u32 tmp, max = 0; 105 u32 i, busier_ch = 0; 106 void __iomem *dfi_regs = info->regs; 107 108 rockchip_dfi_stop_hardware_counter(edev); 109 110 /* Find out which channel is busier */ 111 for (i = 0; i < RK3399_DMC_NUM_CH; i++) { 112 info->ch_usage[i].access = readl_relaxed(dfi_regs + 113 DDRMON_CH0_DFI_ACCESS_NUM + i * 20) * 4; 114 info->ch_usage[i].total = readl_relaxed(dfi_regs + 115 DDRMON_CH0_COUNT_NUM + i * 20); 116 tmp = info->ch_usage[i].access; 117 if (tmp > max) { 118 busier_ch = i; 119 max = tmp; 120 } 121 } 122 rockchip_dfi_start_hardware_counter(edev); 123 124 return busier_ch; 125 } 126 127 static int rockchip_dfi_disable(struct devfreq_event_dev *edev) 128 { 129 struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); 130 131 rockchip_dfi_stop_hardware_counter(edev); 132 clk_disable_unprepare(info->clk); 133 134 return 0; 135 } 136 137 static int rockchip_dfi_enable(struct devfreq_event_dev *edev) 138 { 139 struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); 140 int ret; 141 142 ret = clk_prepare_enable(info->clk); 143 if (ret) { 144 dev_err(&edev->dev, "failed to enable dfi clk: %d\n", ret); 145 return ret; 146 } 147 148 rockchip_dfi_start_hardware_counter(edev); 149 return 0; 150 } 151 152 static int rockchip_dfi_set_event(struct devfreq_event_dev *edev) 153 { 154 return 0; 155 } 156 157 static int rockchip_dfi_get_event(struct devfreq_event_dev *edev, 158 struct devfreq_event_data *edata) 159 { 160 struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); 161 int busier_ch; 162 163 busier_ch = rockchip_dfi_get_busier_ch(edev); 164 165 edata->load_count = info->ch_usage[busier_ch].access; 166 edata->total_count = info->ch_usage[busier_ch].total; 167 168 return 0; 169 } 170 171 static const struct devfreq_event_ops rockchip_dfi_ops = { 172 .disable = rockchip_dfi_disable, 173 .enable = rockchip_dfi_enable, 174 .get_event = rockchip_dfi_get_event, 175 .set_event = rockchip_dfi_set_event, 176 }; 177 178 static const struct of_device_id rockchip_dfi_id_match[] = { 179 { .compatible = "rockchip,rk3399-dfi" }, 180 { }, 181 }; 182 MODULE_DEVICE_TABLE(of, rockchip_dfi_id_match); 183 184 static int rockchip_dfi_probe(struct platform_device *pdev) 185 { 186 struct device *dev = &pdev->dev; 187 struct rockchip_dfi *data; 188 struct resource *res; 189 struct devfreq_event_desc *desc; 190 struct device_node *np = pdev->dev.of_node, *node; 191 192 data = devm_kzalloc(dev, sizeof(struct rockchip_dfi), GFP_KERNEL); 193 if (!data) 194 return -ENOMEM; 195 196 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 197 data->regs = devm_ioremap_resource(&pdev->dev, res); 198 if (IS_ERR(data->regs)) 199 return PTR_ERR(data->regs); 200 201 data->clk = devm_clk_get(dev, "pclk_ddr_mon"); 202 if (IS_ERR(data->clk)) { 203 dev_err(dev, "Cannot get the clk dmc_clk\n"); 204 return PTR_ERR(data->clk); 205 } 206 207 /* try to find the optional reference to the pmu syscon */ 208 node = of_parse_phandle(np, "rockchip,pmu", 0); 209 if (node) { 210 data->regmap_pmu = syscon_node_to_regmap(node); 211 if (IS_ERR(data->regmap_pmu)) 212 return PTR_ERR(data->regmap_pmu); 213 } 214 data->dev = dev; 215 216 desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); 217 if (!desc) 218 return -ENOMEM; 219 220 desc->ops = &rockchip_dfi_ops; 221 desc->driver_data = data; 222 desc->name = np->name; 223 data->desc = desc; 224 225 data->edev = devm_devfreq_event_add_edev(&pdev->dev, desc); 226 if (IS_ERR(data->edev)) { 227 dev_err(&pdev->dev, 228 "failed to add devfreq-event device\n"); 229 return PTR_ERR(data->edev); 230 } 231 232 platform_set_drvdata(pdev, data); 233 234 return 0; 235 } 236 237 static struct platform_driver rockchip_dfi_driver = { 238 .probe = rockchip_dfi_probe, 239 .driver = { 240 .name = "rockchip-dfi", 241 .of_match_table = rockchip_dfi_id_match, 242 }, 243 }; 244 module_platform_driver(rockchip_dfi_driver); 245 246 MODULE_LICENSE("GPL v2"); 247 MODULE_AUTHOR("Lin Huang <hl@rock-chips.com>"); 248 MODULE_DESCRIPTION("Rockchip DFI driver"); 249