Re: irqdesc porting help

From: Maximus
Date: Tue Feb 20 2007 - 02:03:58 EST


Hi,
Sorry for the late response, attached is the code im trying to port
to linux - 2.6.20.


Regards,
Jo




On 2/15/07, Paul Mundt <lethal@xxxxxxxxxxxx> wrote:
On Thu, Feb 15, 2007 at 12:01:37PM +0530, Maximus wrote:
> Im trying to port some drivers between 2.6.14 and 2.6.19
>
> I find that irqdesc has changed completely. how do i port
> the drivers between 2.6.14 and 2.6.19?
>
> is there a porting guide available to port the drivers
> which use irqdesc?.
>
> my drivers use variables triggered, ... which dont exist in 2.6.19
> irqdesc strcuture.
>
Presumably you're talking about the struct hw_interrupt_type and the lack
of an irq_desc[irq].handler? There's some migration helper glue in
include/linux/irq.h that you can use, but you're better off converting
completely. You can at least get it building again by changing to
irq_desc[irq].chip, but you really want a proper irq_chip implementation
to go along with this, rather than munging in the hw_interrupt_type.

You can easily compare before-and-after versions of most of the IRQ
controllers to identify the minimal changes required.

/*
* linux/drivers/i2c/chips/twl4030_core.c
*
* Copyright (C) 2005-2006 Texas Instruments, Inc.
*
* Modifications to defer interrupt handling to a kernel thread:
* Copyright (C) 2006 MontaVista Software, Inc.
*
* Based on tlv320aic23.c:
* Copyright (c) by Kai Svahn <kai.svahn@xxxxxxxxx>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/

/*** Includes */
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel_stat.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/interrupt.h>
#include <linux/random.h>
#include <linux/syscalls.h>
#include <linux/kthread.h>

#include <linux/i2c.h>
#include <linux/slab.h>

#include <asm/arch/twl4030.h>
#include <asm/arch/irqs.h>
#include <asm/irq.h>
#include <asm/arch/irq.h>
#include <asm/mach/irq.h>
#include <asm/arch/gpio.h>
//#include <asm/arch/platform.h>
#include <asm/arch/mux.h>
#include <asm/arch/power_companion.h>

#include <linux/device.h>
#include <asm/arch/bus.h>

#define CONFIG_TWL4030_IRQ_PRIO 26

/**** Macro Definitions */
#define TWL_CLIENT_STRING "TWL4030-ID"
#define TWL_CLIENT_USED 1
#define TWL_CLIENT_FREE 0

/* IRQ Flags */
#define FREE 0
#define USED 1

/** Primary Interrupt Handler on TWL4030 Registers */

/**** Register Definitions */

#define REG_PIH_ISR_P1 (0x1)
#define REG_PIH_ISR_P2 (0x2)
#define REG_PIH_SIR (0x3)

/**** BitField Definitions */

/* PIH_ISR_P1 Fields */
#define BIT_PIH_ISR_P1_PIH_ISR0 (0x000)
#define BIT_PIH_ISR_P1_PIH_ISR0_M (0x00000001)
#define BIT_PIH_ISR_P1_PIH_ISR1 (0x001)
#define BIT_PIH_ISR_P1_PIH_ISR1_M (0x00000002)
#define BIT_PIH_ISR_P1_PIH_ISR2 (0x002)
#define BIT_PIH_ISR_P1_PIH_ISR2_M (0x00000004)
#define BIT_PIH_ISR_P1_PIH_ISR3 (0x003)
#define BIT_PIH_ISR_P1_PIH_ISR3_M (0x00000008)
#define BIT_PIH_ISR_P1_PIH_ISR4 (0x004)
#define BIT_PIH_ISR_P1_PIH_ISR4_M (0x00000010)
#define BIT_PIH_ISR_P1_PIH_ISR5 (0x005)
#define BIT_PIH_ISR_P1_PIH_ISR5_M (0x00000020)
#define BIT_PIH_ISR_P1_PIH_ISR6 (0x006)
#define BIT_PIH_ISR_P1_PIH_ISR6_M (0x00000040)
#define BIT_PIH_ISR_P1_PIH_ISR7 (0x007)
#define BIT_PIH_ISR_P1_PIH_ISR7_M (0x00000080)
/* PIH_ISR_P2 Fields */
#define BIT_PIH_ISR_P2_PIH_ISR0 (0x000)
#define BIT_PIH_ISR_P2_PIH_ISR0_M (0x00000001)
#define BIT_PIH_ISR_P2_PIH_ISR1 (0x001)
#define BIT_PIH_ISR_P2_PIH_ISR1_M (0x00000002)
#define BIT_PIH_ISR_P2_PIH_ISR2 (0x002)
#define BIT_PIH_ISR_P2_PIH_ISR2_M (0x00000004)
#define BIT_PIH_ISR_P2_PIH_ISR3 (0x003)
#define BIT_PIH_ISR_P2_PIH_ISR3_M (0x00000008)
#define BIT_PIH_ISR_P2_PIH_ISR4 (0x004)
#define BIT_PIH_ISR_P2_PIH_ISR4_M (0x00000010)
#define BIT_PIH_ISR_P2_PIH_ISR5 (0x005)
#define BIT_PIH_ISR_P2_PIH_ISR5_M (0x00000020)
#define BIT_PIH_ISR_P2_PIH_ISR6 (0x006)
#define BIT_PIH_ISR_P2_PIH_ISR6_M (0x00000040)
#define BIT_PIH_ISR_P2_PIH_ISR7 (0x007)
#define BIT_PIH_ISR_P2_PIH_ISR7_M (0x00000080)
/* PIH_SIR Fields */
#define BIT_PIH_SIR_PIH1SIR (0x000)
#define BIT_PIH_SIR_PIH1SIR_M (0x00000001)
#define BIT_PIH_SIR_PIH2SIR (0x001)
#define BIT_PIH_SIR_PIH2SIR_M (0x00000002)
#define BIT_PIH_SIR_PIH3SIR (0x002)
#define BIT_PIH_SIR_PIH3SIR_M (0x00000004)
#define BIT_PIH_SIR_PIH4SIR (0x003)
#define BIT_PIH_SIR_PIH4SIR_M (0x00000008)
#define BIT_PIH_SIR_PIH5SIR (0x004)
#define BIT_PIH_SIR_PIH5SIR_M (0x00000010)
#define BIT_PIH_SIR_PIH6SIR (0x005)
#define BIT_PIH_SIR_PIH6SIR_M (0x00000020)

/* Triton Core internal information (BEGIN) */

/* Last - for index max*/
#define TWL4030_MODULE_LAST TWL4030_MODULE_SECURED_REG

/* Slave address */
#define TWL4030_NUM_SLAVES 0x04
#define TWL4030_SLAVENUM_NUM0 0x00
#define TWL4030_SLAVENUM_NUM1 0x01
#define TWL4030_SLAVENUM_NUM2 0x02
#define TWL4030_SLAVENUM_NUM3 0x03
#define TWL4030_SLAVEID_ID0 0x48
#define TWL4030_SLAVEID_ID1 0x49
#define TWL4030_SLAVEID_ID2 0x4A
#define TWL4030_SLAVEID_ID3 0x4B

/* Base Address defns */
/* USB ID */
#define TWL4030_BASEADD_USB 0x0000
/* AUD ID */
#define TWL4030_BASEADD_AUDIO_VOICE 0x0000

/* M1 companion board GPIO base address */
#ifdef CONFIG_TWL4030_CORE_M1
#define TWL4030_BASEADD_GPIO 0x0018
#elif CONFIG_TWL4030_CORE_T2
/* For T2 chip -Use this GPIO addr*/
#define TWL4030_BASEADD_GPIO 0x0098
#endif

#define TWL4030_BASEADD_INTBR 0x0085
#define TWL4030_BASEADD_PIH 0x0080
#define TWL4030_BASEADD_TEST 0x004C
/* AUX ID */
#define TWL4030_BASEADD_INTERRUPTS 0x00B9
#define TWL4030_BASEADD_LED 0x00EE
#define TWL4030_BASEADD_MADC 0x0000
#define TWL4030_BASEADD_MAIN_CHARGE 0x0074
#define TWL4030_BASEADD_PRECHARGE 0x00AA
#define TWL4030_BASEADD_PWM0 0x00F8
#define TWL4030_BASEADD_PWM1 0x00FB
#define TWL4030_BASEADD_PWMA 0x00EF
#define TWL4030_BASEADD_PWMB 0x00F1
#define TWL4030_BASEADD_KEYPAD 0x00D2
/* POWER ID */
#define TWL4030_BASEADD_BACKUP 0x0014
#define TWL4030_BASEADD_INT 0x002E
#define TWL4030_BASEADD_PM_MASTER 0x0036
#define TWL4030_BASEADD_PM_RECIEVER 0x005B
#define TWL4030_BASEADD_RTC 0x001C
#define TWL4030_BASEADD_SECURED_REG 0x0000

/* Triton Core internal information (END) */

/* Pull out the board specific config's defines */
#ifdef CONFIG_TWL4030_CORE_M1
/* M1 companion board with FPGA acting as T2 */
#define CONFIG_I2C_TWL4030_ID0 CONFIG_I2C_TWL4030_M1_ID0
#define CONFIG_I2C_TWL4030_ID1 CONFIG_I2C_TWL4030_M1_ID1
#define CONFIG_I2C_TWL4030_ID2 CONFIG_I2C_TWL4030_M1_ID2
#define CONFIG_I2C_TWL4030_ID3 CONFIG_I2C_TWL4030_M1_ID3

#elif defined (CONFIG_TWL4030_CORE_T2)
/* The Real T2!! */
#define CONFIG_I2C_TWL4030_ID0 CONFIG_I2C_TWL4030_T2_ID0
#define CONFIG_I2C_TWL4030_ID1 CONFIG_I2C_TWL4030_T2_ID1
#define CONFIG_I2C_TWL4030_ID2 CONFIG_I2C_TWL4030_T2_ID2
#define CONFIG_I2C_TWL4030_ID3 CONFIG_I2C_TWL4030_T2_ID3
#else
#error "Unsupported platform!!!"
#endif


/* ----- debug defines ----------------------------------------------- */
/* Debug - four macros:
* FN_IN, FN_OUT(value),D1,D2,D3 enabled based on log level
*/

/* Log level standard used here:
* Log level 3 all messages
* Log level 2 all entry-exit points
* Log level 1 major messages
* Log level 0 no messages
*/
#define TWL4030_LOG_LEVEL 0
/* detail - 0 - no detail
* 1 - function name
* 2 - function name, line number
* prefix is added to every log message
*/
#define TWL4030_DETAIL 0

/* kernel log level*/
//#define TWL4030_K_LOG_LEVEL KERN_DEBUG
#define TWL4030_K_LOG_LEVEL

#if ( TWL4030_DETAIL > 0 )
#define DL1 "%s "
#define DR1 ,__FUNCTION__
#else
#define DL1
#define DR1
#endif
#if ( TWL4030_DETAIL > 1 )
#define DL2 "[%d] "
#define DR2 ,__LINE__
#else
#define DL2
#define DR2
#endif

#define D(format,...)\
printk(TWL4030_K_LOG_LEVEL DL1 DL2 format "\n" DR1 DR2, ## __VA_ARGS__)

#if (TWL4030_LOG_LEVEL >= 1)
#define D1(ARGS...) D(ARGS)
#else
#define D1(ARGS...)
#endif
#if (TWL4030_LOG_LEVEL >= 2)
#define D2(ARGS...) D(ARGS)
#else
#define D2(ARGS...)
#endif
#if (TWL4030_LOG_LEVEL >= 3)
#define D3(ARGS...) D(ARGS)
#else
#define D3(ARGS...)
#endif

#if (TWL4030_LOG_LEVEL >= 2)
#define FN_IN printk("%s Entry\n",__FUNCTION__);
#define FN_OUT(ARG) printk("%s[%d]:Exit(%d)\n",__FUNCTION__,__LINE__,ARG);
#else
#define FN_IN
#define FN_OUT(ARG)
#endif

/* Sys FS Helper functions and macros */
#if CONFIG_I2C_TWL4030_DBG_SYSFS
/* Device Information- dummy */
static struct platform_device twl4030_debug_dev = {
.name = TWL_CLIENT_STRING,
.id = 0,
};

/* 255 Max bytes in a field register */
#define READ_REG_SIZE 255
/* Sys FS support */
static ssize_t show_mod(int mod, struct device *dev, char *buf)
{
int j;
unsigned char temp_buffer[READ_REG_SIZE + 1];
struct timeval stv, stv1, stv2;
int timespent1, timespent2;
char *sval = buf;
/* Read from I2c first 255 bytes (the max we can write in the reg) */
do_gettimeofday(&stv);
if ((j = twl4030_i2c_read(mod, temp_buffer, 0x0, READ_REG_SIZE)) < 0) {
printk(KERN_ERR
"unable to read %d bytes returned %d in module %d\n",
READ_REG_SIZE, j, mod);
return j;
}
do_gettimeofday(&stv1);
/* do a read of the last 256th byte */
if ((j =
twl4030_i2c_read_u8(mod, temp_buffer + READ_REG_SIZE,
READ_REG_SIZE)) < 0) {
printk(KERN_ERR
"unable to read %d reg returned %d in module %d\n",
READ_REG_SIZE, j, mod);
return j;
}
do_gettimeofday(&stv2);
sval += sprintf(sval, " | ");
for (j = 0; j < 0x10; j++) {
sval += sprintf(sval, "%02X ", j);
}
sval += sprintf(sval, "\n--+");

for (j = 0; j < 0x10; j++) {
sval += sprintf(sval, " --");
}

sval += sprintf(sval, "\n00| ");

for (j = 0; j <= READ_REG_SIZE; j++) {
sval += sprintf(sval, "%02X", temp_buffer[j]);
if (j < READ_REG_SIZE) {
sval +=
((j + 1) % 0x10) ? sprintf(sval,
" ") : sprintf(sval,
"\n%02X| ",
j + 1);
}
}
timespent1 =
(stv1.tv_sec - stv.tv_sec) * 1000000 + (stv1.tv_usec - stv.tv_usec);
timespent2 =
(stv2.tv_sec - stv1.tv_sec) * 1000000 + (stv2.tv_usec -
stv1.tv_usec);
sval +=
sprintf(sval, "\nTime Taken(uSec): 255bytes=%d 1byte=%d\n", timespent1,
timespent2);
sval += 1;
*sval = 0;
return sval - buf + 1;
}

/* MSB 8 bits are register address[module reg offset] and LSB 8 bits the value */
static ssize_t
set_mod(int mod, struct device *dev, const char *buf, size_t count)
{
u16 val = (u16) simple_strtoul(buf, NULL, 16);
printk("Reg=0x%02x, val=0x%02x,mod=%d\n", (val & 0xFF00) >> 8,
(val & 0x00FF), mod);
if (twl4030_i2c_write_u8(mod, (val & 0x00FF), (val & 0xFF00) >> 8) < 0) {
printk("FAILED\n");
} else {
printk("SUCCESS\n");
}
return count;
}

/* function generator macros */
#define C_SHOW_MOD(MOD)\
static ssize_t show_mod##MOD( struct device *dev, char *buf)\
{\
return show_mod(MOD, dev, buf);\
}\

#define C_SET_MOD(MOD)\
static ssize_t set_mod##MOD(struct device *dev, const char *buf, size_t count)\
{\
return set_mod(MOD, dev, buf, count);\
}\

#define MAK_MOD(MOD) \
C_SET_MOD(MOD)\
C_SHOW_MOD(MOD)\
static DEVICE_ATTR(twl4030_mod##MOD, S_IWUSR | S_IRUGO, show_mod##MOD, set_mod##MOD);\

#define TWL4030_SYS_DEV_CREAT(MOD)\
device_create_file(&twl4030_debug_dev.dev, &dev_attr_twl4030_mod##MOD);

MAK_MOD(0)
MAK_MOD(1)
MAK_MOD(2)
MAK_MOD(3)
MAK_MOD(4)
MAK_MOD(5)
MAK_MOD(6)
MAK_MOD(7)
MAK_MOD(8)
MAK_MOD(9)
MAK_MOD(10)
MAK_MOD(11)
MAK_MOD(12)
MAK_MOD(13)
MAK_MOD(14)
MAK_MOD(15)
MAK_MOD(16)
MAK_MOD(17)
MAK_MOD(18)
MAK_MOD(19)
MAK_MOD(20)
MAK_MOD(21)
#define TWL_SYS_DEV()\
TWL4030_SYS_DEV_CREAT(0)\
TWL4030_SYS_DEV_CREAT(1)\
TWL4030_SYS_DEV_CREAT(2)\
TWL4030_SYS_DEV_CREAT(3)\
TWL4030_SYS_DEV_CREAT(4)\
TWL4030_SYS_DEV_CREAT(5)\
TWL4030_SYS_DEV_CREAT(6)\
TWL4030_SYS_DEV_CREAT(7)\
TWL4030_SYS_DEV_CREAT(8)\
TWL4030_SYS_DEV_CREAT(9)\
TWL4030_SYS_DEV_CREAT(10)\
TWL4030_SYS_DEV_CREAT(11)\
TWL4030_SYS_DEV_CREAT(12)\
TWL4030_SYS_DEV_CREAT(13)\
TWL4030_SYS_DEV_CREAT(14)\
TWL4030_SYS_DEV_CREAT(15)\
TWL4030_SYS_DEV_CREAT(16)\
TWL4030_SYS_DEV_CREAT(17)\
TWL4030_SYS_DEV_CREAT(18)\
TWL4030_SYS_DEV_CREAT(19)\
TWL4030_SYS_DEV_CREAT(20)\
TWL4030_SYS_DEV_CREAT(21)\


#endif
/**** Helper functions */
static int
twl4030_detect_client(struct i2c_adapter *adapter, unsigned char sid);
static int twl4030_attach_adapter(struct i2c_adapter *adapter);
static int twl4030_detach_client(struct i2c_client *client);
static void do_twl4030_irq(unsigned int irq, struct irqdesc *desc,
struct pt_regs *regs);

static void twl_init_irq(void);

/**** Data Structures */
/* To have info on T2 IRQ substem activated or not */
static unsigned char twl_irq_used = FREE;

/* Structure to define on TWL4030 Slave ID */
struct twl4030_client {
struct i2c_client client;
const char client_name[sizeof(TWL_CLIENT_STRING) + 1];
const unsigned char address;
const char adapter_index;
unsigned char inuse;
struct i2c_msg xfer_msg[2]; /* max numb of i2c_msg required is for read =2 */
struct semaphore xfer_lock; /* To lock access to xfer_msg */
};

/* Module Mapping */
struct twl4030mapping {
unsigned char sid; /* Slave ID */
unsigned char base; /* base address */
};

/* mapping the module id to slave id and base address */
static struct twl4030mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
{TWL4030_SLAVENUM_NUM0,
TWL4030_BASEADD_USB /* TWL4030_MODULE_USB */ },

{TWL4030_SLAVENUM_NUM1,
TWL4030_BASEADD_AUDIO_VOICE /* TWL4030_MODULE_AUDIO_VOICE */ },

{TWL4030_SLAVENUM_NUM1,
TWL4030_BASEADD_GPIO /* TWL4030_MODULE_GPIO */ },

{TWL4030_SLAVENUM_NUM1,
TWL4030_BASEADD_INTBR /* TWL4030_MODULE_INTBR */ },

{TWL4030_SLAVENUM_NUM1,
TWL4030_BASEADD_PIH /* TWL4030_MODULE_PIH */ },

{TWL4030_SLAVENUM_NUM1,
TWL4030_BASEADD_TEST /* TWL4030_MODULE_TEST */ },

{TWL4030_SLAVENUM_NUM2,
TWL4030_BASEADD_KEYPAD /* TWL4030_MODULE_KEYPAD */ },
{TWL4030_SLAVENUM_NUM2,
TWL4030_BASEADD_MADC /* TWL4030_MODULE_MADC */ },
{TWL4030_SLAVENUM_NUM2,
TWL4030_BASEADD_INTERRUPTS /* TWL4030_MODULE_INTERRUPTS */ },
{TWL4030_SLAVENUM_NUM2,
TWL4030_BASEADD_LED /* TWL4030_MODULE_LED */ },
{TWL4030_SLAVENUM_NUM2,
TWL4030_BASEADD_MAIN_CHARGE /* TWL4030_MODULE_MAIN_CHARGE */ },
{TWL4030_SLAVENUM_NUM2,
TWL4030_BASEADD_PRECHARGE /* TWL4030_MODULE_PRECHARGE */ },
{TWL4030_SLAVENUM_NUM2,
TWL4030_BASEADD_PWM0 /* TWL4030_MODULE_PWM0 */ },
{TWL4030_SLAVENUM_NUM2,
TWL4030_BASEADD_PWM1 /* TWL4030_MODULE_PWM1 */ },
{TWL4030_SLAVENUM_NUM2,
TWL4030_BASEADD_PWMA /* TWL4030_MODULE_PWMA */ },
{TWL4030_SLAVENUM_NUM2,
TWL4030_BASEADD_PWMB /* TWL4030_MODULE_PWMB */ },

{TWL4030_SLAVENUM_NUM3,
TWL4030_BASEADD_BACKUP /* TWL4030_MODULE_BACKUP */ },
{TWL4030_SLAVENUM_NUM3,
TWL4030_BASEADD_INT /* TWL4030_MODULE_INT */ },
{TWL4030_SLAVENUM_NUM3,
TWL4030_BASEADD_PM_MASTER /* TWL4030_MODULE_PM_MASTER */ },
{TWL4030_SLAVENUM_NUM3,
TWL4030_BASEADD_PM_RECIEVER /* TWL4030_MODULE_PM_RECIEVER */ },
{TWL4030_SLAVENUM_NUM3,
TWL4030_BASEADD_RTC /* TWL4030_MODULE_RTC */ },
{TWL4030_SLAVENUM_NUM3,
TWL4030_BASEADD_SECURED_REG /* TWL4030_MODULE_SECURED_REG */ },
};

static struct twl4030_client twl4030_modules[TWL4030_NUM_SLAVES] = {
{
.address = TWL4030_SLAVEID_ID0,
.client_name = TWL_CLIENT_STRING "0",
.adapter_index = CONFIG_I2C_TWL4030_ID0,},
{
.address = TWL4030_SLAVEID_ID1,
.client_name = TWL_CLIENT_STRING "1",
.adapter_index = CONFIG_I2C_TWL4030_ID1,},
{
.address = TWL4030_SLAVEID_ID2,
.client_name = TWL_CLIENT_STRING "2",
.adapter_index = CONFIG_I2C_TWL4030_ID2,},
{
.address = TWL4030_SLAVEID_ID3,
.client_name = TWL_CLIENT_STRING "3",
.adapter_index = CONFIG_I2C_TWL4030_ID3,},
};

/* One Client Driver , 4 Clients */
static struct i2c_driver twl4030_driver = {
.name = "TWL4030",
.id = I2C_DRIVERID_EXP0, /* Experimental ID */
.flags = I2C_DF_NOTIFY,
.attach_adapter = twl4030_attach_adapter,
.detach_client = twl4030_detach_client,
};

/* unique client id */
static unsigned int twl4030_id;

/* TWL4030 doesn't have PIH mask,
* hence dummy function for mask
* and unmask.
*/

static void twl4030_i2c_ackirq(unsigned int irq){
FN_IN;
FN_OUT(0);
}

static void twl4030_i2c_disableint(unsigned int irq){
FN_IN;
FN_OUT(0);
}

static void twl4030_i2c_enableint(unsigned int irq){
FN_IN;
FN_OUT(0);
}

/* information for processing in the Work Item */
static struct irqchip twl4030_irq_chip = {
.ack = twl4030_i2c_ackirq,
.mask = twl4030_i2c_disableint,
.unmask = twl4030_i2c_enableint,
};

/**** Global Functions */
/**
* @brief twl4030_i2c_write - Writes a n bit register in TWL4030
*
* @param mod_no - module number
* @param *value - an array of num_bytes+1 containing data to write
* IMPORTANT - Allocate value num_bytes+1 and valid data starts at
* Offset 1.
* @param reg - register address (just offset will do)
* @param num_bytes - number of bytes to transfer
*
* @return result of operation - 0 is success
*/
int twl4030_i2c_write(u8 mod_no, u8 * value, u8 reg, u8 num_bytes)
{
int ret;
int sid;
struct twl4030_client *client;
struct i2c_msg *msg;
FN_IN;
if (unlikely(mod_no > TWL4030_MODULE_LAST)) {
printk(KERN_ERR "TWL4030: Invalid module Number\n");
FN_OUT(EPERM);
return -EPERM;
}
sid = twl4030_map[mod_no].sid;
client = &(twl4030_modules[sid]);

if (unlikely(client->inuse != TWL_CLIENT_USED)) {
printk(KERN_ERR
"TWL4030: I2C Client[%d] is not initialized[%d]\n", sid,
__LINE__);
FN_OUT(EPERM);
return -EPERM;
}
down(&(client->xfer_lock));
/* [MSG1]: fill the register address data
* fill the data Tx buffer
*/
msg = &(client->xfer_msg[0]);
msg->addr = client->address;
if ((client->client.flags & I2C_M_HS) == I2C_M_HS)
/* DO HS Mode transfer */
msg->flags = I2C_M_WR | I2C_M_HS; /* write the register value */
else
msg->flags = I2C_M_WR; /* write the register value */

msg->len = num_bytes + 1; /* only one value */
msg->buf = value;
/* over write the first byte of buffer with the register address */
*value = twl4030_map[mod_no].base + reg;
ret = i2c_transfer(client->client.adapter, client->xfer_msg, 1); /* one message */
up(&(client->xfer_lock));
/* i2cTransfer returns num messages.translate it pls.. */
if (ret>=0)
ret=0;
FN_OUT(ret);
return ret;
}

/**
* @brief twl4030_i2c_read - Reads a n bit register in TWL4030
*
* @param mod_no - module number
* @param *value - an array of num_bytes containing data to be read
* @param reg - register address (just offset will do)
* @param num_bytes - number of bytes to transfer
*
* @return result of operation - num_bytes is success else failure.
*/
int twl4030_i2c_read(u8 mod_no, u8 * value, u8 reg, u8 num_bytes)
{
int ret;
u8 val;
int sid;
struct twl4030_client *client;
struct i2c_msg *msg;
FN_IN;
if (unlikely(mod_no > TWL4030_MODULE_LAST)) {
printk(KERN_ERR "TWL4030: Invalid module Number\n");
FN_OUT(EPERM);
return -EPERM;
}
sid = twl4030_map[mod_no].sid;
client = &(twl4030_modules[sid]);

if (unlikely(client->inuse != TWL_CLIENT_USED)) {
printk(KERN_ERR
"TWL4030: I2C Client[%d] is not initialized[%d]\n", sid,
__LINE__);
FN_OUT(EPERM);
return -EPERM;
}
down(&(client->xfer_lock));
/* [MSG1] fill the register address data */
msg = &(client->xfer_msg[0]);
msg->addr = client->address;
if ((client->client.flags & I2C_M_HS) == I2C_M_HS)
msg->flags = I2C_M_WR | I2C_M_HS; /* Read the register value in HS mode */
else
msg->flags = I2C_M_WR; /* Read the register value */

msg->len = 1; /* only one value */
val = twl4030_map[mod_no].base + reg;
msg->buf = &val;
/* [MSG2] fill the data rx buffer */
msg = &(client->xfer_msg[1]);
msg->addr = client->address;
if ((client->client.flags & I2C_M_HS) == I2C_M_HS)
msg->flags = I2C_M_RD | I2C_M_HS; /* Read the register value in HS mode */
else
msg->flags = I2C_M_RD; /* Read the register value */
msg->len = num_bytes; /* only n bytes */
msg->buf = value;
ret = i2c_transfer(client->client.adapter, client->xfer_msg, 2); /* two messages */
up(&(client->xfer_lock));
/* i2cTransfer returns num messages.translate it pls.. */
if (ret>=0)
ret=0;
FN_OUT(ret);
return ret;
}

/**
* @brief twl4030_i2c_write_u8 - Writes a 8 bit register in TWL4030
*
* @param mod_no - module number
* @param value - the value to be written 8 bit
* @param reg - register address (just offset will do)
*
* @return result of operation - 0 is success
*/
int twl4030_i2c_write_u8(u8 mod_no, u8 value, u8 reg)
{
int ret;
/* 2 bytes offset 1 contains the data offset 0 is used by i2c_write */
u8 temp_buffer[2]={0};
FN_IN;
/* offset 1 contains the data */
temp_buffer[1]=value;
ret = twl4030_i2c_write(mod_no,temp_buffer,reg,1) ;
FN_OUT(ret);
return ret;
}

/**
* @brief twl4030_i2c_read_u8 - Reads a 8 bit register from TWL4030
*
* @param mod_no - module number
* @param *value - the value read 8 bit
* @param reg - register address (just offset will do)
*
* @return result of operation - 0 is success
*/
int twl4030_i2c_read_u8(u8 mod_no, u8 * value, u8 reg)
{
int ret=0;
FN_IN;
ret = twl4030_i2c_read(mod_no,value,reg,1);
FN_OUT(ret);
return ret;
}

/**** Helper Functions */

/*
* do_twl4030_module_irq() is the desc->handle method for each of the twl4030
* module interrupts. It executes in kernel thread context.
* On entry, cpu interrupts are disabled.
*/
static void do_twl4030_module_irq(unsigned int irq, struct irqdesc *desc,
struct pt_regs *regs)
{
struct irqaction *action;
const unsigned int cpu = smp_processor_id();

desc->triggered = 1;

/*
* The desc->handle method would normally call the desc->chip->ack
* method here, but we won't bother since our ack method is NULL.
*/
printk (KERN_INFO "do twl_module_irq\n") ;

if (!desc->disable_depth) {
kstat_cpu(cpu).irqs[irq]++;

action = desc->action;
if (action) {
int ret;
int status = 0;
int retval = 0;

//desc->status |= IRQ_INPROGRESS;

local_irq_enable();

do {
/* Call the ISR with cpu interrupts enabled. */
printk (KERN_INFO "Calling Handler\n") ;
ret = action->handler(irq, action->dev_id,
regs);
if (ret == IRQ_HANDLED)
status |= action->flags;
retval |= ret;
action = action->next;
} while (action);

if (status & SA_SAMPLE_RANDOM)
add_interrupt_randomness(irq);

local_irq_disable();

if (retval != IRQ_HANDLED) {
printk(KERN_ERR "ISR for TWL4030 module"
" irq %d can't handle interrupt\n",
irq);
}

/*
* Here is where we should call the unmask method, but
* again we won't bother since it is NULL.
*/
} else {
printk(KERN_CRIT "TWL4030 module irq %d has no ISR"
" but can't be masked!\n", irq);
}
//desc->status &= ~IRQ_INPROGRESS;
} else {
printk(KERN_CRIT "TWL4030 module irq %d is disabled but can't"
" be masked!\n", irq);
}
}

/*
* twl4030_irq_thread() runs as a kernel thread. It queries the twl4030
* interrupt controller to see which modules are generating interrupt requests
* and then calls the desc->handle method for each module requesting service.
*/
static int twl4030_irq_thread(void *data)
{
struct sched_param param;
int irq = (int) data;
struct irqdesc *desc = irq_desc + irq;
static unsigned i2c_errors;
const static unsigned max_i2c_errors = 100;

current->flags |= PF_NOFREEZE;

param.sched_priority = CONFIG_TWL4030_IRQ_PRIO;
sys_sched_setscheduler(current->pid, SCHED_FIFO, &param);

while (!kthread_should_stop()) {
int ret;
int module_irq;
u8 pih_isr;

ret = twl4030_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr,
REG_PIH_ISR_P1);
if (ret) {
printk(KERN_WARNING "I2C error %d while reading TWL4030"
" PIH ISR register.\n", ret);
if (++i2c_errors >= max_i2c_errors) {
printk(KERN_ERR "Maximum I2C error count"
" exceeded. Terminating %s.\n",
__FUNCTION__);
break;
}
continue;
}

for (module_irq = IH_TWL4030_BASE; 0 != pih_isr;
pih_isr >>= 1, module_irq++)
{
if (pih_isr & 0x1) {
struct irqdesc *d = irq_desc + module_irq;

local_irq_disable();

d->handle(module_irq, d, NULL);

local_irq_enable();
}
}

local_irq_disable();

//desc->status &= ~IRQ_INPROGRESS;
set_current_state(TASK_INTERRUPTIBLE);
desc->chip->unmask(irq);

local_irq_enable();

schedule();
}
set_current_state(TASK_RUNNING);
return 0;
}

/*
* do_twl4030_irq() is the desc->handle method for the twl4030 interrupt.
* This is a chained interrupt, so there is no desc->action method for it.
* Now we need to query the interrupt controller in the twl4030 to determine
* which module is generating the interrupt request. However, we can't do i2c
* transactions in interrupt context, so we must defer that work to a kernel
* thread. All we do here is acknowledge and mask the interrupt and wakeup
* the kernel thread.
*/
static void do_twl4030_irq(unsigned int irq, struct irqdesc *desc,
struct pt_regs *regs)
{
const unsigned int cpu = smp_processor_id();
struct task_struct *thread = (struct task_struct *) desc->data;

desc->triggered = 1;

/*
* Acknowledge, clear _AND_ disable the interrupt.
*/

printk (KERN_INFO "do_twl4030 irq\n") ;

desc->chip->ack(irq);

printk (KERN_INFO "desc_disable depth => %d\n", desc->disable_depth) ;

if (!desc->disable_depth) {
kstat_cpu(cpu).irqs[irq]++;

//desc->status |= IRQ_INPROGRESS;
printk ("thread->state=> %x:%d\n", thread, thread->state) ;
if (thread && thread->state != TASK_RUNNING) {
printk (KERN_INFO "Calling twl_module_irq\n") ;
wake_up_process(thread);
}
}
}

/* attach a client to the adapter */
static int twl4030_detect_client(struct i2c_adapter *adapter, unsigned char sid)
{
int err = 0;
struct twl4030_client *client;

FN_IN;
if (unlikely(sid >= TWL4030_NUM_SLAVES)) {
printk(KERN_ERR "TWL4030: sid[%d] >MOD_LAST[%d]\n", sid,
TWL4030_NUM_SLAVES);
FN_OUT(EPERM);
return -EPERM;
}

/* Check basic functionality */
if (!(err = i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_WRITE_BYTE))) {
printk(KERN_WARNING
"TWL4030: SlaveID=%d functionality check failed\n", sid);
FN_OUT(err);
return err;
}
client = &(twl4030_modules[sid]);
if (unlikely(client->inuse)) {
printk(KERN_ERR "TWL4030: Client is already in Use.....\n");
printk("%s[ID=0x%x] NOT attached to I2c Adapter %s\n",
client->client_name, client->address, adapter->name);
FN_OUT(EPERM);
return -EPERM;
}

memset(&(client->client), 0, sizeof(struct i2c_client));

client->client.addr = client->address;
client->client.adapter = adapter;
client->client.driver = &twl4030_driver;
if ((adapter->flags & I2C_FUNC_HIGH_SPEED) == I2C_FUNC_HIGH_SPEED) {
client->client.flags = I2C_M_HS; /* We are HS capable */
}
memcpy(&(client->client.name), client->client_name,
sizeof(TWL_CLIENT_STRING) + 1);
client->client.id = twl4030_id++;
//XXX client->client.data = (void *)client;
if ((err = i2c_attach_client(&(client->client)))) {
printk(KERN_WARNING
"TWL4030: Couldn't attach Slave %s on Adapter %s[%d][%x]\n",
client->client_name, adapter->name, err, err);
} else {
D1( "TWL4030: %s[ID=0x%x] attached to I2c Adapter %s\n",
client->client_name, client->address, adapter->name);
client->inuse = TWL_CLIENT_USED;
init_MUTEX(&client->xfer_lock);
}
FN_OUT(err);
return err;
}

/* adapter callback */
static int twl4030_attach_adapter(struct i2c_adapter *adapter)
{
int i;
int ret = 0;
static int twl_i2c_adapter = 0;
FN_IN;

printk (KERN_INFO "twl attach adapter\n") ;

for (i = 0; i < TWL4030_NUM_SLAVES; i++) {
/* Check if I need to hook on to this adapter or not */

if (twl4030_modules[i].adapter_index == twl_i2c_adapter) {
printk (KERN_INFO "Detect CLient\n") ;
if ((ret = twl4030_detect_client(adapter, i))) {
printk (KERN_INFO "Free Client: %d\n", i) ;
goto free_client;
}
}
}
twl_i2c_adapter++;

/* Check if the PIH module is initialized, if yes, then init
* the T2 Interrupt subsystem
*/
printk (KERN_INFO "inuse: %d, %d\n", \
twl4030_modules[twl4030_map[TWL4030_MODULE_PIH].sid].inuse, twl_irq_used);

if ((twl4030_modules[twl4030_map[TWL4030_MODULE_PIH].sid].inuse ==
TWL_CLIENT_USED) && (twl_irq_used != USED)) {
printk (KERN_INFO "twl_init_irq") ;
twl_init_irq();
twl_irq_used = USED;
}
FN_OUT(0);
return 0;
free_client:
printk(KERN_ERR
"TWL4030: TWL_CLIENT(Idx=%d] REGISTRATION FAILED=%d[0x%x]\n", i,
ret, ret);
/* ignore current slave..it never got registered */
i--;
while (i >= 0) {
/* now remove all those from the current adapter... */
if (twl4030_modules[i].adapter_index == twl_i2c_adapter) {
(void)
twl4030_detach_client(&(twl4030_modules[i].client));
}
i--;
}
FN_OUT(ret);
return ret;
}

/* adapter's callback */
static int twl4030_detach_client(struct i2c_client *iclient)
{
int err;
//XXX struct twl4030_client *client = (struct twl4030_client *)iclient->data;
FN_IN;
if ((err = i2c_detach_client(iclient))) {
printk(KERN_ERR
"TWL4030: Client deregistration failed, client not detached.\n");
FN_OUT(err);
return err;
}

/* Free 'em up */
//XXX client->inuse = TWL_CLIENT_FREE;
FN_OUT(0);

return 0;
}

struct task_struct *
start_twl4030_irq_thread(int irq)
{
struct task_struct *thread;

thread = kthread_create(twl4030_irq_thread, (void *) irq,
"twl4030 irq %d", irq);
if (!thread) {
printk(KERN_ERR
"%s: could not create twl4030 irq %d thread!\n",
__FUNCTION__, irq);
}

return thread;
}

static void twl_init_irq(void)
{
int i = 0;
int res = 0;
int line = 0;
FN_IN;
#if 0
/*
* PARANOIA:
* Before we enable The module, check if the interrupt module has been initialized successfully
* Already checked - but *if* this going to be used from elsewhere...
*/
if (unlikely
(twl4030_modules[twl4030_map[TWL4030_MODULE_PIH].sid].inuse !=
TWL_CLIENT_USED)) {
printk(KERN_ERR
"TWL4030: TWL4030-Interrupt Module not registered\n");
/* Dont care abt deletion result!!-This should not happen at all */
BUG();
return;
}
#endif
#if (TWL4030_IRQNUM != INT_SYS)
if (omap_request_gpio(TWL4030_GPIO) != 0) {
printk(KERN_ERR "TWL4030: Could not reserve GPIO!\n");
/* Dont care abt deletion result!! */
FN_OUT(-1);
return;
};
omap_set_gpio_direction(TWL4030_GPIO, OMAP24XX_DIR_INPUT);
omap_set_gpio_edge_ctrl(TWL4030_GPIO, OMAP_GPIO_FALLING_EDGE);
#endif
/* We end up with interrupts from other modules before
* they get a chance to handle them...
*/
/* HACK ALERT - No Body to handle power ISR yet... */
/* POWER HACK (BEGIN) */
res = twl4030_i2c_write_u8(TWL4030_MODULE_INT, 0xFF, 0x00); /* PWR_ISR1 */
if (res < 0) {
line = __LINE__;
goto irq_exit_path;
}
res = twl4030_i2c_write_u8(TWL4030_MODULE_INT, 0xFF, 0x02); /* PWR_ISR2 */
if (res < 0) {
line = __LINE__;
goto irq_exit_path;
}
/* MASK PWR -we will need this */
res = twl4030_i2c_write_u8(TWL4030_MODULE_INT, 0xFF, 0x1); /* PWR_IMR1 */
if (res < 0) {
line = __LINE__;
goto irq_exit_path;
}
res = twl4030_i2c_write_u8(TWL4030_MODULE_INT, 0xFF, 0x3); /* PWR_IMR2 */
if (res < 0) {
line = __LINE__;
goto irq_exit_path;
}
/* Clear off any other pending interrupts on power */
res = twl4030_i2c_write_u8(TWL4030_MODULE_INT, 0xFF, 0x00); /* PWR_ISR1 */
if (res < 0) {
line = __LINE__;
goto irq_exit_path;
}
res = twl4030_i2c_write_u8(TWL4030_MODULE_INT, 0xFF, 0x02); /* PWR_ISR2 */
if (res < 0) {
line = __LINE__;
goto irq_exit_path;
}
/* POWER HACK (END) */
/* Slave address 0x4A */
res = twl4030_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xFF, 0x3); /* BCIIMR1_1 */
if (res < 0) {
line = __LINE__;
goto irq_exit_path;
}
res = twl4030_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xFF, 0x4); /* BCIIMR1_2 */
if (res < 0) {
line = __LINE__;
goto irq_exit_path;
}
res = twl4030_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xFF, 0x7); /* BCIIMR2_1 */
if (res < 0) {
line = __LINE__;
goto irq_exit_path;
}
res = twl4030_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xFF, 0x8); /* BCIIMR2_2 */
if (res < 0) {
line = __LINE__;
goto irq_exit_path;
}
/* MAD C */
res = twl4030_i2c_write_u8(TWL4030_MODULE_MADC, 0xFF, 0x62); /* MADC_IMR1 */
if (res < 0) {
line = __LINE__;
goto irq_exit_path;
}
res = twl4030_i2c_write_u8(TWL4030_MODULE_MADC, 0xFF, 0x64); /* MADC_IMR2 */
if (res < 0) {
line = __LINE__;
goto irq_exit_path;
}
/* key Pad */
res = twl4030_i2c_write_u8(TWL4030_MODULE_KEYPAD, 0xFF, (0x12)); /* KEYPAD - IMR1 */
if (res < 0) {
line = __LINE__;
goto irq_exit_path;
}
res = twl4030_i2c_write_u8(TWL4030_MODULE_KEYPAD, 0xFF, (0x14)); /* KEYPAD - IMR2 */
if (res < 0) {
line = __LINE__;
goto irq_exit_path;
}

/* Slave address 0x49 */
res = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0xFF, (0x1C)); /* GPIO_IMR1A */
if (res < 0) {
line = __LINE__;
goto irq_exit_path;
}

res = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0xFF, (0x1D)); /* GPIO_IMR2A */
if (res < 0) {
line = __LINE__;
goto irq_exit_path;
}

res = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0xFF, (0x1E)); /* GPIO_IMR3A */
if (res < 0) {
line = __LINE__;
goto irq_exit_path;
}
res = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0xFF, (0x22)); /* GPIO_IMR1B */
if (res < 0) {
line = __LINE__;
goto irq_exit_path;
}
res = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0xFF, (0x23)); /* GPIO_IMR2B */
if (res < 0) {
line = __LINE__;
goto irq_exit_path;
}
res = twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, 0xFF, (0x24)); /* GPIO_IMR3B */
if (res < 0) {
line = __LINE__;
goto irq_exit_path;
}


/* install an irq handler for each of the PIH modules */
for (i = IH_TWL4030_BASE; i <= IH_TWL4030_END; i++) {
set_irq_chip(i, &twl4030_irq_chip);
set_irq_handler(i, do_twl4030_module_irq);
set_irq_flags(i, IRQF_VALID);
}

/* install an irq handler to demultiplex the TWL4030 interrupt */
set_irq_data(TWL4030_IRQNUM, start_twl4030_irq_thread(TWL4030_IRQNUM));
set_irq_chained_handler(TWL4030_IRQNUM, do_twl4030_irq);

power_companion_init(); /* finish power and clock init */

irq_exit_path:
if (res) {
printk(KERN_ERR
"TWL4030: Unable to register interrupt subsystem[%d][%d]\n",
res, line);
}
FN_OUT(res);
}

static int __init twl4030_init(void)
{
int res;
FN_IN;

if ((res = i2c_add_driver(&twl4030_driver))) {
printk(KERN_ERR
"TWL4030: Driver registration failed, module not inserted.\n");
FN_OUT(res);
return res;
}
#if (TWL4030_LOG_LEVEL >= 1)
{
/* debug dump */
int mod = 0;
for (mod = 0; mod <= TWL4030_MODULE_LAST; mod++) {
u8 sid = twl4030_map[mod].sid;
u8 sadd = twl4030_modules[sid].address;
printk
("W8 - mod=%d[0x%x], sid=%d[0x%x] sad=%d[0x%x], modRegBaseAdd=%d[0x%x] \n",
mod, mod, sid, sid, sadd, sadd,
twl4030_map[mod].base, twl4030_map[mod].base);
}
}
#endif

#if CONFIG_I2C_TWL4030_DBG_SYSFS
{
int ret = 0;
/* A dummy device */
ret = platform_device_register(&twl4030_debug_dev);
if (ret != 0) {
printk(KERN_ERR "Platform dev_register failed =%d\n", ret);
ret = -ENODEV;
} else {
TWL_SYS_DEV();
}
}
#endif

printk(KERN_INFO "TWL4030: Driver registration complete.\n");
FN_OUT(res);
return res;
}

static void __exit twl4030_exit(void)
{
FN_IN;
if (i2c_del_driver(&twl4030_driver)) {
printk(KERN_ERR
"TWL4030: Driver remove failed, module not removed.\n");
}
twl_irq_used = FREE;
#if (TWL4030_IRQNUM != INT_SYS)
omap_free_gpio(TWL4030_GPIO);
#endif

FN_OUT(0);
}

subsys_initcall(twl4030_init);
module_exit(twl4030_exit);

EXPORT_SYMBOL(twl4030_i2c_write_u8);
EXPORT_SYMBOL(twl4030_i2c_read_u8);
EXPORT_SYMBOL(twl4030_i2c_read);
EXPORT_SYMBOL(twl4030_i2c_write);

MODULE_AUTHOR("Texas Instruments, Inc.");
MODULE_DESCRIPTION("I2C Core interface for TWL4030");
MODULE_LICENSE("GPL");