From 647a60748a6138238b6873389ebefcd3576ca73b Mon Sep 17 00:00:00 2001 From: Karthikeyan Ramasubramanian Date: Wed, 3 Feb 2016 12:45:04 -0700 Subject: [PATCH] soc: qcom: Add snapshot of ipc_router_smd_xprt This snapshot is taken as of msm-3.18 commit e70ad0cd (Promotion of kernel.lnx.3.18-151201.) Signed-off-by: Karthikeyan Ramasubramanian --- drivers/soc/qcom/Kconfig | 10 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/ipc_router_smd_xprt.c | 845 +++++++++++++++++++++++++++++++++ 3 files changed, 856 insertions(+) create mode 100644 drivers/soc/qcom/ipc_router_smd_xprt.c diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 9250d74677e2..9c47a087c6f9 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -161,6 +161,16 @@ config QCOM_SMEM The driver provides an interface to items in a heap shared among all processors in a Qualcomm platform. +config MSM_IPC_ROUTER_SMD_XPRT + depends on MSM_SMD + depends on IPC_ROUTER + bool "MSM SMD XPRT Layer" + help + SMD Transport Layer that enables IPC Router communication within + a System-on-Chip(SoC). When the SMD channels become available, + this layer registers a transport with IPC Router and enable + message exchange. + config QCOM_SMD tristate "Qualcomm Shared Memory Driver (SMD)" depends on QCOM_SMEM diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 4836a12dd434..58d3930ebd20 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_MSM_RPM_SMD) += rpm-smd.o ifdef CONFIG_DEBUG_FS obj-$(CONFIG_MSM_RPM_SMD) += rpm-smd-debug.o endif +obj-$(CONFIG_MSM_IPC_ROUTER_SMD_XPRT) += ipc_router_smd_xprt.o obj-$(CONFIG_MSM_PIL_SSR_GENERIC) += subsys-pil-tz.o obj-$(CONFIG_MSM_PIL_MSS_QDSP6V5) += pil-q6v5.o pil-msa.o pil-q6v5-mss.o diff --git a/drivers/soc/qcom/ipc_router_smd_xprt.c b/drivers/soc/qcom/ipc_router_smd_xprt.c new file mode 100644 index 000000000000..60c45080781c --- /dev/null +++ b/drivers/soc/qcom/ipc_router_smd_xprt.c @@ -0,0 +1,845 @@ +/* Copyright (c) 2011-2015, 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. + */ + +/* + * IPC ROUTER SMD XPRT module. + */ +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static int msm_ipc_router_smd_xprt_debug_mask; +module_param_named(debug_mask, msm_ipc_router_smd_xprt_debug_mask, + int, S_IRUGO | S_IWUSR | S_IWGRP); + +#if defined(DEBUG) +#define D(x...) do { \ +if (msm_ipc_router_smd_xprt_debug_mask) \ + pr_info(x); \ +} while (0) +#else +#define D(x...) do { } while (0) +#endif + +#define MIN_FRAG_SZ (IPC_ROUTER_HDR_SIZE + sizeof(union rr_control_msg)) + +#define NUM_SMD_XPRTS 4 +#define XPRT_NAME_LEN (SMD_MAX_CH_NAME_LEN + 12) + +/** + * msm_ipc_router_smd_xprt - IPC Router's SMD XPRT structure + * @list: IPC router's SMD XPRTs list. + * @ch_name: Name of the HSIC endpoint exported by ipc_bridge driver. + * @xprt_name: Name of the XPRT to be registered with IPC Router. + * @edge: SMD channel edge. + * @driver: Platform drivers register by this XPRT. + * @xprt: IPC Router XPRT structure to contain XPRT specific info. + * @channel: SMD channel specific info. + * @smd_xprt_wq: Workqueue to queue read & other XPRT related works. + * @write_avail_wait_q: wait queue for writer thread. + * @in_pkt: Pointer to any partially read packet. + * @is_partial_in_pkt: check pkt completion. + * @read_work: Read Work to perform read operation from SMD. + * @ss_reset_lock: Lock to protect access to the ss_reset flag. + * @ss_reset: flag used to check SSR state. + * @pil: handle to the remote subsystem. + * @sft_close_complete: Variable to indicate completion of SSR handling + * by IPC Router. + * @xprt_version: IPC Router header version supported by this XPRT. + * @xprt_option: XPRT specific options to be handled by IPC Router. + * @disable_pil_loading: Disable PIL Loading of the subsystem. + */ +struct msm_ipc_router_smd_xprt { + struct list_head list; + char ch_name[SMD_MAX_CH_NAME_LEN]; + char xprt_name[XPRT_NAME_LEN]; + uint32_t edge; + struct platform_driver driver; + struct msm_ipc_router_xprt xprt; + smd_channel_t *channel; + struct workqueue_struct *smd_xprt_wq; + wait_queue_head_t write_avail_wait_q; + struct rr_packet *in_pkt; + int is_partial_in_pkt; + struct delayed_work read_work; + spinlock_t ss_reset_lock; /*Subsystem reset lock*/ + int ss_reset; + void *pil; + struct completion sft_close_complete; + unsigned xprt_version; + unsigned xprt_option; + bool disable_pil_loading; +}; + +struct msm_ipc_router_smd_xprt_work { + struct msm_ipc_router_xprt *xprt; + struct work_struct work; +}; + +static void smd_xprt_read_data(struct work_struct *work); +static void smd_xprt_open_event(struct work_struct *work); +static void smd_xprt_close_event(struct work_struct *work); + +/** + * msm_ipc_router_smd_xprt_config - Config. Info. of each SMD XPRT + * @ch_name: Name of the SMD endpoint exported by SMD driver. + * @xprt_name: Name of the XPRT to be registered with IPC Router. + * @edge: ID to differentiate among multiple SMD endpoints. + * @link_id: Network Cluster ID to which this XPRT belongs to. + * @xprt_version: IPC Router header version supported by this XPRT. + * @disable_pil_loading: Disable PIL Loading of the subsystem. + */ +struct msm_ipc_router_smd_xprt_config { + char ch_name[SMD_MAX_CH_NAME_LEN]; + char xprt_name[XPRT_NAME_LEN]; + uint32_t edge; + uint32_t link_id; + unsigned xprt_version; + unsigned xprt_option; + bool disable_pil_loading; +}; + +struct msm_ipc_router_smd_xprt_config smd_xprt_cfg[] = { + {"RPCRPY_CNTL", "ipc_rtr_smd_rpcrpy_cntl", SMD_APPS_MODEM, 1, 1}, + {"IPCRTR", "ipc_rtr_smd_ipcrtr", SMD_APPS_MODEM, 1, 1}, + {"IPCRTR", "ipc_rtr_q6_ipcrtr", SMD_APPS_QDSP, 1, 1}, + {"IPCRTR", "ipc_rtr_wcnss_ipcrtr", SMD_APPS_WCNSS, 1, 1}, +}; + +#define MODULE_NAME "ipc_router_smd_xprt" +#define IPC_ROUTER_SMD_XPRT_WAIT_TIMEOUT 3000 +static int ipc_router_smd_xprt_probe_done; +static struct delayed_work ipc_router_smd_xprt_probe_work; +static DEFINE_MUTEX(smd_remote_xprt_list_lock_lha1); +static LIST_HEAD(smd_remote_xprt_list); + +static bool is_pil_loading_disabled(uint32_t edge); + +static int msm_ipc_router_smd_get_xprt_version( + struct msm_ipc_router_xprt *xprt) +{ + struct msm_ipc_router_smd_xprt *smd_xprtp; + if (!xprt) + return -EINVAL; + smd_xprtp = container_of(xprt, struct msm_ipc_router_smd_xprt, xprt); + + return (int)smd_xprtp->xprt_version; +} + +static int msm_ipc_router_smd_get_xprt_option( + struct msm_ipc_router_xprt *xprt) +{ + struct msm_ipc_router_smd_xprt *smd_xprtp; + if (!xprt) + return -EINVAL; + smd_xprtp = container_of(xprt, struct msm_ipc_router_smd_xprt, xprt); + + return (int)smd_xprtp->xprt_option; +} + +static int msm_ipc_router_smd_remote_write_avail( + struct msm_ipc_router_xprt *xprt) +{ + struct msm_ipc_router_smd_xprt *smd_xprtp = + container_of(xprt, struct msm_ipc_router_smd_xprt, xprt); + + return smd_write_avail(smd_xprtp->channel); +} + +static int msm_ipc_router_smd_remote_write(void *data, + uint32_t len, + struct msm_ipc_router_xprt *xprt) +{ + struct rr_packet *pkt = (struct rr_packet *)data; + struct sk_buff *ipc_rtr_pkt; + int offset, sz_written = 0; + int ret, num_retries = 0; + unsigned long flags; + struct msm_ipc_router_smd_xprt *smd_xprtp = + container_of(xprt, struct msm_ipc_router_smd_xprt, xprt); + + if (!pkt) + return -EINVAL; + + if (!len || pkt->length != len) + return -EINVAL; + + do { + spin_lock_irqsave(&smd_xprtp->ss_reset_lock, flags); + if (smd_xprtp->ss_reset) { + spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock, + flags); + IPC_RTR_ERR("%s: %s chnl reset\n", + __func__, xprt->name); + return -ENETRESET; + } + spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock, flags); + ret = smd_write_start(smd_xprtp->channel, len); + if (ret < 0 && num_retries >= 5) { + IPC_RTR_ERR("%s: Error %d @smd_write_start for %s\n", + __func__, ret, xprt->name); + return ret; + } else if (ret < 0) { + msleep(50); + num_retries++; + } + } while (ret < 0); + + D("%s: Ready to write %d bytes\n", __func__, len); + skb_queue_walk(pkt->pkt_fragment_q, ipc_rtr_pkt) { + offset = 0; + while (offset < ipc_rtr_pkt->len) { + if (!smd_write_segment_avail(smd_xprtp->channel)) + smd_enable_read_intr(smd_xprtp->channel); + + wait_event(smd_xprtp->write_avail_wait_q, + (smd_write_segment_avail(smd_xprtp->channel) || + smd_xprtp->ss_reset)); + smd_disable_read_intr(smd_xprtp->channel); + spin_lock_irqsave(&smd_xprtp->ss_reset_lock, flags); + if (smd_xprtp->ss_reset) { + spin_unlock_irqrestore( + &smd_xprtp->ss_reset_lock, flags); + IPC_RTR_ERR("%s: %s chnl reset\n", + __func__, xprt->name); + return -ENETRESET; + } + spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock, + flags); + + sz_written = smd_write_segment(smd_xprtp->channel, + ipc_rtr_pkt->data + offset, + (ipc_rtr_pkt->len - offset)); + offset += sz_written; + sz_written = 0; + } + D("%s: Wrote %d bytes over %s\n", + __func__, offset, xprt->name); + } + + if (!smd_write_end(smd_xprtp->channel)) + D("%s: Finished writing\n", __func__); + return len; +} + +static int msm_ipc_router_smd_remote_close(struct msm_ipc_router_xprt *xprt) +{ + int rc; + struct msm_ipc_router_smd_xprt *smd_xprtp = + container_of(xprt, struct msm_ipc_router_smd_xprt, xprt); + + rc = smd_close(smd_xprtp->channel); + if (smd_xprtp->pil) { + subsystem_put(smd_xprtp->pil); + smd_xprtp->pil = NULL; + } + return rc; +} + +static void smd_xprt_sft_close_done(struct msm_ipc_router_xprt *xprt) +{ + struct msm_ipc_router_smd_xprt *smd_xprtp = + container_of(xprt, struct msm_ipc_router_smd_xprt, xprt); + + complete_all(&smd_xprtp->sft_close_complete); +} + +static void smd_xprt_read_data(struct work_struct *work) +{ + int pkt_size, sz_read, sz; + struct sk_buff *ipc_rtr_pkt; + void *data; + unsigned long flags; + struct delayed_work *rwork = to_delayed_work(work); + struct msm_ipc_router_smd_xprt *smd_xprtp = + container_of(rwork, struct msm_ipc_router_smd_xprt, read_work); + + spin_lock_irqsave(&smd_xprtp->ss_reset_lock, flags); + if (smd_xprtp->ss_reset) { + spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock, flags); + if (smd_xprtp->in_pkt) + release_pkt(smd_xprtp->in_pkt); + smd_xprtp->is_partial_in_pkt = 0; + IPC_RTR_ERR("%s: %s channel reset\n", + __func__, smd_xprtp->xprt.name); + return; + } + spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock, flags); + + D("%s pkt_size: %d, read_avail: %d\n", __func__, + smd_cur_packet_size(smd_xprtp->channel), + smd_read_avail(smd_xprtp->channel)); + while ((pkt_size = smd_cur_packet_size(smd_xprtp->channel)) && + smd_read_avail(smd_xprtp->channel)) { + if (!smd_xprtp->is_partial_in_pkt) { + smd_xprtp->in_pkt = create_pkt(NULL); + if (!smd_xprtp->in_pkt) { + IPC_RTR_ERR("%s: Couldn't alloc rr_packet\n", + __func__); + return; + } + smd_xprtp->is_partial_in_pkt = 1; + D("%s: Allocated rr_packet\n", __func__); + } + + if (((pkt_size >= MIN_FRAG_SZ) && + (smd_read_avail(smd_xprtp->channel) < MIN_FRAG_SZ)) || + ((pkt_size < MIN_FRAG_SZ) && + (smd_read_avail(smd_xprtp->channel) < pkt_size))) + return; + + sz = smd_read_avail(smd_xprtp->channel); + do { + ipc_rtr_pkt = alloc_skb(sz, GFP_KERNEL); + if (!ipc_rtr_pkt) { + if (sz <= (PAGE_SIZE/2)) { + queue_delayed_work( + smd_xprtp->smd_xprt_wq, + &smd_xprtp->read_work, + msecs_to_jiffies(100)); + return; + } + sz = sz / 2; + } + } while (!ipc_rtr_pkt); + + D("%s: Allocated the sk_buff of size %d\n", __func__, sz); + data = skb_put(ipc_rtr_pkt, sz); + sz_read = smd_read(smd_xprtp->channel, data, sz); + if (sz_read != sz) { + IPC_RTR_ERR("%s: Couldn't read %s completely\n", + __func__, smd_xprtp->xprt.name); + kfree_skb(ipc_rtr_pkt); + release_pkt(smd_xprtp->in_pkt); + smd_xprtp->is_partial_in_pkt = 0; + return; + } + skb_queue_tail(smd_xprtp->in_pkt->pkt_fragment_q, ipc_rtr_pkt); + smd_xprtp->in_pkt->length += sz_read; + if (sz_read != pkt_size) + smd_xprtp->is_partial_in_pkt = 1; + else + smd_xprtp->is_partial_in_pkt = 0; + + if (!smd_xprtp->is_partial_in_pkt) { + D("%s: Packet size read %d\n", + __func__, smd_xprtp->in_pkt->length); + msm_ipc_router_xprt_notify(&smd_xprtp->xprt, + IPC_ROUTER_XPRT_EVENT_DATA, + (void *)smd_xprtp->in_pkt); + release_pkt(smd_xprtp->in_pkt); + smd_xprtp->in_pkt = NULL; + } + } +} + +static void smd_xprt_open_event(struct work_struct *work) +{ + struct msm_ipc_router_smd_xprt_work *xprt_work = + container_of(work, struct msm_ipc_router_smd_xprt_work, work); + struct msm_ipc_router_smd_xprt *smd_xprtp = + container_of(xprt_work->xprt, + struct msm_ipc_router_smd_xprt, xprt); + unsigned long flags; + + spin_lock_irqsave(&smd_xprtp->ss_reset_lock, flags); + smd_xprtp->ss_reset = 0; + spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock, flags); + msm_ipc_router_xprt_notify(xprt_work->xprt, + IPC_ROUTER_XPRT_EVENT_OPEN, NULL); + D("%s: Notified IPC Router of %s OPEN\n", + __func__, xprt_work->xprt->name); + kfree(xprt_work); +} + +static void smd_xprt_close_event(struct work_struct *work) +{ + struct msm_ipc_router_smd_xprt_work *xprt_work = + container_of(work, struct msm_ipc_router_smd_xprt_work, work); + struct msm_ipc_router_smd_xprt *smd_xprtp = + container_of(xprt_work->xprt, + struct msm_ipc_router_smd_xprt, xprt); + + if (smd_xprtp->in_pkt) { + release_pkt(smd_xprtp->in_pkt); + smd_xprtp->in_pkt = NULL; + } + smd_xprtp->is_partial_in_pkt = 0; + init_completion(&smd_xprtp->sft_close_complete); + msm_ipc_router_xprt_notify(xprt_work->xprt, + IPC_ROUTER_XPRT_EVENT_CLOSE, NULL); + D("%s: Notified IPC Router of %s CLOSE\n", + __func__, xprt_work->xprt->name); + wait_for_completion(&smd_xprtp->sft_close_complete); + kfree(xprt_work); +} + +static void msm_ipc_router_smd_remote_notify(void *_dev, unsigned event) +{ + unsigned long flags; + struct msm_ipc_router_smd_xprt *smd_xprtp; + struct msm_ipc_router_smd_xprt_work *xprt_work; + + smd_xprtp = (struct msm_ipc_router_smd_xprt *)_dev; + if (!smd_xprtp) + return; + + switch (event) { + case SMD_EVENT_DATA: + if (smd_read_avail(smd_xprtp->channel)) + queue_delayed_work(smd_xprtp->smd_xprt_wq, + &smd_xprtp->read_work, 0); + if (smd_write_segment_avail(smd_xprtp->channel)) + wake_up(&smd_xprtp->write_avail_wait_q); + break; + + case SMD_EVENT_OPEN: + xprt_work = kmalloc(sizeof(struct msm_ipc_router_smd_xprt_work), + GFP_ATOMIC); + if (!xprt_work) { + IPC_RTR_ERR( + "%s: Couldn't notify %d event to IPC Router\n", + __func__, event); + return; + } + xprt_work->xprt = &smd_xprtp->xprt; + INIT_WORK(&xprt_work->work, smd_xprt_open_event); + queue_work(smd_xprtp->smd_xprt_wq, &xprt_work->work); + break; + + case SMD_EVENT_CLOSE: + spin_lock_irqsave(&smd_xprtp->ss_reset_lock, flags); + smd_xprtp->ss_reset = 1; + spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock, flags); + wake_up(&smd_xprtp->write_avail_wait_q); + xprt_work = kmalloc(sizeof(struct msm_ipc_router_smd_xprt_work), + GFP_ATOMIC); + if (!xprt_work) { + IPC_RTR_ERR( + "%s: Couldn't notify %d event to IPC Router\n", + __func__, event); + return; + } + xprt_work->xprt = &smd_xprtp->xprt; + INIT_WORK(&xprt_work->work, smd_xprt_close_event); + queue_work(smd_xprtp->smd_xprt_wq, &xprt_work->work); + break; + } +} + +static void *msm_ipc_load_subsystem(uint32_t edge) +{ + void *pil = NULL; + const char *peripheral; + bool loading_disabled; + + loading_disabled = is_pil_loading_disabled(edge); + peripheral = smd_edge_to_pil_str(edge); + if (!IS_ERR_OR_NULL(peripheral) && !loading_disabled) { + pil = subsystem_get(peripheral); + if (IS_ERR(pil)) { + IPC_RTR_ERR("%s: Failed to load %s\n", + __func__, peripheral); + pil = NULL; + } + } + return pil; +} + +/** + * find_smd_xprt_list() - Find xprt item specific to an HSIC endpoint + * @pdev: Platform device registered by HSIC's ipc_bridge driver + * + * @return: pointer to msm_ipc_router_smd_xprt if matching endpoint is found, + * else NULL. + * + * This function is used to find specific xprt item from the global xprt list + */ +static struct msm_ipc_router_smd_xprt * + find_smd_xprt_list(struct platform_device *pdev) +{ + struct msm_ipc_router_smd_xprt *smd_xprtp; + + mutex_lock(&smd_remote_xprt_list_lock_lha1); + list_for_each_entry(smd_xprtp, &smd_remote_xprt_list, list) { + if (!strcmp(pdev->name, smd_xprtp->ch_name) + && (pdev->id == smd_xprtp->edge)) { + mutex_unlock(&smd_remote_xprt_list_lock_lha1); + return smd_xprtp; + } + } + mutex_unlock(&smd_remote_xprt_list_lock_lha1); + return NULL; +} + +/** + * is_pil_loading_disabled() - Check if pil loading a subsystem is disabled + * @edge: Edge that points to the remote subsystem. + * + * @return: true if disabled, false if enabled. + */ +static bool is_pil_loading_disabled(uint32_t edge) +{ + struct msm_ipc_router_smd_xprt *smd_xprtp; + + mutex_lock(&smd_remote_xprt_list_lock_lha1); + list_for_each_entry(smd_xprtp, &smd_remote_xprt_list, list) { + if (smd_xprtp->edge == edge) { + mutex_unlock(&smd_remote_xprt_list_lock_lha1); + return smd_xprtp->disable_pil_loading; + } + } + mutex_unlock(&smd_remote_xprt_list_lock_lha1); + return true; +} + +/** + * msm_ipc_router_smd_remote_probe() - Probe an SMD endpoint + * + * @pdev: Platform device corresponding to SMD endpoint. + * + * @return: 0 on success, standard Linux error codes on error. + * + * This function is called when the underlying SMD driver registers + * a platform device, mapped to SMD endpoint. + */ +static int msm_ipc_router_smd_remote_probe(struct platform_device *pdev) +{ + int rc; + struct msm_ipc_router_smd_xprt *smd_xprtp; + + smd_xprtp = find_smd_xprt_list(pdev); + if (!smd_xprtp) { + IPC_RTR_ERR("%s No device with name %s\n", + __func__, pdev->name); + return -EPROBE_DEFER; + } + if (strcmp(pdev->name, smd_xprtp->ch_name) + || (pdev->id != smd_xprtp->edge)) { + IPC_RTR_ERR("%s wrong item name:%s edge:%d\n", + __func__, smd_xprtp->ch_name, smd_xprtp->edge); + return -ENODEV; + } + smd_xprtp->smd_xprt_wq = + create_singlethread_workqueue(pdev->name); + if (!smd_xprtp->smd_xprt_wq) { + IPC_RTR_ERR("%s: WQ creation failed for %s\n", + __func__, pdev->name); + return -EFAULT; + } + + smd_xprtp->pil = msm_ipc_load_subsystem( + smd_xprtp->edge); + rc = smd_named_open_on_edge(smd_xprtp->ch_name, + smd_xprtp->edge, + &smd_xprtp->channel, + smd_xprtp, + msm_ipc_router_smd_remote_notify); + if (rc < 0) { + IPC_RTR_ERR("%s: Channel open failed for %s\n", + __func__, smd_xprtp->ch_name); + if (smd_xprtp->pil) { + subsystem_put(smd_xprtp->pil); + smd_xprtp->pil = NULL; + } + destroy_workqueue(smd_xprtp->smd_xprt_wq); + return rc; + } + + smd_disable_read_intr(smd_xprtp->channel); + + smsm_change_state(SMSM_APPS_STATE, 0, SMSM_RPCINIT); + + return 0; +} + +/** + * msm_ipc_router_smd_driver_register() - register SMD XPRT drivers + * + * @smd_xprtp: pointer to Ipc router smd xprt structure. + * + * @return: 0 on success, standard Linux error codes on error. + * + * This function is called when a new XPRT is added to register platform + * drivers for new XPRT. + */ +static int msm_ipc_router_smd_driver_register( + struct msm_ipc_router_smd_xprt *smd_xprtp) +{ + int ret; + struct msm_ipc_router_smd_xprt *item; + unsigned already_registered = 0; + + mutex_lock(&smd_remote_xprt_list_lock_lha1); + list_for_each_entry(item, &smd_remote_xprt_list, list) { + if (!strcmp(smd_xprtp->ch_name, item->ch_name)) + already_registered = 1; + } + list_add(&smd_xprtp->list, &smd_remote_xprt_list); + mutex_unlock(&smd_remote_xprt_list_lock_lha1); + + if (!already_registered) { + smd_xprtp->driver.driver.name = smd_xprtp->ch_name; + smd_xprtp->driver.driver.owner = THIS_MODULE; + smd_xprtp->driver.probe = msm_ipc_router_smd_remote_probe; + + ret = platform_driver_register(&smd_xprtp->driver); + if (ret) { + IPC_RTR_ERR( + "%s: Failed to register platform driver [%s]\n", + __func__, smd_xprtp->ch_name); + return ret; + } + } else { + IPC_RTR_ERR("%s Already driver registered %s\n", + __func__, smd_xprtp->ch_name); + } + return 0; +} + +/** + * msm_ipc_router_smd_config_init() - init SMD xprt configs + * + * @smd_xprt_config: pointer to SMD xprt configurations. + * + * @return: 0 on success, standard Linux error codes on error. + * + * This function is called to initialize the SMD XPRT pointer with + * the SMD XPRT configurations either from device tree or static arrays. + */ +static int msm_ipc_router_smd_config_init( + struct msm_ipc_router_smd_xprt_config *smd_xprt_config) +{ + struct msm_ipc_router_smd_xprt *smd_xprtp; + + smd_xprtp = kzalloc(sizeof(struct msm_ipc_router_smd_xprt), GFP_KERNEL); + if (IS_ERR_OR_NULL(smd_xprtp)) { + IPC_RTR_ERR("%s: kzalloc() failed for smd_xprtp id:%s\n", + __func__, smd_xprt_config->ch_name); + return -ENOMEM; + } + + smd_xprtp->xprt.link_id = smd_xprt_config->link_id; + smd_xprtp->xprt_version = smd_xprt_config->xprt_version; + smd_xprtp->edge = smd_xprt_config->edge; + smd_xprtp->xprt_option = smd_xprt_config->xprt_option; + smd_xprtp->disable_pil_loading = smd_xprt_config->disable_pil_loading; + + strlcpy(smd_xprtp->ch_name, smd_xprt_config->ch_name, + SMD_MAX_CH_NAME_LEN); + + strlcpy(smd_xprtp->xprt_name, smd_xprt_config->xprt_name, + XPRT_NAME_LEN); + smd_xprtp->xprt.name = smd_xprtp->xprt_name; + + smd_xprtp->xprt.get_version = + msm_ipc_router_smd_get_xprt_version; + smd_xprtp->xprt.get_option = + msm_ipc_router_smd_get_xprt_option; + smd_xprtp->xprt.read_avail = NULL; + smd_xprtp->xprt.read = NULL; + smd_xprtp->xprt.write_avail = + msm_ipc_router_smd_remote_write_avail; + smd_xprtp->xprt.write = msm_ipc_router_smd_remote_write; + smd_xprtp->xprt.close = msm_ipc_router_smd_remote_close; + smd_xprtp->xprt.sft_close_done = smd_xprt_sft_close_done; + smd_xprtp->xprt.priv = NULL; + + init_waitqueue_head(&smd_xprtp->write_avail_wait_q); + smd_xprtp->in_pkt = NULL; + smd_xprtp->is_partial_in_pkt = 0; + INIT_DELAYED_WORK(&smd_xprtp->read_work, smd_xprt_read_data); + spin_lock_init(&smd_xprtp->ss_reset_lock); + smd_xprtp->ss_reset = 0; + + msm_ipc_router_smd_driver_register(smd_xprtp); + + return 0; +} + +/** + * parse_devicetree() - parse device tree binding + * + * @node: pointer to device tree node + * @smd_xprt_config: pointer to SMD XPRT configurations + * + * @return: 0 on success, -ENODEV on failure. + */ +static int parse_devicetree(struct device_node *node, + struct msm_ipc_router_smd_xprt_config *smd_xprt_config) +{ + int ret; + int edge; + int link_id; + int version; + char *key; + const char *ch_name; + const char *remote_ss; + + key = "qcom,ch-name"; + ch_name = of_get_property(node, key, NULL); + if (!ch_name) + goto error; + strlcpy(smd_xprt_config->ch_name, ch_name, SMD_MAX_CH_NAME_LEN); + + key = "qcom,xprt-remote"; + remote_ss = of_get_property(node, key, NULL); + if (!remote_ss) + goto error; + edge = smd_remote_ss_to_edge(remote_ss); + if (edge < 0) + goto error; + smd_xprt_config->edge = edge; + + key = "qcom,xprt-linkid"; + ret = of_property_read_u32(node, key, &link_id); + if (ret) + goto error; + smd_xprt_config->link_id = link_id; + + key = "qcom,xprt-version"; + ret = of_property_read_u32(node, key, &version); + if (ret) + goto error; + smd_xprt_config->xprt_version = version; + + key = "qcom,fragmented-data"; + smd_xprt_config->xprt_option = of_property_read_bool(node, key); + + key = "qcom,disable-pil-loading"; + smd_xprt_config->disable_pil_loading = of_property_read_bool(node, key); + + scnprintf(smd_xprt_config->xprt_name, XPRT_NAME_LEN, "%s_%s", + remote_ss, smd_xprt_config->ch_name); + + return 0; + +error: + IPC_RTR_ERR("%s: missing key: %s\n", __func__, key); + return -ENODEV; +} + +/** + * msm_ipc_router_smd_xprt_probe() - Probe an SMD xprt + * + * @pdev: Platform device corresponding to SMD xprt. + * + * @return: 0 on success, standard Linux error codes on error. + * + * This function is called when the underlying device tree driver registers + * a platform device, mapped to an SMD transport. + */ +static int msm_ipc_router_smd_xprt_probe(struct platform_device *pdev) +{ + int ret; + struct msm_ipc_router_smd_xprt_config smd_xprt_config; + + if (pdev) { + if (pdev->dev.of_node) { + mutex_lock(&smd_remote_xprt_list_lock_lha1); + ipc_router_smd_xprt_probe_done = 1; + mutex_unlock(&smd_remote_xprt_list_lock_lha1); + + ret = parse_devicetree(pdev->dev.of_node, + &smd_xprt_config); + if (ret) { + IPC_RTR_ERR("%s: Failed to parse device tree\n", + __func__); + return ret; + } + + ret = msm_ipc_router_smd_config_init(&smd_xprt_config); + if (ret) { + IPC_RTR_ERR("%s init failed\n", __func__); + return ret; + } + } + } + return 0; +} + +/** + * ipc_router_smd_xprt_probe_worker() - probe worker for non DT configurations + * + * @work: work item to process + * + * This function is called by schedule_delay_work after 3sec and check if + * device tree probe is done or not. If device tree probe fails the default + * configurations read from static array. + */ +static void ipc_router_smd_xprt_probe_worker(struct work_struct *work) +{ + int i, ret; + + BUG_ON(ARRAY_SIZE(smd_xprt_cfg) != NUM_SMD_XPRTS); + + mutex_lock(&smd_remote_xprt_list_lock_lha1); + if (!ipc_router_smd_xprt_probe_done) { + mutex_unlock(&smd_remote_xprt_list_lock_lha1); + for (i = 0; i < ARRAY_SIZE(smd_xprt_cfg); i++) { + ret = msm_ipc_router_smd_config_init(&smd_xprt_cfg[i]); + if (ret) + IPC_RTR_ERR(" %s init failed config idx %d\n", + __func__, i); + } + mutex_lock(&smd_remote_xprt_list_lock_lha1); + } + mutex_unlock(&smd_remote_xprt_list_lock_lha1); +} + +static struct of_device_id msm_ipc_router_smd_xprt_match_table[] = { + { .compatible = "qcom,ipc_router_smd_xprt" }, + {}, +}; + +static struct platform_driver msm_ipc_router_smd_xprt_driver = { + .probe = msm_ipc_router_smd_xprt_probe, + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + .of_match_table = msm_ipc_router_smd_xprt_match_table, + }, +}; + +static int __init msm_ipc_router_smd_xprt_init(void) +{ + int rc; + + rc = platform_driver_register(&msm_ipc_router_smd_xprt_driver); + if (rc) { + IPC_RTR_ERR( + "%s: msm_ipc_router_smd_xprt_driver register failed %d\n", + __func__, rc); + return rc; + } + + INIT_DELAYED_WORK(&ipc_router_smd_xprt_probe_work, + ipc_router_smd_xprt_probe_worker); + schedule_delayed_work(&ipc_router_smd_xprt_probe_work, + msecs_to_jiffies(IPC_ROUTER_SMD_XPRT_WAIT_TIMEOUT)); + return 0; +} + +module_init(msm_ipc_router_smd_xprt_init); +MODULE_DESCRIPTION("IPC Router SMD XPRT"); +MODULE_LICENSE("GPL v2"); -- 2.11.0