1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * DDR Self-Refresh Power Down (SRPD) support for Broadcom STB SoCs 4 * 5 */ 6 7 #include <linux/init.h> 8 #include <linux/io.h> 9 #include <linux/kernel.h> 10 #include <linux/module.h> 11 #include <linux/of_device.h> 12 #include <linux/platform_device.h> 13 14 #define REG_MEMC_CNTRLR_CONFIG 0x00 15 #define CNTRLR_CONFIG_LPDDR4_SHIFT 5 16 #define CNTRLR_CONFIG_MASK 0xf 17 #define REG_MEMC_SRPD_CFG_21 0x20 18 #define REG_MEMC_SRPD_CFG_20 0x34 19 #define REG_MEMC_SRPD_CFG_1x 0x3c 20 #define INACT_COUNT_SHIFT 0 21 #define INACT_COUNT_MASK 0xffff 22 #define SRPD_EN_SHIFT 16 23 24 struct brcmstb_memc_data { 25 u32 srpd_offset; 26 }; 27 28 struct brcmstb_memc { 29 struct device *dev; 30 void __iomem *ddr_ctrl; 31 unsigned int timeout_cycles; 32 u32 frequency; 33 u32 srpd_offset; 34 }; 35 36 static int brcmstb_memc_uses_lpddr4(struct brcmstb_memc *memc) 37 { 38 void __iomem *config = memc->ddr_ctrl + REG_MEMC_CNTRLR_CONFIG; 39 u32 reg; 40 41 reg = readl_relaxed(config) & CNTRLR_CONFIG_MASK; 42 43 return reg == CNTRLR_CONFIG_LPDDR4_SHIFT; 44 } 45 46 static int brcmstb_memc_srpd_config(struct brcmstb_memc *memc, 47 unsigned int cycles) 48 { 49 void __iomem *cfg = memc->ddr_ctrl + memc->srpd_offset; 50 u32 val; 51 52 /* Max timeout supported in HW */ 53 if (cycles > INACT_COUNT_MASK) 54 return -EINVAL; 55 56 memc->timeout_cycles = cycles; 57 58 val = (cycles << INACT_COUNT_SHIFT) & INACT_COUNT_MASK; 59 if (cycles) 60 val |= BIT(SRPD_EN_SHIFT); 61 62 writel_relaxed(val, cfg); 63 /* Ensure the write is committed to the controller */ 64 (void)readl_relaxed(cfg); 65 66 return 0; 67 } 68 69 static ssize_t frequency_show(struct device *dev, 70 struct device_attribute *attr, char *buf) 71 { 72 struct brcmstb_memc *memc = dev_get_drvdata(dev); 73 74 return sprintf(buf, "%d\n", memc->frequency); 75 } 76 77 static ssize_t srpd_show(struct device *dev, 78 struct device_attribute *attr, char *buf) 79 { 80 struct brcmstb_memc *memc = dev_get_drvdata(dev); 81 82 return sprintf(buf, "%d\n", memc->timeout_cycles); 83 } 84 85 static ssize_t srpd_store(struct device *dev, struct device_attribute *attr, 86 const char *buf, size_t count) 87 { 88 struct brcmstb_memc *memc = dev_get_drvdata(dev); 89 unsigned int val; 90 int ret; 91 92 /* 93 * Cannot change the inactivity timeout on LPDDR4 chips because the 94 * dynamic tuning process will also get affected by the inactivity 95 * timeout, thus making it non functional. 96 */ 97 if (brcmstb_memc_uses_lpddr4(memc)) 98 return -EOPNOTSUPP; 99 100 ret = kstrtouint(buf, 10, &val); 101 if (ret < 0) 102 return ret; 103 104 ret = brcmstb_memc_srpd_config(memc, val); 105 if (ret) 106 return ret; 107 108 return count; 109 } 110 111 static DEVICE_ATTR_RO(frequency); 112 static DEVICE_ATTR_RW(srpd); 113 114 static struct attribute *dev_attrs[] = { 115 &dev_attr_frequency.attr, 116 &dev_attr_srpd.attr, 117 NULL, 118 }; 119 120 static struct attribute_group dev_attr_group = { 121 .attrs = dev_attrs, 122 }; 123 124 static const struct of_device_id brcmstb_memc_of_match[]; 125 126 static int brcmstb_memc_probe(struct platform_device *pdev) 127 { 128 const struct brcmstb_memc_data *memc_data; 129 const struct of_device_id *of_id; 130 struct device *dev = &pdev->dev; 131 struct brcmstb_memc *memc; 132 int ret; 133 134 memc = devm_kzalloc(dev, sizeof(*memc), GFP_KERNEL); 135 if (!memc) 136 return -ENOMEM; 137 138 dev_set_drvdata(dev, memc); 139 140 of_id = of_match_device(brcmstb_memc_of_match, dev); 141 memc_data = of_id->data; 142 memc->srpd_offset = memc_data->srpd_offset; 143 144 memc->ddr_ctrl = devm_platform_ioremap_resource(pdev, 0); 145 if (IS_ERR(memc->ddr_ctrl)) 146 return PTR_ERR(memc->ddr_ctrl); 147 148 of_property_read_u32(pdev->dev.of_node, "clock-frequency", 149 &memc->frequency); 150 151 ret = sysfs_create_group(&dev->kobj, &dev_attr_group); 152 if (ret) 153 return ret; 154 155 return 0; 156 } 157 158 static int brcmstb_memc_remove(struct platform_device *pdev) 159 { 160 struct device *dev = &pdev->dev; 161 162 sysfs_remove_group(&dev->kobj, &dev_attr_group); 163 164 return 0; 165 } 166 167 enum brcmstb_memc_hwtype { 168 BRCMSTB_MEMC_V21, 169 BRCMSTB_MEMC_V20, 170 BRCMSTB_MEMC_V1X, 171 }; 172 173 static const struct brcmstb_memc_data brcmstb_memc_versions[] = { 174 { .srpd_offset = REG_MEMC_SRPD_CFG_21 }, 175 { .srpd_offset = REG_MEMC_SRPD_CFG_20 }, 176 { .srpd_offset = REG_MEMC_SRPD_CFG_1x }, 177 }; 178 179 static const struct of_device_id brcmstb_memc_of_match[] = { 180 { 181 .compatible = "brcm,brcmstb-memc-ddr-rev-b.1.x", 182 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V1X] 183 }, 184 { 185 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.0", 186 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V20] 187 }, 188 { 189 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.1", 190 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 191 }, 192 { 193 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.2", 194 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 195 }, 196 { 197 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.3", 198 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 199 }, 200 { 201 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.5", 202 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 203 }, 204 { 205 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.6", 206 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 207 }, 208 { 209 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.7", 210 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 211 }, 212 { 213 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.8", 214 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 215 }, 216 { 217 .compatible = "brcm,brcmstb-memc-ddr-rev-b.3.0", 218 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 219 }, 220 { 221 .compatible = "brcm,brcmstb-memc-ddr-rev-b.3.1", 222 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 223 }, 224 { 225 .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.0", 226 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 227 }, 228 { 229 .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.1", 230 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 231 }, 232 { 233 .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.2", 234 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 235 }, 236 { 237 .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.3", 238 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 239 }, 240 { 241 .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.4", 242 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 243 }, 244 /* default to the original offset */ 245 { 246 .compatible = "brcm,brcmstb-memc-ddr", 247 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V1X] 248 }, 249 {} 250 }; 251 252 static int brcmstb_memc_suspend(struct device *dev) 253 { 254 struct brcmstb_memc *memc = dev_get_drvdata(dev); 255 void __iomem *cfg = memc->ddr_ctrl + memc->srpd_offset; 256 u32 val; 257 258 if (memc->timeout_cycles == 0) 259 return 0; 260 261 /* 262 * Disable SRPD prior to suspending the system since that can 263 * cause issues with other memory clients managed by the ARM 264 * trusted firmware to access memory. 265 */ 266 val = readl_relaxed(cfg); 267 val &= ~BIT(SRPD_EN_SHIFT); 268 writel_relaxed(val, cfg); 269 /* Ensure the write is committed to the controller */ 270 (void)readl_relaxed(cfg); 271 272 return 0; 273 } 274 275 static int brcmstb_memc_resume(struct device *dev) 276 { 277 struct brcmstb_memc *memc = dev_get_drvdata(dev); 278 279 if (memc->timeout_cycles == 0) 280 return 0; 281 282 return brcmstb_memc_srpd_config(memc, memc->timeout_cycles); 283 } 284 285 static DEFINE_SIMPLE_DEV_PM_OPS(brcmstb_memc_pm_ops, brcmstb_memc_suspend, 286 brcmstb_memc_resume); 287 288 static struct platform_driver brcmstb_memc_driver = { 289 .probe = brcmstb_memc_probe, 290 .remove = brcmstb_memc_remove, 291 .driver = { 292 .name = "brcmstb_memc", 293 .of_match_table = brcmstb_memc_of_match, 294 .pm = pm_ptr(&brcmstb_memc_pm_ops), 295 }, 296 }; 297 module_platform_driver(brcmstb_memc_driver); 298 299 MODULE_LICENSE("GPL"); 300 MODULE_AUTHOR("Broadcom"); 301 MODULE_DESCRIPTION("DDR SRPD driver for Broadcom STB chips"); 302