2011-09-22 10:22:18 +04:00
/*
* Copyright ( C ) 2007 - 2011 ST - Ericsson
* License terms : GNU General Public License ( GPL ) version 2
* Low - level core for exclusive access to the AB5500 IC on the I2C bus
* and some basic chip - configuration .
* Author : Bengt Jonsson < bengt . g . jonsson @ stericsson . com >
* Author : Mattias Nilsson < mattias . i . nilsson @ stericsson . com >
* Author : Mattias Wallin < mattias . wallin @ stericsson . com >
* Author : Rickard Andersson < rickard . andersson @ stericsson . com >
* Author : Karl Komierowski < karl . komierowski @ stericsson . com >
* Author : Bibek Basu < bibek . basu @ stericsson . com >
*
* TODO : Event handling with irq_chip . Waiting for PRCMU fw support .
*/
2011-11-07 20:17:04 +04:00
# include <linux/module.h>
2011-09-22 10:22:18 +04:00
# include <linux/mutex.h>
# include <linux/err.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include <linux/device.h>
# include <linux/irq.h>
# include <linux/interrupt.h>
# include <linux/random.h>
# include <linux/mfd/abx500.h>
2011-12-02 17:16:33 +04:00
# include <linux/mfd/abx500/ab5500.h>
2011-09-22 10:22:18 +04:00
# include <linux/list.h>
# include <linux/bitops.h>
# include <linux/spinlock.h>
# include <linux/mfd/core.h>
# include <linux/mfd/db5500-prcmu.h>
2011-09-22 10:22:33 +04:00
# include "ab5500-core.h"
# include "ab5500-debugfs.h"
2011-09-22 10:22:18 +04:00
# define AB5500_NUM_EVENT_REG 23
# define AB5500_IT_LATCH0_REG 0x40
# define AB5500_IT_MASK0_REG 0x60
/*
* Permissible register ranges for reading and writing per device and bank .
*
* The ranges must be listed in increasing address order , and no overlaps are
* allowed . It is assumed that write permission implies read permission
* ( i . e . only RO and RW permissions should be used ) . Ranges with write
* permission must not be split up .
*/
# define NO_RANGE {.count = 0, .range = NULL,}
static struct ab5500_i2c_banks ab5500_bank_ranges [ AB5500_NUM_DEVICES ] = {
[ AB5500_DEVID_USB ] = {
. nbanks = 1 ,
. bank = ( struct ab5500_i2c_ranges [ ] ) {
{
. bankid = AB5500_BANK_USB ,
. nranges = 12 ,
. range = ( struct ab5500_reg_range [ ] ) {
{
. first = 0x01 ,
. last = 0x01 ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x80 ,
. last = 0x83 ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x87 ,
. last = 0x8A ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x8B ,
. last = 0x8B ,
. perm = AB5500_PERM_RO ,
} ,
{
. first = 0x91 ,
. last = 0x92 ,
. perm = AB5500_PERM_RO ,
} ,
{
. first = 0x93 ,
. last = 0x93 ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x94 ,
. last = 0x94 ,
. perm = AB5500_PERM_RO ,
} ,
{
. first = 0xA8 ,
. last = 0xB0 ,
. perm = AB5500_PERM_RO ,
} ,
{
. first = 0xB2 ,
. last = 0xB2 ,
. perm = AB5500_PERM_RO ,
} ,
{
. first = 0xB4 ,
. last = 0xBC ,
. perm = AB5500_PERM_RO ,
} ,
{
. first = 0xBF ,
. last = 0xBF ,
. perm = AB5500_PERM_RO ,
} ,
{
. first = 0xC1 ,
. last = 0xC5 ,
. perm = AB5500_PERM_RO ,
} ,
} ,
} ,
} ,
} ,
[ AB5500_DEVID_ADC ] = {
. nbanks = 1 ,
. bank = ( struct ab5500_i2c_ranges [ ] ) {
{
. bankid = AB5500_BANK_ADC ,
. nranges = 6 ,
. range = ( struct ab5500_reg_range [ ] ) {
{
. first = 0x1F ,
. last = 0x22 ,
. perm = AB5500_PERM_RO ,
} ,
{
. first = 0x23 ,
. last = 0x24 ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x26 ,
. last = 0x2D ,
. perm = AB5500_PERM_RO ,
} ,
{
. first = 0x2F ,
. last = 0x34 ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x37 ,
. last = 0x57 ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x58 ,
. last = 0x58 ,
. perm = AB5500_PERM_RO ,
} ,
} ,
} ,
} ,
} ,
[ AB5500_DEVID_LEDS ] = {
. nbanks = 1 ,
. bank = ( struct ab5500_i2c_ranges [ ] ) {
{
. bankid = AB5500_BANK_LED ,
. nranges = 1 ,
. range = ( struct ab5500_reg_range [ ] ) {
{
. first = 0x00 ,
. last = 0x0C ,
. perm = AB5500_PERM_RW ,
} ,
} ,
} ,
} ,
} ,
[ AB5500_DEVID_VIDEO ] = {
. nbanks = 1 ,
. bank = ( struct ab5500_i2c_ranges [ ] ) {
{
. bankid = AB5500_BANK_VDENC ,
. nranges = 12 ,
. range = ( struct ab5500_reg_range [ ] ) {
{
. first = 0x00 ,
. last = 0x08 ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x09 ,
. last = 0x09 ,
. perm = AB5500_PERM_RO ,
} ,
{
. first = 0x0A ,
. last = 0x12 ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x15 ,
. last = 0x19 ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x1B ,
. last = 0x21 ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x27 ,
. last = 0x2C ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x41 ,
. last = 0x41 ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x45 ,
. last = 0x5B ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x5D ,
. last = 0x5D ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x69 ,
. last = 0x69 ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x6C ,
. last = 0x6D ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x80 ,
. last = 0x81 ,
. perm = AB5500_PERM_RW ,
} ,
} ,
} ,
} ,
} ,
[ AB5500_DEVID_REGULATORS ] = {
. nbanks = 2 ,
. bank = ( struct ab5500_i2c_ranges [ ] ) {
{
. bankid = AB5500_BANK_STARTUP ,
. nranges = 12 ,
. range = ( struct ab5500_reg_range [ ] ) {
{
. first = 0x00 ,
. last = 0x01 ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x1F ,
. last = 0x1F ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x2E ,
. last = 0x2E ,
. perm = AB5500_PERM_RO ,
} ,
{
. first = 0x2F ,
. last = 0x30 ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x50 ,
. last = 0x51 ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x60 ,
. last = 0x61 ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x66 ,
. last = 0x8A ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x8C ,
. last = 0x96 ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0xAA ,
. last = 0xB4 ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0xB7 ,
. last = 0xBF ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0xC1 ,
. last = 0xCA ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0xD3 ,
. last = 0xE0 ,
. perm = AB5500_PERM_RW ,
} ,
} ,
} ,
{
. bankid = AB5500_BANK_SIM_USBSIM ,
. nranges = 1 ,
. range = ( struct ab5500_reg_range [ ] ) {
{
. first = 0x13 ,
. last = 0x19 ,
. perm = AB5500_PERM_RW ,
} ,
} ,
} ,
} ,
} ,
[ AB5500_DEVID_SIM ] = {
. nbanks = 1 ,
. bank = ( struct ab5500_i2c_ranges [ ] ) {
{
. bankid = AB5500_BANK_SIM_USBSIM ,
. nranges = 1 ,
. range = ( struct ab5500_reg_range [ ] ) {
{
. first = 0x13 ,
. last = 0x19 ,
. perm = AB5500_PERM_RW ,
} ,
} ,
} ,
} ,
} ,
[ AB5500_DEVID_RTC ] = {
. nbanks = 1 ,
. bank = ( struct ab5500_i2c_ranges [ ] ) {
{
. bankid = AB5500_BANK_RTC ,
. nranges = 2 ,
. range = ( struct ab5500_reg_range [ ] ) {
{
. first = 0x00 ,
. last = 0x04 ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0x06 ,
. last = 0x0C ,
. perm = AB5500_PERM_RW ,
} ,
} ,
} ,
} ,
} ,
[ AB5500_DEVID_CHARGER ] = {
. nbanks = 1 ,
. bank = ( struct ab5500_i2c_ranges [ ] ) {
{
. bankid = AB5500_BANK_CHG ,
. nranges = 2 ,
. range = ( struct ab5500_reg_range [ ] ) {
{
. first = 0x11 ,
. last = 0x11 ,
. perm = AB5500_PERM_RO ,
} ,
{
. first = 0x12 ,
. last = 0x1B ,
. perm = AB5500_PERM_RW ,
} ,
} ,
} ,
} ,
} ,
[ AB5500_DEVID_FUELGAUGE ] = {
. nbanks = 1 ,
. bank = ( struct ab5500_i2c_ranges [ ] ) {
{
. bankid = AB5500_BANK_FG_BATTCOM_ACC ,
. nranges = 2 ,
. range = ( struct ab5500_reg_range [ ] ) {
{
. first = 0x00 ,
. last = 0x0B ,
. perm = AB5500_PERM_RO ,
} ,
{
. first = 0x0C ,
. last = 0x10 ,
. perm = AB5500_PERM_RW ,
} ,
} ,
} ,
} ,
} ,
[ AB5500_DEVID_VIBRATOR ] = {
. nbanks = 1 ,
. bank = ( struct ab5500_i2c_ranges [ ] ) {
{
. bankid = AB5500_BANK_VIBRA ,
. nranges = 2 ,
. range = ( struct ab5500_reg_range [ ] ) {
{
. first = 0x10 ,
. last = 0x13 ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0xFE ,
. last = 0xFE ,
. perm = AB5500_PERM_RW ,
} ,
} ,
} ,
} ,
} ,
[ AB5500_DEVID_CODEC ] = {
. nbanks = 1 ,
. bank = ( struct ab5500_i2c_ranges [ ] ) {
{
. bankid = AB5500_BANK_AUDIO_HEADSETUSB ,
. nranges = 2 ,
. range = ( struct ab5500_reg_range [ ] ) {
{
. first = 0x00 ,
. last = 0x48 ,
. perm = AB5500_PERM_RW ,
} ,
{
. first = 0xEB ,
. last = 0xFB ,
. perm = AB5500_PERM_RW ,
} ,
} ,
} ,
} ,
} ,
[ AB5500_DEVID_POWER ] = {
. nbanks = 2 ,
. bank = ( struct ab5500_i2c_ranges [ ] ) {
{
. bankid = AB5500_BANK_STARTUP ,
. nranges = 1 ,
. range = ( struct ab5500_reg_range [ ] ) {
{
. first = 0x30 ,
. last = 0x30 ,
. perm = AB5500_PERM_RW ,
} ,
} ,
} ,
{
. bankid = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP ,
. nranges = 1 ,
. range = ( struct ab5500_reg_range [ ] ) {
{
. first = 0x01 ,
. last = 0x01 ,
. perm = AB5500_PERM_RW ,
} ,
} ,
} ,
} ,
} ,
} ;
# define AB5500_IRQ(bank, bit) ((bank) * 8 + (bit))
/* I appologize for the resource names beeing a mix of upper case
* and lower case but I want them to be exact as the documentation */
static struct mfd_cell ab5500_devs [ AB5500_NUM_DEVICES ] = {
[ AB5500_DEVID_LEDS ] = {
. name = " ab5500-leds " ,
. id = AB5500_DEVID_LEDS ,
} ,
[ AB5500_DEVID_POWER ] = {
. name = " ab5500-power " ,
. id = AB5500_DEVID_POWER ,
} ,
[ AB5500_DEVID_REGULATORS ] = {
. name = " ab5500-regulator " ,
. id = AB5500_DEVID_REGULATORS ,
} ,
[ AB5500_DEVID_SIM ] = {
. name = " ab5500-sim " ,
. id = AB5500_DEVID_SIM ,
. num_resources = 1 ,
. resources = ( struct resource [ ] ) {
{
. name = " SIMOFF " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 2 , 0 ) , /*rising*/
. end = AB5500_IRQ ( 2 , 1 ) , /*falling*/
} ,
} ,
} ,
[ AB5500_DEVID_RTC ] = {
. name = " ab5500-rtc " ,
. id = AB5500_DEVID_RTC ,
. num_resources = 1 ,
. resources = ( struct resource [ ] ) {
{
. name = " RTC_Alarm " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 1 , 7 ) ,
. end = AB5500_IRQ ( 1 , 7 ) ,
}
} ,
} ,
[ AB5500_DEVID_CHARGER ] = {
. name = " ab5500-charger " ,
. id = AB5500_DEVID_CHARGER ,
} ,
[ AB5500_DEVID_ADC ] = {
. name = " ab5500-adc " ,
. id = AB5500_DEVID_ADC ,
. num_resources = 10 ,
. resources = ( struct resource [ ] ) {
{
. name = " TRIGGER-0 " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 0 , 0 ) ,
. end = AB5500_IRQ ( 0 , 0 ) ,
} ,
{
. name = " TRIGGER-1 " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 0 , 1 ) ,
. end = AB5500_IRQ ( 0 , 1 ) ,
} ,
{
. name = " TRIGGER-2 " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 0 , 2 ) ,
. end = AB5500_IRQ ( 0 , 2 ) ,
} ,
{
. name = " TRIGGER-3 " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 0 , 3 ) ,
. end = AB5500_IRQ ( 0 , 3 ) ,
} ,
{
. name = " TRIGGER-4 " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 0 , 4 ) ,
. end = AB5500_IRQ ( 0 , 4 ) ,
} ,
{
. name = " TRIGGER-5 " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 0 , 5 ) ,
. end = AB5500_IRQ ( 0 , 5 ) ,
} ,
{
. name = " TRIGGER-6 " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 0 , 6 ) ,
. end = AB5500_IRQ ( 0 , 6 ) ,
} ,
{
. name = " TRIGGER-7 " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 0 , 7 ) ,
. end = AB5500_IRQ ( 0 , 7 ) ,
} ,
{
. name = " TRIGGER-VBAT " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 0 , 8 ) ,
. end = AB5500_IRQ ( 0 , 8 ) ,
} ,
{
. name = " TRIGGER-VBAT-TXON " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 0 , 9 ) ,
. end = AB5500_IRQ ( 0 , 9 ) ,
} ,
} ,
} ,
[ AB5500_DEVID_FUELGAUGE ] = {
. name = " ab5500-fuelgauge " ,
. id = AB5500_DEVID_FUELGAUGE ,
. num_resources = 6 ,
. resources = ( struct resource [ ] ) {
{
. name = " Batt_attach " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 7 , 5 ) ,
. end = AB5500_IRQ ( 7 , 5 ) ,
} ,
{
. name = " Batt_removal " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 7 , 6 ) ,
. end = AB5500_IRQ ( 7 , 6 ) ,
} ,
{
. name = " UART_framing " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 7 , 7 ) ,
. end = AB5500_IRQ ( 7 , 7 ) ,
} ,
{
. name = " UART_overrun " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 8 , 0 ) ,
. end = AB5500_IRQ ( 8 , 0 ) ,
} ,
{
. name = " UART_Rdy_RX " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 8 , 1 ) ,
. end = AB5500_IRQ ( 8 , 1 ) ,
} ,
{
. name = " UART_Rdy_TX " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 8 , 2 ) ,
. end = AB5500_IRQ ( 8 , 2 ) ,
} ,
} ,
} ,
[ AB5500_DEVID_VIBRATOR ] = {
. name = " ab5500-vibrator " ,
. id = AB5500_DEVID_VIBRATOR ,
} ,
[ AB5500_DEVID_CODEC ] = {
. name = " ab5500-codec " ,
. id = AB5500_DEVID_CODEC ,
. num_resources = 3 ,
. resources = ( struct resource [ ] ) {
{
. name = " audio_spkr1_ovc " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 9 , 5 ) ,
. end = AB5500_IRQ ( 9 , 5 ) ,
} ,
{
. name = " audio_plllocked " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 9 , 6 ) ,
. end = AB5500_IRQ ( 9 , 6 ) ,
} ,
{
. name = " audio_spkr2_ovc " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 17 , 4 ) ,
. end = AB5500_IRQ ( 17 , 4 ) ,
} ,
} ,
} ,
[ AB5500_DEVID_USB ] = {
. name = " ab5500-usb " ,
. id = AB5500_DEVID_USB ,
. num_resources = 36 ,
. resources = ( struct resource [ ] ) {
{
. name = " Link_Update " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 22 , 1 ) ,
. end = AB5500_IRQ ( 22 , 1 ) ,
} ,
{
. name = " DCIO " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 8 , 3 ) ,
. end = AB5500_IRQ ( 8 , 4 ) ,
} ,
{
. name = " VBUS_R " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 8 , 5 ) ,
. end = AB5500_IRQ ( 8 , 5 ) ,
} ,
{
. name = " VBUS_F " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 8 , 6 ) ,
. end = AB5500_IRQ ( 8 , 6 ) ,
} ,
{
. name = " CHGstate_10_PCVBUSchg " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 8 , 7 ) ,
. end = AB5500_IRQ ( 8 , 7 ) ,
} ,
{
. name = " DCIOreverse_ovc " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 9 , 0 ) ,
. end = AB5500_IRQ ( 9 , 0 ) ,
} ,
{
. name = " USBCharDetDone " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 9 , 1 ) ,
. end = AB5500_IRQ ( 9 , 1 ) ,
} ,
{
. name = " DCIO_no_limit " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 9 , 2 ) ,
. end = AB5500_IRQ ( 9 , 2 ) ,
} ,
{
. name = " USB_suspend " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 9 , 3 ) ,
. end = AB5500_IRQ ( 9 , 3 ) ,
} ,
{
. name = " DCIOreverse_fwdcurrent " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 9 , 4 ) ,
. end = AB5500_IRQ ( 9 , 4 ) ,
} ,
{
. name = " Vbus_Imeasmax_change " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 9 , 5 ) ,
. end = AB5500_IRQ ( 9 , 6 ) ,
} ,
{
. name = " OVV " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 14 , 5 ) ,
. end = AB5500_IRQ ( 14 , 5 ) ,
} ,
{
. name = " USBcharging_NOTok " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 15 , 3 ) ,
. end = AB5500_IRQ ( 15 , 3 ) ,
} ,
{
. name = " usb_adp_sensoroff " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 15 , 6 ) ,
. end = AB5500_IRQ ( 15 , 6 ) ,
} ,
{
. name = " usb_adp_probeplug " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 15 , 7 ) ,
. end = AB5500_IRQ ( 15 , 7 ) ,
} ,
{
. name = " usb_adp_sinkerror " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 16 , 0 ) ,
. end = AB5500_IRQ ( 16 , 6 ) ,
} ,
{
. name = " usb_adp_sourceerror " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 16 , 1 ) ,
. end = AB5500_IRQ ( 16 , 1 ) ,
} ,
{
. name = " usb_idgnd_r " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 16 , 2 ) ,
. end = AB5500_IRQ ( 16 , 2 ) ,
} ,
{
. name = " usb_idgnd_f " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 16 , 3 ) ,
. end = AB5500_IRQ ( 16 , 3 ) ,
} ,
{
. name = " usb_iddetR1 " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 16 , 4 ) ,
. end = AB5500_IRQ ( 16 , 5 ) ,
} ,
{
. name = " usb_iddetR2 " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 16 , 6 ) ,
. end = AB5500_IRQ ( 16 , 7 ) ,
} ,
{
. name = " usb_iddetR3 " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 17 , 0 ) ,
. end = AB5500_IRQ ( 17 , 1 ) ,
} ,
{
. name = " usb_iddetR4 " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 17 , 2 ) ,
. end = AB5500_IRQ ( 17 , 3 ) ,
} ,
{
. name = " CharTempWindowOk " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 17 , 7 ) ,
. end = AB5500_IRQ ( 18 , 0 ) ,
} ,
{
. name = " USB_SprDetect " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 18 , 1 ) ,
. end = AB5500_IRQ ( 18 , 1 ) ,
} ,
{
. name = " usb_adp_probe_unplug " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 18 , 2 ) ,
. end = AB5500_IRQ ( 18 , 2 ) ,
} ,
{
. name = " VBUSChDrop " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 18 , 3 ) ,
. end = AB5500_IRQ ( 18 , 4 ) ,
} ,
{
. name = " dcio_char_rec_done " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 18 , 5 ) ,
. end = AB5500_IRQ ( 18 , 5 ) ,
} ,
{
. name = " Charging_stopped_by_temp " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 18 , 6 ) ,
. end = AB5500_IRQ ( 18 , 6 ) ,
} ,
{
. name = " CHGstate_11_SafeModeVBUS " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 21 , 1 ) ,
. end = AB5500_IRQ ( 21 , 2 ) ,
} ,
{
. name = " CHGstate_12_comletedVBUS " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 21 , 2 ) ,
. end = AB5500_IRQ ( 21 , 2 ) ,
} ,
{
. name = " CHGstate_13_completedVBUS " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 21 , 3 ) ,
. end = AB5500_IRQ ( 21 , 3 ) ,
} ,
{
. name = " CHGstate_14_FullChgDCIO " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 21 , 4 ) ,
. end = AB5500_IRQ ( 21 , 4 ) ,
} ,
{
. name = " CHGstate_15_SafeModeDCIO " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 21 , 5 ) ,
. end = AB5500_IRQ ( 21 , 5 ) ,
} ,
{
. name = " CHGstate_16_OFFsuspendDCIO " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 21 , 6 ) ,
. end = AB5500_IRQ ( 21 , 6 ) ,
} ,
{
. name = " CHGstate_17_completedDCIO " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 21 , 7 ) ,
. end = AB5500_IRQ ( 21 , 7 ) ,
} ,
} ,
} ,
[ AB5500_DEVID_OTP ] = {
. name = " ab5500-otp " ,
. id = AB5500_DEVID_OTP ,
} ,
[ AB5500_DEVID_VIDEO ] = {
. name = " ab5500-video " ,
. id = AB5500_DEVID_VIDEO ,
. num_resources = 1 ,
. resources = ( struct resource [ ] ) {
{
. name = " plugTVdet " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 22 , 2 ) ,
. end = AB5500_IRQ ( 22 , 2 ) ,
} ,
} ,
} ,
[ AB5500_DEVID_DBIECI ] = {
. name = " ab5500-dbieci " ,
. id = AB5500_DEVID_DBIECI ,
. num_resources = 10 ,
. resources = ( struct resource [ ] ) {
{
. name = " COLL " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 14 , 0 ) ,
. end = AB5500_IRQ ( 14 , 0 ) ,
} ,
{
. name = " RESERR " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 14 , 1 ) ,
. end = AB5500_IRQ ( 14 , 1 ) ,
} ,
{
. name = " FRAERR " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 14 , 2 ) ,
. end = AB5500_IRQ ( 14 , 2 ) ,
} ,
{
. name = " COMERR " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 14 , 3 ) ,
. end = AB5500_IRQ ( 14 , 3 ) ,
} ,
{
. name = " BSI_indicator " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 14 , 4 ) ,
. end = AB5500_IRQ ( 14 , 4 ) ,
} ,
{
. name = " SPDSET " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 14 , 6 ) ,
. end = AB5500_IRQ ( 14 , 6 ) ,
} ,
{
. name = " DSENT " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 14 , 7 ) ,
. end = AB5500_IRQ ( 14 , 7 ) ,
} ,
{
. name = " DREC " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 15 , 0 ) ,
. end = AB5500_IRQ ( 15 , 0 ) ,
} ,
{
. name = " ACCINT " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 15 , 1 ) ,
. end = AB5500_IRQ ( 15 , 1 ) ,
} ,
{
. name = " NOPINT " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 15 , 2 ) ,
. end = AB5500_IRQ ( 15 , 2 ) ,
} ,
} ,
} ,
[ AB5500_DEVID_ONSWA ] = {
. name = " ab5500-onswa " ,
. id = AB5500_DEVID_ONSWA ,
. num_resources = 2 ,
. resources = ( struct resource [ ] ) {
{
. name = " ONSWAn_rising " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 1 , 3 ) ,
. end = AB5500_IRQ ( 1 , 3 ) ,
} ,
{
. name = " ONSWAn_falling " ,
. flags = IORESOURCE_IRQ ,
. start = AB5500_IRQ ( 1 , 4 ) ,
. end = AB5500_IRQ ( 1 , 4 ) ,
} ,
} ,
} ,
} ;
/*
* Functionality for getting / setting register values .
*/
2011-09-22 10:22:33 +04:00
int ab5500_get_register_interruptible_raw ( struct ab5500 * ab ,
u8 bank , u8 reg ,
u8 * value )
2011-09-22 10:22:18 +04:00
{
int err ;
if ( bank > = AB5500_NUM_BANKS )
return - EINVAL ;
err = mutex_lock_interruptible ( & ab - > access_mutex ) ;
if ( err )
return err ;
err = db5500_prcmu_abb_read ( bankinfo [ bank ] . slave_addr , reg , value , 1 ) ;
mutex_unlock ( & ab - > access_mutex ) ;
return err ;
}
static int get_register_page_interruptible ( struct ab5500 * ab , u8 bank ,
u8 first_reg , u8 * regvals , u8 numregs )
{
int err ;
if ( bank > = AB5500_NUM_BANKS )
return - EINVAL ;
err = mutex_lock_interruptible ( & ab - > access_mutex ) ;
if ( err )
return err ;
while ( numregs ) {
/* The hardware limit for get page is 4 */
u8 curnum = min_t ( u8 , numregs , 4u ) ;
err = db5500_prcmu_abb_read ( bankinfo [ bank ] . slave_addr ,
first_reg , regvals , curnum ) ;
if ( err )
goto out ;
numregs - = curnum ;
first_reg + = curnum ;
regvals + = curnum ;
}
out :
mutex_unlock ( & ab - > access_mutex ) ;
return err ;
}
2011-09-22 10:22:33 +04:00
int ab5500_mask_and_set_register_interruptible_raw ( struct ab5500 * ab , u8 bank ,
2011-09-22 10:22:18 +04:00
u8 reg , u8 bitmask , u8 bitvalues )
{
int err = 0 ;
if ( bank > = AB5500_NUM_BANKS )
return - EINVAL ;
if ( bitmask ) {
u8 buf ;
err = mutex_lock_interruptible ( & ab - > access_mutex ) ;
if ( err )
return err ;
if ( bitmask = = 0xFF ) /* No need to read in this case. */
buf = bitvalues ;
else { /* Read and modify the register value. */
err = db5500_prcmu_abb_read ( bankinfo [ bank ] . slave_addr ,
reg , & buf , 1 ) ;
if ( err )
return err ;
buf = ( ( ~ bitmask & buf ) | ( bitmask & bitvalues ) ) ;
}
/* Write the new value. */
err = db5500_prcmu_abb_write ( bankinfo [ bank ] . slave_addr , reg ,
& buf , 1 ) ;
mutex_unlock ( & ab - > access_mutex ) ;
}
return err ;
}
static int
set_register_interruptible ( struct ab5500 * ab , u8 bank , u8 reg , u8 value )
{
2011-09-22 10:22:33 +04:00
return ab5500_mask_and_set_register_interruptible_raw ( ab , bank , reg ,
0xff , value ) ;
2011-09-22 10:22:18 +04:00
}
/*
* Read / write permission checking functions .
*/
static const struct ab5500_i2c_ranges * get_bankref ( u8 devid , u8 bank )
{
u8 i ;
if ( devid < AB5500_NUM_DEVICES ) {
for ( i = 0 ; i < ab5500_bank_ranges [ devid ] . nbanks ; i + + ) {
if ( ab5500_bank_ranges [ devid ] . bank [ i ] . bankid = = bank )
return & ab5500_bank_ranges [ devid ] . bank [ i ] ;
}
}
return NULL ;
}
static bool page_write_allowed ( u8 devid , u8 bank , u8 first_reg , u8 last_reg )
{
u8 i ; /* range loop index */
const struct ab5500_i2c_ranges * bankref ;
bankref = get_bankref ( devid , bank ) ;
if ( bankref = = NULL | | last_reg < first_reg )
return false ;
for ( i = 0 ; i < bankref - > nranges ; i + + ) {
if ( first_reg < bankref - > range [ i ] . first )
break ;
if ( ( last_reg < = bankref - > range [ i ] . last ) & &
( bankref - > range [ i ] . perm & AB5500_PERM_WR ) )
return true ;
}
return false ;
}
static bool reg_write_allowed ( u8 devid , u8 bank , u8 reg )
{
return page_write_allowed ( devid , bank , reg , reg ) ;
}
static bool page_read_allowed ( u8 devid , u8 bank , u8 first_reg , u8 last_reg )
{
u8 i ;
const struct ab5500_i2c_ranges * bankref ;
bankref = get_bankref ( devid , bank ) ;
if ( bankref = = NULL | | last_reg < first_reg )
return false ;
/* Find the range (if it exists in the list) that includes first_reg. */
for ( i = 0 ; i < bankref - > nranges ; i + + ) {
if ( first_reg < bankref - > range [ i ] . first )
return false ;
if ( first_reg < = bankref - > range [ i ] . last )
break ;
}
/* Make sure that the entire range up to and including last_reg is
* readable . This may span several of the ranges in the list .
*/
while ( ( i < bankref - > nranges ) & &
( bankref - > range [ i ] . perm & AB5500_PERM_RD ) ) {
if ( last_reg < = bankref - > range [ i ] . last )
return true ;
if ( ( + + i > = bankref - > nranges ) | |
( bankref - > range [ i ] . first ! =
( bankref - > range [ i - 1 ] . last + 1 ) ) ) {
break ;
}
}
return false ;
}
static bool reg_read_allowed ( u8 devid , u8 bank , u8 reg )
{
return page_read_allowed ( devid , bank , reg , reg ) ;
}
/*
* The exported register access functionality .
*/
static int ab5500_get_chip_id ( struct device * dev )
{
struct ab5500 * ab = dev_get_drvdata ( dev - > parent ) ;
return ( int ) ab - > chip_id ;
}
static int ab5500_mask_and_set_register_interruptible ( struct device * dev ,
u8 bank , u8 reg , u8 bitmask , u8 bitvalues )
{
struct ab5500 * ab ;
struct platform_device * pdev = to_platform_device ( dev ) ;
if ( ( AB5500_NUM_BANKS < = bank ) | |
! reg_write_allowed ( pdev - > id , bank , reg ) )
return - EINVAL ;
ab = dev_get_drvdata ( dev - > parent ) ;
2011-09-22 10:22:33 +04:00
return ab5500_mask_and_set_register_interruptible_raw ( ab , bank , reg ,
2011-09-22 10:22:18 +04:00
bitmask , bitvalues ) ;
}
static int ab5500_set_register_interruptible ( struct device * dev , u8 bank ,
u8 reg , u8 value )
{
return ab5500_mask_and_set_register_interruptible ( dev , bank , reg , 0xFF ,
value ) ;
}
static int ab5500_get_register_interruptible ( struct device * dev , u8 bank ,
u8 reg , u8 * value )
{
struct ab5500 * ab ;
struct platform_device * pdev = to_platform_device ( dev ) ;
if ( ( AB5500_NUM_BANKS < = bank ) | |
! reg_read_allowed ( pdev - > id , bank , reg ) )
return - EINVAL ;
ab = dev_get_drvdata ( dev - > parent ) ;
2011-09-22 10:22:33 +04:00
return ab5500_get_register_interruptible_raw ( ab , bank , reg , value ) ;
2011-09-22 10:22:18 +04:00
}
static int ab5500_get_register_page_interruptible ( struct device * dev , u8 bank ,
u8 first_reg , u8 * regvals , u8 numregs )
{
struct ab5500 * ab ;
struct platform_device * pdev = to_platform_device ( dev ) ;
if ( ( AB5500_NUM_BANKS < = bank ) | |
! page_read_allowed ( pdev - > id , bank ,
first_reg , ( first_reg + numregs - 1 ) ) )
return - EINVAL ;
ab = dev_get_drvdata ( dev - > parent ) ;
return get_register_page_interruptible ( ab , bank , first_reg , regvals ,
numregs ) ;
}
static int
ab5500_event_registers_startup_state_get ( struct device * dev , u8 * event )
{
struct ab5500 * ab ;
ab = dev_get_drvdata ( dev - > parent ) ;
if ( ! ab - > startup_events_read )
return - EAGAIN ; /* Try again later */
memcpy ( event , ab - > startup_events , AB5500_NUM_EVENT_REG ) ;
return 0 ;
}
static struct abx500_ops ab5500_ops = {
. get_chip_id = ab5500_get_chip_id ,
. get_register = ab5500_get_register_interruptible ,
. set_register = ab5500_set_register_interruptible ,
. get_register_page = ab5500_get_register_page_interruptible ,
. set_register_page = NULL ,
. mask_and_set_register = ab5500_mask_and_set_register_interruptible ,
. event_registers_startup_state_get =
ab5500_event_registers_startup_state_get ,
. startup_irq_enabled = NULL ,
} ;
/*
* ab5500_setup : Basic set - up , datastructure creation / destruction
* and I2C interface . This sets up a default config
* in the AB5500 chip so that it will work as expected .
* @ ab : Pointer to ab5500 structure
* @ settings : Pointer to struct abx500_init_settings
* @ size : Size of init data
*/
static int __init ab5500_setup ( struct ab5500 * ab ,
struct abx500_init_settings * settings , unsigned int size )
{
int err = 0 ;
int i ;
for ( i = 0 ; i < size ; i + + ) {
2011-09-22 10:22:33 +04:00
err = ab5500_mask_and_set_register_interruptible_raw ( ab ,
2011-09-22 10:22:18 +04:00
settings [ i ] . bank ,
settings [ i ] . reg ,
0xFF , settings [ i ] . setting ) ;
if ( err )
goto exit_no_setup ;
/* If event mask register update the event mask in ab5500 */
if ( ( settings [ i ] . bank = = AB5500_BANK_IT ) & &
( AB5500_MASK_BASE < = settings [ i ] . reg ) & &
( settings [ i ] . reg < = AB5500_MASK_END ) ) {
ab - > mask [ settings [ i ] . reg - AB5500_MASK_BASE ] =
settings [ i ] . setting ;
}
}
exit_no_setup :
return err ;
}
struct ab_family_id {
u8 id ;
char * name ;
} ;
2012-03-31 00:04:56 +04:00
static const struct ab_family_id ids [ ] __initconst = {
2011-09-22 10:22:18 +04:00
/* AB5500 */
{
. id = AB5500_1_0 ,
. name = " 1.0 "
} ,
{
. id = AB5500_1_1 ,
. name = " 1.1 "
} ,
/* Terminator */
{
. id = 0x00 ,
}
} ;
static int __init ab5500_probe ( struct platform_device * pdev )
{
struct ab5500 * ab ;
struct ab5500_platform_data * ab5500_plf_data =
pdev - > dev . platform_data ;
int err ;
int i ;
ab = kzalloc ( sizeof ( struct ab5500 ) , GFP_KERNEL ) ;
if ( ! ab ) {
dev_err ( & pdev - > dev ,
" could not allocate ab5500 device \n " ) ;
return - ENOMEM ;
}
/* Initialize data structure */
mutex_init ( & ab - > access_mutex ) ;
mutex_init ( & ab - > irq_lock ) ;
ab - > dev = & pdev - > dev ;
platform_set_drvdata ( pdev , ab ) ;
/* Read chip ID register */
2011-09-22 10:22:33 +04:00
err = ab5500_get_register_interruptible_raw ( ab ,
AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP ,
AB5500_CHIP_ID , & ab - > chip_id ) ;
2011-09-22 10:22:18 +04:00
if ( err ) {
dev_err ( & pdev - > dev , " could not communicate with the analog "
" baseband chip \n " ) ;
goto exit_no_detect ;
}
for ( i = 0 ; ids [ i ] . id ! = 0x0 ; i + + ) {
if ( ids [ i ] . id = = ab - > chip_id ) {
snprintf ( & ab - > chip_name [ 0 ] , sizeof ( ab - > chip_name ) - 1 ,
" AB5500 %s " , ids [ i ] . name ) ;
break ;
}
}
if ( ids [ i ] . id = = 0x0 ) {
dev_err ( & pdev - > dev , " unknown analog baseband chip id: 0x%x \n " ,
ab - > chip_id ) ;
dev_err ( & pdev - > dev , " driver not started! \n " ) ;
goto exit_no_detect ;
}
/* Clear and mask all interrupts */
for ( i = 0 ; i < AB5500_NUM_IRQ_REGS ; i + + ) {
u8 latchreg = AB5500_IT_LATCH0_REG + i ;
u8 maskreg = AB5500_IT_MASK0_REG + i ;
u8 val ;
2011-09-22 10:22:33 +04:00
ab5500_get_register_interruptible_raw ( ab , AB5500_BANK_IT ,
latchreg , & val ) ;
2011-09-22 10:22:18 +04:00
set_register_interruptible ( ab , AB5500_BANK_IT , maskreg , 0xff ) ;
ab - > mask [ i ] = ab - > oldmask [ i ] = 0xff ;
}
err = abx500_register_ops ( & pdev - > dev , & ab5500_ops ) ;
if ( err ) {
dev_err ( & pdev - > dev , " ab5500_register ops error \n " ) ;
goto exit_no_detect ;
}
/* Set up and register the platform devices. */
for ( i = 0 ; i < AB5500_NUM_DEVICES ; i + + ) {
ab5500_devs [ i ] . platform_data = ab5500_plf_data - > dev_data [ i ] ;
ab5500_devs [ i ] . pdata_size =
sizeof ( ab5500_plf_data - > dev_data [ i ] ) ;
}
err = mfd_add_devices ( & pdev - > dev , 0 , ab5500_devs ,
ARRAY_SIZE ( ab5500_devs ) , NULL ,
ab5500_plf_data - > irq . base ) ;
if ( err ) {
dev_err ( & pdev - > dev , " ab5500_mfd_add_device error \n " ) ;
goto exit_no_detect ;
}
err = ab5500_setup ( ab , ab5500_plf_data - > init_settings ,
ab5500_plf_data - > init_settings_sz ) ;
if ( err ) {
dev_err ( & pdev - > dev , " ab5500_setup error \n " ) ;
goto exit_no_detect ;
}
ab5500_setup_debugfs ( ab ) ;
dev_info ( & pdev - > dev , " detected AB chip: %s \n " , & ab - > chip_name [ 0 ] ) ;
return 0 ;
exit_no_detect :
kfree ( ab ) ;
return err ;
}
static int __exit ab5500_remove ( struct platform_device * pdev )
{
struct ab5500 * ab = platform_get_drvdata ( pdev ) ;
ab5500_remove_debugfs ( ) ;
mfd_remove_devices ( & pdev - > dev ) ;
kfree ( ab ) ;
return 0 ;
}
static struct platform_driver ab5500_driver = {
. driver = {
. name = " ab5500-core " ,
. owner = THIS_MODULE ,
} ,
. remove = __exit_p ( ab5500_remove ) ,
} ;
static int __init ab5500_core_init ( void )
{
return platform_driver_probe ( & ab5500_driver , ab5500_probe ) ;
}
static void __exit ab5500_core_exit ( void )
{
platform_driver_unregister ( & ab5500_driver ) ;
}
subsys_initcall ( ab5500_core_init ) ;
module_exit ( ab5500_core_exit ) ;
MODULE_AUTHOR ( " Mattias Wallin <mattias.wallin@stericsson.com> " ) ;
MODULE_DESCRIPTION ( " AB5500 core driver " ) ;
MODULE_LICENSE ( " GPL " ) ;