1 /* 2 * Broadcom STB SoCs Bus Unit Interface controls 3 * 4 * Copyright (C) 2015, Broadcom Corporation 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 */ 15 16 #define pr_fmt(fmt) "brcmstb: " KBUILD_MODNAME ": " fmt 17 18 #include <linux/kernel.h> 19 #include <linux/io.h> 20 #include <linux/of_address.h> 21 #include <linux/syscore_ops.h> 22 #include <linux/soc/brcmstb/brcmstb.h> 23 24 #define CPU_CREDIT_REG_MCPx_WR_PAIRING_EN_MASK 0x70000000 25 #define CPU_CREDIT_REG_MCPx_READ_CRED_MASK 0xf 26 #define CPU_CREDIT_REG_MCPx_WRITE_CRED_MASK 0xf 27 #define CPU_CREDIT_REG_MCPx_READ_CRED_SHIFT(x) ((x) * 8) 28 #define CPU_CREDIT_REG_MCPx_WRITE_CRED_SHIFT(x) (((x) * 8) + 4) 29 30 #define CPU_MCP_FLOW_REG_MCPx_RDBUFF_CRED_SHIFT(x) ((x) * 8) 31 #define CPU_MCP_FLOW_REG_MCPx_RDBUFF_CRED_MASK 0xff 32 33 #define CPU_WRITEBACK_CTRL_REG_WB_THROTTLE_THRESHOLD_MASK 0xf 34 #define CPU_WRITEBACK_CTRL_REG_WB_THROTTLE_TIMEOUT_MASK 0xf 35 #define CPU_WRITEBACK_CTRL_REG_WB_THROTTLE_TIMEOUT_SHIFT 4 36 #define CPU_WRITEBACK_CTRL_REG_WB_THROTTLE_ENABLE BIT(8) 37 38 static void __iomem *cpubiuctrl_base; 39 static bool mcp_wr_pairing_en; 40 static const int *cpubiuctrl_regs; 41 42 static inline u32 cbc_readl(int reg) 43 { 44 int offset = cpubiuctrl_regs[reg]; 45 46 if (offset == -1) 47 return (u32)-1; 48 49 return readl_relaxed(cpubiuctrl_base + offset); 50 } 51 52 static inline void cbc_writel(u32 val, int reg) 53 { 54 int offset = cpubiuctrl_regs[reg]; 55 56 if (offset == -1) 57 return; 58 59 writel_relaxed(val, cpubiuctrl_base + offset); 60 } 61 62 enum cpubiuctrl_regs { 63 CPU_CREDIT_REG = 0, 64 CPU_MCP_FLOW_REG, 65 CPU_WRITEBACK_CTRL_REG 66 }; 67 68 static const int b15_cpubiuctrl_regs[] = { 69 [CPU_CREDIT_REG] = 0x184, 70 [CPU_MCP_FLOW_REG] = -1, 71 [CPU_WRITEBACK_CTRL_REG] = -1, 72 }; 73 74 /* Odd cases, e.g: 7260 */ 75 static const int b53_cpubiuctrl_no_wb_regs[] = { 76 [CPU_CREDIT_REG] = 0x0b0, 77 [CPU_MCP_FLOW_REG] = 0x0b4, 78 [CPU_WRITEBACK_CTRL_REG] = -1, 79 }; 80 81 static const int b53_cpubiuctrl_regs[] = { 82 [CPU_CREDIT_REG] = 0x0b0, 83 [CPU_MCP_FLOW_REG] = 0x0b4, 84 [CPU_WRITEBACK_CTRL_REG] = 0x22c, 85 }; 86 87 #define NUM_CPU_BIUCTRL_REGS 3 88 89 static int __init mcp_write_pairing_set(void) 90 { 91 u32 creds = 0; 92 93 if (!cpubiuctrl_base) 94 return -1; 95 96 creds = cbc_readl(CPU_CREDIT_REG); 97 if (mcp_wr_pairing_en) { 98 pr_info("MCP: Enabling write pairing\n"); 99 cbc_writel(creds | CPU_CREDIT_REG_MCPx_WR_PAIRING_EN_MASK, 100 CPU_CREDIT_REG); 101 } else if (creds & CPU_CREDIT_REG_MCPx_WR_PAIRING_EN_MASK) { 102 pr_info("MCP: Disabling write pairing\n"); 103 cbc_writel(creds & ~CPU_CREDIT_REG_MCPx_WR_PAIRING_EN_MASK, 104 CPU_CREDIT_REG); 105 } else { 106 pr_info("MCP: Write pairing already disabled\n"); 107 } 108 109 return 0; 110 } 111 112 static const u32 b53_mach_compat[] = { 113 0x7268, 114 0x7271, 115 0x7278, 116 }; 117 118 static void __init mcp_b53_set(void) 119 { 120 unsigned int i; 121 u32 reg; 122 123 reg = brcmstb_get_family_id(); 124 125 for (i = 0; i < ARRAY_SIZE(b53_mach_compat); i++) { 126 if (BRCM_ID(reg) == b53_mach_compat[i]) 127 break; 128 } 129 130 if (i == ARRAY_SIZE(b53_mach_compat)) 131 return; 132 133 /* Set all 3 MCP interfaces to 8 credits */ 134 reg = cbc_readl(CPU_CREDIT_REG); 135 for (i = 0; i < 3; i++) { 136 reg &= ~(CPU_CREDIT_REG_MCPx_WRITE_CRED_MASK << 137 CPU_CREDIT_REG_MCPx_WRITE_CRED_SHIFT(i)); 138 reg &= ~(CPU_CREDIT_REG_MCPx_READ_CRED_MASK << 139 CPU_CREDIT_REG_MCPx_READ_CRED_SHIFT(i)); 140 reg |= 8 << CPU_CREDIT_REG_MCPx_WRITE_CRED_SHIFT(i); 141 reg |= 8 << CPU_CREDIT_REG_MCPx_READ_CRED_SHIFT(i); 142 } 143 cbc_writel(reg, CPU_CREDIT_REG); 144 145 /* Max out the number of in-flight Jwords reads on the MCP interface */ 146 reg = cbc_readl(CPU_MCP_FLOW_REG); 147 for (i = 0; i < 3; i++) 148 reg |= CPU_MCP_FLOW_REG_MCPx_RDBUFF_CRED_MASK << 149 CPU_MCP_FLOW_REG_MCPx_RDBUFF_CRED_SHIFT(i); 150 cbc_writel(reg, CPU_MCP_FLOW_REG); 151 152 /* Enable writeback throttling, set timeout to 128 cycles, 256 cycles 153 * threshold 154 */ 155 reg = cbc_readl(CPU_WRITEBACK_CTRL_REG); 156 reg |= CPU_WRITEBACK_CTRL_REG_WB_THROTTLE_ENABLE; 157 reg &= ~CPU_WRITEBACK_CTRL_REG_WB_THROTTLE_THRESHOLD_MASK; 158 reg &= ~(CPU_WRITEBACK_CTRL_REG_WB_THROTTLE_TIMEOUT_MASK << 159 CPU_WRITEBACK_CTRL_REG_WB_THROTTLE_TIMEOUT_SHIFT); 160 reg |= 8; 161 reg |= 7 << CPU_WRITEBACK_CTRL_REG_WB_THROTTLE_TIMEOUT_SHIFT; 162 cbc_writel(reg, CPU_WRITEBACK_CTRL_REG); 163 } 164 165 static int __init setup_hifcpubiuctrl_regs(struct device_node *np) 166 { 167 struct device_node *cpu_dn; 168 int ret = 0; 169 170 cpubiuctrl_base = of_iomap(np, 0); 171 if (!cpubiuctrl_base) { 172 pr_err("failed to remap BIU control base\n"); 173 ret = -ENOMEM; 174 goto out; 175 } 176 177 mcp_wr_pairing_en = of_property_read_bool(np, "brcm,write-pairing"); 178 179 cpu_dn = of_get_cpu_node(0, NULL); 180 if (!cpu_dn) { 181 pr_err("failed to obtain CPU device node\n"); 182 ret = -ENODEV; 183 goto out; 184 } 185 186 if (of_device_is_compatible(cpu_dn, "brcm,brahma-b15")) 187 cpubiuctrl_regs = b15_cpubiuctrl_regs; 188 else if (of_device_is_compatible(cpu_dn, "brcm,brahma-b53")) 189 cpubiuctrl_regs = b53_cpubiuctrl_regs; 190 else { 191 pr_err("unsupported CPU\n"); 192 ret = -EINVAL; 193 } 194 of_node_put(cpu_dn); 195 196 if (BRCM_ID(brcmstb_get_family_id()) == 0x7260) 197 cpubiuctrl_regs = b53_cpubiuctrl_no_wb_regs; 198 out: 199 of_node_put(np); 200 return ret; 201 } 202 203 #ifdef CONFIG_PM_SLEEP 204 static u32 cpubiuctrl_reg_save[NUM_CPU_BIUCTRL_REGS]; 205 206 static int brcmstb_cpu_credit_reg_suspend(void) 207 { 208 unsigned int i; 209 210 if (!cpubiuctrl_base) 211 return 0; 212 213 for (i = 0; i < NUM_CPU_BIUCTRL_REGS; i++) 214 cpubiuctrl_reg_save[i] = cbc_readl(i); 215 216 return 0; 217 } 218 219 static void brcmstb_cpu_credit_reg_resume(void) 220 { 221 unsigned int i; 222 223 if (!cpubiuctrl_base) 224 return; 225 226 for (i = 0; i < NUM_CPU_BIUCTRL_REGS; i++) 227 cbc_writel(cpubiuctrl_reg_save[i], i); 228 } 229 230 static struct syscore_ops brcmstb_cpu_credit_syscore_ops = { 231 .suspend = brcmstb_cpu_credit_reg_suspend, 232 .resume = brcmstb_cpu_credit_reg_resume, 233 }; 234 #endif 235 236 237 static int __init brcmstb_biuctrl_init(void) 238 { 239 struct device_node *np; 240 int ret; 241 242 /* We might be running on a multi-platform kernel, don't make this a 243 * fatal error, just bail out early 244 */ 245 np = of_find_compatible_node(NULL, NULL, "brcm,brcmstb-cpu-biu-ctrl"); 246 if (!np) 247 return 0; 248 249 setup_hifcpubiuctrl_regs(np); 250 251 ret = mcp_write_pairing_set(); 252 if (ret) { 253 pr_err("MCP: Unable to disable write pairing!\n"); 254 return ret; 255 } 256 257 mcp_b53_set(); 258 #ifdef CONFIG_PM_SLEEP 259 register_syscore_ops(&brcmstb_cpu_credit_syscore_ops); 260 #endif 261 return 0; 262 } 263 early_initcall(brcmstb_biuctrl_init); 264