sifive: reset: add DM based reset driver for SiFive SoC's
PRCI module within SiFive SoC's has register with which we can reset the sub-systems within the SoC. The resets to DDR and ethernet sub systems within FU540-C000 SoC are active low, and are hold low by default on power-up. Currently these are directly asserted within prci driver via register read/write. With the DM based reset driver support here, we bind the reset driver with clock (prci) driver and assert the reset signals of both sub-system's appropriately. Signed-off-by: Sagar Shrikant Kadam <sagar.kadam@sifive.com> Reviewed-by: Pragnesh Patel <Pragnesh.patel@sifive.com> Reviewed-by: Bin Meng <bin.meng@windriver.com> Tested-by: Bin Meng <bin.meng@windriver.com>
This commit is contained in:
		
							parent
							
								
									ea4e9570eb
								
							
						
					
					
						commit
						d04a46426b
					
				|  | @ -0,0 +1,13 @@ | ||||||
|  | /* SPDX-License-Identifier: GPL-2.0+ */ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2020 SiFive, Inc. | ||||||
|  |  * | ||||||
|  |  * Author: Sagar Kadam <sagar.kadam@sifive.com> | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef __RESET_SIFIVE_H | ||||||
|  | #define __RESET_SIFIVE_H | ||||||
|  | 
 | ||||||
|  | int sifive_reset_bind(struct udevice *dev, ulong count); | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | @ -30,11 +30,15 @@ | ||||||
| 
 | 
 | ||||||
| #include <common.h> | #include <common.h> | ||||||
| #include <asm/io.h> | #include <asm/io.h> | ||||||
|  | #include <asm/arch/reset.h> | ||||||
| #include <clk-uclass.h> | #include <clk-uclass.h> | ||||||
| #include <clk.h> | #include <clk.h> | ||||||
| #include <div64.h> | #include <div64.h> | ||||||
| #include <dm.h> | #include <dm.h> | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
|  | #include <reset-uclass.h> | ||||||
|  | #include <dm/device.h> | ||||||
|  | #include <dm/uclass.h> | ||||||
| #include <linux/delay.h> | #include <linux/delay.h> | ||||||
| #include <linux/err.h> | #include <linux/err.h> | ||||||
| 
 | 
 | ||||||
|  | @ -132,6 +136,7 @@ | ||||||
| 
 | 
 | ||||||
| /* DEVICESRESETREG */ | /* DEVICESRESETREG */ | ||||||
| #define PRCI_DEVICESRESETREG_OFFSET	0x28 | #define PRCI_DEVICESRESETREG_OFFSET	0x28 | ||||||
|  | #define PRCI_DEVICERESETCNT 5 | ||||||
| 
 | 
 | ||||||
| #define PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_MASK \ | #define PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_MASK \ | ||||||
| 			(0x1 << PRCI_RST_DDR_CTRL_N) | 			(0x1 << PRCI_RST_DDR_CTRL_N) | ||||||
|  | @ -525,6 +530,41 @@ static const struct __prci_clock_ops sifive_fu540_prci_tlclksel_clk_ops = { | ||||||
| 	.recalc_rate = sifive_fu540_prci_tlclksel_recalc_rate, | 	.recalc_rate = sifive_fu540_prci_tlclksel_recalc_rate, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | static int __prci_consumer_reset(const char *rst_name, bool trigger) | ||||||
|  | { | ||||||
|  | 	struct udevice *dev; | ||||||
|  | 	struct reset_ctl rst_sig; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	ret = uclass_get_device_by_driver(UCLASS_RESET, | ||||||
|  | 					  DM_GET_DRIVER(sifive_reset), | ||||||
|  | 					  &dev); | ||||||
|  | 	if (ret) { | ||||||
|  | 		dev_err(dev, "Reset driver not found: %d\n", ret); | ||||||
|  | 		return ret; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ret = reset_get_by_name(dev, rst_name, &rst_sig); | ||||||
|  | 	if (ret) { | ||||||
|  | 		dev_err(dev, "failed to get %s reset\n", rst_name); | ||||||
|  | 		return ret; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (reset_valid(&rst_sig)) { | ||||||
|  | 		if (trigger) | ||||||
|  | 			ret = reset_deassert(&rst_sig); | ||||||
|  | 		else | ||||||
|  | 			ret = reset_assert(&rst_sig); | ||||||
|  | 		if (ret) { | ||||||
|  | 			dev_err(dev, "failed to trigger reset id = %ld\n", | ||||||
|  | 				rst_sig.id); | ||||||
|  | 			return ret; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * __prci_ddr_release_reset() - Release DDR reset |  * __prci_ddr_release_reset() - Release DDR reset | ||||||
|  * @pd: struct __prci_data * for the PRCI containing the DDRCLK mux reg |  * @pd: struct __prci_data * for the PRCI containing the DDRCLK mux reg | ||||||
|  | @ -532,19 +572,20 @@ static const struct __prci_clock_ops sifive_fu540_prci_tlclksel_clk_ops = { | ||||||
|  */ |  */ | ||||||
| static void __prci_ddr_release_reset(struct __prci_data *pd) | static void __prci_ddr_release_reset(struct __prci_data *pd) | ||||||
| { | { | ||||||
| 	u32 v; | 	/* Release DDR ctrl reset */ | ||||||
| 
 | 	__prci_consumer_reset("ddr_ctrl", true); | ||||||
| 	v = __prci_readl(pd, PRCI_DEVICESRESETREG_OFFSET); |  | ||||||
| 	v |= PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_MASK; |  | ||||||
| 	__prci_writel(v, PRCI_DEVICESRESETREG_OFFSET, pd); |  | ||||||
| 
 | 
 | ||||||
| 	/* HACK to get the '1 full controller clock cycle'. */ | 	/* HACK to get the '1 full controller clock cycle'. */ | ||||||
| 	asm volatile ("fence"); | 	asm volatile ("fence"); | ||||||
| 	v = __prci_readl(pd, PRCI_DEVICESRESETREG_OFFSET); | 
 | ||||||
| 	v |= (PRCI_DEVICESRESETREG_DDR_AXI_RST_N_MASK | | 	/* Release DDR AXI reset */ | ||||||
| 			PRCI_DEVICESRESETREG_DDR_AHB_RST_N_MASK | | 	__prci_consumer_reset("ddr_axi", true); | ||||||
| 			PRCI_DEVICESRESETREG_DDR_PHY_RST_N_MASK); | 
 | ||||||
| 	__prci_writel(v, PRCI_DEVICESRESETREG_OFFSET, pd); | 	/* Release DDR AHB reset */ | ||||||
|  | 	__prci_consumer_reset("ddr_ahb", true); | ||||||
|  | 
 | ||||||
|  | 	/* Release DDR PHY reset */ | ||||||
|  | 	__prci_consumer_reset("ddr_phy", true); | ||||||
| 
 | 
 | ||||||
| 	/* HACK to get the '1 full controller clock cycle'. */ | 	/* HACK to get the '1 full controller clock cycle'. */ | ||||||
| 	asm volatile ("fence"); | 	asm volatile ("fence"); | ||||||
|  | @ -564,12 +605,8 @@ static void __prci_ddr_release_reset(struct __prci_data *pd) | ||||||
|  */ |  */ | ||||||
| static void __prci_ethernet_release_reset(struct __prci_data *pd) | static void __prci_ethernet_release_reset(struct __prci_data *pd) | ||||||
| { | { | ||||||
| 	u32 v; |  | ||||||
| 
 |  | ||||||
| 	/* Release GEMGXL reset */ | 	/* Release GEMGXL reset */ | ||||||
| 	v = __prci_readl(pd, PRCI_DEVICESRESETREG_OFFSET); | 	__prci_consumer_reset("gemgxl_reset", true); | ||||||
| 	v |= PRCI_DEVICESRESETREG_GEMGXL_RST_N_MASK; |  | ||||||
| 	__prci_writel(v, PRCI_DEVICESRESETREG_OFFSET, pd); |  | ||||||
| 
 | 
 | ||||||
| 	/* Procmon => core clock */ | 	/* Procmon => core clock */ | ||||||
| 	__prci_writel(PRCI_PROCMONCFG_CORE_CLOCK_MASK, PRCI_PROCMONCFG_OFFSET, | 	__prci_writel(PRCI_PROCMONCFG_CORE_CLOCK_MASK, PRCI_PROCMONCFG_OFFSET, | ||||||
|  | @ -754,6 +791,11 @@ static struct clk_ops sifive_fu540_prci_ops = { | ||||||
| 	.disable = sifive_fu540_prci_disable, | 	.disable = sifive_fu540_prci_disable, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | static int sifive_fu540_clk_bind(struct udevice *dev) | ||||||
|  | { | ||||||
|  | 	return sifive_reset_bind(dev, PRCI_DEVICERESETCNT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static const struct udevice_id sifive_fu540_prci_ids[] = { | static const struct udevice_id sifive_fu540_prci_ids[] = { | ||||||
| 	{ .compatible = "sifive,fu540-c000-prci" }, | 	{ .compatible = "sifive,fu540-c000-prci" }, | ||||||
| 	{ } | 	{ } | ||||||
|  | @ -766,4 +808,5 @@ U_BOOT_DRIVER(sifive_fu540_prci) = { | ||||||
| 	.probe = sifive_fu540_prci_probe, | 	.probe = sifive_fu540_prci_probe, | ||||||
| 	.ops = &sifive_fu540_prci_ops, | 	.ops = &sifive_fu540_prci_ops, | ||||||
| 	.priv_auto_alloc_size = sizeof(struct __prci_data), | 	.priv_auto_alloc_size = sizeof(struct __prci_data), | ||||||
|  | 	.bind = sifive_fu540_clk_bind, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -0,0 +1,118 @@ | ||||||
|  | // SPDX-License-Identifier: GPL-2.0+
 | ||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2020 Sifive, Inc. | ||||||
|  |  * Author: Sagar Kadam <sagar.kadam@sifive.com> | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <common.h> | ||||||
|  | #include <dm.h> | ||||||
|  | #include <reset-uclass.h> | ||||||
|  | #include <asm/io.h> | ||||||
|  | #include <dm/device_compat.h> | ||||||
|  | #include <dm/lists.h> | ||||||
|  | #include <linux/bitops.h> | ||||||
|  | 
 | ||||||
|  | #define PRCI_RESETREG_OFFSET 0x28 | ||||||
|  | 
 | ||||||
|  | struct sifive_reset_priv { | ||||||
|  | 	void *base; | ||||||
|  | 	/* number of reset signals */ | ||||||
|  | 	int nr_reset; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int sifive_rst_trigger(struct reset_ctl *rst, bool level) | ||||||
|  | { | ||||||
|  | 	struct sifive_reset_priv *priv = dev_get_priv(rst->dev); | ||||||
|  | 	int id = rst->id; | ||||||
|  | 	int regval = readl(priv->base + PRCI_RESETREG_OFFSET); | ||||||
|  | 
 | ||||||
|  | 	/* Derive bitposition from rst id */ | ||||||
|  | 	if (level) | ||||||
|  | 		/* Reset deassert */ | ||||||
|  | 		regval |= BIT(id); | ||||||
|  | 	else | ||||||
|  | 		/* Reset assert */ | ||||||
|  | 		regval &= ~BIT(id); | ||||||
|  | 
 | ||||||
|  | 	writel(regval, priv->base + PRCI_RESETREG_OFFSET); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int sifive_reset_assert(struct reset_ctl *rst) | ||||||
|  | { | ||||||
|  | 	return sifive_rst_trigger(rst, false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int sifive_reset_deassert(struct reset_ctl *rst) | ||||||
|  | { | ||||||
|  | 	return sifive_rst_trigger(rst, true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int sifive_reset_request(struct reset_ctl *rst) | ||||||
|  | { | ||||||
|  | 	struct sifive_reset_priv *priv = dev_get_priv(rst->dev); | ||||||
|  | 
 | ||||||
|  | 	debug("%s(rst=%p) (dev=%p, id=%lu) (nr_reset=%d)\n", __func__, | ||||||
|  | 	      rst, rst->dev, rst->id, priv->nr_reset); | ||||||
|  | 
 | ||||||
|  | 	if (rst->id > priv->nr_reset) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int sifive_reset_free(struct reset_ctl *rst) | ||||||
|  | { | ||||||
|  | 	struct sifive_reset_priv *priv = dev_get_priv(rst->dev); | ||||||
|  | 
 | ||||||
|  | 	debug("%s(rst=%p) (dev=%p, id=%lu) (nr_reset=%d)\n", __func__, | ||||||
|  | 	      rst, rst->dev, rst->id, priv->nr_reset); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int sifive_reset_probe(struct udevice *dev) | ||||||
|  | { | ||||||
|  | 	struct sifive_reset_priv *priv = dev_get_priv(dev); | ||||||
|  | 
 | ||||||
|  | 	priv->base = dev_remap_addr(dev); | ||||||
|  | 	if (!priv->base) | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int sifive_reset_bind(struct udevice *dev, ulong count) | ||||||
|  | { | ||||||
|  | 	struct udevice *rst_dev; | ||||||
|  | 	struct sifive_reset_priv *priv; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	ret = device_bind_driver_to_node(dev, "sifive-reset", "reset", | ||||||
|  | 					 dev_ofnode(dev), &rst_dev); | ||||||
|  | 	if (ret) { | ||||||
|  | 		dev_err(dev, "failed to bind sifive_reset driver (ret=%d)\n", ret); | ||||||
|  | 		return ret; | ||||||
|  | 	} | ||||||
|  | 	priv = malloc(sizeof(struct sifive_reset_priv)); | ||||||
|  | 	priv->nr_reset = count; | ||||||
|  | 	rst_dev->priv = priv; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const struct reset_ops sifive_reset_ops = { | ||||||
|  | 	.request = sifive_reset_request, | ||||||
|  | 	.rfree = sifive_reset_free, | ||||||
|  | 	.rst_assert = sifive_reset_assert, | ||||||
|  | 	.rst_deassert = sifive_reset_deassert, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | U_BOOT_DRIVER(sifive_reset) = { | ||||||
|  | 	.name		= "sifive-reset", | ||||||
|  | 	.id		= UCLASS_RESET, | ||||||
|  | 	.ops		= &sifive_reset_ops, | ||||||
|  | 	.probe		= sifive_reset_probe, | ||||||
|  | 	.priv_auto_alloc_size = sizeof(struct sifive_reset_priv), | ||||||
|  | }; | ||||||
		Loading…
	
		Reference in New Issue