2011-05-07 03:56:49 +04:00
/*
* 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>
2011-08-26 02:59:01 +04:00
# include <linux/slab.h>
# include <linux/uaccess.h>
2011-05-07 03:56:49 +04:00
# 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 {
2012-08-07 23:47:35 +04:00
struct tty_port port [ PTITTY_MINOR_NUM ] ;
2011-05-07 03:56:49 +04:00
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 ) ;
2012-11-19 22:25:10 +04:00
static const struct pci_device_id pci_ids [ ] = {
2011-05-07 03:56:49 +04:00
{ 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 .
*
2011-06-18 02:09:53 +04:00
* @ 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 .
2011-05-07 03:56:49 +04:00
*
* 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 .
2011-06-18 02:09:53 +04:00
* The thread name is retrieved from ' current ' global variable if ' thread_name '
* is ' NULL ' , else it is retrieved from ' thread_name ' parameter .
2011-05-07 03:56:49 +04:00
* 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 .
*/
2011-06-18 02:09:53 +04:00
static void pti_control_frame_built_and_sent ( struct pti_masterchannel * mc ,
const char * thread_name )
2011-05-07 03:56:49 +04:00
{
2011-09-15 03:22:12 +04:00
/*
* 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 ] ;
2011-05-07 03:56:49 +04:00
struct pti_masterchannel mccontrol = { . master = CONTROL_ID ,
. channel = 0 } ;
2011-06-18 02:09:53 +04:00
const char * thread_name_p ;
2011-05-07 03:56:49 +04:00
const char * control_format = " %3d %3d %s " ;
u8 control_frame [ CONTROL_FRAME_LEN ] ;
2011-06-18 02:09:53 +04:00
if ( ! thread_name ) {
if ( ! in_interrupt ( ) )
get_task_comm ( comm , current ) ;
else
strncpy ( comm , " Interrupt " , TASK_COMM_LEN ) ;
2011-05-07 03:56:49 +04:00
2011-06-18 02:09:53 +04:00
/* Absolutely ensure our buffer is zero terminated. */
comm [ TASK_COMM_LEN - 1 ] = 0 ;
thread_name_p = comm ;
} else {
thread_name_p = thread_name ;
}
2011-05-07 03:56:49 +04:00
mccontrol . channel = pti_control_channel ;
pti_control_channel = ( pti_control_channel + 1 ) & 0x7f ;
snprintf ( control_frame , CONTROL_FRAME_LEN , control_format , mc - > master ,
2011-06-18 02:09:53 +04:00
mc - > channel , thread_name_p ) ;
2011-05-07 03:56:49 +04:00
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 )
{
2011-06-18 02:09:53 +04:00
pti_control_frame_built_and_sent ( mc , NULL ) ;
2011-05-07 03:56:49 +04:00
pti_write_to_aperture ( mc , ( u8 * ) buf , len ) ;
}
/**
* get_id ( ) - Allocate a master and channel ID .
*
2011-06-18 02:09:53 +04:00
* @ 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 .
2011-05-07 03:56:49 +04:00
*
* 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 .
*/
2011-06-18 02:09:53 +04:00
static struct pti_masterchannel * get_id ( u8 * id_array ,
int max_ids ,
int base_id ,
const char * thread_name )
2011-05-07 03:56:49 +04:00
{
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 */
2011-06-18 02:09:53 +04:00
pti_control_frame_built_and_sent ( mc , thread_name ) ;
2011-05-07 03:56:49 +04:00
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 .
*
2011-06-18 02:09:53 +04:00
* @ 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 .
2011-05-07 03:56:49 +04:00
*
* Returns :
* pti_masterchannel struct
* 0 for error
*/
2011-06-18 02:09:53 +04:00
struct pti_masterchannel * pti_request_masterchannel ( u8 type ,
const char * thread_name )
2011-05-07 03:56:49 +04:00
{
struct pti_masterchannel * mc ;
mutex_lock ( & alloclock ) ;
switch ( type ) {
case 0 :
2011-06-18 02:09:53 +04:00
mc = get_id ( drv_data - > ia_app , MAX_APP_IDS ,
APP_BASE_ID , thread_name ) ;
2011-05-07 03:56:49 +04:00
break ;
case 1 :
2011-06-18 02:09:53 +04:00
mc = get_id ( drv_data - > ia_os , MAX_OS_IDS ,
OS_BASE_ID , thread_name ) ;
2011-05-07 03:56:49 +04:00
break ;
case 2 :
2011-06-18 02:09:53 +04:00
mc = get_id ( drv_data - > ia_modem , MAX_MODEM_IDS ,
MODEM_BASE_ID , thread_name ) ;
2011-05-07 03:56:49 +04:00
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 .
*
2011-05-26 01:38:18 +04:00
* @ mc : master , channel apeture ID address to be released . This
* will de - allocate the structure via kfree ( ) .
2011-05-07 03:56:49 +04:00
*/
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 .
*/
2012-08-07 23:47:36 +04:00
return tty_port_open ( tty - > port , tty , filp ) ;
2011-05-07 03:56:49 +04:00
}
/**
* 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 )
{
2012-08-07 23:47:36 +04:00
tty_port_close ( tty - > port , tty , filp ) ;
2011-05-07 03:56:49 +04:00
}
/**
2011-05-26 01:50:26 +04:00
* pti_tty_install ( ) - Used to set up specific master - channels
* to tty ports for organizational purposes when
* tracing viewed from debuging tools .
2011-05-07 03:56:49 +04:00
*
* @ 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 ;
2012-01-31 00:14:30 +04:00
int ret = tty_standard_install ( driver , tty ) ;
2011-05-07 03:56:49 +04:00
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 )
2011-06-18 02:09:53 +04:00
pti_tty_data - > mc = pti_request_masterchannel ( 0 , NULL ) ;
2011-05-07 03:56:49 +04:00
else
2011-06-18 02:09:53 +04:00
pti_tty_data - > mc = pti_request_masterchannel ( 2 , NULL ) ;
2011-05-07 03:56:49 +04:00
2011-05-26 01:45:40 +04:00
if ( pti_tty_data - > mc = = NULL ) {
kfree ( pti_tty_data ) ;
2011-05-07 03:56:49 +04:00
return - ENXIO ;
2011-05-26 01:45:40 +04:00
}
2011-05-07 03:56:49 +04:00
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 ) ;
2011-05-26 01:56:43 +04:00
kfree ( pti_tty_data ) ;
2011-05-07 03:56:49 +04:00
tty - > driver_data = NULL ;
}
/**
* pti_tty_driver_write ( ) - Write trace debugging data through the char
* interface to the PTI HW . Part of the misc device implementation .
*
* @ filp : Contains private data which is used to obtain
* master , channel write ID .
* @ data : trace data to be written .
* @ len : # of byte to write .
*
* Returns :
* int , # of bytes written
* otherwise , error
*/
static int pti_tty_driver_write ( struct tty_struct * tty ,
const unsigned char * buf , int len )
{
struct pti_tty * pti_tty_data = tty - > driver_data ;
if ( ( pti_tty_data ! = NULL ) & & ( pti_tty_data - > mc ! = NULL ) ) {
pti_write_to_aperture ( pti_tty_data - > mc , ( u8 * ) buf , len ) ;
return len ;
}
/*
* we can ' t write to the pti hardware if the private driver_data
* and the mc address is not there .
*/
else
return - EFAULT ;
}
/**
* pti_tty_write_room ( ) - Always returns 2048.
*
* @ tty : contains tty info of the pti driver .
*/
static int pti_tty_write_room ( struct tty_struct * tty )
{
return 2048 ;
}
/**
* pti_char_open ( ) - Open an Application master , channel aperture
* ID to the PTI device . Part of the misc device implementation .
*
* @ inode : not used .
* @ filp : Output - will have a masterchannel struct set containing
* the allocated application PTI aperture write address .
*
* Returns :
* int , 0 for success
* otherwise , a fail value
*/
static int pti_char_open ( struct inode * inode , struct file * filp )
{
struct pti_masterchannel * mc ;
/*
* We really do want to fail immediately if
* pti_request_masterchannel ( ) fails ,
* before assigning the value to filp - > private_data .
* Slightly easier to debug if this driver needs debugging .
*/
2011-06-18 02:09:53 +04:00
mc = pti_request_masterchannel ( 0 , NULL ) ;
2011-05-07 03:56:49 +04:00
if ( mc = = NULL )
return - ENOMEM ;
filp - > private_data = mc ;
return 0 ;
}
/**
* pti_char_release ( ) - Close a char channel to the PTI device . Part
* of the misc device implementation .
*
* @ inode : Not used in this implementaiton .
* @ filp : Contains private_data that contains the master , channel
* ID to be released by the PTI device .
*
* Returns :
* always 0
*/
static int pti_char_release ( struct inode * inode , struct file * filp )
{
pti_release_masterchannel ( filp - > private_data ) ;
2011-05-26 01:38:18 +04:00
filp - > private_data = NULL ;
2011-05-07 03:56:49 +04:00
return 0 ;
}
/**
* pti_char_write ( ) - Write trace debugging data through the char
* interface to the PTI HW . Part of the misc device implementation .
*
* @ filp : Contains private data which is used to obtain
* master , channel write ID .
* @ data : trace data to be written .
* @ len : # of byte to write .
* @ ppose : Not used in this function implementation .
*
* Returns :
* int , # of bytes written
* otherwise , error value
*
* Notes : From side discussions with Alan Cox and experimenting
* with PTI debug HW like Nokia ' s Fido box and Lauterbach
* devices , 8192 byte write buffer used by USER_COPY_SIZE was
* deemed an appropriate size for this type of usage with
* debugging HW .
*/
static ssize_t pti_char_write ( struct file * filp , const char __user * data ,
size_t len , loff_t * ppose )
{
struct pti_masterchannel * mc ;
void * kbuf ;
const char __user * tmp ;
size_t size = USER_COPY_SIZE ;
size_t n = 0 ;
tmp = data ;
mc = filp - > private_data ;
kbuf = kmalloc ( size , GFP_KERNEL ) ;
if ( kbuf = = NULL ) {
pr_err ( " %s(%d): buf allocation failed \n " ,
__func__ , __LINE__ ) ;
return - ENOMEM ;
}
do {
if ( len - n > USER_COPY_SIZE )
size = USER_COPY_SIZE ;
else
size = len - n ;
if ( copy_from_user ( kbuf , tmp , size ) ) {
kfree ( kbuf ) ;
return n ? n : - EFAULT ;
}
pti_write_to_aperture ( mc , kbuf , size ) ;
n + = size ;
tmp + = size ;
} while ( len > n ) ;
kfree ( kbuf ) ;
return len ;
}
static const struct tty_operations pti_tty_driver_ops = {
. open = pti_tty_driver_open ,
. close = pti_tty_driver_close ,
. write = pti_tty_driver_write ,
. write_room = pti_tty_write_room ,
. install = pti_tty_install ,
. cleanup = pti_tty_cleanup
} ;
static const struct file_operations pti_char_driver_ops = {
. owner = THIS_MODULE ,
. write = pti_char_write ,
. open = pti_char_open ,
. release = pti_char_release ,
} ;
static struct miscdevice pti_char_driver = {
. minor = MISC_DYNAMIC_MINOR ,
. name = CHARNAME ,
. fops = & pti_char_driver_ops
} ;
/**
* pti_console_write ( ) - Write to the console that has been acquired .
*
* @ c : Not used in this implementaiton .
* @ buf : Data to be written .
* @ len : Length of buf .
*/
static void pti_console_write ( struct console * c , const char * buf , unsigned len )
{
static struct pti_masterchannel mc = { . master = CONSOLE_ID ,
. channel = 0 } ;
mc . channel = pti_console_channel ;
pti_console_channel = ( pti_console_channel + 1 ) & 0x7f ;
pti_write_full_frame_to_aperture ( & mc , buf , len ) ;
}
/**
* pti_console_device ( ) - Return the driver tty structure and set the
* associated index implementation .
*
* @ c : Console device of the driver .
* @ index : index associated with c .
*
* Returns :
* always value of pti_tty_driver structure when this function
* is called .
*/
static struct tty_driver * pti_console_device ( struct console * c , int * index )
{
* index = c - > index ;
return pti_tty_driver ;
}
/**
* pti_console_setup ( ) - Initialize console variables used by the driver .
*
* @ c : Not used .
* @ opts : Not used .
*
* Returns :
* always 0.
*/
static int pti_console_setup ( struct console * c , char * opts )
{
pti_console_channel = 0 ;
pti_control_channel = 0 ;
return 0 ;
}
/*
* pti_console struct , used to capture OS printk ( ) ' s and shift
* out to the PTI device for debugging . This cannot be
* enabled upon boot because of the possibility of eating
* any serial console printk ' s ( race condition discovered ) .
* The console should be enabled upon when the tty port is
* used for the first time . Since the primary purpose for
* the tty port is to hook up syslog to it , the tty port
* will be open for a really long time .
*/
static struct console pti_console = {
. name = TTYNAME ,
. write = pti_console_write ,
. device = pti_console_device ,
. setup = pti_console_setup ,
. flags = CON_PRINTBUFFER ,
. index = 0 ,
} ;
/**
* pti_port_activate ( ) - Used to start / initialize any items upon
* first opening of tty_port ( ) .
*
* @ port - The tty port number of the PTI device .
* @ tty - The tty struct associated with this device .
*
* Returns :
* always returns 0
*
* Notes : The primary purpose of the PTI tty port 0 is to hook
* the syslog daemon to it ; thus this port will be open for a
* very long time .
*/
static int pti_port_activate ( struct tty_port * port , struct tty_struct * tty )
{
if ( port - > tty - > index = = PTITTY_MINOR_START )
console_start ( & pti_console ) ;
return 0 ;
}
/**
* pti_port_shutdown ( ) - Used to stop / shutdown any items upon the
* last tty port close .
*
* @ port - The tty port number of the PTI device .
*
* Notes : The primary purpose of the PTI tty port 0 is to hook
* the syslog daemon to it ; thus this port will be open for a
* very long time .
*/
static void pti_port_shutdown ( struct tty_port * port )
{
if ( port - > tty - > index = = PTITTY_MINOR_START )
console_stop ( & pti_console ) ;
}
static const struct tty_port_operations tty_port_ops = {
. activate = pti_port_activate ,
. shutdown = pti_port_shutdown ,
} ;
/*
* Note the _probe ( ) call sets everything up and ties the char and tty
* to successfully detecting the PTI device on the pci bus .
*/
/**
* pti_pci_probe ( ) - Used to detect pti on the pci bus and set
* things up in the driver .
*
* @ pdev - pci_dev struct values for pti .
* @ ent - pci_device_id struct for pti driver .
*
* Returns :
* 0 for success
* otherwise , error
*/
2012-11-19 22:23:05 +04:00
static int pti_pci_probe ( struct pci_dev * pdev ,
2011-05-07 03:56:49 +04:00
const struct pci_device_id * ent )
{
2012-08-07 23:47:35 +04:00
unsigned int a ;
2011-05-07 03:56:49 +04:00
int retval = - EINVAL ;
int pci_bar = 1 ;
dev_dbg ( & pdev - > dev , " %s %s(%d): PTI PCI ID %04x:%04x \n " , __FILE__ ,
__func__ , __LINE__ , pdev - > vendor , pdev - > device ) ;
retval = misc_register ( & pti_char_driver ) ;
if ( retval ) {
pr_err ( " %s(%d): CHAR registration failed of pti driver \n " ,
__func__ , __LINE__ ) ;
pr_err ( " %s(%d): Error value returned: %d \n " ,
__func__ , __LINE__ , retval ) ;
2012-08-07 23:47:34 +04:00
goto err ;
2011-05-07 03:56:49 +04:00
}
retval = pci_enable_device ( pdev ) ;
if ( retval ! = 0 ) {
dev_err ( & pdev - > dev ,
" %s: pci_enable_device() returned error %d \n " ,
__func__ , retval ) ;
2012-08-07 23:47:34 +04:00
goto err_unreg_misc ;
2011-05-07 03:56:49 +04:00
}
drv_data = kzalloc ( sizeof ( * drv_data ) , GFP_KERNEL ) ;
if ( drv_data = = NULL ) {
retval = - ENOMEM ;
dev_err ( & pdev - > dev ,
" %s(%d): kmalloc() returned NULL memory. \n " ,
__func__ , __LINE__ ) ;
2012-08-07 23:47:34 +04:00
goto err_disable_pci ;
2011-05-07 03:56:49 +04:00
}
drv_data - > pti_addr = pci_resource_start ( pdev , pci_bar ) ;
retval = pci_request_region ( pdev , pci_bar , dev_name ( & pdev - > dev ) ) ;
if ( retval ! = 0 ) {
dev_err ( & pdev - > dev ,
" %s(%d): pci_request_region() returned error %d \n " ,
__func__ , __LINE__ , retval ) ;
2012-08-07 23:47:34 +04:00
goto err_free_dd ;
2011-05-07 03:56:49 +04:00
}
drv_data - > aperture_base = drv_data - > pti_addr + APERTURE_14 ;
drv_data - > pti_ioaddr =
ioremap_nocache ( ( u32 ) drv_data - > aperture_base ,
APERTURE_LEN ) ;
if ( ! drv_data - > pti_ioaddr ) {
retval = - ENOMEM ;
2012-08-07 23:47:34 +04:00
goto err_rel_reg ;
2011-05-07 03:56:49 +04:00
}
pci_set_drvdata ( pdev , drv_data ) ;
2012-08-07 23:47:35 +04:00
for ( a = 0 ; a < PTITTY_MINOR_NUM ; a + + ) {
struct tty_port * port = & drv_data - > port [ a ] ;
tty_port_init ( port ) ;
port - > ops = & tty_port_ops ;
2011-05-07 03:56:49 +04:00
2012-08-07 23:47:36 +04:00
tty_port_register_device ( port , pti_tty_driver , a , & pdev - > dev ) ;
2012-08-07 23:47:35 +04:00
}
2011-05-07 03:56:49 +04:00
register_console ( & pti_console ) ;
2012-08-07 23:47:34 +04:00
return 0 ;
err_rel_reg :
pci_release_region ( pdev , pci_bar ) ;
err_free_dd :
kfree ( drv_data ) ;
err_disable_pci :
pci_disable_device ( pdev ) ;
err_unreg_misc :
misc_deregister ( & pti_char_driver ) ;
err :
2011-05-07 03:56:49 +04:00
return retval ;
}
2012-08-07 23:47:32 +04:00
/**
* pti_pci_remove ( ) - Driver exit method to remove PTI from
* PCI bus .
* @ pdev : variable containing pci info of PTI .
*/
2012-11-19 22:26:02 +04:00
static void pti_pci_remove ( struct pci_dev * pdev )
2012-08-07 23:47:32 +04:00
{
struct pti_dev * drv_data = pci_get_drvdata ( pdev ) ;
2012-11-15 12:49:56 +04:00
unsigned int a ;
2012-08-07 23:47:32 +04:00
2012-08-07 23:47:33 +04:00
unregister_console ( & pti_console ) ;
2012-11-15 12:49:56 +04:00
for ( a = 0 ; a < PTITTY_MINOR_NUM ; a + + ) {
tty_unregister_device ( pti_tty_driver , a ) ;
tty_port_destroy ( & drv_data - > port [ a ] ) ;
}
2012-08-07 23:47:33 +04:00
2012-08-07 23:47:32 +04:00
iounmap ( drv_data - > pti_ioaddr ) ;
kfree ( drv_data ) ;
pci_release_region ( pdev , 1 ) ;
pci_disable_device ( pdev ) ;
2012-08-07 23:47:33 +04:00
misc_deregister ( & pti_char_driver ) ;
2012-08-07 23:47:32 +04:00
}
2011-05-07 03:56:49 +04:00
static struct pci_driver pti_pci_driver = {
. name = PCINAME ,
. id_table = pci_ids ,
. probe = pti_pci_probe ,
2012-11-19 22:21:23 +04:00
. remove = pti_pci_remove ,
2011-05-07 03:56:49 +04:00
} ;
/**
*
* pti_init ( ) - Overall entry / init call to the pti driver .
* It starts the registration process with the kernel .
*
* Returns :
* int __init , 0 for success
* otherwise value is an error
*
*/
static int __init pti_init ( void )
{
int retval = - EINVAL ;
/* First register module as tty device */
2012-03-05 17:51:52 +04:00
pti_tty_driver = alloc_tty_driver ( PTITTY_MINOR_NUM ) ;
2011-05-07 03:56:49 +04:00
if ( pti_tty_driver = = NULL ) {
pr_err ( " %s(%d): Memory allocation failed for ptiTTY driver \n " ,
__func__ , __LINE__ ) ;
return - ENOMEM ;
}
pti_tty_driver - > driver_name = DRIVERNAME ;
pti_tty_driver - > name = TTYNAME ;
pti_tty_driver - > major = 0 ;
pti_tty_driver - > minor_start = PTITTY_MINOR_START ;
pti_tty_driver - > type = TTY_DRIVER_TYPE_SYSTEM ;
pti_tty_driver - > subtype = SYSTEM_TYPE_SYSCONS ;
pti_tty_driver - > flags = TTY_DRIVER_REAL_RAW |
TTY_DRIVER_DYNAMIC_DEV ;
pti_tty_driver - > init_termios = tty_std_termios ;
tty_set_operations ( pti_tty_driver , & pti_tty_driver_ops ) ;
retval = tty_register_driver ( pti_tty_driver ) ;
if ( retval ) {
pr_err ( " %s(%d): TTY registration failed of pti driver \n " ,
__func__ , __LINE__ ) ;
pr_err ( " %s(%d): Error value returned: %d \n " ,
__func__ , __LINE__ , retval ) ;
2012-08-07 23:47:34 +04:00
goto put_tty ;
2011-05-07 03:56:49 +04:00
}
retval = pci_register_driver ( & pti_pci_driver ) ;
if ( retval ) {
pr_err ( " %s(%d): PCI registration failed of pti driver \n " ,
__func__ , __LINE__ ) ;
pr_err ( " %s(%d): Error value returned: %d \n " ,
__func__ , __LINE__ , retval ) ;
2012-08-07 23:47:34 +04:00
goto unreg_tty ;
2011-05-07 03:56:49 +04:00
}
2012-08-07 23:47:34 +04:00
return 0 ;
unreg_tty :
tty_unregister_driver ( pti_tty_driver ) ;
put_tty :
put_tty_driver ( pti_tty_driver ) ;
pti_tty_driver = NULL ;
2011-05-07 03:56:49 +04:00
return retval ;
}
/**
* pti_exit ( ) - Unregisters this module as a tty and pci driver .
*/
static void __exit pti_exit ( void )
{
2012-08-07 23:47:34 +04:00
tty_unregister_driver ( pti_tty_driver ) ;
2011-05-07 03:56:49 +04:00
pci_unregister_driver ( & pti_pci_driver ) ;
2012-08-07 23:47:34 +04:00
put_tty_driver ( pti_tty_driver ) ;
2011-05-07 03:56:49 +04:00
}
module_init ( pti_init ) ;
module_exit ( pti_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Ken Mills, Jay Freyensee " ) ;
MODULE_DESCRIPTION ( " PTI Driver " ) ;