lineage_kernel_xcoverpro/drivers/misc/samsung/scsc/mifsmapper.c

361 lines
10 KiB
C
Raw Normal View History

2023-06-18 22:53:49 +00:00
/****************************************************************************
*
* Copyright (c) 2014 - 2018 Samsung Electronics Co., Ltd. All rights reserved
*
****************************************************************************/
/* uses */
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <scsc/scsc_logring.h>
#include <linux/bitmap.h>
#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;
}