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