Merge "arm/dt: 8974: Set otg_capability flag to get external VBUS notifications"
diff --git a/Documentation/devicetree/bindings/fb/mdss-edp.txt b/Documentation/devicetree/bindings/fb/mdss-edp.txt
index 4fedc72..3c4e1d3 100644
--- a/Documentation/devicetree/bindings/fb/mdss-edp.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-edp.txt
@@ -4,15 +4,20 @@
VESA EDP display interface specification.
Required properties
-- compatible : Must be "qcom,mdss-edp".
-- reg : Offset and length of the register set for the device.
-- reg-names : Names to refer to register sets related to this device
-- vdda-supply : Phandle for vdd regulator device node.
-- gpio-panel-en : GPIO for supplying power to panel and to backlight driver.
-- status : A string that has to be set to "okay/ok" to enable
- the driver. By default this property will be set to
- "disable". Will be set to "ok/okay" status for specific
- platforms.
+- compatible : Must be "qcom,mdss-edp".
+- reg : Offset and length of the register set for the
+ device.
+- reg-names : Names to refer to register sets related to this
+ device
+- vdda-supply : Phandle for vdd regulator device node.
+- gpio-panel-en : GPIO for supplying power to panel and backlight
+ driver.
+- qcom,panel-lpg-channel : LPG channel for backlight.
+- qcom,panel-pwm-period : PWM period in microseconds.
+- status : A string that has to be set to "okay/ok" to enable
+ the driver. By default this property will be set to
+ "disable". Will be set to "ok/okay" status for
+ specific platforms.
Example:
mdss_edp: qcom,mdss_edp@fd923400 {
@@ -22,6 +27,8 @@
reg-names = "edp_base", "mmss_cc_base";
vdda-supply = <&pm8941_l12>;
gpio-panel-en = <&msmgpio 58 0>;
+ qcom,panel-lpg-channel = <7>; /* LPG Channel 8 */
+ qcom,panel-pwm-period = <53>;
status = "disable";
};
diff --git a/Documentation/devicetree/bindings/power/bq28400-battery.txt b/Documentation/devicetree/bindings/power/bq28400-battery.txt
new file mode 100644
index 0000000..3879b4d
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/bq28400-battery.txt
@@ -0,0 +1,18 @@
+TI BQ28400 Battery Gas Gauge
+
+The bq28400 monitors the battery temperature, capacity, voltage, current etc.
+The device interface is I2C, its I2C slave 7-bit address is 0xb.
+The device is usually embedded inside the "smart battery" pack.
+
+node required properties:
+- compatible: Must be "ti,bq28400-battery".
+- reg: I2C Address must be 0xb.
+
+Example:
+ i2c@f9967000 {
+ battery@b {
+ compatible = "ti,bq28400-battery";
+ reg = <0xb>;
+ };
+ };
+
diff --git a/Documentation/devicetree/bindings/power/smb350.txt b/Documentation/devicetree/bindings/power/smb350.txt
new file mode 100644
index 0000000..6f21236
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/smb350.txt
@@ -0,0 +1,43 @@
+Summit smb350 battery charger
+
+The smb350 charger supports stack-cell battery charging.
+
+The smb350 interface is via I2C bus.
+The i2c slave 7-bit address is programmable at manufacture.
+
+Node required properties:
+- compatible: Must be "summit,smb350-charger".
+- reg: The device 7-bit I2C address.
+- summit,stat-gpio gpio which smb350 STAT pin connects to.
+- summit,chg-en-n-gpio gpio which control charging enable.
+- summit,chg-susp-n-gpio gpio which control device shutdown
+- summit,chg-current-ma charging current in milliamps.
+- summit,term-current-ma charging termination current in milliamps.
+ valid values are 200/300/400/500/600/700.
+ A value of zero means no termination current.
+
+Example:
+ i2c@f9967000 {
+ cell-index = <0>;
+ compatible = "qcom,i2c-qup";
+ reg = <0Xf9967000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg-names = "qup_phys_addr";
+ interrupts = <0 105 0>;
+ interrupt-names = "qup_err_intr";
+ qcom,i2c-bus-freq = <100000>;
+ qcom,i2c-src-freq = <24000000>;
+ label = "blsp_11";
+
+ smb350-charger@2b {
+ compatible = "summit,smb350-charger";
+ reg = <0x2b>; /* 0x56/0x57 */
+ summit,stat-gpio = <&pm8941_gpios 30 0x00>;
+ summit,chg-en-n-gpio = <&pm8941_gpios 10 0x00>;
+ summit,chg-susp-n-gpio = <&pm8941_gpios 13 0x00>;
+ summit,chg-current-ma = <1600>;
+ summit,term-current-ma = <200>;
+ };
+ };
+
diff --git a/arch/arm/boot/dts/mpq8092.dtsi b/arch/arm/boot/dts/mpq8092.dtsi
index 252b9f5..470d540 100644
--- a/arch/arm/boot/dts/mpq8092.dtsi
+++ b/arch/arm/boot/dts/mpq8092.dtsi
@@ -228,6 +228,47 @@
<0x1b80009e>, /* LPG_CHAN_8 */
<0x1bc0009f>; /* LPG_PWM */
};
+
+ sdcc1: qcom,sdcc@f9824000 {
+ cell-index = <1>; /* SDC1 eMMC slot */
+ compatible = "qcom,msm-sdcc";
+ reg = <0xf9824000 0x800>;
+ reg-names = "core_mem";
+ interrupts = <0 123 0>;
+ interrupt-names = "core_irq";
+
+ qcom,sdcc-pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,sdcc-pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,sdcc-pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,sdcc-pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+ qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,sdcc-sup-voltages = <2950 2950>;
+ qcom,sdcc-bus-width = <8>;
+ qcom,sdcc-nonremovable;
+ qcom,sdcc-bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
+ };
+
+ sdcc2: qcom,sdcc@f98a4000 {
+ cell-index = <2>; /* SDC2 SD card slot */
+ compatible = "qcom,msm-sdcc";
+ reg = <0xf98a4000 0x800>;
+ reg-names = "core_mem";
+ interrupts = <0 125 0>;
+ interrupt-names = "core_irq";
+
+ qcom,sdcc-pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,sdcc-pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
+ qcom,sdcc-pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
+ qcom,sdcc-pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
+
+ qcom,sdcc-clk-rates = <400000 25000000 50000000 100000000 200000000>;
+ qcom,sdcc-sup-voltages = <2950 2950>;
+ qcom,sdcc-bus-width = <4>;
+ qcom,sdcc-xpc;
+ qcom,sdcc-bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50", "SDR104";
+ qcom,sdcc-current-limit = <800>;
+ };
};
/include/ "msm-pm8644.dtsi"
diff --git a/arch/arm/boot/dts/msm8974-liquid.dts b/arch/arm/boot/dts/msm8974-liquid.dts
index 2eacb46..d1a6148 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dts
+++ b/arch/arm/boot/dts/msm8974-liquid.dts
@@ -27,6 +27,13 @@
status = "ok";
};
+ i2c@f9967000 {
+ battery@b {
+ compatible = "ti,bq28400-battery";
+ reg = <0xb>;
+ };
+ };
+
gpio_keys {
compatible = "gpio-keys";
input-name = "gpio-keys";
@@ -314,6 +321,13 @@
};
gpio@e300 { /* GPIO 36 */
+ qcom,mode = <1>; /* QPNP_PIN_MODE_DIG_OUT */
+ qcom,output-type = <0>; /* QPNP_PIN_OUT_BUF_CMOS */
+ qcom,pull = <5>; /* QPNP_PIN_PULL_NO */
+ qcom,vin-sel = <2>; /* QPNP_PIN_VIN2 */
+ qcom,out-strength = <3>; /* QPNP_PIN_OUT_STRENGTH_HIGH */
+ qcom,src-select = <3>; /* QPNP_PIN_SEL_FUNC_2 */
+ qcom,master-en = <1>;
};
};
diff --git a/arch/arm/boot/dts/msm8974-mdss.dtsi b/arch/arm/boot/dts/msm8974-mdss.dtsi
index e791348..a51a38d 100644
--- a/arch/arm/boot/dts/msm8974-mdss.dtsi
+++ b/arch/arm/boot/dts/msm8974-mdss.dtsi
@@ -68,6 +68,9 @@
reg-names = "edp_base", "mmss_cc_base";
vdda-supply = <&pm8941_l12>;
gpio-panel-en = <&msmgpio 58 0>;
+ gpio-panel-pwm = <&pm8941_gpios 36 0>;
+ qcom,panel-lpg-channel = <7>; /* LPG Channel 8 */
+ qcom,panel-pwm-period = <53>;
status = "disable";
};
};
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index 11f7c37..00b69ea 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -564,10 +564,12 @@
};
};
- i2c@f9967000 {
+ i2c@f9967000 { /* BLSP#11 */
cell-index = <0>;
compatible = "qcom,i2c-qup";
reg = <0Xf9967000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
reg-names = "qup_phys_addr";
interrupts = <0 105 0>;
interrupt-names = "qup_err_intr";
diff --git a/arch/arm/configs/fsm9xxx-perf_defconfig b/arch/arm/configs/fsm9xxx-perf_defconfig
index 93e84e9..1dc853b 100644
--- a/arch/arm/configs/fsm9xxx-perf_defconfig
+++ b/arch/arm/configs/fsm9xxx-perf_defconfig
@@ -11,7 +11,6 @@
CONFIG_PANIC_TIMEOUT=5
CONFIG_ASHMEM=y
CONFIG_EMBEDDED=y
-# CONFIG_PERF_EVENTS is not set
CONFIG_SLAB=y
CONFIG_PROFILING=y
CONFIG_OPROFILE=m
@@ -84,6 +83,7 @@
CONFIG_IPV6_MROUTE=y
CONFIG_IPV6_PIMSM_V2=y
# CONFIG_NET_ACTIVITY_STATS is not set
+CONFIG_IP_SCTP=y
CONFIG_RFKILL=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_MTD=y
diff --git a/arch/arm/configs/fsm9xxx_defconfig b/arch/arm/configs/fsm9xxx_defconfig
index c45063f..203d3b7 100644
--- a/arch/arm/configs/fsm9xxx_defconfig
+++ b/arch/arm/configs/fsm9xxx_defconfig
@@ -12,7 +12,6 @@
CONFIG_KALLSYMS_ALL=y
CONFIG_ASHMEM=y
CONFIG_EMBEDDED=y
-# CONFIG_PERF_EVENTS is not set
CONFIG_SLAB=y
CONFIG_PROFILING=y
CONFIG_OPROFILE=m
@@ -83,6 +82,7 @@
CONFIG_IPV6_MROUTE=y
CONFIG_IPV6_PIMSM_V2=y
# CONFIG_NET_ACTIVITY_STATS is not set
+CONFIG_IP_SCTP=y
CONFIG_RFKILL=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_MTD=y
diff --git a/arch/arm/configs/msm8910_defconfig b/arch/arm/configs/msm8910_defconfig
index 24e3452..e4dd4fb 100644
--- a/arch/arm/configs/msm8910_defconfig
+++ b/arch/arm/configs/msm8910_defconfig
@@ -41,6 +41,7 @@
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_ARM_ARCH_TIMER=y
+CONFIG_HOTPLUG_CPU=y
CONFIG_PREEMPT=y
CONFIG_AEABI=y
CONFIG_HIGHMEM=y
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index d8d2eae..c49ad93 100644
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -275,6 +275,7 @@
# CONFIG_BATTERY_MSM is not set
CONFIG_QPNP_CHARGER=y
CONFIG_QPNP_BMS=y
+CONFIG_BATTERY_BQ28400=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_SENSORS_QPNP_ADC_CURRENT=y
CONFIG_THERMAL=y
diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig
index 36f417e..e1d4ca0 100644
--- a/arch/arm/configs/msm9625_defconfig
+++ b/arch/arm/configs/msm9625_defconfig
@@ -85,6 +85,13 @@
# CONFIG_NET_VENDOR_CIRRUS is not set
# CONFIG_NET_VENDOR_FARADAY is not set
# CONFIG_NET_VENDOR_INTEL is not set
+CONFIG_WIRELESS=y
+CONFIG_CFG80211=m
+CONFIG_NL80211_TESTMODE=y
+CONFIG_ATH_COMMON=m
+CONFIG_ATH6KL=m
+CONFIG_ATH6KL_SDIO=m
+CONFIG_ATH6KL_DEBUG=y
CONFIG_KS8851=y
# CONFIG_NET_VENDOR_MICROCHIP is not set
# CONFIG_MSM_RMNET is not set
@@ -120,6 +127,7 @@
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_QPNP_PIN=y
CONFIG_GPIO_QPNP_PIN_DEBUG=y
+CONFIG_POWER_SUPPLY=y
CONFIG_HWMON=y
CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
CONFIG_REGULATOR=y
diff --git a/arch/arm/include/asm/outercache.h b/arch/arm/include/asm/outercache.h
index 53426c6..12f71a1 100644
--- a/arch/arm/include/asm/outercache.h
+++ b/arch/arm/include/asm/outercache.h
@@ -92,6 +92,7 @@
static inline void outer_flush_all(void) { }
static inline void outer_inv_all(void) { }
static inline void outer_disable(void) { }
+static inline void outer_resume(void) { }
#endif
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 3f36017..b258707 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -2004,7 +2004,7 @@
config MSM_PIL_TZAPPS
tristate "TZApps Boot Support"
- depends on MSM_PIL
+ depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
help
Support for booting and shutting down TZApps.
@@ -2023,13 +2023,13 @@
config MSM_PIL_VIDC
tristate "Video Core Secure Boot Support"
- depends on MSM_PIL
+ depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
help
Support for authenticating the video core image.
config MSM_PIL_VENUS
tristate "VENUS (Video) Boot Support"
- depends on MSM_PIL
+ depends on MSM_PIL && MSM_SUBSYSTEM_RESTART
help
Support for booting and shutting down the VENUS processor (Video).
Venus is the Video subsystem processor used for video codecs.
diff --git a/arch/arm/mach-msm/board-8064-gpu.c b/arch/arm/mach-msm/board-8064-gpu.c
index fad7092..f35ae6b 100644
--- a/arch/arm/mach-msm/board-8064-gpu.c
+++ b/arch/arm/mach-msm/board-8064-gpu.c
@@ -281,8 +281,11 @@
if (SOCINFO_VERSION_MAJOR(version) == 2) {
kgsl_3d0_pdata.chipid = ADRENO_CHIPID(3, 2, 0, 2);
} else {
+ /* The bootloader has started returning 1.2 for chips that
+ are either 1.1 or 1.2. To handle that and default any
+ future revisions to this path, check for minor version >=1 */
if ((SOCINFO_VERSION_MAJOR(version) == 1) &&
- (SOCINFO_VERSION_MINOR(version) == 1))
+ (SOCINFO_VERSION_MINOR(version) >= 1))
kgsl_3d0_pdata.chipid = ADRENO_CHIPID(3, 2, 0, 1);
else
kgsl_3d0_pdata.chipid = ADRENO_CHIPID(3, 2, 0, 0);
diff --git a/arch/arm/mach-msm/board-8064-pmic.c b/arch/arm/mach-msm/board-8064-pmic.c
index e64a672..c0e325a 100644
--- a/arch/arm/mach-msm/board-8064-pmic.c
+++ b/arch/arm/mach-msm/board-8064-pmic.c
@@ -512,4 +512,7 @@
} else if (machine_is_apq8064_cdp()) {
apq8064_pm8921_chg_pdata.has_dc_supply = true;
}
+
+ if (!machine_is_apq8064_mtp() && !machine_is_apq8064_liquid())
+ apq8064_pm8921_chg_pdata.battery_less_hardware = 1;
}
diff --git a/arch/arm/mach-msm/board-8092.c b/arch/arm/mach-msm/board-8092.c
index bd1762d..3e31f68 100644
--- a/arch/arm/mach-msm/board-8092.c
+++ b/arch/arm/mach-msm/board-8092.c
@@ -18,7 +18,6 @@
#include <linux/of_fdt.h>
#include <linux/of_irq.h>
#include <asm/hardware/gic.h>
-#include <asm/arch_timer.h>
#include <asm/mach/arch.h>
#include <asm/mach/time.h>
#include <mach/socinfo.h>
@@ -30,15 +29,9 @@
#include <linux/irq.h>
#include <linux/irqdomain.h>
+#include "board-dt.h"
#include "clock.h"
-static struct of_device_id irq_match[] __initdata = {
- { .compatible = "qcom,msm-qgic2", .data = gic_of_init, },
- { .compatible = "qcom,msm-gpio", .data = msm_gpio_of_init, },
- { .compatible = "qcom,spmi-pmic-arb", .data = qpnpint_of_init, },
- {}
-};
-
static struct clk_lookup msm_clocks_dummy[] = {
CLK_DUMMY("core_clk", BLSP1_UART_CLK, "msm_serial_hsl.0", OFF),
CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "msm_serial_hsl.0", OFF),
@@ -81,26 +74,7 @@
msm_reserve();
}
-void __init mpq8092_init_irq(void)
-{
- of_irq_init(irq_match);
-}
-
-static void __init mpq8092_dt_timer_init(void)
-{
- arch_timer_of_register();
-}
-
-static struct sys_timer mpq8092_dt_timer = {
- .init = mpq8092_dt_timer_init
-};
-
-static void __init mpq8092_dt_init_irq(void)
-{
- mpq8092_init_irq();
-}
-
-static void __init mpq8092_dt_map_io(void)
+static void __init mpq8092_map_io(void)
{
msm_map_mpq8092_io();
if (socinfo_init() < 0)
@@ -113,21 +87,19 @@
"msm_serial_hsl.0", NULL),
OF_DEV_AUXDATA("qcom,spmi-pmic-arb", 0xFC4C0000, \
"spmi-pmic-arb.0", NULL),
+ OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF9824000, \
+ "msm_sdcc.1", NULL),
+ OF_DEV_AUXDATA("qcom,msm-sdcc", 0xF98A4000, \
+ "msm_sdcc.2", NULL),
{}
};
-static void __init mpq8092_init(struct of_dev_auxdata **adata)
+static void __init mpq8092_init(void)
{
+ struct of_dev_auxdata *adata = mpq8092_auxdata_lookup;
+
mpq8092_init_gpiomux();
- *adata = mpq8092_auxdata_lookup;
msm_clock_init(&mpq8092_clock_init_data);
-}
-
-static void __init mpq8092_dt_init(void)
-{
- struct of_dev_auxdata *adata = NULL;
-
- mpq8092_init(&adata);
of_platform_populate(NULL, of_default_bus_match_table, adata, NULL);
}
@@ -136,12 +108,12 @@
NULL
};
-DT_MACHINE_START(MSM_DT, "Qualcomm MSM (Flattened Device Tree)")
- .map_io = mpq8092_dt_map_io,
- .init_irq = mpq8092_dt_init_irq,
- .init_machine = mpq8092_dt_init,
+DT_MACHINE_START(MSM8092_DT, "Qualcomm MSM 8092 (Flattened Device Tree)")
+ .map_io = mpq8092_map_io,
+ .init_irq = msm_dt_init_irq_nompm,
+ .init_machine = mpq8092_init,
.handle_irq = gic_handle_irq,
- .timer = &mpq8092_dt_timer,
+ .timer = &msm_dt_timer,
.dt_compat = mpq8092_dt_match,
.reserve = mpq8092_dt_reserve,
.init_very_early = mpq8092_early_memory,
diff --git a/arch/arm/mach-msm/board-8910.c b/arch/arm/mach-msm/board-8910.c
index 9afc05e..7039879 100644
--- a/arch/arm/mach-msm/board-8910.c
+++ b/arch/arm/mach-msm/board-8910.c
@@ -37,6 +37,7 @@
#include <mach/socinfo.h>
#include <mach/board.h>
#include <mach/clk-provider.h>
+#include "board-dt.h"
#include "clock.h"
static struct clk_lookup msm_clocks_dummy[] = {
@@ -54,26 +55,6 @@
.size = ARRAY_SIZE(msm_clocks_dummy),
};
-static struct of_device_id irq_match[] __initdata = {
- { .compatible = "qcom,msm-qgic2", .data = gic_of_init, },
- { .compatible = "qcom,msm-gpio", .data = msm_gpio_of_init, },
- {},
-};
-
-static void __init msm8910_dt_timer_init(void)
-{
- arch_timer_of_register();
-}
-
-static struct sys_timer msm8910_dt_timer = {
- .init = msm8910_dt_timer_init
-};
-
-void __init msm8910_init_irq(void)
-{
- of_irq_init(irq_match);
-}
-
void __init msm8910_init(void)
{
msm_clock_init(&msm_dummy_clock_init_data);
@@ -91,9 +72,9 @@
DT_MACHINE_START(MSM8910_DT, "Qualcomm MSM 8910 (Flattened Device Tree)")
.map_io = msm_map_msm8910_io,
- .init_irq = msm8910_init_irq,
+ .init_irq = msm_dt_init_irq_nompm,
.init_machine = msm8910_init,
.handle_irq = gic_handle_irq,
- .timer = &msm8910_dt_timer,
+ .timer = &msm_dt_timer,
.dt_compat = msm8910_dt_match,
MACHINE_END
diff --git a/arch/arm/mach-msm/board-8930-gpu.c b/arch/arm/mach-msm/board-8930-gpu.c
index 99a5a34..578c665 100644
--- a/arch/arm/mach-msm/board-8930-gpu.c
+++ b/arch/arm/mach-msm/board-8930-gpu.c
@@ -163,10 +163,18 @@
{
unsigned int version = socinfo_get_version();
+ /* Set the turbo speed for the AA and AB respectively */
+
if (cpu_is_msm8930aa())
kgsl_3d0_pdata.pwrlevel[0].gpu_freq = 450000000;
+ else if (cpu_is_msm8930ab())
+ kgsl_3d0_pdata.pwrlevel[0].gpu_freq = 500000000;
- if ((SOCINFO_VERSION_MAJOR(version) == 1) &&
+ /* Set up the chip ID based on the SoC version */
+
+ if (cpu_is_msm8930ab())
+ kgsl_3d0_pdata.chipid = ADRENO_CHIPID(3, 0, 5, 3);
+ else if ((SOCINFO_VERSION_MAJOR(version) == 1) &&
(SOCINFO_VERSION_MINOR(version) == 2))
kgsl_3d0_pdata.chipid = ADRENO_CHIPID(3, 0, 5, 2);
else
diff --git a/arch/arm/mach-msm/board-8930-pmic.c b/arch/arm/mach-msm/board-8930-pmic.c
index 8687b2a..0c7666b 100644
--- a/arch/arm/mach-msm/board-8930-pmic.c
+++ b/arch/arm/mach-msm/board-8930-pmic.c
@@ -596,4 +596,7 @@
else if (machine_is_msm8930_cdp())
pm8921_chg_pdata.has_dc_supply = true;
}
+
+ if (!machine_is_msm8930_mtp())
+ pm8921_chg_pdata.battery_less_hardware = 1;
}
diff --git a/arch/arm/mach-msm/board-8930-regulator-pm8038.c b/arch/arm/mach-msm/board-8930-regulator-pm8038.c
index 16a82b4..727c4c6 100644
--- a/arch/arm/mach-msm/board-8930-regulator-pm8038.c
+++ b/arch/arm/mach-msm/board-8930-regulator-pm8038.c
@@ -97,6 +97,8 @@
REGULATOR_SUPPLY("CDC_VDDA_RX", "sitar1p1-slim"),
REGULATOR_SUPPLY("vddp", "0-0048"),
REGULATOR_SUPPLY("mhl_iovcc18", "0-0039"),
+ REGULATOR_SUPPLY("vdd-io", "spi0.0"),
+ REGULATOR_SUPPLY("vdd-phy", "spi0.0"),
};
VREG_CONSUMERS(L12) = {
REGULATOR_SUPPLY("8038_l12", NULL),
diff --git a/arch/arm/mach-msm/board-8930-regulator-pm8917.c b/arch/arm/mach-msm/board-8930-regulator-pm8917.c
index 8898b50..33e38ab 100644
--- a/arch/arm/mach-msm/board-8930-regulator-pm8917.c
+++ b/arch/arm/mach-msm/board-8930-regulator-pm8917.c
@@ -196,6 +196,8 @@
REGULATOR_SUPPLY("mhl_iovcc18", "0-0039"),
REGULATOR_SUPPLY("CDC_VDD_CP", "sitar-slim"),
REGULATOR_SUPPLY("CDC_VDD_CP", "sitar1p1-slim"),
+ REGULATOR_SUPPLY("vdd-io", "spi0.0"),
+ REGULATOR_SUPPLY("vdd-phy", "spi0.0"),
};
VREG_CONSUMERS(S5) = {
REGULATOR_SUPPLY("8917_s5", NULL),
diff --git a/arch/arm/mach-msm/board-8960-pmic.c b/arch/arm/mach-msm/board-8960-pmic.c
index d8a260b..9efedb1 100644
--- a/arch/arm/mach-msm/board-8960-pmic.c
+++ b/arch/arm/mach-msm/board-8960-pmic.c
@@ -612,4 +612,8 @@
if (machine_is_msm8960_fluid())
pm8921_bms_pdata.rconn_mohm = 20;
+
+ if (!machine_is_msm8960_fluid() && !machine_is_msm8960_liquid()
+ && !machine_is_msm8960_fluid())
+ pm8921_chg_pdata.battery_less_hardware = 1;
}
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index 4a78e31..e919d78 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -3216,6 +3216,17 @@
msm_tsens_early_init(&msm_tsens_pdata);
}
+static void __init msm8960_reset_spm_avs(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(msm_spm_data); i++) {
+ struct msm_spm_platform_data *pdata = &msm_spm_data[i];
+ pdata->reg_init_values[MSM_SPM_REG_SAW2_AVS_CTL] = 0;
+ pdata->reg_init_values[MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0;
+ }
+}
+
static void __init msm8960_cdp_init(void)
{
if (meminfo_init(SYS_MEMORY, SZ_256M) < 0)
@@ -3269,8 +3280,12 @@
msm_isa1200_board_info[0].platform_data = &isa1200_1_pdata;
msm8960_i2c_init();
msm8960_gfx_init();
+
+ if (cpu_is_msm8960ab())
+ msm8960_reset_spm_avs();
msm_spm_init(msm_spm_data, ARRAY_SIZE(msm_spm_data));
msm_spm_l2_init(msm_spm_l2_data);
+
msm8960_init_buses();
platform_add_devices(msm8960_footswitch, msm8960_num_footswitch);
if (machine_is_msm8960_liquid())
diff --git a/arch/arm/mach-msm/board-8974.c b/arch/arm/mach-msm/board-8974.c
index 7480437..19fb222 100644
--- a/arch/arm/mach-msm/board-8974.c
+++ b/arch/arm/mach-msm/board-8974.c
@@ -198,7 +198,7 @@
.edge = SMD_APPS_RPM,
.smd_int.irq_name = "rpm_smd_in",
- .smd_int.flags = IRQF_TRIGGER_RISING,
+ .smd_int.flags = IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND,
.smd_int.irq_id = -1,
.smd_int.device_name = "smd_dev",
.smd_int.dev_id = 0,
diff --git a/arch/arm/mach-msm/board-9625.c b/arch/arm/mach-msm/board-9625.c
index 49f2561..5c7eebe 100644
--- a/arch/arm/mach-msm/board-9625.c
+++ b/arch/arm/mach-msm/board-9625.c
@@ -21,11 +21,8 @@
#include <linux/of_irq.h>
#include <linux/memory.h>
#include <asm/mach/map.h>
-#include <asm/hardware/cache-l2x0.h>
#include <asm/hardware/gic.h>
-#include <asm/arch_timer.h>
#include <asm/mach/arch.h>
-#include <asm/mach/time.h>
#include <mach/socinfo.h>
#include <mach/board.h>
#include <mach/restart.h>
@@ -35,19 +32,15 @@
#include <mach/msm_memtypes.h>
#include <mach/msm_iomap.h>
#include <mach/msm_smd.h>
-#include <mach/scm.h>
#include <mach/rpm-smd.h>
#include <mach/rpm-regulator-smd.h>
-#include <mach/mpm.h>
+#include "board-dt.h"
#include "clock.h"
#include "modem_notifier.h"
#include "lpm_resources.h"
#include "spm.h"
#define MSM_KERNEL_EBI_SIZE 0x51000
-#define SCM_SVC_L2CC_PL310 16
-#define L2CC_PL310_CTRL_ID 1
-#define L2CC_PL310_ON 1
static struct memtype_reserve msm9625_reserve_table[] __initdata = {
[MEMTYPE_SMI] = {
@@ -76,10 +69,6 @@
.paddr_to_memtype = msm9625_paddr_to_memtype,
};
-#define L2CC_AUX_CTRL ((0x1 << L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT) | \
- (0x2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) | \
- (0x1 << L2X0_AUX_CTRL_EVNT_MON_BUS_EN_SHIFT))
-
static struct clk_lookup msm_clocks_dummy[] = {
CLK_DUMMY("core_clk", BLSP1_UART_CLK, "msm_serial_hsl.0", OFF),
CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "msm_serial_hsl.0", OFF),
@@ -103,13 +92,6 @@
.size = ARRAY_SIZE(msm_clocks_dummy),
};
-static struct of_device_id irq_match[] __initdata = {
- { .compatible = "qcom,msm-qgic2", .data = gic_of_init, },
- { .compatible = "qcom,msm-gpio", .data = msm_gpio_of_init, },
- { .compatible = "qcom,spmi-pmic-arb", .data = qpnpint_of_init, },
- {}
-};
-
static const char *msm9625_dt_match[] __initconst = {
"qcom,msm9625",
NULL
@@ -129,34 +111,6 @@
{}
};
-static struct of_device_id mpm_match[] __initdata = {
- {.compatible = "qcom,mpm-v2", },
- {},
-};
-
-void __init msm9625_init_irq(void)
-{
- struct device_node *node;
- scm_call_atomic1(SCM_SVC_L2CC_PL310, L2CC_PL310_CTRL_ID, L2CC_PL310_ON);
- l2x0_of_init(0, ~0UL);
- of_irq_init(irq_match);
- node = of_find_matching_node(NULL, mpm_match);
-
- WARN_ON(!node);
-
- if (node)
- of_mpm_init(node);
-}
-
-static void __init msm_dt_timer_init(void)
-{
- arch_timer_of_register();
-}
-
-static struct sys_timer msm_dt_timer = {
- .init = msm_dt_timer_init
-};
-
static void __init msm9625_early_memory(void)
{
reserve_info = &msm9625_reserve_info;
@@ -331,9 +285,9 @@
msm9625_add_drivers();
}
-DT_MACHINE_START(MSM_DT, "Qualcomm MSM (Flattened Device Tree)")
+DT_MACHINE_START(MSM9625_DT, "Qualcomm MSM 9625 (Flattened Device Tree)")
.map_io = msm_map_msm9625_io,
- .init_irq = msm9625_init_irq,
+ .init_irq = msm_dt_init_irq_l2x0,
.init_machine = msm9625_init,
.handle_irq = gic_handle_irq,
.timer = &msm_dt_timer,
diff --git a/arch/arm/mach-msm/board-dt.c b/arch/arm/mach-msm/board-dt.c
index 3654de8..74b0d0d 100644
--- a/arch/arm/mach-msm/board-dt.c
+++ b/arch/arm/mach-msm/board-dt.c
@@ -17,12 +17,18 @@
#include <linux/mfd/wcd9xxx/core.h>
#include <asm/arch_timer.h>
#include <asm/mach/time.h>
+#include <asm/hardware/cache-l2x0.h>
#include <asm/hardware/gic.h>
#include <mach/mpm.h>
#include <mach/qpnp-int.h>
+#include <mach/scm.h>
#include "board-dt.h"
+#define SCM_SVC_L2CC_PL310 16
+#define L2CC_PL310_CTRL_ID 1
+#define L2CC_PL310_ON 1
+
static void __init msm_dt_timer_init(void)
{
arch_timer_of_register();
@@ -62,3 +68,10 @@
{
of_irq_init(irq_match);
}
+
+void __init msm_dt_init_irq_l2x0(void)
+{
+ scm_call_atomic1(SCM_SVC_L2CC_PL310, L2CC_PL310_CTRL_ID, L2CC_PL310_ON);
+ l2x0_of_init(0, ~0UL);
+ msm_dt_init_irq();
+}
diff --git a/arch/arm/mach-msm/board-dt.h b/arch/arm/mach-msm/board-dt.h
index 16a6135..cc3e92c 100644
--- a/arch/arm/mach-msm/board-dt.h
+++ b/arch/arm/mach-msm/board-dt.h
@@ -13,3 +13,4 @@
extern struct sys_timer msm_dt_timer;
void __init msm_dt_init_irq(void);
void __init msm_dt_init_irq_nompm(void);
+void __init msm_dt_init_irq_l2x0(void);
diff --git a/arch/arm/mach-msm/clock-7x30.c b/arch/arm/mach-msm/clock-7x30.c
index e42fe65..e1390db 100644
--- a/arch/arm/mach-msm/clock-7x30.c
+++ b/arch/arm/mach-msm/clock-7x30.c
@@ -160,7 +160,8 @@
VDD_DIG_NONE,
VDD_DIG_LOW,
VDD_DIG_NOMINAL,
- VDD_DIG_HIGH
+ VDD_DIG_HIGH,
+ VDD_DIG_NUM
};
static int set_vdd_dig(struct clk_vdd_class *vdd_class, int level)
@@ -184,15 +185,21 @@
return rc;
}
-static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig);
+static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig, VDD_DIG_NUM);
#define VDD_DIG_FMAX_MAP1(l1, f1) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1), \
- .fmax[VDD_DIG_##l2] = (f2)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
#define PCOM_XO_DISABLE 0
#define PCOM_XO_ENABLE 1
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index 934bf88..472cb68 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -376,7 +376,8 @@
VDD_DIG_NONE,
VDD_DIG_LOW,
VDD_DIG_NOMINAL,
- VDD_DIG_HIGH
+ VDD_DIG_HIGH,
+ VDD_DIG_NUM
};
static int set_vdd_dig_8960(struct clk_vdd_class *vdd_class, int level)
@@ -391,7 +392,7 @@
vdd_uv[level], 1150000, 1);
}
-static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig_8960);
+static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig_8960, VDD_DIG_NUM);
static int rpm_vreg_dig_8930 = RPM_VREG_ID_PM8038_VDD_DIG_CORNER;
static int set_vdd_dig_8930(struct clk_vdd_class *vdd_class, int level)
@@ -409,21 +410,31 @@
}
#define VDD_DIG_FMAX_MAP1(l1, f1) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1), \
- .fmax[VDD_DIG_##l2] = (f2)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP3(l1, f1, l2, f2, l3, f3) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1), \
- .fmax[VDD_DIG_##l2] = (f2), \
- .fmax[VDD_DIG_##l3] = (f3)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ [VDD_DIG_##l3] = (f3), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
enum vdd_sr2_hdmi_pll_levels {
VDD_SR2_HDMI_PLL_OFF,
- VDD_SR2_HDMI_PLL_ON
+ VDD_SR2_HDMI_PLL_ON,
+ VDD_SR2_HDMI_PLL_NUM
};
static int set_vdd_sr2_hdmi_pll_8960(struct clk_vdd_class *vdd_class, int level)
@@ -455,7 +466,8 @@
return rc;
}
-static DEFINE_VDD_CLASS(vdd_sr2_hdmi_pll, set_vdd_sr2_hdmi_pll_8960);
+static DEFINE_VDD_CLASS(vdd_sr2_hdmi_pll, set_vdd_sr2_hdmi_pll_8960,
+ VDD_SR2_HDMI_PLL_NUM);
static int sr2_lreg_uv[] = {
[VDD_SR2_HDMI_PLL_OFF] = 0,
@@ -530,7 +542,10 @@
.rate = 1200000000,
.ops = &clk_ops_local_pll,
.vdd_class = &vdd_sr2_hdmi_pll,
- .fmax[VDD_SR2_HDMI_PLL_ON] = ULONG_MAX,
+ .fmax = (unsigned long[VDD_SR2_HDMI_PLL_NUM]) {
+ [VDD_SR2_HDMI_PLL_ON] = ULONG_MAX
+ },
+ .num_fmax = VDD_SR2_HDMI_PLL_NUM,
CLK_INIT(pll3_clk.c),
},
};
@@ -1532,7 +1547,7 @@
static CLK_SDC(sdc4_clk, 4, 3, 33000000, 67000000);
static CLK_SDC(sdc5_clk, 5, 2, 33000000, 67000000);
-static unsigned long fmax_sdc1_8064v2[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_sdc1_8064v2[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 100000000,
[VDD_DIG_NOMINAL] = 200000000,
};
@@ -1933,7 +1948,7 @@
},
};
-static unsigned long fmax_ce3_8064v2[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_ce3_8064v2[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 57000000,
[VDD_DIG_NOMINAL] = 120000000,
};
@@ -3580,25 +3595,25 @@
F_END
};
-static unsigned long fmax_gfx3d_8064ab[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_gfx3d_8064ab[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 128000000,
[VDD_DIG_NOMINAL] = 325000000,
[VDD_DIG_HIGH] = 450000000
};
-static unsigned long fmax_gfx3d_8064[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_gfx3d_8064[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 128000000,
[VDD_DIG_NOMINAL] = 325000000,
[VDD_DIG_HIGH] = 400000000
};
-static unsigned long fmax_gfx3d_8930[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_gfx3d_8930[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 192000000,
[VDD_DIG_NOMINAL] = 320000000,
[VDD_DIG_HIGH] = 400000000
};
-static unsigned long fmax_gfx3d_8930aa[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_gfx3d_8930aa[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 192000000,
[VDD_DIG_NOMINAL] = 320000000,
[VDD_DIG_HIGH] = 450000000
@@ -3750,7 +3765,7 @@
F_END
};
-static unsigned long fmax_ijpeg_8064[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_ijpeg_8064[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 128000000,
[VDD_DIG_NOMINAL] = 266667000,
[VDD_DIG_HIGH] = 320000000
@@ -3877,7 +3892,7 @@
F_END
};
-static unsigned long fmax_mdp_8064[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_mdp_8064[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 128000000,
[VDD_DIG_NOMINAL] = 266667000
};
@@ -4071,7 +4086,10 @@
.dbg_name = "hdmi_pll_clk",
.ops = &clk_ops_hdmi_pll,
.vdd_class = &vdd_sr2_hdmi_pll,
- .fmax[VDD_SR2_HDMI_PLL_ON] = ULONG_MAX,
+ .fmax = (unsigned long [VDD_SR2_HDMI_PLL_NUM]) {
+ [VDD_SR2_HDMI_PLL_ON] = ULONG_MAX,
+ },
+ .num_fmax = VDD_SR2_HDMI_PLL_NUM,
CLK_INIT(hdmi_pll_clk),
};
@@ -4103,7 +4121,7 @@
F_END
};
-static unsigned long fmax_tv_src_8064[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_tv_src_8064[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 74250000,
[VDD_DIG_NOMINAL] = 149000000
};
@@ -4342,7 +4360,7 @@
},
};
-static unsigned long fmax_vcodec_8064v2[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_vcodec_8064v2[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 100000000,
[VDD_DIG_NOMINAL] = 200000000,
[VDD_DIG_HIGH] = 266670000,
@@ -4424,7 +4442,7 @@
F_END
};
-static unsigned long fmax_vfe_8064[MAX_VDD_LEVELS] __initdata = {
+static unsigned long fmax_vfe_8064[VDD_DIG_NUM] = {
[VDD_DIG_LOW] = 128000000,
[VDD_DIG_NOMINAL] = 266667000,
[VDD_DIG_HIGH] = 320000000
@@ -6565,37 +6583,25 @@
*/
if (cpu_is_apq8064()) {
gfx3d_clk.freq_tbl = clk_tbl_gfx3d_8064;
-
- memcpy(gfx3d_clk.c.fmax, fmax_gfx3d_8064,
- sizeof(gfx3d_clk.c.fmax));
+ gfx3d_clk.c.fmax = fmax_gfx3d_8064;
}
if (cpu_is_apq8064ab()) {
gfx3d_clk.freq_tbl = clk_tbl_gfx3d_8064;
-
- memcpy(gfx3d_clk.c.fmax, fmax_gfx3d_8064ab,
- sizeof(gfx3d_clk.c.fmax));
+ gfx3d_clk.c.fmax = fmax_gfx3d_8064ab;
}
if ((cpu_is_apq8064() &&
SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 2) ||
cpu_is_apq8064ab()) {
- memcpy(vcodec_clk.c.fmax, fmax_vcodec_8064v2,
- sizeof(vcodec_clk.c.fmax));
- memcpy(ce3_src_clk.c.fmax, fmax_ce3_8064v2,
- sizeof(ce3_src_clk.c.fmax));
- memcpy(sdc1_clk.c.fmax, fmax_sdc1_8064v2,
- sizeof(sdc1_clk.c.fmax));
+ vcodec_clk.c.fmax = fmax_vcodec_8064v2;
+ ce3_src_clk.c.fmax = fmax_ce3_8064v2;
+ sdc1_clk.c.fmax = fmax_sdc1_8064v2;
}
if (cpu_is_apq8064() || cpu_is_apq8064ab()) {
- memcpy(ijpeg_clk.c.fmax, fmax_ijpeg_8064,
- sizeof(ijpeg_clk.c.fmax));
- memcpy(mdp_clk.c.fmax, fmax_mdp_8064,
- sizeof(ijpeg_clk.c.fmax));
- memcpy(tv_src_clk.c.fmax, fmax_tv_src_8064,
- sizeof(tv_src_clk.c.fmax));
- memcpy(vfe_clk.c.fmax, fmax_vfe_8064,
- sizeof(vfe_clk.c.fmax));
-
+ ijpeg_clk.c.fmax = fmax_ijpeg_8064;
+ mdp_clk.c.fmax = fmax_mdp_8064;
+ tv_src_clk.c.fmax = fmax_tv_src_8064;
+ vfe_clk.c.fmax = fmax_vfe_8064;
gmem_axi_clk.c.depends = &gfx3d_axi_clk.c;
}
@@ -6604,11 +6610,9 @@
* clocks which differ between 8960 and 8930.
*/
if (cpu_is_msm8930() || cpu_is_msm8627()) {
- memcpy(gfx3d_clk.c.fmax, fmax_gfx3d_8930,
- sizeof(gfx3d_clk.c.fmax));
+ gfx3d_clk.c.fmax = fmax_gfx3d_8930;
} else if (cpu_is_msm8930aa()) {
- memcpy(gfx3d_clk.c.fmax, fmax_gfx3d_8930aa,
- sizeof(gfx3d_clk.c.fmax));
+ gfx3d_clk.c.fmax = fmax_gfx3d_8930aa;
}
if (cpu_is_msm8930() || cpu_is_msm8930aa() || cpu_is_msm8627()) {
gfx3d_clk.freq_tbl = clk_tbl_gfx3d_8930;
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index 246ceaf..76b8abf 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -588,23 +588,33 @@
}
#define VDD_DIG_FMAX_MAP1(l1, f1) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1), \
- .fmax[VDD_DIG_##l2] = (f2)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP3(l1, f1, l2, f2, l3, f3) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1), \
- .fmax[VDD_DIG_##l2] = (f2), \
- .fmax[VDD_DIG_##l3] = (f3)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ [VDD_DIG_##l3] = (f3), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
enum vdd_dig_levels {
VDD_DIG_NONE,
VDD_DIG_LOW,
VDD_DIG_NOMINAL,
- VDD_DIG_HIGH
+ VDD_DIG_HIGH,
+ VDD_DIG_NUM
};
static const int vdd_corner[] = {
@@ -622,7 +632,7 @@
RPM_REGULATOR_CORNER_SUPER_TURBO);
}
-static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig);
+static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig, VDD_DIG_NUM);
#define RPM_MISC_CLK_TYPE 0x306b6c63
#define RPM_BUS_CLK_TYPE 0x316b6c63
diff --git a/arch/arm/mach-msm/clock-8x60.c b/arch/arm/mach-msm/clock-8x60.c
index bc4bb2e..3816b54 100644
--- a/arch/arm/mach-msm/clock-8x60.c
+++ b/arch/arm/mach-msm/clock-8x60.c
@@ -268,7 +268,8 @@
VDD_DIG_NONE,
VDD_DIG_LOW,
VDD_DIG_NOMINAL,
- VDD_DIG_HIGH
+ VDD_DIG_HIGH,
+ VDD_DIG_NUM
};
static int set_vdd_dig(struct clk_vdd_class *vdd_class, int level)
@@ -284,20 +285,29 @@
vdd_uv[level], 1200000, 1);
}
-static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig);
+static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig, VDD_DIG_NUM);
#define VDD_DIG_FMAX_MAP1(l1, f1) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1), \
- .fmax[VDD_DIG_##l2] = (f2)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP3(l1, f1, l2, f2, l3, f3) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1), \
- .fmax[VDD_DIG_##l2] = (f2), \
- .fmax[VDD_DIG_##l3] = (f3)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ [VDD_DIG_##l3] = (f3), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
DEFINE_CLK_RPM_BRANCH(pxo_clk, pxo_a_clk, PXO, 27000000);
DEFINE_CLK_RPM_BRANCH(cxo_clk, cxo_a_clk, CXO, 19200000);
diff --git a/arch/arm/mach-msm/clock-9615.c b/arch/arm/mach-msm/clock-9615.c
index 035ef5c..338361b 100644
--- a/arch/arm/mach-msm/clock-9615.c
+++ b/arch/arm/mach-msm/clock-9615.c
@@ -182,7 +182,8 @@
VDD_DIG_NONE,
VDD_DIG_LOW,
VDD_DIG_NOMINAL,
- VDD_DIG_HIGH
+ VDD_DIG_HIGH,
+ VDD_DIG_NUM
};
static int set_vdd_dig(struct clk_vdd_class *vdd_class, int level)
@@ -198,15 +199,21 @@
RPM_VREG_VOTER3, vdd_corner[level], RPM_VREG_CORNER_HIGH, 1);
}
-static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig);
+static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig, VDD_DIG_NUM);
#define VDD_DIG_FMAX_MAP1(l1, f1) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1), \
- .fmax[VDD_DIG_##l2] = (f2)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
/*
* Clock Descriptions
diff --git a/arch/arm/mach-msm/clock-9625.c b/arch/arm/mach-msm/clock-9625.c
index fb4f32a..b9362cf 100644
--- a/arch/arm/mach-msm/clock-9625.c
+++ b/arch/arm/mach-msm/clock-9625.c
@@ -291,23 +291,33 @@
}
#define VDD_DIG_FMAX_MAP1(l1, f1) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP2(l1, f1, l2, f2) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1), \
- .fmax[VDD_DIG_##l2] = (f2)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
#define VDD_DIG_FMAX_MAP3(l1, f1, l2, f2, l3, f3) \
- .vdd_class = &vdd_dig, \
- .fmax[VDD_DIG_##l1] = (f1), \
- .fmax[VDD_DIG_##l2] = (f2), \
- .fmax[VDD_DIG_##l3] = (f3)
+ .vdd_class = &vdd_dig, \
+ .fmax = (unsigned long[VDD_DIG_NUM]) { \
+ [VDD_DIG_##l1] = (f1), \
+ [VDD_DIG_##l2] = (f2), \
+ [VDD_DIG_##l3] = (f3), \
+ }, \
+ .num_fmax = VDD_DIG_NUM
enum vdd_dig_levels {
VDD_DIG_NONE,
VDD_DIG_LOW,
VDD_DIG_NOMINAL,
- VDD_DIG_HIGH
+ VDD_DIG_HIGH,
+ VDD_DIG_NUM
};
static const int vdd_corner[] = {
@@ -325,7 +335,7 @@
RPM_REGULATOR_CORNER_SUPER_TURBO);
}
-static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig);
+static DEFINE_VDD_CLASS(vdd_dig, set_vdd_dig, VDD_DIG_NUM);
/* TODO: Needs to confirm the below values */
#define RPM_MISC_CLK_TYPE 0x306b6c63
diff --git a/arch/arm/mach-msm/clock-debug.c b/arch/arm/mach-msm/clock-debug.c
index 8bd4433..489d623 100644
--- a/arch/arm/mach-msm/clock-debug.c
+++ b/arch/arm/mach-msm/clock-debug.c
@@ -157,7 +157,7 @@
clock->dbg_name, clock->rate);
return 0;
}
- for (level = 0; level < ARRAY_SIZE(clock->fmax); level++) {
+ for (level = 0; level < clock->num_fmax; level++) {
if (vdd_level == level)
seq_printf(m, "[%lu] ", clock->fmax[level]);
else
@@ -189,7 +189,7 @@
if (!clock->vdd_class) {
fmax = INT_MAX;
} else {
- for (level = 0; level < ARRAY_SIZE(clock->fmax); level++)
+ for (level = 0; level < clock->num_fmax; level++)
if (clock->fmax[level])
fmax = clock->fmax[level];
}
diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c
index 5100980..e9dd974 100644
--- a/arch/arm/mach-msm/clock.c
+++ b/arch/arm/mach-msm/clock.c
@@ -37,11 +37,11 @@
{
int level;
- for (level = 0; level < ARRAY_SIZE(clk->fmax); level++)
+ for (level = 0; level < clk->num_fmax; level++)
if (rate <= clk->fmax[level])
break;
- if (level == ARRAY_SIZE(clk->fmax)) {
+ if (level == clk->num_fmax) {
pr_err("Rate %lu for %s is greater than highest Fmax\n", rate,
clk->dbg_name);
return -EINVAL;
@@ -55,7 +55,7 @@
{
int level, rc;
- for (level = ARRAY_SIZE(vdd_class->level_votes)-1; level > 0; level--)
+ for (level = vdd_class->num_levels-1; level > 0; level--)
if (vdd_class->level_votes[level])
break;
@@ -74,6 +74,9 @@
{
int rc;
+ if (level >= vdd_class->num_levels)
+ return -EINVAL;
+
mutex_lock(&vdd_class->lock);
vdd_class->level_votes[level]++;
rc = update_vdd(vdd_class);
@@ -89,6 +92,9 @@
{
int rc = 0;
+ if (level >= vdd_class->num_levels)
+ return -EINVAL;
+
mutex_lock(&vdd_class->lock);
if (WARN(!vdd_class->level_votes[level],
"Reference counts are incorrect for %s level %d\n",
diff --git a/arch/arm/mach-msm/include/mach/clk-provider.h b/arch/arm/mach-msm/include/mach/clk-provider.h
index 770713d..d47e88e 100644
--- a/arch/arm/mach-msm/include/mach/clk-provider.h
+++ b/arch/arm/mach-msm/include/mach/clk-provider.h
@@ -39,8 +39,6 @@
#define ENABLE_VOTED 4 /* Bit pol: 1 = running; delay on disable */
#define DELAY 5 /* No bit to check, just delay */
-#define MAX_VDD_LEVELS 4
-
/**
* struct clk_vdd_class - Voltage scaling class
* @class_name: name of the class
@@ -52,16 +50,19 @@
struct clk_vdd_class {
const char *class_name;
int (*set_vdd)(struct clk_vdd_class *v_class, int level);
- int level_votes[MAX_VDD_LEVELS];
+ int *level_votes;
+ int num_levels;
unsigned long cur_level;
struct mutex lock;
};
-#define DEFINE_VDD_CLASS(_name, _set_vdd) \
+#define DEFINE_VDD_CLASS(_name, _set_vdd, _num_levels) \
struct clk_vdd_class _name = { \
.class_name = #_name, \
.set_vdd = _set_vdd, \
- .cur_level = ARRAY_SIZE(_name.level_votes), \
+ .level_votes = (int [_num_levels]) {}, \
+ .num_levels = _num_levels, \
+ .cur_level = _num_levels, \
.lock = __MUTEX_INITIALIZER(_name.lock) \
}
@@ -109,7 +110,8 @@
const char *dbg_name;
struct clk *depends;
struct clk_vdd_class *vdd_class;
- unsigned long fmax[MAX_VDD_LEVELS];
+ unsigned long *fmax;
+ int num_fmax;
unsigned long rate;
struct list_head children;
diff --git a/arch/arm/mach-msm/ocmem_sched.c b/arch/arm/mach-msm/ocmem_sched.c
index 38813e1..e5892c3 100644
--- a/arch/arm/mach-msm/ocmem_sched.c
+++ b/arch/arm/mach-msm/ocmem_sched.c
@@ -1153,6 +1153,37 @@
return 0;
}
+static void sched_dequeue(struct ocmem_req *victim_req)
+{
+ struct ocmem_req *req = NULL;
+ struct ocmem_req *next = NULL;
+ int id;
+
+ if (!victim_req)
+ return;
+
+ id = victim_req->owner;
+
+ mutex_lock(&sched_queue_mutex);
+
+ if (list_empty(&sched_queue[id]))
+ goto dequeue_done;
+
+ list_for_each_entry_safe(req, next, &sched_queue[id], sched_list)
+ {
+ if (req == victim_req) {
+ pr_debug("ocmem: Cancelling pending request %p\n",
+ req);
+ list_del(&req->sched_list);
+ goto dequeue_done;
+ }
+ }
+
+dequeue_done:
+ mutex_unlock(&sched_queue_mutex);
+ return;
+}
+
static struct ocmem_req *ocmem_fetch_req(void)
{
int i;
@@ -1379,6 +1410,10 @@
return -EINVAL;
}
+ mutex_lock(&sched_mutex);
+ sched_dequeue(req);
+ mutex_unlock(&sched_mutex);
+
if (!TEST_STATE(req, R_FREE)) {
rc = process_unmap(req, req->req_start, req->req_end);
diff --git a/arch/arm/mach-msm/pil-tzapps.c b/arch/arm/mach-msm/pil-tzapps.c
index 2345453..be78fab 100644
--- a/arch/arm/mach-msm/pil-tzapps.c
+++ b/arch/arm/mach-msm/pil-tzapps.c
@@ -16,9 +16,18 @@
#include <linux/elf.h>
#include <linux/err.h>
+#include <mach/peripheral-loader.h>
+#include <mach/subsystem_restart.h>
+
#include "peripheral-loader.h"
#include "scm-pas.h"
+struct tzapps_data {
+ struct pil_device *pil;
+ struct subsys_device *subsys;
+ struct subsys_desc subsys_desc;
+};
+
static int pil_tzapps_init_image(struct pil_desc *pil, const u8 *metadata,
size_t size)
{
@@ -41,10 +50,28 @@
.shutdown = pil_tzapps_shutdown,
};
+#define subsys_to_drv(d) container_of(d, struct tzapps_data, subsys_desc)
+
+static int tzapps_start(const struct subsys_desc *desc)
+{
+ void *ret;
+
+ ret = pil_get("tzapps");
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+ return 0;
+}
+
+static void tzapps_stop(const struct subsys_desc *desc)
+{
+ struct tzapps_data *drv = subsys_to_drv(desc);
+ pil_put(drv->pil);
+}
+
static int __devinit pil_tzapps_driver_probe(struct platform_device *pdev)
{
struct pil_desc *desc;
- struct pil_device *pil;
+ struct tzapps_data *drv;
if (pas_supported(PAS_TZAPPS) < 0)
return -ENOSYS;
@@ -53,21 +80,38 @@
if (!desc)
return -ENOMEM;
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, drv);
+
desc->name = "tzapps";
desc->dev = &pdev->dev;
desc->ops = &pil_tzapps_ops;
desc->owner = THIS_MODULE;
- pil = msm_pil_register(desc);
- if (IS_ERR(pil))
- return PTR_ERR(pil);
- platform_set_drvdata(pdev, pil);
+ drv->pil = msm_pil_register(desc);
+ if (IS_ERR(drv->pil))
+ return PTR_ERR(drv->pil);
+
+ drv->subsys_desc.name = "tzapps";
+ drv->subsys_desc.dev = &pdev->dev;
+ drv->subsys_desc.owner = THIS_MODULE;
+ drv->subsys_desc.start = tzapps_start;
+ drv->subsys_desc.stop = tzapps_stop;
+
+ drv->subsys = subsys_register(&drv->subsys_desc);
+ if (IS_ERR(drv->subsys)) {
+ msm_pil_unregister(drv->pil);
+ return PTR_ERR(drv->subsys);
+ }
return 0;
}
static int __devexit pil_tzapps_driver_exit(struct platform_device *pdev)
{
- struct pil_device *pil = platform_get_drvdata(pdev);
- msm_pil_unregister(pil);
+ struct tzapps_data *drv = platform_get_drvdata(pdev);
+ subsys_unregister(drv->subsys);
+ msm_pil_unregister(drv->pil);
return 0;
}
diff --git a/arch/arm/mach-msm/pil-venus.c b/arch/arm/mach-msm/pil-venus.c
index e331296..e3125cf 100644
--- a/arch/arm/mach-msm/pil-venus.c
+++ b/arch/arm/mach-msm/pil-venus.c
@@ -28,6 +28,8 @@
#include <mach/iommu.h>
#include <mach/iommu_domains.h>
+#include <mach/subsystem_restart.h>
+#include <mach/peripheral-loader.h>
#include "peripheral-loader.h"
#include "scm-pas.h"
@@ -66,6 +68,8 @@
void __iomem *venus_wrapper_base;
void __iomem *venus_vbif_base;
struct pil_device *pil;
+ struct subsys_device *subsys;
+ struct subsys_desc subsys_desc;
struct regulator *gdsc;
phys_addr_t start_addr;
struct clk *clks[ARRAY_SIZE(clk_names)];
@@ -78,6 +82,8 @@
u32 fw_max_paddr;
};
+#define subsys_to_drv(d) container_of(d, struct venus_data, subsys_desc)
+
static int venus_register_domain(u32 fw_max_sz)
{
struct msm_iova_partition venus_fw_partition = {
@@ -381,6 +387,23 @@
.proxy_unvote = pil_venus_remove_proxy_vote,
};
+static int venus_start(const struct subsys_desc *desc)
+{
+ void *ret;
+ struct venus_data *drv = subsys_to_drv(desc);
+
+ ret = pil_get(drv->subsys_desc.name);
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+ return 0;
+}
+
+static void venus_stop(const struct subsys_desc *desc)
+{
+ struct venus_data *drv = subsys_to_drv(desc);
+ pil_put(drv->pil);
+}
+
static int __devinit pil_venus_probe(struct platform_device *pdev)
{
struct venus_data *drv;
@@ -488,12 +511,25 @@
if (IS_ERR(drv->pil))
return PTR_ERR(drv->pil);
+ drv->subsys_desc.name = desc->name;
+ drv->subsys_desc.owner = THIS_MODULE;
+ drv->subsys_desc.dev = &pdev->dev;
+ drv->subsys_desc.start = venus_start;
+ drv->subsys_desc.stop = venus_stop;
+
+ drv->subsys = subsys_register(&drv->subsys_desc);
+ if (IS_ERR(drv->subsys)) {
+ msm_pil_unregister(drv->pil);
+ return PTR_ERR(drv->subsys);
+ }
+
return 0;
}
static int __devexit pil_venus_remove(struct platform_device *pdev)
{
struct venus_data *drv = platform_get_drvdata(pdev);
+ subsys_unregister(drv->subsys);
msm_pil_unregister(drv->pil);
return 0;
diff --git a/arch/arm/mach-msm/pil-vidc.c b/arch/arm/mach-msm/pil-vidc.c
index e4c6a2d..b2609b1 100644
--- a/arch/arm/mach-msm/pil-vidc.c
+++ b/arch/arm/mach-msm/pil-vidc.c
@@ -17,6 +17,9 @@
#include <linux/err.h>
#include <linux/clk.h>
+#include <mach/peripheral-loader.h>
+#include <mach/subsystem_restart.h>
+
#include "peripheral-loader.h"
#include "scm-pas.h"
@@ -24,6 +27,8 @@
struct clk *smmu_iface;
struct clk *core;
struct pil_device *pil;
+ struct subsys_device *subsys;
+ struct subsys_desc subsys_desc;
};
static int pil_vidc_init_image(struct pil_desc *pil, const u8 *metadata,
@@ -63,6 +68,24 @@
.shutdown = pil_vidc_shutdown,
};
+#define subsys_to_drv(d) container_of(d, struct vidc_data, subsys_desc)
+
+static int vidc_start(const struct subsys_desc *desc)
+{
+ void *ret;
+
+ ret = pil_get("vidc");
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+ return 0;
+}
+
+static void vidc_stop(const struct subsys_desc *desc)
+{
+ struct vidc_data *drv = subsys_to_drv(desc);
+ pil_put(drv->pil);
+}
+
static int __devinit pil_vidc_driver_probe(struct platform_device *pdev)
{
struct pil_desc *desc;
@@ -95,12 +118,25 @@
drv->pil = msm_pil_register(desc);
if (IS_ERR(drv->pil))
return PTR_ERR(drv->pil);
+
+ drv->subsys_desc.name = "vidc";
+ drv->subsys_desc.dev = &pdev->dev;
+ drv->subsys_desc.owner = THIS_MODULE;
+ drv->subsys_desc.start = vidc_start;
+ drv->subsys_desc.stop = vidc_stop;
+
+ drv->subsys = subsys_register(&drv->subsys_desc);
+ if (IS_ERR(drv->subsys)) {
+ msm_pil_unregister(drv->pil);
+ return PTR_ERR(drv->subsys);
+ }
return 0;
}
static int __devexit pil_vidc_driver_exit(struct platform_device *pdev)
{
struct vidc_data *drv = platform_get_drvdata(pdev);
+ subsys_unregister(drv->subsys);
msm_pil_unregister(drv->pil);
return 0;
}
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c
index cb9fc76..bb4da0f 100644
--- a/arch/arm/mm/cache-l2x0.c
+++ b/arch/arm/mm/cache-l2x0.c
@@ -38,6 +38,8 @@
static unsigned int l2x0_ways;
static unsigned long sync_reg_offset = L2X0_CACHE_SYNC;
static void pl310_save(void);
+static void pl310_resume(void);
+static void l2x0_resume(void);
static inline bool is_pl310_rev(int rev)
{
@@ -378,15 +380,18 @@
sync_reg_offset = L2X0_DUMMY_REG;
#endif
outer_cache.set_debug = pl310_set_debug;
+ outer_cache.resume = pl310_resume;
break;
case L2X0_CACHE_ID_PART_L210:
l2x0_ways = (aux >> 13) & 0xf;
type = "L210";
+ outer_cache.resume = l2x0_resume;
break;
default:
/* Assume unknown chips have 8 ways */
l2x0_ways = 8;
type = "L2x0 series";
+ outer_cache.resume = l2x0_resume;
break;
}
diff --git a/block/test-iosched.c b/block/test-iosched.c
index 0a033dc..52070ac 100644
--- a/block/test-iosched.c
+++ b/block/test-iosched.c
@@ -95,6 +95,9 @@
return;
}
+ ptd->test_info.test_duration = jiffies -
+ ptd->test_info.test_duration;
+
test_pr_info("%s: Test is completed", __func__);
test_iosched_mark_test_completion();
@@ -124,7 +127,7 @@
test_rq = (struct test_request *)rq->elv.priv[0];
BUG_ON(!test_rq);
- test_pr_info("%s: request %d completed, err=%d",
+ test_pr_debug("%s: request %d completed, err=%d",
__func__, test_rq->req_id, err);
test_rq->req_completed = true;
@@ -669,6 +672,7 @@
goto error;
}
+ ptd->test_info.test_duration = jiffies;
ret = run_test(ptd);
if (ret) {
test_pr_err("%s: failed to run the test\n", __func__);
@@ -678,6 +682,7 @@
test_pr_info("%s: Waiting for the test completion", __func__);
wait_event(ptd->wait_q, ptd->test_state == TEST_COMPLETED);
+ t_info->test_duration = ptd->test_info.test_duration;
del_timer_sync(&ptd->timeout_timer);
ret = check_test_result(ptd);
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index a37260b..28d0565 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -257,6 +257,7 @@
int mask_check;
int logging_process_id;
struct task_struct *socket_process;
+ struct task_struct *callback_process;
#ifdef CONFIG_DIAG_SDIO_PIPE
unsigned char *buf_in_sdio;
unsigned char *usb_buf_mdm_out;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index c29a1d3f..8a7ae9f 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -262,6 +262,10 @@
(driver->socket_process->tgid == current->tgid)) {
driver->socket_process = NULL;
}
+ if (driver->callback_process &&
+ (driver->callback_process->tgid == current->tgid)) {
+ driver->callback_process = NULL;
+ }
#ifdef CONFIG_DIAG_OVER_USB
/* If the SD logging process exits, change logging to USB mode */
@@ -522,6 +526,11 @@
mutex_lock(&driver->diagchar_mutex);
temp = driver->logging_mode;
driver->logging_mode = (int)ioarg;
+ if (temp == driver->logging_mode) {
+ mutex_unlock(&driver->diagchar_mutex);
+ pr_alert("diag: forbidden logging change requested\n");
+ return 0;
+ }
if (driver->logging_mode == MEMORY_DEVICE_MODE) {
diag_clear_hsic_tbl();
driver->mask_check = 1;
@@ -540,17 +549,17 @@
}
}
}
- if (driver->logging_mode == UART_MODE) {
+ if (driver->logging_mode == UART_MODE ||
+ driver->logging_mode == SOCKET_MODE ||
+ driver->logging_mode == CALLBACK_MODE) {
diag_clear_hsic_tbl();
driver->mask_check = 0;
driver->logging_mode = MEMORY_DEVICE_MODE;
}
- if (driver->logging_mode == SOCKET_MODE) {
- diag_clear_hsic_tbl();
+ if (driver->logging_mode == SOCKET_MODE)
driver->socket_process = current;
- driver->mask_check = 0;
- driver->logging_mode = MEMORY_DEVICE_MODE;
- }
+ if (driver->logging_mode == CALLBACK_MODE)
+ driver->callback_process = current;
driver->logging_process_id = current->tgid;
mutex_unlock(&driver->diagchar_mutex);
if (temp == MEMORY_DEVICE_MODE && driver->logging_mode
@@ -680,7 +689,7 @@
driver->data_ready[index]);
mutex_lock(&driver->diagchar_mutex);
- if ((driver->data_ready[index] & USER_SPACE_LOG_TYPE) && (driver->
+ if ((driver->data_ready[index] & USER_SPACE_DATA_TYPE) && (driver->
logging_mode == MEMORY_DEVICE_MODE)) {
#ifdef CONFIG_DIAG_BRIDGE_CODE
unsigned long spin_lock_flags;
@@ -689,7 +698,7 @@
pr_debug("diag: process woken up\n");
/*Copy the type of data being passed*/
- data_type = driver->data_ready[index] & USER_SPACE_LOG_TYPE;
+ data_type = driver->data_ready[index] & USER_SPACE_DATA_TYPE;
COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
/* place holder for number of data field */
ret += 4;
@@ -892,7 +901,7 @@
/* copy number of data fields */
COPY_USER_SPACE_OR_EXIT(buf+4, num_data, 4);
ret -= 4;
- driver->data_ready[index] ^= USER_SPACE_LOG_TYPE;
+ driver->data_ready[index] ^= USER_SPACE_DATA_TYPE;
if (driver->ch)
queue_work(driver->diag_wq,
&(driver->diag_read_smd_work));
@@ -909,10 +918,10 @@
#endif
APPEND_DEBUG('n');
goto exit;
- } else if (driver->data_ready[index] & USER_SPACE_LOG_TYPE) {
+ } else if (driver->data_ready[index] & USER_SPACE_DATA_TYPE) {
/* In case, the thread wakes up and the logging mode is
not memory device any more, the condition needs to be cleared */
- driver->data_ready[index] ^= USER_SPACE_LOG_TYPE;
+ driver->data_ready[index] ^= USER_SPACE_DATA_TYPE;
}
if (driver->data_ready[index] & DEINIT_TYPE) {
@@ -1026,7 +1035,7 @@
payload_size);
return err;
}
- if (pkt_type == USER_SPACE_LOG_TYPE) {
+ if (pkt_type == USER_SPACE_DATA_TYPE) {
err = copy_from_user(driver->user_space_data, buf + 4,
payload_size);
/* Check masks for On-Device logging */
@@ -1409,6 +1418,7 @@
driver->num_clients = max_clients;
driver->logging_mode = USB_MODE;
driver->socket_process = NULL;
+ driver->callback_process = NULL;
driver->mask_check = 0;
mutex_init(&driver->diagchar_mutex);
init_waitqueue_head(&driver->wait_q);
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index d27ebcf..978b63b 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -212,8 +212,8 @@
* have their data read/logged. Detect and remedy this
* situation.
*/
- if ((driver->data_ready[i] & USER_SPACE_LOG_TYPE) == 0) {
- driver->data_ready[i] |= USER_SPACE_LOG_TYPE;
+ if ((driver->data_ready[i] & USER_SPACE_DATA_TYPE) == 0) {
+ driver->data_ready[i] |= USER_SPACE_DATA_TYPE;
pr_debug("diag: Force wakeup of logging process\n");
wake_up_interruptible(&driver->wait_q);
}
@@ -357,7 +357,7 @@
driver->logging_process_id)
break;
if (i < driver->num_clients) {
- driver->data_ready[i] |= USER_SPACE_LOG_TYPE;
+ driver->data_ready[i] |= USER_SPACE_DATA_TYPE;
pr_debug("diag: wake up logging process\n");
wake_up_interruptible(&driver->wait_q);
} else
diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index 8a9567b..1312448 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -382,7 +382,7 @@
break;
case QCE_MODE_XTS:
- if (creq->encklen == AES128_KEY_SIZE)
+ if (creq->encklen/2 == AES128_KEY_SIZE)
*cmdlistinfo = &cmdlistptr->cipher_aes_128_xts;
else
*cmdlistinfo = &cmdlistptr->cipher_aes_256_xts;
@@ -767,15 +767,30 @@
if ((pce_dev->mode == QCE_MODE_CTR) ||
(pce_dev->mode == QCE_MODE_XTS)) {
uint32_t num_blk = 0;
- uint32_t cntr_iv = 0;
+ uint32_t cntr_iv3 = 0;
+ unsigned long long cntr_iv64 = 0;
+ unsigned char *b = (unsigned char *)(&cntr_iv3);
memcpy(iv, areq->info, sizeof(iv));
- if (pce_dev->mode == QCE_MODE_CTR)
+ if (pce_dev->mode != QCE_MODE_XTS)
num_blk = areq->nbytes/16;
- cntr_iv = (u32)(((u32)(*(iv + 14))) << 8) |
- (u32)(*(iv + 15));
- *(iv + 14) = (char)((cntr_iv + num_blk) >> 8);
- *(iv + 15) = (char)((cntr_iv + num_blk) & 0xFF);
+ else
+ num_blk = 1;
+ cntr_iv3 = ((*(iv + 12) << 24) & 0xff000000) |
+ (((*(iv + 13)) << 16) & 0xff0000) |
+ (((*(iv + 14)) << 8) & 0xff00) |
+ (*(iv + 15) & 0xff);
+ cntr_iv64 =
+ (((unsigned long long)cntr_iv3 &
+ (unsigned long long)0xFFFFFFFFULL) +
+ (unsigned long long)num_blk) %
+ (unsigned long long)(0x100000000ULL);
+
+ cntr_iv3 = (u32)(cntr_iv64 & 0xFFFFFFFF);
+ *(iv + 15) = (char)(*b);
+ *(iv + 14) = (char)(*(b + 1));
+ *(iv + 13) = (char)(*(b + 2));
+ *(iv + 12) = (char)(*(b + 3));
}
} else {
memcpy(iv,
@@ -1456,10 +1471,11 @@
0, &pcl_info->encr_xts_key);
for (i = 1; i < xts_key_reg; i++)
qce_add_cmd_element(pdev, &ce_vaddr,
- (CRYPTO_ENCR_KEY0_REG + i * sizeof(uint32_t)),
- 0, NULL);
+ (CRYPTO_ENCR_XTS_KEY0_REG +
+ i * sizeof(uint32_t)), 0, NULL);
qce_add_cmd_element(pdev, &ce_vaddr,
- CRYPTO_ENCR_XTS_DU_SIZE_REG, 0, NULL);
+ CRYPTO_ENCR_XTS_DU_SIZE_REG, 0,
+ &pcl_info->encr_xts_du_size);
}
if (iv_reg) {
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CNTR0_IV0_REG, 0,
@@ -1624,13 +1640,6 @@
0, &pcl_info->auth_seg_size);
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_CFG_REG,
0, &pcl_info->auth_seg_size);
- } else {
- qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_SIZE_REG,
- 0, &pcl_info->auth_seg_size);
- qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_CFG_REG,
- 0, &pcl_info->auth_seg_size);
- qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_AUTH_SEG_START_REG,
- 0, &pcl_info->auth_seg_size);
}
qce_add_cmd_element(pdev, &ce_vaddr, CRYPTO_CONFIG_REG,
(crypto_cfg | CRYPTO_LITTLE_ENDIAN_MASK),
diff --git a/drivers/crypto/msm/qcryptohw_50.h b/drivers/crypto/msm/qcryptohw_50.h
index 1c904ed..d77311d 100644
--- a/drivers/crypto/msm/qcryptohw_50.h
+++ b/drivers/crypto/msm/qcryptohw_50.h
@@ -144,7 +144,7 @@
#define CRYPTO_ENCR_CCM_INT_CNTR2_REG 0x1A228
#define CRYPTO_ENCR_CCM_INT_CNTR3_REG 0x1A22C
-#define CRYPTO_ENCR_XTS_DU_SIZE_REG 0xA1230
+#define CRYPTO_ENCR_XTS_DU_SIZE_REG 0x1A230
#define CRYPTO_AUTH_SEG_CFG_REG 0x1A300
#define CRYPTO_AUTH_SEG_SIZE_REG 0x1A304
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 18784ac..67cb34a 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -1840,7 +1840,7 @@
goto err;
/* now, wait for the GPU to finish its operations */
- wait_time = jiffies + ADRENO_IDLE_TIMEOUT;
+ wait_time = jiffies + msecs_to_jiffies(ADRENO_IDLE_TIMEOUT);
wait_time_part = jiffies + msecs_to_jiffies(KGSL_TIMEOUT_PART);
while (time_before(jiffies, wait_time)) {
@@ -1869,18 +1869,46 @@
KGSL_DRV_ERR(device, "spun too long waiting for RB to idle\n");
if (KGSL_STATE_DUMP_AND_RECOVER != device->state &&
!adreno_dump_and_recover(device)) {
- wait_time = jiffies + ADRENO_IDLE_TIMEOUT;
+ wait_time = jiffies + msecs_to_jiffies(ADRENO_IDLE_TIMEOUT);
goto retry;
}
return -ETIMEDOUT;
}
+/**
+ * is_adreno_rbbm_status_idle - Check if GPU core is idle by probing
+ * rbbm_status register
+ * @device - Pointer to the GPU device whose idle status is to be
+ * checked
+ * @returns - Returns whether the core is idle (based on rbbm_status)
+ * false if the core is active, true if the core is idle
+ */
+static bool is_adreno_rbbm_status_idle(struct kgsl_device *device)
+{
+ unsigned int reg_rbbm_status;
+ bool status = false;
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+ /* Is the core idle? */
+ adreno_regread(device,
+ adreno_dev->gpudev->reg_rbbm_status,
+ ®_rbbm_status);
+
+ if (adreno_is_a2xx(adreno_dev)) {
+ if (reg_rbbm_status == 0x110)
+ status = true;
+ } else {
+ if (!(reg_rbbm_status & 0x80000000))
+ status = true;
+ }
+ return status;
+}
+
static unsigned int adreno_isidle(struct kgsl_device *device)
{
int status = false;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
- unsigned int rbbm_status;
WARN_ON(device->state == KGSL_STATE_INIT);
/* If the device isn't active, don't force it on. */
@@ -1889,17 +1917,7 @@
GSL_RB_GET_READPTR(rb, &rb->rptr);
if (!device->active_cnt && (rb->rptr == rb->wptr)) {
/* Is the core idle? */
- adreno_regread(device,
- adreno_dev->gpudev->reg_rbbm_status,
- &rbbm_status);
-
- if (adreno_is_a2xx(adreno_dev)) {
- if (rbbm_status == 0x110)
- status = true;
- } else {
- if (!(rbbm_status & 0x80000000))
- status = true;
- }
+ status = is_adreno_rbbm_status_idle(device);
}
} else {
status = true;
@@ -2142,7 +2160,7 @@
if (!adreno_dev->fast_hang_detect)
return 0;
- if (device->ftbl->isidle(device))
+ if (is_adreno_rbbm_status_idle(device))
return 0;
for (i = 0; i < hang_detect_regs_count; i++) {
diff --git a/drivers/gpu/msm/adreno_a2xx_snapshot.c b/drivers/gpu/msm/adreno_a2xx_snapshot.c
index 282440c..ce74c1b 100644
--- a/drivers/gpu/msm/adreno_a2xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a2xx_snapshot.c
@@ -224,6 +224,31 @@
return DEBUG_SECTION_SZ(MIUDEBUG_COUNT);
}
+/* Snapshot the istore memory */
+static int a2xx_snapshot_istore(struct kgsl_device *device, void *snapshot,
+ int remain, void *priv)
+{
+ struct kgsl_snapshot_istore *header = snapshot;
+ unsigned int *data = snapshot + sizeof(*header);
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ int count, i;
+
+ count = adreno_dev->istore_size * adreno_dev->instruction_size;
+
+ if (remain < (count * 4) + sizeof(*header)) {
+ KGSL_DRV_ERR(device,
+ "snapshot: Not enough memory for the istore section");
+ return 0;
+ }
+
+ header->count = adreno_dev->istore_size;
+
+ for (i = 0; i < count; i++)
+ kgsl_regread(device, ADRENO_ISTORE_START + i, &data[i]);
+
+ return (count * 4) + sizeof(*header);
+}
+
/* A2XX GPU snapshot function - this is where all of the A2XX specific
* bits and pieces are grabbed into the snapshot memory
*/
@@ -338,6 +363,18 @@
}
}
+ /*
+ * Only dump the istore on a hang - reading it on a running system
+ * has a non zero chance of hanging the GPU.
+ */
+
+ if (adreno_is_a2xx(adreno_dev) && hang) {
+ snapshot = kgsl_snapshot_add_section(device,
+ KGSL_SNAPSHOT_SECTION_ISTORE, snapshot, remain,
+ a2xx_snapshot_istore, NULL);
+ }
+
+
/* Reset the clock gating */
adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, pmoverride);
diff --git a/drivers/gpu/msm/adreno_a3xx_snapshot.c b/drivers/gpu/msm/adreno_a3xx_snapshot.c
index a410445..e4f5733 100644
--- a/drivers/gpu/msm/adreno_a3xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a3xx_snapshot.c
@@ -220,30 +220,46 @@
return DEBUG_SECTION_SZ(size);
}
-#define DEBUGFS_BLOCK_SIZE 0x40
+struct debugbus_block {
+ unsigned int block_id;
+ unsigned int dwords;
+};
static int a3xx_snapshot_debugbus_block(struct kgsl_device *device,
void *snapshot, int remain, void *priv)
{
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
struct kgsl_snapshot_debugbus *header = snapshot;
- unsigned int id = (unsigned int) priv;
+ struct debugbus_block *block = priv;
unsigned int val;
int i;
unsigned int *data = snapshot + sizeof(*header);
- int size =
- (DEBUGFS_BLOCK_SIZE * sizeof(unsigned int)) + sizeof(*header);
+ unsigned int dwords;
+ int size;
+
+ /*
+ * For A305 and A320 all debug bus regions are the same size (0x40). For
+ * A330, they can be different sizes - most are still 0x40, but some
+ * like CP are larger
+ */
+
+ dwords = adreno_is_a330(adreno_dev) ?
+ block->dwords : 0x40;
+
+ size = (dwords * sizeof(unsigned int)) + sizeof(*header);
if (remain < size) {
SNAPSHOT_ERR_NOMEM(device, "DEBUGBUS");
return 0;
}
- val = (id << 8) | (1 << 16);
+ val = (block->block_id << 8) | (1 << 16);
- header->id = id;
- header->count = DEBUGFS_BLOCK_SIZE;
+ header->id = block->block_id;
+ header->count = dwords;
- for (i = 0; i < DEBUGFS_BLOCK_SIZE; i++) {
+ for (i = 0; i < dwords; i++) {
adreno_regwrite(device, A3XX_RBBM_DEBUG_BUS_CTL, val | i);
adreno_regread(device, A3XX_RBBM_DEBUG_BUS_DATA_STATUS,
&data[i]);
@@ -252,34 +268,34 @@
return size;
}
-static unsigned int debugbus_blocks[] = {
- RBBM_BLOCK_ID_CP,
- RBBM_BLOCK_ID_RBBM,
- RBBM_BLOCK_ID_VBIF,
- RBBM_BLOCK_ID_HLSQ,
- RBBM_BLOCK_ID_UCHE,
- RBBM_BLOCK_ID_PC,
- RBBM_BLOCK_ID_VFD,
- RBBM_BLOCK_ID_VPC,
- RBBM_BLOCK_ID_TSE,
- RBBM_BLOCK_ID_RAS,
- RBBM_BLOCK_ID_VSC,
- RBBM_BLOCK_ID_SP_0,
- RBBM_BLOCK_ID_SP_1,
- RBBM_BLOCK_ID_SP_2,
- RBBM_BLOCK_ID_SP_3,
- RBBM_BLOCK_ID_TPL1_0,
- RBBM_BLOCK_ID_TPL1_1,
- RBBM_BLOCK_ID_TPL1_2,
- RBBM_BLOCK_ID_TPL1_3,
- RBBM_BLOCK_ID_RB_0,
- RBBM_BLOCK_ID_RB_1,
- RBBM_BLOCK_ID_RB_2,
- RBBM_BLOCK_ID_RB_3,
- RBBM_BLOCK_ID_MARB_0,
- RBBM_BLOCK_ID_MARB_1,
- RBBM_BLOCK_ID_MARB_2,
- RBBM_BLOCK_ID_MARB_3,
+static struct debugbus_block debugbus_blocks[] = {
+ { RBBM_BLOCK_ID_CP, 0x52, },
+ { RBBM_BLOCK_ID_RBBM, 0x40, },
+ { RBBM_BLOCK_ID_VBIF, 0x40, },
+ { RBBM_BLOCK_ID_HLSQ, 0x40, },
+ { RBBM_BLOCK_ID_UCHE, 0x40, },
+ { RBBM_BLOCK_ID_PC, 0x40, },
+ { RBBM_BLOCK_ID_VFD, 0x40, },
+ { RBBM_BLOCK_ID_VPC, 0x40, },
+ { RBBM_BLOCK_ID_TSE, 0x40, },
+ { RBBM_BLOCK_ID_RAS, 0x40, },
+ { RBBM_BLOCK_ID_VSC, 0x40, },
+ { RBBM_BLOCK_ID_SP_0, 0x40, },
+ { RBBM_BLOCK_ID_SP_1, 0x40, },
+ { RBBM_BLOCK_ID_SP_2, 0x40, },
+ { RBBM_BLOCK_ID_SP_3, 0x40, },
+ { RBBM_BLOCK_ID_TPL1_0, 0x40, },
+ { RBBM_BLOCK_ID_TPL1_1, 0x40, },
+ { RBBM_BLOCK_ID_TPL1_2, 0x40, },
+ { RBBM_BLOCK_ID_TPL1_3, 0x40, },
+ { RBBM_BLOCK_ID_RB_0, 0x40, },
+ { RBBM_BLOCK_ID_RB_1, 0x40, },
+ { RBBM_BLOCK_ID_RB_2, 0x40, },
+ { RBBM_BLOCK_ID_RB_3, 0x40, },
+ { RBBM_BLOCK_ID_MARB_0, 0x40, },
+ { RBBM_BLOCK_ID_MARB_1, 0x40, },
+ { RBBM_BLOCK_ID_MARB_2, 0x40, },
+ { RBBM_BLOCK_ID_MARB_3, 0x40, },
};
static void *a3xx_snapshot_debugbus(struct kgsl_device *device,
@@ -291,7 +307,7 @@
snapshot = kgsl_snapshot_add_section(device,
KGSL_SNAPSHOT_SECTION_DEBUGBUS, snapshot, remain,
a3xx_snapshot_debugbus_block,
- (void *) debugbus_blocks[i]);
+ (void *) &debugbus_blocks[i]);
}
return snapshot;
@@ -307,6 +323,7 @@
struct kgsl_device *device = &adreno_dev->dev;
struct kgsl_snapshot_registers_list list;
struct kgsl_snapshot_registers regs[2];
+ int size;
regs[0].regs = (unsigned int *) a3xx_registers;
regs[0].count = a3xx_registers_count;
@@ -326,10 +343,14 @@
KGSL_SNAPSHOT_SECTION_REGS, snapshot, remain,
kgsl_snapshot_dump_regs, &list);
- /* CP_STATE_DEBUG indexed registers */
+ /*
+ * CP_STATE_DEBUG indexed registers - 20 on 305 and 320 and 46 on A330
+ */
+ size = adreno_is_a330(adreno_dev) ? 0x2E : 0x14;
+
snapshot = kgsl_snapshot_indexed_registers(device, snapshot,
remain, REG_CP_STATE_DEBUG_INDEX,
- REG_CP_STATE_DEBUG_DATA, 0x0, 0x14);
+ REG_CP_STATE_DEBUG_DATA, 0x0, size);
/* CP_ME indexed registers */
snapshot = kgsl_snapshot_indexed_registers(device, snapshot,
diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c
index 93be980..696073f 100644
--- a/drivers/gpu/msm/adreno_snapshot.c
+++ b/drivers/gpu/msm/adreno_snapshot.c
@@ -510,10 +510,28 @@
break;
if (pkt_is_type3(src[i])) {
- if (adreno_cmd_is_ib(src[i]))
- ib_add_gpu_object(device, ptbase,
- src[i + 1], src[i + 2]);
- else
+ if (adreno_cmd_is_ib(src[i])) {
+ unsigned int gpuaddr = src[i + 1];
+ unsigned int size = src[i + 2];
+ unsigned int ibbase;
+
+ /* Address of the last processed IB2 */
+ kgsl_regread(device, REG_CP_IB2_BASE, &ibbase);
+
+ /*
+ * If this is the last IB2 that was executed,
+ * then push it to make sure it goes into the
+ * static space
+ */
+
+ if (ibbase == gpuaddr)
+ push_object(device,
+ SNAPSHOT_OBJ_TYPE_IB, ptbase,
+ gpuaddr, size);
+ else
+ ib_add_gpu_object(device, ptbase,
+ gpuaddr, size);
+ } else
ib_parse_type3(device, &src[i], ptbase);
} else if (pkt_is_type0(src[i])) {
ib_parse_type0(device, &src[i], ptbase);
@@ -529,31 +547,6 @@
snapshot_frozen_objsize += ret;
}
-/* Snapshot the istore memory */
-static int snapshot_istore(struct kgsl_device *device, void *snapshot,
- int remain, void *priv)
-{
- struct kgsl_snapshot_istore *header = snapshot;
- unsigned int *data = snapshot + sizeof(*header);
- struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- int count, i;
-
- count = adreno_dev->istore_size * adreno_dev->instruction_size;
-
- if (remain < (count * 4) + sizeof(*header)) {
- KGSL_DRV_ERR(device,
- "snapshot: Not enough memory for the istore section");
- return 0;
- }
-
- header->count = adreno_dev->istore_size;
-
- for (i = 0; i < count; i++)
- kgsl_regread(device, ADRENO_ISTORE_START + i, &data[i]);
-
- return (count * 4) + sizeof(*header);
-}
-
/* Snapshot the ringbuffer memory */
static int snapshot_rb(struct kgsl_device *device, void *snapshot,
int remain, void *priv)
@@ -870,17 +863,6 @@
for (i = 0; i < objbufptr; i++)
snapshot = dump_object(device, i, snapshot, remain);
- /*
- * Only dump the istore on a hang - reading it on a running system
- * has a non 0 chance of hanging the GPU
- */
-
- if (hang) {
- snapshot = kgsl_snapshot_add_section(device,
- KGSL_SNAPSHOT_SECTION_ISTORE, snapshot, remain,
- snapshot_istore, NULL);
- }
-
/* Add GPU specific sections - registers mainly, but other stuff too */
if (adreno_dev->gpudev->snapshot)
snapshot = adreno_dev->gpudev->snapshot(adreno_dev, snapshot,
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 7194e47..1b1f0ac 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -1710,6 +1710,8 @@
else
memtype = param->memtype;
+ entry->memdesc.flags = param->flags;
+
switch (memtype) {
case KGSL_USER_MEM_TYPE_PMEM:
if (param->fd == 0 || param->len == 0)
@@ -1768,12 +1770,10 @@
if (result)
goto error;
- entry->memdesc.priv |= param->flags & KGSL_MEMTYPE_MASK;
-
if (entry->memdesc.size >= SZ_1M)
- entry->memdesc.priv |= ilog2(SZ_1M) << KGSL_MEMALIGN_SHIFT;
+ kgsl_memdesc_set_align(&entry->memdesc, ilog2(SZ_1M));
else if (entry->memdesc.size >= SZ_64K)
- entry->memdesc.priv |= ilog2(SZ_64K) << KGSL_MEMALIGN_SHIFT;
+ kgsl_memdesc_set_align(&entry->memdesc, ilog2(SZ_64));
result = kgsl_mmu_map(private->pagetable,
&entry->memdesc,
@@ -2538,6 +2538,9 @@
KGSL_LOG_DUMP(device, "POWER: INTERVAL TIMEOUT = %08X ",
pwr->interval_timeout);
+ KGSL_LOG_DUMP(device, "POWER: NAP ALLOWED = %d | START_STOP_SLEEP_WAKE = %d\n",
+ pwr->nap_allowed, pwr->strtstp_sleepwake);
+
KGSL_LOG_DUMP(device, "GRP_CLK = %lu ",
kgsl_get_clkrate(pwr->grp_clks[0]));
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index 56950a6..17a5b67 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -124,7 +124,10 @@
int (*map_kernel_mem)(struct kgsl_memdesc *);
};
+/* Internal definitions for memdesc->priv */
#define KGSL_MEMDESC_GUARD_PAGE BIT(0)
+/* Set if the memdesc is mapped into all pagetables */
+#define KGSL_MEMDESC_GLOBAL BIT(1)
/* shared memory allocation */
struct kgsl_memdesc {
@@ -134,12 +137,12 @@
unsigned int gpuaddr;
unsigned int physaddr;
unsigned int size;
- unsigned int priv;
+ unsigned int priv; /* Internal flags and settings */
struct scatterlist *sg;
unsigned int sglen; /* Active entries in the sglist */
unsigned int sglen_alloc; /* Allocated entries in the sglist */
struct kgsl_memdesc_ops *ops;
- int flags;
+ unsigned int flags; /* Flags set from userspace */
};
/* List of different memory entry types */
diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c
index b49c260..52097dc 100644
--- a/drivers/gpu/msm/kgsl_debugfs.c
+++ b/drivers/gpu/msm/kgsl_debugfs.c
@@ -163,6 +163,16 @@
return "unknown";
}
+static char get_alignflag(const struct kgsl_memdesc *m)
+{
+ int align = kgsl_memdesc_get_align(m);
+ if (align >= ilog2(SZ_1M))
+ return 'L';
+ else if (align >= ilog2(SZ_64K))
+ return 'l';
+ return '-';
+}
+
static int process_mem_print(struct seq_file *s, void *unused)
{
struct kgsl_mem_entry *entry;
@@ -170,7 +180,6 @@
struct kgsl_process_private *private = s->private;
char flags[4];
char usage[16];
- unsigned int align;
spin_lock(&private->mem_lock);
seq_printf(s, "%8s %8s %5s %10s %16s %5s\n",
@@ -181,20 +190,12 @@
entry = rb_entry(node, struct kgsl_mem_entry, node);
m = &entry->memdesc;
- flags[0] = m->priv & KGSL_MEMFLAGS_GLOBAL ? 'g' : '-';
- flags[1] = m->priv & KGSL_MEMFLAGS_GPUREADONLY ? 'r' : '-';
-
- align = (m->priv & KGSL_MEMALIGN_MASK) >> KGSL_MEMALIGN_SHIFT;
- if (align >= ilog2(SZ_1M))
- flags[2] = 'L';
- else if (align >= ilog2(SZ_64K))
- flags[2] = 'l';
- else
- flags[2] = '-';
-
+ flags[0] = m->priv & KGSL_MEMDESC_GLOBAL ? 'g' : '-';
+ flags[1] = m->flags & KGSL_MEMFLAGS_GPUREADONLY ? 'r' : '-';
+ flags[2] = get_alignflag(m);
flags[3] = '\0';
- kgsl_get_memory_usage(usage, sizeof(usage), m->priv);
+ kgsl_get_memory_usage(usage, sizeof(usage), m->flags);
seq_printf(s, "%08x %8d %5s %10s %16s %5d\n",
m->gpuaddr, m->size, flags,
diff --git a/drivers/gpu/msm/kgsl_drm.c b/drivers/gpu/msm/kgsl_drm.c
index 870a7d7..2003098 100644
--- a/drivers/gpu/msm/kgsl_drm.c
+++ b/drivers/gpu/msm/kgsl_drm.c
@@ -237,14 +237,16 @@
}
}
+ /* Set the flags for the memdesc (probably 0, unless it is cached) */
+ priv->memdesc.priv = 0;
+
if (TYPE_IS_PMEM(priv->type)) {
if (priv->type == DRM_KGSL_GEM_TYPE_EBI ||
priv->type & DRM_KGSL_GEM_PMEM_EBI) {
result = kgsl_sharedmem_ebimem_user(
&priv->memdesc,
priv->pagetable,
- obj->size * priv->bufcount,
- 0);
+ obj->size * priv->bufcount);
if (result) {
DRM_ERROR(
"Unable to allocate PMEM memory\n");
@@ -262,7 +264,7 @@
result = kgsl_sharedmem_page_alloc_user(&priv->memdesc,
priv->pagetable,
- obj->size * priv->bufcount, 0);
+ obj->size * priv->bufcount);
if (result != 0) {
DRM_ERROR(
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 62108f2..07ea48e 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -792,13 +792,13 @@
if (msm_soc_version_supports_iommu_v1()) {
for (i = 0; i < iommu->unit_count; i++) {
iommu->iommu_units[i].reg_map.priv |=
- KGSL_MEMFLAGS_GLOBAL;
+ KGSL_MEMDESC_GLOBAL;
status = kgsl_mmu_map(pagetable,
&(iommu->iommu_units[i].reg_map),
GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
if (status) {
iommu->iommu_units[i].reg_map.priv &=
- ~KGSL_MEMFLAGS_GLOBAL;
+ ~KGSL_MEMDESC_GLOBAL;
goto err;
}
}
@@ -808,7 +808,7 @@
for (i--; i >= 0; i--) {
kgsl_mmu_unmap(pagetable,
&(iommu->iommu_units[i].reg_map));
- iommu->iommu_units[i].reg_map.priv &= ~KGSL_MEMFLAGS_GLOBAL;
+ iommu->iommu_units[i].reg_map.priv &= ~KGSL_MEMDESC_GLOBAL;
}
if (mmu->priv_bank_table) {
kgsl_mmu_putpagetable(mmu->priv_bank_table);
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index dbb88ee..68cd167 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -593,7 +593,7 @@
_get_pool(struct kgsl_pagetable *pagetable, unsigned int flags)
{
if (pagetable->kgsl_pool &&
- (KGSL_MEMFLAGS_GLOBAL & flags))
+ (KGSL_MEMDESC_GLOBAL & flags))
return pagetable->kgsl_pool;
return pagetable->pool;
}
@@ -637,10 +637,9 @@
* the address space is so small.
*/
if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_get_mmutype() &&
- (memdesc->priv & KGSL_MEMALIGN_MASK)) {
- page_align = (memdesc->priv & KGSL_MEMALIGN_MASK)
- >> KGSL_MEMALIGN_SHIFT;
- }
+ kgsl_memdesc_get_align(memdesc) > 0)
+ page_align = kgsl_memdesc_get_align(memdesc);
+
memdesc->gpuaddr = gen_pool_alloc_aligned(pool, size, page_align);
if (memdesc->gpuaddr == 0) {
KGSL_CORE_ERR("gen_pool_alloc(%d) failed from pool: %s\n",
@@ -719,7 +718,7 @@
* Don't clear the gpuaddr on global mappings because they
* may be in use by other pagetables
*/
- if (!(memdesc->priv & KGSL_MEMFLAGS_GLOBAL))
+ if (!(memdesc->priv & KGSL_MEMDESC_GLOBAL))
memdesc->gpuaddr = 0;
return 0;
}
@@ -740,8 +739,7 @@
return 0;
gpuaddr = memdesc->gpuaddr;
- memdesc->priv |= KGSL_MEMFLAGS_GLOBAL
- | (KGSL_MEMTYPE_KERNEL << KGSL_MEMTYPE_SHIFT);
+ memdesc->priv |= KGSL_MEMDESC_GLOBAL;
result = kgsl_mmu_map(pagetable, memdesc, protflags);
if (result)
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 739dcb5..422bd55 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -23,6 +23,7 @@
#include "kgsl_pwrscale.h"
#include "kgsl_device.h"
#include "kgsl_trace.h"
+#include "kgsl_sharedmem.h"
#define KGSL_PWRFLAGS_POWER_ON 0
#define KGSL_PWRFLAGS_CLK_ON 1
@@ -951,6 +952,11 @@
void kgsl_pwrctrl_wake(struct kgsl_device *device)
{
int status;
+ unsigned int context_id;
+ unsigned int state = device->state;
+ unsigned int ts_processed = 0xdeaddead;
+ struct kgsl_context *context;
+
kgsl_pwrctrl_request_state(device, KGSL_STATE_ACTIVE);
switch (device->state) {
case KGSL_STATE_SLUMBER:
@@ -964,6 +970,17 @@
case KGSL_STATE_SLEEP:
kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_ON);
kgsl_pwrscale_wake(device);
+ kgsl_sharedmem_readl(&device->memstore,
+ (unsigned int *) &context_id,
+ KGSL_MEMSTORE_OFFSET(KGSL_MEMSTORE_GLOBAL,
+ current_context));
+ context = idr_find(&device->context_idr, context_id);
+ if (context)
+ ts_processed = kgsl_readtimestamp(device, context,
+ KGSL_TIMESTAMP_RETIRED);
+ KGSL_PWR_INFO(device, "Wake from %s state. CTXT: %d RTRD TS: %08X\n",
+ kgsl_pwrstate_to_str(state),
+ context ? context->id : -1, ts_processed);
/* fall through */
case KGSL_STATE_NAP:
/* Turn on the core clocks */
diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c
index 53dc468..a70647a 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.c
+++ b/drivers/gpu/msm/kgsl_sharedmem.c
@@ -367,7 +367,7 @@
int sglen = memdesc->sglen;
/* Don't free the guard page if it was used */
- if (memdesc->flags & KGSL_MEMDESC_GUARD_PAGE)
+ if (memdesc->priv & KGSL_MEMDESC_GUARD_PAGE)
sglen--;
kgsl_driver.stats.page_alloc -= memdesc->size;
@@ -405,7 +405,7 @@
int i, count = 0;
/* Don't map the guard page if it exists */
- if (memdesc->flags & KGSL_MEMDESC_GUARD_PAGE)
+ if (memdesc->priv & KGSL_MEMDESC_GUARD_PAGE)
sglen--;
/* create a list of pages to call vmap */
@@ -534,7 +534,7 @@
static int
_kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc,
struct kgsl_pagetable *pagetable,
- size_t size, unsigned int flags, unsigned int protflags)
+ size_t size, unsigned int protflags)
{
int pcount = 0, order, ret = 0;
int j, len, page_size, sglen_alloc, sglen = 0;
@@ -562,10 +562,12 @@
if (size >= ((si.freeram << PAGE_SHIFT) - SZ_32M))
return -ENOMEM;
- align = (flags & KGSL_MEMALIGN_MASK) >> KGSL_MEMALIGN_SHIFT;
+ align = (memdesc->flags & KGSL_MEMALIGN_MASK) >> KGSL_MEMALIGN_SHIFT;
page_size = (align >= ilog2(SZ_64K) && size >= SZ_64K)
? SZ_64K : PAGE_SIZE;
+ /* update align flags for what we actually use */
+ kgsl_memdesc_set_align(memdesc, ilog2(page_size));
/*
* There needs to be enough room in the sg structure to be able to
@@ -584,7 +586,6 @@
memdesc->size = size;
memdesc->pagetable = pagetable;
- memdesc->priv |= (flags & KGSL_MEMALIGN_MASK);
memdesc->ops = &kgsl_page_alloc_ops;
memdesc->sg = kgsl_sg_alloc(sglen_alloc);
@@ -664,7 +665,7 @@
if (kgsl_guard_page != NULL) {
sg_set_page(&memdesc->sg[sglen++], kgsl_guard_page,
PAGE_SIZE, 0);
- memdesc->flags |= KGSL_MEMDESC_GUARD_PAGE;
+ memdesc->priv |= KGSL_MEMDESC_GUARD_PAGE;
}
}
@@ -741,7 +742,7 @@
size = ALIGN(size, PAGE_SIZE * 2);
ret = _kgsl_sharedmem_page_alloc(memdesc, pagetable, size,
- 0, GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
+ GSL_PT_PAGE_RV | GSL_PT_PAGE_WV);
if (!ret)
ret = kgsl_page_alloc_map_kernel(memdesc);
if (ret)
@@ -753,7 +754,7 @@
int
kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
struct kgsl_pagetable *pagetable,
- size_t size, int flags)
+ size_t size)
{
unsigned int protflags;
@@ -761,11 +762,11 @@
return -EINVAL;
protflags = GSL_PT_PAGE_RV;
- if (!(flags & KGSL_MEMFLAGS_GPUREADONLY))
+ if (!(memdesc->flags & KGSL_MEMFLAGS_GPUREADONLY))
protflags |= GSL_PT_PAGE_WV;
return _kgsl_sharedmem_page_alloc(memdesc, pagetable, size,
- flags, protflags);
+ protflags);
}
EXPORT_SYMBOL(kgsl_sharedmem_page_alloc_user);
@@ -862,7 +863,7 @@
int
kgsl_sharedmem_ebimem_user(struct kgsl_memdesc *memdesc,
struct kgsl_pagetable *pagetable,
- size_t size, int flags)
+ size_t size)
{
size = ALIGN(size, PAGE_SIZE);
return _kgsl_sharedmem_ebimem(memdesc, pagetable, size);
diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h
index 5a6c4c2..92a6f27 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.h
+++ b/drivers/gpu/msm/kgsl_sharedmem.h
@@ -20,6 +20,8 @@
#include <linux/slab.h>
#include <linux/kmemleak.h>
+#include "kgsl_log.h"
+
struct kgsl_device;
struct kgsl_process_private;
@@ -27,9 +29,6 @@
#define KGSL_CACHE_OP_FLUSH 0x02
#define KGSL_CACHE_OP_CLEAN 0x03
-/** Set if the memdesc is mapped into all pagetables */
-#define KGSL_MEMFLAGS_GLOBAL 0x00000002
-
extern struct kgsl_memdesc_ops kgsl_page_alloc_ops;
int kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc,
@@ -37,13 +36,13 @@
int kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
struct kgsl_pagetable *pagetable,
- size_t size, int flags);
+ size_t size);
int kgsl_sharedmem_alloc_coherent(struct kgsl_memdesc *memdesc, size_t size);
int kgsl_sharedmem_ebimem_user(struct kgsl_memdesc *memdesc,
struct kgsl_pagetable *pagetable,
- size_t size, int flags);
+ size_t size);
int kgsl_sharedmem_ebimem(struct kgsl_memdesc *memdesc,
struct kgsl_pagetable *pagetable,
@@ -71,6 +70,36 @@
int kgsl_sharedmem_init_sysfs(void);
void kgsl_sharedmem_uninit_sysfs(void);
+/*
+ * kgsl_memdesc_get_align - Get alignment flags from a memdesc
+ * @memdesc - the memdesc
+ *
+ * Returns the alignment requested, as power of 2 exponent.
+ */
+static inline int
+kgsl_memdesc_get_align(const struct kgsl_memdesc *memdesc)
+{
+ return (memdesc->flags & KGSL_MEMALIGN_MASK) >> KGSL_MEMALIGN_SHIFT;
+}
+
+/*
+ * kgsl_memdesc_set_align - Set alignment flags of a memdesc
+ * @memdesc - the memdesc
+ * @align - alignment requested, as a power of 2 exponent.
+ */
+static inline int
+kgsl_memdesc_set_align(struct kgsl_memdesc *memdesc, unsigned int align)
+{
+ if (align > 32) {
+ KGSL_CORE_ERR("Alignment too big, restricting to 2^32\n");
+ align = 32;
+ }
+
+ memdesc->flags &= ~KGSL_MEMALIGN_MASK;
+ memdesc->flags |= (align << KGSL_MEMALIGN_SHIFT) & KGSL_MEMALIGN_MASK;
+ return 0;
+}
+
static inline unsigned int kgsl_get_sg_pa(struct scatterlist *sg)
{
/*
@@ -134,7 +163,7 @@
{
if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_NONE)
return kgsl_sharedmem_ebimem(memdesc, pagetable, size);
- memdesc->priv |= (KGSL_MEMTYPE_KERNEL << KGSL_MEMTYPE_SHIFT);
+ memdesc->flags |= (KGSL_MEMTYPE_KERNEL << KGSL_MEMTYPE_SHIFT);
return kgsl_sharedmem_page_alloc(memdesc, pagetable, size);
}
@@ -144,15 +173,14 @@
size_t size, unsigned int flags)
{
int ret;
- unsigned int mask = (KGSL_MEMTYPE_MASK | KGSL_MEMFLAGS_GPUREADONLY);
+
+ memdesc->flags = flags;
+
if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_NONE)
- ret = kgsl_sharedmem_ebimem_user(memdesc, pagetable, size,
- flags);
+ ret = kgsl_sharedmem_ebimem_user(memdesc, pagetable, size);
else
- ret = kgsl_sharedmem_page_alloc_user(memdesc, pagetable, size,
- flags);
- if (ret == 0)
- memdesc->priv |= flags & mask;
+ ret = kgsl_sharedmem_page_alloc_user(memdesc, pagetable, size);
+
return ret;
}
@@ -162,6 +190,8 @@
int ret = kgsl_sharedmem_alloc_coherent(memdesc, size);
if (!ret && (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_NONE))
memdesc->gpuaddr = memdesc->physaddr;
+
+ memdesc->flags |= (KGSL_MEMTYPE_KERNEL << KGSL_MEMTYPE_SHIFT);
return ret;
}
diff --git a/drivers/gpu/msm/kgsl_trace.h b/drivers/gpu/msm/kgsl_trace.h
index 81cb34f..0b247e5 100644
--- a/drivers/gpu/msm/kgsl_trace.h
+++ b/drivers/gpu/msm/kgsl_trace.h
@@ -324,7 +324,7 @@
__entry->size = mem_entry->memdesc.size;
__entry->tgid = mem_entry->priv->pid;
kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
- mem_entry->memdesc.priv);
+ mem_entry->memdesc.flags);
),
TP_printk(
@@ -356,7 +356,7 @@
__entry->type = mem_entry->memtype;
__entry->tgid = mem_entry->priv->pid;
kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
- mem_entry->memdesc.priv);
+ mem_entry->memdesc.flags);
),
TP_printk(
@@ -388,7 +388,7 @@
__entry->type = mem_entry->memtype;
__entry->tgid = mem_entry->priv->pid;
kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
- mem_entry->memdesc.priv);
+ mem_entry->memdesc.flags);
),
TP_printk(
@@ -421,7 +421,7 @@
__entry->gpuaddr = mem_entry->memdesc.gpuaddr;
__entry->size = mem_entry->memdesc.size;
kgsl_get_memory_usage(__entry->usage, sizeof(__entry->usage),
- mem_entry->memdesc.priv);
+ mem_entry->memdesc.flags);
__entry->drawctxt_id = id;
__entry->type = mem_entry->memtype;
__entry->curr_ts = curr_ts;
diff --git a/drivers/media/video/msm_vidc/msm_vidc.c b/drivers/media/video/msm_vidc/msm_vidc.c
index 886113f..7ceb017 100644
--- a/drivers/media/video/msm_vidc/msm_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_vidc.c
@@ -403,6 +403,8 @@
goto err_invalid_core;
}
+ pr_info(VIDC_DBG_TAG "Opening video instance: %p, %d\n",
+ VIDC_INFO, inst, session_type);
mutex_init(&inst->sync_lock);
mutex_init(&inst->bufq[CAPTURE_PORT].lock);
mutex_init(&inst->bufq[OUTPUT_PORT].lock);
@@ -545,7 +547,7 @@
dprintk(VIDC_ERR,
"Failed to move video instance to uninit state\n");
cleanup_instance(inst);
+ pr_info(VIDC_DBG_TAG "Closed video instance: %p\n", VIDC_INFO, inst);
kfree(inst);
- dprintk(VIDC_DBG, "Closed the instance\n");
return 0;
}
diff --git a/drivers/media/video/vcap_vc.c b/drivers/media/video/vcap_vc.c
index c7637b6..f3c9362 100644
--- a/drivers/media/video/vcap_vc.c
+++ b/drivers/media/video/vcap_vc.c
@@ -439,11 +439,10 @@
vc_format->mode << 10,
VCAP_VC_CTRL);
- writel_relaxed(vc_format->h_polar << 4 |
+ writel_relaxed(vc_format->d_polar << 8 |
+ vc_format->h_polar << 4 |
vc_format->v_polar << 0, VCAP_VC_POLARITY);
- writel_relaxed(vc_format->h_polar << 4 |
- vc_format->v_polar << 0, VCAP_VC_POLARITY);
writel_relaxed(((vc_format->htotal << 16) | vc_format->vtotal),
VCAP_VC_V_H_TOTAL);
writel_relaxed(((vc_format->hactive_end << 16) |
diff --git a/drivers/mmc/card/mmc_block_test.c b/drivers/mmc/card/mmc_block_test.c
index 7d3ac83..2307d7a 100644
--- a/drivers/mmc/card/mmc_block_test.c
+++ b/drivers/mmc/card/mmc_block_test.c
@@ -20,6 +20,7 @@
#include <linux/mmc/host.h>
#include <linux/delay.h>
#include <linux/test-iosched.h>
+#include <linux/jiffies.h>
#include "queue.h"
#include <linux/mmc/mmc.h>
@@ -36,6 +37,28 @@
#define SECTOR_SIZE 512
#define NUM_OF_SECTORS_PER_BIO ((BIO_U32_SIZE * 4) / SECTOR_SIZE)
#define BIO_TO_SECTOR(x) (x * NUM_OF_SECTORS_PER_BIO)
+/* the desired long test size to be written or read */
+#define LONG_TEST_MAX_NUM_BYTES (50*1024*1024) /* 50MB */
+/* request queue limitation is 128 requests, and we leave 10 spare requests */
+#define TEST_MAX_REQUESTS 118
+#define LONG_TEST_MAX_NUM_REQS (LONG_TEST_MAX_NUM_BYTES / \
+ (TEST_MAX_BIOS_PER_REQ * sizeof(int) * BIO_U32_SIZE))
+/* this doesn't allow the test requests num to be greater than the maximum */
+#define LONG_TEST_ACTUAL_NUM_REQS \
+ ((TEST_MAX_REQUESTS < LONG_TEST_MAX_NUM_REQS) ? \
+ TEST_MAX_REQUESTS : LONG_TEST_MAX_NUM_REQS)
+#define MB_MSEC_RATIO_APPROXIMATION ((1024 * 1024) / 1000)
+/* actual number of bytes in test */
+#define LONG_TEST_ACTUAL_BYTE_NUM (LONG_TEST_ACTUAL_NUM_REQS * \
+ (TEST_MAX_BIOS_PER_REQ * sizeof(int) * BIO_U32_SIZE))
+/* actual number of MiB in test multiplied by 10, for single digit precision*/
+#define LONG_TEST_ACTUAL_MB_NUM_X_10 ((LONG_TEST_ACTUAL_BYTE_NUM * 10) / \
+ (1024 * 1024))
+/* extract integer value */
+#define LONG_TEST_SIZE_INTEGER (LONG_TEST_ACTUAL_MB_NUM_X_10 / 10)
+/* and calculate the MiB value fraction */
+#define LONG_TEST_SIZE_FRACTION (LONG_TEST_ACTUAL_MB_NUM_X_10 - \
+ (LONG_TEST_SIZE_INTEGER * 10))
#define test_pr_debug(fmt, args...) pr_debug("%s: "fmt"\n", MODULE_NAME, args)
#define test_pr_info(fmt, args...) pr_info("%s: "fmt"\n", MODULE_NAME, args)
@@ -127,6 +150,9 @@
BKOPS_URGENT_LEVEL_2_TWO_REQS,
BKOPS_URGENT_LEVEL_3,
BKOPS_MAX_TESTCASE = BKOPS_URGENT_LEVEL_3,
+
+ TEST_LONG_SEQUENTIAL_READ,
+ TEST_LONG_SEQUENTIAL_WRITE,
};
enum mmc_block_test_group {
@@ -154,6 +180,8 @@
struct dentry *packing_control_test;
struct dentry *discard_sanitize_test;
struct dentry *bkops_test;
+ struct dentry *long_sequential_read_test;
+ struct dentry *long_sequential_write_test;
};
struct mmc_block_test_data {
@@ -562,6 +590,10 @@
return "\nTest urgent BKOPS level 2, followed by a request";
case BKOPS_URGENT_LEVEL_3:
return "\nTest urgent BKOPS level 3";
+ case TEST_LONG_SEQUENTIAL_READ:
+ return "Test long sequential read";
+ case TEST_LONG_SEQUENTIAL_WRITE:
+ return "Test long sequential write";
default:
return "Unknown testcase";
}
@@ -818,8 +850,10 @@
test_pr_info("%s: Adding %d write requests, first req_id=%d", __func__,
num_requests, td->wr_rd_next_req_id);
- for (i = 1; i <= num_requests; i++) {
- start_sec = td->start_sector + 4096 * td->num_of_write_bios;
+ for (i = 1 ; i <= num_requests ; i++) {
+ start_sec =
+ td->start_sector + sizeof(int) *
+ BIO_U32_SIZE * td->num_of_write_bios;
if (is_random)
pseudo_rnd_num_of_bios(bio_seed, &num_bios);
else
@@ -1139,7 +1173,8 @@
if (i > (num_requests / 2))
is_err_expected = 1;
- start_address = td->start_sector + 4096 * td->num_of_write_bios;
+ start_address = td->start_sector +
+ sizeof(int) * BIO_U32_SIZE * td->num_of_write_bios;
ret = test_iosched_add_wr_rd_test_req(is_err_expected, WRITE,
start_address, (i % 5) + 1, TEST_PATTERN_5A,
NULL);
@@ -1243,6 +1278,48 @@
return num_requests;
}
+static int prepare_long_test_requests(struct test_data *td)
+{
+
+ int ret;
+ int start_sec;
+ int j;
+ int test_direction;
+
+ if (td)
+ start_sec = td->start_sector;
+ else {
+ test_pr_err("%s: NULL td\n", __func__);
+ return -EINVAL;
+ }
+
+ if (td->test_info.testcase == TEST_LONG_SEQUENTIAL_WRITE)
+ test_direction = WRITE;
+ else
+ test_direction = READ;
+
+ test_pr_info("%s: Adding %d write requests, first req_id=%d", __func__,
+ LONG_TEST_ACTUAL_NUM_REQS, td->wr_rd_next_req_id);
+
+ for (j = 0; j < LONG_TEST_ACTUAL_NUM_REQS; j++) {
+
+ ret = test_iosched_add_wr_rd_test_req(0, test_direction,
+ start_sec,
+ TEST_MAX_BIOS_PER_REQ,
+ TEST_NO_PATTERN, NULL);
+ if (ret) {
+ test_pr_err("%s: failed to add a bio request",
+ __func__);
+ return ret;
+ }
+
+ start_sec +=
+ (TEST_MAX_BIOS_PER_REQ * sizeof(int) * BIO_U32_SIZE);
+ }
+
+ return 0;
+}
+
/*
* An implementation for the prepare_test_fn pointer in the test_info
* data structure. According to the testcase we add the right number of requests
@@ -1351,9 +1428,15 @@
ret = prepare_packed_control_tests_requests(td, 0,
test_packed_trigger, is_random);
break;
+ case TEST_LONG_SEQUENTIAL_WRITE:
+ ret = prepare_long_test_requests(td);
+ break;
+ case TEST_LONG_SEQUENTIAL_READ:
+ ret = prepare_long_test_requests(td);
+ break;
default:
test_pr_info("%s: Invalid test case...", __func__);
- return -EINVAL;
+ ret = -EINVAL;
}
return ret;
@@ -2430,6 +2513,185 @@
.read = bkops_test_read,
};
+static ssize_t long_sequential_read_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0;
+ int number = -1;
+ unsigned int mtime, integer, fraction;
+
+ test_pr_info("%s: -- Long Sequential Read TEST --", __func__);
+
+ sscanf(buf, "%d", &number);
+
+ if (number <= 0)
+ number = 1;
+
+ memset(&mbtd->test_info, 0, sizeof(struct test_info));
+ mbtd->test_group = TEST_GENERAL_GROUP;
+
+ mbtd->test_info.data = mbtd;
+ mbtd->test_info.prepare_test_fn = prepare_test;
+ mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+
+ for (i = 0 ; i < number ; ++i) {
+ test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+ test_pr_info("%s: ====================", __func__);
+
+ mbtd->test_info.testcase = TEST_LONG_SEQUENTIAL_READ;
+ mbtd->is_random = NON_RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ break;
+
+ mtime = jiffies_to_msecs(mbtd->test_info.test_duration);
+
+ test_pr_info("%s: time is %u msec, size is %u.%u MiB",
+ __func__, mtime, LONG_TEST_SIZE_INTEGER,
+ LONG_TEST_SIZE_FRACTION);
+
+ /* we first multiply in order not to lose precision */
+ mtime *= MB_MSEC_RATIO_APPROXIMATION;
+ /* divide values to get a MiB/sec integer value with one
+ digit of precision. Multiply by 10 for one digit precision
+ */
+ fraction = integer = (LONG_TEST_ACTUAL_BYTE_NUM * 10) / mtime;
+ integer /= 10;
+ /* and calculate the MiB value fraction */
+ fraction -= integer * 10;
+
+ test_pr_info("%s: Throughput: %u.%u MiB/sec\n"
+ , __func__, integer, fraction);
+
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ }
+
+ return count;
+}
+
+static ssize_t long_sequential_read_test_read(struct file *file,
+ char __user *buffer,
+ size_t count,
+ loff_t *offset)
+{
+ memset((void *)buffer, 0, count);
+
+ snprintf(buffer, count,
+ "\nlong_sequential_read_test\n"
+ "=========\n"
+ "Description:\n"
+ "This test runs the following scenarios\n"
+ "- Long Sequential Read Test: this test measures read "
+ "throughput at the driver level by sequentially reading many "
+ "large requests.\n");
+
+ if (message_repeat == 1) {
+ message_repeat = 0;
+ return strnlen(buffer, count);
+ } else
+ return 0;
+}
+
+const struct file_operations long_sequential_read_test_ops = {
+ .open = test_open,
+ .write = long_sequential_read_test_write,
+ .read = long_sequential_read_test_read,
+};
+
+static ssize_t long_sequential_write_test_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ int i = 0;
+ int number = -1;
+ unsigned int mtime, integer, fraction;
+
+ test_pr_info("%s: -- Long Sequential Write TEST --", __func__);
+
+ sscanf(buf, "%d", &number);
+
+ if (number <= 0)
+ number = 1;
+
+ memset(&mbtd->test_info, 0, sizeof(struct test_info));
+ mbtd->test_group = TEST_GENERAL_GROUP;
+
+ mbtd->test_info.data = mbtd;
+ mbtd->test_info.prepare_test_fn = prepare_test;
+ mbtd->test_info.get_test_case_str_fn = get_test_case_str;
+
+ for (i = 0 ; i < number ; ++i) {
+ test_pr_info("%s: Cycle # %d / %d", __func__, i+1, number);
+ test_pr_info("%s: ====================", __func__);
+
+ mbtd->test_info.testcase = TEST_LONG_SEQUENTIAL_WRITE;
+ mbtd->is_random = NON_RANDOM_TEST;
+ ret = test_iosched_start_test(&mbtd->test_info);
+ if (ret)
+ break;
+
+ mtime = jiffies_to_msecs(mbtd->test_info.test_duration);
+
+ test_pr_info("%s: time is %u msec, size is %u.%u MiB",
+ __func__, mtime, LONG_TEST_SIZE_INTEGER,
+ LONG_TEST_SIZE_FRACTION);
+
+ /* we first multiply in order not to lose precision */
+ mtime *= MB_MSEC_RATIO_APPROXIMATION;
+ /* divide values to get a MiB/sec integer value with one
+ digit of precision
+ */
+ fraction = integer = (LONG_TEST_ACTUAL_BYTE_NUM * 10) / mtime;
+ integer /= 10;
+ /* and calculate the MiB value fraction */
+ fraction -= integer * 10;
+
+ test_pr_info("%s: Throughput: %u.%u MiB/sec\n",
+ __func__, integer, fraction);
+
+ /* Allow FS requests to be dispatched */
+ msleep(1000);
+ }
+
+ return count;
+}
+
+static ssize_t long_sequential_write_test_read(struct file *file,
+ char __user *buffer,
+ size_t count,
+ loff_t *offset)
+{
+ memset((void *)buffer, 0, count);
+
+ snprintf(buffer, count,
+ "\nlong_sequential_write_test\n"
+ "=========\n"
+ "Description:\n"
+ "This test runs the following scenarios\n"
+ "- Long Sequential Write Test: this test measures write "
+ "throughput at the driver level by sequentially writing many "
+ "large requests\n");
+
+ if (message_repeat == 1) {
+ message_repeat = 0;
+ return strnlen(buffer, count);
+ } else
+ return 0;
+}
+
+const struct file_operations long_sequential_write_test_ops = {
+ .open = test_open,
+ .write = long_sequential_write_test_write,
+ .read = long_sequential_write_test_read,
+};
+
+
static void mmc_block_test_debugfs_cleanup(void)
{
debugfs_remove(mbtd->debug.random_test_seed);
@@ -2439,6 +2701,8 @@
debugfs_remove(mbtd->debug.packing_control_test);
debugfs_remove(mbtd->debug.discard_sanitize_test);
debugfs_remove(mbtd->debug.bkops_test);
+ debugfs_remove(mbtd->debug.long_sequential_read_test);
+ debugfs_remove(mbtd->debug.long_sequential_write_test);
}
static int mmc_block_test_debugfs_init(void)
@@ -2521,6 +2785,26 @@
if (!mbtd->debug.bkops_test)
goto err_nomem;
+ mbtd->debug.long_sequential_read_test = debugfs_create_file(
+ "long_sequential_read_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &long_sequential_read_test_ops);
+
+ if (!mbtd->debug.long_sequential_read_test)
+ goto err_nomem;
+
+ mbtd->debug.long_sequential_write_test = debugfs_create_file(
+ "long_sequential_write_test",
+ S_IRUGO | S_IWUGO,
+ tests_root,
+ NULL,
+ &long_sequential_write_test_ops);
+
+ if (!mbtd->debug.long_sequential_write_test)
+ goto err_nomem;
+
return 0;
err_nomem:
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 2fbd804..72f4a5c 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -3197,8 +3197,21 @@
/*
* For DDR50 mode, controller needs clock rate to be
* double than what is required on the SD card CLK pin.
+ *
+ * Setting DDR timing mode in controller before setting the
+ * clock rate will make sure that card don't see the double
+ * clock rate even for very small duration. Some eMMC
+ * cards seems to lock up if they see clock frequency > 52MHz.
*/
if (ios->timing == MMC_TIMING_UHS_DDR50) {
+ u32 clk;
+
+ clk = readl_relaxed(host->base + MMCICLOCK);
+ clk &= ~(0x7 << 14); /* clear SELECT_IN field */
+ clk |= (3 << 14); /* set DDR timing mode */
+ writel_relaxed(clk, host->base + MMCICLOCK);
+ msmsdcc_sync_reg_wr(host);
+
/*
* Make sure that we don't double the clock if
* doubled clock rate is already set
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 376750f..7b7e05e 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -343,6 +343,18 @@
SMB349 be operated as a slave device via the power supply
framework.
+config SMB350_CHARGER
+ tristate "smb350 charger"
+ depends on I2C
+ help
+ Say Y to enable battery charging by SMB350 switching mode based
+ external charger. The device supports stack-cell battery charging.
+ The driver configures the device volatile parameters
+ and the charger device works autonomously.
+ The driver supports charger-enable and charger-suspend/resume.
+ The driver reports the charger status via the power supply framework.
+ A charger status change triggers an IRQ via the device STAT pin.
+
config BATTERY_MSM_FAKE
tristate "Fake MSM battery"
depends on ARCH_MSM && BATTERY_MSM
@@ -388,6 +400,18 @@
help
Say Y here to enable Test sysfs Interface for BQ27520 Drivers.
+config BATTERY_BQ28400
+ tristate "BQ28400 battery driver"
+ depends on I2C
+ default n
+ help
+ Say Y here to enable support for batteries with BQ28400 (I2C) chips.
+ The bq28400 Texas Instruments Inc device monitors the battery
+ charging/discharging status via Rsens resistor, typically 10 mohm.
+ It monitors the battery temperature via Thermistor.
+ The device monitors the battery level (Relative-State-Of-Charge).
+ The device is SBS compliant, providing battery info over I2C.
+
config PM8921_CHARGER
tristate "PM8921 Charger driver"
depends on MFD_PM8921_CORE
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 3521cfd..3e74f35 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -48,10 +48,12 @@
obj-$(CONFIG_PM8058_CHARGER) += pmic8058-charger.o
obj-$(CONFIG_ISL9519_CHARGER) += isl9519q.o
obj-$(CONFIG_SMB349_CHARGER) += smb349.o
+obj-$(CONFIG_SMB350_CHARGER) += smb350_charger.o
obj-$(CONFIG_PM8058_FIX_USB) += pm8058_usb_fix.o
obj-$(CONFIG_BATTERY_QCIBAT) += qci_battery.o
obj-$(CONFIG_BATTERY_BQ27520) += bq27520_fuelgauger.o
obj-$(CONFIG_BATTERY_BQ27541) += bq27541_fuelgauger.o
+obj-$(CONFIG_BATTERY_BQ28400) += bq28400_battery.o
obj-$(CONFIG_SMB137B_CHARGER) += smb137b.o
obj-$(CONFIG_PM8XXX_CCADC) += pm8xxx-ccadc.o
obj-$(CONFIG_PM8921_BMS) += pm8921-bms.o
diff --git a/drivers/power/bq28400_battery.c b/drivers/power/bq28400_battery.c
new file mode 100644
index 0000000..39d52cb
--- /dev/null
+++ b/drivers/power/bq28400_battery.c
@@ -0,0 +1,917 @@
+/* Copyright (c) 2012 The Linux Foundation. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+/*
+ * High Level description:
+ * http://www.ti.com/lit/ds/symlink/bq28400.pdf
+ * Thechnical Reference:
+ * http://www.ti.com/lit/ug/sluu431/sluu431.pdf
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/power_supply.h>
+#include <linux/bitops.h>
+#include <linux/regulator/consumer.h>
+#include <linux/printk.h>
+
+#define BQ28400_NAME "bq28400"
+#define BQ28400_REV "1.0"
+
+/* SBS Commands (page 63) */
+
+#define SBS_MANUFACTURER_ACCESS 0x00
+#define SBS_BATTERY_MODE 0x03
+#define SBS_TEMPERATURE 0x08
+#define SBS_VOLTAGE 0x09
+#define SBS_CURRENT 0x0A
+#define SBS_AVG_CURRENT 0x0B
+#define SBS_MAX_ERROR 0x0C
+#define SBS_RSOC 0x0D /* Relative State Of Charge */
+#define SBS_REMAIN_CAPACITY 0x0F
+#define SBS_FULL_CAPACITY 0x10
+#define SBS_CHG_CURRENT 0x14
+#define SBS_CHG_VOLTAGE 0x15
+#define SBS_BATTERY_STATUS 0x16
+#define SBS_CYCLE_COUNT 0x17
+#define SBS_DESIGN_CAPACITY 0x18
+#define SBS_DESIGN_VOLTAGE 0x19
+#define SBS_SPEC_INFO 0x1A
+#define SBS_MANUFACTURE_DATE 0x1B
+#define SBS_SERIAL_NUMBER 0x1C
+#define SBS_MANUFACTURER_NAME 0x20
+#define SBS_DEVICE_NAME 0x21
+#define SBS_DEVICE_CHEMISTRY 0x22
+#define SBS_MANUFACTURER_DATA 0x23
+#define SBS_AUTHENTICATE 0x2F
+#define SBS_CELL_VOLTAGE1 0x3E
+#define SBS_CELL_VOLTAGE2 0x3F
+
+/* Extended SBS Commands (page 71) */
+
+#define SBS_FET_CONTROL 0x46
+#define SBS_SAFETY_ALERT 0x50
+#define SBS_SAFETY_STATUS 0x51
+#define SBS_PE_ALERT 0x52
+#define SBS_PE_STATUS 0x53
+#define SBS_OPERATION_STATUS 0x54
+#define SBS_CHARGING_STATUS 0x55
+#define SBS_FET_STATUS 0x56
+#define SBS_PACK_VOLTAGE 0x5A
+#define SBS_TS0_TEMPERATURE 0x5E
+#define SBS_FULL_ACCESS_KEY 0x61
+#define SBS_PF_KEY 0x62
+#define SBS_AUTH_KEY3 0x63
+#define SBS_AUTH_KEY2 0x64
+#define SBS_AUTH_KEY1 0x65
+#define SBS_AUTH_KEY0 0x66
+#define SBS_MANUFACTURER_INFO 0x70
+#define SBS_SENSE_RESISTOR 0x71
+#define SBS_TEMP_RANGE 0x72
+
+/* SBS Sub-Commands (16 bits) */
+/* SBS_MANUFACTURER_ACCESS CMD */
+#define SUBCMD_DEVICE_TYPE 0x01
+#define SUBCMD_FIRMWARE_VERSION 0x02
+#define SUBCMD_HARDWARE_VERSION 0x03
+#define SUBCMD_DF_CHECKSUM 0x04
+#define SUBCMD_EDV 0x05
+#define SUBCMD_CHEMISTRY_ID 0x08
+
+/* SBS_CHARGING_STATUS */
+#define CHG_STATUS_BATTERY_DEPLETED BIT(0)
+#define CHG_STATUS_OVERCHARGE BIT(1)
+#define CHG_STATUS_OVERCHARGE_CURRENT BIT(2)
+#define CHG_STATUS_OVERCHARGE_VOLTAGE BIT(3)
+#define CHG_STATUS_CELL_BALANCING BIT(6)
+#define CHG_STATUS_HOT_TEMP_CHARGING BIT(8)
+#define CHG_STATUS_STD1_TEMP_CHARGING BIT(9)
+#define CHG_STATUS_STD2_TEMP_CHARGING BIT(10)
+#define CHG_STATUS_LOW_TEMP_CHARGING BIT(11)
+#define CHG_STATUS_PRECHARGING_EXIT BIT(13)
+#define CHG_STATUS_SUSPENDED BIT(14)
+#define CHG_STATUS_DISABLED BIT(15)
+
+/* SBS_FET_STATUS */
+#define FET_STATUS_DISCHARGE BIT(1)
+#define FET_STATUS_CHARGE BIT(2)
+#define FET_STATUS_PRECHARGE BIT(3)
+
+/* SBS_BATTERY_STATUS */
+#define BAT_STATUS_SBS_ERROR 0x0F
+#define BAT_STATUS_EMPTY BIT(4)
+#define BAT_STATUS_FULL BIT(5)
+#define BAT_STATUS_DISCHARGING BIT(6)
+#define BAT_STATUS_OVER_TEMPERATURE BIT(12)
+#define BAT_STATUS_OVER_CHARGED BIT(15)
+
+#define ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN (-2731)
+#define BQ_TERMINATION_CURRENT_MA 200
+
+#define BQ_MAX_STR_LEN 32
+
+struct bq28400_device {
+ struct i2c_client *client;
+ struct delayed_work periodic_user_space_update_work;
+ struct dentry *dent;
+ struct power_supply batt_psy;
+ struct power_supply *dc_psy;
+ bool is_charging_enabled;
+};
+
+static struct bq28400_device *bq28400_dev;
+
+static enum power_supply_property pm_power_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+struct debug_reg {
+ char *name;
+ u8 reg;
+ u16 subcmd;
+};
+
+#define BQ28400_DEBUG_REG(x) {#x, SBS_##x, 0}
+#define BQ28400_DEBUG_SUBREG(x, y) {#y, SBS_##x, SUBCMD_##y}
+
+/* Note: Some register can be read only in Unsealed mode */
+static struct debug_reg bq28400_debug_regs[] = {
+ BQ28400_DEBUG_REG(MANUFACTURER_ACCESS),
+ BQ28400_DEBUG_REG(BATTERY_MODE),
+ BQ28400_DEBUG_REG(TEMPERATURE),
+ BQ28400_DEBUG_REG(VOLTAGE),
+ BQ28400_DEBUG_REG(CURRENT),
+ BQ28400_DEBUG_REG(AVG_CURRENT),
+ BQ28400_DEBUG_REG(MAX_ERROR),
+ BQ28400_DEBUG_REG(RSOC),
+ BQ28400_DEBUG_REG(REMAIN_CAPACITY),
+ BQ28400_DEBUG_REG(FULL_CAPACITY),
+ BQ28400_DEBUG_REG(CHG_CURRENT),
+ BQ28400_DEBUG_REG(CHG_VOLTAGE),
+ BQ28400_DEBUG_REG(BATTERY_STATUS),
+ BQ28400_DEBUG_REG(CYCLE_COUNT),
+ BQ28400_DEBUG_REG(DESIGN_CAPACITY),
+ BQ28400_DEBUG_REG(DESIGN_VOLTAGE),
+ BQ28400_DEBUG_REG(SPEC_INFO),
+ BQ28400_DEBUG_REG(MANUFACTURE_DATE),
+ BQ28400_DEBUG_REG(SERIAL_NUMBER),
+ BQ28400_DEBUG_REG(MANUFACTURER_NAME),
+ BQ28400_DEBUG_REG(DEVICE_NAME),
+ BQ28400_DEBUG_REG(DEVICE_CHEMISTRY),
+ BQ28400_DEBUG_REG(MANUFACTURER_DATA),
+ BQ28400_DEBUG_REG(AUTHENTICATE),
+ BQ28400_DEBUG_REG(CELL_VOLTAGE1),
+ BQ28400_DEBUG_REG(CELL_VOLTAGE2),
+ BQ28400_DEBUG_REG(SAFETY_ALERT),
+ BQ28400_DEBUG_REG(SAFETY_STATUS),
+ BQ28400_DEBUG_REG(PE_ALERT),
+ BQ28400_DEBUG_REG(PE_STATUS),
+ BQ28400_DEBUG_REG(OPERATION_STATUS),
+ BQ28400_DEBUG_REG(CHARGING_STATUS),
+ BQ28400_DEBUG_REG(FET_STATUS),
+ BQ28400_DEBUG_REG(FULL_ACCESS_KEY),
+ BQ28400_DEBUG_REG(PF_KEY),
+ BQ28400_DEBUG_REG(MANUFACTURER_INFO),
+ BQ28400_DEBUG_REG(SENSE_RESISTOR),
+ BQ28400_DEBUG_REG(TEMP_RANGE),
+ BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, DEVICE_TYPE),
+ BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, FIRMWARE_VERSION),
+ BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, HARDWARE_VERSION),
+ BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, DF_CHECKSUM),
+ BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, EDV),
+ BQ28400_DEBUG_SUBREG(MANUFACTURER_ACCESS, CHEMISTRY_ID),
+};
+
+static int bq28400_read_reg(struct i2c_client *client, u8 reg)
+{
+ int val;
+
+ val = i2c_smbus_read_word_data(client, reg);
+ if (val < 0)
+ pr_err("i2c read fail. reg = 0x%x.ret = %d.\n", reg, val);
+ else
+ pr_debug("reg = 0x%02X.val = 0x%04X.\n", reg , val);
+
+ return val;
+}
+
+static int bq28400_write_reg(struct i2c_client *client, u8 reg, u16 val)
+{
+ int ret;
+
+ ret = i2c_smbus_write_word_data(client, reg, val);
+ if (ret < 0)
+ pr_err("i2c read fail. reg = 0x%x.val = 0x%x.ret = %d.\n",
+ reg, val, ret);
+ else
+ pr_debug("reg = 0x%02X.val = 0x%02X.\n", reg , val);
+
+ return ret;
+}
+
+static int bq28400_read_subcmd(struct i2c_client *client, u8 reg, u16 subcmd)
+{
+ int ret;
+ u8 buf[4];
+ u16 val = 0;
+
+ buf[0] = reg;
+ buf[1] = subcmd & 0xFF;
+ buf[2] = (subcmd >> 8) & 0xFF;
+
+ /* Control sub-command */
+ ret = i2c_master_send(client, buf, 3);
+ if (ret < 0) {
+ pr_err("i2c tx fail. reg = 0x%x.ret = %d.\n", reg, ret);
+ return ret;
+ }
+ udelay(66);
+
+ /* Read Result of subcmd */
+ ret = i2c_master_send(client, buf, 1);
+ memset(buf, 0xAA, sizeof(buf));
+ ret = i2c_master_recv(client, buf, 2);
+ if (ret < 0) {
+ pr_err("i2c rx fail. reg = 0x%x.ret = %d.\n", reg, ret);
+ return ret;
+ }
+ val = (buf[1] << 8) + buf[0];
+
+ pr_debug("reg = 0x%02X.subcmd = 0x%x.val = 0x%04X.\n",
+ reg , subcmd, val);
+
+ return val;
+}
+
+static int bq28400_read_block(struct i2c_client *client, u8 reg,
+ u8 len, u8 *buf)
+{
+ int ret;
+ u32 val;
+
+ ret = i2c_smbus_read_i2c_block_data(client, reg, len, buf);
+ val = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
+
+ if (ret < 0)
+ pr_err("i2c read fail. reg = 0x%x.ret = %d.\n", reg, ret);
+ else
+ pr_debug("reg = 0x%02X.val = 0x%04X.\n", reg , val);
+
+ return val;
+}
+
+/*
+ * Read a string from a device.
+ * Returns string length on success or error on failure (negative value).
+ */
+static int bq28400_read_string(struct i2c_client *client, u8 reg, char *str,
+ u8 max_len)
+{
+ int ret;
+ int len;
+
+ ret = bq28400_read_block(client, reg, max_len, str);
+ if (ret < 0)
+ return ret;
+
+ len = str[0]; /* Actual length */
+ if (len > max_len - 2) { /* reduce len byte and null */
+ pr_err("len = %d invalid.\n", len);
+ return -EINVAL;
+ }
+
+ memcpy(&str[0], &str[1], len); /* Move sting to the start */
+ str[len] = 0; /* put NULL after actual size */
+
+ pr_debug("len = %d.str = %s.\n", len, str);
+
+ return len;
+}
+
+#define BQ28400_INVALID_TEMPERATURE -999
+/*
+ * Return the battery temperature in tenths of degree Celsius
+ * Or -99.9 C if something fails.
+ */
+static int bq28400_read_temperature(struct i2c_client *client)
+{
+ int temp;
+
+ /* temperature resolution 0.1 Kelvin */
+ temp = bq28400_read_reg(client, SBS_TEMPERATURE);
+ if (temp < 0)
+ return BQ28400_INVALID_TEMPERATURE;
+
+ temp = temp + ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN;
+
+ pr_debug("temp = %d C\n", temp/10);
+
+ return temp;
+}
+
+/*
+ * Return the battery Voltage in milivolts 0..20 V
+ * Or < 0 if something fails.
+ */
+static int bq28400_read_voltage(struct i2c_client *client)
+{
+ int mvolt = 0;
+
+ mvolt = bq28400_read_reg(client, SBS_VOLTAGE);
+ if (mvolt < 0)
+ return mvolt;
+
+ pr_debug("volt = %d mV.\n", mvolt);
+
+ return mvolt;
+}
+
+/*
+ * Return the battery Current in miliamps
+ * Or 0 if something fails.
+ * Positive current indicates charging
+ * Negative current indicates discharging.
+ * Current-now is calculated every second.
+ */
+static int bq28400_read_current(struct i2c_client *client)
+{
+ s16 current_ma = 0;
+
+ current_ma = bq28400_read_reg(client, SBS_CURRENT);
+
+ pr_debug("current = %d mA.\n", current_ma);
+
+ return current_ma;
+}
+
+/*
+ * Return the Average battery Current in miliamps
+ * Or 0 if something fails.
+ * Positive current indicates charging
+ * Negative current indicates discharging.
+ * Average Current is the rolling 1 minute average current.
+ */
+static int bq28400_read_avg_current(struct i2c_client *client)
+{
+ s16 current_ma = 0;
+
+ current_ma = bq28400_read_reg(client, SBS_AVG_CURRENT);
+
+ pr_debug("avg_current=%d mA.\n", current_ma);
+
+ return current_ma;
+}
+
+/*
+ * Return the battery Relative-State-Of-Charge 0..100 %
+ * Or 0 if something fails.
+ */
+static int bq28400_read_rsoc(struct i2c_client *client)
+{
+ int percentage = 0;
+
+ /* This register is only 1 byte */
+ percentage = i2c_smbus_read_byte_data(client, SBS_RSOC);
+
+ if (percentage < 0)
+ return 0;
+
+ pr_debug("percentage = %d.\n", percentage);
+
+ return percentage;
+}
+
+/*
+ * Return the battery Capacity in mAh.
+ * Or 0 if something fails.
+ */
+static int bq28400_read_full_capacity(struct i2c_client *client)
+{
+ int capacity = 0;
+
+ capacity = bq28400_read_reg(client, SBS_FULL_CAPACITY);
+ if (capacity < 0)
+ return 0;
+
+ pr_debug("full-capacity = %d mAh.\n", capacity);
+
+ return capacity;
+}
+
+/*
+ * Return the battery Capacity in mAh.
+ * Or 0 if something fails.
+ */
+static int bq28400_read_remain_capacity(struct i2c_client *client)
+{
+ int capacity = 0;
+
+ capacity = bq28400_read_reg(client, SBS_REMAIN_CAPACITY);
+ if (capacity < 0)
+ return 0;
+
+ pr_debug("remain-capacity = %d mAh.\n", capacity);
+
+ return capacity;
+}
+
+static int bq28400_enable_charging(struct bq28400_device *bq28400_dev,
+ bool enable)
+{
+ int ret;
+ static bool is_charging_enabled;
+
+ if (bq28400_dev->dc_psy == NULL) {
+ bq28400_dev->dc_psy = power_supply_get_by_name("dc");
+ if (bq28400_dev->dc_psy == NULL) {
+ pr_err("fail to get dc-psy.\n");
+ return -ENODEV;
+ }
+ }
+
+ if (is_charging_enabled == enable) {
+ pr_debug("Charging enable already = %d.\n", enable);
+ return 0;
+ }
+
+ ret = power_supply_set_online(bq28400_dev->dc_psy, enable);
+ if (ret < 0) {
+ pr_err("fail to set dc-psy online to %d.\n", enable);
+ return ret;
+ }
+
+ is_charging_enabled = enable;
+
+ pr_debug("Charging enable = %d.\n", enable);
+
+ return 0;
+}
+
+static int bq28400_get_prop_status(struct i2c_client *client)
+{
+ int status = POWER_SUPPLY_STATUS_UNKNOWN;
+ int rsoc;
+ s16 current_ma = 0;
+ u16 battery_status;
+
+ battery_status = bq28400_read_reg(client, SBS_BATTERY_STATUS);
+
+ if (battery_status & BAT_STATUS_EMPTY)
+ pr_debug("Battery report Empty.\n");
+
+ /* Battery may report FULL before rsoc is 100%
+ * for protection and cell-balancing.
+ * The FULL report may remain when rsoc drops from 100%.
+ */
+ if (battery_status & BAT_STATUS_FULL) {
+ pr_debug("Battery report Full.\n");
+ bq28400_enable_charging(bq28400_dev, false);
+ return POWER_SUPPLY_STATUS_FULL;
+ }
+
+ rsoc = bq28400_read_rsoc(client);
+ current_ma = bq28400_read_current(client);
+
+ if (rsoc == 100) {
+ bq28400_enable_charging(bq28400_dev, false);
+ pr_debug("Full.\n");
+ return POWER_SUPPLY_STATUS_FULL;
+ }
+
+ /*
+ * Positive current indicates charging
+ * Negative current indicates discharging.
+ * Charging is stopped at termination-current.
+ */
+ if (current_ma < 0) {
+ bq28400_enable_charging(bq28400_dev, true);
+ pr_debug("Discharging.\n");
+ status = POWER_SUPPLY_STATUS_DISCHARGING;
+ } else if (current_ma > BQ_TERMINATION_CURRENT_MA) {
+ pr_debug("Charging.\n");
+ status = POWER_SUPPLY_STATUS_CHARGING;
+ } else {
+ pr_debug("Not Charging.\n");
+ status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ }
+
+ return status;
+}
+
+static int bq28400_get_prop_charge_type(struct i2c_client *client)
+{
+ u16 battery_status;
+ u16 chg_status;
+ u16 fet_status;
+
+ battery_status = bq28400_read_reg(client, SBS_BATTERY_STATUS);
+ chg_status = bq28400_read_reg(client, SBS_CHARGING_STATUS);
+ fet_status = bq28400_read_reg(client, SBS_FET_STATUS);
+
+ if (battery_status & BAT_STATUS_DISCHARGING) {
+ pr_debug("Discharging.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+ }
+
+ if (fet_status & FET_STATUS_PRECHARGE) {
+ pr_debug("Pre-Charging.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+ }
+
+ if (chg_status & CHG_STATUS_HOT_TEMP_CHARGING) {
+ pr_debug("Hot-Temp-Charging.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_FAST;
+ }
+
+ if (chg_status & CHG_STATUS_LOW_TEMP_CHARGING) {
+ pr_debug("Low-Temp-Charging.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_FAST;
+ }
+
+ if (chg_status & CHG_STATUS_STD1_TEMP_CHARGING) {
+ pr_debug("STD1-Temp-Charging.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_FAST;
+ }
+
+ if (chg_status & CHG_STATUS_STD2_TEMP_CHARGING) {
+ pr_debug("STD2-Temp-Charging.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_FAST;
+ }
+
+ if (chg_status & CHG_STATUS_BATTERY_DEPLETED)
+ pr_debug("battery_depleted.\n");
+
+ if (chg_status & CHG_STATUS_CELL_BALANCING)
+ pr_debug("cell_balancing.\n");
+
+ if (chg_status & CHG_STATUS_OVERCHARGE) {
+ pr_err("overcharge fault.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+ }
+
+ if (chg_status & CHG_STATUS_SUSPENDED) {
+ pr_info("Suspended.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+ }
+
+ if (chg_status & CHG_STATUS_DISABLED) {
+ pr_info("Disabled.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+ }
+
+ return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+}
+
+static bool bq28400_get_prop_present(struct i2c_client *client)
+{
+ int val;
+
+ val = bq28400_read_reg(client, SBS_BATTERY_STATUS);
+
+ /* If the bq28400 is inside the battery pack
+ * then when battery is removed the i2c transfer will fail.
+ */
+
+ if (val < 0)
+ return false;
+
+ /* TODO - support when bq28400 is not embedded in battery pack */
+
+ return true;
+}
+
+/*
+ * User sapce read the battery info.
+ * Get data online via I2C from the battery gauge.
+ */
+static int bq28400_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ int ret = 0;
+ struct bq28400_device *dev = container_of(psy,
+ struct bq28400_device,
+ batt_psy);
+ struct i2c_client *client = dev->client;
+ static char str[BQ_MAX_STR_LEN];
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = bq28400_get_prop_status(client);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ val->intval = bq28400_get_prop_charge_type(client);
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = bq28400_get_prop_present(client);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = bq28400_read_voltage(client);
+ val->intval *= 1000; /* mV to uV */
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = bq28400_read_rsoc(client);
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ /* Positive current indicates drawing */
+ val->intval = -bq28400_read_current(client);
+ val->intval *= 1000; /* mA to uA */
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ /* Positive current indicates drawing */
+ val->intval = -bq28400_read_avg_current(client);
+ val->intval *= 1000; /* mA to uA */
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = bq28400_read_temperature(client);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ val->intval = bq28400_read_full_capacity(client);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = bq28400_read_remain_capacity(client);
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+ break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ bq28400_read_string(client, SBS_DEVICE_NAME, str, 20);
+ val->strval = str;
+ break;
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ bq28400_read_string(client, SBS_MANUFACTURER_NAME, str, 20);
+ val->strval = str;
+ break;
+ default:
+ pr_err(" psp %d Not supoprted.\n", psp);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int bq28400_set_reg(void *data, u64 val)
+{
+ struct debug_reg *dbg = data;
+ u8 reg = dbg->reg;
+ int ret;
+ struct i2c_client *client = bq28400_dev->client;
+
+ ret = bq28400_write_reg(client, reg, val);
+
+ return ret;
+}
+
+static int bq28400_get_reg(void *data, u64 *val)
+{
+ struct debug_reg *dbg = data;
+ u8 reg = dbg->reg;
+ u16 subcmd = dbg->subcmd;
+ int ret;
+ struct i2c_client *client = bq28400_dev->client;
+
+ if (subcmd)
+ ret = bq28400_read_subcmd(client, reg, subcmd);
+ else
+ ret = bq28400_read_reg(client, reg);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, bq28400_get_reg, bq28400_set_reg,
+ "0x%04llx\n");
+
+static int bq28400_create_debugfs_entries(struct bq28400_device *bq28400_dev)
+{
+ int i;
+
+ bq28400_dev->dent = debugfs_create_dir(BQ28400_NAME, NULL);
+ if (IS_ERR(bq28400_dev->dent)) {
+ pr_err("bq28400 driver couldn't create debugfs dir\n");
+ return -EFAULT;
+ }
+
+ for (i = 0 ; i < ARRAY_SIZE(bq28400_debug_regs) ; i++) {
+ char *name = bq28400_debug_regs[i].name;
+ struct dentry *file;
+ void *data = &bq28400_debug_regs[i];
+
+ file = debugfs_create_file(name, 0644, bq28400_dev->dent,
+ data, ®_fops);
+ if (IS_ERR(file)) {
+ pr_err("debugfs_create_file %s failed.\n", name);
+ return -EFAULT;
+ }
+ }
+
+ return 0;
+}
+
+static int bq28400_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ pr_debug("psp = %d.val = %d.\n", psp, val->intval);
+
+ return -EINVAL;
+}
+
+static void bq28400_external_power_changed(struct power_supply *psy)
+{
+ pr_debug("Notify power_supply_changed.\n");
+ /* Update LEDs and notify uevents */
+ power_supply_changed(&bq28400_dev->batt_psy);
+}
+
+static int __devinit bq28400_register_psy(struct bq28400_device *bq28400_dev)
+{
+ int ret;
+
+ bq28400_dev->batt_psy.name = "battery";
+ bq28400_dev->batt_psy.type = POWER_SUPPLY_TYPE_BATTERY;
+ bq28400_dev->batt_psy.num_supplicants = 0;
+ bq28400_dev->batt_psy.properties = pm_power_props;
+ bq28400_dev->batt_psy.num_properties = ARRAY_SIZE(pm_power_props);
+ bq28400_dev->batt_psy.get_property = bq28400_get_property;
+ bq28400_dev->batt_psy.set_property = bq28400_set_property;
+ bq28400_dev->batt_psy.external_power_changed =
+ bq28400_external_power_changed;
+
+ ret = power_supply_register(&bq28400_dev->client->dev,
+ &bq28400_dev->batt_psy);
+ if (ret) {
+ pr_err("failed to register power_supply. ret=%d.\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * Update userspace every 1 minute.
+ * Normally it takes more than 120 minutes (two hours) to
+ * charge/discahrge the battery,
+ * so updating every 1 minute should be enough for 1% change
+ * detection.
+ * Any immidiate change detected by the DC charger is notified
+ * by the bq28400_external_power_changed callback, which notify
+ * the user space.
+ */
+static void bq28400_periodic_user_space_update_worker(struct work_struct *work)
+{
+ u32 delay_msec = 60*1000;
+
+ pr_debug("Notify user space.\n");
+
+ /* Notify user space via kobject_uevent change notification */
+ power_supply_changed(&bq28400_dev->batt_psy);
+
+ schedule_delayed_work(&bq28400_dev->periodic_user_space_update_work,
+ round_jiffies_relative(msecs_to_jiffies
+ (delay_msec)));
+}
+
+static int __devinit bq28400_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ pr_err(" i2c func fail.\n");
+ return -EIO;
+ }
+
+ bq28400_dev = kzalloc(sizeof(*bq28400_dev), GFP_KERNEL);
+ if (!bq28400_dev) {
+ pr_err(" alloc fail.\n");
+ return -ENOMEM;
+ }
+
+ bq28400_dev->client = client;
+ i2c_set_clientdata(client, bq28400_dev);
+
+ ret = bq28400_register_psy(bq28400_dev);
+ if (ret) {
+ pr_err(" bq28400_register_psy fail.\n");
+ goto err_register_psy;
+ }
+
+ ret = bq28400_create_debugfs_entries(bq28400_dev);
+ if (ret) {
+ pr_err(" bq28400_create_debugfs_entries fail.\n");
+ goto err_debugfs;
+ }
+
+ INIT_DELAYED_WORK(&bq28400_dev->periodic_user_space_update_work,
+ bq28400_periodic_user_space_update_worker);
+
+ schedule_delayed_work(&bq28400_dev->periodic_user_space_update_work,
+ msecs_to_jiffies(1000));
+
+ pr_info("Device is ready.\n");
+
+ return 0;
+
+err_debugfs:
+ if (bq28400_dev->dent)
+ debugfs_remove_recursive(bq28400_dev->dent);
+ power_supply_unregister(&bq28400_dev->batt_psy);
+err_register_psy:
+ kfree(bq28400_dev);
+ bq28400_dev = NULL;
+
+ pr_info("FAIL.\n");
+
+ return ret;
+}
+
+static int __devexit bq28400_remove(struct i2c_client *client)
+{
+ struct bq28400_device *bq28400_dev = i2c_get_clientdata(client);
+
+ power_supply_unregister(&bq28400_dev->batt_psy);
+ if (bq28400_dev->dent)
+ debugfs_remove_recursive(bq28400_dev->dent);
+ kfree(bq28400_dev);
+ bq28400_dev = NULL;
+
+ return 0;
+}
+
+static const struct of_device_id bq28400_match[] = {
+ { .compatible = "ti,bq28400-battery", },
+ { },
+ };
+
+static const struct i2c_device_id bq28400_id[] = {
+ {BQ28400_NAME, 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, bq28400_id);
+
+static struct i2c_driver bq28400_driver = {
+ .driver = {
+ .name = BQ28400_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(bq28400_match),
+ },
+ .probe = bq28400_probe,
+ .remove = __devexit_p(bq28400_remove),
+ .id_table = bq28400_id,
+};
+
+static int __init bq28400_init(void)
+{
+ pr_info(" bq28400 driver rev %s.\n", BQ28400_REV);
+
+ return i2c_add_driver(&bq28400_driver);
+}
+module_init(bq28400_init);
+
+static void __exit bq28400_exit(void)
+{
+ return i2c_del_driver(&bq28400_driver);
+}
+module_exit(bq28400_exit);
+
+MODULE_DESCRIPTION("Driver for BQ28400 charger chip");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:" BQ28400_NAME);
diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c
index 3977f17..d2d0c03 100644
--- a/drivers/power/pm8921-charger.c
+++ b/drivers/power/pm8921-charger.c
@@ -289,6 +289,7 @@
bool has_dc_supply;
u8 active_path;
int recent_reported_soc;
+ int battery_less_hardware;
};
/* user space parameter to limit usb current */
@@ -1420,6 +1421,9 @@
{
int percent_soc;
+ if (chip->battery_less_hardware)
+ return 100;
+
if (!get_prop_batt_present(chip))
percent_soc = voltage_based_capacity(chip);
else
@@ -1582,6 +1586,9 @@
int rc;
struct pm8xxx_adc_chan_result result;
+ if (chip->battery_less_hardware)
+ return 300;
+
rc = pm8xxx_adc_read(chip->batt_temp_channel, &result);
if (rc) {
pr_err("error reading adc channel = %d, rc = %d\n",
@@ -4288,6 +4295,10 @@
chip->rconn_mohm = pdata->rconn_mohm;
chip->led_src_config = pdata->led_src_config;
chip->has_dc_supply = pdata->has_dc_supply;
+ chip->battery_less_hardware = pdata->battery_less_hardware;
+
+ if (chip->battery_less_hardware)
+ charging_disabled = 1;
rc = pm8921_chg_hw_init(chip);
if (rc) {
diff --git a/drivers/power/smb350_charger.c b/drivers/power/smb350_charger.c
new file mode 100644
index 0000000..93e208c
--- /dev/null
+++ b/drivers/power/smb350_charger.c
@@ -0,0 +1,865 @@
+/* Copyright (c) 2012 The Linux Foundation. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/power_supply.h>
+#include <linux/i2c/smb350.h>
+#include <linux/bitops.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/printk.h>
+
+/* Register definitions */
+#define CHG_CURRENT_REG 0x00 /* Non-Volatile + mirror */
+#define CHG_OTHER_CURRENT_REG 0x01 /* Non-Volatile + mirror */
+#define VAR_FUNC_REG 0x02 /* Non-Volatile + mirror */
+#define FLOAT_VOLTAGE_REG 0x03 /* Non-Volatile + mirror */
+#define CHG_CTRL_REG 0x04 /* Non-Volatile + mirror */
+#define STAT_TIMER_REG 0x05 /* Non-Volatile + mirror */
+#define PIN_ENABLE_CTRL_REG 0x06 /* Non-Volatile + mirror */
+#define THERM_CTRL_A_REG 0x07 /* Non-Volatile + mirror */
+#define SYSOK_USB3_SELECT_REG 0x08 /* Non-Volatile + mirror */
+#define CTRL_FUNCTIONS_REG 0x09 /* Non-Volatile + mirror */
+#define OTG_TLIM_THERM_CNTRL_REG 0x0A /* Non-Volatile + mirror */
+#define TEMP_MONITOR_REG 0x0B /* Non-Volatile + mirror */
+#define FAULT_IRQ_REG 0x0C /* Non-Volatile */
+#define IRQ_ENABLE_REG 0x0D /* Non-Volatile */
+#define SYSOK_REG 0x0E /* Non-Volatile + mirror */
+
+#define AUTO_INPUT_VOLT_DETECT_REG 0x10 /* Non-Volatile Read-Only */
+#define STATUS_IRQ_REG 0x11 /* Non-Volatile Read-Only */
+#define I2C_SLAVE_ADDR_REG 0x12 /* Non-Volatile Read-Only */
+
+#define CMD_A_REG 0x30 /* Volatile Read-Write */
+#define CMD_B_REG 0x31 /* Volatile Read-Write */
+#define CMD_C_REG 0x33 /* Volatile Read-Write */
+
+#define IRQ_STATUS_A_REG 0x35 /* Volatile Read-Only */
+#define IRQ_STATUS_B_REG 0x36 /* Volatile Read-Only */
+#define IRQ_STATUS_C_REG 0x37 /* Volatile Read-Only */
+#define IRQ_STATUS_D_REG 0x38 /* Volatile Read-Only */
+#define IRQ_STATUS_E_REG 0x39 /* Volatile Read-Only */
+#define IRQ_STATUS_F_REG 0x3A /* Volatile Read-Only */
+
+#define STATUS_A_REG 0x3B /* Volatile Read-Only */
+#define STATUS_B_REG 0x3D /* Volatile Read-Only */
+/* Note: STATUS_C_REG was removed from SMB349 to SMB350 */
+#define STATUS_D_REG 0x3E /* Volatile Read-Only */
+#define STATUS_E_REG 0x3F /* Volatile Read-Only */
+
+#define IRQ_STATUS_NUM (IRQ_STATUS_F_REG - IRQ_STATUS_A_REG + 1)
+
+/* Status bits and masks */
+#define SMB350_MASK(BITS, POS) ((u8)(((1 << BITS) - 1) << POS))
+#define FAST_CHG_CURRENT_MASK SMB350_MASK(4, 4)
+
+#define SMB350_FAST_CHG_MIN_MA 1000
+#define SMB350_FAST_CHG_STEP_MA 200
+#define SMB350_FAST_CHG_MAX_MA 3600
+
+#define TERM_CURRENT_MASK SMB350_MASK(3, 2)
+
+#define SMB350_TERM_CUR_MIN_MA 200
+#define SMB350_TERM_CUR_STEP_MA 100
+#define SMB350_TERM_CUR_MAX_MA 700
+
+#define CMD_A_VOLATILE_WR_PERM BIT(7)
+#define CHG_CTRL_CURR_TERM_END_CHG BIT(6)
+
+enum smb350_chg_status {
+ SMB_CHG_STATUS_NONE = 0,
+ SMB_CHG_STATUS_PRE_CHARGE = 1,
+ SMB_CHG_STATUS_FAST_CHARGE = 2,
+ SMB_CHG_STATUS_TAPER_CHARGE = 3,
+};
+
+static const char * const smb350_chg_status[] = {
+ "none",
+ "pre-charge",
+ "fast-charge",
+ "taper-charge"
+};
+
+struct smb350_device {
+ /* setup */
+ int chg_current_ma;
+ int term_current_ma;
+ int chg_en_n_gpio;
+ int chg_susp_n_gpio;
+ int stat_gpio;
+ int irq;
+ /* internal */
+ enum smb350_chg_status chg_status;
+ struct i2c_client *client;
+ struct delayed_work irq_work;
+ struct dentry *dent;
+ struct wake_lock chg_wake_lock;
+ struct power_supply dc_psy;
+};
+
+static struct smb350_device *smb350_dev;
+
+struct debug_reg {
+ char *name;
+ u8 reg;
+};
+
+#define SMB350_DEBUG_REG(x) {#x, x##_REG}
+
+static struct debug_reg smb350_debug_regs[] = {
+ SMB350_DEBUG_REG(CHG_CURRENT),
+ SMB350_DEBUG_REG(CHG_OTHER_CURRENT),
+ SMB350_DEBUG_REG(VAR_FUNC),
+ SMB350_DEBUG_REG(FLOAT_VOLTAGE),
+ SMB350_DEBUG_REG(CHG_CTRL),
+ SMB350_DEBUG_REG(STAT_TIMER),
+ SMB350_DEBUG_REG(PIN_ENABLE_CTRL),
+ SMB350_DEBUG_REG(THERM_CTRL_A),
+ SMB350_DEBUG_REG(SYSOK_USB3_SELECT),
+ SMB350_DEBUG_REG(CTRL_FUNCTIONS),
+ SMB350_DEBUG_REG(OTG_TLIM_THERM_CNTRL),
+ SMB350_DEBUG_REG(TEMP_MONITOR),
+ SMB350_DEBUG_REG(FAULT_IRQ),
+ SMB350_DEBUG_REG(IRQ_ENABLE),
+ SMB350_DEBUG_REG(SYSOK),
+ SMB350_DEBUG_REG(AUTO_INPUT_VOLT_DETECT),
+ SMB350_DEBUG_REG(STATUS_IRQ),
+ SMB350_DEBUG_REG(I2C_SLAVE_ADDR),
+ SMB350_DEBUG_REG(CMD_A),
+ SMB350_DEBUG_REG(CMD_B),
+ SMB350_DEBUG_REG(CMD_C),
+ SMB350_DEBUG_REG(IRQ_STATUS_A),
+ SMB350_DEBUG_REG(IRQ_STATUS_B),
+ SMB350_DEBUG_REG(IRQ_STATUS_C),
+ SMB350_DEBUG_REG(IRQ_STATUS_D),
+ SMB350_DEBUG_REG(IRQ_STATUS_E),
+ SMB350_DEBUG_REG(IRQ_STATUS_F),
+ SMB350_DEBUG_REG(STATUS_A),
+ SMB350_DEBUG_REG(STATUS_B),
+ SMB350_DEBUG_REG(STATUS_D),
+ SMB350_DEBUG_REG(STATUS_E),
+};
+
+/*
+ * Read 8-bit register value. return negative value on error.
+ */
+static int smb350_read_reg(struct i2c_client *client, u8 reg)
+{
+ int val;
+
+ val = i2c_smbus_read_byte_data(client, reg);
+ if (val < 0)
+ pr_err("i2c read fail. reg=0x%x.ret=%d.\n", reg, val);
+ else
+ pr_debug("reg=0x%02X.val=0x%02X.\n", reg , val);
+
+ return val;
+}
+
+/*
+ * Write 8-bit register value. return negative value on error.
+ */
+static int smb350_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+ if (ret < 0)
+ pr_err("i2c read fail. reg=0x%x.val=0x%x.ret=%d.\n",
+ reg, val, ret);
+ else
+ pr_debug("reg=0x%02X.val=0x%02X.\n", reg , val);
+
+ return ret;
+}
+
+static int smb350_masked_write(struct i2c_client *client, int reg, u8 mask,
+ u8 val)
+{
+ int ret;
+ int temp;
+ int shift = find_first_bit((unsigned long *) &mask, 8);
+
+ temp = smb350_read_reg(client, reg);
+ if (temp < 0)
+ return temp;
+
+ temp &= ~mask;
+ temp |= (val << shift) & mask;
+ ret = smb350_write_reg(client, reg, temp);
+
+ return ret;
+}
+
+#define SMB350_FLOAT_VOLT_BASE_MV 6920
+#define SMB350_FLOAT_VOLT_STEP_MV 40
+#define SMB350_FLOAT_VOLT_MAX_MV (6920 + 0x2F * 40)
+
+/* Fast-to-Taper charging volatge */
+static int smb350_get_float_voltage(struct i2c_client *client)
+{
+ u16 val = smb350_read_reg(client, STATUS_A_REG);
+
+ val = SMB350_FLOAT_VOLT_BASE_MV +
+ ((val & 0x2F) * SMB350_FLOAT_VOLT_STEP_MV);
+
+ return val;
+}
+
+static bool smb350_is_dc_present(struct i2c_client *client)
+{
+ u16 irq_status_f = smb350_read_reg(client, IRQ_STATUS_F_REG);
+ bool power_ok = irq_status_f & 0x01;
+
+ /* Power-ok , IRQ_STATUS_F_REG bit#0 */
+ if (power_ok)
+ pr_debug("DC is present.\n");
+ else
+ pr_debug("DC is missing.\n");
+
+ return power_ok;
+}
+
+static bool smb350_is_charging(struct i2c_client *client)
+{
+ int val;
+ bool is_charging;
+
+ val = smb350_read_reg(client, STATUS_B_REG);
+ if (val < 0)
+ return false;
+
+ val = (val >> 1) & 0x3;
+
+ is_charging = (val != 0);
+
+ return is_charging;
+}
+
+static int smb350_get_prop_charge_type(struct smb350_device *dev)
+{
+ int status_b;
+ enum smb350_chg_status status;
+ int chg_type = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+ bool chg_enabled;
+ bool charger_err;
+ struct i2c_client *client = dev->client;
+
+ status_b = smb350_read_reg(client, STATUS_B_REG);
+ if (status_b < 0) {
+ pr_err("failed to read STATUS_B_REG.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+ }
+
+ chg_enabled = (bool) (status_b & 0x01);
+ charger_err = (bool) (status_b & (1<<6));
+
+ if (!chg_enabled) {
+ pr_warn("Charging not enabled.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+ }
+
+ if (charger_err) {
+ pr_warn("Charger error detected.\n");
+ return POWER_SUPPLY_CHARGE_TYPE_NONE;
+ }
+
+ status = (status_b >> 1) & 0x3;
+
+ if (status == SMB_CHG_STATUS_NONE)
+ chg_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
+ else if (status == SMB_CHG_STATUS_FAST_CHARGE) /* constant current */
+ chg_type = POWER_SUPPLY_CHARGE_TYPE_FAST;
+ else if (status == SMB_CHG_STATUS_TAPER_CHARGE) /* constant voltage */
+ chg_type = POWER_SUPPLY_CHARGE_TYPE_FAST;
+ else if (status == SMB_CHG_STATUS_PRE_CHARGE)
+ chg_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+
+ pr_debug("smb-chg-status=%d=%s.\n", status, smb350_chg_status[status]);
+
+ if (dev->chg_status != status) { /* Status changed */
+ if (status == SMB_CHG_STATUS_NONE) {
+ pr_debug("Charging stopped.\n");
+ wake_unlock(&dev->chg_wake_lock);
+ } else {
+ pr_debug("Charging started.\n");
+ wake_lock(&dev->chg_wake_lock);
+ }
+ }
+
+ dev->chg_status = status;
+
+ return chg_type;
+}
+
+static void smb350_enable_charging(struct smb350_device *dev, bool enable)
+{
+ int val = !enable; /* active low */
+
+ pr_debug("enable=%d.\n", enable);
+
+ gpio_set_value_cansleep(dev->chg_en_n_gpio, val);
+}
+
+/* When the status bit of a certain condition is read,
+ * the corresponding IRQ signal is cleared.
+ */
+static int smb350_clear_irq(struct i2c_client *client)
+{
+ int ret;
+
+ ret = smb350_read_reg(client, IRQ_STATUS_A_REG);
+ if (ret < 0)
+ return ret;
+ ret = smb350_read_reg(client, IRQ_STATUS_B_REG);
+ if (ret < 0)
+ return ret;
+ ret = smb350_read_reg(client, IRQ_STATUS_C_REG);
+ if (ret < 0)
+ return ret;
+ ret = smb350_read_reg(client, IRQ_STATUS_D_REG);
+ if (ret < 0)
+ return ret;
+ ret = smb350_read_reg(client, IRQ_STATUS_E_REG);
+ if (ret < 0)
+ return ret;
+ ret = smb350_read_reg(client, IRQ_STATUS_F_REG);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * Do the IRQ work from a thread context rather than interrupt context.
+ * Read status registers to clear interrupt source.
+ * Notify the power-supply driver about change detected.
+ * Relevant events for start/stop charging:
+ * 1. DC insert/remove
+ * 2. End-Of-Charging
+ * 3. Battery insert/remove
+ * 4. Temperture too hot/cold
+ * 5. Charging timeout expired.
+ */
+static void smb350_irq_worker(struct work_struct *work)
+{
+ int ret = 0;
+ struct smb350_device *dev =
+ container_of(work, struct smb350_device, irq_work.work);
+
+ ret = smb350_clear_irq(dev->client);
+ if (ret == 0) { /* Cleared ok */
+ /* Notify Battery-psy about status changed */
+ pr_debug("Notify power_supply_changed.\n");
+ power_supply_changed(&dev->dc_psy);
+ }
+}
+
+/*
+ * The STAT pin is low when charging and high when not charging.
+ * When the smb350 start/stop charging the STAT pin triggers an interrupt.
+ * Interrupt is triggered on both rising or falling edge.
+ */
+static irqreturn_t smb350_irq(int irq, void *dev_id)
+{
+ struct smb350_device *dev = dev_id;
+
+ pr_debug("\n");
+
+ /* I2C transfers API should not run in interrupt context */
+ schedule_delayed_work(&dev->irq_work, msecs_to_jiffies(100));
+
+ return IRQ_HANDLED;
+}
+
+static enum power_supply_property pm_power_props[] = {
+ /* real time */
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ /* fixed */
+ POWER_SUPPLY_PROP_MANUFACTURER,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+};
+
+static char *pm_power_supplied_to[] = {
+ "battery",
+};
+
+static int smb350_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ int ret = 0;
+ struct smb350_device *dev = container_of(psy,
+ struct smb350_device,
+ dc_psy);
+ struct i2c_client *client = dev->client;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = smb350_is_dc_present(client);
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = smb350_is_charging(client);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ val->intval = smb350_get_prop_charge_type(dev);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = smb350_get_float_voltage(client);
+ val->intval *= 1000; /* mV to uV */
+ break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = SMB350_NAME;
+ break;
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = "Summit Microelectronics";
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ val->intval = dev->chg_current_ma;
+ break;
+ default:
+ pr_err("Invalid prop = %d.\n", psp);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int smb350_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ int ret = 0;
+ struct smb350_device *dev =
+ container_of(psy, struct smb350_device, dc_psy);
+
+ switch (psp) {
+ /*
+ * Allow a smart battery to Start/Stop charging.
+ * i.e. when End-Of-Charging detected.
+ * The SMB350 can be configured to terminate charging
+ * when charge-current reaching Termination-Current.
+ */
+ case POWER_SUPPLY_PROP_ONLINE:
+ smb350_enable_charging(dev, val->intval);
+ break;
+ default:
+ pr_err("Invalid prop = %d.\n", psp);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int smb350_set_chg_current(struct i2c_client *client, int current_ma)
+{
+ int ret;
+ u8 temp;
+
+ if ((current_ma < SMB350_FAST_CHG_MIN_MA) ||
+ (current_ma > SMB350_FAST_CHG_MAX_MA)) {
+ pr_err("invalid current %d mA.\n", current_ma);
+ return -EINVAL;
+ }
+
+ temp = (current_ma - SMB350_FAST_CHG_MIN_MA) / SMB350_FAST_CHG_STEP_MA;
+
+ pr_debug("fast-chg-current=%d mA setting %02x\n", current_ma, temp);
+
+ ret = smb350_masked_write(client, CHG_CURRENT_REG,
+ FAST_CHG_CURRENT_MASK, temp);
+
+ return ret;
+}
+
+static int smb350_set_term_current(struct i2c_client *client, int current_ma)
+{
+ int ret;
+ u8 temp;
+
+ if ((current_ma < SMB350_TERM_CUR_MIN_MA) ||
+ (current_ma > SMB350_TERM_CUR_MAX_MA)) {
+ pr_err("invalid current %d mA to set\n", current_ma);
+ return -EINVAL;
+ }
+
+ temp = (current_ma - SMB350_TERM_CUR_MIN_MA) / SMB350_TERM_CUR_STEP_MA;
+
+ pr_debug("term-current=%d mA setting %02x\n", current_ma, temp);
+
+ ret = smb350_masked_write(client, CHG_OTHER_CURRENT_REG,
+ TERM_CURRENT_MASK, temp);
+
+ return ret;
+}
+
+static int smb350_set_reg(void *data, u64 val)
+{
+ u32 addr = (u32) data;
+ int ret;
+ struct i2c_client *client = smb350_dev->client;
+
+ ret = smb350_write_reg(client, addr, (u8) val);
+
+ return ret;
+}
+
+static int smb350_get_reg(void *data, u64 *val)
+{
+ u32 addr = (u32) data;
+ int ret;
+ struct i2c_client *client = smb350_dev->client;
+
+ ret = smb350_read_reg(client, addr);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, smb350_get_reg, smb350_set_reg, "0x%02llx\n");
+
+static int smb350_create_debugfs_entries(struct smb350_device *dev)
+{
+ int i;
+ dev->dent = debugfs_create_dir(SMB350_NAME, NULL);
+ if (IS_ERR(dev->dent)) {
+ pr_err("smb350 driver couldn't create debugfs dir\n");
+ return -EFAULT;
+ }
+
+ for (i = 0 ; i < ARRAY_SIZE(smb350_debug_regs) ; i++) {
+ char *name = smb350_debug_regs[i].name;
+ u32 reg = smb350_debug_regs[i].reg;
+ struct dentry *file;
+
+ file = debugfs_create_file(name, 0644, dev->dent,
+ (void *) reg, ®_fops);
+ if (IS_ERR(file)) {
+ pr_err("debugfs_create_file %s failed.\n", name);
+ return -EFAULT;
+ }
+ }
+
+ return 0;
+}
+
+static int smb350_set_volatile_params(struct smb350_device *dev)
+{
+ int ret;
+ struct i2c_client *client = dev->client;
+
+ pr_debug("\n");
+
+ ret = smb350_write_reg(client, CMD_A_REG, CMD_A_VOLATILE_WR_PERM);
+ if (ret) {
+ pr_err("Failed to set VOLATILE_WR_PERM ret=%d\n", ret);
+ return ret;
+ }
+
+ /* Disable SMB350 pulse-IRQ mechanism,
+ * we use interrupts based on charging-status-transition
+ */
+ /* Enable STATUS output (regardless of IRQ-pulses) */
+ smb350_masked_write(client, CMD_A_REG, BIT(0), 0);
+
+ /* Disable LED blinking - avoid periodic irq */
+ smb350_masked_write(client, PIN_ENABLE_CTRL_REG, BIT(7), 0);
+
+ /* Disable Failure SMB-IRQ */
+ ret = smb350_write_reg(client, FAULT_IRQ_REG, 0x00);
+ if (ret) {
+ pr_err("Failed to set FAULT_IRQ_REG ret=%d\n", ret);
+ return ret;
+ }
+
+ /* Disable Event IRQ */
+ ret = smb350_write_reg(client, IRQ_ENABLE_REG, 0x00);
+ if (ret) {
+ pr_err("Failed to set IRQ_ENABLE_REG ret=%d\n", ret);
+ return ret;
+ }
+
+ /* Enable charging/not-charging status output via STAT pin */
+ smb350_masked_write(client, STAT_TIMER_REG, BIT(5), 0);
+
+ /* Disable Automatic Recharge */
+ smb350_masked_write(client, CHG_CTRL_REG, BIT(7), 1);
+
+ /* Set fast-charge current */
+ ret = smb350_set_chg_current(client, dev->chg_current_ma);
+ if (ret) {
+ pr_err("Failed to set FAST_CHG_CURRENT ret=%d\n", ret);
+ return ret;
+ }
+
+ if (dev->term_current_ma > 0) {
+ /* Enable Current Termination */
+ smb350_masked_write(client, CHG_CTRL_REG, BIT(6), 0);
+
+ /* Set Termination current */
+ smb350_set_term_current(client, dev->term_current_ma);
+ } else {
+ /* Disable Current Termination */
+ smb350_masked_write(client, CHG_CTRL_REG, BIT(6), 1);
+ }
+
+ return 0;
+}
+
+static int __devinit smb350_register_psy(struct smb350_device *dev)
+{
+ int ret;
+
+ dev->dc_psy.name = "dc";
+ dev->dc_psy.type = POWER_SUPPLY_TYPE_MAINS;
+ dev->dc_psy.supplied_to = pm_power_supplied_to;
+ dev->dc_psy.num_supplicants = ARRAY_SIZE(pm_power_supplied_to);
+ dev->dc_psy.properties = pm_power_props;
+ dev->dc_psy.num_properties = ARRAY_SIZE(pm_power_props);
+ dev->dc_psy.get_property = smb350_get_property;
+ dev->dc_psy.set_property = smb350_set_property;
+
+ ret = power_supply_register(&dev->client->dev,
+ &dev->dc_psy);
+ if (ret) {
+ pr_err("failed to register power_supply. ret=%d.\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __devinit smb350_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ const struct smb350_platform_data *pdata;
+ struct device_node *dev_node = client->dev.of_node;
+ struct smb350_device *dev;
+
+ /* STAT pin change on start/stop charging */
+ u32 irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ pr_err("i2c func fail.\n");
+ return -EIO;
+ }
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ pr_err("alloc fail.\n");
+ return -ENOMEM;
+ }
+
+ smb350_dev = dev;
+ dev->client = client;
+
+ if (dev_node) {
+ dev->chg_en_n_gpio =
+ of_get_named_gpio(dev_node, "summit,chg-en-n-gpio", 0);
+ pr_debug("chg_en_n_gpio = %d.\n", dev->chg_en_n_gpio);
+
+ dev->chg_susp_n_gpio =
+ of_get_named_gpio(dev_node,
+ "summit,chg-susp-n-gpio", 0);
+ pr_debug("chg_susp_n_gpio = %d.\n", dev->chg_susp_n_gpio);
+
+ dev->stat_gpio =
+ of_get_named_gpio(dev_node, "summit,stat-gpio", 0);
+ pr_debug("stat_gpio = %d.\n", dev->stat_gpio);
+
+ ret = of_property_read_u32(dev_node, "summit,chg-current-ma",
+ &(dev->chg_current_ma));
+ pr_debug("chg_current_ma = %d.\n", dev->chg_current_ma);
+ if (ret) {
+ pr_err("Unable to read chg_current.\n");
+ return ret;
+ }
+ ret = of_property_read_u32(dev_node, "summit,term-current-ma",
+ &(dev->term_current_ma));
+ pr_debug("term_current_ma = %d.\n", dev->term_current_ma);
+ if (ret) {
+ pr_err("Unable to read term_current_ma.\n");
+ return ret;
+ }
+ } else {
+ pdata = client->dev.platform_data;
+
+ if (pdata == NULL) {
+ pr_err("no platform data.\n");
+ return -EINVAL;
+ }
+
+ dev->chg_en_n_gpio = pdata->chg_en_n_gpio;
+ dev->chg_susp_n_gpio = pdata->chg_susp_n_gpio;
+ dev->stat_gpio = pdata->stat_gpio;
+
+ dev->chg_current_ma = pdata->chg_current_ma;
+ dev->term_current_ma = pdata->term_current_ma;
+ }
+
+ ret = gpio_request(dev->stat_gpio, "smb350_stat");
+ if (ret) {
+ pr_err("gpio_request failed for %d ret=%d\n",
+ dev->stat_gpio, ret);
+ goto err_stat_gpio;
+ }
+ dev->irq = gpio_to_irq(dev->stat_gpio);
+ pr_debug("irq#=%d.\n", dev->irq);
+
+ ret = gpio_request(dev->chg_susp_n_gpio, "smb350_suspend");
+ if (ret) {
+ pr_err("gpio_request failed for %d ret=%d\n",
+ dev->chg_susp_n_gpio, ret);
+ goto err_susp_gpio;
+ }
+
+ ret = gpio_request(dev->chg_en_n_gpio, "smb350_charger_enable");
+ if (ret) {
+ pr_err("gpio_request failed for %d ret=%d\n",
+ dev->chg_en_n_gpio, ret);
+ goto err_en_gpio;
+ }
+
+ i2c_set_clientdata(client, dev);
+
+ pr_debug("set charge-enable + not suspend.\n");
+ gpio_set_value_cansleep(dev->chg_en_n_gpio, 1); /* Disable */
+ msleep(100);
+ gpio_set_value_cansleep(dev->chg_susp_n_gpio, 1); /* Normal */
+ msleep(100); /* Allow the device to exist shutdown */
+
+ smb350_read_reg(client, I2C_SLAVE_ADDR_REG);
+
+ ret = smb350_set_volatile_params(dev);
+ if (ret)
+ goto err_set_params;
+
+ ret = smb350_register_psy(dev);
+ if (ret)
+ goto err_set_params;
+
+ ret = smb350_create_debugfs_entries(dev);
+ if (ret)
+ goto err_debugfs;
+
+ INIT_DELAYED_WORK(&dev->irq_work, smb350_irq_worker);
+ wake_lock_init(&dev->chg_wake_lock,
+ WAKE_LOCK_SUSPEND, SMB350_NAME);
+
+ ret = request_irq(dev->irq, smb350_irq, irq_flags,
+ "smb350_irq", dev);
+ if (ret) {
+ pr_err("request_irq %d failed.ret=%d\n", dev->irq, ret);
+ goto err_irq;
+ }
+
+ smb350_enable_charging(dev, true);
+
+ return 0;
+
+err_irq:
+err_debugfs:
+ if (dev->dent)
+ debugfs_remove_recursive(dev->dent);
+err_set_params:
+ gpio_free(dev->chg_en_n_gpio);
+err_en_gpio:
+ gpio_free(dev->chg_susp_n_gpio);
+err_susp_gpio:
+ gpio_free(dev->stat_gpio);
+err_stat_gpio:
+ kfree(smb350_dev);
+ smb350_dev = NULL;
+
+ pr_info("FAIL.\n");
+
+ return ret;
+}
+
+static int __devexit smb350_remove(struct i2c_client *client)
+{
+ struct smb350_device *dev = i2c_get_clientdata(client);
+
+ power_supply_unregister(&dev->dc_psy);
+ gpio_free(dev->chg_en_n_gpio);
+ gpio_free(dev->chg_susp_n_gpio);
+ if (dev->stat_gpio)
+ gpio_free(dev->stat_gpio);
+ if (dev->irq)
+ free_irq(dev->irq, dev);
+ if (dev->dent)
+ debugfs_remove_recursive(dev->dent);
+ kfree(smb350_dev);
+ smb350_dev = NULL;
+
+ return 0;
+}
+
+static const struct i2c_device_id smb350_id[] = {
+ {SMB350_NAME, 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, smb350_id);
+
+static const struct of_device_id smb350_match[] = {
+ { .compatible = "summit,smb350-charger", },
+ { },
+};
+
+static struct i2c_driver smb350_driver = {
+ .driver = {
+ .name = SMB350_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(smb350_match),
+ },
+ .probe = smb350_probe,
+ .remove = __devexit_p(smb350_remove),
+ .id_table = smb350_id,
+};
+
+static int __init smb350_init(void)
+{
+ return i2c_add_driver(&smb350_driver);
+}
+module_init(smb350_init);
+
+static void __exit smb350_exit(void)
+{
+ return i2c_del_driver(&smb350_driver);
+}
+module_exit(smb350_exit);
+
+MODULE_DESCRIPTION("Driver for SMB350 charger chip");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:" SMB350_NAME);
diff --git a/drivers/slimbus/Kconfig b/drivers/slimbus/Kconfig
index a6a068d..72bd28a 100644
--- a/drivers/slimbus/Kconfig
+++ b/drivers/slimbus/Kconfig
@@ -16,4 +16,13 @@
help
Select driver for Qualcomm's Slimbus Master Component.
+config SLIMBUS_MSM_NGD
+ tristate "Qualcomm Slimbus Satellite Component"
+ help
+ Select driver for Qualcomm's Slimbus Satellite Component.
+ This is light-weight slimbus controller driver responsible for
+ communicating with slave HW directly over the bus using messaging
+ interface, and communicating with master component residing on ADSP
+ for bandwidth and data-channel management.
+
endif
diff --git a/drivers/slimbus/Makefile b/drivers/slimbus/Makefile
index 674f057..45d6e6e 100644
--- a/drivers/slimbus/Makefile
+++ b/drivers/slimbus/Makefile
@@ -3,3 +3,4 @@
#
obj-$(CONFIG_SLIMBUS) += slimbus.o
obj-$(CONFIG_SLIMBUS_MSM_CTRL) += slim-msm.o slim-msm-ctrl.o
+obj-$(CONFIG_SLIMBUS_MSM_NGD) += slim-msm.o slim-msm-ngd.o
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
new file mode 100644
index 0000000..1f2a95e
--- /dev/null
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -0,0 +1,962 @@
+/* Copyright (c) 2012, The Linux Foundation. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/slimbus/slimbus.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_slimbus.h>
+#include <linux/timer.h>
+#include <mach/sps.h>
+#include "slim-msm.h"
+#include <mach/qdsp6v2/apr.h>
+
+#define NGD_SLIM_NAME "ngd_msm_ctrl"
+#define SLIM_LA_MGR 0xFF
+#define SLIM_ROOT_FREQ 24576000
+
+#define NGD_BASE_V1(r) (((r) % 2) ? 0x800 : 0xA00)
+#define NGD_BASE_V2(r) (((r) % 2) ? 0x1000 : 0x2000)
+#define NGD_BASE(r, v) ((v) ? NGD_BASE_V2(r) : NGD_BASE_V1(r))
+/* NGD (Non-ported Generic Device) registers */
+enum ngd_reg {
+ NGD_CFG = 0x0,
+ NGD_STATUS = 0x4,
+ NGD_RX_MSGQ_CFG = 0x8,
+ NGD_INT_EN = 0x10,
+ NGD_INT_STAT = 0x14,
+ NGD_INT_CLR = 0x18,
+ NGD_TX_MSG = 0x30,
+ NGD_RX_MSG = 0x70,
+ NGD_IE_STAT = 0xF0,
+ NGD_VE_STAT = 0x100,
+};
+
+enum ngd_msg_cfg {
+ NGD_CFG_ENABLE = 1,
+ NGD_CFG_RX_MSGQ_EN = 1 << 1,
+ NGD_CFG_TX_MSGQ_EN = 1 << 2,
+};
+
+enum ngd_intr {
+ NGD_INT_RECFG_DONE = 1 << 24,
+ NGD_INT_TX_NACKED_2 = 1 << 25,
+ NGD_INT_MSG_BUF_CONTE = 1 << 26,
+ NGD_INT_MSG_TX_INVAL = 1 << 27,
+ NGD_INT_IE_VE_CHG = 1 << 28,
+ NGD_INT_DEV_ERR = 1 << 29,
+ NGD_INT_RX_MSG_RCVD = 1 << 30,
+ NGD_INT_TX_MSG_SENT = 1 << 31,
+};
+
+enum ngd_offsets {
+ NGD_NACKED_MC = 0x7F00000,
+ NGD_ACKED_MC = 0xFE000,
+ NGD_ERROR = 0x1800,
+ NGD_MSGQ_SUPPORT = 0x400,
+ NGD_RX_MSGQ_TIME_OUT = 0x16,
+ NGD_ENUMERATED = 0x1,
+ NGD_TX_BUSY = 0x0,
+};
+
+static irqreturn_t ngd_slim_interrupt(int irq, void *d)
+{
+ struct msm_slim_ctrl *dev = (struct msm_slim_ctrl *)d;
+ void __iomem *ngd = dev->base + NGD_BASE(dev->ctrl.nr, dev->ver);
+ u32 stat = readl_relaxed(ngd + NGD_INT_STAT);
+
+ if (stat & NGD_INT_TX_MSG_SENT) {
+ writel_relaxed(NGD_INT_TX_MSG_SENT, ngd + NGD_INT_CLR);
+ /* Make sure interrupt is cleared */
+ mb();
+ if (dev->wr_comp)
+ complete(dev->wr_comp);
+ } else if ((stat & NGD_INT_MSG_BUF_CONTE) ||
+ (stat & NGD_INT_MSG_TX_INVAL) || (stat & NGD_INT_DEV_ERR) ||
+ (stat & NGD_INT_TX_NACKED_2)) {
+ dev_err(dev->dev, "NGD interrupt error:0x%x", stat);
+ writel_relaxed(stat, ngd + NGD_INT_CLR);
+ /* Guarantee that error interrupts are cleared */
+ mb();
+ if (((stat & NGD_INT_TX_NACKED_2) ||
+ (stat & NGD_INT_MSG_TX_INVAL))) {
+ dev->err = -EIO;
+ if (dev->wr_comp)
+ complete(dev->wr_comp);
+ }
+ }
+ if (stat & NGD_INT_RX_MSG_RCVD) {
+ u32 rx_buf[10];
+ u8 len, i;
+ rx_buf[0] = readl_relaxed(ngd + NGD_RX_MSG);
+ len = rx_buf[0] & 0x1F;
+ for (i = 1; i < ((len + 3) >> 2); i++) {
+ rx_buf[i] = readl_relaxed(ngd + NGD_RX_MSG +
+ (4 * i));
+ dev_dbg(dev->dev, "REG-RX data: %x\n", rx_buf[i]);
+ }
+ msm_slim_rx_enqueue(dev, rx_buf, len);
+ writel_relaxed(NGD_INT_RX_MSG_RCVD,
+ ngd + NGD_INT_CLR);
+ /*
+ * Guarantee that CLR bit write goes through before
+ * queuing work
+ */
+ mb();
+ if (dev->use_rx_msgqs)
+ dev_err(dev->dev,
+ "direct message received even with RX MSGQs");
+ else
+ complete(&dev->rx_msgq_notify);
+ }
+ if (stat & NGD_INT_RECFG_DONE) {
+ writel_relaxed(NGD_INT_RECFG_DONE, ngd + NGD_INT_CLR);
+ /* Guarantee RECONFIG DONE interrupt is cleared */
+ mb();
+ /* In satellite mode, just log the reconfig done IRQ */
+ dev_dbg(dev->dev, "reconfig done IRQ for NGD");
+ }
+ if (stat & NGD_INT_IE_VE_CHG) {
+ writel_relaxed(NGD_INT_IE_VE_CHG, ngd + NGD_INT_CLR);
+ /* Guarantee IE VE change interrupt is cleared */
+ mb();
+ dev_err(dev->dev, "NGD IE VE change");
+ }
+ return IRQ_HANDLED;
+}
+
+static int ngd_get_tid(struct slim_controller *ctrl, struct slim_msg_txn *txn,
+ u8 *tid, struct completion *done)
+{
+ struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+ if (ctrl->last_tid <= 255) {
+ ctrl->txnt = krealloc(ctrl->txnt,
+ (ctrl->last_tid + 1) *
+ sizeof(struct slim_msg_txn *),
+ GFP_KERNEL);
+ if (!ctrl->txnt)
+ return -ENOMEM;
+ dev->msg_cnt = ctrl->last_tid;
+ ctrl->last_tid++;
+ } else {
+ int i;
+ for (i = 0; i < 256; i++) {
+ dev->msg_cnt = ((dev->msg_cnt + 1) & 0xFF);
+ if (ctrl->txnt[dev->msg_cnt] == NULL)
+ break;
+ }
+ if (i >= 256) {
+ dev_err(&ctrl->dev, "out of TID");
+ return -ENOMEM;
+ }
+ }
+ ctrl->txnt[dev->msg_cnt] = txn;
+ txn->tid = dev->msg_cnt;
+ txn->comp = done;
+ *tid = dev->msg_cnt;
+ return 0;
+}
+static int ngd_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn)
+{
+ DECLARE_COMPLETION_ONSTACK(done);
+ DECLARE_COMPLETION_ONSTACK(tx_sent);
+
+ struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+ u32 *pbuf;
+ u8 *puc;
+ int ret = 0;
+ int msgv = -1;
+ u8 la = txn->la;
+ u8 wbuf[SLIM_RX_MSGQ_BUF_LEN];
+
+ if (txn->mt == SLIM_MSG_MT_CORE &&
+ (txn->mc >= SLIM_MSG_MC_BEGIN_RECONFIGURATION &&
+ txn->mc <= SLIM_MSG_MC_RECONFIGURE_NOW)) {
+ return 0;
+ }
+ msgv = msm_slim_get_ctrl(dev);
+ mutex_lock(&dev->tx_lock);
+ if (txn->mc != SLIM_USR_MC_REPORT_SATELLITE &&
+ (dev->state == MSM_CTRL_ASLEEP ||
+ dev->state == MSM_CTRL_SLEEPING)) {
+ int timeout;
+ dev_err(dev->dev, "controller not ready");
+ mutex_unlock(&dev->tx_lock);
+ /* Reconf is signalled when master responds */
+ timeout = wait_for_completion_timeout(&dev->reconf, HZ);
+ if (timeout) {
+ mutex_lock(&dev->tx_lock);
+ } else {
+ if (msgv >= 0)
+ msm_slim_put_ctrl(dev);
+ return -EBUSY;
+ }
+ }
+ if (txn->mt == SLIM_MSG_MT_CORE &&
+ (txn->mc == SLIM_MSG_MC_CONNECT_SOURCE ||
+ txn->mc == SLIM_MSG_MC_CONNECT_SINK ||
+ txn->mc == SLIM_MSG_MC_DISCONNECT_PORT)) {
+ int i = 0;
+ txn->mt = SLIM_MSG_MT_DEST_REFERRED_USER;
+ if (txn->mc == SLIM_MSG_MC_CONNECT_SOURCE)
+ txn->mc = SLIM_USR_MC_CONNECT_SRC;
+ else if (txn->mc == SLIM_MSG_MC_CONNECT_SINK)
+ txn->mc = SLIM_USR_MC_CONNECT_SINK;
+ else if (txn->mc == SLIM_MSG_MC_DISCONNECT_PORT)
+ txn->mc = SLIM_USR_MC_DISCONNECT_PORT;
+ if (txn->la == SLIM_LA_MGR)
+ txn->la = dev->pgdla;
+ wbuf[i++] = txn->la;
+ la = SLIM_LA_MGR;
+ wbuf[i++] = txn->wbuf[0];
+ if (txn->mc != SLIM_USR_MC_DISCONNECT_PORT)
+ wbuf[i++] = txn->wbuf[1];
+ ret = ngd_get_tid(ctrl, txn, &wbuf[i++], &done);
+ if (ret) {
+ pr_err("TID for connect/disconnect fail:%d", ret);
+ goto ngd_xfer_err;
+ }
+ txn->len = i;
+ txn->wbuf = wbuf;
+ txn->rl = txn->len + 4;
+ }
+ txn->rl--;
+ pbuf = msm_get_msg_buf(dev, txn->rl);
+ if (!pbuf) {
+ dev_err(dev->dev, "Message buffer unavailable");
+ ret = -ENOMEM;
+ goto ngd_xfer_err;
+ }
+ dev->err = 0;
+
+ if (txn->dt == SLIM_MSG_DEST_ENUMADDR) {
+ ret = -EPROTONOSUPPORT;
+ goto ngd_xfer_err;
+ }
+ if (txn->dt == SLIM_MSG_DEST_LOGICALADDR)
+ *pbuf = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, txn->mc, 0,
+ la);
+ else
+ *pbuf = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, txn->mc, 1,
+ la);
+ if (txn->dt == SLIM_MSG_DEST_LOGICALADDR)
+ puc = ((u8 *)pbuf) + 3;
+ else
+ puc = ((u8 *)pbuf) + 2;
+ if (txn->rbuf)
+ *(puc++) = txn->tid;
+ if ((txn->mt == SLIM_MSG_MT_CORE) &&
+ ((txn->mc >= SLIM_MSG_MC_REQUEST_INFORMATION &&
+ txn->mc <= SLIM_MSG_MC_REPORT_INFORMATION) ||
+ (txn->mc >= SLIM_MSG_MC_REQUEST_VALUE &&
+ txn->mc <= SLIM_MSG_MC_CHANGE_VALUE))) {
+ *(puc++) = (txn->ec & 0xFF);
+ *(puc++) = (txn->ec >> 8)&0xFF;
+ }
+ if (txn->wbuf)
+ memcpy(puc, txn->wbuf, txn->len);
+ if (txn->mt == SLIM_MSG_MT_DEST_REFERRED_USER &&
+ (txn->mc == SLIM_USR_MC_CONNECT_SRC ||
+ txn->mc == SLIM_USR_MC_CONNECT_SINK ||
+ txn->mc == SLIM_USR_MC_DISCONNECT_PORT) && txn->wbuf &&
+ wbuf[0] == dev->pgdla) {
+ if (txn->mc != SLIM_MSG_MC_DISCONNECT_PORT)
+ dev->err = msm_slim_connect_pipe_port(dev, wbuf[1]);
+ else {
+ struct msm_slim_endp *endpoint = &dev->pipes[wbuf[1]];
+ struct sps_register_event sps_event;
+ memset(&sps_event, 0, sizeof(sps_event));
+ sps_register_event(endpoint->sps, &sps_event);
+ sps_disconnect(endpoint->sps);
+ /*
+ * Remove channel disconnects master-side ports from
+ * channel. No need to send that again on the bus
+ */
+ dev->pipes[wbuf[1]].connected = false;
+ mutex_unlock(&dev->tx_lock);
+ if (msgv >= 0)
+ msm_slim_put_ctrl(dev);
+ return 0;
+ }
+ if (dev->err) {
+ dev_err(dev->dev, "pipe-port connect err:%d", dev->err);
+ goto ngd_xfer_err;
+ }
+ }
+ dev->err = 0;
+ dev->wr_comp = &tx_sent;
+ ret = msm_send_msg_buf(dev, pbuf, txn->rl,
+ NGD_BASE(dev->ctrl.nr, dev->ver) + NGD_TX_MSG);
+ if (!ret) {
+ int timeout = wait_for_completion_timeout(&tx_sent, HZ);
+ if (!timeout)
+ ret = -ETIMEDOUT;
+ else
+ ret = dev->err;
+ }
+ dev->wr_comp = NULL;
+ if (ret) {
+ u32 conf, stat, rx_msgq, int_stat, int_en, int_clr;
+ void __iomem *ngd = dev->base + NGD_BASE(dev->ctrl.nr,
+ dev->ver);
+ dev_err(dev->dev, "TX failed :MC:0x%x,mt:0x%x, ret:%d, ver:%d",
+ txn->mc, txn->mt, ret, dev->ver);
+ conf = readl_relaxed(ngd);
+ stat = readl_relaxed(ngd + NGD_STATUS);
+ rx_msgq = readl_relaxed(ngd + NGD_RX_MSGQ_CFG);
+ int_stat = readl_relaxed(ngd + NGD_INT_STAT);
+ int_en = readl_relaxed(ngd + NGD_INT_EN);
+ int_clr = readl_relaxed(ngd + NGD_INT_CLR);
+
+ pr_err("conf:0x%x,stat:0x%x,rxmsgq:0x%x", conf, stat, rx_msgq);
+ pr_err("int_stat:0x%x,int_en:0x%x,int_cll:0x%x", int_stat,
+ int_en, int_clr);
+ } else if (txn->mt == SLIM_MSG_MT_DEST_REFERRED_USER &&
+ (txn->mc == SLIM_USR_MC_CONNECT_SRC ||
+ txn->mc == SLIM_USR_MC_CONNECT_SINK ||
+ txn->mc == SLIM_USR_MC_DISCONNECT_PORT)) {
+ int timeout;
+ mutex_unlock(&dev->tx_lock);
+ if (msgv >= 0)
+ msm_slim_put_ctrl(dev);
+ timeout = wait_for_completion_timeout(txn->comp, HZ);
+ if (!timeout) {
+ pr_err("connect/disc :0x%x, tid:%d timed out", txn->mc,
+ txn->tid);
+ ret = -ETIMEDOUT;
+ } else {
+ ret = txn->ec;
+ }
+ if (ret)
+ pr_err("connect/disconnect:0x%x,tid:%d err:%d", txn->mc,
+ txn->tid, ret);
+ return ret ? ret : dev->err;
+ }
+ngd_xfer_err:
+ mutex_unlock(&dev->tx_lock);
+ if (msgv >= 0)
+ msm_slim_put_ctrl(dev);
+ return ret ? ret : dev->err;
+}
+
+static int ngd_xferandwait_ack(struct slim_controller *ctrl,
+ struct slim_msg_txn *txn)
+{
+ int ret = ngd_xfer_msg(ctrl, txn);
+ if (!ret) {
+ int timeout;
+ timeout = wait_for_completion_timeout(txn->comp, HZ);
+ if (!timeout) {
+ pr_err("master req:0x%x, tid:%d timed out", txn->mc,
+ txn->tid);
+ ret = -ETIMEDOUT;
+ } else {
+ ret = txn->ec;
+ }
+ }
+ if (ret)
+ pr_err("master msg:0x%x,tid:%d ret:%d", txn->mc,
+ txn->tid, ret);
+
+ return ret;
+}
+
+static int ngd_allocbw(struct slim_device *sb, int *subfrmc, int *clkgear)
+{
+ int ret;
+ struct slim_pending_ch *pch;
+ struct slim_msg_txn txn;
+ struct slim_controller *ctrl = sb->ctrl;
+ DECLARE_COMPLETION_ONSTACK(done);
+ u8 wbuf[SLIM_RX_MSGQ_BUF_LEN];
+
+ txn.mt = SLIM_MSG_MT_DEST_REFERRED_USER;
+ txn.dt = SLIM_MSG_DEST_LOGICALADDR;
+ txn.la = SLIM_LA_MGR;
+ txn.len = 0;
+ txn.ec = 0;
+ txn.wbuf = wbuf;
+ txn.rbuf = NULL;
+
+ list_for_each_entry(pch, &sb->mark_define, pending) {
+ struct slim_ich *slc;
+ slc = &ctrl->chans[pch->chan];
+ if (!slc) {
+ pr_err("no channel in define?");
+ return -ENXIO;
+ }
+ if (txn.len == 0) {
+ wbuf[txn.len++] = (u8) (slc->prop.dataf << 5) |
+ sb->laddr;
+ wbuf[txn.len] = slc->seglen;
+ if (slc->coeff == SLIM_COEFF_3)
+ wbuf[txn.len] |= 1 << 5;
+ wbuf[txn.len++] |= slc->prop.auxf << 6;
+ wbuf[txn.len++] = slc->rootexp << 4 | slc->prop.prot;
+ wbuf[txn.len++] = slc->prrate;
+ ret = ngd_get_tid(ctrl, &txn, &wbuf[txn.len++], &done);
+ if (ret) {
+ pr_err("no tid for channel define?");
+ return -ENXIO;
+ }
+ }
+ wbuf[txn.len++] = slc->chan;
+ }
+ if (txn.len) {
+ txn.mc = SLIM_USR_MC_DEF_ACT_CHAN;
+ txn.rl = txn.len + 4;
+ ret = ngd_xferandwait_ack(ctrl, &txn);
+ if (ret)
+ return ret;
+
+ txn.mc = SLIM_USR_MC_RECONFIG_NOW;
+ txn.len = 2;
+ wbuf[1] = sb->laddr;
+ txn.rl = txn.len + 4;
+ ret = ngd_get_tid(ctrl, &txn, &wbuf[0], &done);
+ if (ret)
+ return ret;
+ ret = ngd_xferandwait_ack(ctrl, &txn);
+ if (ret)
+ return ret;
+ }
+ txn.len = 0;
+ list_for_each_entry(pch, &sb->mark_removal, pending) {
+ struct slim_ich *slc;
+ slc = &ctrl->chans[pch->chan];
+ if (!slc) {
+ pr_err("no channel in removal?");
+ return -ENXIO;
+ }
+ if (txn.len == 0) {
+ wbuf[txn.len++] = (u8) (SLIM_CH_REMOVE << 6) |
+ sb->laddr;
+ ret = ngd_get_tid(ctrl, &txn, &wbuf[txn.len++], &done);
+ if (ret) {
+ pr_err("no tid for channel define?");
+ return -ENXIO;
+ }
+ }
+ wbuf[txn.len++] = slc->chan;
+ }
+ if (txn.len) {
+ txn.mc = SLIM_USR_MC_CHAN_CTRL;
+ txn.rl = txn.len + 4;
+ ret = ngd_xferandwait_ack(ctrl, &txn);
+ if (ret)
+ return ret;
+
+ txn.mc = SLIM_USR_MC_RECONFIG_NOW;
+ txn.len = 2;
+ wbuf[1] = sb->laddr;
+ txn.rl = txn.len + 4;
+ ret = ngd_get_tid(ctrl, &txn, &wbuf[0], &done);
+ if (ret)
+ return ret;
+ ret = ngd_xferandwait_ack(ctrl, &txn);
+ if (ret)
+ return ret;
+ txn.len = 0;
+ }
+ return ret;
+}
+
+static int ngd_set_laddr(struct slim_controller *ctrl, const u8 *ea,
+ u8 elen, u8 laddr)
+{
+ return 0;
+}
+
+static int ngd_get_laddr(struct slim_controller *ctrl, const u8 *ea,
+ u8 elen, u8 *laddr)
+{
+ int ret;
+ u8 wbuf[10];
+ struct slim_msg_txn txn;
+ DECLARE_COMPLETION_ONSTACK(done);
+ txn.mt = SLIM_MSG_MT_DEST_REFERRED_USER;
+ txn.dt = SLIM_MSG_DEST_LOGICALADDR;
+ txn.la = SLIM_LA_MGR;
+ txn.ec = 0;
+ mutex_lock(&ctrl->m_ctrl);
+ ret = ngd_get_tid(ctrl, &txn, &wbuf[0], &done);
+ if (ret) {
+ mutex_unlock(&ctrl->m_ctrl);
+ return ret;
+ }
+ memcpy(&wbuf[1], ea, elen);
+ txn.mc = SLIM_USR_MC_ADDR_QUERY;
+ txn.rl = 11;
+ txn.len = 7;
+ txn.wbuf = wbuf;
+ txn.rbuf = NULL;
+ ret = ngd_xferandwait_ack(ctrl, &txn);
+ if (!ret && txn.la == 0xFF)
+ ret = -ENXIO;
+ else if (!ret)
+ *laddr = txn.la;
+ mutex_unlock(&ctrl->m_ctrl);
+ return ret;
+}
+
+static void ngd_slim_rx(struct msm_slim_ctrl *dev, u8 *buf)
+{
+ u8 mc, mt, len;
+ int ret;
+ u32 msgq_en = 1;
+
+ len = buf[0] & 0x1F;
+ mt = (buf[0] >> 5) & 0x7;
+ mc = buf[1];
+ if (mc == SLIM_USR_MC_MASTER_CAPABILITY &&
+ mt == SLIM_MSG_MT_SRC_REFERRED_USER) {
+ struct slim_msg_txn txn;
+ u8 wbuf[8];
+ txn.dt = SLIM_MSG_DEST_LOGICALADDR;
+ txn.ec = 0;
+ txn.rbuf = NULL;
+ txn.mc = SLIM_USR_MC_REPORT_SATELLITE;
+ txn.mt = SLIM_MSG_MT_SRC_REFERRED_USER;
+ txn.la = SLIM_LA_MGR;
+ txn.rl = 8;
+ wbuf[0] = SAT_MAGIC_LSB;
+ wbuf[1] = SAT_MAGIC_MSB;
+ wbuf[2] = SAT_MSG_VER;
+ wbuf[3] = SAT_MSG_PROT;
+ txn.wbuf = wbuf;
+ txn.len = 4;
+ dev->use_rx_msgqs = 1;
+ msm_slim_sps_init(dev, dev->bam_mem,
+ NGD_BASE(dev->ctrl.nr, dev->ver) + NGD_STATUS, true);
+ if (dev->use_rx_msgqs)
+ msgq_en |= NGD_CFG_RX_MSGQ_EN;
+ writel_relaxed(msgq_en, dev->base +
+ NGD_BASE(dev->ctrl.nr, dev->ver));
+ /* make sure NGD MSG-Q config goes through */
+ mb();
+
+ ret = ngd_xfer_msg(&dev->ctrl, &txn);
+ if (!ret) {
+ dev->state = MSM_CTRL_AWAKE;
+ complete(&dev->reconf);
+ }
+ }
+ if (mc == SLIM_MSG_MC_REPLY_INFORMATION ||
+ mc == SLIM_MSG_MC_REPLY_VALUE) {
+ u8 tid = buf[3];
+ dev_dbg(dev->dev, "tid:%d, len:%d\n", tid, len);
+ slim_msg_response(&dev->ctrl, &buf[4], tid,
+ len - 4);
+ pm_runtime_mark_last_busy(dev->dev);
+ }
+ if (mc == SLIM_USR_MC_ADDR_REPLY &&
+ mt == SLIM_MSG_MT_SRC_REFERRED_USER) {
+ struct slim_msg_txn *txn = dev->ctrl.txnt[buf[3]];
+ u8 failed_ea[6] = {0, 0, 0, 0, 0, 0};
+ if (!txn)
+ return;
+ if (memcmp(&buf[4], failed_ea, 6))
+ txn->la = buf[10];
+ dev->ctrl.txnt[buf[3]] = NULL;
+ complete(txn->comp);
+ }
+ if (mc == SLIM_USR_MC_GENERIC_ACK &&
+ mt == SLIM_MSG_MT_SRC_REFERRED_USER) {
+ struct slim_msg_txn *txn = dev->ctrl.txnt[buf[3]];
+ if (!txn)
+ return;
+ dev_dbg(dev->dev, "got response:tid:%d, response:0x%x",
+ (int)buf[3], buf[4]);
+ if (!(buf[4] & MSM_SAT_SUCCSS)) {
+ dev_err(dev->dev, "TID:%d, NACK code:0x%x", (int)buf[3],
+ buf[4]);
+ txn->ec = -EIO;
+ }
+ dev->ctrl.txnt[buf[3]] = NULL;
+ complete(txn->comp);
+ }
+}
+static int ngd_slim_rx_msgq_thread(void *data)
+{
+ struct msm_slim_ctrl *dev = (struct msm_slim_ctrl *)data;
+ struct completion *notify = &dev->rx_msgq_notify;
+ int ret = 0, index = 0;
+ u32 mc = 0;
+ u32 mt = 0;
+ u32 buffer[10];
+ u8 msg_len = 0;
+
+ while (!kthread_should_stop()) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ ret = wait_for_completion_interruptible(notify);
+ if (ret) {
+ dev_err(dev->dev, "rx thread wait err:%d", ret);
+ continue;
+ }
+ /* 1 irq notification per message */
+ if (!dev->use_rx_msgqs) {
+ msm_slim_rx_dequeue(dev, (u8 *)buffer);
+ ngd_slim_rx(dev, (u8 *)buffer);
+ continue;
+ }
+ ret = msm_slim_rx_msgq_get(dev, buffer, index);
+ if (ret) {
+ dev_err(dev->dev, "rx_msgq_get() failed 0x%x\n", ret);
+ continue;
+ }
+
+ /* Wait for complete message */
+ if (index++ == 0) {
+ msg_len = *buffer & 0x1F;
+ mt = (buffer[0] >> 5) & 0x7;
+ mc = (buffer[0] >> 8) & 0xff;
+ dev_dbg(dev->dev, "MC: %x, MT: %x\n", mc, mt);
+ }
+ if ((index * 4) >= msg_len) {
+ index = 0;
+ ngd_slim_rx(dev, (u8 *)buffer);
+ } else
+ continue;
+ }
+ return 0;
+}
+
+static int __devinit ngd_slim_probe(struct platform_device *pdev)
+{
+ struct msm_slim_ctrl *dev;
+ int ret;
+ struct resource *bam_mem;
+ struct resource *slim_mem;
+ struct resource *irq, *bam_irq;
+ enum apr_subsys_state q6_state;
+ u32 ngd_int;
+
+ q6_state = apr_get_q6_state();
+ if (q6_state == APR_SUBSYS_DOWN) {
+ dev_dbg(&pdev->dev, "defering %s, adsp_state %d\n", __func__,
+ q6_state);
+ return -EPROBE_DEFER;
+ } else
+ dev_dbg(&pdev->dev, "adsp is ready\n");
+
+ slim_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "slimbus_physical");
+ if (!slim_mem) {
+ dev_err(&pdev->dev, "no slimbus physical memory resource\n");
+ return -ENODEV;
+ }
+ bam_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "slimbus_bam_physical");
+ if (!bam_mem) {
+ dev_err(&pdev->dev, "no slimbus BAM memory resource\n");
+ return -ENODEV;
+ }
+ irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+ "slimbus_irq");
+ if (!irq) {
+ dev_err(&pdev->dev, "no slimbus IRQ resource\n");
+ return -ENODEV;
+ }
+ bam_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+ "slimbus_bam_irq");
+ if (!bam_irq) {
+ dev_err(&pdev->dev, "no slimbus BAM IRQ resource\n");
+ return -ENODEV;
+ }
+
+ dev = kzalloc(sizeof(struct msm_slim_ctrl), GFP_KERNEL);
+ if (IS_ERR(dev)) {
+ dev_err(&pdev->dev, "no memory for MSM slimbus controller\n");
+ return PTR_ERR(dev);
+ }
+ dev->dev = &pdev->dev;
+ platform_set_drvdata(pdev, dev);
+ slim_set_ctrldata(&dev->ctrl, dev);
+ dev->base = ioremap(slim_mem->start, resource_size(slim_mem));
+ if (!dev->base) {
+ dev_err(&pdev->dev, "IOremap failed\n");
+ ret = -ENOMEM;
+ goto err_ioremap_failed;
+ }
+ dev->bam.base = ioremap(bam_mem->start, resource_size(bam_mem));
+ if (!dev->bam.base) {
+ dev_err(&pdev->dev, "BAM IOremap failed\n");
+ ret = -ENOMEM;
+ goto err_ioremap_bam_failed;
+ }
+ if (pdev->dev.of_node) {
+
+ ret = of_property_read_u32(pdev->dev.of_node, "cell-index",
+ &dev->ctrl.nr);
+ if (ret) {
+ dev_err(&pdev->dev, "Cell index not specified:%d", ret);
+ goto err_ctrl_failed;
+ }
+ } else {
+ dev->ctrl.nr = pdev->id;
+ }
+ dev->ctrl.nchans = MSM_SLIM_NCHANS;
+ dev->ctrl.nports = MSM_SLIM_NPORTS;
+ dev->framer.rootfreq = SLIM_ROOT_FREQ >> 3;
+ dev->framer.superfreq =
+ dev->framer.rootfreq / SLIM_CL_PER_SUPERFRAME_DIV8;
+ dev->ctrl.a_framer = &dev->framer;
+ dev->ctrl.clkgear = SLIM_MAX_CLK_GEAR;
+ dev->ctrl.set_laddr = ngd_set_laddr;
+ dev->ctrl.get_laddr = ngd_get_laddr;
+ dev->ctrl.allocbw = ngd_allocbw;
+ dev->ctrl.xfer_msg = ngd_xfer_msg;
+ dev->ctrl.wakeup = NULL;
+ dev->ctrl.config_port = msm_config_port;
+ dev->ctrl.port_xfer = msm_slim_port_xfer;
+ dev->ctrl.port_xfer_status = msm_slim_port_xfer_status;
+ /* Reserve some messaging BW for satellite-apps driver communication */
+ dev->ctrl.sched.pending_msgsl = 30;
+ dev->bam_mem = bam_mem;
+
+ init_completion(&dev->reconf);
+ mutex_init(&dev->tx_lock);
+ spin_lock_init(&dev->rx_lock);
+ dev->ee = 1;
+ dev->irq = irq->start;
+ dev->bam.irq = bam_irq->start;
+
+ dev->ver = readl_relaxed(dev->base);
+ /* Version info in 16 MSbits */
+ dev->ver >>= 16;
+ ngd_int = (NGD_INT_RECFG_DONE | NGD_INT_TX_NACKED_2 |
+ NGD_INT_MSG_BUF_CONTE | NGD_INT_MSG_TX_INVAL |
+ NGD_INT_IE_VE_CHG | NGD_INT_DEV_ERR |
+ NGD_INT_TX_MSG_SENT | NGD_INT_RX_MSG_RCVD);
+ init_completion(&dev->rx_msgq_notify);
+
+ /* Register with framework */
+ ret = slim_add_numbered_controller(&dev->ctrl);
+ if (ret) {
+ dev_err(dev->dev, "error adding controller\n");
+ goto err_ctrl_failed;
+ }
+
+ dev->ctrl.dev.parent = &pdev->dev;
+ dev->ctrl.dev.of_node = pdev->dev.of_node;
+ dev->state = MSM_CTRL_ASLEEP;
+
+ ret = request_irq(dev->irq, ngd_slim_interrupt,
+ IRQF_TRIGGER_HIGH, "ngd_slim_irq", dev);
+
+ if (ret) {
+ dev_err(&pdev->dev, "request IRQ failed\n");
+ goto err_request_irq_failed;
+ }
+
+ /* Fire up the Rx message queue thread */
+ dev->rx_msgq_thread = kthread_run(ngd_slim_rx_msgq_thread, dev,
+ NGD_SLIM_NAME "_ngd_msgq_thread");
+ if (IS_ERR(dev->rx_msgq_thread)) {
+ ret = PTR_ERR(dev->rx_msgq_thread);
+ dev_err(dev->dev, "Failed to start Rx message queue thread\n");
+ goto err_thread_create_failed;
+ }
+
+ writel_relaxed(ngd_int, dev->base + NGD_INT_EN +
+ NGD_BASE(dev->ctrl.nr, dev->ver));
+ /*
+ * Enable NGD. Configure NGD in register access mode until master
+ * announcement is received
+ */
+ writel_relaxed(1, dev->base + NGD_BASE(dev->ctrl.nr, dev->ver));
+ /* make sure NGD enabling goes through */
+ mb();
+
+ if (pdev->dev.of_node)
+ of_register_slim_devices(&dev->ctrl);
+
+ /* Add devices registered with board-info now that controller is up */
+ slim_ctrl_add_boarddevs(&dev->ctrl);
+
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, MSM_SLIM_AUTOSUSPEND);
+ pm_runtime_set_active(&pdev->dev);
+
+ dev_dbg(dev->dev, "NGD SB controller is up!\n");
+ return 0;
+
+err_thread_create_failed:
+ free_irq(dev->irq, dev);
+err_request_irq_failed:
+ slim_del_controller(&dev->ctrl);
+err_ctrl_failed:
+ iounmap(dev->bam.base);
+err_ioremap_bam_failed:
+ iounmap(dev->base);
+err_ioremap_failed:
+ kfree(dev);
+ return ret;
+}
+
+static int __devexit ngd_slim_remove(struct platform_device *pdev)
+{
+ struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ free_irq(dev->irq, dev);
+ slim_del_controller(&dev->ctrl);
+ kthread_stop(dev->rx_msgq_thread);
+ iounmap(dev->bam.base);
+ iounmap(dev->base);
+ kfree(dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int ngd_slim_runtime_idle(struct device *device)
+{
+ dev_dbg(device, "pm_runtime: idle...\n");
+ pm_request_autosuspend(device);
+ return -EAGAIN;
+}
+#endif
+
+/*
+ * If PM_RUNTIME is not defined, these 2 functions become helper
+ * functions to be called from system suspend/resume. So they are not
+ * inside ifdef CONFIG_PM_RUNTIME
+ */
+#ifdef CONFIG_PM_SLEEP
+static int ngd_slim_runtime_suspend(struct device *device)
+{
+ struct platform_device *pdev = to_platform_device(device);
+ struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+ int ret;
+ dev_dbg(device, "pm_runtime: suspending...\n");
+ dev->state = MSM_CTRL_SLEEPING;
+ ret = slim_ctrl_clk_pause(&dev->ctrl, false, SLIM_CLK_UNSPECIFIED);
+ if (ret) {
+ dev_err(device, "clk pause not entered:%d", ret);
+ dev->state = MSM_CTRL_AWAKE;
+ } else {
+ dev->state = MSM_CTRL_ASLEEP;
+ }
+ return ret;
+}
+
+static int ngd_slim_runtime_resume(struct device *device)
+{
+ struct platform_device *pdev = to_platform_device(device);
+ struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+ int ret = 0;
+ dev_dbg(device, "pm_runtime: resuming...\n");
+ if (dev->state == MSM_CTRL_ASLEEP)
+ ret = slim_ctrl_clk_pause(&dev->ctrl, true, 0);
+ if (ret) {
+ dev_err(device, "clk pause not exited:%d", ret);
+ dev->state = MSM_CTRL_ASLEEP;
+ } else {
+ dev->state = MSM_CTRL_AWAKE;
+ }
+ return ret;
+}
+
+static int ngd_slim_suspend(struct device *dev)
+{
+ int ret = -EBUSY;
+ if (!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev)) {
+ dev_dbg(dev, "system suspend");
+ ret = ngd_slim_runtime_suspend(dev);
+ }
+ if (ret == -EBUSY) {
+ /*
+ * There is a possibility that some audio stream is active
+ * during suspend. We dont want to return suspend failure in
+ * that case so that display and relevant components can still
+ * go to suspend.
+ * If there is some other error, then it should be passed-on
+ * to system level suspend
+ */
+ ret = 0;
+ }
+ return ret;
+}
+
+static int ngd_slim_resume(struct device *dev)
+{
+ /* If runtime_pm is enabled, this resume shouldn't do anything */
+ if (!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev)) {
+ int ret;
+ dev_dbg(dev, "system resume");
+ ret = ngd_slim_runtime_resume(dev);
+ if (!ret) {
+ pm_runtime_mark_last_busy(dev);
+ pm_request_autosuspend(dev);
+ }
+ return ret;
+
+ }
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops ngd_slim_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(
+ ngd_slim_suspend,
+ ngd_slim_resume
+ )
+ SET_RUNTIME_PM_OPS(
+ ngd_slim_runtime_suspend,
+ ngd_slim_runtime_resume,
+ ngd_slim_runtime_idle
+ )
+};
+
+static struct of_device_id ngd_slim_dt_match[] = {
+ {
+ .compatible = "qcom,slim-ngd",
+ },
+ {}
+};
+
+static struct platform_driver ngd_slim_driver = {
+ .probe = ngd_slim_probe,
+ .remove = ngd_slim_remove,
+ .driver = {
+ .name = NGD_SLIM_NAME,
+ .owner = THIS_MODULE,
+ .pm = &ngd_slim_dev_pm_ops,
+ .of_match_table = ngd_slim_dt_match,
+ },
+};
+
+static int ngd_slim_init(void)
+{
+ return platform_driver_register(&ngd_slim_driver);
+}
+late_initcall(ngd_slim_init);
+
+static void ngd_slim_exit(void)
+{
+ platform_driver_unregister(&ngd_slim_driver);
+}
+module_exit(ngd_slim_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM Slimbus controller");
+MODULE_ALIAS("platform:msm-slim-ngd");
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index 35bb040..7d50620 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -167,6 +167,7 @@
struct device *dev;
void __iomem *base;
struct resource *slew_mem;
+ struct resource *bam_mem;
u32 curr_bw;
u8 msg_cnt;
u32 tx_buf[10];
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index a4c7131..92cbe6f 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -844,9 +844,14 @@
motg->caps & ALLOW_LPM_ON_DEV_SUSPEND;
dcp = motg->chg_type == USB_DCP_CHARGER;
- /* charging detection in progress due to cable plug-in */
- if (test_bit(B_SESS_VLD, &motg->inputs) && !device_bus_suspend &&
- !dcp) {
+ /*
+ * Abort suspend when,
+ * 1. charging detection in progress due to cable plug-in
+ * 2. host mode activation in progress due to Micro-A cable insertion
+ */
+
+ if ((test_bit(B_SESS_VLD, &motg->inputs) && !device_bus_suspend &&
+ !dcp) || test_bit(A_BUS_REQ, &motg->inputs)) {
enable_irq(motg->irq);
return -EBUSY;
}
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index 712d41b..c7f9e55 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -2873,7 +2873,7 @@
pdata->on = mdp4_dtv_on;
pdata->off = mdp4_dtv_off;
mfd->hw_refresh = TRUE;
- mfd->cursor_update = mdp_hw_cursor_update;
+ mfd->cursor_update = mdp_hw_cursor_sync_update;
mfd->dma_fnc = mdp4_dtv_overlay;
mfd->dma = &dma_e_data;
mfd->do_histogram = mdp_do_histogram;
diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c
index 516722e..ee9ca3c 100644
--- a/drivers/video/msm/mdp4_util.c
+++ b/drivers/video/msm/mdp4_util.c
@@ -630,6 +630,7 @@
if (panel & MDP4_PANEL_ATV)
mdp4_overlay1_done_atv();
#endif
+ mdp_hw_cursor_done();
}
#if defined(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL)
if (isr & INTR_OVERLAY2_DONE) {
diff --git a/drivers/video/msm/mdp_cursor.c b/drivers/video/msm/mdp_cursor.c
index f8c08e3..b5930a1 100644
--- a/drivers/video/msm/mdp_cursor.c
+++ b/drivers/video/msm/mdp_cursor.c
@@ -52,7 +52,11 @@
/* disable vsync */
spin_lock_irqsave(&mdp_spin_lock, flag);
- mdp_disable_irq(MDP_OVERLAY0_TERM);
+ if (hdmi_prim_display)
+ mdp_disable_irq(MDP_OVERLAY1_TERM);
+ else
+ mdp_disable_irq(MDP_OVERLAY0_TERM);
+
spin_unlock_irqrestore(&mdp_spin_lock, flag);
}
@@ -78,29 +82,37 @@
*
* Moving this code out of the ISR will cause the MDP to underrun!
*/
+ uint32_t base = 0;
+
+ if (hdmi_prim_display)
+ base = ((uint32_t)(MDP_BASE + 0xB0000));
+ else
+ base = ((uint32_t)(MDP_BASE + 0x90000));
+
+
spin_lock(&mdp_spin_lock);
if (sync_disabled) {
spin_unlock(&mdp_spin_lock);
return;
}
- MDP_OUTP(MDP_BASE + 0x90044, (height << 16) | width);
- MDP_OUTP(MDP_BASE + 0x90048, cursor_buf_phys);
+ MDP_OUTP(base + 0x44, (height << 16) | width);
+ MDP_OUTP(base + 0x48, cursor_buf_phys);
- MDP_OUTP(MDP_BASE + 0x90060,
+ MDP_OUTP(base + 0x60,
(transp_en << 3) | (calpha_en << 1) |
- (inp32(MDP_BASE + 0x90060) & 0x1));
+ (inp32(base + 0x60) & 0x1));
- MDP_OUTP(MDP_BASE + 0x90064, (alpha << 24));
- MDP_OUTP(MDP_BASE + 0x90068, (0xffffff & bg_color));
- MDP_OUTP(MDP_BASE + 0x9006C, (0xffffff & bg_color));
+ MDP_OUTP(base + 0x64, (alpha << 24));
+ MDP_OUTP(base + 0x68, (0xffffff & bg_color));
+ MDP_OUTP(base + 0x6C, (0xffffff & bg_color));
/* enable/disable the cursor as per the last request */
- if (cursor_enabled && !(inp32(MDP_BASE + 0x90060) & (0x1)))
- MDP_OUTP(MDP_BASE + 0x90060, inp32(MDP_BASE + 0x90060) | 0x1);
- else if (!cursor_enabled && (inp32(MDP_BASE + 0x90060) & (0x1)))
- MDP_OUTP(MDP_BASE + 0x90060,
- inp32(MDP_BASE + 0x90060) & (~0x1));
+ if (cursor_enabled && !(inp32(base + 0x60) & (0x1)))
+ MDP_OUTP(base + 0x60, inp32(base + 0x60) | 0x1);
+ else if (!cursor_enabled && (inp32(base + 0x60) & (0x1)))
+ MDP_OUTP(base + 0x60,
+ inp32(base + 0x60) & (~0x1));
/* enqueue the task to disable MDP interrupts */
queue_work(mdp_cursor_ctrl_wq, &mdp_cursor_ctrl_worker);
@@ -119,17 +131,26 @@
if (sync_disabled) {
/* cancel pending task to disable MDP interrupts */
- if (work_pending(&mdp_cursor_ctrl_worker))
+ if (work_pending(&mdp_cursor_ctrl_worker)) {
cancel_work_sync(&mdp_cursor_ctrl_worker);
- else
+ } else {
/* enable irq */
- mdp_enable_irq(MDP_OVERLAY0_TERM);
+ if (hdmi_prim_display)
+ mdp_enable_irq(MDP_OVERLAY1_TERM);
+ else
+ mdp_enable_irq(MDP_OVERLAY0_TERM);
+ }
sync_disabled = 0;
/* enable vsync intr */
- outp32(MDP_INTR_CLEAR, INTR_OVERLAY0_DONE);
- mdp_intr_mask |= INTR_OVERLAY0_DONE;
+ if (hdmi_prim_display) {
+ outp32(MDP_INTR_CLEAR, INTR_OVERLAY1_DONE);
+ mdp_intr_mask |= INTR_OVERLAY1_DONE;
+ } else {
+ outp32(MDP_INTR_CLEAR, INTR_OVERLAY0_DONE);
+ mdp_intr_mask |= INTR_OVERLAY0_DONE;
+ }
outp32(MDP_INTR_ENABLE, mdp_intr_mask);
}
}
@@ -140,14 +161,20 @@
struct fb_image *img = &cursor->image;
unsigned long flag;
int sync_needed = 0, ret = 0;
+ uint32_t base = 0;
if ((img->width > MDP_CURSOR_WIDTH) ||
(img->height > MDP_CURSOR_HEIGHT) ||
(img->depth != 32))
return -EINVAL;
+ if (hdmi_prim_display)
+ base = ((uint32_t)(MDP_BASE + 0xB0000));
+ else
+ base = ((uint32_t)(MDP_BASE + 0x90000));
+
if (cursor->set & FB_CUR_SETPOS)
- MDP_OUTP(MDP_BASE + 0x9004c, (img->dy << 16) | img->dx);
+ MDP_OUTP(base + 0x4c, (img->dy << 16) | img->dx);
if (cursor->set & FB_CUR_SETIMAGE) {
ret = copy_from_user(mfd->cursor_buf, img->data,
diff --git a/drivers/video/msm/mdss/mdss_edp.c b/drivers/video/msm/mdss/mdss_edp.c
index 9460d71..b35be75 100644
--- a/drivers/video/msm/mdss/mdss_edp.c
+++ b/drivers/video/msm/mdss/mdss_edp.c
@@ -22,6 +22,8 @@
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/err.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pwm.h>
#include <asm/system.h>
#include <asm/mach-types.h>
@@ -37,15 +39,14 @@
#define VDDA_UA_ON_LOAD 100000 /* uA units */
#define VDDA_UA_OFF_LOAD 100 /* uA units */
-
static int mdss_edp_get_base_address(struct mdss_edp_drv_pdata *edp_drv);
static int mdss_edp_get_mmss_cc_base_address(struct mdss_edp_drv_pdata
*edp_drv);
static int mdss_edp_regulator_init(struct mdss_edp_drv_pdata *edp_drv);
static int mdss_edp_regulator_on(struct mdss_edp_drv_pdata *edp_drv);
static int mdss_edp_regulator_off(struct mdss_edp_drv_pdata *edp_drv);
-
static int mdss_edp_gpio_panel_en(struct mdss_edp_drv_pdata *edp_drv);
+static int mdss_edp_pwm_config(struct mdss_edp_drv_pdata *edp_drv);
static void mdss_edp_edid2pinfo(struct mdss_edp_drv_pdata *edp_drv);
static void mdss_edp_fill_edid_data(struct mdss_edp_drv_pdata *edp_drv);
@@ -134,7 +135,7 @@
}
/*
- * Enables the gpio that supply power to the panel
+ * Enables the gpio that supply power to the panel and enable the backlight
*/
static int mdss_edp_gpio_panel_en(struct mdss_edp_drv_pdata *edp_drv)
{
@@ -143,7 +144,8 @@
edp_drv->gpio_panel_en = of_get_named_gpio(edp_drv->pdev->dev.of_node,
"gpio-panel-en", 0);
if (!gpio_is_valid(edp_drv->gpio_panel_en)) {
- pr_err("%s: gpio_panel_en not specified\n", __func__);
+ pr_err("%s: gpio_panel_en=%d not specified\n", __func__,
+ edp_drv->gpio_panel_en);
goto gpio_err;
}
@@ -171,7 +173,94 @@
return -ENODEV;
}
-static void mdss_edp_config_sync(unsigned char *edp_base)
+static int mdss_edp_pwm_config(struct mdss_edp_drv_pdata *edp_drv)
+{
+ int ret = 0;
+
+ ret = of_property_read_u32(edp_drv->pdev->dev.of_node,
+ "qcom,panel-pwm-period", &edp_drv->pwm_period);
+ if (ret) {
+ pr_err("%s: panel pwm period is not specified, %d", __func__,
+ edp_drv->pwm_period);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(edp_drv->pdev->dev.of_node,
+ "qcom,panel-lpg-channel", &edp_drv->lpg_channel);
+ if (ret) {
+ pr_err("%s: panel lpg channel is not specified, %d", __func__,
+ edp_drv->lpg_channel);
+ return -EINVAL;
+ }
+
+ edp_drv->bl_pwm = pwm_request(edp_drv->lpg_channel, "lcd-backlight");
+ if (edp_drv->bl_pwm == NULL || IS_ERR(edp_drv->bl_pwm)) {
+ pr_err("%s: pwm request failed", __func__);
+ edp_drv->bl_pwm = NULL;
+ return -EIO;
+ }
+
+ edp_drv->gpio_panel_pwm = of_get_named_gpio(edp_drv->pdev->dev.of_node,
+ "gpio-panel-pwm", 0);
+ if (!gpio_is_valid(edp_drv->gpio_panel_pwm)) {
+ pr_err("%s: gpio_panel_pwm=%d not specified\n", __func__,
+ edp_drv->gpio_panel_pwm);
+ goto edp_free_pwm;
+ }
+
+ ret = gpio_request(edp_drv->gpio_panel_pwm, "disp_pwm");
+ if (ret) {
+ pr_err("%s: Request reset gpio_panel_pwm failed, ret=%d\n",
+ __func__, ret);
+ goto edp_free_gpio_pwm;
+ }
+
+ return 0;
+
+edp_free_gpio_pwm:
+ gpio_free(edp_drv->gpio_panel_pwm);
+edp_free_pwm:
+ pwm_free(edp_drv->bl_pwm);
+ return -ENODEV;
+}
+
+void mdss_edp_set_backlight(struct mdss_panel_data *pdata, u32 bl_level)
+{
+ int ret = 0;
+ struct mdss_edp_drv_pdata *edp_drv = NULL;
+ int bl_max;
+
+ edp_drv = container_of(pdata, struct mdss_edp_drv_pdata, panel_data);
+ if (!edp_drv) {
+ pr_err("%s: Invalid input data\n", __func__);
+ return;
+ }
+
+ bl_max = edp_drv->panel_data.panel_info.bl_max;
+ if (bl_level > bl_max)
+ bl_level = bl_max;
+
+ if (edp_drv->bl_pwm == NULL) {
+ pr_err("%s: edp_drv->bl_pwm=NULL.\n", __func__);
+ return;
+ }
+
+ ret = pwm_config(edp_drv->bl_pwm,
+ bl_level * edp_drv->pwm_period / bl_max,
+ edp_drv->pwm_period);
+ if (ret) {
+ pr_err("%s: pwm_config() failed err=%d.\n", __func__, ret);
+ return;
+ }
+
+ ret = pwm_enable(edp_drv->bl_pwm);
+ if (ret) {
+ pr_err("%s: pwm_enable() failed err=%d\n", __func__, ret);
+ return;
+ }
+}
+
+void mdss_edp_config_sync(unsigned char *edp_base)
{
int ret = 0;
@@ -251,6 +340,7 @@
return -EINVAL;
}
+ pwm_disable(edp_drv->bl_pwm);
mdss_edp_enable(edp_drv->edp_base, 0);
mdss_edp_unconfig_clk(edp_drv->edp_base, edp_drv->mmss_cc_base);
mdss_edp_enable_mainlink(edp_drv->edp_base, 0);
@@ -322,9 +412,12 @@
int ret;
mdss_edp_edid2pinfo(edp_drv);
+ edp_drv->panel_data.panel_info.bl_min = 1;
+ edp_drv->panel_data.panel_info.bl_max = 255;
edp_drv->panel_data.on = mdss_edp_on;
edp_drv->panel_data.off = mdss_edp_off;
+ edp_drv->panel_data.set_backlight = mdss_edp_set_backlight;
ret = mdss_register_panel(&edp_drv->panel_data);
if (ret) {
@@ -468,12 +561,19 @@
if (ret)
goto edp_clk_deinit;
+ ret = mdss_edp_pwm_config(edp_drv);
+ if (ret)
+ goto edp_free_gpio_panel_en;
+
mdss_edp_fill_edid_data(edp_drv);
mdss_edp_fill_dpcd_data(edp_drv);
mdss_edp_device_register(edp_drv);
return 0;
+
+edp_free_gpio_panel_en:
+ gpio_free(edp_drv->gpio_panel_en);
edp_clk_deinit:
mdss_edp_clk_deinit(edp_drv);
mdss_edp_regulator_off(edp_drv);
diff --git a/drivers/video/msm/mdss/mdss_edp.h b/drivers/video/msm/mdss/mdss_edp.h
index 72c061f..00ef206 100644
--- a/drivers/video/msm/mdss/mdss_edp.h
+++ b/drivers/video/msm/mdss/mdss_edp.h
@@ -92,6 +92,12 @@
/* gpios */
int gpio_panel_en;
+ int gpio_panel_pwm;
+
+ /* backlight */
+ struct pwm_device *bl_pwm;
+ int lpg_channel;
+ int pwm_period;
};
void mdss_edp_phy_sw_reset(unsigned char *edp_base);
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index f0e42c2..60fa04e 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -18,14 +18,14 @@
#define EVENT_MASKS_TYPE 4
#define PKT_TYPE 8
#define DEINIT_TYPE 16
-#define USER_SPACE_LOG_TYPE 32
+#define USER_SPACE_DATA_TYPE 32
#define DCI_DATA_TYPE 64
#define USB_MODE 1
#define MEMORY_DEVICE_MODE 2
#define NO_LOGGING_MODE 3
#define UART_MODE 4
#define SOCKET_MODE 5
-
+#define CALLBACK_MODE 6
/* different values that go in for diag_data_type */
#define DATA_TYPE_EVENT 0
#define DATA_TYPE_F3 1
diff --git a/include/linux/i2c/smb350.h b/include/linux/i2c/smb350.h
new file mode 100644
index 0000000..5bb5cec
--- /dev/null
+++ b/include/linux/i2c/smb350.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2012 The Linux Foundation. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+#ifndef __SMB350_H__
+#define __SMB350_H__
+
+#define SMB350_NAME "smb350"
+
+/**
+ * struct smb350_platform_data
+ * structure to pass board specific information to the smb137b charger driver
+ * @chg_current_ma: maximum fast charge current in mA
+ * @term_current_ma: charge termination current in mA
+ * @chg_en_n_gpio: gpio to enable or disable charging
+ * @chg_susp_n_gpio: put active low to allow chip to suspend and disable I2C
+ * @stat_gpio: STAT pin, active low, '0' when charging.
+ */
+struct smb350_platform_data {
+ int chg_en_n_gpio;
+ int chg_susp_n_gpio;
+ int chg_current_ma;
+ int term_current_ma;
+ int stat_gpio;
+};
+
+#endif
diff --git a/include/linux/mfd/pm8xxx/pm8921-charger.h b/include/linux/mfd/pm8xxx/pm8921-charger.h
index 16d4a4b..130fb54 100644
--- a/include/linux/mfd/pm8xxx/pm8921-charger.h
+++ b/include/linux/mfd/pm8xxx/pm8921-charger.h
@@ -163,6 +163,7 @@
enum pm8921_chg_hot_thr hot_thr;
int rconn_mohm;
enum pm8921_chg_led_src_config led_src_config;
+ int battery_less_hardware;
};
enum pm8921_charger_source {
diff --git a/include/linux/test-iosched.h b/include/linux/test-iosched.h
index 1e428c5..b52762c 100644
--- a/include/linux/test-iosched.h
+++ b/include/linux/test-iosched.h
@@ -129,6 +129,8 @@
* @check_test_result_fn: Test specific test result checking
* callback
* @get_test_case_str_fn: Test specific function to get the test name
+ * @test_duration: A jiffies value saved for timing
+ * calculations
* @data: Test specific private data
*/
struct test_info {
@@ -139,6 +141,7 @@
check_test_result_fn *check_test_result_fn;
post_test_fn *post_test_fn;
get_test_case_str_fn *get_test_case_str_fn;
+ unsigned long test_duration;
void *data;
};