Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 1 | /* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 2 | * |
| 3 | * This program is free software; you can redistribute it and/or modify |
| 4 | * it under the terms of the GNU General Public License version 2 and |
| 5 | * only version 2 as published by the Free Software Foundation. |
| 6 | * |
| 7 | * This program is distributed in the hope that it will be useful, |
| 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 10 | * GNU General Public License for more details. |
| 11 | */ |
| 12 | |
| 13 | #define pr_fmt(fmt) "AXI: NOC: %s(): " fmt, __func__ |
| 14 | |
| 15 | #include <linux/slab.h> |
| 16 | #include <linux/io.h> |
| 17 | #include <mach/msm_bus_board.h> |
| 18 | #include "msm_bus_core.h" |
| 19 | #include "msm_bus_noc.h" |
| 20 | |
| 21 | /* NOC_QOS generic */ |
| 22 | #define __CLZ(x) ((8 * sizeof(uint32_t)) - 1 - __fls(x)) |
| 23 | #define SAT_SCALE 16 /* 16 bytes minimum for saturation */ |
| 24 | #define BW_SCALE 256 /* 1/256 byte per cycle unit */ |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 25 | #define QOS_DEFAULT_BASEOFFSET 0x00003000 |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 26 | #define MAX_BW_FIELD (NOC_QOS_BWn_BW_BMSK >> NOC_QOS_BWn_BW_SHFT) |
| 27 | #define MAX_SAT_FIELD (NOC_QOS_SATn_SAT_BMSK >> NOC_QOS_SATn_SAT_SHFT) |
| 28 | |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 29 | #define NOC_QOS_REG_BASE(b, o) ((b) + (o)) |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 30 | |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 31 | #define NOC_QOS_ID_COREIDn_ADDR(b, o, n) \ |
| 32 | (NOC_QOS_REG_BASE(b, o) + 0x80 * (n)) |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 33 | enum noc_qos_id_coreidn { |
| 34 | NOC_QOS_ID_COREIDn_RMSK = 0xffffffff, |
| 35 | NOC_QOS_ID_COREIDn_MAXn = 32, |
| 36 | NOC_QOS_ID_COREIDn_CORECHSUM_BMSK = 0xffffff00, |
| 37 | NOC_QOS_ID_COREIDn_CORECHSUM_SHFT = 0x8, |
| 38 | NOC_QOS_ID_COREIDn_CORETYPEID_BMSK = 0xff, |
| 39 | NOC_QOS_ID_COREIDn_CORETYPEID_SHFT = 0x0, |
| 40 | }; |
| 41 | |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 42 | #define NOC_QOS_ID_REVISIONIDn_ADDR(b, o, n) \ |
| 43 | (NOC_QOS_REG_BASE(b, o) + 0x4 + 0x80 * (n)) |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 44 | enum noc_qos_id_revisionidn { |
| 45 | NOC_QOS_ID_REVISIONIDn_RMSK = 0xffffffff, |
| 46 | NOC_QOS_ID_REVISIONIDn_MAXn = 32, |
| 47 | NOC_QOS_ID_REVISIONIDn_FLEXNOCID_BMSK = 0xffffff00, |
| 48 | NOC_QOS_ID_REVISIONIDn_FLEXNOCID_SHFT = 0x8, |
| 49 | NOC_QOS_ID_REVISIONIDn_USERID_BMSK = 0xff, |
| 50 | NOC_QOS_ID_REVISIONIDn_USERID_SHFT = 0x0, |
| 51 | }; |
| 52 | |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 53 | #define NOC_QOS_PRIORITYn_ADDR(b, o, n) \ |
| 54 | (NOC_QOS_REG_BASE(b, o) + 0x8 + 0x80 * (n)) |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 55 | enum noc_qos_id_priorityn { |
| 56 | NOC_QOS_PRIORITYn_RMSK = 0x0000000f, |
| 57 | NOC_QOS_PRIORITYn_MAXn = 32, |
| 58 | NOC_QOS_PRIORITYn_P1_BMSK = 0xc, |
| 59 | NOC_QOS_PRIORITYn_P1_SHFT = 0x2, |
| 60 | NOC_QOS_PRIORITYn_P0_BMSK = 0x3, |
| 61 | NOC_QOS_PRIORITYn_P0_SHFT = 0x0, |
| 62 | }; |
| 63 | |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 64 | #define NOC_QOS_MODEn_ADDR(b, o, n) \ |
| 65 | (NOC_QOS_REG_BASE(b, o) + 0xC + 0x80 * (n)) |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 66 | enum noc_qos_id_moden_rmsk { |
| 67 | NOC_QOS_MODEn_RMSK = 0x00000003, |
| 68 | NOC_QOS_MODEn_MAXn = 32, |
| 69 | NOC_QOS_MODEn_MODE_BMSK = 0x3, |
| 70 | NOC_QOS_MODEn_MODE_SHFT = 0x0, |
| 71 | }; |
| 72 | |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 73 | #define NOC_QOS_BWn_ADDR(b, o, n) \ |
| 74 | (NOC_QOS_REG_BASE(b, o) + 0x10 + 0x80 * (n)) |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 75 | enum noc_qos_id_bwn { |
| 76 | NOC_QOS_BWn_RMSK = 0x0000ffff, |
| 77 | NOC_QOS_BWn_MAXn = 32, |
| 78 | NOC_QOS_BWn_BW_BMSK = 0xffff, |
| 79 | NOC_QOS_BWn_BW_SHFT = 0x0, |
| 80 | }; |
| 81 | |
| 82 | /* QOS Saturation registers */ |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 83 | #define NOC_QOS_SATn_ADDR(b, o, n) \ |
| 84 | (NOC_QOS_REG_BASE(b, o) + 0x14 + 0x80 * (n)) |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 85 | enum noc_qos_id_saturationn { |
| 86 | NOC_QOS_SATn_RMSK = 0x000003ff, |
| 87 | NOC_QOS_SATn_MAXn = 32, |
| 88 | NOC_QOS_SATn_SAT_BMSK = 0x3ff, |
| 89 | NOC_QOS_SATn_SAT_SHFT = 0x0, |
| 90 | }; |
| 91 | |
| 92 | static int noc_div(uint64_t *a, uint32_t b) |
| 93 | { |
| 94 | if ((*a > 0) && (*a < b)) |
| 95 | return 1; |
| 96 | else |
| 97 | return do_div(*a, b); |
| 98 | } |
| 99 | |
| 100 | /** |
| 101 | * Calculates bw hardware is using from register values |
| 102 | * bw returned is in bytes/sec |
| 103 | */ |
| 104 | static uint64_t noc_bw(uint32_t bw_field, uint32_t qos_freq) |
| 105 | { |
| 106 | uint64_t res; |
| 107 | uint32_t rem, scale; |
| 108 | |
| 109 | res = 2 * qos_freq * bw_field; |
| 110 | scale = BW_SCALE * 1000; |
| 111 | rem = noc_div(&res, scale); |
| 112 | MSM_BUS_DBG("NOC: Calculated bw: %llu\n", res * 1000000ULL); |
| 113 | return res * 1000000ULL; |
| 114 | } |
| 115 | |
| 116 | static uint32_t noc_bw_ceil(long int bw_field, uint32_t qos_freq) |
| 117 | { |
| 118 | uint64_t bw_temp = 2 * qos_freq * bw_field; |
| 119 | uint32_t scale = 1000 * BW_SCALE; |
| 120 | noc_div(&bw_temp, scale); |
| 121 | return bw_temp * 1000000; |
| 122 | } |
| 123 | #define MAX_BW(timebase) noc_bw_ceil(MAX_BW_FIELD, (timebase)) |
| 124 | |
| 125 | /** |
| 126 | * Calculates ws hardware is using from register values |
| 127 | * ws returned is in nanoseconds |
| 128 | */ |
| 129 | static uint32_t noc_ws(uint64_t bw, uint32_t sat, uint32_t qos_freq) |
| 130 | { |
| 131 | if (bw && qos_freq) { |
| 132 | uint32_t bwf = bw * qos_freq; |
| 133 | uint64_t scale = 1000000000000LL * BW_SCALE * |
| 134 | SAT_SCALE * sat; |
| 135 | noc_div(&scale, bwf); |
| 136 | MSM_BUS_DBG("NOC: Calculated ws: %llu\n", scale); |
| 137 | return scale; |
| 138 | } |
| 139 | |
| 140 | return 0; |
| 141 | } |
| 142 | #define MAX_WS(bw, timebase) noc_ws((bw), MAX_SAT_FIELD, (timebase)) |
| 143 | |
| 144 | /* Calculate bandwidth field value for requested bandwidth */ |
| 145 | static uint32_t noc_bw_field(uint64_t bw, uint32_t qos_freq) |
| 146 | { |
| 147 | uint32_t bw_field = 0; |
| 148 | |
| 149 | if (bw) { |
| 150 | uint32_t rem; |
| 151 | uint64_t bw_capped = min_t(uint64_t, bw, MAX_BW(qos_freq)); |
| 152 | uint64_t bwc = bw_capped * BW_SCALE; |
| 153 | uint64_t qf = 2 * qos_freq * 1000; |
| 154 | |
| 155 | rem = noc_div(&bwc, qf); |
| 156 | bw_field = (uint32_t)min_t(uint64_t, bwc, MAX_BW_FIELD); |
| 157 | } |
| 158 | |
| 159 | MSM_BUS_DBG("NOC: bw_field: %u\n", bw_field); |
| 160 | return bw_field; |
| 161 | } |
| 162 | |
| 163 | static uint32_t noc_sat_field(uint64_t bw, uint32_t ws, uint32_t qos_freq) |
| 164 | { |
| 165 | uint32_t sat_field = 0, win; |
| 166 | |
| 167 | if (bw) { |
| 168 | /* Limit to max bw and scale bw to 100 KB increments */ |
| 169 | uint64_t tbw, tscale; |
| 170 | uint64_t bw_scaled = min_t(uint64_t, bw, MAX_BW(qos_freq)); |
| 171 | uint32_t rem = noc_div(&bw_scaled, 100000); |
| 172 | |
| 173 | /** |
| 174 | * Calculate saturation from windows size. |
| 175 | * WS must be at least one arb period. |
| 176 | * Saturation must not exceed max field size |
| 177 | * |
| 178 | * Bandwidth is in 100KB increments |
| 179 | * Window size is in ns |
| 180 | * qos_freq is in KHz |
| 181 | */ |
| 182 | win = max(ws, 1000000 / qos_freq); |
| 183 | tbw = bw_scaled * win * qos_freq; |
| 184 | tscale = 10000000ULL * BW_SCALE * SAT_SCALE; |
| 185 | rem = noc_div(&tbw, tscale); |
| 186 | sat_field = (uint32_t)min_t(uint64_t, tbw, MAX_SAT_FIELD); |
| 187 | } |
| 188 | |
| 189 | MSM_BUS_DBG("NOC: sat_field: %d\n", sat_field); |
| 190 | return sat_field; |
| 191 | } |
| 192 | |
| 193 | static void noc_set_qos_mode(struct msm_bus_noc_info *ninfo, uint32_t mport, |
Gagan Mac | fa8dff7 | 2012-08-30 15:03:40 -0600 | [diff] [blame] | 194 | uint8_t mode, uint8_t perm_mode) |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 195 | { |
| 196 | if (mode < NOC_QOS_MODE_MAX && |
Gagan Mac | fa8dff7 | 2012-08-30 15:03:40 -0600 | [diff] [blame] | 197 | ((1 << mode) & perm_mode)) { |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 198 | uint32_t reg_val; |
| 199 | |
| 200 | reg_val = readl_relaxed(NOC_QOS_MODEn_ADDR(ninfo->base, |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 201 | ninfo->qos_baseoffset, mport)) & NOC_QOS_MODEn_RMSK; |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 202 | writel_relaxed(((reg_val & (~(NOC_QOS_MODEn_MODE_BMSK))) | |
| 203 | (mode & NOC_QOS_MODEn_MODE_BMSK)), |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 204 | NOC_QOS_MODEn_ADDR(ninfo->base, ninfo->qos_baseoffset, |
| 205 | mport)); |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 206 | } |
| 207 | /* Ensure qos mode is set before exiting */ |
| 208 | wmb(); |
| 209 | } |
| 210 | |
| 211 | static void noc_set_qos_priority(struct msm_bus_noc_info *ninfo, uint32_t mport, |
| 212 | struct msm_bus_noc_qos_priority *priority) |
| 213 | { |
| 214 | uint32_t reg_val, val; |
| 215 | |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 216 | reg_val = readl_relaxed(NOC_QOS_PRIORITYn_ADDR(ninfo->base, |
| 217 | ninfo->qos_baseoffset, mport)) |
| 218 | & NOC_QOS_PRIORITYn_RMSK; |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 219 | val = priority->p1 << NOC_QOS_PRIORITYn_P1_SHFT; |
| 220 | writel_relaxed(((reg_val & (~(NOC_QOS_PRIORITYn_P1_BMSK))) | |
| 221 | (val & NOC_QOS_PRIORITYn_P1_BMSK)), |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 222 | NOC_QOS_PRIORITYn_ADDR(ninfo->base, ninfo->qos_baseoffset, |
| 223 | mport)); |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 224 | |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 225 | reg_val = readl_relaxed(NOC_QOS_PRIORITYn_ADDR(ninfo->base, |
| 226 | ninfo->qos_baseoffset, |
| 227 | mport)) |
| 228 | & NOC_QOS_PRIORITYn_RMSK; |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 229 | writel_relaxed(((reg_val & (~(NOC_QOS_PRIORITYn_P0_BMSK))) | |
| 230 | (priority->p0 & NOC_QOS_PRIORITYn_P0_BMSK)), |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 231 | NOC_QOS_PRIORITYn_ADDR(ninfo->base, ninfo->qos_baseoffset, |
| 232 | mport)); |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 233 | /* Ensure qos priority is set before exiting */ |
| 234 | wmb(); |
| 235 | } |
| 236 | |
| 237 | static void msm_bus_noc_set_qos_bw(struct msm_bus_noc_info *ninfo, |
| 238 | uint32_t mport, uint8_t perm_mode, struct msm_bus_noc_qos_bw *qbw) |
| 239 | { |
| 240 | uint32_t reg_val, val, mode; |
| 241 | |
| 242 | if (!ninfo->qos_freq) { |
| 243 | MSM_BUS_DBG("Zero QoS Freq\n"); |
| 244 | return; |
| 245 | } |
| 246 | |
| 247 | |
| 248 | /* If Limiter or Regulator modes are not supported, bw not available*/ |
| 249 | if (perm_mode & (NOC_QOS_PERM_MODE_LIMITER | |
| 250 | NOC_QOS_PERM_MODE_REGULATOR)) { |
| 251 | uint32_t bw_val = noc_bw_field(qbw->bw, ninfo->qos_freq); |
| 252 | uint32_t sat_val = noc_sat_field(qbw->bw, qbw->ws, |
| 253 | ninfo->qos_freq); |
| 254 | |
| 255 | MSM_BUS_DBG("NOC: BW: perm_mode: %d bw_val: %d, sat_val: %d\n", |
| 256 | perm_mode, bw_val, sat_val); |
| 257 | /* |
| 258 | * If in Limiter/Regulator mode, first go to fixed mode. |
| 259 | * Clear QoS accumulator |
| 260 | **/ |
| 261 | mode = readl_relaxed(NOC_QOS_MODEn_ADDR(ninfo->base, |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 262 | ninfo->qos_baseoffset, mport)) |
| 263 | & NOC_QOS_MODEn_MODE_BMSK; |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 264 | if (mode == NOC_QOS_MODE_REGULATOR || mode == |
| 265 | NOC_QOS_MODE_LIMITER) { |
| 266 | reg_val = readl_relaxed(NOC_QOS_MODEn_ADDR(ninfo-> |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 267 | base, ninfo->qos_baseoffset, mport)); |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 268 | val = NOC_QOS_MODE_FIXED; |
| 269 | writel_relaxed((reg_val & (~(NOC_QOS_MODEn_MODE_BMSK))) |
| 270 | | (val & NOC_QOS_MODEn_MODE_BMSK), |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 271 | NOC_QOS_MODEn_ADDR(ninfo->base, |
| 272 | ninfo->qos_baseoffset, mport)); |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 273 | } |
| 274 | |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 275 | reg_val = readl_relaxed(NOC_QOS_BWn_ADDR(ninfo->base, |
| 276 | ninfo->qos_baseoffset, mport)); |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 277 | val = bw_val << NOC_QOS_BWn_BW_SHFT; |
| 278 | writel_relaxed(((reg_val & (~(NOC_QOS_BWn_BW_BMSK))) | |
| 279 | (val & NOC_QOS_BWn_BW_BMSK)), |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 280 | NOC_QOS_BWn_ADDR(ninfo->base, ninfo->qos_baseoffset, |
| 281 | mport)); |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 282 | |
| 283 | MSM_BUS_DBG("NOC: BW: Wrote value: 0x%x\n", ((reg_val & |
| 284 | (~NOC_QOS_BWn_BW_BMSK)) | (val & |
| 285 | NOC_QOS_BWn_BW_BMSK))); |
| 286 | |
| 287 | reg_val = readl_relaxed(NOC_QOS_SATn_ADDR(ninfo->base, |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 288 | ninfo->qos_baseoffset, mport)); |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 289 | val = sat_val << NOC_QOS_SATn_SAT_SHFT; |
| 290 | writel_relaxed(((reg_val & (~(NOC_QOS_SATn_SAT_BMSK))) | |
| 291 | (val & NOC_QOS_SATn_SAT_BMSK)), |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 292 | NOC_QOS_SATn_ADDR(ninfo->base, ninfo->qos_baseoffset, |
| 293 | mport)); |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 294 | |
| 295 | MSM_BUS_DBG("NOC: SAT: Wrote value: 0x%x\n", ((reg_val & |
| 296 | (~NOC_QOS_SATn_SAT_BMSK)) | (val & |
| 297 | NOC_QOS_SATn_SAT_BMSK))); |
| 298 | |
| 299 | /* Set mode back to what it was initially */ |
| 300 | reg_val = readl_relaxed(NOC_QOS_MODEn_ADDR(ninfo->base, |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 301 | ninfo->qos_baseoffset, mport)); |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 302 | writel_relaxed((reg_val & (~(NOC_QOS_MODEn_MODE_BMSK))) |
| 303 | | (mode & NOC_QOS_MODEn_MODE_BMSK), |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 304 | NOC_QOS_MODEn_ADDR(ninfo->base, ninfo->qos_baseoffset, |
| 305 | mport)); |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 306 | /* Ensure that all writes for bandwidth registers have |
| 307 | * completed before returning |
| 308 | */ |
| 309 | wmb(); |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | uint8_t msm_bus_noc_get_qos_mode(struct msm_bus_noc_info *ninfo, |
Gagan Mac | fa8dff7 | 2012-08-30 15:03:40 -0600 | [diff] [blame] | 314 | uint32_t mport, uint32_t mode, uint32_t perm_mode) |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 315 | { |
Gagan Mac | fa8dff7 | 2012-08-30 15:03:40 -0600 | [diff] [blame] | 316 | if (NOC_QOS_MODES_ALL_PERM == perm_mode) |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 317 | return readl_relaxed(NOC_QOS_MODEn_ADDR(ninfo->base, |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 318 | ninfo->qos_baseoffset, mport)) & |
| 319 | NOC_QOS_MODEn_MODE_BMSK; |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 320 | else |
Gagan Mac | fa8dff7 | 2012-08-30 15:03:40 -0600 | [diff] [blame] | 321 | return 31 - __CLZ(mode & |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 322 | NOC_QOS_MODES_ALL_PERM); |
| 323 | } |
| 324 | |
| 325 | void msm_bus_noc_get_qos_priority(struct msm_bus_noc_info *ninfo, |
| 326 | uint32_t mport, struct msm_bus_noc_qos_priority *priority) |
| 327 | { |
| 328 | priority->p1 = (readl_relaxed(NOC_QOS_PRIORITYn_ADDR(ninfo->base, |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 329 | ninfo->qos_baseoffset, mport)) & NOC_QOS_PRIORITYn_P1_BMSK) >> |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 330 | NOC_QOS_PRIORITYn_P1_SHFT; |
| 331 | |
| 332 | priority->p0 = (readl_relaxed(NOC_QOS_PRIORITYn_ADDR(ninfo->base, |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 333 | ninfo->qos_baseoffset, mport)) & NOC_QOS_PRIORITYn_P0_BMSK) >> |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 334 | NOC_QOS_PRIORITYn_P0_SHFT; |
| 335 | } |
| 336 | |
| 337 | void msm_bus_noc_get_qos_bw(struct msm_bus_noc_info *ninfo, |
Gagan Mac | fa8dff7 | 2012-08-30 15:03:40 -0600 | [diff] [blame] | 338 | uint32_t mport, uint8_t perm_mode, struct msm_bus_noc_qos_bw *qbw) |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 339 | { |
Gagan Mac | fa8dff7 | 2012-08-30 15:03:40 -0600 | [diff] [blame] | 340 | if (perm_mode & (NOC_QOS_PERM_MODE_LIMITER | |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 341 | NOC_QOS_PERM_MODE_REGULATOR)) { |
| 342 | uint32_t bw_val = readl_relaxed(NOC_QOS_BWn_ADDR(ninfo-> |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 343 | base, ninfo->qos_baseoffset, mport)) & |
| 344 | NOC_QOS_BWn_BW_BMSK; |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 345 | uint32_t sat = readl_relaxed(NOC_QOS_SATn_ADDR(ninfo-> |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 346 | base, ninfo->qos_baseoffset, mport)) & |
| 347 | NOC_QOS_SATn_SAT_BMSK; |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 348 | |
| 349 | qbw->bw = noc_bw(bw_val, ninfo->qos_freq); |
| 350 | qbw->ws = noc_ws(qbw->bw, sat, ninfo->qos_freq); |
| 351 | } else { |
| 352 | qbw->bw = 0; |
| 353 | qbw->ws = 0; |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | static int msm_bus_noc_mas_init(struct msm_bus_noc_info *ninfo, |
| 358 | struct msm_bus_inode_info *info) |
| 359 | { |
| 360 | int i; |
| 361 | struct msm_bus_noc_qos_priority *prio; |
| 362 | prio = kzalloc(sizeof(struct msm_bus_noc_qos_priority), |
| 363 | GFP_KERNEL); |
| 364 | if (!prio) { |
| 365 | MSM_BUS_WARN("Couldn't alloc prio data for node: %d\n", |
| 366 | info->node_info->id); |
| 367 | return -ENOMEM; |
| 368 | } |
| 369 | |
| 370 | prio->read_prio = info->node_info->prio_rd; |
| 371 | prio->write_prio = info->node_info->prio_wr; |
| 372 | prio->p1 = info->node_info->prio1; |
| 373 | prio->p0 = info->node_info->prio0; |
| 374 | info->hw_data = (void *)prio; |
| 375 | |
| 376 | if (!info->node_info->qport) { |
| 377 | MSM_BUS_DBG("No QoS Ports to init\n"); |
| 378 | return 0; |
| 379 | } |
| 380 | |
| 381 | for (i = 0; i < info->node_info->num_mports; i++) { |
Gagan Mac | 9addee0 | 2012-11-06 16:40:42 -0700 | [diff] [blame] | 382 | if (info->node_info->mode != NOC_QOS_MODE_BYPASS) { |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 383 | noc_set_qos_priority(ninfo, info->node_info->qport[i], |
| 384 | prio); |
| 385 | |
Gagan Mac | 9addee0 | 2012-11-06 16:40:42 -0700 | [diff] [blame] | 386 | if (info->node_info->mode != NOC_QOS_MODE_FIXED) { |
| 387 | struct msm_bus_noc_qos_bw qbw; |
| 388 | qbw.ws = info->node_info->ws; |
| 389 | qbw.bw = 0; |
| 390 | msm_bus_noc_set_qos_bw(ninfo, info->node_info-> |
| 391 | qport[i], info->node_info->perm_mode, |
| 392 | &qbw); |
| 393 | } |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 394 | } |
| 395 | |
| 396 | noc_set_qos_mode(ninfo, info->node_info->qport[i], info-> |
Gagan Mac | fa8dff7 | 2012-08-30 15:03:40 -0600 | [diff] [blame] | 397 | node_info->mode, info->node_info->perm_mode); |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 398 | } |
| 399 | |
| 400 | return 0; |
| 401 | } |
| 402 | |
| 403 | static void msm_bus_noc_node_init(void *hw_data, |
| 404 | struct msm_bus_inode_info *info) |
| 405 | { |
| 406 | struct msm_bus_noc_info *ninfo = |
| 407 | (struct msm_bus_noc_info *)hw_data; |
| 408 | |
| 409 | if (!IS_SLAVE(info->node_info->priv_id)) |
Gagan Mac | fe0cb54 | 2012-08-13 11:23:02 -0600 | [diff] [blame] | 410 | if (info->node_info->hw_sel != MSM_BUS_RPM) |
| 411 | msm_bus_noc_mas_init(ninfo, info); |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 412 | } |
| 413 | |
| 414 | static int msm_bus_noc_allocate_commit_data(struct msm_bus_fabric_registration |
| 415 | *fab_pdata, void **cdata, int ctx) |
| 416 | { |
| 417 | struct msm_bus_noc_commit **cd = (struct msm_bus_noc_commit **)cdata; |
| 418 | struct msm_bus_noc_info *ninfo = |
| 419 | (struct msm_bus_noc_info *)fab_pdata->hw_data; |
| 420 | |
| 421 | *cd = kzalloc(sizeof(struct msm_bus_noc_commit), GFP_KERNEL); |
| 422 | if (!*cd) { |
| 423 | MSM_BUS_DBG("Couldn't alloc mem for cdata\n"); |
| 424 | return -ENOMEM; |
| 425 | } |
| 426 | |
| 427 | (*cd)->mas = ninfo->cdata[ctx].mas; |
| 428 | (*cd)->slv = ninfo->cdata[ctx].slv; |
| 429 | |
| 430 | return 0; |
| 431 | } |
| 432 | |
| 433 | static void *msm_bus_noc_allocate_noc_data(struct platform_device *pdev, |
| 434 | struct msm_bus_fabric_registration *fab_pdata) |
| 435 | { |
| 436 | struct resource *noc_mem; |
| 437 | struct resource *noc_io; |
| 438 | struct msm_bus_noc_info *ninfo; |
| 439 | int i; |
| 440 | |
| 441 | ninfo = kzalloc(sizeof(struct msm_bus_noc_info), GFP_KERNEL); |
| 442 | if (!ninfo) { |
| 443 | MSM_BUS_DBG("Couldn't alloc mem for noc info\n"); |
| 444 | return NULL; |
| 445 | } |
| 446 | |
| 447 | ninfo->nmasters = fab_pdata->nmasters; |
| 448 | ninfo->nqos_masters = fab_pdata->nmasters; |
| 449 | ninfo->nslaves = fab_pdata->nslaves; |
| 450 | ninfo->qos_freq = fab_pdata->qos_freq; |
Girish Mahadevan | b020a5b | 2013-12-06 13:37:06 -0700 | [diff] [blame] | 451 | |
| 452 | if (!fab_pdata->qos_baseoffset) |
| 453 | ninfo->qos_baseoffset = QOS_DEFAULT_BASEOFFSET; |
| 454 | else |
| 455 | ninfo->qos_baseoffset = fab_pdata->qos_baseoffset; |
| 456 | |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 457 | ninfo->mas_modes = kzalloc(sizeof(uint32_t) * fab_pdata->nmasters, |
| 458 | GFP_KERNEL); |
| 459 | if (!ninfo->mas_modes) { |
| 460 | MSM_BUS_DBG("Couldn't alloc mem for noc master-modes\n"); |
| 461 | return NULL; |
| 462 | } |
| 463 | |
| 464 | for (i = 0; i < NUM_CTX; i++) { |
| 465 | ninfo->cdata[i].mas = kzalloc(sizeof(struct |
| 466 | msm_bus_node_hw_info) * fab_pdata->nmasters * 2, |
| 467 | GFP_KERNEL); |
| 468 | if (!ninfo->cdata[i].mas) { |
| 469 | MSM_BUS_DBG("Couldn't alloc mem for noc master-bw\n"); |
| 470 | kfree(ninfo->mas_modes); |
| 471 | kfree(ninfo); |
| 472 | return NULL; |
| 473 | } |
| 474 | |
| 475 | ninfo->cdata[i].slv = kzalloc(sizeof(struct |
| 476 | msm_bus_node_hw_info) * fab_pdata->nslaves * 2, |
| 477 | GFP_KERNEL); |
| 478 | if (!ninfo->cdata[i].slv) { |
| 479 | MSM_BUS_DBG("Couldn't alloc mem for noc master-bw\n"); |
| 480 | kfree(ninfo->cdata[i].mas); |
| 481 | goto err; |
| 482 | } |
| 483 | } |
| 484 | |
| 485 | /* If it's a virtual fabric, don't get memory info */ |
| 486 | if (fab_pdata->virt) |
| 487 | goto skip_mem; |
| 488 | |
| 489 | noc_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 490 | if (!noc_mem && !fab_pdata->virt) { |
| 491 | MSM_BUS_ERR("Cannot get NoC Base address\n"); |
| 492 | goto err; |
| 493 | } |
| 494 | |
| 495 | noc_io = request_mem_region(noc_mem->start, |
| 496 | resource_size(noc_mem), pdev->name); |
| 497 | if (!noc_io) { |
| 498 | MSM_BUS_ERR("NoC memory unavailable\n"); |
| 499 | goto err; |
| 500 | } |
| 501 | |
| 502 | ninfo->base = ioremap(noc_mem->start, resource_size(noc_mem)); |
| 503 | if (!ninfo->base) { |
| 504 | MSM_BUS_ERR("IOremap failed for NoC!\n"); |
| 505 | release_mem_region(noc_mem->start, resource_size(noc_mem)); |
| 506 | goto err; |
| 507 | } |
| 508 | |
| 509 | skip_mem: |
| 510 | fab_pdata->hw_data = (void *)ninfo; |
| 511 | return (void *)ninfo; |
| 512 | |
| 513 | err: |
| 514 | kfree(ninfo->mas_modes); |
| 515 | kfree(ninfo); |
| 516 | return NULL; |
| 517 | } |
| 518 | |
| 519 | static void free_commit_data(void *cdata) |
| 520 | { |
| 521 | struct msm_bus_noc_commit *cd = (struct msm_bus_noc_commit *)cdata; |
| 522 | |
| 523 | kfree(cd->mas); |
| 524 | kfree(cd->slv); |
| 525 | kfree(cd); |
| 526 | } |
| 527 | |
| 528 | static void msm_bus_noc_update_bw(struct msm_bus_inode_info *hop, |
| 529 | struct msm_bus_inode_info *info, |
| 530 | struct msm_bus_fabric_registration *fab_pdata, |
| 531 | void *sel_cdata, int *master_tiers, |
Gagan Mac | b2372ae | 2012-08-20 19:24:32 -0600 | [diff] [blame] | 532 | int64_t add_bw) |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 533 | { |
| 534 | struct msm_bus_noc_info *ninfo; |
| 535 | struct msm_bus_noc_qos_bw qos_bw; |
| 536 | int i, ports; |
Alok Chauhan | ad1730e | 2013-09-18 18:00:20 +0530 | [diff] [blame] | 537 | int64_t bw; |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 538 | struct msm_bus_noc_commit *sel_cd = |
| 539 | (struct msm_bus_noc_commit *)sel_cdata; |
| 540 | |
| 541 | ninfo = (struct msm_bus_noc_info *)fab_pdata->hw_data; |
| 542 | if (!ninfo->qos_freq) { |
| 543 | MSM_BUS_DBG("NOC: No qos frequency to update bw\n"); |
| 544 | return; |
| 545 | } |
| 546 | |
Gagan Mac | b91c407 | 2012-05-09 16:00:31 -0600 | [diff] [blame] | 547 | if (info->node_info->num_mports == 0) { |
| 548 | MSM_BUS_DBG("NOC: Skip Master BW\n"); |
| 549 | goto skip_mas_bw; |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 550 | } |
| 551 | |
| 552 | ports = info->node_info->num_mports; |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 553 | bw = INTERLEAVED_BW(fab_pdata, add_bw, ports); |
| 554 | |
Gagan Mac | b2372ae | 2012-08-20 19:24:32 -0600 | [diff] [blame] | 555 | MSM_BUS_DBG("NOC: Update bw for: %d: %lld\n", |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 556 | info->node_info->priv_id, add_bw); |
| 557 | for (i = 0; i < ports; i++) { |
| 558 | sel_cd->mas[info->node_info->masterp[i]].bw += bw; |
| 559 | sel_cd->mas[info->node_info->masterp[i]].hw_id = |
| 560 | info->node_info->mas_hw_id; |
Gagan Mac | b91c407 | 2012-05-09 16:00:31 -0600 | [diff] [blame] | 561 | MSM_BUS_DBG("NOC: Update mas_bw: ID: %d, BW: %llu ports:%d\n", |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 562 | info->node_info->priv_id, |
| 563 | sel_cd->mas[info->node_info->masterp[i]].bw, |
Gagan Mac | b91c407 | 2012-05-09 16:00:31 -0600 | [diff] [blame] | 564 | ports); |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 565 | /* Check if info is a shared master. |
| 566 | * If it is, mark it dirty |
| 567 | * If it isn't, then set QOS Bandwidth |
| 568 | **/ |
| 569 | if (info->node_info->hw_sel == MSM_BUS_RPM) |
| 570 | sel_cd->mas[info->node_info->masterp[i]].dirty = 1; |
Gagan Mac | b91c407 | 2012-05-09 16:00:31 -0600 | [diff] [blame] | 571 | else { |
| 572 | if (!info->node_info->qport) { |
| 573 | MSM_BUS_DBG("No qos ports to update!\n"); |
| 574 | break; |
| 575 | } |
| 576 | qos_bw.bw = sel_cd->mas[info->node_info->masterp[i]]. |
| 577 | bw; |
| 578 | qos_bw.ws = info->node_info->ws; |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 579 | msm_bus_noc_set_qos_bw(ninfo, |
| 580 | info->node_info->qport[i], |
| 581 | info->node_info->perm_mode, &qos_bw); |
Gagan Mac | b91c407 | 2012-05-09 16:00:31 -0600 | [diff] [blame] | 582 | MSM_BUS_DBG("NOC: QoS: Update mas_bw: ws: %u\n", |
| 583 | qos_bw.ws); |
| 584 | } |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 585 | } |
| 586 | |
Gagan Mac | b91c407 | 2012-05-09 16:00:31 -0600 | [diff] [blame] | 587 | skip_mas_bw: |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 588 | ports = hop->node_info->num_sports; |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 589 | for (i = 0; i < ports; i++) { |
Girish Mahadevan | 32d6179 | 2014-02-03 18:20:48 -0700 | [diff] [blame] | 590 | sel_cd->slv[hop->node_info->slavep[i]].bw += add_bw; |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 591 | sel_cd->slv[hop->node_info->slavep[i]].hw_id = |
| 592 | hop->node_info->slv_hw_id; |
Gagan Mac | b91c407 | 2012-05-09 16:00:31 -0600 | [diff] [blame] | 593 | MSM_BUS_DBG("NOC: Update slave_bw for ID: %d -> %llu\n", |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 594 | hop->node_info->priv_id, |
| 595 | sel_cd->slv[hop->node_info->slavep[i]].bw); |
| 596 | MSM_BUS_DBG("NOC: Update slave_bw for hw_id: %d, index: %d\n", |
| 597 | hop->node_info->slv_hw_id, hop->node_info->slavep[i]); |
| 598 | /* Check if hop is a shared slave. |
| 599 | * If it is, mark it dirty |
| 600 | * If it isn't, then nothing to be done as the |
| 601 | * slaves are in bypass mode. |
| 602 | **/ |
| 603 | if (hop->node_info->hw_sel == MSM_BUS_RPM) |
| 604 | sel_cd->slv[hop->node_info->slavep[i]].dirty = 1; |
| 605 | } |
| 606 | } |
| 607 | |
| 608 | static int msm_bus_noc_commit(struct msm_bus_fabric_registration |
| 609 | *fab_pdata, void *hw_data, void **cdata) |
| 610 | { |
| 611 | MSM_BUS_DBG("\nReached NOC Commit\n"); |
Gagan Mac | b91c407 | 2012-05-09 16:00:31 -0600 | [diff] [blame] | 612 | msm_bus_remote_hw_commit(fab_pdata, hw_data, cdata); |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 613 | return 0; |
| 614 | } |
| 615 | |
| 616 | static int msm_bus_noc_port_halt(uint32_t haltid, uint8_t mport) |
| 617 | { |
| 618 | return 0; |
| 619 | } |
| 620 | |
| 621 | static int msm_bus_noc_port_unhalt(uint32_t haltid, uint8_t mport) |
| 622 | { |
| 623 | return 0; |
| 624 | } |
| 625 | |
| 626 | int msm_bus_noc_hw_init(struct msm_bus_fabric_registration *pdata, |
| 627 | struct msm_bus_hw_algorithm *hw_algo) |
| 628 | { |
| 629 | /* Set interleaving to true by default */ |
| 630 | pdata->il_flag = true; |
| 631 | hw_algo->allocate_commit_data = msm_bus_noc_allocate_commit_data; |
| 632 | hw_algo->allocate_hw_data = msm_bus_noc_allocate_noc_data; |
| 633 | hw_algo->node_init = msm_bus_noc_node_init; |
| 634 | hw_algo->free_commit_data = free_commit_data; |
| 635 | hw_algo->update_bw = msm_bus_noc_update_bw; |
| 636 | hw_algo->commit = msm_bus_noc_commit; |
| 637 | hw_algo->port_halt = msm_bus_noc_port_halt; |
| 638 | hw_algo->port_unhalt = msm_bus_noc_port_unhalt; |
Gagan Mac | 068e84d | 2013-05-28 18:22:33 -0600 | [diff] [blame] | 639 | hw_algo->config_master = NULL; |
Gagan Mac | 8cbbb80 | 2012-02-08 15:35:54 -0700 | [diff] [blame] | 640 | |
| 641 | return 0; |
| 642 | } |
| 643 | |