| /**************************************************************************** |
| * |
| * Copyright (c) 2014 - 2016 Samsung Electronics Co., Ltd. All rights reserved |
| * |
| ****************************************************************************/ |
| |
| /** |
| * MIF Byte Stream (Implementation) |
| */ |
| |
| /* Implements */ |
| #include "scsc_mx_impl.h" |
| #include "mifstream.h" |
| |
| /* Uses */ |
| #include "mifintrbit.h" |
| |
| /* Public Functions */ |
| |
| void mif_stream_config_serialise(struct mif_stream *stream, struct mxstreamconf *stream_conf) |
| { |
| stream_conf->read_bit_idx = stream->read_bit_idx; |
| stream_conf->write_bit_idx = stream->write_bit_idx; |
| cpacketbuffer_config_serialise(&stream->buffer, &stream_conf->buf_conf); |
| } |
| |
| int mif_stream_init(struct mif_stream *stream, enum scsc_mif_abs_target target, enum MIF_STREAM_DIRECTION direction, uint32_t num_packets, uint32_t packet_size, |
| struct scsc_mx *mx, enum MIF_STREAM_INTRBIT_TYPE intrbit, mifintrbit_handler tohost_irq_handler, void *data) |
| { |
| struct mifintrbit *intr; |
| int r, r1, r2; |
| |
| stream->mx = mx; |
| r = cpacketbuffer_init(&stream->buffer, num_packets, packet_size, mx); |
| if (r) |
| return r; |
| |
| intr = scsc_mx_get_intrbit(mx); |
| |
| r1 = mifintrbit_alloc_tohost(intr, tohost_irq_handler, data); |
| if (r1 < 0) { |
| cpacketbuffer_release(&stream->buffer); |
| return r1; |
| } |
| |
| /** |
| * MIF interrupt bit 0 in both the to-r4 and to-m4 registers are reserved |
| * for purpose of forcing panics from the MX Manager directly or via the |
| * gdb monitor stacks. |
| * |
| * At stream initialization the gdb transport requests for the reserved bits |
| * rather than dynamic allocation of interrupt bits. |
| * |
| * So if the interrupt bit type requested is Reserved, just Assign the pre- |
| * reserved interrupt bits. |
| */ |
| if (intrbit == MIF_STREAM_INTRBIT_TYPE_RESERVED) { |
| if (target == SCSC_MIF_ABS_TARGET_M4) |
| r2 = MIFINTRBIT_RESERVED_PANIC_M4; |
| #ifdef CONFIG_SCSC_MX450_GDB_SUPPORT |
| else if (target == SCSC_MIF_ABS_TARGET_M4_1) |
| r2 = MIFINTRBIT_RESERVED_PANIC_M4_1; |
| #endif |
| else |
| r2 = MIFINTRBIT_RESERVED_PANIC_R4; |
| } else |
| r2 = mifintrbit_alloc_fromhost(intr, target); |
| |
| if (r2 < 0) { |
| cpacketbuffer_release(&stream->buffer); |
| mifintrbit_free_tohost(intr, r1); |
| return r2; |
| } |
| switch (direction) { |
| case MIF_STREAM_DIRECTION_OUT: |
| stream->read_bit_idx = r1; |
| stream->write_bit_idx = r2; |
| break; |
| case MIF_STREAM_DIRECTION_IN: |
| /* Default value for the shared memory region */ |
| memset(stream->buffer.buffer, 0xff, num_packets * packet_size); |
| /* Commit */ |
| smp_wmb(); |
| stream->read_bit_idx = r2; |
| stream->write_bit_idx = r1; |
| break; |
| default: |
| cpacketbuffer_release(&stream->buffer); |
| mifintrbit_free_tohost(intr, r1); |
| mifintrbit_free_fromhost(intr, r2, target); |
| return -EINVAL; |
| } |
| stream->direction = direction; |
| stream->peer = (enum MIF_STREAM_PEER)target; |
| return 0; |
| } |
| |
| void mif_stream_release(struct mif_stream *stream) |
| { |
| struct mifintrbit *intr; |
| |
| intr = scsc_mx_get_intrbit(stream->mx); |
| if (stream->direction == MIF_STREAM_DIRECTION_IN) { |
| mifintrbit_free_tohost(intr, stream->write_bit_idx); |
| mifintrbit_free_fromhost(intr, stream->read_bit_idx, (enum scsc_mif_abs_target)stream->peer); |
| } else { |
| mifintrbit_free_tohost(intr, stream->read_bit_idx); |
| mifintrbit_free_fromhost(intr, stream->write_bit_idx, (enum scsc_mif_abs_target)stream->peer); |
| } |
| cpacketbuffer_release(&stream->buffer); |
| } |
| |
| uint32_t mif_stream_read(struct mif_stream *stream, void *buf, uint32_t num_bytes) |
| { |
| struct scsc_mif_abs *mif_abs = scsc_mx_get_mif_abs(stream->mx); |
| |
| uint32_t num_bytes_read = cpacketbuffer_read(&stream->buffer, buf, num_bytes); |
| |
| if (num_bytes_read > 0) |
| /* Signal that the read is finished to anyone interested */ |
| mif_abs->irq_bit_set(mif_abs, stream->read_bit_idx, (enum scsc_mif_abs_target)stream->peer); |
| |
| return num_bytes_read; |
| } |
| |
| const void *mif_stream_peek(struct mif_stream *stream, const void *current_packet) |
| { |
| return cpacketbuffer_peek(&stream->buffer, current_packet); |
| } |
| |
| void mif_stream_peek_complete(struct mif_stream *stream, const void *packet) |
| { |
| struct scsc_mif_abs *mif_abs = scsc_mx_get_mif_abs(stream->mx); |
| |
| cpacketbuffer_peek_complete(&stream->buffer, packet); |
| |
| /* Signal that the read is finished to anyone interested */ |
| mif_abs->irq_bit_set(mif_abs, stream->read_bit_idx, (enum scsc_mif_abs_target)stream->peer); |
| } |
| |
| bool mif_stream_write(struct mif_stream *stream, const void *buf, uint32_t num_bytes) |
| { |
| struct scsc_mif_abs *mif_abs = scsc_mx_get_mif_abs(stream->mx); |
| |
| if (!cpacketbuffer_write(&stream->buffer, buf, num_bytes)) |
| return false; |
| |
| /* Kick the assigned interrupt to let others know new data is available */ |
| mif_abs->irq_bit_set(mif_abs, stream->write_bit_idx, (enum scsc_mif_abs_target)stream->peer); |
| |
| return true; |
| } |
| |
| bool mif_stream_write_gather(struct mif_stream *stream, const void **bufs, uint32_t *lengths, uint32_t num_bufs) |
| { |
| struct scsc_mif_abs *mif_abs = scsc_mx_get_mif_abs(stream->mx); |
| |
| if (!cpacketbuffer_write_gather(&stream->buffer, bufs, lengths, num_bufs)) |
| return false; |
| |
| /* Kick the assigned interrupt to let others know new data is available */ |
| mif_abs->irq_bit_set(mif_abs, stream->write_bit_idx, (enum scsc_mif_abs_target)stream->peer); |
| return true; |
| } |
| |
| uint32_t mif_stream_block_size(struct mif_stream *stream) |
| { |
| return cpacketbuffer_packet_size(&stream->buffer); |
| } |
| |
| uint8_t mif_stream_read_interrupt(struct mif_stream *stream) |
| { |
| return stream->read_bit_idx; |
| } |
| |
| uint8_t mif_stream_write_interrupt(struct mif_stream *stream) |
| { |
| return stream->write_bit_idx; |
| } |
| |
| void mif_stream_log(const struct mif_stream *stream, enum scsc_log_level log_level) |
| { |
| cpacketbuffer_log(&stream->buffer, log_level); |
| } |
| |