988 lines · 26.4 KB · C
RawDownload
/* *  pti.c - PTI driver for cJTAG data extration * *  Copyright (C) Intel 2010 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License 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. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * The PTI (Parallel Trace Interface) driver directs trace data routed from * various parts in the system out through the Intel Penwell PTI port and * out of the mobile device for analysis with a debugging tool * (Lauterbach, Fido). This is part of a solution for the MIPI P1149.7, * compact JTAG, standard. */ #include <linux/init.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/console.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/tty.h>#include <linux/tty_driver.h>#include <linux/pci.h>#include <linux/mutex.h>#include <linux/miscdevice.h>#include <linux/pti.h>#include <linux/slab.h>#include <linux/uaccess.h> #define DRIVERNAME		"pti"#define PCINAME			"pciPTI"#define TTYNAME			"ttyPTI"#define CHARNAME		"pti"#define PTITTY_MINOR_START	0#define PTITTY_MINOR_NUM	2#define MAX_APP_IDS		16   /* 128 channel ids / u8 bit size */#define MAX_OS_IDS		16   /* 128 channel ids / u8 bit size */#define MAX_MODEM_IDS		16   /* 128 channel ids / u8 bit size */#define MODEM_BASE_ID		71   /* modem master ID address    */#define CONTROL_ID		72   /* control master ID address  */#define CONSOLE_ID		73   /* console master ID address  */#define OS_BASE_ID		74   /* base OS master ID address  */#define APP_BASE_ID		80   /* base App master ID address */#define CONTROL_FRAME_LEN	32   /* PTI control frame maximum size */#define USER_COPY_SIZE		8192 /* 8Kb buffer for user space copy */#define APERTURE_14		0x3800000 /* offset to first OS write addr */#define APERTURE_LEN		0x400000  /* address length */ struct pti_tty {	struct pti_masterchannel *mc;}; struct pti_dev {	struct tty_port port[PTITTY_MINOR_NUM];	unsigned long pti_addr;	unsigned long aperture_base;	void __iomem *pti_ioaddr;	u8 ia_app[MAX_APP_IDS];	u8 ia_os[MAX_OS_IDS];	u8 ia_modem[MAX_MODEM_IDS];}; /* * This protects access to ia_app, ia_os, and ia_modem, * which keeps track of channels allocated in * an aperture write id. */static DEFINE_MUTEX(alloclock); static const struct pci_device_id pci_ids[] = {		{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x82B)},		{0}}; static struct tty_driver *pti_tty_driver;static struct pti_dev *drv_data; static unsigned int pti_console_channel;static unsigned int pti_control_channel; /** *  pti_write_to_aperture()- The private write function to PTI HW. * *  @mc: The 'aperture'. It's part of a write address that holds *       a master and channel ID. *  @buf: Data being written to the HW that will ultimately be seen *        in a debugging tool (Fido, Lauterbach). *  @len: Size of buffer. * *  Since each aperture is specified by a unique *  master/channel ID, no two processes will be writing *  to the same aperture at the same time so no lock is required. The *  PTI-Output agent will send these out in the order that they arrived, and *  thus, it will intermix these messages. The debug tool can then later *  regroup the appropriate message segments together reconstituting each *  message. */static void pti_write_to_aperture(struct pti_masterchannel *mc,				  u8 *buf,				  int len){	int dwordcnt;	int final;	int i;	u32 ptiword;	u32 __iomem *aperture;	u8 *p = buf; 	/*	 * calculate the aperture offset from the base using the master and	 * channel id's.	 */	aperture = drv_data->pti_ioaddr + (mc->master << 15)		+ (mc->channel << 8); 	dwordcnt = len >> 2;	final = len - (dwordcnt << 2);	    /* final = trailing bytes    */	if (final == 0 && dwordcnt != 0) {  /* always need a final dword */		final += 4;		dwordcnt--;	} 	for (i = 0; i < dwordcnt; i++) {		ptiword = be32_to_cpu(*(u32 *)p);		p += 4;		iowrite32(ptiword, aperture);	} 	aperture += PTI_LASTDWORD_DTS;	/* adding DTS signals that is EOM */ 	ptiword = 0;	for (i = 0; i < final; i++)		ptiword |= *p++ << (24-(8*i)); 	iowrite32(ptiword, aperture);	return;} /** *  pti_control_frame_built_and_sent()- control frame build and send function. * *  @mc:          The master / channel structure on which the function *                built a control frame. *  @thread_name: The thread name associated with the master / channel or *                'NULL' if using the 'current' global variable. * *  To be able to post process the PTI contents on host side, a control frame *  is added before sending any PTI content. So the host side knows on *  each PTI frame the name of the thread using a dedicated master / channel. *  The thread name is retrieved from 'current' global variable if 'thread_name' *  is 'NULL', else it is retrieved from 'thread_name' parameter. *  This function builds this frame and sends it to a master ID CONTROL_ID. *  The overhead is only 32 bytes since the driver only writes to HW *  in 32 byte chunks. */static void pti_control_frame_built_and_sent(struct pti_masterchannel *mc,					     const char *thread_name){	/*	 * Since we access the comm member in current's task_struct, we only	 * need to be as large as what 'comm' in that structure is.	 */	char comm[TASK_COMM_LEN];	struct pti_masterchannel mccontrol = {.master = CONTROL_ID,					      .channel = 0};	const char *thread_name_p;	const char *control_format = "%3d %3d %s";	u8 control_frame[CONTROL_FRAME_LEN]; 	if (!thread_name) {		if (!in_interrupt())			get_task_comm(comm, current);		else			strncpy(comm, "Interrupt", TASK_COMM_LEN); 		/* Absolutely ensure our buffer is zero terminated. */		comm[TASK_COMM_LEN-1] = 0;		thread_name_p = comm;	} else {		thread_name_p = thread_name;	} 	mccontrol.channel = pti_control_channel;	pti_control_channel = (pti_control_channel + 1) & 0x7f; 	snprintf(control_frame, CONTROL_FRAME_LEN, control_format, mc->master,		mc->channel, thread_name_p);	pti_write_to_aperture(&mccontrol, control_frame, strlen(control_frame));} /** *  pti_write_full_frame_to_aperture()- high level function to *					write to PTI. * *  @mc:  The 'aperture'. It's part of a write address that holds *        a master and channel ID. *  @buf: Data being written to the HW that will ultimately be seen *        in a debugging tool (Fido, Lauterbach). *  @len: Size of buffer. * *  All threads sending data (either console, user space application, ...) *  are calling the high level function to write to PTI meaning that it is *  possible to add a control frame before sending the content. */static void pti_write_full_frame_to_aperture(struct pti_masterchannel *mc,						const unsigned char *buf,						int len){	pti_control_frame_built_and_sent(mc, NULL);	pti_write_to_aperture(mc, (u8 *)buf, len);} /** * get_id()- Allocate a master and channel ID. * * @id_array:    an array of bits representing what channel *               id's are allocated for writing. * @max_ids:     The max amount of available write IDs to use. * @base_id:     The starting SW channel ID, based on the Intel *               PTI arch. * @thread_name: The thread name associated with the master / channel or *               'NULL' if using the 'current' global variable. * * Returns: *	pti_masterchannel struct with master, channel ID address *	0 for error * * Each bit in the arrays ia_app and ia_os correspond to a master and * channel id. The bit is one if the id is taken and 0 if free. For * every master there are 128 channel id's. */static struct pti_masterchannel *get_id(u8 *id_array,					int max_ids,					int base_id,					const char *thread_name){	struct pti_masterchannel *mc;	int i, j, mask; 	mc = kmalloc(sizeof(struct pti_masterchannel), GFP_KERNEL);	if (mc == NULL)		return NULL; 	/* look for a byte with a free bit */	for (i = 0; i < max_ids; i++)		if (id_array[i] != 0xff)			break;	if (i == max_ids) {		kfree(mc);		return NULL;	}	/* find the bit in the 128 possible channel opportunities */	mask = 0x80;	for (j = 0; j < 8; j++) {		if ((id_array[i] & mask) == 0)			break;		mask >>= 1;	} 	/* grab it */	id_array[i] |= mask;	mc->master  = base_id;	mc->channel = ((i & 0xf)<<3) + j;	/* write new master Id / channel Id allocation to channel control */	pti_control_frame_built_and_sent(mc, thread_name);	return mc;} /* * The following three functions: * pti_request_mastercahannel(), mipi_release_masterchannel() * and pti_writedata() are an API for other kernel drivers to * access PTI. */ /** * pti_request_masterchannel()- Kernel API function used to allocate *				a master, channel ID address *				to write to PTI HW. * * @type:        0- request Application  master, channel aperture ID *                  write address. *               1- request OS master, channel aperture ID write *                  address. *               2- request Modem master, channel aperture ID *                  write address. *               Other values, error. * @thread_name: The thread name associated with the master / channel or *               'NULL' if using the 'current' global variable. * * Returns: *	pti_masterchannel struct *	0 for error */struct pti_masterchannel *pti_request_masterchannel(u8 type,						    const char *thread_name){	struct pti_masterchannel *mc; 	mutex_lock(&alloclock); 	switch (type) { 	case 0:		mc = get_id(drv_data->ia_app, MAX_APP_IDS,			    APP_BASE_ID, thread_name);		break; 	case 1:		mc = get_id(drv_data->ia_os, MAX_OS_IDS,			    OS_BASE_ID, thread_name);		break; 	case 2:		mc = get_id(drv_data->ia_modem, MAX_MODEM_IDS,			    MODEM_BASE_ID, thread_name);		break;	default:		mc = NULL;	} 	mutex_unlock(&alloclock);	return mc;}EXPORT_SYMBOL_GPL(pti_request_masterchannel); /** * pti_release_masterchannel()- Kernel API function used to release *				a master, channel ID address *				used to write to PTI HW. * * @mc: master, channel apeture ID address to be released.  This *      will de-allocate the structure via kfree(). */void pti_release_masterchannel(struct pti_masterchannel *mc){	u8 master, channel, i; 	mutex_lock(&alloclock); 	if (mc) {		master = mc->master;		channel = mc->channel; 		if (master == APP_BASE_ID) {			i = channel >> 3;			drv_data->ia_app[i] &=  ~(0x80>>(channel & 0x7));		} else if (master == OS_BASE_ID) {			i = channel >> 3;			drv_data->ia_os[i] &= ~(0x80>>(channel & 0x7));		} else {			i = channel >> 3;			drv_data->ia_modem[i] &= ~(0x80>>(channel & 0x7));		} 		kfree(mc);	} 	mutex_unlock(&alloclock);}EXPORT_SYMBOL_GPL(pti_release_masterchannel); /** * pti_writedata()- Kernel API function used to write trace *                  debugging data to PTI HW. * * @mc:    Master, channel aperture ID address to write to. *         Null value will return with no write occurring. * @buf:   Trace debuging data to write to the PTI HW. *         Null value will return with no write occurring. * @count: Size of buf. Value of 0 or a negative number will *         return with no write occuring. */void pti_writedata(struct pti_masterchannel *mc, u8 *buf, int count){	/*	 * since this function is exported, this is treated like an	 * API function, thus, all parameters should	 * be checked for validity.	 */	if ((mc != NULL) && (buf != NULL) && (count > 0))		pti_write_to_aperture(mc, buf, count);	return;}EXPORT_SYMBOL_GPL(pti_writedata); /* * for the tty_driver_*() basic function descriptions, see tty_driver.h. * Specific header comments made for PTI-related specifics. */ /** * pti_tty_driver_open()- Open an Application master, channel aperture * ID to the PTI device via tty device. * * @tty: tty interface. * @filp: filp interface pased to tty_port_open() call. * * Returns: *	int, 0 for success *	otherwise, fail value * * The main purpose of using the tty device interface is for * each tty port to have a unique PTI write aperture.  In an * example use case, ttyPTI0 gets syslogd and an APP aperture * ID and ttyPTI1 is where the n_tracesink ldisc hooks to route * modem messages into PTI.  Modem trace data does not have to * go to ttyPTI1, but ttyPTI0 and ttyPTI1 do need to be distinct * master IDs.  These messages go through the PTI HW and out of * the handheld platform and to the Fido/Lauterbach device. */static int pti_tty_driver_open(struct tty_struct *tty, struct file *filp){	/*	 * we actually want to allocate a new channel per open, per	 * system arch.  HW gives more than plenty channels for a single	 * system task to have its own channel to write trace data. This	 * also removes a locking requirement for the actual write	 * procedure.	 */	return tty_port_open(tty->port, tty, filp);} /** * pti_tty_driver_close()- close tty device and release Application * master, channel aperture ID to the PTI device via tty device. * * @tty: tty interface. * @filp: filp interface pased to tty_port_close() call. * * The main purpose of using the tty device interface is to route * syslog daemon messages to the PTI HW and out of the handheld platform * and to the Fido/Lauterbach device. */static void pti_tty_driver_close(struct tty_struct *tty, struct file *filp){	tty_port_close(tty->port, tty, filp);} /** * pti_tty_install()- Used to set up specific master-channels *		      to tty ports for organizational purposes when *		      tracing viewed from debuging tools. * * @driver: tty driver information. * @tty: tty struct containing pti information. * * Returns: *	0 for success *	otherwise, error */static int pti_tty_install(struct tty_driver *driver, struct tty_struct *tty){	int idx = tty->index;	struct pti_tty *pti_tty_data;	int ret = tty_standard_install(driver, tty); 	if (ret == 0) {		pti_tty_data = kmalloc(sizeof(struct pti_tty), GFP_KERNEL);		if (pti_tty_data == NULL)			return -ENOMEM; 		if (idx == PTITTY_MINOR_START)			pti_tty_data->mc = pti_request_masterchannel(0, NULL);		else			pti_tty_data->mc = pti_request_masterchannel(2, NULL); 		if (pti_tty_data->mc == NULL) {			kfree(pti_tty_data);			return -ENXIO;		}		tty->driver_data = pti_tty_data;	} 	return ret;} /** * pti_tty_cleanup()- Used to de-allocate master-channel resources *		      tied to tty's of this driver. * * @tty: tty struct containing pti information. */static void pti_tty_cleanup(struct tty_struct *tty){	struct pti_tty *pti_tty_data = tty->driver_data;	if (pti_tty_data == NULL)		return;	pti_release_masterchannel(pti_tty_data->mc);	kfree(pti_tty_data);	tty->driver_data = NULL;

Lines 1–500 of 988.