forked from Minki/linux
138 lines
3.5 KiB
C
138 lines
3.5 KiB
C
|
/*
|
||
|
* Copyright (C) 2017 Synopsys.
|
||
|
*
|
||
|
* Synopsys HSDKv1 SDP reset driver.
|
||
|
*
|
||
|
* This file is licensed under the terms of the GNU General Public
|
||
|
* License version 2. This program is licensed "as is" without any
|
||
|
* warranty of any kind, whether express or implied.
|
||
|
*/
|
||
|
|
||
|
#include <linux/delay.h>
|
||
|
#include <linux/io.h>
|
||
|
#include <linux/iopoll.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/of.h>
|
||
|
#include <linux/platform_device.h>
|
||
|
#include <linux/reset-controller.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/types.h>
|
||
|
|
||
|
#define to_hsdkv1_rst(p) container_of((p), struct hsdkv1_rst, rcdev)
|
||
|
|
||
|
struct hsdkv1_rst {
|
||
|
void __iomem *regs_ctl;
|
||
|
void __iomem *regs_rst;
|
||
|
spinlock_t lock;
|
||
|
struct reset_controller_dev rcdev;
|
||
|
};
|
||
|
|
||
|
static const u32 rst_map[] = {
|
||
|
BIT(16), /* APB_RST */
|
||
|
BIT(17), /* AXI_RST */
|
||
|
BIT(18), /* ETH_RST */
|
||
|
BIT(19), /* USB_RST */
|
||
|
BIT(20), /* SDIO_RST */
|
||
|
BIT(21), /* HDMI_RST */
|
||
|
BIT(22), /* GFX_RST */
|
||
|
BIT(25), /* DMAC_RST */
|
||
|
BIT(31), /* EBI_RST */
|
||
|
};
|
||
|
|
||
|
#define HSDK_MAX_RESETS ARRAY_SIZE(rst_map)
|
||
|
|
||
|
#define CGU_SYS_RST_CTRL 0x0
|
||
|
#define CGU_IP_SW_RESET 0x0
|
||
|
#define CGU_IP_SW_RESET_DELAY_SHIFT 16
|
||
|
#define CGU_IP_SW_RESET_DELAY_MASK GENMASK(31, CGU_IP_SW_RESET_DELAY_SHIFT)
|
||
|
#define CGU_IP_SW_RESET_DELAY 0
|
||
|
#define CGU_IP_SW_RESET_RESET BIT(0)
|
||
|
#define SW_RESET_TIMEOUT 10000
|
||
|
|
||
|
static void hsdkv1_reset_config(struct hsdkv1_rst *rst, unsigned long id)
|
||
|
{
|
||
|
writel(rst_map[id], rst->regs_ctl + CGU_SYS_RST_CTRL);
|
||
|
}
|
||
|
|
||
|
static int hsdkv1_reset_do(struct hsdkv1_rst *rst)
|
||
|
{
|
||
|
u32 reg;
|
||
|
|
||
|
reg = readl(rst->regs_rst + CGU_IP_SW_RESET);
|
||
|
reg &= ~CGU_IP_SW_RESET_DELAY_MASK;
|
||
|
reg |= CGU_IP_SW_RESET_DELAY << CGU_IP_SW_RESET_DELAY_SHIFT;
|
||
|
reg |= CGU_IP_SW_RESET_RESET;
|
||
|
writel(reg, rst->regs_rst + CGU_IP_SW_RESET);
|
||
|
|
||
|
/* wait till reset bit is back to 0 */
|
||
|
return readl_poll_timeout_atomic(rst->regs_rst + CGU_IP_SW_RESET, reg,
|
||
|
!(reg & CGU_IP_SW_RESET_RESET), 5, SW_RESET_TIMEOUT);
|
||
|
}
|
||
|
|
||
|
static int hsdkv1_reset_reset(struct reset_controller_dev *rcdev,
|
||
|
unsigned long id)
|
||
|
{
|
||
|
struct hsdkv1_rst *rst = to_hsdkv1_rst(rcdev);
|
||
|
unsigned long flags;
|
||
|
int ret;
|
||
|
|
||
|
spin_lock_irqsave(&rst->lock, flags);
|
||
|
hsdkv1_reset_config(rst, id);
|
||
|
ret = hsdkv1_reset_do(rst);
|
||
|
spin_unlock_irqrestore(&rst->lock, flags);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static const struct reset_control_ops hsdkv1_reset_ops = {
|
||
|
.reset = hsdkv1_reset_reset,
|
||
|
};
|
||
|
|
||
|
static int hsdkv1_reset_probe(struct platform_device *pdev)
|
||
|
{
|
||
|
struct hsdkv1_rst *rst;
|
||
|
struct resource *mem;
|
||
|
|
||
|
rst = devm_kzalloc(&pdev->dev, sizeof(*rst), GFP_KERNEL);
|
||
|
if (!rst)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||
|
rst->regs_ctl = devm_ioremap_resource(&pdev->dev, mem);
|
||
|
if (IS_ERR(rst->regs_ctl))
|
||
|
return PTR_ERR(rst->regs_ctl);
|
||
|
|
||
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||
|
rst->regs_rst = devm_ioremap_resource(&pdev->dev, mem);
|
||
|
if (IS_ERR(rst->regs_rst))
|
||
|
return PTR_ERR(rst->regs_rst);
|
||
|
|
||
|
spin_lock_init(&rst->lock);
|
||
|
|
||
|
rst->rcdev.owner = THIS_MODULE;
|
||
|
rst->rcdev.ops = &hsdkv1_reset_ops;
|
||
|
rst->rcdev.of_node = pdev->dev.of_node;
|
||
|
rst->rcdev.nr_resets = HSDK_MAX_RESETS;
|
||
|
rst->rcdev.of_reset_n_cells = 1;
|
||
|
|
||
|
return reset_controller_register(&rst->rcdev);
|
||
|
}
|
||
|
|
||
|
static const struct of_device_id hsdkv1_reset_dt_match[] = {
|
||
|
{ .compatible = "snps,hsdk-v1.0-reset" },
|
||
|
{ },
|
||
|
};
|
||
|
|
||
|
static struct platform_driver hsdkv1_reset_driver = {
|
||
|
.probe = hsdkv1_reset_probe,
|
||
|
.driver = {
|
||
|
.name = "hsdk-v1.0-reset",
|
||
|
.of_match_table = hsdkv1_reset_dt_match,
|
||
|
},
|
||
|
};
|
||
|
builtin_platform_driver(hsdkv1_reset_driver);
|
||
|
|
||
|
MODULE_AUTHOR("Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>");
|
||
|
MODULE_DESCRIPTION("Synopsys HSDKv1 SDP reset driver");
|
||
|
MODULE_LICENSE("GPL v2");
|