/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #define pr_fmt(fmt) "AXI: %s(): " fmt, __func__ #include #include #include #include #include #include #include #include #include "msm_bus_core.h" #include "../rpm_resources.h" void msm_bus_rpm_set_mt_mask() { #ifdef CONFIG_MSM_BUS_RPM_MULTI_TIER_ENABLED struct msm_rpm_iv_pair mt[1]; int mask = MSM_RPMRS_MASK_RPM_CTL_MULTI_TIER; mt[0].id = MSM_RPM_ID_RPM_CTL; mt[0].value = 2; msm_rpmrs_set_bits_noirq(MSM_RPM_CTX_SET_0, mt, 1, &mask); #endif } bool msm_bus_rpm_is_mem_interleaved(void) { int status = 0; struct msm_rpm_iv_pair il[2]; uint16_t id[2]; il[0].value = 0; il[1].value = 0; status = msm_bus_board_rpm_get_il_ids(id); if (status) { MSM_BUS_DBG("Dynamic check not supported, " "default: Interleaved memory\n"); goto inter; } il[0].id = id[0]; il[1].id = id[1]; status = msm_rpm_get_status(il, ARRAY_SIZE(il)); if (status) { MSM_BUS_ERR("Status read for interleaving returned: %d\n" "Using interleaved memory by default\n", status); goto inter; } /* * If the start address of EBI1-CH0 is the same as * the start address of EBI1-CH1, the memory is interleaved. * The start addresses are stored in the 16 MSBs of the status * register */ if ((il[0].value & 0xFFFF0000) != (il[1].value & 0xFFFF0000)) { MSM_BUS_DBG("Non-interleaved memory\n"); return false; } inter: MSM_BUS_DBG("Interleaved memory\n"); return true; } #ifndef CONFIG_MSM_BUS_RPM_MULTI_TIER_ENABLED struct commit_data { uint16_t *bwsum; uint16_t *arb; unsigned long *actarb; }; /* * The following macros are used for various operations on commit data. * Commit data is an array of 32 bit integers. The size of arrays is unique * to the fabric. Commit arrays are allocated at run-time based on the number * of masters, slaves and tiered-slaves registered. */ #define MSM_BUS_GET_BW_INFO(val, type, bw) \ do { \ (type) = MSM_BUS_GET_BW_TYPE(val); \ (bw) = MSM_BUS_GET_BW(val); \ } while (0) #define MSM_BUS_GET_BW_INFO_BYTES (val, type, bw) \ do { \ (type) = MSM_BUS_GET_BW_TYPE(val); \ (bw) = msm_bus_get_bw_bytes(val); \ } while (0) #define ROUNDED_BW_VAL_FROM_BYTES(bw) \ ((((bw) >> 17) + 1) & 0x8000 ? 0x7FFF : (((bw) >> 17) + 1)) #define BW_VAL_FROM_BYTES(bw) \ ((((bw) >> 17) & 0x8000) ? 0x7FFF : ((bw) >> 17)) static uint32_t msm_bus_set_bw_bytes(unsigned long bw) { return ((((bw) & 0x1FFFF) && (((bw) >> 17) == 0)) ? ROUNDED_BW_VAL_FROM_BYTES(bw) : BW_VAL_FROM_BYTES(bw)); } uint64_t msm_bus_get_bw_bytes(unsigned long val) { return ((val) & 0x7FFF) << 17; } uint16_t msm_bus_get_bw(unsigned long val) { return (val)&0x7FFF; } static uint16_t msm_bus_create_bw_tier_pair_bytes(uint8_t type, unsigned long bw) { return ((((type) == MSM_BUS_BW_TIER1 ? 1 : 0) << 15) | (msm_bus_set_bw_bytes(bw))); }; uint16_t msm_bus_create_bw_tier_pair(uint8_t type, unsigned long bw) { return (((type) == MSM_BUS_BW_TIER1 ? 1 : 0) << 15) | ((bw) & 0x7FFF); } void msm_bus_rpm_fill_cdata_buffer(int *curr, char *buf, const int max_size, void *cdata, int nmasters, int nslaves, int ntslaves) { int j, c; struct commit_data *cd = (struct commit_data *)cdata; *curr += scnprintf(buf + *curr, max_size - *curr, "BWSum:\n"); for (c = 0; c < nslaves; c++) *curr += scnprintf(buf + *curr, max_size - *curr, "0x%x\t", cd->bwsum[c]); *curr += scnprintf(buf + *curr, max_size - *curr, "\nArb:"); for (c = 0; c < ntslaves; c++) { *curr += scnprintf(buf + *curr, max_size - *curr, "\nTSlave %d:\n", c); for (j = 0; j < nmasters; j++) *curr += scnprintf(buf + *curr, max_size - *curr, " 0x%x\t", cd->arb[(c * nmasters) + j]); } } /** * allocate_commit_data() - Allocate the data for commit array in the * format specified by RPM * @fabric: Fabric device for which commit data is allocated */ static int msm_bus_rpm_allocate_commit_data(struct msm_bus_fabric_registration *fab_pdata, void **cdata, int ctx) { struct commit_data **cd = (struct commit_data **)cdata; *cd = kzalloc(sizeof(struct commit_data), GFP_KERNEL); if (!*cd) { MSM_BUS_DBG("Couldn't alloc mem for cdata\n"); return -ENOMEM; } (*cd)->bwsum = kzalloc((sizeof(uint16_t) * fab_pdata->nslaves), GFP_KERNEL); if (!(*cd)->bwsum) { MSM_BUS_DBG("Couldn't alloc mem for slaves\n"); kfree(*cd); return -ENOMEM; } (*cd)->arb = kzalloc(((sizeof(uint16_t *)) * (fab_pdata->ntieredslaves * fab_pdata->nmasters) + 1), GFP_KERNEL); if (!(*cd)->arb) { MSM_BUS_DBG("Couldn't alloc memory for" " slaves\n"); kfree((*cd)->bwsum); kfree(*cd); return -ENOMEM; } (*cd)->actarb = kzalloc(((sizeof(unsigned long *)) * (fab_pdata->ntieredslaves * fab_pdata->nmasters) + 1), GFP_KERNEL); if (!(*cd)->actarb) { MSM_BUS_DBG("Couldn't alloc memory for" " slaves\n"); kfree((*cd)->bwsum); kfree((*cd)->arb); kfree(*cd); return -ENOMEM; } return 0; } static void free_commit_data(void *cdata) { struct commit_data *cd = (struct commit_data *)cdata; kfree(cd->bwsum); kfree(cd->arb); kfree(cd->actarb); kfree(cd); } /** * allocate_rpm_data() - Allocate the id-value pairs to be * sent to RPM */ static void *msm_bus_rpm_allocate_rpm_data(struct platform_device *pdev, struct msm_bus_fabric_registration *fab_pdata) { struct msm_rpm_iv_pair *rpm_data; uint16_t count = ((fab_pdata->nmasters * fab_pdata->ntieredslaves) + fab_pdata->nslaves + 1)/2; rpm_data = kmalloc((sizeof(struct msm_rpm_iv_pair) * count), GFP_KERNEL); return (void *)rpm_data; } #define BWMASK 0x7FFF #define TIERMASK 0x8000 #define GET_TIER(n) (((n) & TIERMASK) >> 15) static void msm_bus_rpm_update_bw(struct msm_bus_inode_info *hop, struct msm_bus_inode_info *info, struct msm_bus_fabric_registration *fab_pdata, void *sel_cdata, int *master_tiers, int64_t add_bw) { int index, i, j, tiers, ports; struct commit_data *sel_cd = (struct commit_data *)sel_cdata; add_bw = INTERLEAVED_BW(fab_pdata, add_bw, info->node_info->num_mports); ports = INTERLEAVED_VAL(fab_pdata, info->node_info->num_mports); tiers = INTERLEAVED_VAL(fab_pdata, hop->node_info->num_tiers); for (i = 0; i < tiers; i++) { for (j = 0; j < ports; j++) { uint16_t hop_tier; /* * For interleaved gateway ports and slave ports, * there is one-one mapping between gateway port and * the slave port */ if (info->node_info->gateway && i != j && (hop->node_info->num_sports > 1)) continue; if (!hop->node_info->tier) hop_tier = MSM_BUS_BW_TIER2 - 1; else hop_tier = hop->node_info->tier[i] - 1; index = ((hop_tier * fab_pdata->nmasters) + (info->node_info->masterp[j])); /* If there is tier, calculate arb for commit */ if (hop->node_info->tier) { uint16_t tier; unsigned long tieredbw = sel_cd->actarb[index]; if (GET_TIER(sel_cd->arb[index])) tier = MSM_BUS_BW_TIER1; else if (master_tiers) /* * By default master is only in the * tier specified by default. * To change the default tier, client * needs to explicitly request for a * different supported tier */ tier = master_tiers[0]; else tier = MSM_BUS_BW_TIER2; /* * Make sure gateway to slave port bandwidth * is not divided when slave is interleaved */ if (info->node_info->gateway && hop->node_info->num_sports > 1) tieredbw += add_bw; else tieredbw += INTERLEAVED_BW(fab_pdata, add_bw, hop->node_info-> num_sports); /* If bw is 0, update tier to default */ if (!tieredbw) tier = MSM_BUS_BW_TIER2; /* Update Arb for fab,get HW Mport from enum */ sel_cd->arb[index] = msm_bus_create_bw_tier_pair_bytes(tier, tieredbw); sel_cd->actarb[index] = tieredbw; MSM_BUS_DBG("tr:%d mpor:%d tbw:%ld bws: %lld\n", hop_tier, info->node_info->masterp[i], tieredbw, *hop->link_info.sel_bw); } } } /* Update bwsum for slaves on fabric */ ports = INTERLEAVED_VAL(fab_pdata, hop->node_info->num_sports); for (i = 0; i < ports; i++) { sel_cd->bwsum[hop->node_info->slavep[i]] = (uint16_t)msm_bus_create_bw_tier_pair_bytes(0, (uint32_t)msm_bus_div64(hop->node_info->num_sports, *hop->link_info.sel_bw)); MSM_BUS_DBG("slavep:%d, link_bw: %u\n", hop->node_info->slavep[i], (uint32_t) msm_bus_div64(hop->node_info->num_sports, *hop->link_info.sel_bw)); } } #define RPM_SHIFT_VAL 16 #define RPM_SHIFT(n) ((n) << RPM_SHIFT_VAL) static int msm_bus_rpm_compare_cdata( struct msm_bus_fabric_registration *fab_pdata, struct commit_data *cd1, struct commit_data *cd2) { size_t n; int ret; n = sizeof(uint16_t) * fab_pdata->nslaves; ret = memcmp(cd1->bwsum, cd2->bwsum, n); if (ret) { MSM_BUS_DBG("Commit Data bwsum not equal\n"); return ret; } n = sizeof(uint16_t *) * ((fab_pdata->ntieredslaves * fab_pdata->nmasters) + 1); ret = memcmp(cd1->arb, cd2->arb, n); if (ret) { MSM_BUS_DBG("Commit Data arb[%d] not equal\n", n); return ret; } return 0; } static int msm_bus_rpm_commit_arb(struct msm_bus_fabric_registration *fab_pdata, int ctx, struct msm_rpm_iv_pair *rpm_data, struct commit_data *cd, bool valid) { int i, j, offset = 0, status = 0, count, index = 0; /* * count is the number of 2-byte words required to commit the * data to rpm. This is calculated by the following formula. * Commit data is split into two arrays: * 1. arb[nmasters * ntieredslaves] * 2. bwsum[nslaves] */ count = ((fab_pdata->nmasters * fab_pdata->ntieredslaves) + (fab_pdata->nslaves) + 1)/2; offset = fab_pdata->offset; /* * Copy bwsum to rpm data * Since bwsum is uint16, the values need to be adjusted to * be copied to value field of rpm-data, which is 32 bits. */ for (i = 0; i < (fab_pdata->nslaves - 1); i += 2) { rpm_data[index].id = offset + index; rpm_data[index].value = RPM_SHIFT(*(cd->bwsum + i + 1)) | *(cd->bwsum + i); index++; } /* Account for odd number of slaves */ if (fab_pdata->nslaves & 1) { rpm_data[index].id = offset + index; rpm_data[index].value = *(cd->arb); rpm_data[index].value = RPM_SHIFT(rpm_data[index].value) | *(cd->bwsum + i); index++; i = 1; } else i = 0; /* Copy arb values to rpm data */ for (; i < (fab_pdata->ntieredslaves * fab_pdata->nmasters); i += 2) { rpm_data[index].id = offset + index; rpm_data[index].value = RPM_SHIFT(*(cd->arb + i + 1)) | *(cd->arb + i); index++; } MSM_BUS_DBG("rpm data for fab: %d\n", fab_pdata->id); for (i = 0; i < count; i++) MSM_BUS_DBG("%d %x\n", rpm_data[i].id, rpm_data[i].value); MSM_BUS_DBG("Commit Data: Fab: %d BWSum:\n", fab_pdata->id); for (i = 0; i < fab_pdata->nslaves; i++) MSM_BUS_DBG("fab_slaves:0x%x\n", cd->bwsum[i]); MSM_BUS_DBG("Commit Data: Fab: %d Arb:\n", fab_pdata->id); for (i = 0; i < fab_pdata->ntieredslaves; i++) { MSM_BUS_DBG("tiered-slave: %d\n", i); for (j = 0; j < fab_pdata->nmasters; j++) MSM_BUS_DBG(" 0x%x\n", cd->arb[(i * fab_pdata->nmasters) + j]); } MSM_BUS_DBG("calling msm_rpm_set: %d\n", status); msm_bus_dbg_commit_data(fab_pdata->name, cd, fab_pdata-> nmasters, fab_pdata->nslaves, fab_pdata->ntieredslaves, MSM_BUS_DBG_OP); if (fab_pdata->rpm_enabled) { if (valid) { if (ctx == ACTIVE_CTX) { status = msm_rpm_set(MSM_RPM_CTX_SET_0, rpm_data, count); MSM_BUS_DBG("msm_rpm_set returned: %d\n", status); } else if (ctx == DUAL_CTX) { status = msm_rpm_set(MSM_RPM_CTX_SET_SLEEP, rpm_data, count); MSM_BUS_DBG("msm_rpm_set returned: %d\n", status); } } else { if (ctx == ACTIVE_CTX) { status = msm_rpm_clear(MSM_RPM_CTX_SET_0, rpm_data, count); MSM_BUS_DBG("msm_rpm_clear returned: %d\n", status); } else if (ctx == DUAL_CTX) { status = msm_rpm_clear(MSM_RPM_CTX_SET_SLEEP, rpm_data, count); MSM_BUS_DBG("msm_rpm_clear returned: %d\n", status); } } } return status; } #else #define NUM_TIERS 2 #define RPM_SHIFT24(n) ((n) << 24) #define RPM_SHIFT16(n) ((n) << 16) #define RPM_SHIFT8(n) ((n) << 8) struct commit_data { uint16_t *bwsum; uint8_t *arb[NUM_TIERS]; unsigned long *actarb[NUM_TIERS]; }; #define MODE_BIT(val) ((val) & 0x80) #define MODE0_IMM(val) ((val) & 0xF) #define MODE0_SHIFT(val) (((val) & 0x70) >> 4) #define MODE1_STEP 48 /* 48 MB */ #define MODE1_OFFSET 512 /* 512 MB */ #define MODE1_IMM(val) ((val) & 0x7F) #define __CLZ(x) ((8 * sizeof(uint32_t)) - 1 - __fls(x)) static uint8_t msm_bus_set_bw_bytes(unsigned long val) { unsigned int shift; unsigned int intVal; unsigned char result; /* Convert to MB */ intVal = (unsigned int)((val + ((1 << 20) - 1)) >> 20); /** * Divide by 2^20 and round up * A value graeter than 0x1E0 will round up to 512 and overflow * Mode 0 so it should be made Mode 1 */ if (0x1E0 > intVal) { /** * MODE 0 * Compute the shift value * Shift value is 32 - the number of leading zeroes - * 4 to save the most significant 4 bits of the value */ shift = 32 - 4 - min((uint8_t)28, (uint8_t)__CLZ(intVal)); /* Add min value - 1 to force a round up when shifting right */ intVal += (1 << shift) - 1; /* Recompute the shift value in case there was an overflow */ shift = 32 - 4 - min((uint8_t)28, (uint8_t)__CLZ(intVal)); /* Clear the mode bit (msb) and fill in the fields */ result = ((0x70 & (shift << 4)) | (0x0F & (intVal >> shift))); } else { /* MODE 1 */ result = (unsigned char)(0x80 | ((intVal - MODE1_OFFSET + MODE1_STEP - 1) / MODE1_STEP)); } return result; } uint64_t msm_bus_get_bw(unsigned long val) { return MODE_BIT(val) ? /* Mode 1 */ (MODE1_IMM(val) * MODE1_STEP + MODE1_OFFSET) : /* Mode 0 */ (MODE0_IMM(val) << MODE0_SHIFT(val)); } uint64_t msm_bus_get_bw_bytes(unsigned long val) { return msm_bus_get_bw(val) << 20; } static uint8_t msm_bus_create_bw_tier_pair_bytes(uint8_t type, unsigned long bw) { return msm_bus_set_bw_bytes(bw); }; uint8_t msm_bus_create_bw_tier_pair(uint8_t type, unsigned long bw) { return msm_bus_create_bw_tier_pair_bytes(type, bw); }; static int msm_bus_rpm_allocate_commit_data(struct msm_bus_fabric_registration *fab_pdata, void **cdata, int ctx) { struct commit_data **cd = (struct commit_data **)cdata; int i; *cd = kzalloc(sizeof(struct commit_data), GFP_KERNEL); if (!*cd) { MSM_BUS_DBG("Couldn't alloc mem for cdata\n"); goto cdata_err; } (*cd)->bwsum = kzalloc((sizeof(uint16_t) * fab_pdata->nslaves), GFP_KERNEL); if (!(*cd)->bwsum) { MSM_BUS_DBG("Couldn't alloc mem for slaves\n"); goto bwsum_err; } for (i = 0; i < NUM_TIERS; i++) { (*cd)->arb[i] = kzalloc(((sizeof(uint8_t *)) * (fab_pdata->ntieredslaves * fab_pdata->nmasters) + 1), GFP_KERNEL); if (!(*cd)->arb[i]) { MSM_BUS_DBG("Couldn't alloc memory for" " slaves\n"); goto arb_err; } (*cd)->actarb[i] = kzalloc(((sizeof(unsigned long *)) * (fab_pdata->ntieredslaves * fab_pdata->nmasters) + 1), GFP_KERNEL); if (!(*cd)->actarb[i]) { MSM_BUS_DBG("Couldn't alloc memory for" " slaves\n"); kfree((*cd)->arb[i]); goto arb_err; } } return 0; arb_err: for (i = i - 1; i >= 0; i--) { kfree((*cd)->arb[i]); kfree((*cd)->actarb[i]); } bwsum_err: kfree((*cd)->bwsum); cdata_err: kfree(*cd); return -ENOMEM; } static void free_commit_data(void *cdata) { int i; struct commit_data *cd = (struct commit_data *)cdata; kfree(cd->bwsum); for (i = 0; i < NUM_TIERS; i++) { kfree(cd->arb[i]); kfree(cd->actarb[i]); } kfree(cd); } static void *msm_bus_rpm_allocate_rpm_data(struct platform_device *pdev, struct msm_bus_fabric_registration *fab_pdata) { struct msm_rpm_iv_pair *rpm_data; uint16_t count = (((fab_pdata->nmasters * fab_pdata->ntieredslaves * NUM_TIERS)/2) + fab_pdata->nslaves + 1)/2; rpm_data = kmalloc((sizeof(struct msm_rpm_iv_pair) * count), GFP_KERNEL); return (void *)rpm_data; } static int msm_bus_rpm_compare_cdata( struct msm_bus_fabric_registration *fab_pdata, struct commit_data *cd1, struct commit_data *cd2) { size_t n; int i, ret; n = sizeof(uint16_t) * fab_pdata->nslaves; ret = memcmp(cd1->bwsum, cd2->bwsum, n); if (ret) { MSM_BUS_DBG("Commit Data bwsum not equal\n"); return ret; } n = sizeof(uint8_t *) * ((fab_pdata->ntieredslaves * fab_pdata->nmasters) + 1); for (i = 0; i < NUM_TIERS; i++) { ret = memcmp(cd1->arb[i], cd2->arb[i], n); if (ret) { MSM_BUS_DBG("Commit Data arb[%d] not equal\n", i); return ret; } } return 0; } static int msm_bus_rpm_commit_arb(struct msm_bus_fabric_registration *fab_pdata, int ctx, struct msm_rpm_iv_pair *rpm_data, struct commit_data *cd, bool valid) { int i, j, k, offset = 0, status = 0, count, index = 0; /* * count is the number of 2-byte words required to commit the * data to rpm. This is calculated by the following formula. * Commit data is split into two arrays: * 1. arb[nmasters * ntieredslaves][num_tiers] * 2. bwsum[nslaves] */ count = (((fab_pdata->nmasters * fab_pdata->ntieredslaves * NUM_TIERS) /2) + fab_pdata->nslaves + 1)/2; offset = fab_pdata->offset; /* * Copy bwsum to rpm data * Since bwsum is uint16, the values need to be adjusted to * be copied to value field of rpm-data, which is 32 bits. */ for (i = 0; i < (fab_pdata->nslaves - 1); i += 2) { rpm_data[index].id = offset + index; rpm_data[index].value = RPM_SHIFT16(*(cd->bwsum + i + 1)) | *(cd->bwsum + i); index++; } /* Account for odd number of slaves */ if (fab_pdata->nslaves & 1) { rpm_data[index].id = offset + index; rpm_data[index].value = RPM_SHIFT8(*cd->arb[1]) | *(cd->arb[0]); rpm_data[index].value = RPM_SHIFT16(rpm_data[index].value) | *(cd->bwsum + i); index++; i = 1; } else i = 0; /* Copy arb values to rpm data */ for (; i < (fab_pdata->ntieredslaves * fab_pdata->nmasters); i += 2) { uint16_t tv1, tv0; rpm_data[index].id = offset + index; tv0 = RPM_SHIFT8(*(cd->arb[1] + i)) | (*(cd->arb[0] + i)); tv1 = RPM_SHIFT8(*(cd->arb[1] + i + 1)) | (*(cd->arb[0] + i + 1)); rpm_data[index].value = RPM_SHIFT16(tv1) | tv0; index++; } MSM_BUS_DBG("rpm data for fab: %d\n", fab_pdata->id); for (i = 0; i < count; i++) MSM_BUS_DBG("%d %x\n", rpm_data[i].id, rpm_data[i].value); MSM_BUS_DBG("Commit Data: Fab: %d BWSum:\n", fab_pdata->id); for (i = 0; i < fab_pdata->nslaves; i++) MSM_BUS_DBG("fab_slaves:0x%x\n", cd->bwsum[i]); MSM_BUS_DBG("Commit Data: Fab: %d Arb:\n", fab_pdata->id); for (k = 0; k < NUM_TIERS; k++) { MSM_BUS_DBG("Tier: %d\n", k); for (i = 0; i < fab_pdata->ntieredslaves; i++) { MSM_BUS_DBG("tiered-slave: %d\n", i); for (j = 0; j < fab_pdata->nmasters; j++) MSM_BUS_DBG(" 0x%x\n", cd->arb[k][(i * fab_pdata->nmasters) + j]); } } MSM_BUS_DBG("calling msm_rpm_set: %d\n", status); msm_bus_dbg_commit_data(fab_pdata->name, (void *)cd, fab_pdata-> nmasters, fab_pdata->nslaves, fab_pdata->ntieredslaves, MSM_BUS_DBG_OP); if (fab_pdata->rpm_enabled) { if (valid) { if (ctx == ACTIVE_CTX) { status = msm_rpm_set(MSM_RPM_CTX_SET_0, rpm_data, count); MSM_BUS_DBG("msm_rpm_set returned: %d\n", status); } else if (ctx == DUAL_CTX) { status = msm_rpm_set(MSM_RPM_CTX_SET_SLEEP, rpm_data, count); MSM_BUS_DBG("msm_rpm_set returned: %d\n", status); } } else { if (ctx == ACTIVE_CTX) { status = msm_rpm_clear(MSM_RPM_CTX_SET_0, rpm_data, count); MSM_BUS_DBG("msm_rpm_clear returned: %d\n", status); } else if (ctx == DUAL_CTX) { status = msm_rpm_clear(MSM_RPM_CTX_SET_SLEEP, rpm_data, count); MSM_BUS_DBG("msm_rpm_clear returned: %d\n", status); } } } return status; } #define FORMAT_BW(x) \ ((x < 0) ? \ -(msm_bus_get_bw_bytes(msm_bus_create_bw_tier_pair_bytes(0, -(x)))) : \ (msm_bus_get_bw_bytes(msm_bus_create_bw_tier_pair_bytes(0, x)))) static uint16_t msm_bus_pack_bwsum_bytes(unsigned long bw) { return (bw + ((1 << 20) - 1)) >> 20; }; static void msm_bus_rpm_update_bw(struct msm_bus_inode_info *hop, struct msm_bus_inode_info *info, struct msm_bus_fabric_registration *fab_pdata, void *sel_cdata, int *master_tiers, int64_t add_bw) { int index, i, j, tiers, ports; struct commit_data *sel_cd = (struct commit_data *)sel_cdata; add_bw = INTERLEAVED_BW(fab_pdata, add_bw, info->node_info->num_mports); ports = INTERLEAVED_VAL(fab_pdata, info->node_info->num_mports); tiers = INTERLEAVED_VAL(fab_pdata, hop->node_info->num_tiers); for (i = 0; i < tiers; i++) { for (j = 0; j < ports; j++) { uint16_t hop_tier; /* * For interleaved gateway ports and slave ports, * there is one-one mapping between gateway port and * the slave port */ if (info->node_info->gateway && i != j && hop->node_info->num_sports > 1) continue; if (!hop->node_info->tier) hop_tier = MSM_BUS_BW_TIER2 - 1; else hop_tier = hop->node_info->tier[i] - 1; index = ((hop_tier * fab_pdata->nmasters) + (info->node_info->masterp[j])); /* If there is tier, calculate arb for commit */ if (hop->node_info->tier) { uint16_t tier; unsigned long tieredbw; if (master_tiers) tier = master_tiers[0] - 1; else tier = MSM_BUS_BW_TIER2 - 1; tieredbw = sel_cd->actarb[tier][index]; /* * Make sure gateway to slave port bandwidth * is not divided when slave is interleaved */ if (info->node_info->gateway && hop->node_info->num_sports > 1) tieredbw += add_bw; else tieredbw += INTERLEAVED_BW(fab_pdata, add_bw, hop->node_info-> num_sports); /* Update Arb for fab,get HW Mport from enum */ sel_cd->arb[tier][index] = msm_bus_create_bw_tier_pair_bytes(0, tieredbw); sel_cd->actarb[tier][index] = tieredbw; MSM_BUS_DBG("tr:%d mpor:%d tbw:%lu bws: %lld\n", hop_tier, info->node_info->masterp[i], tieredbw, *hop->link_info.sel_bw); } } } /* Update bwsum for slaves on fabric */ ports = INTERLEAVED_VAL(fab_pdata, hop->node_info->num_sports); for (i = 0; i < ports; i++) { sel_cd->bwsum[hop->node_info->slavep[i]] = msm_bus_pack_bwsum_bytes((uint32_t) msm_bus_div64(hop->node_info->num_sports, *hop->link_info.sel_bw)); MSM_BUS_DBG("slavep:%d, link_bw: %lld\n", hop->node_info->slavep[i], msm_bus_div64(hop->node_info->num_sports, *hop->link_info.sel_bw)); } } void msm_bus_rpm_fill_cdata_buffer(int *curr, char *buf, const int max_size, void *cdata, int nmasters, int nslaves, int ntslaves) { int j, k, c; struct commit_data *cd = (struct commit_data *)cdata; *curr += scnprintf(buf + *curr, max_size - *curr, "BWSum:\n"); for (c = 0; c < nslaves; c++) *curr += scnprintf(buf + *curr, max_size - *curr, "0x%x\t", cd->bwsum[c]); *curr += scnprintf(buf + *curr, max_size - *curr, "\nArb:"); for (k = 0; k < NUM_TIERS; k++) { *curr += scnprintf(buf + *curr, max_size - *curr, "\nTier %d:\n", k); for (c = 0; c < ntslaves; c++) { *curr += scnprintf(buf + *curr, max_size - *curr, "TSlave %d:\n", c); for (j = 0; j < nmasters; j++) *curr += scnprintf(buf + *curr, max_size - *curr, " 0x%x\t", cd->arb[k][(c * nmasters) + j]); } } } #endif /** * msm_bus_rpm_commit() - Commit the arbitration data to RPM * @fabric: Fabric for which the data should be committed **/ static int msm_bus_rpm_commit(struct msm_bus_fabric_registration *fab_pdata, void *hw_data, void **cdata) { int ret; bool valid; struct commit_data *dual_cd, *act_cd; struct msm_rpm_iv_pair *rpm_data = (struct msm_rpm_iv_pair *)hw_data; dual_cd = (struct commit_data *)cdata[DUAL_CTX]; act_cd = (struct commit_data *)cdata[ACTIVE_CTX]; /* * If the arb data for active set and sleep set is * different, commit both sets. * If the arb data for active set and sleep set is * the same, invalidate the sleep set. */ ret = msm_bus_rpm_compare_cdata(fab_pdata, act_cd, dual_cd); if (!ret) /* Invalidate sleep set.*/ valid = false; else valid = true; ret = msm_bus_rpm_commit_arb(fab_pdata, DUAL_CTX, rpm_data, dual_cd, valid); if (ret) MSM_BUS_ERR("Error comiting fabric:%d in %d ctx\n", fab_pdata->id, DUAL_CTX); valid = true; ret = msm_bus_rpm_commit_arb(fab_pdata, ACTIVE_CTX, rpm_data, act_cd, valid); if (ret) MSM_BUS_ERR("Error comiting fabric:%d in %d ctx\n", fab_pdata->id, ACTIVE_CTX); return ret; } static int msm_bus_rpm_port_halt(uint32_t haltid, uint8_t mport) { int status = 0; struct msm_bus_halt_vector hvector = {0, 0}; struct msm_rpm_iv_pair rpm_data[2]; MSM_BUS_MASTER_HALT(hvector.haltmask, hvector.haltval, mport); rpm_data[0].id = haltid; rpm_data[0].value = hvector.haltval; rpm_data[1].id = haltid + 1; rpm_data[1].value = hvector.haltmask; MSM_BUS_DBG("ctx: %d, id: %d, value: %d\n", MSM_RPM_CTX_SET_0, rpm_data[0].id, rpm_data[0].value); MSM_BUS_DBG("ctx: %d, id: %d, value: %d\n", MSM_RPM_CTX_SET_0, rpm_data[1].id, rpm_data[1].value); status = msm_rpm_set(MSM_RPM_CTX_SET_0, rpm_data, 2); if (status) MSM_BUS_DBG("msm_rpm_set returned: %d\n", status); return status; } static int msm_bus_rpm_port_unhalt(uint32_t haltid, uint8_t mport) { int status = 0; struct msm_bus_halt_vector hvector = {0, 0}; struct msm_rpm_iv_pair rpm_data[2]; MSM_BUS_MASTER_UNHALT(hvector.haltmask, hvector.haltval, mport); rpm_data[0].id = haltid; rpm_data[0].value = hvector.haltval; rpm_data[1].id = haltid + 1; rpm_data[1].value = hvector.haltmask; MSM_BUS_DBG("unalt: ctx: %d, id: %d, value: %d\n", MSM_RPM_CTX_SET_SLEEP, rpm_data[0].id, rpm_data[0].value); MSM_BUS_DBG("unhalt: ctx: %d, id: %d, value: %d\n", MSM_RPM_CTX_SET_SLEEP, rpm_data[1].id, rpm_data[1].value); status = msm_rpm_set(MSM_RPM_CTX_SET_0, rpm_data, 2); if (status) MSM_BUS_DBG("msm_rpm_set returned: %d\n", status); return status; } int msm_bus_remote_hw_commit(struct msm_bus_fabric_registration *fab_pdata, void *hw_data, void **cdata) { return 0; } int msm_bus_rpm_hw_init(struct msm_bus_fabric_registration *pdata, struct msm_bus_hw_algorithm *hw_algo) { pdata->il_flag = msm_bus_rpm_is_mem_interleaved(); hw_algo->allocate_commit_data = msm_bus_rpm_allocate_commit_data; hw_algo->allocate_hw_data = msm_bus_rpm_allocate_rpm_data; hw_algo->node_init = NULL; hw_algo->free_commit_data = free_commit_data; hw_algo->update_bw = msm_bus_rpm_update_bw; hw_algo->commit = msm_bus_rpm_commit; hw_algo->port_halt = msm_bus_rpm_port_halt; hw_algo->port_unhalt = msm_bus_rpm_port_unhalt; if (!pdata->ahb) pdata->rpm_enabled = 1; return 0; }