Commit 578f1ef9 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'mfd-3.7-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6

Pull MFD changes from Samuel Ortiz:
 "MFD bits for the 3.7 merge window.

  As usual we have a few new drivers:

   - TI LP8788
   - TI OMAP USB TLL
   - Maxim MAX8907
   - SMSC ECE1099
   - Dialog Semiconductor DA9055
   - A simpler syscon driver that allow us to get rid of the anatop one.

  Drivers are also gradually getting Device Tree and IRQ domain support.

  The following drivers got DT support:
   - palmas, 88pm860x, tc3589x and twl4030-audio

  And those ones now use the IRQ domain APIs:
   - 88pm860x, tc3589x, db8500_prcmu

  Also some other interesting changes:
   - Intel's ICH LPC now supports Lynx Point
   - TI's twl4030-audio added a GPO child
   - tps6527 enabled its backlight subdevice
   - The twl6030 pwm driver moved to the new PWM subsystem

  And finally a bunch of cleanup and casual fixes for mc13xxx, 88pm860x,
  palmas, ab8500, wm8994, wm5110, max8907 and the tps65xxx family."

Fix up various annoying conflicts: the DT and IRQ domain support came in
twice and was already in 3.6. And then it was apparently rebased.

Guys, DON'T REBASE!

* tag 'mfd-3.7-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6: (89 commits)
  ARM: dts: Enable 88pm860x pmic
  mfd: 88pm860x: Move gpadc init into touch
  mfd: 88pm860x: Device tree support
  mfd: 88pm860x: Use irqdomain
  mfd: smsc: Add support for smsc gpio io/keypad driver
  backlight: tps65217_bl: Add missing platform_set_drvdata in tps65217_bl_probe
  mfd: DA9055 core driver
  mfd: tps65910: Add alarm interrupt of TPS65910 RTC to mfd device list
  mfd: wm5110: Add register patches for revision B
  mfd: wm5110: Disable control interface error report for WM5110 rev B
  mfd: max8907: Remove regulator-compatible from DT docs
  backlight: Add TPS65217 WLED driver
  mfd: Add backlight as subdevice to the tps65217
  mfd: Provide the PRCMU with its own IRQ domain
  mfd: Fix max8907 sparse warning
  mfd: Add lp8788 mfd driver
  mfd: dbx500: Provide a more accurate smp_twd clock
  mfd: rc5t583: Fix warning messages
  regulator: palmas: Add DT support
  mfd: palmas: Change regulator defns to better suite DT
  ...
parents ecefbd94 74d83781
* Marvell 88PM860x Power Management IC
Required parent device properties:
- compatible : "marvell,88pm860x"
- reg : the I2C slave address for the 88pm860x chip
- interrupts : IRQ line for the 88pm860x chip
- interrupt-controller: describes the 88pm860x as an interrupt controller (has its own domain)
- #interrupt-cells : should be 1.
- The cell is the 88pm860x local IRQ number
Optional parent device properties:
- marvell,88pm860x-irq-read-clr: inicates whether interrupt status is cleared by read
- marvell,88pm860x-slave-addr: 88pm860x are two chips solution. <reg> stores the I2C address
of one chip, and this property stores the I2C address of
another chip.
88pm860x consists of a large and varied group of sub-devices:
Device Supply Names Description
------ ------------ -----------
88pm860x-onkey : : On key
88pm860x-rtc : : RTC
88pm8607 : : Regulators
88pm860x-backlight : : Backlight
88pm860x-led : : Led
88pm860x-touch : : Touchscreen
Example:
pmic: 88pm860x@34 {
compatible = "marvell,88pm860x";
reg = <0x34>;
interrupts = <4>;
interrupt-parent = <&intc>;
interrupt-controller;
#interrupt-cells = <1>;
marvell,88pm860x-irq-read-clr;
marvell,88pm860x-slave-addr = <0x11>;
regulators {
BUCK1 {
regulator-min-microvolt = <1000000>;
regulator-max-microvolt = <1500000>;
regulator-boot-on;
regulator-always-on;
};
LDO1 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <2800000>;
regulator-boot-on;
regulator-always-on;
};
};
rtc {
marvell,88pm860x-vrtc = <1>;
};
touch {
marvell,88pm860x-gpadc-prebias = <1>;
marvell,88pm860x-gpadc-slot-cycle = <1>;
marvell,88pm860x-tsi-prebias = <6>;
marvell,88pm860x-pen-prebias = <16>;
marvell,88pm860x-pen-prechg = <2>;
marvell,88pm860x-resistor-X = <300>;
};
backlights {
backlight-0 {
marvell,88pm860x-iset = <4>;
marvell,88pm860x-pwm = <3>;
};
backlight-2 {
};
};
leds {
led0-red {
marvell,88pm860x-iset = <12>;
};
led0-green {
marvell,88pm860x-iset = <12>;
};
led0-blue {
marvell,88pm860x-iset = <12>;
};
};
};
* System Controller Registers R/W driver
System controller node represents a register region containing a set
of miscellaneous registers. The registers are not cohesive enough to
represent as any specific type of device. The typical use-case is for
some other node's driver, or platform-specific code, to acquire a
reference to the syscon node (e.g. by phandle, node path, or search
using a specific compatible value), interrogate the node (or associated
OS driver) to determine the location of the registers, and access the
registers directly.
Required properties:
- compatible: Should contain "syscon".
- reg: the register region can be accessed from syscon
Examples:
gpr: iomuxc-gpr@020e0000 {
compatible = "fsl,imx6q-iomuxc-gpr", "syscon";
reg = <0x020e0000 0x38>;
};
......@@ -59,6 +59,8 @@ Optional properties:
in TPS6591X datasheet)
- ti,en-gpio-sleep: enable sleep control for gpios
There should be 9 entries here, one for each gpio.
- ti,system-power-controller: Telling whether or not this pmic is controlling
the system power.
Regulator Optional properties:
- ti,regulator-ext-sleep-control: enable external sleep
......@@ -79,6 +81,8 @@ Example:
#interrupt-cells = <2>;
interrupt-controller;
ti,system-power-controller;
ti,vmbch-threshold = 0;
ti,vmbch2-threshold = 0;
ti,en-ck32k-xtal;
......
Texas Instruments TWL family (twl4030) audio module
The audio module inside the TWL family consist of an audio codec and a vibra
driver.
Required properties:
- compatible : must be "ti,twl4030-audio"
Optional properties, nodes:
Audio functionality:
- codec { }: Need to be present if the audio functionality is used. Within this
section the following options can be used:
- ti,digimic_delay: Delay need after enabling the digimic to reduce artifacts
from the start of the recorded sample (in ms)
-ti,ramp_delay_value: HS ramp delay configuration to reduce pop noise
-ti,hs_extmute: Use external mute for HS pop reduction
-ti,hs_extmute_gpio: Use external GPIO to control the external mute
-ti,offset_cncl_path: Offset cancellation path selection, refer to TRM for the
valid values.
Vibra functionality
- ti,enable-vibra: Need to be set to <1> if the vibra functionality is used. if
missing or it is 0, the vibra functionality is disabled.
Example:
&i2c1 {
clock-frequency = <2600000>;
twl: twl@48 {
reg = <0x48>;
interrupts = <7>; /* SYS_NIRQ cascaded to intc */
interrupt-parent = <&intc>;
twl_audio: audio {
compatible = "ti,twl4030-audio";
ti,enable-vibra = <1>;
codec {
ti,ramp_delay_value = <3>;
};
};
};
};
Texas Instruments TWL6040 family
The TWL6040s are 8-channel high quality low-power audio codecs providing audio
and vibra functionality on OMAP4+ platforms.
The TWL6040s are 8-channel high quality low-power audio codecs providing audio,
vibra and GPO functionality on OMAP4+ platforms.
They are connected ot the host processor via i2c for commands, McPDM for audio
data and commands.
......@@ -10,6 +10,8 @@ Required properties:
- reg: must be 0x4b for i2c address
- interrupts: twl6040 has one interrupt line connecteded to the main SoC
- interrupt-parent: The parent interrupt controller
- gpio-controller:
- #gpio-cells = <1>: twl6040 provides GPO lines.
- twl6040,audpwron-gpio: Power on GPIO line for the twl6040
- vio-supply: Regulator for the twl6040 VIO supply
......@@ -37,7 +39,6 @@ Example:
&i2c1 {
twl6040: twl@4b {
compatible = "ti,twl6040";
reg = <0x4b>;
interrupts = <0 119 4>;
interrupt-parent = <&gic>;
......@@ -60,3 +61,5 @@ Example:
};
};
};
/include/ "twl6040.dtsi"
Marvell 88PM860x regulator
Required properties:
- compatible: "marvell,88pm860x"
- reg: I2C slave address
- regulators: A node that houses a sub-node for each regulator within the
device. Each sub-node is identified using the regulator-compatible
property, with valid values listed below.
Example:
pmic: 88pm860x@34 {
compatible = "marvell,88pm860x";
reg = <0x34>;
regulators {
BUCK1 {
regulator-min-microvolt = <1000000>;
regulator-max-microvolt = <1500000>;
regulator-boot-on;
regulator-always-on;
};
BUCK3 {
regulator-min-microvolt = <1000000>;
regulator-max-microvolt = <3000000>;
regulator-boot-on;
regulator-always-on;
};
};
};
MAX8907 regulator
Required properties:
- compatible: "maxim,max8907"
- reg: I2C slave address
- interrupts: The interrupt output of the controller
- mbatt-supply: The input supply for MBATT, BBAT, SDBY, VRTC.
- in-v1-supply: The input supply for SD1.
- in-v2-supply: The input supply for SD2.
- in-v3-supply: The input supply for SD3.
- in1-supply: The input supply for LDO1.
...
- in20-supply: The input supply for LDO20.
- regulators: A node that houses a sub-node for each regulator within the
device. Each sub-node is identified using the node's name (or the deprecated
regulator-compatible property if present), with valid values listed below.
The content of each sub-node is defined by the standard binding for
regulators; see regulator.txt.
Optional properties:
- maxim,system-power-controller: Boolean property indicating that the PMIC
controls the overall system power.
The valid names for regulators are:
sd1, sd2, sd3, ldo1, ldo2, ldo3, ldo4, ldo5, ldo6, ldo7, ldo8, ldo9, ldo10,
ldo11, ldo12, ldo13, ldo14, ldo15, ldo16, ldo17, ldo18, ldo19, ldo20, out5v,
out33v, bbat, sdby, vrtc.
Example:
max8907@3c {
compatible = "maxim,max8907";
reg = <0x3c>;
interrupts = <0 86 0x4>;
maxim,system-power-controller;
mbatt-supply = <&some_reg>;
in-v1-supply = <&mbatt_reg>;
...
in1-supply = <&mbatt_reg>;
...
regulators {
mbatt_reg: mbatt {
regulator-name = "vbat_pmu";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
regulator-always-on;
};
sd1 {
regulator-name = "nvvdd_sv1,vdd_cpu_pmu";
regulator-min-microvolt = <1000000>;
regulator-max-microvolt = <1000000>;
regulator-always-on;
};
sd2 {
regulator-name = "nvvdd_sv2,vdd_core";
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1200000>;
regulator-always-on;
};
...
};
};
};
......@@ -22,6 +22,10 @@ Required properties:
- vinldo678-supply: The input supply for the LDO6, LDO7 and LDO8
- vinldo9-supply: The input supply for the LDO9
Optional properties:
- ti,system-power-controller: Telling whether or not this pmic is controlling
the system power.
Each regulator is defined using the standard binding for regulators.
Note: LDO5 and LDO_RTC is supplied by SYS regulator internally and driver
......@@ -37,6 +41,8 @@ Example:
#gpio-cells = <2>;
gpio-controller;
ti,system-power-controller;
sys-supply = <&some_reg>;
vin-sm0-supply = <&some_reg>;
vin-sm1-supply = <&some_reg>;
......
88pm860x-backlight bindings
Optional properties:
- marvell,88pm860x-iset: Current supplies on backlight device.
- marvell,88pm860x-pwm: PWM frequency on backlight device.
Example:
backlights {
backlight-0 {
marvell,88pm860x-iset = <4>;
marvell,88pm860x-pwm = <3>;
};
backlight-2 {
};
What is smsc-ece1099?
----------------------
The ECE1099 is a 40-Pin 3.3V Keyboard Scan Expansion
or GPIO Expansion device. The device supports a keyboard
scan matrix of 23x8. The device is connected to a Master
via the SMSC BC-Link interface or via the SMBus.
Keypad scan Input(KSI) and Keypad Scan Output(KSO) signals
are multiplexed with GPIOs.
Interrupt generation
--------------------
Interrupts can be generated by an edge detection on a GPIO
pin or an edge detection on one of the bus interface pins.
Interrupts can also be detected on the keyboard scan interface.
The bus interrupt pin (BC_INT# or SMBUS_INT#) is asserted if
any bit in one of the Interrupt Status registers is 1 and
the corresponding Interrupt Mask bit is also 1.
In order for software to determine which device is the source
of an interrupt, it should first read the Group Interrupt Status Register
to determine which Status register group is a source for the interrupt.
Software should read both the Status register and the associated Mask register,
then AND the two values together. Bits that are 1 in the result of the AND
are active interrupts. Software clears an interrupt by writing a 1 to the
corresponding bit in the Status register.
Communication Protocol
----------------------
- SMbus slave Interface
The host processor communicates with the ECE1099 device
through a series of read/write registers via the SMBus
interface. SMBus is a serial communication protocol between
a computer host and its peripheral devices. The SMBus data
rate is 10KHz minimum to 400 KHz maximum
- Slave Bus Interface
The ECE1099 device SMBus implementation is a subset of the
SMBus interface to the host. The device is a slave-only SMBus device.
The implementation in the device is a subset of SMBus since it
only supports four protocols.
The Write Byte, Read Byte, Send Byte, and Receive Byte protocols are the
only valid SMBus protocols for the device.
- BC-LinkTM Interface
The BC-Link is a proprietary bus that allows communication
between a Master device and a Companion device. The Master
device uses this serial bus to read and write registers
located on the Companion device. The bus comprises three signals,
BC_CLK, BC_DAT and BC_INT#. The Master device always provides the
clock, BC_CLK, and the Companion device is the source for an
independent asynchronous interrupt signal, BC_INT#. The ECE1099
supports BC-Link speeds up to 24MHz.
......@@ -400,8 +400,8 @@ clks: ccm@020c4000 {
#clock-cells = <1>;
};
anatop@020c8000 {
compatible = "fsl,imx6q-anatop";
anatop: anatop@020c8000 {
compatible = "fsl,imx6q-anatop", "syscon", "simple-bus";
reg = <0x020c8000 0x1000>;
interrupts = <0 49 0x04 0 54 0x04 0 127 0x04>;
......@@ -531,6 +531,11 @@ gpc@020dc000 {
interrupts = <0 89 0x04 0 90 0x04>;
};
gpr: iomuxc-gpr@020e0000 {
compatible = "fsl,imx6q-iomuxc-gpr", "syscon";
reg = <0x020e0000 0x38>;
};
iomuxc@020e0000 {
compatible = "fsl,imx6q-iomuxc";
reg = <0x020e0000 0x4000>;
......
......@@ -29,6 +29,143 @@ uart1: uart@d4017000 {
};
twsi1: i2c@d4011000 {
status = "okay";
pmic: 88pm860x@34 {
compatible = "marvell,88pm860x";
reg = <0x34>;
interrupts = <4>;
interrupt-parent = <&intc>;
interrupt-controller;
#interrupt-cells = <1>;
marvell,88pm860x-irq-read-clr;
marvell,88pm860x-slave-addr = <0x11>;
regulators {
BUCK1 {
regulator-min-microvolt = <1000000>;
regulator-max-microvolt = <1500000>;
regulator-boot-on;
regulator-always-on;
};
BUCK2 {
regulator-min-microvolt = <1000000>;
regulator-max-microvolt = <1500000>;
regulator-boot-on;
regulator-always-on;
};
BUCK3 {
regulator-min-microvolt = <1000000>;
regulator-max-microvolt = <3000000>;
regulator-boot-on;
regulator-always-on;
};
LDO1 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <2800000>;
regulator-boot-on;
regulator-always-on;
};
LDO2 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
LDO3 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
LDO4 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
LDO5 {
regulator-min-microvolt = <2900000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
LDO6 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
LDO7 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <2900000>;
regulator-boot-on;
regulator-always-on;
};
LDO8 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <2900000>;
regulator-boot-on;
regulator-always-on;
};
LDO9 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
LDO10 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
LDO12 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
LDO13 {
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
LDO14 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
};
rtc {
marvell,88pm860x-vrtc = <1>;
};
touch {
marvell,88pm860x-gpadc-prebias = <1>;
marvell,88pm860x-gpadc-slot-cycle = <1>;
marvell,88pm860x-tsi-prebias = <6>;
marvell,88pm860x-pen-prebias = <16>;
marvell,88pm860x-pen-prechg = <2>;
marvell,88pm860x-resistor-X = <300>;
};
backlights {
backlight-0 {
marvell,88pm860x-iset = <4>;
marvell,88pm860x-pwm = <3>;
};
backlight-2 {
};
};
leds {
led0-red {
marvell,88pm860x-iset = <12>;
};
led0-green {
marvell,88pm860x-iset = <12>;
};
led0-blue {
marvell,88pm860x-iset = <12>;
};
};
};
};
rtc: rtc@d4010000 {
status = "okay";
......
......@@ -120,6 +120,8 @@ gcb3: gpio@d4019100 {
twsi1: i2c@d4011000 {
compatible = "mrvl,mmp-twsi";
#address-cells = <1>;
#size-cells = <0>;
reg = <0xd4011000 0x1000>;
interrupts = <7>;
mrvl,i2c-fast-mode;
......@@ -128,6 +130,8 @@ twsi1: i2c@d4011000 {
twsi2: i2c@d4037000 {
compatible = "mrvl,mmp-twsi";
#address-cells = <1>;
#size-cells = <0>;
reg = <0xd4037000 0x1000>;
interrupts = <54>;
status = "disabled";
......
......@@ -758,7 +758,7 @@ config SOC_IMX6Q
select HAVE_IMX_MMDC
select HAVE_IMX_SRC
select HAVE_SMP
select MFD_ANATOP
select MFD_SYSCON
select PINCTRL
select PINCTRL_IMX6Q
......
......@@ -23,8 +23,9 @@
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/phy.h>
#include <linux/regmap.h>
#include <linux/micrel_phy.h>
#include <linux/mfd/anatop.h>
#include <linux/mfd/syscon.h>
#include <asm/cpuidle.h>
#include <asm/smp_twd.h>
#include <asm/hardware/cache-l2x0.h>
......@@ -118,20 +119,7 @@ static void __init imx6q_sabrelite_init(void)
static void __init imx6q_usb_init(void)
{
struct device_node *np;
struct platform_device *pdev = NULL;
struct anatop *adata = NULL;
np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop");
if (np)
pdev = of_find_device_by_node(np);
if (pdev)
adata = platform_get_drvdata(pdev);
if (!adata) {
if (np)
of_node_put(np);
return;
}
struct regmap *anatop;
#define HW_ANADIG_USB1_CHRG_DETECT 0x000001b0
#define HW_ANADIG_USB2_CHRG_DETECT 0x00000210
......@@ -139,20 +127,21 @@ static void __init imx6q_usb_init(void)
#define BM_ANADIG_USB_CHRG_DETECT_EN_B 0x00100000
#define BM_ANADIG_USB_CHRG_DETECT_CHK_CHRG_B 0x00080000
/*
* The external charger detector needs to be disabled,
* or the signal at DP will be poor
*/
anatop_write_reg(adata, HW_ANADIG_USB1_CHRG_DETECT,
BM_ANADIG_USB_CHRG_DETECT_EN_B
| BM_ANADIG_USB_CHRG_DETECT_CHK_CHRG_B,
~0);
anatop_write_reg(adata, HW_ANADIG_USB2_CHRG_DETECT,
BM_ANADIG_USB_CHRG_DETECT_EN_B |
BM_ANADIG_USB_CHRG_DETECT_CHK_CHRG_B,
~0);
of_node_put(np);
anatop = syscon_regmap_lookup_by_compatible("fsl,imx6q-anatop");
if (!IS_ERR(anatop)) {
/*
* The external charger detector needs to be disabled,
* or the signal at DP will be poor
*/
regmap_write(anatop, HW_ANADIG_USB1_CHRG_DETECT,
BM_ANADIG_USB_CHRG_DETECT_EN_B
| BM_ANADIG_USB_CHRG_DETECT_CHK_CHRG_B);
regmap_write(anatop, HW_ANADIG_USB2_CHRG_DETECT,
BM_ANADIG_USB_CHRG_DETECT_EN_B |
BM_ANADIG_USB_CHRG_DETECT_CHK_CHRG_B);
} else {
pr_warn("failed to find fsl,imx6q-anatop regmap\n");
}
}
static void __init imx6q_init_machine(void)
......
......@@ -33,10 +33,12 @@
#ifdef CONFIG_MFD_OMAP_USB_HOST
#define OMAP_USBHS_DEVICE "usbhs_omap"
#define OMAP_USBTLL_DEVICE "usbhs_tll"
#define USBHS_UHH_HWMODNAME "usb_host_hs"
#define USBHS_TLL_HWMODNAME "usb_tll_hs"
static struct usbhs_omap_platform_data usbhs_data;
static struct usbtll_omap_platform_data usbtll_data;
static struct ehci_hcd_omap_platform_data ehci_data;
static struct ohci_hcd_omap_platform_data ohci_data;
......@@ -485,13 +487,14 @@ void __init setup_4430ohci_io_mux(const enum usbhs_omap_port_mode *port_mode)
void __init usbhs_init(const struct usbhs_omap_board_data *pdata)
{
struct omap_hwmod *oh[2];
struct omap_hwmod *uhh_hwm, *tll_hwm;
struct platform_device *pdev;
int bus_id = -1;
int i;
for (i = 0; i < OMAP3_HS_USB_PORTS; i++) {
usbhs_data.port_mode[i] = pdata->port_mode[i];
usbtll_data.port_mode[i] = pdata->port_mode[i];
ohci_data.port_mode[i] = pdata->port_mode[i];
ehci_data.port_mode[i] = pdata->port_mode[i];
ehci_data.reset_gpio_port[i] = pdata->reset_gpio_port[i];
......@@ -510,25 +513,35 @@ void __init usbhs_init(const struct usbhs_omap_board_data *pdata)
setup_4430ohci_io_mux(pdata->port_mode);
}
oh[0] = omap_hwmod_lookup(USBHS_UHH_HWMODNAME);
if (!oh[0]) {
uhh_hwm = omap_hwmod_lookup(USBHS_UHH_HWMODNAME);
if (!uhh_hwm) {
pr_err("Could not look up %s\n", USBHS_UHH_HWMODNAME);
return;
}
oh[1] = omap_hwmod_lookup(USBHS_TLL_HWMODNAME);
if (!oh[1]) {
tll_hwm = omap_hwmod_lookup(USBHS_TLL_HWMODNAME);
if (!tll_hwm) {
pr_err("Could not look up %s\n", USBHS_TLL_HWMODNAME);
return;
}
pdev = omap_device_build_ss(OMAP_USBHS_DEVICE, bus_id, oh, 2,
(void *)&usbhs_data, sizeof(usbhs_data),
pdev = omap_device_build(OMAP_USBTLL_DEVICE, bus_id, tll_hwm,
&usbtll_data, sizeof(usbtll_data),
omap_uhhtll_latency,
ARRAY_SIZE(omap_uhhtll_latency), false);
if (IS_ERR(pdev)) {
pr_err("Could not build hwmod devices %s,%s\n",
USBHS_UHH_HWMODNAME, USBHS_TLL_HWMODNAME);
pr_err("Could not build hwmod device %s\n",
USBHS_TLL_HWMODNAME);
return;
}
pdev = omap_device_build(OMAP_USBHS_DEVICE, bus_id, uhh_hwm,
&usbhs_data, sizeof(usbhs_data),
omap_uhhtll_latency,
ARRAY_SIZE(omap_uhhtll_latency), false);
if (IS_ERR(pdev)) {
pr_err("Could not build hwmod devices %s\n",
USBHS_UHH_HWMODNAME);
return;
}
}
......
......@@ -9,7 +9,7 @@
*/
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/ab3100.h>
#include <linux/regulator/machine.h>
#include <linux/amba/bus.h>
#include <mach/irqs.h>
......
......@@ -4,6 +4,7 @@
#define __ASM_ARCH_OMAP_USB_H
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/usb/musb.h>
#define OMAP3_HS_USB_PORTS 3
......@@ -63,6 +64,10 @@ struct usbhs_omap_platform_data {
struct ehci_hcd_omap_platform_data *ehci_data;
struct ohci_hcd_omap_platform_data *ohci_data;
};
struct usbtll_omap_platform_data {
enum usbhs_omap_port_mode port_mode[OMAP3_HS_USB_PORTS];
};
/*-------------------------------------------------------------------------*/
struct omap_musb_board_data {
......@@ -81,6 +86,8 @@ enum musb_interface {MUSB_INTERFACE_ULPI, MUSB_INTERFACE_UTMI};
extern void usb_musb_init(struct omap_musb_board_data *board_data);
extern void usbhs_init(const struct usbhs_omap_board_data *pdata);
extern int omap_tll_enable(void);
extern int omap_tll_disable(void);
extern int omap4430_phy_power(struct device *dev, int ID, int on);
extern int omap4430_phy_set_clk(struct device *dev, int on);
......
......@@ -409,6 +409,13 @@ config GPIO_TWL4030
Say yes here to access the GPIO signals of various multi-function
power management chips from Texas Instruments.
config GPIO_TWL6040
tristate "TWL6040 GPO"
depends on TWL6040_CORE
help
Say yes here to access the GPO signals of twl6040
audio chip from Texas Instruments.
config GPIO_WM831X
tristate "WM831x GPIOs"
depends on MFD_WM831X
......
......@@ -68,6 +68,7 @@ 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
obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o
obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o
obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o
obj-$(CONFIG_GPIO_VT8500) += gpio-vt8500.o
......
......@@ -49,6 +49,10 @@ static const u8 ichx_regs[3][3] = {
{0x0c, 0x38, 0x48}, /* LVL[1-3] offsets */
};
static const u8 ichx_reglen[3] = {
0x30, 0x10, 0x10,
};
#define ICHX_WRITE(val, reg, base_res) outl(val, (reg) + (base_res)->start)
#define ICHX_READ(reg, base_res) inl((reg) + (base_res)->start)
......@@ -75,6 +79,7 @@ static struct {
struct resource *pm_base; /* Power Mangagment IO base */
struct ichx_desc *desc; /* Pointer to chipset-specific description */
u32 orig_gpio_ctrl; /* Orig CTRL value, used to restore on exit */
u8 use_gpio; /* Which GPIO groups are usable */
} ichx_priv;
static int modparam_gpiobase = -1; /* dynamic */
......@@ -123,8 +128,16 @@ static int ichx_read_bit(int reg, unsigned nr)
return data & (1 << bit) ? 1 : 0;
}
static int ichx_gpio_check_available(struct gpio_chip *gpio, unsigned nr)
{
return (ichx_priv.use_gpio & (1 << (nr / 32))) ? 0 : -ENXIO;
}
static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
{
if (!ichx_gpio_check_available(gpio, nr))
return -ENXIO;
/*
* Try setting pin as an input and verify it worked since many pins
* are output-only.
......@@ -138,6 +151,9 @@ static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
int val)
{
if (!ichx_gpio_check_available(gpio, nr))
return -ENXIO;
/* Set GPIO output value. */
ichx_write_bit(GPIO_LVL, nr, val, 0);
......@@ -153,6 +169,9 @@ static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
static int ichx_gpio_get(struct gpio_chip *chip, unsigned nr)
{
if (!ichx_gpio_check_available(chip, nr))
return -ENXIO;
return ichx_read_bit(GPIO_LVL, nr);
}
......@@ -161,6 +180,9 @@ static int ich6_gpio_get(struct gpio_chip *chip, unsigned nr)
unsigned long flags;
u32 data;
if (!ichx_gpio_check_available(chip, nr))
return -ENXIO;
/*
* GPI 0 - 15 need to be read from the power management registers on
* a ICH6/3100 bridge.
......@@ -291,6 +313,46 @@ static struct ichx_desc intel5_desc = {
.ngpio = 76,
};
static int __devinit ichx_gpio_request_regions(struct resource *res_base,
const char *name, u8 use_gpio)
{
int i;
if (!res_base || !res_base->start || !res_base->end)
return -ENODEV;
for (i = 0; i < ARRAY_SIZE(ichx_regs[0]); i++) {
if (!(use_gpio & (1 << i)))
continue;
if (!request_region(res_base->start + ichx_regs[0][i],
ichx_reglen[i], name))
goto request_err;
}
return 0;
request_err:
/* Clean up: release already requested regions, if any */
for (i--; i >= 0; i--) {
if (!(use_gpio & (1 << i)))
continue;
release_region(res_base->start + ichx_regs[0][i],
ichx_reglen[i]);
}
return -EBUSY;
}
static void ichx_gpio_release_regions(struct resource *res_base, u8 use_gpio)
{
int i;
for (i = 0; i < ARRAY_SIZE(ichx_regs[0]); i++) {
if (!(use_gpio & (1 << i)))
continue;
release_region(res_base->start + ichx_regs[0][i],
ichx_reglen[i]);
}
}
static int __devinit ichx_gpio_probe(struct platform_device *pdev)
{
struct resource *res_base, *res_pm;
......@@ -329,12 +391,11 @@ static int __devinit ichx_gpio_probe(struct platform_device *pdev)
}
res_base = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPIO);
if (!res_base || !res_base->start || !res_base->end)
return -ENODEV;
if (!request_region(res_base->start, resource_size(res_base),
pdev->name))
return -EBUSY;
ichx_priv.use_gpio = ich_info->use_gpio;
err = ichx_gpio_request_regions(res_base, pdev->name,
ichx_priv.use_gpio);
if (err)
return err;
ichx_priv.gpio_base = res_base;
......@@ -374,8 +435,7 @@ static int __devinit ichx_gpio_probe(struct platform_device *pdev)
return 0;
add_err:
release_region(ichx_priv.gpio_base->start,
resource_size(ichx_priv.gpio_base));
ichx_gpio_release_regions(ichx_priv.gpio_base, ichx_priv.use_gpio);
if (ichx_priv.pm_base)
release_region(ichx_priv.pm_base->start,
resource_size(ichx_priv.pm_base));
......@@ -393,8 +453,7 @@ static int __devexit ichx_gpio_remove(struct platform_device *pdev)
return err;
}
release_region(ichx_priv.gpio_base->start,
resource_size(ichx_priv.gpio_base));
ichx_gpio_release_regions(ichx_priv.gpio_base, ichx_priv.use_gpio);
if (ichx_priv.pm_base)
release_region(ichx_priv.pm_base->start,
resource_size(ichx_priv.pm_base));
......
/*
* Access to GPOs on TWL6040 chip
*
* Copyright (C) 2012 Texas Instruments, Inc.
*
* Authors:
* Sergio Aguirre <saaguirre@ti.com>
* Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kthread.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/mfd/twl6040.h>
static struct gpio_chip twl6040gpo_chip;
static int twl6040gpo_get(struct gpio_chip *chip, unsigned offset)
{
struct twl6040 *twl6040 = dev_get_drvdata(chip->dev->parent);
int ret = 0;
ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL);
if (ret < 0)
return ret;
return (ret >> offset) & 1;
}
static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned offset,
int value)
{
/* This only drives GPOs, and can't change direction */
return 0;
}
static void twl6040gpo_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct twl6040 *twl6040 = dev_get_drvdata(chip->dev->parent);
int ret;
u8 gpoctl;
ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL);
if (ret < 0)
return;
if (value)
gpoctl = ret | (1 << offset);
else
gpoctl = ret & ~(1 << offset);
twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl);
}
static struct gpio_chip twl6040gpo_chip = {
.label = "twl6040",
.owner = THIS_MODULE,
.get = twl6040gpo_get,
.direction_output = twl6040gpo_direction_out,
.set = twl6040gpo_set,
.can_sleep = 1,
};
/*----------------------------------------------------------------------*/
static int __devinit gpo_twl6040_probe(struct platform_device *pdev)
{
struct twl6040_gpo_data *pdata = pdev->dev.platform_data;
struct device *twl6040_core_dev = pdev->dev.parent;
struct twl6040 *twl6040 = dev_get_drvdata(twl6040_core_dev);
int ret;
if (pdata)
twl6040gpo_chip.base = pdata->gpio_base;
else
twl6040gpo_chip.base = -1;
if (twl6040_get_revid(twl6040) < TWL6041_REV_ES2_0)
twl6040gpo_chip.ngpio = 3; /* twl6040 have 3 GPO */
else
twl6040gpo_chip.ngpio = 1; /* twl6041 have 1 GPO */
twl6040gpo_chip.dev = &pdev->dev;
#ifdef CONFIG_OF_GPIO
twl6040gpo_chip.of_node = twl6040_core_dev->of_node;
#endif
ret = gpiochip_add(&twl6040gpo_chip);
if (ret < 0) {
dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret);
twl6040gpo_chip.ngpio = 0;
}
return ret;
}
static int __devexit gpo_twl6040_remove(struct platform_device *pdev)
{
return gpiochip_remove(&twl6040gpo_chip);
}
/* Note: this hardware lives inside an I2C-based multi-function device. */
MODULE_ALIAS("platform:twl6040-gpo");
static struct platform_driver gpo_twl6040_driver = {
.driver = {
.name = "twl6040-gpo",
.owner = THIS_MODULE,
},
.probe = gpo_twl6040_probe,
.remove = gpo_twl6040_remove,
};
module_platform_driver(gpo_twl6040_driver);
MODULE_AUTHOR("Texas Instruments, Inc.");
MODULE_DESCRIPTION("GPO interface for TWL6040");
MODULE_LICENSE("GPL");
......@@ -26,6 +26,7 @@
#include <linux/module.h>
#include <linux/jiffies.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/workqueue.h>
#include <linux/i2c/twl.h>
#include <linux/mfd/twl4030-audio.h>
......@@ -194,13 +195,26 @@ static int twl4030_vibra_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops,
twl4030_vibra_suspend, twl4030_vibra_resume);
static bool twl4030_vibra_check_coexist(struct twl4030_vibra_data *pdata,
struct device_node *node)
{
if (pdata && pdata->coexist)
return true;
if (of_find_node_by_name(node, "codec"))
return true;
return false;
}
static int __devinit twl4030_vibra_probe(struct platform_device *pdev)
{
struct twl4030_vibra_data *pdata = pdev->dev.platform_data;
struct device_node *twl4030_core_node = pdev->dev.parent->of_node;
struct vibra_info *info;
int ret;
if (!pdata) {
if (!pdata && !twl4030_core_node) {
dev_dbg(&pdev->dev, "platform_data not available\n");
return -EINVAL;
}
......@@ -210,7 +224,7 @@ static int __devinit twl4030_vibra_probe(struct platform_device *pdev)
return -ENOMEM;
info->dev = &pdev->dev;
info->coexist = pdata->coexist;
info->coexist = twl4030_vibra_check_coexist(pdata, twl4030_core_node);
INIT_WORK(&info->play_work, vibra_play_work);
info->input_dev = input_allocate_device();
......
......@@ -10,6 +10,7 @@
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/input.h>
......@@ -113,14 +114,69 @@ static void pm860x_touch_close(struct input_dev *dev)
pm860x_set_bits(touch->i2c, MEAS_EN3, data, 0);
}
#ifdef CONFIG_OF
static int __devinit pm860x_touch_dt_init(struct platform_device *pdev,
struct pm860x_chip *chip,
int *res_x)
{
struct device_node *np = pdev->dev.parent->of_node;
struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
: chip->companion;
int data, n, ret;
if (!np)
return -ENODEV;
np = of_find_node_by_name(np, "touch");
if (!np) {
dev_err(&pdev->dev, "Can't find touch node\n");
return -EINVAL;
}
/* set GPADC MISC1 register */
data = 0;
if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-prebias", &n))
data |= (n << 1) & PM8607_GPADC_PREBIAS_MASK;
if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-slot-cycle", &n))
data |= (n << 3) & PM8607_GPADC_SLOT_CYCLE_MASK;
if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-off-scale", &n))
data |= (n << 5) & PM8607_GPADC_OFF_SCALE_MASK;
if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-sw-cal", &n))
data |= (n << 7) & PM8607_GPADC_SW_CAL_MASK;
if (data) {
ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
if (ret < 0)
return -EINVAL;
}
/* set tsi prebias time */
if (!of_property_read_u32(np, "marvell,88pm860x-tsi-prebias", &data)) {
ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
if (ret < 0)
return -EINVAL;
}
/* set prebias & prechg time of pen detect */
data = 0;
if (!of_property_read_u32(np, "marvell,88pm860x-pen-prebias", &n))
data |= n & PM8607_PD_PREBIAS_MASK;
if (!of_property_read_u32(np, "marvell,88pm860x-pen-prechg", &n))
data |= n & PM8607_PD_PRECHG_MASK;
if (data) {
ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
if (ret < 0)
return -EINVAL;
}
of_property_read_u32(np, "marvell,88pm860x-resistor-X", res_x);
return 0;
}
#else
#define pm860x_touch_dt_init(x, y, z) (-1)
#endif
static int __devinit pm860x_touch_probe(struct platform_device *pdev)
{
struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct pm860x_platform_data *pm860x_pdata = \
pdev->dev.parent->platform_data;
struct pm860x_touch_pdata *pdata = NULL;
struct pm860x_touch_pdata *pdata = pdev->dev.platform_data;
struct pm860x_touch *touch;
int irq, ret;
struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
: chip->companion;
int irq, ret, res_x = 0, data = 0;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
......@@ -128,16 +184,55 @@ static int __devinit pm860x_touch_probe(struct platform_device *pdev)
return -EINVAL;
}
if (!pm860x_pdata) {
dev_err(&pdev->dev, "platform data is missing\n");
return -EINVAL;
}
pdata = pm860x_pdata->touch;
if (!pdata) {
dev_err(&pdev->dev, "touchscreen data is missing\n");
return -EINVAL;
if (pm860x_touch_dt_init(pdev, chip, &res_x)) {
if (pdata) {
/* set GPADC MISC1 register */
data = 0;
data |= (pdata->gpadc_prebias << 1)
& PM8607_GPADC_PREBIAS_MASK;
data |= (pdata->slot_cycle << 3)
& PM8607_GPADC_SLOT_CYCLE_MASK;
data |= (pdata->off_scale << 5)
& PM8607_GPADC_OFF_SCALE_MASK;
data |= (pdata->sw_cal << 7)
& PM8607_GPADC_SW_CAL_MASK;
if (data) {
ret = pm860x_reg_write(i2c,
PM8607_GPADC_MISC1, data);
if (ret < 0)
return -EINVAL;
}
/* set tsi prebias time */
if (pdata->tsi_prebias) {
data = pdata->tsi_prebias;
ret = pm860x_reg_write(i2c,
PM8607_TSI_PREBIAS, data);
if (ret < 0)
return -EINVAL;
}
/* set prebias & prechg time of pen detect */
data = 0;
data |= pdata->pen_prebias
& PM8607_PD_PREBIAS_MASK;
data |= (pdata->pen_prechg << 5)
& PM8607_PD_PRECHG_MASK;
if (data) {
ret = pm860x_reg_write(i2c,
PM8607_PD_PREBIAS, data);
if (ret < 0)
return -EINVAL;
}
res_x = pdata->res_x;
} else {
dev_err(&pdev->dev, "failed to get platform data\n");
return -EINVAL;
}
}
/* enable GPADC */
ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1, PM8607_GPADC_EN,
PM8607_GPADC_EN);
if (ret)
return ret;
touch = kzalloc(sizeof(struct pm860x_touch), GFP_KERNEL);
if (touch == NULL)
......@@ -158,9 +253,9 @@ static int __devinit pm860x_touch_probe(struct platform_device *pdev)
touch->idev->open = pm860x_touch_open;
touch->idev->close = pm860x_touch_close;
touch->chip = chip;
touch->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
touch->irq = irq + chip->irq_base;
touch->res_x = pdata->res_x;
touch->i2c = i2c;
touch->irq = irq;
touch->res_x = res_x;
input_set_drvdata(touch->idev, touch);
ret = request_threaded_irq(touch->irq, NULL, pm860x_touch_handler,
......
......@@ -12,6 +12,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/leds.h>
......@@ -20,18 +21,12 @@
#include <linux/mfd/88pm860x.h>
#include <linux/module.h>
#define LED_PWM_SHIFT (3)
#define LED_PWM_MASK (0x1F)
#define LED_CURRENT_MASK (0x07 << 5)
#define LED_BLINK_ON_MASK (0x07)
#define LED_BLINK_MASK (0x7F)
#define LED_BLINK_ON(x) ((x & 0x7) * 66 + 66)
#define LED_BLINK_ON_MIN LED_BLINK_ON(0)
#define LED_BLINK_ON_MAX LED_BLINK_ON(0x7)
#define LED_ON_CONTINUOUS (0x0F << 3)
#define LED_TO_ON(x) ((x - 66) / 66)
#define LED1_BLINK_EN (1 << 1)
#define LED2_BLINK_EN (1 << 2)
......@@ -49,85 +44,25 @@ struct pm860x_led {
unsigned char brightness;
unsigned char current_brightness;
int blink_data;
int blink_time;
int blink_on;
int blink_off;
int reg_control;
int reg_blink;
int blink_mask;
};
/* return offset of color register */
static inline int __led_off(int port)
{
int ret = -EINVAL;
switch (port) {
case PM8606_LED1_RED:
case PM8606_LED1_GREEN:
case PM8606_LED1_BLUE:
ret = port - PM8606_LED1_RED + PM8606_RGB1B;
break;
case PM8606_LED2_RED:
case PM8606_LED2_GREEN:
case PM8606_LED2_BLUE:
ret = port - PM8606_LED2_RED + PM8606_RGB2B;
break;
}
return ret;
}
/* return offset of blink register */
static inline int __blink_off(int port)
{
int ret = -EINVAL;
switch (port) {
case PM8606_LED1_RED:
case PM8606_LED1_GREEN:
case PM8606_LED1_BLUE:
ret = PM8606_RGB1A;
break;
case PM8606_LED2_RED:
case PM8606_LED2_GREEN:
case PM8606_LED2_BLUE:
ret = PM8606_RGB2A;
break;
}
return ret;
}
static inline int __blink_ctl_mask(int port)
{
int ret = -EINVAL;
switch (port) {
case PM8606_LED1_RED:
case PM8606_LED1_GREEN:
case PM8606_LED1_BLUE:
ret = LED1_BLINK_EN;
break;
case PM8606_LED2_RED:
case PM8606_LED2_GREEN:
case PM8606_LED2_BLUE:
ret = LED2_BLINK_EN;
break;
}
return ret;
}
static int led_power_set(struct pm860x_chip *chip, int port, int on)
{
int ret = -EINVAL;
switch (port) {
case PM8606_LED1_RED:
case PM8606_LED1_GREEN:
case PM8606_LED1_BLUE:
case 0:
case 1:
case 2:
ret = on ? pm8606_osc_enable(chip, RGB1_ENABLE) :
pm8606_osc_disable(chip, RGB1_ENABLE);
break;
case PM8606_LED2_RED:
case PM8606_LED2_GREEN:
case PM8606_LED2_BLUE:
case 3:
case 4:
case 5:
ret = on ? pm8606_osc_enable(chip, RGB2_ENABLE) :
pm8606_osc_disable(chip, RGB2_ENABLE);
break;
......@@ -141,7 +76,7 @@ static void pm860x_led_work(struct work_struct *work)
struct pm860x_led *led;
struct pm860x_chip *chip;
unsigned char buf[3];
int mask, ret;
int ret;
led = container_of(work, struct pm860x_led, work);
chip = led->chip;
......@@ -149,34 +84,34 @@ static void pm860x_led_work(struct work_struct *work)
if ((led->current_brightness == 0) && led->brightness) {
led_power_set(chip, led->port, 1);
if (led->iset) {
pm860x_set_bits(led->i2c, __led_off(led->port),
pm860x_set_bits(led->i2c, led->reg_control,
LED_CURRENT_MASK, led->iset);
}
pm860x_set_bits(led->i2c, __blink_off(led->port),
pm860x_set_bits(led->i2c, led->reg_blink,
LED_BLINK_MASK, LED_ON_CONTINUOUS);
mask = __blink_ctl_mask(led->port);
pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, mask);
pm860x_set_bits(led->i2c, PM8606_WLED3B, led->blink_mask,
led->blink_mask);
}
pm860x_set_bits(led->i2c, __led_off(led->port), LED_PWM_MASK,
pm860x_set_bits(led->i2c, led->reg_control, LED_PWM_MASK,
led->brightness);
if (led->brightness == 0) {
pm860x_bulk_read(led->i2c, __led_off(led->port), 3, buf);
pm860x_bulk_read(led->i2c, led->reg_control, 3, buf);
ret = buf[0] & LED_PWM_MASK;
ret |= buf[1] & LED_PWM_MASK;
ret |= buf[2] & LED_PWM_MASK;
if (ret == 0) {
/* unset current since no led is lighting */
pm860x_set_bits(led->i2c, __led_off(led->port),
pm860x_set_bits(led->i2c, led->reg_control,
LED_CURRENT_MASK, 0);
mask = __blink_ctl_mask(led->port);
pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, 0);
pm860x_set_bits(led->i2c, PM8606_WLED3B,
led->blink_mask, 0);
led_power_set(chip, led->port, 0);
}
}
led->current_brightness = led->brightness;
dev_dbg(chip->dev, "Update LED. (reg:%d, brightness:%d)\n",
__led_off(led->port), led->brightness);
led->reg_control, led->brightness);
mutex_unlock(&led->lock);
}
......@@ -189,39 +124,92 @@ static void pm860x_led_set(struct led_classdev *cdev,
schedule_work(&data->work);
}
#ifdef CONFIG_OF
static int pm860x_led_dt_init(struct platform_device *pdev,
struct pm860x_led *data)
{
struct device_node *nproot = pdev->dev.parent->of_node, *np;
int iset = 0;
if (!nproot)
return -ENODEV;
nproot = of_find_node_by_name(nproot, "leds");
if (!nproot) {
dev_err(&pdev->dev, "failed to find leds node\n");
return -ENODEV;
}
for_each_child_of_node(nproot, np) {
if (!of_node_cmp(np->name, data->name)) {
of_property_read_u32(np, "marvell,88pm860x-iset",
&iset);
data->iset = PM8606_LED_CURRENT(iset);
break;
}
}
return 0;
}
#else
#define pm860x_led_dt_init(x, y) (-1)
#endif
static int pm860x_led_probe(struct platform_device *pdev)
{
struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct pm860x_led_pdata *pdata;
struct pm860x_led_pdata *pdata = pdev->dev.platform_data;
struct pm860x_led *data;
struct resource *res;
int ret;
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (res == NULL) {
dev_err(&pdev->dev, "No I/O resource!\n");
return -EINVAL;
}
pdata = pdev->dev.platform_data;
if (pdata == NULL) {
dev_err(&pdev->dev, "No platform data!\n");
return -EINVAL;
}
int ret = 0;
data = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_led), GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
strncpy(data->name, res->name, MFD_NAME_SIZE - 1);
res = platform_get_resource_byname(pdev, IORESOURCE_REG, "control");
if (!res) {
dev_err(&pdev->dev, "No REG resource for control\n");
ret = -ENXIO;
goto out;
}
data->reg_control = res->start;
res = platform_get_resource_byname(pdev, IORESOURCE_REG, "blink");
if (!res) {
dev_err(&pdev->dev, "No REG resource for blink\n");
ret = -ENXIO;
goto out;
}
data->reg_blink = res->start;
memset(data->name, 0, MFD_NAME_SIZE);
switch (pdev->id) {
case 0:
data->blink_mask = LED1_BLINK_EN;
sprintf(data->name, "led0-red");
break;
case 1:
data->blink_mask = LED1_BLINK_EN;
sprintf(data->name, "led0-green");
break;
case 2:
data->blink_mask = LED1_BLINK_EN;
sprintf(data->name, "led0-blue");
break;
case 3:
data->blink_mask = LED2_BLINK_EN;
sprintf(data->name, "led1-red");
break;
case 4:
data->blink_mask = LED2_BLINK_EN;
sprintf(data->name, "led1-green");
break;
case 5:
data->blink_mask = LED2_BLINK_EN;
sprintf(data->name, "led1-blue");
break;
}
dev_set_drvdata(&pdev->dev, data);
data->chip = chip;
data->i2c = (chip->id == CHIP_PM8606) ? chip->client : chip->companion;
data->iset = pdata->iset;
data->port = pdata->flags;
if (data->port < 0) {
dev_err(&pdev->dev, "check device failed\n");
return -EINVAL;
}
data->port = pdev->id;
if (pm860x_led_dt_init(pdev, data))
if (pdata)
data->iset = pdata->iset;
data->current_brightness = 0;
data->cdev.name = data->name;
......@@ -236,6 +224,9 @@ static int pm860x_led_probe(struct platform_device *pdev)
}
pm860x_led_set(&data->cdev, 0);
return 0;
out:
devm_kfree(&pdev->dev, data);
return ret;
}
static int pm860x_led_remove(struct platform_device *pdev)
......
This diff is collapsed.
......@@ -10,12 +10,9 @@
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/mfd/88pm860x.h>
#include <linux/slab.h>
int pm860x_reg_read(struct i2c_client *i2c, int reg)
{
......@@ -91,8 +88,18 @@ static int read_device(struct i2c_client *i2c, int reg,
unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX + 3];
unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX + 2];
struct i2c_adapter *adap = i2c->adapter;
struct i2c_msg msg[2] = {{i2c->addr, 0, 1, msgbuf0},
{i2c->addr, I2C_M_RD, 0, msgbuf1},
struct i2c_msg msg[2] = {
{
.addr = i2c->addr,
.flags = 0,
.len = 1,
.buf = msgbuf0
},
{ .addr = i2c->addr,
.flags = I2C_M_RD,
.len = 0,
.buf = msgbuf1
},
};
int num = 1, ret = 0;
......@@ -231,160 +238,3 @@ int pm860x_page_set_bits(struct i2c_client *i2c, int reg,
return ret;
}
EXPORT_SYMBOL(pm860x_page_set_bits);
static const struct i2c_device_id pm860x_id_table[] = {
{ "88PM860x", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, pm860x_id_table);
static int verify_addr(struct i2c_client *i2c)
{
unsigned short addr_8607[] = {0x30, 0x34};
unsigned short addr_8606[] = {0x10, 0x11};
int size, i;
if (i2c == NULL)
return 0;
size = ARRAY_SIZE(addr_8606);
for (i = 0; i < size; i++) {
if (i2c->addr == *(addr_8606 + i))
return CHIP_PM8606;
}
size = ARRAY_SIZE(addr_8607);
for (i = 0; i < size; i++) {
if (i2c->addr == *(addr_8607 + i))
return CHIP_PM8607;
}
return 0;
}
static struct regmap_config pm860x_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
static int __devinit pm860x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pm860x_platform_data *pdata = client->dev.platform_data;
struct pm860x_chip *chip;
int ret;
if (!pdata) {
pr_info("No platform data in %s!\n", __func__);
return -EINVAL;
}
chip = kzalloc(sizeof(struct pm860x_chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
chip->id = verify_addr(client);
chip->regmap = regmap_init_i2c(client, &pm860x_regmap_config);
if (IS_ERR(chip->regmap)) {
ret = PTR_ERR(chip->regmap);
dev_err(&client->dev, "Failed to allocate register map: %d\n",
ret);
kfree(chip);
return ret;
}
chip->client = client;
i2c_set_clientdata(client, chip);
chip->dev = &client->dev;
dev_set_drvdata(chip->dev, chip);
/*
* Both client and companion client shares same platform driver.
* Driver distinguishes them by pdata->companion_addr.
* pdata->companion_addr is only assigned if companion chip exists.
* At the same time, the companion_addr shouldn't equal to client
* address.
*/
if (pdata->companion_addr && (pdata->companion_addr != client->addr)) {
chip->companion_addr = pdata->companion_addr;
chip->companion = i2c_new_dummy(chip->client->adapter,
chip->companion_addr);
chip->regmap_companion = regmap_init_i2c(chip->companion,
&pm860x_regmap_config);
if (IS_ERR(chip->regmap_companion)) {
ret = PTR_ERR(chip->regmap_companion);
dev_err(&chip->companion->dev,
"Failed to allocate register map: %d\n", ret);
return ret;
}
i2c_set_clientdata(chip->companion, chip);
}
pm860x_device_init(chip, pdata);
return 0;
}
static int __devexit pm860x_remove(struct i2c_client *client)
{
struct pm860x_chip *chip = i2c_get_clientdata(client);
pm860x_device_exit(chip);
if (chip->companion) {
regmap_exit(chip->regmap_companion);
i2c_unregister_device(chip->companion);
}
regmap_exit(chip->regmap);
kfree(chip);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int pm860x_suspend(struct device *dev)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct pm860x_chip *chip = i2c_get_clientdata(client);
if (device_may_wakeup(dev) && chip->wakeup_flag)
enable_irq_wake(chip->core_irq);
return 0;
}
static int pm860x_resume(struct device *dev)
{
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
struct pm860x_chip *chip = i2c_get_clientdata(client);
if (device_may_wakeup(dev) && chip->wakeup_flag)
disable_irq_wake(chip->core_irq);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(pm860x_pm_ops, pm860x_suspend, pm860x_resume);
static struct i2c_driver pm860x_driver = {
.driver = {
.name = "88PM860x",
.owner = THIS_MODULE,
.pm = &pm860x_pm_ops,
},
.probe = pm860x_probe,
.remove = __devexit_p(pm860x_remove),
.id_table = pm860x_id_table,
};
static int __init pm860x_i2c_init(void)
{
int ret;
ret = i2c_add_driver(&pm860x_driver);
if (ret != 0)
pr_err("Failed to register 88PM860x I2C driver: %d\n", ret);
return ret;
}
subsys_initcall(pm860x_i2c_init);
static void __exit pm860x_i2c_exit(void)
{
i2c_del_driver(&pm860x_driver);
}
module_exit(pm860x_i2c_exit);
MODULE_DESCRIPTION("I2C Driver for Marvell 88PM860x");
MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
MODULE_LICENSE("GPL");
......@@ -298,16 +298,6 @@ config MFD_TWL4030_AUDIO
select MFD_CORE
default n
config TWL6030_PWM
tristate "TWL6030 PWM (Pulse Width Modulator) Support"
depends on TWL4030_CORE
select HAVE_PWM
depends on !PWM
default n
help
Say yes here if you want support for TWL6030 PWM.
This is used to control charging LED brightness.
config TWL6040_CORE
bool "Support for TWL6040 audio codec"
depends on I2C=y && GENERIC_HARDIRQS
......@@ -385,6 +375,18 @@ config MFD_T7L66XB
help
Support for Toshiba Mobile IO Controller T7L66XB
config MFD_SMSC
bool "Support for the SMSC ECE1099 series chips"
depends on I2C=y
select MFD_CORE
select REGMAP_I2C
help
If you say yes here you get support for the
ece1099 chips from SMSC.
To compile this driver as a module, choose M here: the
module will be called smsc.
config MFD_TC6387XB
bool "Support Toshiba TC6387XB"
depends on ARM && HAVE_CLK
......@@ -441,6 +443,23 @@ config MFD_DA9052_I2C
for accessing the device, additional drivers must be enabled in
order to use the functionality of the device.
config MFD_DA9055
bool "Dialog Semiconductor DA9055 PMIC Support"
select REGMAP_I2C
select REGMAP_IRQ
select PMIC_DA9055
select MFD_CORE
depends on I2C=y
help
Say yes here for support of Dialog Semiconductor DA9055. This is
a Power Management IC. This driver provides common support for
accessing the device as well as the I2C interface to the chip itself.
Additional drivers must be enabled in order to use the functionality
of the device.
This driver can be built as a module. If built as a module it will be
called "da9055"
config PMIC_ADP5520
bool "Analog Devices ADP5520/01 MFD PMIC Core Support"
depends on I2C=y
......@@ -451,6 +470,16 @@ config PMIC_ADP5520
individual components like LCD backlight, LEDs, GPIOs and Kepad
under the corresponding menus.
config MFD_LP8788
bool "Texas Instruments LP8788 Power Management Unit Driver"
depends on I2C=y
select MFD_CORE
select REGMAP_I2C
select IRQ_DOMAIN
help
TI LP8788 PMU supports regulators, battery charger, RTC,
ADC, backlight driver and current sinks.
config MFD_MAX77686
bool "Maxim Semiconductor MAX77686 PMIC Support"
depends on I2C=y && GENERIC_HARDIRQS
......@@ -477,6 +506,18 @@ config MFD_MAX77693
additional drivers must be enabled in order to use the functionality
of the device.
config MFD_MAX8907
tristate "Maxim Semiconductor MAX8907 PMIC Support"
select MFD_CORE
depends on I2C=y && GENERIC_HARDIRQS
select REGMAP_I2C
select REGMAP_IRQ
help
Say yes here to support for Maxim Semiconductor MAX8907. This is
a Power Management IC. 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_MAX8925
bool "Maxim Semiconductor MAX8925 PMIC Support"
depends on I2C=y && GENERIC_HARDIRQS
......@@ -896,7 +937,7 @@ config MFD_WL1273_CORE
audio codec.
config MFD_OMAP_USB_HOST
bool "Support OMAP USBHS core driver"
bool "Support OMAP USBHS core and TLL driver"
depends on USB_EHCI_HCD_OMAP || USB_OHCI_HCD_OMAP3
default y
help
......@@ -985,13 +1026,13 @@ config MFD_STA2X11
depends on STA2X11
select MFD_CORE
config MFD_ANATOP
bool "Support for Freescale i.MX on-chip ANATOP controller"
depends on SOC_IMX6Q
config MFD_SYSCON
bool "System Controller Register R/W Based on Regmap"
depends on OF
select REGMAP_MMIO
help
Select this option to enable Freescale i.MX on-chip ANATOP
MFD controller. This controller embeds regulator and
thermal devices for Freescale i.MX platforms.
Select this option to enable accessing system control registers
via regmap.
config MFD_PALMAS
bool "Support for the TI Palmas series chips"
......
......@@ -63,7 +63,6 @@ obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o
obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o
obj-$(CONFIG_TWL6040_CORE) += twl6040-core.o twl6040-irq.o
obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
......@@ -77,6 +76,7 @@ obj-$(CONFIG_EZX_PCAP) += ezx-pcap.o
obj-$(CONFIG_MCP) += mcp-core.o
obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o
obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o
obj-$(CONFIG_MFD_SMSC) += smsc-ece1099.o
obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o
ifeq ($(CONFIG_SA1100_ASSABET),y)
......@@ -90,8 +90,14 @@ 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_LP8788) += lp8788.o lp8788-irq.o
da9055-objs := da9055-core.o da9055-i2c.o
obj-$(CONFIG_MFD_DA9055) += da9055.o
obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o
obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o
obj-$(CONFIG_MFD_MAX8907) += max8907.o
max8925-objs := max8925-core.o max8925-i2c.o
obj-$(CONFIG_MFD_MAX8925) += max8925.o
obj-$(CONFIG_MFD_MAX8997) += max8997.o max8997-irq.o
......@@ -120,7 +126,7 @@ obj-$(CONFIG_MFD_TPS6586X) += tps6586x.o
obj-$(CONFIG_MFD_VX855) += vx855.o
obj-$(CONFIG_MFD_WL1273_CORE) += wl1273-core.o
obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o
obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o
obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o
obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o
obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
......@@ -130,5 +136,5 @@ 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_SEC_CORE) += sec-core.o sec-irq.o
obj-$(CONFIG_MFD_ANATOP) += anatop-mfd.o
obj-$(CONFIG_MFD_SYSCON) += syscon.o
obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o
......@@ -21,6 +21,7 @@
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/mfd/core.h>
#include <linux/mfd/ab3100.h>
#include <linux/mfd/abx500.h>
/* These are the only registers inside AB3100 used in this main file */
......
......@@ -472,6 +472,22 @@ static irqreturn_t ab8500_hierarchical_irq(int irq, void *dev)
return IRQ_HANDLED;
}
/**
* 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.
*/
static int ab8500_irq_get_virq(struct ab8500 *ab8500, int irq)
{
if (!ab8500)
return -EINVAL;
return irq_create_mapping(ab8500->domain, irq);
}
static irqreturn_t ab8500_irq(int irq, void *dev)
{
struct ab8500 *ab8500 = dev;
......@@ -501,8 +517,9 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
do {
int bit = __ffs(value);
int line = i * 8 + bit;
int virq = ab8500_irq_get_virq(ab8500, line);
handle_nested_irq(ab8500->irq_base + line);
handle_nested_irq(virq);
value &= ~(1 << bit);
} while (value);
......@@ -511,23 +528,6 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
return IRQ_HANDLED;
}
/**
* 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)
{
if (!ab8500)
return -EINVAL;
return irq_create_mapping(ab8500->domain, irq);
}
EXPORT_SYMBOL_GPL(ab8500_irq_get_virq);
static int ab8500_irq_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hwirq)
{
......@@ -1076,6 +1076,7 @@ static struct mfd_cell __devinitdata ab8500_devs[] = {
},
{
.name = "ab8500-codec",
.of_compatible = "stericsson,ab8500-codec",
},
};
......
/*
* Anatop MFD driver
*
* Copyright (C) 2012 Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
* Copyright (C) 2012 Linaro
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/mfd/anatop.h>
u32 anatop_read_reg(struct anatop *adata, u32 addr)
{
return readl(adata->ioreg + addr);
}
EXPORT_SYMBOL_GPL(anatop_read_reg);
void anatop_write_reg(struct anatop *adata, u32 addr, u32 data, u32 mask)
{
u32 val;
data &= mask;
spin_lock(&adata->reglock);
val = readl(adata->ioreg + addr);
val &= ~mask;
val |= data;
writel(val, adata->ioreg + addr);
spin_unlock(&adata->reglock);
}
EXPORT_SYMBOL_GPL(anatop_write_reg);
static const struct of_device_id of_anatop_match[] = {
{ .compatible = "fsl,imx6q-anatop", },
{ },
};
static int __devinit of_anatop_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
void *ioreg;
struct anatop *drvdata;
ioreg = of_iomap(np, 0);
if (!ioreg)
return -EADDRNOTAVAIL;
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
drvdata->ioreg = ioreg;
spin_lock_init(&drvdata->reglock);
platform_set_drvdata(pdev, drvdata);
of_platform_populate(np, NULL, NULL, dev);
return 0;
}
static int __devexit of_anatop_remove(struct platform_device *pdev)
{
struct anatop *drvdata;
drvdata = platform_get_drvdata(pdev);
iounmap(drvdata->ioreg);
return 0;
}
static struct platform_driver anatop_of_driver = {
.driver = {
.name = "anatop-mfd",
.owner = THIS_MODULE,
.of_match_table = of_anatop_match,
},
.probe = of_anatop_probe,
.remove = of_anatop_remove,
};
static int __init anatop_init(void)
{
return platform_driver_register(&anatop_of_driver);
}
postcore_initcall(anatop_init);
static void __exit anatop_exit(void)
{
platform_driver_unregister(&anatop_of_driver);
}
module_exit(anatop_exit);
MODULE_AUTHOR("Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>");
MODULE_DESCRIPTION("ANATOP MFD driver");
MODULE_LICENSE("GPL v2");
......@@ -94,7 +94,8 @@ static irqreturn_t arizona_ctrlif_err(int irq, void *data)
static irqreturn_t arizona_irq_thread(int irq, void *data)
{
struct arizona *arizona = data;
int i, ret;
unsigned int val;
int ret;
ret = pm_runtime_get_sync(arizona->dev);
if (ret < 0) {
......@@ -102,9 +103,20 @@ static irqreturn_t arizona_irq_thread(int irq, void *data)
return IRQ_NONE;
}
/* Check both domains */
for (i = 0; i < 2; i++)
handle_nested_irq(irq_find_mapping(arizona->virq, i));
/* Always handle the AoD domain */
handle_nested_irq(irq_find_mapping(arizona->virq, 0));
/*
* Check if one of the main interrupts is asserted and only
* check that domain if it is.
*/
ret = regmap_read(arizona->regmap, ARIZONA_IRQ_PIN_STATUS, &val);
if (ret == 0 && val & ARIZONA_IRQ1_STS) {
handle_nested_irq(irq_find_mapping(arizona->virq, 1));
} else if (ret != 0) {
dev_err(arizona->dev, "Failed to read main IRQ status: %d\n",
ret);
}
pm_runtime_mark_last_busy(arizona->dev);
pm_runtime_put_autosuspend(arizona->dev);
......@@ -156,18 +168,36 @@ int arizona_irq_init(struct arizona *arizona)
int flags = IRQF_ONESHOT;
int ret, i;
const struct regmap_irq_chip *aod, *irq;
bool ctrlif_error = true;
switch (arizona->type) {
#ifdef CONFIG_MFD_WM5102
case WM5102:
aod = &wm5102_aod;
irq = &wm5102_irq;
switch (arizona->rev) {
case 0:
ctrlif_error = false;
break;
default:
break;
}
break;
#endif
#ifdef CONFIG_MFD_WM5110
case WM5110:
aod = &wm5110_aod;
irq = &wm5110_irq;
switch (arizona->rev) {
case 0:
case 1:
ctrlif_error = false;
break;
default:
break;
}
break;
#endif
default:
......@@ -226,13 +256,17 @@ int arizona_irq_init(struct arizona *arizona)
}
/* 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;
if (ctrlif_error) {
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 CTRLIF_ERR %d: %d\n",
arizona->irq, ret);
goto err_ctrlif;
}
}
ret = request_threaded_irq(arizona->irq, NULL, arizona_irq_thread,
......
This diff is collapsed.
/* I2C access for DA9055 PMICs.
*
* Copyright(c) 2012 Dialog Semiconductor Ltd.
*
* Author: David Dajun Chen <dchen@diasemi.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/device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/mfd/da9055/core.h>
static int __devinit da9055_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct da9055 *da9055;
int ret;
da9055 = devm_kzalloc(&i2c->dev, sizeof(struct da9055), GFP_KERNEL);
if (!da9055)
return -ENOMEM;
da9055->regmap = devm_regmap_init_i2c(i2c, &da9055_regmap_config);
if (IS_ERR(da9055->regmap)) {
ret = PTR_ERR(da9055->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
ret);
return ret;
}
da9055->dev = &i2c->dev;
da9055->chip_irq = i2c->irq;
i2c_set_clientdata(i2c, da9055);
return da9055_device_init(da9055);
}
static int __devexit da9055_i2c_remove(struct i2c_client *i2c)
{
struct da9055 *da9055 = i2c_get_clientdata(i2c);
da9055_device_exit(da9055);
return 0;
}
static struct i2c_device_id da9055_i2c_id[] = {
{"da9055-pmic", 0},
{ }
};
static struct i2c_driver da9055_i2c_driver = {
.probe = da9055_i2c_probe,
.remove = __devexit_p(da9055_i2c_remove),
.id_table = da9055_i2c_id,
.driver = {
.name = "da9055",
.owner = THIS_MODULE,
},
};
static int __init da9055_i2c_init(void)
{
int ret;
ret = i2c_add_driver(&da9055_i2c_driver);
if (ret != 0) {
pr_err("DA9055 I2C registration failed %d\n", ret);
return ret;
}
return 0;
}
subsys_initcall(da9055_i2c_init);
static void __exit da9055_i2c_exit(void)
{
i2c_del_driver(&da9055_i2c_driver);
}
module_exit(da9055_i2c_exit);
MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
MODULE_DESCRIPTION("I2C driver for Dialog DA9055 PMIC");
MODULE_LICENSE("GPL");
......@@ -270,6 +270,8 @@ static struct {
struct prcmu_fw_version version;
} fw_info;
static struct irq_domain *db8500_irq_domain;
/*
* This vector maps irq numbers to the bits in the bit field used in
* communication with the PRCMU firmware.
......@@ -2624,7 +2626,7 @@ static void prcmu_irq_mask(struct irq_data *d)
spin_lock_irqsave(&mb0_transfer.dbb_irqs_lock, flags);
mb0_transfer.req.dbb_irqs &= ~prcmu_irq_bit[d->irq - IRQ_PRCMU_BASE];
mb0_transfer.req.dbb_irqs &= ~prcmu_irq_bit[d->hwirq];
spin_unlock_irqrestore(&mb0_transfer.dbb_irqs_lock, flags);
......@@ -2638,7 +2640,7 @@ static void prcmu_irq_unmask(struct irq_data *d)
spin_lock_irqsave(&mb0_transfer.dbb_irqs_lock, flags);
mb0_transfer.req.dbb_irqs |= prcmu_irq_bit[d->irq - IRQ_PRCMU_BASE];
mb0_transfer.req.dbb_irqs |= prcmu_irq_bit[d->hwirq];
spin_unlock_irqrestore(&mb0_transfer.dbb_irqs_lock, flags);
......@@ -2678,9 +2680,37 @@ static char *fw_project_name(u8 project)
}
}
static int db8500_irq_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hwirq)
{
irq_set_chip_and_handler(virq, &prcmu_irq_chip,
handle_simple_irq);
set_irq_flags(virq, IRQF_VALID);
return 0;
}
static struct irq_domain_ops db8500_irq_ops = {
.map = db8500_irq_map,
.xlate = irq_domain_xlate_twocell,
};
static int db8500_irq_init(struct device_node *np)
{
db8500_irq_domain = irq_domain_add_legacy(
np, NUM_PRCMU_WAKEUPS, IRQ_PRCMU_BASE,
0, &db8500_irq_ops, NULL);
if (!db8500_irq_domain) {
pr_err("Failed to create irqdomain\n");
return -ENOSYS;
}
return 0;
}
void __init db8500_prcmu_early_init(void)
{
unsigned int i;
if (cpu_is_u8500v2()) {
void *tcpm_base = ioremap_nocache(U8500_PRCMU_TCPM_BASE, SZ_4K);
......@@ -2725,15 +2755,6 @@ void __init db8500_prcmu_early_init(void)
INIT_WORK(&mb0_transfer.mask_work, prcmu_mask_work);
/* Initalize irqs. */
for (i = 0; i < NUM_PRCMU_WAKEUPS; i++) {
unsigned int irq;
irq = IRQ_PRCMU_BASE + i;
irq_set_chip_and_handler(irq, &prcmu_irq_chip,
handle_simple_irq);
set_irq_flags(irq, IRQF_VALID);
}
compute_armss_rate();
}
......@@ -3041,6 +3062,8 @@ static int __devinit db8500_prcmu_probe(struct platform_device *pdev)
goto no_irq_return;
}
db8500_irq_init(np);
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;
......
/*
* TI LP8788 MFD - interrupt handler
*
* Copyright 2012 Texas Instruments
*
* Author: Milo(Woogyom) Kim <milo.kim@ti.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/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/device.h>
#include <linux/mfd/lp8788.h>
#include <linux/module.h>
#include <linux/slab.h>
/* register address */
#define LP8788_INT_1 0x00
#define LP8788_INTEN_1 0x03
#define BASE_INTEN_ADDR LP8788_INTEN_1
#define SIZE_REG 8
#define NUM_REGS 3
/*
* struct lp8788_irq_data
* @lp : used for accessing to lp8788 registers
* @irq_lock : mutex for enabling/disabling the interrupt
* @domain : IRQ domain for handling nested interrupt
* @enabled : status of enabled interrupt
*/
struct lp8788_irq_data {
struct lp8788 *lp;
struct mutex irq_lock;
struct irq_domain *domain;
int enabled[LP8788_INT_MAX];
};
static inline u8 _irq_to_addr(enum lp8788_int_id id)
{
return id / SIZE_REG;
}
static inline u8 _irq_to_enable_addr(enum lp8788_int_id id)
{
return _irq_to_addr(id) + BASE_INTEN_ADDR;
}
static inline u8 _irq_to_mask(enum lp8788_int_id id)
{
return 1 << (id % SIZE_REG);
}
static inline u8 _irq_to_val(enum lp8788_int_id id, int enable)
{
return enable << (id % SIZE_REG);
}
static void lp8788_irq_enable(struct irq_data *data)
{
struct lp8788_irq_data *irqd = irq_data_get_irq_chip_data(data);
irqd->enabled[data->hwirq] = 1;
}
static void lp8788_irq_disable(struct irq_data *data)
{
struct lp8788_irq_data *irqd = irq_data_get_irq_chip_data(data);
irqd->enabled[data->hwirq] = 0;
}
static void lp8788_irq_bus_lock(struct irq_data *data)
{
struct lp8788_irq_data *irqd = irq_data_get_irq_chip_data(data);
mutex_lock(&irqd->irq_lock);
}
static void lp8788_irq_bus_sync_unlock(struct irq_data *data)
{
struct lp8788_irq_data *irqd = irq_data_get_irq_chip_data(data);
enum lp8788_int_id irq = data->hwirq;
u8 addr, mask, val;
addr = _irq_to_enable_addr(irq);
mask = _irq_to_mask(irq);
val = _irq_to_val(irq, irqd->enabled[irq]);
lp8788_update_bits(irqd->lp, addr, mask, val);
mutex_unlock(&irqd->irq_lock);
}
static struct irq_chip lp8788_irq_chip = {
.name = "lp8788",
.irq_enable = lp8788_irq_enable,
.irq_disable = lp8788_irq_disable,
.irq_bus_lock = lp8788_irq_bus_lock,
.irq_bus_sync_unlock = lp8788_irq_bus_sync_unlock,
};
static irqreturn_t lp8788_irq_handler(int irq, void *ptr)
{
struct lp8788_irq_data *irqd = ptr;
struct lp8788 *lp = irqd->lp;
u8 status[NUM_REGS], addr, mask;
bool handled;
int i;
if (lp8788_read_multi_bytes(lp, LP8788_INT_1, status, NUM_REGS))
return IRQ_NONE;
for (i = 0 ; i < LP8788_INT_MAX ; i++) {
addr = _irq_to_addr(i);
mask = _irq_to_mask(i);
/* reporting only if the irq is enabled */
if (status[addr] & mask) {
handle_nested_irq(irq_find_mapping(irqd->domain, i));
handled = true;
}
}
return handled ? IRQ_HANDLED : IRQ_NONE;
}
static int lp8788_irq_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hwirq)
{
struct lp8788_irq_data *irqd = d->host_data;
struct irq_chip *chip = &lp8788_irq_chip;
irq_set_chip_data(virq, irqd);
irq_set_chip_and_handler(virq, chip, handle_edge_irq);
irq_set_nested_thread(virq, 1);
#ifdef CONFIG_ARM
set_irq_flags(virq, IRQF_VALID);
#else
irq_set_noprobe(virq);
#endif
return 0;
}
static struct irq_domain_ops lp8788_domain_ops = {
.map = lp8788_irq_map,
};
int lp8788_irq_init(struct lp8788 *lp, int irq)
{
struct lp8788_irq_data *irqd;
int ret;
if (irq <= 0) {
dev_warn(lp->dev, "invalid irq number: %d\n", irq);
return 0;
}
irqd = devm_kzalloc(lp->dev, sizeof(*irqd), GFP_KERNEL);
if (!irqd)
return -ENOMEM;
irqd->lp = lp;
irqd->domain = irq_domain_add_linear(lp->dev->of_node, LP8788_INT_MAX,
&lp8788_domain_ops, irqd);
if (!irqd->domain) {
dev_err(lp->dev, "failed to add irq domain err\n");
return -EINVAL;
}
lp->irqdm = irqd->domain;
mutex_init(&irqd->irq_lock);
ret = request_threaded_irq(irq, NULL, lp8788_irq_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"lp8788-irq", irqd);
if (ret) {
dev_err(lp->dev, "failed to create a thread for IRQ_N\n");
return ret;
}
lp->irq = irq;
return 0;
}
void lp8788_irq_exit(struct lp8788 *lp)
{
if (lp->irq)
free_irq(lp->irq, lp->irqdm);
}
/*
* TI LP8788 MFD - core interface
*
* Copyright 2012 Texas Instruments
*
* Author: Milo(Woogyom) Kim <milo.kim@ti.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/mfd/core.h>
#include <linux/mfd/lp8788.h>
#include <linux/module.h>
#include <linux/slab.h>
#define MAX_LP8788_REGISTERS 0xA2
#define MFD_DEV_SIMPLE(_name) \
{ \
.name = LP8788_DEV_##_name, \
}
#define MFD_DEV_WITH_ID(_name, _id) \
{ \
.name = LP8788_DEV_##_name, \
.id = _id, \
}
#define MFD_DEV_WITH_RESOURCE(_name, _resource, num_resource) \
{ \
.name = LP8788_DEV_##_name, \
.resources = _resource, \
.num_resources = num_resource, \
}
static struct resource chg_irqs[] = {
/* Charger Interrupts */
{
.start = LP8788_INT_CHG_INPUT_STATE,
.end = LP8788_INT_PRECHG_TIMEOUT,
.name = LP8788_CHG_IRQ,
.flags = IORESOURCE_IRQ,
},
/* Power Routing Switch Interrupts */
{
.start = LP8788_INT_ENTER_SYS_SUPPORT,
.end = LP8788_INT_EXIT_SYS_SUPPORT,
.name = LP8788_PRSW_IRQ,
.flags = IORESOURCE_IRQ,
},
/* Battery Interrupts */
{
.start = LP8788_INT_BATT_LOW,
.end = LP8788_INT_NO_BATT,
.name = LP8788_BATT_IRQ,
.flags = IORESOURCE_IRQ,
},
};
static struct resource rtc_irqs[] = {
{
.start = LP8788_INT_RTC_ALARM1,
.end = LP8788_INT_RTC_ALARM2,
.name = LP8788_ALM_IRQ,
.flags = IORESOURCE_IRQ,
},
};
static struct mfd_cell lp8788_devs[] = {
/* 4 bucks */
MFD_DEV_WITH_ID(BUCK, 1),
MFD_DEV_WITH_ID(BUCK, 2),
MFD_DEV_WITH_ID(BUCK, 3),
MFD_DEV_WITH_ID(BUCK, 4),
/* 12 digital ldos */
MFD_DEV_WITH_ID(DLDO, 1),
MFD_DEV_WITH_ID(DLDO, 2),
MFD_DEV_WITH_ID(DLDO, 3),
MFD_DEV_WITH_ID(DLDO, 4),
MFD_DEV_WITH_ID(DLDO, 5),
MFD_DEV_WITH_ID(DLDO, 6),
MFD_DEV_WITH_ID(DLDO, 7),
MFD_DEV_WITH_ID(DLDO, 8),
MFD_DEV_WITH_ID(DLDO, 9),
MFD_DEV_WITH_ID(DLDO, 10),
MFD_DEV_WITH_ID(DLDO, 11),
MFD_DEV_WITH_ID(DLDO, 12),
/* 10 analog ldos */
MFD_DEV_WITH_ID(ALDO, 1),
MFD_DEV_WITH_ID(ALDO, 2),
MFD_DEV_WITH_ID(ALDO, 3),
MFD_DEV_WITH_ID(ALDO, 4),
MFD_DEV_WITH_ID(ALDO, 5),
MFD_DEV_WITH_ID(ALDO, 6),
MFD_DEV_WITH_ID(ALDO, 7),
MFD_DEV_WITH_ID(ALDO, 8),
MFD_DEV_WITH_ID(ALDO, 9),
MFD_DEV_WITH_ID(ALDO, 10),
/* ADC */
MFD_DEV_SIMPLE(ADC),
/* battery charger */
MFD_DEV_WITH_RESOURCE(CHARGER, chg_irqs, ARRAY_SIZE(chg_irqs)),
/* rtc */
MFD_DEV_WITH_RESOURCE(RTC, rtc_irqs, ARRAY_SIZE(rtc_irqs)),
/* backlight */
MFD_DEV_SIMPLE(BACKLIGHT),
/* current sink for vibrator */
MFD_DEV_SIMPLE(VIBRATOR),
/* current sink for keypad LED */
MFD_DEV_SIMPLE(KEYLED),
};
int lp8788_read_byte(struct lp8788 *lp, u8 reg, u8 *data)
{
int ret;
unsigned int val;
ret = regmap_read(lp->regmap, reg, &val);
if (ret < 0) {
dev_err(lp->dev, "failed to read 0x%.2x\n", reg);
return ret;
}
*data = (u8)val;
return 0;
}
EXPORT_SYMBOL_GPL(lp8788_read_byte);
int lp8788_read_multi_bytes(struct lp8788 *lp, u8 reg, u8 *data, size_t count)
{
return regmap_bulk_read(lp->regmap, reg, data, count);
}
EXPORT_SYMBOL_GPL(lp8788_read_multi_bytes);
int lp8788_write_byte(struct lp8788 *lp, u8 reg, u8 data)
{
return regmap_write(lp->regmap, reg, data);
}
EXPORT_SYMBOL_GPL(lp8788_write_byte);
int lp8788_update_bits(struct lp8788 *lp, u8 reg, u8 mask, u8 data)
{
return regmap_update_bits(lp->regmap, reg, mask, data);
}
EXPORT_SYMBOL_GPL(lp8788_update_bits);
static int lp8788_platform_init(struct lp8788 *lp)
{
struct lp8788_platform_data *pdata = lp->pdata;
return (pdata && pdata->init_func) ? pdata->init_func(lp) : 0;
}
static const struct regmap_config lp8788_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = MAX_LP8788_REGISTERS,
};
static int lp8788_probe(struct i2c_client *cl, const struct i2c_device_id *id)
{
struct lp8788 *lp;
struct lp8788_platform_data *pdata = cl->dev.platform_data;
int ret;
lp = devm_kzalloc(&cl->dev, sizeof(struct lp8788), GFP_KERNEL);
if (!lp)
return -ENOMEM;
lp->regmap = devm_regmap_init_i2c(cl, &lp8788_regmap_config);
if (IS_ERR(lp->regmap)) {
ret = PTR_ERR(lp->regmap);
dev_err(&cl->dev, "regmap init i2c err: %d\n", ret);
return ret;
}
lp->pdata = pdata;
lp->dev = &cl->dev;
i2c_set_clientdata(cl, lp);
ret = lp8788_platform_init(lp);
if (ret)
return ret;
ret = lp8788_irq_init(lp, cl->irq);
if (ret)
return ret;
return mfd_add_devices(lp->dev, -1, lp8788_devs,
ARRAY_SIZE(lp8788_devs), NULL, 0, NULL);
}
static int __devexit lp8788_remove(struct i2c_client *cl)
{
struct lp8788 *lp = i2c_get_clientdata(cl);
mfd_remove_devices(lp->dev);
lp8788_irq_exit(lp);
return 0;
}
static const struct i2c_device_id lp8788_ids[] = {
{"lp8788", 0},
{ }
};
MODULE_DEVICE_TABLE(i2c, lp8788_ids);
static struct i2c_driver lp8788_driver = {
.driver = {
.name = "lp8788",
.owner = THIS_MODULE,
},
.probe = lp8788_probe,
.remove = __devexit_p(lp8788_remove),
.id_table = lp8788_ids,
};
static int __init lp8788_init(void)
{
return i2c_add_driver(&lp8788_driver);
}
subsys_initcall(lp8788_init);
static void __exit lp8788_exit(void)
{
i2c_del_driver(&lp8788_driver);
}
module_exit(lp8788_exit);
MODULE_DESCRIPTION("TI LP8788 MFD Driver");
MODULE_AUTHOR("Milo Kim");
MODULE_LICENSE("GPL");
......@@ -49,6 +49,7 @@
* document number TBD : DH89xxCC
* document number TBD : Panther Point
* document number TBD : Lynx Point
* document number TBD : Lynx Point-LP
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
......@@ -192,6 +193,7 @@ enum lpc_chipsets {
LPC_DH89XXCC, /* DH89xxCC */
LPC_PPT, /* Panther Point */
LPC_LPT, /* Lynx Point */
LPC_LPT_LP, /* Lynx Point-LP */
};
struct lpc_ich_info lpc_chipset_info[] __devinitdata = {
......@@ -468,6 +470,10 @@ struct lpc_ich_info lpc_chipset_info[] __devinitdata = {
.name = "Lynx Point",
.iTCO_version = 2,
},
[LPC_LPT_LP] = {
.name = "Lynx Point_LP",
.iTCO_version = 2,
},
};
/*
......@@ -641,6 +647,14 @@ static DEFINE_PCI_DEVICE_TABLE(lpc_ich_ids) = {
{ PCI_VDEVICE(INTEL, 0x8c5d), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c5e), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c5f), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x9c40), LPC_LPT_LP},
{ PCI_VDEVICE(INTEL, 0x9c41), LPC_LPT_LP},
{ PCI_VDEVICE(INTEL, 0x9c42), LPC_LPT_LP},
{ PCI_VDEVICE(INTEL, 0x9c43), LPC_LPT_LP},
{ PCI_VDEVICE(INTEL, 0x9c44), LPC_LPT_LP},
{ PCI_VDEVICE(INTEL, 0x9c45), LPC_LPT_LP},
{ PCI_VDEVICE(INTEL, 0x9c46), LPC_LPT_LP},
{ PCI_VDEVICE(INTEL, 0x9c47), LPC_LPT_LP},
{ 0, }, /* End of list */
};
MODULE_DEVICE_TABLE(pci, lpc_ich_ids);
......@@ -683,6 +697,30 @@ static void __devinit lpc_ich_finalize_cell(struct mfd_cell *cell,
cell->pdata_size = sizeof(struct lpc_ich_info);
}
/*
* We don't check for resource conflict globally. There are 2 or 3 independent
* GPIO groups and it's enough to have access to one of these to instantiate
* the device.
*/
static int __devinit lpc_ich_check_conflict_gpio(struct resource *res)
{
int ret;
u8 use_gpio = 0;
if (resource_size(res) >= 0x50 &&
!acpi_check_region(res->start + 0x40, 0x10, "LPC ICH GPIO3"))
use_gpio |= 1 << 2;
if (!acpi_check_region(res->start + 0x30, 0x10, "LPC ICH GPIO2"))
use_gpio |= 1 << 1;
ret = acpi_check_region(res->start + 0x00, 0x30, "LPC ICH GPIO1");
if (!ret)
use_gpio |= 1 << 0;
return use_gpio ? use_gpio : ret;
}
static int __devinit lpc_ich_init_gpio(struct pci_dev *dev,
const struct pci_device_id *id)
{
......@@ -740,12 +778,13 @@ static int __devinit lpc_ich_init_gpio(struct pci_dev *dev,
break;
}
ret = acpi_check_resource_conflict(res);
if (ret) {
ret = lpc_ich_check_conflict_gpio(res);
if (ret < 0) {
/* this isn't necessarily fatal for the GPIO */
acpi_conflict = true;
goto gpio_done;
}
lpc_chipset_info[id->driver_data].use_gpio = ret;
lpc_ich_enable_gpio_space(dev);
lpc_ich_finalize_cell(&lpc_ich_cells[LPC_GPIO], id);
......
/*
* max8907.c - mfd driver for MAX8907
*
* Copyright (C) 2010 Gyungoh Yoo <jack.yoo@maxim-ic.com>
* Copyright (C) 2010-2012, NVIDIA CORPORATION. All rights reserved.
*
* 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/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/mfd/core.h>
#include <linux/mfd/max8907.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
static struct mfd_cell max8907_cells[] = {
{ .name = "max8907-regulator", },
{ .name = "max8907-rtc", },
};
static bool max8907_gen_is_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MAX8907_REG_ON_OFF_IRQ1:
case MAX8907_REG_ON_OFF_STAT:
case MAX8907_REG_ON_OFF_IRQ2:
case MAX8907_REG_CHG_IRQ1:
case MAX8907_REG_CHG_IRQ2:
case MAX8907_REG_CHG_STAT:
return true;
default:
return false;
}
}
static bool max8907_gen_is_precious_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MAX8907_REG_ON_OFF_IRQ1:
case MAX8907_REG_ON_OFF_IRQ2:
case MAX8907_REG_CHG_IRQ1:
case MAX8907_REG_CHG_IRQ2:
return true;
default:
return false;
}
}
static bool max8907_gen_is_writeable_reg(struct device *dev, unsigned int reg)
{
return !max8907_gen_is_volatile_reg(dev, reg);
}
static const struct regmap_config max8907_regmap_gen_config = {
.reg_bits = 8,
.val_bits = 8,
.volatile_reg = max8907_gen_is_volatile_reg,
.precious_reg = max8907_gen_is_precious_reg,
.writeable_reg = max8907_gen_is_writeable_reg,
.max_register = MAX8907_REG_LDO20VOUT,
.cache_type = REGCACHE_RBTREE,
};
static bool max8907_rtc_is_volatile_reg(struct device *dev, unsigned int reg)
{
if (reg <= MAX8907_REG_RTC_YEAR2)
return true;
switch (reg) {
case MAX8907_REG_RTC_STATUS:
case MAX8907_REG_RTC_IRQ:
return true;
default:
return false;
}
}
static bool max8907_rtc_is_precious_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MAX8907_REG_RTC_IRQ:
return true;
default:
return false;
}
}
static bool max8907_rtc_is_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MAX8907_REG_RTC_STATUS:
case MAX8907_REG_RTC_IRQ:
return false;
default:
return true;
}
}
static const struct regmap_config max8907_regmap_rtc_config = {
.reg_bits = 8,
.val_bits = 8,
.volatile_reg = max8907_rtc_is_volatile_reg,
.precious_reg = max8907_rtc_is_precious_reg,
.writeable_reg = max8907_rtc_is_writeable_reg,
.max_register = MAX8907_REG_MPL_CNTL,
.cache_type = REGCACHE_RBTREE,
};
static const struct regmap_irq max8907_chg_irqs[] = {
{ .reg_offset = 0, .mask = 1 << 0, },
{ .reg_offset = 0, .mask = 1 << 1, },
{ .reg_offset = 0, .mask = 1 << 2, },
{ .reg_offset = 1, .mask = 1 << 0, },
{ .reg_offset = 1, .mask = 1 << 1, },
{ .reg_offset = 1, .mask = 1 << 2, },
{ .reg_offset = 1, .mask = 1 << 3, },
{ .reg_offset = 1, .mask = 1 << 4, },
{ .reg_offset = 1, .mask = 1 << 5, },
{ .reg_offset = 1, .mask = 1 << 6, },
{ .reg_offset = 1, .mask = 1 << 7, },
};
static const struct regmap_irq_chip max8907_chg_irq_chip = {
.name = "max8907 chg",
.status_base = MAX8907_REG_CHG_IRQ1,
.mask_base = MAX8907_REG_CHG_IRQ1_MASK,
.wake_base = MAX8907_REG_CHG_IRQ1_MASK,
.irq_reg_stride = MAX8907_REG_CHG_IRQ2 - MAX8907_REG_CHG_IRQ1,
.num_regs = 2,
.irqs = max8907_chg_irqs,
.num_irqs = ARRAY_SIZE(max8907_chg_irqs),
};
static const struct regmap_irq max8907_on_off_irqs[] = {
{ .reg_offset = 0, .mask = 1 << 0, },
{ .reg_offset = 0, .mask = 1 << 1, },
{ .reg_offset = 0, .mask = 1 << 2, },
{ .reg_offset = 0, .mask = 1 << 3, },
{ .reg_offset = 0, .mask = 1 << 4, },
{ .reg_offset = 0, .mask = 1 << 5, },
{ .reg_offset = 0, .mask = 1 << 6, },
{ .reg_offset = 0, .mask = 1 << 7, },
{ .reg_offset = 1, .mask = 1 << 0, },
{ .reg_offset = 1, .mask = 1 << 1, },
};
static const struct regmap_irq_chip max8907_on_off_irq_chip = {
.name = "max8907 on_off",
.status_base = MAX8907_REG_ON_OFF_IRQ1,
.mask_base = MAX8907_REG_ON_OFF_IRQ1_MASK,
.irq_reg_stride = MAX8907_REG_ON_OFF_IRQ2 - MAX8907_REG_ON_OFF_IRQ1,
.num_regs = 2,
.irqs = max8907_on_off_irqs,
.num_irqs = ARRAY_SIZE(max8907_on_off_irqs),
};
static const struct regmap_irq max8907_rtc_irqs[] = {
{ .reg_offset = 0, .mask = 1 << 2, },
{ .reg_offset = 0, .mask = 1 << 3, },
};
static const struct regmap_irq_chip max8907_rtc_irq_chip = {
.name = "max8907 rtc",
.status_base = MAX8907_REG_RTC_IRQ,
.mask_base = MAX8907_REG_RTC_IRQ_MASK,
.num_regs = 1,
.irqs = max8907_rtc_irqs,
.num_irqs = ARRAY_SIZE(max8907_rtc_irqs),
};
static struct max8907 *max8907_pm_off;
static void max8907_power_off(void)
{
regmap_update_bits(max8907_pm_off->regmap_gen, MAX8907_REG_RESET_CNFG,
MAX8907_MASK_POWER_OFF, MAX8907_MASK_POWER_OFF);
}
static __devinit int max8907_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct max8907 *max8907;
int ret;
struct max8907_platform_data *pdata = dev_get_platdata(&i2c->dev);
bool pm_off = false;
if (pdata)
pm_off = pdata->pm_off;
else if (i2c->dev.of_node)
pm_off = of_property_read_bool(i2c->dev.of_node,
"maxim,system-power-controller");
max8907 = devm_kzalloc(&i2c->dev, sizeof(struct max8907), GFP_KERNEL);
if (!max8907) {
ret = -ENOMEM;
goto err_alloc_drvdata;
}
max8907->dev = &i2c->dev;
dev_set_drvdata(max8907->dev, max8907);
max8907->i2c_gen = i2c;
i2c_set_clientdata(i2c, max8907);
max8907->regmap_gen = devm_regmap_init_i2c(i2c,
&max8907_regmap_gen_config);
if (IS_ERR(max8907->regmap_gen)) {
ret = PTR_ERR(max8907->regmap_gen);
dev_err(&i2c->dev, "gen regmap init failed: %d\n", ret);
goto err_regmap_gen;
}
max8907->i2c_rtc = i2c_new_dummy(i2c->adapter, MAX8907_RTC_I2C_ADDR);
if (!max8907->i2c_rtc) {
ret = -ENOMEM;
goto err_dummy_rtc;
}
i2c_set_clientdata(max8907->i2c_rtc, max8907);
max8907->regmap_rtc = devm_regmap_init_i2c(max8907->i2c_rtc,
&max8907_regmap_rtc_config);
if (IS_ERR(max8907->regmap_rtc)) {
ret = PTR_ERR(max8907->regmap_rtc);
dev_err(&i2c->dev, "rtc regmap init failed: %d\n", ret);
goto err_regmap_rtc;
}
irq_set_status_flags(max8907->i2c_gen->irq, IRQ_NOAUTOEN);
ret = regmap_add_irq_chip(max8907->regmap_gen, max8907->i2c_gen->irq,
IRQF_ONESHOT | IRQF_SHARED, -1,
&max8907_chg_irq_chip,
&max8907->irqc_chg);
if (ret != 0) {
dev_err(&i2c->dev, "failed to add chg irq chip: %d\n", ret);
goto err_irqc_chg;
}
ret = regmap_add_irq_chip(max8907->regmap_gen, max8907->i2c_gen->irq,
IRQF_ONESHOT | IRQF_SHARED, -1,
&max8907_on_off_irq_chip,
&max8907->irqc_on_off);
if (ret != 0) {
dev_err(&i2c->dev, "failed to add on off irq chip: %d\n", ret);
goto err_irqc_on_off;
}
ret = regmap_add_irq_chip(max8907->regmap_rtc, max8907->i2c_gen->irq,
IRQF_ONESHOT | IRQF_SHARED, -1,
&max8907_rtc_irq_chip,
&max8907->irqc_rtc);
if (ret != 0) {
dev_err(&i2c->dev, "failed to add rtc irq chip: %d\n", ret);
goto err_irqc_rtc;
}
enable_irq(max8907->i2c_gen->irq);
ret = mfd_add_devices(max8907->dev, -1, max8907_cells,
ARRAY_SIZE(max8907_cells), NULL, 0, NULL);
if (ret != 0) {
dev_err(&i2c->dev, "failed to add MFD devices %d\n", ret);
goto err_add_devices;
}
if (pm_off && !pm_power_off) {
max8907_pm_off = max8907;
pm_power_off = max8907_power_off;
}
return 0;
err_add_devices:
regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_rtc);
err_irqc_rtc:
regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_on_off);
err_irqc_on_off:
regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_chg);
err_irqc_chg:
err_regmap_rtc:
i2c_unregister_device(max8907->i2c_rtc);
err_dummy_rtc:
err_regmap_gen:
err_alloc_drvdata:
return ret;
}
static __devexit int max8907_i2c_remove(struct i2c_client *i2c)
{
struct max8907 *max8907 = i2c_get_clientdata(i2c);
mfd_remove_devices(max8907->dev);
regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_rtc);
regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_on_off);
regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_chg);
i2c_unregister_device(max8907->i2c_rtc);
return 0;
}
#ifdef CONFIG_OF
static struct of_device_id max8907_of_match[] = {
{ .compatible = "maxim,max8907" },
{ },
};
MODULE_DEVICE_TABLE(of, max8907_of_match);
#endif
static const struct i2c_device_id max8907_i2c_id[] = {
{"max8907", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, max8907_i2c_id);
static struct i2c_driver max8907_i2c_driver = {
.driver = {
.name = "max8907",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(max8907_of_match),
},
.probe = max8907_i2c_probe,
.remove = max8907_i2c_remove,
.id_table = max8907_i2c_id,
};
static int __init max8907_i2c_init(void)
{
int ret = -ENODEV;
ret = i2c_add_driver(&max8907_i2c_driver);
if (ret != 0)
pr_err("Failed to register I2C driver: %d\n", ret);
return ret;
}
subsys_initcall(max8907_i2c_init);
static void __exit max8907_i2c_exit(void)
{
i2c_del_driver(&max8907_i2c_driver);
}
module_exit(max8907_i2c_exit);
MODULE_DESCRIPTION("MAX8907 multi-function core driver");
MODULE_AUTHOR("Gyungoh Yoo <jack.yoo@maxim-ic.com>");
MODULE_LICENSE("GPL v2");
This diff is collapsed.
......@@ -676,7 +676,6 @@ int mc13xxx_common_init(struct mc13xxx *mc13xxx,
err_mask:
err_revision:
mc13xxx_unlock(mc13xxx);
kfree(mc13xxx);
return ret;
}
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -255,7 +255,7 @@ static irqreturn_t rc5t583_irq(int irq, void *data)
{
struct rc5t583 *rc5t583 = data;
uint8_t int_sts[RC5T583_MAX_INTERRUPT_MASK_REGS];
uint8_t master_int;
uint8_t master_int = 0;
int i;
int ret;
unsigned int rtc_int_sts = 0;
......
......@@ -85,7 +85,7 @@ static int __rc5t583_set_ext_pwrreq1_control(struct device *dev,
int id, int ext_pwr, int slots)
{
int ret;
uint8_t sleepseq_val;
uint8_t sleepseq_val = 0;
unsigned int en_bit;
unsigned int slot_bit;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -236,7 +236,7 @@ static int __devinit tps65090_irq_init(struct tps65090 *tps65090, int irq,
static bool is_volatile_reg(struct device *dev, unsigned int reg)
{
if ((reg == TPS65090_INT_STS) || (reg == TPS65090_INT_STS))
if (reg == TPS65090_INT_STS)
return true;
else
return false;
......
......@@ -34,6 +34,9 @@ static struct mfd_cell tps65217s[] = {
{
.name = "tps65217-pmic",
},
{
.name = "tps65217-bl",
},
};
/**
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -390,7 +390,7 @@ static const __devinitdata struct reg_default wm8958_reva_patch[] = {
static const __devinitdata struct reg_default wm1811_reva_patch[] = {
{ 0x102, 0x3 },
{ 0x56, 0x7 },
{ 0x56, 0xc07 },
{ 0x5d, 0x7e },
{ 0x5e, 0x0 },
{ 0x102, 0x0 },
......
......@@ -1136,7 +1136,7 @@ static bool wm1811_volatile_register(struct device *dev, unsigned int reg)
switch (reg) {
case WM8994_GPIO_6:
if (wm8994->revision > 1)
if (wm8994->cust_id > 1 || wm8994->revision > 1)
return true;
else
return false;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment