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 int 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 return 0; 162 } 163 164 enum brcmstb_memc_hwtype { 165 BRCMSTB_MEMC_V21, 166 BRCMSTB_MEMC_V20, 167 BRCMSTB_MEMC_V1X, 168 }; 169 170 static const struct brcmstb_memc_data brcmstb_memc_versions[] = { 171 { .srpd_offset = REG_MEMC_SRPD_CFG_21 }, 172 { .srpd_offset = REG_MEMC_SRPD_CFG_20 }, 173 { .srpd_offset = REG_MEMC_SRPD_CFG_1x }, 174 }; 175 176 static const struct of_device_id brcmstb_memc_of_match[] = { 177 { 178 .compatible = "brcm,brcmstb-memc-ddr-rev-b.1.x", 179 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V1X] 180 }, 181 { 182 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.0", 183 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V20] 184 }, 185 { 186 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.1", 187 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 188 }, 189 { 190 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.2", 191 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 192 }, 193 { 194 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.3", 195 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 196 }, 197 { 198 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.5", 199 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 200 }, 201 { 202 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.6", 203 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 204 }, 205 { 206 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.7", 207 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 208 }, 209 { 210 .compatible = "brcm,brcmstb-memc-ddr-rev-b.2.8", 211 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 212 }, 213 { 214 .compatible = "brcm,brcmstb-memc-ddr-rev-b.3.0", 215 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 216 }, 217 { 218 .compatible = "brcm,brcmstb-memc-ddr-rev-b.3.1", 219 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 220 }, 221 { 222 .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.0", 223 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 224 }, 225 { 226 .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.1", 227 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 228 }, 229 { 230 .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.2", 231 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 232 }, 233 { 234 .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.3", 235 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 236 }, 237 { 238 .compatible = "brcm,brcmstb-memc-ddr-rev-c.1.4", 239 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 240 }, 241 /* default to the original offset */ 242 { 243 .compatible = "brcm,brcmstb-memc-ddr", 244 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V1X] 245 }, 246 {} 247 }; 248 249 static int brcmstb_memc_suspend(struct device *dev) 250 { 251 struct brcmstb_memc *memc = dev_get_drvdata(dev); 252 void __iomem *cfg = memc->ddr_ctrl + memc->srpd_offset; 253 u32 val; 254 255 if (memc->timeout_cycles == 0) 256 return 0; 257 258 /* 259 * Disable SRPD prior to suspending the system since that can 260 * cause issues with other memory clients managed by the ARM 261 * trusted firmware to access memory. 262 */ 263 val = readl_relaxed(cfg); 264 val &= ~BIT(SRPD_EN_SHIFT); 265 writel_relaxed(val, cfg); 266 /* Ensure the write is committed to the controller */ 267 (void)readl_relaxed(cfg); 268 269 return 0; 270 } 271 272 static int brcmstb_memc_resume(struct device *dev) 273 { 274 struct brcmstb_memc *memc = dev_get_drvdata(dev); 275 276 if (memc->timeout_cycles == 0) 277 return 0; 278 279 return brcmstb_memc_srpd_config(memc, memc->timeout_cycles); 280 } 281 282 static DEFINE_SIMPLE_DEV_PM_OPS(brcmstb_memc_pm_ops, brcmstb_memc_suspend, 283 brcmstb_memc_resume); 284 285 static struct platform_driver brcmstb_memc_driver = { 286 .probe = brcmstb_memc_probe, 287 .remove = brcmstb_memc_remove, 288 .driver = { 289 .name = "brcmstb_memc", 290 .of_match_table = brcmstb_memc_of_match, 291 .pm = pm_ptr(&brcmstb_memc_pm_ops), 292 }, 293 }; 294 module_platform_driver(brcmstb_memc_driver); 295 296 MODULE_LICENSE("GPL"); 297 MODULE_AUTHOR("Broadcom"); 298 MODULE_DESCRIPTION("DDR SRPD driver for Broadcom STB chips"); 299