brintos

brintos / linux-shallow public Read only

0
0
Text · 7.0 KiB · 4f17a93 Raw
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