/****************************************************************************** * * Copyright (c) 2014 - 2017 Samsung Electronics Co., Ltd. All rights reserved * *****************************************************************************/ #ifndef __MBULK_DEF_H__ #define __MBULK_DEF_H__ #include #include /** * 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 * 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__*/