1 /****************************************************************************
3 * Copyright (c) 2014 - 2017 Samsung Electronics Co., Ltd. All rights reserved
5 ****************************************************************************/
8 * Maxwell management transport (implementation)
12 #include "mxmgmt_transport.h"
15 #include <scsc/scsc_logring.h>
16 #include <linux/module.h>
17 #include "mxmgmt_transport_format.h"
18 #include "mifintrbit.h"
20 /* Flag that an error has occurred so the I/O thread processing should stop */
21 void mxmgmt_transport_set_error(struct mxmgmt_transport
*mxmgmt_transport
)
23 SCSC_TAG_WARNING(MXMGT_TRANS
, "I/O thread processing is suspended\n");
25 mxmgmt_transport
->mxmgmt_thread
.block_thread
= 1;
28 /** MIF Interrupt handler for writes made to the AP */
29 static void input_irq_handler(int irq
, void *data
)
31 struct mxmgmt_transport
*mxmgmt_transport
= (struct mxmgmt_transport
*)data
;
32 struct mxmgmt_thread
*th
= &mxmgmt_transport
->mxmgmt_thread
;
33 struct scsc_mif_abs
*mif_abs
;
35 SCSC_TAG_DEBUG(MXMGT_TRANS
, "IN\n");
36 /* Clear the interrupt first to ensure we can't possibly miss one */
37 mif_abs
= scsc_mx_get_mif_abs(mxmgmt_transport
->mx
);
38 mif_abs
->irq_bit_clear(mif_abs
, irq
);
40 /* The the other side wrote some data to the input stream, wake up the thread
41 * that deals with this. */
42 if (th
->task
== NULL
) {
43 SCSC_TAG_ERR(MXMGT_TRANS
, "th is NOT running\n");
47 * If an error has occured, we discard silently all messages from the stream
48 * until the error has been processed and the system has been reinitialised.
50 if (th
->block_thread
== 1) {
51 SCSC_TAG_DEBUG(MXMGT_TRANS
, "discard message.\n");
53 * Do not try to acknowledge a pending interrupt here.
54 * This function is called by a function which in turn can be
55 * running in an atomic or 'disabled irq' level.
61 /* wake up I/O thread */
62 wake_up_interruptible(&th
->wakeup_q
);
65 /** MIF Interrupt handler for acknowledging writes made by the AP */
66 static void output_irq_handler(int irq
, void *data
)
68 struct scsc_mif_abs
*mif_abs
;
69 struct mxmgmt_transport
*mxmgmt_transport
= (struct mxmgmt_transport
*)data
;
71 SCSC_TAG_DEBUG(MXMGT_TRANS
, "OUT\n");
73 /* Clear the interrupt first to ensure we can't possibly miss one */
74 /* The FW read some data from the output stream.
75 * Currently we do not care, so just clear the interrupt. */
76 mif_abs
= scsc_mx_get_mif_abs(mxmgmt_transport
->mx
);
77 mif_abs
->irq_bit_clear(mif_abs
, irq
);
79 /* The driver doesn't use the ack IRQ, so mask it from now on,
80 * otherwise we may get spurious host-wakes.
82 mif_abs
->irq_bit_mask(mif_abs
, irq
);
86 static void thread_wait_until_stopped(struct mxmgmt_transport
*mxmgmt_transport
)
88 struct mxmgmt_thread
*th
= &mxmgmt_transport
->mxmgmt_thread
;
91 * kthread_stop() cannot handle the th exiting while
92 * kthread_should_stop() is false, so sleep until kthread_stop()
95 SCSC_TAG_DEBUG(MXMGT_TRANS
, "%s waiting for the stop signal.\n", th
->name
);
96 set_current_state(TASK_INTERRUPTIBLE
);
97 if (!kthread_should_stop()) {
98 SCSC_TAG_DEBUG(MXMGT_TRANS
, "%s schedule....\n", th
->name
);
103 SCSC_TAG_DEBUG(MXMGT_TRANS
, "%s exiting....\n", th
->name
);
107 * A thread that forwards messages sent across the transport to
108 * the registered handlers for each channel.
110 static int mxmgmt_thread_function(void *arg
)
112 struct mxmgmt_transport
*mxmgmt_transport
= (struct mxmgmt_transport
*)arg
;
113 struct mxmgmt_thread
*th
= &mxmgmt_transport
->mxmgmt_thread
;
114 const struct mxmgr_message
*current_message
;
117 complete(&th
->completion
);
119 th
->block_thread
= 0;
120 while (!kthread_should_stop()) {
121 /* wait until an error occurs, or we need to process something. */
123 ret
= wait_event_interruptible(th
->wakeup_q
,
124 (th
->wakeup_flag
&& !th
->block_thread
) ||
125 kthread_should_stop());
127 if (kthread_should_stop()) {
128 SCSC_TAG_DEBUG(MXMGT_TRANS
, "signalled to exit\n");
132 SCSC_TAG_DEBUG(MXMGT_TRANS
, "wait_event returned %d, thread will exit\n", ret
);
133 thread_wait_until_stopped(mxmgmt_transport
);
137 SCSC_TAG_DEBUG(MXMGT_TRANS
, "wokeup: r=%d\n", ret
);
138 /* Forward each pending message to the applicable channel handler */
139 current_message
= mif_stream_peek(&mxmgmt_transport
->mif_istream
, NULL
);
140 while (current_message
!= NULL
) {
141 mutex_lock(&mxmgmt_transport
->channel_handler_mutex
);
142 if (current_message
->channel_id
< MMTRANS_NUM_CHANNELS
&&
143 mxmgmt_transport
->channel_handler_fns
[current_message
->channel_id
]) {
144 SCSC_TAG_DEBUG(MXMGT_TRANS
, "Calling handler for channel_id: %d\n", current_message
->channel_id
);
145 (*mxmgmt_transport
->channel_handler_fns
[current_message
->channel_id
])(current_message
->payload
,
146 mxmgmt_transport
->channel_handler_data
[current_message
->channel_id
]);
148 /* HERE: Invalid channel or no handler, raise fault or log message */
149 SCSC_TAG_WARNING(MXMGT_TRANS
, "Invalid channel or no handler channel_id: %d\n", current_message
->channel_id
);
150 mutex_unlock(&mxmgmt_transport
->channel_handler_mutex
);
151 /* Remove the current message from the buffer before processing the next
152 * one in case it generated another message, otherwise it's possible we
153 * could run out of space in the stream before we get through all the messages. */
154 mif_stream_peek_complete(&mxmgmt_transport
->mif_istream
, current_message
);
155 current_message
= mif_stream_peek(&mxmgmt_transport
->mif_istream
, NULL
);
159 SCSC_TAG_DEBUG(MXMGT_TRANS
, "exiting....\n");
160 complete(&th
->completion
);
165 static int mxmgmt_thread_start(struct mxmgmt_transport
*mxmgmt_transport
)
168 struct mxmgmt_thread
*th
= &mxmgmt_transport
->mxmgmt_thread
;
170 if (th
->task
!= NULL
) {
171 SCSC_TAG_WARNING(MXMGT_TRANS
, "%s thread already started\n", th
->name
);
175 /* Initialise thread structure */
176 th
->block_thread
= 1;
177 init_waitqueue_head(&th
->wakeup_q
);
178 init_completion(&th
->completion
);
180 snprintf(th
->name
, MXMGMT_THREAD_NAME_MAX_LENGTH
, "mxmgmt_thread");
182 /* Start the kernel thread */
183 th
->task
= kthread_run(mxmgmt_thread_function
, mxmgmt_transport
, "%s", th
->name
);
184 if (IS_ERR(th
->task
)) {
185 SCSC_TAG_ERR(MXMGT_TRANS
, "error creating kthread\n");
186 return (int)PTR_ERR(th
->task
);
189 SCSC_TAG_DEBUG(MXMGT_TRANS
, "Started thread %s\n", th
->name
);
191 /* wait until thread is started */
192 #define MGMT_THREAD_START_TMO_SEC (3)
193 err
= wait_for_completion_timeout(&th
->completion
, msecs_to_jiffies(MGMT_THREAD_START_TMO_SEC
*1000));
195 SCSC_TAG_ERR(MXMGT_TRANS
, "timeout in starting thread\n");
201 static void mgmt_thread_stop(struct mxmgmt_transport
*mxmgmt_transport
)
203 unsigned long left_jiffies
;
204 struct mxmgmt_thread
*th
= &mxmgmt_transport
->mxmgmt_thread
;
207 SCSC_TAG_WARNING(MXMGT_TRANS
, "%s mgmt_thread is already stopped\n", th
->name
);
210 SCSC_TAG_DEBUG(MXMGT_TRANS
, "Stopping %s mgmt_thread\n", th
->name
);
211 kthread_stop(th
->task
);
212 /* wait until th stopped */
213 #define MGMT_THREAD_STOP_TMO_SEC (3)
215 wait_for_completion_timeout(&th
->completion
, msecs_to_jiffies(MGMT_THREAD_STOP_TMO_SEC
*1000));
217 SCSC_TAG_ERR(MXMGT_TRANS
, "Failed to stop mgmt_thread %s\n",
223 void mxmgmt_transport_release(struct mxmgmt_transport
*mxmgmt_transport
)
225 mgmt_thread_stop(mxmgmt_transport
);
226 mif_stream_release(&mxmgmt_transport
->mif_istream
);
227 mif_stream_release(&mxmgmt_transport
->mif_ostream
);
230 void mxmgmt_transport_config_serialise(struct mxmgmt_transport
*mxmgmt_transport
,
231 struct mxtransconf
*trans_conf
)
233 mif_stream_config_serialise(&mxmgmt_transport
->mif_istream
, &trans_conf
->to_ap_stream_conf
);
234 mif_stream_config_serialise(&mxmgmt_transport
->mif_ostream
, &trans_conf
->from_ap_stream_conf
);
238 /** Public functions */
239 int mxmgmt_transport_init(struct mxmgmt_transport
*mxmgmt_transport
, struct scsc_mx
*mx
)
241 #define MEM_LENGTH 512
243 uint32_t mem_length
= MEM_LENGTH
;
244 uint32_t packet_size
= sizeof(struct mxmgr_message
);
245 uint32_t num_packets
;
249 * Initialising a buffer of 1 byte is never legitimate, do not allow it.
250 * The memory buffer length must be a multiple of the packet size.
252 if (mem_length
<= 1 || mem_length
% packet_size
!= 0)
254 memset(mxmgmt_transport
, 0, sizeof(struct mxmgmt_transport
));
255 num_packets
= mem_length
/ packet_size
;
256 mutex_init(&mxmgmt_transport
->channel_handler_mutex
);
257 mxmgmt_transport
->mx
= mx
;
258 r
= mif_stream_init(&mxmgmt_transport
->mif_istream
, SCSC_MIF_ABS_TARGET_R4
, MIF_STREAM_DIRECTION_IN
, num_packets
, packet_size
, mx
, MIF_STREAM_INTRBIT_TYPE_ALLOC
, input_irq_handler
, mxmgmt_transport
);
260 SCSC_TAG_ERR(MXMGT_TRANS
, "mif_stream_init IN failed %d\n", r
);
263 r
= mif_stream_init(&mxmgmt_transport
->mif_ostream
, SCSC_MIF_ABS_TARGET_R4
, MIF_STREAM_DIRECTION_OUT
, num_packets
, packet_size
, mx
, MIF_STREAM_INTRBIT_TYPE_ALLOC
, output_irq_handler
, mxmgmt_transport
);
265 SCSC_TAG_ERR(MXMGT_TRANS
, "mif_stream_init OUT failed %d\n", r
);
266 mif_stream_release(&mxmgmt_transport
->mif_istream
);
270 r
= mxmgmt_thread_start(mxmgmt_transport
);
272 SCSC_TAG_ERR(MXMGT_TRANS
, "mxmgmt_thread_start failed %d\n", r
);
273 mif_stream_release(&mxmgmt_transport
->mif_istream
);
274 mif_stream_release(&mxmgmt_transport
->mif_ostream
);
280 void mxmgmt_transport_register_channel_handler(struct mxmgmt_transport
*mxmgmt_transport
, enum mxmgr_channels channel_id
,
281 mxmgmt_channel_handler handler
, void *data
)
283 if (channel_id
>= MMTRANS_NUM_CHANNELS
) {
284 SCSC_TAG_ERR(MXMGT_TRANS
, "Invalid channel id: %d\n", channel_id
);
287 mutex_lock(&mxmgmt_transport
->channel_handler_mutex
);
288 mxmgmt_transport
->channel_handler_fns
[channel_id
] = handler
;
289 mxmgmt_transport
->channel_handler_data
[channel_id
] = data
;
290 mutex_unlock(&mxmgmt_transport
->channel_handler_mutex
);
293 void mxmgmt_transport_send(struct mxmgmt_transport
*mxmgmt_transport
, enum mxmgr_channels channel_id
,
294 void *message
, uint32_t message_length
)
296 struct mxmgr_message transport_msg
= { .channel_id
= channel_id
};
298 const void *bufs
[2] = { &transport_msg
.channel_id
, message
};
299 uint32_t buf_lengths
[2] = { sizeof(transport_msg
.channel_id
), message_length
};
301 mif_stream_write_gather(&mxmgmt_transport
->mif_ostream
, bufs
, buf_lengths
, 2);