blob: fd7264fbcb3440a05f2cac63f543ddcf3a73161b [file] [log] [blame]
/*
* DDR Self-Refresh Power Down (SRPD) support for Broadcom STB SoCs
*
* Copyright © 2014 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/io.h>
#define REG_SRPD_CONFIG 0x3c
#define INACT_COUNT_SHIFT 0
#define INACT_COUNT_MASK 0xffff
#define SRPD_EN_SHIFT 16
#define SRPD_EN_MASK 0x10000
#define REG_POWER_DOWN_STATUS 0x44
#define SRPD_STATUS_SHIFT 1
#define SRPD_STATUS_MASK 0x2
struct brcmstb_memc {
struct device *dev;
void __iomem *ddr_ctrl;
unsigned int timeout_cycles;
};
static int brcmstb_memc_srpd_config(struct brcmstb_memc *memc,
unsigned int cycles)
{
void __iomem *cfg = memc->ddr_ctrl + REG_SRPD_CONFIG;
u32 val;
/* Max timeout supported in HW */
if (cycles > INACT_COUNT_MASK)
return -EINVAL;
memc->timeout_cycles = cycles;
val = (cycles << INACT_COUNT_SHIFT) & INACT_COUNT_MASK;
if (cycles)
val |= (1 << SRPD_EN_SHIFT);
__raw_writel(val, cfg);
(void)__raw_readl(cfg);
return 0;
}
static ssize_t show_attr_srpd(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct brcmstb_memc *memc = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", memc->timeout_cycles);
}
static ssize_t store_attr_srpd(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct brcmstb_memc *memc = dev_get_drvdata(dev);
unsigned int val;
int ret;
ret = kstrtouint(buf, 10, &val);
if (ret < 0)
return ret;
ret = brcmstb_memc_srpd_config(memc, val);
if (ret)
return ret;
return count;
}
static DEVICE_ATTR(srpd, S_IRUSR | S_IWUSR, show_attr_srpd, store_attr_srpd);
static struct attribute *dev_attrs[] = {
&dev_attr_srpd.attr,
NULL,
};
static struct attribute_group dev_attr_group = {
.attrs = dev_attrs,
};
static int brcmstb_memc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct brcmstb_memc *memc;
struct resource *res;
int ret;
memc = devm_kzalloc(dev, sizeof(*memc), GFP_KERNEL);
if (!memc)
return -ENOMEM;
dev_set_drvdata(dev, memc);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
memc->ddr_ctrl = devm_ioremap_resource(dev, res);
if (IS_ERR(memc->ddr_ctrl))
return PTR_ERR(memc->ddr_ctrl);
ret = sysfs_create_group(&dev->kobj, &dev_attr_group);
if (ret) {
dev_err(dev, "failed to create attribute group (%d)\n", ret);
return ret;
}
dev_info(dev, "registered\n");
return 0;
}
static int brcmstb_memc_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
sysfs_remove_group(&dev->kobj, &dev_attr_group);
return 0;
}
static const struct of_device_id brcmstb_memc_of_match[] = {
{ .compatible = "brcm,brcmstb-memc-ddr" },
{},
};
static struct platform_driver brcmstb_memc_driver = {
.probe = brcmstb_memc_probe,
.remove = brcmstb_memc_remove,
.driver = {
.name = "brcmstb_memc",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(brcmstb_memc_of_match),
},
};
module_platform_driver(brcmstb_memc_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Broadcom Corporation");
MODULE_DESCRIPTION("DDR SRPD driver for Broadcom STB chips");