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; |