298 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * DDR Self-Refresh Power Down (SRPD) support for Broadcom STB SoCs4 *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 0x0016#define CNTRLR_CONFIG_LPDDR4_SHIFT 517#define CNTRLR_CONFIG_MASK 0xf18#define REG_MEMC_SRPD_CFG_21 0x2019#define REG_MEMC_SRPD_CFG_20 0x3420#define REG_MEMC_SRPD_CFG_1x 0x3c21#define INACT_COUNT_SHIFT 022#define INACT_COUNT_MASK 0xffff23#define SRPD_EN_SHIFT 1624 25struct brcmstb_memc_data {26 u32 srpd_offset;27};28 29struct 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 37static 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 47static 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 70static 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 78static 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 86static 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 the95 * dynamic tuning process will also get affected by the inactivity96 * 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 112static DEVICE_ATTR_RO(frequency);113static DEVICE_ATTR_RW(srpd);114 115static struct attribute *dev_attrs[] = {116 &dev_attr_frequency.attr,117 &dev_attr_srpd.attr,118 NULL,119};120 121static struct attribute_group dev_attr_group = {122 .attrs = dev_attrs,123};124 125static 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 155static 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 162enum brcmstb_memc_hwtype {163 BRCMSTB_MEMC_V21,164 BRCMSTB_MEMC_V20,165 BRCMSTB_MEMC_V1X,166};167 168static 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 174static 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};246MODULE_DEVICE_TABLE(of, brcmstb_memc_of_match);247 248static 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 can259 * cause issues with other memory clients managed by the ARM260 * 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 271static 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 281static DEFINE_SIMPLE_DEV_PM_OPS(brcmstb_memc_pm_ops, brcmstb_memc_suspend,282 brcmstb_memc_resume);283 284static 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};293module_platform_driver(brcmstb_memc_driver);294 295MODULE_LICENSE("GPL");296MODULE_AUTHOR("Broadcom");297MODULE_DESCRIPTION("DDR SRPD driver for Broadcom STB chips");298