Merge branch 'for-next' of git://git.infradead.org/users/sameo/mfd-2.6
* 'for-next' of git://git.infradead.org/users/sameo/mfd-2.6: (80 commits) mfd: Fix missing abx500 header file updates mfd: Add missing <linux/io.h> include to intel_msic x86, mrst: add platform support for MSIC MFD driver mfd: Expose TurnOnStatus in ab8500 sysfs mfd: Remove support for early drop ab8500 chip mfd: Add support for ab8500 v3.3 mfd: Add ab8500 interrupt disable hook mfd: Convert db8500-prcmu panic() into pr_crit() mfd: Refactor db8500-prcmu request_clock() function mfd: Rename db8500-prcmu init function mfd: Fix db5500-prcmu defines mfd: db8500-prcmu voltage domain consumers additions mfd: db8500-prcmu reset code retrieval mfd: db8500-prcmu tweak for modem wakeup mfd: Add db8500-pcmu watchdog accessor functions for watchdog mfd: hwacc power state db8500-prcmu accessor mfd: Add db8500-prcmu accessors for PLL and SGA clock mfd: Move to the new db500 PRCMU API mfd: Create a common interface for dbx500 PRCMU drivers mfd: Initialize DB8500 PRCMU regs ... Fix up trivial conflicts in arch/arm/mach-imx/mach-mx31moboard.c arch/arm/mach-omap2/board-omap3beagle.c arch/arm/mach-u300/include/mach/irqs.h drivers/mfd/wm831x-spi.c
This commit is contained in:
@@ -2,23 +2,8 @@
|
||||
# Multifunction miscellaneous devices
|
||||
#
|
||||
|
||||
menuconfig MFD_SUPPORT
|
||||
bool "Multifunction device drivers"
|
||||
depends on HAS_IOMEM
|
||||
default y
|
||||
help
|
||||
Multifunction devices embed several functions (e.g. GPIOs,
|
||||
touchscreens, keyboards, current regulators, power management chips,
|
||||
etc...) in one single integrated circuit. They usually talk to the
|
||||
main CPU through one or more IRQ lines and low speed data busses (SPI,
|
||||
I2C, etc..). They appear as one single device to the main system
|
||||
through the data bus and the MFD framework allows for sub devices
|
||||
(a.k.a. functions) to appear as discrete platform devices.
|
||||
MFDs are typically found on embedded platforms.
|
||||
|
||||
This option alone does not add any kernel code.
|
||||
|
||||
if MFD_SUPPORT
|
||||
if HAS_IOMEM
|
||||
menu "Multifunction device drivers"
|
||||
|
||||
config MFD_CORE
|
||||
tristate
|
||||
@@ -390,6 +375,7 @@ config MFD_WM8400
|
||||
tristate "Support Wolfson Microelectronics WM8400"
|
||||
select MFD_CORE
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Support for the Wolfson Microelecronics WM8400 PMIC and audio
|
||||
CODEC. This driver provides common support for accessing
|
||||
@@ -503,6 +489,7 @@ config MFD_WM8994
|
||||
config MFD_PCF50633
|
||||
tristate "Support for NXP PCF50633"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say yes here if you have NXP PCF50633 chip on your board.
|
||||
This core driver provides register access and IRQ handling
|
||||
@@ -579,6 +566,23 @@ config EZX_PCAP
|
||||
This enables the PCAP ASIC present on EZX Phones. This is
|
||||
needed for MMC, TouchScreen, Sound, USB, etc..
|
||||
|
||||
config AB5500_CORE
|
||||
bool "ST-Ericsson AB5500 Mixed Signal Power Management chip"
|
||||
depends on ABX500_CORE && MFD_DB5500_PRCMU
|
||||
select MFD_CORE
|
||||
help
|
||||
Select this option to enable access to AB5500 power management
|
||||
chip. This connects to the db5500 chip via the I2C bus via PRCMU.
|
||||
This chip embeds various other multimedia funtionalities as well.
|
||||
|
||||
config AB5500_DEBUG
|
||||
bool "Enable debug info via debugfs"
|
||||
depends on AB5500_CORE && DEBUG_FS
|
||||
default y if DEBUG_FS
|
||||
help
|
||||
Select this option if you want debug information from the AB5500
|
||||
using the debug filesystem, debugfs.
|
||||
|
||||
config AB8500_CORE
|
||||
bool "ST-Ericsson AB8500 Mixed Signal Power Management chip"
|
||||
depends on GENERIC_HARDIRQS && ABX500_CORE
|
||||
@@ -615,20 +619,6 @@ config AB8500_GPADC
|
||||
help
|
||||
AB8500 GPADC driver used to convert Acc and battery/ac/usb voltage
|
||||
|
||||
config AB3550_CORE
|
||||
bool "ST-Ericsson AB3550 Mixed Signal Circuit core functions"
|
||||
select MFD_CORE
|
||||
depends on I2C=y && GENERIC_HARDIRQS && ABX500_CORE
|
||||
help
|
||||
Select this to enable the AB3550 Mixed Signal IC core
|
||||
functionality. This connects to a AB3550 on the I2C bus
|
||||
and expose a number of symbols needed for dependent devices
|
||||
to read and write registers and subscribe to events from
|
||||
this multi-functional IC. This is needed to use other features
|
||||
of the AB3550 such as battery-backed RTC, charging control,
|
||||
LEDs, vibrator, system power and temperature, power management
|
||||
and ALSA sound.
|
||||
|
||||
config MFD_DB8500_PRCMU
|
||||
bool "ST-Ericsson DB8500 Power Reset Control Management Unit"
|
||||
depends on UX500_SOC_DB8500
|
||||
@@ -773,7 +763,17 @@ config MFD_AAT2870_CORE
|
||||
additional drivers must be enabled in order to use the
|
||||
functionality of the device.
|
||||
|
||||
endif # MFD_SUPPORT
|
||||
config MFD_INTEL_MSIC
|
||||
bool "Support for Intel MSIC"
|
||||
depends on INTEL_SCU_IPC
|
||||
select MFD_CORE
|
||||
help
|
||||
Select this option to enable access to Intel MSIC (Avatele
|
||||
Passage) chip. This chip embeds audio, battery, GPIO, etc.
|
||||
devices used in Intel Medfield platforms.
|
||||
|
||||
endmenu
|
||||
endif
|
||||
|
||||
menu "Multimedia Capabilities Port drivers"
|
||||
depends on ARCH_SA1100
|
||||
|
@@ -79,7 +79,8 @@ obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o
|
||||
obj-$(CONFIG_ABX500_CORE) += abx500-core.o
|
||||
obj-$(CONFIG_AB3100_CORE) += ab3100-core.o
|
||||
obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o
|
||||
obj-$(CONFIG_AB3550_CORE) += ab3550-core.o
|
||||
obj-$(CONFIG_AB5500_CORE) += ab5500-core.o
|
||||
obj-$(CONFIG_AB5500_DEBUG) += ab5500-debugfs.o
|
||||
obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o
|
||||
obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o
|
||||
obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o
|
||||
@@ -102,3 +103,4 @@ obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o
|
||||
obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o
|
||||
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
|
||||
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
|
||||
obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
|
||||
|
@@ -295,7 +295,7 @@ static ssize_t aat2870_reg_write_file(struct file *file,
|
||||
{
|
||||
struct aat2870_data *aat2870 = file->private_data;
|
||||
char buf[32];
|
||||
int buf_size;
|
||||
ssize_t buf_size;
|
||||
char *start = buf;
|
||||
unsigned long addr, val;
|
||||
int ret;
|
||||
|
@@ -809,7 +809,7 @@ struct ab_family_id {
|
||||
char *name;
|
||||
};
|
||||
|
||||
static const struct ab_family_id ids[] __devinitdata = {
|
||||
static const struct ab_family_id ids[] __devinitconst = {
|
||||
/* AB3100 */
|
||||
{
|
||||
.id = 0xc0,
|
||||
|
File diff suppressed because it is too large
Load Diff
1439
drivers/mfd/ab5500-core.c
Normal file
1439
drivers/mfd/ab5500-core.c
Normal file
File diff suppressed because it is too large
Load Diff
87
drivers/mfd/ab5500-core.h
Normal file
87
drivers/mfd/ab5500-core.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2011 ST-Ericsson
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
* Shared definitions and data structures for the AB5500 MFD driver
|
||||
*/
|
||||
|
||||
/* Read/write operation values. */
|
||||
#define AB5500_PERM_RD (0x01)
|
||||
#define AB5500_PERM_WR (0x02)
|
||||
|
||||
/* Read/write permissions. */
|
||||
#define AB5500_PERM_RO (AB5500_PERM_RD)
|
||||
#define AB5500_PERM_RW (AB5500_PERM_RD | AB5500_PERM_WR)
|
||||
|
||||
#define AB5500_MASK_BASE (0x60)
|
||||
#define AB5500_MASK_END (0x79)
|
||||
#define AB5500_CHIP_ID (0x20)
|
||||
|
||||
/**
|
||||
* struct ab5500_reg_range
|
||||
* @first: the first address of the range
|
||||
* @last: the last address of the range
|
||||
* @perm: access permissions for the range
|
||||
*/
|
||||
struct ab5500_reg_range {
|
||||
u8 first;
|
||||
u8 last;
|
||||
u8 perm;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ab5500_i2c_ranges
|
||||
* @count: the number of ranges in the list
|
||||
* @range: the list of register ranges
|
||||
*/
|
||||
struct ab5500_i2c_ranges {
|
||||
u8 nranges;
|
||||
u8 bankid;
|
||||
const struct ab5500_reg_range *range;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ab5500_i2c_banks
|
||||
* @count: the number of ranges in the list
|
||||
* @range: the list of register ranges
|
||||
*/
|
||||
struct ab5500_i2c_banks {
|
||||
u8 nbanks;
|
||||
const struct ab5500_i2c_ranges *bank;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ab5500_bank
|
||||
* @slave_addr: I2C slave_addr found in AB5500 specification
|
||||
* @name: Documentation name of the bank. For reference
|
||||
*/
|
||||
struct ab5500_bank {
|
||||
u8 slave_addr;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static const struct ab5500_bank bankinfo[AB5500_NUM_BANKS] = {
|
||||
[AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = {
|
||||
AB5500_ADDR_VIT_IO_I2C_CLK_TST_OTP, "VIT_IO_I2C_CLK_TST_OTP"},
|
||||
[AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = {
|
||||
AB5500_ADDR_VDDDIG_IO_I2C_CLK_TST, "VDDDIG_IO_I2C_CLK_TST"},
|
||||
[AB5500_BANK_VDENC] = {AB5500_ADDR_VDENC, "VDENC"},
|
||||
[AB5500_BANK_SIM_USBSIM] = {AB5500_ADDR_SIM_USBSIM, "SIM_USBSIM"},
|
||||
[AB5500_BANK_LED] = {AB5500_ADDR_LED, "LED"},
|
||||
[AB5500_BANK_ADC] = {AB5500_ADDR_ADC, "ADC"},
|
||||
[AB5500_BANK_RTC] = {AB5500_ADDR_RTC, "RTC"},
|
||||
[AB5500_BANK_STARTUP] = {AB5500_ADDR_STARTUP, "STARTUP"},
|
||||
[AB5500_BANK_DBI_ECI] = {AB5500_ADDR_DBI_ECI, "DBI-ECI"},
|
||||
[AB5500_BANK_CHG] = {AB5500_ADDR_CHG, "CHG"},
|
||||
[AB5500_BANK_FG_BATTCOM_ACC] = {
|
||||
AB5500_ADDR_FG_BATTCOM_ACC, "FG_BATCOM_ACC"},
|
||||
[AB5500_BANK_USB] = {AB5500_ADDR_USB, "USB"},
|
||||
[AB5500_BANK_IT] = {AB5500_ADDR_IT, "IT"},
|
||||
[AB5500_BANK_VIBRA] = {AB5500_ADDR_VIBRA, "VIBRA"},
|
||||
[AB5500_BANK_AUDIO_HEADSETUSB] = {
|
||||
AB5500_ADDR_AUDIO_HEADSETUSB, "AUDIO_HEADSETUSB"},
|
||||
};
|
||||
|
||||
int ab5500_get_register_interruptible_raw(struct ab5500 *ab, u8 bank, u8 reg,
|
||||
u8 *value);
|
||||
int ab5500_mask_and_set_register_interruptible_raw(struct ab5500 *ab, u8 bank,
|
||||
u8 reg, u8 bitmask, u8 bitvalues);
|
806
drivers/mfd/ab5500-debugfs.c
Normal file
806
drivers/mfd/ab5500-debugfs.c
Normal file
@@ -0,0 +1,806 @@
|
||||
/*
|
||||
* Copyright (C) 2011 ST-Ericsson
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
* Debugfs support for the AB5500 MFD driver
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/mfd/ab5500/ab5500.h>
|
||||
#include <linux/mfd/abx500.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "ab5500-core.h"
|
||||
#include "ab5500-debugfs.h"
|
||||
|
||||
static struct ab5500_i2c_ranges ab5500_reg_ranges[AB5500_NUM_BANKS] = {
|
||||
[AB5500_BANK_LED] = {
|
||||
.bankid = AB5500_BANK_LED,
|
||||
.nranges = 1,
|
||||
.range = (struct ab5500_reg_range[]) {
|
||||
{
|
||||
.first = 0x00,
|
||||
.last = 0x0C,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
},
|
||||
},
|
||||
[AB5500_BANK_ADC] = {
|
||||
.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_BANK_RTC] = {
|
||||
.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_BANK_STARTUP] = {
|
||||
.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,
|
||||
},
|
||||
},
|
||||
},
|
||||
[AB5500_BANK_DBI_ECI] = {
|
||||
.bankid = AB5500_BANK_DBI_ECI,
|
||||
.nranges = 3,
|
||||
.range = (struct ab5500_reg_range[]) {
|
||||
{
|
||||
.first = 0x00,
|
||||
.last = 0x07,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
{
|
||||
.first = 0x10,
|
||||
.last = 0x10,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
{
|
||||
.first = 0x13,
|
||||
.last = 0x13,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
},
|
||||
},
|
||||
[AB5500_BANK_CHG] = {
|
||||
.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_BANK_FG_BATTCOM_ACC] = {
|
||||
.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_BANK_USB] = {
|
||||
.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_BANK_IT] = {
|
||||
.bankid = AB5500_BANK_IT,
|
||||
.nranges = 4,
|
||||
.range = (struct ab5500_reg_range[]) {
|
||||
{
|
||||
.first = 0x00,
|
||||
.last = 0x02,
|
||||
.perm = AB5500_PERM_RO,
|
||||
},
|
||||
{
|
||||
.first = 0x20,
|
||||
.last = 0x36,
|
||||
.perm = AB5500_PERM_RO,
|
||||
},
|
||||
{
|
||||
.first = 0x40,
|
||||
.last = 0x56,
|
||||
.perm = AB5500_PERM_RO,
|
||||
},
|
||||
{
|
||||
.first = 0x60,
|
||||
.last = 0x76,
|
||||
.perm = AB5500_PERM_RO,
|
||||
},
|
||||
},
|
||||
},
|
||||
[AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = {
|
||||
.bankid = AB5500_BANK_VDDDIG_IO_I2C_CLK_TST,
|
||||
.nranges = 7,
|
||||
.range = (struct ab5500_reg_range[]) {
|
||||
{
|
||||
.first = 0x02,
|
||||
.last = 0x02,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
{
|
||||
.first = 0x12,
|
||||
.last = 0x12,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
{
|
||||
.first = 0x30,
|
||||
.last = 0x34,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
{
|
||||
.first = 0x40,
|
||||
.last = 0x44,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
{
|
||||
.first = 0x50,
|
||||
.last = 0x54,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
{
|
||||
.first = 0x60,
|
||||
.last = 0x64,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
{
|
||||
.first = 0x70,
|
||||
.last = 0x74,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
},
|
||||
},
|
||||
[AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = {
|
||||
.bankid = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP,
|
||||
.nranges = 13,
|
||||
.range = (struct ab5500_reg_range[]) {
|
||||
{
|
||||
.first = 0x01,
|
||||
.last = 0x01,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
{
|
||||
.first = 0x02,
|
||||
.last = 0x02,
|
||||
.perm = AB5500_PERM_RO,
|
||||
},
|
||||
{
|
||||
.first = 0x0D,
|
||||
.last = 0x0F,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
{
|
||||
.first = 0x1C,
|
||||
.last = 0x1C,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
{
|
||||
.first = 0x1E,
|
||||
.last = 0x1E,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
{
|
||||
.first = 0x20,
|
||||
.last = 0x21,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
{
|
||||
.first = 0x25,
|
||||
.last = 0x25,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
{
|
||||
.first = 0x28,
|
||||
.last = 0x2A,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
{
|
||||
.first = 0x30,
|
||||
.last = 0x33,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
{
|
||||
.first = 0x40,
|
||||
.last = 0x43,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
{
|
||||
.first = 0x50,
|
||||
.last = 0x53,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
{
|
||||
.first = 0x60,
|
||||
.last = 0x63,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
{
|
||||
.first = 0x70,
|
||||
.last = 0x73,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
},
|
||||
},
|
||||
[AB5500_BANK_VIBRA] = {
|
||||
.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_BANK_AUDIO_HEADSETUSB] = {
|
||||
.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_BANK_SIM_USBSIM] = {
|
||||
.bankid = AB5500_BANK_SIM_USBSIM,
|
||||
.nranges = 1,
|
||||
.range = (struct ab5500_reg_range[]) {
|
||||
{
|
||||
.first = 0x13,
|
||||
.last = 0x19,
|
||||
.perm = AB5500_PERM_RW,
|
||||
},
|
||||
},
|
||||
},
|
||||
[AB5500_BANK_VDENC] = {
|
||||
.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,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int ab5500_registers_print(struct seq_file *s, void *p)
|
||||
{
|
||||
struct ab5500 *ab = s->private;
|
||||
unsigned int i;
|
||||
u8 bank = (u8)ab->debug_bank;
|
||||
|
||||
seq_printf(s, "ab5500 register values:\n");
|
||||
for (bank = 0; bank < AB5500_NUM_BANKS; bank++) {
|
||||
seq_printf(s, " bank %u, %s (0x%x):\n", bank,
|
||||
bankinfo[bank].name,
|
||||
bankinfo[bank].slave_addr);
|
||||
for (i = 0; i < ab5500_reg_ranges[bank].nranges; i++) {
|
||||
u8 reg;
|
||||
int err;
|
||||
|
||||
for (reg = ab5500_reg_ranges[bank].range[i].first;
|
||||
reg <= ab5500_reg_ranges[bank].range[i].last;
|
||||
reg++) {
|
||||
u8 value;
|
||||
|
||||
err = ab5500_get_register_interruptible_raw(ab,
|
||||
bank, reg,
|
||||
&value);
|
||||
if (err < 0) {
|
||||
dev_err(ab->dev, "get_reg failed %d"
|
||||
"bank 0x%x reg 0x%x\n",
|
||||
err, bank, reg);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = seq_printf(s, "[%d/0x%02X]: 0x%02X\n",
|
||||
bank, reg, value);
|
||||
if (err < 0) {
|
||||
dev_err(ab->dev,
|
||||
"seq_printf overflow\n");
|
||||
/*
|
||||
* Error is not returned here since
|
||||
* the output is wanted in any case
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ab5500_registers_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, ab5500_registers_print, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations ab5500_registers_fops = {
|
||||
.open = ab5500_registers_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ab5500_bank_print(struct seq_file *s, void *p)
|
||||
{
|
||||
struct ab5500 *ab = s->private;
|
||||
|
||||
seq_printf(s, "%d\n", ab->debug_bank);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ab5500_bank_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, ab5500_bank_print, inode->i_private);
|
||||
}
|
||||
|
||||
static ssize_t ab5500_bank_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private;
|
||||
char buf[32];
|
||||
int buf_size;
|
||||
unsigned long user_bank;
|
||||
int err;
|
||||
|
||||
/* Get userspace string and assure termination */
|
||||
buf_size = min(count, (sizeof(buf) - 1));
|
||||
if (copy_from_user(buf, user_buf, buf_size))
|
||||
return -EFAULT;
|
||||
buf[buf_size] = 0;
|
||||
|
||||
err = strict_strtoul(buf, 0, &user_bank);
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
|
||||
if (user_bank >= AB5500_NUM_BANKS) {
|
||||
dev_err(ab->dev,
|
||||
"debugfs error input > number of banks\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ab->debug_bank = user_bank;
|
||||
|
||||
return buf_size;
|
||||
}
|
||||
|
||||
static int ab5500_address_print(struct seq_file *s, void *p)
|
||||
{
|
||||
struct ab5500 *ab = s->private;
|
||||
|
||||
seq_printf(s, "0x%02X\n", ab->debug_address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ab5500_address_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, ab5500_address_print, inode->i_private);
|
||||
}
|
||||
|
||||
static ssize_t ab5500_address_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private;
|
||||
char buf[32];
|
||||
int buf_size;
|
||||
unsigned long user_address;
|
||||
int err;
|
||||
|
||||
/* Get userspace string and assure termination */
|
||||
buf_size = min(count, (sizeof(buf) - 1));
|
||||
if (copy_from_user(buf, user_buf, buf_size))
|
||||
return -EFAULT;
|
||||
buf[buf_size] = 0;
|
||||
|
||||
err = strict_strtoul(buf, 0, &user_address);
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
if (user_address > 0xff) {
|
||||
dev_err(ab->dev,
|
||||
"debugfs error input > 0xff\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ab->debug_address = user_address;
|
||||
return buf_size;
|
||||
}
|
||||
|
||||
static int ab5500_val_print(struct seq_file *s, void *p)
|
||||
{
|
||||
struct ab5500 *ab = s->private;
|
||||
int err;
|
||||
u8 regvalue;
|
||||
|
||||
err = ab5500_get_register_interruptible_raw(ab, (u8)ab->debug_bank,
|
||||
(u8)ab->debug_address, ®value);
|
||||
if (err) {
|
||||
dev_err(ab->dev, "get_reg failed %d, bank 0x%x"
|
||||
", reg 0x%x\n", err, ab->debug_bank,
|
||||
ab->debug_address);
|
||||
return -EINVAL;
|
||||
}
|
||||
seq_printf(s, "0x%02X\n", regvalue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ab5500_val_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, ab5500_val_print, inode->i_private);
|
||||
}
|
||||
|
||||
static ssize_t ab5500_val_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private;
|
||||
char buf[32];
|
||||
int buf_size;
|
||||
unsigned long user_val;
|
||||
int err;
|
||||
u8 regvalue;
|
||||
|
||||
/* Get userspace string and assure termination */
|
||||
buf_size = min(count, (sizeof(buf)-1));
|
||||
if (copy_from_user(buf, user_buf, buf_size))
|
||||
return -EFAULT;
|
||||
buf[buf_size] = 0;
|
||||
|
||||
err = strict_strtoul(buf, 0, &user_val);
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
if (user_val > 0xff) {
|
||||
dev_err(ab->dev,
|
||||
"debugfs error input > 0xff\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
err = ab5500_mask_and_set_register_interruptible_raw(
|
||||
ab, (u8)ab->debug_bank,
|
||||
(u8)ab->debug_address, 0xFF, (u8)user_val);
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
|
||||
ab5500_get_register_interruptible_raw(ab, (u8)ab->debug_bank,
|
||||
(u8)ab->debug_address, ®value);
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
|
||||
return buf_size;
|
||||
}
|
||||
|
||||
static const struct file_operations ab5500_bank_fops = {
|
||||
.open = ab5500_bank_open,
|
||||
.write = ab5500_bank_write,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct file_operations ab5500_address_fops = {
|
||||
.open = ab5500_address_open,
|
||||
.write = ab5500_address_write,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct file_operations ab5500_val_fops = {
|
||||
.open = ab5500_val_open,
|
||||
.write = ab5500_val_write,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct dentry *ab5500_dir;
|
||||
static struct dentry *ab5500_reg_file;
|
||||
static struct dentry *ab5500_bank_file;
|
||||
static struct dentry *ab5500_address_file;
|
||||
static struct dentry *ab5500_val_file;
|
||||
|
||||
void __init ab5500_setup_debugfs(struct ab5500 *ab)
|
||||
{
|
||||
ab->debug_bank = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP;
|
||||
ab->debug_address = AB5500_CHIP_ID;
|
||||
|
||||
ab5500_dir = debugfs_create_dir("ab5500", NULL);
|
||||
if (!ab5500_dir)
|
||||
goto exit_no_debugfs;
|
||||
|
||||
ab5500_reg_file = debugfs_create_file("all-bank-registers",
|
||||
S_IRUGO, ab5500_dir, ab, &ab5500_registers_fops);
|
||||
if (!ab5500_reg_file)
|
||||
goto exit_destroy_dir;
|
||||
|
||||
ab5500_bank_file = debugfs_create_file("register-bank",
|
||||
(S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_bank_fops);
|
||||
if (!ab5500_bank_file)
|
||||
goto exit_destroy_reg;
|
||||
|
||||
ab5500_address_file = debugfs_create_file("register-address",
|
||||
(S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_address_fops);
|
||||
if (!ab5500_address_file)
|
||||
goto exit_destroy_bank;
|
||||
|
||||
ab5500_val_file = debugfs_create_file("register-value",
|
||||
(S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_val_fops);
|
||||
if (!ab5500_val_file)
|
||||
goto exit_destroy_address;
|
||||
|
||||
return;
|
||||
|
||||
exit_destroy_address:
|
||||
debugfs_remove(ab5500_address_file);
|
||||
exit_destroy_bank:
|
||||
debugfs_remove(ab5500_bank_file);
|
||||
exit_destroy_reg:
|
||||
debugfs_remove(ab5500_reg_file);
|
||||
exit_destroy_dir:
|
||||
debugfs_remove(ab5500_dir);
|
||||
exit_no_debugfs:
|
||||
dev_err(ab->dev, "failed to create debugfs entries.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
void __exit ab5500_remove_debugfs(void)
|
||||
{
|
||||
debugfs_remove(ab5500_val_file);
|
||||
debugfs_remove(ab5500_address_file);
|
||||
debugfs_remove(ab5500_bank_file);
|
||||
debugfs_remove(ab5500_reg_file);
|
||||
debugfs_remove(ab5500_dir);
|
||||
}
|
22
drivers/mfd/ab5500-debugfs.h
Normal file
22
drivers/mfd/ab5500-debugfs.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2011 ST-Ericsson
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
* Debugfs interface to the AB5500 core driver
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
void ab5500_setup_debugfs(struct ab5500 *ab);
|
||||
void ab5500_remove_debugfs(void);
|
||||
|
||||
#else /* !CONFIG_DEBUG_FS */
|
||||
|
||||
static inline void ab5500_setup_debugfs(struct ab5500 *ab)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void ab5500_remove_debugfs(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
@@ -92,6 +92,8 @@
|
||||
#define AB8500_REV_REG 0x80
|
||||
#define AB8500_SWITCH_OFF_STATUS 0x00
|
||||
|
||||
#define AB8500_TURN_ON_STATUS 0x00
|
||||
|
||||
/*
|
||||
* Map interrupt numbers to the LATCH and MASK register offsets, Interrupt
|
||||
* numbers are indexed into this array with (num / 8).
|
||||
@@ -293,6 +295,7 @@ static struct irq_chip ab8500_irq_chip = {
|
||||
.irq_bus_lock = ab8500_irq_lock,
|
||||
.irq_bus_sync_unlock = ab8500_irq_sync_unlock,
|
||||
.irq_mask = ab8500_irq_mask,
|
||||
.irq_disable = ab8500_irq_mask,
|
||||
.irq_unmask = ab8500_irq_unmask,
|
||||
};
|
||||
|
||||
@@ -811,12 +814,40 @@ static ssize_t show_switch_off_status(struct device *dev,
|
||||
return sprintf(buf, "%#x\n", value);
|
||||
}
|
||||
|
||||
/*
|
||||
* ab8500 has turned on due to (TURN_ON_STATUS):
|
||||
* 0x01 PORnVbat
|
||||
* 0x02 PonKey1dbF
|
||||
* 0x04 PonKey2dbF
|
||||
* 0x08 RTCAlarm
|
||||
* 0x10 MainChDet
|
||||
* 0x20 VbusDet
|
||||
* 0x40 UsbIDDetect
|
||||
* 0x80 Reserved
|
||||
*/
|
||||
static ssize_t show_turn_on_status(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int ret;
|
||||
u8 value;
|
||||
struct ab8500 *ab8500;
|
||||
|
||||
ab8500 = dev_get_drvdata(dev);
|
||||
ret = get_register_interruptible(ab8500, AB8500_SYS_CTRL1_BLOCK,
|
||||
AB8500_TURN_ON_STATUS, &value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return sprintf(buf, "%#x\n", value);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(chip_id, S_IRUGO, show_chip_id, NULL);
|
||||
static DEVICE_ATTR(switch_off_status, S_IRUGO, show_switch_off_status, NULL);
|
||||
static DEVICE_ATTR(turn_on_status, S_IRUGO, show_turn_on_status, NULL);
|
||||
|
||||
static struct attribute *ab8500_sysfs_entries[] = {
|
||||
&dev_attr_chip_id.attr,
|
||||
&dev_attr_switch_off_status.attr,
|
||||
&dev_attr_turn_on_status.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -843,11 +874,11 @@ int __devinit ab8500_init(struct ab8500 *ab8500)
|
||||
return ret;
|
||||
|
||||
switch (value) {
|
||||
case AB8500_CUTEARLY:
|
||||
case AB8500_CUT1P0:
|
||||
case AB8500_CUT1P1:
|
||||
case AB8500_CUT2P0:
|
||||
case AB8500_CUT3P0:
|
||||
case AB8500_CUT3P3:
|
||||
dev_info(ab8500->dev, "detected chip, revision: %#x\n", value);
|
||||
break;
|
||||
default:
|
||||
|
@@ -143,12 +143,15 @@ struct ab8500_gpadc *ab8500_gpadc_get(char *name)
|
||||
}
|
||||
EXPORT_SYMBOL(ab8500_gpadc_get);
|
||||
|
||||
static int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 input,
|
||||
/**
|
||||
* ab8500_gpadc_ad_to_voltage() - Convert a raw ADC value to a voltage
|
||||
*/
|
||||
int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
|
||||
int ad_value)
|
||||
{
|
||||
int res;
|
||||
|
||||
switch (input) {
|
||||
switch (channel) {
|
||||
case MAIN_CHARGER_V:
|
||||
/* For some reason we don't have calibrated data */
|
||||
if (!gpadc->cal_data[ADC_INPUT_VMAIN].gain) {
|
||||
@@ -232,18 +235,46 @@ static int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 input,
|
||||
}
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(ab8500_gpadc_ad_to_voltage);
|
||||
|
||||
/**
|
||||
* ab8500_gpadc_convert() - gpadc conversion
|
||||
* @input: analog input to be converted to digital data
|
||||
* @channel: analog channel to be converted to digital data
|
||||
*
|
||||
* This function converts the selected analog i/p to digital
|
||||
* data.
|
||||
*/
|
||||
int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input)
|
||||
int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel)
|
||||
{
|
||||
int ad_value;
|
||||
int voltage;
|
||||
|
||||
ad_value = ab8500_gpadc_read_raw(gpadc, channel);
|
||||
if (ad_value < 0) {
|
||||
dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n", channel);
|
||||
return ad_value;
|
||||
}
|
||||
|
||||
voltage = ab8500_gpadc_ad_to_voltage(gpadc, channel, ad_value);
|
||||
|
||||
if (voltage < 0)
|
||||
dev_err(gpadc->dev, "GPADC to voltage conversion failed ch:"
|
||||
" %d AD: 0x%x\n", channel, ad_value);
|
||||
|
||||
return voltage;
|
||||
}
|
||||
EXPORT_SYMBOL(ab8500_gpadc_convert);
|
||||
|
||||
/**
|
||||
* ab8500_gpadc_read_raw() - gpadc read
|
||||
* @channel: analog channel to be read
|
||||
*
|
||||
* This function obtains the raw ADC value, this then needs
|
||||
* to be converted by calling ab8500_gpadc_ad_to_voltage()
|
||||
*/
|
||||
int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
|
||||
{
|
||||
int ret;
|
||||
u16 data = 0;
|
||||
int looplimit = 0;
|
||||
u8 val, low_data, high_data;
|
||||
|
||||
@@ -278,9 +309,9 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Select the input source and set average samples to 16 */
|
||||
/* Select the channel source and set average samples to 16 */
|
||||
ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
|
||||
AB8500_GPADC_CTRL2_REG, (input | SW_AVG_16));
|
||||
AB8500_GPADC_CTRL2_REG, (channel | SW_AVG_16));
|
||||
if (ret < 0) {
|
||||
dev_err(gpadc->dev,
|
||||
"gpadc_conversion: set avg samples failed\n");
|
||||
@@ -292,7 +323,7 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input)
|
||||
* charging current sense if it needed, ABB 3.0 needs some special
|
||||
* treatment too.
|
||||
*/
|
||||
switch (input) {
|
||||
switch (channel) {
|
||||
case MAIN_CHARGER_C:
|
||||
case USB_CHARGER_C:
|
||||
ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
|
||||
@@ -359,7 +390,6 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input)
|
||||
goto out;
|
||||
}
|
||||
|
||||
data = (high_data << 8) | low_data;
|
||||
/* Disable GPADC */
|
||||
ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
|
||||
AB8500_GPADC_CTRL1_REG, DIS_GPADC);
|
||||
@@ -370,8 +400,8 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input)
|
||||
/* Disable VTVout LDO this is required for GPADC */
|
||||
regulator_disable(gpadc->regu);
|
||||
mutex_unlock(&gpadc->ab8500_gpadc_lock);
|
||||
ret = ab8500_gpadc_ad_to_voltage(gpadc, input, data);
|
||||
return ret;
|
||||
|
||||
return (high_data << 8) | low_data;
|
||||
|
||||
out:
|
||||
/*
|
||||
@@ -385,10 +415,10 @@ out:
|
||||
regulator_disable(gpadc->regu);
|
||||
mutex_unlock(&gpadc->ab8500_gpadc_lock);
|
||||
dev_err(gpadc->dev,
|
||||
"gpadc_conversion: Failed to AD convert channel %d\n", input);
|
||||
"gpadc_conversion: Failed to AD convert channel %d\n", channel);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(ab8500_gpadc_convert);
|
||||
EXPORT_SYMBOL(ab8500_gpadc_read_raw);
|
||||
|
||||
/**
|
||||
* ab8500_bm_gpswadcconvend_handler() - isr for s/w gpadc conversion completion
|
||||
|
@@ -584,7 +584,7 @@ static int asic3_gpio_remove(struct platform_device *pdev)
|
||||
return gpiochip_remove(&asic->gpio);
|
||||
}
|
||||
|
||||
static int asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk)
|
||||
static void asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 cdex;
|
||||
@@ -596,8 +596,6 @@ static int asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk)
|
||||
asic3_write_register(asic, ASIC3_OFFSET(CLOCK, CDEX), cdex);
|
||||
}
|
||||
spin_unlock_irqrestore(&asic->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void asic3_clk_disable(struct asic3 *asic, struct asic3_clk *clk)
|
||||
@@ -779,6 +777,8 @@ static struct mfd_cell asic3_cell_mmc = {
|
||||
.name = "tmio-mmc",
|
||||
.enable = asic3_mmc_enable,
|
||||
.disable = asic3_mmc_disable,
|
||||
.suspend = asic3_mmc_disable,
|
||||
.resume = asic3_mmc_enable,
|
||||
.platform_data = &asic3_mmc_data,
|
||||
.pdata_size = sizeof(asic3_mmc_data),
|
||||
.num_resources = ARRAY_SIZE(asic3_mmc_resources),
|
||||
@@ -811,24 +811,43 @@ static int asic3_leds_disable(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asic3_leds_suspend(struct platform_device *pdev)
|
||||
{
|
||||
const struct mfd_cell *cell = mfd_get_cell(pdev);
|
||||
struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
|
||||
|
||||
while (asic3_gpio_get(&asic->gpio, ASIC3_GPIO(C, cell->id)) != 0)
|
||||
msleep(1);
|
||||
|
||||
asic3_clk_disable(asic, &asic->clocks[clock_ledn[cell->id]]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct mfd_cell asic3_cell_leds[ASIC3_NUM_LEDS] = {
|
||||
[0] = {
|
||||
.name = "leds-asic3",
|
||||
.id = 0,
|
||||
.enable = asic3_leds_enable,
|
||||
.disable = asic3_leds_disable,
|
||||
.suspend = asic3_leds_suspend,
|
||||
.resume = asic3_leds_enable,
|
||||
},
|
||||
[1] = {
|
||||
.name = "leds-asic3",
|
||||
.id = 1,
|
||||
.enable = asic3_leds_enable,
|
||||
.disable = asic3_leds_disable,
|
||||
.suspend = asic3_leds_suspend,
|
||||
.resume = asic3_leds_enable,
|
||||
},
|
||||
[2] = {
|
||||
.name = "leds-asic3",
|
||||
.id = 2,
|
||||
.enable = asic3_leds_enable,
|
||||
.disable = asic3_leds_disable,
|
||||
.suspend = asic3_leds_suspend,
|
||||
.resume = asic3_leds_enable,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -949,6 +968,7 @@ static int __init asic3_probe(struct platform_device *pdev)
|
||||
goto out_unmap;
|
||||
}
|
||||
|
||||
asic->gpio.label = "asic3";
|
||||
asic->gpio.base = pdata->gpio_base;
|
||||
asic->gpio.ngpio = ASIC3_NUM_GPIOS;
|
||||
asic->gpio.get = asic3_gpio_get;
|
||||
|
@@ -523,7 +523,7 @@ static int __devinit da903x_probe(struct i2c_client *client,
|
||||
chip->ops->read_events(chip, &tmp);
|
||||
|
||||
ret = request_irq(client->irq, da903x_irq_handler,
|
||||
IRQF_DISABLED | IRQF_TRIGGER_FALLING,
|
||||
IRQF_TRIGGER_FALLING,
|
||||
"da903x", chip);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to request irq %d\n",
|
||||
|
@@ -20,11 +20,11 @@
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/db5500-prcmu.h>
|
||||
#include <linux/mfd/dbx500-prcmu.h>
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/irqs.h>
|
||||
#include <mach/db5500-regs.h>
|
||||
#include "db5500-prcmu-regs.h"
|
||||
#include "dbx500-prcmu-regs.h"
|
||||
|
||||
#define _PRCM_MB_HEADER (tcdm_base + 0xFE8)
|
||||
#define PRCM_REQ_MB0_HEADER (_PRCM_MB_HEADER + 0x0)
|
||||
@@ -109,15 +109,18 @@ enum mb5_header {
|
||||
#define PRCMU_DSI_CLOCK_SETTING 0x00000128
|
||||
/* TVCLK_MGT PLLSW=001 (PLLSOC0) PLLDIV=0x13, = 19.05 MHZ */
|
||||
#define PRCMU_DSI_LP_CLOCK_SETTING 0x00000135
|
||||
#define PRCMU_PLLDSI_FREQ_SETTING 0x0004013C
|
||||
#define PRCMU_PLLDSI_FREQ_SETTING 0x00020121
|
||||
#define PRCMU_DSI_PLLOUT_SEL_SETTING 0x00000002
|
||||
#define PRCMU_ENABLE_ESCAPE_CLOCK_DIV 0x03000101
|
||||
#define PRCMU_ENABLE_ESCAPE_CLOCK_DIV 0x03000201
|
||||
#define PRCMU_DISABLE_ESCAPE_CLOCK_DIV 0x00000101
|
||||
|
||||
#define PRCMU_ENABLE_PLLDSI 0x00000001
|
||||
#define PRCMU_DISABLE_PLLDSI 0x00000000
|
||||
|
||||
#define PRCMU_DSI_RESET_SW 0x00000003
|
||||
#define PRCMU_RESOUTN0_PIN 0x00000001
|
||||
#define PRCMU_RESOUTN1_PIN 0x00000002
|
||||
#define PRCMU_RESOUTN2_PIN 0x00000004
|
||||
|
||||
#define PRCMU_PLLDSI_LOCKP_LOCKED 0x3
|
||||
|
||||
@@ -315,31 +318,31 @@ static bool read_mailbox_0(void)
|
||||
r = false;
|
||||
break;
|
||||
}
|
||||
writel(MBOX_BIT(0), PRCM_ARM_IT1_CLEAR);
|
||||
writel(MBOX_BIT(0), PRCM_ARM_IT1_CLR);
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool read_mailbox_1(void)
|
||||
{
|
||||
writel(MBOX_BIT(1), PRCM_ARM_IT1_CLEAR);
|
||||
writel(MBOX_BIT(1), PRCM_ARM_IT1_CLR);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool read_mailbox_2(void)
|
||||
{
|
||||
writel(MBOX_BIT(2), PRCM_ARM_IT1_CLEAR);
|
||||
writel(MBOX_BIT(2), PRCM_ARM_IT1_CLR);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool read_mailbox_3(void)
|
||||
{
|
||||
writel(MBOX_BIT(3), PRCM_ARM_IT1_CLEAR);
|
||||
writel(MBOX_BIT(3), PRCM_ARM_IT1_CLR);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool read_mailbox_4(void)
|
||||
{
|
||||
writel(MBOX_BIT(4), PRCM_ARM_IT1_CLEAR);
|
||||
writel(MBOX_BIT(4), PRCM_ARM_IT1_CLR);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -360,19 +363,19 @@ static bool read_mailbox_5(void)
|
||||
print_unknown_header_warning(5, header);
|
||||
break;
|
||||
}
|
||||
writel(MBOX_BIT(5), PRCM_ARM_IT1_CLEAR);
|
||||
writel(MBOX_BIT(5), PRCM_ARM_IT1_CLR);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool read_mailbox_6(void)
|
||||
{
|
||||
writel(MBOX_BIT(6), PRCM_ARM_IT1_CLEAR);
|
||||
writel(MBOX_BIT(6), PRCM_ARM_IT1_CLR);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool read_mailbox_7(void)
|
||||
{
|
||||
writel(MBOX_BIT(7), PRCM_ARM_IT1_CLEAR);
|
||||
writel(MBOX_BIT(7), PRCM_ARM_IT1_CLR);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -434,7 +437,7 @@ int __init db5500_prcmu_init(void)
|
||||
return -ENODEV;
|
||||
|
||||
/* Clean up the mailbox interrupts after pre-kernel code. */
|
||||
writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLEAR);
|
||||
writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR);
|
||||
|
||||
r = request_threaded_irq(IRQ_DB5500_PRCMU1, prcmu_irq_handler,
|
||||
prcmu_irq_thread_fn, 0, "prcmu", NULL);
|
||||
|
@@ -1,166 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) STMicroelectronics 2009
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* Author: Kumar Sanghvi <kumar.sanghvi@stericsson.com>
|
||||
* Author: Sundar Iyer <sundar.iyer@stericsson.com>
|
||||
*
|
||||
* License Terms: GNU General Public License v2
|
||||
*
|
||||
* PRCM Unit registers
|
||||
*/
|
||||
#ifndef __DB8500_PRCMU_REGS_H
|
||||
#define __DB8500_PRCMU_REGS_H
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <mach/hardware.h>
|
||||
|
||||
#define BITS(_start, _end) ((BIT(_end) - BIT(_start)) + BIT(_end))
|
||||
|
||||
#define PRCM_ARM_PLLDIVPS 0x118
|
||||
#define PRCM_ARM_PLLDIVPS_ARM_BRM_RATE BITS(0, 5)
|
||||
#define PRCM_ARM_PLLDIVPS_MAX_MASK 0xF
|
||||
|
||||
#define PRCM_PLLARM_LOCKP 0x0A8
|
||||
#define PRCM_PLLARM_LOCKP_PRCM_PLLARM_LOCKP3 BIT(1)
|
||||
|
||||
#define PRCM_ARM_CHGCLKREQ 0x114
|
||||
#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ BIT(0)
|
||||
|
||||
#define PRCM_PLLARM_ENABLE 0x98
|
||||
#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_ENABLE BIT(0)
|
||||
#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_COUNTON BIT(8)
|
||||
|
||||
#define PRCM_ARMCLKFIX_MGT 0x0
|
||||
#define PRCM_A9_RESETN_CLR 0x1f4
|
||||
#define PRCM_A9_RESETN_SET 0x1f0
|
||||
#define PRCM_ARM_LS_CLAMP 0x30C
|
||||
#define PRCM_SRAM_A9 0x308
|
||||
|
||||
/* ARM WFI Standby signal register */
|
||||
#define PRCM_ARM_WFI_STANDBY 0x130
|
||||
#define PRCM_IOCR 0x310
|
||||
#define PRCM_IOCR_IOFORCE BIT(0)
|
||||
|
||||
/* CPU mailbox registers */
|
||||
#define PRCM_MBOX_CPU_VAL 0x0FC
|
||||
#define PRCM_MBOX_CPU_SET 0x100
|
||||
|
||||
/* Dual A9 core interrupt management unit registers */
|
||||
#define PRCM_A9_MASK_REQ 0x328
|
||||
#define PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ BIT(0)
|
||||
|
||||
#define PRCM_A9_MASK_ACK 0x32C
|
||||
#define PRCM_ARMITMSK31TO0 0x11C
|
||||
#define PRCM_ARMITMSK63TO32 0x120
|
||||
#define PRCM_ARMITMSK95TO64 0x124
|
||||
#define PRCM_ARMITMSK127TO96 0x128
|
||||
#define PRCM_POWER_STATE_VAL 0x25C
|
||||
#define PRCM_ARMITVAL31TO0 0x260
|
||||
#define PRCM_ARMITVAL63TO32 0x264
|
||||
#define PRCM_ARMITVAL95TO64 0x268
|
||||
#define PRCM_ARMITVAL127TO96 0x26C
|
||||
|
||||
#define PRCM_HOSTACCESS_REQ 0x334
|
||||
#define PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ BIT(0)
|
||||
|
||||
#define PRCM_ARM_IT1_CLR 0x48C
|
||||
#define PRCM_ARM_IT1_VAL 0x494
|
||||
|
||||
#define PRCM_ITSTATUS0 0x148
|
||||
#define PRCM_ITSTATUS1 0x150
|
||||
#define PRCM_ITSTATUS2 0x158
|
||||
#define PRCM_ITSTATUS3 0x160
|
||||
#define PRCM_ITSTATUS4 0x168
|
||||
#define PRCM_ITSTATUS5 0x484
|
||||
#define PRCM_ITCLEAR5 0x488
|
||||
#define PRCM_ARMIT_MASKXP70_IT 0x1018
|
||||
|
||||
/* System reset register */
|
||||
#define PRCM_APE_SOFTRST 0x228
|
||||
|
||||
/* Level shifter and clamp control registers */
|
||||
#define PRCM_MMIP_LS_CLAMP_SET 0x420
|
||||
#define PRCM_MMIP_LS_CLAMP_CLR 0x424
|
||||
|
||||
/* PRCMU HW semaphore */
|
||||
#define PRCM_SEM 0x400
|
||||
#define PRCM_SEM_PRCM_SEM BIT(0)
|
||||
|
||||
/* PRCMU clock/PLL/reset registers */
|
||||
#define PRCM_PLLDSI_FREQ 0x500
|
||||
#define PRCM_PLLDSI_ENABLE 0x504
|
||||
#define PRCM_PLLDSI_LOCKP 0x508
|
||||
#define PRCM_DSI_PLLOUT_SEL 0x530
|
||||
#define PRCM_DSITVCLK_DIV 0x52C
|
||||
#define PRCM_APE_RESETN_SET 0x1E4
|
||||
#define PRCM_APE_RESETN_CLR 0x1E8
|
||||
|
||||
#define PRCM_TCR 0x1C8
|
||||
#define PRCM_TCR_TENSEL_MASK BITS(0, 7)
|
||||
#define PRCM_TCR_STOP_TIMERS BIT(16)
|
||||
#define PRCM_TCR_DOZE_MODE BIT(17)
|
||||
|
||||
#define PRCM_CLKOCR 0x1CC
|
||||
#define PRCM_CLKOCR_CLKODIV0_SHIFT 0
|
||||
#define PRCM_CLKOCR_CLKODIV0_MASK BITS(0, 5)
|
||||
#define PRCM_CLKOCR_CLKOSEL0_SHIFT 6
|
||||
#define PRCM_CLKOCR_CLKOSEL0_MASK BITS(6, 8)
|
||||
#define PRCM_CLKOCR_CLKODIV1_SHIFT 16
|
||||
#define PRCM_CLKOCR_CLKODIV1_MASK BITS(16, 21)
|
||||
#define PRCM_CLKOCR_CLKOSEL1_SHIFT 22
|
||||
#define PRCM_CLKOCR_CLKOSEL1_MASK BITS(22, 24)
|
||||
#define PRCM_CLKOCR_CLK1TYPE BIT(28)
|
||||
|
||||
#define PRCM_SGACLK_MGT 0x014
|
||||
#define PRCM_UARTCLK_MGT 0x018
|
||||
#define PRCM_MSP02CLK_MGT 0x01C
|
||||
#define PRCM_MSP1CLK_MGT 0x288
|
||||
#define PRCM_I2CCLK_MGT 0x020
|
||||
#define PRCM_SDMMCCLK_MGT 0x024
|
||||
#define PRCM_SLIMCLK_MGT 0x028
|
||||
#define PRCM_PER1CLK_MGT 0x02C
|
||||
#define PRCM_PER2CLK_MGT 0x030
|
||||
#define PRCM_PER3CLK_MGT 0x034
|
||||
#define PRCM_PER5CLK_MGT 0x038
|
||||
#define PRCM_PER6CLK_MGT 0x03C
|
||||
#define PRCM_PER7CLK_MGT 0x040
|
||||
#define PRCM_LCDCLK_MGT 0x044
|
||||
#define PRCM_BMLCLK_MGT 0x04C
|
||||
#define PRCM_HSITXCLK_MGT 0x050
|
||||
#define PRCM_HSIRXCLK_MGT 0x054
|
||||
#define PRCM_HDMICLK_MGT 0x058
|
||||
#define PRCM_APEATCLK_MGT 0x05C
|
||||
#define PRCM_APETRACECLK_MGT 0x060
|
||||
#define PRCM_MCDECLK_MGT 0x064
|
||||
#define PRCM_IPI2CCLK_MGT 0x068
|
||||
#define PRCM_DSIALTCLK_MGT 0x06C
|
||||
#define PRCM_DMACLK_MGT 0x074
|
||||
#define PRCM_B2R2CLK_MGT 0x078
|
||||
#define PRCM_TVCLK_MGT 0x07C
|
||||
#define PRCM_UNIPROCLK_MGT 0x278
|
||||
#define PRCM_SSPCLK_MGT 0x280
|
||||
#define PRCM_RNGCLK_MGT 0x284
|
||||
#define PRCM_UICCCLK_MGT 0x27C
|
||||
|
||||
#define PRCM_CLK_MGT_CLKPLLDIV_MASK BITS(0, 4)
|
||||
#define PRCM_CLK_MGT_CLKPLLSW_MASK BITS(5, 7)
|
||||
#define PRCM_CLK_MGT_CLKEN BIT(8)
|
||||
|
||||
/* ePOD and memory power signal control registers */
|
||||
#define PRCM_EPOD_C_SET 0x410
|
||||
#define PRCM_SRAM_LS_SLEEP 0x304
|
||||
|
||||
/* Debug power control unit registers */
|
||||
#define PRCM_POWER_STATE_SET 0x254
|
||||
|
||||
/* Miscellaneous unit registers */
|
||||
#define PRCM_DSI_SW_RESET 0x324
|
||||
#define PRCM_GPIOCR 0x138
|
||||
|
||||
/* GPIOCR register */
|
||||
#define PRCM_GPIOCR_SPI2_SELECT BIT(23)
|
||||
|
||||
#define PRCM_DDR_SUBSYS_APE_MINBW 0x438
|
||||
|
||||
#endif /* __DB8500_PRCMU_REGS_H */
|
File diff suppressed because it is too large
Load Diff
@@ -10,11 +10,49 @@
|
||||
* PRCM Unit registers
|
||||
*/
|
||||
|
||||
#ifndef __MACH_PRCMU_REGS_H
|
||||
#define __MACH_PRCMU_REGS_H
|
||||
#ifndef __DB8500_PRCMU_REGS_H
|
||||
#define __DB8500_PRCMU_REGS_H
|
||||
|
||||
#include <mach/hardware.h>
|
||||
|
||||
#define BITS(_start, _end) ((BIT(_end) - BIT(_start)) + BIT(_end))
|
||||
|
||||
#define PRCM_SVACLK_MGT_OFF 0x008
|
||||
#define PRCM_SIACLK_MGT_OFF 0x00C
|
||||
#define PRCM_SGACLK_MGT_OFF 0x014
|
||||
#define PRCM_UARTCLK_MGT_OFF 0x018
|
||||
#define PRCM_MSP02CLK_MGT_OFF 0x01C
|
||||
#define PRCM_I2CCLK_MGT_OFF 0x020
|
||||
#define PRCM_SDMMCCLK_MGT_OFF 0x024
|
||||
#define PRCM_SLIMCLK_MGT_OFF 0x028
|
||||
#define PRCM_PER1CLK_MGT_OFF 0x02C
|
||||
#define PRCM_PER2CLK_MGT_OFF 0x030
|
||||
#define PRCM_PER3CLK_MGT_OFF 0x034
|
||||
#define PRCM_PER5CLK_MGT_OFF 0x038
|
||||
#define PRCM_PER6CLK_MGT_OFF 0x03C
|
||||
#define PRCM_PER7CLK_MGT_OFF 0x040
|
||||
#define PRCM_PWMCLK_MGT_OFF 0x044 /* for DB5500 */
|
||||
#define PRCM_IRDACLK_MGT_OFF 0x048 /* for DB5500 */
|
||||
#define PRCM_IRRCCLK_MGT_OFF 0x04C /* for DB5500 */
|
||||
#define PRCM_LCDCLK_MGT_OFF 0x044
|
||||
#define PRCM_BMLCLK_MGT_OFF 0x04C
|
||||
#define PRCM_HSITXCLK_MGT_OFF 0x050
|
||||
#define PRCM_HSIRXCLK_MGT_OFF 0x054
|
||||
#define PRCM_HDMICLK_MGT_OFF 0x058
|
||||
#define PRCM_APEATCLK_MGT_OFF 0x05C
|
||||
#define PRCM_APETRACECLK_MGT_OFF 0x060
|
||||
#define PRCM_MCDECLK_MGT_OFF 0x064
|
||||
#define PRCM_IPI2CCLK_MGT_OFF 0x068
|
||||
#define PRCM_DSIALTCLK_MGT_OFF 0x06C
|
||||
#define PRCM_DMACLK_MGT_OFF 0x074
|
||||
#define PRCM_B2R2CLK_MGT_OFF 0x078
|
||||
#define PRCM_TVCLK_MGT_OFF 0x07C
|
||||
#define PRCM_UNIPROCLK_MGT_OFF 0x278
|
||||
#define PRCM_SSPCLK_MGT_OFF 0x280
|
||||
#define PRCM_RNGCLK_MGT_OFF 0x284
|
||||
#define PRCM_UICCCLK_MGT_OFF 0x27C
|
||||
#define PRCM_MSP1CLK_MGT_OFF 0x288
|
||||
|
||||
#define PRCM_ARM_PLLDIVPS (_PRCMU_BASE + 0x118)
|
||||
#define PRCM_ARM_PLLDIVPS_ARM_BRM_RATE 0x3f
|
||||
#define PRCM_ARM_PLLDIVPS_MAX_MASK 0xf
|
||||
@@ -30,11 +68,15 @@
|
||||
#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_COUNTON 0x100
|
||||
|
||||
#define PRCM_ARMCLKFIX_MGT (_PRCMU_BASE + 0x0)
|
||||
#define PRCM_A9PL_FORCE_CLKEN (_PRCMU_BASE + 0x19C)
|
||||
#define PRCM_A9_RESETN_CLR (_PRCMU_BASE + 0x1f4)
|
||||
#define PRCM_A9_RESETN_SET (_PRCMU_BASE + 0x1f0)
|
||||
#define PRCM_ARM_LS_CLAMP (_PRCMU_BASE + 0x30c)
|
||||
#define PRCM_SRAM_A9 (_PRCMU_BASE + 0x308)
|
||||
|
||||
#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9PL_FORCE_CLKEN BIT(0)
|
||||
#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9AXI_FORCE_CLKEN BIT(1)
|
||||
|
||||
/* ARM WFI Standby signal register */
|
||||
#define PRCM_ARM_WFI_STANDBY (_PRCMU_BASE + 0x130)
|
||||
#define PRCM_IOCR (_PRCMU_BASE + 0x310)
|
||||
@@ -61,12 +103,18 @@
|
||||
#define PRCM_ARMITVAL127TO96 (_PRCMU_BASE + 0x26C)
|
||||
|
||||
#define PRCM_HOSTACCESS_REQ (_PRCMU_BASE + 0x334)
|
||||
#define PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ 0x1
|
||||
#define ARM_WAKEUP_MODEM 0x1
|
||||
|
||||
#define PRCM_ARM_IT1_CLEAR (_PRCMU_BASE + 0x48C)
|
||||
#define PRCM_ARM_IT1_CLR (_PRCMU_BASE + 0x48C)
|
||||
#define PRCM_ARM_IT1_VAL (_PRCMU_BASE + 0x494)
|
||||
#define PRCM_HOLD_EVT (_PRCMU_BASE + 0x174)
|
||||
|
||||
#define PRCM_MOD_AWAKE_STATUS (_PRCMU_BASE + 0x4A0)
|
||||
#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE BIT(0)
|
||||
#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE BIT(1)
|
||||
#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_VMODEM_OFF_ISO BIT(2)
|
||||
|
||||
#define PRCM_ITSTATUS0 (_PRCMU_BASE + 0x148)
|
||||
#define PRCM_ITSTATUS1 (_PRCMU_BASE + 0x150)
|
||||
#define PRCM_ITSTATUS2 (_PRCMU_BASE + 0x158)
|
||||
@@ -87,16 +135,21 @@
|
||||
#define PRCM_PLLDSI_FREQ (_PRCMU_BASE + 0x500)
|
||||
#define PRCM_PLLDSI_ENABLE (_PRCMU_BASE + 0x504)
|
||||
#define PRCM_PLLDSI_LOCKP (_PRCMU_BASE + 0x508)
|
||||
#define PRCM_LCDCLK_MGT (_PRCMU_BASE + 0x044)
|
||||
#define PRCM_MCDECLK_MGT (_PRCMU_BASE + 0x064)
|
||||
#define PRCM_HDMICLK_MGT (_PRCMU_BASE + 0x058)
|
||||
#define PRCM_TVCLK_MGT (_PRCMU_BASE + 0x07c)
|
||||
#define PRCM_LCDCLK_MGT (_PRCMU_BASE + PRCM_LCDCLK_MGT_OFF)
|
||||
#define PRCM_MCDECLK_MGT (_PRCMU_BASE + PRCM_MCDECLK_MGT_OFF)
|
||||
#define PRCM_HDMICLK_MGT (_PRCMU_BASE + PRCM_HDMICLK_MGT_OFF)
|
||||
#define PRCM_TVCLK_MGT (_PRCMU_BASE + PRCM_TVCLK_MGT_OFF)
|
||||
#define PRCM_DSI_PLLOUT_SEL (_PRCMU_BASE + 0x530)
|
||||
#define PRCM_DSITVCLK_DIV (_PRCMU_BASE + 0x52C)
|
||||
#define PRCM_PLLDSI_LOCKP (_PRCMU_BASE + 0x508)
|
||||
#define PRCM_APE_RESETN_SET (_PRCMU_BASE + 0x1E4)
|
||||
#define PRCM_APE_RESETN_CLR (_PRCMU_BASE + 0x1E8)
|
||||
|
||||
#define PRCM_CLKOCR (_PRCMU_BASE + 0x1CC)
|
||||
#define PRCM_CLKOCR_CLKOUT0_REF_CLK (1 << 0)
|
||||
#define PRCM_CLKOCR_CLKOUT0_MASK BITS(0, 13)
|
||||
#define PRCM_CLKOCR_CLKOUT1_REF_CLK (1 << 16)
|
||||
#define PRCM_CLKOCR_CLKOUT1_MASK BITS(16, 29)
|
||||
|
||||
/* ePOD and memory power signal control registers */
|
||||
#define PRCM_EPOD_C_SET (_PRCMU_BASE + 0x410)
|
||||
@@ -111,5 +164,41 @@
|
||||
#define PRCM_GPIOCR_DBG_STM_MOD_CMD1 0x800
|
||||
#define PRCM_GPIOCR_DBG_UARTMOD_CMD0 0x1
|
||||
|
||||
/* PRCMU HW semaphore */
|
||||
#define PRCM_SEM (_PRCMU_BASE + 0x400)
|
||||
#define PRCM_SEM_PRCM_SEM BIT(0)
|
||||
|
||||
#endif /* __MACH_PRCMU__REGS_H */
|
||||
#define PRCM_TCR (_PRCMU_BASE + 0x1C8)
|
||||
#define PRCM_TCR_TENSEL_MASK BITS(0, 7)
|
||||
#define PRCM_TCR_STOP_TIMERS BIT(16)
|
||||
#define PRCM_TCR_DOZE_MODE BIT(17)
|
||||
|
||||
#define PRCM_CLKOCR_CLKODIV0_SHIFT 0
|
||||
#define PRCM_CLKOCR_CLKODIV0_MASK BITS(0, 5)
|
||||
#define PRCM_CLKOCR_CLKOSEL0_SHIFT 6
|
||||
#define PRCM_CLKOCR_CLKOSEL0_MASK BITS(6, 8)
|
||||
#define PRCM_CLKOCR_CLKODIV1_SHIFT 16
|
||||
#define PRCM_CLKOCR_CLKODIV1_MASK BITS(16, 21)
|
||||
#define PRCM_CLKOCR_CLKOSEL1_SHIFT 22
|
||||
#define PRCM_CLKOCR_CLKOSEL1_MASK BITS(22, 24)
|
||||
#define PRCM_CLKOCR_CLK1TYPE BIT(28)
|
||||
|
||||
#define PRCM_CLK_MGT_CLKPLLDIV_MASK BITS(0, 4)
|
||||
#define PRCM_CLK_MGT_CLKPLLSW_MASK BITS(5, 7)
|
||||
#define PRCM_CLK_MGT_CLKEN BIT(8)
|
||||
|
||||
/* GPIOCR register */
|
||||
#define PRCM_GPIOCR_SPI2_SELECT BIT(23)
|
||||
|
||||
#define PRCM_DDR_SUBSYS_APE_MINBW (_PRCMU_BASE + 0x438)
|
||||
#define PRCM_CGATING_BYPASS (_PRCMU_BASE + 0x134)
|
||||
#define PRCM_CGATING_BYPASS_ICN2 BIT(6)
|
||||
|
||||
/* Miscellaneous unit registers */
|
||||
#define PRCM_RESOUTN_SET (_PRCMU_BASE + 0x214)
|
||||
#define PRCM_RESOUTN_CLR (_PRCMU_BASE + 0x218)
|
||||
|
||||
/* System reset register */
|
||||
#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228)
|
||||
|
||||
#endif /* __DB8500_PRCMU_REGS_H */
|
502
drivers/mfd/intel_msic.c
Normal file
502
drivers/mfd/intel_msic.c
Normal file
@@ -0,0 +1,502 @@
|
||||
/*
|
||||
* Driver for Intel MSIC
|
||||
*
|
||||
* Copyright (C) 2011, Intel Corporation
|
||||
* Author: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/intel_msic.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
#define MSIC_VENDOR(id) ((id >> 6) & 3)
|
||||
#define MSIC_VERSION(id) (id & 0x3f)
|
||||
#define MSIC_MAJOR(id) ('A' + ((id >> 3) & 7))
|
||||
#define MSIC_MINOR(id) (id & 7)
|
||||
|
||||
/*
|
||||
* MSIC interrupt tree is readable from SRAM at INTEL_MSIC_IRQ_PHYS_BASE.
|
||||
* Since IRQ block starts from address 0x002 we need to substract that from
|
||||
* the actual IRQ status register address.
|
||||
*/
|
||||
#define MSIC_IRQ_STATUS(x) (INTEL_MSIC_IRQ_PHYS_BASE + ((x) - 2))
|
||||
#define MSIC_IRQ_STATUS_ACCDET MSIC_IRQ_STATUS(INTEL_MSIC_ACCDET)
|
||||
|
||||
/*
|
||||
* The SCU hardware has limitation of 16 bytes per read/write buffer on
|
||||
* Medfield.
|
||||
*/
|
||||
#define SCU_IPC_RWBUF_LIMIT 16
|
||||
|
||||
/**
|
||||
* struct intel_msic - an MSIC MFD instance
|
||||
* @pdev: pointer to the platform device
|
||||
* @vendor: vendor ID
|
||||
* @version: chip version
|
||||
* @irq_base: base address of the mapped MSIC SRAM interrupt tree
|
||||
*/
|
||||
struct intel_msic {
|
||||
struct platform_device *pdev;
|
||||
unsigned vendor;
|
||||
unsigned version;
|
||||
void __iomem *irq_base;
|
||||
};
|
||||
|
||||
static struct resource msic_touch_resources[] = {
|
||||
{
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource msic_adc_resources[] = {
|
||||
{
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource msic_battery_resources[] = {
|
||||
{
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource msic_gpio_resources[] = {
|
||||
{
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource msic_audio_resources[] = {
|
||||
{
|
||||
.name = "IRQ",
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
/*
|
||||
* We will pass IRQ_BASE to the driver now but this can be removed
|
||||
* when/if the driver starts to use intel_msic_irq_read().
|
||||
*/
|
||||
{
|
||||
.name = "IRQ_BASE",
|
||||
.flags = IORESOURCE_MEM,
|
||||
.start = MSIC_IRQ_STATUS_ACCDET,
|
||||
.end = MSIC_IRQ_STATUS_ACCDET,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource msic_hdmi_resources[] = {
|
||||
{
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource msic_thermal_resources[] = {
|
||||
{
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource msic_power_btn_resources[] = {
|
||||
{
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource msic_ocd_resources[] = {
|
||||
{
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Devices that are part of the MSIC and are available via firmware
|
||||
* populated SFI DEVS table.
|
||||
*/
|
||||
static struct mfd_cell msic_devs[] = {
|
||||
[INTEL_MSIC_BLOCK_TOUCH] = {
|
||||
.name = "msic_touch",
|
||||
.num_resources = ARRAY_SIZE(msic_touch_resources),
|
||||
.resources = msic_touch_resources,
|
||||
},
|
||||
[INTEL_MSIC_BLOCK_ADC] = {
|
||||
.name = "msic_adc",
|
||||
.num_resources = ARRAY_SIZE(msic_adc_resources),
|
||||
.resources = msic_adc_resources,
|
||||
},
|
||||
[INTEL_MSIC_BLOCK_BATTERY] = {
|
||||
.name = "msic_battery",
|
||||
.num_resources = ARRAY_SIZE(msic_battery_resources),
|
||||
.resources = msic_battery_resources,
|
||||
},
|
||||
[INTEL_MSIC_BLOCK_GPIO] = {
|
||||
.name = "msic_gpio",
|
||||
.num_resources = ARRAY_SIZE(msic_gpio_resources),
|
||||
.resources = msic_gpio_resources,
|
||||
},
|
||||
[INTEL_MSIC_BLOCK_AUDIO] = {
|
||||
.name = "msic_audio",
|
||||
.num_resources = ARRAY_SIZE(msic_audio_resources),
|
||||
.resources = msic_audio_resources,
|
||||
},
|
||||
[INTEL_MSIC_BLOCK_HDMI] = {
|
||||
.name = "msic_hdmi",
|
||||
.num_resources = ARRAY_SIZE(msic_hdmi_resources),
|
||||
.resources = msic_hdmi_resources,
|
||||
},
|
||||
[INTEL_MSIC_BLOCK_THERMAL] = {
|
||||
.name = "msic_thermal",
|
||||
.num_resources = ARRAY_SIZE(msic_thermal_resources),
|
||||
.resources = msic_thermal_resources,
|
||||
},
|
||||
[INTEL_MSIC_BLOCK_POWER_BTN] = {
|
||||
.name = "msic_power_btn",
|
||||
.num_resources = ARRAY_SIZE(msic_power_btn_resources),
|
||||
.resources = msic_power_btn_resources,
|
||||
},
|
||||
[INTEL_MSIC_BLOCK_OCD] = {
|
||||
.name = "msic_ocd",
|
||||
.num_resources = ARRAY_SIZE(msic_ocd_resources),
|
||||
.resources = msic_ocd_resources,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Other MSIC related devices which are not directly available via SFI DEVS
|
||||
* table. These can be pseudo devices, regulators etc. which are needed for
|
||||
* different purposes.
|
||||
*
|
||||
* These devices appear only after the MSIC driver itself is initialized so
|
||||
* we can guarantee that the SCU IPC interface is ready.
|
||||
*/
|
||||
static struct mfd_cell msic_other_devs[] = {
|
||||
/* Audio codec in the MSIC */
|
||||
{
|
||||
.id = -1,
|
||||
.name = "sn95031",
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* intel_msic_reg_read - read a single MSIC register
|
||||
* @reg: register to read
|
||||
* @val: register value is placed here
|
||||
*
|
||||
* Read a single register from MSIC. Returns %0 on success and negative
|
||||
* errno in case of failure.
|
||||
*
|
||||
* Function may sleep.
|
||||
*/
|
||||
int intel_msic_reg_read(unsigned short reg, u8 *val)
|
||||
{
|
||||
return intel_scu_ipc_ioread8(reg, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_msic_reg_read);
|
||||
|
||||
/**
|
||||
* intel_msic_reg_write - write a single MSIC register
|
||||
* @reg: register to write
|
||||
* @val: value to write to that register
|
||||
*
|
||||
* Write a single MSIC register. Returns 0 on success and negative
|
||||
* errno in case of failure.
|
||||
*
|
||||
* Function may sleep.
|
||||
*/
|
||||
int intel_msic_reg_write(unsigned short reg, u8 val)
|
||||
{
|
||||
return intel_scu_ipc_iowrite8(reg, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_msic_reg_write);
|
||||
|
||||
/**
|
||||
* intel_msic_reg_update - update a single MSIC register
|
||||
* @reg: register to update
|
||||
* @val: value to write to the register
|
||||
* @mask: specifies which of the bits are updated (%0 = don't update,
|
||||
* %1 = update)
|
||||
*
|
||||
* Perform an update to a register @reg. @mask is used to specify which
|
||||
* bits are updated. Returns %0 in case of success and negative errno in
|
||||
* case of failure.
|
||||
*
|
||||
* Function may sleep.
|
||||
*/
|
||||
int intel_msic_reg_update(unsigned short reg, u8 val, u8 mask)
|
||||
{
|
||||
return intel_scu_ipc_update_register(reg, val, mask);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_msic_reg_update);
|
||||
|
||||
/**
|
||||
* intel_msic_bulk_read - read an array of registers
|
||||
* @reg: array of register addresses to read
|
||||
* @buf: array where the read values are placed
|
||||
* @count: number of registers to read
|
||||
*
|
||||
* Function reads @count registers from the MSIC using addresses passed in
|
||||
* @reg. Read values are placed in @buf. Reads are performed atomically
|
||||
* wrt. MSIC.
|
||||
*
|
||||
* Returns %0 in case of success and negative errno in case of failure.
|
||||
*
|
||||
* Function may sleep.
|
||||
*/
|
||||
int intel_msic_bulk_read(unsigned short *reg, u8 *buf, size_t count)
|
||||
{
|
||||
if (WARN_ON(count > SCU_IPC_RWBUF_LIMIT))
|
||||
return -EINVAL;
|
||||
|
||||
return intel_scu_ipc_readv(reg, buf, count);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_msic_bulk_read);
|
||||
|
||||
/**
|
||||
* intel_msic_bulk_write - write an array of values to the MSIC registers
|
||||
* @reg: array of registers to write
|
||||
* @buf: values to write to each register
|
||||
* @count: number of registers to write
|
||||
*
|
||||
* Function writes @count registers in @buf to MSIC. Writes are performed
|
||||
* atomically wrt MSIC. Returns %0 in case of success and negative errno in
|
||||
* case of failure.
|
||||
*
|
||||
* Function may sleep.
|
||||
*/
|
||||
int intel_msic_bulk_write(unsigned short *reg, u8 *buf, size_t count)
|
||||
{
|
||||
if (WARN_ON(count > SCU_IPC_RWBUF_LIMIT))
|
||||
return -EINVAL;
|
||||
|
||||
return intel_scu_ipc_writev(reg, buf, count);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_msic_bulk_write);
|
||||
|
||||
/**
|
||||
* intel_msic_irq_read - read a register from an MSIC interrupt tree
|
||||
* @msic: MSIC instance
|
||||
* @reg: interrupt register (between %INTEL_MSIC_IRQLVL1 and
|
||||
* %INTEL_MSIC_RESETIRQ2)
|
||||
* @val: value of the register is placed here
|
||||
*
|
||||
* This function can be used by an MSIC subdevice interrupt handler to read
|
||||
* a register value from the MSIC interrupt tree. In this way subdevice
|
||||
* drivers don't have to map in the interrupt tree themselves but can just
|
||||
* call this function instead.
|
||||
*
|
||||
* Function doesn't sleep and is callable from interrupt context.
|
||||
*
|
||||
* Returns %-EINVAL if @reg is outside of the allowed register region.
|
||||
*/
|
||||
int intel_msic_irq_read(struct intel_msic *msic, unsigned short reg, u8 *val)
|
||||
{
|
||||
if (WARN_ON(reg < INTEL_MSIC_IRQLVL1 || reg > INTEL_MSIC_RESETIRQ2))
|
||||
return -EINVAL;
|
||||
|
||||
*val = readb(msic->irq_base + (reg - INTEL_MSIC_IRQLVL1));
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_msic_irq_read);
|
||||
|
||||
static int __devinit intel_msic_init_devices(struct intel_msic *msic)
|
||||
{
|
||||
struct platform_device *pdev = msic->pdev;
|
||||
struct intel_msic_platform_data *pdata = pdev->dev.platform_data;
|
||||
int ret, i;
|
||||
|
||||
if (pdata->gpio) {
|
||||
struct mfd_cell *cell = &msic_devs[INTEL_MSIC_BLOCK_GPIO];
|
||||
|
||||
cell->platform_data = pdata->gpio;
|
||||
cell->pdata_size = sizeof(*pdata->gpio);
|
||||
}
|
||||
|
||||
if (pdata->ocd) {
|
||||
unsigned gpio = pdata->ocd->gpio;
|
||||
|
||||
ret = gpio_request_one(gpio, GPIOF_IN, "ocd_gpio");
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register OCD GPIO\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gpio_to_irq(gpio);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "no IRQ number for OCD GPIO\n");
|
||||
gpio_free(gpio);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Update the IRQ number for the OCD */
|
||||
pdata->irq[INTEL_MSIC_BLOCK_OCD] = ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(msic_devs); i++) {
|
||||
if (!pdata->irq[i])
|
||||
continue;
|
||||
|
||||
ret = mfd_add_devices(&pdev->dev, -1, &msic_devs[i], 1, NULL,
|
||||
pdata->irq[i]);
|
||||
if (ret)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = mfd_add_devices(&pdev->dev, 0, msic_other_devs,
|
||||
ARRAY_SIZE(msic_other_devs), NULL, 0);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
mfd_remove_devices(&pdev->dev);
|
||||
if (pdata->ocd)
|
||||
gpio_free(pdata->ocd->gpio);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __devexit intel_msic_remove_devices(struct intel_msic *msic)
|
||||
{
|
||||
struct platform_device *pdev = msic->pdev;
|
||||
struct intel_msic_platform_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
mfd_remove_devices(&pdev->dev);
|
||||
|
||||
if (pdata->ocd)
|
||||
gpio_free(pdata->ocd->gpio);
|
||||
}
|
||||
|
||||
static int __devinit intel_msic_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct intel_msic_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct intel_msic *msic;
|
||||
struct resource *res;
|
||||
u8 id0, id1;
|
||||
int ret;
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "no platform data passed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* First validate that we have an MSIC in place */
|
||||
ret = intel_scu_ipc_ioread8(INTEL_MSIC_ID0, &id0);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to identify the MSIC chip (ID0)\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
ret = intel_scu_ipc_ioread8(INTEL_MSIC_ID1, &id1);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to identify the MSIC chip (ID1)\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if (MSIC_VENDOR(id0) != MSIC_VENDOR(id1)) {
|
||||
dev_err(&pdev->dev, "invalid vendor ID: %x, %x\n", id0, id1);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
msic = kzalloc(sizeof(*msic), GFP_KERNEL);
|
||||
if (!msic)
|
||||
return -ENOMEM;
|
||||
|
||||
msic->vendor = MSIC_VENDOR(id0);
|
||||
msic->version = MSIC_VERSION(id0);
|
||||
msic->pdev = pdev;
|
||||
|
||||
/*
|
||||
* Map in the MSIC interrupt tree area in SRAM. This is exposed to
|
||||
* the clients via intel_msic_irq_read().
|
||||
*/
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "failed to get SRAM iomem resource\n");
|
||||
ret = -ENODEV;
|
||||
goto fail_free_msic;
|
||||
}
|
||||
|
||||
res = request_mem_region(res->start, resource_size(res), pdev->name);
|
||||
if (!res) {
|
||||
ret = -EBUSY;
|
||||
goto fail_free_msic;
|
||||
}
|
||||
|
||||
msic->irq_base = ioremap_nocache(res->start, resource_size(res));
|
||||
if (!msic->irq_base) {
|
||||
dev_err(&pdev->dev, "failed to map SRAM memory\n");
|
||||
ret = -ENOMEM;
|
||||
goto fail_release_region;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, msic);
|
||||
|
||||
ret = intel_msic_init_devices(msic);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to initialize MSIC devices\n");
|
||||
goto fail_unmap_mem;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "Intel MSIC version %c%d (vendor %#x)\n",
|
||||
MSIC_MAJOR(msic->version), MSIC_MINOR(msic->version),
|
||||
msic->vendor);
|
||||
|
||||
return 0;
|
||||
|
||||
fail_unmap_mem:
|
||||
iounmap(msic->irq_base);
|
||||
fail_release_region:
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
fail_free_msic:
|
||||
kfree(msic);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit intel_msic_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct intel_msic *msic = platform_get_drvdata(pdev);
|
||||
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
intel_msic_remove_devices(msic);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
iounmap(msic->irq_base);
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
kfree(msic);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver intel_msic_driver = {
|
||||
.probe = intel_msic_probe,
|
||||
.remove = __devexit_p(intel_msic_remove),
|
||||
.driver = {
|
||||
.name = "intel_msic",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init intel_msic_init(void)
|
||||
{
|
||||
return platform_driver_register(&intel_msic_driver);
|
||||
}
|
||||
module_init(intel_msic_init);
|
||||
|
||||
static void __exit intel_msic_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&intel_msic_driver);
|
||||
}
|
||||
module_exit(intel_msic_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Driver for Intel MSIC");
|
||||
MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
|
||||
MODULE_LICENSE("GPL");
|
@@ -328,7 +328,7 @@ static int __devexit jz4740_adc_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct platform_driver jz4740_adc_driver = {
|
||||
static struct platform_driver jz4740_adc_driver = {
|
||||
.probe = jz4740_adc_probe,
|
||||
.remove = __devexit_p(jz4740_adc_remove),
|
||||
.driver = {
|
||||
|
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/mfd/core.h>
|
||||
@@ -142,7 +143,6 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
|
||||
|
||||
max8997->irq_base = pdata->irq_base;
|
||||
max8997->ono = pdata->ono;
|
||||
max8997->wakeup = pdata->wakeup;
|
||||
|
||||
mutex_init(&max8997->iolock);
|
||||
|
||||
@@ -169,6 +169,9 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
|
||||
if (ret < 0)
|
||||
goto err_mfd;
|
||||
|
||||
/* MAX8997 has a power button input. */
|
||||
device_init_wakeup(max8997->dev, pdata->wakeup);
|
||||
|
||||
return ret;
|
||||
|
||||
err_mfd:
|
||||
@@ -398,7 +401,29 @@ static int max8997_restore(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max8997_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
|
||||
struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
irq_set_irq_wake(max8997->irq, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max8997_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
|
||||
struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
irq_set_irq_wake(max8997->irq, 0);
|
||||
return max8997_irq_resume(max8997);
|
||||
}
|
||||
|
||||
const struct dev_pm_ops max8997_pm = {
|
||||
.suspend = max8997_suspend,
|
||||
.resume = max8997_resume,
|
||||
.freeze = max8997_freeze,
|
||||
.restore = max8997_restore,
|
||||
};
|
||||
|
@@ -26,20 +26,10 @@ struct mc13xxx {
|
||||
|
||||
irq_handler_t irqhandler[MC13XXX_NUM_IRQ];
|
||||
void *irqdata[MC13XXX_NUM_IRQ];
|
||||
};
|
||||
|
||||
struct mc13783 {
|
||||
struct mc13xxx mc13xxx;
|
||||
|
||||
int adcflags;
|
||||
};
|
||||
|
||||
struct mc13xxx *mc13783_to_mc13xxx(struct mc13783 *mc13783)
|
||||
{
|
||||
return &mc13783->mc13xxx;
|
||||
}
|
||||
EXPORT_SYMBOL(mc13783_to_mc13xxx);
|
||||
|
||||
#define MC13XXX_IRQSTAT0 0
|
||||
#define MC13XXX_IRQSTAT0_ADCDONEI (1 << 0)
|
||||
#define MC13XXX_IRQSTAT0_ADCBISDONEI (1 << 1)
|
||||
@@ -136,14 +126,14 @@ EXPORT_SYMBOL(mc13783_to_mc13xxx);
|
||||
#define MC13XXX_REVISION_FAB (0x03 << 11)
|
||||
#define MC13XXX_REVISION_ICIDCODE (0x3f << 13)
|
||||
|
||||
#define MC13783_ADC1 44
|
||||
#define MC13783_ADC1_ADEN (1 << 0)
|
||||
#define MC13783_ADC1_RAND (1 << 1)
|
||||
#define MC13783_ADC1_ADSEL (1 << 3)
|
||||
#define MC13783_ADC1_ASC (1 << 20)
|
||||
#define MC13783_ADC1_ADTRIGIGN (1 << 21)
|
||||
#define MC13XXX_ADC1 44
|
||||
#define MC13XXX_ADC1_ADEN (1 << 0)
|
||||
#define MC13XXX_ADC1_RAND (1 << 1)
|
||||
#define MC13XXX_ADC1_ADSEL (1 << 3)
|
||||
#define MC13XXX_ADC1_ASC (1 << 20)
|
||||
#define MC13XXX_ADC1_ADTRIGIGN (1 << 21)
|
||||
|
||||
#define MC13783_ADC2 45
|
||||
#define MC13XXX_ADC2 45
|
||||
|
||||
#define MC13XXX_NUMREGS 0x3f
|
||||
|
||||
@@ -487,7 +477,7 @@ enum mc13xxx_id {
|
||||
MC13XXX_ID_INVALID,
|
||||
};
|
||||
|
||||
const char *mc13xxx_chipname[] = {
|
||||
static const char *mc13xxx_chipname[] = {
|
||||
[MC13XXX_ID_MC13783] = "mc13783",
|
||||
[MC13XXX_ID_MC13892] = "mc13892",
|
||||
};
|
||||
@@ -558,8 +548,6 @@ static const char *mc13xxx_get_chipname(struct mc13xxx *mc13xxx)
|
||||
return mc13xxx_chipname[devid->driver_data];
|
||||
}
|
||||
|
||||
#include <linux/mfd/mc13783.h>
|
||||
|
||||
int mc13xxx_get_flags(struct mc13xxx *mc13xxx)
|
||||
{
|
||||
struct mc13xxx_platform_data *pdata =
|
||||
@@ -569,15 +557,15 @@ int mc13xxx_get_flags(struct mc13xxx *mc13xxx)
|
||||
}
|
||||
EXPORT_SYMBOL(mc13xxx_get_flags);
|
||||
|
||||
#define MC13783_ADC1_CHAN0_SHIFT 5
|
||||
#define MC13783_ADC1_CHAN1_SHIFT 8
|
||||
#define MC13XXX_ADC1_CHAN0_SHIFT 5
|
||||
#define MC13XXX_ADC1_CHAN1_SHIFT 8
|
||||
|
||||
struct mc13xxx_adcdone_data {
|
||||
struct mc13xxx *mc13xxx;
|
||||
struct completion done;
|
||||
};
|
||||
|
||||
static irqreturn_t mc13783_handler_adcdone(int irq, void *data)
|
||||
static irqreturn_t mc13xxx_handler_adcdone(int irq, void *data)
|
||||
{
|
||||
struct mc13xxx_adcdone_data *adcdone_data = data;
|
||||
|
||||
@@ -588,12 +576,11 @@ static irqreturn_t mc13783_handler_adcdone(int irq, void *data)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#define MC13783_ADC_WORKING (1 << 0)
|
||||
#define MC13XXX_ADC_WORKING (1 << 0)
|
||||
|
||||
int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
|
||||
int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode,
|
||||
unsigned int channel, unsigned int *sample)
|
||||
{
|
||||
struct mc13xxx *mc13xxx = &mc13783->mc13xxx;
|
||||
u32 adc0, adc1, old_adc0;
|
||||
int i, ret;
|
||||
struct mc13xxx_adcdone_data adcdone_data = {
|
||||
@@ -605,51 +592,51 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
|
||||
|
||||
mc13xxx_lock(mc13xxx);
|
||||
|
||||
if (mc13783->adcflags & MC13783_ADC_WORKING) {
|
||||
if (mc13xxx->adcflags & MC13XXX_ADC_WORKING) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mc13783->adcflags |= MC13783_ADC_WORKING;
|
||||
mc13xxx->adcflags |= MC13XXX_ADC_WORKING;
|
||||
|
||||
mc13xxx_reg_read(mc13xxx, MC13783_ADC0, &old_adc0);
|
||||
mc13xxx_reg_read(mc13xxx, MC13XXX_ADC0, &old_adc0);
|
||||
|
||||
adc0 = MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2;
|
||||
adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN | MC13783_ADC1_ASC;
|
||||
adc0 = MC13XXX_ADC0_ADINC1 | MC13XXX_ADC0_ADINC2;
|
||||
adc1 = MC13XXX_ADC1_ADEN | MC13XXX_ADC1_ADTRIGIGN | MC13XXX_ADC1_ASC;
|
||||
|
||||
if (channel > 7)
|
||||
adc1 |= MC13783_ADC1_ADSEL;
|
||||
adc1 |= MC13XXX_ADC1_ADSEL;
|
||||
|
||||
switch (mode) {
|
||||
case MC13783_ADC_MODE_TS:
|
||||
adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_TSMOD0 |
|
||||
MC13783_ADC0_TSMOD1;
|
||||
adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
|
||||
case MC13XXX_ADC_MODE_TS:
|
||||
adc0 |= MC13XXX_ADC0_ADREFEN | MC13XXX_ADC0_TSMOD0 |
|
||||
MC13XXX_ADC0_TSMOD1;
|
||||
adc1 |= 4 << MC13XXX_ADC1_CHAN1_SHIFT;
|
||||
break;
|
||||
|
||||
case MC13783_ADC_MODE_SINGLE_CHAN:
|
||||
adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
|
||||
adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT;
|
||||
adc1 |= MC13783_ADC1_RAND;
|
||||
case MC13XXX_ADC_MODE_SINGLE_CHAN:
|
||||
adc0 |= old_adc0 & MC13XXX_ADC0_TSMOD_MASK;
|
||||
adc1 |= (channel & 0x7) << MC13XXX_ADC1_CHAN0_SHIFT;
|
||||
adc1 |= MC13XXX_ADC1_RAND;
|
||||
break;
|
||||
|
||||
case MC13783_ADC_MODE_MULT_CHAN:
|
||||
adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK;
|
||||
adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
|
||||
case MC13XXX_ADC_MODE_MULT_CHAN:
|
||||
adc0 |= old_adc0 & MC13XXX_ADC0_TSMOD_MASK;
|
||||
adc1 |= 4 << MC13XXX_ADC1_CHAN1_SHIFT;
|
||||
break;
|
||||
|
||||
default:
|
||||
mc13783_unlock(mc13783);
|
||||
mc13xxx_unlock(mc13xxx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(&mc13783->mc13xxx.spidev->dev, "%s: request irq\n", __func__);
|
||||
mc13xxx_irq_request(mc13xxx, MC13783_IRQ_ADCDONE,
|
||||
mc13783_handler_adcdone, __func__, &adcdone_data);
|
||||
mc13xxx_irq_ack(mc13xxx, MC13783_IRQ_ADCDONE);
|
||||
dev_dbg(&mc13xxx->spidev->dev, "%s: request irq\n", __func__);
|
||||
mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_ADCDONE,
|
||||
mc13xxx_handler_adcdone, __func__, &adcdone_data);
|
||||
mc13xxx_irq_ack(mc13xxx, MC13XXX_IRQ_ADCDONE);
|
||||
|
||||
mc13xxx_reg_write(mc13xxx, MC13783_ADC0, adc0);
|
||||
mc13xxx_reg_write(mc13xxx, MC13783_ADC1, adc1);
|
||||
mc13xxx_reg_write(mc13xxx, MC13XXX_ADC0, adc0);
|
||||
mc13xxx_reg_write(mc13xxx, MC13XXX_ADC1, adc1);
|
||||
|
||||
mc13xxx_unlock(mc13xxx);
|
||||
|
||||
@@ -660,27 +647,27 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
|
||||
|
||||
mc13xxx_lock(mc13xxx);
|
||||
|
||||
mc13xxx_irq_free(mc13xxx, MC13783_IRQ_ADCDONE, &adcdone_data);
|
||||
mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_ADCDONE, &adcdone_data);
|
||||
|
||||
if (ret > 0)
|
||||
for (i = 0; i < 4; ++i) {
|
||||
ret = mc13xxx_reg_read(mc13xxx,
|
||||
MC13783_ADC2, &sample[i]);
|
||||
MC13XXX_ADC2, &sample[i]);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
if (mode == MC13783_ADC_MODE_TS)
|
||||
if (mode == MC13XXX_ADC_MODE_TS)
|
||||
/* restore TSMOD */
|
||||
mc13xxx_reg_write(mc13xxx, MC13783_ADC0, old_adc0);
|
||||
mc13xxx_reg_write(mc13xxx, MC13XXX_ADC0, old_adc0);
|
||||
|
||||
mc13783->adcflags &= ~MC13783_ADC_WORKING;
|
||||
mc13xxx->adcflags &= ~MC13XXX_ADC_WORKING;
|
||||
out:
|
||||
mc13xxx_unlock(mc13xxx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mc13783_adc_do_conversion);
|
||||
EXPORT_SYMBOL_GPL(mc13xxx_adc_do_conversion);
|
||||
|
||||
static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx,
|
||||
const char *format, void *pdata, size_t pdata_size)
|
||||
@@ -716,6 +703,11 @@ static int mc13xxx_probe(struct spi_device *spi)
|
||||
enum mc13xxx_id id;
|
||||
int ret;
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&spi->dev, "invalid platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
|
||||
if (!mc13xxx)
|
||||
return -ENOMEM;
|
||||
@@ -763,10 +755,8 @@ err_revision:
|
||||
if (pdata->flags & MC13XXX_USE_CODEC)
|
||||
mc13xxx_add_subdevice(mc13xxx, "%s-codec");
|
||||
|
||||
if (pdata->flags & MC13XXX_USE_REGULATOR) {
|
||||
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator",
|
||||
&pdata->regulators, sizeof(pdata->regulators));
|
||||
}
|
||||
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator",
|
||||
&pdata->regulators, sizeof(pdata->regulators));
|
||||
|
||||
if (pdata->flags & MC13XXX_USE_RTC)
|
||||
mc13xxx_add_subdevice(mc13xxx, "%s-rtc");
|
||||
@@ -774,10 +764,14 @@ err_revision:
|
||||
if (pdata->flags & MC13XXX_USE_TOUCHSCREEN)
|
||||
mc13xxx_add_subdevice(mc13xxx, "%s-ts");
|
||||
|
||||
if (pdata->flags & MC13XXX_USE_LED)
|
||||
if (pdata->leds)
|
||||
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-led",
|
||||
pdata->leds, sizeof(*pdata->leds));
|
||||
|
||||
if (pdata->buttons)
|
||||
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-pwrbutton",
|
||||
pdata->buttons, sizeof(*pdata->buttons));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -1226,7 +1226,7 @@ static int menelaus_probe(struct i2c_client *client,
|
||||
menelaus_write_reg(MENELAUS_MCT_CTRL1, 0x73);
|
||||
|
||||
if (client->irq > 0) {
|
||||
err = request_irq(client->irq, menelaus_irq, IRQF_DISABLED,
|
||||
err = request_irq(client->irq, menelaus_irq, 0,
|
||||
DRIVER_NAME, menelaus);
|
||||
if (err) {
|
||||
dev_dbg(&client->dev, "can't get IRQ %d, err %d\n",
|
||||
|
@@ -23,45 +23,22 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <linux/mfd/pcf50633/core.h>
|
||||
|
||||
static int __pcf50633_read(struct pcf50633 *pcf, u8 reg, int num, u8 *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(pcf->i2c_client, reg,
|
||||
num, data);
|
||||
if (ret < 0)
|
||||
dev_err(pcf->dev, "Error reading %d regs at %d\n", num, reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __pcf50633_write(struct pcf50633 *pcf, u8 reg, int num, u8 *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_i2c_block_data(pcf->i2c_client, reg,
|
||||
num, data);
|
||||
if (ret < 0)
|
||||
dev_err(pcf->dev, "Error writing %d regs at %d\n", num, reg);
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/* Read a block of up to 32 regs */
|
||||
int pcf50633_read_block(struct pcf50633 *pcf, u8 reg,
|
||||
int nr_regs, u8 *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&pcf->lock);
|
||||
ret = __pcf50633_read(pcf, reg, nr_regs, data);
|
||||
mutex_unlock(&pcf->lock);
|
||||
ret = regmap_raw_read(pcf->regmap, reg, data, nr_regs);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
return nr_regs;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcf50633_read_block);
|
||||
|
||||
@@ -71,21 +48,22 @@ int pcf50633_write_block(struct pcf50633 *pcf , u8 reg,
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&pcf->lock);
|
||||
ret = __pcf50633_write(pcf, reg, nr_regs, data);
|
||||
mutex_unlock(&pcf->lock);
|
||||
ret = regmap_raw_write(pcf->regmap, reg, data, nr_regs);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
return nr_regs;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcf50633_write_block);
|
||||
|
||||
u8 pcf50633_reg_read(struct pcf50633 *pcf, u8 reg)
|
||||
{
|
||||
u8 val;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&pcf->lock);
|
||||
__pcf50633_read(pcf, reg, 1, &val);
|
||||
mutex_unlock(&pcf->lock);
|
||||
ret = regmap_read(pcf->regmap, reg, &val);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
return val;
|
||||
}
|
||||
@@ -93,56 +71,19 @@ EXPORT_SYMBOL_GPL(pcf50633_reg_read);
|
||||
|
||||
int pcf50633_reg_write(struct pcf50633 *pcf, u8 reg, u8 val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&pcf->lock);
|
||||
ret = __pcf50633_write(pcf, reg, 1, &val);
|
||||
mutex_unlock(&pcf->lock);
|
||||
|
||||
return ret;
|
||||
return regmap_write(pcf->regmap, reg, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcf50633_reg_write);
|
||||
|
||||
int pcf50633_reg_set_bit_mask(struct pcf50633 *pcf, u8 reg, u8 mask, u8 val)
|
||||
{
|
||||
int ret;
|
||||
u8 tmp;
|
||||
|
||||
val &= mask;
|
||||
|
||||
mutex_lock(&pcf->lock);
|
||||
ret = __pcf50633_read(pcf, reg, 1, &tmp);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
tmp &= ~mask;
|
||||
tmp |= val;
|
||||
ret = __pcf50633_write(pcf, reg, 1, &tmp);
|
||||
|
||||
out:
|
||||
mutex_unlock(&pcf->lock);
|
||||
|
||||
return ret;
|
||||
return regmap_update_bits(pcf->regmap, reg, mask, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcf50633_reg_set_bit_mask);
|
||||
|
||||
int pcf50633_reg_clear_bits(struct pcf50633 *pcf, u8 reg, u8 val)
|
||||
{
|
||||
int ret;
|
||||
u8 tmp;
|
||||
|
||||
mutex_lock(&pcf->lock);
|
||||
ret = __pcf50633_read(pcf, reg, 1, &tmp);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
tmp &= ~val;
|
||||
ret = __pcf50633_write(pcf, reg, 1, &tmp);
|
||||
|
||||
out:
|
||||
mutex_unlock(&pcf->lock);
|
||||
|
||||
return ret;
|
||||
return regmap_update_bits(pcf->regmap, reg, val, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pcf50633_reg_clear_bits);
|
||||
|
||||
@@ -251,6 +192,11 @@ static int pcf50633_resume(struct device *dev)
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(pcf50633_pm, pcf50633_suspend, pcf50633_resume);
|
||||
|
||||
static struct regmap_config pcf50633_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static int __devinit pcf50633_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *ids)
|
||||
{
|
||||
@@ -272,16 +218,23 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
|
||||
|
||||
mutex_init(&pcf->lock);
|
||||
|
||||
pcf->regmap = regmap_init_i2c(client, &pcf50633_regmap_config);
|
||||
if (IS_ERR(pcf->regmap)) {
|
||||
ret = PTR_ERR(pcf->regmap);
|
||||
dev_err(pcf->dev, "Failed to allocate register map: %d\n",
|
||||
ret);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, pcf);
|
||||
pcf->dev = &client->dev;
|
||||
pcf->i2c_client = client;
|
||||
|
||||
version = pcf50633_reg_read(pcf, 0);
|
||||
variant = pcf50633_reg_read(pcf, 1);
|
||||
if (version < 0 || variant < 0) {
|
||||
dev_err(pcf->dev, "Unable to probe pcf50633\n");
|
||||
ret = -ENODEV;
|
||||
goto err_free;
|
||||
goto err_regmap;
|
||||
}
|
||||
|
||||
dev_info(pcf->dev, "Probed device version %d variant %d\n",
|
||||
@@ -328,6 +281,8 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
|
||||
|
||||
return 0;
|
||||
|
||||
err_regmap:
|
||||
regmap_exit(pcf->regmap);
|
||||
err_free:
|
||||
kfree(pcf);
|
||||
|
||||
@@ -351,6 +306,7 @@ static int __devexit pcf50633_remove(struct i2c_client *client)
|
||||
for (i = 0; i < PCF50633_NUM_REGULATORS; i++)
|
||||
platform_device_unregister(pcf->regulator_pdev[i]);
|
||||
|
||||
regmap_exit(pcf->regmap);
|
||||
kfree(pcf);
|
||||
|
||||
return 0;
|
||||
|
@@ -357,6 +357,7 @@ static int __devexit tc3589x_remove(struct i2c_client *client)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int tc3589x_suspend(struct device *dev)
|
||||
{
|
||||
struct tc3589x *tc3589x = dev_get_drvdata(dev);
|
||||
@@ -387,6 +388,7 @@ static int tc3589x_resume(struct device *dev)
|
||||
|
||||
static const SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend,
|
||||
tc3589x_resume);
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id tc3589x_id[] = {
|
||||
{ "tc3589x", 24 },
|
||||
|
@@ -697,7 +697,7 @@ static int __devinit timb_probe(struct pci_dev *dev,
|
||||
dev_err(&dev->dev, "The driver supports an older "
|
||||
"version of the FPGA, please update the driver to "
|
||||
"support %d.%d\n", priv->fw.major, priv->fw.minor);
|
||||
goto err_ioremap;
|
||||
goto err_config;
|
||||
}
|
||||
if (priv->fw.major < TIMB_SUPPORTED_MAJOR ||
|
||||
priv->fw.minor < TIMB_REQUIRED_MINOR) {
|
||||
@@ -705,13 +705,13 @@ static int __devinit timb_probe(struct pci_dev *dev,
|
||||
"please upgrade the FPGA to at least: %d.%d\n",
|
||||
priv->fw.major, priv->fw.minor,
|
||||
TIMB_SUPPORTED_MAJOR, TIMB_REQUIRED_MINOR);
|
||||
goto err_ioremap;
|
||||
goto err_config;
|
||||
}
|
||||
|
||||
msix_entries = kzalloc(TIMBERDALE_NR_IRQS * sizeof(*msix_entries),
|
||||
GFP_KERNEL);
|
||||
if (!msix_entries)
|
||||
goto err_ioremap;
|
||||
goto err_config;
|
||||
|
||||
for (i = 0; i < TIMBERDALE_NR_IRQS; i++)
|
||||
msix_entries[i].entry = i;
|
||||
@@ -825,6 +825,8 @@ err_mfd:
|
||||
err_create_file:
|
||||
pci_disable_msix(dev);
|
||||
err_msix:
|
||||
kfree(msix_entries);
|
||||
err_config:
|
||||
iounmap(priv->ctl_membase);
|
||||
err_ioremap:
|
||||
release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE);
|
||||
@@ -833,7 +835,6 @@ err_request:
|
||||
err_start:
|
||||
pci_disable_device(dev);
|
||||
err_enable:
|
||||
kfree(msix_entries);
|
||||
kfree(priv);
|
||||
pci_set_drvdata(dev, NULL);
|
||||
return -ENODEV;
|
||||
|
@@ -131,9 +131,6 @@ int tps65912_device_init(struct tps65912 *tps65912)
|
||||
if (init_data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
init_data->irq = pmic_plat_data->irq;
|
||||
init_data->irq_base = pmic_plat_data->irq;
|
||||
|
||||
mutex_init(&tps65912->io_mutex);
|
||||
dev_set_drvdata(tps65912->dev, tps65912);
|
||||
|
||||
@@ -153,10 +150,13 @@ int tps65912_device_init(struct tps65912 *tps65912)
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
init_data->irq = pmic_plat_data->irq;
|
||||
init_data->irq_base = pmic_plat_data->irq;
|
||||
ret = tps65912_irq_init(tps65912, init_data->irq, init_data);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
kfree(init_data);
|
||||
return ret;
|
||||
|
||||
err:
|
||||
|
@@ -109,7 +109,7 @@
|
||||
#define twl_has_watchdog() false
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE) ||\
|
||||
#if defined(CONFIG_MFD_TWL4030_AUDIO) || defined(CONFIG_MFD_TWL4030_AUDIO_MODULE) ||\
|
||||
defined(CONFIG_TWL6040_CORE) || defined(CONFIG_TWL6040_CORE_MODULE)
|
||||
#define twl_has_codec() true
|
||||
#else
|
||||
|
@@ -30,7 +30,6 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/i2c/twl.h>
|
||||
@@ -278,59 +277,6 @@ static const struct sih sih_modules_twl5031[8] = {
|
||||
|
||||
static unsigned twl4030_irq_base;
|
||||
|
||||
static struct completion irq_event;
|
||||
|
||||
/*
|
||||
* This thread processes interrupts reported by the Primary Interrupt Handler.
|
||||
*/
|
||||
static int twl4030_irq_thread(void *data)
|
||||
{
|
||||
long irq = (long)data;
|
||||
static unsigned i2c_errors;
|
||||
static const unsigned max_i2c_errors = 100;
|
||||
|
||||
|
||||
current->flags |= PF_NOFREEZE;
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
int ret;
|
||||
int module_irq;
|
||||
u8 pih_isr;
|
||||
|
||||
/* Wait for IRQ, then read PIH irq status (also blocking) */
|
||||
wait_for_completion_interruptible(&irq_event);
|
||||
|
||||
ret = twl_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr,
|
||||
REG_PIH_ISR_P1);
|
||||
if (ret) {
|
||||
pr_warning("twl4030: I2C error %d reading PIH ISR\n",
|
||||
ret);
|
||||
if (++i2c_errors >= max_i2c_errors) {
|
||||
printk(KERN_ERR "Maximum I2C error count"
|
||||
" exceeded. Terminating %s.\n",
|
||||
__func__);
|
||||
break;
|
||||
}
|
||||
complete(&irq_event);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* these handlers deal with the relevant SIH irq status */
|
||||
local_irq_disable();
|
||||
for (module_irq = twl4030_irq_base;
|
||||
pih_isr;
|
||||
pih_isr >>= 1, module_irq++) {
|
||||
if (pih_isr & 0x1)
|
||||
generic_handle_irq(module_irq);
|
||||
}
|
||||
local_irq_enable();
|
||||
|
||||
enable_irq(irq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* handle_twl4030_pih() is the desc->handle method for the twl4030 interrupt.
|
||||
* This is a chained interrupt, so there is no desc->action method for it.
|
||||
@@ -342,9 +288,25 @@ static int twl4030_irq_thread(void *data)
|
||||
*/
|
||||
static irqreturn_t handle_twl4030_pih(int irq, void *devid)
|
||||
{
|
||||
/* Acknowledge, clear *AND* mask the interrupt... */
|
||||
disable_irq_nosync(irq);
|
||||
complete(devid);
|
||||
int module_irq;
|
||||
irqreturn_t ret;
|
||||
u8 pih_isr;
|
||||
|
||||
ret = twl_i2c_read_u8(TWL4030_MODULE_PIH, &pih_isr,
|
||||
REG_PIH_ISR_P1);
|
||||
if (ret) {
|
||||
pr_warning("twl4030: I2C error %d reading PIH ISR\n", ret);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/* these handlers deal with the relevant SIH irq status */
|
||||
for (module_irq = twl4030_irq_base;
|
||||
pih_isr;
|
||||
pih_isr >>= 1, module_irq++) {
|
||||
if (pih_isr & 0x1)
|
||||
handle_nested_irq(module_irq);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
/*----------------------------------------------------------------------*/
|
||||
@@ -460,114 +422,18 @@ static inline void activate_irq(int irq)
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static DEFINE_SPINLOCK(sih_agent_lock);
|
||||
|
||||
static struct workqueue_struct *wq;
|
||||
|
||||
struct sih_agent {
|
||||
int irq_base;
|
||||
const struct sih *sih;
|
||||
|
||||
u32 imr;
|
||||
bool imr_change_pending;
|
||||
struct work_struct mask_work;
|
||||
|
||||
u32 edge_change;
|
||||
struct work_struct edge_work;
|
||||
|
||||
struct mutex irq_lock;
|
||||
};
|
||||
|
||||
static void twl4030_sih_do_mask(struct work_struct *work)
|
||||
{
|
||||
struct sih_agent *agent;
|
||||
const struct sih *sih;
|
||||
union {
|
||||
u8 bytes[4];
|
||||
u32 word;
|
||||
} imr;
|
||||
int status;
|
||||
|
||||
agent = container_of(work, struct sih_agent, mask_work);
|
||||
|
||||
/* see what work we have */
|
||||
spin_lock_irq(&sih_agent_lock);
|
||||
if (agent->imr_change_pending) {
|
||||
sih = agent->sih;
|
||||
/* byte[0] gets overwritten as we write ... */
|
||||
imr.word = cpu_to_le32(agent->imr << 8);
|
||||
agent->imr_change_pending = false;
|
||||
} else
|
||||
sih = NULL;
|
||||
spin_unlock_irq(&sih_agent_lock);
|
||||
if (!sih)
|
||||
return;
|
||||
|
||||
/* write the whole mask ... simpler than subsetting it */
|
||||
status = twl_i2c_write(sih->module, imr.bytes,
|
||||
sih->mask[irq_line].imr_offset, sih->bytes_ixr);
|
||||
if (status)
|
||||
pr_err("twl4030: %s, %s --> %d\n", __func__,
|
||||
"write", status);
|
||||
}
|
||||
|
||||
static void twl4030_sih_do_edge(struct work_struct *work)
|
||||
{
|
||||
struct sih_agent *agent;
|
||||
const struct sih *sih;
|
||||
u8 bytes[6];
|
||||
u32 edge_change;
|
||||
int status;
|
||||
|
||||
agent = container_of(work, struct sih_agent, edge_work);
|
||||
|
||||
/* see what work we have */
|
||||
spin_lock_irq(&sih_agent_lock);
|
||||
edge_change = agent->edge_change;
|
||||
agent->edge_change = 0;
|
||||
sih = edge_change ? agent->sih : NULL;
|
||||
spin_unlock_irq(&sih_agent_lock);
|
||||
if (!sih)
|
||||
return;
|
||||
|
||||
/* Read, reserving first byte for write scratch. Yes, this
|
||||
* could be cached for some speedup ... but be careful about
|
||||
* any processor on the other IRQ line, EDR registers are
|
||||
* shared.
|
||||
*/
|
||||
status = twl_i2c_read(sih->module, bytes + 1,
|
||||
sih->edr_offset, sih->bytes_edr);
|
||||
if (status) {
|
||||
pr_err("twl4030: %s, %s --> %d\n", __func__,
|
||||
"read", status);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Modify only the bits we know must change */
|
||||
while (edge_change) {
|
||||
int i = fls(edge_change) - 1;
|
||||
struct irq_data *idata = irq_get_irq_data(i + agent->irq_base);
|
||||
int byte = 1 + (i >> 2);
|
||||
int off = (i & 0x3) * 2;
|
||||
unsigned int type;
|
||||
|
||||
bytes[byte] &= ~(0x03 << off);
|
||||
|
||||
type = irqd_get_trigger_type(idata);
|
||||
if (type & IRQ_TYPE_EDGE_RISING)
|
||||
bytes[byte] |= BIT(off + 1);
|
||||
if (type & IRQ_TYPE_EDGE_FALLING)
|
||||
bytes[byte] |= BIT(off + 0);
|
||||
|
||||
edge_change &= ~BIT(i);
|
||||
}
|
||||
|
||||
/* Write */
|
||||
status = twl_i2c_write(sih->module, bytes,
|
||||
sih->edr_offset, sih->bytes_edr);
|
||||
if (status)
|
||||
pr_err("twl4030: %s, %s --> %d\n", __func__,
|
||||
"write", status);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
@@ -579,50 +445,125 @@ static void twl4030_sih_do_edge(struct work_struct *work)
|
||||
|
||||
static void twl4030_sih_mask(struct irq_data *data)
|
||||
{
|
||||
struct sih_agent *sih = irq_data_get_irq_chip_data(data);
|
||||
unsigned long flags;
|
||||
struct sih_agent *agent = irq_data_get_irq_chip_data(data);
|
||||
|
||||
spin_lock_irqsave(&sih_agent_lock, flags);
|
||||
sih->imr |= BIT(data->irq - sih->irq_base);
|
||||
sih->imr_change_pending = true;
|
||||
queue_work(wq, &sih->mask_work);
|
||||
spin_unlock_irqrestore(&sih_agent_lock, flags);
|
||||
agent->imr |= BIT(data->irq - agent->irq_base);
|
||||
agent->imr_change_pending = true;
|
||||
}
|
||||
|
||||
static void twl4030_sih_unmask(struct irq_data *data)
|
||||
{
|
||||
struct sih_agent *sih = irq_data_get_irq_chip_data(data);
|
||||
unsigned long flags;
|
||||
struct sih_agent *agent = irq_data_get_irq_chip_data(data);
|
||||
|
||||
spin_lock_irqsave(&sih_agent_lock, flags);
|
||||
sih->imr &= ~BIT(data->irq - sih->irq_base);
|
||||
sih->imr_change_pending = true;
|
||||
queue_work(wq, &sih->mask_work);
|
||||
spin_unlock_irqrestore(&sih_agent_lock, flags);
|
||||
agent->imr &= ~BIT(data->irq - agent->irq_base);
|
||||
agent->imr_change_pending = true;
|
||||
}
|
||||
|
||||
static int twl4030_sih_set_type(struct irq_data *data, unsigned trigger)
|
||||
{
|
||||
struct sih_agent *sih = irq_data_get_irq_chip_data(data);
|
||||
unsigned long flags;
|
||||
struct sih_agent *agent = irq_data_get_irq_chip_data(data);
|
||||
|
||||
if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&sih_agent_lock, flags);
|
||||
if (irqd_get_trigger_type(data) != trigger) {
|
||||
sih->edge_change |= BIT(data->irq - sih->irq_base);
|
||||
queue_work(wq, &sih->edge_work);
|
||||
}
|
||||
spin_unlock_irqrestore(&sih_agent_lock, flags);
|
||||
if (irqd_get_trigger_type(data) != trigger)
|
||||
agent->edge_change |= BIT(data->irq - agent->irq_base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void twl4030_sih_bus_lock(struct irq_data *data)
|
||||
{
|
||||
struct sih_agent *agent = irq_data_get_irq_chip_data(data);
|
||||
|
||||
mutex_lock(&agent->irq_lock);
|
||||
}
|
||||
|
||||
static void twl4030_sih_bus_sync_unlock(struct irq_data *data)
|
||||
{
|
||||
struct sih_agent *agent = irq_data_get_irq_chip_data(data);
|
||||
const struct sih *sih = agent->sih;
|
||||
int status;
|
||||
|
||||
if (agent->imr_change_pending) {
|
||||
union {
|
||||
u32 word;
|
||||
u8 bytes[4];
|
||||
} imr;
|
||||
|
||||
/* byte[0] gets overwriten as we write ... */
|
||||
imr.word = cpu_to_le32(agent->imr << 8);
|
||||
agent->imr_change_pending = false;
|
||||
|
||||
/* write the whole mask ... simpler than subsetting it */
|
||||
status = twl_i2c_write(sih->module, imr.bytes,
|
||||
sih->mask[irq_line].imr_offset,
|
||||
sih->bytes_ixr);
|
||||
if (status)
|
||||
pr_err("twl4030: %s, %s --> %d\n", __func__,
|
||||
"write", status);
|
||||
}
|
||||
|
||||
if (agent->edge_change) {
|
||||
u32 edge_change;
|
||||
u8 bytes[6];
|
||||
|
||||
edge_change = agent->edge_change;
|
||||
agent->edge_change = 0;
|
||||
|
||||
/*
|
||||
* Read, reserving first byte for write scratch. Yes, this
|
||||
* could be cached for some speedup ... but be careful about
|
||||
* any processor on the other IRQ line, EDR registers are
|
||||
* shared.
|
||||
*/
|
||||
status = twl_i2c_read(sih->module, bytes + 1,
|
||||
sih->edr_offset, sih->bytes_edr);
|
||||
if (status) {
|
||||
pr_err("twl4030: %s, %s --> %d\n", __func__,
|
||||
"read", status);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Modify only the bits we know must change */
|
||||
while (edge_change) {
|
||||
int i = fls(edge_change) - 1;
|
||||
struct irq_data *idata;
|
||||
int byte = 1 + (i >> 2);
|
||||
int off = (i & 0x3) * 2;
|
||||
unsigned int type;
|
||||
|
||||
idata = irq_get_irq_data(i + agent->irq_base);
|
||||
|
||||
bytes[byte] &= ~(0x03 << off);
|
||||
|
||||
type = irqd_get_trigger_type(idata);
|
||||
if (type & IRQ_TYPE_EDGE_RISING)
|
||||
bytes[byte] |= BIT(off + 1);
|
||||
if (type & IRQ_TYPE_EDGE_FALLING)
|
||||
bytes[byte] |= BIT(off + 0);
|
||||
|
||||
edge_change &= ~BIT(i);
|
||||
}
|
||||
|
||||
/* Write */
|
||||
status = twl_i2c_write(sih->module, bytes,
|
||||
sih->edr_offset, sih->bytes_edr);
|
||||
if (status)
|
||||
pr_err("twl4030: %s, %s --> %d\n", __func__,
|
||||
"write", status);
|
||||
}
|
||||
|
||||
mutex_unlock(&agent->irq_lock);
|
||||
}
|
||||
|
||||
static struct irq_chip twl4030_sih_irq_chip = {
|
||||
.name = "twl4030",
|
||||
.irq_mask = twl4030_sih_mask,
|
||||
.irq_mask = twl4030_sih_mask,
|
||||
.irq_unmask = twl4030_sih_unmask,
|
||||
.irq_set_type = twl4030_sih_set_type,
|
||||
.irq_bus_lock = twl4030_sih_bus_lock,
|
||||
.irq_bus_sync_unlock = twl4030_sih_bus_sync_unlock,
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
@@ -655,9 +596,7 @@ static void handle_twl4030_sih(unsigned irq, struct irq_desc *desc)
|
||||
int isr;
|
||||
|
||||
/* reading ISR acks the IRQs, using clear-on-read mode */
|
||||
local_irq_enable();
|
||||
isr = sih_read_isr(sih);
|
||||
local_irq_disable();
|
||||
|
||||
if (isr < 0) {
|
||||
pr_err("twl4030: %s SIH, read ISR error %d\n",
|
||||
@@ -672,7 +611,7 @@ static void handle_twl4030_sih(unsigned irq, struct irq_desc *desc)
|
||||
isr &= ~BIT(irq);
|
||||
|
||||
if (irq < sih->bits)
|
||||
generic_handle_irq(agent->irq_base + irq);
|
||||
handle_nested_irq(agent->irq_base + irq);
|
||||
else
|
||||
pr_err("twl4030: %s SIH, invalid ISR bit %d\n",
|
||||
sih->name, irq);
|
||||
@@ -718,15 +657,14 @@ int twl4030_sih_setup(int module)
|
||||
agent->irq_base = irq_base;
|
||||
agent->sih = sih;
|
||||
agent->imr = ~0;
|
||||
INIT_WORK(&agent->mask_work, twl4030_sih_do_mask);
|
||||
INIT_WORK(&agent->edge_work, twl4030_sih_do_edge);
|
||||
mutex_init(&agent->irq_lock);
|
||||
|
||||
for (i = 0; i < sih->bits; i++) {
|
||||
irq = irq_base + i;
|
||||
|
||||
irq_set_chip_data(irq, agent);
|
||||
irq_set_chip_and_handler(irq, &twl4030_sih_irq_chip,
|
||||
handle_edge_irq);
|
||||
irq_set_chip_data(irq, agent);
|
||||
activate_irq(irq);
|
||||
}
|
||||
|
||||
@@ -758,7 +696,6 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
|
||||
|
||||
int status;
|
||||
int i;
|
||||
struct task_struct *task;
|
||||
|
||||
/*
|
||||
* Mask and clear all TWL4030 interrupts since initially we do
|
||||
@@ -768,12 +705,6 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
wq = create_singlethread_workqueue("twl4030-irqchip");
|
||||
if (!wq) {
|
||||
pr_err("twl4030: workqueue FAIL\n");
|
||||
return -ESRCH;
|
||||
}
|
||||
|
||||
twl4030_irq_base = irq_base;
|
||||
|
||||
/* install an irq handler for each of the SIH modules;
|
||||
@@ -787,6 +718,7 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
|
||||
for (i = irq_base; i < irq_end; i++) {
|
||||
irq_set_chip_and_handler(i, &twl4030_irq_chip,
|
||||
handle_simple_irq);
|
||||
irq_set_nested_thread(i, 1);
|
||||
activate_irq(i);
|
||||
}
|
||||
twl4030_irq_next = i;
|
||||
@@ -801,34 +733,22 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
|
||||
}
|
||||
|
||||
/* install an irq handler to demultiplex the TWL4030 interrupt */
|
||||
|
||||
|
||||
init_completion(&irq_event);
|
||||
|
||||
status = request_irq(irq_num, handle_twl4030_pih, IRQF_DISABLED,
|
||||
"TWL4030-PIH", &irq_event);
|
||||
status = request_threaded_irq(irq_num, NULL, handle_twl4030_pih, 0,
|
||||
"TWL4030-PIH", NULL);
|
||||
if (status < 0) {
|
||||
pr_err("twl4030: could not claim irq%d: %d\n", irq_num, status);
|
||||
goto fail_rqirq;
|
||||
}
|
||||
|
||||
task = kthread_run(twl4030_irq_thread, (void *)(long)irq_num,
|
||||
"twl4030-irq");
|
||||
if (IS_ERR(task)) {
|
||||
pr_err("twl4030: could not create irq %d thread!\n", irq_num);
|
||||
status = PTR_ERR(task);
|
||||
goto fail_kthread;
|
||||
}
|
||||
return status;
|
||||
fail_kthread:
|
||||
free_irq(irq_num, &irq_event);
|
||||
fail_rqirq:
|
||||
/* clean up twl4030_sih_setup */
|
||||
fail:
|
||||
for (i = irq_base; i < irq_end; i++)
|
||||
for (i = irq_base; i < irq_end; i++) {
|
||||
irq_set_nested_thread(i, 0);
|
||||
irq_set_chip_and_handler(i, NULL, NULL);
|
||||
destroy_workqueue(wq);
|
||||
wq = NULL;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@@ -740,6 +740,28 @@ static int __devinit twl4030_madc_probe(struct platform_device *pdev)
|
||||
TWL4030_BCI_BCICTL1);
|
||||
goto err_i2c;
|
||||
}
|
||||
|
||||
/* Check that MADC clock is on */
|
||||
ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, ®val, TWL4030_REG_GPBR1);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to read reg GPBR1 0x%X\n",
|
||||
TWL4030_REG_GPBR1);
|
||||
goto err_i2c;
|
||||
}
|
||||
|
||||
/* If MADC clk is not on, turn it on */
|
||||
if (!(regval & TWL4030_GPBR1_MADC_HFCLK_EN)) {
|
||||
dev_info(&pdev->dev, "clk disabled, enabling\n");
|
||||
regval |= TWL4030_GPBR1_MADC_HFCLK_EN;
|
||||
ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, regval,
|
||||
TWL4030_REG_GPBR1);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to write reg GPBR1 0x%X\n",
|
||||
TWL4030_REG_GPBR1);
|
||||
goto err_i2c;
|
||||
}
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, madc);
|
||||
mutex_init(&madc->lock);
|
||||
ret = request_threaded_irq(platform_get_irq(pdev, 0), NULL,
|
||||
|
@@ -37,6 +37,7 @@
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/i2c/twl.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#include "twl-core.h"
|
||||
|
||||
@@ -83,8 +84,48 @@ static int twl6030_interrupt_mapping[24] = {
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static unsigned twl6030_irq_base;
|
||||
static int twl_irq;
|
||||
static bool twl_irq_wake_enabled;
|
||||
|
||||
static struct completion irq_event;
|
||||
static atomic_t twl6030_wakeirqs = ATOMIC_INIT(0);
|
||||
|
||||
static int twl6030_irq_pm_notifier(struct notifier_block *notifier,
|
||||
unsigned long pm_event, void *unused)
|
||||
{
|
||||
int chained_wakeups;
|
||||
|
||||
switch (pm_event) {
|
||||
case PM_SUSPEND_PREPARE:
|
||||
chained_wakeups = atomic_read(&twl6030_wakeirqs);
|
||||
|
||||
if (chained_wakeups && !twl_irq_wake_enabled) {
|
||||
if (enable_irq_wake(twl_irq))
|
||||
pr_err("twl6030 IRQ wake enable failed\n");
|
||||
else
|
||||
twl_irq_wake_enabled = true;
|
||||
} else if (!chained_wakeups && twl_irq_wake_enabled) {
|
||||
disable_irq_wake(twl_irq);
|
||||
twl_irq_wake_enabled = false;
|
||||
}
|
||||
|
||||
disable_irq(twl_irq);
|
||||
break;
|
||||
|
||||
case PM_POST_SUSPEND:
|
||||
enable_irq(twl_irq);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block twl6030_irq_pm_notifier_block = {
|
||||
.notifier_call = twl6030_irq_pm_notifier,
|
||||
};
|
||||
|
||||
/*
|
||||
* This thread processes interrupts reported by the Primary Interrupt Handler.
|
||||
@@ -187,6 +228,16 @@ static inline void activate_irq(int irq)
|
||||
#endif
|
||||
}
|
||||
|
||||
int twl6030_irq_set_wake(struct irq_data *d, unsigned int on)
|
||||
{
|
||||
if (on)
|
||||
atomic_inc(&twl6030_wakeirqs);
|
||||
else
|
||||
atomic_dec(&twl6030_wakeirqs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static unsigned twl6030_irq_next;
|
||||
@@ -318,10 +369,12 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
|
||||
twl6030_irq_chip = dummy_irq_chip;
|
||||
twl6030_irq_chip.name = "twl6030";
|
||||
twl6030_irq_chip.irq_set_type = NULL;
|
||||
twl6030_irq_chip.irq_set_wake = twl6030_irq_set_wake;
|
||||
|
||||
for (i = irq_base; i < irq_end; i++) {
|
||||
irq_set_chip_and_handler(i, &twl6030_irq_chip,
|
||||
handle_simple_irq);
|
||||
irq_set_chip_data(i, (void *)irq_num);
|
||||
activate_irq(i);
|
||||
}
|
||||
|
||||
@@ -331,6 +384,14 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
|
||||
|
||||
/* install an irq handler to demultiplex the TWL6030 interrupt */
|
||||
init_completion(&irq_event);
|
||||
|
||||
status = request_irq(irq_num, handle_twl6030_pih, 0,
|
||||
"TWL6030-PIH", &irq_event);
|
||||
if (status < 0) {
|
||||
pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status);
|
||||
goto fail_irq;
|
||||
}
|
||||
|
||||
task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq");
|
||||
if (IS_ERR(task)) {
|
||||
pr_err("twl6030: could not create irq %d thread!\n", irq_num);
|
||||
@@ -338,17 +399,14 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)
|
||||
goto fail_kthread;
|
||||
}
|
||||
|
||||
status = request_irq(irq_num, handle_twl6030_pih, IRQF_DISABLED,
|
||||
"TWL6030-PIH", &irq_event);
|
||||
if (status < 0) {
|
||||
pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status);
|
||||
goto fail_irq;
|
||||
}
|
||||
twl_irq = irq_num;
|
||||
register_pm_notifier(&twl6030_irq_pm_notifier_block);
|
||||
return status;
|
||||
fail_irq:
|
||||
free_irq(irq_num, &irq_event);
|
||||
|
||||
fail_kthread:
|
||||
free_irq(irq_num, &irq_event);
|
||||
|
||||
fail_irq:
|
||||
for (i = irq_base; i < irq_end; i++)
|
||||
irq_set_chip_and_handler(i, NULL, NULL);
|
||||
return status;
|
||||
@@ -356,6 +414,7 @@ fail_kthread:
|
||||
|
||||
int twl6030_exit_irq(void)
|
||||
{
|
||||
unregister_pm_notifier(&twl6030_irq_pm_notifier_block);
|
||||
|
||||
if (twl6030_irq_base) {
|
||||
pr_err("twl6030: can't yet clean up IRQs?\n");
|
||||
|
@@ -420,12 +420,19 @@ static int wm831x_irq_set_type(struct irq_data *data, unsigned int type)
|
||||
switch (type) {
|
||||
case IRQ_TYPE_EDGE_BOTH:
|
||||
wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_INT_MODE;
|
||||
wm831x->gpio_level[irq] = false;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL;
|
||||
wm831x->gpio_level[irq] = false;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
wm831x->gpio_update[irq] = 0x10000;
|
||||
wm831x->gpio_level[irq] = false;
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL;
|
||||
wm831x->gpio_level[irq] = true;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@@ -449,7 +456,7 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct wm831x *wm831x = data;
|
||||
unsigned int i;
|
||||
int primary, status_addr;
|
||||
int primary, status_addr, ret;
|
||||
int status_regs[WM831X_NUM_IRQ_REGS] = { 0 };
|
||||
int read[WM831X_NUM_IRQ_REGS] = { 0 };
|
||||
int *status;
|
||||
@@ -507,6 +514,19 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
|
||||
|
||||
if (*status & wm831x_irqs[i].mask)
|
||||
handle_nested_irq(wm831x->irq_base + i);
|
||||
|
||||
/* Simulate an edge triggered IRQ by polling the input
|
||||
* status. This is sucky but improves interoperability.
|
||||
*/
|
||||
if (primary == WM831X_GP_INT &&
|
||||
wm831x->gpio_level[i - WM831X_IRQ_GPIO_1]) {
|
||||
ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL);
|
||||
while (ret & 1 << (i - WM831X_IRQ_GPIO_1)) {
|
||||
handle_nested_irq(wm831x->irq_base + i);
|
||||
ret = wm831x_reg_read(wm831x,
|
||||
WM831X_GPIO_LEVEL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -596,8 +616,6 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
|
||||
"No interrupt specified - functionality limited\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Enable top level interrupts, we mask at secondary level */
|
||||
wm831x_reg_write(wm831x, WM831X_SYSTEM_INTERRUPTS_MASK, 0);
|
||||
|
||||
|
@@ -217,6 +217,47 @@ static int wm8994_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = wm8994_reg_read(wm8994, WM8994_POWER_MANAGEMENT_4);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to read power status: %d\n", ret);
|
||||
} else if (ret & (WM8994_AIF2ADCL_ENA | WM8994_AIF2ADCR_ENA |
|
||||
WM8994_AIF1ADC2L_ENA | WM8994_AIF1ADC2R_ENA |
|
||||
WM8994_AIF1ADC1L_ENA | WM8994_AIF1ADC1R_ENA)) {
|
||||
dev_dbg(dev, "CODEC still active, ignoring suspend\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = wm8994_reg_read(wm8994, WM8994_POWER_MANAGEMENT_5);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to read power status: %d\n", ret);
|
||||
} else if (ret & (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA |
|
||||
WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA |
|
||||
WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA)) {
|
||||
dev_dbg(dev, "CODEC still active, ignoring suspend\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (wm8994->type) {
|
||||
case WM8958:
|
||||
ret = wm8994_reg_read(wm8994, WM8958_MIC_DETECT_1);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to read power status: %d\n", ret);
|
||||
} else if (ret & WM8958_MICD_ENA) {
|
||||
dev_dbg(dev, "CODEC still active, ignoring suspend\n");
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Disable LDO pulldowns while the device is suspended if we
|
||||
* don't know that something will be driving them. */
|
||||
if (!wm8994->ldo_ena_always_driven)
|
||||
wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2,
|
||||
WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD,
|
||||
WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD);
|
||||
|
||||
/* GPIO configuration state is saved here since we may be configuring
|
||||
* the GPIO alternate functions even if we're not using the gpiolib
|
||||
* driver for them.
|
||||
@@ -286,6 +327,11 @@ static int wm8994_resume(struct device *dev)
|
||||
if (ret < 0)
|
||||
dev_err(dev, "Failed to restore GPIO registers: %d\n", ret);
|
||||
|
||||
/* Disable LDO pulldowns while the device is active */
|
||||
wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2,
|
||||
WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD,
|
||||
0);
|
||||
|
||||
wm8994->suspended = false;
|
||||
|
||||
return 0;
|
||||
@@ -467,8 +513,15 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
|
||||
pdata->gpio_defaults[i]);
|
||||
}
|
||||
}
|
||||
|
||||
wm8994->ldo_ena_always_driven = pdata->ldo_ena_always_driven;
|
||||
}
|
||||
|
||||
/* Disable LDO pulldowns while the device is active */
|
||||
wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2,
|
||||
WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD,
|
||||
0);
|
||||
|
||||
/* In some system designs where the regulators are not in use,
|
||||
* we can achieve a small reduction in leakage currents by
|
||||
* floating LDO outputs. This bit makes no difference if the
|
||||
|
Reference in New Issue
Block a user