📄 twl4030_core.c
字号:
/* * twl4030_core.c - driver for TWL4030 PM and audio CODEC device * * 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@nokia.com> * * Code cleanup and modifications to IRQ handler. * by syed khasim <x0khasim@ti.com> * * 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 * */#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/slab_def.h>#include <linux/i2c.h>#include <linux/slab.h>#include <linux/clk.h>#include <linux/device.h>#include <linux/platform_device.h>#include <asm/irq.h>#include <asm/mach/irq.h>#include <asm/arch/twl4030.h>#include <asm/arch/gpio.h>#include <asm/arch/mux.h>#include <asm/arch/power_companion.h>#define DRIVER_NAME "twl4030"#define pr_err(fmt, arg...) printk(KERN_ERR DRIVER_NAME ": " fmt, ##arg);/**** 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)/* 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#define TWL4030_BASEADD_GPIO 0x0098#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) *//* Few power values */#define R_CFG_BOOT 0x05#define R_PROTECT_KEY 0x0E/* access control */#define KEY_UNLOCK1 0xce#define KEY_UNLOCK2 0xec#define KEY_LOCK 0x00#define HFCLK_FREQ_19p2_MHZ (1 << 0)#define HFCLK_FREQ_26_MHZ (2 << 0)#define HFCLK_FREQ_38p4_MHZ (3 << 0)#define HIGH_PERF_SQ (1 << 3)#define CONFIG_I2C_TWL4030_ID 1 /* on I2C1 for OMAP3 BEAGLE */#define twl4030_sysfs_debug_create() /* no definition */#define twl4030_sysfs_debug_remove() /* no definition */extern int power_companion_init(void);/**** Helper functions */static inttwl4030_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, irq_desc_t *desc);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;static struct completion irq_event;/* 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; /* max numb of i2c_msg required is for read =2 */ struct i2c_msg xfer_msg[2]; /* To lock access to xfer_msg */ struct semaphore xfer_lock;};/* 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_SLAVENUM_NUM1, TWL4030_BASEADD_AUDIO_VOICE }, { TWL4030_SLAVENUM_NUM1, TWL4030_BASEADD_GPIO }, { TWL4030_SLAVENUM_NUM1, TWL4030_BASEADD_INTBR }, { TWL4030_SLAVENUM_NUM1, TWL4030_BASEADD_PIH }, { TWL4030_SLAVENUM_NUM1, TWL4030_BASEADD_TEST }, { TWL4030_SLAVENUM_NUM2, TWL4030_BASEADD_KEYPAD }, { TWL4030_SLAVENUM_NUM2, TWL4030_BASEADD_MADC }, { TWL4030_SLAVENUM_NUM2, TWL4030_BASEADD_INTERRUPTS }, { TWL4030_SLAVENUM_NUM2, TWL4030_BASEADD_LED }, { TWL4030_SLAVENUM_NUM2, TWL4030_BASEADD_MAIN_CHARGE }, { TWL4030_SLAVENUM_NUM2, TWL4030_BASEADD_PRECHARGE }, { TWL4030_SLAVENUM_NUM2, TWL4030_BASEADD_PWM0 }, { TWL4030_SLAVENUM_NUM2, TWL4030_BASEADD_PWM1 }, { TWL4030_SLAVENUM_NUM2, TWL4030_BASEADD_PWMA }, { TWL4030_SLAVENUM_NUM2, TWL4030_BASEADD_PWMB }, { TWL4030_SLAVENUM_NUM3, TWL4030_BASEADD_BACKUP }, { TWL4030_SLAVENUM_NUM3, TWL4030_BASEADD_INT }, { TWL4030_SLAVENUM_NUM3, TWL4030_BASEADD_PM_MASTER }, { TWL4030_SLAVENUM_NUM3, TWL4030_BASEADD_PM_RECIEVER }, { TWL4030_SLAVENUM_NUM3, TWL4030_BASEADD_RTC }, { TWL4030_SLAVENUM_NUM3, TWL4030_BASEADD_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_ID, }, { .address = TWL4030_SLAVEID_ID1, .client_name = TWL_CLIENT_STRING "1", .adapter_index = CONFIG_I2C_TWL4030_ID, }, { .address = TWL4030_SLAVEID_ID2, .client_name = TWL_CLIENT_STRING "2", .adapter_index = CONFIG_I2C_TWL4030_ID, }, { .address = TWL4030_SLAVEID_ID3, .client_name = TWL_CLIENT_STRING "3", .adapter_index = CONFIG_I2C_TWL4030_ID, },};/* One Client Driver , 4 Clients */static struct i2c_driver twl4030_driver = { .driver.name = "TWL4030 I2C", .attach_adapter = twl4030_attach_adapter, .detach_client = twl4030_detach_client,};/* * TWL4030 doesn't have PIH mask, hence dummy function for mask * and unmask. */static void twl4030_i2c_ackirq(unsigned int irq) {}static void twl4030_i2c_disableint(unsigned int irq) {}static void twl4030_i2c_enableint(unsigned int irq) {}/* information for processing in the Work Item */static struct irq_chip 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; if (unlikely(mod_no > TWL4030_MODULE_LAST)) { pr_err("Invalid module Number\n"); return -EPERM; } sid = twl4030_map[mod_no].sid; client = &(twl4030_modules[sid]); if (unlikely(client->inuse != TWL_CLIENT_USED)) { pr_err("I2C Client[%d] is not initialized[%d]\n", sid,__LINE__); 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; msg->len = num_bytes + 1; msg->flags = 0; 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); up(&(client->xfer_lock)); /* i2cTransfer returns num messages.translate it pls.. */ if (ret >= 0) ret = 0; 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; if (unlikely(mod_no > TWL4030_MODULE_LAST)) { pr_err("Invalid module Number\n"); return -EPERM; } sid = twl4030_map[mod_no].sid; client = &(twl4030_modules[sid]); if (unlikely(client->inuse != TWL_CLIENT_USED)) { pr_err("I2C Client[%d] is not initialized[%d]\n", sid, __LINE__); return -EPERM; } down(&(client->xfer_lock)); /* [MSG1] fill the register address data */ msg = &(client->xfer_msg[0]); msg->addr = client->address; msg->len = 1; msg->flags = 0; /* Read the register 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; 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); up(&(client->xfer_lock)); /* i2cTransfer returns num messages.translate it pls.. */ if (ret >= 0) ret = 0; 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 }; /* offset 1 contains the data */ temp_buffer[1] = value; ret = twl4030_i2c_write(mod_no, temp_buffer, reg, 1); 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; ret = twl4030_i2c_read(mod_no, value, reg, 1); 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, irq_desc_t *desc){ struct irqaction *action; const unsigned int cpu = smp_processor_id(); /* * Earlier this was desc->triggered = 1; */ desc->status |= IRQ_LEVEL; /* * The desc->handle method would normally call the desc->chip->ack * method here, but we won't bother since our ack method is NULL. */ if (!desc->depth) { kstat_cpu(cpu).irqs[irq]++; action = desc->action; if (action) { int ret; int status = 0; int retval = 0; local_irq_enable(); do { /* Call the ISR with cpu interrupts enabled */ ret = action->handler(irq, action->dev_id); if (ret == IRQ_HANDLED) status |= action->flags; retval |= ret; action = action->next; } while (action); if (status & IRQF_SAMPLE_RANDOM) add_interrupt_randomness(irq);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -