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 <asm/io.h> | ||||
| #include <asm/arch/reset.h> | ||||
| #include <clk-uclass.h> | ||||
| #include <clk.h> | ||||
| #include <div64.h> | ||||
| #include <dm.h> | ||||
| #include <errno.h> | ||||
| #include <reset-uclass.h> | ||||
| #include <dm/device.h> | ||||
| #include <dm/uclass.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/err.h> | ||||
| 
 | ||||
|  | @ -132,6 +136,7 @@ | |||
| 
 | ||||
| /* DEVICESRESETREG */ | ||||
| #define PRCI_DEVICESRESETREG_OFFSET	0x28 | ||||
| #define PRCI_DEVICERESETCNT 5 | ||||
| 
 | ||||
| #define PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_MASK \ | ||||
| 			(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, | ||||
| }; | ||||
| 
 | ||||
| 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 | ||||
|  * @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) | ||||
| { | ||||
| 	u32 v; | ||||
| 
 | ||||
| 	v = __prci_readl(pd, PRCI_DEVICESRESETREG_OFFSET); | ||||
| 	v |= PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_MASK; | ||||
| 	__prci_writel(v, PRCI_DEVICESRESETREG_OFFSET, pd); | ||||
| 	/* Release DDR ctrl reset */ | ||||
| 	__prci_consumer_reset("ddr_ctrl", true); | ||||
| 
 | ||||
| 	/* HACK to get the '1 full controller clock cycle'. */ | ||||
| 	asm volatile ("fence"); | ||||
| 	v = __prci_readl(pd, PRCI_DEVICESRESETREG_OFFSET); | ||||
| 	v |= (PRCI_DEVICESRESETREG_DDR_AXI_RST_N_MASK | | ||||
| 			PRCI_DEVICESRESETREG_DDR_AHB_RST_N_MASK | | ||||
| 			PRCI_DEVICESRESETREG_DDR_PHY_RST_N_MASK); | ||||
| 	__prci_writel(v, PRCI_DEVICESRESETREG_OFFSET, pd); | ||||
| 
 | ||||
| 	/* Release DDR AXI reset */ | ||||
| 	__prci_consumer_reset("ddr_axi", true); | ||||
| 
 | ||||
| 	/* 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'. */ | ||||
| 	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) | ||||
| { | ||||
| 	u32 v; | ||||
| 
 | ||||
| 	/* Release GEMGXL reset */ | ||||
| 	v = __prci_readl(pd, PRCI_DEVICESRESETREG_OFFSET); | ||||
| 	v |= PRCI_DEVICESRESETREG_GEMGXL_RST_N_MASK; | ||||
| 	__prci_writel(v, PRCI_DEVICESRESETREG_OFFSET, pd); | ||||
| 	__prci_consumer_reset("gemgxl_reset", true); | ||||
| 
 | ||||
| 	/* Procmon => core clock */ | ||||
| 	__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, | ||||
| }; | ||||
| 
 | ||||
| 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[] = { | ||||
| 	{ .compatible = "sifive,fu540-c000-prci" }, | ||||
| 	{ } | ||||
|  | @ -766,4 +808,5 @@ U_BOOT_DRIVER(sifive_fu540_prci) = { | |||
| 	.probe = sifive_fu540_prci_probe, | ||||
| 	.ops = &sifive_fu540_prci_ops, | ||||
| 	.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