platform: msm_shared: Add RPM GLink Client

Glink Client provides api's to drivers that want to
turn on/off regulators and clocks controlled by RPM.
Internally it provides functions to opens channels
to glink, formats request in RPM messaging format
and also provides functions to tear down the connection
before booting to HLOS

Change-Id: I4cddee5c38906a675b07920983a62a7c90a47d90
diff --git a/platform/msm_shared/rpm-glink.c b/platform/msm_shared/rpm-glink.c
new file mode 100644
index 0000000..01cae61
--- /dev/null
+++ b/platform/msm_shared/rpm-glink.c
@@ -0,0 +1,379 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * 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.
+ *     * Neither the name of The Linux Fundation, Inc. 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 "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER 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.
+ *
+ */
+
+#include <rpm-glink.h>
+#include <smd.h>
+#include <glink.h>
+#include <glink_rpm.h>
+#include <xport_rpm.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <arch/defines.h>
+#include <debug.h>
+#include <stdlib.h>
+#include <platform/timer.h>
+#include <platform/interrupts.h>
+#include <platform/clock.h>
+#include <platform/iomap.h>
+#include <platform/irqs.h>
+#include <pm8x41.h>
+#include <kernel/event.h>
+
+#define RPM_REQ_MAGIC 0x00716572
+#define RPM_CMD_MAGIC 0x00646d63
+#define REQ_MSG_LENGTH 0x14
+#define CMD_MSG_LENGTH 0x08
+#define ACK_MSG_LENGTH 0x0C
+
+glink_handle_type rpm_glink_port, ssr_glink_port;
+static uint32_t msg_id;
+static event_t wait_for_data;
+
+extern glink_err_type glink_wait_link_down(glink_handle_type handle);
+
+glink_err_type rpm_glink_send_data(uint32_t *data, uint32_t len, msg_type type)
+{
+	rpm_req req;
+	rpm_cmd cmd;
+	glink_err_type send_err = 0;
+	uint32_t len_to_rpm = 0;
+	void *rpm_data = NULL;
+	event_init(&wait_for_data, false, EVENT_FLAG_AUTOUNSIGNAL);
+
+	switch(type)
+	{
+		case RPM_REQUEST_TYPE:
+			req.hdr.type = RPM_REQ_MAGIC;
+			req.hdr.len = len + REQ_MSG_LENGTH;//20
+			req.req_hdr.id = ++msg_id;
+			req.req_hdr.set = 0;
+			req.req_hdr.resourceType = data[RESOURCETYPE];
+			req.req_hdr.resourceId = data[RESOURCEID];
+			req.req_hdr.dataLength = len;
+
+			fill_kvp_object(&req.data, data, len);
+			len_to_rpm = req.req_hdr.dataLength + 0x28;
+
+			rpm_data = (void*) malloc(len_to_rpm);
+			ASSERT(rpm_data);
+			memset(rpm_data, 0, len_to_rpm);
+			memcpy(rpm_data, &req.hdr, sizeof(rpm_gen_hdr));
+			memcpy(rpm_data + sizeof(rpm_gen_hdr), &req.req_hdr, sizeof(rpm_req_hdr));
+			memcpy(rpm_data + sizeof(rpm_gen_hdr)+ sizeof(rpm_req_hdr), req.data, len);
+
+			// Send Data Request to RPM
+			send_err = glink_tx(rpm_glink_port, NULL, (const void *)rpm_data, len_to_rpm, 0);
+			if (send_err)
+			{
+				dprintf(CRITICAL, "%s:%d, Glink tx error\n", __func__, __LINE__);
+				free(rpm_data);
+				free_kvp_object(&req.data);
+				break;
+			}
+#ifdef DEBUG_GLINK
+			dprintf(INFO, "%s:%d, Wait till we receive response from RPM\n", __func__, __LINE__);
+#endif
+			event_wait(&wait_for_data);
+			free(rpm_data);
+			free_kvp_object(&req.data);
+			break;
+		case RPM_CMD_TYPE:
+			cmd.hdr.type = RPM_CMD_MAGIC;
+			cmd.hdr.len = CMD_MSG_LENGTH;//0x8;
+			len_to_rpm  = sizeof(rpm_cmd);
+
+			fill_kvp_object(&cmd.data, data, len);
+			send_err = glink_tx(rpm_glink_port, NULL, (const void *)&cmd, len_to_rpm, 0);
+			if (send_err)
+				dprintf(CRITICAL, "%s:%d, Glink tx error\n", __func__, __LINE__);
+			free_kvp_object(&cmd.data);
+			break;
+		default:
+			dprintf(CRITICAL, "Invalid RPM Request\n");
+			break;
+	}
+
+	return send_err;
+}
+
+uint32_t rpm_glink_recv_data(char *rx_buffer, uint32_t* len)
+{
+	rpm_ack_msg *resp;
+	msg_type type = 0;
+	uint32_t ret = 0;
+	/* As per the current design rpm response does not exceed 20 bytes */
+	if (rx_buffer == NULL)
+	{
+		dprintf(CRITICAL, "Invalid pointer to data received from RPM\n");
+		return 99;
+	}
+	resp = (rpm_ack_msg *)rx_buffer;
+
+	arch_invalidate_cache_range((addr_t)resp, sizeof(rpm_gen_hdr));
+
+	if(resp->hdr.type == RPM_CMD_MAGIC)
+	{
+		type = RPM_CMD_TYPE;
+	}
+	else if(resp->hdr.type == RPM_REQ_MAGIC)
+	{
+		type = RPM_REQUEST_TYPE;
+	}
+
+	if (type == RPM_CMD_TYPE && resp->hdr.len == ACK_MSG_LENGTH)
+	{
+		dprintf(SPEW, "Received SUCCESS CMD ACK\n");
+	}
+	else if (type == RPM_REQUEST_TYPE && resp->hdr.len == ACK_MSG_LENGTH)
+	{
+		dprintf(SPEW, "Received SUCCESS REQ ACK \n");
+	}
+	else
+	{
+		ret = 1;
+		dprintf(CRITICAL, "Received ERROR ACK \n");
+	}
+
+	if(!ret)
+	{
+		ret = sizeof(rpm_gen_hdr) + sizeof(kvp_data);
+	}
+#ifdef DEBUG_GLINK
+	dprintf(INFO, "%s:%d Return value %u\n", __func__, __LINE__, ret);
+#endif
+	return ret;
+}
+
+void rpm_vector_glink_ssr_isr(glink_handle_type port, void *unused_open_data, void *unused_pkt_priv,
+							void *buffer, size_t size, size_t intent_used,
+							glink_buffer_provider_fn vprovider, glink_buffer_provider_fn pprovider)
+{
+	char rx_buffer[12];
+	char *return_buffer = NULL;
+	uint32_t ret = 0;
+	uint32_t offset = 0;
+	size_t return_size = 0;
+
+#ifdef DEBUG_GLINK
+	dprintf(INFO, "RPM Vector GLINK SSR ISR\n");
+#endif
+	if (size == 0)
+	{
+		dprintf(CRITICAL, "Invalid size of RPM response\n");
+		ASSERT(0);
+	}
+	if (size > sizeof(rx_buffer))
+	{
+		dprintf(CRITICAL, "Need larger RPM rx buffer. Size of Local Buffer: %u\tSize of RX Buffer: %u\n", size, sizeof(rx_buffer));
+		ASSERT(0);
+	}
+	do
+	{
+		return_buffer = vprovider(buffer, offset, &return_size);
+		if(return_buffer)
+		{
+			memcpy(rx_buffer+offset,return_buffer, return_size);
+			offset += return_size;
+		}
+	} while(return_buffer);
+	ret = rpm_glink_recv_data(rx_buffer,(uint32_t *)&size);
+	if(ret)
+	{
+		dprintf(CRITICAL, "Return value from recv_data: %x\n", ret);
+	}
+	// Release the mutex
+#ifdef DEBUG_GLINK
+	dprintf(INFO, "Received Data from RPM\n");
+#endif
+	event_signal(&wait_for_data, false);
+}
+
+void rpm_vector_glink_isr(glink_handle_type port, void *unused_open_data, void *unused_pkt_priv,
+							void *buffer, size_t size, size_t intent_used,
+							glink_buffer_provider_fn vprovider, glink_buffer_provider_fn pprovider)
+{
+	char rx_buffer[64];
+	char *return_buffer = NULL;
+	uint32_t ret = 0;
+	uint32_t offset = 0;
+	size_t return_size = 0;
+#ifdef DEBUG_GLINK
+	dprintf(INFO, "RPM Vector GLINK ISR\n");
+#endif
+	if (size == 0)
+	{
+		dprintf(CRITICAL, "Invalid size of RPM response\n");
+		ASSERT(0);
+	}
+	if (size > sizeof(rx_buffer))
+	{
+		dprintf(CRITICAL, "Need larger RPM rx buffer. Size of Local Buffer: %u\tSize of RX Buffer: %u\n", size, sizeof(rx_buffer));
+		ASSERT(0);
+	}
+	do
+	{
+		return_buffer = vprovider(buffer, offset, &return_size);
+		if(return_buffer)
+		{
+			memcpy(rx_buffer+offset,return_buffer, return_size);
+			offset += return_size;
+		}
+	} while(return_buffer);
+	ret = rpm_glink_recv_data(rx_buffer, (uint32_t *)&size);
+	if(ret)
+	{
+		dprintf(CRITICAL, "Return value from recv_data: %x\n", ret);
+	}
+	// Release the mutex
+#ifdef DEBUG_GLINK
+	dprintf(INFO, "Received Data from RPM\n");
+#endif
+	event_signal(&wait_for_data, false);
+}
+
+void rpm_glink_notify_state_isr(glink_handle_type handle, void *data, glink_channel_event_type event)
+{
+	if(event == GLINK_CONNECTED)
+	{
+		dprintf(INFO, "Glink Connection between APPS and RPM established\n");
+		return;
+	}
+}
+
+void rpm_glink_tx_done_isr(void)
+{
+	//empty function for tx_done cb. Nothing required here for now since we are always in
+	//"single-threaded" operation of sending GLink requests
+	return;
+}
+
+void rpm_glink_open(glink_link_info_type *link_info, void* priv)
+{
+	glink_err_type ret;
+	glink_open_config_type glink_open_cfg = {0}, glink_ssr_open_cfg = {0};
+
+	// Open channel for tx
+	glink_open_cfg.name = "rpm_requests";
+	glink_open_cfg.remote_ss = link_info->remote_ss;
+	glink_open_cfg.notify_rx = NULL;
+	glink_open_cfg.notify_rxv = (glink_rxv_notification_cb)rpm_vector_glink_isr;
+	glink_open_cfg.notify_tx_done = (glink_tx_notification_cb)rpm_glink_tx_done_isr;
+	glink_open_cfg.notify_state = (glink_state_notification_cb)rpm_glink_notify_state_isr;
+	glink_open_cfg.priv = NULL;
+	ret = glink_open(&glink_open_cfg, &rpm_glink_port);
+	if (ret == GLINK_STATUS_SUCCESS)
+		dprintf(INFO, "Opening RPM Glink Port success\n");
+	else
+	{
+		dprintf(CRITICAL, "Opening RPM Glink Port failure %d\n", ret);
+		ASSERT(0);
+	}
+
+	// Open Channel for tear down
+	glink_ssr_open_cfg.name = "glink_ssr";
+	glink_ssr_open_cfg.remote_ss = link_info->remote_ss;
+	glink_ssr_open_cfg.notify_rx = NULL;
+	glink_ssr_open_cfg.notify_rxv = (glink_rxv_notification_cb)rpm_vector_glink_ssr_isr;
+	glink_ssr_open_cfg.notify_tx_done = (glink_tx_notification_cb)rpm_glink_tx_done_isr;
+	glink_ssr_open_cfg.notify_state = (glink_state_notification_cb)rpm_glink_notify_state_isr;
+	glink_ssr_open_cfg.priv = NULL;
+
+	ret = glink_open(&glink_ssr_open_cfg, &ssr_glink_port);
+	if (ret == GLINK_STATUS_SUCCESS)
+		dprintf(INFO, "Opening SSR Glink Port success\n");
+	else
+	{
+		dprintf(CRITICAL, "Opening SSR Glink Port failure %d\n", ret);
+		ASSERT(0);
+	}
+
+}
+
+void rpm_glink_init()
+{
+	glink_err_type ret;
+	glink_link_id_type link_id;
+
+	dprintf(INFO, "RPM GLink Init\n");
+	// Initialize RPM transport
+	ret = xport_rpm_init(NULL);
+	if (ret == GLINK_STATUS_SUCCESS)
+	{
+		unmask_interrupt(GLINK_IPC_IRQ);
+		GLINK_LINK_ID_STRUCT_INIT(link_id);
+		link_id.remote_ss = "rpm";
+		link_id.link_notifier = (glink_link_state_notif_cb)rpm_glink_open;
+		glink_register_link_state_cb(&link_id, NULL);
+	}
+	else
+	{
+		dprintf(CRITICAL, "RPM Glink Init Failure\n");
+		ASSERT(0);
+	}
+}
+
+void rpm_glink_uninit()
+{
+	rpm_ssr_req req;
+	glink_err_type ret;
+	uint32_t len_to_rpm, loop = 100000;
+
+	// update ssr request
+	req.version = 0;
+	req.cmd = 0;
+	req.seqnumber = 0;
+	memset(req.name, 0, sizeof(req.name));
+	strncpy(req.name, "apss", 4);
+	req.namelength = strlen(req.name);
+	len_to_rpm = sizeof(rpm_ssr_req);
+	dprintf(INFO, "RPM GLINK UnInit\n");
+	ret = glink_tx(ssr_glink_port, NULL, (const void *)&req, len_to_rpm, 0);
+
+	if (ret)
+	{
+		dprintf(CRITICAL, "Glink SSR Channel: Tx for link tear down request failure\n");
+		ASSERT(0);
+	}
+
+#ifdef DEBUG_GLINK
+	dprintf(INFO, "%s:%d, Wait till we receive response from RPM\n", __func__, __LINE__);
+#endif
+	// loop till the FIFO indices are cleared
+	while((ret = glink_wait_link_down(ssr_glink_port)) && loop)
+	{
+		loop--;
+		mdelay(1);
+		continue;
+	}
+	if (!loop)
+	{
+		dprintf(INFO, "%s:%d, Tearing down Glink SSR Channel Timed out\n", __func__, __LINE__);
+		ASSERT(0);
+	}
+}