diff options
Diffstat (limited to 'arch/arm/mach-zynq/slcr.c')
| -rw-r--r-- | arch/arm/mach-zynq/slcr.c | 123 | 
1 files changed, 108 insertions, 15 deletions
diff --git a/arch/arm/mach-zynq/slcr.c b/arch/arm/mach-zynq/slcr.c index 1836d5a3460..c43a2d16e22 100644 --- a/arch/arm/mach-zynq/slcr.c +++ b/arch/arm/mach-zynq/slcr.c @@ -15,7 +15,9 @@   */  #include <linux/io.h> +#include <linux/mfd/syscon.h>  #include <linux/of_address.h> +#include <linux/regmap.h>  #include <linux/clk/zynq.h>  #include "common.h" @@ -24,12 +26,80 @@  #define SLCR_PS_RST_CTRL_OFFSET		0x200 /* PS Software Reset Control */  #define SLCR_A9_CPU_RST_CTRL_OFFSET	0x244 /* CPU Software Reset Control */  #define SLCR_REBOOT_STATUS_OFFSET	0x258 /* PS Reboot Status */ +#define SLCR_PSS_IDCODE			0x530 /* PS IDCODE */  #define SLCR_UNLOCK_MAGIC		0xDF0D  #define SLCR_A9_CPU_CLKSTOP		0x10  #define SLCR_A9_CPU_RST			0x1 +#define SLCR_PSS_IDCODE_DEVICE_SHIFT	12 +#define SLCR_PSS_IDCODE_DEVICE_MASK	0x1F -void __iomem *zynq_slcr_base; +static void __iomem *zynq_slcr_base; +static struct regmap *zynq_slcr_regmap; + +/** + * zynq_slcr_write - Write to a register in SLCR block + * + * @val:	Value to write to the register + * @offset:	Register offset in SLCR block + * + * Return:	a negative value on error, 0 on success + */ +static int zynq_slcr_write(u32 val, u32 offset) +{ +	if (!zynq_slcr_regmap) { +		writel(val, zynq_slcr_base + offset); +		return 0; +	} + +	return regmap_write(zynq_slcr_regmap, offset, val); +} + +/** + * zynq_slcr_read - Read a register in SLCR block + * + * @val:	Pointer to value to be read from SLCR + * @offset:	Register offset in SLCR block + * + * Return:	a negative value on error, 0 on success + */ +static int zynq_slcr_read(u32 *val, u32 offset) +{ +	if (zynq_slcr_regmap) +		return regmap_read(zynq_slcr_regmap, offset, val); + +	*val = readl(zynq_slcr_base + offset); + +	return 0; +} + +/** + * zynq_slcr_unlock - Unlock SLCR registers + * + * Return:	a negative value on error, 0 on success + */ +static inline int zynq_slcr_unlock(void) +{ +	zynq_slcr_write(SLCR_UNLOCK_MAGIC, SLCR_UNLOCK_OFFSET); + +	return 0; +} + +/** + * zynq_slcr_get_device_id - Read device code id + * + * Return:	Device code id + */ +u32 zynq_slcr_get_device_id(void) +{ +	u32 val; + +	zynq_slcr_read(&val, SLCR_PSS_IDCODE); +	val >>= SLCR_PSS_IDCODE_DEVICE_SHIFT; +	val &= SLCR_PSS_IDCODE_DEVICE_MASK; + +	return val; +}  /**   * zynq_slcr_system_reset - Reset the entire system. @@ -43,16 +113,16 @@ void zynq_slcr_system_reset(void)  	 * Note that this seems to require raw i/o  	 * functions or there's a lockup?  	 */ -	writel(SLCR_UNLOCK_MAGIC, zynq_slcr_base + SLCR_UNLOCK_OFFSET); +	zynq_slcr_unlock();  	/*  	 * Clear 0x0F000000 bits of reboot status register to workaround  	 * the FSBL not loading the bitstream after soft-reboot  	 * This is a temporary solution until we know more.  	 */ -	reboot = readl(zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET); -	writel(reboot & 0xF0FFFFFF, zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET); -	writel(1, zynq_slcr_base + SLCR_PS_RST_CTRL_OFFSET); +	zynq_slcr_read(&reboot, SLCR_REBOOT_STATUS_OFFSET); +	zynq_slcr_write(reboot & 0xF0FFFFFF, SLCR_REBOOT_STATUS_OFFSET); +	zynq_slcr_write(1, SLCR_PS_RST_CTRL_OFFSET);  }  /** @@ -61,11 +131,13 @@ void zynq_slcr_system_reset(void)   */  void zynq_slcr_cpu_start(int cpu)  { -	u32 reg = readl(zynq_slcr_base + SLCR_A9_CPU_RST_CTRL_OFFSET); +	u32 reg; + +	zynq_slcr_read(®, SLCR_A9_CPU_RST_CTRL_OFFSET);  	reg &= ~(SLCR_A9_CPU_RST << cpu); -	writel(reg, zynq_slcr_base + SLCR_A9_CPU_RST_CTRL_OFFSET); +	zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET);  	reg &= ~(SLCR_A9_CPU_CLKSTOP << cpu); -	writel(reg, zynq_slcr_base + SLCR_A9_CPU_RST_CTRL_OFFSET); +	zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET);  }  /** @@ -74,19 +146,40 @@ void zynq_slcr_cpu_start(int cpu)   */  void zynq_slcr_cpu_stop(int cpu)  { -	u32 reg = readl(zynq_slcr_base + SLCR_A9_CPU_RST_CTRL_OFFSET); +	u32 reg; + +	zynq_slcr_read(®, SLCR_A9_CPU_RST_CTRL_OFFSET);  	reg |= (SLCR_A9_CPU_CLKSTOP | SLCR_A9_CPU_RST) << cpu; -	writel(reg, zynq_slcr_base + SLCR_A9_CPU_RST_CTRL_OFFSET); +	zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET);  }  /** - * zynq_slcr_init - * Returns 0 on success, negative errno otherwise. + * zynq_slcr_init - Regular slcr driver init + * + * Return:	0 on success, negative errno otherwise.   *   * Called early during boot from platform code to remap SLCR area.   */  int __init zynq_slcr_init(void)  { +	zynq_slcr_regmap = syscon_regmap_lookup_by_compatible("xlnx,zynq-slcr"); +	if (IS_ERR(zynq_slcr_regmap)) { +		pr_err("%s: failed to find zynq-slcr\n", __func__); +		return -ENODEV; +	} + +	return 0; +} + +/** + * zynq_early_slcr_init - Early slcr init function + * + * Return:	0 on success, negative errno otherwise. + * + * Called very early during boot from platform code to unlock SLCR. + */ +int __init zynq_early_slcr_init(void) +{  	struct device_node *np;  	np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-slcr"); @@ -101,13 +194,13 @@ int __init zynq_slcr_init(void)  		BUG();  	} +	np->data = (__force void *)zynq_slcr_base; +  	/* unlock the SLCR so that registers can be changed */ -	writel(SLCR_UNLOCK_MAGIC, zynq_slcr_base + SLCR_UNLOCK_OFFSET); +	zynq_slcr_unlock();  	pr_info("%s mapped to %p\n", np->name, zynq_slcr_base); -	zynq_clock_init(zynq_slcr_base); -  	of_node_put(np);  	return 0;  | 
