/**************************************************************************** * * Copyright (c) 2014 - 2018 Samsung Electronics Co., Ltd. All rights reserved * ****************************************************************************/ /* uses */ #include #include #include #include #include #include "scsc_mif_abs.h" /* Implements */ #include "mifsmapper.h" static int mifsmapper_get_num_banks(u8 *phy_map, u8 *log_map, bool large) { u8 i = 0, count = 0; for (i = 0; i < SCSC_MIF_SMAPPER_MAX_BANKS; i++) { if (large && phy_map[i] == SCSC_MIF_ABS_LARGE_BANK) { log_map[count] = i; count++; } else if (!large && phy_map[i] == SCSC_MIF_ABS_SMALL_BANK) { log_map[count] = i; count++; } } return count; } int mifsmapper_init(struct mifsmapper *smapper, struct scsc_mif_abs *mif) { /* TODO: Protect the function if allocations fail */ struct scsc_mif_smapper_info bank_info; u8 i = 0, total_num_banks; u8 phy_map[SCSC_MIF_SMAPPER_MAX_BANKS] = { 0 }; u8 log_map_large[SCSC_MIF_SMAPPER_MAX_BANKS] = { 0 }; u8 log_map_small[SCSC_MIF_SMAPPER_MAX_BANKS] = { 0 }; if (smapper->in_use) return -EBUSY; SCSC_TAG_INFO(MIF, "Init SMAPPER\n"); spin_lock_init(&smapper->lock); /* Get physical mapping of the banks */ if (mif->mif_smapper_get_mapping(mif, phy_map, &smapper->align)) { SCSC_TAG_ERR(MIF, "SMAPPER is not present\n"); return -EINVAL; } smapper->in_use = true; smapper->mif = mif; smapper->num_large_banks = mifsmapper_get_num_banks(phy_map, log_map_large, true); smapper->num_small_banks = mifsmapper_get_num_banks(phy_map, log_map_small, false); total_num_banks = smapper->num_large_banks + smapper->num_small_banks; smapper->bank = kmalloc_array(total_num_banks, sizeof(struct mifsmapper_bank), GFP_KERNEL); smapper->bank_bm_large = kmalloc(BITS_TO_LONGS(smapper->num_large_banks) * sizeof(unsigned long), GFP_KERNEL); bitmap_zero(smapper->bank_bm_large, smapper->num_large_banks); smapper->bank_bm_small = kmalloc(BITS_TO_LONGS(smapper->num_small_banks) * sizeof(unsigned long), GFP_KERNEL); bitmap_zero(smapper->bank_bm_small, smapper->num_small_banks); /* LSB bit of banks will be the large banks the rest will be the small banks */ /* Get large bank info */ for (; i < smapper->num_large_banks; i++) { /* get phy bank */ mif->mif_smapper_get_bank_info(mif, log_map_large[i], &bank_info); smapper->bank[i].entries_bm = kmalloc(BITS_TO_LONGS(bank_info.num_entries) * sizeof(unsigned long), GFP_KERNEL); smapper->bank[i].num_entries = bank_info.num_entries; smapper->bank[i].mem_range_bytes = bank_info.mem_range_bytes; smapper->bank[i].phy_index = log_map_large[i]; SCSC_TAG_INFO(MIF, "phy bank %d mapped to logical %d. Large, entries %d range 0x%x\n", log_map_large[i], i, bank_info.num_entries, bank_info.mem_range_bytes); bitmap_zero(smapper->bank[i].entries_bm, bank_info.num_entries); } /* Get small bank info */ for (; i < total_num_banks; i++) { /* get phy bank */ mif->mif_smapper_get_bank_info(mif, log_map_small[i - smapper->num_large_banks], &bank_info); smapper->bank[i].entries_bm = kmalloc(BITS_TO_LONGS(bank_info.num_entries) * sizeof(unsigned long), GFP_KERNEL); smapper->bank[i].num_entries = bank_info.num_entries; smapper->bank[i].mem_range_bytes = bank_info.mem_range_bytes; smapper->bank[i].phy_index = log_map_small[i - smapper->num_large_banks]; SCSC_TAG_INFO(MIF, "phy bank %d mapped to logical %d. Small, entries %d range 0x%x\n", log_map_small[i - smapper->num_large_banks], i, bank_info.num_entries, bank_info.mem_range_bytes); bitmap_zero(smapper->bank[i].entries_bm, bank_info.num_entries); } return 0; } u16 mifsmapper_get_alignment(struct mifsmapper *smapper) { return smapper->align; } int mifsmapper_alloc_bank(struct mifsmapper *smapper, bool large_bank, u32 entry_size, u16 *entries) { struct mifsmapper_bank *bank; unsigned long *bitmap; u8 max_banks, offset = 0; int which_bit = 0; spin_lock(&smapper->lock); if (!smapper->in_use) goto error; bank = smapper->bank; if (large_bank) { max_banks = smapper->num_large_banks; bitmap = smapper->bank_bm_large; } else { max_banks = smapper->num_small_banks; bitmap = smapper->bank_bm_small; offset = smapper->num_large_banks; } /* Search for free slots */ which_bit = find_first_zero_bit(bitmap, max_banks); if (which_bit >= max_banks) goto error; /* Update bit mask */ set_bit(which_bit, bitmap); /* Retrieve Bank capabilities and return the number of entries available */ /* size must be a power of 2 */ /* TODO : check that granularity is correct */ BUG_ON(!is_power_of_2(entry_size)); /* Clear bank entries */ bitmap_zero(bank[which_bit + offset].entries_bm, bank[which_bit + offset].num_entries); *entries = bank[which_bit + offset].mem_range_bytes/entry_size; /* Saturate */ if (*entries > bank[which_bit + offset].num_entries) *entries = bank[which_bit + offset].num_entries; else if (*entries < bank[which_bit + offset].num_entries) { u16 i; SCSC_TAG_INFO(MIF, "Nominal entries %d reduced to %d\n", bank[which_bit + offset].num_entries, *entries); for (i = *entries; i < bank[which_bit + offset].num_entries; i++) /* Mark the MSB of the bitmap as used */ set_bit(i, bank[which_bit + offset].entries_bm); } /* Update number of entries */ bank[which_bit + offset].num_entries = *entries; bank[which_bit + offset].num_entries_left = *entries; bank[which_bit + offset].in_use = true; bank[which_bit + offset].granularity = entry_size; SCSC_TAG_INFO(MIF, "entries %d bank.num_entries %d large bank %d logical bank %d entries left %d\n", *entries, bank[which_bit + offset].num_entries, large_bank, which_bit + offset, bank[which_bit + offset].num_entries_left); spin_unlock(&smapper->lock); return which_bit + offset; error: SCSC_TAG_ERR(MIF, "Error allocating bank\n"); *entries = 0; spin_unlock(&smapper->lock); return -EIO; } int mifsmapper_free_bank(struct mifsmapper *smapper, u8 bank) { unsigned long *bitmap; u8 max_banks, offset = 0; struct mifsmapper_bank *bank_en; spin_lock(&smapper->lock); if (!smapper->in_use || ((bank >= (smapper->num_large_banks + smapper->num_small_banks)))) goto error; /* check if it is a large or small bank */ if (bank >= smapper->num_large_banks) { max_banks = smapper->num_small_banks; bitmap = smapper->bank_bm_small; offset = bank - smapper->num_large_banks; } else { max_banks = smapper->num_large_banks; bitmap = smapper->bank_bm_large; offset = bank; } /* Update bit mask */ if (!test_and_clear_bit(offset, bitmap)) SCSC_TAG_ERR(MIF, "bank was not allocated\n"); bank_en = smapper->bank; bank_en[bank].in_use = false; spin_unlock(&smapper->lock); return 0; error: SCSC_TAG_ERR(MIF, "Error freeing bank %d\n", bank); spin_unlock(&smapper->lock); return -EIO; } int mifsmapper_get_entries(struct mifsmapper *smapper, u8 bank, u8 num_entries, u8 *entries) { struct mifsmapper_bank *bank_en; unsigned long *bitmap; u32 max_bits, i, ent; if (!smapper->bank) return -EINVAL; bank_en = smapper->bank; if (!bank_en[bank].in_use) { SCSC_TAG_ERR(MIF, "Bank %d not allocated.\n", bank); return -EINVAL; } max_bits = bank_en[bank].num_entries_left; ent = bank_en[bank].num_entries; if (num_entries > max_bits) { SCSC_TAG_ERR(MIF, "Not enough entries. Requested %d, left %d\n", num_entries, max_bits); return -ENOMEM; } bitmap = bank_en[bank].entries_bm; for (i = 0; i < num_entries; i++) { entries[i] = find_first_zero_bit(bitmap, ent); if (entries[i] >= ent) return -EIO; /* Update bit mask */ set_bit(entries[i], bitmap); } smapper->bank[bank].num_entries_left -= num_entries; return 0; } int mifsmapper_free_entries(struct mifsmapper *smapper, u8 bank, u8 num_entries, u8 *entries) { struct mifsmapper_bank *bank_en; unsigned long *bitmap; u32 max_bits, i, ent, total = 0; if (!smapper->bank) return -EINVAL; bank_en = smapper->bank; if (!bank_en[bank].in_use) { SCSC_TAG_ERR(MIF, "Bank %d not allocated.\n", bank); return -EINVAL; } max_bits = bank_en[bank].num_entries_left; ent = bank_en[bank].num_entries; if ((max_bits + num_entries) > ent) { SCSC_TAG_ERR(MIF, "Tried to free more entries. Requested %d, left %d\n", num_entries, max_bits); return -ENOMEM; } bitmap = bank_en[bank].entries_bm; for (i = 0; i < num_entries; i++) { /* Update bit mask */ if (!test_and_clear_bit(entries[i], bitmap)) SCSC_TAG_ERR(MIF, "entry was not allocated\n"); else total++; } smapper->bank[bank].num_entries_left += total; return 0; } void mifsmapper_configure(struct mifsmapper *smapper, u32 granularity) { struct scsc_mif_abs *mif; /* Get abs implementation */ mif = smapper->mif; mif->mif_smapper_configure(mif, granularity); } int mifsmapper_write_sram(struct mifsmapper *smapper, u8 bank, u8 num_entries, u8 first_entry, dma_addr_t *addr) { struct scsc_mif_abs *mif; if (!smapper->bank[bank].in_use) { SCSC_TAG_ERR(MIF, "Bank %d not allocated.\n", bank); return -EINVAL; } /* Get abs implementation */ mif = smapper->mif; /* use the phy address of the bank */ return mif->mif_smapper_write_sram(mif, smapper->bank[bank].phy_index, num_entries, first_entry, addr); } u32 mifsmapper_get_bank_base_address(struct mifsmapper *smapper, u8 bank) { struct scsc_mif_abs *mif; /* Get abs implementation */ mif = smapper->mif; return mif->mif_smapper_get_bank_base_address(mif, bank); } int mifsmapper_deinit(struct mifsmapper *smapper) { u8 i = 0, total_num_banks; spin_lock(&smapper->lock); SCSC_TAG_INFO(MIF, "Deinit SMAPPER\n"); if (!smapper->in_use) { spin_unlock(&smapper->lock); return -ENODEV; } total_num_banks = smapper->num_large_banks + smapper->num_small_banks; for (; i < total_num_banks; i++) { kfree(smapper->bank[i].entries_bm); smapper->bank[i].num_entries = 0; smapper->bank[i].mem_range_bytes = 0; } kfree(smapper->bank_bm_large); kfree(smapper->bank_bm_small); kfree(smapper->bank); smapper->in_use = false; spin_unlock(&smapper->lock); return 0; }