MFD bits for the 3.6 merge window.
We have support for a few new drivers: - Samsung s2mps11 - Wolfson Microelectronics wm5102 and wm5110 - Marvell 88PM800 and 88PM805 - TI twl6041 We also have our regular driver improvements: - Device tree and IRQ domain support for STE AB8500 - Regmap and devm_* API conversion for TI tps6586x - Device tree support for Samsung max77686 - devm_* API conversion for STE AB3100 Besides that, quite a lot of fixing and cleanup for mc13xxx, tps65910, tps65090, da9052 and twl-core. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABAgAGBQJQFpGVAAoJEIqAPN1PVmxKFNoP/1dkYngrxxV6cxdyLJ74APhG lVKPgaDxQhdgfwCZJmMeZK1UphZo80cWnEXG6sTHZUEQdTaslSJu5SuPfUM+fo7e 52/dU0nx0ZE04pwPQLHbidS4TmHlbLg9oM2kmIf9RO5rg34GodwVgRL/4+k1qvhz aWYJt9erFhQOpqaSX66mXHSuhvzHypbcE7d2B2Ykmh3NoYiH2w9H9KmIbbb+ZLq8 +Bp/i5Ys/vfooo+8IE2w6KZfIzMwsmmtWjjr/38yuQJaKZCh/zn23DM9HsdrVf++ RzfniRF4YBxmeKi7zi8MFIYys8filTCXA9dXbGSAKCuUCT37yZRnUxTeN1bn7Bux A7KRpG7pUKQKVKqCTndvK5LcQKlT33XyW2ZzV1wVWX2JkCJ+gilPeykb8IabNvGX nIp0STEGR/WdCLEAKo8pJF7Usn0RuUzAug02SG/mQ6dpnLoZqp0Od5W7gRhT7M7h hXr/xKJ6cG5YwicpAdy5kJJ0dRgQrtaHwxrF0B68AXZ7CmAtkPuEGCYhUCFnGQUH XJ0CodAqqVBRyYiQS4zIpIh2nqhIdsgv4OC1+kVLbubQk+PR88zG9Jvg6i1HQi/A OHi7N5Wite3YUrs3sBzDKnEc/Il2YRhVaz2SLVNfZR0PS7hywHN3rK/tVFINTgei jNEz1H6hu7ToNLfs0UzP =c28c -----END PGP SIGNATURE----- Merge tag 'mfd-3.6-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6 Pull MFD bits from Samuel Ortiz: "We have support for a few new drivers: - Samsung s2mps11 - Wolfson Microelectronics wm5102 and wm5110 - Marvell 88PM800 and 88PM805 - TI twl6041 We also have our regular driver improvements: - Device tree and IRQ domain support for STE AB8500 - Regmap and devm_* API conversion for TI tps6586x - Device tree support for Samsung max77686 - devm_* API conversion for STE AB3100 Besides that, quite a lot of fixing and cleanup for mc13xxx, tps65910, tps65090, da9052 and twl-core." Fix up mostly trivial conflicts, with the exception of drivers/usb/host/ehci-omap.c in particular, which had some re-organization of the reset sequence (commit 1a49e2ac9651: "EHCI: centralize controller initialization") that clashed with commit2761a63945
("mfd: USB: Fix the omap-usb EHCI ULPI PHY reset fix issues"). In particular, commit2761a63945
moved the usb_add_hcd() to the *middle* of the reset sequence, which clashes fairly badly with the reset sequence re-organization (although it could have been done inside the new omap_ehci_init() function). I left that part of commit2761a63945
just undone. * tag 'mfd-3.6-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6: (110 commits) mfd: Ensure AB8500 platform data is passed through db8500-prcmu to MFD Core mfd: Arizone core should select MFD_CORE mfd: Fix arizona-irq.c build by selecting REGMAP_IRQ mfd: Add debug trace on entering and leaving arizone runtime suspend mfd: Correct tps65090 cell names mfd: Remove gpio support from tps6586x core driver ARM: tegra: defconfig: Enable tps6586x gpio gpio: tps6586x: Add gpio support through platform driver mfd: Cache tps6586x register through regmap mfd: Use regmap for tps6586x register access. mfd: Use devm managed resources for tps6586x input: Add onkey support for 88PM80X PMIC mfd: Add support for twl6041 mfd: Fix twl6040 revision information mfd: Matches should be NULL when populate anatop child devices input: ab8500-ponkey: Create AB8500 domain IRQ mapping mfd: Add missing out of memory check for pcf50633 Documentation: Describe the AB8500 Device Tree bindings mfd: Add tps65910 32-kHz-crystal-input init mfd: Drop modifying mc13xxx driver's id_table in probe ...
This commit is contained in:
commit
3e701cdfe6
|
@ -0,0 +1,123 @@
|
|||
* AB8500 Multi-Functional Device (MFD)
|
||||
|
||||
Required parent device properties:
|
||||
- compatible : contains "stericsson,ab8500";
|
||||
- interrupts : contains the IRQ line for the AB8500
|
||||
- interrupt-controller : describes the AB8500 as an Interrupt Controller (has its own domain)
|
||||
- #interrupt-cells : should be 2, for 2-cell format
|
||||
- The first cell is the AB8500 local IRQ number
|
||||
- The second cell is used to specify optional parameters
|
||||
- bits[3:0] trigger type and level flags:
|
||||
1 = low-to-high edge triggered
|
||||
2 = high-to-low edge triggered
|
||||
4 = active high level-sensitive
|
||||
8 = active low level-sensitive
|
||||
|
||||
Optional parent device properties:
|
||||
- reg : contains the PRCMU mailbox address for the AB8500 i2c port
|
||||
|
||||
The AB8500 consists of a large and varied group of sub-devices:
|
||||
|
||||
Device IRQ Names Supply Names Description
|
||||
------ --------- ------------ -----------
|
||||
ab8500-bm : : : Battery Manager
|
||||
ab8500-btemp : : : Battery Temperature
|
||||
ab8500-charger : : : Battery Charger
|
||||
ab8500-fg : : : Fuel Gauge
|
||||
ab8500-gpadc : HW_CONV_END : vddadc : Analogue to Digital Converter
|
||||
SW_CONV_END : :
|
||||
ab8500-gpio : : : GPIO Controller
|
||||
ab8500-ponkey : ONKEY_DBF : : Power-on Key
|
||||
ONKEY_DBR : :
|
||||
ab8500-pwm : : : Pulse Width Modulator
|
||||
ab8500-regulator : : : Regulators
|
||||
ab8500-rtc : 60S : : Real Time Clock
|
||||
: ALARM : :
|
||||
ab8500-sysctrl : : : System Control
|
||||
ab8500-usb : ID_WAKEUP_R : vddulpivio18 : Universal Serial Bus
|
||||
: ID_WAKEUP_F : v-ape :
|
||||
: VBUS_DET_F : musb_1v8 :
|
||||
: VBUS_DET_R : :
|
||||
: USB_LINK_STATUS : :
|
||||
: USB_ADP_PROBE_PLUG : :
|
||||
: USB_ADP_PROBE_UNPLUG : :
|
||||
|
||||
Required child device properties:
|
||||
- compatible : "stericsson,ab8500-[bm|btemp|charger|fg|gpadc|gpio|ponkey|
|
||||
pwm|regulator|rtc|sysctrl|usb]";
|
||||
|
||||
Optional child device properties:
|
||||
- interrupts : contains the device IRQ(s) using the 2-cell format (see above)
|
||||
- interrupt-names : contains names of IRQ resource in the order in which they were
|
||||
supplied in the interrupts property
|
||||
- <supply_name>-supply : contains a phandle to the regulator supply node in Device Tree
|
||||
|
||||
ab8500@5 {
|
||||
compatible = "stericsson,ab8500";
|
||||
reg = <5>; /* mailbox 5 is i2c */
|
||||
interrupts = <0 40 0x4>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
|
||||
ab8500-rtc {
|
||||
compatible = "stericsson,ab8500-rtc";
|
||||
interrupts = <17 0x4
|
||||
18 0x4>;
|
||||
interrupt-names = "60S", "ALARM";
|
||||
};
|
||||
|
||||
ab8500-gpadc {
|
||||
compatible = "stericsson,ab8500-gpadc";
|
||||
interrupts = <32 0x4
|
||||
39 0x4>;
|
||||
interrupt-names = "HW_CONV_END", "SW_CONV_END";
|
||||
vddadc-supply = <&ab8500_ldo_tvout_reg>;
|
||||
};
|
||||
|
||||
ab8500-usb {
|
||||
compatible = "stericsson,ab8500-usb";
|
||||
interrupts = < 90 0x4
|
||||
96 0x4
|
||||
14 0x4
|
||||
15 0x4
|
||||
79 0x4
|
||||
74 0x4
|
||||
75 0x4>;
|
||||
interrupt-names = "ID_WAKEUP_R",
|
||||
"ID_WAKEUP_F",
|
||||
"VBUS_DET_F",
|
||||
"VBUS_DET_R",
|
||||
"USB_LINK_STATUS",
|
||||
"USB_ADP_PROBE_PLUG",
|
||||
"USB_ADP_PROBE_UNPLUG";
|
||||
vddulpivio18-supply = <&ab8500_ldo_initcore_reg>;
|
||||
v-ape-supply = <&db8500_vape_reg>;
|
||||
musb_1v8-supply = <&db8500_vsmps2_reg>;
|
||||
};
|
||||
|
||||
ab8500-ponkey {
|
||||
compatible = "stericsson,ab8500-ponkey";
|
||||
interrupts = <6 0x4
|
||||
7 0x4>;
|
||||
interrupt-names = "ONKEY_DBF", "ONKEY_DBR";
|
||||
};
|
||||
|
||||
ab8500-sysctrl {
|
||||
compatible = "stericsson,ab8500-sysctrl";
|
||||
};
|
||||
|
||||
ab8500-pwm {
|
||||
compatible = "stericsson,ab8500-pwm";
|
||||
};
|
||||
|
||||
ab8500-regulators {
|
||||
compatible = "stericsson,ab8500-regulator";
|
||||
|
||||
ab8500_ldo_aux1_reg: ab8500_ldo_aux1 {
|
||||
/*
|
||||
* See: Documentation/devicetree/bindings/regulator/regulator.txt
|
||||
* for more information on regulators
|
||||
*/
|
||||
};
|
||||
};
|
||||
};
|
|
@ -0,0 +1,59 @@
|
|||
Maxim MAX77686 multi-function device
|
||||
|
||||
MAX77686 is a Mulitifunction device with PMIC, RTC and Charger on chip. It is
|
||||
interfaced to host controller using i2c interface. PMIC and Charger submodules
|
||||
are addressed using same i2c slave address whereas RTC submodule uses
|
||||
different i2c slave address,presently for which we are statically creating i2c
|
||||
client while probing.This document describes the binding for mfd device and
|
||||
PMIC submodule.
|
||||
|
||||
Required properties:
|
||||
- compatible : Must be "maxim,max77686";
|
||||
- reg : Specifies the i2c slave address of PMIC block.
|
||||
- interrupts : This i2c device has an IRQ line connected to the main SoC.
|
||||
- interrupt-parent : The parent interrupt controller.
|
||||
|
||||
Optional node:
|
||||
- voltage-regulators : The regulators of max77686 have to be instantiated
|
||||
under subnode named "voltage-regulators" using the following format.
|
||||
|
||||
regulator_name {
|
||||
regulator-compatible = LDOn/BUCKn
|
||||
standard regulator constraints....
|
||||
};
|
||||
refer Documentation/devicetree/bindings/regulator/regulator.txt
|
||||
|
||||
The regulator-compatible property of regulator should initialized with string
|
||||
to get matched with their hardware counterparts as follow:
|
||||
|
||||
-LDOn : for LDOs, where n can lie in range 1 to 26.
|
||||
example: LDO1, LDO2, LDO26.
|
||||
-BUCKn : for BUCKs, where n can lie in range 1 to 9.
|
||||
example: BUCK1, BUCK5, BUCK9.
|
||||
|
||||
Example:
|
||||
|
||||
max77686@09 {
|
||||
compatible = "maxim,max77686";
|
||||
interrupt-parent = <&wakeup_eint>;
|
||||
interrupts = <26 0>;
|
||||
reg = <0x09>;
|
||||
|
||||
voltage-regulators {
|
||||
ldo11_reg {
|
||||
regulator-compatible = "LDO11";
|
||||
regulator-name = "vdd_ldo11";
|
||||
regulator-min-microvolt = <1900000>;
|
||||
regulator-max-microvolt = <1900000>;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
buck1_reg {
|
||||
regulator-compatible = "BUCK1";
|
||||
regulator-name = "vdd_mif";
|
||||
regulator-min-microvolt = <950000>;
|
||||
regulator-max-microvolt = <1300000>;
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
};
|
||||
}
|
|
@ -81,7 +81,7 @@ Example:
|
|||
|
||||
ti,vmbch-threshold = 0;
|
||||
ti,vmbch2-threshold = 0;
|
||||
|
||||
ti,en-ck32k-xtal;
|
||||
ti,en-gpio-sleep = <0 0 1 0 0 0 0 0 0>;
|
||||
|
||||
vcc1-supply = <®_parent>;
|
||||
|
|
|
@ -6,7 +6,7 @@ They are connected ot the host processor via i2c for commands, McPDM for audio
|
|||
data and commands.
|
||||
|
||||
Required properties:
|
||||
- compatible : Must be "ti,twl6040";
|
||||
- compatible : "ti,twl6040" for twl6040, "ti,twl6041" for twl6041
|
||||
- reg: must be 0x4b for i2c address
|
||||
- interrupts: twl6040 has one interrupt line connecteded to the main SoC
|
||||
- interrupt-parent: The parent interrupt controller
|
||||
|
|
10
MAINTAINERS
10
MAINTAINERS
|
@ -5912,6 +5912,16 @@ L: linux-fbdev@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/video/s3c-fb.c
|
||||
|
||||
SAMSUNG MULTIFUNCTION DEVICE DRIVERS
|
||||
M: Sangbeom Kim <sbkim73@samsung.com>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/mfd/sec*.c
|
||||
F: drivers/regulator/s2m*.c
|
||||
F: drivers/regulator/s5m*.c
|
||||
F: drivers/rtc/rtc-sec.c
|
||||
F: include/linux/mfd/samsung/
|
||||
|
||||
SERIAL DRIVERS
|
||||
M: Alan Cox <alan@linux.intel.com>
|
||||
L: linux-serial@vger.kernel.org
|
||||
|
|
|
@ -106,6 +106,7 @@ CONFIG_I2C_TEGRA=y
|
|||
CONFIG_SPI=y
|
||||
CONFIG_SPI_TEGRA=y
|
||||
CONFIG_GPIO_TPS65910=y
|
||||
CONFIG_GPIO_TPS6586X=y
|
||||
CONFIG_POWER_SUPPLY=y
|
||||
CONFIG_BATTERY_SBS=y
|
||||
CONFIG_SENSORS_LM90=y
|
||||
|
|
|
@ -1066,12 +1066,8 @@ static struct platform_device nuri_max8903_device = {
|
|||
static void __init nuri_power_init(void)
|
||||
{
|
||||
int gpio;
|
||||
int irq_base = IRQ_GPIO_END + 1;
|
||||
int ta_en = 0;
|
||||
|
||||
nuri_max8997_pdata.irq_base = irq_base;
|
||||
irq_base += MAX8997_IRQ_NR;
|
||||
|
||||
gpio = EXYNOS4_GPX0(7);
|
||||
gpio_request(gpio, "AP_PMIC_IRQ");
|
||||
s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(0xf));
|
||||
|
|
|
@ -426,7 +426,6 @@ static struct max8997_platform_data __initdata origen_max8997_pdata = {
|
|||
.buck1_gpiodvs = false,
|
||||
.buck2_gpiodvs = false,
|
||||
.buck5_gpiodvs = false,
|
||||
.irq_base = IRQ_GPIO_END + 1,
|
||||
|
||||
.ignore_gpiodvs_side_effect = true,
|
||||
.buck125_default_idx = 0x0,
|
||||
|
|
|
@ -211,24 +211,6 @@ static struct ab8500_platform_data ab8500_platdata = {
|
|||
.codec = &ab8500_codec_pdata,
|
||||
};
|
||||
|
||||
static struct resource ab8500_resources[] = {
|
||||
[0] = {
|
||||
.start = IRQ_DB8500_AB8500,
|
||||
.end = IRQ_DB8500_AB8500,
|
||||
.flags = IORESOURCE_IRQ
|
||||
}
|
||||
};
|
||||
|
||||
struct platform_device ab8500_device = {
|
||||
.name = "ab8500-core",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.platform_data = &ab8500_platdata,
|
||||
},
|
||||
.num_resources = 1,
|
||||
.resource = ab8500_resources,
|
||||
};
|
||||
|
||||
/*
|
||||
* TPS61052
|
||||
*/
|
||||
|
@ -443,7 +425,6 @@ static struct hash_platform_data u8500_hash1_platform_data = {
|
|||
/* add any platform devices here - TODO */
|
||||
static struct platform_device *mop500_platform_devs[] __initdata = {
|
||||
&mop500_gpio_keys_device,
|
||||
&ab8500_device,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_STE_DMA40
|
||||
|
@ -605,7 +586,6 @@ static struct platform_device *snowball_platform_devs[] __initdata = {
|
|||
&snowball_led_dev,
|
||||
&snowball_key_dev,
|
||||
&snowball_sbnet_dev,
|
||||
&ab8500_device,
|
||||
};
|
||||
|
||||
static void __init mop500_init_machine(void)
|
||||
|
@ -617,9 +597,8 @@ static void __init mop500_init_machine(void)
|
|||
mop500_gpio_keys[0].gpio = GPIO_PROX_SENSOR;
|
||||
|
||||
mop500_pinmaps_init();
|
||||
parent = u8500_init_devices();
|
||||
parent = u8500_init_devices(&ab8500_platdata);
|
||||
|
||||
/* FIXME: parent of ab8500 should be prcmu */
|
||||
for (i = 0; i < ARRAY_SIZE(mop500_platform_devs); i++)
|
||||
mop500_platform_devs[i]->dev.parent = parent;
|
||||
|
||||
|
@ -652,7 +631,7 @@ static void __init snowball_init_machine(void)
|
|||
int i;
|
||||
|
||||
snowball_pinmaps_init();
|
||||
parent = u8500_init_devices();
|
||||
parent = u8500_init_devices(&ab8500_platdata);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(snowball_platform_devs); i++)
|
||||
snowball_platform_devs[i]->dev.parent = parent;
|
||||
|
@ -684,7 +663,7 @@ static void __init hrefv60_init_machine(void)
|
|||
mop500_gpio_keys[0].gpio = HREFV60_PROX_SENSE_GPIO;
|
||||
|
||||
hrefv60_pinmaps_init();
|
||||
parent = u8500_init_devices();
|
||||
parent = u8500_init_devices(&ab8500_platdata);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mop500_platform_devs); i++)
|
||||
mop500_platform_devs[i]->dev.parent = parent;
|
||||
|
@ -785,9 +764,6 @@ static const struct of_device_id u8500_local_bus_nodes[] = {
|
|||
/* only create devices below soc node */
|
||||
{ .compatible = "stericsson,db8500", },
|
||||
{ .compatible = "stericsson,db8500-prcmu", },
|
||||
{ .compatible = "stericsson,db8500-prcmu-regulator", },
|
||||
{ .compatible = "stericsson,ab8500", },
|
||||
{ .compatible = "stericsson,ab8500-regulator", },
|
||||
{ .compatible = "simple-bus"},
|
||||
{ },
|
||||
};
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mfd/abx500/ab8500.h>
|
||||
|
||||
#include <asm/mach/map.h>
|
||||
#include <asm/pmu.h>
|
||||
|
@ -115,7 +116,7 @@ static irqreturn_t db8500_pmu_handler(int irq, void *dev, irq_handler_t handler)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static struct arm_pmu_platdata db8500_pmu_platdata = {
|
||||
struct arm_pmu_platdata db8500_pmu_platdata = {
|
||||
.handle_irq = db8500_pmu_handler,
|
||||
};
|
||||
|
||||
|
@ -206,7 +207,7 @@ static struct device * __init db8500_soc_device_init(void)
|
|||
/*
|
||||
* This function is called from the board init
|
||||
*/
|
||||
struct device * __init u8500_init_devices(void)
|
||||
struct device * __init u8500_init_devices(struct ab8500_platform_data *ab8500)
|
||||
{
|
||||
struct device *parent;
|
||||
int i;
|
||||
|
@ -223,6 +224,8 @@ struct device * __init u8500_init_devices(void)
|
|||
for (i = 0; i < ARRAY_SIZE(platform_devs); i++)
|
||||
platform_devs[i]->dev.parent = parent;
|
||||
|
||||
db8500_prcmu_device.dev.platform_data = ab8500;
|
||||
|
||||
platform_add_devices(platform_devs, ARRAY_SIZE(platform_devs));
|
||||
|
||||
return parent;
|
||||
|
|
|
@ -13,11 +13,12 @@
|
|||
|
||||
#include <asm/mach/time.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mfd/abx500/ab8500.h>
|
||||
|
||||
void __init ux500_map_io(void);
|
||||
extern void __init u8500_map_io(void);
|
||||
|
||||
extern struct device * __init u8500_init_devices(void);
|
||||
extern struct device * __init u8500_init_devices(struct ab8500_platform_data *ab8500);
|
||||
|
||||
extern void __init ux500_init_irq(void);
|
||||
extern void __init ux500_init_late(void);
|
||||
|
|
|
@ -33,7 +33,7 @@ config EXTCON_MAX77693
|
|||
|
||||
config EXTCON_MAX8997
|
||||
tristate "MAX8997 EXTCON Support"
|
||||
depends on MFD_MAX8997
|
||||
depends on MFD_MAX8997 && IRQ_DOMAIN
|
||||
help
|
||||
If you say yes here you get support for the MUIC device of
|
||||
Maxim MAX8997 PMIC. The MAX8997 MUIC is a USB port accessory
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/mfd/max8997.h>
|
||||
#include <linux/mfd/max8997-private.h>
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/irqdomain.h>
|
||||
|
||||
#define DEV_NAME "max8997-muic"
|
||||
|
||||
|
@ -77,6 +78,7 @@
|
|||
struct max8997_muic_irq {
|
||||
unsigned int irq;
|
||||
const char *name;
|
||||
unsigned int virq;
|
||||
};
|
||||
|
||||
static struct max8997_muic_irq muic_irqs[] = {
|
||||
|
@ -343,12 +345,10 @@ static void max8997_muic_irq_work(struct work_struct *work)
|
|||
{
|
||||
struct max8997_muic_info *info = container_of(work,
|
||||
struct max8997_muic_info, irq_work);
|
||||
struct max8997_dev *max8997 = i2c_get_clientdata(info->muic);
|
||||
u8 status[2];
|
||||
u8 adc, chg_type;
|
||||
|
||||
int irq_type = info->irq - max8997->irq_base;
|
||||
int ret;
|
||||
int irq_type = 0;
|
||||
int i, ret;
|
||||
|
||||
mutex_lock(&info->mutex);
|
||||
|
||||
|
@ -363,6 +363,10 @@ static void max8997_muic_irq_work(struct work_struct *work)
|
|||
dev_dbg(info->dev, "%s: STATUS1:0x%x, 2:0x%x\n", __func__,
|
||||
status[0], status[1]);
|
||||
|
||||
for (i = 0 ; i < ARRAY_SIZE(muic_irqs) ; i++)
|
||||
if (info->irq == muic_irqs[i].virq)
|
||||
irq_type = muic_irqs[i].irq;
|
||||
|
||||
switch (irq_type) {
|
||||
case MAX8997_MUICIRQ_ADC:
|
||||
adc = status[0] & STATUS1_ADC_MASK;
|
||||
|
@ -448,11 +452,15 @@ static int __devinit max8997_muic_probe(struct platform_device *pdev)
|
|||
|
||||
for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) {
|
||||
struct max8997_muic_irq *muic_irq = &muic_irqs[i];
|
||||
int virq = 0;
|
||||
|
||||
ret = request_threaded_irq(pdata->irq_base + muic_irq->irq,
|
||||
NULL, max8997_muic_irq_handler,
|
||||
0, muic_irq->name,
|
||||
info);
|
||||
virq = irq_create_mapping(max8997->irq_domain, muic_irq->irq);
|
||||
if (!virq)
|
||||
goto err_irq;
|
||||
muic_irq->virq = virq;
|
||||
|
||||
ret = request_threaded_irq(virq, NULL,max8997_muic_irq_handler,
|
||||
0, muic_irq->name, info);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed: irq request (IRQ: %d,"
|
||||
|
@ -496,7 +504,7 @@ err_extcon:
|
|||
kfree(info->edev);
|
||||
err_irq:
|
||||
while (--i >= 0)
|
||||
free_irq(pdata->irq_base + muic_irqs[i].irq, info);
|
||||
free_irq(muic_irqs[i].virq, info);
|
||||
kfree(info);
|
||||
err_kfree:
|
||||
return ret;
|
||||
|
@ -505,11 +513,10 @@ err_kfree:
|
|||
static int __devexit max8997_muic_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct max8997_muic_info *info = platform_get_drvdata(pdev);
|
||||
struct max8997_dev *max8997 = i2c_get_clientdata(info->muic);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
|
||||
free_irq(max8997->irq_base + muic_irqs[i].irq, info);
|
||||
free_irq(muic_irqs[i].virq, info);
|
||||
cancel_work_sync(&info->irq_work);
|
||||
|
||||
extcon_dev_unregister(info->edev);
|
||||
|
|
|
@ -597,6 +597,13 @@ config GPIO_AB8500
|
|||
help
|
||||
Select this to enable the AB8500 IC GPIO driver
|
||||
|
||||
config GPIO_TPS6586X
|
||||
bool "TPS6586X GPIO"
|
||||
depends on MFD_TPS6586X
|
||||
help
|
||||
Select this option to enable GPIO driver for the TPS6586X
|
||||
chip family.
|
||||
|
||||
config GPIO_TPS65910
|
||||
bool "TPS65910 GPIO"
|
||||
depends on MFD_TPS65910
|
||||
|
|
|
@ -63,6 +63,7 @@ obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
|
|||
obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o
|
||||
obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o
|
||||
obj-$(CONFIG_ARCH_DAVINCI_TNETV107X) += gpio-tnetv107x.o
|
||||
obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o
|
||||
obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o
|
||||
obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o
|
||||
obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* TI TPS6586x GPIO driver
|
||||
*
|
||||
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
|
||||
* Author: Laxman dewangan <ldewangan@nvidia.com>
|
||||
*
|
||||
* Based on tps6586x.c
|
||||
* Copyright (c) 2010 CompuLab Ltd.
|
||||
* Mike Rapoport <mike@compulab.co.il>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/tps6586x.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
/* GPIO control registers */
|
||||
#define TPS6586X_GPIOSET1 0x5d
|
||||
#define TPS6586X_GPIOSET2 0x5e
|
||||
|
||||
struct tps6586x_gpio {
|
||||
struct gpio_chip gpio_chip;
|
||||
struct device *parent;
|
||||
};
|
||||
|
||||
static inline struct tps6586x_gpio *to_tps6586x_gpio(struct gpio_chip *chip)
|
||||
{
|
||||
return container_of(chip, struct tps6586x_gpio, gpio_chip);
|
||||
}
|
||||
|
||||
static int tps6586x_gpio_get(struct gpio_chip *gc, unsigned offset)
|
||||
{
|
||||
struct tps6586x_gpio *tps6586x_gpio = to_tps6586x_gpio(gc);
|
||||
uint8_t val;
|
||||
int ret;
|
||||
|
||||
ret = tps6586x_read(tps6586x_gpio->parent, TPS6586X_GPIOSET2, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return !!(val & (1 << offset));
|
||||
}
|
||||
|
||||
static void tps6586x_gpio_set(struct gpio_chip *gc, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
struct tps6586x_gpio *tps6586x_gpio = to_tps6586x_gpio(gc);
|
||||
|
||||
tps6586x_update(tps6586x_gpio->parent, TPS6586X_GPIOSET2,
|
||||
value << offset, 1 << offset);
|
||||
}
|
||||
|
||||
static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
struct tps6586x_gpio *tps6586x_gpio = to_tps6586x_gpio(gc);
|
||||
uint8_t val, mask;
|
||||
|
||||
tps6586x_gpio_set(gc, offset, value);
|
||||
|
||||
val = 0x1 << (offset * 2);
|
||||
mask = 0x3 << (offset * 2);
|
||||
|
||||
return tps6586x_update(tps6586x_gpio->parent, TPS6586X_GPIOSET1,
|
||||
val, mask);
|
||||
}
|
||||
|
||||
static int __devinit tps6586x_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tps6586x_platform_data *pdata;
|
||||
struct tps6586x_gpio *tps6586x_gpio;
|
||||
int ret;
|
||||
|
||||
pdata = dev_get_platdata(pdev->dev.parent);
|
||||
tps6586x_gpio = devm_kzalloc(&pdev->dev,
|
||||
sizeof(*tps6586x_gpio), GFP_KERNEL);
|
||||
if (!tps6586x_gpio) {
|
||||
dev_err(&pdev->dev, "Could not allocate tps6586x_gpio\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tps6586x_gpio->parent = pdev->dev.parent;
|
||||
|
||||
tps6586x_gpio->gpio_chip.owner = THIS_MODULE;
|
||||
tps6586x_gpio->gpio_chip.label = pdev->name;
|
||||
tps6586x_gpio->gpio_chip.dev = &pdev->dev;
|
||||
tps6586x_gpio->gpio_chip.ngpio = 4;
|
||||
tps6586x_gpio->gpio_chip.can_sleep = 1;
|
||||
|
||||
/* FIXME: add handling of GPIOs as dedicated inputs */
|
||||
tps6586x_gpio->gpio_chip.direction_output = tps6586x_gpio_output;
|
||||
tps6586x_gpio->gpio_chip.set = tps6586x_gpio_set;
|
||||
tps6586x_gpio->gpio_chip.get = tps6586x_gpio_get;
|
||||
|
||||
#ifdef CONFIG_OF_GPIO
|
||||
tps6586x_gpio->gpio_chip.of_node = pdev->dev.parent->of_node;
|
||||
#endif
|
||||
if (pdata && pdata->gpio_base)
|
||||
tps6586x_gpio->gpio_chip.base = pdata->gpio_base;
|
||||
else
|
||||
tps6586x_gpio->gpio_chip.base = -1;
|
||||
|
||||
ret = gpiochip_add(&tps6586x_gpio->gpio_chip);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, tps6586x_gpio);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit tps6586x_gpio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tps6586x_gpio *tps6586x_gpio = platform_get_drvdata(pdev);
|
||||
|
||||
return gpiochip_remove(&tps6586x_gpio->gpio_chip);
|
||||
}
|
||||
|
||||
static struct platform_driver tps6586x_gpio_driver = {
|
||||
.driver.name = "tps6586x-gpio",
|
||||
.driver.owner = THIS_MODULE,
|
||||
.probe = tps6586x_gpio_probe,
|
||||
.remove = __devexit_p(tps6586x_gpio_remove),
|
||||
};
|
||||
|
||||
static int __init tps6586x_gpio_init(void)
|
||||
{
|
||||
return platform_driver_register(&tps6586x_gpio_driver);
|
||||
}
|
||||
subsys_initcall(tps6586x_gpio_init);
|
||||
|
||||
static void __exit tps6586x_gpio_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&tps6586x_gpio_driver);
|
||||
}
|
||||
module_exit(tps6586x_gpio_exit);
|
||||
|
||||
MODULE_ALIAS("platform:tps6586x-gpio");
|
||||
MODULE_DESCRIPTION("GPIO interface for TPS6586X PMIC");
|
||||
MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Marvell 88PM80x ONKEY driver
|
||||
*
|
||||
* Copyright (C) 2012 Marvell International Ltd.
|
||||
* Haojian Zhuang <haojian.zhuang@marvell.com>
|
||||
* Qiao Zhou <zhouqiao@marvell.com>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General
|
||||
* Public License. See the file "COPYING" in the main directory of this
|
||||
* archive for more details.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/mfd/88pm80x.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define PM800_LONG_ONKEY_EN (1 << 0)
|
||||
#define PM800_LONG_KEY_DELAY (8) /* 1 .. 16 seconds */
|
||||
#define PM800_LONKEY_PRESS_TIME ((PM800_LONG_KEY_DELAY-1) << 4)
|
||||
#define PM800_LONKEY_PRESS_TIME_MASK (0xF0)
|
||||
#define PM800_SW_PDOWN (1 << 5)
|
||||
|
||||
struct pm80x_onkey_info {
|
||||
struct input_dev *idev;
|
||||
struct pm80x_chip *pm80x;
|
||||
struct regmap *map;
|
||||
int irq;
|
||||
};
|
||||
|
||||
/* 88PM80x gives us an interrupt when ONKEY is held */
|
||||
static irqreturn_t pm80x_onkey_handler(int irq, void *data)
|
||||
{
|
||||
struct pm80x_onkey_info *info = data;
|
||||
int ret = 0;
|
||||
unsigned int val;
|
||||
|
||||
ret = regmap_read(info->map, PM800_STATUS_1, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(info->idev->dev.parent, "failed to read status: %d\n", ret);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
val &= PM800_ONKEY_STS1;
|
||||
|
||||
input_report_key(info->idev, KEY_POWER, val);
|
||||
input_sync(info->idev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(pm80x_onkey_pm_ops, pm80x_dev_suspend,
|
||||
pm80x_dev_resume);
|
||||
|
||||
static int __devinit pm80x_onkey_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
||||
struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
|
||||
struct pm80x_onkey_info *info;
|
||||
int err;
|
||||
|
||||
info = kzalloc(sizeof(struct pm80x_onkey_info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
info->pm80x = chip;
|
||||
|
||||
info->irq = platform_get_irq(pdev, 0);
|
||||
if (info->irq < 0) {
|
||||
dev_err(&pdev->dev, "No IRQ resource!\n");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
info->map = info->pm80x->regmap;
|
||||
if (!info->map) {
|
||||
dev_err(&pdev->dev, "no regmap!\n");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
info->idev = input_allocate_device();
|
||||
if (!info->idev) {
|
||||
dev_err(&pdev->dev, "Failed to allocate input dev\n");
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
info->idev->name = "88pm80x_on";
|
||||
info->idev->phys = "88pm80x_on/input0";
|
||||
info->idev->id.bustype = BUS_I2C;
|
||||
info->idev->dev.parent = &pdev->dev;
|
||||
info->idev->evbit[0] = BIT_MASK(EV_KEY);
|
||||
__set_bit(KEY_POWER, info->idev->keybit);
|
||||
|
||||
err = pm80x_request_irq(info->pm80x, info->irq, pm80x_onkey_handler,
|
||||
IRQF_ONESHOT, "onkey", info);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "Failed to request IRQ: #%d: %d\n",
|
||||
info->irq, err);
|
||||
goto out_reg;
|
||||
}
|
||||
|
||||
err = input_register_device(info->idev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Can't register input device: %d\n", err);
|
||||
goto out_irq;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
/* Enable long onkey detection */
|
||||
regmap_update_bits(info->map, PM800_RTC_MISC4, PM800_LONG_ONKEY_EN,
|
||||
PM800_LONG_ONKEY_EN);
|
||||
/* Set 8-second interval */
|
||||
regmap_update_bits(info->map, PM800_RTC_MISC3,
|
||||
PM800_LONKEY_PRESS_TIME_MASK,
|
||||
PM800_LONKEY_PRESS_TIME);
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
return 0;
|
||||
|
||||
out_irq:
|
||||
pm80x_free_irq(info->pm80x, info->irq, info);
|
||||
out_reg:
|
||||
input_free_device(info->idev);
|
||||
out:
|
||||
kfree(info);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devexit pm80x_onkey_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct pm80x_onkey_info *info = platform_get_drvdata(pdev);
|
||||
|
||||
device_init_wakeup(&pdev->dev, 0);
|
||||
pm80x_free_irq(info->pm80x, info->irq, info);
|
||||
input_unregister_device(info->idev);
|
||||
kfree(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver pm80x_onkey_driver = {
|
||||
.driver = {
|
||||
.name = "88pm80x-onkey",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &pm80x_onkey_pm_ops,
|
||||
},
|
||||
.probe = pm80x_onkey_probe,
|
||||
.remove = __devexit_p(pm80x_onkey_remove),
|
||||
};
|
||||
|
||||
module_platform_driver(pm80x_onkey_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Marvell 88PM80x ONKEY driver");
|
||||
MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
|
||||
MODULE_ALIAS("platform:88pm80x-onkey");
|
|
@ -22,6 +22,16 @@ config INPUT_88PM860X_ONKEY
|
|||
To compile this driver as a module, choose M here: the module
|
||||
will be called 88pm860x_onkey.
|
||||
|
||||
config INPUT_88PM80X_ONKEY
|
||||
tristate "88PM80x ONKEY support"
|
||||
depends on MFD_88PM800
|
||||
help
|
||||
Support the ONKEY of Marvell 88PM80x PMICs as an input device
|
||||
reporting power button status.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called 88pm80x_onkey.
|
||||
|
||||
config INPUT_AB8500_PONKEY
|
||||
tristate "AB8500 Pon (PowerOn) Key"
|
||||
depends on AB8500_CORE
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
# Each configuration option enables a list of files.
|
||||
|
||||
obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o
|
||||
obj-$(CONFIG_INPUT_88PM80X_ONKEY) += 88pm80x_onkey.o
|
||||
obj-$(CONFIG_INPUT_AB8500_PONKEY) += ab8500-ponkey.o
|
||||
obj-$(CONFIG_INPUT_AD714X) += ad714x.o
|
||||
obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o
|
||||
|
|
|
@ -74,8 +74,8 @@ static int __devinit ab8500_ponkey_probe(struct platform_device *pdev)
|
|||
|
||||
ponkey->idev = input;
|
||||
ponkey->ab8500 = ab8500;
|
||||
ponkey->irq_dbf = irq_dbf;
|
||||
ponkey->irq_dbr = irq_dbr;
|
||||
ponkey->irq_dbf = ab8500_irq_get_virq(ab8500, irq_dbf);
|
||||
ponkey->irq_dbr = ab8500_irq_get_virq(ab8500, irq_dbr);
|
||||
|
||||
input->name = "AB8500 POn(PowerOn) Key";
|
||||
input->dev.parent = &pdev->dev;
|
||||
|
|
|
@ -0,0 +1,596 @@
|
|||
/*
|
||||
* Base driver for Marvell 88PM800
|
||||
*
|
||||
* Copyright (C) 2012 Marvell International Ltd.
|
||||
* Haojian Zhuang <haojian.zhuang@marvell.com>
|
||||
* Joseph(Yossi) Hanin <yhanin@marvell.com>
|
||||
* Qiao Zhou <zhouqiao@marvell.com>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General
|
||||
* Public License. See the file "COPYING" in the main directory of this
|
||||
* archive for more details.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/88pm80x.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define PM800_CHIP_ID (0x00)
|
||||
|
||||
/* Interrupt Registers */
|
||||
#define PM800_INT_STATUS1 (0x05)
|
||||
#define PM800_ONKEY_INT_STS1 (1 << 0)
|
||||
#define PM800_EXTON_INT_STS1 (1 << 1)
|
||||
#define PM800_CHG_INT_STS1 (1 << 2)
|
||||
#define PM800_BAT_INT_STS1 (1 << 3)
|
||||
#define PM800_RTC_INT_STS1 (1 << 4)
|
||||
#define PM800_CLASSD_OC_INT_STS1 (1 << 5)
|
||||
|
||||
#define PM800_INT_STATUS2 (0x06)
|
||||
#define PM800_VBAT_INT_STS2 (1 << 0)
|
||||
#define PM800_VSYS_INT_STS2 (1 << 1)
|
||||
#define PM800_VCHG_INT_STS2 (1 << 2)
|
||||
#define PM800_TINT_INT_STS2 (1 << 3)
|
||||
#define PM800_GPADC0_INT_STS2 (1 << 4)
|
||||
#define PM800_TBAT_INT_STS2 (1 << 5)
|
||||
#define PM800_GPADC2_INT_STS2 (1 << 6)
|
||||
#define PM800_GPADC3_INT_STS2 (1 << 7)
|
||||
|
||||
#define PM800_INT_STATUS3 (0x07)
|
||||
|
||||
#define PM800_INT_STATUS4 (0x08)
|
||||
#define PM800_GPIO0_INT_STS4 (1 << 0)
|
||||
#define PM800_GPIO1_INT_STS4 (1 << 1)
|
||||
#define PM800_GPIO2_INT_STS4 (1 << 2)
|
||||
#define PM800_GPIO3_INT_STS4 (1 << 3)
|
||||
#define PM800_GPIO4_INT_STS4 (1 << 4)
|
||||
|
||||
#define PM800_INT_ENA_1 (0x09)
|
||||
#define PM800_ONKEY_INT_ENA1 (1 << 0)
|
||||
#define PM800_EXTON_INT_ENA1 (1 << 1)
|
||||
#define PM800_CHG_INT_ENA1 (1 << 2)
|
||||
#define PM800_BAT_INT_ENA1 (1 << 3)
|
||||
#define PM800_RTC_INT_ENA1 (1 << 4)
|
||||
#define PM800_CLASSD_OC_INT_ENA1 (1 << 5)
|
||||
|
||||
#define PM800_INT_ENA_2 (0x0A)
|
||||
#define PM800_VBAT_INT_ENA2 (1 << 0)
|
||||
#define PM800_VSYS_INT_ENA2 (1 << 1)
|
||||
#define PM800_VCHG_INT_ENA2 (1 << 2)
|
||||
#define PM800_TINT_INT_ENA2 (1 << 3)
|
||||
|
||||
#define PM800_INT_ENA_3 (0x0B)
|
||||
#define PM800_GPADC0_INT_ENA3 (1 << 0)
|
||||
#define PM800_GPADC1_INT_ENA3 (1 << 1)
|
||||
#define PM800_GPADC2_INT_ENA3 (1 << 2)
|
||||
#define PM800_GPADC3_INT_ENA3 (1 << 3)
|
||||
#define PM800_GPADC4_INT_ENA3 (1 << 4)
|
||||
|
||||
#define PM800_INT_ENA_4 (0x0C)
|
||||
#define PM800_GPIO0_INT_ENA4 (1 << 0)
|
||||
#define PM800_GPIO1_INT_ENA4 (1 << 1)
|
||||
#define PM800_GPIO2_INT_ENA4 (1 << 2)
|
||||
#define PM800_GPIO3_INT_ENA4 (1 << 3)
|
||||
#define PM800_GPIO4_INT_ENA4 (1 << 4)
|
||||
|
||||
/* number of INT_ENA & INT_STATUS regs */
|
||||
#define PM800_INT_REG_NUM (4)
|
||||
|
||||
/* Interrupt Number in 88PM800 */
|
||||
enum {
|
||||
PM800_IRQ_ONKEY, /*EN1b0 *//*0 */
|
||||
PM800_IRQ_EXTON, /*EN1b1 */
|
||||
PM800_IRQ_CHG, /*EN1b2 */
|
||||
PM800_IRQ_BAT, /*EN1b3 */
|
||||
PM800_IRQ_RTC, /*EN1b4 */
|
||||
PM800_IRQ_CLASSD, /*EN1b5 *//*5 */
|
||||
PM800_IRQ_VBAT, /*EN2b0 */
|
||||
PM800_IRQ_VSYS, /*EN2b1 */
|
||||
PM800_IRQ_VCHG, /*EN2b2 */
|
||||
PM800_IRQ_TINT, /*EN2b3 */
|
||||
PM800_IRQ_GPADC0, /*EN3b0 *//*10 */
|
||||
PM800_IRQ_GPADC1, /*EN3b1 */
|
||||
PM800_IRQ_GPADC2, /*EN3b2 */
|
||||
PM800_IRQ_GPADC3, /*EN3b3 */
|
||||
PM800_IRQ_GPADC4, /*EN3b4 */
|
||||
PM800_IRQ_GPIO0, /*EN4b0 *//*15 */
|
||||
PM800_IRQ_GPIO1, /*EN4b1 */
|
||||
PM800_IRQ_GPIO2, /*EN4b2 */
|
||||
PM800_IRQ_GPIO3, /*EN4b3 */
|
||||
PM800_IRQ_GPIO4, /*EN4b4 *//*19 */
|
||||
PM800_MAX_IRQ,
|
||||
};
|
||||
|
||||
enum {
|
||||
/* Procida */
|
||||
PM800_CHIP_A0 = 0x60,
|
||||
PM800_CHIP_A1 = 0x61,
|
||||
PM800_CHIP_B0 = 0x62,
|
||||
PM800_CHIP_C0 = 0x63,
|
||||
PM800_CHIP_END = PM800_CHIP_C0,
|
||||
|
||||
/* Make sure to update this to the last stepping */
|
||||
PM8XXX_CHIP_END = PM800_CHIP_END
|
||||
};
|
||||
|
||||
static const struct i2c_device_id pm80x_id_table[] = {
|
||||
{"88PM800", CHIP_PM800},
|
||||
{} /* NULL terminated */
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pm80x_id_table);
|
||||
|
||||
static struct resource rtc_resources[] = {
|
||||
{
|
||||
.name = "88pm80x-rtc",
|
||||
.start = PM800_IRQ_RTC,
|
||||
.end = PM800_IRQ_RTC,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell rtc_devs[] = {
|
||||
{
|
||||
.name = "88pm80x-rtc",
|
||||
.num_resources = ARRAY_SIZE(rtc_resources),
|
||||
.resources = &rtc_resources[0],
|
||||
.id = -1,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource onkey_resources[] = {
|
||||
{
|
||||
.name = "88pm80x-onkey",
|
||||
.start = PM800_IRQ_ONKEY,
|
||||
.end = PM800_IRQ_ONKEY,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell onkey_devs[] = {
|
||||
{
|
||||
.name = "88pm80x-onkey",
|
||||
.num_resources = 1,
|
||||
.resources = &onkey_resources[0],
|
||||
.id = -1,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_irq pm800_irqs[] = {
|
||||
/* INT0 */
|
||||
[PM800_IRQ_ONKEY] = {
|
||||
.mask = PM800_ONKEY_INT_ENA1,
|
||||
},
|
||||
[PM800_IRQ_EXTON] = {
|
||||
.mask = PM800_EXTON_INT_ENA1,
|
||||
},
|
||||
[PM800_IRQ_CHG] = {
|
||||
.mask = PM800_CHG_INT_ENA1,
|
||||
},
|
||||
[PM800_IRQ_BAT] = {
|
||||
.mask = PM800_BAT_INT_ENA1,
|
||||
},
|
||||
[PM800_IRQ_RTC] = {
|
||||
.mask = PM800_RTC_INT_ENA1,
|
||||
},
|
||||
[PM800_IRQ_CLASSD] = {
|
||||
.mask = PM800_CLASSD_OC_INT_ENA1,
|
||||
},
|
||||
/* INT1 */
|
||||
[PM800_IRQ_VBAT] = {
|
||||
.reg_offset = 1,
|
||||
.mask = PM800_VBAT_INT_ENA2,
|
||||
},
|
||||
[PM800_IRQ_VSYS] = {
|
||||
.reg_offset = 1,
|
||||
.mask = PM800_VSYS_INT_ENA2,
|
||||
},
|
||||
[PM800_IRQ_VCHG] = {
|
||||
.reg_offset = 1,
|
||||
.mask = PM800_VCHG_INT_ENA2,
|
||||
},
|
||||
[PM800_IRQ_TINT] = {
|
||||
.reg_offset = 1,
|
||||
.mask = PM800_TINT_INT_ENA2,
|
||||
},
|
||||
/* INT2 */
|
||||
[PM800_IRQ_GPADC0] = {
|
||||
.reg_offset = 2,
|
||||
.mask = PM800_GPADC0_INT_ENA3,
|
||||
},
|
||||
[PM800_IRQ_GPADC1] = {
|
||||
.reg_offset = 2,
|
||||
.mask = PM800_GPADC1_INT_ENA3,
|
||||
},
|
||||
[PM800_IRQ_GPADC2] = {
|
||||
.reg_offset = 2,
|
||||
.mask = PM800_GPADC2_INT_ENA3,
|
||||
},
|
||||
[PM800_IRQ_GPADC3] = {
|
||||
.reg_offset = 2,
|
||||
.mask = PM800_GPADC3_INT_ENA3,
|
||||
},
|
||||
[PM800_IRQ_GPADC4] = {
|
||||
.reg_offset = 2,
|
||||
.mask = PM800_GPADC4_INT_ENA3,
|
||||
},
|
||||
/* INT3 */
|
||||
[PM800_IRQ_GPIO0] = {
|
||||
.reg_offset = 3,
|
||||
.mask = PM800_GPIO0_INT_ENA4,
|
||||
},
|
||||
[PM800_IRQ_GPIO1] = {
|
||||
.reg_offset = 3,
|
||||
.mask = PM800_GPIO1_INT_ENA4,
|
||||
},
|
||||
[PM800_IRQ_GPIO2] = {
|
||||
.reg_offset = 3,
|
||||
.mask = PM800_GPIO2_INT_ENA4,
|
||||
},
|
||||
[PM800_IRQ_GPIO3] = {
|
||||
.reg_offset = 3,
|
||||
.mask = PM800_GPIO3_INT_ENA4,
|
||||
},
|
||||
[PM800_IRQ_GPIO4] = {
|
||||
.reg_offset = 3,
|
||||
.mask = PM800_GPIO4_INT_ENA4,
|
||||
},
|
||||
};
|
||||
|
||||
static int __devinit device_gpadc_init(struct pm80x_chip *chip,
|
||||
struct pm80x_platform_data *pdata)
|
||||
{
|
||||
struct pm80x_subchip *subchip = chip->subchip;
|
||||
struct regmap *map = subchip->regmap_gpadc;
|
||||
int data = 0, mask = 0, ret = 0;
|
||||
|
||||
if (!map) {
|
||||
dev_warn(chip->dev,
|
||||
"Warning: gpadc regmap is not available!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
/*
|
||||
* initialize GPADC without activating it turn on GPADC
|
||||
* measurments
|
||||
*/
|
||||
ret = regmap_update_bits(map,
|
||||
PM800_GPADC_MISC_CONFIG2,
|
||||
PM800_GPADC_MISC_GPFSM_EN,
|
||||
PM800_GPADC_MISC_GPFSM_EN);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
/*
|
||||
* This function configures the ADC as requires for
|
||||
* CP implementation.CP does not "own" the ADC configuration
|
||||
* registers and relies on AP.
|
||||
* Reason: enable automatic ADC measurements needed
|
||||
* for CP to get VBAT and RF temperature readings.
|
||||
*/
|
||||
ret = regmap_update_bits(map, PM800_GPADC_MEAS_EN1,
|
||||
PM800_MEAS_EN1_VBAT, PM800_MEAS_EN1_VBAT);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
ret = regmap_update_bits(map, PM800_GPADC_MEAS_EN2,
|
||||
(PM800_MEAS_EN2_RFTMP | PM800_MEAS_GP0_EN),
|
||||
(PM800_MEAS_EN2_RFTMP | PM800_MEAS_GP0_EN));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* the defult of PM800 is GPADC operates at 100Ks/s rate
|
||||
* and Number of GPADC slots with active current bias prior
|
||||
* to GPADC sampling = 1 slot for all GPADCs set for
|
||||
* Temprature mesurmants
|
||||
*/
|
||||
mask = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN1 |
|
||||
PM800_GPADC_GP_BIAS_EN2 | PM800_GPADC_GP_BIAS_EN3);
|
||||
|
||||
if (pdata && (pdata->batt_det == 0))
|
||||
data = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN1 |
|
||||
PM800_GPADC_GP_BIAS_EN2 | PM800_GPADC_GP_BIAS_EN3);
|
||||
else
|
||||
data = (PM800_GPADC_GP_BIAS_EN0 | PM800_GPADC_GP_BIAS_EN2 |
|
||||
PM800_GPADC_GP_BIAS_EN3);
|
||||
|
||||
ret = regmap_update_bits(map, PM800_GP_BIAS_ENA1, mask, data);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
dev_info(chip->dev, "pm800 device_gpadc_init: Done\n");
|
||||
return 0;
|
||||
|
||||
out:
|
||||
dev_info(chip->dev, "pm800 device_gpadc_init: Failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devinit device_irq_init_800(struct pm80x_chip *chip)
|
||||
{
|
||||
struct regmap *map = chip->regmap;
|
||||
unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
|
||||
int data, mask, ret = -EINVAL;
|
||||
|
||||
if (!map || !chip->irq) {
|
||||
dev_err(chip->dev, "incorrect parameters\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* irq_mode defines the way of clearing interrupt. it's read-clear by
|
||||
* default.
|
||||
*/
|
||||
mask =
|
||||
PM800_WAKEUP2_INV_INT | PM800_WAKEUP2_INT_CLEAR |
|
||||
PM800_WAKEUP2_INT_MASK;
|
||||
|
||||
data = PM800_WAKEUP2_INT_CLEAR;
|
||||
ret = regmap_update_bits(map, PM800_WAKEUP2, mask, data);
|
||||
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret =
|
||||
regmap_add_irq_chip(chip->regmap, chip->irq, flags, -1,
|
||||
chip->regmap_irq_chip, &chip->irq_data);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void device_irq_exit_800(struct pm80x_chip *chip)
|
||||
{
|
||||
regmap_del_irq_chip(chip->irq, chip->irq_data);
|
||||
}
|
||||
|
||||
static struct regmap_irq_chip pm800_irq_chip = {
|
||||
.name = "88pm800",
|
||||
.irqs = pm800_irqs,
|
||||
.num_irqs = ARRAY_SIZE(pm800_irqs),
|
||||
|
||||
.num_regs = 4,
|
||||
.status_base = PM800_INT_STATUS1,
|
||||
.mask_base = PM800_INT_ENA_1,
|
||||
.ack_base = PM800_INT_STATUS1,
|
||||
};
|
||||
|
||||
static int pm800_pages_init(struct pm80x_chip *chip)
|
||||
{
|
||||
struct pm80x_subchip *subchip;
|
||||
struct i2c_client *client = chip->client;
|
||||
|
||||
subchip = chip->subchip;
|
||||
/* PM800 block power: i2c addr 0x31 */
|
||||
if (subchip->power_page_addr) {
|
||||
subchip->power_page =
|
||||
i2c_new_dummy(client->adapter, subchip->power_page_addr);
|
||||
subchip->regmap_power =
|
||||
devm_regmap_init_i2c(subchip->power_page,
|
||||
&pm80x_regmap_config);
|
||||
i2c_set_clientdata(subchip->power_page, chip);
|
||||
} else
|
||||
dev_info(chip->dev,
|
||||
"PM800 block power 0x31: No power_page_addr\n");
|
||||
|
||||
/* PM800 block GPADC: i2c addr 0x32 */
|
||||
if (subchip->gpadc_page_addr) {
|
||||
subchip->gpadc_page = i2c_new_dummy(client->adapter,
|
||||
subchip->gpadc_page_addr);
|
||||
subchip->regmap_gpadc =
|
||||
devm_regmap_init_i2c(subchip->gpadc_page,
|
||||
&pm80x_regmap_config);
|
||||
i2c_set_clientdata(subchip->gpadc_page, chip);
|
||||
} else
|
||||
dev_info(chip->dev,
|
||||
"PM800 block GPADC 0x32: No gpadc_page_addr\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pm800_pages_exit(struct pm80x_chip *chip)
|
||||
{
|
||||
struct pm80x_subchip *subchip;
|
||||
|
||||
regmap_exit(chip->regmap);
|
||||
i2c_unregister_device(chip->client);
|
||||
|
||||
subchip = chip->subchip;
|
||||
if (subchip->power_page) {
|
||||
regmap_exit(subchip->regmap_power);
|
||||
i2c_unregister_device(subchip->power_page);
|
||||
}
|
||||
if (subchip->gpadc_page) {
|
||||
regmap_exit(subchip->regmap_gpadc);
|
||||
i2c_unregister_device(subchip->gpadc_page);
|
||||
}
|
||||
}
|
||||
|
||||
static int __devinit device_800_init(struct pm80x_chip *chip,
|
||||
struct pm80x_platform_data *pdata)
|
||||
{
|
||||
int ret, pmic_id;
|
||||
unsigned int val;
|
||||
|
||||
ret = regmap_read(chip->regmap, PM800_CHIP_ID, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
pmic_id = val & PM80X_VERSION_MASK;
|
||||
|
||||
if ((pmic_id >= PM800_CHIP_A0) && (pmic_id <= PM800_CHIP_END)) {
|
||||
chip->version = val;
|
||||
dev_info(chip->dev,
|
||||
"88PM80x:Marvell 88PM800 (ID:0x%x) detected\n", val);
|
||||
} else {
|
||||
dev_err(chip->dev,
|
||||
"Failed to detect Marvell 88PM800:ChipID[0x%x]\n", val);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* alarm wake up bit will be clear in device_irq_init(),
|
||||
* read before that
|
||||
*/
|
||||
ret = regmap_read(chip->regmap, PM800_RTC_CONTROL, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to read RTC register: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
if (val & PM800_ALARM_WAKEUP) {
|
||||
if (pdata && pdata->rtc)
|
||||
pdata->rtc->rtc_wakeup = 1;
|
||||
}
|
||||
|
||||
ret = device_gpadc_init(chip, pdata);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "[%s]Failed to init gpadc\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
chip->regmap_irq_chip = &pm800_irq_chip;
|
||||
|
||||
ret = device_irq_init_800(chip);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "[%s]Failed to init pm800 irq\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret =
|
||||
mfd_add_devices(chip->dev, 0, &onkey_devs[0],
|
||||
ARRAY_SIZE(onkey_devs), &onkey_resources[0], 0);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to add onkey subdev\n");
|
||||
goto out_dev;
|
||||
} else
|
||||
dev_info(chip->dev, "[%s]:Added mfd onkey_devs\n", __func__);
|
||||
|
||||
if (pdata && pdata->rtc) {
|
||||
rtc_devs[0].platform_data = pdata->rtc;
|
||||
rtc_devs[0].pdata_size = sizeof(struct pm80x_rtc_pdata);
|
||||
ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
|
||||
ARRAY_SIZE(rtc_devs), NULL, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to add rtc subdev\n");
|
||||
goto out_dev;
|
||||
} else
|
||||
dev_info(chip->dev,
|
||||
"[%s]:Added mfd rtc_devs\n", __func__);
|
||||
}
|
||||
|
||||
return 0;
|
||||
out_dev:
|
||||
mfd_remove_devices(chip->dev);
|
||||
device_irq_exit_800(chip);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devinit pm800_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret = 0;
|
||||
struct pm80x_chip *chip;
|
||||
struct pm80x_platform_data *pdata = client->dev.platform_data;
|
||||
struct pm80x_subchip *subchip;
|
||||
|
||||
ret = pm80x_init(client, id);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "pm800_init fail\n");
|
||||
goto out_init;
|
||||
}
|
||||
|
||||
chip = i2c_get_clientdata(client);
|
||||
|
||||
/* init subchip for PM800 */
|
||||
subchip =
|
||||
devm_kzalloc(&client->dev, sizeof(struct pm80x_subchip),
|
||||
GFP_KERNEL);
|
||||
if (!subchip) {
|
||||
ret = -ENOMEM;
|
||||
goto err_subchip_alloc;
|
||||
}
|
||||
|
||||
subchip->power_page_addr = pdata->power_page_addr;
|
||||
subchip->gpadc_page_addr = pdata->gpadc_page_addr;
|
||||
chip->subchip = subchip;
|
||||
|
||||
ret = device_800_init(chip, pdata);
|
||||
if (ret) {
|
||||
dev_err(chip->dev, "%s id 0x%x failed!\n", __func__, chip->id);
|
||||
goto err_800_init;
|
||||
}
|
||||
|
||||
ret = pm800_pages_init(chip);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "pm800_pages_init failed!\n");
|
||||
goto err_page_init;
|
||||
}
|
||||
|
||||
if (pdata->plat_config)
|
||||
pdata->plat_config(chip, pdata);
|
||||
|
||||
err_page_init:
|
||||
mfd_remove_devices(chip->dev);
|
||||
device_irq_exit_800(chip);
|
||||
err_800_init:
|
||||
devm_kfree(&client->dev, subchip);
|
||||
err_subchip_alloc:
|
||||
pm80x_deinit(client);
|
||||
out_init:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit pm800_remove(struct i2c_client *client)
|
||||
{
|
||||
struct pm80x_chip *chip = i2c_get_clientdata(client);
|
||||
|
||||
mfd_remove_devices(chip->dev);
|
||||
device_irq_exit_800(chip);
|
||||
|
||||
pm800_pages_exit(chip);
|
||||
devm_kfree(&client->dev, chip->subchip);
|
||||
|
||||
pm80x_deinit(client);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_driver pm800_driver = {
|
||||
.driver = {
|
||||
.name = "88PM80X",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &pm80x_pm_ops,
|
||||
},
|
||||
.probe = pm800_probe,
|
||||
.remove = __devexit_p(pm800_remove),
|
||||
.id_table = pm80x_id_table,
|
||||
};
|
||||
|
||||
static int __init pm800_i2c_init(void)
|
||||
{
|
||||
return i2c_add_driver(&pm800_driver);
|
||||
}
|
||||
subsys_initcall(pm800_i2c_init);
|
||||
|
||||
static void __exit pm800_i2c_exit(void)
|
||||
{
|
||||
i2c_del_driver(&pm800_driver);
|
||||
}
|
||||
module_exit(pm800_i2c_exit);
|
||||
|
||||
MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM800");
|
||||
MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,301 @@
|
|||
/*
|
||||
* Base driver for Marvell 88PM805
|
||||
*
|
||||
* Copyright (C) 2012 Marvell International Ltd.
|
||||
* Haojian Zhuang <haojian.zhuang@marvell.com>
|
||||
* Joseph(Yossi) Hanin <yhanin@marvell.com>
|
||||
* Qiao Zhou <zhouqiao@marvell.com>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General
|
||||
* Public License. See the file "COPYING" in the main directory of this
|
||||
* archive for more details.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/88pm80x.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define PM805_CHIP_ID (0x00)
|
||||
|
||||
static const struct i2c_device_id pm80x_id_table[] = {
|
||||
{"88PM805", CHIP_PM805},
|
||||
{} /* NULL terminated */
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pm80x_id_table);
|
||||
|
||||
/* Interrupt Number in 88PM805 */
|
||||
enum {
|
||||
PM805_IRQ_LDO_OFF, /*0 */
|
||||
PM805_IRQ_SRC_DPLL_LOCK, /*1 */
|
||||
PM805_IRQ_CLIP_FAULT,
|
||||
PM805_IRQ_MIC_CONFLICT,
|
||||
PM805_IRQ_HP2_SHRT,
|
||||
PM805_IRQ_HP1_SHRT, /*5 */
|
||||
PM805_IRQ_FINE_PLL_FAULT,
|
||||
PM805_IRQ_RAW_PLL_FAULT,
|
||||
PM805_IRQ_VOLP_BTN_DET,
|
||||
PM805_IRQ_VOLM_BTN_DET,
|
||||
PM805_IRQ_SHRT_BTN_DET, /*10 */
|
||||
PM805_IRQ_MIC_DET, /*11 */
|
||||
|
||||
PM805_MAX_IRQ,
|
||||
};
|
||||
|
||||
static struct resource codec_resources[] = {
|
||||
{
|
||||
/* Headset microphone insertion or removal */
|
||||
.name = "micin",
|
||||
.start = PM805_IRQ_MIC_DET,
|
||||
.end = PM805_IRQ_MIC_DET,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
{
|
||||
/* Audio short HP1 */
|
||||
.name = "audio-short1",
|
||||
.start = PM805_IRQ_HP1_SHRT,
|
||||
.end = PM805_IRQ_HP1_SHRT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
{
|
||||
/* Audio short HP2 */
|
||||
.name = "audio-short2",
|
||||
.start = PM805_IRQ_HP2_SHRT,
|
||||
.end = PM805_IRQ_HP2_SHRT,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell codec_devs[] = {
|
||||
{
|
||||
.name = "88pm80x-codec",
|
||||
.num_resources = ARRAY_SIZE(codec_resources),
|
||||
.resources = &codec_resources[0],
|
||||
.id = -1,
|
||||
},
|
||||
};
|
||||
|
||||
static struct regmap_irq pm805_irqs[] = {
|
||||
/* INT0 */
|
||||
[PM805_IRQ_LDO_OFF] = {
|
||||
.mask = PM805_INT1_HP1_SHRT,
|
||||
},
|
||||
[PM805_IRQ_SRC_DPLL_LOCK] = {
|
||||
.mask = PM805_INT1_HP2_SHRT,
|
||||
},
|
||||
[PM805_IRQ_CLIP_FAULT] = {
|
||||
.mask = PM805_INT1_MIC_CONFLICT,
|
||||
},
|
||||
[PM805_IRQ_MIC_CONFLICT] = {
|
||||
.mask = PM805_INT1_CLIP_FAULT,
|
||||
},
|
||||
[PM805_IRQ_HP2_SHRT] = {
|
||||
.mask = PM805_INT1_LDO_OFF,
|
||||
},
|
||||
[PM805_IRQ_HP1_SHRT] = {
|
||||
.mask = PM805_INT1_SRC_DPLL_LOCK,
|
||||
},
|
||||
/* INT1 */
|
||||
[PM805_IRQ_FINE_PLL_FAULT] = {
|
||||
.reg_offset = 1,
|
||||
.mask = PM805_INT2_MIC_DET,
|
||||
},
|
||||
[PM805_IRQ_RAW_PLL_FAULT] = {
|
||||
.reg_offset = 1,
|
||||
.mask = PM805_INT2_SHRT_BTN_DET,
|
||||
},
|
||||
[PM805_IRQ_VOLP_BTN_DET] = {
|
||||
.reg_offset = 1,
|
||||
.mask = PM805_INT2_VOLM_BTN_DET,
|
||||
},
|
||||
[PM805_IRQ_VOLM_BTN_DET] = {
|
||||
.reg_offset = 1,
|
||||
.mask = PM805_INT2_VOLP_BTN_DET,
|
||||
},
|
||||
[PM805_IRQ_SHRT_BTN_DET] = {
|
||||
.reg_offset = 1,
|
||||
.mask = PM805_INT2_RAW_PLL_FAULT,
|
||||
},
|
||||
[PM805_IRQ_MIC_DET] = {
|
||||
.reg_offset = 1,
|
||||
.mask = PM805_INT2_FINE_PLL_FAULT,
|
||||
},
|
||||
};
|
||||
|
||||
static int __devinit device_irq_init_805(struct pm80x_chip *chip)
|
||||
{
|
||||
struct regmap *map = chip->regmap;
|
||||
unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
|
||||
int data, mask, ret = -EINVAL;
|
||||
|
||||
if (!map || !chip->irq) {
|
||||
dev_err(chip->dev, "incorrect parameters\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* irq_mode defines the way of clearing interrupt. it's read-clear by
|
||||
* default.
|
||||
*/
|
||||
mask =
|
||||
PM805_STATUS0_INT_CLEAR | PM805_STATUS0_INV_INT |
|
||||
PM800_STATUS0_INT_MASK;
|
||||
|
||||
data = PM805_STATUS0_INT_CLEAR;
|
||||
ret = regmap_update_bits(map, PM805_INT_STATUS0, mask, data);
|
||||
/*
|
||||
* PM805_INT_STATUS is under 32K clock domain, so need to
|
||||
* add proper delay before the next I2C register access.
|
||||
*/
|
||||
msleep(1);
|
||||
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret =
|
||||
regmap_add_irq_chip(chip->regmap, chip->irq, flags, -1,
|
||||
chip->regmap_irq_chip, &chip->irq_data);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void device_irq_exit_805(struct pm80x_chip *chip)
|
||||
{
|
||||
regmap_del_irq_chip(chip->irq, chip->irq_data);
|
||||
}
|
||||
|
||||
static struct regmap_irq_chip pm805_irq_chip = {
|
||||
.name = "88pm805",
|
||||
.irqs = pm805_irqs,
|
||||
.num_irqs = ARRAY_SIZE(pm805_irqs),
|
||||
|
||||
.num_regs = 2,
|
||||
.status_base = PM805_INT_STATUS1,
|
||||
.mask_base = PM805_INT_MASK1,
|
||||
.ack_base = PM805_INT_STATUS1,
|
||||
};
|
||||
|
||||
static int __devinit device_805_init(struct pm80x_chip *chip)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int val;
|
||||
struct regmap *map = chip->regmap;
|
||||
|
||||
if (!map) {
|
||||
dev_err(chip->dev, "regmap is invalid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = regmap_read(map, PM805_CHIP_ID, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
|
||||
goto out_irq_init;
|
||||
}
|
||||
chip->version = val;
|
||||
|
||||
chip->regmap_irq_chip = &pm805_irq_chip;
|
||||
|
||||
ret = device_irq_init_805(chip);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to init pm805 irq!\n");
|
||||
goto out_irq_init;
|
||||
}
|
||||
|
||||
ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
|
||||
ARRAY_SIZE(codec_devs), &codec_resources[0], 0);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to add codec subdev\n");
|
||||
goto out_codec;
|
||||
} else
|
||||
dev_info(chip->dev, "[%s]:Added mfd codec_devs\n", __func__);
|
||||
|
||||
return 0;
|
||||
|
||||
out_codec:
|
||||
device_irq_exit_805(chip);
|
||||
out_irq_init:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devinit pm805_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret = 0;
|
||||
struct pm80x_chip *chip;
|
||||
struct pm80x_platform_data *pdata = client->dev.platform_data;
|
||||
|
||||
ret = pm80x_init(client, id);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "pm805_init fail!\n");
|
||||
goto out_init;
|
||||
}
|
||||
|
||||
chip = i2c_get_clientdata(client);
|
||||
|
||||
ret = device_805_init(chip);
|
||||
if (ret) {
|
||||
dev_err(chip->dev, "%s id 0x%x failed!\n", __func__, chip->id);
|
||||
goto err_805_init;
|
||||
}
|
||||
|
||||
if (pdata->plat_config)
|
||||
pdata->plat_config(chip, pdata);
|
||||
|
||||
err_805_init:
|
||||
pm80x_deinit(client);
|
||||
out_init:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit pm805_remove(struct i2c_client *client)
|
||||
{
|
||||
struct pm80x_chip *chip = i2c_get_clientdata(client);
|
||||
|
||||
mfd_remove_devices(chip->dev);
|
||||
device_irq_exit_805(chip);
|
||||
|
||||
pm80x_deinit(client);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_driver pm805_driver = {
|
||||
.driver = {
|
||||
.name = "88PM80X",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &pm80x_pm_ops,
|
||||
},
|
||||
.probe = pm805_probe,
|
||||
.remove = __devexit_p(pm805_remove),
|
||||
.id_table = pm80x_id_table,
|
||||
};
|
||||
|
||||
static int __init pm805_i2c_init(void)
|
||||
{
|
||||
return i2c_add_driver(&pm805_driver);
|
||||
}
|
||||
subsys_initcall(pm805_i2c_init);
|
||||
|
||||
static void __exit pm805_i2c_exit(void)
|
||||
{
|
||||
i2c_del_driver(&pm805_driver);
|
||||
}
|
||||
module_exit(pm805_i2c_exit);
|
||||
|
||||
MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM805");
|
||||
MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* I2C driver for Marvell 88PM80x
|
||||
*
|
||||
* Copyright (C) 2012 Marvell International Ltd.
|
||||
* Haojian Zhuang <haojian.zhuang@marvell.com>
|
||||
* Joseph(Yossi) Hanin <yhanin@marvell.com>
|
||||
* Qiao Zhou <zhouqiao@marvell.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/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mfd/88pm80x.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
/*
|
||||
* workaround: some registers needed by pm805 are defined in pm800, so
|
||||
* need to use this global variable to maintain the relation between
|
||||
* pm800 and pm805. would remove it after HW chip fixes the issue.
|
||||
*/
|
||||
static struct pm80x_chip *g_pm80x_chip;
|
||||
|
||||
const struct regmap_config pm80x_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(pm80x_regmap_config);
|
||||
|
||||
int __devinit pm80x_init(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct pm80x_chip *chip;
|
||||
struct regmap *map;
|
||||
int ret = 0;
|
||||
|
||||
chip =
|
||||
devm_kzalloc(&client->dev, sizeof(struct pm80x_chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
map = devm_regmap_init_i2c(client, &pm80x_regmap_config);
|
||||
if (IS_ERR(map)) {
|
||||
ret = PTR_ERR(map);
|
||||
dev_err(&client->dev, "Failed to allocate register map: %d\n",
|
||||
ret);
|
||||
goto err_regmap_init;
|
||||
}
|
||||
|
||||
chip->id = id->driver_data;
|
||||
if (chip->id < CHIP_PM800 || chip->id > CHIP_PM805) {
|
||||
ret = -EINVAL;
|
||||
goto err_chip_id;
|
||||
}
|
||||
|
||||
chip->client = client;
|
||||
chip->regmap = map;
|
||||
|
||||
chip->irq = client->irq;
|
||||
|
||||
chip->dev = &client->dev;
|
||||
dev_set_drvdata(chip->dev, chip);
|
||||
i2c_set_clientdata(chip->client, chip);
|
||||
|
||||
device_init_wakeup(&client->dev, 1);
|
||||
|
||||
/*
|
||||
* workaround: set g_pm80x_chip to the first probed chip. if the
|
||||
* second chip is probed, just point to the companion to each
|
||||
* other so that pm805 can access those specific register. would
|
||||
* remove it after HW chip fixes the issue.
|
||||
*/
|
||||
if (!g_pm80x_chip)
|
||||
g_pm80x_chip = chip;
|
||||
else {
|
||||
chip->companion = g_pm80x_chip->client;
|
||||
g_pm80x_chip->companion = chip->client;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_chip_id:
|
||||
regmap_exit(map);
|
||||
err_regmap_init:
|
||||
devm_kfree(&client->dev, chip);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm80x_init);
|
||||
|
||||
int pm80x_deinit(struct i2c_client *client)
|
||||
{
|
||||
struct pm80x_chip *chip = i2c_get_clientdata(client);
|
||||
|
||||
/*
|
||||
* workaround: clear the dependency between pm800 and pm805.
|
||||
* would remove it after HW chip fixes the issue.
|
||||
*/
|
||||
if (g_pm80x_chip->companion)
|
||||
g_pm80x_chip->companion = NULL;
|
||||
else
|
||||
g_pm80x_chip = NULL;
|
||||
|
||||
regmap_exit(chip->regmap);
|
||||
devm_kfree(&client->dev, chip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm80x_deinit);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int pm80x_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
|
||||
struct pm80x_chip *chip = i2c_get_clientdata(client);
|
||||
|
||||
if (chip && chip->wu_flag)
|
||||
if (device_may_wakeup(chip->dev))
|
||||
enable_irq_wake(chip->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm80x_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
|
||||
struct pm80x_chip *chip = i2c_get_clientdata(client);
|
||||
|
||||
if (chip && chip->wu_flag)
|
||||
if (device_may_wakeup(chip->dev))
|
||||
disable_irq_wake(chip->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
SIMPLE_DEV_PM_OPS(pm80x_pm_ops, pm80x_suspend, pm80x_resume);
|
||||
EXPORT_SYMBOL_GPL(pm80x_pm_ops);
|
||||
|
||||
MODULE_DESCRIPTION("I2C Driver for Marvell 88PM80x");
|
||||
MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -90,6 +90,10 @@ static struct resource charger_resources[] __devinitdata = {
|
|||
{PM8607_IRQ_VCHG, PM8607_IRQ_VCHG, "vchg voltage", IORESOURCE_IRQ,},
|
||||
};
|
||||
|
||||
static struct resource preg_resources[] __devinitdata = {
|
||||
{PM8606_ID_PREG, PM8606_ID_PREG, "preg", IORESOURCE_IO,},
|
||||
};
|
||||
|
||||
static struct resource rtc_resources[] __devinitdata = {
|
||||
{PM8607_IRQ_RTC, PM8607_IRQ_RTC, "rtc", IORESOURCE_IRQ,},
|
||||
};
|
||||
|
@ -142,9 +146,19 @@ static struct mfd_cell codec_devs[] = {
|
|||
{"88pm860x-codec", -1,},
|
||||
};
|
||||
|
||||
static struct regulator_consumer_supply preg_supply[] = {
|
||||
REGULATOR_SUPPLY("preg", "charger-manager"),
|
||||
};
|
||||
|
||||
static struct regulator_init_data preg_init_data = {
|
||||
.num_consumer_supplies = ARRAY_SIZE(preg_supply),
|
||||
.consumer_supplies = &preg_supply[0],
|
||||
};
|
||||
|
||||
static struct mfd_cell power_devs[] = {
|
||||
{"88pm860x-battery", -1,},
|
||||
{"88pm860x-charger", -1,},
|
||||
{"88pm860x-preg", -1,},
|
||||
};
|
||||
|
||||
static struct mfd_cell rtc_devs[] = {
|
||||
|
@ -768,6 +782,15 @@ static void __devinit device_power_init(struct pm860x_chip *chip,
|
|||
&charger_resources[0], chip->irq_base);
|
||||
if (ret < 0)
|
||||
dev_err(chip->dev, "Failed to add charger subdev\n");
|
||||
|
||||
power_devs[2].platform_data = &preg_init_data;
|
||||
power_devs[2].pdata_size = sizeof(struct regulator_init_data);
|
||||
power_devs[2].num_resources = ARRAY_SIZE(preg_resources);
|
||||
power_devs[2].resources = &preg_resources[0],
|
||||
ret = mfd_add_devices(chip->dev, 0, &power_devs[2], 1,
|
||||
&preg_resources[0], chip->irq_base);
|
||||
if (ret < 0)
|
||||
dev_err(chip->dev, "Failed to add preg subdev\n");
|
||||
}
|
||||
|
||||
static void __devinit device_onkey_init(struct pm860x_chip *chip,
|
||||
|
|
|
@ -7,6 +7,7 @@ menu "Multifunction device drivers"
|
|||
|
||||
config MFD_CORE
|
||||
tristate
|
||||
select IRQ_DOMAIN
|
||||
default n
|
||||
|
||||
config MFD_88PM860X
|
||||
|
@ -20,6 +21,30 @@ config MFD_88PM860X
|
|||
select individual components like voltage regulators, RTC and
|
||||
battery-charger under the corresponding menus.
|
||||
|
||||
config MFD_88PM800
|
||||
tristate "Support Marvell 88PM800"
|
||||
depends on I2C=y && GENERIC_HARDIRQS
|
||||
select REGMAP_I2C
|
||||
select REGMAP_IRQ
|
||||
select MFD_CORE
|
||||
help
|
||||
This supports for Marvell 88PM800 Power Management IC.
|
||||
This includes the I2C driver and the core APIs _only_, you have to
|
||||
select individual components like voltage regulators, RTC and
|
||||
battery-charger under the corresponding menus.
|
||||
|
||||
config MFD_88PM805
|
||||
tristate "Support Marvell 88PM805"
|
||||
depends on I2C=y && GENERIC_HARDIRQS
|
||||
select REGMAP_I2C
|
||||
select REGMAP_IRQ
|
||||
select MFD_CORE
|
||||
help
|
||||
This supports for Marvell 88PM805 Power Management IC. This includes
|
||||
the I2C driver and the core APIs _only_, you have to select individual
|
||||
components like codec device, headset/Mic device under the
|
||||
corresponding menus.
|
||||
|
||||
config MFD_SM501
|
||||
tristate "Support for Silicon Motion SM501"
|
||||
---help---
|
||||
|
@ -173,8 +198,9 @@ config MFD_TPS65217
|
|||
|
||||
config MFD_TPS6586X
|
||||
bool "TPS6586x Power Management chips"
|
||||
depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS
|
||||
depends on I2C=y && GENERIC_HARDIRQS
|
||||
select MFD_CORE
|
||||
select REGMAP_I2C
|
||||
depends on REGULATOR
|
||||
help
|
||||
If you say yes here you get support for the TPS6586X series of
|
||||
|
@ -424,6 +450,19 @@ config PMIC_ADP5520
|
|||
individual components like LCD backlight, LEDs, GPIOs and Kepad
|
||||
under the corresponding menus.
|
||||
|
||||
config MFD_MAX77686
|
||||
bool "Maxim Semiconductor MAX77686 PMIC Support"
|
||||
depends on I2C=y && GENERIC_HARDIRQS
|
||||
select MFD_CORE
|
||||
select REGMAP_I2C
|
||||
select IRQ_DOMAIN
|
||||
help
|
||||
Say yes here to support for Maxim Semiconductor MAX77686.
|
||||
This is a Power Management IC with RTC on chip.
|
||||
This driver provides common support for accessing the device;
|
||||
additional drivers must be enabled in order to use the functionality
|
||||
of the device.
|
||||
|
||||
config MFD_MAX77693
|
||||
bool "Maxim Semiconductor MAX77693 PMIC Support"
|
||||
depends on I2C=y && GENERIC_HARDIRQS
|
||||
|
@ -451,6 +490,7 @@ config MFD_MAX8997
|
|||
bool "Maxim Semiconductor MAX8997/8966 PMIC Support"
|
||||
depends on I2C=y && GENERIC_HARDIRQS
|
||||
select MFD_CORE
|
||||
select IRQ_DOMAIN
|
||||
help
|
||||
Say yes here to support for Maxim Semiconductor MAX8997/8966.
|
||||
This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic,
|
||||
|
@ -470,17 +510,56 @@ config MFD_MAX8998
|
|||
additional drivers must be enabled in order to use the functionality
|
||||
of the device.
|
||||
|
||||
config MFD_S5M_CORE
|
||||
bool "SAMSUNG S5M Series Support"
|
||||
config MFD_SEC_CORE
|
||||
bool "SAMSUNG Electronics PMIC Series Support"
|
||||
depends on I2C=y && GENERIC_HARDIRQS
|
||||
select MFD_CORE
|
||||
select REGMAP_I2C
|
||||
select REGMAP_IRQ
|
||||
help
|
||||
Support for the Samsung Electronics S5M MFD series.
|
||||
Support for the Samsung Electronics MFD series.
|
||||
This driver provides common support for accessing the device,
|
||||
additional drivers must be enabled in order to use the functionality
|
||||
of the device
|
||||
|
||||
config MFD_ARIZONA
|
||||
select REGMAP
|
||||
select REGMAP_IRQ
|
||||
select MFD_CORE
|
||||
bool
|
||||
|
||||
config MFD_ARIZONA_I2C
|
||||
tristate "Support Wolfson Microelectronics Arizona platform with I2C"
|
||||
select MFD_ARIZONA
|
||||
select MFD_CORE
|
||||
select REGMAP_I2C
|
||||
depends on I2C
|
||||
help
|
||||
Support for the Wolfson Microelectronics Arizona platform audio SoC
|
||||
core functionality controlled via I2C.
|
||||
|
||||
config MFD_ARIZONA_SPI
|
||||
tristate "Support Wolfson Microelectronics Arizona platform with SPI"
|
||||
select MFD_ARIZONA
|
||||
select MFD_CORE
|
||||
select REGMAP_SPI
|
||||
depends on SPI_MASTER
|
||||
help
|
||||
Support for the Wolfson Microelectronics Arizona platform audio SoC
|
||||
core functionality controlled via I2C.
|
||||
|
||||
config MFD_WM5102
|
||||
bool "Support Wolfson Microelectronics WM5102"
|
||||
depends on MFD_ARIZONA
|
||||
help
|
||||
Support for Wolfson Microelectronics WM5102 low power audio SoC
|
||||
|
||||
config MFD_WM5110
|
||||
bool "Support Wolfson Microelectronics WM5110"
|
||||
depends on MFD_ARIZONA
|
||||
help
|
||||
Support for Wolfson Microelectronics WM5110 low power audio SoC
|
||||
|
||||
config MFD_WM8400
|
||||
bool "Support Wolfson Microelectronics WM8400"
|
||||
select MFD_CORE
|
||||
|
@ -698,6 +777,7 @@ config AB8500_CORE
|
|||
bool "ST-Ericsson AB8500 Mixed Signal Power Management chip"
|
||||
depends on GENERIC_HARDIRQS && ABX500_CORE && MFD_DB8500_PRCMU
|
||||
select MFD_CORE
|
||||
select IRQ_DOMAIN
|
||||
help
|
||||
Select this option to enable access to AB8500 power management
|
||||
chip. This connects to U8500 either on the SSP/SPI bus (deprecated
|
||||
|
@ -705,16 +785,6 @@ config AB8500_CORE
|
|||
the irq_chip parts for handling the Mixed Signal chip events.
|
||||
This chip embeds various other multimedia funtionalities as well.
|
||||
|
||||
config AB8500_I2C_CORE
|
||||
bool "AB8500 register access via PRCMU I2C"
|
||||
depends on AB8500_CORE && MFD_DB8500_PRCMU
|
||||
default y
|
||||
help
|
||||
This enables register access to the AB8500 chip via PRCMU I2C.
|
||||
The AB8500 chip can be accessed via SPI or I2C. On DB8500 hardware
|
||||
the I2C bus is connected to the Power Reset
|
||||
and Mangagement Unit, PRCMU.
|
||||
|
||||
config AB8500_DEBUG
|
||||
bool "Enable debug info via debugfs"
|
||||
depends on AB8500_CORE && DEBUG_FS
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
88pm860x-objs := 88pm860x-core.o 88pm860x-i2c.o
|
||||
obj-$(CONFIG_MFD_88PM860X) += 88pm860x.o
|
||||
obj-$(CONFIG_MFD_88PM800) += 88pm800.o 88pm80x.o
|
||||
obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o
|
||||
obj-$(CONFIG_MFD_SM501) += sm501.o
|
||||
obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o
|
||||
|
||||
|
@ -24,6 +26,16 @@ obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o
|
|||
obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o
|
||||
obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o tmio_core.o
|
||||
|
||||
obj-$(CONFIG_MFD_ARIZONA) += arizona-core.o
|
||||
obj-$(CONFIG_MFD_ARIZONA) += arizona-irq.o
|
||||
obj-$(CONFIG_MFD_ARIZONA_I2C) += arizona-i2c.o
|
||||
obj-$(CONFIG_MFD_ARIZONA_SPI) += arizona-spi.o
|
||||
ifneq ($(CONFIG_MFD_WM5102),n)
|
||||
obj-$(CONFIG_MFD_ARIZONA) += wm5102-tables.o
|
||||
endif
|
||||
ifneq ($(CONFIG_MFD_WM5110),n)
|
||||
obj-$(CONFIG_MFD_ARIZONA) += wm5110-tables.o
|
||||
endif
|
||||
obj-$(CONFIG_MFD_WM8400) += wm8400-core.o
|
||||
wm831x-objs := wm831x-core.o wm831x-irq.o wm831x-otp.o
|
||||
wm831x-objs += wm831x-auxadc.o
|
||||
|
@ -78,6 +90,7 @@ obj-$(CONFIG_PMIC_DA9052) += da9052-core.o
|
|||
obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o
|
||||
obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o
|
||||
|
||||
obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o
|
||||
obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o
|
||||
max8925-objs := max8925-core.o max8925-i2c.o
|
||||
obj-$(CONFIG_MFD_MAX8925) += max8925.o
|
||||
|
@ -116,6 +129,6 @@ obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
|
|||
obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
|
||||
obj-$(CONFIG_MFD_PALMAS) += palmas.o
|
||||
obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
|
||||
obj-$(CONFIG_MFD_S5M_CORE) += s5m-core.o s5m-irq.o
|
||||
obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o
|
||||
obj-$(CONFIG_MFD_ANATOP) += anatop-mfd.o
|
||||
obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o
|
||||
|
|
|
@ -867,7 +867,7 @@ static int __devinit ab3100_probe(struct i2c_client *client,
|
|||
int err;
|
||||
int i;
|
||||
|
||||
ab3100 = kzalloc(sizeof(struct ab3100), GFP_KERNEL);
|
||||
ab3100 = devm_kzalloc(&client->dev, sizeof(struct ab3100), GFP_KERNEL);
|
||||
if (!ab3100) {
|
||||
dev_err(&client->dev, "could not allocate AB3100 device\n");
|
||||
return -ENOMEM;
|
||||
|
@ -921,7 +921,7 @@ static int __devinit ab3100_probe(struct i2c_client *client,
|
|||
|
||||
/* Attach a second dummy i2c_client to the test register address */
|
||||
ab3100->testreg_client = i2c_new_dummy(client->adapter,
|
||||
client->addr + 1);
|
||||
client->addr + 1);
|
||||
if (!ab3100->testreg_client) {
|
||||
err = -ENOMEM;
|
||||
goto exit_no_testreg_client;
|
||||
|
@ -931,13 +931,13 @@ static int __devinit ab3100_probe(struct i2c_client *client,
|
|||
if (err)
|
||||
goto exit_no_setup;
|
||||
|
||||
err = request_threaded_irq(client->irq, NULL, ab3100_irq_handler,
|
||||
IRQF_ONESHOT, "ab3100-core", ab3100);
|
||||
/* This real unpredictable IRQ is of course sampled for entropy */
|
||||
rand_initialize_irq(client->irq);
|
||||
|
||||
err = devm_request_threaded_irq(&client->dev,
|
||||
client->irq, NULL, ab3100_irq_handler,
|
||||
IRQF_ONESHOT, "ab3100-core", ab3100);
|
||||
if (err)
|
||||
goto exit_no_irq;
|
||||
/* This real unpredictable IRQ is of course sampled for entropy */
|
||||
rand_initialize_irq(client->irq);
|
||||
|
||||
err = abx500_register_ops(&client->dev, &ab3100_ops);
|
||||
if (err)
|
||||
|
@ -962,7 +962,6 @@ static int __devinit ab3100_probe(struct i2c_client *client,
|
|||
i2c_unregister_device(ab3100->testreg_client);
|
||||
exit_no_testreg_client:
|
||||
exit_no_detect:
|
||||
kfree(ab3100);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -972,16 +971,8 @@ static int __devexit ab3100_remove(struct i2c_client *client)
|
|||
|
||||
/* Unregister subdevices */
|
||||
mfd_remove_devices(&client->dev);
|
||||
|
||||
ab3100_remove_debugfs();
|
||||
i2c_unregister_device(ab3100->testreg_client);
|
||||
|
||||
/*
|
||||
* At this point, all subscribers should have unregistered
|
||||
* their notifiers so deactivate IRQ
|
||||
*/
|
||||
free_irq(client->irq, ab3100);
|
||||
kfree(ab3100);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -140,7 +141,7 @@ static const char ab8500_version_str[][7] = {
|
|||
[AB8500_VERSION_AB8540] = "AB8540",
|
||||
};
|
||||
|
||||
static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data)
|
||||
static int ab8500_prcmu_write(struct ab8500 *ab8500, u16 addr, u8 data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -150,7 +151,7 @@ static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int ab8500_i2c_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask,
|
||||
static int ab8500_prcmu_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask,
|
||||
u8 data)
|
||||
{
|
||||
int ret;
|
||||
|
@ -162,7 +163,7 @@ static int ab8500_i2c_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int ab8500_i2c_read(struct ab8500 *ab8500, u16 addr)
|
||||
static int ab8500_prcmu_read(struct ab8500 *ab8500, u16 addr)
|
||||
{
|
||||
int ret;
|
||||
u8 data;
|
||||
|
@ -361,7 +362,7 @@ static void ab8500_irq_sync_unlock(struct irq_data *data)
|
|||
static void ab8500_irq_mask(struct irq_data *data)
|
||||
{
|
||||
struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data);
|
||||
int offset = data->irq - ab8500->irq_base;
|
||||
int offset = data->hwirq;
|
||||
int index = offset / 8;
|
||||
int mask = 1 << (offset % 8);
|
||||
|
||||
|
@ -371,7 +372,7 @@ static void ab8500_irq_mask(struct irq_data *data)
|
|||
static void ab8500_irq_unmask(struct irq_data *data)
|
||||
{
|
||||
struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data);
|
||||
int offset = data->irq - ab8500->irq_base;
|
||||
int offset = data->hwirq;
|
||||
int index = offset / 8;
|
||||
int mask = 1 << (offset % 8);
|
||||
|
||||
|
@ -510,38 +511,51 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ab8500_irq_init(struct ab8500 *ab8500)
|
||||
/**
|
||||
* ab8500_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ
|
||||
*
|
||||
* @ab8500: ab8500_irq controller to operate on.
|
||||
* @irq: index of the interrupt requested in the chip IRQs
|
||||
*
|
||||
* Useful for drivers to request their own IRQs.
|
||||
*/
|
||||
int ab8500_irq_get_virq(struct ab8500 *ab8500, int irq)
|
||||
{
|
||||
int base = ab8500->irq_base;
|
||||
int irq;
|
||||
int num_irqs;
|
||||
if (!ab8500)
|
||||
return -EINVAL;
|
||||
|
||||
if (is_ab9540(ab8500))
|
||||
num_irqs = AB9540_NR_IRQS;
|
||||
else if (is_ab8505(ab8500))
|
||||
num_irqs = AB8505_NR_IRQS;
|
||||
else
|
||||
num_irqs = AB8500_NR_IRQS;
|
||||
return irq_create_mapping(ab8500->domain, irq);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ab8500_irq_get_virq);
|
||||
|
||||
for (irq = base; irq < base + num_irqs; irq++) {
|
||||
irq_set_chip_data(irq, ab8500);
|
||||
irq_set_chip_and_handler(irq, &ab8500_irq_chip,
|
||||
handle_simple_irq);
|
||||
irq_set_nested_thread(irq, 1);
|
||||
static int ab8500_irq_map(struct irq_domain *d, unsigned int virq,
|
||||
irq_hw_number_t hwirq)
|
||||
{
|
||||
struct ab8500 *ab8500 = d->host_data;
|
||||
|
||||
if (!ab8500)
|
||||
return -EINVAL;
|
||||
|
||||
irq_set_chip_data(virq, ab8500);
|
||||
irq_set_chip_and_handler(virq, &ab8500_irq_chip,
|
||||
handle_simple_irq);
|
||||
irq_set_nested_thread(virq, 1);
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, IRQF_VALID);
|
||||
set_irq_flags(virq, IRQF_VALID);
|
||||
#else
|
||||
irq_set_noprobe(irq);
|
||||
irq_set_noprobe(virq);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ab8500_irq_remove(struct ab8500 *ab8500)
|
||||
static struct irq_domain_ops ab8500_irq_ops = {
|
||||
.map = ab8500_irq_map,
|
||||
.xlate = irq_domain_xlate_twocell,
|
||||
};
|
||||
|
||||
static int ab8500_irq_init(struct ab8500 *ab8500, struct device_node *np)
|
||||
{
|
||||
int base = ab8500->irq_base;
|
||||
int irq;
|
||||
int num_irqs;
|
||||
|
||||
if (is_ab9540(ab8500))
|
||||
|
@ -551,13 +565,22 @@ static void ab8500_irq_remove(struct ab8500 *ab8500)
|
|||
else
|
||||
num_irqs = AB8500_NR_IRQS;
|
||||
|
||||
for (irq = base; irq < base + num_irqs; irq++) {
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, 0);
|
||||
#endif
|
||||
irq_set_chip_and_handler(irq, NULL, NULL);
|
||||
irq_set_chip_data(irq, NULL);
|
||||
if (ab8500->irq_base) {
|
||||
ab8500->domain = irq_domain_add_legacy(
|
||||
NULL, num_irqs, ab8500->irq_base,
|
||||
0, &ab8500_irq_ops, ab8500);
|
||||
}
|
||||
else {
|
||||
ab8500->domain = irq_domain_add_linear(
|
||||
np, num_irqs, &ab8500_irq_ops, ab8500);
|
||||
}
|
||||
|
||||
if (!ab8500->domain) {
|
||||
dev_err(ab8500->dev, "Failed to create irqdomain\n");
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ab8500_suspend(struct ab8500 *ab8500)
|
||||
|
@ -947,54 +970,69 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = {
|
|||
#ifdef CONFIG_DEBUG_FS
|
||||
{
|
||||
.name = "ab8500-debug",
|
||||
.of_compatible = "stericsson,ab8500-debug",
|
||||
.num_resources = ARRAY_SIZE(ab8500_debug_resources),
|
||||
.resources = ab8500_debug_resources,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.name = "ab8500-sysctrl",
|
||||
.of_compatible = "stericsson,ab8500-sysctrl",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-regulator",
|
||||
.of_compatible = "stericsson,ab8500-regulator",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-gpadc",
|
||||
.of_compatible = "stericsson,ab8500-gpadc",
|
||||
.num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
|
||||
.resources = ab8500_gpadc_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-rtc",
|
||||
.of_compatible = "stericsson,ab8500-rtc",
|
||||
.num_resources = ARRAY_SIZE(ab8500_rtc_resources),
|
||||
.resources = ab8500_rtc_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-acc-det",
|
||||
.of_compatible = "stericsson,ab8500-acc-det",
|
||||
.num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
|
||||
.resources = ab8500_av_acc_detect_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-poweron-key",
|
||||
.of_compatible = "stericsson,ab8500-poweron-key",
|
||||
.num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
|
||||
.resources = ab8500_poweronkey_db_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-pwm",
|
||||
.of_compatible = "stericsson,ab8500-pwm",
|
||||
.id = 1,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-pwm",
|
||||
.of_compatible = "stericsson,ab8500-pwm",
|
||||
.id = 2,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-pwm",
|
||||
.of_compatible = "stericsson,ab8500-pwm",
|
||||
.id = 3,
|
||||
},
|
||||
{ .name = "ab8500-leds", },
|
||||
{
|
||||
.name = "ab8500-leds",
|
||||
.of_compatible = "stericsson,ab8500-leds",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-denc",
|
||||
.of_compatible = "stericsson,ab8500-denc",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-temp",
|
||||
.of_compatible = "stericsson,ab8500-temp",
|
||||
.num_resources = ARRAY_SIZE(ab8500_temp_resources),
|
||||
.resources = ab8500_temp_resources,
|
||||
},
|
||||
|
@ -1026,11 +1064,13 @@ static struct mfd_cell __devinitdata ab8500_bm_devs[] = {
|
|||
static struct mfd_cell __devinitdata ab8500_devs[] = {
|
||||
{
|
||||
.name = "ab8500-gpio",
|
||||
.of_compatible = "stericsson,ab8500-gpio",
|
||||
.num_resources = ARRAY_SIZE(ab8500_gpio_resources),
|
||||
.resources = ab8500_gpio_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-usb",
|
||||
.of_compatible = "stericsson,ab8500-usb",
|
||||
.num_resources = ARRAY_SIZE(ab8500_usb_resources),
|
||||
.resources = ab8500_usb_resources,
|
||||
},
|
||||
|
@ -1207,16 +1247,17 @@ static struct attribute_group ab9540_attr_group = {
|
|||
.attrs = ab9540_sysfs_entries,
|
||||
};
|
||||
|
||||
static const struct of_device_id ab8500_match[] = {
|
||||
{
|
||||
.compatible = "stericsson,ab8500",
|
||||
.data = (void *)AB8500_VERSION_AB8500,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static int __devinit ab8500_probe(struct platform_device *pdev)
|
||||
{
|
||||
static char *switch_off_status[] = {
|
||||
"Swoff bit programming",
|
||||
"Thermal protection activation",
|
||||
"Vbat lower then BattOk falling threshold",
|
||||
"Watchdog expired",
|
||||
"Non presence of 32kHz clock",
|
||||
"Battery level lower than power on reset threshold",
|
||||
"Power on key 1 pressed longer than 10 seconds",
|
||||
"DB8500 thermal shutdown"};
|
||||
struct ab8500_platform_data *plat = dev_get_platdata(&pdev->dev);
|
||||
const struct platform_device_id *platid = platform_get_device_id(pdev);
|
||||
enum ab8500_version version = AB8500_VERSION_UNDEFINED;
|
||||
|
@ -1233,14 +1274,6 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
|
|||
|
||||
if (plat)
|
||||
ab8500->irq_base = plat->irq_base;
|
||||
else if (np)
|
||||
ret = of_property_read_u32(np, "stericsson,irq-base", &ab8500->irq_base);
|
||||
|
||||
if (!ab8500->irq_base) {
|
||||
dev_info(&pdev->dev, "couldn't find irq-base\n");
|
||||
ret = -EINVAL;
|
||||
goto out_free_ab8500;
|
||||
}
|
||||
|
||||
ab8500->dev = &pdev->dev;
|
||||
|
||||
|
@ -1252,9 +1285,9 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
|
|||
|
||||
ab8500->irq = resource->start;
|
||||
|
||||
ab8500->read = ab8500_i2c_read;
|
||||
ab8500->write = ab8500_i2c_write;
|
||||
ab8500->write_masked = ab8500_i2c_write_masked;
|
||||
ab8500->read = ab8500_prcmu_read;
|
||||
ab8500->write = ab8500_prcmu_write;
|
||||
ab8500->write_masked = ab8500_prcmu_write_masked;
|
||||
|
||||
mutex_init(&ab8500->lock);
|
||||
mutex_init(&ab8500->irq_lock);
|
||||
|
@ -1264,9 +1297,6 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
|
|||
|
||||
if (platid)
|
||||
version = platid->driver_data;
|
||||
else if (np)
|
||||
version = (unsigned int)
|
||||
of_match_device(ab8500_match, &pdev->dev)->data;
|
||||
|
||||
if (version != AB8500_VERSION_UNDEFINED)
|
||||
ab8500->version = version;
|
||||
|
@ -1323,7 +1353,20 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
|
|||
AB8500_SWITCH_OFF_STATUS, &value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
dev_info(ab8500->dev, "switch off status: %#x", value);
|
||||
dev_info(ab8500->dev, "switch off cause(s) (%#x): ", value);
|
||||
|
||||
if (value) {
|
||||
for (i = 0; i < ARRAY_SIZE(switch_off_status); i++) {
|
||||
if (value & 1)
|
||||
printk(KERN_CONT " \"%s\"",
|
||||
switch_off_status[i]);
|
||||
value = value >> 1;
|
||||
|
||||
}
|
||||
printk(KERN_CONT "\n");
|
||||
} else {
|
||||
printk(KERN_CONT " None\n");
|
||||
}
|
||||
|
||||
if (plat && plat->init)
|
||||
plat->init(ab8500);
|
||||
|
@ -1352,53 +1395,50 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
|
|||
for (i = 0; i < ab8500->mask_size; i++)
|
||||
ab8500->mask[i] = ab8500->oldmask[i] = 0xff;
|
||||
|
||||
if (ab8500->irq_base) {
|
||||
ret = ab8500_irq_init(ab8500);
|
||||
if (ret)
|
||||
goto out_freeoldmask;
|
||||
ret = ab8500_irq_init(ab8500, np);
|
||||
if (ret)
|
||||
goto out_freeoldmask;
|
||||
|
||||
/* Activate this feature only in ab9540 */
|
||||
/* till tests are done on ab8500 1p2 or later*/
|
||||
if (is_ab9540(ab8500))
|
||||
ret = request_threaded_irq(ab8500->irq, NULL,
|
||||
/* Activate this feature only in ab9540 */
|
||||
/* till tests are done on ab8500 1p2 or later*/
|
||||
if (is_ab9540(ab8500)) {
|
||||
ret = request_threaded_irq(ab8500->irq, NULL,
|
||||
ab8500_hierarchical_irq,
|
||||
IRQF_ONESHOT | IRQF_NO_SUSPEND,
|
||||
"ab8500", ab8500);
|
||||
else
|
||||
ret = request_threaded_irq(ab8500->irq, NULL,
|
||||
}
|
||||
else {
|
||||
ret = request_threaded_irq(ab8500->irq, NULL,
|
||||
ab8500_irq,
|
||||
IRQF_ONESHOT | IRQF_NO_SUSPEND,
|
||||
"ab8500", ab8500);
|
||||
if (ret)
|
||||
goto out_removeirq;
|
||||
goto out_freeoldmask;
|
||||
}
|
||||
|
||||
if (!np) {
|
||||
ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs,
|
||||
ARRAY_SIZE(abx500_common_devs), NULL,
|
||||
ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs,
|
||||
ARRAY_SIZE(abx500_common_devs), NULL,
|
||||
ab8500->irq_base);
|
||||
if (ret)
|
||||
goto out_freeirq;
|
||||
|
||||
if (is_ab9540(ab8500))
|
||||
ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
|
||||
ARRAY_SIZE(ab9540_devs), NULL,
|
||||
ab8500->irq_base);
|
||||
else
|
||||
ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
|
||||
ARRAY_SIZE(ab8500_devs), NULL,
|
||||
ab8500->irq_base);
|
||||
if (ret)
|
||||
goto out_freeirq;
|
||||
|
||||
if (ret)
|
||||
goto out_freeirq;
|
||||
|
||||
if (is_ab9540(ab8500))
|
||||
ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
|
||||
ARRAY_SIZE(ab9540_devs), NULL,
|
||||
ab8500->irq_base);
|
||||
else
|
||||
ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
|
||||
ARRAY_SIZE(ab8500_devs), NULL,
|
||||
ab8500->irq_base);
|
||||
if (ret)
|
||||
goto out_freeirq;
|
||||
|
||||
if (is_ab9540(ab8500) || is_ab8505(ab8500))
|
||||
ret = mfd_add_devices(ab8500->dev, 0, ab9540_ab8505_devs,
|
||||
ARRAY_SIZE(ab9540_ab8505_devs), NULL,
|
||||
ab8500->irq_base);
|
||||
if (ret)
|
||||
goto out_freeirq;
|
||||
}
|
||||
if (is_ab9540(ab8500) || is_ab8505(ab8500))
|
||||
ret = mfd_add_devices(ab8500->dev, 0, ab9540_ab8505_devs,
|
||||
ARRAY_SIZE(ab9540_ab8505_devs), NULL,
|
||||
ab8500->irq_base);
|
||||
if (ret)
|
||||
goto out_freeirq;
|
||||
|
||||
if (!no_bm) {
|
||||
/* Add battery management devices */
|
||||
|
@ -1417,15 +1457,11 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
|
|||
&ab8500_attr_group);
|
||||
if (ret)
|
||||
dev_err(ab8500->dev, "error creating sysfs entries\n");
|
||||
else
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
|
||||
out_freeirq:
|
||||
if (ab8500->irq_base)
|
||||
free_irq(ab8500->irq, ab8500);
|
||||
out_removeirq:
|
||||
if (ab8500->irq_base)
|
||||
ab8500_irq_remove(ab8500);
|
||||
free_irq(ab8500->irq, ab8500);
|
||||
out_freeoldmask:
|
||||
kfree(ab8500->oldmask);
|
||||
out_freemask:
|
||||
|
@ -1444,11 +1480,10 @@ static int __devexit ab8500_remove(struct platform_device *pdev)
|
|||
sysfs_remove_group(&ab8500->dev->kobj, &ab9540_attr_group);
|
||||
else
|
||||
sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group);
|
||||
|
||||
mfd_remove_devices(ab8500->dev);
|
||||
if (ab8500->irq_base) {
|
||||
free_irq(ab8500->irq, ab8500);
|
||||
ab8500_irq_remove(ab8500);
|
||||
}
|
||||
free_irq(ab8500->irq, ab8500);
|
||||
|
||||
kfree(ab8500->oldmask);
|
||||
kfree(ab8500->mask);
|
||||
kfree(ab8500);
|
||||
|
@ -1468,7 +1503,6 @@ static struct platform_driver ab8500_core_driver = {
|
|||
.driver = {
|
||||
.name = "ab8500-core",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = ab8500_match,
|
||||
},
|
||||
.probe = ab8500_probe,
|
||||
.remove = __devexit_p(ab8500_remove),
|
||||
|
@ -1484,7 +1518,7 @@ static void __exit ab8500_core_exit(void)
|
|||
{
|
||||
platform_driver_unregister(&ab8500_core_driver);
|
||||
}
|
||||
arch_initcall(ab8500_core_init);
|
||||
core_initcall(ab8500_core_init);
|
||||
module_exit(ab8500_core_exit);
|
||||
|
||||
MODULE_AUTHOR("Mattias Wallin, Srinidhi Kasagar, Rabin Vincent");
|
||||
|
|
|
@ -31,12 +31,12 @@ struct ab8500_reg_range {
|
|||
};
|
||||
|
||||
/**
|
||||
* struct ab8500_i2c_ranges
|
||||
* struct ab8500_prcmu_ranges
|
||||
* @num_ranges: the number of ranges in the list
|
||||
* @bankid: bank identifier
|
||||
* @range: the list of register ranges
|
||||
*/
|
||||
struct ab8500_i2c_ranges {
|
||||
struct ab8500_prcmu_ranges {
|
||||
u8 num_ranges;
|
||||
u8 bankid;
|
||||
const struct ab8500_reg_range *range;
|
||||
|
@ -47,7 +47,7 @@ struct ab8500_i2c_ranges {
|
|||
|
||||
#define AB8500_REV_REG 0x80
|
||||
|
||||
static struct ab8500_i2c_ranges debug_ranges[AB8500_NUM_BANKS] = {
|
||||
static struct ab8500_prcmu_ranges debug_ranges[AB8500_NUM_BANKS] = {
|
||||
[0x0] = {
|
||||
.num_ranges = 0,
|
||||
.range = 0,
|
||||
|
@ -608,16 +608,10 @@ static int __devexit ab8500_debug_remove(struct platform_device *plf)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ab8500_debug_match[] = {
|
||||
{ .compatible = "stericsson,ab8500-debug", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver ab8500_debug_driver = {
|
||||
.driver = {
|
||||
.name = "ab8500-debug",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = ab8500_debug_match,
|
||||
},
|
||||
.probe = ab8500_debug_probe,
|
||||
.remove = __devexit_p(ab8500_debug_remove)
|
||||
|
|
|
@ -599,7 +599,8 @@ static int __devinit ab8500_gpadc_probe(struct platform_device *pdev)
|
|||
/* Register interrupt - SwAdcComplete */
|
||||
ret = request_threaded_irq(gpadc->irq, NULL,
|
||||
ab8500_bm_gpswadcconvend_handler,
|
||||
IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc", gpadc);
|
||||
IRQF_ONESHOT | IRQF_NO_SUSPEND | IRQF_SHARED,
|
||||
"ab8500-gpadc", gpadc);
|
||||
if (ret < 0) {
|
||||
dev_err(gpadc->dev, "Failed to register interrupt, irq: %d\n",
|
||||
gpadc->irq);
|
||||
|
@ -648,18 +649,12 @@ static int __devexit ab8500_gpadc_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ab8500_gpadc_match[] = {
|
||||
{ .compatible = "stericsson,ab8500-gpadc", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver ab8500_gpadc_driver = {
|
||||
.probe = ab8500_gpadc_probe,
|
||||
.remove = __devexit_p(ab8500_gpadc_remove),
|
||||
.driver = {
|
||||
.name = "ab8500-gpadc",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = ab8500_gpadc_match,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -61,16 +61,10 @@ static int __devexit ab8500_sysctrl_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ab8500_sysctrl_match[] = {
|
||||
{ .compatible = "stericsson,ab8500-sysctrl", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver ab8500_sysctrl_driver = {
|
||||
.driver = {
|
||||
.name = "ab8500-sysctrl",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = ab8500_sysctrl_match,
|
||||
},
|
||||
.probe = ab8500_sysctrl_probe,
|
||||
.remove = __devexit_p(ab8500_sysctrl_remove),
|
||||
|
|
|
@ -320,7 +320,7 @@ static int __devexit adp5520_remove(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int adp5520_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
|
|
@ -83,7 +83,7 @@ static int __devinit of_anatop_probe(struct platform_device *pdev)
|
|||
drvdata->ioreg = ioreg;
|
||||
spin_lock_init(&drvdata->reglock);
|
||||
platform_set_drvdata(pdev, drvdata);
|
||||
of_platform_populate(np, of_anatop_match, NULL, dev);
|
||||
of_platform_populate(np, NULL, NULL, dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,566 @@
|
|||
/*
|
||||
* Arizona core driver
|
||||
*
|
||||
* Copyright 2012 Wolfson Microelectronics plc
|
||||
*
|
||||
* Author: Mark Brown <broonie@opensource.wolfsonmicro.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/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/mfd/arizona/core.h>
|
||||
#include <linux/mfd/arizona/registers.h>
|
||||
|
||||
#include "arizona.h"
|
||||
|
||||
static const char *wm5102_core_supplies[] = {
|
||||
"AVDD",
|
||||
"DBVDD1",
|
||||
};
|
||||
|
||||
int arizona_clk32k_enable(struct arizona *arizona)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&arizona->clk_lock);
|
||||
|
||||
arizona->clk32k_ref++;
|
||||
|
||||
if (arizona->clk32k_ref == 1)
|
||||
ret = regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
|
||||
ARIZONA_CLK_32K_ENA,
|
||||
ARIZONA_CLK_32K_ENA);
|
||||
|
||||
if (ret != 0)
|
||||
arizona->clk32k_ref--;
|
||||
|
||||
mutex_unlock(&arizona->clk_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(arizona_clk32k_enable);
|
||||
|
||||
int arizona_clk32k_disable(struct arizona *arizona)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&arizona->clk_lock);
|
||||
|
||||
BUG_ON(arizona->clk32k_ref <= 0);
|
||||
|
||||
arizona->clk32k_ref--;
|
||||
|
||||
if (arizona->clk32k_ref == 0)
|
||||
regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
|
||||
ARIZONA_CLK_32K_ENA, 0);
|
||||
|
||||
mutex_unlock(&arizona->clk_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(arizona_clk32k_disable);
|
||||
|
||||
static irqreturn_t arizona_clkgen_err(int irq, void *data)
|
||||
{
|
||||
struct arizona *arizona = data;
|
||||
|
||||
dev_err(arizona->dev, "CLKGEN error\n");
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t arizona_underclocked(int irq, void *data)
|
||||
{
|
||||
struct arizona *arizona = data;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_8,
|
||||
&val);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Failed to read underclock status: %d\n",
|
||||
ret);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if (val & ARIZONA_AIF3_UNDERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "AIF3 underclocked\n");
|
||||
if (val & ARIZONA_AIF3_UNDERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "AIF3 underclocked\n");
|
||||
if (val & ARIZONA_AIF2_UNDERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "AIF1 underclocked\n");
|
||||
if (val & ARIZONA_ISRC2_UNDERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "ISRC2 underclocked\n");
|
||||
if (val & ARIZONA_ISRC1_UNDERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "ISRC1 underclocked\n");
|
||||
if (val & ARIZONA_FX_UNDERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "FX underclocked\n");
|
||||
if (val & ARIZONA_ASRC_UNDERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "ASRC underclocked\n");
|
||||
if (val & ARIZONA_DAC_UNDERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "DAC underclocked\n");
|
||||
if (val & ARIZONA_ADC_UNDERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "ADC underclocked\n");
|
||||
if (val & ARIZONA_MIXER_UNDERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "Mixer underclocked\n");
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t arizona_overclocked(int irq, void *data)
|
||||
{
|
||||
struct arizona *arizona = data;
|
||||
unsigned int val[2];
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_6,
|
||||
&val[0], 2);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Failed to read overclock status: %d\n",
|
||||
ret);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if (val[0] & ARIZONA_PWM_OVERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "PWM overclocked\n");
|
||||
if (val[0] & ARIZONA_FX_CORE_OVERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "FX core overclocked\n");
|
||||
if (val[0] & ARIZONA_DAC_SYS_OVERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "DAC SYS overclocked\n");
|
||||
if (val[0] & ARIZONA_DAC_WARP_OVERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "DAC WARP overclocked\n");
|
||||
if (val[0] & ARIZONA_ADC_OVERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "ADC overclocked\n");
|
||||
if (val[0] & ARIZONA_MIXER_OVERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "Mixer overclocked\n");
|
||||
if (val[0] & ARIZONA_AIF3_SYNC_OVERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "AIF3 overclocked\n");
|
||||
if (val[0] & ARIZONA_AIF2_SYNC_OVERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "AIF2 overclocked\n");
|
||||
if (val[0] & ARIZONA_AIF1_SYNC_OVERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "AIF1 overclocked\n");
|
||||
if (val[0] & ARIZONA_PAD_CTRL_OVERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "Pad control overclocked\n");
|
||||
|
||||
if (val[1] & ARIZONA_SLIMBUS_SUBSYS_OVERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "Slimbus subsystem overclocked\n");
|
||||
if (val[1] & ARIZONA_SLIMBUS_ASYNC_OVERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "Slimbus async overclocked\n");
|
||||
if (val[1] & ARIZONA_SLIMBUS_SYNC_OVERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "Slimbus sync overclocked\n");
|
||||
if (val[1] & ARIZONA_ASRC_ASYNC_SYS_OVERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "ASRC async system overclocked\n");
|
||||
if (val[1] & ARIZONA_ASRC_ASYNC_WARP_OVERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "ASRC async WARP overclocked\n");
|
||||
if (val[1] & ARIZONA_ASRC_SYNC_SYS_OVERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "ASRC sync system overclocked\n");
|
||||
if (val[1] & ARIZONA_ASRC_SYNC_WARP_OVERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "ASRC sync WARP overclocked\n");
|
||||
if (val[1] & ARIZONA_ADSP2_1_OVERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "DSP1 overclocked\n");
|
||||
if (val[1] & ARIZONA_ISRC2_OVERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "ISRC2 overclocked\n");
|
||||
if (val[1] & ARIZONA_ISRC1_OVERCLOCKED_STS)
|
||||
dev_err(arizona->dev, "ISRC1 overclocked\n");
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int arizona_wait_for_boot(struct arizona *arizona)
|
||||
{
|
||||
unsigned int reg;
|
||||
int ret, i;
|
||||
|
||||
/*
|
||||
* We can't use an interrupt as we need to runtime resume to do so,
|
||||
* we won't race with the interrupt handler as it'll be blocked on
|
||||
* runtime resume.
|
||||
*/
|
||||
for (i = 0; i < 5; i++) {
|
||||
msleep(1);
|
||||
|
||||
ret = regmap_read(arizona->regmap,
|
||||
ARIZONA_INTERRUPT_RAW_STATUS_5, ®);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Failed to read boot state: %d\n",
|
||||
ret);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reg & ARIZONA_BOOT_DONE_STS)
|
||||
break;
|
||||
}
|
||||
|
||||
if (reg & ARIZONA_BOOT_DONE_STS) {
|
||||
regmap_write(arizona->regmap, ARIZONA_INTERRUPT_STATUS_5,
|
||||
ARIZONA_BOOT_DONE_STS);
|
||||
} else {
|
||||
dev_err(arizona->dev, "Device boot timed out: %x\n", reg);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(arizona->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
static int arizona_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct arizona *arizona = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
dev_dbg(arizona->dev, "Leaving AoD mode\n");
|
||||
|
||||
ret = regulator_enable(arizona->dcvdd);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Failed to enable DCVDD: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
regcache_cache_only(arizona->regmap, false);
|
||||
|
||||
ret = arizona_wait_for_boot(arizona);
|
||||
if (ret != 0) {
|
||||
regulator_disable(arizona->dcvdd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
regcache_sync(arizona->regmap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int arizona_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct arizona *arizona = dev_get_drvdata(dev);
|
||||
|
||||
dev_dbg(arizona->dev, "Entering AoD mode\n");
|
||||
|
||||
regulator_disable(arizona->dcvdd);
|
||||
regcache_cache_only(arizona->regmap, true);
|
||||
regcache_mark_dirty(arizona->regmap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
const struct dev_pm_ops arizona_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(arizona_runtime_suspend,
|
||||
arizona_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(arizona_pm_ops);
|
||||
|
||||
static struct mfd_cell early_devs[] = {
|
||||
{ .name = "arizona-ldo1" },
|
||||
};
|
||||
|
||||
static struct mfd_cell wm5102_devs[] = {
|
||||
{ .name = "arizona-extcon" },
|
||||
{ .name = "arizona-gpio" },
|
||||
{ .name = "arizona-micsupp" },
|
||||
{ .name = "arizona-pwm" },
|
||||
{ .name = "wm5102-codec" },
|
||||
};
|
||||
|
||||
static struct mfd_cell wm5110_devs[] = {
|
||||
{ .name = "arizona-extcon" },
|
||||
{ .name = "arizona-gpio" },
|
||||
{ .name = "arizona-micsupp" },
|
||||
{ .name = "arizona-pwm" },
|
||||
{ .name = "wm5110-codec" },
|
||||
};
|
||||
|
||||
int __devinit arizona_dev_init(struct arizona *arizona)
|
||||
{
|
||||
struct device *dev = arizona->dev;
|
||||
const char *type_name;
|
||||
unsigned int reg, val;
|
||||
int ret, i;
|
||||
|
||||
dev_set_drvdata(arizona->dev, arizona);
|
||||
mutex_init(&arizona->clk_lock);
|
||||
|
||||
if (dev_get_platdata(arizona->dev))
|
||||
memcpy(&arizona->pdata, dev_get_platdata(arizona->dev),
|
||||
sizeof(arizona->pdata));
|
||||
|
||||
regcache_cache_only(arizona->regmap, true);
|
||||
|
||||
switch (arizona->type) {
|
||||
case WM5102:
|
||||
case WM5110:
|
||||
for (i = 0; i < ARRAY_SIZE(wm5102_core_supplies); i++)
|
||||
arizona->core_supplies[i].supply
|
||||
= wm5102_core_supplies[i];
|
||||
arizona->num_core_supplies = ARRAY_SIZE(wm5102_core_supplies);
|
||||
break;
|
||||
default:
|
||||
dev_err(arizona->dev, "Unknown device type %d\n",
|
||||
arizona->type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = mfd_add_devices(arizona->dev, -1, early_devs,
|
||||
ARRAY_SIZE(early_devs), NULL, 0);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to add early children: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_regulator_bulk_get(dev, arizona->num_core_supplies,
|
||||
arizona->core_supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to request core supplies: %d\n",
|
||||
ret);
|
||||
goto err_early;
|
||||
}
|
||||
|
||||
arizona->dcvdd = devm_regulator_get(arizona->dev, "DCVDD");
|
||||
if (IS_ERR(arizona->dcvdd)) {
|
||||
ret = PTR_ERR(arizona->dcvdd);
|
||||
dev_err(dev, "Failed to request DCVDD: %d\n", ret);
|
||||
goto err_early;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(arizona->num_core_supplies,
|
||||
arizona->core_supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to enable core supplies: %d\n",
|
||||
ret);
|
||||
goto err_early;
|
||||
}
|
||||
|
||||
ret = regulator_enable(arizona->dcvdd);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to enable DCVDD: %d\n", ret);
|
||||
goto err_enable;
|
||||
}
|
||||
|
||||
if (arizona->pdata.reset) {
|
||||
/* Start out with /RESET low to put the chip into reset */
|
||||
ret = gpio_request_one(arizona->pdata.reset,
|
||||
GPIOF_DIR_OUT | GPIOF_INIT_LOW,
|
||||
"arizona /RESET");
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to request /RESET: %d\n", ret);
|
||||
goto err_dcvdd;
|
||||
}
|
||||
|
||||
gpio_set_value_cansleep(arizona->pdata.reset, 1);
|
||||
}
|
||||
|
||||
regcache_cache_only(arizona->regmap, false);
|
||||
|
||||
ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, ®);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to read ID register: %d\n", ret);
|
||||
goto err_reset;
|
||||
}
|
||||
|
||||
ret = regmap_read(arizona->regmap, ARIZONA_DEVICE_REVISION,
|
||||
&arizona->rev);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to read revision register: %d\n", ret);
|
||||
goto err_reset;
|
||||
}
|
||||
arizona->rev &= ARIZONA_DEVICE_REVISION_MASK;
|
||||
|
||||
switch (reg) {
|
||||
#ifdef CONFIG_MFD_WM5102
|
||||
case 0x5102:
|
||||
type_name = "WM5102";
|
||||
if (arizona->type != WM5102) {
|
||||
dev_err(arizona->dev, "WM5102 registered as %d\n",
|
||||
arizona->type);
|
||||
arizona->type = WM5102;
|
||||
}
|
||||
ret = wm5102_patch(arizona);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_MFD_WM5110
|
||||
case 0x5110:
|
||||
type_name = "WM5110";
|
||||
if (arizona->type != WM5110) {
|
||||
dev_err(arizona->dev, "WM5110 registered as %d\n",
|
||||
arizona->type);
|
||||
arizona->type = WM5110;
|
||||
}
|
||||
ret = wm5110_patch(arizona);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
dev_err(arizona->dev, "Unknown device ID %x\n", reg);
|
||||
goto err_reset;
|
||||
}
|
||||
|
||||
dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A');
|
||||
|
||||
if (ret != 0)
|
||||
dev_err(arizona->dev, "Failed to apply patch: %d\n", ret);
|
||||
|
||||
/* If we have a /RESET GPIO we'll already be reset */
|
||||
if (!arizona->pdata.reset) {
|
||||
ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0);
|
||||
if (ret != 0) {
|
||||
dev_err(dev, "Failed to reset device: %d\n", ret);
|
||||
goto err_reset;
|
||||
}
|
||||
}
|
||||
|
||||
ret = arizona_wait_for_boot(arizona);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Device failed initial boot: %d\n", ret);
|
||||
goto err_reset;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
|
||||
if (!arizona->pdata.gpio_defaults[i])
|
||||
continue;
|
||||
|
||||
regmap_write(arizona->regmap, ARIZONA_GPIO1_CTRL + i,
|
||||
arizona->pdata.gpio_defaults[i]);
|
||||
}
|
||||
|
||||
pm_runtime_set_autosuspend_delay(arizona->dev, 100);
|
||||
pm_runtime_use_autosuspend(arizona->dev);
|
||||
pm_runtime_enable(arizona->dev);
|
||||
|
||||
/* Chip default */
|
||||
if (!arizona->pdata.clk32k_src)
|
||||
arizona->pdata.clk32k_src = ARIZONA_32KZ_MCLK2;
|
||||
|
||||
switch (arizona->pdata.clk32k_src) {
|
||||
case ARIZONA_32KZ_MCLK1:
|
||||
case ARIZONA_32KZ_MCLK2:
|
||||
regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
|
||||
ARIZONA_CLK_32K_SRC_MASK,
|
||||
arizona->pdata.clk32k_src - 1);
|
||||
break;
|
||||
case ARIZONA_32KZ_NONE:
|
||||
regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1,
|
||||
ARIZONA_CLK_32K_SRC_MASK, 2);
|
||||
break;
|
||||
default:
|
||||
dev_err(arizona->dev, "Invalid 32kHz clock source: %d\n",
|
||||
arizona->pdata.clk32k_src);
|
||||
ret = -EINVAL;
|
||||
goto err_reset;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARIZONA_MAX_INPUT; i++) {
|
||||
/* Default for both is 0 so noop with defaults */
|
||||
val = arizona->pdata.dmic_ref[i]
|
||||
<< ARIZONA_IN1_DMIC_SUP_SHIFT;
|
||||
val |= arizona->pdata.inmode[i] << ARIZONA_IN1_MODE_SHIFT;
|
||||
|
||||
regmap_update_bits(arizona->regmap,
|
||||
ARIZONA_IN1L_CONTROL + (i * 8),
|
||||
ARIZONA_IN1_DMIC_SUP_MASK |
|
||||
ARIZONA_IN1_MODE_MASK, val);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARIZONA_MAX_OUTPUT; i++) {
|
||||
/* Default is 0 so noop with defaults */
|
||||
if (arizona->pdata.out_mono[i])
|
||||
val = ARIZONA_OUT1_MONO;
|
||||
else
|
||||
val = 0;
|
||||
|
||||
regmap_update_bits(arizona->regmap,
|
||||
ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8),
|
||||
ARIZONA_OUT1_MONO, val);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) {
|
||||
if (arizona->pdata.spk_mute[i])
|
||||
regmap_update_bits(arizona->regmap,
|
||||
ARIZONA_PDM_SPK1_CTRL_1 + (i * 2),
|
||||
ARIZONA_SPK1_MUTE_ENDIAN_MASK |
|
||||
ARIZONA_SPK1_MUTE_SEQ1_MASK,
|
||||
arizona->pdata.spk_mute[i]);
|
||||
|
||||
if (arizona->pdata.spk_fmt[i])
|
||||
regmap_update_bits(arizona->regmap,
|
||||
ARIZONA_PDM_SPK1_CTRL_2 + (i * 2),
|
||||
ARIZONA_SPK1_FMT_MASK,
|
||||
arizona->pdata.spk_fmt[i]);
|
||||
}
|
||||
|
||||
/* Set up for interrupts */
|
||||
ret = arizona_irq_init(arizona);
|
||||
if (ret != 0)
|
||||
goto err_reset;
|
||||
|
||||
arizona_request_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, "CLKGEN error",
|
||||
arizona_clkgen_err, arizona);
|
||||
arizona_request_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, "Overclocked",
|
||||
arizona_overclocked, arizona);
|
||||
arizona_request_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, "Underclocked",
|
||||
arizona_underclocked, arizona);
|
||||
|
||||
switch (arizona->type) {
|
||||
case WM5102:
|
||||
ret = mfd_add_devices(arizona->dev, -1, wm5102_devs,
|
||||
ARRAY_SIZE(wm5102_devs), NULL, 0);
|
||||
break;
|
||||
case WM5110:
|
||||
ret = mfd_add_devices(arizona->dev, -1, wm5110_devs,
|
||||
ARRAY_SIZE(wm5102_devs), NULL, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Failed to add subdevices: %d\n", ret);
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
regulator_disable(arizona->dcvdd);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
err_irq:
|
||||
arizona_irq_exit(arizona);
|
||||
err_reset:
|
||||
if (arizona->pdata.reset) {
|
||||
gpio_set_value_cansleep(arizona->pdata.reset, 1);
|
||||
gpio_free(arizona->pdata.reset);
|
||||
}
|
||||
err_dcvdd:
|
||||
regulator_disable(arizona->dcvdd);
|
||||
err_enable:
|
||||
regulator_bulk_disable(arizona->num_core_supplies,
|
||||
arizona->core_supplies);
|
||||
err_early:
|
||||
mfd_remove_devices(dev);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(arizona_dev_init);
|
||||
|
||||
int __devexit arizona_dev_exit(struct arizona *arizona)
|
||||
{
|
||||
mfd_remove_devices(arizona->dev);
|
||||
arizona_free_irq(arizona, ARIZONA_IRQ_UNDERCLOCKED, arizona);
|
||||
arizona_free_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, arizona);
|
||||
arizona_free_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, arizona);
|
||||
pm_runtime_disable(arizona->dev);
|
||||
arizona_irq_exit(arizona);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(arizona_dev_exit);
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Arizona-i2c.c -- Arizona I2C bus interface
|
||||
*
|
||||
* Copyright 2012 Wolfson Microelectronics plc
|
||||
*
|
||||
* Author: Mark Brown <broonie@opensource.wolfsonmicro.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/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/mfd/arizona/core.h>
|
||||
|
||||
#include "arizona.h"
|
||||
|
||||
static __devinit int arizona_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct arizona *arizona;
|
||||
const struct regmap_config *regmap_config;
|
||||
int ret;
|
||||
|
||||
switch (id->driver_data) {
|
||||
#ifdef CONFIG_MFD_WM5102
|
||||
case WM5102:
|
||||
regmap_config = &wm5102_i2c_regmap;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_MFD_WM5110
|
||||
case WM5110:
|
||||
regmap_config = &wm5110_i2c_regmap;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
dev_err(&i2c->dev, "Unknown device type %ld\n",
|
||||
id->driver_data);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
arizona = devm_kzalloc(&i2c->dev, sizeof(*arizona), GFP_KERNEL);
|
||||
if (arizona == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
arizona->regmap = devm_regmap_init_i2c(i2c, regmap_config);
|
||||
if (IS_ERR(arizona->regmap)) {
|
||||
ret = PTR_ERR(arizona->regmap);
|
||||
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
arizona->type = id->driver_data;
|
||||
arizona->dev = &i2c->dev;
|
||||
arizona->irq = i2c->irq;
|
||||
|
||||
return arizona_dev_init(arizona);
|
||||
}
|
||||
|
||||
static int __devexit arizona_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
struct arizona *arizona = dev_get_drvdata(&i2c->dev);
|
||||
arizona_dev_exit(arizona);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id arizona_i2c_id[] = {
|
||||
{ "wm5102", WM5102 },
|
||||
{ "wm5110", WM5110 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, arizona_i2c_id);
|
||||
|
||||
static struct i2c_driver arizona_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "arizona",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &arizona_pm_ops,
|
||||
},
|
||||
.probe = arizona_i2c_probe,
|
||||
.remove = __devexit_p(arizona_i2c_remove),
|
||||
.id_table = arizona_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(arizona_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Arizona I2C bus interface");
|
||||
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,275 @@
|
|||
/*
|
||||
* Arizona interrupt support
|
||||
*
|
||||
* Copyright 2012 Wolfson Microelectronics plc
|
||||
*
|
||||
* Author: Mark Brown <broonie@opensource.wolfsonmicro.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/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/mfd/arizona/core.h>
|
||||
#include <linux/mfd/arizona/registers.h>
|
||||
|
||||
#include "arizona.h"
|
||||
|
||||
static int arizona_map_irq(struct arizona *arizona, int irq)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_irq_get_virq(arizona->aod_irq_chip, irq);
|
||||
if (ret < 0)
|
||||
ret = regmap_irq_get_virq(arizona->irq_chip, irq);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int arizona_request_irq(struct arizona *arizona, int irq, char *name,
|
||||
irq_handler_t handler, void *data)
|
||||
{
|
||||
irq = arizona_map_irq(arizona, irq);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
return request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT,
|
||||
name, data);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(arizona_request_irq);
|
||||
|
||||
void arizona_free_irq(struct arizona *arizona, int irq, void *data)
|
||||
{
|
||||
irq = arizona_map_irq(arizona, irq);
|
||||
if (irq < 0)
|
||||
return;
|
||||
|
||||
free_irq(irq, data);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(arizona_free_irq);
|
||||
|
||||
int arizona_set_irq_wake(struct arizona *arizona, int irq, int on)
|
||||
{
|
||||
irq = arizona_map_irq(arizona, irq);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
return irq_set_irq_wake(irq, on);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(arizona_set_irq_wake);
|
||||
|
||||
static irqreturn_t arizona_boot_done(int irq, void *data)
|
||||
{
|
||||
struct arizona *arizona = data;
|
||||
|
||||
dev_dbg(arizona->dev, "Boot done\n");
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t arizona_ctrlif_err(int irq, void *data)
|
||||
{
|
||||
struct arizona *arizona = data;
|
||||
|
||||
/*
|
||||
* For pretty much all potential sources a register cache sync
|
||||
* won't help, we've just got a software bug somewhere.
|
||||
*/
|
||||
dev_err(arizona->dev, "Control interface error\n");
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t arizona_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct arizona *arizona = data;
|
||||
int i, ret;
|
||||
|
||||
ret = pm_runtime_get_sync(arizona->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(arizona->dev, "Failed to resume device: %d\n", ret);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/* Check both domains */
|
||||
for (i = 0; i < 2; i++)
|
||||
handle_nested_irq(irq_find_mapping(arizona->virq, i));
|
||||
|
||||
pm_runtime_mark_last_busy(arizona->dev);
|
||||
pm_runtime_put_autosuspend(arizona->dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void arizona_irq_enable(struct irq_data *data)
|
||||
{
|
||||
}
|
||||
|
||||
static void arizona_irq_disable(struct irq_data *data)
|
||||
{
|
||||
}
|
||||
|
||||
static struct irq_chip arizona_irq_chip = {
|
||||
.name = "arizona",
|
||||
.irq_disable = arizona_irq_disable,
|
||||
.irq_enable = arizona_irq_enable,
|
||||
};
|
||||
|
||||
static int arizona_irq_map(struct irq_domain *h, unsigned int virq,
|
||||
irq_hw_number_t hw)
|
||||
{
|
||||
struct regmap_irq_chip_data *data = h->host_data;
|
||||
|
||||
irq_set_chip_data(virq, data);
|
||||
irq_set_chip_and_handler(virq, &arizona_irq_chip, handle_edge_irq);
|
||||
irq_set_nested_thread(virq, 1);
|
||||
|
||||
/* ARM needs us to explicitly flag the IRQ as valid
|
||||
* and will set them noprobe when we do so. */
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(virq, IRQF_VALID);
|
||||
#else
|
||||
irq_set_noprobe(virq);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops arizona_domain_ops = {
|
||||
.map = arizona_irq_map,
|
||||
.xlate = irq_domain_xlate_twocell,
|
||||
};
|
||||
|
||||
int arizona_irq_init(struct arizona *arizona)
|
||||
{
|
||||
int flags = IRQF_ONESHOT;
|
||||
int ret, i;
|
||||
const struct regmap_irq_chip *aod, *irq;
|
||||
|
||||
switch (arizona->type) {
|
||||
#ifdef CONFIG_MFD_WM5102
|
||||
case WM5102:
|
||||
aod = &wm5102_aod;
|
||||
irq = &wm5102_irq;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_MFD_WM5110
|
||||
case WM5110:
|
||||
aod = &wm5110_aod;
|
||||
irq = &wm5110_irq;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
BUG_ON("Unknown Arizona class device" == NULL);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (arizona->pdata.irq_active_high) {
|
||||
ret = regmap_update_bits(arizona->regmap, ARIZONA_IRQ_CTRL_1,
|
||||
ARIZONA_IRQ_POL, 0);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Couldn't set IRQ polarity: %d\n",
|
||||
ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
flags |= IRQF_TRIGGER_HIGH;
|
||||
} else {
|
||||
flags |= IRQF_TRIGGER_LOW;
|
||||
}
|
||||
|
||||
/* Allocate a virtual IRQ domain to distribute to the regmap domains */
|
||||
arizona->virq = irq_domain_add_linear(NULL, 2, &arizona_domain_ops,
|
||||
arizona);
|
||||
if (!arizona->virq) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = regmap_add_irq_chip(arizona->regmap,
|
||||
irq_create_mapping(arizona->virq, 0),
|
||||
IRQF_ONESHOT, -1, aod,
|
||||
&arizona->aod_irq_chip);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Failed to add AOD IRQs: %d\n", ret);
|
||||
goto err_domain;
|
||||
}
|
||||
|
||||
ret = regmap_add_irq_chip(arizona->regmap,
|
||||
irq_create_mapping(arizona->virq, 1),
|
||||
IRQF_ONESHOT, -1, irq,
|
||||
&arizona->irq_chip);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Failed to add AOD IRQs: %d\n", ret);
|
||||
goto err_aod;
|
||||
}
|
||||
|
||||
/* Make sure the boot done IRQ is unmasked for resumes */
|
||||
i = arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE);
|
||||
ret = request_threaded_irq(i, NULL, arizona_boot_done, IRQF_ONESHOT,
|
||||
"Boot done", arizona);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Failed to request boot done %d: %d\n",
|
||||
arizona->irq, ret);
|
||||
goto err_boot_done;
|
||||
}
|
||||
|
||||
/* Handle control interface errors in the core */
|
||||
i = arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR);
|
||||
ret = request_threaded_irq(i, NULL, arizona_ctrlif_err, IRQF_ONESHOT,
|
||||
"Control interface error", arizona);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Failed to request boot done %d: %d\n",
|
||||
arizona->irq, ret);
|
||||
goto err_ctrlif;
|
||||
}
|
||||
|
||||
ret = request_threaded_irq(arizona->irq, NULL, arizona_irq_thread,
|
||||
flags, "arizona", arizona);
|
||||
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Failed to request IRQ %d: %d\n",
|
||||
arizona->irq, ret);
|
||||
goto err_main_irq;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_main_irq:
|
||||
free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR), arizona);
|
||||
err_ctrlif:
|
||||
free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE), arizona);
|
||||
err_boot_done:
|
||||
regmap_del_irq_chip(irq_create_mapping(arizona->virq, 1),
|
||||
arizona->irq_chip);
|
||||
err_aod:
|
||||
regmap_del_irq_chip(irq_create_mapping(arizona->virq, 0),
|
||||
arizona->aod_irq_chip);
|
||||
err_domain:
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int arizona_irq_exit(struct arizona *arizona)
|
||||
{
|
||||
free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR), arizona);
|
||||
free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE), arizona);
|
||||
regmap_del_irq_chip(irq_create_mapping(arizona->virq, 1),
|
||||
arizona->irq_chip);
|
||||
regmap_del_irq_chip(irq_create_mapping(arizona->virq, 0),
|
||||
arizona->aod_irq_chip);
|
||||
free_irq(arizona->irq, arizona);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* arizona-spi.c -- Arizona SPI bus interface
|
||||
*
|
||||
* Copyright 2012 Wolfson Microelectronics plc
|
||||
*
|
||||
* Author: Mark Brown <broonie@opensource.wolfsonmicro.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/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <linux/mfd/arizona/core.h>
|
||||
|
||||
#include "arizona.h"
|
||||
|
||||
static int __devinit arizona_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct arizona *arizona;
|
||||
const struct regmap_config *regmap_config;
|
||||
int ret;
|
||||
|
||||
switch (id->driver_data) {
|
||||
#ifdef CONFIG_MFD_WM5102
|
||||
case WM5102:
|
||||
regmap_config = &wm5102_spi_regmap;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_MFD_WM5110
|
||||
case WM5110:
|
||||
regmap_config = &wm5110_spi_regmap;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
dev_err(&spi->dev, "Unknown device type %ld\n",
|
||||
id->driver_data);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
arizona = devm_kzalloc(&spi->dev, sizeof(*arizona), GFP_KERNEL);
|
||||
if (arizona == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
arizona->regmap = devm_regmap_init_spi(spi, regmap_config);
|
||||
if (IS_ERR(arizona->regmap)) {
|
||||
ret = PTR_ERR(arizona->regmap);
|
||||
dev_err(&spi->dev, "Failed to allocate register map: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
arizona->type = id->driver_data;
|
||||
arizona->dev = &spi->dev;
|
||||
arizona->irq = spi->irq;
|
||||
|
||||
return arizona_dev_init(arizona);
|
||||
}
|
||||
|
||||
static int __devexit arizona_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
struct arizona *arizona = dev_get_drvdata(&spi->dev);
|
||||
arizona_dev_exit(arizona);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id arizona_spi_ids[] = {
|
||||
{ "wm5102", WM5102 },
|
||||
{ "wm5110", WM5110 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, arizona_spi_ids);
|
||||
|
||||
static struct spi_driver arizona_spi_driver = {
|
||||
.driver = {
|
||||
.name = "arizona",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &arizona_pm_ops,
|
||||
},
|
||||
.probe = arizona_spi_probe,
|
||||
.remove = __devexit_p(arizona_spi_remove),
|
||||
.id_table = arizona_spi_ids,
|
||||
};
|
||||
|
||||
module_spi_driver(arizona_spi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Arizona SPI bus interface");
|
||||
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* wm5102.h -- WM5102 MFD internals
|
||||
*
|
||||
* Copyright 2012 Wolfson Microelectronics plc
|
||||
*
|
||||
* Author: Mark Brown <broonie@opensource.wolfsonmicro.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.
|
||||
*/
|
||||
|
||||
#ifndef _WM5102_H
|
||||
#define _WM5102_H
|
||||
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
struct wm_arizona;
|
||||
|
||||
extern const struct regmap_config wm5102_i2c_regmap;
|
||||
extern const struct regmap_config wm5102_spi_regmap;
|
||||
|
||||
extern const struct regmap_config wm5110_i2c_regmap;
|
||||
extern const struct regmap_config wm5110_spi_regmap;
|
||||
|
||||
extern const struct dev_pm_ops arizona_pm_ops;
|
||||
|
||||
extern const struct regmap_irq_chip wm5102_aod;
|
||||
extern const struct regmap_irq_chip wm5102_irq;
|
||||
|
||||
extern const struct regmap_irq_chip wm5110_aod;
|
||||
extern const struct regmap_irq_chip wm5110_irq;
|
||||
|
||||
int arizona_dev_init(struct arizona *arizona);
|
||||
int arizona_dev_exit(struct arizona *arizona);
|
||||
int arizona_irq_init(struct arizona *arizona);
|
||||
int arizona_irq_exit(struct arizona *arizona);
|
||||
|
||||
#endif
|
|
@ -772,7 +772,6 @@ EXPORT_SYMBOL_GPL(da9052_regmap_config);
|
|||
int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id)
|
||||
{
|
||||
struct da9052_pdata *pdata = da9052->dev->platform_data;
|
||||
struct irq_desc *desc;
|
||||
int ret;
|
||||
|
||||
mutex_init(&da9052->auxadc_lock);
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <linux/uaccess.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/dbx500-prcmu.h>
|
||||
#include <linux/mfd/abx500/ab8500.h>
|
||||
#include <linux/regulator/db8500-prcmu.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <asm/hardware/gic.h>
|
||||
|
@ -2269,10 +2270,10 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
|
|||
/**
|
||||
* prcmu_ac_wake_req - should be called whenever ARM wants to wakeup Modem
|
||||
*/
|
||||
void prcmu_ac_wake_req(void)
|
||||
int prcmu_ac_wake_req(void)
|
||||
{
|
||||
u32 val;
|
||||
u32 status;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&mb0_transfer.ac_wake_lock);
|
||||
|
||||
|
@ -2282,39 +2283,32 @@ void prcmu_ac_wake_req(void)
|
|||
|
||||
atomic_set(&ac_wake_req_state, 1);
|
||||
|
||||
retry:
|
||||
writel((val | PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ), PRCM_HOSTACCESS_REQ);
|
||||
/*
|
||||
* Force Modem Wake-up before hostaccess_req ping-pong.
|
||||
* It prevents Modem to enter in Sleep while acking the hostaccess
|
||||
* request. The 31us delay has been calculated by HWI.
|
||||
*/
|
||||
val |= PRCM_HOSTACCESS_REQ_WAKE_REQ;
|
||||
writel(val, PRCM_HOSTACCESS_REQ);
|
||||
|
||||
udelay(31);
|
||||
|
||||
val |= PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ;
|
||||
writel(val, PRCM_HOSTACCESS_REQ);
|
||||
|
||||
if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
|
||||
msecs_to_jiffies(5000))) {
|
||||
#if defined(CONFIG_DBX500_PRCMU_DEBUG)
|
||||
db8500_prcmu_debug_dump(__func__, true, true);
|
||||
#endif
|
||||
pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n",
|
||||
__func__);
|
||||
goto unlock_and_return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The modem can generate an AC_WAKE_ACK, and then still go to sleep.
|
||||
* As a workaround, we wait, and then check that the modem is indeed
|
||||
* awake (in terms of the value of the PRCM_MOD_AWAKE_STATUS
|
||||
* register, which may not be the whole truth).
|
||||
*/
|
||||
udelay(400);
|
||||
status = (readl(PRCM_MOD_AWAKE_STATUS) & BITS(0, 2));
|
||||
if (status != (PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE |
|
||||
PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE)) {
|
||||
pr_err("prcmu: %s received ack, but modem not awake (0x%X).\n",
|
||||
__func__, status);
|
||||
udelay(1200);
|
||||
writel(val, PRCM_HOSTACCESS_REQ);
|
||||
if (wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
|
||||
msecs_to_jiffies(5000)))
|
||||
goto retry;
|
||||
pr_crit("prcmu: %s timed out (5 s) waiting for AC_SLEEP_ACK.\n",
|
||||
__func__);
|
||||
ret = -EFAULT;
|
||||
}
|
||||
|
||||
unlock_and_return:
|
||||
mutex_unlock(&mb0_transfer.ac_wake_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2945,14 +2939,31 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct resource ab8500_resources[] = {
|
||||
[0] = {
|
||||
.start = IRQ_DB8500_AB8500,
|
||||
.end = IRQ_DB8500_AB8500,
|
||||
.flags = IORESOURCE_IRQ
|
||||
}
|
||||
};
|
||||
|
||||
static struct mfd_cell db8500_prcmu_devs[] = {
|
||||
{
|
||||
.name = "db8500-prcmu-regulators",
|
||||
.of_compatible = "stericsson,db8500-prcmu-regulator",
|
||||
.platform_data = &db8500_regulators,
|
||||
.pdata_size = sizeof(db8500_regulators),
|
||||
},
|
||||
{
|
||||
.name = "cpufreq-u8500",
|
||||
.of_compatible = "stericsson,cpufreq-u8500",
|
||||
},
|
||||
{
|
||||
.name = "ab8500-core",
|
||||
.of_compatible = "stericsson,ab8500",
|
||||
.num_resources = ARRAY_SIZE(ab8500_resources),
|
||||
.resources = ab8500_resources,
|
||||
.id = AB8500_VERSION_AB8500,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -2962,8 +2973,9 @@ static struct mfd_cell db8500_prcmu_devs[] = {
|
|||
*/
|
||||
static int __devinit db8500_prcmu_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ab8500_platform_data *ab8500_platdata = pdev->dev.platform_data;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
int irq = 0, err = 0;
|
||||
int irq = 0, err = 0, i;
|
||||
|
||||
if (ux500_is_svp())
|
||||
return -ENODEV;
|
||||
|
@ -2987,16 +2999,21 @@ static int __devinit db8500_prcmu_probe(struct platform_device *pdev)
|
|||
goto no_irq_return;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(db8500_prcmu_devs); i++) {
|
||||
if (!strcmp(db8500_prcmu_devs[i].name, "ab8500-core")) {
|
||||
db8500_prcmu_devs[i].platform_data = ab8500_platdata;
|
||||
db8500_prcmu_devs[i].pdata_size = sizeof(struct ab8500_platform_data);
|
||||
}
|
||||
}
|
||||
|
||||
if (cpu_is_u8500v20_or_later())
|
||||
prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
|
||||
|
||||
if (!np) {
|
||||
err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs,
|
||||
ARRAY_SIZE(db8500_prcmu_devs), NULL, 0);
|
||||
if (err) {
|
||||
pr_err("prcmu: Failed to add subdevices\n");
|
||||
return err;
|
||||
}
|
||||
err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs,
|
||||
ARRAY_SIZE(db8500_prcmu_devs), NULL, 0);
|
||||
if (err) {
|
||||
pr_err("prcmu: Failed to add subdevices\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
pr_info("DB8500 PRCMU initialized\n");
|
||||
|
@ -3004,11 +3021,16 @@ static int __devinit db8500_prcmu_probe(struct platform_device *pdev)
|
|||
no_irq_return:
|
||||
return err;
|
||||
}
|
||||
static const struct of_device_id db8500_prcmu_match[] = {
|
||||
{ .compatible = "stericsson,db8500-prcmu"},
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_driver db8500_prcmu_driver = {
|
||||
.driver = {
|
||||
.name = "db8500-prcmu",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = db8500_prcmu_match,
|
||||
},
|
||||
.probe = db8500_prcmu_probe,
|
||||
};
|
||||
|
@ -3018,7 +3040,7 @@ static int __init db8500_prcmu_init(void)
|
|||
return platform_driver_register(&db8500_prcmu_driver);
|
||||
}
|
||||
|
||||
arch_initcall(db8500_prcmu_init);
|
||||
core_initcall(db8500_prcmu_init);
|
||||
|
||||
MODULE_AUTHOR("Mattias Nilsson <mattias.i.nilsson@stericsson.com>");
|
||||
MODULE_DESCRIPTION("DB8500 PRCM Unit driver");
|
||||
|
|
|
@ -106,6 +106,7 @@
|
|||
|
||||
#define PRCM_HOSTACCESS_REQ (_PRCMU_BASE + 0x334)
|
||||
#define PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ 0x1
|
||||
#define PRCM_HOSTACCESS_REQ_WAKE_REQ BIT(16)
|
||||
#define ARM_WAKEUP_MODEM 0x1
|
||||
|
||||
#define PRCM_ARM_IT1_CLR (_PRCMU_BASE + 0x48C)
|
||||
|
|
|
@ -0,0 +1,319 @@
|
|||
/*
|
||||
* max77686-irq.c - Interrupt controller support for MAX77686
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics Co.Ltd
|
||||
* Chiwoong Byun <woong.byun@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* This driver is based on max8997-irq.c
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/mfd/max77686.h>
|
||||
#include <linux/mfd/max77686-private.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
enum {
|
||||
MAX77686_DEBUG_IRQ_INFO = 1 << 0,
|
||||
MAX77686_DEBUG_IRQ_MASK = 1 << 1,
|
||||
MAX77686_DEBUG_IRQ_INT = 1 << 2,
|
||||
};
|
||||
|
||||
static int debug_mask = 0;
|
||||
module_param(debug_mask, int, 0);
|
||||
MODULE_PARM_DESC(debug_mask, "Set debug_mask : 0x0=off 0x1=IRQ_INFO 0x2=IRQ_MASK 0x4=IRQ_INI)");
|
||||
|
||||
static const u8 max77686_mask_reg[] = {
|
||||
[PMIC_INT1] = MAX77686_REG_INT1MSK,
|
||||
[PMIC_INT2] = MAX77686_REG_INT2MSK,
|
||||
[RTC_INT] = MAX77686_RTC_INTM,
|
||||
};
|
||||
|
||||
static struct regmap *max77686_get_regmap(struct max77686_dev *max77686,
|
||||
enum max77686_irq_source src)
|
||||
{
|
||||
switch (src) {
|
||||
case PMIC_INT1 ... PMIC_INT2:
|
||||
return max77686->regmap;
|
||||
case RTC_INT:
|
||||
return max77686->rtc_regmap;
|
||||
default:
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
struct max77686_irq_data {
|
||||
int mask;
|
||||
enum max77686_irq_source group;
|
||||
};
|
||||
|
||||
#define DECLARE_IRQ(idx, _group, _mask) \
|
||||
[(idx)] = { .group = (_group), .mask = (_mask) }
|
||||
static const struct max77686_irq_data max77686_irqs[] = {
|
||||
DECLARE_IRQ(MAX77686_PMICIRQ_PWRONF, PMIC_INT1, 1 << 0),
|
||||
DECLARE_IRQ(MAX77686_PMICIRQ_PWRONR, PMIC_INT1, 1 << 1),
|
||||
DECLARE_IRQ(MAX77686_PMICIRQ_JIGONBF, PMIC_INT1, 1 << 2),
|
||||
DECLARE_IRQ(MAX77686_PMICIRQ_JIGONBR, PMIC_INT1, 1 << 3),
|
||||
DECLARE_IRQ(MAX77686_PMICIRQ_ACOKBF, PMIC_INT1, 1 << 4),
|
||||
DECLARE_IRQ(MAX77686_PMICIRQ_ACOKBR, PMIC_INT1, 1 << 5),
|
||||
DECLARE_IRQ(MAX77686_PMICIRQ_ONKEY1S, PMIC_INT1, 1 << 6),
|
||||
DECLARE_IRQ(MAX77686_PMICIRQ_MRSTB, PMIC_INT1, 1 << 7),
|
||||
DECLARE_IRQ(MAX77686_PMICIRQ_140C, PMIC_INT2, 1 << 0),
|
||||
DECLARE_IRQ(MAX77686_PMICIRQ_120C, PMIC_INT2, 1 << 1),
|
||||
DECLARE_IRQ(MAX77686_RTCIRQ_RTC60S, RTC_INT, 1 << 0),
|
||||
DECLARE_IRQ(MAX77686_RTCIRQ_RTCA1, RTC_INT, 1 << 1),
|
||||
DECLARE_IRQ(MAX77686_RTCIRQ_RTCA2, RTC_INT, 1 << 2),
|
||||
DECLARE_IRQ(MAX77686_RTCIRQ_SMPL, RTC_INT, 1 << 3),
|
||||
DECLARE_IRQ(MAX77686_RTCIRQ_RTC1S, RTC_INT, 1 << 4),
|
||||
DECLARE_IRQ(MAX77686_RTCIRQ_WTSR, RTC_INT, 1 << 5),
|
||||
};
|
||||
|
||||
static void max77686_irq_lock(struct irq_data *data)
|
||||
{
|
||||
struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
|
||||
|
||||
if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
|
||||
pr_info("%s\n", __func__);
|
||||
|
||||
mutex_lock(&max77686->irqlock);
|
||||
}
|
||||
|
||||
static void max77686_irq_sync_unlock(struct irq_data *data)
|
||||
{
|
||||
struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) {
|
||||
u8 mask_reg = max77686_mask_reg[i];
|
||||
struct regmap *map = max77686_get_regmap(max77686, i);
|
||||
|
||||
if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
|
||||
pr_debug("%s: mask_reg[%d]=0x%x, cur=0x%x\n",
|
||||
__func__, i, mask_reg, max77686->irq_masks_cur[i]);
|
||||
|
||||
if (mask_reg == MAX77686_REG_INVALID ||
|
||||
IS_ERR_OR_NULL(map))
|
||||
continue;
|
||||
|
||||
max77686->irq_masks_cache[i] = max77686->irq_masks_cur[i];
|
||||
|
||||
regmap_write(map, max77686_mask_reg[i],
|
||||
max77686->irq_masks_cur[i]);
|
||||
}
|
||||
|
||||
mutex_unlock(&max77686->irqlock);
|
||||
}
|
||||
|
||||
static const inline struct max77686_irq_data *to_max77686_irq(int irq)
|
||||
{
|
||||
struct irq_data *data = irq_get_irq_data(irq);
|
||||
return &max77686_irqs[data->hwirq];
|
||||
}
|
||||
|
||||
static void max77686_irq_mask(struct irq_data *data)
|
||||
{
|
||||
struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
|
||||
const struct max77686_irq_data *irq_data = to_max77686_irq(data->irq);
|
||||
|
||||
max77686->irq_masks_cur[irq_data->group] |= irq_data->mask;
|
||||
|
||||
if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
|
||||
pr_info("%s: group=%d, cur=0x%x\n",
|
||||
__func__, irq_data->group,
|
||||
max77686->irq_masks_cur[irq_data->group]);
|
||||
}
|
||||
|
||||
static void max77686_irq_unmask(struct irq_data *data)
|
||||
{
|
||||
struct max77686_dev *max77686 = irq_get_chip_data(data->irq);
|
||||
const struct max77686_irq_data *irq_data = to_max77686_irq(data->irq);
|
||||
|
||||
max77686->irq_masks_cur[irq_data->group] &= ~irq_data->mask;
|
||||
|
||||
if (debug_mask & MAX77686_DEBUG_IRQ_MASK)
|
||||
pr_info("%s: group=%d, cur=0x%x\n",
|
||||
__func__, irq_data->group,
|
||||
max77686->irq_masks_cur[irq_data->group]);
|
||||
}
|
||||
|
||||
static struct irq_chip max77686_irq_chip = {
|
||||
.name = "max77686",
|
||||
.irq_bus_lock = max77686_irq_lock,
|
||||
.irq_bus_sync_unlock = max77686_irq_sync_unlock,
|
||||
.irq_mask = max77686_irq_mask,
|
||||
.irq_unmask = max77686_irq_unmask,
|
||||
};
|
||||
|
||||
static irqreturn_t max77686_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct max77686_dev *max77686 = data;
|
||||
unsigned int irq_reg[MAX77686_IRQ_GROUP_NR] = {};
|
||||
unsigned int irq_src;
|
||||
int ret;
|
||||
int i, cur_irq;
|
||||
|
||||
ret = regmap_read(max77686->regmap, MAX77686_REG_INTSRC, &irq_src);
|
||||
if (ret < 0) {
|
||||
dev_err(max77686->dev, "Failed to read interrupt source: %d\n",
|
||||
ret);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if (debug_mask & MAX77686_DEBUG_IRQ_INT)
|
||||
pr_info("%s: irq_src=0x%x\n", __func__, irq_src);
|
||||
|
||||
if (irq_src == MAX77686_IRQSRC_PMIC) {
|
||||
ret = regmap_bulk_read(max77686->regmap,
|
||||
MAX77686_REG_INT1, irq_reg, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(max77686->dev, "Failed to read interrupt source: %d\n",
|
||||
ret);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if (debug_mask & MAX77686_DEBUG_IRQ_INT)
|
||||
pr_info("%s: int1=0x%x, int2=0x%x\n", __func__,
|
||||
irq_reg[PMIC_INT1], irq_reg[PMIC_INT2]);
|
||||
}
|
||||
|
||||
if (irq_src & MAX77686_IRQSRC_RTC) {
|
||||
ret = regmap_read(max77686->rtc_regmap,
|
||||
MAX77686_RTC_INT, &irq_reg[RTC_INT]);
|
||||
if (ret < 0) {
|
||||
dev_err(max77686->dev, "Failed to read interrupt source: %d\n",
|
||||
ret);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if (debug_mask & MAX77686_DEBUG_IRQ_INT)
|
||||
pr_info("%s: rtc int=0x%x\n", __func__,
|
||||
irq_reg[RTC_INT]);
|
||||
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++)
|
||||
irq_reg[i] &= ~max77686->irq_masks_cur[i];
|
||||
|
||||
for (i = 0; i < MAX77686_IRQ_NR; i++) {
|
||||
if (irq_reg[max77686_irqs[i].group] & max77686_irqs[i].mask) {
|
||||
cur_irq = irq_find_mapping(max77686->irq_domain, i);
|
||||
if (cur_irq)
|
||||
handle_nested_irq(cur_irq);
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int max77686_irq_domain_map(struct irq_domain *d, unsigned int irq,
|
||||
irq_hw_number_t hw)
|
||||
{
|
||||
struct max77686_dev *max77686 = d->host_data;
|
||||
|
||||
irq_set_chip_data(irq, max77686);
|
||||
irq_set_chip_and_handler(irq, &max77686_irq_chip, handle_edge_irq);
|
||||
irq_set_nested_thread(irq, 1);
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, IRQF_VALID);
|
||||
#else
|
||||
irq_set_noprobe(irq);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops max77686_irq_domain_ops = {
|
||||
.map = max77686_irq_domain_map,
|
||||
};
|
||||
|
||||
int max77686_irq_init(struct max77686_dev *max77686)
|
||||
{
|
||||
struct irq_domain *domain;
|
||||
int i;
|
||||
int ret;
|
||||
int val;
|
||||
struct regmap *map;
|
||||
|
||||
mutex_init(&max77686->irqlock);
|
||||
|
||||
if (max77686->irq_gpio && !max77686->irq) {
|
||||
max77686->irq = gpio_to_irq(max77686->irq_gpio);
|
||||
|
||||
if (debug_mask & MAX77686_DEBUG_IRQ_INT) {
|
||||
ret = gpio_request(max77686->irq_gpio, "pmic_irq");
|
||||
if (ret < 0) {
|
||||
dev_err(max77686->dev,
|
||||
"Failed to request gpio %d with ret:"
|
||||
"%d\n", max77686->irq_gpio, ret);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
gpio_direction_input(max77686->irq_gpio);
|
||||
val = gpio_get_value(max77686->irq_gpio);
|
||||
gpio_free(max77686->irq_gpio);
|
||||
pr_info("%s: gpio_irq=%x\n", __func__, val);
|
||||
}
|
||||
}
|
||||
|
||||
if (!max77686->irq) {
|
||||
dev_err(max77686->dev, "irq is not specified\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Mask individual interrupt sources */
|
||||
for (i = 0; i < MAX77686_IRQ_GROUP_NR; i++) {
|
||||
max77686->irq_masks_cur[i] = 0xff;
|
||||
max77686->irq_masks_cache[i] = 0xff;
|
||||
map = max77686_get_regmap(max77686, i);
|
||||
|
||||
if (IS_ERR_OR_NULL(map))
|
||||
continue;
|
||||
if (max77686_mask_reg[i] == MAX77686_REG_INVALID)
|
||||
continue;
|
||||
|
||||
regmap_write(map, max77686_mask_reg[i], 0xff);
|
||||
}
|
||||
domain = irq_domain_add_linear(NULL, MAX77686_IRQ_NR,
|
||||
&max77686_irq_domain_ops, max77686);
|
||||
if (!domain) {
|
||||
dev_err(max77686->dev, "could not create irq domain\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
max77686->irq_domain = domain;
|
||||
|
||||
ret = request_threaded_irq(max77686->irq, NULL, max77686_irq_thread,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"max77686-irq", max77686);
|
||||
|
||||
if (ret)
|
||||
dev_err(max77686->dev, "Failed to request IRQ %d: %d\n",
|
||||
max77686->irq, ret);
|
||||
|
||||
|
||||
if (debug_mask & MAX77686_DEBUG_IRQ_INFO)
|
||||
pr_info("%s-\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void max77686_irq_exit(struct max77686_dev *max77686)
|
||||
{
|
||||
if (max77686->irq)
|
||||
free_irq(max77686->irq, max77686);
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* max77686.c - mfd core driver for the Maxim 77686
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
* Chiwoong Byun <woong.byun@smasung.com>
|
||||
* Jonghwa Lee <jonghwa3.lee@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* This driver is based on max8997.c
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/max77686.h>
|
||||
#include <linux/mfd/max77686-private.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#define I2C_ADDR_RTC (0x0C >> 1)
|
||||
|
||||
static struct mfd_cell max77686_devs[] = {
|
||||
{ .name = "max77686-pmic", },
|
||||
{ .name = "max77686-rtc", },
|
||||
};
|
||||
|
||||
static struct regmap_config max77686_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id __devinitdata max77686_pmic_dt_match[] = {
|
||||
{.compatible = "maxim,max77686", .data = 0},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct max77686_platform_data *max77686_i2c_parse_dt_pdata(struct device
|
||||
*dev)
|
||||
{
|
||||
struct max77686_platform_data *pd;
|
||||
|
||||
pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
|
||||
if (!pd) {
|
||||
dev_err(dev, "could not allocate memory for pdata\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev->platform_data = pd;
|
||||
return pd;
|
||||
}
|
||||
#else
|
||||
static struct max77686_platform_data *max77686_i2c_parse_dt_pdata(struct device
|
||||
*dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int max77686_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct max77686_dev *max77686 = NULL;
|
||||
struct max77686_platform_data *pdata = i2c->dev.platform_data;
|
||||
unsigned int data;
|
||||
int ret = 0;
|
||||
|
||||
if (i2c->dev.of_node)
|
||||
pdata = max77686_i2c_parse_dt_pdata(&i2c->dev);
|
||||
|
||||
if (!pdata) {
|
||||
ret = -EIO;
|
||||
dev_err(&i2c->dev, "No platform data found.\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
max77686 = kzalloc(sizeof(struct max77686_dev), GFP_KERNEL);
|
||||
if (max77686 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
max77686->regmap = regmap_init_i2c(i2c, &max77686_regmap_config);
|
||||
if (IS_ERR(max77686->regmap)) {
|
||||
ret = PTR_ERR(max77686->regmap);
|
||||
dev_err(max77686->dev, "Failed to allocate register map: %d\n",
|
||||
ret);
|
||||
kfree(max77686);
|
||||
return ret;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(i2c, max77686);
|
||||
max77686->dev = &i2c->dev;
|
||||
max77686->i2c = i2c;
|
||||
max77686->type = id->driver_data;
|
||||
|
||||
max77686->wakeup = pdata->wakeup;
|
||||
max77686->irq_gpio = pdata->irq_gpio;
|
||||
max77686->irq = i2c->irq;
|
||||
|
||||
if (regmap_read(max77686->regmap,
|
||||
MAX77686_REG_DEVICE_ID, &data) < 0) {
|
||||
dev_err(max77686->dev,
|
||||
"device not found on this channel (this is not an error)\n");
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
} else
|
||||
dev_info(max77686->dev, "device found\n");
|
||||
|
||||
max77686->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC);
|
||||
i2c_set_clientdata(max77686->rtc, max77686);
|
||||
|
||||
max77686_irq_init(max77686);
|
||||
|
||||
ret = mfd_add_devices(max77686->dev, -1, max77686_devs,
|
||||
ARRAY_SIZE(max77686_devs), NULL, 0);
|
||||
|
||||
if (ret < 0)
|
||||
goto err_mfd;
|
||||
|
||||
return ret;
|
||||
|
||||
err_mfd:
|
||||
mfd_remove_devices(max77686->dev);
|
||||
i2c_unregister_device(max77686->rtc);
|
||||
err:
|
||||
kfree(max77686);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max77686_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
|
||||
|
||||
mfd_remove_devices(max77686->dev);
|
||||
i2c_unregister_device(max77686->rtc);
|
||||
kfree(max77686);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max77686_i2c_id[] = {
|
||||
{ "max77686", TYPE_MAX77686 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max77686_i2c_id);
|
||||
|
||||
static struct i2c_driver max77686_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "max77686",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(max77686_pmic_dt_match),
|
||||
},
|
||||
.probe = max77686_i2c_probe,
|
||||
.remove = max77686_i2c_remove,
|
||||
.id_table = max77686_i2c_id,
|
||||
};
|
||||
|
||||
static int __init max77686_i2c_init(void)
|
||||
{
|
||||
return i2c_add_driver(&max77686_i2c_driver);
|
||||
}
|
||||
/* init early so consumer devices can complete system boot */
|
||||
subsys_initcall(max77686_i2c_init);
|
||||
|
||||
static void __exit max77686_i2c_exit(void)
|
||||
{
|
||||
i2c_del_driver(&max77686_i2c_driver);
|
||||
}
|
||||
module_exit(max77686_i2c_exit);
|
||||
|
||||
MODULE_DESCRIPTION("MAXIM 77686 multi-function core driver");
|
||||
MODULE_AUTHOR("Chiwoong Byun <woong.byun@samsung.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -138,8 +138,6 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
|
|||
|
||||
max77693->wakeup = pdata->wakeup;
|
||||
|
||||
mutex_init(&max77693->iolock);
|
||||
|
||||
if (max77693_read_reg(max77693->regmap,
|
||||
MAX77693_PMIC_REG_PMIC_ID2, ®_data) < 0) {
|
||||
dev_err(max77693->dev, "device not found on this channel\n");
|
||||
|
@ -156,7 +154,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
|
|||
|
||||
ret = max77693_irq_init(max77693);
|
||||
if (ret < 0)
|
||||
goto err_mfd;
|
||||
goto err_irq;
|
||||
|
||||
pm_runtime_set_active(max77693->dev);
|
||||
|
||||
|
@ -170,11 +168,11 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
|
|||
return ret;
|
||||
|
||||
err_mfd:
|
||||
max77693_irq_exit(max77693);
|
||||
err_irq:
|
||||
i2c_unregister_device(max77693->muic);
|
||||
i2c_unregister_device(max77693->haptic);
|
||||
err_regmap:
|
||||
kfree(max77693);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -183,6 +181,7 @@ static int max77693_i2c_remove(struct i2c_client *i2c)
|
|||
struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
|
||||
|
||||
mfd_remove_devices(max77693->dev);
|
||||
max77693_irq_exit(max77693);
|
||||
i2c_unregister_device(max77693->muic);
|
||||
i2c_unregister_device(max77693->haptic);
|
||||
|
||||
|
@ -215,7 +214,7 @@ static int max77693_resume(struct device *dev)
|
|||
return max77693_irq_resume(max77693);
|
||||
}
|
||||
|
||||
const struct dev_pm_ops max77693_pm = {
|
||||
static const struct dev_pm_ops max77693_pm = {
|
||||
.suspend = max77693_suspend,
|
||||
.resume = max77693_resume,
|
||||
};
|
||||
|
|
|
@ -75,9 +75,9 @@ static struct mfd_cell power_devs[] = {
|
|||
static struct resource rtc_resources[] = {
|
||||
{
|
||||
.name = "max8925-rtc",
|
||||
.start = MAX8925_RTC_IRQ,
|
||||
.end = MAX8925_RTC_IRQ_MASK,
|
||||
.flags = IORESOURCE_IO,
|
||||
.start = MAX8925_IRQ_RTC_ALARM0,
|
||||
.end = MAX8925_IRQ_RTC_ALARM0,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -598,7 +598,7 @@ int __devinit max8925_device_init(struct max8925_chip *chip,
|
|||
|
||||
ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
|
||||
ARRAY_SIZE(rtc_devs),
|
||||
&rtc_resources[0], 0);
|
||||
&rtc_resources[0], chip->irq_base);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to add rtc subdev\n");
|
||||
goto out;
|
||||
|
|
|
@ -142,7 +142,8 @@ static void max8997_irq_sync_unlock(struct irq_data *data)
|
|||
static const inline struct max8997_irq_data *
|
||||
irq_to_max8997_irq(struct max8997_dev *max8997, int irq)
|
||||
{
|
||||
return &max8997_irqs[irq - max8997->irq_base];
|
||||
struct irq_data *data = irq_get_irq_data(irq);
|
||||
return &max8997_irqs[data->hwirq];
|
||||
}
|
||||
|
||||
static void max8997_irq_mask(struct irq_data *data)
|
||||
|
@ -182,7 +183,7 @@ static irqreturn_t max8997_irq_thread(int irq, void *data)
|
|||
u8 irq_reg[MAX8997_IRQ_GROUP_NR] = {};
|
||||
u8 irq_src;
|
||||
int ret;
|
||||
int i;
|
||||
int i, cur_irq;
|
||||
|
||||
ret = max8997_read_reg(max8997->i2c, MAX8997_REG_INTSRC, &irq_src);
|
||||
if (ret < 0) {
|
||||
|
@ -269,8 +270,11 @@ static irqreturn_t max8997_irq_thread(int irq, void *data)
|
|||
|
||||
/* Report */
|
||||
for (i = 0; i < MAX8997_IRQ_NR; i++) {
|
||||
if (irq_reg[max8997_irqs[i].group] & max8997_irqs[i].mask)
|
||||
handle_nested_irq(max8997->irq_base + i);
|
||||
if (irq_reg[max8997_irqs[i].group] & max8997_irqs[i].mask) {
|
||||
cur_irq = irq_find_mapping(max8997->irq_domain, i);
|
||||
if (cur_irq)
|
||||
handle_nested_irq(cur_irq);
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
@ -278,26 +282,40 @@ static irqreturn_t max8997_irq_thread(int irq, void *data)
|
|||
|
||||
int max8997_irq_resume(struct max8997_dev *max8997)
|
||||
{
|
||||
if (max8997->irq && max8997->irq_base)
|
||||
max8997_irq_thread(max8997->irq_base, max8997);
|
||||
if (max8997->irq && max8997->irq_domain)
|
||||
max8997_irq_thread(0, max8997);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max8997_irq_domain_map(struct irq_domain *d, unsigned int irq,
|
||||
irq_hw_number_t hw)
|
||||
{
|
||||
struct max8997_dev *max8997 = d->host_data;
|
||||
|
||||
irq_set_chip_data(irq, max8997);
|
||||
irq_set_chip_and_handler(irq, &max8997_irq_chip, handle_edge_irq);
|
||||
irq_set_nested_thread(irq, 1);
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, IRQF_VALID);
|
||||
#else
|
||||
irq_set_noprobe(irq);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops max8997_irq_domain_ops = {
|
||||
.map = max8997_irq_domain_map,
|
||||
};
|
||||
|
||||
int max8997_irq_init(struct max8997_dev *max8997)
|
||||
{
|
||||
struct irq_domain *domain;
|
||||
int i;
|
||||
int cur_irq;
|
||||
int ret;
|
||||
u8 val;
|
||||
|
||||
if (!max8997->irq) {
|
||||
dev_warn(max8997->dev, "No interrupt specified.\n");
|
||||
max8997->irq_base = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!max8997->irq_base) {
|
||||
dev_err(max8997->dev, "No interrupt base specified.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -327,19 +345,13 @@ int max8997_irq_init(struct max8997_dev *max8997)
|
|||
true : false;
|
||||
}
|
||||
|
||||
/* Register with genirq */
|
||||
for (i = 0; i < MAX8997_IRQ_NR; i++) {
|
||||
cur_irq = i + max8997->irq_base;
|
||||
irq_set_chip_data(cur_irq, max8997);
|
||||
irq_set_chip_and_handler(cur_irq, &max8997_irq_chip,
|
||||
handle_edge_irq);
|
||||
irq_set_nested_thread(cur_irq, 1);
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(cur_irq, IRQF_VALID);
|
||||
#else
|
||||
irq_set_noprobe(cur_irq);
|
||||
#endif
|
||||
domain = irq_domain_add_linear(NULL, MAX8997_IRQ_NR,
|
||||
&max8997_irq_domain_ops, max8997);
|
||||
if (!domain) {
|
||||
dev_err(max8997->dev, "could not create irq domain\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
max8997->irq_domain = domain;
|
||||
|
||||
ret = request_threaded_irq(max8997->irq, NULL, max8997_irq_thread,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
|
|
|
@ -143,7 +143,6 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
|
|||
if (!pdata)
|
||||
goto err;
|
||||
|
||||
max8997->irq_base = pdata->irq_base;
|
||||
max8997->ono = pdata->ono;
|
||||
|
||||
mutex_init(&max8997->iolock);
|
||||
|
@ -206,7 +205,7 @@ static const struct i2c_device_id max8997_i2c_id[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max8998_i2c_id);
|
||||
|
||||
u8 max8997_dumpaddr_pmic[] = {
|
||||
static u8 max8997_dumpaddr_pmic[] = {
|
||||
MAX8997_REG_INT1MSK,
|
||||
MAX8997_REG_INT2MSK,
|
||||
MAX8997_REG_INT3MSK,
|
||||
|
@ -331,7 +330,7 @@ u8 max8997_dumpaddr_pmic[] = {
|
|||
MAX8997_REG_DVSOKTIMER5,
|
||||
};
|
||||
|
||||
u8 max8997_dumpaddr_muic[] = {
|
||||
static u8 max8997_dumpaddr_muic[] = {
|
||||
MAX8997_MUIC_REG_INTMASK1,
|
||||
MAX8997_MUIC_REG_INTMASK2,
|
||||
MAX8997_MUIC_REG_INTMASK3,
|
||||
|
@ -341,7 +340,7 @@ u8 max8997_dumpaddr_muic[] = {
|
|||
MAX8997_MUIC_REG_CONTROL3,
|
||||
};
|
||||
|
||||
u8 max8997_dumpaddr_haptic[] = {
|
||||
static u8 max8997_dumpaddr_haptic[] = {
|
||||
MAX8997_HAPTIC_REG_CONF1,
|
||||
MAX8997_HAPTIC_REG_CONF2,
|
||||
MAX8997_HAPTIC_REG_DRVCONF,
|
||||
|
@ -423,7 +422,7 @@ static int max8997_resume(struct device *dev)
|
|||
return max8997_irq_resume(max8997);
|
||||
}
|
||||
|
||||
const struct dev_pm_ops max8997_pm = {
|
||||
static const struct dev_pm_ops max8997_pm = {
|
||||
.suspend = max8997_suspend,
|
||||
.resume = max8997_resume,
|
||||
.freeze = max8997_freeze,
|
||||
|
|
|
@ -723,10 +723,6 @@ void mc13xxx_common_cleanup(struct mc13xxx *mc13xxx)
|
|||
free_irq(mc13xxx->irq, mc13xxx);
|
||||
|
||||
mfd_remove_devices(mc13xxx->dev);
|
||||
|
||||
regmap_exit(mc13xxx->regmap);
|
||||
|
||||
kfree(mc13xxx);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mc13xxx_common_cleanup);
|
||||
|
||||
|
|
|
@ -53,17 +53,11 @@ static struct regmap_config mc13xxx_regmap_i2c_config = {
|
|||
static int mc13xxx_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
const struct of_device_id *of_id;
|
||||
struct i2c_driver *idrv = to_i2c_driver(client->dev.driver);
|
||||
struct mc13xxx *mc13xxx;
|
||||
struct mc13xxx_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||
int ret;
|
||||
|
||||
of_id = of_match_device(mc13xxx_dt_ids, &client->dev);
|
||||
if (of_id)
|
||||
idrv->id_table = (const struct i2c_device_id*) of_id->data;
|
||||
|
||||
mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
|
||||
mc13xxx = devm_kzalloc(&client->dev, sizeof(*mc13xxx), GFP_KERNEL);
|
||||
if (!mc13xxx)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -72,13 +66,13 @@ static int mc13xxx_i2c_probe(struct i2c_client *client,
|
|||
mc13xxx->dev = &client->dev;
|
||||
mutex_init(&mc13xxx->lock);
|
||||
|
||||
mc13xxx->regmap = regmap_init_i2c(client, &mc13xxx_regmap_i2c_config);
|
||||
mc13xxx->regmap = devm_regmap_init_i2c(client,
|
||||
&mc13xxx_regmap_i2c_config);
|
||||
if (IS_ERR(mc13xxx->regmap)) {
|
||||
ret = PTR_ERR(mc13xxx->regmap);
|
||||
dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n",
|
||||
ret);
|
||||
dev_set_drvdata(&client->dev, NULL);
|
||||
kfree(mc13xxx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -119,17 +119,11 @@ static struct regmap_bus regmap_mc13xxx_bus = {
|
|||
|
||||
static int mc13xxx_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct of_device_id *of_id;
|
||||
struct spi_driver *sdrv = to_spi_driver(spi->dev.driver);
|
||||
struct mc13xxx *mc13xxx;
|
||||
struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev);
|
||||
int ret;
|
||||
|
||||
of_id = of_match_device(mc13xxx_dt_ids, &spi->dev);
|
||||
if (of_id)
|
||||
sdrv->id_table = &mc13xxx_device_id[(enum mc13xxx_id) of_id->data];
|
||||
|
||||
mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
|
||||
mc13xxx = devm_kzalloc(&spi->dev, sizeof(*mc13xxx), GFP_KERNEL);
|
||||
if (!mc13xxx)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -139,15 +133,14 @@ static int mc13xxx_spi_probe(struct spi_device *spi)
|
|||
mc13xxx->dev = &spi->dev;
|
||||
mutex_init(&mc13xxx->lock);
|
||||
|
||||
mc13xxx->regmap = regmap_init(&spi->dev, ®map_mc13xxx_bus, &spi->dev,
|
||||
&mc13xxx_regmap_spi_config);
|
||||
|
||||
mc13xxx->regmap = devm_regmap_init(&spi->dev, ®map_mc13xxx_bus,
|
||||
&spi->dev,
|
||||
&mc13xxx_regmap_spi_config);
|
||||
if (IS_ERR(mc13xxx->regmap)) {
|
||||
ret = PTR_ERR(mc13xxx->regmap);
|
||||
dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n",
|
||||
ret);
|
||||
dev_set_drvdata(&spi->dev, NULL);
|
||||
kfree(mc13xxx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
int mfd_cell_enable(struct platform_device *pdev)
|
||||
{
|
||||
|
@ -76,6 +78,8 @@ static int mfd_add_device(struct device *parent, int id,
|
|||
{
|
||||
struct resource *res;
|
||||
struct platform_device *pdev;
|
||||
struct device_node *np = NULL;
|
||||
struct irq_domain *domain = NULL;
|
||||
int ret = -ENOMEM;
|
||||
int r;
|
||||
|
||||
|
@ -89,6 +93,16 @@ static int mfd_add_device(struct device *parent, int id,
|
|||
|
||||
pdev->dev.parent = parent;
|
||||
|
||||
if (parent->of_node && cell->of_compatible) {
|
||||
for_each_child_of_node(parent->of_node, np) {
|
||||
if (of_device_is_compatible(np, cell->of_compatible)) {
|
||||
pdev->dev.of_node = np;
|
||||
domain = irq_find_host(parent->of_node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cell->pdata_size) {
|
||||
ret = platform_device_add_data(pdev,
|
||||
cell->platform_data, cell->pdata_size);
|
||||
|
@ -112,10 +126,18 @@ static int mfd_add_device(struct device *parent, int id,
|
|||
res[r].end = mem_base->start +
|
||||
cell->resources[r].end;
|
||||
} else if (cell->resources[r].flags & IORESOURCE_IRQ) {
|
||||
res[r].start = irq_base +
|
||||
cell->resources[r].start;
|
||||
res[r].end = irq_base +
|
||||
cell->resources[r].end;
|
||||
if (domain) {
|
||||
/* Unable to create mappings for IRQ ranges. */
|
||||
WARN_ON(cell->resources[r].start !=
|
||||
cell->resources[r].end);
|
||||
res[r].start = res[r].end = irq_create_mapping(
|
||||
domain, cell->resources[r].start);
|
||||
} else {
|
||||
res[r].start = irq_base +
|
||||
cell->resources[r].start;
|
||||
res[r].end = irq_base +
|
||||
cell->resources[r].end;
|
||||
}
|
||||
} else {
|
||||
res[r].parent = cell->resources[r].parent;
|
||||
res[r].start = cell->resources[r].start;
|
||||
|
|
|
@ -253,8 +253,13 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
|
|||
}
|
||||
|
||||
pdev->dev.parent = pcf->dev;
|
||||
platform_device_add_data(pdev, &pdata->reg_init_data[i],
|
||||
sizeof(pdata->reg_init_data[i]));
|
||||
if (platform_device_add_data(pdev, &pdata->reg_init_data[i],
|
||||
sizeof(pdata->reg_init_data[i])) < 0) {
|
||||
platform_device_put(pdev);
|
||||
dev_err(pcf->dev, "Out of memory for regulator parameters %d\n",
|
||||
i);
|
||||
continue;
|
||||
}
|
||||
pcf->regulator_pdev[i] = pdev;
|
||||
|
||||
platform_device_add(pdev);
|
||||
|
|
|
@ -1,206 +0,0 @@
|
|||
/*
|
||||
* s5m87xx.c
|
||||
*
|
||||
* Copyright (c) 2011 Samsung Electronics Co., Ltd
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#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>
|
||||
#include <linux/mfd/s5m87xx/s5m-core.h>
|
||||
#include <linux/mfd/s5m87xx/s5m-pmic.h>
|
||||
#include <linux/mfd/s5m87xx/s5m-rtc.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
static struct mfd_cell s5m8751_devs[] = {
|
||||
{
|
||||
.name = "s5m8751-pmic",
|
||||
}, {
|
||||
.name = "s5m-charger",
|
||||
}, {
|
||||
.name = "s5m8751-codec",
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell s5m8763_devs[] = {
|
||||
{
|
||||
.name = "s5m8763-pmic",
|
||||
}, {
|
||||
.name = "s5m-rtc",
|
||||
}, {
|
||||
.name = "s5m-charger",
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell s5m8767_devs[] = {
|
||||
{
|
||||
.name = "s5m8767-pmic",
|
||||
}, {
|
||||
.name = "s5m-rtc",
|
||||
},
|
||||
};
|
||||
|
||||
int s5m_reg_read(struct s5m87xx_dev *s5m87xx, u8 reg, void *dest)
|
||||
{
|
||||
return regmap_read(s5m87xx->regmap, reg, dest);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(s5m_reg_read);
|
||||
|
||||
int s5m_bulk_read(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf)
|
||||
{
|
||||
return regmap_bulk_read(s5m87xx->regmap, reg, buf, count);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(s5m_bulk_read);
|
||||
|
||||
int s5m_reg_write(struct s5m87xx_dev *s5m87xx, u8 reg, u8 value)
|
||||
{
|
||||
return regmap_write(s5m87xx->regmap, reg, value);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(s5m_reg_write);
|
||||
|
||||
int s5m_bulk_write(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf)
|
||||
{
|
||||
return regmap_raw_write(s5m87xx->regmap, reg, buf, count);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(s5m_bulk_write);
|
||||
|
||||
int s5m_reg_update(struct s5m87xx_dev *s5m87xx, u8 reg, u8 val, u8 mask)
|
||||
{
|
||||
return regmap_update_bits(s5m87xx->regmap, reg, mask, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(s5m_reg_update);
|
||||
|
||||
static struct regmap_config s5m_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static int s5m87xx_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct s5m_platform_data *pdata = i2c->dev.platform_data;
|
||||
struct s5m87xx_dev *s5m87xx;
|
||||
int ret;
|
||||
|
||||
s5m87xx = devm_kzalloc(&i2c->dev, sizeof(struct s5m87xx_dev),
|
||||
GFP_KERNEL);
|
||||
if (s5m87xx == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(i2c, s5m87xx);
|
||||
s5m87xx->dev = &i2c->dev;
|
||||
s5m87xx->i2c = i2c;
|
||||
s5m87xx->irq = i2c->irq;
|
||||
s5m87xx->type = id->driver_data;
|
||||
|
||||
if (pdata) {
|
||||
s5m87xx->device_type = pdata->device_type;
|
||||
s5m87xx->ono = pdata->ono;
|
||||
s5m87xx->irq_base = pdata->irq_base;
|
||||
s5m87xx->wakeup = pdata->wakeup;
|
||||
}
|
||||
|
||||
s5m87xx->regmap = devm_regmap_init_i2c(i2c, &s5m_regmap_config);
|
||||
if (IS_ERR(s5m87xx->regmap)) {
|
||||
ret = PTR_ERR(s5m87xx->regmap);
|
||||
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
s5m87xx->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
|
||||
i2c_set_clientdata(s5m87xx->rtc, s5m87xx);
|
||||
|
||||
if (pdata && pdata->cfg_pmic_irq)
|
||||
pdata->cfg_pmic_irq();
|
||||
|
||||
s5m_irq_init(s5m87xx);
|
||||
|
||||
pm_runtime_set_active(s5m87xx->dev);
|
||||
|
||||
switch (s5m87xx->device_type) {
|
||||
case S5M8751X:
|
||||
ret = mfd_add_devices(s5m87xx->dev, -1, s5m8751_devs,
|
||||
ARRAY_SIZE(s5m8751_devs), NULL, 0);
|
||||
break;
|
||||
case S5M8763X:
|
||||
ret = mfd_add_devices(s5m87xx->dev, -1, s5m8763_devs,
|
||||
ARRAY_SIZE(s5m8763_devs), NULL, 0);
|
||||
break;
|
||||
case S5M8767X:
|
||||
ret = mfd_add_devices(s5m87xx->dev, -1, s5m8767_devs,
|
||||
ARRAY_SIZE(s5m8767_devs), NULL, 0);
|
||||
break;
|
||||
default:
|
||||
/* If this happens the probe function is problem */
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
return ret;
|
||||
|
||||
err:
|
||||
mfd_remove_devices(s5m87xx->dev);
|
||||
s5m_irq_exit(s5m87xx);
|
||||
i2c_unregister_device(s5m87xx->rtc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int s5m87xx_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c);
|
||||
|
||||
mfd_remove_devices(s5m87xx->dev);
|
||||
s5m_irq_exit(s5m87xx);
|
||||
i2c_unregister_device(s5m87xx->rtc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id s5m87xx_i2c_id[] = {
|
||||
{ "s5m87xx", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, s5m87xx_i2c_id);
|
||||
|
||||
static struct i2c_driver s5m87xx_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "s5m87xx",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = s5m87xx_i2c_probe,
|
||||
.remove = s5m87xx_i2c_remove,
|
||||
.id_table = s5m87xx_i2c_id,
|
||||
};
|
||||
|
||||
static int __init s5m87xx_i2c_init(void)
|
||||
{
|
||||
return i2c_add_driver(&s5m87xx_i2c_driver);
|
||||
}
|
||||
|
||||
subsys_initcall(s5m87xx_i2c_init);
|
||||
|
||||
static void __exit s5m87xx_i2c_exit(void)
|
||||
{
|
||||
i2c_del_driver(&s5m87xx_i2c_driver);
|
||||
}
|
||||
module_exit(s5m87xx_i2c_exit);
|
||||
|
||||
MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
|
||||
MODULE_DESCRIPTION("Core support for the S5M MFD");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1,495 +0,0 @@
|
|||
/*
|
||||
* s5m-irq.c
|
||||
*
|
||||
* Copyright (c) 2011 Samsung Electronics Co., Ltd
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/mfd/s5m87xx/s5m-core.h>
|
||||
|
||||
struct s5m_irq_data {
|
||||
int reg;
|
||||
int mask;
|
||||
};
|
||||
|
||||
static struct s5m_irq_data s5m8767_irqs[] = {
|
||||
[S5M8767_IRQ_PWRR] = {
|
||||
.reg = 1,
|
||||
.mask = S5M8767_IRQ_PWRR_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_PWRF] = {
|
||||
.reg = 1,
|
||||
.mask = S5M8767_IRQ_PWRF_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_PWR1S] = {
|
||||
.reg = 1,
|
||||
.mask = S5M8767_IRQ_PWR1S_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_JIGR] = {
|
||||
.reg = 1,
|
||||
.mask = S5M8767_IRQ_JIGR_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_JIGF] = {
|
||||
.reg = 1,
|
||||
.mask = S5M8767_IRQ_JIGF_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_LOWBAT2] = {
|
||||
.reg = 1,
|
||||
.mask = S5M8767_IRQ_LOWBAT2_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_LOWBAT1] = {
|
||||
.reg = 1,
|
||||
.mask = S5M8767_IRQ_LOWBAT1_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_MRB] = {
|
||||
.reg = 2,
|
||||
.mask = S5M8767_IRQ_MRB_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_DVSOK2] = {
|
||||
.reg = 2,
|
||||
.mask = S5M8767_IRQ_DVSOK2_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_DVSOK3] = {
|
||||
.reg = 2,
|
||||
.mask = S5M8767_IRQ_DVSOK3_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_DVSOK4] = {
|
||||
.reg = 2,
|
||||
.mask = S5M8767_IRQ_DVSOK4_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_RTC60S] = {
|
||||
.reg = 3,
|
||||
.mask = S5M8767_IRQ_RTC60S_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_RTCA1] = {
|
||||
.reg = 3,
|
||||
.mask = S5M8767_IRQ_RTCA1_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_RTCA2] = {
|
||||
.reg = 3,
|
||||
.mask = S5M8767_IRQ_RTCA2_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_SMPL] = {
|
||||
.reg = 3,
|
||||
.mask = S5M8767_IRQ_SMPL_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_RTC1S] = {
|
||||
.reg = 3,
|
||||
.mask = S5M8767_IRQ_RTC1S_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_WTSR] = {
|
||||
.reg = 3,
|
||||
.mask = S5M8767_IRQ_WTSR_MASK,
|
||||
},
|
||||
};
|
||||
|
||||
static struct s5m_irq_data s5m8763_irqs[] = {
|
||||
[S5M8763_IRQ_DCINF] = {
|
||||
.reg = 1,
|
||||
.mask = S5M8763_IRQ_DCINF_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_DCINR] = {
|
||||
.reg = 1,
|
||||
.mask = S5M8763_IRQ_DCINR_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_JIGF] = {
|
||||
.reg = 1,
|
||||
.mask = S5M8763_IRQ_JIGF_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_JIGR] = {
|
||||
.reg = 1,
|
||||
.mask = S5M8763_IRQ_JIGR_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_PWRONF] = {
|
||||
.reg = 1,
|
||||
.mask = S5M8763_IRQ_PWRONF_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_PWRONR] = {
|
||||
.reg = 1,
|
||||
.mask = S5M8763_IRQ_PWRONR_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_WTSREVNT] = {
|
||||
.reg = 2,
|
||||
.mask = S5M8763_IRQ_WTSREVNT_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_SMPLEVNT] = {
|
||||
.reg = 2,
|
||||
.mask = S5M8763_IRQ_SMPLEVNT_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_ALARM1] = {
|
||||
.reg = 2,
|
||||
.mask = S5M8763_IRQ_ALARM1_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_ALARM0] = {
|
||||
.reg = 2,
|
||||
.mask = S5M8763_IRQ_ALARM0_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_ONKEY1S] = {
|
||||
.reg = 3,
|
||||
.mask = S5M8763_IRQ_ONKEY1S_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_TOPOFFR] = {
|
||||
.reg = 3,
|
||||
.mask = S5M8763_IRQ_TOPOFFR_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_DCINOVPR] = {
|
||||
.reg = 3,
|
||||
.mask = S5M8763_IRQ_DCINOVPR_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_CHGRSTF] = {
|
||||
.reg = 3,
|
||||
.mask = S5M8763_IRQ_CHGRSTF_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_DONER] = {
|
||||
.reg = 3,
|
||||
.mask = S5M8763_IRQ_DONER_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_CHGFAULT] = {
|
||||
.reg = 3,
|
||||
.mask = S5M8763_IRQ_CHGFAULT_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_LOBAT1] = {
|
||||
.reg = 4,
|
||||
.mask = S5M8763_IRQ_LOBAT1_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_LOBAT2] = {
|
||||
.reg = 4,
|
||||
.mask = S5M8763_IRQ_LOBAT2_MASK,
|
||||
},
|
||||
};
|
||||
|
||||
static inline struct s5m_irq_data *
|
||||
irq_to_s5m8767_irq(struct s5m87xx_dev *s5m87xx, int irq)
|
||||
{
|
||||
return &s5m8767_irqs[irq - s5m87xx->irq_base];
|
||||
}
|
||||
|
||||
static void s5m8767_irq_lock(struct irq_data *data)
|
||||
{
|
||||
struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
|
||||
|
||||
mutex_lock(&s5m87xx->irqlock);
|
||||
}
|
||||
|
||||
static void s5m8767_irq_sync_unlock(struct irq_data *data)
|
||||
{
|
||||
struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(s5m87xx->irq_masks_cur); i++) {
|
||||
if (s5m87xx->irq_masks_cur[i] != s5m87xx->irq_masks_cache[i]) {
|
||||
s5m87xx->irq_masks_cache[i] = s5m87xx->irq_masks_cur[i];
|
||||
s5m_reg_write(s5m87xx, S5M8767_REG_INT1M + i,
|
||||
s5m87xx->irq_masks_cur[i]);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&s5m87xx->irqlock);
|
||||
}
|
||||
|
||||
static void s5m8767_irq_unmask(struct irq_data *data)
|
||||
{
|
||||
struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
|
||||
struct s5m_irq_data *irq_data = irq_to_s5m8767_irq(s5m87xx,
|
||||
data->irq);
|
||||
|
||||
s5m87xx->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
|
||||
}
|
||||
|
||||
static void s5m8767_irq_mask(struct irq_data *data)
|
||||
{
|
||||
struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
|
||||
struct s5m_irq_data *irq_data = irq_to_s5m8767_irq(s5m87xx,
|
||||
data->irq);
|
||||
|
||||
s5m87xx->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
|
||||
}
|
||||
|
||||
static struct irq_chip s5m8767_irq_chip = {
|
||||
.name = "s5m8767",
|
||||
.irq_bus_lock = s5m8767_irq_lock,
|
||||
.irq_bus_sync_unlock = s5m8767_irq_sync_unlock,
|
||||
.irq_mask = s5m8767_irq_mask,
|
||||
.irq_unmask = s5m8767_irq_unmask,
|
||||
};
|
||||
|
||||
static inline struct s5m_irq_data *
|
||||
irq_to_s5m8763_irq(struct s5m87xx_dev *s5m87xx, int irq)
|
||||
{
|
||||
return &s5m8763_irqs[irq - s5m87xx->irq_base];
|
||||
}
|
||||
|
||||
static void s5m8763_irq_lock(struct irq_data *data)
|
||||
{
|
||||
struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
|
||||
|
||||
mutex_lock(&s5m87xx->irqlock);
|
||||
}
|
||||
|
||||
static void s5m8763_irq_sync_unlock(struct irq_data *data)
|
||||
{
|
||||
struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(s5m87xx->irq_masks_cur); i++) {
|
||||
if (s5m87xx->irq_masks_cur[i] != s5m87xx->irq_masks_cache[i]) {
|
||||
s5m87xx->irq_masks_cache[i] = s5m87xx->irq_masks_cur[i];
|
||||
s5m_reg_write(s5m87xx, S5M8763_REG_IRQM1 + i,
|
||||
s5m87xx->irq_masks_cur[i]);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&s5m87xx->irqlock);
|
||||
}
|
||||
|
||||
static void s5m8763_irq_unmask(struct irq_data *data)
|
||||
{
|
||||
struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
|
||||
struct s5m_irq_data *irq_data = irq_to_s5m8763_irq(s5m87xx,
|
||||
data->irq);
|
||||
|
||||
s5m87xx->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
|
||||
}
|
||||
|
||||
static void s5m8763_irq_mask(struct irq_data *data)
|
||||
{
|
||||
struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
|
||||
struct s5m_irq_data *irq_data = irq_to_s5m8763_irq(s5m87xx,
|
||||
data->irq);
|
||||
|
||||
s5m87xx->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
|
||||
}
|
||||
|
||||
static struct irq_chip s5m8763_irq_chip = {
|
||||
.name = "s5m8763",
|
||||
.irq_bus_lock = s5m8763_irq_lock,
|
||||
.irq_bus_sync_unlock = s5m8763_irq_sync_unlock,
|
||||
.irq_mask = s5m8763_irq_mask,
|
||||
.irq_unmask = s5m8763_irq_unmask,
|
||||
};
|
||||
|
||||
|
||||
static irqreturn_t s5m8767_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct s5m87xx_dev *s5m87xx = data;
|
||||
u8 irq_reg[NUM_IRQ_REGS-1];
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
|
||||
ret = s5m_bulk_read(s5m87xx, S5M8767_REG_INT1,
|
||||
NUM_IRQ_REGS - 1, irq_reg);
|
||||
if (ret < 0) {
|
||||
dev_err(s5m87xx->dev, "Failed to read interrupt register: %d\n",
|
||||
ret);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_IRQ_REGS - 1; i++)
|
||||
irq_reg[i] &= ~s5m87xx->irq_masks_cur[i];
|
||||
|
||||
for (i = 0; i < S5M8767_IRQ_NR; i++) {
|
||||
if (irq_reg[s5m8767_irqs[i].reg - 1] & s5m8767_irqs[i].mask)
|
||||
handle_nested_irq(s5m87xx->irq_base + i);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t s5m8763_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct s5m87xx_dev *s5m87xx = data;
|
||||
u8 irq_reg[NUM_IRQ_REGS];
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = s5m_bulk_read(s5m87xx, S5M8763_REG_IRQ1,
|
||||
NUM_IRQ_REGS, irq_reg);
|
||||
if (ret < 0) {
|
||||
dev_err(s5m87xx->dev, "Failed to read interrupt register: %d\n",
|
||||
ret);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_IRQ_REGS; i++)
|
||||
irq_reg[i] &= ~s5m87xx->irq_masks_cur[i];
|
||||
|
||||
for (i = 0; i < S5M8763_IRQ_NR; i++) {
|
||||
if (irq_reg[s5m8763_irqs[i].reg - 1] & s5m8763_irqs[i].mask)
|
||||
handle_nested_irq(s5m87xx->irq_base + i);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int s5m_irq_resume(struct s5m87xx_dev *s5m87xx)
|
||||
{
|
||||
if (s5m87xx->irq && s5m87xx->irq_base){
|
||||
switch (s5m87xx->device_type) {
|
||||
case S5M8763X:
|
||||
s5m8763_irq_thread(s5m87xx->irq_base, s5m87xx);
|
||||
break;
|
||||
case S5M8767X:
|
||||
s5m8767_irq_thread(s5m87xx->irq_base, s5m87xx);
|
||||
break;
|
||||
default:
|
||||
dev_err(s5m87xx->dev,
|
||||
"Unknown device type %d\n",
|
||||
s5m87xx->device_type);
|
||||
return -EINVAL;
|
||||
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int s5m_irq_init(struct s5m87xx_dev *s5m87xx)
|
||||
{
|
||||
int i;
|
||||
int cur_irq;
|
||||
int ret = 0;
|
||||
int type = s5m87xx->device_type;
|
||||
|
||||
if (!s5m87xx->irq) {
|
||||
dev_warn(s5m87xx->dev,
|
||||
"No interrupt specified, no interrupts\n");
|
||||
s5m87xx->irq_base = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!s5m87xx->irq_base) {
|
||||
dev_err(s5m87xx->dev,
|
||||
"No interrupt base specified, no interrupts\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
mutex_init(&s5m87xx->irqlock);
|
||||
|
||||
switch (type) {
|
||||
case S5M8763X:
|
||||
for (i = 0; i < NUM_IRQ_REGS; i++) {
|
||||
s5m87xx->irq_masks_cur[i] = 0xff;
|
||||
s5m87xx->irq_masks_cache[i] = 0xff;
|
||||
s5m_reg_write(s5m87xx, S5M8763_REG_IRQM1 + i,
|
||||
0xff);
|
||||
}
|
||||
|
||||
s5m_reg_write(s5m87xx, S5M8763_REG_STATUSM1, 0xff);
|
||||
s5m_reg_write(s5m87xx, S5M8763_REG_STATUSM2, 0xff);
|
||||
|
||||
for (i = 0; i < S5M8763_IRQ_NR; i++) {
|
||||
cur_irq = i + s5m87xx->irq_base;
|
||||
irq_set_chip_data(cur_irq, s5m87xx);
|
||||
irq_set_chip_and_handler(cur_irq, &s5m8763_irq_chip,
|
||||
handle_edge_irq);
|
||||
irq_set_nested_thread(cur_irq, 1);
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(cur_irq, IRQF_VALID);
|
||||
#else
|
||||
irq_set_noprobe(cur_irq);
|
||||
#endif
|
||||
}
|
||||
|
||||
ret = request_threaded_irq(s5m87xx->irq, NULL,
|
||||
s5m8763_irq_thread,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"s5m87xx-irq", s5m87xx);
|
||||
if (ret) {
|
||||
dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n",
|
||||
s5m87xx->irq, ret);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case S5M8767X:
|
||||
for (i = 0; i < NUM_IRQ_REGS - 1; i++) {
|
||||
s5m87xx->irq_masks_cur[i] = 0xff;
|
||||
s5m87xx->irq_masks_cache[i] = 0xff;
|
||||
s5m_reg_write(s5m87xx, S5M8767_REG_INT1M + i,
|
||||
0xff);
|
||||
}
|
||||
for (i = 0; i < S5M8767_IRQ_NR; i++) {
|
||||
cur_irq = i + s5m87xx->irq_base;
|
||||
irq_set_chip_data(cur_irq, s5m87xx);
|
||||
if (ret) {
|
||||
dev_err(s5m87xx->dev,
|
||||
"Failed to irq_set_chip_data %d: %d\n",
|
||||
s5m87xx->irq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
irq_set_chip_and_handler(cur_irq, &s5m8767_irq_chip,
|
||||
handle_edge_irq);
|
||||
irq_set_nested_thread(cur_irq, 1);
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(cur_irq, IRQF_VALID);
|
||||
#else
|
||||
irq_set_noprobe(cur_irq);
|
||||
#endif
|
||||
}
|
||||
|
||||
ret = request_threaded_irq(s5m87xx->irq, NULL,
|
||||
s5m8767_irq_thread,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"s5m87xx-irq", s5m87xx);
|
||||
if (ret) {
|
||||
dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n",
|
||||
s5m87xx->irq, ret);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_err(s5m87xx->dev,
|
||||
"Unknown device type %d\n", s5m87xx->device_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!s5m87xx->ono)
|
||||
return 0;
|
||||
|
||||
switch (type) {
|
||||
case S5M8763X:
|
||||
ret = request_threaded_irq(s5m87xx->ono, NULL,
|
||||
s5m8763_irq_thread,
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_TRIGGER_RISING |
|
||||
IRQF_ONESHOT, "s5m87xx-ono",
|
||||
s5m87xx);
|
||||
break;
|
||||
case S5M8767X:
|
||||
ret = request_threaded_irq(s5m87xx->ono, NULL,
|
||||
s5m8767_irq_thread,
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_TRIGGER_RISING |
|
||||
IRQF_ONESHOT, "s5m87xx-ono", s5m87xx);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n",
|
||||
s5m87xx->ono, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void s5m_irq_exit(struct s5m87xx_dev *s5m87xx)
|
||||
{
|
||||
if (s5m87xx->ono)
|
||||
free_irq(s5m87xx->ono, s5m87xx);
|
||||
|
||||
if (s5m87xx->irq)
|
||||
free_irq(s5m87xx->irq, s5m87xx);
|
||||
}
|
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
* sec-core.c
|
||||
*
|
||||
* Copyright (c) 2012 Samsung Electronics Co., Ltd
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#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>
|
||||
#include <linux/mfd/samsung/core.h>
|
||||
#include <linux/mfd/samsung/irq.h>
|
||||
#include <linux/mfd/samsung/rtc.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
static struct mfd_cell s5m8751_devs[] = {
|
||||
{
|
||||
.name = "s5m8751-pmic",
|
||||
}, {
|
||||
.name = "s5m-charger",
|
||||
}, {
|
||||
.name = "s5m8751-codec",
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell s5m8763_devs[] = {
|
||||
{
|
||||
.name = "s5m8763-pmic",
|
||||
}, {
|
||||
.name = "s5m-rtc",
|
||||
}, {
|
||||
.name = "s5m-charger",
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell s5m8767_devs[] = {
|
||||
{
|
||||
.name = "s5m8767-pmic",
|
||||
}, {
|
||||
.name = "s5m-rtc",
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell s2mps11_devs[] = {
|
||||
{
|
||||
.name = "s2mps11-pmic",
|
||||
},
|
||||
};
|
||||
|
||||
int sec_reg_read(struct sec_pmic_dev *sec_pmic, u8 reg, void *dest)
|
||||
{
|
||||
return regmap_read(sec_pmic->regmap, reg, dest);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sec_reg_read);
|
||||
|
||||
int sec_bulk_read(struct sec_pmic_dev *sec_pmic, u8 reg, int count, u8 *buf)
|
||||
{
|
||||
return regmap_bulk_read(sec_pmic->regmap, reg, buf, count);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sec_bulk_read);
|
||||
|
||||
int sec_reg_write(struct sec_pmic_dev *sec_pmic, u8 reg, u8 value)
|
||||
{
|
||||
return regmap_write(sec_pmic->regmap, reg, value);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sec_reg_write);
|
||||
|
||||
int sec_bulk_write(struct sec_pmic_dev *sec_pmic, u8 reg, int count, u8 *buf)
|
||||
{
|
||||
return regmap_raw_write(sec_pmic->regmap, reg, buf, count);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sec_bulk_write);
|
||||
|
||||
int sec_reg_update(struct sec_pmic_dev *sec_pmic, u8 reg, u8 val, u8 mask)
|
||||
{
|
||||
return regmap_update_bits(sec_pmic->regmap, reg, mask, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sec_reg_update);
|
||||
|
||||
static struct regmap_config sec_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static int sec_pmic_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct sec_platform_data *pdata = i2c->dev.platform_data;
|
||||
struct sec_pmic_dev *sec_pmic;
|
||||
int ret;
|
||||
|
||||
sec_pmic = devm_kzalloc(&i2c->dev, sizeof(struct sec_pmic_dev),
|
||||
GFP_KERNEL);
|
||||
if (sec_pmic == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(i2c, sec_pmic);
|
||||
sec_pmic->dev = &i2c->dev;
|
||||
sec_pmic->i2c = i2c;
|
||||
sec_pmic->irq = i2c->irq;
|
||||
sec_pmic->type = id->driver_data;
|
||||
|
||||
if (pdata) {
|
||||
sec_pmic->device_type = pdata->device_type;
|
||||
sec_pmic->ono = pdata->ono;
|
||||
sec_pmic->irq_base = pdata->irq_base;
|
||||
sec_pmic->wakeup = pdata->wakeup;
|
||||
}
|
||||
|
||||
sec_pmic->regmap = devm_regmap_init_i2c(i2c, &sec_regmap_config);
|
||||
if (IS_ERR(sec_pmic->regmap)) {
|
||||
ret = PTR_ERR(sec_pmic->regmap);
|
||||
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
sec_pmic->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
|
||||
i2c_set_clientdata(sec_pmic->rtc, sec_pmic);
|
||||
|
||||
if (pdata && pdata->cfg_pmic_irq)
|
||||
pdata->cfg_pmic_irq();
|
||||
|
||||
sec_irq_init(sec_pmic);
|
||||
|
||||
pm_runtime_set_active(sec_pmic->dev);
|
||||
|
||||
switch (sec_pmic->device_type) {
|
||||
case S5M8751X:
|
||||
ret = mfd_add_devices(sec_pmic->dev, -1, s5m8751_devs,
|
||||
ARRAY_SIZE(s5m8751_devs), NULL, 0);
|
||||
break;
|
||||
case S5M8763X:
|
||||
ret = mfd_add_devices(sec_pmic->dev, -1, s5m8763_devs,
|
||||
ARRAY_SIZE(s5m8763_devs), NULL, 0);
|
||||
break;
|
||||
case S5M8767X:
|
||||
ret = mfd_add_devices(sec_pmic->dev, -1, s5m8767_devs,
|
||||
ARRAY_SIZE(s5m8767_devs), NULL, 0);
|
||||
break;
|
||||
case S2MPS11X:
|
||||
ret = mfd_add_devices(sec_pmic->dev, -1, s2mps11_devs,
|
||||
ARRAY_SIZE(s2mps11_devs), NULL, 0);
|
||||
break;
|
||||
default:
|
||||
/* If this happens the probe function is problem */
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
return ret;
|
||||
|
||||
err:
|
||||
mfd_remove_devices(sec_pmic->dev);
|
||||
sec_irq_exit(sec_pmic);
|
||||
i2c_unregister_device(sec_pmic->rtc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sec_pmic_remove(struct i2c_client *i2c)
|
||||
{
|
||||
struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c);
|
||||
|
||||
mfd_remove_devices(sec_pmic->dev);
|
||||
sec_irq_exit(sec_pmic);
|
||||
i2c_unregister_device(sec_pmic->rtc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id sec_pmic_id[] = {
|
||||
{ "sec_pmic", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, sec_pmic_id);
|
||||
|
||||
static struct i2c_driver sec_pmic_driver = {
|
||||
.driver = {
|
||||
.name = "sec_pmic",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = sec_pmic_probe,
|
||||
.remove = sec_pmic_remove,
|
||||
.id_table = sec_pmic_id,
|
||||
};
|
||||
|
||||
static int __init sec_pmic_init(void)
|
||||
{
|
||||
return i2c_add_driver(&sec_pmic_driver);
|
||||
}
|
||||
|
||||
subsys_initcall(sec_pmic_init);
|
||||
|
||||
static void __exit sec_pmic_exit(void)
|
||||
{
|
||||
i2c_del_driver(&sec_pmic_driver);
|
||||
}
|
||||
module_exit(sec_pmic_exit);
|
||||
|
||||
MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
|
||||
MODULE_DESCRIPTION("Core support for the S5M MFD");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,317 @@
|
|||
/*
|
||||
* sec-irq.c
|
||||
*
|
||||
* Copyright (c) 2011 Samsung Electronics Co., Ltd
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/mfd/samsung/core.h>
|
||||
#include <linux/mfd/samsung/irq.h>
|
||||
#include <linux/mfd/samsung/s2mps11.h>
|
||||
#include <linux/mfd/samsung/s5m8763.h>
|
||||
#include <linux/mfd/samsung/s5m8767.h>
|
||||
|
||||
static struct regmap_irq s2mps11_irqs[] = {
|
||||
[S2MPS11_IRQ_PWRONF] = {
|
||||
.reg_offset = 1,
|
||||
.mask = S2MPS11_IRQ_PWRONF_MASK,
|
||||
},
|
||||
[S2MPS11_IRQ_PWRONR] = {
|
||||
.reg_offset = 1,
|
||||
.mask = S2MPS11_IRQ_PWRONR_MASK,
|
||||
},
|
||||
[S2MPS11_IRQ_JIGONBF] = {
|
||||
.reg_offset = 1,
|
||||
.mask = S2MPS11_IRQ_JIGONBF_MASK,
|
||||
},
|
||||
[S2MPS11_IRQ_JIGONBR] = {
|
||||
.reg_offset = 1,
|
||||
.mask = S2MPS11_IRQ_JIGONBR_MASK,
|
||||
},
|
||||
[S2MPS11_IRQ_ACOKBF] = {
|
||||
.reg_offset = 1,
|
||||
.mask = S2MPS11_IRQ_ACOKBF_MASK,
|
||||
},
|
||||
[S2MPS11_IRQ_ACOKBR] = {
|
||||
.reg_offset = 1,
|
||||
.mask = S2MPS11_IRQ_ACOKBR_MASK,
|
||||
},
|
||||
[S2MPS11_IRQ_PWRON1S] = {
|
||||
.reg_offset = 1,
|
||||
.mask = S2MPS11_IRQ_PWRON1S_MASK,
|
||||
},
|
||||
[S2MPS11_IRQ_MRB] = {
|
||||
.reg_offset = 1,
|
||||
.mask = S2MPS11_IRQ_MRB_MASK,
|
||||
},
|
||||
[S2MPS11_IRQ_RTC60S] = {
|
||||
.reg_offset = 2,
|
||||
.mask = S2MPS11_IRQ_RTC60S_MASK,
|
||||
},
|
||||
[S2MPS11_IRQ_RTCA1] = {
|
||||
.reg_offset = 2,
|
||||
.mask = S2MPS11_IRQ_RTCA1_MASK,
|
||||
},
|
||||
[S2MPS11_IRQ_RTCA2] = {
|
||||
.reg_offset = 2,
|
||||
.mask = S2MPS11_IRQ_RTCA2_MASK,
|
||||
},
|
||||
[S2MPS11_IRQ_SMPL] = {
|
||||
.reg_offset = 2,
|
||||
.mask = S2MPS11_IRQ_SMPL_MASK,
|
||||
},
|
||||
[S2MPS11_IRQ_RTC1S] = {
|
||||
.reg_offset = 2,
|
||||
.mask = S2MPS11_IRQ_RTC1S_MASK,
|
||||
},
|
||||
[S2MPS11_IRQ_WTSR] = {
|
||||
.reg_offset = 2,
|
||||
.mask = S2MPS11_IRQ_WTSR_MASK,
|
||||
},
|
||||
[S2MPS11_IRQ_INT120C] = {
|
||||
.reg_offset = 3,
|
||||
.mask = S2MPS11_IRQ_INT120C_MASK,
|
||||
},
|
||||
[S2MPS11_IRQ_INT140C] = {
|
||||
.reg_offset = 3,
|
||||
.mask = S2MPS11_IRQ_INT140C_MASK,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static struct regmap_irq s5m8767_irqs[] = {
|
||||
[S5M8767_IRQ_PWRR] = {
|
||||
.reg_offset = 1,
|
||||
.mask = S5M8767_IRQ_PWRR_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_PWRF] = {
|
||||
.reg_offset = 1,
|
||||
.mask = S5M8767_IRQ_PWRF_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_PWR1S] = {
|
||||
.reg_offset = 1,
|
||||
.mask = S5M8767_IRQ_PWR1S_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_JIGR] = {
|
||||
.reg_offset = 1,
|
||||
.mask = S5M8767_IRQ_JIGR_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_JIGF] = {
|
||||
.reg_offset = 1,
|
||||
.mask = S5M8767_IRQ_JIGF_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_LOWBAT2] = {
|
||||
.reg_offset = 1,
|
||||
.mask = S5M8767_IRQ_LOWBAT2_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_LOWBAT1] = {
|
||||
.reg_offset = 1,
|
||||
.mask = S5M8767_IRQ_LOWBAT1_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_MRB] = {
|
||||
.reg_offset = 2,
|
||||
.mask = S5M8767_IRQ_MRB_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_DVSOK2] = {
|
||||
.reg_offset = 2,
|
||||
.mask = S5M8767_IRQ_DVSOK2_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_DVSOK3] = {
|
||||
.reg_offset = 2,
|
||||
.mask = S5M8767_IRQ_DVSOK3_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_DVSOK4] = {
|
||||
.reg_offset = 2,
|
||||
.mask = S5M8767_IRQ_DVSOK4_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_RTC60S] = {
|
||||
.reg_offset = 3,
|
||||
.mask = S5M8767_IRQ_RTC60S_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_RTCA1] = {
|
||||
.reg_offset = 3,
|
||||
.mask = S5M8767_IRQ_RTCA1_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_RTCA2] = {
|
||||
.reg_offset = 3,
|
||||
.mask = S5M8767_IRQ_RTCA2_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_SMPL] = {
|
||||
.reg_offset = 3,
|
||||
.mask = S5M8767_IRQ_SMPL_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_RTC1S] = {
|
||||
.reg_offset = 3,
|
||||
.mask = S5M8767_IRQ_RTC1S_MASK,
|
||||
},
|
||||
[S5M8767_IRQ_WTSR] = {
|
||||
.reg_offset = 3,
|
||||
.mask = S5M8767_IRQ_WTSR_MASK,
|
||||
},
|
||||
};
|
||||
|
||||
static struct regmap_irq s5m8763_irqs[] = {
|
||||
[S5M8763_IRQ_DCINF] = {
|
||||
.reg_offset = 1,
|
||||
.mask = S5M8763_IRQ_DCINF_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_DCINR] = {
|
||||
.reg_offset = 1,
|
||||
.mask = S5M8763_IRQ_DCINR_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_JIGF] = {
|
||||
.reg_offset = 1,
|
||||
.mask = S5M8763_IRQ_JIGF_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_JIGR] = {
|
||||
.reg_offset = 1,
|
||||
.mask = S5M8763_IRQ_JIGR_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_PWRONF] = {
|
||||
.reg_offset = 1,
|
||||
.mask = S5M8763_IRQ_PWRONF_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_PWRONR] = {
|
||||
.reg_offset = 1,
|
||||
.mask = S5M8763_IRQ_PWRONR_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_WTSREVNT] = {
|
||||
.reg_offset = 2,
|
||||
.mask = S5M8763_IRQ_WTSREVNT_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_SMPLEVNT] = {
|
||||
.reg_offset = 2,
|
||||
.mask = S5M8763_IRQ_SMPLEVNT_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_ALARM1] = {
|
||||
.reg_offset = 2,
|
||||
.mask = S5M8763_IRQ_ALARM1_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_ALARM0] = {
|
||||
.reg_offset = 2,
|
||||
.mask = S5M8763_IRQ_ALARM0_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_ONKEY1S] = {
|
||||
.reg_offset = 3,
|
||||
.mask = S5M8763_IRQ_ONKEY1S_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_TOPOFFR] = {
|
||||
.reg_offset = 3,
|
||||
.mask = S5M8763_IRQ_TOPOFFR_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_DCINOVPR] = {
|
||||
.reg_offset = 3,
|
||||
.mask = S5M8763_IRQ_DCINOVPR_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_CHGRSTF] = {
|
||||
.reg_offset = 3,
|
||||
.mask = S5M8763_IRQ_CHGRSTF_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_DONER] = {
|
||||
.reg_offset = 3,
|
||||
.mask = S5M8763_IRQ_DONER_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_CHGFAULT] = {
|
||||
.reg_offset = 3,
|
||||
.mask = S5M8763_IRQ_CHGFAULT_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_LOBAT1] = {
|
||||
.reg_offset = 4,
|
||||
.mask = S5M8763_IRQ_LOBAT1_MASK,
|
||||
},
|
||||
[S5M8763_IRQ_LOBAT2] = {
|
||||
.reg_offset = 4,
|
||||
.mask = S5M8763_IRQ_LOBAT2_MASK,
|
||||
},
|
||||
};
|
||||
|
||||
static struct regmap_irq_chip s2mps11_irq_chip = {
|
||||
.name = "s2mps11",
|
||||
.irqs = s2mps11_irqs,
|
||||
.num_irqs = ARRAY_SIZE(s2mps11_irqs),
|
||||
.num_regs = 3,
|
||||
.status_base = S2MPS11_REG_INT1,
|
||||
.mask_base = S2MPS11_REG_INT1M,
|
||||
.ack_base = S2MPS11_REG_INT1,
|
||||
};
|
||||
|
||||
static struct regmap_irq_chip s5m8767_irq_chip = {
|
||||
.name = "s5m8767",
|
||||
.irqs = s5m8767_irqs,
|
||||
.num_irqs = ARRAY_SIZE(s5m8767_irqs),
|
||||
.num_regs = 3,
|
||||
.status_base = S5M8767_REG_INT1,
|
||||
.mask_base = S5M8767_REG_INT1M,
|
||||
.ack_base = S5M8767_REG_INT1,
|
||||
};
|
||||
|
||||
static struct regmap_irq_chip s5m8763_irq_chip = {
|
||||
.name = "s5m8763",
|
||||
.irqs = s5m8763_irqs,
|
||||
.num_irqs = ARRAY_SIZE(s5m8763_irqs),
|
||||
.num_regs = 4,
|
||||
.status_base = S5M8763_REG_IRQ1,
|
||||
.mask_base = S5M8763_REG_IRQM1,
|
||||
.ack_base = S5M8763_REG_IRQ1,
|
||||
};
|
||||
|
||||
int sec_irq_init(struct sec_pmic_dev *sec_pmic)
|
||||
{
|
||||
int ret = 0;
|
||||
int type = sec_pmic->device_type;
|
||||
|
||||
if (!sec_pmic->irq) {
|
||||
dev_warn(sec_pmic->dev,
|
||||
"No interrupt specified, no interrupts\n");
|
||||
sec_pmic->irq_base = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case S5M8763X:
|
||||
ret = regmap_add_irq_chip(sec_pmic->regmap, sec_pmic->irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
sec_pmic->irq_base, &s5m8763_irq_chip,
|
||||
&sec_pmic->irq_data);
|
||||
break;
|
||||
case S5M8767X:
|
||||
ret = regmap_add_irq_chip(sec_pmic->regmap, sec_pmic->irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
sec_pmic->irq_base, &s5m8767_irq_chip,
|
||||
&sec_pmic->irq_data);
|
||||
break;
|
||||
case S2MPS11X:
|
||||
ret = regmap_add_irq_chip(sec_pmic->regmap, sec_pmic->irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
sec_pmic->irq_base, &s2mps11_irq_chip,
|
||||
&sec_pmic->irq_data);
|
||||
break;
|
||||
default:
|
||||
dev_err(sec_pmic->dev, "Unknown device type %d\n",
|
||||
sec_pmic->device_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
dev_err(sec_pmic->dev, "Failed to register IRQ chip: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sec_irq_exit(struct sec_pmic_dev *sec_pmic)
|
||||
{
|
||||
regmap_del_irq_chip(sec_pmic->irq, sec_pmic->irq_data);
|
||||
}
|
|
@ -357,7 +357,7 @@ static int __devexit tc3589x_remove(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int tc3589x_suspend(struct device *dev)
|
||||
{
|
||||
struct tc3589x *tc3589x = dev_get_drvdata(dev);
|
||||
|
@ -385,11 +385,10 @@ static int tc3589x_resume(struct device *dev)
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend,
|
||||
tc3589x_resume);
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend, tc3589x_resume);
|
||||
|
||||
static const struct i2c_device_id tc3589x_id[] = {
|
||||
{ "tc3589x", 24 },
|
||||
{ }
|
||||
|
@ -399,9 +398,7 @@ MODULE_DEVICE_TABLE(i2c, tc3589x_id);
|
|||
static struct i2c_driver tc3589x_driver = {
|
||||
.driver.name = "tc3589x",
|
||||
.driver.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.driver.pm = &tc3589x_dev_pm_ops,
|
||||
#endif
|
||||
.probe = tc3589x_probe,
|
||||
.remove = __devexit_p(tc3589x_remove),
|
||||
.id_table = tc3589x_id,
|
||||
|
|
|
@ -71,10 +71,10 @@ static const struct tps65090_irq_data tps65090_irqs[] = {
|
|||
|
||||
static struct mfd_cell tps65090s[] = {
|
||||
{
|
||||
.name = "tps65910-pmic",
|
||||
.name = "tps65090-pmic",
|
||||
},
|
||||
{
|
||||
.name = "tps65910-regulator",
|
||||
.name = "tps65090-regulator",
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -21,17 +21,14 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/tps6586x.h>
|
||||
|
||||
/* GPIO control registers */
|
||||
#define TPS6586X_GPIOSET1 0x5d
|
||||
#define TPS6586X_GPIOSET2 0x5e
|
||||
|
||||
/* interrupt control registers */
|
||||
#define TPS6586X_INT_ACK1 0xb5
|
||||
#define TPS6586X_INT_ACK2 0xb6
|
||||
|
@ -48,6 +45,9 @@
|
|||
/* device id */
|
||||
#define TPS6586X_VERSIONCRC 0xcd
|
||||
|
||||
/* Maximum register */
|
||||
#define TPS6586X_MAX_REGISTER (TPS6586X_VERSIONCRC + 1)
|
||||
|
||||
struct tps6586x_irq_data {
|
||||
u8 mask_reg;
|
||||
u8 mask_mask;
|
||||
|
@ -89,226 +89,96 @@ static const struct tps6586x_irq_data tps6586x_irqs[] = {
|
|||
[TPS6586X_INT_RTC_ALM2] = TPS6586X_IRQ(TPS6586X_INT_MASK4, 1 << 1),
|
||||
};
|
||||
|
||||
static struct mfd_cell tps6586x_cell[] = {
|
||||
{
|
||||
.name = "tps6586x-gpio",
|
||||
},
|
||||
{
|
||||
.name = "tps6586x-rtc",
|
||||
},
|
||||
{
|
||||
.name = "tps6586x-onkey",
|
||||
},
|
||||
};
|
||||
|
||||
struct tps6586x {
|
||||
struct mutex lock;
|
||||
struct device *dev;
|
||||
struct i2c_client *client;
|
||||
struct regmap *regmap;
|
||||
|
||||
struct gpio_chip gpio;
|
||||
struct irq_chip irq_chip;
|
||||
struct mutex irq_lock;
|
||||
int irq_base;
|
||||
u32 irq_en;
|
||||
u8 mask_cache[5];
|
||||
u8 mask_reg[5];
|
||||
};
|
||||
|
||||
static inline int __tps6586x_read(struct i2c_client *client,
|
||||
int reg, uint8_t *val)
|
||||
static inline struct tps6586x *dev_to_tps6586x(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, reg);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = (uint8_t)ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int __tps6586x_reads(struct i2c_client *client, int reg,
|
||||
int len, uint8_t *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(client, reg, len, val);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed reading from 0x%02x\n", reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int __tps6586x_write(struct i2c_client *client,
|
||||
int reg, uint8_t val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, reg, val);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n",
|
||||
val, reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int __tps6586x_writes(struct i2c_client *client, int reg,
|
||||
int len, uint8_t *val)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
ret = __tps6586x_write(client, reg + i, *(val + i));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return i2c_get_clientdata(to_i2c_client(dev));
|
||||
}
|
||||
|
||||
int tps6586x_write(struct device *dev, int reg, uint8_t val)
|
||||
{
|
||||
return __tps6586x_write(to_i2c_client(dev), reg, val);
|
||||
struct tps6586x *tps6586x = dev_to_tps6586x(dev);
|
||||
|
||||
return regmap_write(tps6586x->regmap, reg, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tps6586x_write);
|
||||
|
||||
int tps6586x_writes(struct device *dev, int reg, int len, uint8_t *val)
|
||||
{
|
||||
return __tps6586x_writes(to_i2c_client(dev), reg, len, val);
|
||||
struct tps6586x *tps6586x = dev_to_tps6586x(dev);
|
||||
|
||||
return regmap_bulk_write(tps6586x->regmap, reg, val, len);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tps6586x_writes);
|
||||
|
||||
int tps6586x_read(struct device *dev, int reg, uint8_t *val)
|
||||
{
|
||||
return __tps6586x_read(to_i2c_client(dev), reg, val);
|
||||
struct tps6586x *tps6586x = dev_to_tps6586x(dev);
|
||||
unsigned int rval;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(tps6586x->regmap, reg, &rval);
|
||||
if (!ret)
|
||||
*val = rval;
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tps6586x_read);
|
||||
|
||||
int tps6586x_reads(struct device *dev, int reg, int len, uint8_t *val)
|
||||
{
|
||||
return __tps6586x_reads(to_i2c_client(dev), reg, len, val);
|
||||
struct tps6586x *tps6586x = dev_to_tps6586x(dev);
|
||||
|
||||
return regmap_bulk_read(tps6586x->regmap, reg, val, len);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tps6586x_reads);
|
||||
|
||||
int tps6586x_set_bits(struct device *dev, int reg, uint8_t bit_mask)
|
||||
{
|
||||
struct tps6586x *tps6586x = dev_get_drvdata(dev);
|
||||
uint8_t reg_val;
|
||||
int ret = 0;
|
||||
struct tps6586x *tps6586x = dev_to_tps6586x(dev);
|
||||
|
||||
mutex_lock(&tps6586x->lock);
|
||||
|
||||
ret = __tps6586x_read(to_i2c_client(dev), reg, ®_val);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if ((reg_val & bit_mask) != bit_mask) {
|
||||
reg_val |= bit_mask;
|
||||
ret = __tps6586x_write(to_i2c_client(dev), reg, reg_val);
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&tps6586x->lock);
|
||||
return ret;
|
||||
return regmap_update_bits(tps6586x->regmap, reg, bit_mask, bit_mask);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tps6586x_set_bits);
|
||||
|
||||
int tps6586x_clr_bits(struct device *dev, int reg, uint8_t bit_mask)
|
||||
{
|
||||
struct tps6586x *tps6586x = dev_get_drvdata(dev);
|
||||
uint8_t reg_val;
|
||||
int ret = 0;
|
||||
struct tps6586x *tps6586x = dev_to_tps6586x(dev);
|
||||
|
||||
mutex_lock(&tps6586x->lock);
|
||||
|
||||
ret = __tps6586x_read(to_i2c_client(dev), reg, ®_val);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (reg_val & bit_mask) {
|
||||
reg_val &= ~bit_mask;
|
||||
ret = __tps6586x_write(to_i2c_client(dev), reg, reg_val);
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&tps6586x->lock);
|
||||
return ret;
|
||||
return regmap_update_bits(tps6586x->regmap, reg, bit_mask, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tps6586x_clr_bits);
|
||||
|
||||
int tps6586x_update(struct device *dev, int reg, uint8_t val, uint8_t mask)
|
||||
{
|
||||
struct tps6586x *tps6586x = dev_get_drvdata(dev);
|
||||
uint8_t reg_val;
|
||||
int ret = 0;
|
||||
struct tps6586x *tps6586x = dev_to_tps6586x(dev);
|
||||
|
||||
mutex_lock(&tps6586x->lock);
|
||||
|
||||
ret = __tps6586x_read(tps6586x->client, reg, ®_val);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if ((reg_val & mask) != val) {
|
||||
reg_val = (reg_val & ~mask) | val;
|
||||
ret = __tps6586x_write(tps6586x->client, reg, reg_val);
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&tps6586x->lock);
|
||||
return ret;
|
||||
return regmap_update_bits(tps6586x->regmap, reg, mask, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tps6586x_update);
|
||||
|
||||
static int tps6586x_gpio_get(struct gpio_chip *gc, unsigned offset)
|
||||
{
|
||||
struct tps6586x *tps6586x = container_of(gc, struct tps6586x, gpio);
|
||||
uint8_t val;
|
||||
int ret;
|
||||
|
||||
ret = __tps6586x_read(tps6586x->client, TPS6586X_GPIOSET2, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return !!(val & (1 << offset));
|
||||
}
|
||||
|
||||
|
||||
static void tps6586x_gpio_set(struct gpio_chip *chip, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
struct tps6586x *tps6586x = container_of(chip, struct tps6586x, gpio);
|
||||
|
||||
tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET2,
|
||||
value << offset, 1 << offset);
|
||||
}
|
||||
|
||||
static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
struct tps6586x *tps6586x = container_of(gc, struct tps6586x, gpio);
|
||||
uint8_t val, mask;
|
||||
|
||||
tps6586x_gpio_set(gc, offset, value);
|
||||
|
||||
val = 0x1 << (offset * 2);
|
||||
mask = 0x3 << (offset * 2);
|
||||
|
||||
return tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET1, val, mask);
|
||||
}
|
||||
|
||||
static int tps6586x_gpio_init(struct tps6586x *tps6586x, int gpio_base)
|
||||
{
|
||||
if (!gpio_base)
|
||||
return 0;
|
||||
|
||||
tps6586x->gpio.owner = THIS_MODULE;
|
||||
tps6586x->gpio.label = tps6586x->client->name;
|
||||
tps6586x->gpio.dev = tps6586x->dev;
|
||||
tps6586x->gpio.base = gpio_base;
|
||||
tps6586x->gpio.ngpio = 4;
|
||||
tps6586x->gpio.can_sleep = 1;
|
||||
|
||||
/* FIXME: add handling of GPIOs as dedicated inputs */
|
||||
tps6586x->gpio.direction_output = tps6586x_gpio_output;
|
||||
tps6586x->gpio.set = tps6586x_gpio_set;
|
||||
tps6586x->gpio.get = tps6586x_gpio_get;
|
||||
|
||||
return gpiochip_add(&tps6586x->gpio);
|
||||
}
|
||||
|
||||
static int __remove_subdev(struct device *dev, void *unused)
|
||||
{
|
||||
platform_device_unregister(to_platform_device(dev));
|
||||
|
@ -354,12 +224,11 @@ static void tps6586x_irq_sync_unlock(struct irq_data *data)
|
|||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tps6586x->mask_reg); i++) {
|
||||
if (tps6586x->mask_reg[i] != tps6586x->mask_cache[i]) {
|
||||
if (!WARN_ON(tps6586x_write(tps6586x->dev,
|
||||
TPS6586X_INT_MASK1 + i,
|
||||
tps6586x->mask_reg[i])))
|
||||
tps6586x->mask_cache[i] = tps6586x->mask_reg[i];
|
||||
}
|
||||
int ret;
|
||||
ret = tps6586x_write(tps6586x->dev,
|
||||
TPS6586X_INT_MASK1 + i,
|
||||
tps6586x->mask_reg[i]);
|
||||
WARN_ON(ret);
|
||||
}
|
||||
|
||||
mutex_unlock(&tps6586x->irq_lock);
|
||||
|
@ -406,7 +275,6 @@ static int __devinit tps6586x_irq_init(struct tps6586x *tps6586x, int irq,
|
|||
|
||||
mutex_init(&tps6586x->irq_lock);
|
||||
for (i = 0; i < 5; i++) {
|
||||
tps6586x->mask_cache[i] = 0xff;
|
||||
tps6586x->mask_reg[i] = 0xff;
|
||||
tps6586x_write(tps6586x->dev, TPS6586X_INT_MASK1 + i, 0xff);
|
||||
}
|
||||
|
@ -556,6 +424,23 @@ static struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *clien
|
|||
}
|
||||
#endif
|
||||
|
||||
static bool is_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
/* Cache all interrupt mask register */
|
||||
if ((reg >= TPS6586X_INT_MASK1) && (reg <= TPS6586X_INT_MASK5))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct regmap_config tps6586x_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = TPS6586X_MAX_REGISTER - 1,
|
||||
.volatile_reg = is_volatile_reg,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
@ -579,29 +464,39 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
|
|||
|
||||
dev_info(&client->dev, "VERSIONCRC is %02x\n", ret);
|
||||
|
||||
tps6586x = kzalloc(sizeof(struct tps6586x), GFP_KERNEL);
|
||||
if (tps6586x == NULL)
|
||||
tps6586x = devm_kzalloc(&client->dev, sizeof(*tps6586x), GFP_KERNEL);
|
||||
if (tps6586x == NULL) {
|
||||
dev_err(&client->dev, "memory for tps6586x alloc failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tps6586x->client = client;
|
||||
tps6586x->dev = &client->dev;
|
||||
i2c_set_clientdata(client, tps6586x);
|
||||
|
||||
mutex_init(&tps6586x->lock);
|
||||
tps6586x->regmap = devm_regmap_init_i2c(client,
|
||||
&tps6586x_regmap_config);
|
||||
if (IS_ERR(tps6586x->regmap)) {
|
||||
ret = PTR_ERR(tps6586x->regmap);
|
||||
dev_err(&client->dev, "regmap init failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
if (client->irq) {
|
||||
ret = tps6586x_irq_init(tps6586x, client->irq,
|
||||
pdata->irq_base);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "IRQ init failed: %d\n", ret);
|
||||
goto err_irq_init;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = tps6586x_gpio_init(tps6586x, pdata->gpio_base);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "GPIO registration failed: %d\n", ret);
|
||||
goto err_gpio_init;
|
||||
ret = mfd_add_devices(tps6586x->dev, -1,
|
||||
tps6586x_cell, ARRAY_SIZE(tps6586x_cell), NULL, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "mfd_add_devices failed: %d\n", ret);
|
||||
goto err_mfd_add;
|
||||
}
|
||||
|
||||
ret = tps6586x_add_subdevs(tps6586x, pdata);
|
||||
|
@ -613,38 +508,21 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
|
|||
return 0;
|
||||
|
||||
err_add_devs:
|
||||
if (pdata->gpio_base) {
|
||||
ret = gpiochip_remove(&tps6586x->gpio);
|
||||
if (ret)
|
||||
dev_err(&client->dev, "Can't remove gpio chip: %d\n",
|
||||
ret);
|
||||
}
|
||||
err_gpio_init:
|
||||
mfd_remove_devices(tps6586x->dev);
|
||||
err_mfd_add:
|
||||
if (client->irq)
|
||||
free_irq(client->irq, tps6586x);
|
||||
err_irq_init:
|
||||
kfree(tps6586x);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit tps6586x_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct tps6586x *tps6586x = i2c_get_clientdata(client);
|
||||
struct tps6586x_platform_data *pdata = client->dev.platform_data;
|
||||
int ret;
|
||||
|
||||
if (client->irq)
|
||||
free_irq(client->irq, tps6586x);
|
||||
|
||||
if (pdata->gpio_base) {
|
||||
ret = gpiochip_remove(&tps6586x->gpio);
|
||||
if (ret)
|
||||
dev_err(&client->dev, "Can't remove gpio chip: %d\n",
|
||||
ret);
|
||||
}
|
||||
|
||||
tps6586x_remove_subdevs(tps6586x);
|
||||
kfree(tps6586x);
|
||||
mfd_remove_devices(tps6586x->dev);
|
||||
if (client->irq)
|
||||
free_irq(client->irq, tps6586x);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -68,6 +68,24 @@ static const struct regmap_config tps65910_regmap_config = {
|
|||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static int __devinit tps65910_ck32k_init(struct tps65910 *tps65910,
|
||||
struct tps65910_board *pmic_pdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!pmic_pdata->en_ck32k_xtal)
|
||||
return 0;
|
||||
|
||||
ret = tps65910_reg_clear_bits(tps65910, TPS65910_DEVCTRL,
|
||||
DEVCTRL_CK32K_CTRL_MASK);
|
||||
if (ret < 0) {
|
||||
dev_err(tps65910->dev, "clear ck32k_ctrl failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit tps65910_sleepinit(struct tps65910 *tps65910,
|
||||
struct tps65910_board *pmic_pdata)
|
||||
{
|
||||
|
@ -175,6 +193,9 @@ static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client,
|
|||
else if (*chip_id == TPS65911)
|
||||
dev_warn(&client->dev, "VMBCH2-Threshold not specified");
|
||||
|
||||
prop = of_property_read_bool(np, "ti,en-ck32k-xtal");
|
||||
board_info->en_ck32k_xtal = prop;
|
||||
|
||||
board_info->irq = client->irq;
|
||||
board_info->irq_base = -1;
|
||||
|
||||
|
@ -243,7 +264,7 @@ static __devinit int tps65910_i2c_probe(struct i2c_client *i2c,
|
|||
init_data->irq_base = pmic_plat_data->irq_base;
|
||||
|
||||
tps65910_irq_init(tps65910, init_data->irq, init_data);
|
||||
|
||||
tps65910_ck32k_init(tps65910, pmic_plat_data);
|
||||
tps65910_sleepinit(tps65910, pmic_plat_data);
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -568,7 +568,6 @@ add_numbered_child(unsigned chip, const char *name, int num,
|
|||
goto err;
|
||||
}
|
||||
|
||||
device_init_wakeup(&pdev->dev, can_wakeup);
|
||||
pdev->dev.parent = &twl->client->dev;
|
||||
|
||||
if (pdata) {
|
||||
|
@ -593,6 +592,8 @@ add_numbered_child(unsigned chip, const char *name, int num,
|
|||
}
|
||||
|
||||
status = platform_device_add(pdev);
|
||||
if (status == 0)
|
||||
device_init_wakeup(&pdev->dev, can_wakeup);
|
||||
|
||||
err:
|
||||
if (status < 0) {
|
||||
|
|
|
@ -64,19 +64,15 @@ int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg)
|
|||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
mutex_lock(&twl6040->io_mutex);
|
||||
/* Vibra control registers from cache */
|
||||
if (unlikely(reg == TWL6040_REG_VIBCTLL ||
|
||||
reg == TWL6040_REG_VIBCTLR)) {
|
||||
val = twl6040->vibra_ctrl_cache[VIBRACTRL_MEMBER(reg)];
|
||||
} else {
|
||||
ret = regmap_read(twl6040->regmap, reg, &val);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&twl6040->io_mutex);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&twl6040->io_mutex);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
@ -86,12 +82,10 @@ int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg, u8 val)
|
|||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&twl6040->io_mutex);
|
||||
ret = regmap_write(twl6040->regmap, reg, val);
|
||||
/* Cache the vibra control registers */
|
||||
if (reg == TWL6040_REG_VIBCTLL || reg == TWL6040_REG_VIBCTLR)
|
||||
twl6040->vibra_ctrl_cache[VIBRACTRL_MEMBER(reg)] = val;
|
||||
mutex_unlock(&twl6040->io_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -99,23 +93,13 @@ EXPORT_SYMBOL(twl6040_reg_write);
|
|||
|
||||
int twl6040_set_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&twl6040->io_mutex);
|
||||
ret = regmap_update_bits(twl6040->regmap, reg, mask, mask);
|
||||
mutex_unlock(&twl6040->io_mutex);
|
||||
return ret;
|
||||
return regmap_update_bits(twl6040->regmap, reg, mask, mask);
|
||||
}
|
||||
EXPORT_SYMBOL(twl6040_set_bits);
|
||||
|
||||
int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&twl6040->io_mutex);
|
||||
ret = regmap_update_bits(twl6040->regmap, reg, mask, 0);
|
||||
mutex_unlock(&twl6040->io_mutex);
|
||||
return ret;
|
||||
return regmap_update_bits(twl6040->regmap, reg, mask, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(twl6040_clear_bits);
|
||||
|
||||
|
@ -573,7 +557,6 @@ static int __devinit twl6040_probe(struct i2c_client *client,
|
|||
twl6040->irq = client->irq;
|
||||
|
||||
mutex_init(&twl6040->mutex);
|
||||
mutex_init(&twl6040->io_mutex);
|
||||
init_completion(&twl6040->ready);
|
||||
|
||||
twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV);
|
||||
|
@ -696,6 +679,7 @@ static int __devexit twl6040_remove(struct i2c_client *client)
|
|||
|
||||
static const struct i2c_device_id twl6040_i2c_id[] = {
|
||||
{ "twl6040", 0, },
|
||||
{ "twl6041", 0, },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, twl6040_i2c_id);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -32,9 +32,6 @@
|
|||
#include <linux/mfd/wm8350/supply.h>
|
||||
#include <linux/mfd/wm8350/wdt.h>
|
||||
|
||||
#define WM8350_UNLOCK_KEY 0x0013
|
||||
#define WM8350_LOCK_KEY 0x0000
|
||||
|
||||
#define WM8350_CLOCK_CONTROL_1 0x28
|
||||
#define WM8350_AIF_TEST 0x74
|
||||
|
||||
|
@ -63,181 +60,32 @@
|
|||
/*
|
||||
* WM8350 Device IO
|
||||
*/
|
||||
static DEFINE_MUTEX(io_mutex);
|
||||
static DEFINE_MUTEX(reg_lock_mutex);
|
||||
|
||||
/* Perform a physical read from the device.
|
||||
*/
|
||||
static int wm8350_phys_read(struct wm8350 *wm8350, u8 reg, int num_regs,
|
||||
u16 *dest)
|
||||
{
|
||||
int i, ret;
|
||||
int bytes = num_regs * 2;
|
||||
|
||||
dev_dbg(wm8350->dev, "volatile read\n");
|
||||
ret = regmap_raw_read(wm8350->regmap, reg, dest, bytes);
|
||||
|
||||
for (i = reg; i < reg + num_regs; i++) {
|
||||
/* Cache is CPU endian */
|
||||
dest[i - reg] = be16_to_cpu(dest[i - reg]);
|
||||
|
||||
/* Mask out non-readable bits */
|
||||
dest[i - reg] &= wm8350_reg_io_map[i].readable;
|
||||
}
|
||||
|
||||
dump(num_regs, dest);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wm8350_read(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *dest)
|
||||
{
|
||||
int i;
|
||||
int end = reg + num_regs;
|
||||
int ret = 0;
|
||||
int bytes = num_regs * 2;
|
||||
|
||||
if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) {
|
||||
dev_err(wm8350->dev, "invalid reg %x\n",
|
||||
reg + num_regs - 1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(wm8350->dev,
|
||||
"%s R%d(0x%2.2x) %d regs\n", __func__, reg, reg, num_regs);
|
||||
|
||||
#if WM8350_BUS_DEBUG
|
||||
/* we can _safely_ read any register, but warn if read not supported */
|
||||
for (i = reg; i < end; i++) {
|
||||
if (!wm8350_reg_io_map[i].readable)
|
||||
dev_warn(wm8350->dev,
|
||||
"reg R%d is not readable\n", i);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* if any volatile registers are required, then read back all */
|
||||
for (i = reg; i < end; i++)
|
||||
if (wm8350_reg_io_map[i].vol)
|
||||
return wm8350_phys_read(wm8350, reg, num_regs, dest);
|
||||
|
||||
/* no volatiles, then cache is good */
|
||||
dev_dbg(wm8350->dev, "cache read\n");
|
||||
memcpy(dest, &wm8350->reg_cache[reg], bytes);
|
||||
dump(num_regs, dest);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int is_reg_locked(struct wm8350 *wm8350, u8 reg)
|
||||
{
|
||||
if (reg == WM8350_SECURITY ||
|
||||
wm8350->reg_cache[WM8350_SECURITY] == WM8350_UNLOCK_KEY)
|
||||
return 0;
|
||||
|
||||
if ((reg >= WM8350_GPIO_FUNCTION_SELECT_1 &&
|
||||
reg <= WM8350_GPIO_FUNCTION_SELECT_4) ||
|
||||
(reg >= WM8350_BATTERY_CHARGER_CONTROL_1 &&
|
||||
reg <= WM8350_BATTERY_CHARGER_CONTROL_3))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm8350_write(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *src)
|
||||
{
|
||||
int i;
|
||||
int end = reg + num_regs;
|
||||
int bytes = num_regs * 2;
|
||||
|
||||
if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) {
|
||||
dev_err(wm8350->dev, "invalid reg %x\n",
|
||||
reg + num_regs - 1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* it's generally not a good idea to write to RO or locked registers */
|
||||
for (i = reg; i < end; i++) {
|
||||
if (!wm8350_reg_io_map[i].writable) {
|
||||
dev_err(wm8350->dev,
|
||||
"attempted write to read only reg R%d\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (is_reg_locked(wm8350, i)) {
|
||||
dev_err(wm8350->dev,
|
||||
"attempted write to locked reg R%d\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
src[i - reg] &= wm8350_reg_io_map[i].writable;
|
||||
|
||||
wm8350->reg_cache[i] =
|
||||
(wm8350->reg_cache[i] & ~wm8350_reg_io_map[i].writable)
|
||||
| src[i - reg];
|
||||
|
||||
src[i - reg] = cpu_to_be16(src[i - reg]);
|
||||
}
|
||||
|
||||
/* Actually write it out */
|
||||
return regmap_raw_write(wm8350->regmap, reg, src, bytes);
|
||||
}
|
||||
|
||||
/*
|
||||
* Safe read, modify, write methods
|
||||
*/
|
||||
int wm8350_clear_bits(struct wm8350 *wm8350, u16 reg, u16 mask)
|
||||
{
|
||||
u16 data;
|
||||
int err;
|
||||
|
||||
mutex_lock(&io_mutex);
|
||||
err = wm8350_read(wm8350, reg, 1, &data);
|
||||
if (err) {
|
||||
dev_err(wm8350->dev, "read from reg R%d failed\n", reg);
|
||||
goto out;
|
||||
}
|
||||
|
||||
data &= ~mask;
|
||||
err = wm8350_write(wm8350, reg, 1, &data);
|
||||
if (err)
|
||||
dev_err(wm8350->dev, "write to reg R%d failed\n", reg);
|
||||
out:
|
||||
mutex_unlock(&io_mutex);
|
||||
return err;
|
||||
return regmap_update_bits(wm8350->regmap, reg, mask, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm8350_clear_bits);
|
||||
|
||||
int wm8350_set_bits(struct wm8350 *wm8350, u16 reg, u16 mask)
|
||||
{
|
||||
u16 data;
|
||||
int err;
|
||||
|
||||
mutex_lock(&io_mutex);
|
||||
err = wm8350_read(wm8350, reg, 1, &data);
|
||||
if (err) {
|
||||
dev_err(wm8350->dev, "read from reg R%d failed\n", reg);
|
||||
goto out;
|
||||
}
|
||||
|
||||
data |= mask;
|
||||
err = wm8350_write(wm8350, reg, 1, &data);
|
||||
if (err)
|
||||
dev_err(wm8350->dev, "write to reg R%d failed\n", reg);
|
||||
out:
|
||||
mutex_unlock(&io_mutex);
|
||||
return err;
|
||||
return regmap_update_bits(wm8350->regmap, reg, mask, mask);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm8350_set_bits);
|
||||
|
||||
u16 wm8350_reg_read(struct wm8350 *wm8350, int reg)
|
||||
{
|
||||
u16 data;
|
||||
unsigned int data;
|
||||
int err;
|
||||
|
||||
mutex_lock(&io_mutex);
|
||||
err = wm8350_read(wm8350, reg, 1, &data);
|
||||
err = regmap_read(wm8350->regmap, reg, &data);
|
||||
if (err)
|
||||
dev_err(wm8350->dev, "read from reg R%d failed\n", reg);
|
||||
|
||||
mutex_unlock(&io_mutex);
|
||||
return data;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm8350_reg_read);
|
||||
|
@ -245,13 +93,11 @@ EXPORT_SYMBOL_GPL(wm8350_reg_read);
|
|||
int wm8350_reg_write(struct wm8350 *wm8350, int reg, u16 val)
|
||||
{
|
||||
int ret;
|
||||
u16 data = val;
|
||||
|
||||
mutex_lock(&io_mutex);
|
||||
ret = wm8350_write(wm8350, reg, 1, &data);
|
||||
ret = regmap_write(wm8350->regmap, reg, val);
|
||||
|
||||
if (ret)
|
||||
dev_err(wm8350->dev, "write to reg R%d failed\n", reg);
|
||||
mutex_unlock(&io_mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm8350_reg_write);
|
||||
|
@ -261,12 +107,11 @@ int wm8350_block_read(struct wm8350 *wm8350, int start_reg, int regs,
|
|||
{
|
||||
int err = 0;
|
||||
|
||||
mutex_lock(&io_mutex);
|
||||
err = wm8350_read(wm8350, start_reg, regs, dest);
|
||||
err = regmap_bulk_read(wm8350->regmap, start_reg, dest, regs);
|
||||
if (err)
|
||||
dev_err(wm8350->dev, "block read starting from R%d failed\n",
|
||||
start_reg);
|
||||
mutex_unlock(&io_mutex);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm8350_block_read);
|
||||
|
@ -276,12 +121,11 @@ int wm8350_block_write(struct wm8350 *wm8350, int start_reg, int regs,
|
|||
{
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&io_mutex);
|
||||
ret = wm8350_write(wm8350, start_reg, regs, src);
|
||||
ret = regmap_bulk_write(wm8350->regmap, start_reg, src, regs);
|
||||
if (ret)
|
||||
dev_err(wm8350->dev, "block write starting at R%d failed\n",
|
||||
start_reg);
|
||||
mutex_unlock(&io_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm8350_block_write);
|
||||
|
@ -295,15 +139,20 @@ EXPORT_SYMBOL_GPL(wm8350_block_write);
|
|||
*/
|
||||
int wm8350_reg_lock(struct wm8350 *wm8350)
|
||||
{
|
||||
u16 key = WM8350_LOCK_KEY;
|
||||
int ret;
|
||||
|
||||
mutex_lock(®_lock_mutex);
|
||||
|
||||
ldbg(__func__);
|
||||
mutex_lock(&io_mutex);
|
||||
ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key);
|
||||
|
||||
ret = wm8350_reg_write(wm8350, WM8350_SECURITY, WM8350_LOCK_KEY);
|
||||
if (ret)
|
||||
dev_err(wm8350->dev, "lock failed\n");
|
||||
mutex_unlock(&io_mutex);
|
||||
|
||||
wm8350->unlocked = false;
|
||||
|
||||
mutex_unlock(®_lock_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm8350_reg_lock);
|
||||
|
@ -319,15 +168,20 @@ EXPORT_SYMBOL_GPL(wm8350_reg_lock);
|
|||
*/
|
||||
int wm8350_reg_unlock(struct wm8350 *wm8350)
|
||||
{
|
||||
u16 key = WM8350_UNLOCK_KEY;
|
||||
int ret;
|
||||
|
||||
mutex_lock(®_lock_mutex);
|
||||
|
||||
ldbg(__func__);
|
||||
mutex_lock(&io_mutex);
|
||||
ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key);
|
||||
|
||||
ret = wm8350_reg_write(wm8350, WM8350_SECURITY, WM8350_UNLOCK_KEY);
|
||||
if (ret)
|
||||
dev_err(wm8350->dev, "unlock failed\n");
|
||||
mutex_unlock(&io_mutex);
|
||||
|
||||
wm8350->unlocked = true;
|
||||
|
||||
mutex_unlock(®_lock_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm8350_reg_unlock);
|
||||
|
@ -394,146 +248,6 @@ static irqreturn_t wm8350_auxadc_irq(int irq, void *irq_data)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cache is always host endian.
|
||||
*/
|
||||
static int wm8350_create_cache(struct wm8350 *wm8350, int type, int mode)
|
||||
{
|
||||
int i, ret = 0;
|
||||
u16 value;
|
||||
const u16 *reg_map;
|
||||
|
||||
switch (type) {
|
||||
case 0:
|
||||
switch (mode) {
|
||||
#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0
|
||||
case 0:
|
||||
reg_map = wm8350_mode0_defaults;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1
|
||||
case 1:
|
||||
reg_map = wm8350_mode1_defaults;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2
|
||||
case 2:
|
||||
reg_map = wm8350_mode2_defaults;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3
|
||||
case 3:
|
||||
reg_map = wm8350_mode3_defaults;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
dev_err(wm8350->dev,
|
||||
"WM8350 configuration mode %d not supported\n",
|
||||
mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
switch (mode) {
|
||||
#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_0
|
||||
case 0:
|
||||
reg_map = wm8351_mode0_defaults;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_1
|
||||
case 1:
|
||||
reg_map = wm8351_mode1_defaults;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_2
|
||||
case 2:
|
||||
reg_map = wm8351_mode2_defaults;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_MFD_WM8351_CONFIG_MODE_3
|
||||
case 3:
|
||||
reg_map = wm8351_mode3_defaults;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
dev_err(wm8350->dev,
|
||||
"WM8351 configuration mode %d not supported\n",
|
||||
mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
switch (mode) {
|
||||
#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_0
|
||||
case 0:
|
||||
reg_map = wm8352_mode0_defaults;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_1
|
||||
case 1:
|
||||
reg_map = wm8352_mode1_defaults;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_2
|
||||
case 2:
|
||||
reg_map = wm8352_mode2_defaults;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_MFD_WM8352_CONFIG_MODE_3
|
||||
case 3:
|
||||
reg_map = wm8352_mode3_defaults;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
dev_err(wm8350->dev,
|
||||
"WM8352 configuration mode %d not supported\n",
|
||||
mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(wm8350->dev,
|
||||
"WM835x configuration mode %d not supported\n",
|
||||
mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
wm8350->reg_cache =
|
||||
kmalloc(sizeof(u16) * (WM8350_MAX_REGISTER + 1), GFP_KERNEL);
|
||||
if (wm8350->reg_cache == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Read the initial cache state back from the device - this is
|
||||
* a PMIC so the device many not be in a virgin state and we
|
||||
* can't rely on the silicon values.
|
||||
*/
|
||||
ret = regmap_raw_read(wm8350->regmap, 0, wm8350->reg_cache,
|
||||
sizeof(u16) * (WM8350_MAX_REGISTER + 1));
|
||||
if (ret < 0) {
|
||||
dev_err(wm8350->dev,
|
||||
"failed to read initial cache values\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Mask out uncacheable/unreadable bits and the audio. */
|
||||
for (i = 0; i < WM8350_MAX_REGISTER; i++) {
|
||||
if (wm8350_reg_io_map[i].readable &&
|
||||
(i < WM8350_CLOCK_CONTROL_1 || i > WM8350_AIF_TEST)) {
|
||||
value = be16_to_cpu(wm8350->reg_cache[i]);
|
||||
value &= wm8350_reg_io_map[i].readable;
|
||||
wm8350->reg_cache[i] = value;
|
||||
} else
|
||||
wm8350->reg_cache[i] = reg_map[i];
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(wm8350->reg_cache);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register a client device. This is non-fatal since there is no need to
|
||||
* fail the entire device init due to a single platform device failing.
|
||||
|
@ -681,18 +395,12 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
|
|||
goto err;
|
||||
}
|
||||
|
||||
ret = wm8350_create_cache(wm8350, mask_rev, mode);
|
||||
if (ret < 0) {
|
||||
dev_err(wm8350->dev, "Failed to create register cache\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_init(&wm8350->auxadc_mutex);
|
||||
init_completion(&wm8350->auxadc_done);
|
||||
|
||||
ret = wm8350_irq_init(wm8350, irq, pdata);
|
||||
if (ret < 0)
|
||||
goto err_free;
|
||||
goto err;
|
||||
|
||||
if (wm8350->irq_base) {
|
||||
ret = request_threaded_irq(wm8350->irq_base +
|
||||
|
@ -730,8 +438,6 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
|
|||
|
||||
err_irq:
|
||||
wm8350_irq_exit(wm8350);
|
||||
err_free:
|
||||
kfree(wm8350->reg_cache);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
@ -758,8 +464,6 @@ void wm8350_device_exit(struct wm8350 *wm8350)
|
|||
free_irq(wm8350->irq_base + WM8350_IRQ_AUXADC_DATARDY, wm8350);
|
||||
|
||||
wm8350_irq_exit(wm8350);
|
||||
|
||||
kfree(wm8350->reg_cache);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm8350_device_exit);
|
||||
|
||||
|
|
|
@ -23,11 +23,6 @@
|
|||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
static const struct regmap_config wm8350_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
};
|
||||
|
||||
static int wm8350_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
|
|
@ -432,11 +432,9 @@ static void wm8350_irq_sync_unlock(struct irq_data *data)
|
|||
for (i = 0; i < ARRAY_SIZE(wm8350->irq_masks); i++) {
|
||||
/* If there's been a change in the mask write it back
|
||||
* to the hardware. */
|
||||
if (wm8350->irq_masks[i] !=
|
||||
wm8350->reg_cache[WM8350_INT_STATUS_1_MASK + i])
|
||||
WARN_ON(wm8350_reg_write(wm8350,
|
||||
WM8350_INT_STATUS_1_MASK + i,
|
||||
wm8350->irq_masks[i]));
|
||||
WARN_ON(regmap_update_bits(wm8350->regmap,
|
||||
WM8350_INT_STATUS_1_MASK + i,
|
||||
0xffff, wm8350->irq_masks[i]));
|
||||
}
|
||||
|
||||
mutex_unlock(&wm8350->irq_lock);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -283,9 +283,24 @@ static int wm8994_suspend(struct device *dev)
|
|||
wm8994_reg_write(wm8994, WM8994_SOFTWARE_RESET,
|
||||
wm8994_reg_read(wm8994, WM8994_SOFTWARE_RESET));
|
||||
|
||||
regcache_cache_only(wm8994->regmap, true);
|
||||
regcache_mark_dirty(wm8994->regmap);
|
||||
|
||||
/* Restore GPIO registers to prevent problems with mismatched
|
||||
* pin configurations.
|
||||
*/
|
||||
ret = regcache_sync_region(wm8994->regmap, WM8994_GPIO_1,
|
||||
WM8994_GPIO_11);
|
||||
if (ret != 0)
|
||||
dev_err(dev, "Failed to restore GPIO registers: %d\n", ret);
|
||||
|
||||
/* In case one of the GPIOs is used as a wake input. */
|
||||
ret = regcache_sync_region(wm8994->regmap,
|
||||
WM8994_INTERRUPT_STATUS_1_MASK,
|
||||
WM8994_INTERRUPT_STATUS_1_MASK);
|
||||
if (ret != 0)
|
||||
dev_err(dev, "Failed to restore interrupt mask: %d\n", ret);
|
||||
|
||||
regcache_cache_only(wm8994->regmap, true);
|
||||
wm8994->suspended = true;
|
||||
|
||||
ret = regulator_bulk_disable(wm8994->num_supplies,
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/mfd/wm8994/core.h>
|
||||
#include <linux/mfd/wm8994/pdata.h>
|
||||
#include <linux/mfd/wm8994/registers.h>
|
||||
|
||||
#include <linux/delay.h>
|
||||
|
@ -139,6 +140,8 @@ static struct regmap_irq_chip wm8994_irq_chip = {
|
|||
int wm8994_irq_init(struct wm8994 *wm8994)
|
||||
{
|
||||
int ret;
|
||||
unsigned long irqflags;
|
||||
struct wm8994_pdata *pdata = wm8994->dev->platform_data;
|
||||
|
||||
if (!wm8994->irq) {
|
||||
dev_warn(wm8994->dev,
|
||||
|
@ -147,8 +150,13 @@ int wm8994_irq_init(struct wm8994 *wm8994)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* select user or default irq flags */
|
||||
irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
|
||||
if (pdata->irq_flags)
|
||||
irqflags = pdata->irq_flags;
|
||||
|
||||
ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq,
|
||||
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||
irqflags,
|
||||
wm8994->irq_base, &wm8994_irq_chip,
|
||||
&wm8994->irq_data);
|
||||
if (ret != 0) {
|
||||
|
|
|
@ -142,16 +142,10 @@ static int __devexit ab8500_pwm_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ab8500_pwm_match[] = {
|
||||
{ .compatible = "stericsson,ab8500-pwm", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver ab8500_pwm_driver = {
|
||||
.driver = {
|
||||
.name = "ab8500-pwm",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = ab8500_pwm_match,
|
||||
},
|
||||
.probe = ab8500_pwm_probe,
|
||||
.remove = __devexit_p(ab8500_pwm_remove),
|
||||
|
|
|
@ -272,7 +272,7 @@ config REGULATOR_S2MPS11
|
|||
|
||||
config REGULATOR_S5M8767
|
||||
tristate "Samsung S5M8767A voltage regulator"
|
||||
depends on MFD_S5M_CORE
|
||||
depends on MFD_SEC_CORE
|
||||
help
|
||||
This driver supports a Samsung S5M8767A voltage output regulator
|
||||
via I2C bus. S5M8767A have 9 Bucks and 28 LDOs output and
|
||||
|
|
|
@ -848,18 +848,12 @@ static __devexit int ab8500_regulator_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ab8500_regulator_match[] = {
|
||||
{ .compatible = "stericsson,ab8500-regulator", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver ab8500_regulator_driver = {
|
||||
.probe = ab8500_regulator_probe,
|
||||
.remove = __devexit_p(ab8500_regulator_remove),
|
||||
.driver = {
|
||||
.name = "ab8500-regulator",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = ab8500_regulator_match,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -547,16 +547,10 @@ static int __exit db8500_regulator_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id db8500_prcmu_regulator_match[] = {
|
||||
{ .compatible = "stericsson,db8500-prcmu-regulator", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver db8500_regulator_driver = {
|
||||
.driver = {
|
||||
.name = "db8500-prcmu-regulators",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = db8500_prcmu_regulator_match,
|
||||
},
|
||||
.probe = db8500_regulator_probe,
|
||||
.remove = __exit_p(db8500_regulator_remove),
|
||||
|
|
|
@ -19,15 +19,15 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/mfd/s5m87xx/s5m-core.h>
|
||||
#include <linux/mfd/s5m87xx/s5m-pmic.h>
|
||||
#include <linux/mfd/samsung/core.h>
|
||||
#include <linux/mfd/samsung/s5m8767.h>
|
||||
|
||||
struct s5m8767_info {
|
||||
struct device *dev;
|
||||
struct s5m87xx_dev *iodev;
|
||||
struct sec_pmic_dev *iodev;
|
||||
int num_regulators;
|
||||
struct regulator_dev **rdev;
|
||||
struct s5m_opmode_data *opmode;
|
||||
struct sec_opmode_data *opmode;
|
||||
|
||||
int ramp_delay;
|
||||
bool buck2_ramp;
|
||||
|
@ -45,43 +45,43 @@ struct s5m8767_info {
|
|||
int buck_gpioindex;
|
||||
};
|
||||
|
||||
struct s5m_voltage_desc {
|
||||
struct sec_voltage_desc {
|
||||
int max;
|
||||
int min;
|
||||
int step;
|
||||
};
|
||||
|
||||
static const struct s5m_voltage_desc buck_voltage_val1 = {
|
||||
static const struct sec_voltage_desc buck_voltage_val1 = {
|
||||
.max = 2225000,
|
||||
.min = 650000,
|
||||
.step = 6250,
|
||||
};
|
||||
|
||||
static const struct s5m_voltage_desc buck_voltage_val2 = {
|
||||
static const struct sec_voltage_desc buck_voltage_val2 = {
|
||||
.max = 1600000,
|
||||
.min = 600000,
|
||||
.step = 6250,
|
||||
};
|
||||
|
||||
static const struct s5m_voltage_desc buck_voltage_val3 = {
|
||||
static const struct sec_voltage_desc buck_voltage_val3 = {
|
||||
.max = 3000000,
|
||||
.min = 750000,
|
||||
.step = 12500,
|
||||
};
|
||||
|
||||
static const struct s5m_voltage_desc ldo_voltage_val1 = {
|
||||
static const struct sec_voltage_desc ldo_voltage_val1 = {
|
||||
.max = 3950000,
|
||||
.min = 800000,
|
||||
.step = 50000,
|
||||
};
|
||||
|
||||
static const struct s5m_voltage_desc ldo_voltage_val2 = {
|
||||
static const struct sec_voltage_desc ldo_voltage_val2 = {
|
||||
.max = 2375000,
|
||||
.min = 800000,
|
||||
.step = 25000,
|
||||
};
|
||||
|
||||
static const struct s5m_voltage_desc *reg_voltage_map[] = {
|
||||
static const struct sec_voltage_desc *reg_voltage_map[] = {
|
||||
[S5M8767_LDO1] = &ldo_voltage_val2,
|
||||
[S5M8767_LDO2] = &ldo_voltage_val2,
|
||||
[S5M8767_LDO3] = &ldo_voltage_val1,
|
||||
|
@ -213,7 +213,7 @@ static int s5m8767_reg_is_enabled(struct regulator_dev *rdev)
|
|||
else if (ret)
|
||||
return ret;
|
||||
|
||||
ret = s5m_reg_read(s5m8767->iodev, reg, &val);
|
||||
ret = sec_reg_read(s5m8767->iodev, reg, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -230,7 +230,7 @@ static int s5m8767_reg_enable(struct regulator_dev *rdev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return s5m_reg_update(s5m8767->iodev, reg, enable_ctrl, mask);
|
||||
return sec_reg_update(s5m8767->iodev, reg, enable_ctrl, mask);
|
||||
}
|
||||
|
||||
static int s5m8767_reg_disable(struct regulator_dev *rdev)
|
||||
|
@ -243,7 +243,7 @@ static int s5m8767_reg_disable(struct regulator_dev *rdev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return s5m_reg_update(s5m8767->iodev, reg, ~mask, mask);
|
||||
return sec_reg_update(s5m8767->iodev, reg, ~mask, mask);
|
||||
}
|
||||
|
||||
static int s5m8767_get_voltage_register(struct regulator_dev *rdev, int *_reg)
|
||||
|
@ -305,7 +305,7 @@ static int s5m8767_get_voltage_sel(struct regulator_dev *rdev)
|
|||
|
||||
mask = (reg_id < S5M8767_BUCK1) ? 0x3f : 0xff;
|
||||
|
||||
ret = s5m_reg_read(s5m8767->iodev, reg, &val);
|
||||
ret = sec_reg_read(s5m8767->iodev, reg, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -315,7 +315,7 @@ static int s5m8767_get_voltage_sel(struct regulator_dev *rdev)
|
|||
}
|
||||
|
||||
static int s5m8767_convert_voltage_to_sel(
|
||||
const struct s5m_voltage_desc *desc,
|
||||
const struct sec_voltage_desc *desc,
|
||||
int min_vol, int max_vol)
|
||||
{
|
||||
int selector = 0;
|
||||
|
@ -407,7 +407,7 @@ static int s5m8767_set_voltage_sel(struct regulator_dev *rdev,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return s5m_reg_update(s5m8767->iodev, reg, selector, mask);
|
||||
return sec_reg_update(s5m8767->iodev, reg, selector, mask);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -416,7 +416,7 @@ static int s5m8767_set_voltage_time_sel(struct regulator_dev *rdev,
|
|||
unsigned int new_sel)
|
||||
{
|
||||
struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev);
|
||||
const struct s5m_voltage_desc *desc;
|
||||
const struct sec_voltage_desc *desc;
|
||||
int reg_id = rdev_get_id(rdev);
|
||||
|
||||
desc = reg_voltage_map[reg_id];
|
||||
|
@ -501,8 +501,8 @@ static struct regulator_desc regulators[] = {
|
|||
|
||||
static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct s5m87xx_dev *iodev = dev_get_drvdata(pdev->dev.parent);
|
||||
struct s5m_platform_data *pdata = dev_get_platdata(iodev->dev);
|
||||
struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
|
||||
struct sec_platform_data *pdata = dev_get_platdata(iodev->dev);
|
||||
struct regulator_config config = { };
|
||||
struct regulator_dev **rdev;
|
||||
struct s5m8767_info *s5m8767;
|
||||
|
@ -671,13 +671,13 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
|
|||
|
||||
if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs ||
|
||||
pdata->buck4_gpiodvs) {
|
||||
s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK2CTRL,
|
||||
sec_reg_update(s5m8767->iodev, S5M8767_REG_BUCK2CTRL,
|
||||
(pdata->buck2_gpiodvs) ? (1 << 1) : (0 << 1),
|
||||
1 << 1);
|
||||
s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK3CTRL,
|
||||
sec_reg_update(s5m8767->iodev, S5M8767_REG_BUCK3CTRL,
|
||||
(pdata->buck3_gpiodvs) ? (1 << 1) : (0 << 1),
|
||||
1 << 1);
|
||||
s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK4CTRL,
|
||||
sec_reg_update(s5m8767->iodev, S5M8767_REG_BUCK4CTRL,
|
||||
(pdata->buck4_gpiodvs) ? (1 << 1) : (0 << 1),
|
||||
1 << 1);
|
||||
}
|
||||
|
@ -685,61 +685,61 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
|
|||
/* Initialize GPIO DVS registers */
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (s5m8767->buck2_gpiodvs) {
|
||||
s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK2DVS1 + i,
|
||||
sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK2DVS1 + i,
|
||||
s5m8767->buck2_vol[i]);
|
||||
}
|
||||
|
||||
if (s5m8767->buck3_gpiodvs) {
|
||||
s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK3DVS1 + i,
|
||||
sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK3DVS1 + i,
|
||||
s5m8767->buck3_vol[i]);
|
||||
}
|
||||
|
||||
if (s5m8767->buck4_gpiodvs) {
|
||||
s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK4DVS1 + i,
|
||||
sec_reg_write(s5m8767->iodev, S5M8767_REG_BUCK4DVS1 + i,
|
||||
s5m8767->buck4_vol[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (s5m8767->buck2_ramp)
|
||||
s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x08, 0x08);
|
||||
sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x08, 0x08);
|
||||
|
||||
if (s5m8767->buck3_ramp)
|
||||
s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x04, 0x04);
|
||||
sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x04, 0x04);
|
||||
|
||||
if (s5m8767->buck4_ramp)
|
||||
s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x02, 0x02);
|
||||
sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x02, 0x02);
|
||||
|
||||
if (s5m8767->buck2_ramp || s5m8767->buck3_ramp
|
||||
|| s5m8767->buck4_ramp) {
|
||||
switch (s5m8767->ramp_delay) {
|
||||
case 5:
|
||||
s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
|
||||
sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
|
||||
0x40, 0xf0);
|
||||
break;
|
||||
case 10:
|
||||
s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
|
||||
sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
|
||||
0x90, 0xf0);
|
||||
break;
|
||||
case 25:
|
||||
s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
|
||||
sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
|
||||
0xd0, 0xf0);
|
||||
break;
|
||||
case 50:
|
||||
s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
|
||||
sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
|
||||
0xe0, 0xf0);
|
||||
break;
|
||||
case 100:
|
||||
s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
|
||||
sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
|
||||
0xf0, 0xf0);
|
||||
break;
|
||||
default:
|
||||
s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
|
||||
sec_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP,
|
||||
0x90, 0xf0);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < pdata->num_regulators; i++) {
|
||||
const struct s5m_voltage_desc *desc;
|
||||
const struct sec_voltage_desc *desc;
|
||||
int id = pdata->regulators[i].id;
|
||||
|
||||
desc = reg_voltage_map[id];
|
||||
|
|
|
@ -135,6 +135,16 @@ config RTC_DRV_88PM860X
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-88pm860x.
|
||||
|
||||
config RTC_DRV_88PM80X
|
||||
tristate "Marvell 88PM80x"
|
||||
depends on RTC_CLASS && I2C && MFD_88PM800
|
||||
help
|
||||
If you say yes here you get support for RTC function in Marvell
|
||||
88PM80x chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-88pm80x.
|
||||
|
||||
config RTC_DRV_DS1307
|
||||
tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00, EPSON RX-8025"
|
||||
help
|
||||
|
|
|
@ -16,6 +16,7 @@ rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
|
|||
# Keep the list ordered.
|
||||
|
||||
obj-$(CONFIG_RTC_DRV_88PM860X) += rtc-88pm860x.o
|
||||
obj-$(CONFIG_RTC_DRV_88PM80X) += rtc-88pm80x.o
|
||||
obj-$(CONFIG_RTC_DRV_AB3100) += rtc-ab3100.o
|
||||
obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o
|
||||
obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o
|
||||
|
|
|
@ -0,0 +1,371 @@
|
|||
/*
|
||||
* Real Time Clock driver for Marvell 88PM80x PMIC
|
||||
*
|
||||
* Copyright (c) 2012 Marvell International Ltd.
|
||||
* Wenzeng Chen<wzch@marvell.com>
|
||||
* Qiao Zhou <zhouqiao@marvell.com>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General
|
||||
* Public License. See the file "COPYING" in the main directory of this
|
||||
* archive for more details.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/88pm80x.h>
|
||||
#include <linux/rtc.h>
|
||||
|
||||
#define PM800_RTC_COUNTER1 (0xD1)
|
||||
#define PM800_RTC_COUNTER2 (0xD2)
|
||||
#define PM800_RTC_COUNTER3 (0xD3)
|
||||
#define PM800_RTC_COUNTER4 (0xD4)
|
||||
#define PM800_RTC_EXPIRE1_1 (0xD5)
|
||||
#define PM800_RTC_EXPIRE1_2 (0xD6)
|
||||
#define PM800_RTC_EXPIRE1_3 (0xD7)
|
||||
#define PM800_RTC_EXPIRE1_4 (0xD8)
|
||||
#define PM800_RTC_TRIM1 (0xD9)
|
||||
#define PM800_RTC_TRIM2 (0xDA)
|
||||
#define PM800_RTC_TRIM3 (0xDB)
|
||||
#define PM800_RTC_TRIM4 (0xDC)
|
||||
#define PM800_RTC_EXPIRE2_1 (0xDD)
|
||||
#define PM800_RTC_EXPIRE2_2 (0xDE)
|
||||
#define PM800_RTC_EXPIRE2_3 (0xDF)
|
||||
#define PM800_RTC_EXPIRE2_4 (0xE0)
|
||||
|
||||
#define PM800_POWER_DOWN_LOG1 (0xE5)
|
||||
#define PM800_POWER_DOWN_LOG2 (0xE6)
|
||||
|
||||
struct pm80x_rtc_info {
|
||||
struct pm80x_chip *chip;
|
||||
struct regmap *map;
|
||||
struct rtc_device *rtc_dev;
|
||||
struct device *dev;
|
||||
struct delayed_work calib_work;
|
||||
|
||||
int irq;
|
||||
int vrtc;
|
||||
};
|
||||
|
||||
static irqreturn_t rtc_update_handler(int irq, void *data)
|
||||
{
|
||||
struct pm80x_rtc_info *info = (struct pm80x_rtc_info *)data;
|
||||
int mask;
|
||||
|
||||
mask = PM800_ALARM | PM800_ALARM_WAKEUP;
|
||||
regmap_update_bits(info->map, PM800_RTC_CONTROL, mask | PM800_ALARM1_EN,
|
||||
mask);
|
||||
rtc_update_irq(info->rtc_dev, 1, RTC_AF);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int pm80x_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct pm80x_rtc_info *info = dev_get_drvdata(dev);
|
||||
|
||||
if (enabled)
|
||||
regmap_update_bits(info->map, PM800_RTC_CONTROL,
|
||||
PM800_ALARM1_EN, PM800_ALARM1_EN);
|
||||
else
|
||||
regmap_update_bits(info->map, PM800_RTC_CONTROL,
|
||||
PM800_ALARM1_EN, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the next alarm time given the requested alarm time mask
|
||||
* and the current time.
|
||||
*/
|
||||
static void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now,
|
||||
struct rtc_time *alrm)
|
||||
{
|
||||
unsigned long next_time;
|
||||
unsigned long now_time;
|
||||
|
||||
next->tm_year = now->tm_year;
|
||||
next->tm_mon = now->tm_mon;
|
||||
next->tm_mday = now->tm_mday;
|
||||
next->tm_hour = alrm->tm_hour;
|
||||
next->tm_min = alrm->tm_min;
|
||||
next->tm_sec = alrm->tm_sec;
|
||||
|
||||
rtc_tm_to_time(now, &now_time);
|
||||
rtc_tm_to_time(next, &next_time);
|
||||
|
||||
if (next_time < now_time) {
|
||||
/* Advance one day */
|
||||
next_time += 60 * 60 * 24;
|
||||
rtc_time_to_tm(next_time, next);
|
||||
}
|
||||
}
|
||||
|
||||
static int pm80x_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct pm80x_rtc_info *info = dev_get_drvdata(dev);
|
||||
unsigned char buf[4];
|
||||
unsigned long ticks, base, data;
|
||||
regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
|
||||
base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]);
|
||||
|
||||
/* load 32-bit read-only counter */
|
||||
regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4);
|
||||
data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
ticks = base + data;
|
||||
dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
|
||||
base, data, ticks);
|
||||
rtc_time_to_tm(ticks, tm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct pm80x_rtc_info *info = dev_get_drvdata(dev);
|
||||
unsigned char buf[4];
|
||||
unsigned long ticks, base, data;
|
||||
if ((tm->tm_year < 70) || (tm->tm_year > 138)) {
|
||||
dev_dbg(info->dev,
|
||||
"Set time %d out of range. Please set time between 1970 to 2038.\n",
|
||||
1900 + tm->tm_year);
|
||||
return -EINVAL;
|
||||
}
|
||||
rtc_tm_to_time(tm, &ticks);
|
||||
|
||||
/* load 32-bit read-only counter */
|
||||
regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4);
|
||||
data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
base = ticks - data;
|
||||
dev_dbg(info->dev, "set base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
|
||||
base, data, ticks);
|
||||
buf[0] = base & 0xFF;
|
||||
buf[1] = (base >> 8) & 0xFF;
|
||||
buf[2] = (base >> 16) & 0xFF;
|
||||
buf[3] = (base >> 24) & 0xFF;
|
||||
regmap_raw_write(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm80x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct pm80x_rtc_info *info = dev_get_drvdata(dev);
|
||||
unsigned char buf[4];
|
||||
unsigned long ticks, base, data;
|
||||
int ret;
|
||||
|
||||
regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
|
||||
base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]);
|
||||
|
||||
regmap_raw_read(info->map, PM800_RTC_EXPIRE1_1, buf, 4);
|
||||
data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
ticks = base + data;
|
||||
dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
|
||||
base, data, ticks);
|
||||
|
||||
rtc_time_to_tm(ticks, &alrm->time);
|
||||
regmap_read(info->map, PM800_RTC_CONTROL, &ret);
|
||||
alrm->enabled = (ret & PM800_ALARM1_EN) ? 1 : 0;
|
||||
alrm->pending = (ret & (PM800_ALARM | PM800_ALARM_WAKEUP)) ? 1 : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm80x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct pm80x_rtc_info *info = dev_get_drvdata(dev);
|
||||
struct rtc_time now_tm, alarm_tm;
|
||||
unsigned long ticks, base, data;
|
||||
unsigned char buf[4];
|
||||
int mask;
|
||||
|
||||
regmap_update_bits(info->map, PM800_RTC_CONTROL, PM800_ALARM1_EN, 0);
|
||||
|
||||
regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
|
||||
base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]);
|
||||
|
||||
/* load 32-bit read-only counter */
|
||||
regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4);
|
||||
data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
ticks = base + data;
|
||||
dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
|
||||
base, data, ticks);
|
||||
|
||||
rtc_time_to_tm(ticks, &now_tm);
|
||||
dev_dbg(info->dev, "%s, now time : %lu\n", __func__, ticks);
|
||||
rtc_next_alarm_time(&alarm_tm, &now_tm, &alrm->time);
|
||||
/* get new ticks for alarm in 24 hours */
|
||||
rtc_tm_to_time(&alarm_tm, &ticks);
|
||||
dev_dbg(info->dev, "%s, alarm time: %lu\n", __func__, ticks);
|
||||
data = ticks - base;
|
||||
|
||||
buf[0] = data & 0xff;
|
||||
buf[1] = (data >> 8) & 0xff;
|
||||
buf[2] = (data >> 16) & 0xff;
|
||||
buf[3] = (data >> 24) & 0xff;
|
||||
regmap_raw_write(info->map, PM800_RTC_EXPIRE1_1, buf, 4);
|
||||
if (alrm->enabled) {
|
||||
mask = PM800_ALARM | PM800_ALARM_WAKEUP | PM800_ALARM1_EN;
|
||||
regmap_update_bits(info->map, PM800_RTC_CONTROL, mask, mask);
|
||||
} else {
|
||||
mask = PM800_ALARM | PM800_ALARM_WAKEUP | PM800_ALARM1_EN;
|
||||
regmap_update_bits(info->map, PM800_RTC_CONTROL, mask,
|
||||
PM800_ALARM | PM800_ALARM_WAKEUP);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops pm80x_rtc_ops = {
|
||||
.read_time = pm80x_rtc_read_time,
|
||||
.set_time = pm80x_rtc_set_time,
|
||||
.read_alarm = pm80x_rtc_read_alarm,
|
||||
.set_alarm = pm80x_rtc_set_alarm,
|
||||
.alarm_irq_enable = pm80x_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int pm80x_rtc_suspend(struct device *dev)
|
||||
{
|
||||
return pm80x_dev_suspend(dev);
|
||||
}
|
||||
|
||||
static int pm80x_rtc_resume(struct device *dev)
|
||||
{
|
||||
return pm80x_dev_resume(dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(pm80x_rtc_pm_ops, pm80x_rtc_suspend, pm80x_rtc_resume);
|
||||
|
||||
static int __devinit pm80x_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
|
||||
struct pm80x_platform_data *pm80x_pdata;
|
||||
struct pm80x_rtc_pdata *pdata = NULL;
|
||||
struct pm80x_rtc_info *info;
|
||||
struct rtc_time tm;
|
||||
unsigned long ticks = 0;
|
||||
int ret;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
if (pdata == NULL)
|
||||
dev_warn(&pdev->dev, "No platform data!\n");
|
||||
|
||||
info =
|
||||
devm_kzalloc(&pdev->dev, sizeof(struct pm80x_rtc_info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
info->irq = platform_get_irq(pdev, 0);
|
||||
if (info->irq < 0) {
|
||||
dev_err(&pdev->dev, "No IRQ resource!\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
info->chip = chip;
|
||||
info->map = chip->regmap;
|
||||
if (!info->map) {
|
||||
dev_err(&pdev->dev, "no regmap!\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
info->dev = &pdev->dev;
|
||||
dev_set_drvdata(&pdev->dev, info);
|
||||
|
||||
ret = pm80x_request_irq(chip, info->irq, rtc_update_handler,
|
||||
IRQF_ONESHOT, "rtc", info);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
|
||||
info->irq, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = pm80x_rtc_read_time(&pdev->dev, &tm);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to read initial time.\n");
|
||||
goto out_rtc;
|
||||
}
|
||||
if ((tm.tm_year < 70) || (tm.tm_year > 138)) {
|
||||
tm.tm_year = 70;
|
||||
tm.tm_mon = 0;
|
||||
tm.tm_mday = 1;
|
||||
tm.tm_hour = 0;
|
||||
tm.tm_min = 0;
|
||||
tm.tm_sec = 0;
|
||||
ret = pm80x_rtc_set_time(&pdev->dev, &tm);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to set initial time.\n");
|
||||
goto out_rtc;
|
||||
}
|
||||
}
|
||||
rtc_tm_to_time(&tm, &ticks);
|
||||
|
||||
info->rtc_dev = rtc_device_register("88pm80x-rtc", &pdev->dev,
|
||||
&pm80x_rtc_ops, THIS_MODULE);
|
||||
ret = PTR_ERR(info->rtc_dev);
|
||||
if (IS_ERR(info->rtc_dev)) {
|
||||
dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
|
||||
goto out_rtc;
|
||||
}
|
||||
/*
|
||||
* enable internal XO instead of internal 3.25MHz clock since it can
|
||||
* free running in PMIC power-down state.
|
||||
*/
|
||||
regmap_update_bits(info->map, PM800_RTC_CONTROL, PM800_RTC1_USE_XO,
|
||||
PM800_RTC1_USE_XO);
|
||||
|
||||
if (pdev->dev.parent->platform_data) {
|
||||
pm80x_pdata = pdev->dev.parent->platform_data;
|
||||
pdata = pm80x_pdata->rtc;
|
||||
if (pdata)
|
||||
info->rtc_dev->dev.platform_data = &pdata->rtc_wakeup;
|
||||
}
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
return 0;
|
||||
out_rtc:
|
||||
pm80x_free_irq(chip, info->irq, info);
|
||||
out:
|
||||
devm_kfree(&pdev->dev, info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit pm80x_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct pm80x_rtc_info *info = platform_get_drvdata(pdev);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
rtc_device_unregister(info->rtc_dev);
|
||||
pm80x_free_irq(info->chip, info->irq, info);
|
||||
devm_kfree(&pdev->dev, info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver pm80x_rtc_driver = {
|
||||
.driver = {
|
||||
.name = "88pm80x-rtc",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &pm80x_rtc_pm_ops,
|
||||
},
|
||||
.probe = pm80x_rtc_probe,
|
||||
.remove = __devexit_p(pm80x_rtc_remove),
|
||||
};
|
||||
|
||||
module_platform_driver(pm80x_rtc_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Marvell 88PM80x RTC driver");
|
||||
MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
|
||||
MODULE_ALIAS("platform:88pm80x-rtc");
|
|
@ -69,6 +69,7 @@ struct max8925_rtc_info {
|
|||
struct max8925_chip *chip;
|
||||
struct i2c_client *rtc;
|
||||
struct device *dev;
|
||||
int irq;
|
||||
};
|
||||
|
||||
static irqreturn_t rtc_update_handler(int irq, void *data)
|
||||
|
@ -250,7 +251,7 @@ static int __devinit max8925_rtc_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
|
||||
struct max8925_rtc_info *info;
|
||||
int irq, ret;
|
||||
int ret;
|
||||
|
||||
info = kzalloc(sizeof(struct max8925_rtc_info), GFP_KERNEL);
|
||||
if (!info)
|
||||
|
@ -258,13 +259,13 @@ static int __devinit max8925_rtc_probe(struct platform_device *pdev)
|
|||
info->chip = chip;
|
||||
info->rtc = chip->rtc;
|
||||
info->dev = &pdev->dev;
|
||||
irq = chip->irq_base + MAX8925_IRQ_RTC_ALARM0;
|
||||
info->irq = platform_get_irq(pdev, 0);
|
||||
|
||||
ret = request_threaded_irq(irq, NULL, rtc_update_handler,
|
||||
ret = request_threaded_irq(info->irq, NULL, rtc_update_handler,
|
||||
IRQF_ONESHOT, "rtc-alarm0", info);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
|
||||
irq, ret);
|
||||
info->irq, ret);
|
||||
goto out_irq;
|
||||
}
|
||||
|
||||
|
@ -285,7 +286,7 @@ static int __devinit max8925_rtc_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
out_rtc:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
free_irq(chip->irq_base + MAX8925_IRQ_RTC_ALARM0, info);
|
||||
free_irq(info->irq, info);
|
||||
out_irq:
|
||||
kfree(info);
|
||||
return ret;
|
||||
|
@ -296,7 +297,7 @@ static int __devexit max8925_rtc_remove(struct platform_device *pdev)
|
|||
struct max8925_rtc_info *info = platform_get_drvdata(pdev);
|
||||
|
||||
if (info) {
|
||||
free_irq(info->chip->irq_base + MAX8925_IRQ_RTC_ALARM0, info);
|
||||
free_irq(info->irq, info);
|
||||
rtc_device_unregister(info->rtc_dev);
|
||||
kfree(info);
|
||||
}
|
||||
|
|
|
@ -152,14 +152,14 @@ static int omap_ehci_init(struct usb_hcd *hcd)
|
|||
struct ehci_hcd_omap_platform_data *pdata;
|
||||
|
||||
pdata = hcd->self.controller->platform_data;
|
||||
|
||||
/* Hold PHYs in reset while initializing EHCI controller */
|
||||
if (pdata->phy_reset) {
|
||||
if (gpio_is_valid(pdata->reset_gpio_port[0]))
|
||||
gpio_request_one(pdata->reset_gpio_port[0],
|
||||
GPIOF_OUT_INIT_LOW, "USB1 PHY reset");
|
||||
gpio_set_value_cansleep(pdata->reset_gpio_port[0], 0);
|
||||
|
||||
if (gpio_is_valid(pdata->reset_gpio_port[1]))
|
||||
gpio_request_one(pdata->reset_gpio_port[1],
|
||||
GPIOF_OUT_INIT_LOW, "USB2 PHY reset");
|
||||
gpio_set_value_cansleep(pdata->reset_gpio_port[1], 0);
|
||||
|
||||
/* Hold the PHY in RESET for enough time till DIR is high */
|
||||
udelay(10);
|
||||
|
|
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
* Marvell 88PM80x Interface
|
||||
*
|
||||
* Copyright (C) 2012 Marvell International Ltd.
|
||||
* Qiao Zhou <zhouqiao@marvell.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.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MFD_88PM80X_H
|
||||
#define __LINUX_MFD_88PM80X_H
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/atomic.h>
|
||||
|
||||
#define PM80X_VERSION_MASK (0xFF) /* 80X chip ID mask */
|
||||
enum {
|
||||
CHIP_INVALID = 0,
|
||||
CHIP_PM800,
|
||||
CHIP_PM805,
|
||||
CHIP_MAX,
|
||||
};
|
||||
|
||||
enum {
|
||||
PM800_ID_BUCK1 = 0,
|
||||
PM800_ID_BUCK2,
|
||||
PM800_ID_BUCK3,
|
||||
PM800_ID_BUCK4,
|
||||
PM800_ID_BUCK5,
|
||||
|
||||
PM800_ID_LDO1,
|
||||
PM800_ID_LDO2,
|
||||
PM800_ID_LDO3,
|
||||
PM800_ID_LDO4,
|
||||
PM800_ID_LDO5,
|
||||
PM800_ID_LDO6,
|
||||
PM800_ID_LDO7,
|
||||
PM800_ID_LDO8,
|
||||
PM800_ID_LDO9,
|
||||
PM800_ID_LDO10,
|
||||
PM800_ID_LDO11,
|
||||
PM800_ID_LDO12,
|
||||
PM800_ID_LDO13,
|
||||
PM800_ID_LDO14,
|
||||
PM800_ID_LDO15,
|
||||
PM800_ID_LDO16,
|
||||
PM800_ID_LDO17,
|
||||
PM800_ID_LDO18,
|
||||
PM800_ID_LDO19,
|
||||
|
||||
PM800_ID_RG_MAX,
|
||||
};
|
||||
#define PM800_MAX_REGULATOR PM800_ID_RG_MAX /* 5 Bucks, 19 LDOs */
|
||||
#define PM800_NUM_BUCK (5) /*5 Bucks */
|
||||
#define PM800_NUM_LDO (19) /*19 Bucks */
|
||||
|
||||
/* page 0 basic: slave adder 0x60 */
|
||||
|
||||
#define PM800_STATUS_1 (0x01)
|
||||
#define PM800_ONKEY_STS1 (1 << 0)
|
||||
#define PM800_EXTON_STS1 (1 << 1)
|
||||
#define PM800_CHG_STS1 (1 << 2)
|
||||
#define PM800_BAT_STS1 (1 << 3)
|
||||
#define PM800_VBUS_STS1 (1 << 4)
|
||||
#define PM800_LDO_PGOOD_STS1 (1 << 5)
|
||||
#define PM800_BUCK_PGOOD_STS1 (1 << 6)
|
||||
|
||||
#define PM800_STATUS_2 (0x02)
|
||||
#define PM800_RTC_ALARM_STS2 (1 << 0)
|
||||
|
||||
/* Wakeup Registers */
|
||||
#define PM800_WAKEUP1 (0x0D)
|
||||
|
||||
#define PM800_WAKEUP2 (0x0E)
|
||||
#define PM800_WAKEUP2_INV_INT (1 << 0)
|
||||
#define PM800_WAKEUP2_INT_CLEAR (1 << 1)
|
||||
#define PM800_WAKEUP2_INT_MASK (1 << 2)
|
||||
|
||||
#define PM800_POWER_UP_LOG (0x10)
|
||||
|
||||
/* Referance and low power registers */
|
||||
#define PM800_LOW_POWER1 (0x20)
|
||||
#define PM800_LOW_POWER2 (0x21)
|
||||
#define PM800_LOW_POWER_CONFIG3 (0x22)
|
||||
#define PM800_LOW_POWER_CONFIG4 (0x23)
|
||||
|
||||
/* GPIO register */
|
||||
#define PM800_GPIO_0_1_CNTRL (0x30)
|
||||
#define PM800_GPIO0_VAL (1 << 0)
|
||||
#define PM800_GPIO0_GPIO_MODE(x) (x << 1)
|
||||
#define PM800_GPIO1_VAL (1 << 4)
|
||||
#define PM800_GPIO1_GPIO_MODE(x) (x << 5)
|
||||
|
||||
#define PM800_GPIO_2_3_CNTRL (0x31)
|
||||
#define PM800_GPIO2_VAL (1 << 0)
|
||||
#define PM800_GPIO2_GPIO_MODE(x) (x << 1)
|
||||
#define PM800_GPIO3_VAL (1 << 4)
|
||||
#define PM800_GPIO3_GPIO_MODE(x) (x << 5)
|
||||
#define PM800_GPIO3_MODE_MASK 0x1F
|
||||
#define PM800_GPIO3_HEADSET_MODE PM800_GPIO3_GPIO_MODE(6)
|
||||
|
||||
#define PM800_GPIO_4_CNTRL (0x32)
|
||||
#define PM800_GPIO4_VAL (1 << 0)
|
||||
#define PM800_GPIO4_GPIO_MODE(x) (x << 1)
|
||||
|
||||
#define PM800_HEADSET_CNTRL (0x38)
|
||||
#define PM800_HEADSET_DET_EN (1 << 7)
|
||||
#define PM800_HSDET_SLP (1 << 1)
|
||||
/* PWM register */
|
||||
#define PM800_PWM1 (0x40)
|
||||
#define PM800_PWM2 (0x41)
|
||||
#define PM800_PWM3 (0x42)
|
||||
#define PM800_PWM4 (0x43)
|
||||
|
||||
/* RTC Registers */
|
||||
#define PM800_RTC_CONTROL (0xD0)
|
||||
#define PM800_RTC_MISC1 (0xE1)
|
||||
#define PM800_RTC_MISC2 (0xE2)
|
||||
#define PM800_RTC_MISC3 (0xE3)
|
||||
#define PM800_RTC_MISC4 (0xE4)
|
||||
#define PM800_RTC_MISC5 (0xE7)
|
||||
/* bit definitions of RTC Register 1 (0xD0) */
|
||||
#define PM800_ALARM1_EN (1 << 0)
|
||||
#define PM800_ALARM_WAKEUP (1 << 4)
|
||||
#define PM800_ALARM (1 << 5)
|
||||
#define PM800_RTC1_USE_XO (1 << 7)
|
||||
|
||||
/* Regulator Control Registers: BUCK1,BUCK5,LDO1 have DVC */
|
||||
|
||||
/* buck registers */
|
||||
#define PM800_SLEEP_BUCK1 (0x30)
|
||||
|
||||
/* BUCK Sleep Mode Register 1: BUCK[1..4] */
|
||||
#define PM800_BUCK_SLP1 (0x5A)
|
||||
#define PM800_BUCK1_SLP1_SHIFT 0
|
||||
#define PM800_BUCK1_SLP1_MASK (0x3 << PM800_BUCK1_SLP1_SHIFT)
|
||||
|
||||
/* page 2 GPADC: slave adder 0x02 */
|
||||
#define PM800_GPADC_MEAS_EN1 (0x01)
|
||||
#define PM800_MEAS_EN1_VBAT (1 << 2)
|
||||
#define PM800_GPADC_MEAS_EN2 (0x02)
|
||||
#define PM800_MEAS_EN2_RFTMP (1 << 0)
|
||||
#define PM800_MEAS_GP0_EN (1 << 2)
|
||||
#define PM800_MEAS_GP1_EN (1 << 3)
|
||||
#define PM800_MEAS_GP2_EN (1 << 4)
|
||||
#define PM800_MEAS_GP3_EN (1 << 5)
|
||||
#define PM800_MEAS_GP4_EN (1 << 6)
|
||||
|
||||
#define PM800_GPADC_MISC_CONFIG1 (0x05)
|
||||
#define PM800_GPADC_MISC_CONFIG2 (0x06)
|
||||
#define PM800_GPADC_MISC_GPFSM_EN (1 << 0)
|
||||
#define PM800_GPADC_SLOW_MODE(x) (x << 3)
|
||||
|
||||
#define PM800_GPADC_MISC_CONFIG3 (0x09)
|
||||
#define PM800_GPADC_MISC_CONFIG4 (0x0A)
|
||||
|
||||
#define PM800_GPADC_PREBIAS1 (0x0F)
|
||||
#define PM800_GPADC0_GP_PREBIAS_TIME(x) (x << 0)
|
||||
#define PM800_GPADC_PREBIAS2 (0x10)
|
||||
|
||||
#define PM800_GP_BIAS_ENA1 (0x14)
|
||||
#define PM800_GPADC_GP_BIAS_EN0 (1 << 0)
|
||||
#define PM800_GPADC_GP_BIAS_EN1 (1 << 1)
|
||||
#define PM800_GPADC_GP_BIAS_EN2 (1 << 2)
|
||||
#define PM800_GPADC_GP_BIAS_EN3 (1 << 3)
|
||||
|
||||
#define PM800_GP_BIAS_OUT1 (0x15)
|
||||
#define PM800_BIAS_OUT_GP0 (1 << 0)
|
||||
#define PM800_BIAS_OUT_GP1 (1 << 1)
|
||||
#define PM800_BIAS_OUT_GP2 (1 << 2)
|
||||
#define PM800_BIAS_OUT_GP3 (1 << 3)
|
||||
|
||||
#define PM800_GPADC0_LOW_TH 0x20
|
||||
#define PM800_GPADC1_LOW_TH 0x21
|
||||
#define PM800_GPADC2_LOW_TH 0x22
|
||||
#define PM800_GPADC3_LOW_TH 0x23
|
||||
#define PM800_GPADC4_LOW_TH 0x24
|
||||
|
||||
#define PM800_GPADC0_UPP_TH 0x30
|
||||
#define PM800_GPADC1_UPP_TH 0x31
|
||||
#define PM800_GPADC2_UPP_TH 0x32
|
||||
#define PM800_GPADC3_UPP_TH 0x33
|
||||
#define PM800_GPADC4_UPP_TH 0x34
|
||||
|
||||
#define PM800_VBBAT_MEAS1 0x40
|
||||
#define PM800_VBBAT_MEAS2 0x41
|
||||
#define PM800_VBAT_MEAS1 0x42
|
||||
#define PM800_VBAT_MEAS2 0x43
|
||||
#define PM800_VSYS_MEAS1 0x44
|
||||
#define PM800_VSYS_MEAS2 0x45
|
||||
#define PM800_VCHG_MEAS1 0x46
|
||||
#define PM800_VCHG_MEAS2 0x47
|
||||
#define PM800_TINT_MEAS1 0x50
|
||||
#define PM800_TINT_MEAS2 0x51
|
||||
#define PM800_PMOD_MEAS1 0x52
|
||||
#define PM800_PMOD_MEAS2 0x53
|
||||
|
||||
#define PM800_GPADC0_MEAS1 0x54
|
||||
#define PM800_GPADC0_MEAS2 0x55
|
||||
#define PM800_GPADC1_MEAS1 0x56
|
||||
#define PM800_GPADC1_MEAS2 0x57
|
||||
#define PM800_GPADC2_MEAS1 0x58
|
||||
#define PM800_GPADC2_MEAS2 0x59
|
||||
#define PM800_GPADC3_MEAS1 0x5A
|
||||
#define PM800_GPADC3_MEAS2 0x5B
|
||||
#define PM800_GPADC4_MEAS1 0x5C
|
||||
#define PM800_GPADC4_MEAS2 0x5D
|
||||
|
||||
#define PM800_GPADC4_AVG1 0xA8
|
||||
#define PM800_GPADC4_AVG2 0xA9
|
||||
|
||||
/* 88PM805 Registers */
|
||||
#define PM805_MAIN_POWERUP (0x01)
|
||||
#define PM805_INT_STATUS0 (0x02) /* for ena/dis all interrupts */
|
||||
|
||||
#define PM805_STATUS0_INT_CLEAR (1 << 0)
|
||||
#define PM805_STATUS0_INV_INT (1 << 1)
|
||||
#define PM800_STATUS0_INT_MASK (1 << 2)
|
||||
|
||||
#define PM805_INT_STATUS1 (0x03)
|
||||
|
||||
#define PM805_INT1_HP1_SHRT (1 << 0)
|
||||
#define PM805_INT1_HP2_SHRT (1 << 1)
|
||||
#define PM805_INT1_MIC_CONFLICT (1 << 2)
|
||||
#define PM805_INT1_CLIP_FAULT (1 << 3)
|
||||
#define PM805_INT1_LDO_OFF (1 << 4)
|
||||
#define PM805_INT1_SRC_DPLL_LOCK (1 << 5)
|
||||
|
||||
#define PM805_INT_STATUS2 (0x04)
|
||||
|
||||
#define PM805_INT2_MIC_DET (1 << 0)
|
||||
#define PM805_INT2_SHRT_BTN_DET (1 << 1)
|
||||
#define PM805_INT2_VOLM_BTN_DET (1 << 2)
|
||||
#define PM805_INT2_VOLP_BTN_DET (1 << 3)
|
||||
#define PM805_INT2_RAW_PLL_FAULT (1 << 4)
|
||||
#define PM805_INT2_FINE_PLL_FAULT (1 << 5)
|
||||
|
||||
#define PM805_INT_MASK1 (0x05)
|
||||
#define PM805_INT_MASK2 (0x06)
|
||||
#define PM805_SHRT_BTN_DET (1 << 1)
|
||||
|
||||
/* number of status and int reg in a row */
|
||||
#define PM805_INT_REG_NUM (2)
|
||||
|
||||
#define PM805_MIC_DET1 (0x07)
|
||||
#define PM805_MIC_DET_EN_MIC_DET (1 << 0)
|
||||
#define PM805_MIC_DET2 (0x08)
|
||||
#define PM805_MIC_DET_STATUS1 (0x09)
|
||||
|
||||
#define PM805_MIC_DET_STATUS3 (0x0A)
|
||||
#define PM805_AUTO_SEQ_STATUS1 (0x0B)
|
||||
#define PM805_AUTO_SEQ_STATUS2 (0x0C)
|
||||
|
||||
#define PM805_ADC_SETTING1 (0x10)
|
||||
#define PM805_ADC_SETTING2 (0x11)
|
||||
#define PM805_ADC_SETTING3 (0x11)
|
||||
#define PM805_ADC_GAIN1 (0x12)
|
||||
#define PM805_ADC_GAIN2 (0x13)
|
||||
#define PM805_DMIC_SETTING (0x15)
|
||||
#define PM805_DWS_SETTING (0x16)
|
||||
#define PM805_MIC_CONFLICT_STS (0x17)
|
||||
|
||||
#define PM805_PDM_SETTING1 (0x20)
|
||||
#define PM805_PDM_SETTING2 (0x21)
|
||||
#define PM805_PDM_SETTING3 (0x22)
|
||||
#define PM805_PDM_CONTROL1 (0x23)
|
||||
#define PM805_PDM_CONTROL2 (0x24)
|
||||
#define PM805_PDM_CONTROL3 (0x25)
|
||||
|
||||
#define PM805_HEADPHONE_SETTING (0x26)
|
||||
#define PM805_HEADPHONE_GAIN_A2A (0x27)
|
||||
#define PM805_HEADPHONE_SHORT_STATE (0x28)
|
||||
#define PM805_EARPHONE_SETTING (0x29)
|
||||
#define PM805_AUTO_SEQ_SETTING (0x2A)
|
||||
|
||||
struct pm80x_rtc_pdata {
|
||||
int vrtc;
|
||||
int rtc_wakeup;
|
||||
};
|
||||
|
||||
struct pm80x_subchip {
|
||||
struct i2c_client *power_page; /* chip client for power page */
|
||||
struct i2c_client *gpadc_page; /* chip client for gpadc page */
|
||||
struct regmap *regmap_power;
|
||||
struct regmap *regmap_gpadc;
|
||||
unsigned short power_page_addr; /* power page I2C address */
|
||||
unsigned short gpadc_page_addr; /* gpadc page I2C address */
|
||||
};
|
||||
|
||||
struct pm80x_chip {
|
||||
struct pm80x_subchip *subchip;
|
||||
struct device *dev;
|
||||
struct i2c_client *client;
|
||||
struct i2c_client *companion;
|
||||
struct regmap *regmap;
|
||||
struct regmap_irq_chip *regmap_irq_chip;
|
||||
struct regmap_irq_chip_data *irq_data;
|
||||
unsigned char version;
|
||||
int id;
|
||||
int irq;
|
||||
int irq_mode;
|
||||
unsigned long wu_flag;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
struct pm80x_platform_data {
|
||||
struct pm80x_rtc_pdata *rtc;
|
||||
unsigned short power_page_addr; /* power page I2C address */
|
||||
unsigned short gpadc_page_addr; /* gpadc page I2C address */
|
||||
int irq_mode; /* Clear interrupt by read/write(0/1) */
|
||||
int batt_det; /* enable/disable */
|
||||
int (*plat_config)(struct pm80x_chip *chip,
|
||||
struct pm80x_platform_data *pdata);
|
||||
};
|
||||
|
||||
extern const struct dev_pm_ops pm80x_pm_ops;
|
||||
extern const struct regmap_config pm80x_regmap_config;
|
||||
|
||||
static inline int pm80x_request_irq(struct pm80x_chip *pm80x, int irq,
|
||||
irq_handler_t handler, unsigned long flags,
|
||||
const char *name, void *data)
|
||||
{
|
||||
if (!pm80x->irq_data)
|
||||
return -EINVAL;
|
||||
return request_threaded_irq(regmap_irq_get_virq(pm80x->irq_data, irq),
|
||||
NULL, handler, flags, name, data);
|
||||
}
|
||||
|
||||
static inline void pm80x_free_irq(struct pm80x_chip *pm80x, int irq, void *data)
|
||||
{
|
||||
if (!pm80x->irq_data)
|
||||
return;
|
||||
free_irq(regmap_irq_get_virq(pm80x->irq_data, irq), data);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static inline int pm80x_dev_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
set_bit((1 << irq), &chip->wu_flag);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int pm80x_dev_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
clear_bit((1 << irq), &chip->wu_flag);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern int pm80x_init(struct i2c_client *client,
|
||||
const struct i2c_device_id *id) __devinit;
|
||||
extern int pm80x_deinit(struct i2c_client *client);
|
||||
#endif /* __LINUX_MFD_88PM80X_H */
|
|
@ -136,6 +136,7 @@ enum {
|
|||
PM8607_ID_LDO13,
|
||||
PM8607_ID_LDO14,
|
||||
PM8607_ID_LDO15,
|
||||
PM8606_ID_PREG,
|
||||
|
||||
PM8607_ID_RG_MAX,
|
||||
};
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/irqdomain.h>
|
||||
|
||||
struct device;
|
||||
|
||||
|
@ -227,6 +228,7 @@ enum ab8500_version {
|
|||
* @irq_lock: genirq bus lock
|
||||
* @transfer_ongoing: 0 if no transfer ongoing
|
||||
* @irq: irq line
|
||||
* @irq_domain: irq domain
|
||||
* @version: chip version id (e.g. ab8500 or ab9540)
|
||||
* @chip_id: chip revision id
|
||||
* @write: register write
|
||||
|
@ -247,6 +249,7 @@ struct ab8500 {
|
|||
atomic_t transfer_ongoing;
|
||||
int irq_base;
|
||||
int irq;
|
||||
struct irq_domain *domain;
|
||||
enum ab8500_version version;
|
||||
u8 chip_id;
|
||||
|
||||
|
@ -338,4 +341,6 @@ static inline int is_ab8500_2p0(struct ab8500 *ab)
|
|||
return (is_ab8500(ab) && (ab->chip_id == AB8500_CUT2P0));
|
||||
}
|
||||
|
||||
int ab8500_irq_get_virq(struct ab8500 *ab8500, int irq);
|
||||
|
||||
#endif /* MFD_AB8500_H */
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Arizona MFD internals
|
||||
*
|
||||
* Copyright 2012 Wolfson Microelectronics plc
|
||||
*
|
||||
* Author: Mark Brown <broonie@opensource.wolfsonmicro.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.
|
||||
*/
|
||||
|
||||
#ifndef _WM_ARIZONA_CORE_H
|
||||
#define _WM_ARIZONA_CORE_H
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/mfd/arizona/pdata.h>
|
||||
|
||||
#define ARIZONA_MAX_CORE_SUPPLIES 3
|
||||
|
||||
enum arizona_type {
|
||||
WM5102 = 1,
|
||||
WM5110 = 2,
|
||||
};
|
||||
|
||||
#define ARIZONA_IRQ_GP1 0
|
||||
#define ARIZONA_IRQ_GP2 1
|
||||
#define ARIZONA_IRQ_GP3 2
|
||||
#define ARIZONA_IRQ_GP4 3
|
||||
#define ARIZONA_IRQ_GP5_FALL 4
|
||||
#define ARIZONA_IRQ_GP5_RISE 5
|
||||
#define ARIZONA_IRQ_JD_FALL 6
|
||||
#define ARIZONA_IRQ_JD_RISE 7
|
||||
#define ARIZONA_IRQ_DSP1_RAM_RDY 8
|
||||
#define ARIZONA_IRQ_DSP2_RAM_RDY 9
|
||||
#define ARIZONA_IRQ_DSP3_RAM_RDY 10
|
||||
#define ARIZONA_IRQ_DSP4_RAM_RDY 11
|
||||
#define ARIZONA_IRQ_DSP_IRQ1 12
|
||||
#define ARIZONA_IRQ_DSP_IRQ2 13
|
||||
#define ARIZONA_IRQ_DSP_IRQ3 14
|
||||
#define ARIZONA_IRQ_DSP_IRQ4 15
|
||||
#define ARIZONA_IRQ_DSP_IRQ5 16
|
||||
#define ARIZONA_IRQ_DSP_IRQ6 17
|
||||
#define ARIZONA_IRQ_DSP_IRQ7 18
|
||||
#define ARIZONA_IRQ_DSP_IRQ8 19
|
||||
#define ARIZONA_IRQ_SPK_SHUTDOWN_WARN 20
|
||||
#define ARIZONA_IRQ_SPK_SHUTDOWN 21
|
||||
#define ARIZONA_IRQ_MICDET 22
|
||||
#define ARIZONA_IRQ_HPDET 23
|
||||
#define ARIZONA_IRQ_WSEQ_DONE 24
|
||||
#define ARIZONA_IRQ_DRC2_SIG_DET 25
|
||||
#define ARIZONA_IRQ_DRC1_SIG_DET 26
|
||||
#define ARIZONA_IRQ_ASRC2_LOCK 27
|
||||
#define ARIZONA_IRQ_ASRC1_LOCK 28
|
||||
#define ARIZONA_IRQ_UNDERCLOCKED 29
|
||||
#define ARIZONA_IRQ_OVERCLOCKED 30
|
||||
#define ARIZONA_IRQ_FLL2_LOCK 31
|
||||
#define ARIZONA_IRQ_FLL1_LOCK 32
|
||||
#define ARIZONA_IRQ_CLKGEN_ERR 33
|
||||
#define ARIZONA_IRQ_CLKGEN_ERR_ASYNC 34
|
||||
#define ARIZONA_IRQ_ASRC_CFG_ERR 35
|
||||
#define ARIZONA_IRQ_AIF3_ERR 36
|
||||
#define ARIZONA_IRQ_AIF2_ERR 37
|
||||
#define ARIZONA_IRQ_AIF1_ERR 38
|
||||
#define ARIZONA_IRQ_CTRLIF_ERR 39
|
||||
#define ARIZONA_IRQ_MIXER_DROPPED_SAMPLES 40
|
||||
#define ARIZONA_IRQ_ASYNC_CLK_ENA_LOW 41
|
||||
#define ARIZONA_IRQ_SYSCLK_ENA_LOW 42
|
||||
#define ARIZONA_IRQ_ISRC1_CFG_ERR 43
|
||||
#define ARIZONA_IRQ_ISRC2_CFG_ERR 44
|
||||
#define ARIZONA_IRQ_BOOT_DONE 45
|
||||
#define ARIZONA_IRQ_DCS_DAC_DONE 46
|
||||
#define ARIZONA_IRQ_DCS_HP_DONE 47
|
||||
#define ARIZONA_IRQ_FLL2_CLOCK_OK 48
|
||||
#define ARIZONA_IRQ_FLL1_CLOCK_OK 49
|
||||
|
||||
#define ARIZONA_NUM_IRQ 50
|
||||
|
||||
struct arizona {
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
|
||||
enum arizona_type type;
|
||||
unsigned int rev;
|
||||
|
||||
int num_core_supplies;
|
||||
struct regulator_bulk_data core_supplies[ARIZONA_MAX_CORE_SUPPLIES];
|
||||
struct regulator *dcvdd;
|
||||
|
||||
struct arizona_pdata pdata;
|
||||
|
||||
int irq;
|
||||
struct irq_domain *virq;
|
||||
struct regmap_irq_chip_data *aod_irq_chip;
|
||||
struct regmap_irq_chip_data *irq_chip;
|
||||
|
||||
struct mutex clk_lock;
|
||||
int clk32k_ref;
|
||||
};
|
||||
|
||||
int arizona_clk32k_enable(struct arizona *arizona);
|
||||
int arizona_clk32k_disable(struct arizona *arizona);
|
||||
|
||||
int arizona_request_irq(struct arizona *arizona, int irq, char *name,
|
||||
irq_handler_t handler, void *data);
|
||||
void arizona_free_irq(struct arizona *arizona, int irq, void *data);
|
||||
int arizona_set_irq_wake(struct arizona *arizona, int irq, int on);
|
||||
|
||||
int wm5102_patch(struct arizona *arizona);
|
||||
int wm5110_patch(struct arizona *arizona);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* Platform data for Arizona devices
|
||||
*
|
||||
* Copyright 2012 Wolfson Microelectronics. PLC.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _ARIZONA_PDATA_H
|
||||
#define _ARIZONA_PDATA_H
|
||||
|
||||
#define ARIZONA_GPN_DIR 0x8000 /* GPN_DIR */
|
||||
#define ARIZONA_GPN_DIR_MASK 0x8000 /* GPN_DIR */
|
||||
#define ARIZONA_GPN_DIR_SHIFT 15 /* GPN_DIR */
|
||||
#define ARIZONA_GPN_DIR_WIDTH 1 /* GPN_DIR */
|
||||
#define ARIZONA_GPN_PU 0x4000 /* GPN_PU */
|
||||
#define ARIZONA_GPN_PU_MASK 0x4000 /* GPN_PU */
|
||||
#define ARIZONA_GPN_PU_SHIFT 14 /* GPN_PU */
|
||||
#define ARIZONA_GPN_PU_WIDTH 1 /* GPN_PU */
|
||||
#define ARIZONA_GPN_PD 0x2000 /* GPN_PD */
|
||||
#define ARIZONA_GPN_PD_MASK 0x2000 /* GPN_PD */
|
||||
#define ARIZONA_GPN_PD_SHIFT 13 /* GPN_PD */
|
||||
#define ARIZONA_GPN_PD_WIDTH 1 /* GPN_PD */
|
||||
#define ARIZONA_GPN_LVL 0x0800 /* GPN_LVL */
|
||||
#define ARIZONA_GPN_LVL_MASK 0x0800 /* GPN_LVL */
|
||||
#define ARIZONA_GPN_LVL_SHIFT 11 /* GPN_LVL */
|
||||
#define ARIZONA_GPN_LVL_WIDTH 1 /* GPN_LVL */
|
||||
#define ARIZONA_GPN_POL 0x0400 /* GPN_POL */
|
||||
#define ARIZONA_GPN_POL_MASK 0x0400 /* GPN_POL */
|
||||
#define ARIZONA_GPN_POL_SHIFT 10 /* GPN_POL */
|
||||
#define ARIZONA_GPN_POL_WIDTH 1 /* GPN_POL */
|
||||
#define ARIZONA_GPN_OP_CFG 0x0200 /* GPN_OP_CFG */
|
||||
#define ARIZONA_GPN_OP_CFG_MASK 0x0200 /* GPN_OP_CFG */
|
||||
#define ARIZONA_GPN_OP_CFG_SHIFT 9 /* GPN_OP_CFG */
|
||||
#define ARIZONA_GPN_OP_CFG_WIDTH 1 /* GPN_OP_CFG */
|
||||
#define ARIZONA_GPN_DB 0x0100 /* GPN_DB */
|
||||
#define ARIZONA_GPN_DB_MASK 0x0100 /* GPN_DB */
|
||||
#define ARIZONA_GPN_DB_SHIFT 8 /* GPN_DB */
|
||||
#define ARIZONA_GPN_DB_WIDTH 1 /* GPN_DB */
|
||||
#define ARIZONA_GPN_FN_MASK 0x007F /* GPN_FN - [6:0] */
|
||||
#define ARIZONA_GPN_FN_SHIFT 0 /* GPN_FN - [6:0] */
|
||||
#define ARIZONA_GPN_FN_WIDTH 7 /* GPN_FN - [6:0] */
|
||||
|
||||
#define ARIZONA_MAX_GPIO 5
|
||||
|
||||
#define ARIZONA_32KZ_MCLK1 1
|
||||
#define ARIZONA_32KZ_MCLK2 2
|
||||
#define ARIZONA_32KZ_NONE 3
|
||||
|
||||
#define ARIZONA_MAX_INPUT 4
|
||||
|
||||
#define ARIZONA_DMIC_MICVDD 0
|
||||
#define ARIZONA_DMIC_MICBIAS1 1
|
||||
#define ARIZONA_DMIC_MICBIAS2 2
|
||||
#define ARIZONA_DMIC_MICBIAS3 3
|
||||
|
||||
#define ARIZONA_INMODE_DIFF 0
|
||||
#define ARIZONA_INMODE_SE 1
|
||||
#define ARIZONA_INMODE_DMIC 2
|
||||
|
||||
#define ARIZONA_MAX_OUTPUT 6
|
||||
|
||||
#define ARIZONA_MAX_PDM_SPK 2
|
||||
|
||||
struct regulator_init_data;
|
||||
|
||||
struct arizona_micd_config {
|
||||
unsigned int src;
|
||||
unsigned int bias;
|
||||
bool gpio;
|
||||
};
|
||||
|
||||
struct arizona_pdata {
|
||||
int reset; /** GPIO controlling /RESET, if any */
|
||||
int ldoena; /** GPIO controlling LODENA, if any */
|
||||
|
||||
/** Regulator configuration for MICVDD */
|
||||
struct regulator_init_data *micvdd;
|
||||
|
||||
/** Regulator configuration for LDO1 */
|
||||
struct regulator_init_data *ldo1;
|
||||
|
||||
/** If a direct 32kHz clock is provided on an MCLK specify it here */
|
||||
int clk32k_src;
|
||||
|
||||
bool irq_active_high; /** IRQ polarity */
|
||||
|
||||
/* Base GPIO */
|
||||
int gpio_base;
|
||||
|
||||
/** Pin state for GPIO pins */
|
||||
int gpio_defaults[ARIZONA_MAX_GPIO];
|
||||
|
||||
/** GPIO for mic detection polarity */
|
||||
int micd_pol_gpio;
|
||||
|
||||
/** Headset polarity configurations */
|
||||
struct arizona_micd_config *micd_configs;
|
||||
int num_micd_configs;
|
||||
|
||||
/** Reference voltage for DMIC inputs */
|
||||
int dmic_ref[ARIZONA_MAX_INPUT];
|
||||
|
||||
/** Mode of input structures */
|
||||
int inmode[ARIZONA_MAX_INPUT];
|
||||
|
||||
/** Mode for outputs */
|
||||
bool out_mono[ARIZONA_MAX_OUTPUT];
|
||||
|
||||
/** PDM speaker mute setting */
|
||||
unsigned int spk_mute[ARIZONA_MAX_PDM_SPK];
|
||||
|
||||
/** PDM speaker format */
|
||||
unsigned int spk_fmt[ARIZONA_MAX_PDM_SPK];
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -36,6 +36,11 @@ struct mfd_cell {
|
|||
/* platform data passed to the sub devices drivers */
|
||||
void *platform_data;
|
||||
size_t pdata_size;
|
||||
/*
|
||||
* Device Tree compatible string
|
||||
* See: Documentation/devicetree/usage-model.txt Chapter 2.2 for details
|
||||
*/
|
||||
const char *of_compatible;
|
||||
|
||||
/*
|
||||
* These resources can be specified relative to the parent device.
|
||||
|
|
|
@ -530,7 +530,7 @@ int db8500_prcmu_stop_temp_sense(void);
|
|||
int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size);
|
||||
int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size);
|
||||
|
||||
void prcmu_ac_wake_req(void);
|
||||
int prcmu_ac_wake_req(void);
|
||||
void prcmu_ac_sleep_req(void);
|
||||
void db8500_prcmu_modem_reset(void);
|
||||
|
||||
|
@ -680,7 +680,10 @@ static inline int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
|
|||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static inline void prcmu_ac_wake_req(void) {}
|
||||
static inline int prcmu_ac_wake_req(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void prcmu_ac_sleep_req(void) {}
|
||||
|
||||
|
|
|
@ -345,7 +345,7 @@ static inline u16 prcmu_get_reset_code(void)
|
|||
return db8500_prcmu_get_reset_code();
|
||||
}
|
||||
|
||||
void prcmu_ac_wake_req(void);
|
||||
int prcmu_ac_wake_req(void);
|
||||
void prcmu_ac_sleep_req(void);
|
||||
static inline void prcmu_modem_reset(void)
|
||||
{
|
||||
|
@ -533,7 +533,10 @@ static inline u16 prcmu_get_reset_code(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline void prcmu_ac_wake_req(void) {}
|
||||
static inline int prcmu_ac_wake_req(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void prcmu_ac_sleep_req(void) {}
|
||||
|
||||
|
|
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* max77686.h - Voltage regulator driver for the Maxim 77686
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electrnoics
|
||||
* Chiwoong Byun <woong.byun@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MFD_MAX77686_PRIV_H
|
||||
#define __LINUX_MFD_MAX77686_PRIV_H
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define MAX77686_REG_INVALID (0xff)
|
||||
|
||||
enum max77686_pmic_reg {
|
||||
MAX77686_REG_DEVICE_ID = 0x00,
|
||||
MAX77686_REG_INTSRC = 0x01,
|
||||
MAX77686_REG_INT1 = 0x02,
|
||||
MAX77686_REG_INT2 = 0x03,
|
||||
|
||||
MAX77686_REG_INT1MSK = 0x04,
|
||||
MAX77686_REG_INT2MSK = 0x05,
|
||||
|
||||
MAX77686_REG_STATUS1 = 0x06,
|
||||
MAX77686_REG_STATUS2 = 0x07,
|
||||
|
||||
MAX77686_REG_PWRON = 0x08,
|
||||
MAX77686_REG_ONOFF_DELAY = 0x09,
|
||||
MAX77686_REG_MRSTB = 0x0A,
|
||||
/* Reserved: 0x0B-0x0F */
|
||||
|
||||
MAX77686_REG_BUCK1CTRL = 0x10,
|
||||
MAX77686_REG_BUCK1OUT = 0x11,
|
||||
MAX77686_REG_BUCK2CTRL1 = 0x12,
|
||||
MAX77686_REG_BUCK234FREQ = 0x13,
|
||||
MAX77686_REG_BUCK2DVS1 = 0x14,
|
||||
MAX77686_REG_BUCK2DVS2 = 0x15,
|
||||
MAX77686_REG_BUCK2DVS3 = 0x16,
|
||||
MAX77686_REG_BUCK2DVS4 = 0x17,
|
||||
MAX77686_REG_BUCK2DVS5 = 0x18,
|
||||
MAX77686_REG_BUCK2DVS6 = 0x19,
|
||||
MAX77686_REG_BUCK2DVS7 = 0x1A,
|
||||
MAX77686_REG_BUCK2DVS8 = 0x1B,
|
||||
MAX77686_REG_BUCK3CTRL1 = 0x1C,
|
||||
/* Reserved: 0x1D */
|
||||
MAX77686_REG_BUCK3DVS1 = 0x1E,
|
||||
MAX77686_REG_BUCK3DVS2 = 0x1F,
|
||||
MAX77686_REG_BUCK3DVS3 = 0x20,
|
||||
MAX77686_REG_BUCK3DVS4 = 0x21,
|
||||
MAX77686_REG_BUCK3DVS5 = 0x22,
|
||||
MAX77686_REG_BUCK3DVS6 = 0x23,
|
||||
MAX77686_REG_BUCK3DVS7 = 0x24,
|
||||
MAX77686_REG_BUCK3DVS8 = 0x25,
|
||||
MAX77686_REG_BUCK4CTRL1 = 0x26,
|
||||
/* Reserved: 0x27 */
|
||||
MAX77686_REG_BUCK4DVS1 = 0x28,
|
||||
MAX77686_REG_BUCK4DVS2 = 0x29,
|
||||
MAX77686_REG_BUCK4DVS3 = 0x2A,
|
||||
MAX77686_REG_BUCK4DVS4 = 0x2B,
|
||||
MAX77686_REG_BUCK4DVS5 = 0x2C,
|
||||
MAX77686_REG_BUCK4DVS6 = 0x2D,
|
||||
MAX77686_REG_BUCK4DVS7 = 0x2E,
|
||||
MAX77686_REG_BUCK4DVS8 = 0x2F,
|
||||
MAX77686_REG_BUCK5CTRL = 0x30,
|
||||
MAX77686_REG_BUCK5OUT = 0x31,
|
||||
MAX77686_REG_BUCK6CTRL = 0x32,
|
||||
MAX77686_REG_BUCK6OUT = 0x33,
|
||||
MAX77686_REG_BUCK7CTRL = 0x34,
|
||||
MAX77686_REG_BUCK7OUT = 0x35,
|
||||
MAX77686_REG_BUCK8CTRL = 0x36,
|
||||
MAX77686_REG_BUCK8OUT = 0x37,
|
||||
MAX77686_REG_BUCK9CTRL = 0x38,
|
||||
MAX77686_REG_BUCK9OUT = 0x39,
|
||||
/* Reserved: 0x3A-0x3F */
|
||||
|
||||
MAX77686_REG_LDO1CTRL1 = 0x40,
|
||||
MAX77686_REG_LDO2CTRL1 = 0x41,
|
||||
MAX77686_REG_LDO3CTRL1 = 0x42,
|
||||
MAX77686_REG_LDO4CTRL1 = 0x43,
|
||||
MAX77686_REG_LDO5CTRL1 = 0x44,
|
||||
MAX77686_REG_LDO6CTRL1 = 0x45,
|
||||
MAX77686_REG_LDO7CTRL1 = 0x46,
|
||||
MAX77686_REG_LDO8CTRL1 = 0x47,
|
||||
MAX77686_REG_LDO9CTRL1 = 0x48,
|
||||
MAX77686_REG_LDO10CTRL1 = 0x49,
|
||||
MAX77686_REG_LDO11CTRL1 = 0x4A,
|
||||
MAX77686_REG_LDO12CTRL1 = 0x4B,
|
||||
MAX77686_REG_LDO13CTRL1 = 0x4C,
|
||||
MAX77686_REG_LDO14CTRL1 = 0x4D,
|
||||
MAX77686_REG_LDO15CTRL1 = 0x4E,
|
||||
MAX77686_REG_LDO16CTRL1 = 0x4F,
|
||||
MAX77686_REG_LDO17CTRL1 = 0x50,
|
||||
MAX77686_REG_LDO18CTRL1 = 0x51,
|
||||
MAX77686_REG_LDO19CTRL1 = 0x52,
|
||||
MAX77686_REG_LDO20CTRL1 = 0x53,
|
||||
MAX77686_REG_LDO21CTRL1 = 0x54,
|
||||
MAX77686_REG_LDO22CTRL1 = 0x55,
|
||||
MAX77686_REG_LDO23CTRL1 = 0x56,
|
||||
MAX77686_REG_LDO24CTRL1 = 0x57,
|
||||
MAX77686_REG_LDO25CTRL1 = 0x58,
|
||||
MAX77686_REG_LDO26CTRL1 = 0x59,
|
||||
/* Reserved: 0x5A-0x5F */
|
||||
MAX77686_REG_LDO1CTRL2 = 0x60,
|
||||
MAX77686_REG_LDO2CTRL2 = 0x61,
|
||||
MAX77686_REG_LDO3CTRL2 = 0x62,
|
||||
MAX77686_REG_LDO4CTRL2 = 0x63,
|
||||
MAX77686_REG_LDO5CTRL2 = 0x64,
|
||||
MAX77686_REG_LDO6CTRL2 = 0x65,
|
||||
MAX77686_REG_LDO7CTRL2 = 0x66,
|
||||
MAX77686_REG_LDO8CTRL2 = 0x67,
|
||||
MAX77686_REG_LDO9CTRL2 = 0x68,
|
||||
MAX77686_REG_LDO10CTRL2 = 0x69,
|
||||
MAX77686_REG_LDO11CTRL2 = 0x6A,
|
||||
MAX77686_REG_LDO12CTRL2 = 0x6B,
|
||||
MAX77686_REG_LDO13CTRL2 = 0x6C,
|
||||
MAX77686_REG_LDO14CTRL2 = 0x6D,
|
||||
MAX77686_REG_LDO15CTRL2 = 0x6E,
|
||||
MAX77686_REG_LDO16CTRL2 = 0x6F,
|
||||
MAX77686_REG_LDO17CTRL2 = 0x70,
|
||||
MAX77686_REG_LDO18CTRL2 = 0x71,
|
||||
MAX77686_REG_LDO19CTRL2 = 0x72,
|
||||
MAX77686_REG_LDO20CTRL2 = 0x73,
|
||||
MAX77686_REG_LDO21CTRL2 = 0x74,
|
||||
MAX77686_REG_LDO22CTRL2 = 0x75,
|
||||
MAX77686_REG_LDO23CTRL2 = 0x76,
|
||||
MAX77686_REG_LDO24CTRL2 = 0x77,
|
||||
MAX77686_REG_LDO25CTRL2 = 0x78,
|
||||
MAX77686_REG_LDO26CTRL2 = 0x79,
|
||||
/* Reserved: 0x7A-0x7D */
|
||||
|
||||
MAX77686_REG_BBAT_CHG = 0x7E,
|
||||
MAX77686_REG_32KHZ = 0x7F,
|
||||
|
||||
MAX77686_REG_PMIC_END = 0x80,
|
||||
};
|
||||
|
||||
enum max77686_rtc_reg {
|
||||
MAX77686_RTC_INT = 0x00,
|
||||
MAX77686_RTC_INTM = 0x01,
|
||||
MAX77686_RTC_CONTROLM = 0x02,
|
||||
MAX77686_RTC_CONTROL = 0x03,
|
||||
MAX77686_RTC_UPDATE0 = 0x04,
|
||||
/* Reserved: 0x5 */
|
||||
MAX77686_WTSR_SMPL_CNTL = 0x06,
|
||||
MAX77686_RTC_SEC = 0x07,
|
||||
MAX77686_RTC_MIN = 0x08,
|
||||
MAX77686_RTC_HOUR = 0x09,
|
||||
MAX77686_RTC_WEEKDAY = 0x0A,
|
||||
MAX77686_RTC_MONTH = 0x0B,
|
||||
MAX77686_RTC_YEAR = 0x0C,
|
||||
MAX77686_RTC_DATE = 0x0D,
|
||||
MAX77686_ALARM1_SEC = 0x0E,
|
||||
MAX77686_ALARM1_MIN = 0x0F,
|
||||
MAX77686_ALARM1_HOUR = 0x10,
|
||||
MAX77686_ALARM1_WEEKDAY = 0x11,
|
||||
MAX77686_ALARM1_MONTH = 0x12,
|
||||
MAX77686_ALARM1_YEAR = 0x13,
|
||||
MAX77686_ALARM1_DATE = 0x14,
|
||||
MAX77686_ALARM2_SEC = 0x15,
|
||||
MAX77686_ALARM2_MIN = 0x16,
|
||||
MAX77686_ALARM2_HOUR = 0x17,
|
||||
MAX77686_ALARM2_WEEKDAY = 0x18,
|
||||
MAX77686_ALARM2_MONTH = 0x19,
|
||||
MAX77686_ALARM2_YEAR = 0x1A,
|
||||
MAX77686_ALARM2_DATE = 0x1B,
|
||||
};
|
||||
|
||||
#define MAX77686_IRQSRC_PMIC (0)
|
||||
#define MAX77686_IRQSRC_RTC (1 << 0)
|
||||
|
||||
enum max77686_irq_source {
|
||||
PMIC_INT1 = 0,
|
||||
PMIC_INT2,
|
||||
RTC_INT,
|
||||
|
||||
MAX77686_IRQ_GROUP_NR,
|
||||
};
|
||||
|
||||
enum max77686_irq {
|
||||
MAX77686_PMICIRQ_PWRONF,
|
||||
MAX77686_PMICIRQ_PWRONR,
|
||||
MAX77686_PMICIRQ_JIGONBF,
|
||||
MAX77686_PMICIRQ_JIGONBR,
|
||||
MAX77686_PMICIRQ_ACOKBF,
|
||||
MAX77686_PMICIRQ_ACOKBR,
|
||||
MAX77686_PMICIRQ_ONKEY1S,
|
||||
MAX77686_PMICIRQ_MRSTB,
|
||||
|
||||
MAX77686_PMICIRQ_140C,
|
||||
MAX77686_PMICIRQ_120C,
|
||||
|
||||
MAX77686_RTCIRQ_RTC60S,
|
||||
MAX77686_RTCIRQ_RTCA1,
|
||||
MAX77686_RTCIRQ_RTCA2,
|
||||
MAX77686_RTCIRQ_SMPL,
|
||||
MAX77686_RTCIRQ_RTC1S,
|
||||
MAX77686_RTCIRQ_WTSR,
|
||||
|
||||
MAX77686_IRQ_NR,
|
||||
};
|
||||
|
||||
struct max77686_dev {
|
||||
struct device *dev;
|
||||
struct i2c_client *i2c; /* 0xcc / PMIC, Battery Control, and FLASH */
|
||||
struct i2c_client *rtc; /* slave addr 0x0c */
|
||||
|
||||
int type;
|
||||
|
||||
struct regmap *regmap; /* regmap for mfd */
|
||||
struct regmap *rtc_regmap; /* regmap for rtc */
|
||||
|
||||
struct irq_domain *irq_domain;
|
||||
|
||||
int irq;
|
||||
int irq_gpio;
|
||||
bool wakeup;
|
||||
struct mutex irqlock;
|
||||
int irq_masks_cur[MAX77686_IRQ_GROUP_NR];
|
||||
int irq_masks_cache[MAX77686_IRQ_GROUP_NR];
|
||||
};
|
||||
|
||||
enum max77686_types {
|
||||
TYPE_MAX77686,
|
||||
};
|
||||
|
||||
extern int max77686_irq_init(struct max77686_dev *max77686);
|
||||
extern void max77686_irq_exit(struct max77686_dev *max77686);
|
||||
extern int max77686_irq_resume(struct max77686_dev *max77686);
|
||||
|
||||
#endif /* __LINUX_MFD_MAX77686_PRIV_H */
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* max77686.h - Driver for the Maxim 77686
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electrnoics
|
||||
* Chiwoong Byun <woong.byun@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* This driver is based on max8997.h
|
||||
*
|
||||
* MAX77686 has PMIC, RTC devices.
|
||||
* The devices share the same I2C bus and included in
|
||||
* this mfd driver.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MFD_MAX77686_H
|
||||
#define __LINUX_MFD_MAX77686_H
|
||||
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
/* MAX77686 regulator IDs */
|
||||
enum max77686_regulators {
|
||||
MAX77686_LDO1 = 0,
|
||||
MAX77686_LDO2,
|
||||
MAX77686_LDO3,
|
||||
MAX77686_LDO4,
|
||||
MAX77686_LDO5,
|
||||
MAX77686_LDO6,
|
||||
MAX77686_LDO7,
|
||||
MAX77686_LDO8,
|
||||
MAX77686_LDO9,
|
||||
MAX77686_LDO10,
|
||||
MAX77686_LDO11,
|
||||
MAX77686_LDO12,
|
||||
MAX77686_LDO13,
|
||||
MAX77686_LDO14,
|
||||
MAX77686_LDO15,
|
||||
MAX77686_LDO16,
|
||||
MAX77686_LDO17,
|
||||
MAX77686_LDO18,
|
||||
MAX77686_LDO19,
|
||||
MAX77686_LDO20,
|
||||
MAX77686_LDO21,
|
||||
MAX77686_LDO22,
|
||||
MAX77686_LDO23,
|
||||
MAX77686_LDO24,
|
||||
MAX77686_LDO25,
|
||||
MAX77686_LDO26,
|
||||
MAX77686_BUCK1,
|
||||
MAX77686_BUCK2,
|
||||
MAX77686_BUCK3,
|
||||
MAX77686_BUCK4,
|
||||
MAX77686_BUCK5,
|
||||
MAX77686_BUCK6,
|
||||
MAX77686_BUCK7,
|
||||
MAX77686_BUCK8,
|
||||
MAX77686_BUCK9,
|
||||
|
||||
MAX77686_REG_MAX,
|
||||
};
|
||||
|
||||
struct max77686_regulator_data {
|
||||
int id;
|
||||
struct regulator_init_data *initdata;
|
||||
};
|
||||
|
||||
enum max77686_opmode {
|
||||
MAX77686_OPMODE_NORMAL,
|
||||
MAX77686_OPMODE_LP,
|
||||
MAX77686_OPMODE_STANDBY,
|
||||
};
|
||||
|
||||
struct max77686_opmode_data {
|
||||
int id;
|
||||
int mode;
|
||||
};
|
||||
|
||||
struct max77686_platform_data {
|
||||
/* IRQ */
|
||||
int irq_gpio;
|
||||
int ono;
|
||||
int wakeup;
|
||||
|
||||
/* ---- PMIC ---- */
|
||||
struct max77686_regulator_data *regulators;
|
||||
int num_regulators;
|
||||
|
||||
struct max77686_opmode_data *opmode_data;
|
||||
|
||||
/*
|
||||
* GPIO-DVS feature is not enabled with the current version of
|
||||
* MAX77686 driver. Buck2/3/4_voltages[0] is used as the default
|
||||
* voltage at probe. DVS/SELB gpios are set as OUTPUT-LOW.
|
||||
*/
|
||||
int buck234_gpio_dvs[3]; /* GPIO of [0]DVS1, [1]DVS2, [2]DVS3 */
|
||||
int buck234_gpio_selb[3]; /* [0]SELB2, [1]SELB3, [2]SELB4 */
|
||||
unsigned int buck2_voltage[8]; /* buckx_voltage in uV */
|
||||
unsigned int buck3_voltage[8];
|
||||
unsigned int buck4_voltage[8];
|
||||
};
|
||||
|
||||
#endif /* __LINUX_MFD_MAX77686_H */
|
|
@ -190,7 +190,6 @@ struct max77693_dev {
|
|||
struct i2c_client *i2c; /* 0xCC , PMIC, Charger, Flash LED */
|
||||
struct i2c_client *muic; /* 0x4A , MUIC */
|
||||
struct i2c_client *haptic; /* 0x90 , Haptic */
|
||||
struct mutex iolock;
|
||||
|
||||
int type;
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#define __LINUX_MFD_MAX8997_PRIV_H
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/irqdomain.h>
|
||||
|
||||
#define MAX8997_REG_INVALID (0xff)
|
||||
|
||||
|
@ -325,7 +327,7 @@ struct max8997_dev {
|
|||
|
||||
int irq;
|
||||
int ono;
|
||||
int irq_base;
|
||||
struct irq_domain *irq_domain;
|
||||
struct mutex irqlock;
|
||||
int irq_masks_cur[MAX8997_IRQ_GROUP_NR];
|
||||
int irq_masks_cache[MAX8997_IRQ_GROUP_NR];
|
||||
|
|
|
@ -181,7 +181,6 @@ struct max8997_led_platform_data {
|
|||
|
||||
struct max8997_platform_data {
|
||||
/* IRQ */
|
||||
int irq_base;
|
||||
int ono;
|
||||
int wakeup;
|
||||
|
||||
|
|
|
@ -1,379 +0,0 @@
|
|||
/*
|
||||
* s5m-core.h
|
||||
*
|
||||
* Copyright (c) 2011 Samsung Electronics Co., Ltd
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MFD_S5M_CORE_H
|
||||
#define __LINUX_MFD_S5M_CORE_H
|
||||
|
||||
#define NUM_IRQ_REGS 4
|
||||
|
||||
enum s5m_device_type {
|
||||
S5M8751X,
|
||||
S5M8763X,
|
||||
S5M8767X,
|
||||
};
|
||||
|
||||
/* S5M8767 registers */
|
||||
enum s5m8767_reg {
|
||||
S5M8767_REG_ID,
|
||||
S5M8767_REG_INT1,
|
||||
S5M8767_REG_INT2,
|
||||
S5M8767_REG_INT3,
|
||||
S5M8767_REG_INT1M,
|
||||
S5M8767_REG_INT2M,
|
||||
S5M8767_REG_INT3M,
|
||||
S5M8767_REG_STATUS1,
|
||||
S5M8767_REG_STATUS2,
|
||||
S5M8767_REG_STATUS3,
|
||||
S5M8767_REG_CTRL1,
|
||||
S5M8767_REG_CTRL2,
|
||||
S5M8767_REG_LOWBAT1,
|
||||
S5M8767_REG_LOWBAT2,
|
||||
S5M8767_REG_BUCHG,
|
||||
S5M8767_REG_DVSRAMP,
|
||||
S5M8767_REG_DVSTIMER2 = 0x10,
|
||||
S5M8767_REG_DVSTIMER3,
|
||||
S5M8767_REG_DVSTIMER4,
|
||||
S5M8767_REG_LDO1,
|
||||
S5M8767_REG_LDO2,
|
||||
S5M8767_REG_LDO3,
|
||||
S5M8767_REG_LDO4,
|
||||
S5M8767_REG_LDO5,
|
||||
S5M8767_REG_LDO6,
|
||||
S5M8767_REG_LDO7,
|
||||
S5M8767_REG_LDO8,
|
||||
S5M8767_REG_LDO9,
|
||||
S5M8767_REG_LDO10,
|
||||
S5M8767_REG_LDO11,
|
||||
S5M8767_REG_LDO12,
|
||||
S5M8767_REG_LDO13,
|
||||
S5M8767_REG_LDO14 = 0x20,
|
||||
S5M8767_REG_LDO15,
|
||||
S5M8767_REG_LDO16,
|
||||
S5M8767_REG_LDO17,
|
||||
S5M8767_REG_LDO18,
|
||||
S5M8767_REG_LDO19,
|
||||
S5M8767_REG_LDO20,
|
||||
S5M8767_REG_LDO21,
|
||||
S5M8767_REG_LDO22,
|
||||
S5M8767_REG_LDO23,
|
||||
S5M8767_REG_LDO24,
|
||||
S5M8767_REG_LDO25,
|
||||
S5M8767_REG_LDO26,
|
||||
S5M8767_REG_LDO27,
|
||||
S5M8767_REG_LDO28,
|
||||
S5M8767_REG_UVLO = 0x31,
|
||||
S5M8767_REG_BUCK1CTRL1,
|
||||
S5M8767_REG_BUCK1CTRL2,
|
||||
S5M8767_REG_BUCK2CTRL,
|
||||
S5M8767_REG_BUCK2DVS1,
|
||||
S5M8767_REG_BUCK2DVS2,
|
||||
S5M8767_REG_BUCK2DVS3,
|
||||
S5M8767_REG_BUCK2DVS4,
|
||||
S5M8767_REG_BUCK2DVS5,
|
||||
S5M8767_REG_BUCK2DVS6,
|
||||
S5M8767_REG_BUCK2DVS7,
|
||||
S5M8767_REG_BUCK2DVS8,
|
||||
S5M8767_REG_BUCK3CTRL,
|
||||
S5M8767_REG_BUCK3DVS1,
|
||||
S5M8767_REG_BUCK3DVS2,
|
||||
S5M8767_REG_BUCK3DVS3,
|
||||
S5M8767_REG_BUCK3DVS4,
|
||||
S5M8767_REG_BUCK3DVS5,
|
||||
S5M8767_REG_BUCK3DVS6,
|
||||
S5M8767_REG_BUCK3DVS7,
|
||||
S5M8767_REG_BUCK3DVS8,
|
||||
S5M8767_REG_BUCK4CTRL,
|
||||
S5M8767_REG_BUCK4DVS1,
|
||||
S5M8767_REG_BUCK4DVS2,
|
||||
S5M8767_REG_BUCK4DVS3,
|
||||
S5M8767_REG_BUCK4DVS4,
|
||||
S5M8767_REG_BUCK4DVS5,
|
||||
S5M8767_REG_BUCK4DVS6,
|
||||
S5M8767_REG_BUCK4DVS7,
|
||||
S5M8767_REG_BUCK4DVS8,
|
||||
S5M8767_REG_BUCK5CTRL1,
|
||||
S5M8767_REG_BUCK5CTRL2,
|
||||
S5M8767_REG_BUCK5CTRL3,
|
||||
S5M8767_REG_BUCK5CTRL4,
|
||||
S5M8767_REG_BUCK5CTRL5,
|
||||
S5M8767_REG_BUCK6CTRL1,
|
||||
S5M8767_REG_BUCK6CTRL2,
|
||||
S5M8767_REG_BUCK7CTRL1,
|
||||
S5M8767_REG_BUCK7CTRL2,
|
||||
S5M8767_REG_BUCK8CTRL1,
|
||||
S5M8767_REG_BUCK8CTRL2,
|
||||
S5M8767_REG_BUCK9CTRL1,
|
||||
S5M8767_REG_BUCK9CTRL2,
|
||||
S5M8767_REG_LDO1CTRL,
|
||||
S5M8767_REG_LDO2_1CTRL,
|
||||
S5M8767_REG_LDO2_2CTRL,
|
||||
S5M8767_REG_LDO2_3CTRL,
|
||||
S5M8767_REG_LDO2_4CTRL,
|
||||
S5M8767_REG_LDO3CTRL,
|
||||
S5M8767_REG_LDO4CTRL,
|
||||
S5M8767_REG_LDO5CTRL,
|
||||
S5M8767_REG_LDO6CTRL,
|
||||
S5M8767_REG_LDO7CTRL,
|
||||
S5M8767_REG_LDO8CTRL,
|
||||
S5M8767_REG_LDO9CTRL,
|
||||
S5M8767_REG_LDO10CTRL,
|
||||
S5M8767_REG_LDO11CTRL,
|
||||
S5M8767_REG_LDO12CTRL,
|
||||
S5M8767_REG_LDO13CTRL,
|
||||
S5M8767_REG_LDO14CTRL,
|
||||
S5M8767_REG_LDO15CTRL,
|
||||
S5M8767_REG_LDO16CTRL,
|
||||
S5M8767_REG_LDO17CTRL,
|
||||
S5M8767_REG_LDO18CTRL,
|
||||
S5M8767_REG_LDO19CTRL,
|
||||
S5M8767_REG_LDO20CTRL,
|
||||
S5M8767_REG_LDO21CTRL,
|
||||
S5M8767_REG_LDO22CTRL,
|
||||
S5M8767_REG_LDO23CTRL,
|
||||
S5M8767_REG_LDO24CTRL,
|
||||
S5M8767_REG_LDO25CTRL,
|
||||
S5M8767_REG_LDO26CTRL,
|
||||
S5M8767_REG_LDO27CTRL,
|
||||
S5M8767_REG_LDO28CTRL,
|
||||
};
|
||||
|
||||
/* S5M8763 registers */
|
||||
enum s5m8763_reg {
|
||||
S5M8763_REG_IRQ1,
|
||||
S5M8763_REG_IRQ2,
|
||||
S5M8763_REG_IRQ3,
|
||||
S5M8763_REG_IRQ4,
|
||||
S5M8763_REG_IRQM1,
|
||||
S5M8763_REG_IRQM2,
|
||||
S5M8763_REG_IRQM3,
|
||||
S5M8763_REG_IRQM4,
|
||||
S5M8763_REG_STATUS1,
|
||||
S5M8763_REG_STATUS2,
|
||||
S5M8763_REG_STATUSM1,
|
||||
S5M8763_REG_STATUSM2,
|
||||
S5M8763_REG_CHGR1,
|
||||
S5M8763_REG_CHGR2,
|
||||
S5M8763_REG_LDO_ACTIVE_DISCHARGE1,
|
||||
S5M8763_REG_LDO_ACTIVE_DISCHARGE2,
|
||||
S5M8763_REG_BUCK_ACTIVE_DISCHARGE3,
|
||||
S5M8763_REG_ONOFF1,
|
||||
S5M8763_REG_ONOFF2,
|
||||
S5M8763_REG_ONOFF3,
|
||||
S5M8763_REG_ONOFF4,
|
||||
S5M8763_REG_BUCK1_VOLTAGE1,
|
||||
S5M8763_REG_BUCK1_VOLTAGE2,
|
||||
S5M8763_REG_BUCK1_VOLTAGE3,
|
||||
S5M8763_REG_BUCK1_VOLTAGE4,
|
||||
S5M8763_REG_BUCK2_VOLTAGE1,
|
||||
S5M8763_REG_BUCK2_VOLTAGE2,
|
||||
S5M8763_REG_BUCK3,
|
||||
S5M8763_REG_BUCK4,
|
||||
S5M8763_REG_LDO1_LDO2,
|
||||
S5M8763_REG_LDO3,
|
||||
S5M8763_REG_LDO4,
|
||||
S5M8763_REG_LDO5,
|
||||
S5M8763_REG_LDO6,
|
||||
S5M8763_REG_LDO7,
|
||||
S5M8763_REG_LDO7_LDO8,
|
||||
S5M8763_REG_LDO9_LDO10,
|
||||
S5M8763_REG_LDO11,
|
||||
S5M8763_REG_LDO12,
|
||||
S5M8763_REG_LDO13,
|
||||
S5M8763_REG_LDO14,
|
||||
S5M8763_REG_LDO15,
|
||||
S5M8763_REG_LDO16,
|
||||
S5M8763_REG_BKCHR,
|
||||
S5M8763_REG_LBCNFG1,
|
||||
S5M8763_REG_LBCNFG2,
|
||||
};
|
||||
|
||||
enum s5m8767_irq {
|
||||
S5M8767_IRQ_PWRR,
|
||||
S5M8767_IRQ_PWRF,
|
||||
S5M8767_IRQ_PWR1S,
|
||||
S5M8767_IRQ_JIGR,
|
||||
S5M8767_IRQ_JIGF,
|
||||
S5M8767_IRQ_LOWBAT2,
|
||||
S5M8767_IRQ_LOWBAT1,
|
||||
|
||||
S5M8767_IRQ_MRB,
|
||||
S5M8767_IRQ_DVSOK2,
|
||||
S5M8767_IRQ_DVSOK3,
|
||||
S5M8767_IRQ_DVSOK4,
|
||||
|
||||
S5M8767_IRQ_RTC60S,
|
||||
S5M8767_IRQ_RTCA1,
|
||||
S5M8767_IRQ_RTCA2,
|
||||
S5M8767_IRQ_SMPL,
|
||||
S5M8767_IRQ_RTC1S,
|
||||
S5M8767_IRQ_WTSR,
|
||||
|
||||
S5M8767_IRQ_NR,
|
||||
};
|
||||
|
||||
#define S5M8767_IRQ_PWRR_MASK (1 << 0)
|
||||
#define S5M8767_IRQ_PWRF_MASK (1 << 1)
|
||||
#define S5M8767_IRQ_PWR1S_MASK (1 << 3)
|
||||
#define S5M8767_IRQ_JIGR_MASK (1 << 4)
|
||||
#define S5M8767_IRQ_JIGF_MASK (1 << 5)
|
||||
#define S5M8767_IRQ_LOWBAT2_MASK (1 << 6)
|
||||
#define S5M8767_IRQ_LOWBAT1_MASK (1 << 7)
|
||||
|
||||
#define S5M8767_IRQ_MRB_MASK (1 << 2)
|
||||
#define S5M8767_IRQ_DVSOK2_MASK (1 << 3)
|
||||
#define S5M8767_IRQ_DVSOK3_MASK (1 << 4)
|
||||
#define S5M8767_IRQ_DVSOK4_MASK (1 << 5)
|
||||
|
||||
#define S5M8767_IRQ_RTC60S_MASK (1 << 0)
|
||||
#define S5M8767_IRQ_RTCA1_MASK (1 << 1)
|
||||
#define S5M8767_IRQ_RTCA2_MASK (1 << 2)
|
||||
#define S5M8767_IRQ_SMPL_MASK (1 << 3)
|
||||
#define S5M8767_IRQ_RTC1S_MASK (1 << 4)
|
||||
#define S5M8767_IRQ_WTSR_MASK (1 << 5)
|
||||
|
||||
enum s5m8763_irq {
|
||||
S5M8763_IRQ_DCINF,
|
||||
S5M8763_IRQ_DCINR,
|
||||
S5M8763_IRQ_JIGF,
|
||||
S5M8763_IRQ_JIGR,
|
||||
S5M8763_IRQ_PWRONF,
|
||||
S5M8763_IRQ_PWRONR,
|
||||
|
||||
S5M8763_IRQ_WTSREVNT,
|
||||
S5M8763_IRQ_SMPLEVNT,
|
||||
S5M8763_IRQ_ALARM1,
|
||||
S5M8763_IRQ_ALARM0,
|
||||
|
||||
S5M8763_IRQ_ONKEY1S,
|
||||
S5M8763_IRQ_TOPOFFR,
|
||||
S5M8763_IRQ_DCINOVPR,
|
||||
S5M8763_IRQ_CHGRSTF,
|
||||
S5M8763_IRQ_DONER,
|
||||
S5M8763_IRQ_CHGFAULT,
|
||||
|
||||
S5M8763_IRQ_LOBAT1,
|
||||
S5M8763_IRQ_LOBAT2,
|
||||
|
||||
S5M8763_IRQ_NR,
|
||||
};
|
||||
|
||||
#define S5M8763_IRQ_DCINF_MASK (1 << 2)
|
||||
#define S5M8763_IRQ_DCINR_MASK (1 << 3)
|
||||
#define S5M8763_IRQ_JIGF_MASK (1 << 4)
|
||||
#define S5M8763_IRQ_JIGR_MASK (1 << 5)
|
||||
#define S5M8763_IRQ_PWRONF_MASK (1 << 6)
|
||||
#define S5M8763_IRQ_PWRONR_MASK (1 << 7)
|
||||
|
||||
#define S5M8763_IRQ_WTSREVNT_MASK (1 << 0)
|
||||
#define S5M8763_IRQ_SMPLEVNT_MASK (1 << 1)
|
||||
#define S5M8763_IRQ_ALARM1_MASK (1 << 2)
|
||||
#define S5M8763_IRQ_ALARM0_MASK (1 << 3)
|
||||
|
||||
#define S5M8763_IRQ_ONKEY1S_MASK (1 << 0)
|
||||
#define S5M8763_IRQ_TOPOFFR_MASK (1 << 2)
|
||||
#define S5M8763_IRQ_DCINOVPR_MASK (1 << 3)
|
||||
#define S5M8763_IRQ_CHGRSTF_MASK (1 << 4)
|
||||
#define S5M8763_IRQ_DONER_MASK (1 << 5)
|
||||
#define S5M8763_IRQ_CHGFAULT_MASK (1 << 7)
|
||||
|
||||
#define S5M8763_IRQ_LOBAT1_MASK (1 << 0)
|
||||
#define S5M8763_IRQ_LOBAT2_MASK (1 << 1)
|
||||
|
||||
#define S5M8763_ENRAMP (1 << 4)
|
||||
|
||||
/**
|
||||
* struct s5m87xx_dev - s5m87xx master device for sub-drivers
|
||||
* @dev: master device of the chip (can be used to access platform data)
|
||||
* @i2c: i2c client private data for regulator
|
||||
* @rtc: i2c client private data for rtc
|
||||
* @iolock: mutex for serializing io access
|
||||
* @irqlock: mutex for buslock
|
||||
* @irq_base: base IRQ number for s5m87xx, required for IRQs
|
||||
* @irq: generic IRQ number for s5m87xx
|
||||
* @ono: power onoff IRQ number for s5m87xx
|
||||
* @irq_masks_cur: currently active value
|
||||
* @irq_masks_cache: cached hardware value
|
||||
* @type: indicate which s5m87xx "variant" is used
|
||||
*/
|
||||
struct s5m87xx_dev {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct i2c_client *i2c;
|
||||
struct i2c_client *rtc;
|
||||
struct mutex iolock;
|
||||
struct mutex irqlock;
|
||||
|
||||
int device_type;
|
||||
int irq_base;
|
||||
int irq;
|
||||
int ono;
|
||||
u8 irq_masks_cur[NUM_IRQ_REGS];
|
||||
u8 irq_masks_cache[NUM_IRQ_REGS];
|
||||
int type;
|
||||
bool wakeup;
|
||||
};
|
||||
|
||||
int s5m_irq_init(struct s5m87xx_dev *s5m87xx);
|
||||
void s5m_irq_exit(struct s5m87xx_dev *s5m87xx);
|
||||
int s5m_irq_resume(struct s5m87xx_dev *s5m87xx);
|
||||
|
||||
extern int s5m_reg_read(struct s5m87xx_dev *s5m87xx, u8 reg, void *dest);
|
||||
extern int s5m_bulk_read(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf);
|
||||
extern int s5m_reg_write(struct s5m87xx_dev *s5m87xx, u8 reg, u8 value);
|
||||
extern int s5m_bulk_write(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf);
|
||||
extern int s5m_reg_update(struct s5m87xx_dev *s5m87xx, u8 reg, u8 val, u8 mask);
|
||||
|
||||
struct s5m_platform_data {
|
||||
struct s5m_regulator_data *regulators;
|
||||
struct s5m_opmode_data *opmode;
|
||||
int device_type;
|
||||
int num_regulators;
|
||||
|
||||
int irq_base;
|
||||
int (*cfg_pmic_irq)(void);
|
||||
|
||||
int ono;
|
||||
bool wakeup;
|
||||
bool buck_voltage_lock;
|
||||
|
||||
int buck_gpios[3];
|
||||
int buck_ds[3];
|
||||
int buck2_voltage[8];
|
||||
bool buck2_gpiodvs;
|
||||
int buck3_voltage[8];
|
||||
bool buck3_gpiodvs;
|
||||
int buck4_voltage[8];
|
||||
bool buck4_gpiodvs;
|
||||
|
||||
int buck_set1;
|
||||
int buck_set2;
|
||||
int buck_set3;
|
||||
int buck2_enable;
|
||||
int buck3_enable;
|
||||
int buck4_enable;
|
||||
int buck_default_idx;
|
||||
int buck2_default_idx;
|
||||
int buck3_default_idx;
|
||||
int buck4_default_idx;
|
||||
|
||||
int buck_ramp_delay;
|
||||
bool buck2_ramp_enable;
|
||||
bool buck3_ramp_enable;
|
||||
bool buck4_ramp_enable;
|
||||
|
||||
int buck2_init;
|
||||
int buck3_init;
|
||||
int buck4_init;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_MFD_S5M_CORE_H */
|
|
@ -1,129 +0,0 @@
|
|||
/* s5m87xx.h
|
||||
*
|
||||
* Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.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.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MFD_S5M_PMIC_H
|
||||
#define __LINUX_MFD_S5M_PMIC_H
|
||||
|
||||
#include <linux/regulator/machine.h>
|
||||
|
||||
/* S5M8767 regulator ids */
|
||||
enum s5m8767_regulators {
|
||||
S5M8767_LDO1,
|
||||
S5M8767_LDO2,
|
||||
S5M8767_LDO3,
|
||||
S5M8767_LDO4,
|
||||
S5M8767_LDO5,
|
||||
S5M8767_LDO6,
|
||||
S5M8767_LDO7,
|
||||
S5M8767_LDO8,
|
||||
S5M8767_LDO9,
|
||||
S5M8767_LDO10,
|
||||
S5M8767_LDO11,
|
||||
S5M8767_LDO12,
|
||||
S5M8767_LDO13,
|
||||
S5M8767_LDO14,
|
||||
S5M8767_LDO15,
|
||||
S5M8767_LDO16,
|
||||
S5M8767_LDO17,
|
||||
S5M8767_LDO18,
|
||||
S5M8767_LDO19,
|
||||
S5M8767_LDO20,
|
||||
S5M8767_LDO21,
|
||||
S5M8767_LDO22,
|
||||
S5M8767_LDO23,
|
||||
S5M8767_LDO24,
|
||||
S5M8767_LDO25,
|
||||
S5M8767_LDO26,
|
||||
S5M8767_LDO27,
|
||||
S5M8767_LDO28,
|
||||
S5M8767_BUCK1,
|
||||
S5M8767_BUCK2,
|
||||
S5M8767_BUCK3,
|
||||
S5M8767_BUCK4,
|
||||
S5M8767_BUCK5,
|
||||
S5M8767_BUCK6,
|
||||
S5M8767_BUCK7,
|
||||
S5M8767_BUCK8,
|
||||
S5M8767_BUCK9,
|
||||
S5M8767_AP_EN32KHZ,
|
||||
S5M8767_CP_EN32KHZ,
|
||||
|
||||
S5M8767_REG_MAX,
|
||||
};
|
||||
|
||||
#define S5M8767_ENCTRL_SHIFT 6
|
||||
|
||||
/* S5M8763 regulator ids */
|
||||
enum s5m8763_regulators {
|
||||
S5M8763_LDO1,
|
||||
S5M8763_LDO2,
|
||||
S5M8763_LDO3,
|
||||
S5M8763_LDO4,
|
||||
S5M8763_LDO5,
|
||||
S5M8763_LDO6,
|
||||
S5M8763_LDO7,
|
||||
S5M8763_LDO8,
|
||||
S5M8763_LDO9,
|
||||
S5M8763_LDO10,
|
||||
S5M8763_LDO11,
|
||||
S5M8763_LDO12,
|
||||
S5M8763_LDO13,
|
||||
S5M8763_LDO14,
|
||||
S5M8763_LDO15,
|
||||
S5M8763_LDO16,
|
||||
S5M8763_BUCK1,
|
||||
S5M8763_BUCK2,
|
||||
S5M8763_BUCK3,
|
||||
S5M8763_BUCK4,
|
||||
S5M8763_AP_EN32KHZ,
|
||||
S5M8763_CP_EN32KHZ,
|
||||
S5M8763_ENCHGVI,
|
||||
S5M8763_ESAFEUSB1,
|
||||
S5M8763_ESAFEUSB2,
|
||||
};
|
||||
|
||||
/**
|
||||
* s5m87xx_regulator_data - regulator data
|
||||
* @id: regulator id
|
||||
* @initdata: regulator init data (contraints, supplies, ...)
|
||||
*/
|
||||
struct s5m_regulator_data {
|
||||
int id;
|
||||
struct regulator_init_data *initdata;
|
||||
};
|
||||
|
||||
/*
|
||||
* s5m_opmode_data - regulator operation mode data
|
||||
* @id: regulator id
|
||||
* @mode: regulator operation mode
|
||||
*/
|
||||
struct s5m_opmode_data {
|
||||
int id;
|
||||
int mode;
|
||||
};
|
||||
|
||||
/*
|
||||
* s5m regulator operation mode
|
||||
* S5M_OPMODE_OFF Regulator always OFF
|
||||
* S5M_OPMODE_ON Regulator always ON
|
||||
* S5M_OPMODE_LOWPOWER Regulator is on in low-power mode
|
||||
* S5M_OPMODE_SUSPEND Regulator is changed by PWREN pin
|
||||
* If PWREN is high, regulator is on
|
||||
* If PWREN is low, regulator is off
|
||||
*/
|
||||
|
||||
enum s5m_opmode {
|
||||
S5M_OPMODE_OFF,
|
||||
S5M_OPMODE_ON,
|
||||
S5M_OPMODE_LOWPOWER,
|
||||
S5M_OPMODE_SUSPEND,
|
||||
};
|
||||
|
||||
#endif /* __LINUX_MFD_S5M_PMIC_H */
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* core.h
|
||||
*
|
||||
* copyright (c) 2011 Samsung Electronics Co., Ltd
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MFD_SEC_CORE_H
|
||||
#define __LINUX_MFD_SEC_CORE_H
|
||||
|
||||
#define NUM_IRQ_REGS 4
|
||||
|
||||
enum sec_device_type {
|
||||
S5M8751X,
|
||||
S5M8763X,
|
||||
S5M8767X,
|
||||
S2MPS11X,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sec_pmic_dev - s5m87xx master device for sub-drivers
|
||||
* @dev: master device of the chip (can be used to access platform data)
|
||||
* @i2c: i2c client private data for regulator
|
||||
* @rtc: i2c client private data for rtc
|
||||
* @iolock: mutex for serializing io access
|
||||
* @irqlock: mutex for buslock
|
||||
* @irq_base: base IRQ number for sec-pmic, required for IRQs
|
||||
* @irq: generic IRQ number for s5m87xx
|
||||
* @ono: power onoff IRQ number for s5m87xx
|
||||
* @irq_masks_cur: currently active value
|
||||
* @irq_masks_cache: cached hardware value
|
||||
* @type: indicate which s5m87xx "variant" is used
|
||||
*/
|
||||
struct sec_pmic_dev {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct i2c_client *i2c;
|
||||
struct i2c_client *rtc;
|
||||
struct mutex iolock;
|
||||
struct mutex irqlock;
|
||||
|
||||
int device_type;
|
||||
int irq_base;
|
||||
int irq;
|
||||
struct regmap_irq_chip_data *irq_data;
|
||||
|
||||
int ono;
|
||||
u8 irq_masks_cur[NUM_IRQ_REGS];
|
||||
u8 irq_masks_cache[NUM_IRQ_REGS];
|
||||
int type;
|
||||
bool wakeup;
|
||||
};
|
||||
|
||||
int sec_irq_init(struct sec_pmic_dev *sec_pmic);
|
||||
void sec_irq_exit(struct sec_pmic_dev *sec_pmic);
|
||||
int sec_irq_resume(struct sec_pmic_dev *sec_pmic);
|
||||
|
||||
extern int sec_reg_read(struct sec_pmic_dev *sec_pmic, u8 reg, void *dest);
|
||||
extern int sec_bulk_read(struct sec_pmic_dev *sec_pmic, u8 reg, int count, u8 *buf);
|
||||
extern int sec_reg_write(struct sec_pmic_dev *sec_pmic, u8 reg, u8 value);
|
||||
extern int sec_bulk_write(struct sec_pmic_dev *sec_pmic, u8 reg, int count, u8 *buf);
|
||||
extern int sec_reg_update(struct sec_pmic_dev *sec_pmic, u8 reg, u8 val, u8 mask);
|
||||
|
||||
struct sec_platform_data {
|
||||
struct sec_regulator_data *regulators;
|
||||
struct sec_opmode_data *opmode;
|
||||
int device_type;
|
||||
int num_regulators;
|
||||
|
||||
int irq_base;
|
||||
int (*cfg_pmic_irq)(void);
|
||||
|
||||
int ono;
|
||||
bool wakeup;
|
||||
bool buck_voltage_lock;
|
||||
|
||||
int buck_gpios[3];
|
||||
int buck_ds[3];
|
||||
int buck2_voltage[8];
|
||||
bool buck2_gpiodvs;
|
||||
int buck3_voltage[8];
|
||||
bool buck3_gpiodvs;
|
||||
int buck4_voltage[8];
|
||||
bool buck4_gpiodvs;
|
||||
|
||||
int buck_set1;
|
||||
int buck_set2;
|
||||
int buck_set3;
|
||||
int buck2_enable;
|
||||
int buck3_enable;
|
||||
int buck4_enable;
|
||||
int buck_default_idx;
|
||||
int buck2_default_idx;
|
||||
int buck3_default_idx;
|
||||
int buck4_default_idx;
|
||||
|
||||
int buck_ramp_delay;
|
||||
|
||||
int buck2_ramp_delay;
|
||||
int buck34_ramp_delay;
|
||||
int buck5_ramp_delay;
|
||||
int buck16_ramp_delay;
|
||||
int buck7810_ramp_delay;
|
||||
int buck9_ramp_delay;
|
||||
|
||||
bool buck2_ramp_enable;
|
||||
bool buck3_ramp_enable;
|
||||
bool buck4_ramp_enable;
|
||||
bool buck6_ramp_enable;
|
||||
|
||||
int buck2_init;
|
||||
int buck3_init;
|
||||
int buck4_init;
|
||||
};
|
||||
|
||||
/**
|
||||
* sec_regulator_data - regulator data
|
||||
* @id: regulator id
|
||||
* @initdata: regulator init data (contraints, supplies, ...)
|
||||
*/
|
||||
struct sec_regulator_data {
|
||||
int id;
|
||||
struct regulator_init_data *initdata;
|
||||
};
|
||||
|
||||
/*
|
||||
* sec_opmode_data - regulator operation mode data
|
||||
* @id: regulator id
|
||||
* @mode: regulator operation mode
|
||||
*/
|
||||
struct sec_opmode_data {
|
||||
int id;
|
||||
int mode;
|
||||
};
|
||||
|
||||
/*
|
||||
* samsung regulator operation mode
|
||||
* SEC_OPMODE_OFF Regulator always OFF
|
||||
* SEC_OPMODE_ON Regulator always ON
|
||||
* SEC_OPMODE_LOWPOWER Regulator is on in low-power mode
|
||||
* SEC_OPMODE_SUSPEND Regulator is changed by PWREN pin
|
||||
* If PWREN is high, regulator is on
|
||||
* If PWREN is low, regulator is off
|
||||
*/
|
||||
|
||||
enum sec_opmode {
|
||||
SEC_OPMODE_OFF,
|
||||
SEC_OPMODE_ON,
|
||||
SEC_OPMODE_LOWPOWER,
|
||||
SEC_OPMODE_SUSPEND,
|
||||
};
|
||||
|
||||
#endif /* __LINUX_MFD_SEC_CORE_H */
|
|
@ -0,0 +1,152 @@
|
|||
/* irq.h
|
||||
*
|
||||
* Copyright (c) 2012 Samsung Electronics Co., Ltd
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MFD_SEC_IRQ_H
|
||||
#define __LINUX_MFD_SEC_IRQ_H
|
||||
|
||||
enum s2mps11_irq {
|
||||
S2MPS11_IRQ_PWRONF,
|
||||
S2MPS11_IRQ_PWRONR,
|
||||
S2MPS11_IRQ_JIGONBF,
|
||||
S2MPS11_IRQ_JIGONBR,
|
||||
S2MPS11_IRQ_ACOKBF,
|
||||
S2MPS11_IRQ_ACOKBR,
|
||||
S2MPS11_IRQ_PWRON1S,
|
||||
S2MPS11_IRQ_MRB,
|
||||
|
||||
S2MPS11_IRQ_RTC60S,
|
||||
S2MPS11_IRQ_RTCA1,
|
||||
S2MPS11_IRQ_RTCA2,
|
||||
S2MPS11_IRQ_SMPL,
|
||||
S2MPS11_IRQ_RTC1S,
|
||||
S2MPS11_IRQ_WTSR,
|
||||
|
||||
S2MPS11_IRQ_INT120C,
|
||||
S2MPS11_IRQ_INT140C,
|
||||
|
||||
S2MPS11_IRQ_NR,
|
||||
};
|
||||
|
||||
#define S2MPS11_IRQ_PWRONF_MASK (1 << 0)
|
||||
#define S2MPS11_IRQ_PWRONR_MASK (1 << 1)
|
||||
#define S2MPS11_IRQ_JIGONBF_MASK (1 << 2)
|
||||
#define S2MPS11_IRQ_JIGONBR_MASK (1 << 3)
|
||||
#define S2MPS11_IRQ_ACOKBF_MASK (1 << 4)
|
||||
#define S2MPS11_IRQ_ACOKBR_MASK (1 << 5)
|
||||
#define S2MPS11_IRQ_PWRON1S_MASK (1 << 6)
|
||||
#define S2MPS11_IRQ_MRB_MASK (1 << 7)
|
||||
|
||||
#define S2MPS11_IRQ_RTC60S_MASK (1 << 0)
|
||||
#define S2MPS11_IRQ_RTCA1_MASK (1 << 1)
|
||||
#define S2MPS11_IRQ_RTCA2_MASK (1 << 2)
|
||||
#define S2MPS11_IRQ_SMPL_MASK (1 << 3)
|
||||
#define S2MPS11_IRQ_RTC1S_MASK (1 << 4)
|
||||
#define S2MPS11_IRQ_WTSR_MASK (1 << 5)
|
||||
|
||||
#define S2MPS11_IRQ_INT120C_MASK (1 << 0)
|
||||
#define S2MPS11_IRQ_INT140C_MASK (1 << 1)
|
||||
|
||||
enum s5m8767_irq {
|
||||
S5M8767_IRQ_PWRR,
|
||||
S5M8767_IRQ_PWRF,
|
||||
S5M8767_IRQ_PWR1S,
|
||||
S5M8767_IRQ_JIGR,
|
||||
S5M8767_IRQ_JIGF,
|
||||
S5M8767_IRQ_LOWBAT2,
|
||||
S5M8767_IRQ_LOWBAT1,
|
||||
|
||||
S5M8767_IRQ_MRB,
|
||||
S5M8767_IRQ_DVSOK2,
|
||||
S5M8767_IRQ_DVSOK3,
|
||||
S5M8767_IRQ_DVSOK4,
|
||||
|
||||
S5M8767_IRQ_RTC60S,
|
||||
S5M8767_IRQ_RTCA1,
|
||||
S5M8767_IRQ_RTCA2,
|
||||
S5M8767_IRQ_SMPL,
|
||||
S5M8767_IRQ_RTC1S,
|
||||
S5M8767_IRQ_WTSR,
|
||||
|
||||
S5M8767_IRQ_NR,
|
||||
};
|
||||
|
||||
#define S5M8767_IRQ_PWRR_MASK (1 << 0)
|
||||
#define S5M8767_IRQ_PWRF_MASK (1 << 1)
|
||||
#define S5M8767_IRQ_PWR1S_MASK (1 << 3)
|
||||
#define S5M8767_IRQ_JIGR_MASK (1 << 4)
|
||||
#define S5M8767_IRQ_JIGF_MASK (1 << 5)
|
||||
#define S5M8767_IRQ_LOWBAT2_MASK (1 << 6)
|
||||
#define S5M8767_IRQ_LOWBAT1_MASK (1 << 7)
|
||||
|
||||
#define S5M8767_IRQ_MRB_MASK (1 << 2)
|
||||
#define S5M8767_IRQ_DVSOK2_MASK (1 << 3)
|
||||
#define S5M8767_IRQ_DVSOK3_MASK (1 << 4)
|
||||
#define S5M8767_IRQ_DVSOK4_MASK (1 << 5)
|
||||
|
||||
#define S5M8767_IRQ_RTC60S_MASK (1 << 0)
|
||||
#define S5M8767_IRQ_RTCA1_MASK (1 << 1)
|
||||
#define S5M8767_IRQ_RTCA2_MASK (1 << 2)
|
||||
#define S5M8767_IRQ_SMPL_MASK (1 << 3)
|
||||
#define S5M8767_IRQ_RTC1S_MASK (1 << 4)
|
||||
#define S5M8767_IRQ_WTSR_MASK (1 << 5)
|
||||
|
||||
enum s5m8763_irq {
|
||||
S5M8763_IRQ_DCINF,
|
||||
S5M8763_IRQ_DCINR,
|
||||
S5M8763_IRQ_JIGF,
|
||||
S5M8763_IRQ_JIGR,
|
||||
S5M8763_IRQ_PWRONF,
|
||||
S5M8763_IRQ_PWRONR,
|
||||
|
||||
S5M8763_IRQ_WTSREVNT,
|
||||
S5M8763_IRQ_SMPLEVNT,
|
||||
S5M8763_IRQ_ALARM1,
|
||||
S5M8763_IRQ_ALARM0,
|
||||
|
||||
S5M8763_IRQ_ONKEY1S,
|
||||
S5M8763_IRQ_TOPOFFR,
|
||||
S5M8763_IRQ_DCINOVPR,
|
||||
S5M8763_IRQ_CHGRSTF,
|
||||
S5M8763_IRQ_DONER,
|
||||
S5M8763_IRQ_CHGFAULT,
|
||||
|
||||
S5M8763_IRQ_LOBAT1,
|
||||
S5M8763_IRQ_LOBAT2,
|
||||
|
||||
S5M8763_IRQ_NR,
|
||||
};
|
||||
|
||||
#define S5M8763_IRQ_DCINF_MASK (1 << 2)
|
||||
#define S5M8763_IRQ_DCINR_MASK (1 << 3)
|
||||
#define S5M8763_IRQ_JIGF_MASK (1 << 4)
|
||||
#define S5M8763_IRQ_JIGR_MASK (1 << 5)
|
||||
#define S5M8763_IRQ_PWRONF_MASK (1 << 6)
|
||||
#define S5M8763_IRQ_PWRONR_MASK (1 << 7)
|
||||
|
||||
#define S5M8763_IRQ_WTSREVNT_MASK (1 << 0)
|
||||
#define S5M8763_IRQ_SMPLEVNT_MASK (1 << 1)
|
||||
#define S5M8763_IRQ_ALARM1_MASK (1 << 2)
|
||||
#define S5M8763_IRQ_ALARM0_MASK (1 << 3)
|
||||
|
||||
#define S5M8763_IRQ_ONKEY1S_MASK (1 << 0)
|
||||
#define S5M8763_IRQ_TOPOFFR_MASK (1 << 2)
|
||||
#define S5M8763_IRQ_DCINOVPR_MASK (1 << 3)
|
||||
#define S5M8763_IRQ_CHGRSTF_MASK (1 << 4)
|
||||
#define S5M8763_IRQ_DONER_MASK (1 << 5)
|
||||
#define S5M8763_IRQ_CHGFAULT_MASK (1 << 7)
|
||||
|
||||
#define S5M8763_IRQ_LOBAT1_MASK (1 << 0)
|
||||
#define S5M8763_IRQ_LOBAT2_MASK (1 << 1)
|
||||
|
||||
#define S5M8763_ENRAMP (1 << 4)
|
||||
|
||||
#endif /* __LINUX_MFD_SEC_IRQ_H */
|
|
@ -1,5 +1,4 @@
|
|||
/*
|
||||
* s5m-rtc.h
|
||||
/* rtc.h
|
||||
*
|
||||
* Copyright (c) 2011 Samsung Electronics Co., Ltd
|
||||
* http://www.samsung.com
|
||||
|
@ -11,39 +10,39 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MFD_S5M_RTC_H
|
||||
#define __LINUX_MFD_S5M_RTC_H
|
||||
#ifndef __LINUX_MFD_SEC_RTC_H
|
||||
#define __LINUX_MFD_SEC_RTC_H
|
||||
|
||||
enum s5m87xx_rtc_reg {
|
||||
S5M87XX_RTC_SEC,
|
||||
S5M87XX_RTC_MIN,
|
||||
S5M87XX_RTC_HOUR,
|
||||
S5M87XX_RTC_WEEKDAY,
|
||||
S5M87XX_RTC_DATE,
|
||||
S5M87XX_RTC_MONTH,
|
||||
S5M87XX_RTC_YEAR1,
|
||||
S5M87XX_RTC_YEAR2,
|
||||
S5M87XX_ALARM0_SEC,
|
||||
S5M87XX_ALARM0_MIN,
|
||||
S5M87XX_ALARM0_HOUR,
|
||||
S5M87XX_ALARM0_WEEKDAY,
|
||||
S5M87XX_ALARM0_DATE,
|
||||
S5M87XX_ALARM0_MONTH,
|
||||
S5M87XX_ALARM0_YEAR1,
|
||||
S5M87XX_ALARM0_YEAR2,
|
||||
S5M87XX_ALARM1_SEC,
|
||||
S5M87XX_ALARM1_MIN,
|
||||
S5M87XX_ALARM1_HOUR,
|
||||
S5M87XX_ALARM1_WEEKDAY,
|
||||
S5M87XX_ALARM1_DATE,
|
||||
S5M87XX_ALARM1_MONTH,
|
||||
S5M87XX_ALARM1_YEAR1,
|
||||
S5M87XX_ALARM1_YEAR2,
|
||||
S5M87XX_ALARM0_CONF,
|
||||
S5M87XX_ALARM1_CONF,
|
||||
S5M87XX_RTC_STATUS,
|
||||
S5M87XX_WTSR_SMPL_CNTL,
|
||||
S5M87XX_RTC_UDR_CON,
|
||||
enum sec_rtc_reg {
|
||||
SEC_RTC_SEC,
|
||||
SEC_RTC_MIN,
|
||||
SEC_RTC_HOUR,
|
||||
SEC_RTC_WEEKDAY,
|
||||
SEC_RTC_DATE,
|
||||
SEC_RTC_MONTH,
|
||||
SEC_RTC_YEAR1,
|
||||
SEC_RTC_YEAR2,
|
||||
SEC_ALARM0_SEC,
|
||||
SEC_ALARM0_MIN,
|
||||
SEC_ALARM0_HOUR,
|
||||
SEC_ALARM0_WEEKDAY,
|
||||
SEC_ALARM0_DATE,
|
||||
SEC_ALARM0_MONTH,
|
||||
SEC_ALARM0_YEAR1,
|
||||
SEC_ALARM0_YEAR2,
|
||||
SEC_ALARM1_SEC,
|
||||
SEC_ALARM1_MIN,
|
||||
SEC_ALARM1_HOUR,
|
||||
SEC_ALARM1_WEEKDAY,
|
||||
SEC_ALARM1_DATE,
|
||||
SEC_ALARM1_MONTH,
|
||||
SEC_ALARM1_YEAR1,
|
||||
SEC_ALARM1_YEAR2,
|
||||
SEC_ALARM0_CONF,
|
||||
SEC_ALARM1_CONF,
|
||||
SEC_RTC_STATUS,
|
||||
SEC_WTSR_SMPL_CNTL,
|
||||
SEC_RTC_UDR_CON,
|
||||
};
|
||||
|
||||
#define RTC_I2C_ADDR (0x0C >> 1)
|
||||
|
@ -81,4 +80,4 @@ enum {
|
|||
RTC_YEAR2,
|
||||
};
|
||||
|
||||
#endif /* __LINUX_MFD_S5M_RTC_H */
|
||||
#endif /* __LINUX_MFD_SEC_RTC_H */
|
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
* s2mps11.h
|
||||
*
|
||||
* Copyright (c) 2012 Samsung Electronics Co., Ltd
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MFD_S2MPS11_H
|
||||
#define __LINUX_MFD_S2MPS11_H
|
||||
|
||||
/* S2MPS11 registers */
|
||||
enum s2mps11_reg {
|
||||
S2MPS11_REG_ID,
|
||||
S2MPS11_REG_INT1,
|
||||
S2MPS11_REG_INT2,
|
||||
S2MPS11_REG_INT3,
|
||||
S2MPS11_REG_INT1M,
|
||||
S2MPS11_REG_INT2M,
|
||||
S2MPS11_REG_INT3M,
|
||||
S2MPS11_REG_ST1,
|
||||
S2MPS11_REG_ST2,
|
||||
S2MPS11_REG_OFFSRC,
|
||||
S2MPS11_REG_PWRONSRC,
|
||||
S2MPS11_REG_RTC_CTRL,
|
||||
S2MPS11_REG_CTRL1,
|
||||
S2MPS11_REG_ETC_TEST,
|
||||
S2MPS11_REG_RSVD3,
|
||||
S2MPS11_REG_BU_CHG,
|
||||
S2MPS11_REG_RAMP,
|
||||
S2MPS11_REG_RAMP_BUCK,
|
||||
S2MPS11_REG_LDO1_8,
|
||||
S2MPS11_REG_LDO9_16,
|
||||
S2MPS11_REG_LDO17_24,
|
||||
S2MPS11_REG_LDO25_32,
|
||||
S2MPS11_REG_LDO33_38,
|
||||
S2MPS11_REG_LDO1_8_1,
|
||||
S2MPS11_REG_LDO9_16_1,
|
||||
S2MPS11_REG_LDO17_24_1,
|
||||
S2MPS11_REG_LDO25_32_1,
|
||||
S2MPS11_REG_LDO33_38_1,
|
||||
S2MPS11_REG_OTP_ADRL,
|
||||
S2MPS11_REG_OTP_ADRH,
|
||||
S2MPS11_REG_OTP_DATA,
|
||||
S2MPS11_REG_MON1SEL,
|
||||
S2MPS11_REG_MON2SEL,
|
||||
S2MPS11_REG_LEE,
|
||||
S2MPS11_REG_RSVD_NO,
|
||||
S2MPS11_REG_UVLO,
|
||||
S2MPS11_REG_LEE_NO,
|
||||
S2MPS11_REG_B1CTRL1,
|
||||
S2MPS11_REG_B1CTRL2,
|
||||
S2MPS11_REG_B2CTRL1,
|
||||
S2MPS11_REG_B2CTRL2,
|
||||
S2MPS11_REG_B3CTRL1,
|
||||
S2MPS11_REG_B3CTRL2,
|
||||
S2MPS11_REG_B4CTRL1,
|
||||
S2MPS11_REG_B4CTRL2,
|
||||
S2MPS11_REG_B5CTRL1,
|
||||
S2MPS11_REG_BUCK5_SW,
|
||||
S2MPS11_REG_B5CTRL2,
|
||||
S2MPS11_REG_B5CTRL3,
|
||||
S2MPS11_REG_B5CTRL4,
|
||||
S2MPS11_REG_B5CTRL5,
|
||||
S2MPS11_REG_B6CTRL1,
|
||||
S2MPS11_REG_B6CTRL2,
|
||||
S2MPS11_REG_B7CTRL1,
|
||||
S2MPS11_REG_B7CTRL2,
|
||||
S2MPS11_REG_B8CTRL1,
|
||||
S2MPS11_REG_B8CTRL2,
|
||||
S2MPS11_REG_B9CTRL1,
|
||||
S2MPS11_REG_B9CTRL2,
|
||||
S2MPS11_REG_B10CTRL1,
|
||||
S2MPS11_REG_B10CTRL2,
|
||||
S2MPS11_REG_L1CTRL,
|
||||
S2MPS11_REG_L2CTRL,
|
||||
S2MPS11_REG_L3CTRL,
|
||||
S2MPS11_REG_L4CTRL,
|
||||
S2MPS11_REG_L5CTRL,
|
||||
S2MPS11_REG_L6CTRL,
|
||||
S2MPS11_REG_L7CTRL,
|
||||
S2MPS11_REG_L8CTRL,
|
||||
S2MPS11_REG_L9CTRL,
|
||||
S2MPS11_REG_L10CTRL,
|
||||
S2MPS11_REG_L11CTRL,
|
||||
S2MPS11_REG_L12CTRL,
|
||||
S2MPS11_REG_L13CTRL,
|
||||
S2MPS11_REG_L14CTRL,
|
||||
S2MPS11_REG_L15CTRL,
|
||||
S2MPS11_REG_L16CTRL,
|
||||
S2MPS11_REG_L17CTRL,
|
||||
S2MPS11_REG_L18CTRL,
|
||||
S2MPS11_REG_L19CTRL,
|
||||
S2MPS11_REG_L20CTRL,
|
||||
S2MPS11_REG_L21CTRL,
|
||||
S2MPS11_REG_L22CTRL,
|
||||
S2MPS11_REG_L23CTRL,
|
||||
S2MPS11_REG_L24CTRL,
|
||||
S2MPS11_REG_L25CTRL,
|
||||
S2MPS11_REG_L26CTRL,
|
||||
S2MPS11_REG_L27CTRL,
|
||||
S2MPS11_REG_L28CTRL,
|
||||
S2MPS11_REG_L29CTRL,
|
||||
S2MPS11_REG_L30CTRL,
|
||||
S2MPS11_REG_L31CTRL,
|
||||
S2MPS11_REG_L32CTRL,
|
||||
S2MPS11_REG_L33CTRL,
|
||||
S2MPS11_REG_L34CTRL,
|
||||
S2MPS11_REG_L35CTRL,
|
||||
S2MPS11_REG_L36CTRL,
|
||||
S2MPS11_REG_L37CTRL,
|
||||
S2MPS11_REG_L38CTRL,
|
||||
};
|
||||
|
||||
/* S2MPS11 regulator ids */
|
||||
enum s2mps11_regulators {
|
||||
S2MPS11_LDO1,
|
||||
S2MPS11_LDO2,
|
||||
S2MPS11_LDO3,
|
||||
S2MPS11_LDO4,
|
||||
S2MPS11_LDO5,
|
||||
S2MPS11_LDO6,
|
||||
S2MPS11_LDO7,
|
||||
S2MPS11_LDO8,
|
||||
S2MPS11_LDO9,
|
||||
S2MPS11_LDO10,
|
||||
S2MPS11_LDO11,
|
||||
S2MPS11_LDO12,
|
||||
S2MPS11_LDO13,
|
||||
S2MPS11_LDO14,
|
||||
S2MPS11_LDO15,
|
||||
S2MPS11_LDO16,
|
||||
S2MPS11_LDO17,
|
||||
S2MPS11_LDO18,
|
||||
S2MPS11_LDO19,
|
||||
S2MPS11_LDO20,
|
||||
S2MPS11_LDO21,
|
||||
S2MPS11_LDO22,
|
||||
S2MPS11_LDO23,
|
||||
S2MPS11_LDO24,
|
||||
S2MPS11_LDO25,
|
||||
S2MPS11_LDO26,
|
||||
S2MPS11_LDO27,
|
||||
S2MPS11_LDO28,
|
||||
S2MPS11_LDO29,
|
||||
S2MPS11_LDO30,
|
||||
S2MPS11_LDO31,
|
||||
S2MPS11_LDO32,
|
||||
S2MPS11_LDO33,
|
||||
S2MPS11_LDO34,
|
||||
S2MPS11_LDO35,
|
||||
S2MPS11_LDO36,
|
||||
S2MPS11_LDO37,
|
||||
S2MPS11_LDO38,
|
||||
S2MPS11_BUCK1,
|
||||
S2MPS11_BUCK2,
|
||||
S2MPS11_BUCK3,
|
||||
S2MPS11_BUCK4,
|
||||
S2MPS11_BUCK5,
|
||||
S2MPS11_BUCK6,
|
||||
S2MPS11_BUCK7,
|
||||
S2MPS11_BUCK8,
|
||||
S2MPS11_BUCK9,
|
||||
S2MPS11_BUCK10,
|
||||
S2MPS11_AP_EN32KHZ,
|
||||
S2MPS11_CP_EN32KHZ,
|
||||
S2MPS11_BT_EN32KHZ,
|
||||
|
||||
S2MPS11_REG_MAX,
|
||||
};
|
||||
|
||||
#define S2MPS11_BUCK_MIN1 600000
|
||||
#define S2MPS11_BUCK_MIN2 750000
|
||||
#define S2MPS11_BUCK_MIN3 3000000
|
||||
#define S2MPS11_LDO_MIN 800000
|
||||
#define S2MPS11_BUCK_STEP1 6250
|
||||
#define S2MPS11_BUCK_STEP2 12500
|
||||
#define S2MPS11_BUCK_STEP3 25000
|
||||
#define S2MPS11_LDO_STEP1 50000
|
||||
#define S2MPS11_LDO_STEP2 25000
|
||||
#define S2MPS11_LDO_VSEL_MASK 0x3F
|
||||
#define S2MPS11_BUCK_VSEL_MASK 0xFF
|
||||
#define S2MPS11_ENABLE_MASK (0x03 << S2MPS11_ENABLE_SHIFT)
|
||||
#define S2MPS11_ENABLE_SHIFT 0x06
|
||||
#define S2MPS11_LDO_N_VOLTAGES (S2MPS11_LDO_VSEL_MASK + 1)
|
||||
#define S2MPS11_BUCK_N_VOLTAGES (S2MPS11_BUCK_VSEL_MASK + 1)
|
||||
|
||||
#define S2MPS11_PMIC_EN_SHIFT 6
|
||||
#define S2MPS11_REGULATOR_MAX (S2MPS11_REG_MAX - 3)
|
||||
|
||||
#endif /* __LINUX_MFD_S2MPS11_H */
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue