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 24struct brcmstb_memc_data { 25 u32 srpd_offset; 26}; 27 28struct 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 36static 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 46static 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 69static 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 77static 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 85static 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 111static DEVICE_ATTR_RO(frequency); 112static DEVICE_ATTR_RW(srpd); 113 114static struct attribute *dev_attrs[] = { 115 &dev_attr_frequency.attr, 116 &dev_attr_srpd.attr, 117 NULL, 118}; 119 120static struct attribute_group dev_attr_group = { 121 .attrs = dev_attrs, 122}; 123 124static const struct of_device_id brcmstb_memc_of_match[]; 125 126static 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 158static 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 167enum brcmstb_memc_hwtype { 168 BRCMSTB_MEMC_V21, 169 BRCMSTB_MEMC_V20, 170 BRCMSTB_MEMC_V1X, 171}; 172 173static 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 179static 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 252static 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 275static 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 285static DEFINE_SIMPLE_DEV_PM_OPS(brcmstb_memc_pm_ops, brcmstb_memc_suspend, 286 brcmstb_memc_resume); 287 288static 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}; 297module_platform_driver(brcmstb_memc_driver); 298 299MODULE_LICENSE("GPL"); 300MODULE_AUTHOR("Broadcom"); 301MODULE_DESCRIPTION("DDR SRPD driver for Broadcom STB chips"); 302