diff options
91 files changed, 2050 insertions, 420 deletions
diff --git a/Documentation/devicetree/bindings/power/omap-core-dvfs.txt b/Documentation/devicetree/bindings/power/omap-core-dvfs.txt new file mode 100644 index 00000000000..aed56ec062d --- /dev/null +++ b/Documentation/devicetree/bindings/power/omap-core-dvfs.txt @@ -0,0 +1,48 @@ +Core dynamic voltage and frequency scaling driver for Texas Instruments OMAP SoC +It allows to save power during active system state by changing core voltage and +frequency based on CPU performance. + +Required Properties: +- compatible: Should be "ti,omap-core-dvfs" +- l3_clkname: An clock name of core L3 interconnect clock +- dpll_clkname: An clock name of DPLL output clock +- core_dvfs-supply: An reference node of core voltage regulator +- operating-points: An array of 2-tuples items, and each item consists + of frequency and voltage like <freq-kHz vol-uV>. Physical frequency value + my be different from value in this array becasue of rounding to closest + possible value based on integer divider. For example if DPLL output clock + value is 332Mhz and value in array is 200Mhz then physical frequency will + be 166Mhz because divider is integer. You have to use frequency values, + which are multiple of DPLL output frequency to avoid rounding. +- map: An array of 2-tuples items, and each item consists + of CPU frequency and matching core frequency in kHz. + CPU frequency values should be used from CPU operating-points array + +Optional Properties: +- voltage-tolerance: percent value, which voltage in operating-points array + may deviate from its value + +Example: + dvfs { + compatible = "simple-bus"; + + core_dvfs: core_dvfs { + compatible = "ti,omap-core-dvfs"; + + l3_clkname = "l3_ick"; + dpll_clkname = "dpll3_m2_ck"; + core_dvfs-supply = <&omap_tps65912_dcdc4>; + + operating-points = < + /* kHz uV */ + 100000 950000 + 200000 1150000 + >; + map = < + /* cpu freq, core freq*/ + 300000 100000 + 600000 200000 + 800000 200000 + >; + }; + }; diff --git a/Documentation/devicetree/bindings/power_supply/max17042_battery.txt b/Documentation/devicetree/bindings/power_supply/max17042_battery.txt index 1f2ba1991ec..785a5d214ae 100644 --- a/Documentation/devicetree/bindings/power_supply/max17042_battery.txt +++ b/Documentation/devicetree/bindings/power_supply/max17042_battery.txt @@ -26,6 +26,11 @@ Optional subnodes: - maxim,configuration : Contains configuraiton data for POR initialization of the device defined in Maxim appnote. This data is either specific to how the host device is used or battery specific characteristic. + Optionally can be defrined as "maxim,configuration-<batt-id>" where batt-id + is set in /chosen path of yoru respective device tree. As a result, the driver + will load maxim,configuration-<batt-id> you specified. This will allow us + to use multiple battery suppliers within the same software binary. + NOTE: the length of this node should be less or equal to 64 bytes. - maxim,temp-conv : Contains a table to convert a value from Temperature register to a "real" temp value. This conversion table should be used when @@ -33,6 +38,8 @@ Optional subnodes: measurements. Required "maxim,configuration" node properties : + - version: version of the configuration data. Update revision to force + device being reprogramed without POR event. - config : CONFIG register value (u16) specific to the device application. - full_soc_thresh : FullSOCThr register value (u16). The appnote has a recommended value. @@ -110,6 +117,15 @@ Example: tgain = /bits/ 16 <0xE71C>; toff = /bits/ 16 <0x251A>; } + + /* optional you can define additional maxim,configuration-<batt-id> */ + /* the total length of the node name should not be bigger than 64 */ + /* bytes including null termination character. */ + /* maxim,configuration-SNN5951A { */ + ... + ... + } + maxim,temp-conv { start = /bits/ 16 <(-20)>; /* in degrees C */ result = /bits/ 16 < /* in 1/10 degrees C */ diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 3458d6343e0..6e5c7c7333b 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -22,6 +22,15 @@ ip_no_pmtu_disc - BOOLEAN min_pmtu - INTEGER default 552 - minimum discovered Path MTU +fwmark_reflect - BOOLEAN + Controls the fwmark of kernel-generated IPv4 reply packets that are not + associated with a socket for example, TCP RSTs or ICMP echo replies). + If unset, these packets have a fwmark of zero. If set, they have the + fwmark of the packet they are replying to. Similarly affects the fwmark + used by internal routing lookups triggered by incoming packets, such as + the ones used for Path MTU Discovery. + Default: 0 + route/max_size - INTEGER Maximum number of routes allowed in the kernel. Increase this when using large numbers of interfaces and/or routes. @@ -468,6 +477,16 @@ tcp_fastopen - INTEGER See include/net/tcp.h and the code for more details. +tcp_fwmark_accept - BOOLEAN + If set, incoming connections to listening sockets that do not have a + socket mark will set the mark of the accepting socket to the fwmark of + the incoming SYN packet. This will cause all packets on that connection + (starting from the first SYNACK) to be sent with that fwmark. The + listening socket's mark is unchanged. Listening sockets that already + have a fwmark set via setsockopt(SOL_SOCKET, SO_MARK, ...) are + unaffected. + Default: 0 + tcp_syn_retries - INTEGER Number of times initial SYNs for an active TCP connection attempt will be retransmitted. Should not be higher than 255. Default value @@ -1087,6 +1106,15 @@ conf/all/forwarding - BOOLEAN proxy_ndp - BOOLEAN Do proxy ndp. +fwmark_reflect - BOOLEAN + Controls the fwmark of kernel-generated IPv6 reply packets that are not + associated with a socket for example, TCP RSTs or ICMPv6 echo replies). + If unset, these packets have a fwmark of zero. If set, they have the + fwmark of the packet they are replying to. Similarly affects the fwmark + used by internal routing lookups triggered by incoming packets, such as + the ones used for Path MTU Discovery. + Default: 0 + conf/interface/*: Change special settings per interface. diff --git a/arch/arm/boot/dts/omap3-minnow.dtsi b/arch/arm/boot/dts/omap3-minnow.dtsi index aaadebb253b..efdb2d53f9a 100644 --- a/arch/arm/boot/dts/omap3-minnow.dtsi +++ b/arch/arm/boot/dts/omap3-minnow.dtsi @@ -7,6 +7,8 @@ */ /* reserve memory for ram_console */ /memreserve/ 0x83000000 0x20000; /* IMAGE_DOWN_LOAD_ADDR */ +/memreserve/ 0x83020000 0x16000; /* OMAP_SECURE_RAM_STORAGE_ADDR */ +/memreserve/ 0x83036000 0x9000; /* OMAP3_SECURE_RAM_CTX_ADDR */ /include/ "omap36xx.dtsi" @@ -93,6 +95,8 @@ //pixel_format = <1>; //hs_clk = <90000000 150000000>; /* min max*/ //lp_clk = <7000000 9000000>; /* min max*/ + /* panel dimensions (width and height) in um */ + panel_size_um = <39744 36020>; pinctrl-names = "viopulldown", "viooutput"; pinctrl-0 = <&display_vddio_pulldown>; pinctrl-1 = <&display_vddio_output>; @@ -200,17 +204,21 @@ mot,mic_bias3_en = <&gpio3 20 0>; /* gpio-084 */ }; - omap_pimic { + regulators { + compatible = "simple-bus"; + omap_tps65912_dcdc1: omap_vdd1 { compatible = "ti,omap-tps65912-dcdc1"; ti,boot-voltage-micro-volts = <1275000>; ti,vp = <&vp_mpu>; + regulator-name = "omap_vdd1"; }; omap_tps65912_dcdc4: omap_vdd2 { compatible = "ti,omap-tps65912-dcdc4"; ti,boot-voltage-micro-volts = <1200000>; ti,vp = <&vp_core>; + regulator-name = "omap_vdd2"; }; }; @@ -272,10 +280,6 @@ vc_mpu{ ti,master-channel; }; - - vc_core{ - ti,use-master-slave-addr; - }; }; &glbl_prm { @@ -309,6 +313,8 @@ }; &i2c2 { + + clock-frequency = <400000>; m4sensorhub@39 { compatible = "mot,m4sensorhub"; status = "ok"; @@ -346,11 +352,52 @@ interrupts = <12 0>; /* gpio-140 IRQ */ maxim,malicious_supply = "ac"; maxim,enable_por_init; + + /* This is the default battery profile attributes used by max17xxxx driver */ + /* It is used when batt-id isn't defined in /chosen as batt-id. The */ + /* Value is originally used under the assumption that this is an LG battery*/ + /* Otherwise, the driver will either pick SNN5951A or SNN5950A as defined */ + /* Right below it. */ maxim,configuration { /* Required properties */ + version = /bits/ 16 <0x0001>; + config = /bits/ 16 <0x6254>; /* Tex = 0, Ss = Aen = TS = Ten = ETHRM = 1 */ + full_soc_thresh = /bits/ 16 <0x5F00>; /* app note constant */ + design_cap = /bits/ 16 <606>; /* in 5uVh/Rsns = 0.5 mAh */ + ichgt_term = /bits/ 16 <0x0066>; /* battery attribute */ + learn_cfg = /bits/ 16 <0x2606>; /* app note constant */ + filter_cfg = /bits/ 16 <0x87A4>; /* app note constant */ + relax_cfg = /bits/ 16 <0x043B>; /* Load = 5 mA, dV = 3.7 mV, dT = 6 mins */ + fullcap = /bits/ 16 <662>; /* in 5uVh/Rsns = 0.5 mAh */ + fullcapnom = /bits/ 16 <662>; /* in 5uVh/Rsns = 0.5 mAh */ + qrtbl00 = /bits/ 16 <0x1B94>; /* battery attribute */ + qrtbl10 = /bits/ 16 <0x0E94>; /* battery attribute */ + qrtbl20 = /bits/ 16 <0x0594>; /* battery attribute */ + qrtbl30 = /bits/ 16 <0x0294>; /* battery attribute */ + rcomp0 = /bits/ 16 <0x0052>; /* battery attribute */ + tcompc0 = /bits/ 16 <0x333A>; /* battery attribute */ + maxim,cell-char-tbl = /bits/ 16 < /* battery attribute */ + 0x9cf0 0xa310 0xb180 0xb360 0xb6a0 0xb6e0 + 0xba10 0xbb10 0xbbd0 0xbc10 0xc200 0xc250 + 0xc7c0 0xc820 0xd050 0xd710 0x0140 0x0110 + 0x0210 0x02c0 0x1000 0x0a00 0x06c0 0x31a0 + 0x0af0 0x1170 0x0cb0 0x08d0 0x0aa0 0x0880 + 0x07b0 0x07b0 0x0100 0x0100 0x0100 0x0100 + 0x0100 0x0100 0x0100 0x0100 0x0100 0x0100 + 0x0100 0x0100 0x0100 0x0100 0x0100 0x0100 + >; + /* Optional properties */ + tgain = /bits/ 16 <0xE71C>; /* thermistor attribute */ + toff = /bits/ 16 <0x251A>; /* thermistor attribute */ + }; + + /* LG Battery profile */ + maxim,configuration-SNN5951A { + /* Required properties */ + version = /bits/ 16 <0x0001>; config = /bits/ 16 <0x6254>; /* Tex = 0, Ss = Aen = TS = Ten = ETHRM = 1 */ full_soc_thresh = /bits/ 16 <0x5F00>; /* app note constant */ - design_cap = /bits/ 16 <662>; /* in 5uVh/Rsns = 0.5 mAh */ + design_cap = /bits/ 16 <606>; /* in 5uVh/Rsns = 0.5 mAh */ ichgt_term = /bits/ 16 <0x0066>; /* battery attribute */ learn_cfg = /bits/ 16 <0x2606>; /* app note constant */ filter_cfg = /bits/ 16 <0x87A4>; /* app note constant */ @@ -377,6 +424,40 @@ tgain = /bits/ 16 <0xE71C>; /* thermistor attribute */ toff = /bits/ 16 <0x251A>; /* thermistor attribute */ }; + /* Lishen battery profile */ + maxim,configuration-SNN5950A { + /* Required properties */ + version = /bits/ 16 <0x0001>; + config = /bits/ 16 <0x6254>; /* Tex = 0, TS = Ten = ETHRM = 1, Aen = 1 */ + full_soc_thresh = /bits/ 16 <0x5F00>; /* app note constant */ + design_cap = /bits/ 16 <662>; /* in 5uVh/Rsns = 0.5 mAh */ + ichgt_term = /bits/ 16 <0x0066>; /* battery attribute */ + learn_cfg = /bits/ 16 <0x2606>; /* app note constant */ + filter_cfg = /bits/ 16 <0x87A4>; /* app note constant */ + relax_cfg = /bits/ 16 <0x203B>; /* Load = 5 mA, dV = 3.7 mV, dT = 6 mins */ + fullcap = /bits/ 16 <662>; /* in 5uVh/Rsns = 0.5 mAh */ + fullcapnom = /bits/ 16 <662>; /* in 5uVh/Rsns = 0.5 mAh */ + qrtbl00 = /bits/ 16 <0x069A>; /* battery attribute */ + qrtbl10 = /bits/ 16 <0x0398>; /* battery attribute */ + qrtbl20 = /bits/ 16 <0x0298>; /* battery attribute */ + qrtbl30 = /bits/ 16 <0x0296>; /* battery attribute */ + rcomp0 = /bits/ 16 <0x003C>; /* battery attribute */ + tcompc0 = /bits/ 16 <0x231C>; /* battery attribute */ + maxim,cell-char-tbl = /bits/ 16 < /* battery attribute */ + 0x9f80 0xa030 0xb220 0xb440 0xb890 0xb910 + 0xba10 0xba40 0xbf10 0xbf60 0xc2d0 0xc5e0 + 0xc640 0xcc90 0xd520 0xd780 0x05d0 0x00e0 + 0x01e0 0x03a0 0x08e0 0x1000 0x4000 0x1670 + 0x23d0 0x0df0 0x0a70 0x0aa0 0x06f0 0x0790 + 0x06b0 0x06b0 0x0100 0x0100 0x0100 0x0100 + 0x0100 0x0100 0x0100 0x0100 0x0100 0x0100 + 0x0100 0x0100 0x0100 0x0100 0x0100 0x0100 + >; + /* Optional properties */ + tgain = /bits/ 16 <0xE71C>; /* thermistor attribute */ + toff = /bits/ 16 <0x251A>; /* thermistor attribute */ + }; + maxim,temp-conv { start = /bits/ 16 <(-20)>; /* in degrees C */ result = /bits/ 16 < diff --git a/arch/arm/configs/minnow_defconfig b/arch/arm/configs/minnow_defconfig index 38ae97a68a7..fe129184caf 100644 --- a/arch/arm/configs/minnow_defconfig +++ b/arch/arm/configs/minnow_defconfig @@ -1154,6 +1154,7 @@ CONFIG_MOT_UTAG=y CONFIG_BQ5105X_CTRL=y CONFIG_BQ5105X_DETECT=y CONFIG_WAKEUP_SOURCE_NOTIFY=y +CONFIG_ALS_WHILE_CHARGING=y # CONFIG_C2PORT is not set # @@ -1399,7 +1400,7 @@ CONFIG_INPUT_MISC=y # CONFIG_INPUT_POWERMATE is not set # CONFIG_INPUT_YEALINK is not set # CONFIG_INPUT_CM109 is not set -# CONFIG_INPUT_UINPUT is not set +CONFIG_INPUT_UINPUT=y # CONFIG_INPUT_GPIO is not set # CONFIG_INPUT_PCF8574 is not set # CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set diff --git a/arch/arm/mach-omap2/cclock3xxx_data.c b/arch/arm/mach-omap2/cclock3xxx_data.c index e823edd05eb..50d8b4587f6 100644 --- a/arch/arm/mach-omap2/cclock3xxx_data.c +++ b/arch/arm/mach-omap2/cclock3xxx_data.c @@ -137,6 +137,10 @@ DEFINE_CLK_DIVIDER(dpll3_m2_ck, "dpll3_ck", &dpll3_ck, 0x0, OMAP3430_CORE_DPLL_CLKOUT_DIV_WIDTH, CLK_DIVIDER_ONE_BASED, NULL); +/* placeholder for ops substitution */ +static struct clk_ops dpll3_m2_ck_subops = { +}; + static struct clk core_ck; static const char *core_ck_parent_names[] = { @@ -3566,6 +3570,11 @@ int __init omap3xxx_clk_init(void) dpll4_m4x2_ck = dpll4_m4x2_ck_3630; dpll4_m5x2_ck = dpll4_m5x2_ck_3630; dpll4_m6x2_ck = dpll4_m6x2_ck_3630; + memcpy( + &dpll3_m2_ck_subops, dpll3_m2_ck.ops, sizeof(struct clk_ops)); + + dpll3_m2_ck_subops.set_rate = omap3_core_dpll_m2_set_rate; + dpll3_m2_ck.ops = &dpll3_m2_ck_subops; } /* diff --git a/arch/arm/mach-omap2/clkt34xx_dpll3m2.c b/arch/arm/mach-omap2/clkt34xx_dpll3m2.c index eb69acf2101..1fc3b607c7d 100644 --- a/arch/arm/mach-omap2/clkt34xx_dpll3m2.c +++ b/arch/arm/mach-omap2/clkt34xx_dpll3m2.c @@ -55,15 +55,16 @@ int omap3_core_dpll_m2_set_rate(struct clk_hw *hw, unsigned long rate, struct omap_sdrc_params *sdrc_cs0; struct omap_sdrc_params *sdrc_cs1; int ret; - unsigned long clkrate; + unsigned long clkrate, flags; if (!clk || !rate) return -EINVAL; - validrate = omap2_clksel_round_rate_div(clk, rate, &new_div); + validrate = clk_round_rate(hw->clk, rate); if (validrate != rate) return -EINVAL; + new_div = parent_rate / validrate; sdrcrate = __clk_get_rate(sdrc_ick_p); clkrate = __clk_get_rate(hw->clk); if (rate > clkrate) @@ -101,6 +102,7 @@ int omap3_core_dpll_m2_set_rate(struct clk_hw *hw, unsigned long rate, sdrc_cs1->rfr_ctrl, sdrc_cs1->actim_ctrla, sdrc_cs1->actim_ctrlb, sdrc_cs1->mr); + local_irq_save(flags); if (sdrc_cs1) omap3_configure_core_dpll( new_div, unlock_dll, c, rate > clkrate, @@ -114,6 +116,7 @@ int omap3_core_dpll_m2_set_rate(struct clk_hw *hw, unsigned long rate, sdrc_cs0->rfr_ctrl, sdrc_cs0->actim_ctrla, sdrc_cs0->actim_ctrlb, sdrc_cs0->mr, 0, 0, 0, 0); + local_irq_restore(flags); return 0; } diff --git a/arch/arm/mach-omap2/control.c b/arch/arm/mach-omap2/control.c index ab5ec4b28ab..a7da7ccd1a1 100644 --- a/arch/arm/mach-omap2/control.c +++ b/arch/arm/mach-omap2/control.c @@ -92,7 +92,7 @@ struct omap3_scratchpad_sdrc_block { u32 block_size; }; -void *omap3_secure_ram_storage; +phys_addr_t omap3_secure_ram_context; /* * This is used to store ARM registers in SDRAM before attempting @@ -360,7 +360,7 @@ void omap3_save_scratchpad_contents(void) scratchpad_contents.secure_ram_restore_ptr = 0x0; else scratchpad_contents.secure_ram_restore_ptr = - (u32) __pa(omap3_secure_ram_storage); + (u32) omap3_secure_ram_context; scratchpad_contents.sdrc_module_semaphore = 0x0; scratchpad_contents.prcm_block_offset = 0x2C; scratchpad_contents.sdrc_block_offset = 0x64; diff --git a/arch/arm/mach-omap2/omap-secure.c b/arch/arm/mach-omap2/omap-secure.c index b970440cffc..55b24c32099 100644 --- a/arch/arm/mach-omap2/omap-secure.c +++ b/arch/arm/mach-omap2/omap-secure.c @@ -58,10 +58,7 @@ u32 omap_secure_dispatcher(u32 idx, u32 flag, u32 nargs, u32 arg1, u32 arg2, /* Allocate the memory to save secure ram */ int __init omap_secure_ram_reserve_memblock(void) { - u32 size = OMAP_SECURE_RAM_STORAGE; - - size = ALIGN(size, SECTION_SIZE); - omap_secure_memblock_base = arm_memblock_steal(size, SECTION_SIZE); + omap_secure_memblock_base = OMAP_SECURE_RAM_STORAGE_ADDR; return 0; } diff --git a/arch/arm/mach-omap2/omap-secure.h b/arch/arm/mach-omap2/omap-secure.h index 0e729170c46..dd894db8599 100644 --- a/arch/arm/mach-omap2/omap-secure.h +++ b/arch/arm/mach-omap2/omap-secure.h @@ -28,6 +28,7 @@ /* Maximum Secure memory storage size */ #define OMAP_SECURE_RAM_STORAGE (88 * SZ_1K) +#define OMAP_SECURE_RAM_STORAGE_ADDR 0x83020000 /* Secure low power HAL API index */ #define OMAP4_HAL_SAVESECURERAM_INDEX 0x1a diff --git a/arch/arm/mach-omap2/pm-debug.c b/arch/arm/mach-omap2/pm-debug.c index b5dff42606c..ea5a306d1f1 100644 --- a/arch/arm/mach-omap2/pm-debug.c +++ b/arch/arm/mach-omap2/pm-debug.c @@ -38,8 +38,16 @@ #include "pm.h" #include "pm-debug-regs.h" +/* enable_off_mode is the runtime flag for enable/disable of offmode */ u32 enable_off_mode; +/* + * global_disable_off_mode supercedes enable_off_mode and keeps + * offmode disabled as long as it is set + */ +bool global_disable_off_mode = 1; +module_param(global_disable_off_mode, bool, 0644); + #ifdef CONFIG_DEBUG_FS #include <linux/debugfs.h> #include <linux/seq_file.h> @@ -236,9 +244,13 @@ static int option_set(void *data, u64 val) { u32 *option = data; - *option = val; - if (option == &enable_off_mode) { + if (global_disable_off_mode) { + pr_warn("Disable global_disable_off_mode " + "before enabling off mode"); + return -EINVAL; + } + if (val) omap_pm_enable_off_mode(); else @@ -247,6 +259,8 @@ static int option_set(void *data, u64 val) omap3_pm_off_mode_enable(val); } + *option = val; + return 0; } diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c index 303a8d2012b..b2dc18a6b82 100644 --- a/arch/arm/mach-omap2/pm.c +++ b/arch/arm/mach-omap2/pm.c @@ -311,13 +311,6 @@ int __init omap2_common_pm_late_init(void) /* Smartreflex device init */ omap_devinit_smartreflex(); - } else { - struct device_node *np; - np = of_find_node_by_name(NULL, "omap_pimic"); - if (np) { - of_platform_populate(np, NULL, NULL, NULL); - of_node_put(np); - } } /* cpufreq dummy device instantiation */ diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h index 4f6f52b5215..551db0bb4a6 100644 --- a/arch/arm/mach-omap2/pm.h +++ b/arch/arm/mach-omap2/pm.h @@ -30,7 +30,7 @@ static inline int omap4_idle_init(void) } #endif -extern void *omap3_secure_ram_storage; +extern phys_addr_t omap3_secure_ram_context; extern void omap3_pm_off_mode_enable(int); extern void omap_sram_idle(bool in_suspend); extern int omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused); diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c index 6f67bfa0b03..bdb2388ef4c 100644 --- a/arch/arm/mach-omap2/pm34xx.c +++ b/arch/arm/mach-omap2/pm34xx.c @@ -59,6 +59,10 @@ #include "pad_wkup.h" +#define OMAP3_SECURE_RAM_CTX_ADDR 0x83036000 + +extern bool global_disable_off_mode; + /* pm34xx errata defined in pm.h */ u16 pm34xx_errata; bool suspend_debug; @@ -133,8 +137,7 @@ static void omap3_save_secure_ram_context(void) * will hang the system. */ pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_ON); - ret = _omap_save_secure_sram((u32 *) - __pa(omap3_secure_ram_storage)); + ret = _omap_save_secure_sram((u32 *)omap3_secure_ram_context); pwrdm_set_next_pwrst(mpu_pwrdm, mpu_next_state); /* Following is for error tracking, it should not happen */ if (ret) { @@ -667,6 +670,12 @@ void omap3_pm_off_mode_enable(int enable) pwrst->next_state = PWRDM_POWER_RET; pr_warn("%s: Core OFF disabled due to errata i583\n", __func__); + } else if (global_disable_off_mode && + pwrst->pwrdm == core_pwrdm && + state == PWRDM_POWER_OFF) { + pwrst->next_state = PWRDM_POWER_RET; + pr_warn("%s: Core OFF disabled due global disable\n", + __func__); } else { pwrst->next_state = state; } @@ -741,13 +750,14 @@ static void __init pm_errata_configure(void) /* Enable the l2 cache toggling in sleep logic */ enable_omap3630_toggle_l2_on_restore(); - if (omap_rev() < OMAP3630_REV_ES1_2) + if (omap_rev() < OMAP3630_REV_ES1_2) { pm34xx_errata |= PM_PER_MEMORIES_ERRATUM_i582; #ifndef CONFIG_DISABLE_OMAP_ERRATA_i583 pm34xx_errata |= PM_SDRC_WAKEUP_ERRATUM_i583; #endif - if (meminfo.bank[0].size > 256 * (1024 * 1024)) - pm34xx_errata |= PM_SDRC_WAKEUP_ERRATUM_i583; + if (meminfo.bank[0].size > 256 * (1024 * 1024)) + pm34xx_errata |= PM_SDRC_WAKEUP_ERRATUM_i583; + } } else if (cpu_is_omap34xx()) { pm34xx_errata |= PM_PER_MEMORIES_ERRATUM_i582; @@ -818,8 +828,7 @@ int __init omap3_pm_init(void) omap_pm_suspend = omap3_pm_suspend; #endif - if (omap_pm_get_off_mode()) - omap3_pm_off_mode_enable(true); + omap3_pm_off_mode_enable(omap_pm_get_off_mode()); arm_pm_idle = omap3_pm_idle; omap3_idle_init(); @@ -850,15 +859,16 @@ int __init omap3_pm_init(void) * XXX Technically this workaround is only needed if off-mode * or OSWR is enabled. */ - if (IS_PM34XX_ERRATUM(PM_PER_MEMORIES_ERRATUM_i582)) - clkdm_add_wkdep(per_clkdm, wkup_clkdm); + + /* + * The PER wake dependency is still needed on ES1.2 which + * has fixed i582 + */ + clkdm_add_wkdep(per_clkdm, wkup_clkdm); clkdm_add_wkdep(neon_clkdm, mpu_clkdm); if (omap_type() != OMAP2_DEVICE_TYPE_GP) { - omap3_secure_ram_storage = - kmalloc(0x803F, GFP_KERNEL); - if (!omap3_secure_ram_storage) - pr_err("Memory allocation failed when allocating for secure sram context\n"); + omap3_secure_ram_context = OMAP3_SECURE_RAM_CTX_ADDR; local_irq_disable(); diff --git a/arch/arm/mach-omap2/sdram-toshiba-hynix-numonyx.h b/arch/arm/mach-omap2/sdram-toshiba-hynix-numonyx.h index c7acdb8312d..fe835c96b7d 100644 --- a/arch/arm/mach-omap2/sdram-toshiba-hynix-numonyx.h +++ b/arch/arm/mach-omap2/sdram-toshiba-hynix-numonyx.h @@ -17,43 +17,43 @@ static struct omap_sdrc_params JEDEC_JESD209A_sdrc_params[] = { [0] = { .rate = 200000000, - .actim_ctrla = 0xE2E1B4C6, - .actim_ctrlb = 0x00022228, + .actim_ctrla = 0x7ae1b4c6, + .actim_ctrlb = 0x00021217, .rfr_ctrl = 0x0005E602, .mr = 0x00000032, }, [1] = { .rate = 100000000, - .actim_ctrla = 0x7211B485, - .actim_ctrlb = 0x00022214, + .actim_ctrla = 0x41912286, + .actim_ctrlb = 0x0001110c, .rfr_ctrl = 0x0002DA02, .mr = 0x00000032, }, [2] = { .rate = 166000000, - .actim_ctrla = 0xE2E1B4C6, - .actim_ctrlb = 0x00022228, + .actim_ctrla = 0x6A9DB4C6, + .actim_ctrlb = 0x00021214, .rfr_ctrl = 0x0004DD02, .mr = 0x00000032, }, [3] = { .rate = 83000000, - .actim_ctrla = 0x7215B485, - .actim_ctrlb = 0x00022214, + .actim_ctrla = 0x39512286, + .actim_ctrlb = 0x0001110C, .rfr_ctrl = 0x00025602, .mr = 0x00000032, }, [4] = { .rate = 160000000, - .actim_ctrla = 0xBA9DB4C6, - .actim_ctrlb = 0x00022220, + .actim_ctrla = 0x625DB4C6, + .actim_ctrlb = 0x00021213, .rfr_ctrl = 0x0004AE02, .mr = 0x00000032, }, [5] = { .rate = 80000000, - .actim_ctrla = 0x49512284, - .actim_ctrlb = 0x0001120C, + .actim_ctrla = 0x31512284, + .actim_ctrlb = 0x0001110C, .rfr_ctrl = 0x23E02, .mr = 0x00000032, }, diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 01e21037d8f..d7872b96019 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -27,6 +27,7 @@ #include <linux/pm.h> #include <linux/suspend.h> #include <linux/syscore_ops.h> +#include <linux/reboot.h> #include <generated/utsrelease.h> @@ -130,6 +131,7 @@ struct firmware_buf { struct page **pages; int nr_pages; int page_array_size; + struct list_head pending_list; #endif char fw_id[]; }; @@ -171,6 +173,9 @@ static struct firmware_buf *__allocate_fw_buf(const char *fw_name, strcpy(buf->fw_id, fw_name); buf->fwc = fwc; init_completion(&buf->completion); +#ifdef CONFIG_FW_LOADER_USER_HELPER + INIT_LIST_HEAD(&buf->pending_list); +#endif pr_debug("%s: fw-%s buf=%p\n", __func__, fw_name, buf); @@ -446,10 +451,8 @@ static struct firmware_priv *to_firmware_priv(struct device *dev) return container_of(dev, struct firmware_priv, dev); } -static void fw_load_abort(struct firmware_priv *fw_priv) +static void __fw_load_abort(struct firmware_buf *buf) { - struct firmware_buf *buf = fw_priv->buf; - /* * There is a small window in which user can write to 'loading' * between loading done and disappearance of 'loading' @@ -457,8 +460,16 @@ static void fw_load_abort(struct firmware_priv *fw_priv) if (test_bit(FW_STATUS_DONE, &buf->status)) return; + list_del_init(&buf->pending_list); set_bit(FW_STATUS_ABORT, &buf->status); complete_all(&buf->completion); +} + +static void fw_load_abort(struct firmware_priv *fw_priv) +{ + struct firmware_buf *buf = fw_priv->buf; + + __fw_load_abort(buf); /* avoid user action after loading abort */ fw_priv->buf = NULL; @@ -467,6 +478,25 @@ static void fw_load_abort(struct firmware_priv *fw_priv) #define is_fw_load_aborted(buf) \ test_bit(FW_STATUS_ABORT, &(buf)->status) +static LIST_HEAD(pending_fw_head); + +/* reboot notifier for avoid deadlock with usermode_lock */ +static int fw_shutdown_notify(struct notifier_block *unused1, + unsigned long unused2, void *unused3) +{ + mutex_lock(&fw_lock); + while (!list_empty(&pending_fw_head)) + __fw_load_abort(list_first_entry(&pending_fw_head, + struct firmware_buf, + pending_list)); + mutex_unlock(&fw_lock); + return NOTIFY_DONE; +} + +static struct notifier_block fw_shutdown_nb = { + .notifier_call = fw_shutdown_notify, +}; + static ssize_t firmware_timeout_show(struct class *class, struct class_attribute *attr, char *buf) @@ -619,6 +649,7 @@ static ssize_t firmware_loading_store(struct device *dev, * is completed. * */ fw_map_pages_buf(fw_buf); + list_del_init(&fw_buf->pending_list); complete_all(&fw_buf->completion); break; } @@ -853,8 +884,15 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, goto err_del_dev; } + mutex_lock(&fw_lock); + list_add(&buf->pending_list, &pending_fw_head); + mutex_unlock(&fw_lock); + retval = device_create_file(f_dev, &dev_attr_loading); if (retval) { + mutex_lock(&fw_lock); + list_del_init(&buf->pending_list); + mutex_unlock(&fw_lock); dev_err(f_dev, "%s: device_create_file failed\n", __func__); goto err_del_bin_attr; } @@ -1526,6 +1564,7 @@ static int __init firmware_class_init(void) { fw_cache_init(); #ifdef CONFIG_FW_LOADER_USER_HELPER + register_reboot_notifier(&fw_shutdown_nb); return class_register(&firmware_class); #else return 0; @@ -1539,6 +1578,7 @@ static void __exit firmware_class_exit(void) unregister_pm_notifier(&fw_cache.pm_notify); #endif #ifdef CONFIG_FW_LOADER_USER_HELPER + unregister_reboot_notifier(&fw_shutdown_nb); class_unregister(&firmware_class); #endif } diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c index dd7bc175ca8..0b9f6a3c66c 100644 --- a/drivers/cpufreq/cpufreq-cpu0.c +++ b/drivers/cpufreq/cpufreq-cpu0.c @@ -150,15 +150,22 @@ out: static int cpu0_cpufreq_pm_notify(struct notifier_block *nb, unsigned long event, void *dummy) { + static unsigned int old_policy_max; mutex_lock(&cpu0_cpufreq_lock); if (event == PM_SUSPEND_PREPARE) { struct cpufreq_policy *policy = cpufreq_cpu_get(0); is_suspended = true; + old_policy_max = policy->max; + policy->max = policy->cpuinfo.max_freq; pr_debug("cpu0 cpufreq suspend: setting frequency to %d kHz\n", policy->max); __cpu0_set_target(policy, policy->max, CPUFREQ_RELATION_L); cpufreq_cpu_put(policy); } else if (event == PM_POST_SUSPEND) { + struct cpufreq_policy *policy = cpufreq_cpu_get(0); + policy->max = old_policy_max; + __cpu0_set_target(policy, policy->max, CPUFREQ_RELATION_L); + cpufreq_cpu_put(policy); is_suspended = false; } mutex_unlock(&cpu0_cpufreq_lock); @@ -182,6 +189,7 @@ static int cpu0_cpufreq_reboot_notify(struct notifier_block *nb, mutex_lock(&cpu0_cpufreq_lock); policy = cpufreq_cpu_get(0); is_suspended = true; + policy->max = policy->cpuinfo.max_freq; pr_info("cpu0 cpufreq shutdown: setting frequency to %d kHz\n", policy->max); __cpu0_set_target(policy, policy->max, CPUFREQ_RELATION_L); diff --git a/drivers/gpu/pvr/services4/srvkm/env/linux/osfunc.c b/drivers/gpu/pvr/services4/srvkm/env/linux/osfunc.c index c9b577eddbe..f13d0cfa586 100755 --- a/drivers/gpu/pvr/services4/srvkm/env/linux/osfunc.c +++ b/drivers/gpu/pvr/services4/srvkm/env/linux/osfunc.c @@ -3169,7 +3169,7 @@ PVRSRV_ERROR OSEventObjectSignalKM(IMG_HANDLE hOSEventKM) ******************************************************************************/ IMG_BOOL OSProcHasPrivSrvInit(IMG_VOID) { - return (capable(CAP_SYS_MODULE) != 0) ? IMG_TRUE : IMG_FALSE; + return (capable(CAP_SYS_ADMIN) != 0) ? IMG_TRUE : IMG_FALSE; } /*! diff --git a/drivers/leds/leds-lm3535.c b/drivers/leds/leds-lm3535.c index 3f157942803..23b7558674e 100644 --- a/drivers/leds/leds-lm3535.c +++ b/drivers/leds/leds-lm3535.c @@ -50,10 +50,11 @@ #ifdef CONFIG_LM3535_ESD_RECOVERY #include <mot/esd_poll.h> #endif /* CONFIG_LM3535_ESD_RECOVERY */ -#ifdef CONFIG_WAKEUP_SOURCE_NOTIFY #include <linux/notifier.h> +#ifdef CONFIG_WAKEUP_SOURCE_NOTIFY +#include <linux/als_notify.h> #include <linux/wakeup_source_notify.h> -#define MIN_DOCK_BVALUE 36 +#define MIN_DOCK_BVALUE 36 #include <linux/m4sensorhub.h> #include <linux/m4sensorhub/MemMapUserSettings.h> #endif @@ -273,7 +274,12 @@ struct lm3535 { int prevent_als_read; /* Whether to prevent als reads for a time */ #ifdef CONFIG_WAKEUP_SOURCE_NOTIFY atomic_t docked; + atomic_t alsstatus; +#ifdef CONFIG_ALS_WHILE_CHARGING + atomic_t interactive; +#endif struct notifier_block dock_nb; + struct notifier_block als_nb; #endif }; static DEFINE_MUTEX(lm3535_mutex); @@ -574,30 +580,34 @@ static uint8_t lm3535_convert_value (unsigned value, unsigned zone) reg = res / als_denom; #ifdef CONFIG_WAKEUP_SOURCE_NOTIFY - if (!lm3535_data.prevent_als_read) { - /* make sure this is atleast as high as corresponding ambient - * mode value for current ALS condition */ - m4sensorhub = m4sensorhub_client_get_drvdata(); - size = m4sensorhub_reg_getsize(m4sensorhub, - M4SH_REG_LIGHTSENSOR_SIGNAL); - if (size != sizeof(als)) { - pr_err("can't get M4 reg size for ALS\n"); - ambient_als_backlight = 0; - } else if (size != m4sensorhub_reg_read(m4sensorhub, - M4SH_REG_LIGHTSENSOR_SIGNAL, - (char *)&als)) { - pr_err("error reading M4 ALS value\n"); - ambient_als_backlight = 0; - } else { - adjust_als = true; - /* prevent als reads for next 500 ms */ - lm3535_data.prevent_als_read = 1; - schedule_delayed_work(&lm3535_data.als_delayed_work, - msecs_to_jiffies(500)); + if (atomic_read(&lm3535_data.alsstatus)) { + if (!lm3535_data.prevent_als_read) { + /* make sure this is atleast as + high as corresponding ambient + * mode value for current ALS condition */ + m4sensorhub = m4sensorhub_client_get_drvdata(); + size = m4sensorhub_reg_getsize(m4sensorhub, + M4SH_REG_LIGHTSENSOR_SIGNAL); + if (size != sizeof(als)) { + pr_err("can't get M4 reg size for ALS\n"); + ambient_als_backlight = 0; + } else if (size != m4sensorhub_reg_read(m4sensorhub, + M4SH_REG_LIGHTSENSOR_SIGNAL, + (char *)&als)) { + pr_err("error reading M4 ALS value\n"); + ambient_als_backlight = 0; + } else { + adjust_als = true; + /* prevent als reads for next 500 ms */ + lm3535_data.prevent_als_read = 1; + schedule_delayed_work( + &lm3535_data.als_delayed_work, + msecs_to_jiffies(500)); + } + } else if (ambient_als_backlight > reg) { + /* If valid, use previously read als value */ + reg = ambient_als_backlight; } - } else if (ambient_als_backlight > reg) { - /* If valid, use previously read als value */ - reg = ambient_als_backlight; } if (adjust_als) { @@ -606,7 +616,7 @@ static uint8_t lm3535_convert_value (unsigned value, unsigned zone) if (ambient_als_backlight > reg) reg = ambient_als_backlight; } -#endif +#endif /* CONFIG_WAKEUP_SOURCE_NOTIFY*/ printk_br(KERN_INFO "%s: v=%d, z=%d, res=0x%x, reg=0x%x\n", __func__, value, zone, res, reg); @@ -660,6 +670,31 @@ static void lm3535_brightness_set_raw_als(struct led_classdev *led_cdev, mutex_unlock(&lm3535_mutex); } #endif + +#ifdef CONFIG_WAKEUP_SOURCE_NOTIFY +static int lm3535_als_notifier(struct notifier_block *self, + unsigned long action, void *dev) +{ + pr_info("%s: ALS value is %lu\n", __func__, action); + switch (action) { + case ALS_ENABLED: + case ALS_DISABLED: + atomic_set(&lm3535_data.use_als, (action == ALS_ENABLED)); + break; + default: +#ifdef CONFIG_ALS_WHILE_CHARGING + if (atomic_read(&lm3535_data.interactive) == 0) + lm3535_brightness_set_raw_als(led_get_default_dev(), + (unsigned int)action); + else + pr_info("%s: ignoring ALS notifications\n", __func__); +#endif + break; + } + return NOTIFY_OK; +} +#endif /* CONFIG_WAKEUP_SOURCE_NOTIFY */ + static void lm3535_brightness_set (struct led_classdev *led_cdev, enum led_brightness value) { @@ -707,6 +742,7 @@ static void lm3535_brightness_set (struct led_classdev *led_cdev, /* Calculate brightness value for each zone relative to its cap */ bvalue = lm3535_convert_value (value, bright_zone); + #ifdef CONFIG_WAKEUP_SOURCE_NOTIFY if (atomic_read(&lm3535_data.docked) && (bvalue < MIN_DOCK_BVALUE)) bvalue = MIN_DOCK_BVALUE; /* hard code for dock mode */ @@ -922,6 +958,38 @@ static ssize_t lm3535_suspend_store (struct device *dev, } static DEVICE_ATTR(suspend, 0644, lm3535_suspend_show, lm3535_suspend_store); +#ifdef CONFIG_ALS_WHILE_CHARGING +static ssize_t lm3535_interactive_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", atomic_read(&lm3535_data.interactive)); +} + +static ssize_t lm3535_interactive_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned value = 0; + + if (!buf || size == 0) { + pr_err("%s: invalid command\n", __func__); + return -EINVAL; + } + + sscanf(buf, "%d", &value); + if (value) + atomic_set(&lm3535_data.interactive, 1); + else + atomic_set(&lm3535_data.interactive, 0); + + return size; +} +static DEVICE_ATTR(interactive, S_IRUGO | S_IWUSR, + lm3535_interactive_show, + lm3535_interactive_store); +#endif + /* This function is called by i2c_probe */ static int lm3535_probe (struct i2c_client *client, const struct i2c_device_id *id) @@ -997,6 +1065,18 @@ static int lm3535_probe (struct i2c_client *client, misc_deregister (&als_miscdev); return ret; } +#ifdef CONFIG_ALS_WHILE_CHARGING + ret = device_create_file(lm3535_led.dev, &dev_attr_interactive); + if (ret) { + pr_err("err creating interactive file for %s: %d\n", + lm3535_led.name, ret); + led_classdev_unregister(&lm3535_led); + led_classdev_unregister(&lm3535_led_noramp); + device_remove_file(lm3535_led.dev, &dev_attr_suspend); + misc_deregister(&als_miscdev); + return ret; + } +#endif dev_set_drvdata (lm3535_led.dev, &lm3535_led); #if 0 lm3535_data.idev = input_allocate_device(); @@ -1029,15 +1109,23 @@ static int lm3535_probe (struct i2c_client *client, register_early_suspend (&early_suspend_data); #endif - lm3535_led.brightness = 255; - lm3535_led_noramp.brightness = 255; - //lm3535_brightness_set (&lm3535_led_noramp, 255); - lm3535_write_reg (LM3535_BRIGHTNESS_CTRL_REG_A, 0x79, __FUNCTION__); - lm3535_data.initialized = 1; + lm3535_led.brightness = 84; + lm3535_led_noramp.brightness = 84; + /* lm3535_brightness_set (&lm3535_led_noramp, 255); */ + lm3535_write_reg(LM3535_BRIGHTNESS_CTRL_REG_A, 87, __func__); + lm3535_data.initialized = 1; #ifdef CONFIG_WAKEUP_SOURCE_NOTIFY atomic_set(&lm3535_data.docked, 0); + /* default setting for minnow is to use ALS */ + atomic_set(&lm3535_data.alsstatus, 1); +#ifdef CONFIG_ALS_WHILE_CHARGING + atomic_set(&lm3535_data.interactive, 1); +#endif lm3535_data.dock_nb.notifier_call = lm3535_dock_notifier; wakeup_source_register_notify(&lm3535_data.dock_nb); + + lm3535_data.als_nb.notifier_call = lm3535_als_notifier; + als_register_notify(&lm3535_data.als_nb); #endif /* CONFIG_WAKEUP_SOURCE_NOTIFY */ INIT_DELAYED_WORK(&lm3535_data.als_delayed_work, @@ -1515,10 +1603,18 @@ static int lm3535_remove (struct i2c_client *client) /* led_classdev_unregister (&lm3535_led_noramp); */ misc_deregister (&als_miscdev); device_remove_file (lm3535_led.dev, &dev_attr_suspend); +#ifdef CONFIG_ALS_WHILE_CHARGING + device_remove_file(lm3535_led.dev, &dev_attr_interactive); +#endif #if 0 input_unregister_device (lm3535_data.idev); input_free_device (lm3535_data.idev); #endif + +#ifdef CONFIG_WAKEUP_SOURCE_NOTIFY + wakeup_source_unregister_notify(&lm3535_data.dock_nb); + als_unregister_notify(&lm3535_data.als_nb); +#endif return 0; } diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index c0704d70b97..e14fcb9363f 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -171,6 +171,7 @@ m4sensorhub-objs := m4sensorhub-core.o \ m4sensorhub-reg.o \ m4sensorhub-irq.o \ m4sensorhub-panic.o \ + m4sensorhub-extern.o \ m4sensorhub-stm32-fw.o \ m4sensorhub-stm32_401-fw.o diff --git a/drivers/mfd/m4sensorhub-core.c b/drivers/mfd/m4sensorhub-core.c index a10ab20280b..203109e6ac7 100644 --- a/drivers/mfd/m4sensorhub-core.c +++ b/drivers/mfd/m4sensorhub-core.c @@ -90,6 +90,12 @@ static ssize_t m4sensorhub_get_dbg(struct device *dev, /* BEGIN BOARD FILE */ /* TODO: replace with request array */ +int m4sensorhub_get_current_mode(void) +{ + return m4sensorhub_misc_data.mode; +} +EXPORT_SYMBOL_GPL(m4sensorhub_get_current_mode); + int m4sensorhub_set_bootmode(struct m4sensorhub_data *m4sensorhub, enum m4sensorhub_bootmode bootmode) { @@ -496,6 +502,12 @@ static void m4sensorhub_initialize(const struct firmware *firmware, return; } + err = m4sensorhub_extern_init(&m4sensorhub_misc_data); + if (err < 0) { + KDEBUG(M4SH_ERROR, "%s: Extern init failed.\n", __func__); + return; + } + /* Initialize all the m4 drivers */ inc = inithead; arg.p_m4sensorhub_data = &m4sensorhub_misc_data; @@ -540,20 +552,27 @@ static DEVICE_ATTR(debug_level, S_IRUSR|S_IWUSR, m4sensorhub_get_dbg, static ssize_t m4sensorhub_get_loglevel(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned long long loglevel; + uint32_t logenable[LOG_EN_SIZE], len, i; m4sensorhub_reg_read(&m4sensorhub_misc_data, - M4SH_REG_LOG_LOGENABLE, (char *)&loglevel); - KDEBUG(M4SH_INFO, "M4 loglevel = %llx", loglevel); - return sprintf(buf, "%llu\n", loglevel); + M4SH_REG_LOG_LOGENABLE, (char *)&logenable); + + len = sprintf(buf, "M4 log levels = 0x"); + for (i = 0; i < LOG_EN_SIZE; i++) + len += sprintf(buf+len, "%08x", logenable[i]); + KDEBUG(M4SH_INFO, "%s\n", buf); + return snprintf(buf, PAGE_SIZE, "%s\n", buf); } -void m4sensorhub_update_loglevels(char *tag, char *level, - unsigned long long *log_levels) +void m4sensorhub_update_loglevels(int8_t *tag, int8_t *level, + uint32_t *log_level) { - int i; - int levelindex = -1; - int tagindex = -1; - unsigned long long mask; + uint32_t i; + int32_t levelindex = -1; + int32_t tagindex = -1; + uint32_t mask; + int32_t logenableindex; + int32_t en = LOG_TAGS_PER_ENABLE; + int32_t b = LOG_NO_OF_BITS_PER_TAG; for (i = 0; i < LOG_LEVELS_MAX; i++) { if (strcmp(acLogLevels[i], level) == 0) { @@ -568,16 +587,21 @@ void m4sensorhub_update_loglevels(char *tag, char *level, break; } } - if ((tagindex == -1) || (levelindex == -1)) return; + logenableindex = tagindex/LOG_TAGS_PER_ENABLE; + /*Clear the revelant bits*/ - mask = 0x03; - *log_levels &= ~(mask << (tagindex * 2)); + mask = LOG_TAG_MASK; + *(log_level+logenableindex) &= ~(mask << ((tagindex % en) * b)); /*set debug level for the relevant bits*/ - *log_levels |= (levelindex << (tagindex * 2)); - KDEBUG(M4SH_INFO, "New M4 log levels = 0x%llx\n", *log_levels); + *(log_level+logenableindex) |= (levelindex << ((tagindex % en) * b)); + + KDEBUG(M4SH_DEBUG, "New M4 log levels = "); + for (i = 0; i < LOG_EN_SIZE; i++) + KDEBUG(M4SH_DEBUG, "enable %d = 0x%08x ", i, *(log_level+i)); + KDEBUG(M4SH_DEBUG, "\n"); } /* Usage: adb shell into the directory of sysinterface log_level and @@ -585,25 +609,29 @@ void m4sensorhub_update_loglevels(char *tag, char *level, static ssize_t m4sensorhub_set_loglevel(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long long cur_loglevels; + uint32_t cur_loglevels[LOG_EN_SIZE]; char *tag, *level; char **logbuf = (char **) &buf; m4sensorhub_reg_read(&m4sensorhub_misc_data, - M4SH_REG_LOG_LOGENABLE, (char *)&cur_loglevels); + M4SH_REG_LOG_LOGENABLE, (char *)cur_loglevels); + while (1) { tag = strsep(logbuf, "=,\n "); - if (tag == NULL) + if ((tag == NULL) || (logbuf == NULL)) break; level = strsep(logbuf, "=,\n "); - if (level == NULL) + if ((level == NULL) || (logbuf == NULL)) break; - m4sensorhub_update_loglevels(tag, level, &cur_loglevels); + m4sensorhub_update_loglevels(tag, level, + (uint32_t *)cur_loglevels); } - return m4sensorhub_reg_write(&m4sensorhub_misc_data, - M4SH_REG_LOG_LOGENABLE, (char *)&cur_loglevels, - m4sh_no_mask); + m4sensorhub_reg_write(&m4sensorhub_misc_data, + M4SH_REG_LOG_LOGENABLE, (char *)cur_loglevels, + m4sh_no_mask); + + return count; } static DEVICE_ATTR(log_level, S_IRUSR|S_IWUSR, m4sensorhub_get_loglevel, diff --git a/drivers/mfd/m4sensorhub-extern.c b/drivers/mfd/m4sensorhub-extern.c new file mode 100644 index 00000000000..771dc5cc4f3 --- /dev/null +++ b/drivers/mfd/m4sensorhub-extern.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2014 Motorola, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Adds ability to program periodic interrupts from user space that + * can wake the phone out of low power modes. + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/m4sensorhub.h> +#include <linux/slab.h> + +#define m4ext_err(format, args...) KDEBUG(M4SH_ERROR, format, ## args) + +struct m4sensorhub_data *m4ext_m4; +DEFINE_MUTEX(m4ext_mutex); +uint8_t m4ext_display_status; +uint8_t m4ext_audio_status; + +static void m4ext_panic_callback(struct m4sensorhub_data *m4, void *context) +{ + int err; + + mutex_lock(&m4ext_mutex); + + if (m4ext_m4 == NULL) { + m4ext_err("%s: M4 data is NULL.\n", __func__); + err = -ENODATA; + goto m4ext_panic_callback_fail; + } + + err = m4sensorhub_reg_write(m4, M4SH_REG_USERSETTINGS_SCREENSTATUS, + (char *)&m4ext_display_status, m4sh_no_mask); + if (err < 0) + m4ext_err("%s: Screen status write failed (%d).\n", + __func__, err); + else if (err != 1) + m4ext_err("%s: Screen status wrote %d bytes instead of 1.\n", + __func__, err); + + err = m4sensorhub_reg_write(m4, M4SH_REG_USERSETTINGS_AUDIOSTATUS, + (char *)&m4ext_audio_status, m4sh_no_mask); + if (err < 0) + m4ext_err("%s: Audio status write failed (%d).\n", + __func__, err); + else if (err != 1) + m4ext_err("%s: Audio status wrote %d bytes instead of 1.\n", + __func__, err); + +m4ext_panic_callback_fail: + mutex_unlock(&m4ext_mutex); + return; +} + +int m4sensorhub_extern_init(struct m4sensorhub_data *m4) +{ + int err; + + mutex_lock(&m4ext_mutex); + + err = m4sensorhub_panic_register(m4, PANICHDL_EXTERN_RESTORE, + m4ext_panic_callback, NULL); + if (err < 0) { + m4ext_err("%s: Failed to register panic callback.\n", __func__); + goto m4sensorhub_extern_init_fail; + } + + m4ext_m4 = m4; + +m4sensorhub_extern_init_fail: + mutex_unlock(&m4ext_mutex); + return err; +} +EXPORT_SYMBOL_GPL(m4sensorhub_extern_init); + +int m4sensorhub_extern_set_display_status(uint8_t status) +{ + int err; + + mutex_lock(&m4ext_mutex); + + if (m4ext_m4 == NULL) { + m4ext_err("%s: M4 data is NULL.\n", __func__); + err = -ENODATA; + goto m4sensorhub_extern_set_display_status_fail; + } + + m4ext_display_status = status; + + err = m4sensorhub_reg_write(m4ext_m4, + M4SH_REG_USERSETTINGS_SCREENSTATUS, + (char *)&status, m4sh_no_mask); + if (err < 0) { + m4ext_err("%s: I2C write failed (%d).\n", __func__, err); + goto m4sensorhub_extern_set_display_status_fail; + } else if (err != 1) { + m4ext_err("%s: Wrote %d bytes instead of 1.\n", __func__, err); + err = -EINVAL; + goto m4sensorhub_extern_set_display_status_fail; + } + + err = 0; + +m4sensorhub_extern_set_display_status_fail: + mutex_unlock(&m4ext_mutex); + return err; +} +EXPORT_SYMBOL_GPL(m4sensorhub_extern_set_display_status); + +int m4sensorhub_extern_set_audio_status(uint8_t status) +{ + int err; + + mutex_lock(&m4ext_mutex); + + if (m4ext_m4 == NULL) { + m4ext_err("%s: M4 data is NULL.\n", __func__); + err = -ENODATA; + goto m4sensorhub_extern_set_audio_status_fail; + } + + m4ext_audio_status = status; + + err = m4sensorhub_reg_write(m4ext_m4, + M4SH_REG_USERSETTINGS_AUDIOSTATUS, + (char *)&status, m4sh_no_mask); + if (err < 0) { + m4ext_err("%s: I2C write failed (%d).\n", __func__, err); + goto m4sensorhub_extern_set_audio_status_fail; + } else if (err != 1) { + m4ext_err("%s: Wrote %d bytes instead of 1.\n", __func__, err); + err = -EINVAL; + goto m4sensorhub_extern_set_audio_status_fail; + } + + err = 0; + +m4sensorhub_extern_set_audio_status_fail: + mutex_unlock(&m4ext_mutex); + return err; +} +EXPORT_SYMBOL_GPL(m4sensorhub_extern_set_audio_status); diff --git a/drivers/mfd/m4sensorhub-panic.c b/drivers/mfd/m4sensorhub-panic.c index b127adeba5c..34ad6658453 100644 --- a/drivers/mfd/m4sensorhub-panic.c +++ b/drivers/mfd/m4sensorhub-panic.c @@ -40,6 +40,7 @@ static const char *callback_name[PANICHDL_MAX] = { [PANICHDL_FUSION_RESTORE] = "fusion_restore", [PANICHDL_MPU9150_RESTORE] = "mpu9150_restore", [PANICHDL_PEDOMETER_RESTORE] = "pedometer_restore", + [PANICHDL_EXTERN_RESTORE] = "extern_restore", }; struct m4sensorhub_panic_callback { diff --git a/drivers/mfd/m4sensorhub-reg.h b/drivers/mfd/m4sensorhub-reg.h index 96719a08927..753ea9a8846 100644 --- a/drivers/mfd/m4sensorhub-reg.h +++ b/drivers/mfd/m4sensorhub-reg.h @@ -87,9 +87,9 @@ static const struct { [M4SH_REG_FUSION_LOCALX] = {M4SH_TYPE_FUSION, 0x10, 4}, [M4SH_REG_FUSION_LOCALY] = {M4SH_TYPE_FUSION, 0x14, 4}, [M4SH_REG_FUSION_LOCALZ] = {M4SH_TYPE_FUSION, 0x18, 4}, - [M4SH_REG_FUSION_WORLDX] = {M4SH_TYPE_FUSION, 0x1c, 4}, - [M4SH_REG_FUSION_WORLDY] = {M4SH_TYPE_FUSION, 0x20, 4}, - [M4SH_REG_FUSION_WORLDZ] = {M4SH_TYPE_FUSION, 0x24, 4}, + [M4SH_REG_FUSION_GRAVITYX] = {M4SH_TYPE_FUSION, 0x1c, 4}, + [M4SH_REG_FUSION_GRAVITYY] = {M4SH_TYPE_FUSION, 0x20, 4}, + [M4SH_REG_FUSION_GRAVITYZ] = {M4SH_TYPE_FUSION, 0x24, 4}, [M4SH_REG_FUSION_ROTATIONVECTOR] = {M4SH_TYPE_FUSION, 0x28, 16}, [M4SH_REG_FUSION_HEADING] = {M4SH_TYPE_FUSION, 0x38, 2}, [M4SH_REG_FUSION_HEADING_ACCURACY] = {M4SH_TYPE_FUSION, 0x3a, 1}, @@ -112,6 +112,8 @@ static const struct { [M4SH_REG_METS_METS] = {M4SH_TYPE_METS, 0x4, 4}, [M4SH_REG_METS_CALORIES] = {M4SH_TYPE_METS, 0x8, 4}, [M4SH_REG_METS_HEALTHYMINUTES] = {M4SH_TYPE_METS, 0xc, 4}, + [M4SH_REG_METS_METS_NO_RMR] = {M4SH_TYPE_METS, 0x10, 4}, + [M4SH_REG_METS_CALORIES_NO_RMR] = {M4SH_TYPE_METS, 0x14, 4}, [M4SH_REG_USERSETTINGS_SCREENSTATUS] = {M4SH_TYPE_USERSETTINGS, 0x0, 1}, [M4SH_REG_USERSETTINGS_USERAGE] = {M4SH_TYPE_USERSETTINGS, 0x1, 1}, [M4SH_REG_USERSETTINGS_USERGENDER] = {M4SH_TYPE_USERSETTINGS, 0x2, 1}, @@ -205,7 +207,7 @@ static const unsigned int bank_size_tbl[M4SH_TYPE__NUM] = { [M4SH_TYPE_FUSION] = 59, [M4SH_TYPE_COMPASS] = 17, [M4SH_TYPE_GYRO] = 16, - [M4SH_TYPE_METS] = 16, + [M4SH_TYPE_METS] = 24, [M4SH_TYPE_USERSETTINGS] = 8, [M4SH_TYPE_POWER] = 9, [M4SH_TYPE_LOCATION] = 14, diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 5b96da5fda8..5aed79aa3c4 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -586,6 +586,13 @@ config WAKEUP_SOURCE_NOTIFY help Driver to allow early notification of wakeups +config ALS_WHILE_CHARGING + tristate "use ALS when on charger" + depends on WAKEUP_SOURCE_NOTIFY + help + ALS when unit is on charger + + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 2f57dbac599..39d1e04e896 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -68,3 +68,4 @@ obj-$(CONFIG_MOT_UTAG) += utag/ obj-$(CONFIG_BQ5105X_CTRL) += bq5105x_ctrl.o obj-$(CONFIG_BQ5105X_DETECT) += bq5105x_detect.o obj-$(CONFIG_WAKEUP_SOURCE_NOTIFY) += wakeup_source_notify.o +obj-$(CONFIG_WAKEUP_SOURCE_NOTIFY) += als_notify.o diff --git a/drivers/misc/als_notify.c b/drivers/misc/als_notify.c new file mode 100644 index 00000000000..cbb4f3a55cd --- /dev/null +++ b/drivers/misc/als_notify.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2014 Motorola Mobility LLC. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/notifier.h> +#include <linux/als_notify.h> + +static BLOCKING_NOTIFIER_HEAD(als_notifier_list); + +/** + * als_register_notify - register a notifier callback for triggering display init + * @nb: pointer to the notifier block for the callback events. + * + */ +void als_register_notify(struct notifier_block *nb) +{ + blocking_notifier_chain_register(&als_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(als_register_notify); + +/** + * als_unregister_notify - unregister a notifier callback + * @nb: pointer to the notifier block for the callback events. + * + * als_register_notify() must have been previously called + * for this function to work properly. + */ +void als_unregister_notify(struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(&als_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(als_unregister_notify); + +void als_notify_subscriber(unsigned long event) +{ + blocking_notifier_call_chain(&als_notifier_list, event, NULL); +} +EXPORT_SYMBOL_GPL(als_notify_subscriber); diff --git a/drivers/misc/c55_ctrl.c b/drivers/misc/c55_ctrl.c index 54ffc6e5b2b..cf763272922 100644 --- a/drivers/misc/c55_ctrl.c +++ b/drivers/misc/c55_ctrl.c @@ -143,7 +143,6 @@ static ssize_t c55_ctrl_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct c55_ctrl_data *cdata = dev_get_drvdata(dev); - struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata(); int mode; if (kstrtoint(buf, 10, &mode) < 0) @@ -154,7 +153,7 @@ static ssize_t c55_ctrl_enable(struct device *dev, return -EINVAL; } - if (m4sensorhub->mode != NORMALMODE) { + if (m4sensorhub_get_current_mode() != NORMALMODE) { dev_err(dev, "M4 not ready, Unable to set screen status\n"); return -EINVAL; } @@ -169,9 +168,7 @@ static ssize_t c55_ctrl_enable(struct device *dev, gpio_set_value(cdata->ap_c55_int_gpio, 1); - if (m4sensorhub_reg_write_1byte - (m4sensorhub, M4SH_REG_USERSETTINGS_AUDIOSTATUS, - AUDIO_STATUS_ON, 0xFF) != 1) { + if (m4sensorhub_extern_set_audio_status(AUDIO_STATUS_ON) < 0) { dev_err(dev, "Unable to set screen status to 0x01\n"); mutex_unlock(&cdata->ctrl_mutex); return -EINVAL; @@ -189,9 +186,7 @@ static ssize_t c55_ctrl_enable(struct device *dev, cdata->c55_ap_int_enabled = 0; } - if (m4sensorhub_reg_write_1byte - (m4sensorhub, M4SH_REG_USERSETTINGS_AUDIOSTATUS, - AUDIO_STATUS_OFF, 0xFF) != 1) { + if (m4sensorhub_extern_set_audio_status(AUDIO_STATUS_OFF) < 0) { dev_err(dev, "Unable to set screen status to 0x00\n"); mutex_unlock(&cdata->ctrl_mutex); return -EINVAL; @@ -363,7 +358,6 @@ static int c55_ctrl_remove(struct platform_device *pdev) static int c55_ctrl_suspend(struct platform_device *dev, pm_message_t state) { struct c55_ctrl_data *cdata = dev_get_drvdata(&dev->dev); - struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata(); if (cdata->c55_mode != C55_OFF) { dev_warn(&dev->dev, "C55 still ON when going into suspend\n"); @@ -375,9 +369,7 @@ static int c55_ctrl_suspend(struct platform_device *dev, pm_message_t state) cdata->c55_ap_int_enabled = 0; } - if (m4sensorhub_reg_write_1byte - (m4sensorhub, M4SH_REG_USERSETTINGS_AUDIOSTATUS, - AUDIO_STATUS_OFF, 0xFF) != 1) { + if (m4sensorhub_extern_set_audio_status(AUDIO_STATUS_OFF) < 0) { dev_err(&dev->dev, "Unable to set screen status to 0x00\n"); } diff --git a/drivers/misc/m4sensorhub_als.c b/drivers/misc/m4sensorhub_als.c index 3f61edefff2..6445d9f0181 100644 --- a/drivers/misc/m4sensorhub_als.c +++ b/drivers/misc/m4sensorhub_als.c @@ -28,12 +28,21 @@ #include <linux/m4sensorhub.h> #include <linux/input.h> #include <linux/slab.h> +#include <linux/delay.h> +#ifdef CONFIG_WAKEUP_SOURCE_NOTIFY +#include <linux/notifier.h> +#include <linux/als_notify.h> +#ifdef CONFIG_ALS_WHILE_CHARGING +#include <linux/wakeup_source_notify.h> +#endif /* CONFIG_ALS_WHILE_CHARGING */ +#endif #define m4als_err(format, args...) KDEBUG(M4SH_ERROR, format, ## args) #define M4ALS_DRIVER_NAME "m4sensorhub_als" #define M4ALS_IRQ_ENABLED_BIT 0 +#define ALS_SAMPLERATE_WHILE_DOCKED 10 /* in seconds */ struct m4als_driver_data { struct platform_device *pdev; @@ -42,13 +51,39 @@ struct m4als_driver_data { struct input_dev *indev; struct delayed_work m4als_work; + /* Beware of changing this from uint16, check als_notify.h + since notifier uses values outside luminosity range for + conveying enable/disable status */ uint16_t luminosity; int16_t samplerate; int16_t latest_samplerate; int16_t fastest_rate; uint16_t status; +#ifdef CONFIG_ALS_WHILE_CHARGING + struct notifier_block charger_nb; + bool chargerstatus; +#endif }; +#ifdef CONFIG_ALS_WHILE_CHARGING +static int charger_notify(struct notifier_block *self, + unsigned long action, void *dev) +{ + struct m4als_driver_data *dd = + container_of(self, struct m4als_driver_data, charger_nb); + switch (action) { + case DISPLAY_WAKE_EVENT_DOCKON: + case DISPLAY_WAKE_EVENT_DOCKOFF: + dd->chargerstatus = (action == DISPLAY_WAKE_EVENT_DOCKON); + pr_info("%s: dd->chargerstatus is %d\n", + __func__, dd->chargerstatus); + break; + } + + return NOTIFY_OK; +} +#endif + static void m4als_work_func(struct work_struct *work) { int err = 0; @@ -81,8 +116,18 @@ static void m4als_work_func(struct work_struct *work) dd->luminosity = luminosity; - input_event(dd->indev, EV_MSC, MSC_RAW, dd->luminosity); - input_sync(dd->indev); +#ifdef CONFIG_ALS_WHILE_CHARGING + if (dd->chargerstatus == true) { + als_notify_subscriber(luminosity); + } else { + input_event(dd->indev, EV_MSC, MSC_RAW, dd->luminosity); + input_sync(dd->indev); + } +#else + input_event(dd->indev, EV_MSC, MSC_RAW, dd->luminosity); + input_sync(dd->indev); +#endif + if (dd->samplerate > 0) queue_delayed_work(system_freezable_wq, &(dd->m4als_work), msecs_to_jiffies(dd->samplerate)); @@ -101,6 +146,12 @@ static int m4als_set_samplerate(struct m4als_driver_data *dd, int16_t rate) int err = 0; int size = 0; +#ifdef CONFIG_ALS_WHILE_CHARGING + if (rate == -1 && dd->chargerstatus == true) { + rate = ALS_SAMPLERATE_WHILE_DOCKED * 1000; + } +#endif + if ((rate >= 0) && (rate <= dd->fastest_rate)) rate = dd->fastest_rate; @@ -132,9 +183,17 @@ static int m4als_set_samplerate(struct m4als_driver_data *dd, int16_t rate) } cancel_delayed_work(&(dd->m4als_work)); dd->samplerate = rate; - if (dd->samplerate > 0) + if (dd->samplerate > 0) { queue_delayed_work(system_freezable_wq, &(dd->m4als_work), msecs_to_jiffies(rate)); +#ifdef CONFIG_WAKEUP_SOURCE_NOTIFY + als_notify_subscriber(ALS_ENABLED); +#endif + } else { +#ifdef CONFIG_WAKEUP_SOURCE_NOTIFY + als_notify_subscriber(ALS_DISABLED); +#endif + } m4als_set_samplerate_fail: return err; @@ -153,6 +212,8 @@ static ssize_t m4als_setrate_store(struct device *dev, int err = 0; struct m4als_driver_data *dd = dev_get_drvdata(dev); int value = 0; + int regsize = 0; + uint16_t luminosity = 0; mutex_lock(&(dd->mutex)); @@ -175,6 +236,32 @@ static ssize_t m4als_setrate_store(struct device *dev, goto m4als_enable_store_exit; } + /* Read and send raw value for gesture wakeup */ + msleep(120); + regsize = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_LIGHTSENSOR_SIGNAL); + if (regsize < 0) { + m4als_err("%s: Reading from invalid register %d.\n", + __func__, regsize); + err = regsize; + goto m4als_enable_store_exit; + } + + err = m4sensorhub_reg_read(dd->m4, M4SH_REG_LIGHTSENSOR_SIGNAL, + (char *)&luminosity); + if (err < 0) { + m4als_err("%s: Failed to read luminosity data.\n", __func__); + goto m4als_enable_store_exit; + } else if (err != regsize) { + m4als_err("%s: Read %d bytes instead of %d.\n", + __func__, err, regsize); + goto m4als_enable_store_exit; + } + + dd->luminosity = luminosity; + + input_event(dd->indev, EV_MSC, MSC_RAW, dd->luminosity); + input_sync(dd->indev); + m4als_enable_store_exit: if (err < 0) m4als_err("%s: Failed with error code %d.\n", __func__, err); @@ -304,12 +391,6 @@ static int m4als_driver_init(struct init_calldata *p_arg) goto m4als_driver_init_fail; } - err = m4als_create_sysfs(dd); - if (err < 0) { - m4als_err("%s: Failed to create sysfs.\n", __func__); - goto m4als_driver_init_sysfs_fail; - } - INIT_DELAYED_WORK(&(dd->m4als_work), m4als_work_func); err = m4sensorhub_panic_register(dd->m4, PANICHDL_ALS_RESTORE, @@ -318,8 +399,6 @@ static int m4als_driver_init(struct init_calldata *p_arg) KDEBUG(M4SH_ERROR, "Als panic callback register failed\n"); goto m4als_driver_init_exit; -m4als_driver_init_sysfs_fail: - input_unregister_device(dd->indev); m4als_driver_init_fail: m4als_err("%s: Init failed with error code %d.\n", __func__, err); m4als_driver_init_exit: @@ -359,8 +438,22 @@ static int m4als_probe(struct platform_device *pdev) goto m4als_probe_fail; } + err = m4als_create_sysfs(dd); + if (err < 0) { + m4als_err("%s: Failed to create sysfs.\n", __func__); + goto m4als_driver_init_sysfs_fail; + } + +#ifdef CONFIG_ALS_WHILE_CHARGING + dd->chargerstatus = false; + dd->charger_nb.notifier_call = charger_notify; + wakeup_source_register_notify(&dd->charger_nb); +#endif + return 0; +m4als_driver_init_sysfs_fail: + m4sensorhub_unregister_initcall(m4als_driver_init); m4als_probe_fail: mutex_destroy(&(dd->mutex)); kfree(dd); @@ -377,6 +470,9 @@ static int __exit m4als_remove(struct platform_device *pdev) cancel_delayed_work(&(dd->m4als_work)); m4als_remove_sysfs(dd); m4sensorhub_unregister_initcall(m4als_driver_init); +#ifdef CONFIG_ALS_WHILE_CHARGING + wakeup_source_unregister_notify(&dd->charger_nb); +#endif if (dd->indev != NULL) input_unregister_device(dd->indev); mutex_destroy(&(dd->mutex)); diff --git a/drivers/misc/m4sensorhub_fusion.c b/drivers/misc/m4sensorhub_fusion.c index e1330f56263..816b89bea39 100644 --- a/drivers/misc/m4sensorhub_fusion.c +++ b/drivers/misc/m4sensorhub_fusion.c @@ -126,6 +126,90 @@ static void m4fus_work_func(struct work_struct *work) dd->iiodat[1].type = FUSION_TYPE_ORIENTATION; dd->iiodat[1].timestamp = iio_get_time_ns(); + size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_FUSION_GRAVITYX); + err = m4sensorhub_reg_read(dd->m4, M4SH_REG_FUSION_GRAVITYX, + (char *)&(dd->iiodat[2].values[0])); + if (err < 0) { + m4fus_err("%s: Failed to read gravityX data.\n", __func__); + goto m4fus_isr_fail; + } else if (err != size) { + m4fus_err("%s: Read %d bytes instead of %d for %s.\n", + __func__, err, size, "gravityX"); + err = -EBADE; + goto m4fus_isr_fail; + } + + size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_FUSION_GRAVITYY); + err = m4sensorhub_reg_read(dd->m4, M4SH_REG_FUSION_GRAVITYY, + (char *)&(dd->iiodat[2].values[1])); + if (err < 0) { + m4fus_err("%s: Failed to read gravityY data.\n", __func__); + goto m4fus_isr_fail; + } else if (err != size) { + m4fus_err("%s: Read %d bytes instead of %d for %s.\n", + __func__, err, size, "gravityY"); + err = -EBADE; + goto m4fus_isr_fail; + } + + size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_FUSION_GRAVITYZ); + err = m4sensorhub_reg_read(dd->m4, M4SH_REG_FUSION_GRAVITYZ, + (char *)&(dd->iiodat[2].values[2])); + if (err < 0) { + m4fus_err("%s: Failed to read gravityZ data.\n", __func__); + goto m4fus_isr_fail; + } else if (err != size) { + m4fus_err("%s: Read %d bytes instead of %d for %s.\n", + __func__, err, size, "gravityZ"); + err = -EBADE; + goto m4fus_isr_fail; + } + + dd->iiodat[2].type = FUSION_TYPE_GRAVITY; + dd->iiodat[2].timestamp = iio_get_time_ns(); + + size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_FUSION_LOCALX); + err = m4sensorhub_reg_read(dd->m4, M4SH_REG_FUSION_LOCALX, + (char *)&(dd->iiodat[3].values[0])); + if (err < 0) { + m4fus_err("%s: Failed to read localX data.\n", __func__); + goto m4fus_isr_fail; + } else if (err != size) { + m4fus_err("%s: Read %d bytes instead of %d for %s.\n", + __func__, err, size, "localX"); + err = -EBADE; + goto m4fus_isr_fail; + } + + size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_FUSION_LOCALY); + err = m4sensorhub_reg_read(dd->m4, M4SH_REG_FUSION_LOCALY, + (char *)&(dd->iiodat[3].values[1])); + if (err < 0) { + m4fus_err("%s: Failed to read localY data.\n", __func__); + goto m4fus_isr_fail; + } else if (err != size) { + m4fus_err("%s: Read %d bytes instead of %d for %s.\n", + __func__, err, size, "localY"); + err = -EBADE; + goto m4fus_isr_fail; + } + + size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_FUSION_LOCALZ); + err = m4sensorhub_reg_read(dd->m4, M4SH_REG_FUSION_LOCALZ, + (char *)&(dd->iiodat[3].values[2])); + if (err < 0) { + m4fus_err("%s: Failed to read localZ data.\n", __func__); + goto m4fus_isr_fail; + } else if (err != size) { + m4fus_err("%s: Read %d bytes instead of %d for %s.\n", + __func__, err, size, "localZ"); + err = -EBADE; + goto m4fus_isr_fail; + } + + dd->iiodat[3].type = FUSION_TYPE_LINEAR_ACCELERATION; + dd->iiodat[3].timestamp = iio_get_time_ns(); + /* * For some reason, IIO knows we are sending an array, * so all FUSION_TYPE_* indicies will be sent diff --git a/drivers/misc/m4sensorhub_gesture.c b/drivers/misc/m4sensorhub_gesture.c index d5e364ea6c5..9ccbbe3091e 100644 --- a/drivers/misc/m4sensorhub_gesture.c +++ b/drivers/misc/m4sensorhub_gesture.c @@ -114,7 +114,7 @@ static void m4ges_isr(enum m4sensorhub_irqs int_event, void *handle) /* the GESTURE_VIEW is only effect for kernel now * do not send gesture to android */ - goto m4ges_isr_fail; + goto m4ges_no_iio_push; } #endif /* CONFIG_WAKEUP_SOURCE_NOTIFY */ @@ -122,6 +122,12 @@ static void m4ges_isr(enum m4sensorhub_irqs int_event, void *handle) iio_push_to_buffers(iio, (unsigned char *)&(dd->iiodat)); dd->gesture_count++; +m4ges_no_iio_push: + /* Log gestures received */ + pr_info("%s: Gesture received: count=%u, type=%hhu, value=%hhd.\n", + __func__, dd->gesture_count, dd->iiodat.gesture_type, + dd->iiodat.gesture_value); + m4ges_isr_fail: if (err < 0) m4ges_err("%s: Failed with error code %d.\n", __func__, err); diff --git a/drivers/misc/m4sensorhub_mpu9150.c b/drivers/misc/m4sensorhub_mpu9150.c index a2411c27d4f..55d160bc607 100644 --- a/drivers/misc/m4sensorhub_mpu9150.c +++ b/drivers/misc/m4sensorhub_mpu9150.c @@ -79,8 +79,6 @@ struct mpu9150_client { struct mutex mutex; /* prevent concurrent thread access */ struct delayed_work mpu9150_work[NUM_TYPES]; signed short fastest_rate[NUM_TYPES]; - int calibration_done; - int app_override; }; struct mpu9150_client *misc_mpu9150_data; @@ -142,23 +140,6 @@ static void m4_report_mpu9150_inputevent( } } -static void m4_queue_delayed_work(struct mpu9150_client *dd, - int delay, enum mpu9150_sensor type) -{ - if (type == TYPE_COMPASS) { - /* initial calibration is not done and there is - no app requesting compass data */ - if ((!dd->calibration_done) && (!dd->app_override)) - /* For current drain saving, m4 is sampling - compass at 40ms while omap is polling - for compass data at 15 secs */ - delay = 15000; - } - queue_delayed_work(system_freezable_wq, - &(dd->mpu9150_work[type]), - msecs_to_jiffies(delay)); -} - static void m4_set_mpu9150_delay(struct mpu9150_client *mpu9150_client_data, int delay, enum mpu9150_sensor type) { @@ -197,18 +178,19 @@ static void m4_set_mpu9150_delay(struct mpu9150_client *mpu9150_client_data, cancel_delayed_work(&(dd->mpu9150_work[type])); dd->samplerate[type] = delay; if (dd->samplerate[type] > 0) - m4_queue_delayed_work(dd, delay, type); + queue_delayed_work(system_freezable_wq, + &(dd->mpu9150_work[type]), + msecs_to_jiffies(delay)); + } } - static void m4_read_mpu9150_data(struct mpu9150_client *mpu9150_client_data, enum mpu9150_sensor type) { sCompassData compassdata; sAccelData acceldata; sGyroData gyrodata; - struct mpu9150_client *dd = mpu9150_client_data; switch (type) { case TYPE_GYRO: @@ -249,18 +231,6 @@ static void m4_read_mpu9150_data(struct mpu9150_client *mpu9150_client_data, mpu9150_client_data->compass_data.cz = compassdata.z; mpu9150_client_data->compass_data.ca = compassdata.accuracy; - /* Check if calibration is complete */ - if ((!(dd->calibration_done)) && (compassdata.accuracy)) { - dd->calibration_done = 1; - KDEBUG(M4SH_INFO, "Calibration complete\n"); - /* Stop compass sampling if no app is using the data */ - if (dd->app_override == 0) { - m4_set_mpu9150_delay(dd, - -1, - TYPE_COMPASS); - KDEBUG(M4SH_INFO, "Init cal done. Turning off compass"); - } - } break; default: @@ -320,7 +290,9 @@ static void m4compass_work_func(struct work_struct *work) m4_report_mpu9150_inputevent(dd, TYPE_COMPASS); rate = dd->samplerate[TYPE_COMPASS]; if (rate > 0) - m4_queue_delayed_work(dd, rate, TYPE_COMPASS); + queue_delayed_work(system_freezable_wq, + &(dd->mpu9150_work[TYPE_COMPASS]), + msecs_to_jiffies(rate)); mutex_unlock(&(dd->mutex)); } @@ -407,20 +379,8 @@ static ssize_t m4_mpu9150_write_compass_setdelay(struct device *dev, mutex_lock(&(misc_mpu9150_data->mutex)); - if (misc_mpu9150_data->calibration_done == 0) { - /* If calibration is not complete and app tries to - turn off ignore */ - if (scanresult < 0) { - misc_mpu9150_data->app_override = 0; - goto compass_setdelay_exit; - } else { - misc_mpu9150_data->app_override = 1; - } - } m4_set_mpu9150_delay(misc_mpu9150_data, scanresult, TYPE_COMPASS); -compass_setdelay_exit: - mutex_unlock(&(misc_mpu9150_data->mutex)); return count; @@ -611,7 +571,9 @@ static void mpu9150_panic_restore(struct m4sensorhub_data *m4sensorhub, m4_set_mpu9150_delay(dd, rate, type); cancel_delayed_work(&(dd->mpu9150_work[type])); if (rate > 0) - m4_queue_delayed_work(dd, rate, type); + queue_delayed_work(system_freezable_wq, + &(dd->mpu9150_work[type]), + msecs_to_jiffies(rate)); } mutex_unlock(&(dd->mutex)); } @@ -633,9 +595,7 @@ static int mpu9150_driver_init(struct init_calldata *p_arg) dd); if (ret < 0) KDEBUG(M4SH_ERROR, "HR panic callback register failed\n"); - m4_set_mpu9150_delay(dd, - 40, - TYPE_COMPASS); + mutex_unlock(&(dd->mutex)); return ret; } @@ -668,8 +628,6 @@ static int mpu9150_client_probe(struct platform_device *pdev) mpu9150_client_data->fastest_rate[TYPE_ACCEL] = 40; mpu9150_client_data->fastest_rate[TYPE_GYRO] = 40; mpu9150_client_data->fastest_rate[TYPE_COMPASS] = 40; - mpu9150_client_data->calibration_done = 0; - mpu9150_client_data->app_override = 0; mpu9150_client_data->input_dev = input_allocate_device(); if (!mpu9150_client_data->input_dev) { diff --git a/drivers/misc/m4sensorhub_pedometer.c b/drivers/misc/m4sensorhub_pedometer.c index cf326454bea..0be14698e1d 100644 --- a/drivers/misc/m4sensorhub_pedometer.c +++ b/drivers/misc/m4sensorhub_pedometer.c @@ -40,6 +40,8 @@ #define M4PED_IRQ_ENABLED_BIT 0 #define M4PED_FEATURE_ENABLED_BIT 1 +#define M4PED_USERDATA_SIZE 5 + struct m4ped_driver_data { struct platform_device *pdev; struct m4sensorhub_data *m4; @@ -47,9 +49,12 @@ struct m4ped_driver_data { struct m4sensorhub_pedometer_iio_data iiodat; struct m4sensorhub_pedometer_iio_data base_dat; - struct delayed_work m4ped_work; + struct m4sensorhub_pedometer_iio_data last_dat; + struct delayed_work m4ped_work; + int16_t samplerate; int16_t fastest_rate; + uint8_t userdata[M4PED_USERDATA_SIZE]; uint16_t status; }; @@ -57,6 +62,7 @@ static int m4ped_read_report_data(struct iio_dev *iio, struct m4ped_driver_data *dd) { int err = 0, size = 0; + struct m4sensorhub_pedometer_iio_data dat; /*input validations */ if ((iio == NULL) || (dd == NULL)) { @@ -80,7 +86,7 @@ static int m4ped_read_report_data(struct iio_dev *iio, size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_PEDOMETER_TOTALDISTANCE); err = m4sensorhub_reg_read(dd->m4, M4SH_REG_PEDOMETER_TOTALDISTANCE, - (char *)&(dd->iiodat.total_distance)); + (char *)&(dat.total_distance)); if (err < 0) { m4ped_err("%s: Failed to read total_distance data.\n", __func__); @@ -94,7 +100,7 @@ static int m4ped_read_report_data(struct iio_dev *iio, size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_PEDOMETER_TOTALSTEPS); err = m4sensorhub_reg_read(dd->m4, M4SH_REG_PEDOMETER_TOTALSTEPS, - (char *)&(dd->iiodat.total_steps)); + (char *)&(dat.total_steps)); if (err < 0) { m4ped_err("%s: Failed to read total_steps data.\n", __func__); goto m4ped_read_fail; @@ -121,7 +127,7 @@ static int m4ped_read_report_data(struct iio_dev *iio, size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_METS_HEALTHYMINUTES); err = m4sensorhub_reg_read(dd->m4, M4SH_REG_METS_HEALTHYMINUTES, - (char *)&(dd->iiodat.healthy_minutes)); + (char *)&(dat.healthy_minutes)); if (err < 0) { m4ped_err("%s: Failed to read healthy_minutes data.\n", __func__); @@ -135,7 +141,7 @@ static int m4ped_read_report_data(struct iio_dev *iio, size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_METS_CALORIES); err = m4sensorhub_reg_read(dd->m4, M4SH_REG_METS_CALORIES, - (char *)&(dd->iiodat.calories)); + (char *)&(dat.calories)); if (err < 0) { m4ped_err("%s: Failed to read calories data.\n", __func__); goto m4ped_read_fail; @@ -146,12 +152,63 @@ static int m4ped_read_report_data(struct iio_dev *iio, goto m4ped_read_fail; } + size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_METS_CALORIES_NO_RMR); + err = m4sensorhub_reg_read(dd->m4, M4SH_REG_METS_CALORIES_NO_RMR, + (char *)&(dat.calories_normr)); + if (err < 0) { + m4ped_err("%s: Failed to read calories_normr data.\n", + __func__); + goto m4ped_read_fail; + } else if (err != size) { + m4ped_err("%s: Read %d bytes instead of %d for %s.\n", + __func__, err, size, "calories_normr"); + err = -EBADE; + goto m4ped_read_fail; + } + dd->iiodat.timestamp = iio_get_time_ns(); - dd->iiodat.total_distance += dd->base_dat.total_distance; - dd->iiodat.total_steps += dd->base_dat.total_steps; - dd->iiodat.healthy_minutes += dd->base_dat.healthy_minutes; - dd->iiodat.calories += dd->base_dat.calories; + /* Save data if these values decrease (they monotonically increase) */ + if ((dat.total_distance < dd->last_dat.total_distance) || + (dat.total_steps < dd->last_dat.total_steps) || + (dat.healthy_minutes < dd->last_dat.healthy_minutes) || + (dat.calories < dd->last_dat.calories) || + (dat.calories_normr < dd->last_dat.calories_normr)) { + m4ped_err("%s: Error: Current = %u %u %u %u %u " + "Last = %u %u %u %u %u, Base = %u %u %u %u %u\n", + __func__, dat.total_distance, + dat.total_steps, dat.healthy_minutes, + dat.calories, dat.calories_normr, + dd->last_dat.total_distance, + dd->last_dat.total_steps, + dd->last_dat.healthy_minutes, dd->last_dat.calories, + dd->last_dat.calories_normr, + dd->base_dat.total_distance, + dd->base_dat.total_steps, + dd->base_dat.healthy_minutes, dd->base_dat.calories, + dd->base_dat.calories_normr); + m4ped_err("%s: iio = %u %u %u %u %u\n", __func__, + dd->iiodat.total_distance, + dd->iiodat.total_steps, + dd->iiodat.healthy_minutes, dd->iiodat.calories, + dd->iiodat.calories_normr); + goto m4ped_read_fail; + } + + dd->last_dat.total_distance = dat.total_distance; + dd->last_dat.total_steps = dat.total_steps; + dd->last_dat.healthy_minutes = dat.healthy_minutes; + dd->last_dat.calories = dat.calories; + dd->last_dat.calories_normr = dat.calories_normr; + + dd->iiodat.total_distance = dat.total_distance + + dd->base_dat.total_distance; + dd->iiodat.total_steps = dat.total_steps + dd->base_dat.total_steps; + dd->iiodat.healthy_minutes = dat.healthy_minutes + + dd->base_dat.healthy_minutes; + dd->iiodat.calories = dat.calories + dd->base_dat.calories; + dd->iiodat.calories_normr = dat.calories_normr + + dd->base_dat.calories_normr; iio_push_to_buffers(iio, (unsigned char *)&(dd->iiodat)); @@ -181,6 +238,42 @@ static void m4ped_work_func(struct work_struct *work) return; } +static int m4ped_write_userdata(struct m4ped_driver_data *dd) +{ + int err; + + err = m4sensorhub_reg_write(dd->m4, M4SH_REG_USERSETTINGS_USERAGE, + &(dd->userdata[0]), m4sh_no_mask); + if (err < 0) { + m4ped_err("%s: Failed to write age.\n", __func__); + goto m4ped_write_userdata_fail; + } + + err = m4sensorhub_reg_write(dd->m4, M4SH_REG_USERSETTINGS_USERGENDER, + &(dd->userdata[1]), m4sh_no_mask); + if (err < 0) { + m4ped_err("%s: Failed to write gender.\n", __func__); + goto m4ped_write_userdata_fail; + } + + err = m4sensorhub_reg_write(dd->m4, M4SH_REG_USERSETTINGS_USERHEIGHT, + &(dd->userdata[2]), m4sh_no_mask); + if (err < 0) { + m4ped_err("%s: Failed to write height.\n", __func__); + goto m4ped_write_userdata_fail; + } + + err = m4sensorhub_reg_write(dd->m4, M4SH_REG_USERSETTINGS_USERWEIGHT, + &(dd->userdata[3]), m4sh_no_mask); + if (err < 0) { + m4ped_err("%s: Failed to write weight.\n", __func__); + goto m4ped_write_userdata_fail; + } + +m4ped_write_userdata_fail: + return err; +} + static int m4ped_set_samplerate(struct iio_dev *iio, int16_t rate) { int err = 0; @@ -278,13 +371,14 @@ static ssize_t m4ped_iiodata_show(struct device *dev, mutex_lock(&(dd->mutex)); size = snprintf(buf, PAGE_SIZE, - "%s%hhu\n%s%u\n%s%u\n%s%hu\n%s%u\n%s%u\n", + "%s%hhu\n%s%u\n%s%u\n%s%hu\n%s%u\n%s%u\n%s%u\n", "ped_activity: ", dd->iiodat.ped_activity, "total_distance: ", dd->iiodat.total_distance, "total_steps: ", dd->iiodat.total_steps, "current_speed: ", dd->iiodat.current_speed, "healthy_minutes: ", dd->iiodat.healthy_minutes, - "calories: ", dd->iiodat.calories); + "calories: ", dd->iiodat.calories, + "calories_normr: ", dd->iiodat.calories_normr); mutex_unlock(&(dd->mutex)); return size; } @@ -298,11 +392,11 @@ static ssize_t m4ped_userdata_show(struct device *dev, struct iio_dev *iio = platform_get_drvdata(pdev); struct m4ped_driver_data *dd = iio_priv(iio); ssize_t size = 0; - uint8_t data[5] = {0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t data[M4PED_USERDATA_SIZE] = {0x00}; mutex_lock(&(dd->mutex)); - err = m4sensorhub_reg_read_n(dd->m4, M4SH_REG_USERSETTINGS_SCREENSTATUS, + err = m4sensorhub_reg_read_n(dd->m4, M4SH_REG_USERSETTINGS_USERAGE, (char *)&(data[0]), ARRAY_SIZE(data)); if (err < 0) { m4ped_err("%s: Failed to read user data.\n", __func__); @@ -315,11 +409,11 @@ static ssize_t m4ped_userdata_show(struct device *dev, } size = snprintf(buf, PAGE_SIZE, - "%s%s\n%s%hhu\n%s%hhu\n%s%hhu\n", - "Gender (M/F): ", data[2] ? "M" : "F", - "Age (yrs): ", data[1], - "Height (cm): ", data[3], - "Weight (kg): ", data[4]); + "%s%s\n%s%hhu\n%s%hhu\n%s%hu\n", + "Gender (M/F): ", data[1] ? "M" : "F", + "Age (yrs): ", data[0], + "Height (cm): ", data[2], + "Weight (kg): ", data[3] | (data[4] << 8)); m4ped_userdata_show_fail: mutex_unlock(&(dd->mutex)); @@ -334,6 +428,7 @@ m4ped_userdata_show_fail: * Example: * Female, 22, 168cm, 49kg * 0x00,0x16,0xA7,0x31\n + * echo 0x00,0x16,0xA7,0x31 > userdata */ static ssize_t m4ped_userdata_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) @@ -344,7 +439,7 @@ static ssize_t m4ped_userdata_store(struct device *dev, struct m4ped_driver_data *dd = iio_priv(iio); unsigned int value = 0; unsigned char convbuf[5] = {0x00, 0x00, 0x00, 0x00, 0x00}; - unsigned char outbuf[4] = {0x00, 0x00, 0x00, 0x00}; + unsigned char outbuf[M4PED_USERDATA_SIZE] = {0x00}; int i = 0; mutex_lock(&(dd->mutex)); @@ -373,31 +468,16 @@ static ssize_t m4ped_userdata_store(struct device *dev, outbuf[i] = (unsigned char) value; } - err = m4sensorhub_reg_write(dd->m4, M4SH_REG_USERSETTINGS_USERAGE, - &(outbuf[1]), m4sh_no_mask); - if (err < 0) { - m4ped_err("%s: Failed to write user data.\n", __func__); - goto m4ped_userdata_store_fail; - } + for (i = 0; i < M4PED_USERDATA_SIZE; i++) + dd->userdata[i] = outbuf[i]; - err = m4sensorhub_reg_write(dd->m4, M4SH_REG_USERSETTINGS_USERGENDER, - &(outbuf[0]), m4sh_no_mask); - if (err < 0) { - m4ped_err("%s: Failed to write user data.\n", __func__); - goto m4ped_userdata_store_fail; - } - - err = m4sensorhub_reg_write(dd->m4, M4SH_REG_USERSETTINGS_USERHEIGHT, - &(outbuf[2]), m4sh_no_mask); - if (err < 0) { - m4ped_err("%s: Failed to write user data.\n", __func__); - goto m4ped_userdata_store_fail; - } + dd->userdata[0] = outbuf[1]; /* Age */ + dd->userdata[1] = outbuf[0]; /* Gender */ - err = m4sensorhub_reg_write_n(dd->m4, M4SH_REG_USERSETTINGS_USERWEIGHT, - &(outbuf[3]), m4sh_no_mask, 1); + err = m4ped_write_userdata(dd); if (err < 0) { - m4ped_err("%s: Failed to write user data.\n", __func__); + m4ped_err("%s: Failed to write user data (%d).\n", + __func__, err); goto m4ped_userdata_store_fail; } @@ -606,13 +686,12 @@ static void m4ped_panic_restore(struct m4sensorhub_data *m4sensorhub, mutex_lock(&(dd->mutex)); - dd->base_dat.total_distance = dd->iiodat.total_distance; - dd->base_dat.total_steps = dd->iiodat.total_steps; - dd->base_dat.healthy_minutes = dd->iiodat.healthy_minutes; - dd->base_dat.calories = dd->iiodat.calories; - m4ped_err("%s: Pedometer bases after panic = %d %d %d %d", __func__, - dd->base_dat.total_distance, dd->base_dat.total_steps, - dd->base_dat.healthy_minutes, dd->base_dat.calories); + err = m4ped_write_userdata(dd); + if (err < 0) { + m4ped_err("%s: Failed to write user data (%d).\n", + __func__, err); + goto m4ped_panic_restore_fail; + } if (!(dd->status & (1 << M4PED_FEATURE_ENABLED_BIT))) { err = m4sensorhub_reg_write(dd->m4, M4SH_REG_PEDOMETER_ENABLE, @@ -631,6 +710,19 @@ static void m4ped_panic_restore(struct m4sensorhub_data *m4sensorhub, goto m4ped_panic_restore_fail; } } + /* Update base and reset last */ + dd->base_dat.total_distance = dd->iiodat.total_distance; + dd->base_dat.total_steps = dd->iiodat.total_steps; + dd->base_dat.healthy_minutes = dd->iiodat.healthy_minutes; + dd->base_dat.calories = dd->iiodat.calories; + dd->base_dat.calories_normr = dd->iiodat.calories_normr; + + dd->last_dat.total_distance = 0; + dd->last_dat.total_steps = 0; + dd->last_dat.healthy_minutes = 0; + dd->last_dat.calories = 0; + dd->last_dat.calories_normr = 0; + cancel_delayed_work(&(dd->m4ped_work)); if (dd->samplerate > 0) queue_delayed_work(system_freezable_wq, &(dd->m4ped_work), @@ -692,6 +784,12 @@ static int m4ped_probe(struct platform_device *pdev) dd->fastest_rate = 1000; /* in milli secs */ dd->status = dd->status | (1 << M4PED_FEATURE_ENABLED_BIT); + dd->userdata[0] = 0x23; /* Age (35) */ + dd->userdata[1] = 0x01; /* Gender (Male) */ + dd->userdata[2] = 0xB2; /* Height (178cm) */ + dd->userdata[3] = 0x5B; /* Weight (91kg) */ + dd->userdata[4] = 0x00; /* Weight */ + err = m4ped_create_iiodev(iio); /* iio and dd are freed on fail */ if (err < 0) { m4ped_err("%s: Failed to create IIO device.\n", __func__); diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 5d04f741899..60d5c8e01cb 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2311,6 +2311,8 @@ static const struct mmc_fixup blk_fixups[] = MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), MMC_FIXUP("VZL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), + MMC_FIXUP("4FEACB", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), MMC_FIXUP("004G90", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc, MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), diff --git a/drivers/power/avs/Makefile b/drivers/power/avs/Makefile index 9827e6ce4ec..7b8ef7ee817 100644 --- a/drivers/power/avs/Makefile +++ b/drivers/power/avs/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_POWER_AVS_OMAP) += smartreflex.o ifneq ($(CONFIG_POWER_TI_HARDWARE_VOLTAGE_CONTROL),) # OMAP Common -omap-volt-common = omap_vc.o omap_vp.o +omap-volt-common = omap_vc.o omap_vp.o omap_core_dvfs.o # OMAP SoC specific ifneq ($(CONFIG_ARCH_OMAP3),) diff --git a/drivers/power/avs/omap_core_dvfs.c b/drivers/power/avs/omap_core_dvfs.c new file mode 100644 index 00000000000..2f98d534fbb --- /dev/null +++ b/drivers/power/avs/omap_core_dvfs.c @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2014 Motorola Mobility LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/opp.h> +#include <linux/of_device.h> +#include <linux/regulator/consumer.h> +#include <linux/regmap.h> +#include <linux/clk.h> +#include <linux/err.h> + +#define DRIVER_NAME "omap-core-dvfs" + + +struct omap_core_dvfs_map { + unsigned long cpu_freq; + unsigned long core_freq; +}; + +struct omap_core_dvfs_data { + struct device *dev; + struct clk *l3_clock; + struct clk *dpll_clock; + struct regulator *reg; + unsigned int volt_tolerance; + long curr_freq; + struct omap_core_dvfs_map *map; +}; + +static struct omap_core_dvfs_data *core_dvfs_data; + +static const struct of_device_id omap_core_dvfs_match_tbl[] = { + {.compatible = "ti,omap-core-dvfs"}, + {}, +}; +MODULE_DEVICE_TABLE(of, omap_core_dvfs_match_tbl); +static int get_match_freq(unsigned long cpu_freq, unsigned long *core_freq) +{ + struct omap_core_dvfs_map *map = core_dvfs_data->map; + while (map->core_freq && map->cpu_freq) { + if (map->cpu_freq == cpu_freq) { + *core_freq = map->core_freq; + return 0; + } + map++; + } + return -ENODATA; +} +static int cpufreq_trans(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct cpufreq_freqs *freqs = (struct cpufreq_freqs *)data; + struct omap_core_dvfs_data *pdata = core_dvfs_data; + int ret = 0; + long d, new_freq, dpll_freq, old_freq; + struct opp *opp; + unsigned long volt = 0, volt_old = 0, tol = 0; + + if (IS_ERR_OR_NULL(freqs)) + return -ENODATA; + + if (val != CPUFREQ_PRECHANGE || freqs->new == freqs->old) + return 0; + + ret = get_match_freq(freqs->new * 1000, &new_freq); + if (ret) { + pr_err( + "Could not find cpu freq %u in core map\n", freqs->new * 1000); + goto f_out; + } + if (pdata->curr_freq == new_freq) + return 0; + + /* calculate target frequency to be set in dpll */ + old_freq = clk_get_rate(pdata->l3_clock); + d = clk_get_rate(pdata->dpll_clock) / old_freq; + dpll_freq = clk_round_rate(pdata->dpll_clock, new_freq * d); + if (dpll_freq < 0) + dpll_freq = new_freq * d; + + rcu_read_lock(); + opp = opp_find_freq_ceil(pdata->dev, &new_freq); + if (IS_ERR(opp)) { + rcu_read_unlock(); + pr_err("failed to find core OPP for %ld\n", new_freq); + ret = PTR_ERR(opp); + goto f_out; + } + volt = opp_get_voltage(opp); + rcu_read_unlock(); + + tol = volt * pdata->volt_tolerance / 100; + volt_old = regulator_get_voltage(pdata->reg); + + pr_debug("L3 DVFS %ld MHz, %ld mV --> %ld MHz, %ld mV\n", + old_freq / 1000000, volt_old ? volt_old / 1000 : -1, + dpll_freq / d / 1000000, volt ? volt / 1000 : -1); + + if (freqs->new > freqs->old) { + ret = regulator_set_voltage_tol(pdata->reg, volt, tol); + if (ret) { + pr_err("failed to scale core voltage up: %d\n", ret); + goto f_out; + } + } + ret = clk_set_rate(pdata->dpll_clock, dpll_freq); + if (ret) { + pr_err("failed to set core clock rate: %d\n", ret); + regulator_set_voltage_tol(pdata->reg, volt_old, tol); + goto f_out; + } + if (freqs->new < freqs->old) { + ret = regulator_set_voltage_tol(pdata->reg, volt, tol); + if (ret) { + pr_err("failed to scale voltage down: %d\n", ret); + clk_set_rate(pdata->dpll_clock, old_freq * d); + goto f_out; + } + } + pdata->curr_freq = new_freq; +f_out: + return ret; +} + +static struct notifier_block cpufreq_trans_block = { + .notifier_call = cpufreq_trans +}; + +static int of_init_opp_map(struct device *dev, struct omap_core_dvfs_map **map) +{ + const struct property *prop; + const __be32 *val; + struct omap_core_dvfs_map *m; + int nr, i; + + prop = of_find_property(dev->of_node, "map", NULL); + if (!prop) + return -ENODEV; + if (!prop->value) + return -ENODATA; + + /* + * Each entry is a set of tuples consisting of freq-kHz from + * OPP CPU list and index of matching OPP in core list. + */ + nr = prop->length / sizeof(u32); + if (nr % 2) { + dev_err(dev, "Invalid map\n"); + return -EINVAL; + } + + m = devm_kzalloc(dev, prop->length, GFP_KERNEL); + if (!m) { + dev_err(dev, "Unable to create new map\n"); + return -ENOMEM; + } + *map = m; + val = prop->value; + for (i = 0; i < nr; i += 2, m++) { + m->cpu_freq = be32_to_cpup(val++) * 1000; + m->core_freq = be32_to_cpup(val++) * 1000; + } + m->cpu_freq = 0; + m->core_freq = 0; + + return 0; +} + +static int omap_core_dvfs_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *nd = dev->of_node; + int ret; + struct omap_core_dvfs_data *data; + const char *pname, *str; + struct regulator *reg; + + if (!nd) { + dev_err(dev, "no OF information?\n"); + return -EINVAL; + } + + reg = devm_regulator_get(dev, "core_dvfs"); + if (IS_ERR(reg)) { + dev_err(dev, "core_dvfs regulator not ready, retry\n"); + return -EPROBE_DEFER; + } + + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) { + dev_err(dev, "Unable to allocate data\n"); + return -ENOMEM; + } + + ret = of_init_opp_table(dev); + if (ret) { + dev_err(dev, "Failed to init OPP table: %d\n", ret); + goto fail; + } + + ret = of_init_opp_map(dev, &data->map); + if (ret) { + dev_err(dev, "Failed to init map: %d\n", ret); + goto fail; + } + pname = "l3_clkname"; + ret = of_property_read_string(nd, pname, &str); + if (ret) + goto property_err; + + data->l3_clock = devm_clk_get(dev, str); + if (IS_ERR(data->l3_clock)) { + ret = PTR_ERR(data->l3_clock); + dev_err(dev, "Failed to get %s clock: %d\n", str, ret); + goto fail; + } + + pname = "dpll_clkname"; + ret = of_property_read_string(nd, pname, &str); + if (ret) + goto property_err; + + data->dpll_clock = devm_clk_get(dev, str); + if (IS_ERR(data->dpll_clock)) { + ret = PTR_ERR(data->dpll_clock); + dev_err(dev, "Failed to get %s clock: %d\n", str, ret); + goto fail; + } + + of_property_read_u32(nd, "voltage-tolerance", &data->volt_tolerance); + + ret = cpufreq_register_notifier(&cpufreq_trans_block, + CPUFREQ_TRANSITION_NOTIFIER); + if (ret) { + dev_err(dev, "CPU notifier registration failed with %d\n", ret); + cpufreq_unregister_notifier( + &cpufreq_trans_block, CPUFREQ_TRANSITION_NOTIFIER); + goto fail; + } + data->reg = reg; + data->dev = dev; + core_dvfs_data = data; + platform_set_drvdata(pdev, data); + + return 0; + +property_err: + dev_err(dev, " Missing/Invalid '%s' property\n", pname); + +fail: + return ret; +} + +static struct platform_driver omap_core_dvfs_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(omap_core_dvfs_match_tbl), + }, + .probe = omap_core_dvfs_probe, +}; +static int __init omap_core_dvfs_init(void) +{ + int ret; + ret = platform_driver_register(&omap_core_dvfs_driver); + if (ret) + pr_err("driver register failed for omap_pmic(%d)\n", ret); + return ret; +} +device_initcall_sync(omap_core_dvfs_init); + +static void __exit omap_core_dvfs_exit(void) +{ + platform_driver_unregister(&omap_core_dvfs_driver); +} +module_exit(omap_core_dvfs_exit); + +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_AUTHOR("Motorola Mobility LLC"); +MODULE_DESCRIPTION("OMAP Global PRM driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index 6d0327f120a..c6c70c4afeb 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -64,15 +64,23 @@ CONFIG_TS_BIT_ENBL | CONFIG_SS_BIT_ENBL) #define MODEL_LOCK1 0X0000 #define MODEL_LOCK2 0X0000 -#define dQ_ACC_DIV 0x4 -#define dP_ACC_100 0x1900 -#define dP_ACC_200 0x3200 +#define MAX17042_INIT_NUM_CYCLES 160 +#define MAX17047_INIT_NUM_CYCLES 96 + +#define MAX17042_dQ_ACC_DIV 4 +#define MAX17047_dQ_ACC_DIV 16 + +#define MAX17042_dP_ACC_200 0x3200 +#define MAX17047_dP_ACC_200 0x0C80 #define MAX17042_IC_VERSION 0x0092 #define MAX17047_IC_VERSION 0x00AC /* same for max17050 */ +#define MAX17042_AGE_DIV 256 + #define INIT_DATA_PROPERTY "maxim,regs-init-data" #define CONFIG_NODE "maxim,configuration" +#define VERSION_PROPERTY "version" #define CONFIG_PROPERTY "config" #define FULL_SOC_THRESH_PROPERTY "full_soc_thresh" #define DESIGN_CAP_PROPERTY "design_cap" @@ -119,6 +127,26 @@ struct max17042_chip { int malicious_online; }; +#ifdef CONFIG_OF +const char *get_dts_batt_id(struct device *dev) +{ + int lenp; + const char *retval = NULL; + struct device_node *n = of_find_node_by_path("/chosen"); + + if (n) { + retval = of_get_property(n, "batt-id", &lenp); + if (!retval || !lenp) { + dev_err(dev, "%s: batt-id len %d\n", __func__, lenp); + retval = NULL; + } + of_node_put(n); + } + + return retval; +} +#endif + static int max17042_write_reg(struct i2c_client *client, u8 reg, u16 value) { int ret = i2c_smbus_write_word_data(client, reg, value); @@ -598,8 +626,9 @@ static void max17042_update_capacity_regs(struct max17042_chip *chip) max17042_write_verify_reg(chip->client, MAX17042_FullCAP, config->fullcap); + /* Set DesignCap to fullcapnom here */ max17042_write_reg(chip->client, MAX17042_DesignCap, - config->design_cap); + config->fullcapnom); max17042_write_verify_reg(chip->client, MAX17042_FullCAPNom, config->fullcapnom); } @@ -614,10 +643,21 @@ static void max17042_reset_vfsoc0_reg(struct max17042_chip *chip) max17042_write_reg(chip->client, MAX17042_VFSOC0Enable, VFSOC0_LOCK); } +static void max17042_advance_to_coulomb_counter_mode(struct max17042_chip *chip) +{ + u16 value = (chip->chip_type == MAX17042 ? + MAX17042_INIT_NUM_CYCLES : MAX17047_INIT_NUM_CYCLES); + max17042_write_verify_reg(chip->client, MAX17042_Cycles, value); +} + static void max17042_load_new_capacity_params(struct max17042_chip *chip) { u16 rep_cap, dq_acc, vfSoc; u32 rem_cap; + u16 dQ_ACC_DIV = (chip->chip_type == MAX17042 ? + MAX17042_dQ_ACC_DIV : MAX17047_dQ_ACC_DIV); + u16 dP_ACC_200 = (chip->chip_type == MAX17042 ? + MAX17042_dP_ACC_200 : MAX17047_dP_ACC_200); struct max17042_config_data *config = chip->pdata->config_data; @@ -669,25 +709,14 @@ static inline void max17042_override_por_values(struct max17042_chip *chip) config->soc_alrt_thresh); max17042_override_por(client, MAX17042_CONFIG, config->config); max17042_override_por(client, MAX17042_SHDNTIMER, config->shdntimer); - - max17042_override_por(client, MAX17042_DesignCap, config->design_cap); - max17042_override_por(client, MAX17042_ICHGTerm, config->ichgt_term); - max17042_override_por(client, MAX17042_AtRate, config->at_rate); - max17042_override_por(client, MAX17042_LearnCFG, config->learn_cfg); - max17042_override_por(client, MAX17042_FilterCFG, config->filter_cfg); - max17042_override_por(client, MAX17042_RelaxCFG, config->relax_cfg); max17042_override_por(client, MAX17042_MiscCFG, config->misc_cfg); max17042_override_por(client, MAX17042_MaskSOC, config->masksoc); - max17042_override_por(client, MAX17042_FullCAP, config->fullcap); - max17042_override_por(client, MAX17042_FullCAPNom, config->fullcapnom); if (chip->chip_type == MAX17042) max17042_override_por(client, MAX17042_SOC_empty, config->socempty); max17042_override_por(client, MAX17042_LAvg_empty, config->lavg_empty); - max17042_override_por(client, MAX17042_dQacc, config->dqacc); - max17042_override_por(client, MAX17042_dPacc, config->dpacc); if (chip->chip_type == MAX17042) max17042_override_por(client, MAX17042_V_empty, config->vempty); @@ -696,8 +725,6 @@ static inline void max17042_override_por_values(struct max17042_chip *chip) max17042_override_por(client, MAX17042_TempNom, config->temp_nom); max17042_override_por(client, MAX17042_TempLim, config->temp_lim); max17042_override_por(client, MAX17042_FCTC, config->fctc); - max17042_override_por(client, MAX17042_RCOMP0, config->rcomp0); - max17042_override_por(client, MAX17042_TempCo, config->tcompc0); if (chip->chip_type == MAX17042) { max17042_override_por(client, MAX17042_EmptyTempCo, config->empty_tempco); @@ -748,6 +775,9 @@ static int max17042_init_chip(struct max17042_chip *chip) /* reset vfsoc0 reg */ max17042_reset_vfsoc0_reg(chip); + /* advance to coulomb-counter mode */ + max17042_advance_to_coulomb_counter_mode(chip); + /* load new capacity params */ max17042_load_new_capacity_params(chip); @@ -821,8 +851,13 @@ static void max17042_init_worker(struct work_struct *work) ret = max17042_init_chip(chip); } - if (!ret) + if (!ret) { chip->init_complete = 1; + if (chip->chip_type == MAX17047) { + max17042_write_reg(chip->client, MAX17047_Config_Ver, + chip->pdata->config_data->version); + } + } mutex_unlock(&chip->lock); } @@ -831,11 +866,16 @@ static void max17042_malicious_removed_worker(struct work_struct *work) { struct max17042_chip *chip = container_of(work, struct max17042_chip, work_malicious_removed); + int ret; mutex_lock(&chip->lock); max17042_perform_soft_POR(chip); - max17042_init_chip(chip); + ret = max17042_init_chip(chip); + if (!ret && chip->chip_type == MAX17047) + max17042_write_reg(chip->client, MAX17047_Config_Ver, + chip->pdata->config_data->version); + dev_info(&chip->client->dev, "malicious ps removed, chip re-inited\n"); mutex_unlock(&chip->lock); @@ -949,6 +989,10 @@ static int max17042_cfg_rqrd_prop(struct device *dev, struct device_node *np, struct max17042_config_data *config_data) { + if (of_property_read_u16(np, VERSION_PROPERTY, + &config_data->version)) + return -EINVAL; + if (of_property_read_u16(np, CONFIG_PROPERTY, &config_data->config)) return -EINVAL; @@ -1008,13 +1052,26 @@ static void max17042_cfg_optnl_prop(struct device_node *np, static struct max17042_config_data * max17042_get_config_data(struct device *dev) { + char *config_node = NULL; + char config_node_path[64]; struct max17042_config_data *config_data; struct device_node *np = dev->of_node; if (!np) return NULL; - np = of_get_child_by_name(np, CONFIG_NODE); + config_node = (char *)get_dts_batt_id(dev); + if (config_node) { + snprintf(config_node_path, sizeof(config_node_path), + "%s-%s", CONFIG_NODE, config_node); + config_node = config_node_path; + } else { + config_node = CONFIG_NODE; + } + + dev_info(dev, "using %s profile\n", config_node); + + np = of_get_child_by_name(np, config_node); if (!np) return NULL; @@ -1175,6 +1232,23 @@ static int max17042_debugfs_write_data(void *data, u64 val) DEFINE_SIMPLE_ATTRIBUTE(data_fops, max17042_debugfs_read_data, max17042_debugfs_write_data, "0x%02llx\n"); +static int max17042_debugfs_read_capacity(void *data, u64 *val) +{ + struct max17042_chip *chip = (struct max17042_chip *)data; + *val = chip->debugfs_capacity; + return 0; +} + +static int max17042_debugfs_write_capacity(void *data, u64 val) +{ + struct max17042_chip *chip = (struct max17042_chip *)data; + chip->debugfs_capacity = val; + power_supply_changed(&chip->battery); + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(capacity_fops, max17042_debugfs_read_capacity, + max17042_debugfs_write_capacity, "%llu\n"); + static int max17042_debugfs_create(struct max17042_chip *chip) { chip->debugfs_root = debugfs_create_dir(dev_name(&chip->client->dev), @@ -1191,8 +1265,8 @@ static int max17042_debugfs_create(struct max17042_chip *chip) goto err_debugfs; chip->debugfs_capacity = 0xFF; - if (!debugfs_create_u8("capacity", S_IRUGO | S_IWUSR, - chip->debugfs_root, &chip->debugfs_capacity)) + if (!debugfs_create_file("capacity", S_IRUGO | S_IWUSR, + chip->debugfs_root, chip, &capacity_fops)) goto err_debugfs; return 0; @@ -1226,7 +1300,6 @@ static void max17042_external_power_changed(struct power_supply *psy) } } - static ssize_t max17042_show_alert_threshold(struct device *dev, struct device_attribute *attr, char *buf) @@ -1256,8 +1329,21 @@ static ssize_t max17042_store_alert_threshold(struct device *dev, static DEVICE_ATTR(alert_threshold, S_IRUGO | S_IWUSR, max17042_show_alert_threshold, max17042_store_alert_threshold); + +static ssize_t max17042_show_battery_age(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct max17042_chip *chip = dev_get_drvdata(dev); + int ret = max17042_read_reg(chip->client, MAX17042_Age); + + return ret < 0 ? ret : sprintf(buf, "%u\n", ret / MAX17042_AGE_DIV); +} +static DEVICE_ATTR(battery_age, S_IRUGO, max17042_show_battery_age, NULL); + static struct attribute *max17042_attrs[] = { &dev_attr_alert_threshold.attr, + &dev_attr_battery_age.attr, NULL, }; @@ -1265,6 +1351,20 @@ static struct attribute_group max17042_attr_group = { .attrs = max17042_attrs, }; +static bool max17042_new_config_data(struct max17042_chip *chip) +{ + int ret; + + if (chip->chip_type == MAX17042) + return false; + + ret = max17042_read_reg(chip->client, MAX17047_Config_Ver); + if (ret < 0) + return false; + + return (chip->pdata->config_data->version != ret); +} + static int max17042_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1384,7 +1484,7 @@ static int max17042_probe(struct i2c_client *client, } reg = max17042_read_reg(chip->client, MAX17042_STATUS); - if (reg & STATUS_POR_BIT) { + if (reg & STATUS_POR_BIT || max17042_new_config_data(chip)) { INIT_WORK(&chip->work, max17042_init_worker); schedule_work(&chip->work); } else { diff --git a/drivers/regulator/omap-pmic-regulator.c b/drivers/regulator/omap-pmic-regulator.c index a19f9ebb7de..2eab7c158e2 100644 --- a/drivers/regulator/omap-pmic-regulator.c +++ b/drivers/regulator/omap-pmic-regulator.c @@ -661,7 +661,22 @@ static struct platform_driver omap_pmic_driver = { }, .probe = omap_pmic_probe, }; -module_platform_driver(omap_pmic_driver); + +static int __init omap_pmic_init(void) +{ + int ret; + ret = platform_driver_register(&omap_pmic_driver); + if (ret) + pr_err("driver register failed for omap_pmic (%d)\n", ret); + return ret; +} +device_initcall_sync(omap_pmic_init); + +static void __exit omap_pmic_exit(void) +{ + platform_driver_unregister(&omap_pmic_driver); +} +module_exit(omap_pmic_exit); MODULE_DESCRIPTION("OMAP Generic PMIC Regulator"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-sensorhub.c b/drivers/rtc/rtc-sensorhub.c index f4e713d741f..16f95758f93 100644 --- a/drivers/rtc/rtc-sensorhub.c +++ b/drivers/rtc/rtc-sensorhub.c @@ -223,6 +223,9 @@ static int rtc_sensorhub_rtc_set_time(struct device *p_dev, "set time, but failed to set M4 clock!\n"); return -EIO; } + dev_dbg(p_dev, "Set RTC time to %d-%02d-%02d %02d:%02d:%02d UTC (%ld)\n", + p_tm->tm_year + 1900, p_tm->tm_mon + 1, p_tm->tm_mday, + p_tm->tm_hour, p_tm->tm_min, p_tm->tm_sec, sec); return 0; } diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 7d041f28cf2..3009a8909cc 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -172,6 +172,7 @@ struct uart_omap_port { struct pinctrl_state *pin_default; struct pinctrl_state *pin_idle; bool is_suspending; + spinlock_t delayed_rts_lock; /* protect need_delayed_rts*/ bool need_delayed_rts; bool in_transmit; int ext_rt_cnt; @@ -1372,12 +1373,16 @@ static int serial_omap_prepare(struct device *dev) static void serial_omap_complete(struct device *dev) { struct uart_omap_port *up = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&up->delayed_rts_lock, flags); if (up->need_delayed_rts && up->pin_default && up->pin_idle) { pinctrl_select_state(up->pins, up->pin_default); up->need_delayed_rts = 0; } up->is_suspending = false; + spin_unlock_irqrestore(&up->delayed_rts_lock, flags); } static int serial_omap_suspend(struct device *dev) @@ -1747,6 +1752,7 @@ static void serial_omap_restore_context(struct uart_omap_port *up) static int serial_omap_runtime_suspend(struct device *dev) { struct uart_omap_port *up = dev_get_drvdata(dev); + unsigned long flags; /* * When using 'no_console_suspend', the console UART must not be @@ -1762,8 +1768,10 @@ static int serial_omap_runtime_suspend(struct device *dev) return -EINVAL; if (up->pin_idle) { + spin_lock_irqsave(&up->delayed_rts_lock, flags); pinctrl_select_state(up->pins, up->pin_idle); up->need_delayed_rts = 0; + spin_unlock_irqrestore(&up->delayed_rts_lock, flags); } up->context_loss_cnt = serial_omap_get_context_loss_count(up); diff --git a/drivers/video/omap2/displays/panel-minnow-common.h b/drivers/video/omap2/displays/panel-minnow-common.h index 1001cc67f46..13ce3050a9e 100644 --- a/drivers/video/omap2/displays/panel-minnow-common.h +++ b/drivers/video/omap2/displays/panel-minnow-common.h @@ -18,7 +18,7 @@ #ifndef _MINNOW_PANEL_COMMON_HEADER_ -#define INIT_DATA_VERSION (0x072314) /*MM/DD/YY*/ +#define INIT_DATA_VERSION (0x081214) /*MM/DD/YY*/ /* This header file is used to sync Bootloader and Kernel Display Initialize * Structure/Data, please make sure sync it for both Bootloader/Kernel when * it changes some settings for Solomon/Orise. Bootloader should pass @@ -215,8 +215,8 @@ static u8 panel_init_ssd2848_320x320[] = { 2, OTM3201_CMD, 0xE9, 0x46, /* Display Inversion Control (RB1h) */ 2, OTM3201_CMD, 0xB1, 0x12, -/* ??? undefined */ -2, OTM3201_CMD, 0xE2, 0xF0, +/* MIPI RX Delay Setting (RE2h) */ +2, OTM3201_CMD, 0xE2, 0xF5, /* Display Waveform Cycle setting (RBAh) */ 5, OTM3201_CMD, 0xBA, 0x06, 0x15, 0x2B, 0x01, /* RGB Interface Blanking Porch setting (RB3h) diff --git a/drivers/video/omap2/displays/panel-minnow.c b/drivers/video/omap2/displays/panel-minnow.c index 8d127ae401f..d1014b0af1d 100644 --- a/drivers/video/omap2/displays/panel-minnow.c +++ b/drivers/video/omap2/displays/panel-minnow.c @@ -302,6 +302,8 @@ struct minnow_panel_data { int id_panel; int x_offset; int y_offset; + int xres_um; + int yres_um; int reset_ms; int release_ms; @@ -439,6 +441,27 @@ static void minnow_panel_sync_resume_mlocked(struct minnow_panel_data *mpd) #endif #ifdef CONFIG_WAKEUP_SOURCE_NOTIFY +static char *action_to_str(unsigned long action) +{ + switch (action) { + case DISPLAY_WAKE_EVENT_POWERKEY: + return "power_key"; + case DISPLAY_WAKE_EVENT_TOUCH: + return "touch"; + case DISPLAY_WAKE_EVENT_GESTURE: + return "gesture_wrist"; + case DISPLAY_WAKE_EVENT_GESTURE_VIEWON: + return "gesture_view_on"; + case DISPLAY_WAKE_EVENT_GESTURE_VIEWOFF: + return "gesture_view_off"; + case DISPLAY_WAKE_EVENT_DOCKON: + return "dock_on"; + case DISPLAY_WAKE_EVENT_DOCKOFF: + return "dock_off"; + } + return "unsupported"; +} + static int omapdss_displayenable_notify(struct notifier_block *self, unsigned long action, void *dev) { @@ -448,7 +471,8 @@ static int omapdss_displayenable_notify(struct notifier_block *self, if (GET_WAKEUP_EVENT_TYPE(action) != WAKEUP_DISPLAY) return NOTIFY_OK; - dev_info(&mpd->dssdev->dev, "%s, action is %lu", __func__, action); + dev_info(&mpd->dssdev->dev, "%s, action is %lu-%s", + __func__, action, action_to_str(action)); switch (action) { case DISPLAY_WAKE_EVENT_POWERKEY: @@ -1420,6 +1444,14 @@ static void minnow_panel_get_resolution(struct omap_dss_device *dssdev, *yres = dssdev->panel.timings.y_res; } +static void minnow_panel_get_dimensions(struct omap_dss_device *dssdev, + u32 *xres, u32 *yres) +{ + struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev); + *xres = mpd->xres_um; + *yres = mpd->yres_um; +} + static ssize_t minnow_panel_errors_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1874,6 +1906,49 @@ static ssize_t minnow_panel_store_interactivemode(struct device *dev, return r ? r : count; } +static ssize_t minnow_panel_show_smartambient(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev); + unsigned t; + + t = mpd->smart_ambient; + + return snprintf(buf, PAGE_SIZE, "%u\n", t); +} + +static ssize_t minnow_panel_store_smartambient(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev); + unsigned long t; + int r; + bool enable; + + r = kstrtoul(buf, 10, &t); + if (!r) { + mutex_lock(&mpd->lock); + enable = !!t; + if (mpd->state != DISPLAY_ENABLE) { + dev_err(&dssdev->dev, "%s failed as display is not enabled\n", + __func__); + r = -EBUSY; + } else if (mpd->smart_ambient != enable) { + mpd->smart_ambient = enable; + } + mutex_unlock(&mpd->lock); + if (r) + dev_err(&dssdev->dev, "setting smartambient_status to %ld failed %d\n", + t, r); + else + dev_dbg(&dssdev->dev, "setting smartambient_status to %ld succeeded\n", + t); + } + + return r ? r : count; +} static ssize_t minnow_panel_show_ambient_timeout(struct device *dev, struct device_attribute *attr, char *buf) { @@ -2050,6 +2125,9 @@ static DEVICE_ATTR(init_data, S_IRUGO | S_IWUSR, static DEVICE_ATTR(interactivemode, S_IRUGO | S_IWUSR, minnow_panel_show_interactivemode, minnow_panel_store_interactivemode); +static DEVICE_ATTR(smartambient, S_IRUSR | S_IWUSR, + minnow_panel_show_smartambient, + minnow_panel_store_smartambient); static DEVICE_ATTR(ambient_timeout, S_IRUGO | S_IWUSR, minnow_panel_show_ambient_timeout, minnow_panel_store_ambient_timeout); @@ -2076,6 +2154,7 @@ static struct attribute *minnow_panel_attrs[] = { #endif #ifdef CONFIG_HAS_AMBIENTMODE &dev_attr_interactivemode.attr, + &dev_attr_smartambient.attr, &dev_attr_ambient_timeout.attr, #endif #ifdef PANEL_PERF_TIME @@ -2409,6 +2488,15 @@ static int minnow_panel_dt_init(struct minnow_panel_data *mpd) mpd->dsi_config.lp_clk_max); } + mpd->xres_um = 0; + mpd->yres_um = 0; + if (!of_property_read_u32_array(dt_node, "panel_size_um", range, 2)) { + mpd->xres_um = range[0]; + mpd->yres_um = range[1]; + DTINFO("physical panel width = %d um, height = %d um\n", + mpd->xres_um, mpd->yres_um); + } + return 0; } @@ -2453,6 +2541,7 @@ static int minnow_panel_probe(struct omap_dss_device *dssdev) mpd->dssdev = dssdev; mpd->first_enable = true; mpd->m4_state = DISPLAY_ENABLE; + mpd->interactive = true; r = minnow_panel_dt_init(mpd); if (r) @@ -3030,8 +3119,9 @@ static int minnow_panel_enable_mlocked(struct minnow_panel_data *mpd) bool update; int r = 0; - dev_info(&dssdev->dev, "%s: current state = %d\n", - __func__, dssdev->state); + dev_info(&dssdev->dev, "%s: current display is %s\n", __func__, + dssdev->state == OMAP_DSS_DISPLAY_DISABLED + ? "disabled" : "enabled"); if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED) { wake_lock(&mpd->wake_lock); @@ -3067,8 +3157,9 @@ static void minnow_panel_disable_mlocked(struct minnow_panel_data *mpd) { struct omap_dss_device *dssdev = mpd->dssdev; - dev_info(&dssdev->dev, "%s: current state = %d\n", - __func__, dssdev->state); + dev_info(&dssdev->dev, "%s: current display is %s\n", __func__, + dssdev->state == OMAP_DSS_DISPLAY_DISABLED + ? "disabled" : "enabled"); wake_lock(&mpd->wake_lock); mpd->early_inited = false; @@ -3091,7 +3182,6 @@ static void minnow_panel_disable_mlocked(struct minnow_panel_data *mpd) static void minnow_panel_sync_display_status_mlocked( struct minnow_panel_data *mpd) { - struct m4sensorhub_data *m4sensorhub; enum display_state m4_state = mpd->state; /* special case for dock mode, set to DISPLAY_ENABLE * to block all wakeup gestures @@ -3104,21 +3194,19 @@ static void minnow_panel_sync_display_status_mlocked( /* be safety to sync resume states first */ minnow_panel_sync_resume_mlocked(mpd); - m4sensorhub = m4sensorhub_client_get_drvdata(); - if (m4sensorhub->mode != NORMALMODE) { + if (m4sensorhub_get_current_mode() != NORMALMODE) { dev_err(&mpd->dssdev->dev, "M4 is not ready, unable to set screen status(%d)\n", m4_state); return; } - if (m4sensorhub_reg_write_1byte(m4sensorhub, - M4SH_REG_USERSETTINGS_SCREENSTATUS, - m4_state, 0xFF) != 1) { + + if (m4sensorhub_extern_set_display_status(m4_state) < 0) { dev_err(&mpd->dssdev->dev, - "Unable to set screen status(%d) to M4\n", - m4_state); + "Unable to set screen status(%d) to M4\n", m4_state); return; } + dev_dbg(&mpd->dssdev->dev, "Set screen status(%d) to M4 success!\n", m4_state); mpd->m4_state = m4_state; @@ -3145,13 +3233,32 @@ static void led_set_dim_brightness(struct device *dev) } #endif /* CONFIG_HAS_AMBIENTMODE */ +static char *state_to_str(enum display_state state) +{ + switch (state) { + case DISPLAY_DISABLE: + return "normal_off"; + case DISPLAY_ENABLE: + return "normal_on"; +#ifdef CONFIG_HAS_AMBIENTMODE + case DISPLAY_AMBIENT_OFF: + return "ambient_off"; + case DISPLAY_AMBIENT_ON: + return "ambient_on"; +#endif + } + return "unknown???"; +} + static int minnow_panel_change_state_mlocked(struct minnow_panel_data *mpd, int state) { int r = 0; dev_info(&mpd->dssdev->dev, - "change state %d ==> %d\n", mpd->state, state); + "change state %d(%s) ==> %d(%s)\n", + mpd->state, state_to_str(mpd->state), + state, state_to_str(state)); /* already in state, return success */ if (state == mpd->state) { @@ -3364,7 +3471,7 @@ static void minnow_panel_te_timeout_work_callback(struct work_struct *work) static int minnow_panel_enable(struct omap_dss_device *dssdev) { struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev); - int r; + int r, state; mutex_lock(&mpd->lock); #ifdef CONFIG_WAKEUP_SOURCE_NOTIFY @@ -3374,7 +3481,12 @@ static int minnow_panel_enable(struct omap_dss_device *dssdev) cancel_delayed_work(&mpd->early_init_timeout_work); } #endif - r = minnow_panel_change_state_mlocked(mpd, DISPLAY_ENABLE); + state = DISPLAY_ENABLE; +#ifdef CONFIG_HAS_AMBIENTMODE + if (!mpd->interactive) + state = DISPLAY_AMBIENT_ON; +#endif + r = minnow_panel_change_state_mlocked(mpd, state); mutex_unlock(&mpd->lock); return r; } @@ -3726,6 +3838,7 @@ static struct omap_dss_driver minnow_panel_driver = { .sync = minnow_panel_sync, .get_resolution = minnow_panel_get_resolution, + .get_dimensions = minnow_panel_get_dimensions, .get_recommended_bpp = omapdss_default_get_recommended_bpp, .enable_te = minnow_panel_enable_te, diff --git a/include/linux/als_notify.h b/include/linux/als_notify.h new file mode 100644 index 00000000000..1c35ca54727 --- /dev/null +++ b/include/linux/als_notify.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2014 Motorola Mobility LLC. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef ALS_NOTIFY_H +#define ALS_NOTIFY_H + +#ifdef __KERNEL__ + +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif + +enum als_state { + ALS_ENABLED = (UINT16_MAX + 1), + ALS_DISABLED +}; + +extern void als_register_notify(struct notifier_block *nb); +extern void als_unregister_notify(struct notifier_block *nb); +extern void als_notify_subscriber(unsigned long event); +#endif /* __KERNEL__ */ + +#endif /* ALS_NOTIFY_H */ diff --git a/include/linux/iio/m4sensorhub/m4sensorhub_fusion.h b/include/linux/iio/m4sensorhub/m4sensorhub_fusion.h index 57680018826..6748393e0e5 100644 --- a/include/linux/iio/m4sensorhub/m4sensorhub_fusion.h +++ b/include/linux/iio/m4sensorhub/m4sensorhub_fusion.h @@ -26,6 +26,9 @@ enum m4sensorhub_fusion_iio_type { FUSION_TYPE_ROTATION = 0, FUSION_TYPE_ORIENTATION = 1, + FUSION_TYPE_GRAVITY = 2, + FUSION_TYPE_LINEAR_ACCELERATION = 3, + M4FUS_NUM_FUSION_BUFFERS }; struct m4sensorhub_fusion_iio_data { @@ -38,6 +41,5 @@ struct m4sensorhub_fusion_iio_data { #define M4FUS_DATA_STRUCT_SIZE_BITS \ (sizeof(struct m4sensorhub_fusion_iio_data) * 8) -#define M4FUS_NUM_FUSION_BUFFERS 2 #endif /* _M4SENSORHUB_FUSION_IIO_H */ diff --git a/include/linux/iio/m4sensorhub/m4sensorhub_pedometer.h b/include/linux/iio/m4sensorhub/m4sensorhub_pedometer.h index 2e4d6cd9bec..28af1e76359 100644 --- a/include/linux/iio/m4sensorhub/m4sensorhub_pedometer.h +++ b/include/linux/iio/m4sensorhub/m4sensorhub_pedometer.h @@ -30,6 +30,7 @@ struct m4sensorhub_pedometer_iio_data { uint16_t current_speed; uint32_t healthy_minutes; uint32_t calories; + uint32_t calories_normr; long long timestamp; } __packed; diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 850e95bc766..867833ba6bd 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -36,6 +36,7 @@ struct ipv6_devconf { __s32 accept_ra_rt_info_max_plen; #endif #endif + __s32 accept_ra_rt_table; __s32 proxy_ndp; __s32 accept_source_route; #ifdef CONFIG_IPV6_OPTIMISTIC_DAD diff --git a/include/linux/m4sensorhub.h b/include/linux/m4sensorhub.h index 5bc54f0885b..5eb2ec0586f 100644 --- a/include/linux/m4sensorhub.h +++ b/include/linux/m4sensorhub.h @@ -73,6 +73,7 @@ enum m4sensorhub_panichdl_index { PANICHDL_ALS_RESTORE, PANICHDL_MPU9150_RESTORE, PANICHDL_PEDOMETER_RESTORE, + PANICHDL_EXTERN_RESTORE, /* * Please add enum before PANICHDL_IRQ_RESTORE * to make sure IRQ restore will be called last. @@ -241,5 +242,11 @@ bool m4sensorhub_preflash_callbacks_exist(void); /* For FW flash core */ int m4sensorhub_irq_disable_all(struct m4sensorhub_data *m4sensorhub); +/* External System Calls for Non-M4 Drivers */ +int m4sensorhub_extern_init(struct m4sensorhub_data *m4); /* Init for core */ +int m4sensorhub_extern_set_audio_status(uint8_t status); +int m4sensorhub_extern_set_display_status(uint8_t status); +int m4sensorhub_get_current_mode(void); + #endif /* __KERNEL__ */ #endif /* __M4SENSORHUB_H__ */ diff --git a/include/linux/m4sensorhub/MemMapLog.h b/include/linux/m4sensorhub/MemMapLog.h index 9aab2f4cea8..2b7882a4df4 100644 --- a/include/linux/m4sensorhub/MemMapLog.h +++ b/include/linux/m4sensorhub/MemMapLog.h @@ -1,53 +1,58 @@ -/**********************************************************************
-*
-* Copyright (C) 2012 Motorola, Inc.
-*
-**********************************************************************
-File : MemMapLog.h
-Purpose :
-**********************************************************************/
-#ifndef __MEMMAP_LOG_H__
-#define __MEMMAP_LOG_H__
-/****************************** Defines *******************************/
-struct memMapLog
-{
- u64 logEnable;
- u8 isLogImmediate;
-};
-
-#define LOG_MAX 24
-#define LOG_LEVELS_MAX 4
-
-static char acLogTags[LOG_MAX][40] = {
- "LOG_GENERAL",
- "LOG_TIMER",
- "LOG_ACCEL",
- "LOG_TEMPERATURE",
- "LOG_PRESSURE",
- "LOG_PEDOMETER",
- "LOG_TCMD",
- "LOG_GYRO",
- "LOG_COMPASS",
- "LOG_FUSION",
- "LOG_METS",
- "LOG_GESTURE",
- "LOG_POWER",
- "LOG_CORRELATION",
- "LOG_GPS",
- "LOG_DL",
- "LOG_AUDIO",
- "LOG_DISP",
- "LOG_WRIST",
- "LOG_PASSIVE",
- "LOG_EMG"
- "LOG_HR",
- "LOG_ALS"
-};
-
-static char acLogLevels[LOG_LEVELS_MAX][15] = {
- "LOG_DISABLE",
- "LOG_ERROR",
- "LOG_VERBOSE",
- "LOG_DEBUG"
-};
-#endif /* __MEMMAP_LOG_H__ */
+/********************************************************************** +* +* Copyright (C) 2012 Motorola, Inc. +* +********************************************************************** +File : MemMapLog.h +Purpose : +**********************************************************************/ +#ifndef __MEMMAP_LOG_H__ +#define __MEMMAP_LOG_H__ +/****************************** Defines *******************************/ +#define LOG_EN_SIZE 2 +/* Number of log tags per element of logenable array */ +#define LOG_TAGS_PER_ENABLE 16 +#define LOG_MAX 23 +#define LOG_NO_OF_BITS_PER_TAG 2 +/*This is set to 0x03 since each logtag uses 2 bits in logenable */ +#define LOG_TAG_MASK 0x03 +#define LOG_LEVELS_MAX 4 + +struct memMapLog { + u32 logEnable[LOG_EN_SIZE]; + u8 isLogImmediate; +}; + +static char acLogTags[LOG_MAX][40] = { + "LOG_GENERAL", + "LOG_TIMER", + "LOG_ACCEL", + "LOG_TEMPERATURE", + "LOG_PRESSURE", + "LOG_PEDOMETER", + "LOG_TCMD", + "LOG_GYRO", + "LOG_COMPASS", + "LOG_FUSION", + "LOG_METS", + "LOG_GESTURE", + "LOG_POWER", + "LOG_CORRELATION", + "LOG_GPS", + "LOG_DL", + "LOG_AUDIO", + "LOG_DISP", + "LOG_WRIST", + "LOG_PASSIVE", + "LOG_EMG", + "LOG_HR", + "LOG_ALS", +}; + +static char acLogLevels[LOG_LEVELS_MAX][15] = { + "LOG_DISABLE", + "LOG_ERROR", + "LOG_VERBOSE", + "LOG_DEBUG" +}; +#endif /* __MEMMAP_LOG_H__ */ diff --git a/include/linux/m4sensorhub/m4sensorhub_reg_enum.h b/include/linux/m4sensorhub/m4sensorhub_reg_enum.h index 040df03a2ed..1399e104cc5 100644 --- a/include/linux/m4sensorhub/m4sensorhub_reg_enum.h +++ b/include/linux/m4sensorhub/m4sensorhub_reg_enum.h @@ -81,9 +81,9 @@ enum m4sensorhub_reg { M4SH_REG_FUSION_LOCALX, M4SH_REG_FUSION_LOCALY, M4SH_REG_FUSION_LOCALZ, - M4SH_REG_FUSION_WORLDX, - M4SH_REG_FUSION_WORLDY, - M4SH_REG_FUSION_WORLDZ, + M4SH_REG_FUSION_GRAVITYX, + M4SH_REG_FUSION_GRAVITYY, + M4SH_REG_FUSION_GRAVITYZ, M4SH_REG_FUSION_ROTATIONVECTOR, M4SH_REG_FUSION_HEADING, M4SH_REG_FUSION_HEADING_ACCURACY, @@ -106,6 +106,8 @@ enum m4sensorhub_reg { M4SH_REG_METS_METS, M4SH_REG_METS_CALORIES, M4SH_REG_METS_HEALTHYMINUTES, + M4SH_REG_METS_METS_NO_RMR, + M4SH_REG_METS_CALORIES_NO_RMR, M4SH_REG_USERSETTINGS_SCREENSTATUS, M4SH_REG_USERSETTINGS_USERAGE, M4SH_REG_USERSETTINGS_USERGENDER, diff --git a/include/linux/power/max17042_battery.h b/include/linux/power/max17042_battery.h index 77542380c52..d3214b54fd2 100644 --- a/include/linux/power/max17042_battery.h +++ b/include/linux/power/max17042_battery.h @@ -120,6 +120,7 @@ enum max17042_register { enum max17047_register { MAX17047_QRTbl00 = 0x12, MAX17047_FullSOCThr = 0x13, + MAX17047_Config_Ver = 0x20, /* Reserved register */ MAX17047_QRTbl10 = 0x22, MAX17047_QRTbl20 = 0x32, MAX17047_V_empty = 0x3A, @@ -139,6 +140,8 @@ struct max17042_reg_data { }; struct max17042_config_data { + /* Increment version for new config data to be programed */ + u16 version; /* External current sense resistor value in milli-ohms */ u32 cur_sense_val; diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 21f702704f2..96a8afe33c5 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -183,6 +183,8 @@ static inline bool ipv6_is_mld(struct sk_buff *skb, int nexthdr, int offset) extern void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao); +u32 addrconf_rt_table(const struct net_device *dev, u32 default_table); + /* * anycast prototypes (anycast.c) */ diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index e361f488242..4ac12e14c6d 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -23,6 +23,8 @@ struct fib_rule { struct fib_rule __rcu *ctarget; char iifname[IFNAMSIZ]; char oifname[IFNAMSIZ]; + kuid_t uid_start; + kuid_t uid_end; struct rcu_head rcu; struct net * fr_net; }; @@ -80,7 +82,9 @@ struct fib_rules_ops { [FRA_FWMARK] = { .type = NLA_U32 }, \ [FRA_FWMASK] = { .type = NLA_U32 }, \ [FRA_TABLE] = { .type = NLA_U32 }, \ - [FRA_GOTO] = { .type = NLA_U32 } + [FRA_GOTO] = { .type = NLA_U32 }, \ + [FRA_UID_START] = { .type = NLA_U32 }, \ + [FRA_UID_END] = { .type = NLA_U32 } static inline void fib_rule_get(struct fib_rule *rule) { diff --git a/include/net/flow.h b/include/net/flow.h index 628e11b98c5..c91e2aae3fb 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -10,6 +10,7 @@ #include <linux/socket.h> #include <linux/in6.h> #include <linux/atomic.h> +#include <linux/uidgid.h> struct flowi_common { int flowic_oif; @@ -23,6 +24,7 @@ struct flowi_common { #define FLOWI_FLAG_CAN_SLEEP 0x02 #define FLOWI_FLAG_KNOWN_NH 0x04 __u32 flowic_secid; + kuid_t flowic_uid; }; union flowi_uli { @@ -59,6 +61,7 @@ struct flowi4 { #define flowi4_proto __fl_common.flowic_proto #define flowi4_flags __fl_common.flowic_flags #define flowi4_secid __fl_common.flowic_secid +#define flowi4_uid __fl_common.flowic_uid /* (saddr,daddr) must be grouped, same order as in IP header */ __be32 saddr; @@ -78,7 +81,8 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif, __u32 mark, __u8 tos, __u8 scope, __u8 proto, __u8 flags, __be32 daddr, __be32 saddr, - __be16 dport, __be16 sport) + __be16 dport, __be16 sport, + kuid_t uid) { fl4->flowi4_oif = oif; fl4->flowi4_iif = 0; @@ -88,6 +92,7 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif, fl4->flowi4_proto = proto; fl4->flowi4_flags = flags; fl4->flowi4_secid = 0; + fl4->flowi4_uid = uid; fl4->daddr = daddr; fl4->saddr = saddr; fl4->fl4_dport = dport; @@ -115,6 +120,7 @@ struct flowi6 { #define flowi6_proto __fl_common.flowic_proto #define flowi6_flags __fl_common.flowic_flags #define flowi6_secid __fl_common.flowic_secid +#define flowi6_uid __fl_common.flowic_uid struct in6_addr daddr; struct in6_addr saddr; __be32 flowlabel; @@ -158,6 +164,7 @@ struct flowi { #define flowi_proto u.__fl_common.flowic_proto #define flowi_flags u.__fl_common.flowic_flags #define flowi_secid u.__fl_common.flowic_secid +#define flowi_uid u.__fl_common.flowic_uid } __attribute__((__aligned__(BITS_PER_LONG/8))); static inline struct flowi *flowi4_to_flowi(struct flowi4 *fl4) diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 7235ae73a1e..9528e10fa0b 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -88,6 +88,7 @@ struct inet_request_sock { acked : 1, no_srccheck: 1; kmemcheck_bitfield_end(flags); + u32 ir_mark; struct ip_options_rcu *opt; }; @@ -96,6 +97,14 @@ static inline struct inet_request_sock *inet_rsk(const struct request_sock *sk) return (struct inet_request_sock *)sk; } +static inline u32 inet_request_mark(struct sock *sk, struct sk_buff *skb) +{ + if (!sk->sk_mark && sock_net(sk)->ipv4.sysctl_tcp_fwmark_accept) + return skb->mark; + + return sk->sk_mark; +} + struct inet_cork { unsigned int flags; __be32 addr; diff --git a/include/net/ip.h b/include/net/ip.h index a68f838a132..02fc145ecc4 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -153,6 +153,7 @@ struct ip_reply_arg { /* -1 if not needed */ int bound_dev_if; u8 tos; + kuid_t uid; }; #define IP_REPLY_ARG_NOSRCCHECK 1 @@ -225,6 +226,9 @@ extern void ipfrag_init(void); extern void ip_static_sysctl_init(void); +#define IP4_REPLY_MARK(net, mark) \ + ((net)->ipv4.sysctl_fwmark_reflect ? (mark) : 0) + static inline bool ip_is_fragment(const struct iphdr *iph) { return (iph->frag_off & htons(IP_MF | IP_OFFSET)) != 0; diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 260f83f16bc..25b4500f28c 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -131,7 +131,7 @@ extern int rt6_route_rcv(struct net_device *dev, const struct in6_addr *gwaddr); extern void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, - int oif, u32 mark); + int oif, u32 mark, kuid_t uid); extern void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu); extern void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark); diff --git a/include/net/ipv6.h b/include/net/ipv6.h index ab47582f6c0..cc344ca9d0a 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -111,6 +111,9 @@ struct frag_hdr { #define IP6_MF 0x0001 +#define IP6_REPLY_MARK(net, mark) \ + ((net)->ipv6.sysctl.fwmark_reflect ? (mark) : 0) + #include <net/sock.h> /* sysctls */ diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 2ba9de89e8e..0dd6f0b3ead 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -64,6 +64,9 @@ struct netns_ipv4 { int sysctl_tcp_ecn; + int sysctl_fwmark_reflect; + int sysctl_tcp_fwmark_accept; + kgid_t sysctl_ping_group_range[2]; long sysctl_tcp_mem[3]; diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index 005e2c2e39a..4b9f99e3a91 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -28,6 +28,7 @@ struct netns_sysctl_ipv6 { int ip6_rt_mtu_expires; int ip6_rt_min_advmss; int icmpv6_time; + int fwmark_reflect; }; struct netns_ipv6 { diff --git a/include/net/route.h b/include/net/route.h index 2ea40c1b5e0..647bb2adbff 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -142,7 +142,7 @@ static inline struct rtable *ip_route_output_ports(struct net *net, struct flowi flowi4_init_output(fl4, oif, sk ? sk->sk_mark : 0, tos, RT_SCOPE_UNIVERSE, proto, sk ? inet_sk_flowi_flags(sk) : 0, - daddr, saddr, dport, sport); + daddr, saddr, dport, sport, sk ? sock_i_uid(sk) : 0); if (sk) security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); return ip_route_output_flow(net, fl4, sk); @@ -253,7 +253,8 @@ static inline void ip_route_connect_init(struct flowi4 *fl4, __be32 dst, __be32 flow_flags |= FLOWI_FLAG_CAN_SLEEP; flowi4_init_output(fl4, oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, - protocol, flow_flags, dst, src, dport, sport); + protocol, flow_flags, dst, src, dport, sport, + sock_i_uid(sk)); } static inline struct rtable *ip_route_connect(struct flowi4 *fl4, diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h index 51da65b68b8..9dcdb6251cb 100644 --- a/include/uapi/linux/fib_rules.h +++ b/include/uapi/linux/fib_rules.h @@ -49,6 +49,8 @@ enum { FRA_TABLE, /* Extended table id */ FRA_FWMASK, /* mask for netfilter mark */ FRA_OIFNAME, + FRA_UID_START, /* UID range */ + FRA_UID_END, __FRA_MAX }; diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index 4bda4cf5b0f..4214fac1bf4 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h @@ -160,6 +160,7 @@ enum { DEVCONF_ACCEPT_DAD, DEVCONF_FORCE_TLLAO, DEVCONF_NDISC_NOTIFY, + DEVCONF_ACCEPT_RA_RT_TABLE, DEVCONF_MAX }; diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index 7a2144e1afa..07c1146c1f5 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -297,6 +297,7 @@ enum rtattr_type_t { RTA_TABLE, RTA_MARK, RTA_MFC_STATS, + RTA_UID, __RTA_MAX }; diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index 07f7c6f9330..acd35596981 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c @@ -264,6 +264,9 @@ static int alarmtimer_suspend(struct device *dev) if (ret < 0) goto out; now = rtc_tm_to_ktime(tm); + dev_dbg(dev, "RTC is %lld next alarm will be in %lld seconds at %lld\n", + div64_u64(now.tv64, 1000000000), div64_u64(min.tv64, 1000000000), + div64_u64(ktime_to_ns(ktime_add(now, min)), 1000000000)); now = ktime_add(now, min); /* Set alarm, if in the past reject suspend briefly to handle */ diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index d5a9f8ead0d..a40a876b855 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -31,6 +31,8 @@ int fib_default_rule_add(struct fib_rules_ops *ops, r->pref = pref; r->table = table; r->flags = flags; + r->uid_start = INVALID_UID; + r->uid_end = INVALID_UID; r->fr_net = hold_net(ops->fro_net); /* The lock is not required here, the list in unreacheable @@ -179,6 +181,23 @@ void fib_rules_unregister(struct fib_rules_ops *ops) } EXPORT_SYMBOL_GPL(fib_rules_unregister); +static inline kuid_t fib_nl_uid(struct nlattr *nla) +{ + return make_kuid(current_user_ns(), nla_get_u32(nla)); +} + +static int nla_put_uid(struct sk_buff *skb, int idx, kuid_t uid) +{ + return nla_put_u32(skb, idx, from_kuid_munged(current_user_ns(), uid)); +} + +static int fib_uid_range_match(struct flowi *fl, struct fib_rule *rule) +{ + return (!uid_valid(rule->uid_start) && !uid_valid(rule->uid_end)) || + (uid_gte(fl->flowi_uid, rule->uid_start) && + uid_lte(fl->flowi_uid, rule->uid_end)); +} + static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, struct flowi *fl, int flags) { @@ -193,6 +212,9 @@ static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, if ((rule->mark ^ fl->flowi_mark) & rule->mark_mask) goto out; + if (!fib_uid_range_match(fl, rule)) + goto out; + ret = ops->match(rule, fl, flags); out: return (rule->flags & FIB_RULE_INVERT) ? !ret : ret; @@ -363,6 +385,19 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh) } else if (rule->action == FR_ACT_GOTO) goto errout_free; + /* UID start and end must either both be valid or both unspecified. */ + rule->uid_start = rule->uid_end = INVALID_UID; + if (tb[FRA_UID_START] || tb[FRA_UID_END]) { + if (tb[FRA_UID_START] && tb[FRA_UID_END]) { + rule->uid_start = fib_nl_uid(tb[FRA_UID_START]); + rule->uid_end = fib_nl_uid(tb[FRA_UID_END]); + } + if (!uid_valid(rule->uid_start) || + !uid_valid(rule->uid_end) || + !uid_lte(rule->uid_start, rule->uid_end)) + goto errout_free; + } + err = ops->configure(rule, skb, frh, tb); if (err < 0) goto errout_free; @@ -445,7 +480,8 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh) if (frh->action && (frh->action != rule->action)) continue; - if (frh->table && (frh_get_table(frh, tb) != rule->table)) + if (frh_get_table(frh, tb) && + (frh_get_table(frh, tb) != rule->table)) continue; if (tb[FRA_PRIORITY] && @@ -468,6 +504,14 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh) (rule->mark_mask != nla_get_u32(tb[FRA_FWMASK]))) continue; + if (tb[FRA_UID_START] && + !uid_eq(rule->uid_start, fib_nl_uid(tb[FRA_UID_START]))) + continue; + + if (tb[FRA_UID_END] && + !uid_eq(rule->uid_end, fib_nl_uid(tb[FRA_UID_END]))) + continue; + if (!ops->compare(rule, frh, tb)) continue; @@ -524,7 +568,9 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, + nla_total_size(4) /* FRA_PRIORITY */ + nla_total_size(4) /* FRA_TABLE */ + nla_total_size(4) /* FRA_FWMARK */ - + nla_total_size(4); /* FRA_FWMASK */ + + nla_total_size(4) /* FRA_FWMASK */ + + nla_total_size(4) /* FRA_UID_START */ + + nla_total_size(4); /* FRA_UID_END */ if (ops->nlmsg_payload) payload += ops->nlmsg_payload(rule); @@ -578,7 +624,11 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, ((rule->mark_mask || rule->mark) && nla_put_u32(skb, FRA_FWMASK, rule->mark_mask)) || (rule->target && - nla_put_u32(skb, FRA_GOTO, rule->target))) + nla_put_u32(skb, FRA_GOTO, rule->target)) || + (uid_valid(rule->uid_start) && + nla_put_uid(skb, FRA_UID_START, rule->uid_start)) || + (uid_valid(rule->uid_end) && + nla_put_uid(skb, FRA_UID_END, rule->uid_end))) goto nla_put_failure; if (ops->fill(rule, skb, frh) < 0) goto nla_put_failure; diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index c7629a209f9..ffffeb448ec 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -531,6 +531,7 @@ const struct nla_policy rtm_ipv4_policy[RTA_MAX + 1] = { [RTA_METRICS] = { .type = NLA_NESTED }, [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, [RTA_FLOW] = { .type = NLA_U32 }, + [RTA_UID] = { .type = NLA_U32 }, }; static int rtm_to_fib_config(struct net *net, struct sk_buff *skb, diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 562efd91f45..cc38f44306e 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -337,6 +337,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) struct sock *sk; struct inet_sock *inet; __be32 daddr, saddr; + u32 mark = IP4_REPLY_MARK(net, skb->mark); if (ip_options_echo(&icmp_param->replyopts.opt.opt, skb)) return; @@ -349,6 +350,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) icmp_param->data.icmph.checksum = 0; inet->tos = ip_hdr(skb)->tos; + sk->sk_mark = mark; daddr = ipc.addr = ip_hdr(skb)->saddr; saddr = fib_compute_spec_dst(skb); ipc.opt = NULL; @@ -361,6 +363,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) memset(&fl4, 0, sizeof(fl4)); fl4.daddr = daddr; fl4.saddr = saddr; + fl4.flowi4_mark = mark; fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos); fl4.flowi4_proto = IPPROTO_ICMP; security_skb_classify_flow(skb, flowi4_to_flowi(&fl4)); @@ -379,7 +382,7 @@ static struct rtable *icmp_route_lookup(struct net *net, struct flowi4 *fl4, struct sk_buff *skb_in, const struct iphdr *iph, - __be32 saddr, u8 tos, + __be32 saddr, u8 tos, u32 mark, int type, int code, struct icmp_bxm *param) { @@ -391,6 +394,7 @@ static struct rtable *icmp_route_lookup(struct net *net, fl4->daddr = (param->replyopts.opt.opt.srr ? param->replyopts.opt.opt.faddr : iph->saddr); fl4->saddr = saddr; + fl4->flowi4_mark = mark; fl4->flowi4_tos = RT_TOS(tos); fl4->flowi4_proto = IPPROTO_ICMP; fl4->fl4_icmp_type = type; @@ -488,6 +492,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) struct flowi4 fl4; __be32 saddr; u8 tos; + u32 mark; struct net *net; struct sock *sk; @@ -584,6 +589,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) tos = icmp_pointers[type].error ? ((iph->tos & IPTOS_TOS_MASK) | IPTOS_PREC_INTERNETCONTROL) : iph->tos; + mark = IP4_REPLY_MARK(net, skb_in->mark); if (ip_options_echo(&icmp_param.replyopts.opt.opt, skb_in)) goto out_unlock; @@ -600,11 +606,12 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) icmp_param.skb = skb_in; icmp_param.offset = skb_network_offset(skb_in); inet_sk(sk)->tos = tos; + sk->sk_mark = mark; ipc.addr = iph->saddr; ipc.opt = &icmp_param.replyopts.opt; ipc.tx_flags = 0; - rt = icmp_route_lookup(net, &fl4, skb_in, iph, saddr, tos, + rt = icmp_route_lookup(net, &fl4, skb_in, iph, saddr, tos, mark, type, code, &icmp_param); if (IS_ERR(rt)) goto out_unlock; diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 6acb541c909..6dfec2f1821 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -417,12 +417,13 @@ struct dst_entry *inet_csk_route_req(struct sock *sk, struct net *net = sock_net(sk); int flags = inet_sk_flowi_flags(sk); - flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark, + flowi4_init_output(fl4, sk->sk_bound_dev_if, ireq->ir_mark, RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, sk->sk_protocol, flags, (opt && opt->opt.srr) ? opt->opt.faddr : ireq->rmt_addr, - ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport); + ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport, + sock_i_uid(sk)); security_req_classify_flow(req, flowi4_to_flowi(fl4)); rt = ip_route_output_flow(net, fl4, sk); if (IS_ERR(rt)) @@ -454,11 +455,12 @@ struct dst_entry *inet_csk_route_child_sock(struct sock *sk, rcu_read_lock(); opt = rcu_dereference(newinet->inet_opt); - flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark, + flowi4_init_output(fl4, sk->sk_bound_dev_if, inet_rsk(req)->ir_mark, RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, sk->sk_protocol, inet_sk_flowi_flags(sk), (opt && opt->opt.srr) ? opt->opt.faddr : ireq->rmt_addr, - ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport); + ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport, + sock_i_uid(sk)); security_req_classify_flow(req, flowi4_to_flowi(fl4)); rt = ip_route_output_flow(net, fl4, sk); if (IS_ERR(rt)) @@ -688,6 +690,8 @@ struct sock *inet_csk_clone_lock(const struct sock *sk, inet_sk(newsk)->inet_sport = inet_rsk(req)->loc_port; newsk->sk_write_space = sk_stream_write_space; + newsk->sk_mark = inet_rsk(req)->ir_mark; + newicsk->icsk_retransmits = 0; newicsk->icsk_backoff = 0; newicsk->icsk_probes_out = 0; diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 4bcabf3ab4c..8e20e940558 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1497,12 +1497,14 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr, daddr = replyopts.opt.opt.faddr; } - flowi4_init_output(&fl4, arg->bound_dev_if, 0, + flowi4_init_output(&fl4, arg->bound_dev_if, + IP4_REPLY_MARK(net, skb->mark), RT_TOS(arg->tos), RT_SCOPE_UNIVERSE, ip_hdr(skb)->protocol, ip_reply_arg_flowi_flags(arg), daddr, saddr, - tcp_hdr(skb)->source, tcp_hdr(skb)->dest); + tcp_hdr(skb)->source, tcp_hdr(skb)->dest, + arg->uid); security_skb_classify_flow(skb, flowi4_to_flowi(&fl4)); rt = ip_route_output_key(net, &fl4); if (IS_ERR(rt)) diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 111e5a40959..b83d82951ca 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -768,7 +768,8 @@ int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, sk->sk_protocol, - inet_sk_flowi_flags(sk), faddr, saddr, 0, 0); + inet_sk_flowi_flags(sk), faddr, saddr, 0, 0, + sock_i_uid(sk)); security_sk_classify_flow(sk, flowi4_to_flowi(&fl4)); rt = ip_route_output_flow(net, &fl4, sk); diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index dd44e0ab600..b8287330c57 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -572,7 +572,8 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, RT_SCOPE_UNIVERSE, inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, inet_sk_flowi_flags(sk) | FLOWI_FLAG_CAN_SLEEP, - daddr, saddr, 0, 0); + daddr, saddr, 0, 0, + sock_i_uid(sk)); if (!inet->hdrincl) { err = raw_probe_proto_opt(&fl4, msg); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index d35bbf0cf40..42cd979d163 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -500,7 +500,7 @@ void __ip_select_ident(struct iphdr *iph, struct dst_entry *dst, int more) } EXPORT_SYMBOL(__ip_select_ident); -static void __build_flow_key(struct flowi4 *fl4, const struct sock *sk, +static void __build_flow_key(struct flowi4 *fl4, struct sock *sk, const struct iphdr *iph, int oif, u8 tos, u8 prot, u32 mark, int flow_flags) @@ -516,11 +516,12 @@ static void __build_flow_key(struct flowi4 *fl4, const struct sock *sk, flowi4_init_output(fl4, oif, mark, tos, RT_SCOPE_UNIVERSE, prot, flow_flags, - iph->daddr, iph->saddr, 0, 0); + iph->daddr, iph->saddr, 0, 0, + sk ? sock_i_uid(sk) : 0); } static void build_skb_flow_key(struct flowi4 *fl4, const struct sk_buff *skb, - const struct sock *sk) + struct sock *sk) { const struct iphdr *iph = ip_hdr(skb); int oif = skb->dev->ifindex; @@ -531,7 +532,7 @@ static void build_skb_flow_key(struct flowi4 *fl4, const struct sk_buff *skb, __build_flow_key(fl4, sk, iph, oif, tos, prot, mark, 0); } -static void build_sk_flow_key(struct flowi4 *fl4, const struct sock *sk) +static void build_sk_flow_key(struct flowi4 *fl4, struct sock *sk) { const struct inet_sock *inet = inet_sk(sk); const struct ip_options_rcu *inet_opt; @@ -545,11 +546,12 @@ static void build_sk_flow_key(struct flowi4 *fl4, const struct sock *sk) RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, inet_sk_flowi_flags(sk), - daddr, inet->inet_saddr, 0, 0); + daddr, inet->inet_saddr, 0, 0, + sock_i_uid(sk)); rcu_read_unlock(); } -static void ip_rt_build_flow_key(struct flowi4 *fl4, const struct sock *sk, +static void ip_rt_build_flow_key(struct flowi4 *fl4, struct sock *sk, const struct sk_buff *skb) { if (skb) @@ -956,6 +958,9 @@ void ipv4_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu, struct flowi4 fl4; struct rtable *rt; + if (!mark) + mark = IP4_REPLY_MARK(net, skb->mark); + __build_flow_key(&fl4, NULL, iph, oif, RT_TOS(iph->tos), protocol, mark, flow_flags); rt = __ip_route_output_key(net, &fl4); @@ -973,6 +978,10 @@ static void __ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) struct rtable *rt; __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0); + + if (!fl4.flowi4_mark) + fl4.flowi4_mark = IP4_REPLY_MARK(sock_net(sk), skb->mark); + rt = __ip_route_output_key(sock_net(sk), &fl4); if (!IS_ERR(rt)) { __ip_rt_update_pmtu(rt, &fl4, mtu); @@ -2280,6 +2289,11 @@ static int rt_fill_info(struct net *net, __be32 dst, __be32 src, nla_put_u32(skb, RTA_MARK, fl4->flowi4_mark)) goto nla_put_failure; + if (!uid_eq(fl4->flowi4_uid, INVALID_UID) && + nla_put_u32(skb, RTA_UID, + from_kuid_munged(current_user_ns(), fl4->flowi4_uid))) + goto nla_put_failure; + error = rt->dst.error; if (rt_is_input_route(rt)) { @@ -2329,6 +2343,7 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) int err; int mark; struct sk_buff *skb; + kuid_t uid; err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv4_policy); if (err < 0) @@ -2356,6 +2371,10 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) dst = tb[RTA_DST] ? nla_get_be32(tb[RTA_DST]) : 0; iif = tb[RTA_IIF] ? nla_get_u32(tb[RTA_IIF]) : 0; mark = tb[RTA_MARK] ? nla_get_u32(tb[RTA_MARK]) : 0; + if (tb[RTA_UID]) + uid = make_kuid(current_user_ns(), nla_get_u32(tb[RTA_UID])); + else + uid = (iif ? INVALID_UID : current_uid()); memset(&fl4, 0, sizeof(fl4)); fl4.daddr = dst; @@ -2363,6 +2382,7 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) fl4.flowi4_tos = rtm->rtm_tos; fl4.flowi4_oif = tb[RTA_OIF] ? nla_get_u32(tb[RTA_OIF]) : 0; fl4.flowi4_mark = mark; + fl4.flowi4_uid = uid; if (iif) { struct net_device *dev; diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index b05c96e7af8..c94032b95c6 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -312,6 +312,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, ireq->rmt_port = th->source; ireq->loc_addr = ip_hdr(skb)->daddr; ireq->rmt_addr = ip_hdr(skb)->saddr; + ireq->ir_mark = inet_request_mark(sk, skb); ireq->ecn_ok = ecn_ok; ireq->snd_wscale = tcp_opt.snd_wscale; ireq->sack_ok = tcp_opt.sack_ok; @@ -348,11 +349,12 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, * hasn't changed since we received the original syn, but I see * no easy way to do this. */ - flowi4_init_output(&fl4, sk->sk_bound_dev_if, sk->sk_mark, + flowi4_init_output(&fl4, sk->sk_bound_dev_if, ireq->ir_mark, RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, IPPROTO_TCP, inet_sk_flowi_flags(sk), (opt && opt->srr) ? opt->faddr : ireq->rmt_addr, - ireq->loc_addr, th->source, th->dest); + ireq->loc_addr, th->source, th->dest, + sock_i_uid(sk)); security_req_classify_flow(req, flowi4_to_flowi(&fl4)); rt = ip_route_output_key(sock_net(sk), &fl4); if (IS_ERR(rt)) { diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index f9bb5d7488e..cc5fa7da12e 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -859,6 +859,20 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = ipv4_tcp_mem, }, + { + .procname = "fwmark_reflect", + .data = &init_net.ipv4.sysctl_fwmark_reflect, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { + .procname = "tcp_fwmark_accept", + .data = &init_net.ipv4.sysctl_tcp_fwmark_accept, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { } }; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 7999fc55c83..40ec14507f7 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1527,6 +1527,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) ireq->rmt_addr = saddr; ireq->no_srccheck = inet_sk(sk)->transparent; ireq->opt = tcp_v4_save_options(skb); + ireq->ir_mark = inet_request_mark(sk, skb); if (security_inet_conn_request(sk, skb, req)) goto drop_and_free; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 0bf5d399a03..35ab330ed95 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -962,7 +962,8 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, flowi4_init_output(fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, sk->sk_protocol, inet_sk_flowi_flags(sk)|FLOWI_FLAG_CAN_SLEEP, - faddr, saddr, dport, inet->inet_sport); + faddr, saddr, dport, inet->inet_sport, + sock_i_uid(sk)); security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); rt = ip_route_output_flow(net, fl4, sk); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 4ab4c38958c..cec8cb4d292 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -198,6 +198,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { .accept_ra_rt_info_max_plen = 0, #endif #endif + .accept_ra_rt_table = 0, .proxy_ndp = 0, .accept_source_route = 0, /* we do not accept RH0 by default. */ .disable_ipv6 = 0, @@ -232,6 +233,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .accept_ra_rt_info_max_plen = 0, #endif #endif + .accept_ra_rt_table = 0, .proxy_ndp = 0, .accept_source_route = 0, /* we do not accept RH0 by default. */ .disable_ipv6 = 0, @@ -1910,6 +1912,31 @@ static void __ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmp } #endif +u32 addrconf_rt_table(const struct net_device *dev, u32 default_table) { + /* Determines into what table to put autoconf PIO/RIO/default routes + * learned on this device. + * + * - If 0, use the same table for every device. This puts routes into + * one of RT_TABLE_{PREFIX,INFO,DFLT} depending on the type of route + * (but note that these three are currently all equal to + * RT6_TABLE_MAIN). + * - If > 0, use the specified table. + * - If < 0, put routes into table dev->ifindex + (-rt_table). + */ + struct inet6_dev *idev = in6_dev_get(dev); + u32 table; + int sysctl = idev->cnf.accept_ra_rt_table; + if (sysctl == 0) { + table = default_table; + } else if (sysctl > 0) { + table = (u32) sysctl; + } else { + table = (unsigned) dev->ifindex + (-sysctl); + } + in6_dev_put(idev); + return table; +} + /* * Add prefix route. */ @@ -1919,7 +1946,7 @@ addrconf_prefix_route(struct in6_addr *pfx, int plen, struct net_device *dev, unsigned long expires, u32 flags) { struct fib6_config cfg = { - .fc_table = RT6_TABLE_PREFIX, + .fc_table = addrconf_rt_table(dev, RT6_TABLE_PREFIX), .fc_metric = IP6_RT_PRIO_ADDRCONF, .fc_ifindex = dev->ifindex, .fc_expires = expires, @@ -1953,7 +1980,8 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx, struct rt6_info *rt = NULL; struct fib6_table *table; - table = fib6_get_table(dev_net(dev), RT6_TABLE_PREFIX); + table = fib6_get_table(dev_net(dev), + addrconf_rt_table(dev, RT6_TABLE_PREFIX)); if (table == NULL) return NULL; @@ -4159,6 +4187,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN] = cnf->accept_ra_rt_info_max_plen; #endif #endif + array[DEVCONF_ACCEPT_RA_RT_TABLE] = cnf->accept_ra_rt_table; array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp; array[DEVCONF_ACCEPT_SOURCE_ROUTE] = cnf->accept_source_route; #ifdef CONFIG_IPV6_OPTIMISTIC_DAD @@ -4868,6 +4897,13 @@ static struct addrconf_sysctl_table #endif #endif { + .procname = "accept_ra_rt_table", + .data = &ipv6_devconf.accept_ra_rt_table, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { .procname = "proxy_ndp", .data = &ipv6_devconf.proxy_ndp, .maxlen = sizeof(int), diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index f0081f9cc84..ae17f62189a 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -695,6 +695,7 @@ int inet6_sk_rebuild_header(struct sock *sk) fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = inet->inet_dport; fl6.fl6_sport = inet->inet_sport; + fl6.flowi6_uid = sock_i_uid(sk); security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); final_p = fl6_update_dst(&fl6, np->opt, &final); diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index bb02e176cb7..b903e19463c 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -630,7 +630,7 @@ static void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (type == NDISC_REDIRECT) ip6_redirect(skb, net, 0, 0); else - ip6_update_pmtu(skb, net, info, 0, 0); + ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); xfrm_state_put(x); } diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 4b56cbbc789..00b4a5f6eea 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -162,6 +162,7 @@ ipv4_connected: fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = inet->inet_dport; fl6.fl6_sport = inet->inet_sport; + fl6.flowi6_uid = sock_i_uid(sk); if (!fl6.flowi6_oif && (addr_type&IPV6_ADDR_MULTICAST)) fl6.flowi6_oif = np->mcast_oif; diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 40ffd72243a..fdc81cb29e8 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -449,7 +449,7 @@ static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (type == NDISC_REDIRECT) ip6_redirect(skb, net, 0, 0); else - ip6_update_pmtu(skb, net, info, 0, 0); + ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); xfrm_state_put(x); } diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 1d2902e6178..12b1a942dc9 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -90,7 +90,7 @@ static void icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, struct net *net = dev_net(skb->dev); if (type == ICMPV6_PKT_TOOBIG) - ip6_update_pmtu(skb, net, info, 0, 0); + ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); else if (type == NDISC_REDIRECT) ip6_redirect(skb, net, 0, 0); @@ -397,6 +397,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) int len; int hlimit; int err = 0; + u32 mark = IP6_REPLY_MARK(net, skb->mark); if ((u8 *)hdr < skb->head || (skb->network_header + sizeof(*hdr)) > skb->tail) @@ -462,6 +463,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) fl6.daddr = hdr->saddr; if (saddr) fl6.saddr = *saddr; + fl6.flowi6_mark = mark; fl6.flowi6_oif = iif; fl6.fl6_icmp_type = type; fl6.fl6_icmp_code = code; @@ -470,6 +472,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) sk = icmpv6_xmit_lock(net); if (sk == NULL) return; + sk->sk_mark = mark; np = inet6_sk(sk); if (!icmpv6_xrlim_allow(sk, type, &fl6)) @@ -551,6 +554,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) struct dst_entry *dst; int err = 0; int hlimit; + u32 mark = IP6_REPLY_MARK(net, skb->mark); saddr = &ipv6_hdr(skb)->daddr; @@ -567,11 +571,13 @@ static void icmpv6_echo_reply(struct sk_buff *skb) fl6.saddr = *saddr; fl6.flowi6_oif = skb->dev->ifindex; fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY; + fl6.flowi6_mark = mark; security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); sk = icmpv6_xmit_lock(net); if (sk == NULL) return; + sk->sk_mark = mark; np = inet6_sk(sk); if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index e4311cbc8b4..65a46058c85 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -81,9 +81,10 @@ struct dst_entry *inet6_csk_route_req(struct sock *sk, final_p = fl6_update_dst(fl6, np->opt, &final); fl6->saddr = treq->loc_addr; fl6->flowi6_oif = treq->iif; - fl6->flowi6_mark = sk->sk_mark; + fl6->flowi6_mark = inet_rsk(req)->ir_mark; fl6->fl6_dport = inet_rsk(req)->rmt_port; fl6->fl6_sport = inet_rsk(req)->loc_port; + fl6->flowi6_uid = sock_i_uid(sk); security_req_classify_flow(req, flowi6_to_flowi(fl6)); dst = ip6_dst_lookup_flow(sk, fl6, final_p, false); @@ -211,6 +212,7 @@ static struct dst_entry *inet6_csk_route_socket(struct sock *sk, fl6->flowi6_mark = sk->sk_mark; fl6->fl6_sport = inet->inet_sport; fl6->fl6_dport = inet->inet_dport; + fl6->flowi6_uid = sock_i_uid(sk); security_sk_classify_flow(sk, flowi6_to_flowi(fl6)); final_p = fl6_update_dst(fl6, np->opt, &final); diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c index 7af5aee75d9..a1beb59a841 100644 --- a/net/ipv6/ipcomp6.c +++ b/net/ipv6/ipcomp6.c @@ -78,7 +78,7 @@ static void ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (type == NDISC_REDIRECT) ip6_redirect(skb, net, 0, 0); else - ip6_update_pmtu(skb, net, info, 0, 0); + ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID); xfrm_state_put(x); } diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index e2cc17cd1cf..5f0d294b36c 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -158,6 +158,8 @@ int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, fl6.flowi6_proto = IPPROTO_ICMPV6; fl6.saddr = np->saddr; fl6.daddr = *daddr; + fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_uid = sock_i_uid(sk); fl6.fl6_icmp_type = user_icmph.icmp6_type; fl6.fl6_icmp_code = user_icmph.icmp6_code; security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index eedff8ccded..dfef31581f8 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -761,6 +761,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_uid = sock_i_uid(sk); if (sin6) { if (addr_len < SIN6_LEN_RFC2133) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index ad0aa6b0b86..bad36468dcd 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -85,13 +85,12 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb); #ifdef CONFIG_IPV6_ROUTE_INFO -static struct rt6_info *rt6_add_route_info(struct net *net, +static struct rt6_info *rt6_add_route_info(struct net_device *dev, const struct in6_addr *prefix, int prefixlen, - const struct in6_addr *gwaddr, int ifindex, - unsigned int pref); -static struct rt6_info *rt6_get_route_info(struct net *net, + const struct in6_addr *gwaddr, unsigned int pref); +static struct rt6_info *rt6_get_route_info(struct net_device *dev, const struct in6_addr *prefix, int prefixlen, - const struct in6_addr *gwaddr, int ifindex); + const struct in6_addr *gwaddr); #endif static u32 *ipv6_cow_metrics(struct dst_entry *dst, unsigned long old) @@ -643,7 +642,6 @@ static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict) int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, const struct in6_addr *gwaddr) { - struct net *net = dev_net(dev); struct route_info *rinfo = (struct route_info *) opt; struct in6_addr prefix_buf, *prefix; unsigned int pref; @@ -685,8 +683,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, prefix = &prefix_buf; } - rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr, - dev->ifindex); + rt = rt6_get_route_info(dev, prefix, rinfo->prefix_len, gwaddr); if (rt && !lifetime) { ip6_del_rt(rt); @@ -694,8 +691,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, } if (!rt && lifetime) - rt = rt6_add_route_info(net, prefix, rinfo->prefix_len, gwaddr, dev->ifindex, - pref); + rt = rt6_add_route_info(dev, prefix, rinfo->prefix_len, gwaddr, pref); else if (rt) rt->rt6i_flags = RTF_ROUTEINFO | (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); @@ -1103,7 +1099,7 @@ static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, } void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, - int oif, u32 mark) + int oif, u32 mark, kuid_t uid) { const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; struct dst_entry *dst; @@ -1111,11 +1107,12 @@ void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_oif = oif; - fl6.flowi6_mark = mark; + fl6.flowi6_mark = mark ? mark : IP6_REPLY_MARK(net, skb->mark); fl6.flowi6_flags = 0; fl6.daddr = iph->daddr; fl6.saddr = iph->saddr; fl6.flowlabel = ip6_flowinfo(iph); + fl6.flowi6_uid = uid; dst = ip6_route_output(net, NULL, &fl6); if (!dst->error) @@ -1127,7 +1124,7 @@ EXPORT_SYMBOL_GPL(ip6_update_pmtu); void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) { ip6_update_pmtu(skb, sock_net(sk), mtu, - sk->sk_bound_dev_if, sk->sk_mark); + sk->sk_bound_dev_if, sk->sk_mark, sock_i_uid(sk)); } EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); @@ -1796,15 +1793,16 @@ static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, } #ifdef CONFIG_IPV6_ROUTE_INFO -static struct rt6_info *rt6_get_route_info(struct net *net, +static struct rt6_info *rt6_get_route_info(struct net_device *dev, const struct in6_addr *prefix, int prefixlen, - const struct in6_addr *gwaddr, int ifindex) + const struct in6_addr *gwaddr) { struct fib6_node *fn; struct rt6_info *rt = NULL; struct fib6_table *table; - table = fib6_get_table(net, RT6_TABLE_INFO); + table = fib6_get_table(dev_net(dev), + addrconf_rt_table(dev, RT6_TABLE_INFO)); if (!table) return NULL; @@ -1814,7 +1812,7 @@ static struct rt6_info *rt6_get_route_info(struct net *net, goto out; for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { - if (rt->dst.dev->ifindex != ifindex) + if (rt->dst.dev->ifindex != dev->ifindex) continue; if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) continue; @@ -1828,21 +1826,20 @@ out: return rt; } -static struct rt6_info *rt6_add_route_info(struct net *net, +static struct rt6_info *rt6_add_route_info(struct net_device *dev, const struct in6_addr *prefix, int prefixlen, - const struct in6_addr *gwaddr, int ifindex, - unsigned int pref) + const struct in6_addr *gwaddr, unsigned int pref) { struct fib6_config cfg = { - .fc_table = RT6_TABLE_INFO, + .fc_table = addrconf_rt_table(dev, RT6_TABLE_INFO), .fc_metric = IP6_RT_PRIO_USER, - .fc_ifindex = ifindex, + .fc_ifindex = dev->ifindex, .fc_dst_len = prefixlen, .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | RTF_UP | RTF_PREF(pref), .fc_nlinfo.portid = 0, .fc_nlinfo.nlh = NULL, - .fc_nlinfo.nl_net = net, + .fc_nlinfo.nl_net = dev_net(dev), }; cfg.fc_dst = *prefix; @@ -1854,7 +1851,7 @@ static struct rt6_info *rt6_add_route_info(struct net *net, ip6_route_add(&cfg); - return rt6_get_route_info(net, prefix, prefixlen, gwaddr, ifindex); + return rt6_get_route_info(dev, prefix, prefixlen, gwaddr); } #endif @@ -1863,7 +1860,8 @@ struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_dev struct rt6_info *rt; struct fib6_table *table; - table = fib6_get_table(dev_net(dev), RT6_TABLE_DFLT); + table = fib6_get_table(dev_net(dev), + addrconf_rt_table(dev, RT6_TABLE_MAIN)); if (!table) return NULL; @@ -1885,7 +1883,7 @@ struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, unsigned int pref) { struct fib6_config cfg = { - .fc_table = RT6_TABLE_DFLT, + .fc_table = addrconf_rt_table(dev, RT6_TABLE_DFLT), .fc_metric = IP6_RT_PRIO_USER, .fc_ifindex = dev->ifindex, .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | @@ -1902,28 +1900,17 @@ struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, return rt6_get_dflt_router(gwaddr, dev); } -void rt6_purge_dflt_routers(struct net *net) -{ - struct rt6_info *rt; - struct fib6_table *table; - /* NOTE: Keep consistent with rt6_get_dflt_router */ - table = fib6_get_table(net, RT6_TABLE_DFLT); - if (!table) - return; +int rt6_addrconf_purge(struct rt6_info *rt, void *arg) { + if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && + (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) + return -1; + return 0; +} -restart: - read_lock_bh(&table->tb6_lock); - for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { - if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && - (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) { - dst_hold(&rt->dst); - read_unlock_bh(&table->tb6_lock); - ip6_del_rt(rt); - goto restart; - } - } - read_unlock_bh(&table->tb6_lock); +void rt6_purge_dflt_routers(struct net *net) +{ + fib6_clean_all(net, rt6_addrconf_purge, 0, NULL); } static void rtmsg_to_fib6_config(struct net *net, @@ -2213,6 +2200,7 @@ static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { [RTA_PRIORITY] = { .type = NLA_U32 }, [RTA_METRICS] = { .type = NLA_NESTED }, [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, + [RTA_UID] = { .type = NLA_U32 }, }; static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, @@ -2599,6 +2587,12 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh) if (tb[RTA_OIF]) oif = nla_get_u32(tb[RTA_OIF]); + if (tb[RTA_UID]) + fl6.flowi6_uid = make_kuid(current_user_ns(), + nla_get_u32(tb[RTA_UID])); + else + fl6.flowi6_uid = iif ? INVALID_UID : current_uid(); + if (iif) { struct net_device *dev; int flags = 0; diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index d5dda20bd71..ba8622daffd 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -212,6 +212,8 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) ipv6_addr_type(&ireq6->rmt_addr) & IPV6_ADDR_LINKLOCAL) ireq6->iif = inet6_iif(skb); + ireq->ir_mark = inet_request_mark(sk, skb); + req->expires = 0UL; req->num_retrans = 0; ireq->ecn_ok = ecn_ok; @@ -238,9 +240,10 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) final_p = fl6_update_dst(&fl6, np->opt, &final); fl6.saddr = ireq6->loc_addr; fl6.flowi6_oif = sk->sk_bound_dev_if; - fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_mark = ireq->ir_mark; fl6.fl6_dport = inet_rsk(req)->rmt_port; fl6.fl6_sport = inet_sk(sk)->inet_sport; + fl6.flowi6_uid = sock_i_uid(sk); security_req_classify_flow(req, flowi6_to_flowi(&fl6)); dst = ip6_dst_lookup_flow(sk, &fl6, final_p, false); diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index e85c48bd404..53a9f5a6453 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c @@ -24,6 +24,13 @@ static ctl_table ipv6_table_template[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "fwmark_reflect", + .data = &init_net.ipv6.sysctl.fwmark_reflect, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { } }; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 0a17ed9eaf3..a4fc647deb0 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -252,6 +252,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = usin->sin6_port; fl6.fl6_sport = inet->inet_sport; + fl6.flowi6_uid = sock_i_uid(sk); final_p = fl6_update_dst(&fl6, np->opt, &final); @@ -791,6 +792,7 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, fl6.flowi6_proto = IPPROTO_TCP; if (ipv6_addr_type(&fl6.daddr) & IPV6_ADDR_LINKLOCAL) fl6.flowi6_oif = inet6_iif(skb); + fl6.flowi6_mark = IP6_REPLY_MARK(net, skb->mark); fl6.fl6_dport = t1->dest; fl6.fl6_sport = t1->source; security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); @@ -999,6 +1001,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) TCP_ECN_create_request(req, skb, sock_net(sk)); treq->iif = sk->sk_bound_dev_if; + inet_rsk(req)->ir_mark = inet_request_mark(sk, skb); /* So that link locals have meaning */ if (!sk->sk_bound_dev_if && diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 42923b14dfa..e6dd85da906 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1147,6 +1147,7 @@ do_udp_sendmsg: fl6.flowi6_oif = np->sticky_pktinfo.ipi6_ifindex; fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_uid = sock_i_uid(sk); if (msg->msg_controllen) { opt = &opt_space; |