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_LPDDR5_SHIFT 6 18 #define CNTRLR_CONFIG_MASK 0xf 19 #define REG_MEMC_SRPD_CFG_21 0x20 20 #define REG_MEMC_SRPD_CFG_20 0x34 21 #define REG_MEMC_SRPD_CFG_1x 0x3c 22 #define INACT_COUNT_SHIFT 0 23 #define INACT_COUNT_MASK 0xffff 24 #define SRPD_EN_SHIFT 16 25 26 struct brcmstb_memc_data { 27 u32 srpd_offset; 28 }; 29 30 struct brcmstb_memc { 31 struct device *dev; 32 void __iomem *ddr_ctrl; 33 unsigned int timeout_cycles; 34 u32 frequency; 35 u32 srpd_offset; 36 }; 37 38 static int brcmstb_memc_uses_lpddr45(struct brcmstb_memc *memc) 39 { 40 void __iomem *config = memc->ddr_ctrl + REG_MEMC_CNTRLR_CONFIG; 41 u32 reg; 42 43 reg = readl_relaxed(config) & CNTRLR_CONFIG_MASK; 44 45 return reg == CNTRLR_CONFIG_LPDDR4_SHIFT || 46 reg == CNTRLR_CONFIG_LPDDR5_SHIFT; 47 } 48 49 static int brcmstb_memc_srpd_config(struct brcmstb_memc *memc, 50 unsigned int cycles) 51 { 52 void __iomem *cfg = memc->ddr_ctrl + memc->srpd_offset; 53 u32 val; 54 55 /* Max timeout supported in HW */ 56 if (cycles > INACT_COUNT_MASK) 57 return -EINVAL; 58 59 memc->timeout_cycles = cycles; 60 61 val = (cycles << INACT_COUNT_SHIFT) & INACT_COUNT_MASK; 62 if (cycles) 63 val |= BIT(SRPD_EN_SHIFT); 64 65 writel_relaxed(val, cfg); 66 /* Ensure the write is committed to the controller */ 67 (void)readl_relaxed(cfg); 68 69 return 0; 70 } 71 72 static ssize_t frequency_show(struct device *dev, 73 struct device_attribute *attr, char *buf) 74 { 75 struct brcmstb_memc *memc = dev_get_drvdata(dev); 76 77 return sprintf(buf, "%d\n", memc->frequency); 78 } 79 80 static ssize_t srpd_show(struct device *dev, 81 struct device_attribute *attr, char *buf) 82 { 83 struct brcmstb_memc *memc = dev_get_drvdata(dev); 84 85 return sprintf(buf, "%d\n", memc->timeout_cycles); 86 } 87 88 static ssize_t srpd_store(struct device *dev, struct device_attribute *attr, 89 const char *buf, size_t count) 90 { 91 struct brcmstb_memc *memc = dev_get_drvdata(dev); 92 unsigned int val; 93 int ret; 94 95 /* 96 * Cannot change the inactivity timeout on LPDDR4 chips because the 97 * dynamic tuning process will also get affected by the inactivity 98 * timeout, thus making it non functional. 99 */ 100 if (brcmstb_memc_uses_lpddr45(memc)) 101 return -EOPNOTSUPP; 102 103 ret = kstrtouint(buf, 10, &val); 104 if (ret < 0) 105 return ret; 106 107 ret = brcmstb_memc_srpd_config(memc, val); 108 if (ret) 109 return ret; 110 111 return count; 112 } 113 114 static DEVICE_ATTR_RO(frequency); 115 static DEVICE_ATTR_RW(srpd); 116 117 static struct attribute *dev_attrs[] = { 118 &dev_attr_frequency.attr, 119 &dev_attr_srpd.attr, 120 NULL, 121 }; 122 123 static struct attribute_group dev_attr_group = { 124 .attrs = dev_attrs, 125 }; 126 127 static int brcmstb_memc_probe(struct platform_device *pdev) 128 { 129 const struct brcmstb_memc_data *memc_data; 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 memc_data = device_get_match_data(dev); 141 memc->srpd_offset = memc_data->srpd_offset; 142 143 memc->ddr_ctrl = devm_platform_ioremap_resource(pdev, 0); 144 if (IS_ERR(memc->ddr_ctrl)) 145 return PTR_ERR(memc->ddr_ctrl); 146 147 of_property_read_u32(pdev->dev.of_node, "clock-frequency", 148 &memc->frequency); 149 150 ret = sysfs_create_group(&dev->kobj, &dev_attr_group); 151 if (ret) 152 return ret; 153 154 return 0; 155 } 156 157 static void brcmstb_memc_remove(struct platform_device *pdev) 158 { 159 struct device *dev = &pdev->dev; 160 161 sysfs_remove_group(&dev->kobj, &dev_attr_group); 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 /* default to the V21 offset */ 190 { 191 .compatible = "brcm,brcmstb-memc-ddr", 192 .data = &brcmstb_memc_versions[BRCMSTB_MEMC_V21] 193 }, 194 {} 195 }; 196 MODULE_DEVICE_TABLE(of, brcmstb_memc_of_match); 197 198 static int brcmstb_memc_suspend(struct device *dev) 199 { 200 struct brcmstb_memc *memc = dev_get_drvdata(dev); 201 void __iomem *cfg = memc->ddr_ctrl + memc->srpd_offset; 202 u32 val; 203 204 if (memc->timeout_cycles == 0) 205 return 0; 206 207 /* 208 * Disable SRPD prior to suspending the system since that can 209 * cause issues with other memory clients managed by the ARM 210 * trusted firmware to access memory. 211 */ 212 val = readl_relaxed(cfg); 213 val &= ~BIT(SRPD_EN_SHIFT); 214 writel_relaxed(val, cfg); 215 /* Ensure the write is committed to the controller */ 216 (void)readl_relaxed(cfg); 217 218 return 0; 219 } 220 221 static int brcmstb_memc_resume(struct device *dev) 222 { 223 struct brcmstb_memc *memc = dev_get_drvdata(dev); 224 225 if (memc->timeout_cycles == 0) 226 return 0; 227 228 return brcmstb_memc_srpd_config(memc, memc->timeout_cycles); 229 } 230 231 static DEFINE_SIMPLE_DEV_PM_OPS(brcmstb_memc_pm_ops, brcmstb_memc_suspend, 232 brcmstb_memc_resume); 233 234 static struct platform_driver brcmstb_memc_driver = { 235 .probe = brcmstb_memc_probe, 236 .remove = brcmstb_memc_remove, 237 .driver = { 238 .name = "brcmstb_memc", 239 .of_match_table = brcmstb_memc_of_match, 240 .pm = pm_ptr(&brcmstb_memc_pm_ops), 241 }, 242 }; 243 module_platform_driver(brcmstb_memc_driver); 244 245 MODULE_LICENSE("GPL"); 246 MODULE_AUTHOR("Broadcom"); 247 MODULE_DESCRIPTION("DDR SRPD driver for Broadcom STB chips"); 248