diff options
author | Andrey Konovalov <andrey.konovalov@linaro.org> | 2012-07-23 20:55:30 +0400 |
---|---|---|
committer | Andrey Konovalov <andrey.konovalov@linaro.org> | 2012-07-23 20:55:30 +0400 |
commit | 7ff32b1a50ecbf5f04e8ee66562c799fdff0356d (patch) | |
tree | f8672703052683a12651c1ddac8b64e43f959173 | |
parent | 962fcb662e04799ecf0e629e989acad36e47f53c (diff) | |
parent | 7480ea215d989a8114404f3f4a860d9a576cfce9 (diff) |
Merge branch 'tracking-samslt-dt' into merge-linux-linaro
-rw-r--r-- | Documentation/devicetree/bindings/arm/samsung/wakeup-eint.txt | 152 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/mmc/samsung-sdhci.txt | 70 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/regulator/max8997-pmic.txt | 133 | ||||
-rw-r--r-- | arch/arm/boot/dts/exynos4210-origen.dts | 180 | ||||
-rw-r--r-- | arch/arm/boot/dts/exynos4210.dtsi | 36 | ||||
-rw-r--r-- | arch/arm/mach-exynos/Kconfig | 2 | ||||
-rw-r--r-- | arch/arm/mach-exynos/common.c | 139 | ||||
-rw-r--r-- | arch/arm/mach-exynos/include/mach-exynos/regs-gpio.h | 4 | ||||
-rw-r--r-- | arch/arm/mach-exynos/mach-exynos4-dt.c | 34 | ||||
-rw-r--r-- | arch/arm/mach-exynos/mach-nuri.c | 4 | ||||
-rw-r--r-- | arch/arm/mach-exynos/mach-origen.c | 1 | ||||
-rw-r--r-- | arch/arm/mach-exynos/pm_domains.c | 3 | ||||
-rw-r--r-- | drivers/mfd/max8997-irq.c | 61 | ||||
-rw-r--r-- | drivers/mfd/max8997.c | 74 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci-s3c.c | 150 | ||||
-rw-r--r-- | drivers/regulator/max8997.c | 140 | ||||
-rw-r--r-- | include/linux/mfd/max8997-private.h | 5 | ||||
-rw-r--r-- | include/linux/mfd/max8997.h | 2 |
18 files changed, 1099 insertions, 91 deletions
diff --git a/Documentation/devicetree/bindings/arm/samsung/wakeup-eint.txt b/Documentation/devicetree/bindings/arm/samsung/wakeup-eint.txt new file mode 100644 index 000000000000..52aa0493914a --- /dev/null +++ b/Documentation/devicetree/bindings/arm/samsung/wakeup-eint.txt @@ -0,0 +1,152 @@ +* Samsung GPIO Wakeup Interrupt Controller + +This document is split into following sections. + +[1] Samsung Exynos4 GPIO Wakeup Interrupt Source Controller +[2] Samsung Exynos5 GPIO Wakeup Interrupt Source Controller + + +[1] Samsung Exynos4 GPIO Wakeup Interrupt Source Controller + +Samsung Exynos4 processor supports 32 external wakeup interrupt sources. First +16 of these interrupts are directly connected to GIC and the rest 16 of the +interrupts are grouped together to deliver a single interrupt to GIC. + +Required properties: + + - compatible: should be "samsung,exynos4210-wakeup-eint". + + - reg: physical base address of the controller and length of memory + mapped region. + + - interrupt-controller: Identifies the node as an interrupt controller. + + - interrupt-cells: Specifies the number of cells required to specify the + interrupt source number. The value of should be <2>. The first cell + represents the wakeup interrupt source number and the second cell + should be zero (currently unused). + + - interrupts: List of interrupts generated by the gpio wakeup interrupt + controller which are connected to a parent interrupt controller. The + format of the interrupt specifier depends on the interrupt parent + controller. + +Optional properties: + + - interrupt-parent: phandle of the parent interrupt controller, required + if not inheriting the interrupt parent from the parent node. + +Example: + + The following example is from the Exynos4210 dtsi file. + + wakeup_eint: interrupt-controller-wakeup-eint { + compatible = "samsung,exynos4210-wakeup-eint"; + reg = <0x11000000 0x1000>; + #interrupt-cells = <2>; + interrupt-controller; + interrupts = <0 16 0>, <0 17 0>, <0 18 0>, <0 19 0>, + <0 20 0>, <0 21 0>, <0 22 0>, <0 23 0>, + <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>, + <0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>, + <0 32 0>; + }; + + +[2] Samsung Exynos5 GPIO Wakeup Interrupt Source Controller + +Samsung Exynos5 processor supports 32 external wakeup interrupt sources. First +16 of these interrupts are directly connected to GIC and the rest 16 of the +interrupts are grouped together to deliver a single interrupt to interrupt +combiner controller. + +Since the wakeup interrupts has two interrupt parents, a interrupt nexus +child node is used that includes a interrupt-map used to translate wakeup +interrupt specifiers into gic and combiner domain interrupts. + +Required properties: + + - compatible: should be "samsung,exynos5210-wakeup-eint". + + - reg: physical base address of the controller and length of memory + mapped region. + + - interrupt-controller: Identifies the node as an interrupt controller. + + - interrupt-cells: Specifies the number of cells required to specify the + interrupt source number. The value of should be <2>. The first cell + represents the wakeup interrupt source number and the second cell + should be zero (currently unused). + + - interrupts: List of interrupts generated by the gpio wakeup interrupt + controller. Since both gic and combiner controllers are interrupt + parents, a interrupt nexus child node is used to translate the interrupt + specifiers into respective gic and combiner interrupt domains (see below). + The interrupt specifier should be two cells - the first cell should be the + interrupt number originating from the wakeup controller and the second + cell should be zero (unused). + + - interrupt-parent: The phandle of the interrupt nexus child node. + + - interrupt-nexus child node: This node is used to translate the interrupt + specifiers of the wakeup interrupt controller node to the respecitive + gic or combiner interrupt domain. The interrupt nexus node should include + the following properties. + + - interrupt-cells: Specifies the number of cells required to specify the + interrupt source number. The value of should be <2>. The first cell + represents the wakeup interrupt source number and the second cell + should be zero (currently unused). + + - #address-cells: value of this property should be zero. + + - #size-cells: value of this property should be zero. + + - interrupt-map: The interrpt-map specifies how interrupt specifiers are + translated. There should be a interrupt-map entry for each interrupt + generated by the wakeup interrupt controller. The format of each entry + in the interrupt map should be + + <#intr 0 parent-phandle parent-interrupt-specifier> + + where #intr is one of the entries in the 'interrupts' property of the + parent node. + + +Example: + + wakeup_eint: interrupt-controller@11400000 { + compatible = "samsung,exynos5210-wakeup-eint"; + reg = <0x11400000 0x1000>; + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&eint_nexus>; + interrupts = <0x0 0>, <0x1 0>, <0x2 0>, <0x3 0>, + <0x4 0>, <0x5 0>, <0x6 0>, <0x7 0>, + <0x8 0>, <0x9 0>, <0xa 0>, <0xb 0>, + <0xc 0>, <0xd 0>, <0xe 0>, <0xf 0>, + <0x10 0>; + + wakeup_map: interrupt-map { + #interrupt-cells = <2>; + #address-cells = <0>; + #size-cells = <0>; + interrupt-map = <0x0 0 &combiner 23 0>, + <0x1 0 &combiner 24 0>, + <0x2 0 &combiner 25 0>, + <0x3 0 &combiner 25 1>, + <0x4 0 &combiner 26 0>, + <0x5 0 &combiner 26 1>, + <0x6 0 &combiner 27 0>, + <0x7 0 &combiner 27 1>, + <0x8 0 &combiner 28 0>, + <0x9 0 &combiner 28 1>, + <0xa 0 &combiner 29 0>, + <0xb 0 &combiner 29 1>, + <0xc 0 &combiner 30 0>, + <0xd 0 &combiner 30 1>, + <0xe 0 &combiner 31 0>, + <0xf 0 &combiner 31 1>, + <0x10 0 &gic 0 32 0>; + }; + }; diff --git a/Documentation/devicetree/bindings/mmc/samsung-sdhci.txt b/Documentation/devicetree/bindings/mmc/samsung-sdhci.txt new file mode 100644 index 000000000000..8cbdd2941d4e --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/samsung-sdhci.txt @@ -0,0 +1,70 @@ +* Samsung's SDHCI Controller device tree bindings + +Samsung's SDHCI controller is used as a connectivity interface with external +MMC, SD and eMMC storage mediums. + +Required SoC Specific Properties: +- compatible: should be one of the following + - "samsung,s3c6410-sdhci": For controllers compatible with s3c6410 sdhci + controller. + - "samsung,exynos4210-sdhci": For controller compatible with Exynos4 sdhci + controller. + +- reg: physical base address of the controller and length of memory mapped + region. + +- interrupts: The interrupt number to the cpu. The interrupt specifier format + depends on the interrupt controller. + + +Required Board Specific Properties: +- gpios: Should specify the gpios used for clock, command and data lines. The + gpio specifier format depends on the gpio controller. Note: There is no + particular order in which the gpio's have to be listed. + + +Optional Board Specific Properties: +- samsung,sdhci-bus-width: Number of data lines connected to the controller. + Note: This excludes the clock,command and card detect lines. If this property + is not specified, default value is 1. + +- samsung,cd-gpio-invert: If 'samsung,sdhci-cd-gpio' card detect method is + selected, this property can be optionally specified to invert the value of + external card detect gpio line. + +- One of the following properties for card detect type. + - samsung,sdhci-cd-internal: Card detect line from the card slot is + connected to the card detect pad of the sdhci controller. A gpio is + used for this connection (with possible pin function settings). + - samsung,sdhci-cd-gpio: A gpio line (with possible pin function settings) + is used a card detect line. This gpio line is not connected to card detect + pad of the sdhci controller. + - samsung,sdhci-cd-none: There is no card detect line. Polling is used to + detect the presence of the card. (DEFAULT, if no card detect property + is specified). + - samsung,sdhci-cd-permanent: There is no card detect line. The card is + permanently connected to the sdhci controller. + +- gpio-cd: The gpio to be used as card detect line for + 'samsung,sdhci-cd-internal' or 'samsung,sdhci-cd-gpio' card detection method. + The gpio specifier format depends on the gpio controller. + +Example: + sdhci@12530000 { + compatible = "samsung,exynos4210-sdhci"; + reg = <0x12530000 0x100>; + interrupts = <139>; + samsung,sdhci-bus-width = <4>; + samsung,sdhci-cd-internal; + gpio-cd = <&gpk2 2 2 3 3>; + gpios = <&gpk2 0 2 0 3>, /* clock line */ + <&gpk2 1 2 0 3>, /* command line */ + <&gpk2 3 2 3 3>, /* data line 0 */ + <&gpk2 4 2 3 3>, /* data line 1 */ + <&gpk2 5 2 3 3>, /* data line 2 */ + <&gpk2 6 2 3 3>; /* data line 3 */ + }; + + Note: This example shows both SoC specific and board specific properties + in a single device node. The properties can be actually be seperated + into SoC specific node and board specific node. diff --git a/Documentation/devicetree/bindings/regulator/max8997-pmic.txt b/Documentation/devicetree/bindings/regulator/max8997-pmic.txt new file mode 100644 index 000000000000..90a730bb0e95 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/max8997-pmic.txt @@ -0,0 +1,133 @@ +* Maxim MAX8997 Voltage and Current Regulator + +The Maxim MAX8997 is a multi-function device which includes volatage and +current regulators, rtc, charger controller and other sub-blocks. It is +interfaced to the host controller using a i2c interface. Each sub-block is +addressed by the host system using different i2c slave address. This document +describes the bindings for 'pmic' sub-block of max8997. + +Required properties: +- compatible: Should be "maxim,max8997-pmic". +- reg: Specifies the i2c slave address of the pmic block. It should be 0x66. + +- max8997,pmic-buck1-dvs-voltage: A set of 8 voltage values in micro-volt (uV) + units for buck1 when changing voltage using gpio dvs. Refer to [1] below + for additional information. + +- max8997,pmic-buck2-dvs-voltage: A set of 8 voltage values in micro-volt (uV) + units for buck2 when changing voltage using gpio dvs. Refer to [1] below + for additional information. + +- max8997,pmic-buck5-dvs-voltage: A set of 8 voltage values in micro-volt (uV) + units for buck5 when changing voltage using gpio dvs. Refer to [1] below + for additional information. + +[1] If none of the 'max8997,pmic-buck[1/2/5]-uses-gpio-dvs' optional + property is specified, the 'max8997,pmic-buck[1/2/5]-dvs-voltage' + property should specify atleast one voltage level (which would be a + safe operating voltage). + + If either of the 'max8997,pmic-buck[1/2/5]-uses-gpio-dvs' optional + property is specified, then all the eigth voltage values for the + 'max8997,pmic-buck[1/2/5]-dvs-voltage' should be specified. + +Optional properties: +- interrupt-parent: Specifies the phandle of the interrupt controller to which + the interrupts from max8997 are delivered to. +- interrupts: Interrupt specifiers for two interrupt sources. + - First interrupt specifier is for 'irq1' interrupt. + - Second interrupt specifier is for 'alert' interrupt. +- max8997,pmic-buck1-uses-gpio-dvs: 'buck1' can be controlled by gpio dvs. +- max8997,pmic-buck2-uses-gpio-dvs: 'buck2' can be controlled by gpio dvs. +- max8997,pmic-buck5-uses-gpio-dvs: 'buck5' can be controlled by gpio dvs. + +Additional properties required if either of the optional properties are used: +- max8997,pmic-ignore-gpiodvs-side-effect: When GPIO-DVS mode is used for + multiple bucks, changing the voltage value of one of the bucks may affect + that of another buck, which is the side effect of the change (set_voltage). + Use this property to ignore such side effects and change the voltage. + +- max8997,pmic-buck125-default-dvs-idx: Default voltage setting selected from + the possible 8 options selectable by the dvs gpios. The value of this + property should be between 0 and 7. If not specified or if out of range, the + default value of this property is set to 0. + +- max8997,pmic-buck125-dvs-gpios: GPIO specifiers for three host gpio's used + for dvs. The format of the gpio specifier depends in the gpio controller. + + +Regulators: The regulators of max8997 that have to be instantiated should be +included in a sub-node named 'regulators'. Regulator nodes included in this +sub-node should be of the format as below. Note: The 'n' in LDOn and BUCKn +represents the LDO or BUCK number as per the datasheet of max8997. + + For LDO's: + LDOn { + standard regulator bindings here + }; + + For BUCK's: + BUCKn { + standard regulator bindings here + }; + +The bindings inside the regulator nodes use the standard regulator bindings +which are documented elsewhere. + +Example: + + max8997_pmic@66 { + compatible = "maxim,max8997-pmic"; + interrupt-parent = <&wakeup_eint>; + reg = <0x66>; + interrupts = <4 0>, <3 0>; + + max8997,pmic-buck1-uses-gpio-dvs; + max8997,pmic-buck2-uses-gpio-dvs; + max8997,pmic-buck5-uses-gpio-dvs; + + max8997,pmic-ignore-gpiodvs-side-effect; + max8997,pmic-buck125-default-dvs-idx = <0>; + + max8997,pmic-buck125-dvs-gpios = <&gpx0 0 1 0 0>, /* SET1 */ + <&gpx0 1 1 0 0>, /* SET2 */ + <&gpx0 2 1 0 0>; /* SET3 */ + + max8997,pmic-buck1-dvs-voltage = <1350000>, <1300000>, + <1250000>, <1200000>, + <1150000>, <1100000>, + <1000000>, <950000>; + + max8997,pmic-buck2-dvs-voltage = <1100000>, <1100000>, + <1100000>, <1100000>, + <1000000>, <1000000>, + <1000000>, <1000000>; + + max8997,pmic-buck5-dvs-voltage = <1200000>, <1200000>, + <1200000>, <1200000>, + <1200000>, <1200000>, + <1200000>, <1200000>; + + regulators { + ldo1_reg: LDO1 { + regulator-name = "VDD_ABB_3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + ldo2_reg: LDO2 { + regulator-name = "VDD_ALIVE_1.1V"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + regulator-always-on; + }; + + buck1_reg: BUCK1 { + regulator-name = "VDD_ARM_1.2V"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-boot-on; + }; + }; + }; diff --git a/arch/arm/boot/dts/exynos4210-origen.dts b/arch/arm/boot/dts/exynos4210-origen.dts index b8c476384eef..75a9df50ab4d 100644 --- a/arch/arm/boot/dts/exynos4210-origen.dts +++ b/arch/arm/boot/dts/exynos4210-origen.dts @@ -55,6 +55,66 @@ <&gpk0 6 2 3 3>; }; + lcd_fimd0: lcd_panel0 { + compatible = "lcd-powercontrol"; + vcc-lcd-supply = <&buck7_reg>; + lcd-reset-gpio = <&gpe3 4 1 0 0>; + lcd-htiming = <64 16 48 1024>; + lcd-vtiming = <64 16 3 600>; + }; + + fimd@11C00000 { + samsung,fimd-display = <&lcd_fimd0>; + samsung,fimd-vidout-rgb; + samsung,fimd-inv-hsync; + samsung,fimd-inv-vsync; + samsung,fimd-inv-vclk; + samsung,fimd-frame-rate = <60>; + + gpios = <&gpf0 0 2 0 0>, + <&gpf0 1 2 0 0>, + <&gpf0 2 2 0 0>, + <&gpf0 3 2 0 0>, + <&gpf0 4 2 0 0>, + <&gpf0 5 2 0 0>, + <&gpf0 6 2 0 0>, + <&gpf0 7 2 0 0>, + <&gpf1 0 2 0 0>, + <&gpf1 1 2 0 0>, + <&gpf1 2 2 0 0>, + <&gpf1 3 2 0 0>, + <&gpf1 4 2 0 0>, + <&gpf1 5 2 0 0>, + <&gpf1 6 2 0 0>, + <&gpf1 7 2 0 0>, + <&gpf2 0 2 0 0>, + <&gpf2 1 2 0 0>, + <&gpf2 2 2 0 0>, + <&gpf2 3 2 0 0>, + <&gpf2 4 2 0 0>, + <&gpf2 5 2 0 0>, + <&gpf2 6 2 0 0>, + <&gpf2 7 2 0 0>, + <&gpf3 0 2 0 0>, + <&gpf3 1 2 0 0>, + <&gpf3 2 2 0 0>, + <&gpf3 3 2 0 0>; + + window0 { + samsung,fimd-win-id = <0>; + samsung,fimd-win-bpp = <32 24>; + samsung,fimd-win-res = <1024 600>; + samsung,fimd-win-vres = <1024 600>; + }; + + window1 { + samsung,fimd-win-id = <1>; + samsung,fimd-win-bpp = <32 24>; + samsung,fimd-win-res = <1024 600>; + samsung,fimd-win-vres = <1024 600>; + }; + }; + gpio_keys { compatible = "gpio-keys"; #address-cells = <1>; @@ -104,7 +164,89 @@ }; i2c@13860000 { - status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <20000>; + gpios = <&gpd1 0 2 3 0>, + <&gpd1 1 2 3 0>; + + max8997_pmic@66 { + compatible = "maxim,max8997-pmic"; + interrupt-parent = <&wakeup_eint>; + reg = <0x66>; + interrupts = <4 0>, <3 0>; + + max8997,pmic-buck1-uses-gpio-dvs; + max8997,pmic-buck2-uses-gpio-dvs; + max8997,pmic-buck5-uses-gpio-dvs; + max8997,pmic-ignore-gpiodvs-side-effect; + max8997,pmic-buck125-default-dvs-idx = <0>; + max8997,pmic-buck125-dvs-gpios = <&gpx0 0 1 0 0>, /* SET1 */ + <&gpx0 1 1 0 0>, /* SET2 */ + <&gpx0 2 1 0 0>; /* SET3 */ + max8997,pmic-buck1-dvs-voltage = <1350000>, <1300000>, + <1250000>, <1200000>, + <1150000>, <1100000>, + <1000000>, <950000>; + max8997,pmic-buck2-dvs-voltage = <1100000>, <1100000>, + <1100000>, <1100000>, + <1000000>, <1000000>, + <1000000>, <1000000>; + max8997,pmic-buck5-dvs-voltage = <1200000>, <1200000>, + <1200000>, <1200000>, + <1200000>, <1200000>, + <1200000>, <1200000>; + + regulators { + ldo1_reg: LDO1 { + regulator-name = "VDD_ABB_3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + ldo2_reg: LDO2 { + regulator-name = "VDD_ALIVE_1.1V"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + regulator-always-on; + }; + + ldo3_reg: LDO3 { + regulator-name = "VMIPI_1.1V"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + }; + + ldo4_reg: LDO4 { + regulator-name = "VDD_RTC_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo6_reg: LDO6 { + regulator-name = "VMIPI_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo7_reg: LDO7 { + regulator-name = "VDD_AUD_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + buck7_reg: BUCK7 { + regulator-name = "vcc-lcd"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + }; + }; + }; }; i2c@13870000 { @@ -134,4 +276,40 @@ i2c@138D0000 { status = "disabled"; }; + + pd_cam: pd-cam { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023C00 0x10>; + }; + + pd_tv: pd-tv { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023C20 0x10>; + }; + + pd_mfc: pd-mfc { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023C40 0x10>; + }; + + pd_g3d: pd-g3d { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023C60 0x10>; + }; + + pd_lcd0: pd-lcd0 { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023C80 0x10>; + }; + + pd_lcd1: pd-lcd1 { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023CA0 0x10>; + }; + + pd_gps: pd-gps { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023CE0 0x10>; + samsung,exynos4210-pd_off; + }; }; diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi index a1dd2ee83753..c33de8ae88f3 100644 --- a/arch/arm/boot/dts/exynos4210.dtsi +++ b/arch/arm/boot/dts/exynos4210.dtsi @@ -33,6 +33,42 @@ reg = <0x10490000 0x1000>, <0x10480000 0x100>; }; + combiner:interrupt-controller@10440000 { + compatible = "samsung,exynos4210-combiner"; + #interrupt-cells = <2>; + interrupt-controller; + samsung,combiner-nr = <16>; + reg = <0x10440000 0x1000>; + interrupts = <0 0 0>, <0 1 0>, <0 2 0>, <0 3 0>, + <0 4 0>, <0 5 0>, <0 6 0>, <0 7 0>, + <0 8 0>, <0 9 0>, <0 10 0>, <0 11 0>, + <0 12 0>, <0 13 0>, <0 14 0>, <0 15 0>; + }; + + wakeup_eint: interrupt-controller-wakeup-eint { + compatible = "samsung,exynos4210-wakeup-eint"; + reg = <0x11000000 0x1000>; + #interrupt-cells = <2>; + interrupt-controller; + interrupts = <0 16 0>, <0 17 0>, <0 18 0>, <0 19 0>, + <0 20 0>, <0 21 0>, <0 22 0>, <0 23 0>, + <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>, + <0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>, + <0 32 0>; + }; + + power-domain-lcd0 { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023C00 0x10>; + }; + + fimd@11C00000 { + compatible = "samsung,exynos4210-fimd"; + interrupt-parent = <&combiner>; + reg = <0x11C00000 0x8000>; + interrupts = <11 1>, <11 0>, <11 2>; + }; + watchdog@10060000 { compatible = "samsung,s3c2410-wdt"; reg = <0x10060000 0x100>; diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index cb4b8602d0df..f7cabdb9168c 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -409,6 +409,8 @@ config MACH_EXYNOS4_DT select USE_OF select ARM_AMBA select HAVE_SAMSUNG_KEYPAD if INPUT_KEYBOARD + select SAMSUNG_DEV_BACKLIGHT + select SAMSUNG_DEV_PWM help Machine support for Samsung Exynos4 machine with device tree enabled. Select this if a fdt blob is available for the Exynos4 SoC based board. diff --git a/arch/arm/mach-exynos/common.c b/arch/arm/mach-exynos/common.c index 39a95f311736..29a2f9901b0d 100644 --- a/arch/arm/mach-exynos/common.c +++ b/arch/arm/mach-exynos/common.c @@ -64,6 +64,8 @@ static void exynos4_init_clocks(int xtal); static void exynos5_init_clocks(int xtal); static void exynos_init_uarts(struct s3c2410_uartcfg *cfg, int no); static int exynos_init(void); +static int exynos_init_irq_eint(struct device_node *np, + struct device_node *parent); static struct cpu_table cpu_ids[] __initdata = { { @@ -602,6 +604,8 @@ static const struct of_device_id exynos4_dt_irq_match[] = { { .compatible = "arm,cortex-a9-gic", .data = gic_of_init, }, { .compatible = "samsung,exynos4210-combiner", .data = combiner_of_init, }, + { .compatible = "samsung,exynos4210-wakeup-eint", + .data = exynos_init_irq_eint, }, {}, }; #endif @@ -619,8 +623,10 @@ void __init exynos4_init_irq(void) of_irq_init(exynos4_dt_irq_match); #endif - if (!of_have_populated_dt()) + if (!of_have_populated_dt()) { combiner_init(S5P_VA_COMBINER_BASE, NULL); + exynos_init_irq_eint(NULL, NULL); + } /* * The parameters of s5p_init_irq() are for VIC init. @@ -632,10 +638,31 @@ void __init exynos4_init_irq(void) void __init exynos5_init_irq(void) { + int irq; + struct device_node *np; + #ifdef CONFIG_OF of_irq_init(exynos4_dt_irq_match); #endif /* + * The Exynos5 wakeup interrupt controller has two-interrupt parents, + * gic and combiner. Hence, a interrupt nexus node is used to translate + * interrupt specifers for the interrupts which wakeup interrupt + * controller deliver to the gic and combiner. + * + * When using a interrupt nexus node (which is child node of the wakeup + * controller node), the interrupt parent of the wakeup controller node + * is set as the nexus node and the nexus node does not have a + * 'interrupt-controller' property. Hence, the of_irq_init function + * will not be able to invoke the intializer function for wakeup + * interrupt controller. So call the initiazer explicitly here. + */ + np = of_find_compatible_node(NULL, NULL, + "samsung,exynos5210-wakeup-eint"); + if (np) + exynos_init_irq_eint(np, NULL); + + /* * The parameters of s5p_init_irq() are for VIC init. * Theses parameters should be NULL and 0 because EXYNOS4 * uses GIC instead of VIC. @@ -766,6 +793,9 @@ static DEFINE_SPINLOCK(eint_lock); static unsigned int eint0_15_data[16]; +#define EXYNOS_EINT_NR 32 +static struct irq_domain *irq_domain; + static inline int exynos4_irq_to_gpio(unsigned int irq) { if (irq < IRQ_EINT(0)) @@ -856,9 +886,9 @@ static inline void exynos_irq_eint_mask(struct irq_data *data) u32 mask; spin_lock(&eint_lock); - mask = __raw_readl(EINT_MASK(exynos_eint_base, data->irq)); - mask |= EINT_OFFSET_BIT(data->irq); - __raw_writel(mask, EINT_MASK(exynos_eint_base, data->irq)); + mask = __raw_readl(EINT_MASK(exynos_eint_base, data->hwirq)); + mask |= EINT_OFFSET_BIT(data->hwirq); + __raw_writel(mask, EINT_MASK(exynos_eint_base, data->hwirq)); spin_unlock(&eint_lock); } @@ -867,16 +897,16 @@ static void exynos_irq_eint_unmask(struct irq_data *data) u32 mask; spin_lock(&eint_lock); - mask = __raw_readl(EINT_MASK(exynos_eint_base, data->irq)); - mask &= ~(EINT_OFFSET_BIT(data->irq)); - __raw_writel(mask, EINT_MASK(exynos_eint_base, data->irq)); + mask = __raw_readl(EINT_MASK(exynos_eint_base, data->hwirq)); + mask &= ~(EINT_OFFSET_BIT(data->hwirq)); + __raw_writel(mask, EINT_MASK(exynos_eint_base, data->hwirq)); spin_unlock(&eint_lock); } static inline void exynos_irq_eint_ack(struct irq_data *data) { - __raw_writel(EINT_OFFSET_BIT(data->irq), - EINT_PEND(exynos_eint_base, data->irq)); + __raw_writel(EINT_OFFSET_BIT(data->hwirq), + EINT_PEND(exynos_eint_base, data->hwirq)); } static void exynos_irq_eint_maskack(struct irq_data *data) @@ -887,7 +917,7 @@ static void exynos_irq_eint_maskack(struct irq_data *data) static int exynos_irq_eint_set_type(struct irq_data *data, unsigned int type) { - int offs = EINT_OFFSET(data->irq); + int offs = data->hwirq; int shift; u32 ctrl, mask; u32 newvalue = 0; @@ -922,10 +952,10 @@ static int exynos_irq_eint_set_type(struct irq_data *data, unsigned int type) mask = 0x7 << shift; spin_lock(&eint_lock); - ctrl = __raw_readl(EINT_CON(exynos_eint_base, data->irq)); + ctrl = __raw_readl(EINT_CON(exynos_eint_base, data->hwirq)); ctrl &= ~mask; ctrl |= newvalue << shift; - __raw_writel(ctrl, EINT_CON(exynos_eint_base, data->irq)); + __raw_writel(ctrl, EINT_CON(exynos_eint_base, data->hwirq)); spin_unlock(&eint_lock); if (soc_is_exynos5250()) @@ -969,7 +999,7 @@ static inline void exynos_irq_demux_eint(unsigned int start) while (status) { irq = fls(status) - 1; - generic_handle_irq(irq + start); + generic_handle_irq(irq_find_mapping(irq_domain, irq + start)); status &= ~(1 << irq); } } @@ -978,8 +1008,8 @@ static void exynos_irq_demux_eint16_31(unsigned int irq, struct irq_desc *desc) { struct irq_chip *chip = irq_get_chip(irq); chained_irq_enter(chip, desc); - exynos_irq_demux_eint(IRQ_EINT(16)); - exynos_irq_demux_eint(IRQ_EINT(24)); + exynos_irq_demux_eint(16); + exynos_irq_demux_eint(24); chained_irq_exit(chip, desc); } @@ -987,6 +1017,7 @@ static void exynos_irq_eint0_15(unsigned int irq, struct irq_desc *desc) { u32 *irq_data = irq_get_handler_data(irq); struct irq_chip *chip = irq_get_chip(irq); + int eint_irq; chained_irq_enter(chip, desc); chip->irq_mask(&desc->irq_data); @@ -994,50 +1025,68 @@ static void exynos_irq_eint0_15(unsigned int irq, struct irq_desc *desc) if (chip->irq_ack) chip->irq_ack(&desc->irq_data); - generic_handle_irq(*irq_data); + eint_irq = irq_find_mapping(irq_domain, *irq_data); + generic_handle_irq(eint_irq); chip->irq_unmask(&desc->irq_data); chained_irq_exit(chip, desc); } -static int __init exynos_init_irq_eint(void) +static int exynos_eint_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) { - int irq; + irq_set_chip_and_handler(irq, &exynos_irq_eint, handle_level_irq); + set_irq_flags(irq, IRQF_VALID); + return 0; +} - if (soc_is_exynos5250()) - exynos_eint_base = ioremap(EXYNOS5_PA_GPIO1, SZ_4K); - else - exynos_eint_base = ioremap(EXYNOS4_PA_GPIO2, SZ_4K); +static struct irq_domain_ops exynos_eint_irq_domain_ops = { + .map = exynos_eint_irq_domain_map, +}; + +static int __init exynos_init_irq_eint(struct device_node *np, + struct device_node *parent) +{ + int irq, *src_int, irq_base, irq_eint; + unsigned int paddr; - if (exynos_eint_base == NULL) { + if (!np) { + paddr = soc_is_exynos5250() ? EXYNOS5_PA_GPIO1 : + EXYNOS4_PA_GPIO2; + exynos_eint_base = ioremap(paddr, SZ_4K); + } else { + exynos_eint_base = of_iomap(np, 0); + } + if (!exynos_eint_base) { pr_err("unable to ioremap for EINT base address\n"); - return -ENOMEM; + return -ENXIO; } - for (irq = 0 ; irq <= 31 ; irq++) { - irq_set_chip_and_handler(IRQ_EINT(irq), &exynos_irq_eint, - handle_level_irq); - set_irq_flags(IRQ_EINT(irq), IRQF_VALID); + irq_base = irq_alloc_descs(IRQ_EINT(0), 1, EXYNOS_EINT_NR, 0); + if (IS_ERR_VALUE(irq_base)) { + irq_base = IRQ_EINT(0); + pr_warning("%s: irq desc alloc failed. Continuing with %d as " + "linux irq base\n", __func__, irq_base); } - irq_set_chained_handler(EXYNOS_IRQ_EINT16_31, exynos_irq_demux_eint16_31); - - for (irq = 0 ; irq <= 15 ; irq++) { - eint0_15_data[irq] = IRQ_EINT(irq); - - if (soc_is_exynos5250()) { - irq_set_handler_data(exynos5_eint0_15_src_int[irq], - &eint0_15_data[irq]); - irq_set_chained_handler(exynos5_eint0_15_src_int[irq], - exynos_irq_eint0_15); - } else { - irq_set_handler_data(exynos4_eint0_15_src_int[irq], - &eint0_15_data[irq]); - irq_set_chained_handler(exynos4_eint0_15_src_int[irq], - exynos_irq_eint0_15); - } + irq_domain = irq_domain_add_legacy(np, EXYNOS_EINT_NR, irq_base, 0, + &exynos_eint_irq_domain_ops, NULL); + if (WARN_ON(!irq_domain)) { + pr_warning("%s: irq domain init failed\n", __func__); + return 0; + } + + irq_eint = np ? irq_of_parse_and_map(np, 16) : EXYNOS_IRQ_EINT16_31; + irq_set_chained_handler(irq_eint, exynos_irq_demux_eint16_31); + + for (irq = 0 ; irq <= 15; irq++) { + eint0_15_data[irq] = irq; + src_int = soc_is_exynos5250() ? exynos5_eint0_15_src_int : + exynos4_eint0_15_src_int; + irq_eint = np ? irq_of_parse_and_map(np, irq) : src_int[irq]; + irq_set_handler_data(irq_eint, &eint0_15_data[irq]); + irq_set_chained_handler(irq_eint, exynos_irq_eint0_15); } return 0; } -arch_initcall(exynos_init_irq_eint); diff --git a/arch/arm/mach-exynos/include/mach-exynos/regs-gpio.h b/arch/arm/mach-exynos/include/mach-exynos/regs-gpio.h index 077ebfd55f6f..31178953df19 100644 --- a/arch/arm/mach-exynos/include/mach-exynos/regs-gpio.h +++ b/arch/arm/mach-exynos/include/mach-exynos/regs-gpio.h @@ -16,13 +16,13 @@ #include <mach-exynos/map.h> #include <mach-exynos/irqs.h> -#define EINT_REG_NR(x) (EINT_OFFSET(x) >> 3) +#define EINT_REG_NR(x) ((x) >> 3) #define EINT_CON(b, x) (b + 0xE00 + (EINT_REG_NR(x) * 4)) #define EINT_FLTCON(b, x) (b + 0xE80 + (EINT_REG_NR(x) * 4)) #define EINT_MASK(b, x) (b + 0xF00 + (EINT_REG_NR(x) * 4)) #define EINT_PEND(b, x) (b + 0xF40 + (EINT_REG_NR(x) * 4)) -#define EINT_OFFSET_BIT(x) (1 << (EINT_OFFSET(x) & 0x7)) +#define EINT_OFFSET_BIT(x) (1 << ((x) & 0x7)) /* compatibility for plat-s5p/irq-pm.c */ #define EXYNOS4_EINT40CON (S5P_VA_GPIO2 + 0xE00) diff --git a/arch/arm/mach-exynos/mach-exynos4-dt.c b/arch/arm/mach-exynos/mach-exynos4-dt.c index 03efe1be3cad..c0ace48f70ad 100644 --- a/arch/arm/mach-exynos/mach-exynos4-dt.c +++ b/arch/arm/mach-exynos/mach-exynos4-dt.c @@ -14,6 +14,16 @@ #include <linux/of_platform.h> #include <linux/serial_core.h> +#include <linux/pwm_backlight.h> +#include <plat/backlight.h> +#include <plat/gpio-cfg.h> +#include <linux/gpio.h> +#include <plat/regs-fb-v4.h> +#include <linux/fb.h> +#include <plat/fb.h> +#include <linux/lcd.h> +#include <plat/devs.h> + #include <asm/mach/arch.h> #include <asm/hardware/gic.h> #include <mach-exynos/map.h> @@ -23,6 +33,17 @@ #include "common.h" +/* LCD Backlight data */ +static struct samsung_bl_gpio_info origen_bl_gpio_info = { + .no = EXYNOS4_GPD0(0), + .func = S3C_GPIO_SFN(2), +}; + +static struct platform_pwm_backlight_data origen_bl_data = { + .pwm_id = 0, + .pwm_period_ns = 1000, +}; + /* * The following lookup table is used to override device names when devices * are registered from device tree. This is temporarily added to enable @@ -57,6 +78,8 @@ static const struct of_dev_auxdata exynos4210_auxdata_lookup[] __initconst = { "s3c2440-i2c.0", NULL), OF_DEV_AUXDATA("arm,pl330", EXYNOS4_PA_PDMA0, "dma-pl330.0", NULL), OF_DEV_AUXDATA("arm,pl330", EXYNOS4_PA_PDMA1, "dma-pl330.1", NULL), + OF_DEV_AUXDATA("samsung,exynos4210-fimd", EXYNOS4_PA_FIMD0, + "exynos4-fb.0", NULL), {}, }; @@ -66,10 +89,21 @@ static void __init exynos4210_dt_map_io(void) s3c24xx_init_clocks(24000000); } +static void __init exynos4_setup_fimd(void) +{ + unsigned int reg; + + reg = __raw_readl(S3C_VA_SYS + 0x0210); + reg |= (1 << 1); + __raw_writel(reg, S3C_VA_SYS + 0x0210); +} + static void __init exynos4210_dt_machine_init(void) { of_platform_populate(NULL, of_default_bus_match_table, exynos4210_auxdata_lookup, NULL); + samsung_bl_set(&origen_bl_gpio_info, &origen_bl_data); + exynos4_setup_fimd(); } static char const *exynos4210_dt_compat[] __initdata = { diff --git a/arch/arm/mach-exynos/mach-nuri.c b/arch/arm/mach-exynos/mach-nuri.c index 9bef191bfc5e..74e16f51b5a0 100644 --- a/arch/arm/mach-exynos/mach-nuri.c +++ b/arch/arm/mach-exynos/mach-nuri.c @@ -1067,12 +1067,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)); diff --git a/arch/arm/mach-exynos/mach-origen.c b/arch/arm/mach-exynos/mach-origen.c index 06e2b7782e86..55fbf2a4f435 100644 --- a/arch/arm/mach-exynos/mach-origen.c +++ b/arch/arm/mach-exynos/mach-origen.c @@ -425,7 +425,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, diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c index f08197f7cde0..c90ff16896b6 100644 --- a/arch/arm/mach-exynos/pm_domains.c +++ b/arch/arm/mach-exynos/pm_domains.c @@ -200,6 +200,7 @@ arch_initcall(exynos4_pm_init_power_domain); int __init exynos_pm_late_initcall(void) { - pm_genpd_poweroff_unused(); + if (!of_have_populated_dt()) + pm_genpd_poweroff_unused(); return 0; } diff --git a/drivers/mfd/max8997-irq.c b/drivers/mfd/max8997-irq.c index 09274cf7c33b..00390a117ae6 100644 --- a/drivers/mfd/max8997-irq.c +++ b/drivers/mfd/max8997-irq.c @@ -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,18 +345,11 @@ 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; } ret = request_threaded_irq(max8997->irq, NULL, max8997_irq_thread, diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c index cb83a7ab53e7..1c38e98d4d2e 100644 --- a/drivers/mfd/max8997.c +++ b/drivers/mfd/max8997.c @@ -21,8 +21,10 @@ * This driver is based on max8998.c */ +#include <linux/err.h> #include <linux/slab.h> #include <linux/i2c.h> +#include <linux/of_irq.h> #include <linux/interrupt.h> #include <linux/pm_runtime.h> #include <linux/module.h> @@ -47,6 +49,13 @@ static struct mfd_cell max8997_devs[] = { { .name = "max8997-led", .id = 2 }, }; +#ifdef CONFIG_OF +static struct of_device_id __devinitdata max8997_pmic_dt_match[] = { + { .compatible = "maxim,max8997-pmic", .data = TYPE_MAX8997 }, + {}, +}; +#endif + int max8997_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest) { struct max8997_dev *max8997 = i2c_get_clientdata(i2c); @@ -123,6 +132,58 @@ int max8997_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask) } EXPORT_SYMBOL_GPL(max8997_update_reg); +#ifdef CONFIG_OF +/* + * Only the common platform data elements for max8997 are parsed here from the + * device tree. Other sub-modules of max8997 such as pmic, rtc and others have + * to parse their own platform data elements from device tree. + * + * The max8997 platform data structure is instantiated here and the drivers for + * the sub-modules need not instantiate another instance while parsing their + * platform data. + */ +static struct max8997_platform_data *max8997_i2c_parse_dt_pdata( + struct device *dev) +{ + struct max8997_platform_data *pd; + + pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); + if (!pd) { + dev_err(dev, "could not allocate memory for pdata\n"); + return ERR_PTR(-ENOMEM); + } + + pd->ono = irq_of_parse_and_map(dev->of_node, 1); + + /* + * ToDo: the 'wakeup' member in the platform data is more of a linux + * specfic information. Hence, there is no binding for that yet and + * not parsed here. + */ + + return pd; +} +#else +static int max8997_i2c_parse_dt_pdata(struct device *dev, + struct max8997_platform_data **pdata) +{ + return 0; +} +#endif + +static inline int max8997_i2c_get_driver_data(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ +#ifdef CONFIG_OF + if (i2c->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(max8997_pmic_dt_match, i2c->dev.of_node); + return (int)match->data; + } +#endif + return (int)id->driver_data; +} + static int max8997_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -137,13 +198,21 @@ static int max8997_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, max8997); max8997->dev = &i2c->dev; max8997->i2c = i2c; - max8997->type = id->driver_data; + max8997->type = max8997_i2c_get_driver_data(i2c, id); max8997->irq = i2c->irq; + if (max8997->dev->of_node) { + pdata = max8997_i2c_parse_dt_pdata(max8997->dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto err; + } + } + if (!pdata) goto err; - max8997->irq_base = pdata->irq_base; + max8997->pdata = pdata; max8997->ono = pdata->ono; mutex_init(&max8997->iolock); @@ -435,6 +504,7 @@ static struct i2c_driver max8997_i2c_driver = { .name = "max8997", .owner = THIS_MODULE, .pm = &max8997_pm, + .of_match_table = of_match_ptr(max8997_pmic_dt_match), }, .probe = max8997_i2c_probe, .remove = max8997_i2c_remove, diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index ef44fae5f3ee..cf3e8ec8a3f0 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -33,6 +33,8 @@ #include "sdhci.h" #define MAX_BUS_CLK (4) +/* Number of gpio's used is max data bus width + command and clock lines */ +#define NUM_GPIOS(x) (x + 2) /** * struct sdhci_s3c - S3C SDHCI instance @@ -41,6 +43,7 @@ * @ioarea: The resource created when we claimed the IO area. * @pdata: The platform data for this controller. * @cur_clk: The index of the current bus clock. + * @gpios: List of gpio numbers parsed from device tree. * @clk_io: The clock for the internal bus interface. * @clk_bus: The clocks that are available for the SD/MMC bus clock. */ @@ -52,6 +55,7 @@ struct sdhci_s3c { unsigned int cur_clk; int ext_cd_irq; int ext_cd_gpio; + int *gpios; struct clk *clk_io; struct clk *clk_bus[MAX_BUS_CLK]; @@ -419,9 +423,110 @@ static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc) } } +#ifdef CONFIG_OF +static int __devinit sdhci_s3c_parse_dt(struct device *dev, + struct sdhci_host *host, struct s3c_sdhci_platdata *pdata) +{ + struct device_node *node = dev->of_node; + struct sdhci_s3c *ourhost = to_s3c(host); + u32 max_width; + int gpio, cnt, ret; + + /* if the bus-width property is not specified, assume width as 1 */ + if (of_property_read_u32(node, "samsung,sdhci-bus-width", + &max_width)) + max_width = 1; + pdata->max_width = max_width; + + ourhost->gpios = devm_kzalloc(dev, NUM_GPIOS(pdata->max_width) * + sizeof(int), GFP_KERNEL); + if (!ourhost->gpios) + return -ENOMEM; + + /* get the card detection method */ + if (of_get_property(node, "samsung,sdhci-cd-internal", NULL)) + pdata->cd_type = S3C_SDHCI_CD_INTERNAL; + else if (of_get_property(node, "samsung,sdhci-cd-gpio", NULL)) + pdata->cd_type = S3C_SDHCI_CD_GPIO; + else if (of_get_property(node, "samsung,sdhci-cd-none", NULL)) + pdata->cd_type = S3C_SDHCI_CD_NONE; + else if (of_get_property(node, "samsung,sdhci-cd-permanent", NULL)) + pdata->cd_type = S3C_SDHCI_CD_PERMANENT; + else + pdata->cd_type = S3C_SDHCI_CD_NONE; + + /* get the gpio used for card detection */ + if (pdata->cd_type == S3C_SDHCI_CD_GPIO || + pdata->cd_type == S3C_SDHCI_CD_INTERNAL) { + gpio = of_get_named_gpio(node, "gpio-cd", 0); + if (!gpio_is_valid(gpio)) { + dev_err(dev, "invalid card detect gpio specified\n"); + return -EINVAL; + } + } + + if (pdata->cd_type == S3C_SDHCI_CD_GPIO) { + pdata->ext_cd_gpio = gpio; + ourhost->ext_cd_gpio = -1; /* invalid gpio number */ + if (of_get_property(node, "samsung,cd-gpio-invert", NULL)) + pdata->ext_cd_gpio_invert = 1; + } else if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL) { + ret = gpio_request(gpio, "sdhci-cd"); + if (ret) { + dev_err(dev, "card detect gpio request failed\n"); + return -EINVAL; + } + ourhost->ext_cd_gpio = gpio; + } + + /* get the gpios for command, clock and data lines */ + for (cnt = 0; cnt < NUM_GPIOS(pdata->max_width); cnt++) { + gpio = of_get_gpio(node, cnt); + if (!gpio_is_valid(gpio)) { + dev_err(dev, "invalid gpio[%d]\n", cnt); + goto err_free_dt_cd_gpio; + } + ourhost->gpios[cnt] = gpio; + } + + for (cnt = 0; cnt < NUM_GPIOS(pdata->max_width); cnt++) { + ret = gpio_request(ourhost->gpios[cnt], "sdhci-gpio"); + if (ret) { + dev_err(dev, "gpio[%d] request failed\n", cnt); + goto err_free_dt_gpios; + } + } + + return 0; + + err_free_dt_gpios: + while (--cnt >= 0) + gpio_free(ourhost->gpios[cnt]); + err_free_dt_cd_gpio: + if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL) + gpio_free(ourhost->ext_cd_gpio); + return -EINVAL; +} +#else +static int __devinit sdhci_s3c_parse_dt(struct device *dev, + struct sdhci_host *host, struct s3c_sdhci_platdata *pdata) +{ + return -EINVAL; +} +#endif + +static const struct of_device_id sdhci_s3c_dt_match[]; + static inline struct sdhci_s3c_drv_data *sdhci_s3c_get_driver_data( struct platform_device *pdev) { +#ifdef CONFIG_OF + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(sdhci_s3c_dt_match, pdev->dev.of_node); + return (struct sdhci_s3c_drv_data *)match->data; + } +#endif return (struct sdhci_s3c_drv_data *) platform_get_device_id(pdev)->driver_data; } @@ -436,7 +541,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) struct resource *res; int ret, irq, ptr, clks; - if (!pdev->dev.platform_data) { + if (!pdev->dev.platform_data && !pdev->dev.of_node) { dev_err(dev, "no device data specified\n"); return -ENOENT; } @@ -452,21 +557,28 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) dev_err(dev, "sdhci_alloc_host() failed\n"); return PTR_ERR(host); } + sc = sdhci_priv(host); pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) { ret = -ENOMEM; - goto err_io_clk; + goto err_pdata; + } + + if (pdev->dev.of_node) { + ret = sdhci_s3c_parse_dt(&pdev->dev, host, pdata); + if (ret) + goto err_pdata; + } else { + memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata)); + sc->ext_cd_gpio = -1; /* invalid gpio number */ } - memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata)); drv_data = sdhci_s3c_get_driver_data(pdev); - sc = sdhci_priv(host); sc->host = host; sc->pdev = pdev; sc->pdata = pdata; - sc->ext_cd_gpio = -1; /* invalid gpio number */ platform_set_drvdata(pdev, host); @@ -631,6 +743,12 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) clk_put(sc->clk_io); err_io_clk: + for (ptr = 0; ptr < NUM_GPIOS(sc->pdata->max_width); ptr++) + gpio_free(sc->gpios[ptr]); + if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL) + gpio_free(sc->ext_cd_gpio); + + err_pdata: sdhci_free_host(host); return ret; @@ -638,9 +756,9 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) static int __devexit sdhci_s3c_remove(struct platform_device *pdev) { - struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data; struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_s3c *sc = sdhci_priv(host); + struct s3c_sdhci_platdata *pdata = sc->pdata; int ptr; if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_cleanup) @@ -665,6 +783,15 @@ static int __devexit sdhci_s3c_remove(struct platform_device *pdev) clk_disable(sc->clk_io); clk_put(sc->clk_io); + iounmap(host->ioaddr); + release_resource(sc->ioarea); + kfree(sc->ioarea); + + if (pdev->dev.of_node) { + for (ptr = 0; ptr < NUM_GPIOS(sc->pdata->max_width); ptr++) + gpio_free(sc->gpios[ptr]); + } + sdhci_free_host(host); platform_set_drvdata(pdev, NULL); @@ -737,6 +864,16 @@ static struct platform_device_id sdhci_s3c_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, sdhci_s3c_driver_ids); +#ifdef CONFIG_OF +static const struct of_device_id sdhci_s3c_dt_match[] = { + { .compatible = "samsung,s3c6410-sdhci", }, + { .compatible = "samsung,exynos4210-sdhci", + .data = &exynos4_sdhci_drv_data }, + {}, +}; +MODULE_DEVICE_TABLE(of, sdhci_s3c_dt_match); +#endif + static struct platform_driver sdhci_s3c_driver = { .probe = sdhci_s3c_probe, .remove = __devexit_p(sdhci_s3c_remove), @@ -744,6 +881,7 @@ static struct platform_driver sdhci_s3c_driver = { .driver = { .owner = THIS_MODULE, .name = "s3c-sdhci", + .of_match_table = of_match_ptr(sdhci_s3c_dt_match), .pm = SDHCI_S3C_PMOPS, }, }; diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997.c index 704cd49ef375..9f6ee821da15 100644 --- a/drivers/regulator/max8997.c +++ b/drivers/regulator/max8997.c @@ -24,6 +24,7 @@ #include <linux/bug.h> #include <linux/err.h> #include <linux/gpio.h> +#include <linux/of_gpio.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -31,6 +32,7 @@ #include <linux/regulator/machine.h> #include <linux/mfd/max8997.h> #include <linux/mfd/max8997-private.h> +#include <linux/regulator/of_regulator.h> struct max8997_data { struct device *dev; @@ -933,10 +935,138 @@ static struct regulator_desc regulators[] = { max8997_charger_fixedstate_ops), }; +#ifdef CONFIG_OF +static int max8997_pmic_dt_parse_dvs_gpio(struct max8997_dev *iodev, + struct max8997_platform_data *pdata, + struct device_node *pmic_np) +{ + int i, gpio; + + for (i = 0; i < 3; i++) { + gpio = of_get_named_gpio(pmic_np, + "max8997,pmic-buck125-dvs-gpios", i); + if (!gpio_is_valid(gpio)) { + dev_err(iodev->dev, "invalid gpio[%d]: %d\n", i, gpio); + return -EINVAL; + } + pdata->buck125_gpios[i] = gpio; + } + return 0; +} + +static int max8997_pmic_dt_parse_pdata(struct max8997_dev *iodev, + struct max8997_platform_data *pdata) +{ + struct device_node *pmic_np, *regulators_np, *reg_np; + struct max8997_regulator_data *rdata; + unsigned int i, dvs_voltage_nr = 1, ret; + + pmic_np = iodev->dev->of_node; + if (!pmic_np) { + dev_err(iodev->dev, "could not find pmic sub-node\n"); + return -ENODEV; + } + + regulators_np = of_find_node_by_name(pmic_np, "regulators"); + if (!regulators_np) { + dev_err(iodev->dev, "could not find regulators sub-node\n"); + return -EINVAL; + } + + /* count the number of regulators to be supported in pmic */ + pdata->num_regulators = 0; + for_each_child_of_node(regulators_np, reg_np) + pdata->num_regulators++; + + rdata = devm_kzalloc(iodev->dev, sizeof(*rdata) * + pdata->num_regulators, GFP_KERNEL); + if (!rdata) { + dev_err(iodev->dev, "could not allocate memory for " + "regulator data\n"); + return -ENOMEM; + } + + pdata->regulators = rdata; + for_each_child_of_node(regulators_np, reg_np) { + for (i = 0; i < ARRAY_SIZE(regulators); i++) + if (!of_node_cmp(reg_np->name, regulators[i].name)) + break; + rdata->id = i; + rdata->initdata = of_get_regulator_init_data( + iodev->dev, reg_np); + rdata->reg_node = reg_np; + rdata++; + } + + if (of_get_property(pmic_np, "max8997,pmic-buck1-uses-gpio-dvs", NULL)) + pdata->buck1_gpiodvs = true; + + if (of_get_property(pmic_np, "max8997,pmic-buck2-uses-gpio-dvs", NULL)) + pdata->buck2_gpiodvs = true; + + if (of_get_property(pmic_np, "max8997,pmic-buck5-uses-gpio-dvs", NULL)) + pdata->buck5_gpiodvs = true; + + if (pdata->buck1_gpiodvs || pdata->buck2_gpiodvs || + pdata->buck5_gpiodvs) { + ret = max8997_pmic_dt_parse_dvs_gpio(iodev, pdata, pmic_np); + if (ret) + return -EINVAL; + + if (of_property_read_u32(pmic_np, + "max8997,pmic-buck125-default-dvs-idx", + &pdata->buck125_default_idx)) { + pdata->buck125_default_idx = 0; + } else { + if (pdata->buck125_default_idx >= 8) { + pdata->buck125_default_idx = 0; + dev_info(iodev->dev, "invalid value for " + "default dvs index, using 0 instead\n"); + } + } + + if (of_get_property(pmic_np, + "max8997,pmic-ignore-gpiodvs-side-effect", NULL)) + pdata->ignore_gpiodvs_side_effect = true; + + dvs_voltage_nr = 8; + } + + if (of_property_read_u32_array(pmic_np, + "max8997,pmic-buck1-dvs-voltage", + pdata->buck1_voltage, dvs_voltage_nr)) { + dev_err(iodev->dev, "buck1 voltages not specified\n"); + return -EINVAL; + } + + if (of_property_read_u32_array(pmic_np, + "max8997,pmic-buck2-dvs-voltage", + pdata->buck2_voltage, dvs_voltage_nr)) { + dev_err(iodev->dev, "buck2 voltages not specified\n"); + return -EINVAL; + } + + if (of_property_read_u32_array(pmic_np, + "max8997,pmic-buck5-dvs-voltage", + pdata->buck5_voltage, dvs_voltage_nr)) { + dev_err(iodev->dev, "buck5 voltages not specified\n"); + return -EINVAL; + } + + return 0; +} +#else +static int max8997_pmic_dt_parse_pdata(struct max8997_dev *iodev, + struct max8997_platform_data *pdata) +{ + return 0; +} +#endif /* CONFIG_OF */ + static __devinit int max8997_pmic_probe(struct platform_device *pdev) { struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); - struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev); + struct max8997_platform_data *pdata = iodev->pdata; struct regulator_config config = { }; struct regulator_dev **rdev; struct max8997_data *max8997; @@ -944,11 +1074,17 @@ static __devinit int max8997_pmic_probe(struct platform_device *pdev) int i, ret, size; u8 max_buck1 = 0, max_buck2 = 0, max_buck5 = 0; - if (!pdata) { + if (IS_ERR_OR_NULL(pdata)) { dev_err(pdev->dev.parent, "No platform init data supplied.\n"); return -ENODEV; } + if (iodev->dev->of_node) { + ret = max8997_pmic_dt_parse_pdata(iodev, pdata); + if (ret) + return ret; + } + max8997 = devm_kzalloc(&pdev->dev, sizeof(struct max8997_data), GFP_KERNEL); if (!max8997) diff --git a/include/linux/mfd/max8997-private.h b/include/linux/mfd/max8997-private.h index 3f4deb62d6b0..6ae21bf47d64 100644 --- a/include/linux/mfd/max8997-private.h +++ b/include/linux/mfd/max8997-private.h @@ -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) @@ -314,6 +316,7 @@ enum max8997_irq { #define MAX8997_NUM_GPIO 12 struct max8997_dev { struct device *dev; + struct max8997_platform_data *pdata; struct i2c_client *i2c; /* 0xcc / PMIC, Battery Control, and FLASH */ struct i2c_client *rtc; /* slave addr 0x0c */ struct i2c_client *haptic; /* slave addr 0x90 */ @@ -325,7 +328,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]; diff --git a/include/linux/mfd/max8997.h b/include/linux/mfd/max8997.h index b40c08cd30bc..1d4a4fe6ac33 100644 --- a/include/linux/mfd/max8997.h +++ b/include/linux/mfd/max8997.h @@ -75,6 +75,7 @@ enum max8998_regulators { struct max8997_regulator_data { int id; struct regulator_init_data *initdata; + struct device_node *reg_node; }; enum max8997_muic_usb_type { @@ -181,7 +182,6 @@ struct max8997_led_platform_data { struct max8997_platform_data { /* IRQ */ - int irq_base; int ono; int wakeup; |