<commit_before>/* Copyright (c) 2011-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/types.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/stat.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/usb/msm_hsusb.h>
#include <mach/usb_bam.h>
#include <mach/sps.h>
#include <mach/ipa.h>
#include <linux/workqueue.h>
#include <linux/dma-mapping.h>
#include <mach/msm_smsm.h>
#include <linux/pm_runtime.h>

#define USB_THRESHOLD 512
#define USB_BAM_MAX_STR_LEN 50
#define USB_BAM_TIMEOUT (10*HZ)

#define USB_BAM_NR_PORTS	4

enum usb_bam_sm {
	USB_BAM_SM_INIT = 0,
	USB_BAM_SM_PLUG_NOTIFIED,
	USB_BAM_SM_PLUG_ACKED,
	USB_BAM_SM_UNPLUG_NOTIFIED,
};

struct usb_bam_peer_handshake_info {
	enum usb_bam_sm state;
	bool client_ready;
	bool ack_received;
	int pending_work;
	struct usb_bam_event_info reset_event;
};

struct usb_bam_sps_type {
	struct sps_bam_props usb_props;
	struct sps_pipe **sps_pipes;
	struct sps_connect *sps_connections;
};

/**
* struct usb_bam_ctx_type - represents the usb bam driver entity
* @usb_bam_sps: holds the sps pipes the usb bam driver holds
*	against the sps driver.
* @usb_bam_pdev: the platfrom device that represents the usb bam.
* @usb_bam_wq: Worqueue used for managing states of reset against
*	a peer bam.
* @qscratch_ram1_reg: The memory region mapped to the qscratch
*	registers.
* @max_connections: The maximum number of pipes that are configured
*	in the platform data.
* @mem_clk: Clock that controls the usb bam driver memory in
*	case the usb bam uses its private memory for the pipes.
* @mem_iface_clk: Clock that controls the usb bam private memory in
*	case the usb bam uses its private memory for the pipes.
* @qdss_core_name: Stores the name of the core ("ssusb", "hsusb" or "hsic")
*	that it used as a peer of the qdss in bam2bam mode.
* @h_bam: This array stores for each BAM ("ssusb", "hsusb" or "hsic") the
*	handle/device of the sps driver.
* @pipes_enabled_per_bam: This array stores for each BAM
*	("ssusb", "hsusb" or "hsic") the number of pipes currently enabled.
* @inactivity_timer_ms: The timeout configuration per each bam for inactivity
*	timer feature.
* @is_bam_inactivity: Is there no activity on all pipes belongs to a
*	specific bam. (no activity = no data is pulled or pushed
*	from/into ones of the pipes).
*/
struct usb_bam_ctx_type {
	struct usb_bam_sps_type usb_bam_sps;
	struct platform_device *usb_bam_pdev;
	struct workqueue_struct *usb_bam_wq;
	void __iomem *qscratch_ram1_reg;
	u8 max_connections;
	struct clk *mem_clk;
	struct clk *mem_iface_clk;
	char qdss_core_name[USB_BAM_MAX_STR_LEN];
	u32 h_bam[MAX_BAMS];
	u8 pipes_enabled_per_bam[MAX_BAMS];
	u32 inactivity_timer_ms[MAX_BAMS];
	bool is_bam_inactivity[MAX_BAMS];
	struct completion reset_done;
};

static char *bam_enable_strings[MAX_BAMS] = {
	[SSUSB_BAM] = "ssusb",
	[HSUSB_BAM] = "hsusb",
	[HSIC_BAM]  = "hsic",
};

static enum usb_bam ipa_rm_bams[] = {HSUSB_BAM, HSIC_BAM, SSUSB_BAM};

static enum ipa_client_type ipa_rm_resource_prod[MAX_BAMS] = {
	[HSUSB_BAM] = IPA_RM_RESOURCE_USB_PROD,
	[HSIC_BAM]  = IPA_RM_RESOURCE_HSIC_PROD,
	[SSUSB_BAM] = IPA_RM_RESOURCE_USB_PROD,
};

static enum ipa_client_type ipa_rm_resource_cons[MAX_BAMS] = {
	[HSUSB_BAM] = IPA_RM_RESOURCE_USB_CONS,
	[HSIC_BAM]  = IPA_RM_RESOURCE_HSIC_CONS,
	[SSUSB_BAM] = IPA_RM_RESOURCE_USB_CONS,
};

static int usb_cons_request_resource(void);
static int usb_cons_release_resource(void);
static int ss_usb_cons_request_resource(void);
static int ss_usb_cons_release_resource(void);
static int hsic_cons_request_resource(void);
static int hsic_cons_release_resource(void);


static int (*request_resource_cb[MAX_BAMS])(void) = {
	[HSUSB_BAM] = usb_cons_request_resource,
	[HSIC_BAM]  = hsic_cons_request_resource,
	[SSUSB_BAM] = ss_usb_cons_request_resource,
};

static int (*release_resource_cb[MAX_BAMS])(void)  = {
	[HSUSB_BAM] = usb_cons_release_resource,
	[HSIC_BAM]  = hsic_cons_release_resource,
	[SSUSB_BAM] = ss_usb_cons_release_resource,
};

struct usb_bam_ipa_handshake_info {
	enum ipa_rm_event cur_prod_state;
	enum ipa_rm_event cur_cons_state;

	enum usb_bam_mode cur_bam_mode;
	enum usb_bam bam_type;
	bool lpm_wait_handshake;
	int connect_complete;
	bool lpm_wait_pipes;
	int bus_suspend;
	bool disconnected;
	bool in_lpm;
	bool pending_lpm;
	u8 prod_pipes_enabled_per_bam;

	int (*wake_cb)(void *);
	void *wake_param;

	u32 suspend_src_idx[USB_BAM_NR_PORTS];
	u32 suspend_dst_idx[USB_BAM_NR_PORTS];
	u32 resume_src_idx[USB_BAM_NR_PORTS];
	u32 resume_dst_idx[USB_BAM_NR_PORTS];

	u32 pipes_to_suspend;
	u32 pipes_suspended;
	u32 pipes_resumed;

	struct completion prod_avail;
	struct completion prod_released;

	struct mutex suspend_resume_mutex;
	struct work_struct resume_work;
	struct work_struct suspend_work;
	struct work_struct finish_suspend_work;
};

struct usb_bam_host_info {
	struct device *dev;
	bool in_lpm;
};

static spinlock_t usb_bam_ipa_handshake_info_lock;
static struct usb_bam_ipa_handshake_info info[MAX_BAMS];
static spinlock_t usb_bam_peer_handshake_info_lock;
static struct usb_bam_peer_handshake_info peer_handshake_info;
static spinlock_t usb_bam_lock; /* Protect ctx and usb_bam_connections */
static struct usb_bam_pipe_connect *usb_bam_connections;
static struct usb_bam_ctx_type ctx;
static struct usb_bam_host_info host_info[MAX_BAMS];
static struct device *usb_device;

static int __usb_bam_register_wake_cb(int idx, int (*callback)(void *user),
	void *param, bool trigger_cb_per_pipe);
static void wait_for_prod_release(enum usb_bam cur_bam);

void msm_bam_set_hsic_host_dev(struct device *dev)
{
	if (dev) {
		/* Hold the device until allowing lpm */
		info[HSIC_BAM].in_lpm = false;
		pr_debug("%s: Getting hsic device %x\n", __func__,
			(int)dev);
		pm_runtime_get(dev);
	} else if (host_info[HSIC_BAM].dev) {
		pr_debug("%s: Putting hsic device %x\n", __func__,
			(int)host_info[HSIC_BAM].dev);
		/* Just free previous device*/
		info[HSIC_BAM].in_lpm = true;
		pm_runtime_put(host_info[HSIC_BAM].dev);
	}

	host_info[HSIC_BAM].dev = dev;
	host_info[HSIC_BAM].in_lpm = false;
}

void msm_bam_set_usb_dev(struct device *dev)
{
	pr_debug("%s: Updating usb device for power managment\n", __func__);
	usb_device = dev;
}

void msm_bam_set_usb_host_dev(struct device *dev)
{
	host_info[HSUSB_BAM].dev = dev;
	host_info[HSUSB_BAM].in_lpm = false;
}

static int get_bam_type_from_core_name(const char *name)
{
	if (strnstr(name, bam_enable_strings[SSUSB_BAM],
			USB_BAM_MAX_STR_LEN) ||
		strnstr(name, "dwc3", USB_BAM_MAX_STR_LEN))
		return SSUSB_BAM;
	else if (strnstr(name, bam_enable_strings[HSIC_BAM],
			USB_BAM_MAX_STR_LEN))
		return HSIC_BAM;
	else if (strnstr(name, bam_enable_strings[HSUSB_BAM],
			USB_BAM_MAX_STR_LEN) ||
		strnstr(name, "ci", USB_BAM_MAX_STR_LEN))
		return HSUSB_BAM;

	pr_err("%s: invalid BAM name(%s)\n", __func__, name);
	return -EINVAL;
}

static bool bam_use_private_mem(enum usb_bam bam)
{
	int i;

	for (i = 0; i < ctx.max_connections; i++)
		if (usb_bam_connections[i].bam_type == bam &&
			usb_bam_connections[i].mem_type == USB_PRIVATE_MEM)
				return true;

	return false;
}

static void usb_bam_set_inactivity_timer(enum usb_bam bam)
{
	struct sps_timer_ctrl timer_ctrl;
	struct usb_bam_pipe_connect *pipe_connect;
	struct sps_pipe *pipe = NULL;
	int i;

	pr_debug("%s: enter\n", __func__);

	/*
	 * Since we configure global incativity timer for all pipes
	 * and not per each pipe, it is enough to use some pipe
	 * handle associated with this bam, so just find the first one.
	 * This pipe handle is required due to SPS driver API we use below.
	 */
	for (i = 0; i < ctx.max_connections; i++) {
		pipe_connect = &usb_bam_connections[i];
		if (pipe_connect->bam_type == bam &&
		    pipe_connect->enabled) {
			pipe = ctx.usb_bam_sps.sps_pipes[i];
			break;
		}
	}

	if (!pipe) {
		pr_warning("%s: Bam %s has no connected pipes\n", __func__,
			bam_enable_strings[bam]);
		return;
	}

	timer_ctrl.op = SPS_TIMER_OP_CONFIG;
	timer_ctrl.mode = SPS_TIMER_MODE_ONESHOT;
	timer_ctrl.timeout_msec = ctx.inactivity_timer_ms[bam];
	sps_timer_ctrl(pipe, &timer_ctrl, NULL);

	timer_ctrl.op = SPS_TIMER_OP_RESET;
	sps_timer_ctrl(pipe, &timer_ctrl, NULL);
}

static int connect_pipe(u8 idx, u32 *usb_pipe_idx)
{
	int ret, ram1_value;
	enum usb_bam bam;
	struct usb_bam_sps_type usb_bam_sps = ctx.usb_bam_sps;
	struct sps_pipe **pipe = &(usb_bam_sps.sps_pipes[idx]);
	struct sps_connect *sps_connection = &usb_bam_sps.sps_connections[idx];
	struct msm_usb_bam_platform_data *pdata =
		ctx.usb_bam_pdev->dev.platform_data;
	struct usb_bam_pipe_connect *pipe_connect = &usb_bam_connections[idx];
	enum usb_bam_pipe_dir dir = pipe_connect->dir;
	struct sps_mem_buffer *data_buf = &(pipe_connect->data_mem_buf);
	struct sps_mem_buffer *desc_buf = &(pipe_connect->desc_mem_buf);

	*pipe = sps_alloc_endpoint();
	if (*pipe == NULL) {
		pr_err("%s: sps_alloc_endpoint failed\n", __func__);
		return -ENOMEM;
	}

	ret = sps_get_config(*pipe, sps_connection);
	if (ret) {
		pr_err("%s: tx get config failed %d\n", __func__, ret);
		goto free_sps_endpoint;
	}

	ret = sps_phy2h(pipe_connect->src_phy_addr, &(sps_connection->source));
	if (ret) {
		pr_err("%s: sps_phy2h failed (src BAM) %d\n", __func__, ret);
		goto free_sps_endpoint;
	}

	sps_connection->src_pipe_index = pipe_connect->src_pipe_index;
	ret = sps_phy2h(pipe_connect->dst_phy_addr,
		&(sps_connection->destination));
	if (ret) {
		pr_err("%s: sps_phy2h failed (dst BAM) %d\n", __func__, ret);
		goto free_sps_endpoint;
	}
	sps_connection->dest_pipe_index = pipe_connect->dst_pipe_index;

	if (dir == USB_TO_PEER_PERIPHERAL) {
		sps_connection->mode = SPS_MODE_SRC;
		*usb_pipe_idx = pipe_connect->src_pipe_index;
	} else {
		sps_connection->mode = SPS_MODE_DEST;
		*usb_pipe_idx = pipe_connect->dst_pipe_index;
	}

	switch (pipe_connect->mem_type) {
	case SPS_PIPE_MEM:
		pr_debug("%s: USB BAM using SPS pipe memory\n", __func__);
		ret = sps_setup_bam2bam_fifo(
			data_buf,
			pipe_connect->data_fifo_base_offset,
			pipe_connect->data_fifo_size, 1);
		if (ret) {
			pr_err("%s: data fifo setup failure %d\n", __func__,
				ret);
			goto free_sps_endpoint;
		}

		ret = sps_setup_bam2bam_fifo(
			desc_buf,
			pipe_connect->desc_fifo_base_offset,
			pipe_connect->desc_fifo_size, 1);
		if (ret) {
			pr_err("%s: desc. fifo setup failure %d\n", __func__,
				ret);
			goto free_sps_endpoint;
		}
		break;
	case USB_PRIVATE_MEM:
		pr_debug("%s: USB BAM using private memory\n", __func__);

		if (IS_ERR(ctx.mem_clk) || IS_ERR(ctx.mem_iface_clk)) {
			pr_err("%s: Failed to enable USB mem_clk\n", __func__);
			ret = IS_ERR(ctx.mem_clk);
			goto free_sps_endpoint;
		}

		clk_prepare_enable(ctx.mem_clk);
		clk_prepare_enable(ctx.mem_iface_clk);

		/*
		 * Enable USB PRIVATE RAM to be used for BAM FIFOs
		 * HSUSB: Only RAM13 is used for BAM FIFOs
		 * SSUSB: RAM11, 12, 13 are used for BAM FIFOs
		 */
		bam = pipe_connect->bam_type;

		if (bam == HSUSB_BAM)
			ram1_value = 0x4;
		else
			ram1_value = 0x7;

		pr_debug("Writing 0x%x to QSCRATCH_RAM1\n", ram1_value);
		writel_relaxed(ram1_value, ctx.qscratch_ram1_reg);
		/* fall through */
	case OCI_MEM:
		if (pipe_connect->mem_type == OCI_MEM)
			pr_debug("%s: USB BAM using oci memory\n", __func__);

		data_buf->phys_base =
			pipe_connect->data_fifo_base_offset +
				pdata->usb_bam_fifo_baseaddr;
		data_buf->size = pipe_connect->data_fifo_size;
		data_buf->base =
			ioremap(data_buf->phys_base, data_buf->size);
		memset(data_buf->base, 0, data_buf->size);

		desc_buf->phys_base =
			pipe_connect->desc_fifo_base_offset +
				pdata->usb_bam_fifo_baseaddr;
		desc_buf->size = pipe_connect->desc_fifo_size;
		desc_buf->base =
			ioremap(desc_buf->phys_base, desc_buf->size);
		memset(desc_buf->base, 0, desc_buf->size);
		break;
	case SYSTEM_MEM:
		pr_debug("%s: USB BAM using system memory\n", __func__);
		/* BAM would use system memory, allocate FIFOs */
		data_buf->size = pipe_connect->data_fifo_size;
		data_buf->base =
			dma_alloc_coherent(&ctx.usb_bam_pdev->dev,
			pipe_connect->data_fifo_size,
			&(data_buf->phys_base),
			0);
		memset(data_buf->base, 0, pipe_connect->data_fifo_size);

		desc_buf->size = pipe_connect->desc_fifo_size;
		desc_buf->base =
			dma_alloc_coherent(&ctx.usb_bam_pdev->dev,
			pipe_connect->desc_fifo_size,
			&(desc_buf->phys_base),
			0);
		memset(desc_buf->base, 0, pipe_connect->desc_fifo_size);
		break;
	default:
		pr_err("%s: invalid mem type\n", __func__);
		goto free_sps_endpoint;
	}

	sps_connection->data = *data_buf;
	sps_connection->desc = *desc_buf;
	sps_connection->event_thresh = 16;
	sps_connection->options = SPS_O_AUTO_ENABLE;

	ret = sps_connect(*pipe, sps_connection);
	if (ret < 0) {
		pr_err("%s: sps_connect failed %d\n", __func__, ret);
		goto error;
	}

	return 0;

error:
	sps_disconnect(*pipe);
free_sps_endpoint:
	sps_free_endpoint(*pipe);
	return ret;
}

static int connect_pipe_ipa(u8 idx,
			struct usb_bam_connect_ipa_params *ipa_params)
{
	int ret;
	struct usb_bam_sps_type usb_bam_sps = ctx.usb_bam_sps;
	enum usb_bam_pipe_dir dir = ipa_params->dir;
	struct sps_pipe **pipe = &(usb_bam_sps.sps_pipes[idx]);
	struct sps_connect *sps_connection = &usb_bam_sps.sps_connections[idx];
	struct usb_bam_pipe_connect *pipe_connect = &usb_bam_connections[idx];

	struct ipa_connect_params ipa_in_params;
	struct ipa_sps_params sps_out_params;
	u32 usb_handle, usb_phy_addr;
	u32 clnt_hdl = 0;

	memset(&ipa_in_params, 0, sizeof(ipa_in_params));
	memset(&sps_out_params, 0, sizeof(sps_out_params));

	if (dir == USB_TO_PEER_PERIPHERAL) {
		usb_phy_addr = pipe_connect->src_phy_addr;
		ipa_in_params.client_ep_idx = pipe_connect->src_pipe_index;
		ipa_in_params.client = ipa_params->src_client;
	} else {
		usb_phy_addr = pipe_connect->dst_phy_addr;
		ipa_in_params.client_ep_idx = pipe_connect->dst_pipe_index;
		ipa_in_params.client = ipa_params->dst_client;
	}
	/* Get HSUSB / HSIC bam handle */
	ret = sps_phy2h(usb_phy_addr, &usb_handle);
	if (ret) {
		pr_err("%s: sps_phy2h failed (HSUSB/HSIC BAM) %d\n",
			__func__, ret);
		return ret;
	}

	pipe_connect->activity_notify = ipa_params->activity_notify;
	pipe_connect->inactivity_notify = ipa_params->inactivity_notify;
	pipe_connect->priv = ipa_params->priv;

	/* IPA input parameters */
	ipa_in_params.client_bam_hdl = usb_handle;
	ipa_in_params.desc_fifo_sz = pipe_connect->desc_fifo_size;
	ipa_in_params.data_fifo_sz = pipe_connect->data_fifo_size;
	ipa_in_params.notify = ipa_params->notify;
	ipa_in_params.priv = ipa_params->priv;

	/* If BAM is using dedicated SPS pipe memory, get it */

	if (pipe_connect->mem_type == SPS_PIPE_MEM) {
		pr_debug("%s: USB BAM using SPS pipe memory\n", __func__);
		ret = sps_setup_bam2bam_fifo(
			&pipe_connect->data_mem_buf,
			pipe_connect->data_fifo_base_offset,
			pipe_connect->data_fifo_size, 1);
		if (ret) {
			pr_err("%s: data fifo setup failure %d\n",
				__func__, ret);
			return ret;
		}

		ret = sps_setup_bam2bam_fifo(
			&pipe_connect->desc_mem_buf,
			pipe_connect->desc_fifo_base_offset,
			pipe_connect->desc_fifo_size, 1);
		if (ret) {
			pr_err("%s: desc. fifo setup failure %d\n",
				__func__, ret);
			return ret;
		}

		ipa_in_params.desc = pipe_connect->desc_mem_buf;
		ipa_in_params.data = pipe_connect->data_mem_buf;
	}

	memcpy(&ipa_in_params.ipa_ep_cfg, &ipa_params->ipa_ep_cfg,
		   sizeof(struct ipa_ep_cfg));

	ret = ipa_connect(&ipa_in_params, &sps_out_params, &clnt_hdl);
	if (ret) {
		pr_err("%s: ipa_connect failed\n", __func__);
		return ret;
	}
	pipe_connect->ipa_clnt_hdl = clnt_hdl;

	*pipe = sps_alloc_endpoint();
	if (*pipe == NULL) {
		pr_err("%s: sps_alloc_endpoint failed\n", __func__);
		ret = -ENOMEM;
		goto disconnect_ipa;
	}

	ret = sps_get_config(*pipe, sps_connection);
	if (ret) {
		pr_err("%s: tx get config failed %d\n", __func__ , ret);
		goto free_sps_endpoints;
	}

	if (dir == USB_TO_PEER_PERIPHERAL) {
		/* USB src IPA dest */
		sps_connection->mode = SPS_MODE_SRC;
		ipa_params->cons_clnt_hdl = clnt_hdl;
		sps_connection->source = usb_handle;
		sps_connection->destination = sps_out_params.ipa_bam_hdl;
		sps_connection->src_pipe_index = pipe_connect->src_pipe_index;
		sps_connection->dest_pipe_index = sps_out_params.ipa_ep_idx;
		ipa_params->ipa_cons_ep_idx = sps_out_params.ipa_ep_idx;
		*(ipa_params->src_pipe) = sps_connection->src_pipe_index;
		pipe_connect->dst_pipe_index = sps_out_params.ipa_ep_idx;
		pr_debug("%s: BAM pipe usb[%x]->ipa[%x] connection\n",
			__func__,
			pipe_connect->src_pipe_index,
			pipe_connect->dst_pipe_index);
		sps_connection->options = SPS_O_NO_DISABLE;
	} else {
		/* IPA src, USB dest */
		sps_connection->mode = SPS_MODE_DEST;
		ipa_params->prod_clnt_hdl = clnt_hdl;
		sps_connection->source = sps_out_params.ipa_bam_hdl;
		sps_connection->destination = usb_handle;
		sps_connection->src_pipe_index = sps_out_params.ipa_ep_idx;
		ipa_params->ipa_prod_ep_idx = sps_out_params.ipa_ep_idx;
		sps_connection->dest_pipe_index = pipe_connect->dst_pipe_index;
		*(ipa_params->dst_pipe) = sps_connection->dest_pipe_index;
		pipe_connect->src_pipe_index = sps_out_params.ipa_ep_idx;
		pr_debug("%s: BAM pipe ipa[%x]->usb[%x] connection\n",
			__func__,
			pipe_connect->src_pipe_index,
			pipe_connect->dst_pipe_index);
		sps_connection->options = 0;
	}

	sps_connection->data = sps_out_params.data;
	sps_connection->desc = sps_out_params.desc;
	sps_connection->event_thresh = 16;
	sps_connection->options |= SPS_O_AUTO_ENABLE;

	ret = sps_connect(*pipe, sps_connection);
	if (ret < 0) {
		pr_err("%s: sps_connect failed %d\n", __func__, ret);
		goto error;
	}

	return 0;

error:
	sps_disconnect(*pipe);
free_sps_endpoints:
	sps_free_endpoint(*pipe);
disconnect_ipa:
	ipa_disconnect(clnt_hdl);
	return ret;
}

static int disconnect_pipe(u8 idx)
{
	struct usb_bam_pipe_connect *pipe_connect =
		&usb_bam_connections[idx];
	struct sps_pipe *pipe = ctx.usb_bam_sps.sps_pipes[idx];
	struct sps_connect *sps_connection =
		&ctx.usb_bam_sps.sps_connections[idx];

	sps_disconnect(pipe);<commit_msg>usb: bam: Store memory buffers info for future queries<commit_after>/* Copyright (c) 2011-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/types.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/stat.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/usb/msm_hsusb.h>
#include <mach/usb_bam.h>
#include <mach/sps.h>
#include <mach/ipa.h>
#include <linux/workqueue.h>
#include <linux/dma-mapping.h>
#include <mach/msm_smsm.h>
#include <linux/pm_runtime.h>

#define USB_THRESHOLD 512
#define USB_BAM_MAX_STR_LEN 50
#define USB_BAM_TIMEOUT (10*HZ)

#define USB_BAM_NR_PORTS	4

enum usb_bam_sm {
	USB_BAM_SM_INIT = 0,
	USB_BAM_SM_PLUG_NOTIFIED,
	USB_BAM_SM_PLUG_ACKED,
	USB_BAM_SM_UNPLUG_NOTIFIED,
};

struct usb_bam_peer_handshake_info {
	enum usb_bam_sm state;
	bool client_ready;
	bool ack_received;
	int pending_work;
	struct usb_bam_event_info reset_event;
};

struct usb_bam_sps_type {
	struct sps_bam_props usb_props;
	struct sps_pipe **sps_pipes;
	struct sps_connect *sps_connections;
};

/**
* struct usb_bam_ctx_type - represents the usb bam driver entity
* @usb_bam_sps: holds the sps pipes the usb bam driver holds
*	against the sps driver.
* @usb_bam_pdev: the platfrom device that represents the usb bam.
* @usb_bam_wq: Worqueue used for managing states of reset against
*	a peer bam.
* @qscratch_ram1_reg: The memory region mapped to the qscratch
*	registers.
* @max_connections: The maximum number of pipes that are configured
*	in the platform data.
* @mem_clk: Clock that controls the usb bam driver memory in
*	case the usb bam uses its private memory for the pipes.
* @mem_iface_clk: Clock that controls the usb bam private memory in
*	case the usb bam uses its private memory for the pipes.
* @qdss_core_name: Stores the name of the core ("ssusb", "hsusb" or "hsic")
*	that it used as a peer of the qdss in bam2bam mode.
* @h_bam: This array stores for each BAM ("ssusb", "hsusb" or "hsic") the
*	handle/device of the sps driver.
* @pipes_enabled_per_bam: This array stores for each BAM
*	("ssusb", "hsusb" or "hsic") the number of pipes currently enabled.
* @inactivity_timer_ms: The timeout configuration per each bam for inactivity
*	timer feature.
* @is_bam_inactivity: Is there no activity on all pipes belongs to a
*	specific bam. (no activity = no data is pulled or pushed
*	from/into ones of the pipes).
*/
struct usb_bam_ctx_type {
	struct usb_bam_sps_type usb_bam_sps;
	struct platform_device *usb_bam_pdev;
	struct workqueue_struct *usb_bam_wq;
	void __iomem *qscratch_ram1_reg;
	u8 max_connections;
	struct clk *mem_clk;
	struct clk *mem_iface_clk;
	char qdss_core_name[USB_BAM_MAX_STR_LEN];
	u32 h_bam[MAX_BAMS];
	u8 pipes_enabled_per_bam[MAX_BAMS];
	u32 inactivity_timer_ms[MAX_BAMS];
	bool is_bam_inactivity[MAX_BAMS];
	struct completion reset_done;
};

static char *bam_enable_strings[MAX_BAMS] = {
	[SSUSB_BAM] = "ssusb",
	[HSUSB_BAM] = "hsusb",
	[HSIC_BAM]  = "hsic",
};

static enum usb_bam ipa_rm_bams[] = {HSUSB_BAM, HSIC_BAM, SSUSB_BAM};

static enum ipa_client_type ipa_rm_resource_prod[MAX_BAMS] = {
	[HSUSB_BAM] = IPA_RM_RESOURCE_USB_PROD,
	[HSIC_BAM]  = IPA_RM_RESOURCE_HSIC_PROD,
	[SSUSB_BAM] = IPA_RM_RESOURCE_USB_PROD,
};

static enum ipa_client_type ipa_rm_resource_cons[MAX_BAMS] = {
	[HSUSB_BAM] = IPA_RM_RESOURCE_USB_CONS,
	[HSIC_BAM]  = IPA_RM_RESOURCE_HSIC_CONS,
	[SSUSB_BAM] = IPA_RM_RESOURCE_USB_CONS,
};

static int usb_cons_request_resource(void);
static int usb_cons_release_resource(void);
static int ss_usb_cons_request_resource(void);
static int ss_usb_cons_release_resource(void);
static int hsic_cons_request_resource(void);
static int hsic_cons_release_resource(void);


static int (*request_resource_cb[MAX_BAMS])(void) = {
	[HSUSB_BAM] = usb_cons_request_resource,
	[HSIC_BAM]  = hsic_cons_request_resource,
	[SSUSB_BAM] = ss_usb_cons_request_resource,
};

static int (*release_resource_cb[MAX_BAMS])(void)  = {
	[HSUSB_BAM] = usb_cons_release_resource,
	[HSIC_BAM]  = hsic_cons_release_resource,
	[SSUSB_BAM] = ss_usb_cons_release_resource,
};

struct usb_bam_ipa_handshake_info {
	enum ipa_rm_event cur_prod_state;
	enum ipa_rm_event cur_cons_state;

	enum usb_bam_mode cur_bam_mode;
	enum usb_bam bam_type;
	bool lpm_wait_handshake;
	int connect_complete;
	bool lpm_wait_pipes;
	int bus_suspend;
	bool disconnected;
	bool in_lpm;
	bool pending_lpm;
	u8 prod_pipes_enabled_per_bam;

	int (*wake_cb)(void *);
	void *wake_param;

	u32 suspend_src_idx[USB_BAM_NR_PORTS];
	u32 suspend_dst_idx[USB_BAM_NR_PORTS];
	u32 resume_src_idx[USB_BAM_NR_PORTS];
	u32 resume_dst_idx[USB_BAM_NR_PORTS];

	u32 pipes_to_suspend;
	u32 pipes_suspended;
	u32 pipes_resumed;

	struct completion prod_avail;
	struct completion prod_released;

	struct mutex suspend_resume_mutex;
	struct work_struct resume_work;
	struct work_struct suspend_work;
	struct work_struct finish_suspend_work;
};

struct usb_bam_host_info {
	struct device *dev;
	bool in_lpm;
};

static spinlock_t usb_bam_ipa_handshake_info_lock;
static struct usb_bam_ipa_handshake_info info[MAX_BAMS];
static spinlock_t usb_bam_peer_handshake_info_lock;
static struct usb_bam_peer_handshake_info peer_handshake_info;
static spinlock_t usb_bam_lock; /* Protect ctx and usb_bam_connections */
static struct usb_bam_pipe_connect *usb_bam_connections;
static struct usb_bam_ctx_type ctx;
static struct usb_bam_host_info host_info[MAX_BAMS];
static struct device *usb_device;

static int __usb_bam_register_wake_cb(int idx, int (*callback)(void *user),
	void *param, bool trigger_cb_per_pipe);
static void wait_for_prod_release(enum usb_bam cur_bam);

void msm_bam_set_hsic_host_dev(struct device *dev)
{
	if (dev) {
		/* Hold the device until allowing lpm */
		info[HSIC_BAM].in_lpm = false;
		pr_debug("%s: Getting hsic device %x\n", __func__,
			(int)dev);
		pm_runtime_get(dev);
	} else if (host_info[HSIC_BAM].dev) {
		pr_debug("%s: Putting hsic device %x\n", __func__,
			(int)host_info[HSIC_BAM].dev);
		/* Just free previous device*/
		info[HSIC_BAM].in_lpm = true;
		pm_runtime_put(host_info[HSIC_BAM].dev);
	}

	host_info[HSIC_BAM].dev = dev;
	host_info[HSIC_BAM].in_lpm = false;
}

void msm_bam_set_usb_dev(struct device *dev)
{
	pr_debug("%s: Updating usb device for power managment\n", __func__);
	usb_device = dev;
}

void msm_bam_set_usb_host_dev(struct device *dev)
{
	host_info[HSUSB_BAM].dev = dev;
	host_info[HSUSB_BAM].in_lpm = false;
}

static int get_bam_type_from_core_name(const char *name)
{
	if (strnstr(name, bam_enable_strings[SSUSB_BAM],
			USB_BAM_MAX_STR_LEN) ||
		strnstr(name, "dwc3", USB_BAM_MAX_STR_LEN))
		return SSUSB_BAM;
	else if (strnstr(name, bam_enable_strings[HSIC_BAM],
			USB_BAM_MAX_STR_LEN))
		return HSIC_BAM;
	else if (strnstr(name, bam_enable_strings[HSUSB_BAM],
			USB_BAM_MAX_STR_LEN) ||
		strnstr(name, "ci", USB_BAM_MAX_STR_LEN))
		return HSUSB_BAM;

	pr_err("%s: invalid BAM name(%s)\n", __func__, name);
	return -EINVAL;
}

static bool bam_use_private_mem(enum usb_bam bam)
{
	int i;

	for (i = 0; i < ctx.max_connections; i++)
		if (usb_bam_connections[i].bam_type == bam &&
			usb_bam_connections[i].mem_type == USB_PRIVATE_MEM)
				return true;

	return false;
}

static void usb_bam_set_inactivity_timer(enum usb_bam bam)
{
	struct sps_timer_ctrl timer_ctrl;
	struct usb_bam_pipe_connect *pipe_connect;
	struct sps_pipe *pipe = NULL;
	int i;

	pr_debug("%s: enter\n", __func__);

	/*
	 * Since we configure global incativity timer for all pipes
	 * and not per each pipe, it is enough to use some pipe
	 * handle associated with this bam, so just find the first one.
	 * This pipe handle is required due to SPS driver API we use below.
	 */
	for (i = 0; i < ctx.max_connections; i++) {
		pipe_connect = &usb_bam_connections[i];
		if (pipe_connect->bam_type == bam &&
		    pipe_connect->enabled) {
			pipe = ctx.usb_bam_sps.sps_pipes[i];
			break;
		}
	}

	if (!pipe) {
		pr_warning("%s: Bam %s has no connected pipes\n", __func__,
			bam_enable_strings[bam]);
		return;
	}

	timer_ctrl.op = SPS_TIMER_OP_CONFIG;
	timer_ctrl.mode = SPS_TIMER_MODE_ONESHOT;
	timer_ctrl.timeout_msec = ctx.inactivity_timer_ms[bam];
	sps_timer_ctrl(pipe, &timer_ctrl, NULL);

	timer_ctrl.op = SPS_TIMER_OP_RESET;
	sps_timer_ctrl(pipe, &timer_ctrl, NULL);
}

static int connect_pipe(u8 idx, u32 *usb_pipe_idx)
{
	int ret, ram1_value;
	enum usb_bam bam;
	struct usb_bam_sps_type usb_bam_sps = ctx.usb_bam_sps;
	struct sps_pipe **pipe = &(usb_bam_sps.sps_pipes[idx]);
	struct sps_connect *sps_connection = &usb_bam_sps.sps_connections[idx];
	struct msm_usb_bam_platform_data *pdata =
		ctx.usb_bam_pdev->dev.platform_data;
	struct usb_bam_pipe_connect *pipe_connect = &usb_bam_connections[idx];
	enum usb_bam_pipe_dir dir = pipe_connect->dir;
	struct sps_mem_buffer *data_buf = &(pipe_connect->data_mem_buf);
	struct sps_mem_buffer *desc_buf = &(pipe_connect->desc_mem_buf);

	*pipe = sps_alloc_endpoint();
	if (*pipe == NULL) {
		pr_err("%s: sps_alloc_endpoint failed\n", __func__);
		return -ENOMEM;
	}

	ret = sps_get_config(*pipe, sps_connection);
	if (ret) {
		pr_err("%s: tx get config failed %d\n", __func__, ret);
		goto free_sps_endpoint;
	}

	ret = sps_phy2h(pipe_connect->src_phy_addr, &(sps_connection->source));
	if (ret) {
		pr_err("%s: sps_phy2h failed (src BAM) %d\n", __func__, ret);
		goto free_sps_endpoint;
	}

	sps_connection->src_pipe_index = pipe_connect->src_pipe_index;
	ret = sps_phy2h(pipe_connect->dst_phy_addr,
		&(sps_connection->destination));
	if (ret) {
		pr_err("%s: sps_phy2h failed (dst BAM) %d\n", __func__, ret);
		goto free_sps_endpoint;
	}
	sps_connection->dest_pipe_index = pipe_connect->dst_pipe_index;

	if (dir == USB_TO_PEER_PERIPHERAL) {
		sps_connection->mode = SPS_MODE_SRC;
		*usb_pipe_idx = pipe_connect->src_pipe_index;
	} else {
		sps_connection->mode = SPS_MODE_DEST;
		*usb_pipe_idx = pipe_connect->dst_pipe_index;
	}

	switch (pipe_connect->mem_type) {
	case SPS_PIPE_MEM:
		pr_debug("%s: USB BAM using SPS pipe memory\n", __func__);
		ret = sps_setup_bam2bam_fifo(
			data_buf,
			pipe_connect->data_fifo_base_offset,
			pipe_connect->data_fifo_size, 1);
		if (ret) {
			pr_err("%s: data fifo setup failure %d\n", __func__,
				ret);
			goto free_sps_endpoint;
		}

		ret = sps_setup_bam2bam_fifo(
			desc_buf,
			pipe_connect->desc_fifo_base_offset,
			pipe_connect->desc_fifo_size, 1);
		if (ret) {
			pr_err("%s: desc. fifo setup failure %d\n", __func__,
				ret);
			goto free_sps_endpoint;
		}
		break;
	case USB_PRIVATE_MEM:
		pr_debug("%s: USB BAM using private memory\n", __func__);

		if (IS_ERR(ctx.mem_clk) || IS_ERR(ctx.mem_iface_clk)) {
			pr_err("%s: Failed to enable USB mem_clk\n", __func__);
			ret = IS_ERR(ctx.mem_clk);
			goto free_sps_endpoint;
		}

		clk_prepare_enable(ctx.mem_clk);
		clk_prepare_enable(ctx.mem_iface_clk);

		/*
		 * Enable USB PRIVATE RAM to be used for BAM FIFOs
		 * HSUSB: Only RAM13 is used for BAM FIFOs
		 * SSUSB: RAM11, 12, 13 are used for BAM FIFOs
		 */
		bam = pipe_connect->bam_type;

		if (bam == HSUSB_BAM)
			ram1_value = 0x4;
		else
			ram1_value = 0x7;

		pr_debug("Writing 0x%x to QSCRATCH_RAM1\n", ram1_value);
		writel_relaxed(ram1_value, ctx.qscratch_ram1_reg);
		/* fall through */
	case OCI_MEM:
		if (pipe_connect->mem_type == OCI_MEM)
			pr_debug("%s: USB BAM using oci memory\n", __func__);

		data_buf->phys_base =
			pipe_connect->data_fifo_base_offset +
				pdata->usb_bam_fifo_baseaddr;
		data_buf->size = pipe_connect->data_fifo_size;
		data_buf->base =
			ioremap(data_buf->phys_base, data_buf->size);
		memset(data_buf->base, 0, data_buf->size);

		desc_buf->phys_base =
			pipe_connect->desc_fifo_base_offset +
				pdata->usb_bam_fifo_baseaddr;
		desc_buf->size = pipe_connect->desc_fifo_size;
		desc_buf->base =
			ioremap(desc_buf->phys_base, desc_buf->size);
		memset(desc_buf->base, 0, desc_buf->size);
		break;
	case SYSTEM_MEM:
		pr_debug("%s: USB BAM using system memory\n", __func__);
		/* BAM would use system memory, allocate FIFOs */
		data_buf->size = pipe_connect->data_fifo_size;
		data_buf->base =
			dma_alloc_coherent(&ctx.usb_bam_pdev->dev,
			pipe_connect->data_fifo_size,
			&(data_buf->phys_base),
			0);
		memset(data_buf->base, 0, pipe_connect->data_fifo_size);

		desc_buf->size = pipe_connect->desc_fifo_size;
		desc_buf->base =
			dma_alloc_coherent(&ctx.usb_bam_pdev->dev,
			pipe_connect->desc_fifo_size,
			&(desc_buf->phys_base),
			0);
		memset(desc_buf->base, 0, pipe_connect->desc_fifo_size);
		break;
	default:
		pr_err("%s: invalid mem type\n", __func__);
		goto free_sps_endpoint;
	}

	sps_connection->data = *data_buf;
	sps_connection->desc = *desc_buf;
	sps_connection->event_thresh = 16;
	sps_connection->options = SPS_O_AUTO_ENABLE;

	ret = sps_connect(*pipe, sps_connection);
	if (ret < 0) {
		pr_err("%s: sps_connect failed %d\n", __func__, ret);
		goto error;
	}

	return 0;

error:
	sps_disconnect(*pipe);
free_sps_endpoint:
	sps_free_endpoint(*pipe);
	return ret;
}

static int connect_pipe_ipa(u8 idx,
			struct usb_bam_connect_ipa_params *ipa_params)
{
	int ret;
	struct usb_bam_sps_type usb_bam_sps = ctx.usb_bam_sps;
	enum usb_bam_pipe_dir dir = ipa_params->dir;
	struct sps_pipe **pipe = &(usb_bam_sps.sps_pipes[idx]);
	struct sps_connect *sps_connection = &usb_bam_sps.sps_connections[idx];
	struct usb_bam_pipe_connect *pipe_connect = &usb_bam_connections[idx];

	struct ipa_connect_params ipa_in_params;
	struct ipa_sps_params sps_out_params;
	u32 usb_handle, usb_phy_addr;
	u32 clnt_hdl = 0;

	memset(&ipa_in_params, 0, sizeof(ipa_in_params));
	memset(&sps_out_params, 0, sizeof(sps_out_params));

	if (dir == USB_TO_PEER_PERIPHERAL) {
		usb_phy_addr = pipe_connect->src_phy_addr;
		ipa_in_params.client_ep_idx = pipe_connect->src_pipe_index;
		ipa_in_params.client = ipa_params->src_client;
	} else {
		usb_phy_addr = pipe_connect->dst_phy_addr;
		ipa_in_params.client_ep_idx = pipe_connect->dst_pipe_index;
		ipa_in_params.client = ipa_params->dst_client;
	}
	/* Get HSUSB / HSIC bam handle */
	ret = sps_phy2h(usb_phy_addr, &usb_handle);
	if (ret) {
		pr_err("%s: sps_phy2h failed (HSUSB/HSIC BAM) %d\n",
			__func__, ret);
		return ret;
	}

	pipe_connect->activity_notify = ipa_params->activity_notify;
	pipe_connect->inactivity_notify = ipa_params->inactivity_notify;
	pipe_connect->priv = ipa_params->priv;

	/* IPA input parameters */
	ipa_in_params.client_bam_hdl = usb_handle;
	ipa_in_params.desc_fifo_sz = pipe_connect->desc_fifo_size;
	ipa_in_params.data_fifo_sz = pipe_connect->data_fifo_size;
	ipa_in_params.notify = ipa_params->notify;
	ipa_in_params.priv = ipa_params->priv;

	/* If BAM is using dedicated SPS pipe memory, get it */

	if (pipe_connect->mem_type == SPS_PIPE_MEM) {
		pr_debug("%s: USB BAM using SPS pipe memory\n", __func__);
		ret = sps_setup_bam2bam_fifo(
			&pipe_connect->data_mem_buf,
			pipe_connect->data_fifo_base_offset,
			pipe_connect->data_fifo_size, 1);
		if (ret) {
			pr_err("%s: data fifo setup failure %d\n",
				__func__, ret);
			return ret;
		}

		ret = sps_setup_bam2bam_fifo(
			&pipe_connect->desc_mem_buf,
			pipe_connect->desc_fifo_base_offset,
			pipe_connect->desc_fifo_size, 1);
		if (ret) {
			pr_err("%s: desc. fifo setup failure %d\n",
				__func__, ret);
			return ret;
		}

		ipa_in_params.desc = pipe_connect->desc_mem_buf;
		ipa_in_params.data = pipe_connect->data_mem_buf;
	}

	memcpy(&ipa_in_params.ipa_ep_cfg, &ipa_params->ipa_ep_cfg,
		   sizeof(struct ipa_ep_cfg));

	ret = ipa_connect(&ipa_in_params, &sps_out_params, &clnt_hdl);
	if (ret) {
		pr_err("%s: ipa_connect failed\n", __func__);
		return ret;
	}
	pipe_connect->ipa_clnt_hdl = clnt_hdl;

	*pipe = sps_alloc_endpoint();
	if (*pipe == NULL) {
		pr_err("%s: sps_alloc_endpoint failed\n", __func__);
		ret = -ENOMEM;
		goto disconnect_ipa;
	}

	ret = sps_get_config(*pipe, sps_connection);
	if (ret) {
		pr_err("%s: tx get config failed %d\n", __func__ , ret);
		goto free_sps_endpoints;
	}

	if (dir == USB_TO_PEER_PERIPHERAL) {
		/* USB src IPA dest */
		sps_connection->mode = SPS_MODE_SRC;
		ipa_params->cons_clnt_hdl = clnt_hdl;
		sps_connection->source = usb_handle;
		sps_connection->destination = sps_out_params.ipa_bam_hdl;
		sps_connection->src_pipe_index = pipe_connect->src_pipe_index;
		sps_connection->dest_pipe_index = sps_out_params.ipa_ep_idx;
		ipa_params->ipa_cons_ep_idx = sps_out_params.ipa_ep_idx;
		*(ipa_params->src_pipe) = sps_connection->src_pipe_index;
		pipe_connect->dst_pipe_index = sps_out_params.ipa_ep_idx;
		pr_debug("%s: BAM pipe usb[%x]->ipa[%x] connection\n",
			__func__,
			pipe_connect->src_pipe_index,
			pipe_connect->dst_pipe_index);
		sps_connection->options = SPS_O_NO_DISABLE;
	} else {
		/* IPA src, USB dest */
		sps_connection->mode = SPS_MODE_DEST;
		ipa_params->prod_clnt_hdl = clnt_hdl;
		sps_connection->source = sps_out_params.ipa_bam_hdl;
		sps_connection->destination = usb_handle;
		sps_connection->src_pipe_index = sps_out_params.ipa_ep_idx;
		ipa_params->ipa_prod_ep_idx = sps_out_params.ipa_ep_idx;
		sps_connection->dest_pipe_index = pipe_connect->dst_pipe_index;
		*(ipa_params->dst_pipe) = sps_connection->dest_pipe_index;
		pipe_connect->src_pipe_index = sps_out_params.ipa_ep_idx;
		pr_debug("%s: BAM pipe ipa[%x]->usb[%x] connection\n",
			__func__,
			pipe_connect->src_pipe_index,
			pipe_connect->dst_pipe_index);
		sps_connection->options = 0;
	}

	pipe_connect->data_mem_buf = sps_out_params.data;
	pipe_connect->desc_mem_buf = sps_out_params.desc;

	sps_connection->data = sps_out_params.data;
	sps_connection->desc = sps_out_params.desc;
	sps_connection->event_thresh = 16;
	sps_connection->options |= SPS_O_AUTO_ENABLE;

	ret = sps_connect(*pipe, sps_connection);
	if (ret < 0) {
		pr_err("%s: sps_connect failed %d\n", __func__, ret);
		goto error;
	}

	return 0;

error:
	sps_disconnect(*pipe);
free_sps_endpoints:
	sps_free_endpoint(*pipe);
disconnect_ipa:
	ipa_disconnect(clnt_hdl);
	return ret;
}

static int disconnect_pipe(u8 idx)
{
	struct usb_bam_pipe_connect *pipe_connect =
		&usb_bam_connections[idx];
	struct sps_pipe *pipe = ctx.usb_bam_sps.sps_pipes[idx];
	struct sps_connect *sps_connection =
		&ctx.usb_bam_sps.sps_connections[idx];

	sps_disconnect(pipe);
{'fraction_non_alphanumeric': 0.10863270777479893, 'fraction_numerical': 0.005737265415549598, 'mean_word_length': 5.343707482993198, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 295, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5370842', 'n_tokens_mistral': 15927, 'n_tokens_neox': 15183, 'n_words': 3011}