18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Device State Control Registers driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2011 Texas Instruments Incorporated
68c2ecf20Sopenharmony_ci *  Author: Mark Salter <msalter@redhat.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/*
108c2ecf20Sopenharmony_ci * The Device State Control Registers (DSCR) provide SoC level control over
118c2ecf20Sopenharmony_ci * a number of peripherals. Details vary considerably among the various SoC
128c2ecf20Sopenharmony_ci * parts. In general, the DSCR block will provide one or more configuration
138c2ecf20Sopenharmony_ci * registers often protected by a lock register. One or more key values must
148c2ecf20Sopenharmony_ci * be written to a lock register in order to unlock the configuration register.
158c2ecf20Sopenharmony_ci * The configuration register may be used to enable (and disable in some
168c2ecf20Sopenharmony_ci * cases) SoC pin drivers, peripheral clock sources (internal or pin), etc.
178c2ecf20Sopenharmony_ci * In some cases, a configuration register is write once or the individual
188c2ecf20Sopenharmony_ci * bits are write once. That is, you may be able to enable a device, but
198c2ecf20Sopenharmony_ci * will not be able to disable it.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * In addition to device configuration, the DSCR block may provide registers
228c2ecf20Sopenharmony_ci * which are used to reset SoC peripherals, provide device ID information,
238c2ecf20Sopenharmony_ci * provide MAC addresses, and other miscellaneous functions.
248c2ecf20Sopenharmony_ci */
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <linux/of.h>
278c2ecf20Sopenharmony_ci#include <linux/of_address.h>
288c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
298c2ecf20Sopenharmony_ci#include <linux/module.h>
308c2ecf20Sopenharmony_ci#include <linux/io.h>
318c2ecf20Sopenharmony_ci#include <linux/delay.h>
328c2ecf20Sopenharmony_ci#include <asm/soc.h>
338c2ecf20Sopenharmony_ci#include <asm/dscr.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define MAX_DEVSTATE_IDS   32
368c2ecf20Sopenharmony_ci#define MAX_DEVCTL_REGS     8
378c2ecf20Sopenharmony_ci#define MAX_DEVSTAT_REGS    8
388c2ecf20Sopenharmony_ci#define MAX_LOCKED_REGS     4
398c2ecf20Sopenharmony_ci#define MAX_SOC_EMACS       2
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistruct rmii_reset_reg {
428c2ecf20Sopenharmony_ci	u32 reg;
438c2ecf20Sopenharmony_ci	u32 mask;
448c2ecf20Sopenharmony_ci};
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/*
478c2ecf20Sopenharmony_ci * Some registerd may be locked. In order to write to these
488c2ecf20Sopenharmony_ci * registers, the key value must first be written to the lockreg.
498c2ecf20Sopenharmony_ci */
508c2ecf20Sopenharmony_cistruct locked_reg {
518c2ecf20Sopenharmony_ci	u32 reg;	/* offset from base */
528c2ecf20Sopenharmony_ci	u32 lockreg;	/* offset from base */
538c2ecf20Sopenharmony_ci	u32 key;	/* unlock key */
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/*
578c2ecf20Sopenharmony_ci * This describes a contiguous area of like control bits used to enable/disable
588c2ecf20Sopenharmony_ci * SoC devices. Each controllable device is given an ID which is used by the
598c2ecf20Sopenharmony_ci * individual device drivers to control the device state. These IDs start at
608c2ecf20Sopenharmony_ci * zero and are assigned sequentially to the control bitfield ranges described
618c2ecf20Sopenharmony_ci * by this structure.
628c2ecf20Sopenharmony_ci */
638c2ecf20Sopenharmony_cistruct devstate_ctl_reg {
648c2ecf20Sopenharmony_ci	u32 reg;		/* register holding the control bits */
658c2ecf20Sopenharmony_ci	u8  start_id;		/* start id of this range */
668c2ecf20Sopenharmony_ci	u8  num_ids;		/* number of devices in this range */
678c2ecf20Sopenharmony_ci	u8  enable_only;	/* bits are write-once to enable only */
688c2ecf20Sopenharmony_ci	u8  enable;		/* value used to enable device */
698c2ecf20Sopenharmony_ci	u8  disable;		/* value used to disable device */
708c2ecf20Sopenharmony_ci	u8  shift;		/* starting (rightmost) bit in range */
718c2ecf20Sopenharmony_ci	u8  nbits;		/* number of bits per device */
728c2ecf20Sopenharmony_ci};
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/*
768c2ecf20Sopenharmony_ci * This describes a region of status bits indicating the state of
778c2ecf20Sopenharmony_ci * various devices. This is used internally to wait for status
788c2ecf20Sopenharmony_ci * change completion when enabling/disabling a device. Status is
798c2ecf20Sopenharmony_ci * optional and not all device controls will have a corresponding
808c2ecf20Sopenharmony_ci * status.
818c2ecf20Sopenharmony_ci */
828c2ecf20Sopenharmony_cistruct devstate_stat_reg {
838c2ecf20Sopenharmony_ci	u32 reg;		/* register holding the status bits */
848c2ecf20Sopenharmony_ci	u8  start_id;		/* start id of this range */
858c2ecf20Sopenharmony_ci	u8  num_ids;		/* number of devices in this range */
868c2ecf20Sopenharmony_ci	u8  enable;		/* value indicating enabled state */
878c2ecf20Sopenharmony_ci	u8  disable;		/* value indicating disabled state */
888c2ecf20Sopenharmony_ci	u8  shift;		/* starting (rightmost) bit in range */
898c2ecf20Sopenharmony_ci	u8  nbits;		/* number of bits per device */
908c2ecf20Sopenharmony_ci};
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistruct devstate_info {
938c2ecf20Sopenharmony_ci	struct devstate_ctl_reg *ctl;
948c2ecf20Sopenharmony_ci	struct devstate_stat_reg *stat;
958c2ecf20Sopenharmony_ci};
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/* These are callbacks to SOC-specific code. */
988c2ecf20Sopenharmony_cistruct dscr_ops {
998c2ecf20Sopenharmony_ci	void (*init)(struct device_node *node);
1008c2ecf20Sopenharmony_ci};
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistruct dscr_regs {
1038c2ecf20Sopenharmony_ci	spinlock_t		lock;
1048c2ecf20Sopenharmony_ci	void __iomem		*base;
1058c2ecf20Sopenharmony_ci	u32			kick_reg[2];
1068c2ecf20Sopenharmony_ci	u32			kick_key[2];
1078c2ecf20Sopenharmony_ci	struct locked_reg	locked[MAX_LOCKED_REGS];
1088c2ecf20Sopenharmony_ci	struct devstate_info	devstate_info[MAX_DEVSTATE_IDS];
1098c2ecf20Sopenharmony_ci	struct rmii_reset_reg   rmii_resets[MAX_SOC_EMACS];
1108c2ecf20Sopenharmony_ci	struct devstate_ctl_reg devctl[MAX_DEVCTL_REGS];
1118c2ecf20Sopenharmony_ci	struct devstate_stat_reg devstat[MAX_DEVSTAT_REGS];
1128c2ecf20Sopenharmony_ci};
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic struct dscr_regs	dscr;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic struct locked_reg *find_locked_reg(u32 reg)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	int i;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_LOCKED_REGS; i++)
1218c2ecf20Sopenharmony_ci		if (dscr.locked[i].key && reg == dscr.locked[i].reg)
1228c2ecf20Sopenharmony_ci			return &dscr.locked[i];
1238c2ecf20Sopenharmony_ci	return NULL;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci/*
1278c2ecf20Sopenharmony_ci * Write to a register with one lock
1288c2ecf20Sopenharmony_ci */
1298c2ecf20Sopenharmony_cistatic void dscr_write_locked1(u32 reg, u32 val,
1308c2ecf20Sopenharmony_ci			       u32 lock, u32 key)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	void __iomem *reg_addr = dscr.base + reg;
1338c2ecf20Sopenharmony_ci	void __iomem *lock_addr = dscr.base + lock;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	/*
1368c2ecf20Sopenharmony_ci	 * For some registers, the lock is relocked after a short number
1378c2ecf20Sopenharmony_ci	 * of cycles. We have to put the lock write and register write in
1388c2ecf20Sopenharmony_ci	 * the same fetch packet to meet this timing. The .align ensures
1398c2ecf20Sopenharmony_ci	 * the two stw instructions are in the same fetch packet.
1408c2ecf20Sopenharmony_ci	 */
1418c2ecf20Sopenharmony_ci	asm volatile ("b	.s2	0f\n"
1428c2ecf20Sopenharmony_ci		      "nop	5\n"
1438c2ecf20Sopenharmony_ci		      "    .align 5\n"
1448c2ecf20Sopenharmony_ci		      "0:\n"
1458c2ecf20Sopenharmony_ci		      "stw	.D1T2	%3,*%2\n"
1468c2ecf20Sopenharmony_ci		      "stw	.D1T2	%1,*%0\n"
1478c2ecf20Sopenharmony_ci		      :
1488c2ecf20Sopenharmony_ci		      : "a"(reg_addr), "b"(val), "a"(lock_addr), "b"(key)
1498c2ecf20Sopenharmony_ci		);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	/* in case the hw doesn't reset the lock */
1528c2ecf20Sopenharmony_ci	soc_writel(0, lock_addr);
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci/*
1568c2ecf20Sopenharmony_ci * Write to a register protected by two lock registers
1578c2ecf20Sopenharmony_ci */
1588c2ecf20Sopenharmony_cistatic void dscr_write_locked2(u32 reg, u32 val,
1598c2ecf20Sopenharmony_ci			       u32 lock0, u32 key0,
1608c2ecf20Sopenharmony_ci			       u32 lock1, u32 key1)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	soc_writel(key0, dscr.base + lock0);
1638c2ecf20Sopenharmony_ci	soc_writel(key1, dscr.base + lock1);
1648c2ecf20Sopenharmony_ci	soc_writel(val, dscr.base + reg);
1658c2ecf20Sopenharmony_ci	soc_writel(0, dscr.base + lock0);
1668c2ecf20Sopenharmony_ci	soc_writel(0, dscr.base + lock1);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic void dscr_write(u32 reg, u32 val)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	struct locked_reg *lock;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	lock = find_locked_reg(reg);
1748c2ecf20Sopenharmony_ci	if (lock)
1758c2ecf20Sopenharmony_ci		dscr_write_locked1(reg, val, lock->lockreg, lock->key);
1768c2ecf20Sopenharmony_ci	else if (dscr.kick_key[0])
1778c2ecf20Sopenharmony_ci		dscr_write_locked2(reg, val, dscr.kick_reg[0], dscr.kick_key[0],
1788c2ecf20Sopenharmony_ci				   dscr.kick_reg[1], dscr.kick_key[1]);
1798c2ecf20Sopenharmony_ci	else
1808c2ecf20Sopenharmony_ci		soc_writel(val, dscr.base + reg);
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci/*
1858c2ecf20Sopenharmony_ci * Drivers can use this interface to enable/disable SoC IP blocks.
1868c2ecf20Sopenharmony_ci */
1878c2ecf20Sopenharmony_civoid dscr_set_devstate(int id, enum dscr_devstate_t state)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	struct devstate_ctl_reg *ctl;
1908c2ecf20Sopenharmony_ci	struct devstate_stat_reg *stat;
1918c2ecf20Sopenharmony_ci	struct devstate_info *info;
1928c2ecf20Sopenharmony_ci	u32 ctl_val, val;
1938c2ecf20Sopenharmony_ci	int ctl_shift, ctl_mask;
1948c2ecf20Sopenharmony_ci	unsigned long flags;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (!dscr.base)
1978c2ecf20Sopenharmony_ci		return;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	if (id < 0 || id >= MAX_DEVSTATE_IDS)
2008c2ecf20Sopenharmony_ci		return;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	info = &dscr.devstate_info[id];
2038c2ecf20Sopenharmony_ci	ctl = info->ctl;
2048c2ecf20Sopenharmony_ci	stat = info->stat;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	if (ctl == NULL)
2078c2ecf20Sopenharmony_ci		return;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	ctl_shift = ctl->shift + ctl->nbits * (id - ctl->start_id);
2108c2ecf20Sopenharmony_ci	ctl_mask = ((1 << ctl->nbits) - 1) << ctl_shift;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	switch (state) {
2138c2ecf20Sopenharmony_ci	case DSCR_DEVSTATE_ENABLED:
2148c2ecf20Sopenharmony_ci		ctl_val = ctl->enable << ctl_shift;
2158c2ecf20Sopenharmony_ci		break;
2168c2ecf20Sopenharmony_ci	case DSCR_DEVSTATE_DISABLED:
2178c2ecf20Sopenharmony_ci		if (ctl->enable_only)
2188c2ecf20Sopenharmony_ci			return;
2198c2ecf20Sopenharmony_ci		ctl_val = ctl->disable << ctl_shift;
2208c2ecf20Sopenharmony_ci		break;
2218c2ecf20Sopenharmony_ci	default:
2228c2ecf20Sopenharmony_ci		return;
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dscr.lock, flags);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	val = soc_readl(dscr.base + ctl->reg);
2288c2ecf20Sopenharmony_ci	val &= ~ctl_mask;
2298c2ecf20Sopenharmony_ci	val |= ctl_val;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	dscr_write(ctl->reg, val);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dscr.lock, flags);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if (!stat)
2368c2ecf20Sopenharmony_ci		return;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	ctl_shift = stat->shift + stat->nbits * (id - stat->start_id);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (state == DSCR_DEVSTATE_ENABLED)
2418c2ecf20Sopenharmony_ci		ctl_val = stat->enable;
2428c2ecf20Sopenharmony_ci	else
2438c2ecf20Sopenharmony_ci		ctl_val = stat->disable;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	do {
2468c2ecf20Sopenharmony_ci		val = soc_readl(dscr.base + stat->reg);
2478c2ecf20Sopenharmony_ci		val >>= ctl_shift;
2488c2ecf20Sopenharmony_ci		val &= ((1 << stat->nbits) - 1);
2498c2ecf20Sopenharmony_ci	} while (val != ctl_val);
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dscr_set_devstate);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci/*
2548c2ecf20Sopenharmony_ci * Drivers can use this to reset RMII module.
2558c2ecf20Sopenharmony_ci */
2568c2ecf20Sopenharmony_civoid dscr_rmii_reset(int id, int assert)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	struct rmii_reset_reg *r;
2598c2ecf20Sopenharmony_ci	unsigned long flags;
2608c2ecf20Sopenharmony_ci	u32 val;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	if (id < 0 || id >= MAX_SOC_EMACS)
2638c2ecf20Sopenharmony_ci		return;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	r = &dscr.rmii_resets[id];
2668c2ecf20Sopenharmony_ci	if (r->mask == 0)
2678c2ecf20Sopenharmony_ci		return;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dscr.lock, flags);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	val = soc_readl(dscr.base + r->reg);
2728c2ecf20Sopenharmony_ci	if (assert)
2738c2ecf20Sopenharmony_ci		dscr_write(r->reg, val | r->mask);
2748c2ecf20Sopenharmony_ci	else
2758c2ecf20Sopenharmony_ci		dscr_write(r->reg, val & ~(r->mask));
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dscr.lock, flags);
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dscr_rmii_reset);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic void __init dscr_parse_devstat(struct device_node *node,
2828c2ecf20Sopenharmony_ci				      void __iomem *base)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	u32 val;
2858c2ecf20Sopenharmony_ci	int err;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	err = of_property_read_u32_array(node, "ti,dscr-devstat", &val, 1);
2888c2ecf20Sopenharmony_ci	if (!err)
2898c2ecf20Sopenharmony_ci		c6x_devstat = soc_readl(base + val);
2908c2ecf20Sopenharmony_ci	printk(KERN_INFO "DEVSTAT: %08x\n", c6x_devstat);
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic void __init dscr_parse_silicon_rev(struct device_node *node,
2948c2ecf20Sopenharmony_ci					 void __iomem *base)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	u32 vals[3];
2978c2ecf20Sopenharmony_ci	int err;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	err = of_property_read_u32_array(node, "ti,dscr-silicon-rev", vals, 3);
3008c2ecf20Sopenharmony_ci	if (!err) {
3018c2ecf20Sopenharmony_ci		c6x_silicon_rev = soc_readl(base + vals[0]);
3028c2ecf20Sopenharmony_ci		c6x_silicon_rev >>= vals[1];
3038c2ecf20Sopenharmony_ci		c6x_silicon_rev &= vals[2];
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci/*
3088c2ecf20Sopenharmony_ci * Some SoCs will have a pair of fuse registers which hold
3098c2ecf20Sopenharmony_ci * an ethernet MAC address. The "ti,dscr-mac-fuse-regs"
3108c2ecf20Sopenharmony_ci * property is a mapping from fuse register bytes to MAC
3118c2ecf20Sopenharmony_ci * address bytes. The expected format is:
3128c2ecf20Sopenharmony_ci *
3138c2ecf20Sopenharmony_ci *	ti,dscr-mac-fuse-regs = <reg0 b3 b2 b1 b0
3148c2ecf20Sopenharmony_ci *				 reg1 b3 b2 b1 b0>
3158c2ecf20Sopenharmony_ci *
3168c2ecf20Sopenharmony_ci * reg0 and reg1 are the offsets of the two fuse registers.
3178c2ecf20Sopenharmony_ci * b3-b0 positionally represent bytes within the fuse register.
3188c2ecf20Sopenharmony_ci * b3 is the most significant byte and b0 is the least.
3198c2ecf20Sopenharmony_ci * Allowable values for b3-b0 are:
3208c2ecf20Sopenharmony_ci *
3218c2ecf20Sopenharmony_ci *	  0 = fuse register byte not used in MAC address
3228c2ecf20Sopenharmony_ci *      1-6 = index+1 into c6x_fuse_mac[]
3238c2ecf20Sopenharmony_ci */
3248c2ecf20Sopenharmony_cistatic void __init dscr_parse_mac_fuse(struct device_node *node,
3258c2ecf20Sopenharmony_ci				       void __iomem *base)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	u32 vals[10], fuse;
3288c2ecf20Sopenharmony_ci	int f, i, j, err;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	err = of_property_read_u32_array(node, "ti,dscr-mac-fuse-regs",
3318c2ecf20Sopenharmony_ci					 vals, 10);
3328c2ecf20Sopenharmony_ci	if (err)
3338c2ecf20Sopenharmony_ci		return;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	for (f = 0; f < 2; f++) {
3368c2ecf20Sopenharmony_ci		fuse = soc_readl(base + vals[f * 5]);
3378c2ecf20Sopenharmony_ci		for (j = (f * 5) + 1, i = 24; i >= 0; i -= 8, j++)
3388c2ecf20Sopenharmony_ci			if (vals[j] && vals[j] <= 6)
3398c2ecf20Sopenharmony_ci				c6x_fuse_mac[vals[j] - 1] = fuse >> i;
3408c2ecf20Sopenharmony_ci	}
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic void __init dscr_parse_rmii_resets(struct device_node *node,
3448c2ecf20Sopenharmony_ci					  void __iomem *base)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	const __be32 *p;
3478c2ecf20Sopenharmony_ci	int i, size;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	/* look for RMII reset registers */
3508c2ecf20Sopenharmony_ci	p = of_get_property(node, "ti,dscr-rmii-resets", &size);
3518c2ecf20Sopenharmony_ci	if (p) {
3528c2ecf20Sopenharmony_ci		/* parse all the reg/mask pairs we can handle */
3538c2ecf20Sopenharmony_ci		size /= (sizeof(*p) * 2);
3548c2ecf20Sopenharmony_ci		if (size > MAX_SOC_EMACS)
3558c2ecf20Sopenharmony_ci			size = MAX_SOC_EMACS;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci		for (i = 0; i < size; i++) {
3588c2ecf20Sopenharmony_ci			dscr.rmii_resets[i].reg = be32_to_cpup(p++);
3598c2ecf20Sopenharmony_ci			dscr.rmii_resets[i].mask = be32_to_cpup(p++);
3608c2ecf20Sopenharmony_ci		}
3618c2ecf20Sopenharmony_ci	}
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic void __init dscr_parse_privperm(struct device_node *node,
3668c2ecf20Sopenharmony_ci				       void __iomem *base)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	u32 vals[2];
3698c2ecf20Sopenharmony_ci	int err;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	err = of_property_read_u32_array(node, "ti,dscr-privperm", vals, 2);
3728c2ecf20Sopenharmony_ci	if (err)
3738c2ecf20Sopenharmony_ci		return;
3748c2ecf20Sopenharmony_ci	dscr_write(vals[0], vals[1]);
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci/*
3788c2ecf20Sopenharmony_ci * SoCs may have "locked" DSCR registers which can only be written
3798c2ecf20Sopenharmony_ci * to only after writing a key value to a lock registers. These
3808c2ecf20Sopenharmony_ci * regisers can be described with the "ti,dscr-locked-regs" property.
3818c2ecf20Sopenharmony_ci * This property provides a list of register descriptions with each
3828c2ecf20Sopenharmony_ci * description consisting of three values.
3838c2ecf20Sopenharmony_ci *
3848c2ecf20Sopenharmony_ci *	ti,dscr-locked-regs = <reg0 lockreg0 key0
3858c2ecf20Sopenharmony_ci *                               ...
3868c2ecf20Sopenharmony_ci *                             regN lockregN keyN>;
3878c2ecf20Sopenharmony_ci *
3888c2ecf20Sopenharmony_ci * reg is the offset of the locked register
3898c2ecf20Sopenharmony_ci * lockreg is the offset of the lock register
3908c2ecf20Sopenharmony_ci * key is the unlock key written to lockreg
3918c2ecf20Sopenharmony_ci *
3928c2ecf20Sopenharmony_ci */
3938c2ecf20Sopenharmony_cistatic void __init dscr_parse_locked_regs(struct device_node *node,
3948c2ecf20Sopenharmony_ci					  void __iomem *base)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	struct locked_reg *r;
3978c2ecf20Sopenharmony_ci	const __be32 *p;
3988c2ecf20Sopenharmony_ci	int i, size;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	p = of_get_property(node, "ti,dscr-locked-regs", &size);
4018c2ecf20Sopenharmony_ci	if (p) {
4028c2ecf20Sopenharmony_ci		/* parse all the register descriptions we can handle */
4038c2ecf20Sopenharmony_ci		size /= (sizeof(*p) * 3);
4048c2ecf20Sopenharmony_ci		if (size > MAX_LOCKED_REGS)
4058c2ecf20Sopenharmony_ci			size = MAX_LOCKED_REGS;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci		for (i = 0; i < size; i++) {
4088c2ecf20Sopenharmony_ci			r = &dscr.locked[i];
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci			r->reg = be32_to_cpup(p++);
4118c2ecf20Sopenharmony_ci			r->lockreg = be32_to_cpup(p++);
4128c2ecf20Sopenharmony_ci			r->key = be32_to_cpup(p++);
4138c2ecf20Sopenharmony_ci		}
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci/*
4188c2ecf20Sopenharmony_ci * SoCs may have DSCR registers which are only write enabled after
4198c2ecf20Sopenharmony_ci * writing specific key values to two registers. The two key registers
4208c2ecf20Sopenharmony_ci * and the key values can be parsed from a "ti,dscr-kick-regs"
4218c2ecf20Sopenharmony_ci * propety with the following layout:
4228c2ecf20Sopenharmony_ci *
4238c2ecf20Sopenharmony_ci *	ti,dscr-kick-regs = <kickreg0 key0 kickreg1 key1>
4248c2ecf20Sopenharmony_ci *
4258c2ecf20Sopenharmony_ci * kickreg is the offset of the "kick" register
4268c2ecf20Sopenharmony_ci * key is the value which unlocks writing for protected regs
4278c2ecf20Sopenharmony_ci */
4288c2ecf20Sopenharmony_cistatic void __init dscr_parse_kick_regs(struct device_node *node,
4298c2ecf20Sopenharmony_ci					void __iomem *base)
4308c2ecf20Sopenharmony_ci{
4318c2ecf20Sopenharmony_ci	u32 vals[4];
4328c2ecf20Sopenharmony_ci	int err;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	err = of_property_read_u32_array(node, "ti,dscr-kick-regs", vals, 4);
4358c2ecf20Sopenharmony_ci	if (!err) {
4368c2ecf20Sopenharmony_ci		dscr.kick_reg[0] = vals[0];
4378c2ecf20Sopenharmony_ci		dscr.kick_key[0] = vals[1];
4388c2ecf20Sopenharmony_ci		dscr.kick_reg[1] = vals[2];
4398c2ecf20Sopenharmony_ci		dscr.kick_key[1] = vals[3];
4408c2ecf20Sopenharmony_ci	}
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci/*
4458c2ecf20Sopenharmony_ci * SoCs may provide controls to enable/disable individual IP blocks. These
4468c2ecf20Sopenharmony_ci * controls in the DSCR usually control pin drivers but also may control
4478c2ecf20Sopenharmony_ci * clocking and or resets. The device tree is used to describe the bitfields
4488c2ecf20Sopenharmony_ci * in registers used to control device state. The number of bits and their
4498c2ecf20Sopenharmony_ci * values may vary even within the same register.
4508c2ecf20Sopenharmony_ci *
4518c2ecf20Sopenharmony_ci * The layout of these bitfields is described by the ti,dscr-devstate-ctl-regs
4528c2ecf20Sopenharmony_ci * property. This property is a list where each element describes a contiguous
4538c2ecf20Sopenharmony_ci * range of control fields with like properties. Each element of the list
4548c2ecf20Sopenharmony_ci * consists of 7 cells with the following values:
4558c2ecf20Sopenharmony_ci *
4568c2ecf20Sopenharmony_ci *   start_id num_ids reg enable disable start_bit nbits
4578c2ecf20Sopenharmony_ci *
4588c2ecf20Sopenharmony_ci * start_id is device id for the first device control in the range
4598c2ecf20Sopenharmony_ci * num_ids is the number of device controls in the range
4608c2ecf20Sopenharmony_ci * reg is the offset of the register holding the control bits
4618c2ecf20Sopenharmony_ci * enable is the value to enable a device
4628c2ecf20Sopenharmony_ci * disable is the value to disable a device (0xffffffff if cannot disable)
4638c2ecf20Sopenharmony_ci * start_bit is the bit number of the first bit in the range
4648c2ecf20Sopenharmony_ci * nbits is the number of bits per device control
4658c2ecf20Sopenharmony_ci */
4668c2ecf20Sopenharmony_cistatic void __init dscr_parse_devstate_ctl_regs(struct device_node *node,
4678c2ecf20Sopenharmony_ci						void __iomem *base)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	struct devstate_ctl_reg *r;
4708c2ecf20Sopenharmony_ci	const __be32 *p;
4718c2ecf20Sopenharmony_ci	int i, j, size;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	p = of_get_property(node, "ti,dscr-devstate-ctl-regs", &size);
4748c2ecf20Sopenharmony_ci	if (p) {
4758c2ecf20Sopenharmony_ci		/* parse all the ranges we can handle */
4768c2ecf20Sopenharmony_ci		size /= (sizeof(*p) * 7);
4778c2ecf20Sopenharmony_ci		if (size > MAX_DEVCTL_REGS)
4788c2ecf20Sopenharmony_ci			size = MAX_DEVCTL_REGS;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci		for (i = 0; i < size; i++) {
4818c2ecf20Sopenharmony_ci			r = &dscr.devctl[i];
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci			r->start_id = be32_to_cpup(p++);
4848c2ecf20Sopenharmony_ci			r->num_ids = be32_to_cpup(p++);
4858c2ecf20Sopenharmony_ci			r->reg = be32_to_cpup(p++);
4868c2ecf20Sopenharmony_ci			r->enable = be32_to_cpup(p++);
4878c2ecf20Sopenharmony_ci			r->disable = be32_to_cpup(p++);
4888c2ecf20Sopenharmony_ci			if (r->disable == 0xffffffff)
4898c2ecf20Sopenharmony_ci				r->enable_only = 1;
4908c2ecf20Sopenharmony_ci			r->shift = be32_to_cpup(p++);
4918c2ecf20Sopenharmony_ci			r->nbits = be32_to_cpup(p++);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci			for (j = r->start_id;
4948c2ecf20Sopenharmony_ci			     j < (r->start_id + r->num_ids);
4958c2ecf20Sopenharmony_ci			     j++)
4968c2ecf20Sopenharmony_ci				dscr.devstate_info[j].ctl = r;
4978c2ecf20Sopenharmony_ci		}
4988c2ecf20Sopenharmony_ci	}
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci/*
5028c2ecf20Sopenharmony_ci * SoCs may provide status registers indicating the state (enabled/disabled) of
5038c2ecf20Sopenharmony_ci * devices on the SoC. The device tree is used to describe the bitfields in
5048c2ecf20Sopenharmony_ci * registers used to provide device status. The number of bits and their
5058c2ecf20Sopenharmony_ci * values used to provide status may vary even within the same register.
5068c2ecf20Sopenharmony_ci *
5078c2ecf20Sopenharmony_ci * The layout of these bitfields is described by the ti,dscr-devstate-stat-regs
5088c2ecf20Sopenharmony_ci * property. This property is a list where each element describes a contiguous
5098c2ecf20Sopenharmony_ci * range of status fields with like properties. Each element of the list
5108c2ecf20Sopenharmony_ci * consists of 7 cells with the following values:
5118c2ecf20Sopenharmony_ci *
5128c2ecf20Sopenharmony_ci *   start_id num_ids reg enable disable start_bit nbits
5138c2ecf20Sopenharmony_ci *
5148c2ecf20Sopenharmony_ci * start_id is device id for the first device status in the range
5158c2ecf20Sopenharmony_ci * num_ids is the number of devices covered by the range
5168c2ecf20Sopenharmony_ci * reg is the offset of the register holding the status bits
5178c2ecf20Sopenharmony_ci * enable is the value indicating device is enabled
5188c2ecf20Sopenharmony_ci * disable is the value indicating device is disabled
5198c2ecf20Sopenharmony_ci * start_bit is the bit number of the first bit in the range
5208c2ecf20Sopenharmony_ci * nbits is the number of bits per device status
5218c2ecf20Sopenharmony_ci */
5228c2ecf20Sopenharmony_cistatic void __init dscr_parse_devstate_stat_regs(struct device_node *node,
5238c2ecf20Sopenharmony_ci						 void __iomem *base)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	struct devstate_stat_reg *r;
5268c2ecf20Sopenharmony_ci	const __be32 *p;
5278c2ecf20Sopenharmony_ci	int i, j, size;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	p = of_get_property(node, "ti,dscr-devstate-stat-regs", &size);
5308c2ecf20Sopenharmony_ci	if (p) {
5318c2ecf20Sopenharmony_ci		/* parse all the ranges we can handle */
5328c2ecf20Sopenharmony_ci		size /= (sizeof(*p) * 7);
5338c2ecf20Sopenharmony_ci		if (size > MAX_DEVSTAT_REGS)
5348c2ecf20Sopenharmony_ci			size = MAX_DEVSTAT_REGS;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci		for (i = 0; i < size; i++) {
5378c2ecf20Sopenharmony_ci			r = &dscr.devstat[i];
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci			r->start_id = be32_to_cpup(p++);
5408c2ecf20Sopenharmony_ci			r->num_ids = be32_to_cpup(p++);
5418c2ecf20Sopenharmony_ci			r->reg = be32_to_cpup(p++);
5428c2ecf20Sopenharmony_ci			r->enable = be32_to_cpup(p++);
5438c2ecf20Sopenharmony_ci			r->disable = be32_to_cpup(p++);
5448c2ecf20Sopenharmony_ci			r->shift = be32_to_cpup(p++);
5458c2ecf20Sopenharmony_ci			r->nbits = be32_to_cpup(p++);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci			for (j = r->start_id;
5488c2ecf20Sopenharmony_ci			     j < (r->start_id + r->num_ids);
5498c2ecf20Sopenharmony_ci			     j++)
5508c2ecf20Sopenharmony_ci				dscr.devstate_info[j].stat = r;
5518c2ecf20Sopenharmony_ci		}
5528c2ecf20Sopenharmony_ci	}
5538c2ecf20Sopenharmony_ci}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_cistatic struct of_device_id dscr_ids[] __initdata = {
5568c2ecf20Sopenharmony_ci	{ .compatible = "ti,c64x+dscr" },
5578c2ecf20Sopenharmony_ci	{}
5588c2ecf20Sopenharmony_ci};
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci/*
5618c2ecf20Sopenharmony_ci * Probe for DSCR area.
5628c2ecf20Sopenharmony_ci *
5638c2ecf20Sopenharmony_ci * This has to be done early on in case timer or interrupt controller
5648c2ecf20Sopenharmony_ci * needs something. e.g. On C6455 SoC, timer must be enabled through
5658c2ecf20Sopenharmony_ci * DSCR before it is functional.
5668c2ecf20Sopenharmony_ci */
5678c2ecf20Sopenharmony_civoid __init dscr_probe(void)
5688c2ecf20Sopenharmony_ci{
5698c2ecf20Sopenharmony_ci	struct device_node *node;
5708c2ecf20Sopenharmony_ci	void __iomem *base;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	spin_lock_init(&dscr.lock);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	node = of_find_matching_node(NULL, dscr_ids);
5758c2ecf20Sopenharmony_ci	if (!node)
5768c2ecf20Sopenharmony_ci		return;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	base = of_iomap(node, 0);
5798c2ecf20Sopenharmony_ci	if (!base) {
5808c2ecf20Sopenharmony_ci		of_node_put(node);
5818c2ecf20Sopenharmony_ci		return;
5828c2ecf20Sopenharmony_ci	}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	dscr.base = base;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	dscr_parse_devstat(node, base);
5878c2ecf20Sopenharmony_ci	dscr_parse_silicon_rev(node, base);
5888c2ecf20Sopenharmony_ci	dscr_parse_mac_fuse(node, base);
5898c2ecf20Sopenharmony_ci	dscr_parse_rmii_resets(node, base);
5908c2ecf20Sopenharmony_ci	dscr_parse_locked_regs(node, base);
5918c2ecf20Sopenharmony_ci	dscr_parse_kick_regs(node, base);
5928c2ecf20Sopenharmony_ci	dscr_parse_devstate_ctl_regs(node, base);
5938c2ecf20Sopenharmony_ci	dscr_parse_devstate_stat_regs(node, base);
5948c2ecf20Sopenharmony_ci	dscr_parse_privperm(node, base);
5958c2ecf20Sopenharmony_ci}
596