| /****************************************************************************** |
| * |
| * Copyright (C) 2016-2017 Cadence Design Systems, Inc. |
| * All rights reserved worldwide. |
| * |
| * Redistribution and use in source and binary forms, with or without modification, |
| * are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation and/or |
| * other materials provided with the distribution. |
| * |
| * 3. Neither the name of the copyright holder nor the names of its contributors |
| * may be used to endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
| * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
| * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
| * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
| * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE |
| * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| * |
| * Copyright 2017 NXP |
| * |
| ****************************************************************************** |
| * |
| * util.h |
| * |
| ****************************************************************************** |
| */ |
| |
| #ifndef UTIL_H_ |
| #define UTIL_H_ |
| |
| #include <linux/delay.h> |
| #include <linux/mutex.h> |
| |
| /** |
| * \addtogroup GENERAL_API |
| * \{ |
| */ |
| /** status code returned by API calls */ |
| typedef enum { |
| /** operation succedded */ |
| CDN_OK = 0, |
| /** CEC operation succedded */ |
| CDN_CEC_ERR_NONE = 0, |
| /** mailbox is currently sending or receiving data */ |
| CDN_BSY, |
| /** message set up and ready to be sent, no data sent yet */ |
| CDN_STARTED, |
| /** error encountered while reading/writing APB */ |
| CDN_ERR, |
| /** reply returned with bad opcode */ |
| CDN_BAD_OPCODE, |
| /** reply returned with bad module */ |
| CDN_BAD_MODULE, |
| /** reply not supported mode */ |
| CDN_ERROR_NOT_SUPPORTED, |
| /** Invalid argument passed to CEC API function */ |
| CDN_CEC_ERR_INVALID_ARG, |
| /** |
| * TX Buffer for CEC Messages is full. This is applicable only |
| * when TX Buffers for CEC Messages are implemented in the HW. |
| */ |
| CDN_CEC_ERR_TX_BUFF_FULL, |
| /** No Messages in the RX Buffers are present. */ |
| CDN_CEC_ERR_RX_BUFF_EMPTY, |
| /** Timeout during TX operation */ |
| CDN_CEC_ERR_TX_TIMEOUT, |
| /** Timeout during RX operation */ |
| CDN_CEC_ERR_RX_TIMEOUT, |
| /** Data transmision fail. */ |
| CDN_CEC_ERR_TX_FAILED, |
| /** Data reception fail. */ |
| CDN_CEC_ERR_RX_FAILED, |
| /** Operation aborted. */ |
| CDN_CEC_ERR_ABORT, |
| } CDN_API_STATUS; |
| |
| typedef enum { |
| CDN_DPTX, |
| CDN_HDMITX_TYPHOON, |
| CDN_HDMITX_KIRAN, |
| } CDN_PROTOCOL_TYPE; |
| |
| typedef enum { |
| CDN_BUS_TYPE_APB = 0, |
| CDN_BUS_TYPE_SAPB = 1 |
| } CDN_BUS_TYPE; |
| |
| typedef struct { |
| /** apb write status */ |
| enum tx_status_enum { |
| /** one or more bytes written */ |
| CDN_TX_WRITE = 0, |
| /** nothing to write */ |
| CDN_TX_NOTHING = 1, |
| /** mailbox full, 0 bytes written */ |
| CDN_TX_FULL = 2, |
| /** APB error while writing */ |
| CDN_TX_APB_ERROR = 3 |
| } tx_status:3; |
| /** apb read status */ |
| enum rx_status_enum { |
| /** 1 or more bytes read */ |
| CDN_RX_READ = 0, |
| /** mailbox empty, 0 bytes read */ |
| CDN_RX_EMPTY = 1, |
| /** apb error while reading */ |
| CDN_RX_APB_ERROR = 2 |
| } rx_status:2; |
| /** indicates end of currenly recived message */ |
| u8 rxend:1; |
| /** end of tx message reached */ |
| u8 txend:1; |
| } INTERNAL_MBOX_STATUS; |
| |
| struct hdp_mem { |
| void __iomem *regs_base; |
| void __iomem *ss_base; |
| }; |
| |
| struct hdp_rw_func { |
| int (*read_reg) (struct hdp_mem *mem, u32 addr, u32 *value); |
| int (*write_reg) (struct hdp_mem *mem, u32 addr, u32 value); |
| int (*sread_reg) (struct hdp_mem *mem, u32 addr, u32 *value); |
| int (*swrite_reg) (struct hdp_mem *mem, u32 addr, u32 value); |
| }; |
| |
| typedef struct { |
| u8 txBuffer[1024]; |
| u8 rxBuffer[1024]; |
| u32 txi; //iterators |
| u32 rxi; |
| u8 txEnable; //data readt to send |
| u8 rxEnable; |
| u8 running; |
| CDN_BUS_TYPE bus_type; |
| u32 tmp; |
| |
| struct mutex mutex; //mutex may replace running |
| struct hdp_mem mem; |
| struct hdp_rw_func *rw; |
| } state_struct; |
| /** |
| * \addtogroup UTILS |
| * \{ |
| */ |
| #define INTERNAL_CMD_HEAD_SIZE 4 |
| |
| /** |
| * \brief expands to blocking function body |
| * \param x - function call |
| */ |
| #define MAILBOX_FILL_TIMEOUT 1500 |
| #define internal_block_function(y, x) \ |
| do { \ |
| unsigned long end_jiffies = jiffies + \ |
| msecs_to_jiffies(MAILBOX_FILL_TIMEOUT); \ |
| CDN_API_STATUS ret; \ |
| mutex_lock(y); \ |
| do { \ |
| ret = x; \ |
| cpu_relax(); \ |
| } while (time_after(end_jiffies, jiffies) && \ |
| (ret == CDN_BSY || ret == CDN_STARTED)); \ |
| mutex_unlock(y); \ |
| return ret; \ |
| } while (0) |
| |
| /** |
| * \brief write message and write response (if any), non-blocking way. Also sets state.running = 0 |
| */ |
| #define internal_process_messages(state) \ |
| do { \ |
| if (state->txEnable && !internal_mbox_tx_process(state).txend) \ |
| return CDN_BSY; \ |
| if (state->rxEnable && !internal_mbox_rx_process(state).rxend) \ |
| return CDN_BSY; \ |
| state->running = 0; \ |
| } while (0) |
| |
| #define internal_opcode_ok_or_return(state, module, opcode) do { \ |
| CDN_API_STATUS ret; \ |
| ret = internal_test_rx_head(state, module, opcode); \ |
| if (ret != CDN_OK) \ |
| return ret; \ |
| } while (0) |
| |
| #define internal_opcode_match_or_return(state) do { \ |
| CDN_API_STATUS ret; \ |
| ret = internal_test_rx_head_match(state); \ |
| if (ret != CDN_OK) \ |
| return ret; \ |
| } while (0) |
| |
| /* macro for simple tx only command, command format as in mkfullmsg (with count) */ |
| #define internal_macro_command_tx(state, module, opcode, bustype, command...) \ |
| do { \ |
| if (!state->running) { \ |
| internal_tx_mkfullmsg(state, module, opcode, command); \ |
| state->bus_type = bustype; \ |
| return CDN_STARTED; \ |
| } \ |
| internal_process_messages(state); \ |
| } while (0) |
| |
| /* macro for command with response with matching opcode, command format as in mkfullmsg (with count) */ |
| #define internal_macro_command_txrx(state, module, opcode, bustype, command...) \ |
| do { \ |
| if (!state->running) { \ |
| internal_tx_mkfullmsg(state, module, opcode, command); \ |
| state->bus_type = bustype; \ |
| state->rxEnable = 1; \ |
| return CDN_STARTED; \ |
| } \ |
| internal_process_messages(state); \ |
| internal_opcode_match_or_return(state); \ |
| } while (0) |
| |
| /** |
| * \brief put val into dest in big endian format |
| * \param val - value to put |
| * \param dest - place to put value |
| * \param bytes - true size of val in bytes. for example if bytes = 2 val is treated as short int |
| */ |
| void internal_itobe(int val, volatile u8 *dest, int bytes); |
| |
| /** |
| * \brief read big endian value from src and return it |
| * \param src - source to read from |
| * \param bytes - size of read value |
| * \return result |
| */ |
| u32 internal_betoi(volatile u8 const *src, u8 bytes); |
| |
| /** |
| * \brief create message from size and value pairs; also sets state.runnging and state.txEnable |
| * \param dest - pointer to write message to |
| * \param valNo - number of values to write |
| * \param ... - pairs of size and value, each value is written after another. if size is positive value, value is written with #internal_itobe, if size is negative, value is treated as src pointer for memcpy |
| * |
| * example: |
| * |
| * u16 x = 0xAABB; |
| * |
| * internal_mkmsg(dest, 3, 1, 1, 2, 3, -2, &x); |
| * |
| * will write 01 00 03 AA BB to dest |
| */ |
| u32 internal_mkmsg(volatile u8 *dest, int valNo, ...); |
| u32 internal_vmkmsg(volatile u8 *dest, int valNo, va_list vl); |
| |
| /** |
| * \brief setup message header in txBuffer, set txEnable = 1 |
| */ |
| void internal_mbox_tx_enable(state_struct *state, u8 module, u8 opcode, |
| u16 length); |
| |
| /** |
| * \brief write from txBuffer to mailbox untill full or end of message. |
| * |
| * when txEnable == 0 writes nothing |
| * when write reaches end of message set txEnable = 0 |
| */ |
| |
| /** |
| * \brief combination of #internal_mkmsg and #internal_mbox_tx_enable |
| * |
| * #internal_mkmsg dest and #internal_mbox_tx_enable length are determined automaticly |
| * this function also sets state.txEnable = 1 and state.running |
| */ |
| void internal_tx_mkfullmsg(state_struct *state, u8 module, u8 opcode, |
| int valNo, ...); |
| void internal_vtx_mkfullmsg(state_struct *state, u8 module, u8 opcode, |
| int valNo, va_list vl); |
| |
| /** |
| * \brief read from state.txBuffer and store results in specified pointers |
| * \param valNo - numbero of values to read |
| * \param ... - pairs of size and ptr |
| * |
| * this function is similar to #internal_mkmsg - |
| * |
| * when size is positive read value using #internal_betoi |
| * when size is negative mempcy from txBuffer to ptr -size bytes |
| * when size is 0 write to ptr addres of current position in rxbuffer |
| * when ptr is NULL ignore size bytes (if size is negative this will rewind buffer) |
| */ |
| void internal_readmsg(state_struct *state, int valNo, ...); |
| void internal_vreadmsg(state_struct *state, int valNo, va_list vl); |
| |
| INTERNAL_MBOX_STATUS internal_mbox_tx_process(state_struct *state); |
| /** |
| * \brief read to rxBuffer from mailbox untill empty or end of message |
| * |
| * when rxEnable == 0 reads nothing |
| * when end of message reached sets rxEnable = 0 |
| */ |
| INTERNAL_MBOX_STATUS internal_mbox_rx_process(state_struct *state); |
| |
| /** |
| * \brief check if apb is available |
| * \return !(rxEnable && txEable) |
| */ |
| u32 internal_apb_available(state_struct *state); |
| |
| /** |
| * \brief test if parameters match module and opcode in rxBuffer |
| * \return CDN_OK or CDN_BAD_OPCODE or CDN_BAD_MODULE |
| */ |
| CDN_API_STATUS internal_test_rx_head(state_struct *state, u8 module, |
| u8 opcode); |
| |
| CDN_API_STATUS internal_test_rx_head_match(state_struct *state); |
| |
| /** |
| * \brief print current fw and lib version |
| */ |
| void print_fw_ver(state_struct *state); |
| |
| int cdn_apb_read(state_struct *state, u32 addr, u32 *value); |
| int cdn_sapb_read(state_struct *state, u32 addr, u32 *value); |
| int cdn_apb_write(state_struct *state, u32 addr, u32 value); |
| int cdn_sapb_write(state_struct *state, u32 addr, u32 value); |
| void cdn_sleep(u32 ms); |
| void cdn_usleep(u32 us); |
| u16 internal_get_msg_len(state_struct *state); |
| |
| #endif |