blob: d3fff5e1528448f931218fd8d1b68180d073ec5d [file] [log] [blame]
/******************************************************************************
*
* Copyright (c) 2014 - 2017 Samsung Electronics Co., Ltd. All rights reserved
*
*****************************************************************************/
#ifndef __MBULK_DEF_H__
#define __MBULK_DEF_H__
#include <linux/bug.h>
#include <scsc/scsc_mifram.h>
/**
* mbulk
*
* The mbulk supports ....
*/
/* MIF_HIP_CFG_BLUK_BUFFER_ALIGNE = 4
* Hence, data buffer is always aligned to 4.
*/
#define MBULK_SIG_BUFSZ_ROUNDUP(sig_bufsz) round_up(sig_bufsz, 4)
/**
* bulk memory descriptor, managing semgments allocated from mbulk pools
*
* mbulk is defined in public header, to avoid the function call dummy operations...
* Privae variables staring with __ should be not be referred by the client
* codes.
*
* mbulk structure is shared with the host. Hence the structure should be packed.
* data buffer size in byte
*
* This could be duplicated as the buffer size can be calculated from
* pool block size. There are two reasons why the data buffer size is
* set in the descritpor:
* - if mbulk comes from the host, the firmware is not aware of its pool
* configuration
* - if the data buffer is less than the block size in the pool, it's more
* cache efficient to flush only the data buffer area
* <Q> probably the firwmare has to invalidate the entire block ?
*
*/
typedef u16 mbulk_len_t;
struct mbulk {
scsc_mifram_ref next_offset; /** next mbulk offset */
u8 flag; /** mbulk flags */
enum mbulk_class clas; /** bulk buffer classification */
u8 pid; /** mbulk pool id */
u8 refcnt; /** reference counter of bulk buffer */
mbulk_len_t dat_bufsz; /** data buffer size in byte */
mbulk_len_t sig_bufsz; /** signal buffer size in byte */
mbulk_len_t len; /** valid data length */
mbulk_len_t head; /** start offset of data after mbulk structure */
scsc_mifram_ref chain_next_offset; /** chain next mbulk offset */
} __packed;
/* mbulk flags */
#define MBULK_F_SIG (1 << 0) /** has a signal after mbulk descriptor*/
#define MBULK_F_READONLY (1 << 1) /** is read-only */
#define MBULK_F_OBOUND (1 << 2) /** other CPU(host) owns the buffer */
#define MBULK_F_WAKEUP (1 << 3) /** frame waking up the host */
#define MBULK_F_FREE (1 << 5) /** mbulk alreay freed */
#define MBULK_F_CHAIN_HEAD (1 << 6) /** is an head in scatter/gather chain */
#define MBULK_F_CHAIN (1 << 7) /** is scatter/gather chain */
/* TODO: Define max number of chained mbulk */
#define MBULK_MAX_CHAIN 16
/* the buffer address just after mbulk descriptor */
#define MBULK_SEG_B(m) ((uintptr_t)(m) + sizeof(struct mbulk))
/* raw buffer size of mbulk segment including struct mbulk */
#define MBULK_SEG_RAW_BUFSZ(m) (sizeof(struct mbulk) + (m)->sig_bufsz + (m)->dat_bufsz)
/* operations against "a" mbulk segment. */
#define MBULK_SEG_NEXT(m) ((m)->next_offset)
#define MBULK_SEG_REFCNT(m) ((m)->refcnt)
#define MBULK_SEG_HAS_SIGNAL(m) ((m)->flag & MBULK_F_SIG)
#define MBULK_SEG_CHAIN_NEXT(m) ((m)->chain_next_offset)
#define MBULK_SEG_IS_CHAIN_HEAD(m) ((m)->flag & MBULK_F_CHAIN_HEAD)
#define MBULK_SEG_IS_CHAINED(m) ((m)->flag & MBULK_F_CHAIN)
#define MBULK_SEG_IS_READONLY(m) ((m)->flag & MBULK_F_READONLY)
#define MBULK_SEG_SET_READONLY(m) ((m)->flag |= MBULK_F_READONLY)
#define MBULK_SEG_DAT(m) ((char *)MBULK_SEG_B(m) + (m)->head)
#define MBULK_SEG_DAT_AT(m, off) ((char *)MBULK_SEG_B(m) + (m)->head + (mbulk_len_t)(off))
#define MBULK_SEG_DAT_BUFSIZE(m) ((size_t)((m)->dat_bufsz))
#define MBULK_SEG_SIG_BUFSIZE(m) ((size_t)((m)->sig_bufsz))
#define MBULK_SEG_LEN(m) ((m)->len)
#define MBULK_SEG_HEADROOM(m) ((size_t)((m)->head - (m)->sig_bufsz))
#define MBULK_SEG_TAILROOM(m) ((size_t)((m)->dat_bufsz - (MBULK_SEG_HEADROOM(m) + (m)->len)))
static inline bool mbulk_seg_reserve_head(struct mbulk *m, size_t headroom)
{
if (WARN_ON(!(m->dat_bufsz >= headroom)))
return false;
m->head += (mbulk_len_t)headroom;
return true;
}
static inline bool mbulk_seg_adjust_range(struct mbulk *m, size_t headroom,
size_t len)
{
if (WARN_ON(!(m->dat_bufsz >= (headroom + len))))
return false;
m->head = m->sig_bufsz + (mbulk_len_t)headroom;
m->len = (mbulk_len_t)len;
return true;
}
static inline bool mbulk_seg_prepend_head(struct mbulk *m, size_t more)
{
if (WARN_ON(!(MBULK_SEG_HEADROOM(m) >= more)))
return false;
m->head -= (mbulk_len_t)more;
m->len += (mbulk_len_t)more;
return true;
}
static inline bool mbulk_seg_append_tail(struct mbulk *m, size_t more)
{
if (WARN_ON(!(MBULK_SEG_TAILROOM(m) >= more)))
return false;
m->len += (mbulk_len_t)more;
return true;
}
static inline bool mbulk_seg_trim_head(struct mbulk *m, size_t less)
{
m->head += (mbulk_len_t)less;
m->len = (m->len <= (mbulk_len_t)less) ? 0 : (m->len - (mbulk_len_t)less);
return true;
}
static inline bool mbulk_seg_trim_tail(struct mbulk *m, size_t less)
{
if (WARN_ON(!(m->len >= (mbulk_len_t)less)))
return false;
m->len -= (mbulk_len_t)less;
return true;
}
/**
* free the bulk buffer of a segment
*
* Simply decrement the bulk reference counter and put to the pool if
* it is zero. Note that the signal buffer is not affected.
*
*/
void mbulk_seg_free(struct mbulk *m);
/**
* duplicate the bulk buffer of a mbulk segment
*
* This is used to share the read-only bulk buffer. The reference counter is
* increased by one each time it is duplicated.
*
*/
struct mbulk *mbulk_seg_duplicate(struct mbulk *m);
/**
* clone the bulk buffer of a mbulk segment
*
* A separate mbulk segment is created and the data is copied to it.
* If \copy_sig is true, then the signal is copied as well.
*
*/
struct mbulk *mbulk_seg_clone(const struct mbulk *m, enum mbulk_class clas, bool copy_sig);
#endif /*__MBULK_DEF_H__*/