123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441 |
- /* arch/arm/mach-msm/smp2p_loopback.c
- *
- * Copyright (c) 2013, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include <linux/debugfs.h>
- #include <linux/list.h>
- #include <linux/ctype.h>
- #include <linux/jiffies.h>
- #include <linux/slab.h>
- #include <linux/delay.h>
- #include <linux/completion.h>
- #include <linux/termios.h>
- #include <linux/module.h>
- #include <linux/remote_spinlock.h>
- #include "smem_private.h"
- #include "smp2p_private.h"
- /**
- * struct smp2p_loopback_ctx - Representation of remote loopback object.
- *
- * @proc_id: Processor id of the processor that sends the loopback commands.
- * @out: Handle to the smem entry structure for providing the response.
- * @out_nb: Notifies the opening of local entry.
- * @out_is_active: Outbound entry events should be processed.
- * @in_nb: Notifies changes in the remote entry.
- * @in_is_active: Inbound entry events should be processed.
- * @rmt_lpb_work: Work item that handles the incoming loopback commands.
- * @rmt_cmd: Structure that holds the current and previous value of the entry.
- */
- struct smp2p_loopback_ctx {
- int proc_id;
- struct msm_smp2p_out *out;
- struct notifier_block out_nb;
- bool out_is_active;
- struct notifier_block in_nb;
- bool in_is_active;
- struct work_struct rmt_lpb_work;
- struct msm_smp2p_update_notif rmt_cmd;
- };
- static struct smp2p_loopback_ctx remote_loopback[SMP2P_NUM_PROCS];
- static struct msm_smp2p_remote_mock remote_mock;
- /**
- * remote_spinlock_test - Handles remote spinlock test.
- *
- * @ctx: Loopback context
- */
- static void remote_spinlock_test(struct smp2p_loopback_ctx *ctx)
- {
- uint32_t test_request;
- uint32_t test_response;
- unsigned long flags;
- int n;
- unsigned lock_count = 0;
- remote_spinlock_t *smem_spinlock;
- test_request = 0x0;
- SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
- smem_spinlock = smem_get_remote_spinlock();
- if (!smem_spinlock) {
- pr_err("%s: unable to get remote spinlock\n", __func__);
- return;
- }
- for (;;) {
- remote_spin_lock_irqsave(smem_spinlock, flags);
- ++lock_count;
- SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_LOCKED);
- (void)msm_smp2p_out_write(ctx->out, test_request);
- for (n = 0; n < 10000; ++n) {
- (void)msm_smp2p_in_read(ctx->proc_id,
- "smp2p", &test_response);
- test_response = SMP2P_GET_RMT_CMD(test_response);
- if (test_response == SMP2P_LB_CMD_RSPIN_END)
- break;
- if (test_response != SMP2P_LB_CMD_RSPIN_UNLOCKED)
- SMP2P_ERR("%s: invalid spinlock command %x\n",
- __func__, test_response);
- }
- if (test_response == SMP2P_LB_CMD_RSPIN_END) {
- SMP2P_SET_RMT_CMD_TYPE_RESP(test_request);
- SMP2P_SET_RMT_CMD(test_request,
- SMP2P_LB_CMD_RSPIN_END);
- SMP2P_SET_RMT_DATA(test_request, lock_count);
- (void)msm_smp2p_out_write(ctx->out, test_request);
- break;
- }
- SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_UNLOCKED);
- (void)msm_smp2p_out_write(ctx->out, test_request);
- remote_spin_unlock_irqrestore(smem_spinlock, flags);
- }
- remote_spin_unlock_irqrestore(smem_spinlock, flags);
- }
- /**
- * smp2p_rmt_lpb_worker - Handles incoming remote loopback commands.
- *
- * @work: Work Item scheduled to handle the incoming commands.
- */
- static void smp2p_rmt_lpb_worker(struct work_struct *work)
- {
- struct smp2p_loopback_ctx *ctx;
- int lpb_cmd;
- int lpb_cmd_type;
- int lpb_data;
- ctx = container_of(work, struct smp2p_loopback_ctx, rmt_lpb_work);
- if (!ctx->in_is_active || !ctx->out_is_active)
- return;
- if (ctx->rmt_cmd.previous_value == ctx->rmt_cmd.current_value)
- return;
- lpb_cmd_type = SMP2P_GET_RMT_CMD_TYPE(ctx->rmt_cmd.current_value);
- lpb_cmd = SMP2P_GET_RMT_CMD(ctx->rmt_cmd.current_value);
- lpb_data = SMP2P_GET_RMT_DATA(ctx->rmt_cmd.current_value);
- if (lpb_cmd & SMP2P_RLPB_IGNORE)
- return;
- switch (lpb_cmd) {
- case SMP2P_LB_CMD_NOOP:
- /* Do nothing */
- break;
- case SMP2P_LB_CMD_ECHO:
- SMP2P_SET_RMT_CMD_TYPE(ctx->rmt_cmd.current_value, 0);
- SMP2P_SET_RMT_DATA(ctx->rmt_cmd.current_value,
- lpb_data);
- (void)msm_smp2p_out_write(ctx->out,
- ctx->rmt_cmd.current_value);
- break;
- case SMP2P_LB_CMD_CLEARALL:
- ctx->rmt_cmd.current_value = 0;
- (void)msm_smp2p_out_write(ctx->out,
- ctx->rmt_cmd.current_value);
- break;
- case SMP2P_LB_CMD_PINGPONG:
- SMP2P_SET_RMT_CMD_TYPE(ctx->rmt_cmd.current_value, 0);
- if (lpb_data) {
- lpb_data--;
- SMP2P_SET_RMT_DATA(ctx->rmt_cmd.current_value,
- lpb_data);
- (void)msm_smp2p_out_write(ctx->out,
- ctx->rmt_cmd.current_value);
- }
- break;
- case SMP2P_LB_CMD_RSPIN_START:
- remote_spinlock_test(ctx);
- break;
- case SMP2P_LB_CMD_RSPIN_LOCKED:
- case SMP2P_LB_CMD_RSPIN_UNLOCKED:
- case SMP2P_LB_CMD_RSPIN_END:
- /* not used for remote spinlock test */
- break;
- default:
- SMP2P_DBG("%s: Unknown loopback command %x\n",
- __func__, lpb_cmd);
- break;
- }
- }
- /**
- * smp2p_rmt_in_edge_notify - Schedules a work item to handle the commands.
- *
- * @nb: Notifier block, this is called when the value in remote entry changes.
- * @event: Takes value SMP2P_ENTRY_UPDATE or SMP2P_OPEN based on the event.
- * @data: Consists of previous and current value in case of entry update.
- * @returns: 0 for success (return value required for notifier chains).
- */
- static int smp2p_rmt_in_edge_notify(struct notifier_block *nb,
- unsigned long event, void *data)
- {
- struct smp2p_loopback_ctx *ctx;
- if (!(event == SMP2P_ENTRY_UPDATE || event == SMP2P_OPEN))
- return 0;
- ctx = container_of(nb, struct smp2p_loopback_ctx, in_nb);
- if (data && ctx->in_is_active) {
- ctx->rmt_cmd =
- *(struct msm_smp2p_update_notif *)data;
- schedule_work(&ctx->rmt_lpb_work);
- }
- return 0;
- }
- /**
- * smp2p_rmt_out_edge_notify - Notifies on the opening of the outbound entry.
- *
- * @nb: Notifier block, this is called when the local entry is open.
- * @event: Takes on value SMP2P_OPEN when the local entry is open.
- * @data: Consist of current value of the remote entry, if entry is open.
- * @returns: 0 for success (return value required for notifier chains).
- */
- static int smp2p_rmt_out_edge_notify(struct notifier_block *nb,
- unsigned long event, void *data)
- {
- struct smp2p_loopback_ctx *ctx;
- ctx = container_of(nb, struct smp2p_loopback_ctx, out_nb);
- if (event == SMP2P_OPEN)
- SMP2P_DBG("%s: 'smp2p':%d opened\n", __func__,
- ctx->proc_id);
- return 0;
- }
- /**
- * msm_smp2p_init_rmt_lpb - Initializes the remote loopback object.
- *
- * @ctx: Pointer to remote loopback object that needs to be initialized.
- * @pid: Processor id of the processor that is sending the commands.
- * @entry: Name of the entry that needs to be opened locally.
- * @returns: 0 on success, standard Linux error code otherwise.
- */
- static int msm_smp2p_init_rmt_lpb(struct smp2p_loopback_ctx *ctx,
- int pid, const char *entry)
- {
- int ret = 0;
- int tmp;
- if (!ctx || !entry || pid > SMP2P_NUM_PROCS)
- return -EINVAL;
- ctx->in_nb.notifier_call = smp2p_rmt_in_edge_notify;
- ctx->out_nb.notifier_call = smp2p_rmt_out_edge_notify;
- ctx->proc_id = pid;
- ctx->in_is_active = true;
- ctx->out_is_active = true;
- tmp = msm_smp2p_out_open(pid, entry, &ctx->out_nb,
- &ctx->out);
- if (tmp) {
- SMP2P_ERR("%s: open failed outbound entry '%s':%d - ret %d\n",
- __func__, entry, pid, tmp);
- ret = tmp;
- }
- tmp = msm_smp2p_in_register(ctx->proc_id,
- SMP2P_RLPB_ENTRY_NAME,
- &ctx->in_nb);
- if (tmp) {
- SMP2P_ERR("%s: unable to open inbound entry '%s':%d - ret %d\n",
- __func__, entry, pid, tmp);
- ret = tmp;
- }
- return ret;
- }
- /**
- * msm_smp2p_init_rmt_lpb_proc - Wrapper over msm_smp2p_init_rmt_lpb
- *
- * @remote_pid: Processor ID of the processor that sends loopback command.
- * @returns: Pointer to outbound entry handle.
- */
- void *msm_smp2p_init_rmt_lpb_proc(int remote_pid)
- {
- int tmp;
- void *ret = NULL;
- tmp = msm_smp2p_init_rmt_lpb(&remote_loopback[remote_pid],
- remote_pid, SMP2P_RLPB_ENTRY_NAME);
- if (!tmp)
- ret = remote_loopback[remote_pid].out;
- return ret;
- }
- EXPORT_SYMBOL(msm_smp2p_init_rmt_lpb_proc);
- /**
- * msm_smp2p_deinit_rmt_lpb_proc - Unregister support for remote processor.
- *
- * @remote_pid: Processor ID of the remote system.
- * @returns: 0 on success, standard Linux error code otherwise.
- *
- * Unregister loopback support for remote processor.
- */
- int msm_smp2p_deinit_rmt_lpb_proc(int remote_pid)
- {
- int ret = 0;
- int tmp;
- struct smp2p_loopback_ctx *ctx;
- if (remote_pid >= SMP2P_NUM_PROCS)
- return -EINVAL;
- ctx = &remote_loopback[remote_pid];
- /* abort any pending notifications */
- remote_loopback[remote_pid].out_is_active = false;
- remote_loopback[remote_pid].in_is_active = false;
- flush_work(&ctx->rmt_lpb_work);
- /* unregister entries */
- tmp = msm_smp2p_out_close(&remote_loopback[remote_pid].out);
- remote_loopback[remote_pid].out = NULL;
- if (tmp) {
- SMP2P_ERR("%s: outbound 'smp2p':%d close failed %d\n",
- __func__, remote_pid, tmp);
- ret = tmp;
- }
- tmp = msm_smp2p_in_unregister(remote_pid,
- SMP2P_RLPB_ENTRY_NAME, &remote_loopback[remote_pid].in_nb);
- if (tmp) {
- SMP2P_ERR("%s: inbound 'smp2p':%d close failed %d\n",
- __func__, remote_pid, tmp);
- ret = tmp;
- }
- return ret;
- }
- EXPORT_SYMBOL(msm_smp2p_deinit_rmt_lpb_proc);
- /**
- * msm_smp2p_set_remote_mock_exists - Sets the remote mock configuration.
- *
- * @item_exists: true = Remote mock SMEM item exists
- *
- * This is used in the testing environment to simulate the existence of the
- * remote smem item in order to test the negotiation algorithm.
- */
- void msm_smp2p_set_remote_mock_exists(bool item_exists)
- {
- remote_mock.item_exists = item_exists;
- }
- EXPORT_SYMBOL(msm_smp2p_set_remote_mock_exists);
- /**
- * msm_smp2p_get_remote_mock - Get remote mock object.
- *
- * @returns: Point to the remote mock object.
- */
- void *msm_smp2p_get_remote_mock(void)
- {
- return &remote_mock;
- }
- EXPORT_SYMBOL(msm_smp2p_get_remote_mock);
- /**
- * msm_smp2p_get_remote_mock_smem_item - Returns a pointer to remote item.
- *
- * @size: Size of item.
- * @returns: Pointer to mock remote smem item.
- */
- void *msm_smp2p_get_remote_mock_smem_item(uint32_t *size)
- {
- void *ptr = NULL;
- if (remote_mock.item_exists) {
- *size = sizeof(remote_mock.remote_item);
- ptr = &(remote_mock.remote_item);
- }
- return ptr;
- }
- EXPORT_SYMBOL(msm_smp2p_get_remote_mock_smem_item);
- /**
- * smp2p_remote_mock_rx_interrupt - Triggers receive interrupt for mock proc.
- *
- * @returns: 0 for success
- *
- * This function simulates the receiving of interrupt by the mock remote
- * processor in a testing environment.
- */
- int smp2p_remote_mock_rx_interrupt(void)
- {
- remote_mock.rx_interrupt_count++;
- if (remote_mock.initialized)
- complete(&remote_mock.cb_completion);
- return 0;
- }
- EXPORT_SYMBOL(smp2p_remote_mock_rx_interrupt);
- /**
- * smp2p_remote_mock_tx_interrupt - Calls the SMP2P interrupt handler.
- *
- * This function calls the interrupt handler of the Apps processor to simulate
- * receiving interrupts from a remote processor.
- */
- static void smp2p_remote_mock_tx_interrupt(void)
- {
- msm_smp2p_interrupt_handler(SMP2P_REMOTE_MOCK_PROC);
- }
- /**
- * smp2p_remote_mock_init - Initialize the remote mock and loopback objects.
- *
- * @returns: 0 for success
- */
- static int __init smp2p_remote_mock_init(void)
- {
- int i;
- smp2p_init_header(&remote_mock.remote_item.header,
- SMP2P_REMOTE_MOCK_PROC, SMP2P_APPS_PROC,
- 0, 0);
- remote_mock.rx_interrupt_count = 0;
- remote_mock.rx_interrupt = smp2p_remote_mock_rx_interrupt;
- remote_mock.tx_interrupt = smp2p_remote_mock_tx_interrupt;
- remote_mock.item_exists = false;
- init_completion(&remote_mock.cb_completion);
- remote_mock.initialized = true;
- for (i = 0; i < SMP2P_NUM_PROCS; i++) {
- INIT_WORK(&(remote_loopback[i].rmt_lpb_work),
- smp2p_rmt_lpb_worker);
- if (i == SMP2P_REMOTE_MOCK_PROC)
- /* do not register loopback for remote mock proc */
- continue;
- msm_smp2p_init_rmt_lpb(&remote_loopback[i],
- i, SMP2P_RLPB_ENTRY_NAME);
- }
- return 0;
- }
- module_init(smp2p_remote_mock_init);
|