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