blob: ff61cc8a56bd8bf70ea8df634b8e2fca0d201ce1 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2023 MediaTek Inc. All Rights Reserved.
*
* Author: Frank-zj Lin <frank-zj.lin@mediatek.com>
*/
#include <linux/hashtable.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <net/pptp.h>
#include "tops/hwspinlock.h"
#include "tops/seq_gen.h"
#define PPTP_SEQ_HT_BITS 4
struct pptp_seq {
struct hlist_node hlist;
int seq_gen_idx;
uint16_t call_id;
};
static DEFINE_HASHTABLE(pptp_seq_ht, PPTP_SEQ_HT_BITS);
static DEFINE_SPINLOCK(pptp_seq_ht_lock);
static struct pptp_seq *mtk_tops_pptp_seq_find_no_lock(uint16_t call_id)
{
struct pptp_seq *pptp_seq;
hash_for_each_possible(pptp_seq_ht, pptp_seq, hlist, call_id) {
if (pptp_seq->call_id == call_id)
return pptp_seq;
}
return ERR_PTR(-ENODEV);
}
static int mtk_tops_pptp_seq_alloc_no_lock(uint16_t call_id, uint32_t seq_start,
int *seq_gen_idx)
{
struct pptp_seq *pptp_seq;
int ret;
if (!IS_ERR(mtk_tops_pptp_seq_find_no_lock(call_id)))
return -EBUSY;
ret = mtk_tops_seq_gen_alloc(seq_gen_idx);
if (ret)
return ret;
pptp_seq = kzalloc(sizeof(struct pptp_seq), GFP_KERNEL);
if (!pptp_seq) {
mtk_tops_seq_gen_free(*seq_gen_idx);
return -ENOMEM;
}
pptp_seq->seq_gen_idx = *seq_gen_idx;
pptp_seq->call_id = call_id;
hash_add(pptp_seq_ht, &pptp_seq->hlist, pptp_seq->call_id);
mtk_tops_seq_gen_set_32(*seq_gen_idx, seq_start);
return 0;
}
int mtk_tops_pptp_seq_alloc(uint16_t call_id, uint32_t seq_start,
int *seq_gen_idx)
{
unsigned long flag;
int ret;
spin_lock_irqsave(&pptp_seq_ht_lock, flag);
ret = mtk_tops_pptp_seq_alloc_no_lock(call_id, seq_start, seq_gen_idx);
spin_unlock_irqrestore(&pptp_seq_ht_lock, flag);
return ret;
}
static void mtk_tops_pptp_seq_free_no_lock(uint16_t call_id)
{
struct pptp_seq *pptp_seq;
pptp_seq = mtk_tops_pptp_seq_find_no_lock(call_id);
if (IS_ERR(pptp_seq))
return;
mtk_tops_seq_gen_free(pptp_seq->seq_gen_idx);
hash_del(&pptp_seq->hlist);
kfree(pptp_seq);
}
void mtk_tops_pptp_seq_free(uint16_t call_id)
{
unsigned long flag;
spin_lock_irqsave(&pptp_seq_ht_lock, flag);
mtk_tops_pptp_seq_free_no_lock(call_id);
spin_unlock_irqrestore(&pptp_seq_ht_lock, flag);
}
static int mtk_tops_pptp_seq_next_no_lock(uint16_t call_id, uint32_t *val)
{
struct pptp_seq *pptp_seq;
pptp_seq = mtk_tops_pptp_seq_find_no_lock(call_id);
if (IS_ERR(pptp_seq))
return -EINVAL;
return mtk_tops_seq_gen_next_32(pptp_seq->seq_gen_idx, val);
}
static int mtk_tops_pptp_seq_next(uint16_t call_id, uint32_t *val)
{
unsigned long flag;
int ret;
spin_lock_irqsave(&pptp_seq_ht_lock, flag);
mtk_tops_hwspin_lock(HWSPINLOCK_GROUP_CLUST,
HWSPINLOCK_CLUST_SLOT_PPTP_SEQ);
ret = mtk_tops_pptp_seq_next_no_lock(call_id, val);
mtk_tops_hwspin_unlock(HWSPINLOCK_GROUP_CLUST,
HWSPINLOCK_CLUST_SLOT_PPTP_SEQ);
spin_unlock_irqrestore(&pptp_seq_ht_lock, flag);
return ret;
}
static int mtk_tops_pptp_seq_get_seq_gen_idx_no_lock(uint16_t call_id,
int *seq_gen_idx)
{
struct pptp_seq *pptp_seq;
pptp_seq = mtk_tops_pptp_seq_find_no_lock(call_id);
if (IS_ERR(pptp_seq))
return -EINVAL;
*seq_gen_idx = pptp_seq->seq_gen_idx;
return 0;
}
int mtk_tops_pptp_seq_get_seq_gen_idx(uint16_t call_id, int *seq_gen_idx)
{
unsigned long flag;
int ret;
spin_lock_irqsave(&pptp_seq_ht_lock, flag);
ret = mtk_tops_pptp_seq_get_seq_gen_idx_no_lock(call_id, seq_gen_idx);
spin_unlock_irqrestore(&pptp_seq_ht_lock, flag);
return ret;
}
void mtk_tops_pptp_seq_init(void)
{
mtk_pptp_seq_next = mtk_tops_pptp_seq_next;
}
void mtk_tops_pptp_seq_deinit(void)
{
mtk_pptp_seq_next = NULL;
}