summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/power/omap-core-dvfs.txt48
-rw-r--r--Documentation/devicetree/bindings/power_supply/max17042_battery.txt16
-rw-r--r--Documentation/networking/ip-sysctl.txt28
-rw-r--r--arch/arm/boot/dts/omap3-minnow.dtsi93
-rw-r--r--arch/arm/configs/minnow_defconfig3
-rw-r--r--arch/arm/mach-omap2/cclock3xxx_data.c9
-rw-r--r--arch/arm/mach-omap2/clkt34xx_dpll3m2.c7
-rw-r--r--arch/arm/mach-omap2/control.c4
-rw-r--r--arch/arm/mach-omap2/omap-secure.c5
-rw-r--r--arch/arm/mach-omap2/omap-secure.h1
-rw-r--r--arch/arm/mach-omap2/pm-debug.c18
-rw-r--r--arch/arm/mach-omap2/pm.c7
-rw-r--r--arch/arm/mach-omap2/pm.h2
-rw-r--r--arch/arm/mach-omap2/pm34xx.c36
-rw-r--r--arch/arm/mach-omap2/sdram-toshiba-hynix-numonyx.h24
-rw-r--r--drivers/base/firmware_class.c46
-rw-r--r--drivers/cpufreq/cpufreq-cpu0.c8
-rwxr-xr-xdrivers/gpu/pvr/services4/srvkm/env/linux/osfunc.c2
-rw-r--r--drivers/leds/leds-lm3535.c158
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/m4sensorhub-core.c74
-rw-r--r--drivers/mfd/m4sensorhub-extern.c158
-rw-r--r--drivers/mfd/m4sensorhub-panic.c1
-rw-r--r--drivers/mfd/m4sensorhub-reg.h10
-rw-r--r--drivers/misc/Kconfig7
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/als_notify.c50
-rw-r--r--drivers/misc/c55_ctrl.c16
-rw-r--r--drivers/misc/m4sensorhub_als.c118
-rw-r--r--drivers/misc/m4sensorhub_fusion.c84
-rw-r--r--drivers/misc/m4sensorhub_gesture.c8
-rw-r--r--drivers/misc/m4sensorhub_mpu9150.c64
-rw-r--r--drivers/misc/m4sensorhub_pedometer.c194
-rw-r--r--drivers/mmc/card/block.c2
-rw-r--r--drivers/power/avs/Makefile2
-rw-r--r--drivers/power/avs/omap_core_dvfs.c303
-rw-r--r--drivers/power/max17042_battery.c148
-rw-r--r--drivers/regulator/omap-pmic-regulator.c17
-rw-r--r--drivers/rtc/rtc-sensorhub.c3
-rw-r--r--drivers/tty/serial/omap-serial.c8
-rw-r--r--drivers/video/omap2/displays/panel-minnow-common.h6
-rw-r--r--drivers/video/omap2/displays/panel-minnow.c145
-rw-r--r--include/linux/als_notify.h36
-rw-r--r--include/linux/iio/m4sensorhub/m4sensorhub_fusion.h4
-rw-r--r--include/linux/iio/m4sensorhub/m4sensorhub_pedometer.h1
-rw-r--r--include/linux/ipv6.h1
-rw-r--r--include/linux/m4sensorhub.h7
-rw-r--r--include/linux/m4sensorhub/MemMapLog.h111
-rw-r--r--include/linux/m4sensorhub/m4sensorhub_reg_enum.h8
-rw-r--r--include/linux/power/max17042_battery.h3
-rw-r--r--include/net/addrconf.h2
-rw-r--r--include/net/fib_rules.h6
-rw-r--r--include/net/flow.h9
-rw-r--r--include/net/inet_sock.h9
-rw-r--r--include/net/ip.h4
-rw-r--r--include/net/ip6_route.h2
-rw-r--r--include/net/ipv6.h3
-rw-r--r--include/net/netns/ipv4.h3
-rw-r--r--include/net/netns/ipv6.h1
-rw-r--r--include/net/route.h5
-rw-r--r--include/uapi/linux/fib_rules.h2
-rw-r--r--include/uapi/linux/ipv6.h1
-rw-r--r--include/uapi/linux/rtnetlink.h1
-rw-r--r--kernel/time/alarmtimer.c3
-rw-r--r--net/core/fib_rules.c56
-rw-r--r--net/ipv4/fib_frontend.c1
-rw-r--r--net/ipv4/icmp.c11
-rw-r--r--net/ipv4/inet_connection_sock.c12
-rw-r--r--net/ipv4/ip_output.c6
-rw-r--r--net/ipv4/ping.c3
-rw-r--r--net/ipv4/raw.c3
-rw-r--r--net/ipv4/route.c32
-rw-r--r--net/ipv4/syncookies.c6
-rw-r--r--net/ipv4/sysctl_net_ipv4.c14
-rw-r--r--net/ipv4/tcp_ipv4.c1
-rw-r--r--net/ipv4/udp.c3
-rw-r--r--net/ipv6/addrconf.c40
-rw-r--r--net/ipv6/af_inet6.c1
-rw-r--r--net/ipv6/ah6.c2
-rw-r--r--net/ipv6/datagram.c1
-rw-r--r--net/ipv6/esp6.c2
-rw-r--r--net/ipv6/icmp.c8
-rw-r--r--net/ipv6/inet6_connection_sock.c4
-rw-r--r--net/ipv6/ipcomp6.c2
-rw-r--r--net/ipv6/ping.c2
-rw-r--r--net/ipv6/raw.c1
-rw-r--r--net/ipv6/route.c86
-rw-r--r--net/ipv6/syncookies.c5
-rw-r--r--net/ipv6/sysctl_net_ipv6.c7
-rw-r--r--net/ipv6/tcp_ipv6.c3
-rw-r--r--net/ipv6/udp.c1
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;