Merge "ARM: dts: msm8226: Add GPU ID for version 2"
diff --git a/Documentation/devicetree/bindings/coresight/coresight.txt b/Documentation/devicetree/bindings/coresight/coresight.txt
index ce797d3..8d1d46d 100644
--- a/Documentation/devicetree/bindings/coresight/coresight.txt
+++ b/Documentation/devicetree/bindings/coresight/coresight.txt
@@ -226,8 +226,9 @@
compatible = "qcom,coresight-hwevent";
reg = <0xfdf30018 0x80>,
<0xf9011080 0x80>,
- <0xfd4ab160 0x80>;
- reg-names = "mmss-mux", "apcs-mux", "ppss-mux";
+ <0xfd4ab160 0x80>,
+ <0xfc401600 0x80>;
+ reg-names = "mmss-mux", "apcs-mux", "ppss-mux", "gcc-mux";
coresight-id = <29>;
coresight-name = "coresight-hwevent";
diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt
index 4e16ba4..a336287 100644
--- a/Documentation/devicetree/bindings/gpio/gpio.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio.txt
@@ -75,4 +75,40 @@
gpio-controller;
};
+2.1) gpio-controller and pinctrl subsystem
+------------------------------------------
+gpio-controller on a SOC might be tightly coupled with the pinctrl
+subsystem, in the sense that the pins can be used by other functions
+together with optional gpio feature.
+
+While the pin allocation is totally managed by the pin ctrl subsystem,
+gpio (under gpiolib) is still maintained by gpio drivers. It may happen
+that different pin ranges in a SoC is managed by different gpio drivers.
+
+This makes it logical to let gpio drivers announce their pin ranges to
+the pin ctrl subsystem and call 'pinctrl_request_gpio' in order to
+request the corresponding pin before any gpio usage.
+
+For this, the gpio controller can use a pinctrl phandle and pins to
+announce the pinrange to the pin ctrl subsystem. For example,
+
+ qe_pio_e: gpio-controller@1460 {
+ #gpio-cells = <2>;
+ compatible = "fsl,qe-pario-bank-e", "fsl,qe-pario-bank";
+ reg = <0x1460 0x18>;
+ gpio-controller;
+ gpio-ranges = <&pinctrl1 20 10>, <&pinctrl2 50 20>;
+
+ }
+
+where,
+ &pinctrl1 and &pinctrl2 is the phandle to the pinctrl DT node.
+
+ Next values specify the base pin and number of pins for the range
+ handled by 'qe_pio_e' gpio. In the given example from base pin 20 to
+ pin 29 under pinctrl1 and pin 50 to pin 69 under pinctrl2 is handled
+ by this gpio controller.
+
+The pinctrl node must have "#gpio-range-cells" property to show number of
+arguments to pass with phandle from gpio controllers node.
diff --git a/Documentation/devicetree/bindings/media/video/msm-cci.txt b/Documentation/devicetree/bindings/media/video/msm-cci.txt
index a2503cd..c01193c 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cci.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cci.txt
@@ -37,8 +37,10 @@
MSM sensor node contains properties of camera sensor
Required properties:
-- compatible : should be "qcom" followed by sensor name
+- compatible : should be manufacturer name followed by sensor name
- "qcom,s5k3l1yx"
+ - "shinetech,gc0339"
+ - "shinetech,hi256"
- reg : should contain i2c slave address of the device
- qcom,slave-id : should contain i2c slave address, device id address
and expected id read value
diff --git a/Documentation/devicetree/bindings/nfc/nfc-nci.txt b/Documentation/devicetree/bindings/nfc/nfc-nci.txt
new file mode 100644
index 0000000..f70d90f
--- /dev/null
+++ b/Documentation/devicetree/bindings/nfc/nfc-nci.txt
@@ -0,0 +1,28 @@
+Qualcomm QCA199x NFC NCI device
+
+Near Field Communication (NFC) device is based on NFC Controller Interface (NCI)
+
+Required properties:
+
+- compatible: "qcom,nfc-nci"
+- reg: NCI i2c slave address.
+- qcom,dis-gpio: specific gpio for hardware reset.
+- qcom,irq-gpio: specific gpio for read interrupt.
+- interrupt-parent: Should be phandle for the interrupt controller
+ that services interrupts for this device.
+- interrupts: should contain the NFC interrupt. NFC has one read interrupt.
+- qcom,clk-gpio: pmic gpio on which bbclk2 signal is coming.
+
+Example:
+
+ i2c@f9925000 { /* BLSP1 QUP3 */
+ nfc-nci@0e {
+ compatible = "qcom,nfc-nci";
+ reg = <0x0e>;
+ qcom,irq-gpio = <&msmgpio 21 0x00>;
+ qcom,dis-gpio = <&msmgpio 20 0x00>;
+ interrupt-parent = <&msmgpio>;
+ interrupts = <21 0>;
+ qcom,clk-gpio = <&pm8226_gpios 3 0>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
index ebb3134..ac314ea 100644
--- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
@@ -50,7 +50,10 @@
- Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for
below optional properties:
- qcom,msm_bus,name
- - qcom,msm_bus,num_cases
+ - qcom,msm_bus,num_cases - There are three valid cases for this: NONE, MAX
+ and MIN bandwidth votes. Minimum two cases must be defined for
+ both NONE and MAX votes. If MIN vote is different from NONE VOTE
+ then specify third case for MIN VOTE.
- qcom,msm_bus,active_only
- qcom,msm_bus,num_paths
- qcom,msm_bus,vectors
@@ -76,6 +79,8 @@
HSPHY.
- qcom,hsusb-log2-itc: value of 2^(log2_itc-1) will be used as the
interrupt threshold (ITC), when log2_itc is between 1 to 7.
+- qcom,hsusb-l1-supported: If present, the device supports l1 (Link power
+ management).
Example HSUSB OTG controller device node :
usb@f9690000 {
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 8fa9f4a..0ebbe63 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -40,6 +40,7 @@
samsung Samsung Semiconductor
sbs Smart Battery System
schindler Schindler
+shinetech Shine Tech Corporation, Ltd.
sil Silicon Image
simtek
sirf SiRF Technology, Inc.
diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt
index 2a596a4..ef4fa7b 100644
--- a/Documentation/driver-model/devres.txt
+++ b/Documentation/driver-model/devres.txt
@@ -276,3 +276,7 @@
devm_regulator_get()
devm_regulator_put()
devm_regulator_bulk_get()
+
+PINCTRL
+ devm_pinctrl_get()
+ devm_pinctrl_put()
diff --git a/Documentation/gpio.txt b/Documentation/gpio.txt
index 620a078..1d5b334 100644
--- a/Documentation/gpio.txt
+++ b/Documentation/gpio.txt
@@ -436,6 +436,48 @@
signaling rate accordingly.
+GPIO controllers and the pinctrl subsystem
+------------------------------------------
+
+A GPIO controller on a SOC might be tightly coupled with the pinctrl
+subsystem, in the sense that the pins can be used by other functions
+together with an optional gpio feature. We have already covered the
+case where e.g. a GPIO controller need to reserve a pin or set the
+direction of a pin by calling any of:
+
+pinctrl_request_gpio()
+pinctrl_free_gpio()
+pinctrl_gpio_direction_input()
+pinctrl_gpio_direction_output()
+
+But how does the pin control subsystem cross-correlate the GPIO
+numbers (which are a global business) to a certain pin on a certain
+pin controller?
+
+This is done by registering "ranges" of pins, which are essentially
+cross-reference tables. These are described in
+Documentation/pinctrl.txt
+
+While the pin allocation is totally managed by the pinctrl subsystem,
+gpio (under gpiolib) is still maintained by gpio drivers. It may happen
+that different pin ranges in a SoC is managed by different gpio drivers.
+
+This makes it logical to let gpio drivers announce their pin ranges to
+the pin ctrl subsystem before it will call 'pinctrl_request_gpio' in order
+to request the corresponding pin to be prepared by the pinctrl subsystem
+before any gpio usage.
+
+For this, the gpio controller can register its pin range with pinctrl
+subsystem. There are two ways of doing it currently: with or without DT.
+
+For with DT support refer to Documentation/devicetree/bindings/gpio/gpio.txt.
+
+For non-DT support, user can call gpiochip_add_pin_range() with appropriate
+parameters to register a range of gpio pins with a pinctrl driver. For this
+exact name string of pinctrl device has to be passed as one of the
+argument to this routine.
+
+
What do these conventions omit?
===============================
One of the biggest things these conventions omit is pin multiplexing, since
diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt
index d97bccf..e454a6a 100644
--- a/Documentation/pinctrl.txt
+++ b/Documentation/pinctrl.txt
@@ -152,11 +152,9 @@
};
-static int foo_list_groups(struct pinctrl_dev *pctldev, unsigned selector)
+static int foo_get_groups_count(struct pinctrl_dev *pctldev)
{
- if (selector >= ARRAY_SIZE(foo_groups))
- return -EINVAL;
- return 0;
+ return ARRAY_SIZE(foo_groups);
}
static const char *foo_get_group_name(struct pinctrl_dev *pctldev,
@@ -175,7 +173,7 @@
}
static struct pinctrl_ops foo_pctrl_ops = {
- .list_groups = foo_list_groups,
+ .get_groups_count = foo_get_groups_count,
.get_group_name = foo_get_group_name,
.get_group_pins = foo_get_group_pins,
};
@@ -186,13 +184,12 @@
.pctlops = &foo_pctrl_ops,
};
-The pin control subsystem will call the .list_groups() function repeatedly
-beginning on 0 until it returns non-zero to determine legal selectors, then
-it will call the other functions to retrieve the name and pins of the group.
-Maintaining the data structure of the groups is up to the driver, this is
-just a simple example - in practice you may need more entries in your group
-structure, for example specific register ranges associated with each group
-and so on.
+The pin control subsystem will call the .get_groups_count() function to
+determine total number of legal selectors, then it will call the other functions
+to retrieve the name and pins of the group. Maintaining the data structure of
+the groups is up to the driver, this is just a simple example - in practice you
+may need more entries in your group structure, for example specific register
+ranges associated with each group and so on.
Pin configuration
@@ -362,6 +359,10 @@
the range ID value, so that the pin controller knows which range it should
deal with.
+Calling pinctrl_add_gpio_range from pinctrl driver is DEPRECATED. Please see
+section 2.1 of Documentation/devicetree/bindings/gpio/gpio.txt on how to bind
+pinctrl and gpio drivers.
+
PINMUX interfaces
=================
@@ -606,11 +607,9 @@
};
-static int foo_list_groups(struct pinctrl_dev *pctldev, unsigned selector)
+static int foo_get_groups_count(struct pinctrl_dev *pctldev)
{
- if (selector >= ARRAY_SIZE(foo_groups))
- return -EINVAL;
- return 0;
+ return ARRAY_SIZE(foo_groups);
}
static const char *foo_get_group_name(struct pinctrl_dev *pctldev,
@@ -629,7 +628,7 @@
}
static struct pinctrl_ops foo_pctrl_ops = {
- .list_groups = foo_list_groups,
+ .get_groups_count = foo_get_groups_count,
.get_group_name = foo_get_group_name,
.get_group_pins = foo_get_group_pins,
};
@@ -663,11 +662,9 @@
},
};
-int foo_list_funcs(struct pinctrl_dev *pctldev, unsigned selector)
+int foo_get_functions_count(struct pinctrl_dev *pctldev)
{
- if (selector >= ARRAY_SIZE(foo_functions))
- return -EINVAL;
- return 0;
+ return ARRAY_SIZE(foo_functions);
}
const char *foo_get_fname(struct pinctrl_dev *pctldev, unsigned selector)
@@ -703,7 +700,7 @@
}
struct pinmux_ops foo_pmxops = {
- .list_functions = foo_list_funcs,
+ .get_functions_count = foo_get_functions_count,
.get_function_name = foo_get_fname,
.get_function_groups = foo_get_groups,
.enable = foo_enable,
@@ -952,13 +949,13 @@
The result of grabbing this mapping from the device with something like
this (see next paragraph):
- p = pinctrl_get(dev);
+ p = devm_pinctrl_get(dev);
s = pinctrl_lookup_state(p, "8bit");
ret = pinctrl_select_state(p, s);
or more simply:
- p = pinctrl_get_select(dev, "8bit");
+ p = devm_pinctrl_get_select(dev, "8bit");
Will be that you activate all the three bottom records in the mapping at
once. Since they share the same name, pin controller device, function and
@@ -970,6 +967,18 @@
Pinmux requests from drivers
============================
+When a device driver is about to probe the device core will automatically
+attempt to issue pinctrl_get_select_default() on these devices.
+This way driver writers do not need to add any of the boilerplate code
+of the type found below. However when doing fine-grained state selection
+and not using the "default" state, you may have to do some device driver
+handling of the pinctrl handles and states.
+
+So if you just want to put the pins for a certain device into the default
+state and be done with it, there is nothing you need to do besides
+providing the proper mapping table. The device core will take care of
+the rest.
+
Generally it is discouraged to let individual drivers get and enable pin
control. So if possible, handle the pin control in platform code or some other
place where you have access to all the affected struct device * pointers. In
@@ -992,7 +1001,7 @@
/* Allocate a state holder named "foo" etc */
struct foo_state *foo = ...;
- foo->p = pinctrl_get(&device);
+ foo->p = devm_pinctrl_get(&device);
if (IS_ERR(foo->p)) {
/* FIXME: clean up "foo" here */
return PTR_ERR(foo->p);
@@ -1000,24 +1009,17 @@
foo->s = pinctrl_lookup_state(foo->p, PINCTRL_STATE_DEFAULT);
if (IS_ERR(foo->s)) {
- pinctrl_put(foo->p);
/* FIXME: clean up "foo" here */
return PTR_ERR(s);
}
ret = pinctrl_select_state(foo->s);
if (ret < 0) {
- pinctrl_put(foo->p);
/* FIXME: clean up "foo" here */
return ret;
}
}
-foo_remove()
-{
- pinctrl_put(state->p);
-}
-
This get/lookup/select/put sequence can just as well be handled by bus drivers
if you don't want each and every driver to handle it and you know the
arrangement on your bus.
@@ -1029,6 +1031,11 @@
kernel memory to hold the pinmux state. All mapping table parsing or similar
slow operations take place within this API.
+- devm_pinctrl_get() is a variant of pinctrl_get() that causes pinctrl_put()
+ to be called automatically on the retrieved pointer when the associated
+ device is removed. It is recommended to use this function over plain
+ pinctrl_get().
+
- pinctrl_lookup_state() is called in process context to obtain a handle to a
specific state for a the client device. This operation may be slow too.
@@ -1041,14 +1048,75 @@
- pinctrl_put() frees all information associated with a pinctrl handle.
+- devm_pinctrl_put() is a variant of pinctrl_put() that may be used to
+ explicitly destroy a pinctrl object returned by devm_pinctrl_get().
+ However, use of this function will be rare, due to the automatic cleanup
+ that will occur even without calling it.
+
+ pinctrl_get() must be paired with a plain pinctrl_put().
+ pinctrl_get() may not be paired with devm_pinctrl_put().
+ devm_pinctrl_get() can optionally be paired with devm_pinctrl_put().
+ devm_pinctrl_get() may not be paired with plain pinctrl_put().
+
Usually the pin control core handled the get/put pair and call out to the
device drivers bookkeeping operations, like checking available functions and
the associated pins, whereas the enable/disable pass on to the pin controller
driver which takes care of activating and/or deactivating the mux setting by
quickly poking some registers.
-The pins are allocated for your device when you issue the pinctrl_get() call,
-after this you should be able to see this in the debugfs listing of all pins.
+The pins are allocated for your device when you issue the devm_pinctrl_get()
+call, after this you should be able to see this in the debugfs listing of all
+pins.
+
+NOTE: the pinctrl system will return -EPROBE_DEFER if it cannot find the
+requested pinctrl handles, for example if the pinctrl driver has not yet
+registered. Thus make sure that the error path in your driver gracefully
+cleans up and is ready to retry the probing later in the startup process.
+
+
+Drivers needing both pin control and GPIOs
+==========================================
+
+Again, it is discouraged to let drivers lookup and select pin control states
+themselves, but again sometimes this is unavoidable.
+
+So say that your driver is fetching its resources like this:
+
+#include <linux/pinctrl/consumer.h>
+#include <linux/gpio.h>
+
+struct pinctrl *pinctrl;
+int gpio;
+
+pinctrl = devm_pinctrl_get_select_default(&dev);
+gpio = devm_gpio_request(&dev, 14, "foo");
+
+Here we first request a certain pin state and then request GPIO 14 to be
+used. If you're using the subsystems orthogonally like this, you should
+nominally always get your pinctrl handle and select the desired pinctrl
+state BEFORE requesting the GPIO. This is a semantic convention to avoid
+situations that can be electrically unpleasant, you will certainly want to
+mux in and bias pins in a certain way before the GPIO subsystems starts to
+deal with them.
+
+The above can be hidden: using the device core, the pinctrl core may be
+setting up the config and muxing for the pins right before the device is
+probing, nevertheless orthogonal to the GPIO subsystem.
+
+But there are also situations where it makes sense for the GPIO subsystem
+to communicate directly with with the pinctrl subsystem, using the latter
+as a back-end. This is when the GPIO driver may call out to the functions
+described in the section "Pin control interaction with the GPIO subsystem"
+above. This only involves per-pin multiplexing, and will be completely
+hidden behind the gpio_*() function namespace. In this case, the driver
+need not interact with the pin control subsystem at all.
+
+If a pin control driver and a GPIO driver is dealing with the same pins
+and the use cases involve multiplexing, you MUST implement the pin controller
+as a back-end for the GPIO driver like this, unless your hardware design
+is such that the GPIO controller can override the pin controller's
+multiplexing state through hardware without the need to interact with the
+pin control system.
System pin control hogging
@@ -1094,13 +1162,13 @@
#include <linux/pinctrl/consumer.h>
-foo_switch()
-{
- struct pinctrl *p;
- struct pinctrl_state *s1, *s2;
+struct pinctrl *p;
+struct pinctrl_state *s1, *s2;
+foo_probe()
+{
/* Setup */
- p = pinctrl_get(&device);
+ p = devm_pinctrl_get(&device);
if (IS_ERR(p))
...
@@ -1111,7 +1179,10 @@
s2 = pinctrl_lookup_state(foo->p, "pos-B");
if (IS_ERR(s2))
...
+}
+foo_switch()
+{
/* Enable on position A */
ret = pinctrl_select_state(s1);
if (ret < 0)
@@ -1125,8 +1196,8 @@
...
...
-
- pinctrl_put(p);
}
-The above has to be done from process context.
+The above has to be done from process context. The reservation of the pins
+will be done when the state is activated, so in effect one specific pin
+can be used by different functions at different times on a running system.
diff --git a/arch/arm/boot/dts/dsi-panel-hx8394a-720p-video.dtsi b/arch/arm/boot/dts/dsi-panel-hx8394a-720p-video.dtsi
new file mode 100644
index 0000000..25c1851
--- /dev/null
+++ b/arch/arm/boot/dts/dsi-panel-hx8394a-720p-video.dtsi
@@ -0,0 +1,107 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+ qcom,mdss_dsi_hx8394a_720p_video {
+ compatible = "qcom,mdss-dsi-panel";
+ label = "hx8394a 720p video mode dsi panel";
+ status = "disable";
+ qcom,dsi-ctrl-phandle = <&mdss_dsi0>;
+ qcom,rst-gpio = <&msmgpio 25 0>;
+ qcom,mdss-pan-res = <720 1280>;
+ qcom,mdss-pan-bpp = <24>;
+ qcom,mdss-pan-dest = "display_1";
+ qcom,mdss-pan-porch-values = <59 60 79 10 2 7>;
+ qcom,mdss-pan-underflow-clr = <0xff>;
+ qcom,mdss-pan-bl-ctrl = "bl_ctrl_wled";
+ qcom,mdss-pan-bl-levels = <1 4095>;
+ qcom,mdss-pan-dsi-mode = <0>;
+ qcom,mdss-pan-dsi-h-pulse-mode = <1>;
+ qcom,mdss-pan-dsi-h-power-stop = <0 0 0>;
+ qcom,mdss-pan-dsi-bllp-power-stop = <1 1>;
+ qcom,mdss-pan-dsi-traffic-mode = <2>;
+ qcom,mdss-pan-dsi-dst-format = <3>;
+ qcom,mdss-pan-dsi-vc = <0>;
+ qcom,mdss-pan-dsi-rgb-swap = <0>;
+ qcom,mdss-pan-dsi-data-lanes = <1 1 1 1>; /* 4 lanes */
+ qcom,mdss-pan-dsi-dlane-swap = <0>;
+ qcom,mdss-pan-dsi-t-clk = <0x2d 0x1f>;
+ qcom,mdss-pan-dsi-stream = <0>;
+ qcom,mdss-pan-dsi-mdp-tr = <0x0>;
+ qcom,mdss-pan-dsi-dma-tr = <0x04>;
+ qcom,mdss-pan-dsi-frame-rate = <60>;
+ qcom,panel-phy-regulatorSettings = [07 09 03 00 /* Regualotor settings */
+ 20 00 01];
+ qcom,panel-phy-timingSettings = [8d 24 19 00 34 34
+ 1d 26 2a 03 04 00];
+ qcom,panel-phy-strengthCtrl = [ff 06];
+ qcom,panel-phy-bistCtrl = [00 00 b1 ff /* BIST Ctrl settings */
+ 00 00];
+ qcom,panel-phy-laneConfig = [00 00 00 00 00 00 00 01 97 /* lane0 config */
+ 00 00 00 00 05 00 00 01 97 /* lane1 config */
+ 00 00 00 00 0a 00 00 01 97 /* lane2 config */
+ 00 00 00 00 0f 00 00 01 97 /* lane3 config */
+ 00 c0 00 00 00 00 00 01 bb]; /* Clk ln config */
+ qcom,panel-on-cmds = [39 01 00 00 00 00 04
+ b9 ff 83 94
+ 39 01 00 00 00 00 05
+ c7 00 10 00 10
+ 39 01 00 00 00 00 02
+ bc 07
+ 39 01 00 00 00 00 02
+ ba 13
+ 39 01 00 00 00 00 10
+ b1 01 00 07 83 01
+ 12 0f 32 38 29 29
+ 50 02 00 00
+ 39 01 00 00 00 00 07
+ b2 00 c8 09 05 00
+ 71
+ 39 01 00 00 00 00 02
+ cc 05
+ 05 01 00 00 00 00 02 00 00
+ 39 01 00 00 00 00 35
+ d5 00 00 00 00 0a
+ 00 01 00 00 00 33
+ 00 23 45 67 01 01
+ 23 88 88 88 88 88
+ 88 88 99 99 99 88
+ 88 99 88 54 32 10
+ 76 32 10 88 88 88
+ 88 88 88 88 99 99
+ 99 88 88 88 99
+ 39 01 00 00 00 00 17
+ b4 80 08 32 10 00
+ 32 15 08 32 12 20
+ 33 05 4c 05 37 05
+ 3f 1e 5f 5f 06
+ 39 01 00 00 00 00 02
+ b6 00
+ 39 01 00 00 00 00 23
+ e0 01 05 07 25 35
+ 3f 0b 32 04 09 0e
+ 10 13 10 14 16 1b
+ 01 05 07 25 35 3f
+ 0b 32 04 09 0e 10
+ 13 10 14 16 1b
+ 05 01 00 00 00 00 02 00 00
+ 39 01 00 00 00 00 04
+ bf 06 00 10
+ 05 01 00 00 c8 00 02 11 00
+ 05 01 00 00 32 00 02 29 00];
+
+ qcom,on-cmds-dsi-state = "DSI_LP_MODE";
+ qcom,panel-off-cmds = [05 01 00 00 0a 00 02 28 00
+ 05 01 00 00 96 00 02 10 00];
+ qcom,off-cmds-dsi-state = "DSI_HS_MODE";
+ };
+};
diff --git a/arch/arm/boot/dts/dsi-panel-nt35590-720p-cmd.dtsi b/arch/arm/boot/dts/dsi-panel-nt35590-720p-cmd.dtsi
index 7942567..2a6bbf9 100644
--- a/arch/arm/boot/dts/dsi-panel-nt35590-720p-cmd.dtsi
+++ b/arch/arm/boot/dts/dsi-panel-nt35590-720p-cmd.dtsi
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-/ {
+&soc {
qcom,mdss_dsi_nt35590_720p_cmd {
compatible = "qcom,mdss-dsi-panel";
label = "nt35590 720p command mode dsi panel";
@@ -58,473 +58,474 @@
00 00 00 00 0a 00 00 01 97 /* lane2 config */
00 00 00 00 0f 00 00 01 97 /* lane3 config */
00 c0 00 00 00 00 00 01 bb]; /* Clk ln config */
- qcom,panel-on-cmds = [29 01 00 00 00 02 FF EE
- 29 01 00 00 00 02 26 08
- 29 01 00 00 00 02 26 00
- 29 01 00 00 10 02 FF 00
- 29 01 00 00 00 02 BA 03
- 29 01 00 00 00 02 C2 08
- 29 01 00 00 00 02 FF 01
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 00 4A
- 29 01 00 00 00 02 01 33
- 29 01 00 00 00 02 02 53
- 29 01 00 00 00 02 03 55
- 29 01 00 00 00 02 04 55
- 29 01 00 00 00 02 05 33
- 29 01 00 00 00 02 06 22
- 29 01 00 00 00 02 08 56
- 29 01 00 00 00 02 09 8F
- 29 01 00 00 00 02 36 73
- 29 01 00 00 00 02 0B 9F
- 29 01 00 00 00 02 0C 9F
- 29 01 00 00 00 02 0D 2F
- 29 01 00 00 00 02 0E 24
- 29 01 00 00 00 02 11 83
- 29 01 00 00 00 02 12 03
- 29 01 00 00 00 02 71 2C
- 29 01 00 00 00 02 6F 03
- 29 01 00 00 00 02 0F 0A
- 29 01 00 00 00 02 FF 05
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 01 00
- 29 01 00 00 00 02 02 8B
- 29 01 00 00 00 02 03 82
- 29 01 00 00 00 02 04 82
- 29 01 00 00 00 02 05 30
- 29 01 00 00 00 02 06 33
- 29 01 00 00 00 02 07 01
- 29 01 00 00 00 02 08 00
- 29 01 00 00 00 02 09 46
- 29 01 00 00 00 02 0A 46
- 29 01 00 00 00 02 0D 0B
- 29 01 00 00 00 02 0E 1D
- 29 01 00 00 00 02 0F 08
- 29 01 00 00 00 02 10 53
- 29 01 00 00 00 02 11 00
- 29 01 00 00 00 02 12 00
- 29 01 00 00 00 02 14 01
- 29 01 00 00 00 02 15 00
- 29 01 00 00 00 02 16 05
- 29 01 00 00 00 02 17 00
- 29 01 00 00 00 02 19 7F
- 29 01 00 00 00 02 1A FF
- 29 01 00 00 00 02 1B 0F
- 29 01 00 00 00 02 1C 00
- 29 01 00 00 00 02 1D 00
- 29 01 00 00 00 02 1E 00
- 29 01 00 00 00 02 1F 07
- 29 01 00 00 00 02 20 00
- 29 01 00 00 00 02 21 06
- 29 01 00 00 00 02 22 55
- 29 01 00 00 00 02 23 4D
- 29 01 00 00 00 02 2D 02
- 29 01 00 00 00 02 28 01
- 29 01 00 00 00 02 2F 02
- 29 01 00 00 00 02 83 01
- 29 01 00 00 00 02 9E 58
- 29 01 00 00 00 02 9F 6A
- 29 01 00 00 00 02 A0 01
- 29 01 00 00 00 02 A2 10
- 29 01 00 00 00 02 BB 0A
- 29 01 00 00 00 02 BC 0A
- 29 01 00 00 00 02 32 08
- 29 01 00 00 00 02 33 B8
- 29 01 00 00 00 02 36 01
- 29 01 00 00 00 02 37 00
- 29 01 00 00 00 02 43 00
- 29 01 00 00 00 02 4B 21
- 29 01 00 00 00 02 4C 03
- 29 01 00 00 00 02 50 21
- 29 01 00 00 00 02 51 03
- 29 01 00 00 00 02 58 21
- 29 01 00 00 00 02 59 03
- 29 01 00 00 00 02 5D 21
- 29 01 00 00 00 02 5E 03
- 29 01 00 00 00 02 6C 00
- 29 01 00 00 00 02 6D 00
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 FF 01
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 75 00
- 29 01 00 00 00 02 76 7D
- 29 01 00 00 00 02 77 00
- 29 01 00 00 00 02 78 8A
- 29 01 00 00 00 02 79 00
- 29 01 00 00 00 02 7A 9C
- 29 01 00 00 00 02 7B 00
- 29 01 00 00 00 02 7C B1
- 29 01 00 00 00 02 7D 00
- 29 01 00 00 00 02 7E BF
- 29 01 00 00 00 02 7F 00
- 29 01 00 00 00 02 80 CF
- 29 01 00 00 00 02 81 00
- 29 01 00 00 00 02 82 DD
- 29 01 00 00 00 02 83 00
- 29 01 00 00 00 02 84 E8
- 29 01 00 00 00 02 85 00
- 29 01 00 00 00 02 86 F2
- 29 01 00 00 00 02 87 01
- 29 01 00 00 00 02 88 1F
- 29 01 00 00 00 02 89 01
- 29 01 00 00 00 02 8A 41
- 29 01 00 00 00 02 8B 01
- 29 01 00 00 00 02 8C 78
- 29 01 00 00 00 02 8D 01
- 29 01 00 00 00 02 8E A5
- 29 01 00 00 00 02 8F 01
- 29 01 00 00 00 02 90 EE
- 29 01 00 00 00 02 91 02
- 29 01 00 00 00 02 92 29
- 29 01 00 00 00 02 93 02
- 29 01 00 00 00 02 94 2A
- 29 01 00 00 00 02 95 02
- 29 01 00 00 00 02 96 5D
- 29 01 00 00 00 02 97 02
- 29 01 00 00 00 02 98 93
- 29 01 00 00 00 02 99 02
- 29 01 00 00 00 02 9A B8
- 29 01 00 00 00 02 9B 02
- 29 01 00 00 00 02 9C E7
- 29 01 00 00 00 02 9D 03
- 29 01 00 00 00 02 9E 07
- 29 01 00 00 00 02 9F 03
- 29 01 00 00 00 02 A0 37
- 29 01 00 00 00 02 A2 03
- 29 01 00 00 00 02 A3 46
- 29 01 00 00 00 02 A4 03
- 29 01 00 00 00 02 A5 56
- 29 01 00 00 00 02 A6 03
- 29 01 00 00 00 02 A7 66
- 29 01 00 00 00 02 A9 03
- 29 01 00 00 00 02 AA 7A
- 29 01 00 00 00 02 AB 03
- 29 01 00 00 00 02 AC 93
- 29 01 00 00 00 02 AD 03
- 29 01 00 00 00 02 AE A3
- 29 01 00 00 00 02 AF 03
- 29 01 00 00 00 02 B0 B4
- 29 01 00 00 00 02 B1 03
- 29 01 00 00 00 02 B2 CB
- 29 01 00 00 00 02 B3 00
- 29 01 00 00 00 02 B4 7D
- 29 01 00 00 00 02 B5 00
- 29 01 00 00 00 02 B6 8A
- 29 01 00 00 00 02 B7 00
- 29 01 00 00 00 02 B8 9C
- 29 01 00 00 00 02 B9 00
- 29 01 00 00 00 02 BA B1
- 29 01 00 00 00 02 BB 00
- 29 01 00 00 00 02 BC BF
- 29 01 00 00 00 02 BD 00
- 29 01 00 00 00 02 BE CF
- 29 01 00 00 00 02 BF 00
- 29 01 00 00 00 02 C0 DD
- 29 01 00 00 00 02 C1 00
- 29 01 00 00 00 02 C2 E8
- 29 01 00 00 00 02 C3 00
- 29 01 00 00 00 02 C4 F2
- 29 01 00 00 00 02 C5 01
- 29 01 00 00 00 02 C6 1F
- 29 01 00 00 00 02 C7 01
- 29 01 00 00 00 02 C8 41
- 29 01 00 00 00 02 C9 01
- 29 01 00 00 00 02 CA 78
- 29 01 00 00 00 02 CB 01
- 29 01 00 00 00 02 CC A5
- 29 01 00 00 00 02 CD 01
- 29 01 00 00 00 02 CE EE
- 29 01 00 00 00 02 CF 02
- 29 01 00 00 00 02 D0 29
- 29 01 00 00 00 02 D1 02
- 29 01 00 00 00 02 D2 2A
- 29 01 00 00 00 02 D3 02
- 29 01 00 00 00 02 D4 5D
- 29 01 00 00 00 02 D5 02
- 29 01 00 00 00 02 D6 93
- 29 01 00 00 00 02 D7 02
- 29 01 00 00 00 02 D8 B8
- 29 01 00 00 00 02 D9 02
- 29 01 00 00 00 02 DA E7
- 29 01 00 00 00 02 DB 03
- 29 01 00 00 00 02 DC 07
- 29 01 00 00 00 02 DD 03
- 29 01 00 00 00 02 DE 37
- 29 01 00 00 00 02 DF 03
- 29 01 00 00 00 02 E0 46
- 29 01 00 00 00 02 E1 03
- 29 01 00 00 00 02 E2 56
- 29 01 00 00 00 02 E3 03
- 29 01 00 00 00 02 E4 66
- 29 01 00 00 00 02 E5 03
- 29 01 00 00 00 02 E6 7A
- 29 01 00 00 00 02 E7 03
- 29 01 00 00 00 02 E8 93
- 29 01 00 00 00 02 E9 03
- 29 01 00 00 00 02 EA A3
- 29 01 00 00 00 02 EB 03
- 29 01 00 00 00 02 EC B4
- 29 01 00 00 00 02 ED 03
- 29 01 00 00 00 02 EE CB
- 29 01 00 00 00 02 EF 00
- 29 01 00 00 00 02 F0 ED
- 29 01 00 00 00 02 F1 00
- 29 01 00 00 00 02 F2 F3
- 29 01 00 00 00 02 F3 00
- 29 01 00 00 00 02 F4 FE
- 29 01 00 00 00 02 F5 01
- 29 01 00 00 00 02 F6 09
- 29 01 00 00 00 02 F7 01
- 29 01 00 00 00 02 F8 13
- 29 01 00 00 00 02 F9 01
- 29 01 00 00 00 02 FA 1D
- 29 01 00 00 00 02 FF 02
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 00 01
- 29 01 00 00 00 02 01 26
- 29 01 00 00 00 02 02 01
- 29 01 00 00 00 02 03 2F
- 29 01 00 00 00 02 04 01
- 29 01 00 00 00 02 05 37
- 29 01 00 00 00 02 06 01
- 29 01 00 00 00 02 07 56
- 29 01 00 00 00 02 08 01
- 29 01 00 00 00 02 09 70
- 29 01 00 00 00 02 0A 01
- 29 01 00 00 00 02 0B 9D
- 29 01 00 00 00 02 0C 01
- 29 01 00 00 00 02 0D C2
- 29 01 00 00 00 02 0E 01
- 29 01 00 00 00 02 0F FF
- 29 01 00 00 00 02 10 02
- 29 01 00 00 00 02 11 31
- 29 01 00 00 00 02 12 02
- 29 01 00 00 00 02 13 32
- 29 01 00 00 00 02 14 02
- 29 01 00 00 00 02 15 60
- 29 01 00 00 00 02 16 02
- 29 01 00 00 00 02 17 94
- 29 01 00 00 00 02 18 02
- 29 01 00 00 00 02 19 B5
- 29 01 00 00 00 02 1A 02
- 29 01 00 00 00 02 1B E3
- 29 01 00 00 00 02 1C 03
- 29 01 00 00 00 02 1D 03
- 29 01 00 00 00 02 1E 03
- 29 01 00 00 00 02 1F 2D
- 29 01 00 00 00 02 20 03
- 29 01 00 00 00 02 21 3A
- 29 01 00 00 00 02 22 03
- 29 01 00 00 00 02 23 48
- 29 01 00 00 00 02 24 03
- 29 01 00 00 00 02 25 57
- 29 01 00 00 00 02 26 03
- 29 01 00 00 00 02 27 68
- 29 01 00 00 00 02 28 03
- 29 01 00 00 00 02 29 7B
- 29 01 00 00 00 02 2A 03
- 29 01 00 00 00 02 2B 90
- 29 01 00 00 00 02 2D 03
- 29 01 00 00 00 02 2F A0
- 29 01 00 00 00 02 30 03
- 29 01 00 00 00 02 31 CB
- 29 01 00 00 00 02 32 00
- 29 01 00 00 00 02 33 ED
- 29 01 00 00 00 02 34 00
- 29 01 00 00 00 02 35 F3
- 29 01 00 00 00 02 36 00
- 29 01 00 00 00 02 37 FE
- 29 01 00 00 00 02 38 01
- 29 01 00 00 00 02 39 09
- 29 01 00 00 00 02 3A 01
- 29 01 00 00 00 02 3B 13
- 29 01 00 00 00 02 3D 01
- 29 01 00 00 00 02 3F 1D
- 29 01 00 00 00 02 40 01
- 29 01 00 00 00 02 41 26
- 29 01 00 00 00 02 42 01
- 29 01 00 00 00 02 43 2F
- 29 01 00 00 00 02 44 01
- 29 01 00 00 00 02 45 37
- 29 01 00 00 00 02 46 01
- 29 01 00 00 00 02 47 56
- 29 01 00 00 00 02 48 01
- 29 01 00 00 00 02 49 70
- 29 01 00 00 00 02 4A 01
- 29 01 00 00 00 02 4B 9D
- 29 01 00 00 00 02 4C 01
- 29 01 00 00 00 02 4D C2
- 29 01 00 00 00 02 4E 01
- 29 01 00 00 00 02 4F FF
- 29 01 00 00 00 02 50 02
- 29 01 00 00 00 02 51 31
- 29 01 00 00 00 02 52 02
- 29 01 00 00 00 02 53 32
- 29 01 00 00 00 02 54 02
- 29 01 00 00 00 02 55 60
- 29 01 00 00 00 02 56 02
- 29 01 00 00 00 02 58 94
- 29 01 00 00 00 02 59 02
- 29 01 00 00 00 02 5A B5
- 29 01 00 00 00 02 5B 02
- 29 01 00 00 00 02 5C E3
- 29 01 00 00 00 02 5D 03
- 29 01 00 00 00 02 5E 03
- 29 01 00 00 00 02 5F 03
- 29 01 00 00 00 02 60 2D
- 29 01 00 00 00 02 61 03
- 29 01 00 00 00 02 62 3A
- 29 01 00 00 00 02 63 03
- 29 01 00 00 00 02 64 48
- 29 01 00 00 00 02 65 03
- 29 01 00 00 00 02 66 57
- 29 01 00 00 00 02 67 03
- 29 01 00 00 00 02 68 68
- 29 01 00 00 00 02 69 03
- 29 01 00 00 00 02 6A 7B
- 29 01 00 00 00 02 6B 03
- 29 01 00 00 00 02 6C 90
- 29 01 00 00 00 02 6D 03
- 29 01 00 00 00 02 6E A0
- 29 01 00 00 00 02 6F 03
- 29 01 00 00 00 02 70 CB
- 29 01 00 00 00 02 71 00
- 29 01 00 00 00 02 72 19
- 29 01 00 00 00 02 73 00
- 29 01 00 00 00 02 74 36
- 29 01 00 00 00 02 75 00
- 29 01 00 00 00 02 76 55
- 29 01 00 00 00 02 77 00
- 29 01 00 00 00 02 78 70
- 29 01 00 00 00 02 79 00
- 29 01 00 00 00 02 7A 83
- 29 01 00 00 00 02 7B 00
- 29 01 00 00 00 02 7C 99
- 29 01 00 00 00 02 7D 00
- 29 01 00 00 00 02 7E A8
- 29 01 00 00 00 02 7F 00
- 29 01 00 00 00 02 80 B7
- 29 01 00 00 00 02 81 00
- 29 01 00 00 00 02 82 C5
- 29 01 00 00 00 02 83 00
- 29 01 00 00 00 02 84 F7
- 29 01 00 00 00 02 85 01
- 29 01 00 00 00 02 86 1E
- 29 01 00 00 00 02 87 01
- 29 01 00 00 00 02 88 60
- 29 01 00 00 00 02 89 01
- 29 01 00 00 00 02 8A 95
- 29 01 00 00 00 02 8B 01
- 29 01 00 00 00 02 8C E1
- 29 01 00 00 00 02 8D 02
- 29 01 00 00 00 02 8E 20
- 29 01 00 00 00 02 8F 02
- 29 01 00 00 00 02 90 23
- 29 01 00 00 00 02 91 02
- 29 01 00 00 00 02 92 59
- 29 01 00 00 00 02 93 02
- 29 01 00 00 00 02 94 94
- 29 01 00 00 00 02 95 02
- 29 01 00 00 00 02 96 B4
- 29 01 00 00 00 02 97 02
- 29 01 00 00 00 02 98 E1
- 29 01 00 00 00 02 99 03
- 29 01 00 00 00 02 9A 01
- 29 01 00 00 00 02 9B 03
- 29 01 00 00 00 02 9C 28
- 29 01 00 00 00 02 9D 03
- 29 01 00 00 00 02 9E 30
- 29 01 00 00 00 02 9F 03
- 29 01 00 00 00 02 A0 37
- 29 01 00 00 00 02 A2 03
- 29 01 00 00 00 02 A3 3B
- 29 01 00 00 00 02 A4 03
- 29 01 00 00 00 02 A5 40
- 29 01 00 00 00 02 A6 03
- 29 01 00 00 00 02 A7 50
- 29 01 00 00 00 02 A9 03
- 29 01 00 00 00 02 AA 6D
- 29 01 00 00 00 02 AB 03
- 29 01 00 00 00 02 AC 80
- 29 01 00 00 00 02 AD 03
- 29 01 00 00 00 02 AE CB
- 29 01 00 00 00 02 AF 00
- 29 01 00 00 00 02 B0 19
- 29 01 00 00 00 02 B1 00
- 29 01 00 00 00 02 B2 36
- 29 01 00 00 00 02 B3 00
- 29 01 00 00 00 02 B4 55
- 29 01 00 00 00 02 B5 00
- 29 01 00 00 00 02 B6 70
- 29 01 00 00 00 02 B7 00
- 29 01 00 00 00 02 B8 83
- 29 01 00 00 00 02 B9 00
- 29 01 00 00 00 02 BA 99
- 29 01 00 00 00 02 BB 00
- 29 01 00 00 00 02 BC A8
- 29 01 00 00 00 02 BD 00
- 29 01 00 00 00 02 BE B7
- 29 01 00 00 00 02 BF 00
- 29 01 00 00 00 02 C0 C5
- 29 01 00 00 00 02 C1 00
- 29 01 00 00 00 02 C2 F7
- 29 01 00 00 00 02 C3 01
- 29 01 00 00 00 02 C4 1E
- 29 01 00 00 00 02 C5 01
- 29 01 00 00 00 02 C6 60
- 29 01 00 00 00 02 C7 01
- 29 01 00 00 00 02 C8 95
- 29 01 00 00 00 02 C9 01
- 29 01 00 00 00 02 CA E1
- 29 01 00 00 00 02 CB 02
- 29 01 00 00 00 02 CC 20
- 29 01 00 00 00 02 CD 02
- 29 01 00 00 00 02 CE 23
- 29 01 00 00 00 02 CF 02
- 29 01 00 00 00 02 D0 59
- 29 01 00 00 00 02 D1 02
- 29 01 00 00 00 02 D2 94
- 29 01 00 00 00 02 D3 02
- 29 01 00 00 00 02 D4 B4
- 29 01 00 00 00 02 D5 02
- 29 01 00 00 00 02 D6 E1
- 29 01 00 00 00 02 D7 03
- 29 01 00 00 00 02 D8 01
- 29 01 00 00 00 02 D9 03
- 29 01 00 00 00 02 DA 28
- 29 01 00 00 00 02 DB 03
- 29 01 00 00 00 02 DC 30
- 29 01 00 00 00 02 DD 03
- 29 01 00 00 00 02 DE 37
- 29 01 00 00 00 02 DF 03
- 29 01 00 00 00 02 E0 3B
- 29 01 00 00 00 02 E1 03
- 29 01 00 00 00 02 E2 40
- 29 01 00 00 00 02 E3 03
- 29 01 00 00 00 02 E4 50
- 29 01 00 00 00 02 E5 03
- 29 01 00 00 00 02 E6 6D
- 29 01 00 00 00 02 E7 03
- 29 01 00 00 00 02 E8 80
- 29 01 00 00 00 02 E9 03
- 29 01 00 00 00 02 EA CB
- 29 01 00 00 00 02 FF 01
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 FF 02
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 FF 04
- 29 01 00 00 00 02 FB 01
- 29 01 00 00 00 02 FF 00
- 29 01 00 00 64 02 11 00
- 29 01 00 00 00 02 FF EE
- 29 01 00 00 00 02 12 50
- 29 01 00 00 00 02 13 02
- 29 01 00 00 00 02 6A 60
- 29 01 00 00 00 02 FF 00
- 29 01 00 00 78 02 29 00];
+ qcom,panel-on-cmds = [29 01 00 00 00 00 02 FF EE
+ 29 01 00 00 00 00 02 26 08
+ 29 01 00 00 00 00 02 26 00
+ 29 01 00 00 10 00 02 FF 00
+ 29 01 00 00 00 00 02 BA 03
+ 29 01 00 00 00 00 02 C2 08
+ 29 01 00 00 00 00 02 FF 01
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 00 4A
+ 29 01 00 00 00 00 02 01 33
+ 29 01 00 00 00 00 02 02 53
+ 29 01 00 00 00 00 02 03 55
+ 29 01 00 00 00 00 02 04 55
+ 29 01 00 00 00 00 02 05 33
+ 29 01 00 00 00 00 02 06 22
+ 29 01 00 00 00 00 02 08 56
+ 29 01 00 00 00 00 02 09 8F
+ 29 01 00 00 00 00 02 36 73
+ 29 01 00 00 00 00 02 0B 9F
+ 29 01 00 00 00 00 02 0C 9F
+ 29 01 00 00 00 00 02 0D 2F
+ 29 01 00 00 00 00 02 0E 24
+ 29 01 00 00 00 00 02 11 83
+ 29 01 00 00 00 00 02 12 03
+ 29 01 00 00 00 00 02 71 2C
+ 29 01 00 00 00 00 02 6F 03
+ 29 01 00 00 00 00 02 0F 0A
+ 29 01 00 00 00 00 02 FF 05
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 01 00
+ 29 01 00 00 00 00 02 02 8B
+ 29 01 00 00 00 00 02 03 82
+ 29 01 00 00 00 00 02 04 82
+ 29 01 00 00 00 00 02 05 30
+ 29 01 00 00 00 00 02 06 33
+ 29 01 00 00 00 00 02 07 01
+ 29 01 00 00 00 00 02 08 00
+ 29 01 00 00 00 00 02 09 46
+ 29 01 00 00 00 00 02 0A 46
+ 29 01 00 00 00 00 02 0D 0B
+ 29 01 00 00 00 00 02 0E 1D
+ 29 01 00 00 00 00 02 0F 08
+ 29 01 00 00 00 00 02 10 53
+ 29 01 00 00 00 00 02 11 00
+ 29 01 00 00 00 00 02 12 00
+ 29 01 00 00 00 00 02 14 01
+ 29 01 00 00 00 00 02 15 00
+ 29 01 00 00 00 00 02 16 05
+ 29 01 00 00 00 00 02 17 00
+ 29 01 00 00 00 00 02 19 7F
+ 29 01 00 00 00 00 02 1A FF
+ 29 01 00 00 00 00 02 1B 0F
+ 29 01 00 00 00 00 02 1C 00
+ 29 01 00 00 00 00 02 1D 00
+ 29 01 00 00 00 00 02 1E 00
+ 29 01 00 00 00 00 02 1F 07
+ 29 01 00 00 00 00 02 20 00
+ 29 01 00 00 00 00 02 21 06
+ 29 01 00 00 00 00 02 22 55
+ 29 01 00 00 00 00 02 23 4D
+ 29 01 00 00 00 00 02 2D 02
+ 29 01 00 00 00 00 02 28 01
+ 29 01 00 00 00 00 02 2F 02
+ 29 01 00 00 00 00 02 83 01
+ 29 01 00 00 00 00 02 9E 58
+ 29 01 00 00 00 00 02 9F 6A
+ 29 01 00 00 00 00 02 A0 01
+ 29 01 00 00 00 00 02 A2 10
+ 29 01 00 00 00 00 02 BB 0A
+ 29 01 00 00 00 00 02 BC 0A
+ 29 01 00 00 00 00 02 32 08
+ 29 01 00 00 00 00 02 33 B8
+ 29 01 00 00 00 00 02 36 01
+ 29 01 00 00 00 00 02 37 00
+ 29 01 00 00 00 00 02 43 00
+ 29 01 00 00 00 00 02 4B 21
+ 29 01 00 00 00 00 02 4C 03
+ 29 01 00 00 00 00 02 50 21
+ 29 01 00 00 00 00 02 51 03
+ 29 01 00 00 00 00 02 58 21
+ 29 01 00 00 00 00 02 59 03
+ 29 01 00 00 00 00 02 5D 21
+ 29 01 00 00 00 00 02 5E 03
+ 29 01 00 00 00 00 02 6C 00
+ 29 01 00 00 00 00 02 6D 00
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 FF 01
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 75 00
+ 29 01 00 00 00 00 02 76 7D
+ 29 01 00 00 00 00 02 77 00
+ 29 01 00 00 00 00 02 78 8A
+ 29 01 00 00 00 00 02 79 00
+ 29 01 00 00 00 00 02 7A 9C
+ 29 01 00 00 00 00 02 7B 00
+ 29 01 00 00 00 00 02 7C B1
+ 29 01 00 00 00 00 02 7D 00
+ 29 01 00 00 00 00 02 7E BF
+ 29 01 00 00 00 00 02 7F 00
+ 29 01 00 00 00 00 02 80 CF
+ 29 01 00 00 00 00 02 81 00
+ 29 01 00 00 00 00 02 82 DD
+ 29 01 00 00 00 00 02 83 00
+ 29 01 00 00 00 00 02 84 E8
+ 29 01 00 00 00 00 02 85 00
+ 29 01 00 00 00 00 02 86 F2
+ 29 01 00 00 00 00 02 87 01
+ 29 01 00 00 00 00 02 88 1F
+ 29 01 00 00 00 00 02 89 01
+ 29 01 00 00 00 00 02 8A 41
+ 29 01 00 00 00 00 02 8B 01
+ 29 01 00 00 00 00 02 8C 78
+ 29 01 00 00 00 00 02 8D 01
+ 29 01 00 00 00 00 02 8E A5
+ 29 01 00 00 00 00 02 8F 01
+ 29 01 00 00 00 00 02 90 EE
+ 29 01 00 00 00 00 02 91 02
+ 29 01 00 00 00 00 02 92 29
+ 29 01 00 00 00 00 02 93 02
+ 29 01 00 00 00 00 02 94 2A
+ 29 01 00 00 00 00 02 95 02
+ 29 01 00 00 00 00 02 96 5D
+ 29 01 00 00 00 00 02 97 02
+ 29 01 00 00 00 00 02 98 93
+ 29 01 00 00 00 00 02 99 02
+ 29 01 00 00 00 00 02 9A B8
+ 29 01 00 00 00 00 02 9B 02
+ 29 01 00 00 00 00 02 9C E7
+ 29 01 00 00 00 00 02 9D 03
+ 29 01 00 00 00 00 02 9E 07
+ 29 01 00 00 00 00 02 9F 03
+ 29 01 00 00 00 00 02 A0 37
+ 29 01 00 00 00 00 02 A2 03
+ 29 01 00 00 00 00 02 A3 46
+ 29 01 00 00 00 00 02 A4 03
+ 29 01 00 00 00 00 02 A5 56
+ 29 01 00 00 00 00 02 A6 03
+ 29 01 00 00 00 00 02 A7 66
+ 29 01 00 00 00 00 02 A9 03
+ 29 01 00 00 00 00 02 AA 7A
+ 29 01 00 00 00 00 02 AB 03
+ 29 01 00 00 00 00 02 AC 93
+ 29 01 00 00 00 00 02 AD 03
+ 29 01 00 00 00 00 02 AE A3
+ 29 01 00 00 00 00 02 AF 03
+ 29 01 00 00 00 00 02 B0 B4
+ 29 01 00 00 00 00 02 B1 03
+ 29 01 00 00 00 00 02 B2 CB
+ 29 01 00 00 00 00 02 B3 00
+ 29 01 00 00 00 00 02 B4 7D
+ 29 01 00 00 00 00 02 B5 00
+ 29 01 00 00 00 00 02 B6 8A
+ 29 01 00 00 00 00 02 B7 00
+ 29 01 00 00 00 00 02 B8 9C
+ 29 01 00 00 00 00 02 B9 00
+ 29 01 00 00 00 00 02 BA B1
+ 29 01 00 00 00 00 02 BB 00
+ 29 01 00 00 00 00 02 BC BF
+ 29 01 00 00 00 00 02 BD 00
+ 29 01 00 00 00 00 02 BE CF
+ 29 01 00 00 00 00 02 BF 00
+ 29 01 00 00 00 00 02 C0 DD
+ 29 01 00 00 00 00 02 C1 00
+ 29 01 00 00 00 00 02 C2 E8
+ 29 01 00 00 00 00 02 C3 00
+ 29 01 00 00 00 00 02 C4 F2
+ 29 01 00 00 00 00 02 C5 01
+ 29 01 00 00 00 00 02 C6 1F
+ 29 01 00 00 00 00 02 C7 01
+ 29 01 00 00 00 00 02 C8 41
+ 29 01 00 00 00 00 02 C9 01
+ 29 01 00 00 00 00 02 CA 78
+ 29 01 00 00 00 00 02 CB 01
+ 29 01 00 00 00 00 02 CC A5
+ 29 01 00 00 00 00 02 CD 01
+ 29 01 00 00 00 00 02 CE EE
+ 29 01 00 00 00 00 02 CF 02
+ 29 01 00 00 00 00 02 D0 29
+ 29 01 00 00 00 00 02 D1 02
+ 29 01 00 00 00 00 02 D2 2A
+ 29 01 00 00 00 00 02 D3 02
+ 29 01 00 00 00 00 02 D4 5D
+ 29 01 00 00 00 00 02 D5 02
+ 29 01 00 00 00 00 02 D6 93
+ 29 01 00 00 00 00 02 D7 02
+ 29 01 00 00 00 00 02 D8 B8
+ 29 01 00 00 00 00 02 D9 02
+ 29 01 00 00 00 00 02 DA E7
+ 29 01 00 00 00 00 02 DB 03
+ 29 01 00 00 00 00 02 DC 07
+ 29 01 00 00 00 00 02 DD 03
+ 29 01 00 00 00 00 02 DE 37
+ 29 01 00 00 00 00 02 DF 03
+ 29 01 00 00 00 00 02 E0 46
+ 29 01 00 00 00 00 02 E1 03
+ 29 01 00 00 00 00 02 E2 56
+ 29 01 00 00 00 00 02 E3 03
+ 29 01 00 00 00 00 02 E4 66
+ 29 01 00 00 00 00 02 E5 03
+ 29 01 00 00 00 00 02 E6 7A
+ 29 01 00 00 00 00 02 E7 03
+ 29 01 00 00 00 00 02 E8 93
+ 29 01 00 00 00 00 02 E9 03
+ 29 01 00 00 00 00 02 EA A3
+ 29 01 00 00 00 00 02 EB 03
+ 29 01 00 00 00 00 02 EC B4
+ 29 01 00 00 00 00 02 ED 03
+ 29 01 00 00 00 00 02 EE CB
+ 29 01 00 00 00 00 02 EF 00
+ 29 01 00 00 00 00 02 F0 ED
+ 29 01 00 00 00 00 02 F1 00
+ 29 01 00 00 00 00 02 F2 F3
+ 29 01 00 00 00 00 02 F3 00
+ 29 01 00 00 00 00 02 F4 FE
+ 29 01 00 00 00 00 02 F5 01
+ 29 01 00 00 00 00 02 F6 09
+ 29 01 00 00 00 00 02 F7 01
+ 29 01 00 00 00 00 02 F8 13
+ 29 01 00 00 00 00 02 F9 01
+ 29 01 00 00 00 00 02 FA 1D
+ 29 01 00 00 00 00 02 FF 02
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 00 01
+ 29 01 00 00 00 00 02 01 26
+ 29 01 00 00 00 00 02 02 01
+ 29 01 00 00 00 00 02 03 2F
+ 29 01 00 00 00 00 02 04 01
+ 29 01 00 00 00 00 02 05 37
+ 29 01 00 00 00 00 02 06 01
+ 29 01 00 00 00 00 02 07 56
+ 29 01 00 00 00 00 02 08 01
+ 29 01 00 00 00 00 02 09 70
+ 29 01 00 00 00 00 02 0A 01
+ 29 01 00 00 00 00 02 0B 9D
+ 29 01 00 00 00 00 02 0C 01
+ 29 01 00 00 00 00 02 0D C2
+ 29 01 00 00 00 00 02 0E 01
+ 29 01 00 00 00 00 02 0F FF
+ 29 01 00 00 00 00 02 10 02
+ 29 01 00 00 00 00 02 11 31
+ 29 01 00 00 00 00 02 12 02
+ 29 01 00 00 00 00 02 13 32
+ 29 01 00 00 00 00 02 14 02
+ 29 01 00 00 00 00 02 15 60
+ 29 01 00 00 00 00 02 16 02
+ 29 01 00 00 00 00 02 17 94
+ 29 01 00 00 00 00 02 18 02
+ 29 01 00 00 00 00 02 19 B5
+ 29 01 00 00 00 00 02 1A 02
+ 29 01 00 00 00 00 02 1B E3
+ 29 01 00 00 00 00 02 1C 03
+ 29 01 00 00 00 00 02 1D 03
+ 29 01 00 00 00 00 02 1E 03
+ 29 01 00 00 00 00 02 1F 2D
+ 29 01 00 00 00 00 02 20 03
+ 29 01 00 00 00 00 02 21 3A
+ 29 01 00 00 00 00 02 22 03
+ 29 01 00 00 00 00 02 23 48
+ 29 01 00 00 00 00 02 24 03
+ 29 01 00 00 00 00 02 25 57
+ 29 01 00 00 00 00 02 26 03
+ 29 01 00 00 00 00 02 27 68
+ 29 01 00 00 00 00 02 28 03
+ 29 01 00 00 00 00 02 29 7B
+ 29 01 00 00 00 00 02 2A 03
+ 29 01 00 00 00 00 02 2B 90
+ 29 01 00 00 00 00 02 2D 03
+ 29 01 00 00 00 00 02 2F A0
+ 29 01 00 00 00 00 02 30 03
+ 29 01 00 00 00 00 02 31 CB
+ 29 01 00 00 00 00 02 32 00
+ 29 01 00 00 00 00 02 33 ED
+ 29 01 00 00 00 00 02 34 00
+ 29 01 00 00 00 00 02 35 F3
+ 29 01 00 00 00 00 02 36 00
+ 29 01 00 00 00 00 02 37 FE
+ 29 01 00 00 00 00 02 38 01
+ 29 01 00 00 00 00 02 39 09
+ 29 01 00 00 00 00 02 3A 01
+ 29 01 00 00 00 00 02 3B 13
+ 29 01 00 00 00 00 02 3D 01
+ 29 01 00 00 00 00 02 3F 1D
+ 29 01 00 00 00 00 02 40 01
+ 29 01 00 00 00 00 02 41 26
+ 29 01 00 00 00 00 02 42 01
+ 29 01 00 00 00 00 02 43 2F
+ 29 01 00 00 00 00 02 44 01
+ 29 01 00 00 00 00 02 45 37
+ 29 01 00 00 00 00 02 46 01
+ 29 01 00 00 00 00 02 47 56
+ 29 01 00 00 00 00 02 48 01
+ 29 01 00 00 00 00 02 49 70
+ 29 01 00 00 00 00 02 4A 01
+ 29 01 00 00 00 00 02 4B 9D
+ 29 01 00 00 00 00 02 4C 01
+ 29 01 00 00 00 00 02 4D C2
+ 29 01 00 00 00 00 02 4E 01
+ 29 01 00 00 00 00 02 4F FF
+ 29 01 00 00 00 00 02 50 02
+ 29 01 00 00 00 00 02 51 31
+ 29 01 00 00 00 00 02 52 02
+ 29 01 00 00 00 00 02 53 32
+ 29 01 00 00 00 00 02 54 02
+ 29 01 00 00 00 00 02 55 60
+ 29 01 00 00 00 00 02 56 02
+ 29 01 00 00 00 00 02 58 94
+ 29 01 00 00 00 00 02 59 02
+ 29 01 00 00 00 00 02 5A B5
+ 29 01 00 00 00 00 02 5B 02
+ 29 01 00 00 00 00 02 5C E3
+ 29 01 00 00 00 00 02 5D 03
+ 29 01 00 00 00 00 02 5E 03
+ 29 01 00 00 00 00 02 5F 03
+ 29 01 00 00 00 00 02 60 2D
+ 29 01 00 00 00 00 02 61 03
+ 29 01 00 00 00 00 02 62 3A
+ 29 01 00 00 00 00 02 63 03
+ 29 01 00 00 00 00 02 64 48
+ 29 01 00 00 00 00 02 65 03
+ 29 01 00 00 00 00 02 66 57
+ 29 01 00 00 00 00 02 67 03
+ 29 01 00 00 00 00 02 68 68
+ 29 01 00 00 00 00 02 69 03
+ 29 01 00 00 00 00 02 6A 7B
+ 29 01 00 00 00 00 02 6B 03
+ 29 01 00 00 00 00 02 6C 90
+ 29 01 00 00 00 00 02 6D 03
+ 29 01 00 00 00 00 02 6E A0
+ 29 01 00 00 00 00 02 6F 03
+ 29 01 00 00 00 00 02 70 CB
+ 29 01 00 00 00 00 02 71 00
+ 29 01 00 00 00 00 02 72 19
+ 29 01 00 00 00 00 02 73 00
+ 29 01 00 00 00 00 02 74 36
+ 29 01 00 00 00 00 02 75 00
+ 29 01 00 00 00 00 02 76 55
+ 29 01 00 00 00 00 02 77 00
+ 29 01 00 00 00 00 02 78 70
+ 29 01 00 00 00 00 02 79 00
+ 29 01 00 00 00 00 02 7A 83
+ 29 01 00 00 00 00 02 7B 00
+ 29 01 00 00 00 00 02 7C 99
+ 29 01 00 00 00 00 02 7D 00
+ 29 01 00 00 00 00 02 7E A8
+ 29 01 00 00 00 00 02 7F 00
+ 29 01 00 00 00 00 02 80 B7
+ 29 01 00 00 00 00 02 81 00
+ 29 01 00 00 00 00 02 82 C5
+ 29 01 00 00 00 00 02 83 00
+ 29 01 00 00 00 00 02 84 F7
+ 29 01 00 00 00 00 02 85 01
+ 29 01 00 00 00 00 02 86 1E
+ 29 01 00 00 00 00 02 87 01
+ 29 01 00 00 00 00 02 88 60
+ 29 01 00 00 00 00 02 89 01
+ 29 01 00 00 00 00 02 8A 95
+ 29 01 00 00 00 00 02 8B 01
+ 29 01 00 00 00 00 02 8C E1
+ 29 01 00 00 00 00 02 8D 02
+ 29 01 00 00 00 00 02 8E 20
+ 29 01 00 00 00 00 02 8F 02
+ 29 01 00 00 00 00 02 90 23
+ 29 01 00 00 00 00 02 91 02
+ 29 01 00 00 00 00 02 92 59
+ 29 01 00 00 00 00 02 93 02
+ 29 01 00 00 00 00 02 94 94
+ 29 01 00 00 00 00 02 95 02
+ 29 01 00 00 00 00 02 96 B4
+ 29 01 00 00 00 00 02 97 02
+ 29 01 00 00 00 00 02 98 E1
+ 29 01 00 00 00 00 02 99 03
+ 29 01 00 00 00 00 02 9A 01
+ 29 01 00 00 00 00 02 9B 03
+ 29 01 00 00 00 00 02 9C 28
+ 29 01 00 00 00 00 02 9D 03
+ 29 01 00 00 00 00 02 9E 30
+ 29 01 00 00 00 00 02 9F 03
+ 29 01 00 00 00 00 02 A0 37
+ 29 01 00 00 00 00 02 A2 03
+ 29 01 00 00 00 00 02 A3 3B
+ 29 01 00 00 00 00 02 A4 03
+ 29 01 00 00 00 00 02 A5 40
+ 29 01 00 00 00 00 02 A6 03
+ 29 01 00 00 00 00 02 A7 50
+ 29 01 00 00 00 00 02 A9 03
+ 29 01 00 00 00 00 02 AA 6D
+ 29 01 00 00 00 00 02 AB 03
+ 29 01 00 00 00 00 02 AC 80
+ 29 01 00 00 00 00 02 AD 03
+ 29 01 00 00 00 00 02 AE CB
+ 29 01 00 00 00 00 02 AF 00
+ 29 01 00 00 00 00 02 B0 19
+ 29 01 00 00 00 00 02 B1 00
+ 29 01 00 00 00 00 02 B2 36
+ 29 01 00 00 00 00 02 B3 00
+ 29 01 00 00 00 00 02 B4 55
+ 29 01 00 00 00 00 02 B5 00
+ 29 01 00 00 00 00 02 B6 70
+ 29 01 00 00 00 00 02 B7 00
+ 29 01 00 00 00 00 02 B8 83
+ 29 01 00 00 00 00 02 B9 00
+ 29 01 00 00 00 00 02 BA 99
+ 29 01 00 00 00 00 02 BB 00
+ 29 01 00 00 00 00 02 BC A8
+ 29 01 00 00 00 00 02 BD 00
+ 29 01 00 00 00 00 02 BE B7
+ 29 01 00 00 00 00 02 BF 00
+ 29 01 00 00 00 00 02 C0 C5
+ 29 01 00 00 00 00 02 C1 00
+ 29 01 00 00 00 00 02 C2 F7
+ 29 01 00 00 00 00 02 C3 01
+ 29 01 00 00 00 00 02 C4 1E
+ 29 01 00 00 00 00 02 C5 01
+ 29 01 00 00 00 00 02 C6 60
+ 29 01 00 00 00 00 02 C7 01
+ 29 01 00 00 00 00 02 C8 95
+ 29 01 00 00 00 00 02 C9 01
+ 29 01 00 00 00 00 02 CA E1
+ 29 01 00 00 00 00 02 CB 02
+ 29 01 00 00 00 00 02 CC 20
+ 29 01 00 00 00 00 02 CD 02
+ 29 01 00 00 00 00 02 CE 23
+ 29 01 00 00 00 00 02 CF 02
+ 29 01 00 00 00 00 02 D0 59
+ 29 01 00 00 00 00 02 D1 02
+ 29 01 00 00 00 00 02 D2 94
+ 29 01 00 00 00 00 02 D3 02
+ 29 01 00 00 00 00 02 D4 B4
+ 29 01 00 00 00 00 02 D5 02
+ 29 01 00 00 00 00 02 D6 E1
+ 29 01 00 00 00 00 02 D7 03
+ 29 01 00 00 00 00 02 D8 01
+ 29 01 00 00 00 00 02 D9 03
+ 29 01 00 00 00 00 02 DA 28
+ 29 01 00 00 00 00 02 DB 03
+ 29 01 00 00 00 00 02 DC 30
+ 29 01 00 00 00 00 02 DD 03
+ 29 01 00 00 00 00 02 DE 37
+ 29 01 00 00 00 00 02 DF 03
+ 29 01 00 00 00 00 02 E0 3B
+ 29 01 00 00 00 00 02 E1 03
+ 29 01 00 00 00 00 02 E2 40
+ 29 01 00 00 00 00 02 E3 03
+ 29 01 00 00 00 00 02 E4 50
+ 29 01 00 00 00 00 02 E5 03
+ 29 01 00 00 00 00 02 E6 6D
+ 29 01 00 00 00 00 02 E7 03
+ 29 01 00 00 00 00 02 E8 80
+ 29 01 00 00 00 00 02 E9 03
+ 29 01 00 00 00 00 02 EA CB
+ 29 01 00 00 00 00 02 FF 01
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 FF 02
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 FF 04
+ 29 01 00 00 00 00 02 FB 01
+ 29 01 00 00 00 00 02 FF 00
+ 29 01 00 00 64 00 02 11 00
+ 29 01 00 00 00 00 02 FF EE
+ 29 01 00 00 00 00 02 12 50
+ 29 01 00 00 00 00 02 13 02
+ 29 01 00 00 00 00 02 6A 60
+ 29 01 00 00 00 00 02 FF 00
+ 29 01 00 00 78 00 02 29 00];
+
qcom,on-cmds-dsi-state = "DSI_LP_MODE";
- qcom,panel-off-cmds = [05 01 00 00 32 02 28 00
- 05 01 00 00 78 02 10 00];
+ qcom,panel-off-cmds = [05 01 00 00 32 00 02 28 00
+ 05 01 00 00 78 00 02 10 00];
qcom,off-cmds-dsi-state = "DSI_HS_MODE";
};
};
diff --git a/arch/arm/boot/dts/msm-pm8941.dtsi b/arch/arm/boot/dts/msm-pm8941.dtsi
index ce050a4..b801da8 100644
--- a/arch/arm/boot/dts/msm-pm8941.dtsi
+++ b/arch/arm/boot/dts/msm-pm8941.dtsi
@@ -148,14 +148,15 @@
<0x0 0x40 0x6>,
<0x0 0x40 0x7>;
- interrupt-names = "vsense_for_r",
- "vsense_avg",
- "sw_cc_thr",
- "ocv_thr",
- "charge_begin",
- "good_ocv",
+ interrupt-names = "cc_thr",
"ocv_for_r",
- "cc_thr";
+ "good_ocv",
+ "charge_begin",
+ "ocv_thr",
+ "sw_cc_thr",
+ "vsense_avg",
+ "vsense_for_r";
+
};
};
diff --git a/arch/arm/boot/dts/msm8226-pm.dtsi b/arch/arm/boot/dts/msm8226-pm.dtsi
index 703ba4b..d9bd5e8 100644
--- a/arch/arm/boot/dts/msm8226-pm.dtsi
+++ b/arch/arm/boot/dts/msm8226-pm.dtsi
@@ -18,7 +18,7 @@
reg = <0xf9089000 0x1000>;
qcom,core-id = <0>;
qcom,saw2-ver-reg = <0xfd0>;
- qcom,saw2-cfg = <0x01>;
+ qcom,saw2-cfg = <0x00>;
qcom,saw2-spm-dly= <0x3c102800>;
qcom,saw2-spm-ctl = <0x0>;
qcom,saw2-spm-cmd-wfi = [60 03 60 0b 0f];
@@ -35,7 +35,7 @@
reg = <0xf9099000 0x1000>;
qcom,core-id = <1>;
qcom,saw2-ver-reg = <0xfd0>;
- qcom,saw2-cfg = <0x01>;
+ qcom,saw2-cfg = <0x00>;
qcom,saw2-spm-dly= <0x3c102800>;
qcom,saw2-spm-ctl = <0x0>;
qcom,saw2-spm-cmd-wfi = [60 03 60 0b 0f];
@@ -52,7 +52,7 @@
reg = <0xf90a9000 0x1000>;
qcom,core-id = <2>;
qcom,saw2-ver-reg = <0xfd0>;
- qcom,saw2-cfg = <0x01>;
+ qcom,saw2-cfg = <0x00>;
qcom,saw2-spm-dly= <0x3c102800>;
qcom,saw2-spm-ctl = <0x0>;
qcom,saw2-spm-cmd-wfi = [60 03 60 0b 0f];
@@ -69,7 +69,7 @@
reg = <0xf90b9000 0x1000>;
qcom,core-id = <3>;
qcom,saw2-ver-reg = <0xfd0>;
- qcom,saw2-cfg = <0x01>;
+ qcom,saw2-cfg = <0x00>;
qcom,saw2-spm-dly= <0x3c102800>;
qcom,saw2-spm-ctl = <0x0>;
qcom,saw2-spm-cmd-wfi = [60 03 60 0b 0f];
@@ -367,6 +367,13 @@
qcom,pc-resets-timer;
};
+ qcom,cpu-sleep-status@f9088008{
+ compatible = "qcom,cpu-sleep-status";
+ reg = <0xf9088008 0x100>;
+ qcom,cpu-alias-addr = <0x10000>;
+ qcom,sleep-status-mask= <0x80000>;
+ };
+
qcom,rpm-log@fc19dc00 {
compatible = "qcom,rpm-log";
reg = <0xfc19dc00 0x4000>;
@@ -384,4 +391,10 @@
reg-names = "phys_addr_base";
qcom,sleep-stats-version = <2>;
};
+
+ qcom,rpm-rbcpr-stats@fc000000 {
+ compatible = "qcom,rpmrbcpr-stats";
+ reg = <0xfc000000 0x1a0000>;
+ qcom,start-offset = <0x190010>;
+ };
};
diff --git a/arch/arm/boot/dts/msm8226-qrd.dtsi b/arch/arm/boot/dts/msm8226-qrd.dtsi
index 7018c6a..64cbb10 100644
--- a/arch/arm/boot/dts/msm8226-qrd.dtsi
+++ b/arch/arm/boot/dts/msm8226-qrd.dtsi
@@ -323,8 +323,9 @@
&pm8226_chg {
status = "okay";
- qcom,chg-vddmax-mv = <4350>;
- qcom,chg-vddsafe-mv = <4350>;
+ qcom,vddmax-mv = <4350>;
+ qcom,vddsafe-mv = <4380>;
+ qcom,tchg-mins = <240>;
};
&pm8226_gpios {
diff --git a/arch/arm/boot/dts/msm8226-v1.dtsi b/arch/arm/boot/dts/msm8226-v1.dtsi
index 7d3977f..d471bec 100644
--- a/arch/arm/boot/dts/msm8226-v1.dtsi
+++ b/arch/arm/boot/dts/msm8226-v1.dtsi
@@ -17,9 +17,3 @@
*/
/include/ "msm8226.dtsi"
-&soc {
- qcom,acpuclk@f9011050 {
- reg = <0xf9011050 0x8>;
- reg-names = "rcg_base";
- };
-};
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index e22f590..b14a406 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -741,6 +741,8 @@
qcom,acpuclk@f9011050 {
compatible = "qcom,acpuclk-a7";
+ reg = <0xf9011050 0x8>;
+ reg-names = "rcg_base";
a7_cpu-supply = <&apc_vreg_corner>;
};
diff --git a/arch/arm/boot/dts/msm8610-pm.dtsi b/arch/arm/boot/dts/msm8610-pm.dtsi
index faa7a41..c48eaf7 100644
--- a/arch/arm/boot/dts/msm8610-pm.dtsi
+++ b/arch/arm/boot/dts/msm8610-pm.dtsi
@@ -18,7 +18,7 @@
reg = <0xf9089000 0x1000>;
qcom,core-id = <0>;
qcom,saw2-ver-reg = <0xfd0>;
- qcom,saw2-cfg = <0x01>;
+ qcom,saw2-cfg = <0x00>;
qcom,saw2-spm-dly= <0x3c102800>;
qcom,saw2-spm-ctl = <0x0>;
qcom,saw2-spm-cmd-wfi = [60 03 60 0b 0f];
@@ -35,7 +35,7 @@
reg = <0xf9099000 0x1000>;
qcom,core-id = <1>;
qcom,saw2-ver-reg = <0xfd0>;
- qcom,saw2-cfg = <0x01>;
+ qcom,saw2-cfg = <0x00>;
qcom,saw2-spm-dly= <0x3c102800>;
qcom,saw2-spm-ctl = <0x0>;
qcom,saw2-spm-cmd-wfi = [60 03 60 0b 0f];
@@ -52,7 +52,7 @@
reg = <0xf90a9000 0x1000>;
qcom,core-id = <2>;
qcom,saw2-ver-reg = <0xfd0>;
- qcom,saw2-cfg = <0x01>;
+ qcom,saw2-cfg = <0x00>;
qcom,saw2-spm-dly= <0x3c102800>;
qcom,saw2-spm-ctl = <0x0>;
qcom,saw2-spm-cmd-wfi = [60 03 60 0b 0f];
@@ -69,7 +69,7 @@
reg = <0xf90b9000 0x1000>;
qcom,core-id = <3>;
qcom,saw2-ver-reg = <0xfd0>;
- qcom,saw2-cfg = <0x01>;
+ qcom,saw2-cfg = <0x00>;
qcom,saw2-spm-dly= <0x3c102800>;
qcom,saw2-spm-ctl = <0x0>;
qcom,saw2-spm-cmd-wfi = [60 03 60 0b 0f];
@@ -369,6 +369,13 @@
qcom,pc-resets-timer;
};
+ qcom,cpu-sleep-status@f9088008{
+ compatible = "qcom,cpu-sleep-status";
+ reg = <0xf9088008 0x100>;
+ qcom,cpu-alias-addr = <0x10000>;
+ qcom,sleep-status-mask= <0x80000>;
+ };
+
qcom,rpm-log@fc19dc00 {
compatible = "qcom,rpm-log";
reg = <0xfc19dc00 0x4000>;
@@ -386,4 +393,10 @@
reg-names = "phys_addr_base";
qcom,sleep-stats-version = <2>;
};
+
+ qcom,rpm-rbcpr-stats@fc000000 {
+ compatible = "qcom,rpmrbcpr-stats";
+ reg = <0xfc000000 0x1a0000>;
+ qcom,start-offset = <0x190010>;
+ };
};
diff --git a/arch/arm/boot/dts/msm8610-qrd-camera-sensor.dtsi b/arch/arm/boot/dts/msm8610-qrd-camera-sensor.dtsi
index e3bd631..2e18ed7 100644
--- a/arch/arm/boot/dts/msm8610-qrd-camera-sensor.dtsi
+++ b/arch/arm/boot/dts/msm8610-qrd-camera-sensor.dtsi
@@ -45,7 +45,42 @@
qcom,sensor-mode = <1>;
qcom,cci-master = <0>;
};
-
-
+ qcom,camera@78 {
+ compatible = "shinetech,gc0339";
+ reg = <0x78>;
+ qcom,slave-id = <0x42 0x00 0xc8>;
+ qcom,csiphy-sd-index = <1>;
+ qcom,csid-sd-index = <1>;
+ qcom,mount-angle = <180>;
+ qcom,sensor-name = "SKUAA_ST_gc0339";
+ cam_vdig-supply = <&pm8110_l14>;
+ cam_vana-supply = <&pm8110_l19>;
+ cam_vio-supply = <&pm8110_l14>;
+ qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana";
+ qcom,cam-vreg-type = <0 1 0>;
+ qcom,cam-vreg-min-voltage = <1800000 1800000 2850000>;
+ qcom,cam-vreg-max-voltage = <1800000 1800000 2850000>;
+ qcom,cam-vreg-op-mode = <200000 0 80000>;
+ qcom,gpio-no-mux = <0>;
+ gpios = <&msmgpio 14 0>,
+ <&msmgpio 15 0>,
+ <&msmgpio 8 0>;
+ qcom,gpio-reset = <1>;
+ qcom,gpio-standby = <2>;
+ qcom,gpio-req-tbl-num = <0 1 2>;
+ qcom,gpio-req-tbl-flags = <1 0 0>;
+ qcom,gpio-req-tbl-label = "CAMIF_MCLK",
+ "CAM_RESET",
+ "CAM_STANDBY";
+ qcom,gpio-set-tbl-num = <1 1>;
+ qcom,gpio-set-tbl-flags = <0 2>;
+ qcom,gpio-set-tbl-delay = <1000 4000>;
+ qcom,csi-lane-assign = <0xe4>;
+ qcom,csi-lane-mask = <0x3>;
+ qcom,sensor-position = <1>;
+ qcom,sensor-mode = <1>;
+ qcom,cci-master = <0>;
+ status = "ok";
+ };
};
diff --git a/arch/arm/boot/dts/msm8610-qrd.dts b/arch/arm/boot/dts/msm8610-qrd.dts
index 90225c0..7b45194 100644
--- a/arch/arm/boot/dts/msm8610-qrd.dts
+++ b/arch/arm/boot/dts/msm8610-qrd.dts
@@ -78,6 +78,15 @@
};
};
+ flashlight {
+ compatible = "qcom,leds-gpio-flash";
+ status = "okay";
+ qcom,flash-en = <&msmgpio 18 0>;
+ qcom,flash-now = <&msmgpio 19 0>;
+ linux,name = "flashlight";
+ linux,default-trigger = "flashlight-trigger";
+ };
+
gpio_keys {
compatible = "gpio-keys";
input-name = "gpio-keys";
diff --git a/arch/arm/boot/dts/msm8610.dtsi b/arch/arm/boot/dts/msm8610.dtsi
index 72d9317..d6aef92 100644
--- a/arch/arm/boot/dts/msm8610.dtsi
+++ b/arch/arm/boot/dts/msm8610.dtsi
@@ -769,6 +769,9 @@
qcom,temp-hysteresis = <10>;
qcom,freq-step = <2>;
qcom,freq-control-mask = <0xf>;
+ qcom,core-limit-temp = <80>;
+ qcom,core-temp-hysteresis = <10>;
+ qcom,core-control-mask = <0xe>;
};
qcom,ipc-spinlock@fd484000 {
diff --git a/arch/arm/boot/dts/msm8974-coresight.dtsi b/arch/arm/boot/dts/msm8974-coresight.dtsi
index 1610f1f..9fee2e5 100644
--- a/arch/arm/boot/dts/msm8974-coresight.dtsi
+++ b/arch/arm/boot/dts/msm8974-coresight.dtsi
@@ -369,8 +369,9 @@
compatible = "qcom,coresight-hwevent";
reg = <0xfdf30018 0x80>,
<0xf9011080 0x80>,
- <0xfd4ab160 0x80>;
- reg-names = "mmss-mux", "apcs-mux", "ppss-mux";
+ <0xfd4ab160 0x80>,
+ <0xfc401600 0x80>;
+ reg-names = "mmss-mux", "apcs-mux", "ppss-mux", "gcc-mux";
coresight-id = <29>;
coresight-name = "coresight-hwevent";
diff --git a/arch/arm/boot/dts/msm8974-v2-pm.dtsi b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
index eed1aae..686cdd8 100644
--- a/arch/arm/boot/dts/msm8974-v2-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
@@ -26,12 +26,11 @@
qcom,saw2-spm-dly= <0x3C102800>;
qcom,saw2-spm-ctl = <0x1>;
qcom,saw2-spm-cmd-wfi = [03 0b 0f];
- qcom,saw2-spm-cmd-ret = [42 1b 00 d0 c0 a0 90 03 d0 98 a2 c0
- 0b 00 42 1b 0f];
- qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
- 10 0b 30 06 26 30 0f];
- qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
- 10 0b 30 06 26 30 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d8 5B 03 d8 5b 0b 00 42 1b 0f];
+ qcom,saw2-spm-cmd-spc = [00 20 80 10 E8 5B 03 3B E8 5B 82 10 0B
+ 30 06 26 30 0F];
+ qcom,saw2-spm-cmd-pc = [00 20 80 10 E8 5B 07 3B E8 5B 82 10 0B
+ 30 06 26 30 0F];
};
qcom,spm@f9099000 {
@@ -49,12 +48,11 @@
qcom,saw2-spm-dly= <0x3C102800>;
qcom,saw2-spm-ctl = <0x1>;
qcom,saw2-spm-cmd-wfi = [03 0b 0f];
- qcom,saw2-spm-cmd-ret = [42 1b 00 d0 c0 a0 90 03 d0 98 a2 c0
- 0b 00 42 1b 0f];
- qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
- 10 0b 30 06 26 30 0f];
- qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
- 10 0b 30 06 26 30 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d8 5B 03 d8 5b 0b 00 42 1b 0f];
+ qcom,saw2-spm-cmd-spc = [00 20 80 10 E8 5B 03 3B E8 5B 82 10 0B
+ 30 06 26 30 0F];
+ qcom,saw2-spm-cmd-pc = [00 20 80 10 E8 5B 07 3B E8 5B 82 10 0B
+ 30 06 26 30 0F];
};
qcom,spm@f90a9000 {
@@ -72,12 +70,11 @@
qcom,saw2-spm-dly= <0x3C102800>;
qcom,saw2-spm-ctl = <0x1>;
qcom,saw2-spm-cmd-wfi = [03 0b 0f];
- qcom,saw2-spm-cmd-ret = [42 1b 00 d0 c0 a0 90 03 d0 98 a2 c0
- 0b 00 42 1b 0f];
- qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
- 10 0b 30 06 26 30 0f];
- qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
- 10 0b 30 06 26 30 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d8 5B 03 d8 5b 0b 00 42 1b 0f];
+ qcom,saw2-spm-cmd-spc = [00 20 80 10 E8 5B 03 3B E8 5B 82 10 0B
+ 30 06 26 30 0F];
+ qcom,saw2-spm-cmd-pc = [00 20 80 10 E8 5B 07 3B E8 5B 82 10 0B
+ 30 06 26 30 0F];
};
qcom,spm@f90b9000 {
@@ -95,12 +92,11 @@
qcom,saw2-spm-dly= <0x3C102800>;
qcom,saw2-spm-ctl = <0x1>;
qcom,saw2-spm-cmd-wfi = [03 0b 0f];
- qcom,saw2-spm-cmd-ret = [42 1b 00 d0 c0 a0 90 03 d0 98 a2 c0
- 0b 00 42 1b 0f];
- qcom,saw2-spm-cmd-spc = [00 20 80 10 90 a0 b0 03 3b 98 a2 b0 82
- 10 0b 30 06 26 30 0f];
- qcom,saw2-spm-cmd-pc = [00 20 80 10 90 a0 b0 07 3b 98 a2 b0 82
- 10 0b 30 06 26 30 0f];
+ qcom,saw2-spm-cmd-ret = [42 1b 00 d8 5B 03 d8 5b 0b 00 42 1b 0f];
+ qcom,saw2-spm-cmd-spc = [00 20 80 10 E8 5B 03 3B E8 5B 82 10 0B
+ 30 06 26 30 0F];
+ qcom,saw2-spm-cmd-pc = [00 20 80 10 E8 5B 07 3B E8 5B 82 10 0B
+ 30 06 26 30 0F];
};
qcom,spm@f9012000 {
@@ -125,7 +121,7 @@
qcom,pfm-port = <0x2>;
qcom,saw2-spm-cmd-ret = [1f 00 03 00 0f];
qcom,saw2-spm-cmd-gdhs = [00 32 42 07 44 50 02 32 50 0f];
- qcom,saw2-spm-cmd-pc = [00 32 b0 11 42 07 01 b0 44
+ qcom,saw2-spm-cmd-pc = [00 10 32 b0 11 42 07 01 b0 12 44
50 02 32 50 0f];
};
@@ -453,4 +449,10 @@
reg-names = "phys_addr_base";
qcom,sleep-stats-version = <2>;
};
+
+ qcom,rpm-rbcpr-stats@fc000000 {
+ compatible = "qcom,rpmrbcpr-stats";
+ reg = <0xfc000000 0x1a0000>;
+ qcom,start-offset = <0x190010>;
+ };
};
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index a098ce9..068f581 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -864,7 +864,7 @@
vbus_dwc3-supply = <&pm8941_mvs1>;
qcom,dwc-usb3-msm-dbm-eps = <4>;
qcom,vdd-voltage-level = <1 5 7>;
- qcom,dwc-hsphy-init = <0x00D195A4>;
+ qcom,dwc-hsphy-init = <0x00D191A4>;
qcom,msm-bus,name = "usb3";
qcom,msm-bus,num-cases = <2>;
@@ -1635,12 +1635,16 @@
&gdsc_venus {
qcom,clock-names = "core_clk";
qcom,skip-logic-collapse;
+ qcom,retain-periph;
+ qcom,retain-mem;
status = "ok";
};
&gdsc_mdss {
qcom,clock-names = "core_clk", "lut_clk";
+ qcom,skip-logic-collapse;
qcom,retain-periph;
+ qcom,retain-mem;
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm9625-v2.1.dtsi b/arch/arm/boot/dts/msm9625-v2.1.dtsi
index 65ff96a..5720700 100644
--- a/arch/arm/boot/dts/msm9625-v2.1.dtsi
+++ b/arch/arm/boot/dts/msm9625-v2.1.dtsi
@@ -34,3 +34,7 @@
&ipa_hw {
qcom,ipa-hw-ver = <2>; /* IPA h-w revision */
};
+
+&hsusb_otg {
+ qcom,hsusb-l1-supported;
+};
diff --git a/arch/arm/boot/dts/msm9625-v2.dtsi b/arch/arm/boot/dts/msm9625-v2.dtsi
index b078309..14105cb 100644
--- a/arch/arm/boot/dts/msm9625-v2.dtsi
+++ b/arch/arm/boot/dts/msm9625-v2.dtsi
@@ -46,3 +46,7 @@
&ldrex_spinlock {
status = "ok";
};
+
+&hsusb_otg {
+ qcom,hsusb-l1-supported;
+};
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index 636c0f8..616995f 100644
--- a/arch/arm/boot/dts/msm9625.dtsi
+++ b/arch/arm/boot/dts/msm9625.dtsi
@@ -148,7 +148,7 @@
interrupts = <0 109 0>;
};
- usb@f9a55000 {
+ hsusb_otg: usb@f9a55000 {
compatible = "qcom,hsusb-otg";
reg = <0xf9a55000 0x400>;
interrupts = <0 134 0 0 140 0>;
diff --git a/arch/arm/boot/dts/msmsamarium.dtsi b/arch/arm/boot/dts/msmsamarium.dtsi
index 968daff..a9cca0b 100644
--- a/arch/arm/boot/dts/msmsamarium.dtsi
+++ b/arch/arm/boot/dts/msmsamarium.dtsi
@@ -96,4 +96,9 @@
qcom,pet-time = <10000>;
qcom,ipi-ping;
};
+
+ qcom,msm-mem-hole {
+ compatible = "qcom,msm-mem-hole";
+ qcom,memblock-remove = <0x07f00000 0x8000000>; /* Address and size of hole */
+ };
};
diff --git a/arch/arm/configs/msm8226-perf_defconfig b/arch/arm/configs/msm8226-perf_defconfig
index 4f0872a..a9d93b0 100644
--- a/arch/arm/configs/msm8226-perf_defconfig
+++ b/arch/arm/configs/msm8226-perf_defconfig
@@ -327,6 +327,18 @@
CONFIG_USB_EHCI_EHSET=y
CONFIG_USB_EHCI_MSM=y
CONFIG_USB_ACM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_STORAGE_DATAFAB=y
+CONFIG_USB_STORAGE_FREECOM=y
+CONFIG_USB_STORAGE_ISD200=y
+CONFIG_USB_STORAGE_USBAT=y
+CONFIG_USB_STORAGE_SDDR09=y
+CONFIG_USB_STORAGE_SDDR55=y
+CONFIG_USB_STORAGE_JUMPSHOT=y
+CONFIG_USB_STORAGE_ALAUDA=y
+CONFIG_USB_STORAGE_ONETOUCH=y
+CONFIG_USB_STORAGE_KARMA=y
+CONFIG_USB_STORAGE_CYPRESS_ATACB=y
CONFIG_USB_SERIAL=y
CONFIG_USB_SERIAL_CSVT=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
@@ -407,8 +419,10 @@
CONFIG_CRYPTO_MD4=y
CONFIG_CRYPTO_ARC4=y
CONFIG_CRYPTO_TWOFISH=y
+CONFIG_NFC_QNCI=y
CONFIG_CRYPTO_DEV_QCRYPTO=m
CONFIG_CRYPTO_DEV_QCE=y
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_MOBICORE_SUPPORT=m
CONFIG_MOBICORE_API=m
+CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
diff --git a/arch/arm/configs/msm8226_defconfig b/arch/arm/configs/msm8226_defconfig
index 3705a55..506de21 100644
--- a/arch/arm/configs/msm8226_defconfig
+++ b/arch/arm/configs/msm8226_defconfig
@@ -352,6 +352,18 @@
CONFIG_USB_EHCI_EHSET=y
CONFIG_USB_EHCI_MSM=y
CONFIG_USB_ACM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_STORAGE_DATAFAB=y
+CONFIG_USB_STORAGE_FREECOM=y
+CONFIG_USB_STORAGE_ISD200=y
+CONFIG_USB_STORAGE_USBAT=y
+CONFIG_USB_STORAGE_SDDR09=y
+CONFIG_USB_STORAGE_SDDR55=y
+CONFIG_USB_STORAGE_JUMPSHOT=y
+CONFIG_USB_STORAGE_ALAUDA=y
+CONFIG_USB_STORAGE_ONETOUCH=y
+CONFIG_USB_STORAGE_KARMA=y
+CONFIG_USB_STORAGE_CYPRESS_ATACB=y
CONFIG_USB_SERIAL=y
CONFIG_USB_SERIAL_CSVT=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
@@ -443,8 +455,10 @@
CONFIG_CRYPTO_MD4=y
CONFIG_CRYPTO_ARC4=y
CONFIG_CRYPTO_TWOFISH=y
+CONFIG_NFC_QNCI=y
CONFIG_CRYPTO_DEV_QCRYPTO=m
CONFIG_CRYPTO_DEV_QCE=y
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_MOBICORE_SUPPORT=m
CONFIG_MOBICORE_API=m
+CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
diff --git a/arch/arm/configs/msm8610-perf_defconfig b/arch/arm/configs/msm8610-perf_defconfig
index 9869fbf..3a08849 100644
--- a/arch/arm/configs/msm8610-perf_defconfig
+++ b/arch/arm/configs/msm8610-perf_defconfig
@@ -272,6 +272,7 @@
CONFIG_VIDEO_V4L2_SUBDEV_API=y
# CONFIG_MSM_CAMERA is not set
CONFIG_OV8825=y
+CONFIG_s5k4e1=y
CONFIG_HI256=y
CONFIG_MSM_CAMERA_SENSOR=y
# CONFIG_MSM_CPP is not set
@@ -381,3 +382,4 @@
# CONFIG_CRYPTO_HW is not set
CONFIG_CRC_CCITT=y
CONFIG_INPUT_KXTJ9=y
+CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
diff --git a/arch/arm/configs/msm8610_defconfig b/arch/arm/configs/msm8610_defconfig
index 24c5d9c..1998293 100644
--- a/arch/arm/configs/msm8610_defconfig
+++ b/arch/arm/configs/msm8610_defconfig
@@ -270,6 +270,7 @@
CONFIG_VIDEO_V4L2_SUBDEV_API=y
# CONFIG_MSM_CAMERA is not set
CONFIG_OV8825=y
+CONFIG_s5k4e1=y
CONFIG_HI256=y
CONFIG_MSM_CAMERA_SENSOR=y
CONFIG_MSM_CCI=y
@@ -352,6 +353,7 @@
CONFIG_MMC_SDHCI_MSM=y
CONFIG_MMC_MSM_SPS_SUPPORT=y
CONFIG_LEDS_QPNP=y
+CONFIG_LEDS_MSM_GPIO_FLASH=y
CONFIG_LEDS_TRIGGERS=y
CONFIG_SWITCH=y
CONFIG_RTC_CLASS=y
@@ -423,3 +425,4 @@
CONFIG_CRYPTO_DEV_QCEDEV=m
CONFIG_CRC_CCITT=y
CONFIG_INPUT_KXTJ9=y
+CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index d770e3f..f88ef41 100644
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -63,6 +63,7 @@
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_EVENT_TIMER=y
+CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
CONFIG_MSM_BUS_SCALING=y
CONFIG_MSM_WATCHDOG_V2=y
CONFIG_MSM_MEMORY_DUMP=y
@@ -480,3 +481,4 @@
CONFIG_CRYPTO_DEV_QCRYPTO=m
CONFIG_CRYPTO_DEV_QCE=y
CONFIG_CRYPTO_DEV_QCEDEV=y
+CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=y
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index 8eecf79..d910d93 100644
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -62,6 +62,7 @@
CONFIG_MSM_TZ_LOG=y
CONFIG_MSM_DIRECT_SCLK_ACCESS=y
CONFIG_MSM_EVENT_TIMER=y
+CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y
CONFIG_MSM_BUS_SCALING=y
CONFIG_MSM_BUSPM_DEV=m
CONFIG_MSM_WATCHDOG_V2=y
@@ -512,3 +513,4 @@
CONFIG_CRYPTO_DEV_QCRYPTO=m
CONFIG_CRYPTO_DEV_QCE=y
CONFIG_CRYPTO_DEV_QCEDEV=y
+CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=y
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index e9a236e..17db01e 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -309,6 +309,7 @@
obj-$(CONFIG_ARCH_MSM8610) += board-8610.o board-8610-gpiomux.o
obj-$(CONFIG_ARCH_MSM8610) += clock-local2.o clock-pll.o clock-8610.o clock-rpm.o clock-voter.o
obj-$(CONFIG_ARCH_MSM8610) += clock-dsi-8610.o
+obj-$(CONFIG_ARCH_MSMKRYPTON) += clock-local2.o clock-pll.o clock-krypton.o clock-rpm.o clock-voter.o
obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire.o board-sapphire-gpio.o
obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-keypad.o board-sapphire-panel.o
diff --git a/arch/arm/mach-msm/acpuclock.c b/arch/arm/mach-msm/acpuclock.c
index c722304..2c120da 100644
--- a/arch/arm/mach-msm/acpuclock.c
+++ b/arch/arm/mach-msm/acpuclock.c
@@ -44,6 +44,8 @@
uint32_t acpuclk_get_switch_time(void)
{
+ if (!acpuclk_data)
+ return 0;
return acpuclk_data->switch_time_us;
}
diff --git a/arch/arm/mach-msm/board-8226-gpiomux.c b/arch/arm/mach-msm/board-8226-gpiomux.c
index 378edc8..a32031d 100644
--- a/arch/arm/mach-msm/board-8226-gpiomux.c
+++ b/arch/arm/mach-msm/board-8226-gpiomux.c
@@ -136,8 +136,8 @@
static struct gpiomux_setting lcd_rst_act_cfg = {
.func = GPIOMUX_FUNC_GPIO,
.drv = GPIOMUX_DRV_8MA,
- .pull = GPIOMUX_PULL_NONE,
- .dir = GPIOMUX_OUT_LOW,
+ .pull = GPIOMUX_PULL_UP,
+ .dir = GPIOMUX_OUT_HIGH,
};
static struct gpiomux_setting lcd_rst_sus_cfg = {
@@ -211,6 +211,18 @@
[GPIOMUX_SUSPENDED] = &gpio_spi_cs_eth_config,
},
},
+ { /* NFC */
+ .gpio = 10, /* BLSP1 QUP3 I2C_DAT */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_i2c_config,
+ },
+ },
+ { /* NFC */
+ .gpio = 11, /* BLSP1 QUP3 I2C_CLK */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_i2c_config,
+ },
+ },
};
static struct msm_gpiomux_config msm_synaptics_configs[] __initdata = {
diff --git a/arch/arm/mach-msm/board-krypton.c b/arch/arm/mach-msm/board-krypton.c
index b40fcfa..0d06b83 100644
--- a/arch/arm/mach-msm/board-krypton.c
+++ b/arch/arm/mach-msm/board-krypton.c
@@ -31,19 +31,6 @@
#include "clock.h"
#include "devices.h"
-static struct clk_lookup msm_clocks_dummy[] = {
- CLK_DUMMY("core_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
- CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
- CLK_DUMMY("core_clk", SPI_CLK, "f9928000.spi", OFF),
- CLK_DUMMY("iface_clk", SPI_P_CLK, "f9928000.spi", OFF),
-
-};
-
-static struct clock_init_data msm_dummy_clock_init_data __initdata = {
- .table = msm_clocks_dummy,
- .size = ARRAY_SIZE(msm_clocks_dummy),
-};
-
static struct of_dev_auxdata msmkrypton_auxdata_lookup[] __initdata = {
{}
};
@@ -57,7 +44,7 @@
void __init msmkrypton_add_drivers(void)
{
msm_smd_init();
- msm_clock_init(&msm_dummy_clock_init_data);
+ msm_clock_init(&msmkrypton_clock_init_data);
}
static void __init msmkrypton_map_io(void)
diff --git a/arch/arm/mach-msm/clock-8226.c b/arch/arm/mach-msm/clock-8226.c
index 7f0a394..2a4617d 100644
--- a/arch/arm/mach-msm/clock-8226.c
+++ b/arch/arm/mach-msm/clock-8226.c
@@ -2901,6 +2901,7 @@
static DEFINE_CLK_BRANCH_VOTER(cxo_pil_mss_clk, &xo.c);
static DEFINE_CLK_BRANCH_VOTER(cxo_wlan_clk, &xo.c);
static DEFINE_CLK_BRANCH_VOTER(cxo_pil_pronto_clk, &xo.c);
+static DEFINE_CLK_BRANCH_VOTER(cxo_lpm_clk, &xo.c);
#ifdef CONFIG_DEBUG_FS
@@ -3113,6 +3114,9 @@
CLK_LOOKUP("apc3_m_clk", apc3_m_clk, ""),
CLK_LOOKUP("l2_m_clk", l2_m_clk, ""),
+ /* LPM Resources */
+ CLK_LOOKUP("xo", cxo_lpm_clk.c, "fc4281d0.qcom,mpm"),
+
/* PIL-LPASS */
CLK_LOOKUP("xo", cxo_pil_lpass_clk.c, "fe200000.qcom,lpass"),
CLK_LOOKUP("core_clk", q6ss_xo_clk.c, "fe200000.qcom,lpass"),
@@ -3125,7 +3129,8 @@
CLK_LOOKUP("bus_clk", gcc_mss_q6_bimc_axi_clk.c, "fc880000.qcom,mss"),
CLK_LOOKUP("iface_clk", gcc_mss_cfg_ahb_clk.c, "fc880000.qcom,mss"),
CLK_LOOKUP("mem_clk", gcc_boot_rom_ahb_clk.c, "fc880000.qcom,mss"),
-
+ /* NFC */
+ CLK_LOOKUP("ref_clk", cxo_d1_a_pin.c, "2-000e"),
/* PIL-PRONTO */
CLK_LOOKUP("xo", cxo_pil_pronto_clk.c, "fb21b000.qcom,pronto"),
@@ -3250,6 +3255,9 @@
CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "f9927000.i2c"),
CLK_LOOKUP("core_clk", gcc_blsp1_qup5_i2c_apps_clk.c, "f9927000.i2c"),
+ /* I2C Clocks nfc */
+ CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "f9925000.i2c"),
+ CLK_LOOKUP("core_clk", gcc_blsp1_qup3_i2c_apps_clk.c, "f9925000.i2c"),
/* lsuart-v14 Clocks */
CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "f991f000.serial"),
CLK_LOOKUP("core_clk", gcc_blsp1_uart3_apps_clk.c, "f991f000.serial"),
@@ -3315,7 +3323,6 @@
CLK_LOOKUP("core_clk", gcc_blsp1_qup1_i2c_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp1_qup2_i2c_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp1_qup2_spi_apps_clk.c, ""),
- CLK_LOOKUP("core_clk", gcc_blsp1_qup3_i2c_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp1_qup3_spi_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp1_qup4_i2c_apps_clk.c, ""),
CLK_LOOKUP("core_clk", gcc_blsp1_qup4_spi_apps_clk.c, ""),
diff --git a/arch/arm/mach-msm/clock-8610.c b/arch/arm/mach-msm/clock-8610.c
index 18a81d0..eb9a627 100644
--- a/arch/arm/mach-msm/clock-8610.c
+++ b/arch/arm/mach-msm/clock-8610.c
@@ -510,6 +510,15 @@
static DEFINE_CLK_VOTER(pnoc_sps_clk, &pnoc_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(pnoc_iommu_clk, &pnoc_clk.c, LONG_MAX);
+static DEFINE_CLK_BRANCH_VOTER(cxo_otg_clk, &gcc_xo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(cxo_lpass_pil_clk, &gcc_xo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(cxo_lpm_clk, &gcc_xo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(cxo_pil_pronto_clk, &gcc_xo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(cxo_mss_pil_clk, &gcc_xo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(cxo_pil_mba_clk, &gcc_xo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(cxo_wlan_clk, &gcc_xo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(cxo_acpu_clk, &gcc_xo_clk_src.c);
+
static DEFINE_CLK_MEASURE(apc0_m_clk);
static DEFINE_CLK_MEASURE(apc1_m_clk);
static DEFINE_CLK_MEASURE(apc2_m_clk);
@@ -2740,17 +2749,18 @@
};
static struct clk_lookup msm_clocks_8610[] = {
- CLK_LOOKUP("xo", gcc_xo_clk_src.c, "msm_otg"),
- CLK_LOOKUP("xo", gcc_xo_clk_src.c, "fe200000.qcom,lpass"),
+ CLK_LOOKUP("xo", cxo_otg_clk.c, "msm_otg"),
+ CLK_LOOKUP("xo", cxo_lpass_pil_clk.c, "fe200000.qcom,lpass"),
+ CLK_LOOKUP("xo", cxo_lpm_clk.c, "fc4281d0.qcom,mpm"),
- CLK_LOOKUP("xo", gcc_xo_clk_src.c, "fc880000.qcom,mss"),
+ CLK_LOOKUP("xo", cxo_mss_pil_clk.c, "fc880000.qcom,mss"),
CLK_LOOKUP("bus_clk", gcc_mss_q6_bimc_axi_clk.c, "fc880000.qcom,mss"),
CLK_LOOKUP("iface_clk", gcc_mss_cfg_ahb_clk.c, "fc880000.qcom,mss"),
CLK_LOOKUP("mem_clk", gcc_boot_rom_ahb_clk.c, "fc880000.qcom,mss"),
- CLK_LOOKUP("xo", gcc_xo_clk_src.c, "pil-mba"),
- CLK_LOOKUP("xo", gcc_xo_clk_src.c, "fb000000.qcom,wcnss-wlan"),
- CLK_LOOKUP("xo", gcc_xo_clk_src.c, "fb21b000.qcom,pronto"),
+ CLK_LOOKUP("xo", cxo_pil_mba_clk.c, "pil-mba"),
+ CLK_LOOKUP("xo", cxo_wlan_clk.c, "fb000000.qcom,wcnss-wlan"),
+ CLK_LOOKUP("xo", cxo_pil_pronto_clk.c, "fb21b000.qcom,pronto"),
CLK_LOOKUP("measure", measure_clk.c, "debug"),
CLK_LOOKUP("iface_clk", gcc_blsp1_ahb_clk.c, "f991f000.serial"),
@@ -3000,9 +3010,11 @@
/* MM sensor clocks */
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6-006f"),
+ CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6-007d"),
CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6-006d"),
CLK_LOOKUP("cam_src_clk", mclk1_clk_src.c, "6-0078"),
CLK_LOOKUP("cam_clk", mclk0_clk.c, "6-006f"),
+ CLK_LOOKUP("cam_clk", mclk0_clk.c, "6-007d"),
CLK_LOOKUP("cam_clk", mclk0_clk.c, "6-006d"),
CLK_LOOKUP("cam_clk", mclk1_clk.c, "6-0078"),
@@ -3072,7 +3084,7 @@
CLK_LOOKUP("iface_clk", q6ss_ahb_lfabif_clk.c, "fe200000.qcom,lpass"),
CLK_LOOKUP("reg_clk", q6ss_ahbm_clk.c, "fe200000.qcom,lpass"),
- CLK_LOOKUP("xo", gcc_xo_a_clk_src.c, "f9011050.qcom,acpuclk"),
+ CLK_LOOKUP("xo", cxo_acpu_clk.c, "f9011050.qcom,acpuclk"),
CLK_LOOKUP("gpll0", gpll0_ao_clk_src.c, "f9011050.qcom,acpuclk"),
CLK_LOOKUP("a7sspll", a7sspll.c, "f9011050.qcom,acpuclk"),
@@ -3082,7 +3094,7 @@
CLK_LOOKUP("measure_clk", apc3_m_clk, ""),
CLK_LOOKUP("measure_clk", l2_m_clk, ""),
- CLK_LOOKUP("xo", gcc_xo_clk_src.c, "fb000000.qcom,wcnss-wlan"),
+ CLK_LOOKUP("xo", cxo_wlan_clk.c, "fb000000.qcom,wcnss-wlan"),
CLK_LOOKUP("rf_clk", cxo_a1.c, "fb000000.qcom,wcnss-wlan"),
CLK_LOOKUP("iface_clk", mdp_ahb_clk.c, "fd900000.qcom,mdss_mdp"),
diff --git a/arch/arm/mach-msm/clock-8974.c b/arch/arm/mach-msm/clock-8974.c
index ac34ed3..bd5a12e 100644
--- a/arch/arm/mach-msm/clock-8974.c
+++ b/arch/arm/mach-msm/clock-8974.c
@@ -831,6 +831,7 @@
static DEFINE_CLK_BRANCH_VOTER(cxo_pil_pronto_clk, &cxo_clk_src.c);
static DEFINE_CLK_BRANCH_VOTER(cxo_dwc3_clk, &cxo_clk_src.c);
static DEFINE_CLK_BRANCH_VOTER(cxo_ehci_host_clk, &cxo_clk_src.c);
+static DEFINE_CLK_BRANCH_VOTER(cxo_lpm_clk, &cxo_clk_src.c);
static struct clk_freq_tbl ftbl_gcc_usb30_master_clk[] = {
F(125000000, gpll0, 1, 5, 24),
@@ -4855,6 +4856,7 @@
CLK_LOOKUP("xo", cxo_pil_pronto_clk.c, "fb21b000.qcom,pronto"),
CLK_LOOKUP("xo", cxo_dwc3_clk.c, "msm_dwc3"),
CLK_LOOKUP("xo", cxo_ehci_host_clk.c, "msm_ehci_host"),
+ CLK_LOOKUP("xo", cxo_lpm_clk.c, "fc4281d0.qcom,mpm"),
CLK_LOOKUP("measure", measure_clk.c, "debug"),
diff --git a/arch/arm/mach-msm/clock-9625.c b/arch/arm/mach-msm/clock-9625.c
index 14648ec..3b07069 100644
--- a/arch/arm/mach-msm/clock-9625.c
+++ b/arch/arm/mach-msm/clock-9625.c
@@ -430,9 +430,10 @@
static DEFINE_CLK_VOTER(pnoc_sdcc2_clk, &pnoc_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(pnoc_sdcc3_clk, &pnoc_clk.c, LONG_MAX);
-
static DEFINE_CLK_VOTER(pnoc_sps_clk, &pnoc_clk.c, LONG_MAX);
+static DEFINE_CLK_BRANCH_VOTER(cxo_lpm_clk, &cxo_clk_src.c);
+
static struct clk_freq_tbl ftbl_gcc_ipa_clk[] = {
F( 50000000, gpll0, 12, 0, 0),
F( 92310000, gpll0, 6.5, 0, 0),
@@ -1762,6 +1763,7 @@
static struct clk_lookup msm_clocks_9625[] = {
CLK_LOOKUP("xo", cxo_clk_src.c, ""),
+ CLK_LOOKUP("xo", cxo_lpm_clk.c, "fc4281d0.qcom,mpm"),
CLK_LOOKUP("measure", measure_clk.c, "debug"),
CLK_LOOKUP("pll0", gpll0_activeonly_clk_src.c, "f9010008.qcom,acpuclk"),
diff --git a/arch/arm/mach-msm/clock-debug.c b/arch/arm/mach-msm/clock-debug.c
index fe43b72..8ec87d1 100644
--- a/arch/arm/mach-msm/clock-debug.c
+++ b/arch/arm/mach-msm/clock-debug.c
@@ -22,6 +22,8 @@
#include <linux/list.h>
#include <linux/clkdev.h>
#include <linux/uaccess.h>
+#include <linux/mutex.h>
+
#include <mach/clk-provider.h>
#include "clock.h"
@@ -351,49 +353,24 @@
debugfs_remove_recursive(clk_dir);
return -ENOMEM;
}
-
-/**
- * clock_debug_register() - Add additional clocks to clock debugfs hierarchy
- * @table: Table of clocks to create debugfs nodes for
- * @size: Size of @table
- *
- * Use this function to register additional clocks in debugfs. The clock debugfs
- * hierarchy must have already been initialized with clock_debug_init() prior to
- * calling this function. Unlike clock_debug_init(), this may be called multiple
- * times with different clock lists and can be used after the kernel has
- * finished booting.
- */
-int clock_debug_register(struct clk_lookup *table, size_t size)
-{
- struct clk_table *clk_table;
- unsigned long flags;
- int i;
-
- clk_table = kmalloc(sizeof(*clk_table), GFP_KERNEL);
- if (!clk_table)
- return -ENOMEM;
-
- clk_table->clocks = table;
- clk_table->num_clocks = size;
-
- spin_lock_irqsave(&clk_list_lock, flags);
- list_add_tail(&clk_table->node, &clk_list);
- spin_unlock_irqrestore(&clk_list_lock, flags);
-
- for (i = 0; i < size; i++)
- clock_debug_add(table[i].clk);
-
- return 0;
-}
+static DEFINE_MUTEX(clk_debug_lock);
+static int clk_debug_init_once;
/**
* clock_debug_init() - Initialize clock debugfs
+ * Lock clk_debug_lock before invoking this function.
*/
-int __init clock_debug_init(void)
+static int clock_debug_init(void)
{
+ if (clk_debug_init_once)
+ return 0;
+
+ clk_debug_init_once = 1;
+
debugfs_base = debugfs_create_dir("clk", NULL);
if (!debugfs_base)
return -ENOMEM;
+
if (!debugfs_create_u32("debug_suspend", S_IRUGO | S_IWUSR,
debugfs_base, &debug_suspend)) {
debugfs_remove_recursive(debugfs_base);
@@ -407,6 +384,45 @@
return 0;
}
+/**
+ * clock_debug_register() - Add additional clocks to clock debugfs hierarchy
+ * @table: Table of clocks to create debugfs nodes for
+ * @size: Size of @table
+ *
+ */
+int clock_debug_register(struct clk_lookup *table, size_t size)
+{
+ struct clk_table *clk_table;
+ unsigned long flags;
+ int i, ret;
+
+ mutex_lock(&clk_debug_lock);
+
+ ret = clock_debug_init();
+ if (ret)
+ goto out;
+
+ clk_table = kmalloc(sizeof(*clk_table), GFP_KERNEL);
+ if (!clk_table) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ clk_table->clocks = table;
+ clk_table->num_clocks = size;
+
+ spin_lock_irqsave(&clk_list_lock, flags);
+ list_add_tail(&clk_table->node, &clk_list);
+ spin_unlock_irqrestore(&clk_list_lock, flags);
+
+ for (i = 0; i < size; i++)
+ clock_debug_add(table[i].clk);
+
+out:
+ mutex_unlock(&clk_debug_lock);
+ return ret;
+}
+
static int clock_debug_print_clock(struct clk *c)
{
char *start = "";
diff --git a/arch/arm/mach-msm/clock-krypton.c b/arch/arm/mach-msm/clock-krypton.c
new file mode 100644
index 0000000..aaee003
--- /dev/null
+++ b/arch/arm/mach-msm/clock-krypton.c
@@ -0,0 +1,125 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/regulator/consumer.h>
+
+#include <mach/rpm-regulator-smd.h>
+#include <mach/socinfo.h>
+#include <mach/rpm-smd.h>
+
+#include "clock-local2.h"
+#include "clock-pll.h"
+#include "clock-rpm.h"
+#include "clock-voter.h"
+#include "clock.h"
+
+static struct clk_lookup msm_clocks_dummy[] = {
+ CLK_DUMMY("core_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
+ CLK_DUMMY("iface_clk", BLSP1_UART_CLK, "f991f000.serial", OFF),
+ CLK_DUMMY("core_clk", SPI_CLK, "f9928000.spi", OFF),
+ CLK_DUMMY("iface_clk", SPI_P_CLK, "f9928000.spi", OFF),
+
+ CLK_DUMMY("bus_clk", cnoc_msmbus_clk.c, "msm_config_noc", OFF),
+ CLK_DUMMY("bus_a_clk", cnoc_msmbus_a_clk.c, "msm_config_noc", OFF),
+ CLK_DUMMY("bus_clk", snoc_msmbus_clk.c, "msm_sys_noc", OFF),
+ CLK_DUMMY("bus_a_clk", snoc_msmbus_a_clk.c, "msm_sys_noc", OFF),
+ CLK_DUMMY("bus_clk", pnoc_msmbus_clk.c, "msm_periph_noc", OFF),
+ CLK_DUMMY("bus_a_clk", pnoc_msmbus_a_clk.c, "msm_periph_noc", OFF),
+ CLK_DUMMY("mem_clk", bimc_msmbus_clk.c, "msm_bimc", OFF),
+ CLK_DUMMY("mem_a_clk", bimc_msmbus_a_clk.c, "msm_bimc", OFF),
+ CLK_DUMMY("mem_clk", bimc_acpu_a_clk.c, "", OFF),
+
+ CLK_DUMMY("clktype", gcc_imem_axi_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_imem_cfg_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_mss_cfg_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_mss_q6_bimc_axi_clk , "drivername", OFF),
+ CLK_DUMMY("mem_clk", gcc_usb30_master_clk , "drivername", OFF),
+ CLK_DUMMY("sleep_clk", gcc_usb30_sleep_clk , "drivername", OFF),
+ CLK_DUMMY("utmi_clk", gcc_usb30_mock_utmi_clk , "drivername", OFF),
+ CLK_DUMMY("iface_clk", gcc_usb_hsic_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_usb_hsic_system_clk , "drivername", OFF),
+ CLK_DUMMY("phyclk", gcc_usb_hsic_clk , "drivername", OFF),
+ CLK_DUMMY("cal_clk", gcc_usb_hsic_io_cal_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_usb_hs_system_clk , "drivername", OFF),
+ CLK_DUMMY("iface_clk", gcc_usb_hs_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("sleep_a_clk", gcc_usb2a_phy_sleep_clk , "drivername", OFF),
+ CLK_DUMMY("sleep_b_clk", gcc_usb2b_phy_sleep_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_sdcc2_apps_clk , "drivername", OFF),
+ CLK_DUMMY("iface_clk", gcc_sdcc2_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_sdcc3_apps_clk , "drivername", OFF),
+ CLK_DUMMY("iface_clk", gcc_sdcc3_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", sdcc3_apps_clk_src , "drivername", OFF),
+ CLK_DUMMY("iface_clk", gcc_blsp1_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup1_spi_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup1_i2c_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_uart1_apps_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup2_spi_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup2_i2c_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_uart2_apps_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup3_spi_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup3_i2c_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_uart3_apps_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup4_spi_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup4_i2c_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_uart4_apps_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup5_spi_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup5_i2c_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_uart5_apps_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup6_spi_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_qup6_i2c_apps_clk, "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_blsp1_uart6_apps_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", blsp1_uart6_apps_clk_src , "drivername", OFF),
+ CLK_DUMMY("iface_clk", gcc_pdm_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_pdm2_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_prng_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("dma_bam_pclk", gcc_bam_dma_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("mem_clk", gcc_boot_rom_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_ce1_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_ce1_axi_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_ce1_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk_src", ce1_clk_src , "drivername", OFF),
+ CLK_DUMMY("bus_clk", gcc_lpass_q6_axi_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", pcie_pipe_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_gp1_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", gp1_clk_src , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_gp2_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", gp2_clk_src , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_gp3_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", gp3_clk_src , "drivername", OFF),
+ CLK_DUMMY("core_clk", gcc_ipa_clk , "drivername", OFF),
+ CLK_DUMMY("iface_clk", gcc_ipa_cnoc_clk , "drivername", OFF),
+ CLK_DUMMY("inactivity_clk", gcc_ipa_sleep_clk , "drivername", OFF),
+ CLK_DUMMY("core_clk_src", ipa_clk_src , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_dcs_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", dcs_clk_src , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_pcie_cfg_ahb_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_pcie_pipe_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_pcie_axi_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_pcie_sleep_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", gcc_pcie_axi_mstr_clk , "drivername", OFF),
+ CLK_DUMMY("clktype", pcie_pipe_clk_src , "drivername", OFF),
+ CLK_DUMMY("clktype", pcie_aux_clk_src , "drivername", OFF),
+};
+
+struct clock_init_data msmkrypton_clock_init_data __initdata = {
+ .table = msm_clocks_dummy,
+ .size = ARRAY_SIZE(msm_clocks_dummy),
+};
diff --git a/arch/arm/mach-msm/clock-mdss-8974.c b/arch/arm/mach-msm/clock-mdss-8974.c
index 1245287..6877b4d9 100644
--- a/arch/arm/mach-msm/clock-mdss-8974.c
+++ b/arch/arm/mach-msm/clock-mdss-8974.c
@@ -906,18 +906,19 @@
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
udelay(1000);
- do {
+ pll_locked = dsi_pll_lock_status();
+ for (i = 0; (i < 4) && !pll_locked; i++) {
+ DSS_REG_W(mdss_dsi_base,
+ DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07);
+ if (i != 0)
+ DSS_REG_W(mdss_dsi_base,
+ DSI_0_PHY_PLL_UNIPHY_PLL_CAL_CFG1, 0x34);
+ udelay(1);
+ DSS_REG_W(mdss_dsi_base,
+ DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
+ udelay(1000);
pll_locked = dsi_pll_lock_status();
- if (!pll_locked) {
- DSS_REG_W(mdss_dsi_base,
- DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07);
- udelay(1);
- DSS_REG_W(mdss_dsi_base,
- DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
- udelay(1000);
- i++;
- }
- } while ((i < 3) && !pll_locked);
+ }
if (pll_locked)
pr_debug("%s: PLL Locked at attempt #%d\n", __func__, i);
@@ -940,17 +941,17 @@
* PLL to successfully lock
*/
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x01);
- udelay(1);
+ udelay(200);
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
- udelay(1);
+ udelay(200);
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07);
- udelay(1);
+ udelay(200);
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
- udelay(1);
+ udelay(200);
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x07);
- udelay(1);
+ udelay(200);
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
- udelay(1);
+ udelay(1000);
pll_locked = dsi_pll_lock_status();
pr_debug("%s: PLL status = %s\n", __func__,
@@ -1029,6 +1030,7 @@
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x05);
udelay(200);
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0d);
+ udelay(1);
DSS_REG_W(mdss_dsi_base, DSI_0_PHY_PLL_UNIPHY_PLL_GLB_CFG, 0x0f);
udelay(1000);
diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c
index 582bccf..3c43223 100644
--- a/arch/arm/mach-msm/clock.c
+++ b/arch/arm/mach-msm/clock.c
@@ -23,6 +23,7 @@
#include <linux/clkdev.h>
#include <linux/list.h>
#include <linux/regulator/consumer.h>
+#include <linux/mutex.h>
#include <trace/events/power.h>
#include <mach/clk-provider.h>
#include "clock.h"
@@ -39,6 +40,8 @@
};
static LIST_HEAD(handoff_vdd_list);
+static DEFINE_MUTEX(msm_clock_init_lock);
+
/* Find the voltage level required for a given rate. */
int find_vdd_level(struct clk *clk, unsigned long rate)
{
@@ -573,7 +576,7 @@
}
EXPORT_SYMBOL(clk_set_flags);
-static struct clock_init_data *clk_init_data;
+static LIST_HEAD(initdata_list);
static void init_sibling_lists(struct clk_lookup *clock_tbl, size_t num_clocks)
{
@@ -588,33 +591,6 @@
}
}
-/**
- * msm_clock_register() - Register additional clock tables
- * @table: Table of clocks
- * @size: Size of @table
- *
- * Upon return, clock APIs may be used to control clocks registered using this
- * function. This API may only be used after msm_clock_init() has completed.
- * Unlike msm_clock_init(), this function may be called multiple times with
- * different clock lists and used after the kernel has finished booting.
- */
-int msm_clock_register(struct clk_lookup *table, size_t size)
-{
- if (!clk_init_data)
- return -ENODEV;
-
- if (!table)
- return -EINVAL;
-
- init_sibling_lists(table, size);
- clkdev_add_table(table, size);
- clock_debug_register(table, size);
-
- return 0;
-}
-EXPORT_SYMBOL(msm_clock_register);
-
-
static void vdd_class_init(struct clk_vdd_class *vdd)
{
struct handoff_vdd *v;
@@ -646,7 +622,7 @@
list_add_tail(&v->list, &handoff_vdd_list);
}
-static int __init __handoff_clk(struct clk *clk)
+static int __handoff_clk(struct clk *clk)
{
enum handoff state = HANDOFF_DISABLED_CLK;
struct handoff_clk *h = NULL;
@@ -726,29 +702,20 @@
}
/**
- * msm_clock_init() - Register and initialize a clock driver
- * @data: Driver-specific clock initialization data
+ * msm_clock_register() - Register additional clock tables
+ * @table: Table of clocks
+ * @size: Size of @table
*
- * Upon return from this call, clock APIs may be used to control
- * clocks registered with this API.
+ * Upon return, clock APIs may be used to control clocks registered using this
+ * function.
*/
-int __init msm_clock_init(struct clock_init_data *data)
+int msm_clock_register(struct clk_lookup *table, size_t size)
{
- unsigned n;
- struct clk_lookup *clock_tbl;
- size_t num_clocks;
+ int n = 0;
- if (!data)
- return -EINVAL;
+ mutex_lock(&msm_clock_init_lock);
- clk_init_data = data;
- if (clk_init_data->pre_init)
- clk_init_data->pre_init();
-
- clock_tbl = data->table;
- num_clocks = data->size;
-
- init_sibling_lists(clock_tbl, num_clocks);
+ init_sibling_lists(table, size);
/*
* Enable regulators and temporarily set them up at maximum voltage.
@@ -757,23 +724,50 @@
* late_init, by which time we assume all the clocks would have been
* handed off.
*/
- for (n = 0; n < num_clocks; n++)
- vdd_class_init(clock_tbl[n].clk->vdd_class);
+ for (n = 0; n < size; n++)
+ vdd_class_init(table[n].clk->vdd_class);
/*
* Detect and preserve initial clock state until clock_late_init() or
* a driver explicitly changes it, whichever is first.
*/
- for (n = 0; n < num_clocks; n++)
- __handoff_clk(clock_tbl[n].clk);
+ for (n = 0; n < size; n++)
+ __handoff_clk(table[n].clk);
- clkdev_add_table(clock_tbl, num_clocks);
+ clkdev_add_table(table, size);
- if (clk_init_data->post_init)
- clk_init_data->post_init();
+ clock_debug_register(table, size);
- clock_debug_init();
- clock_debug_register(clock_tbl, num_clocks);
+ mutex_unlock(&msm_clock_init_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_clock_register);
+
+/**
+ * msm_clock_init() - Register and initialize a clock driver
+ * @data: Driver-specific clock initialization data
+ *
+ * Upon return from this call, clock APIs may be used to control
+ * clocks registered with this API.
+ */
+int __init msm_clock_init(struct clock_init_data *data)
+{
+ if (!data)
+ return -EINVAL;
+
+ if (data->pre_init)
+ data->pre_init();
+
+ mutex_lock(&msm_clock_init_lock);
+ if (data->late_init)
+ list_add(&data->list, &initdata_list);
+ mutex_unlock(&msm_clock_init_lock);
+
+ msm_clock_register(data->table, data->size);
+
+ if (data->post_init)
+ data->post_init();
return 0;
}
@@ -782,12 +776,21 @@
{
struct handoff_clk *h, *h_temp;
struct handoff_vdd *v, *v_temp;
+ struct clock_init_data *initdata, *initdata_temp;
int ret = 0;
- if (clk_init_data->late_init)
- ret = clk_init_data->late_init();
-
pr_info("%s: Removing enables held for handed-off clocks\n", __func__);
+
+ mutex_lock(&msm_clock_init_lock);
+
+ list_for_each_entry_safe(initdata, initdata_temp,
+ &initdata_list, list) {
+ ret = initdata->late_init();
+ if (ret)
+ pr_err("%s: %pS failed late_init.\n", __func__,
+ initdata);
+ }
+
list_for_each_entry_safe(h, h_temp, &handoff_list, list) {
clk_disable_unprepare(h->clk);
list_del(&h->list);
@@ -800,6 +803,11 @@
kfree(v);
}
+ mutex_unlock(&msm_clock_init_lock);
+
return ret;
}
-late_initcall(clock_late_init);
+/* clock_late_init should run only after all deferred probing
+ * (excluding DLKM probes) has completed.
+ */
+late_initcall_sync(clock_late_init);
diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h
index 2a65d2f..e294f4c 100644
--- a/arch/arm/mach-msm/clock.h
+++ b/arch/arm/mach-msm/clock.h
@@ -26,6 +26,7 @@
* @late_init: called during late init
*/
struct clock_init_data {
+ struct list_head list;
struct clk_lookup *table;
size_t size;
void (*pre_init)(void);
@@ -55,16 +56,15 @@
extern struct clock_init_data msm8226_rumi_clock_init_data;
extern struct clock_init_data msm8084_clock_init_data;
extern struct clock_init_data mpq8092_clock_init_data;
+extern struct clock_init_data msmkrypton_clock_init_data;
int msm_clock_init(struct clock_init_data *data);
int find_vdd_level(struct clk *clk, unsigned long rate);
#ifdef CONFIG_DEBUG_FS
-int clock_debug_init(void);
int clock_debug_register(struct clk_lookup *t, size_t s);
void clock_debug_print_enabled(void);
#else
-static inline int clock_debug_init(void) { return 0; }
static inline int clock_debug_register(struct clk_lookup *t, size_t s)
{
return 0;
diff --git a/arch/arm/mach-msm/include/mach/iommu.h b/arch/arm/mach-msm/include/mach/iommu.h
index eeda7ce..541ca44 100644
--- a/arch/arm/mach-msm/include/mach/iommu.h
+++ b/arch/arm/mach-msm/include/mach/iommu.h
@@ -189,7 +189,8 @@
};
enum dump_reg {
- DUMP_REG_FAR0,
+ DUMP_REG_FIRST,
+ DUMP_REG_FAR0 = DUMP_REG_FIRST,
DUMP_REG_FAR1,
DUMP_REG_PAR0,
DUMP_REG_PAR1,
@@ -201,10 +202,23 @@
DUMP_REG_SCTLR,
DUMP_REG_ACTLR,
DUMP_REG_PRRR,
+ DUMP_REG_MAIR0 = DUMP_REG_PRRR,
DUMP_REG_NMRR,
+ DUMP_REG_MAIR1 = DUMP_REG_NMRR,
MAX_DUMP_REGS,
};
+struct dump_regs_tbl {
+ /*
+ * To keep things context-bank-agnostic, we only store the CB
+ * register offset in `key'
+ */
+ unsigned long key;
+ const char *name;
+ int offset;
+};
+extern struct dump_regs_tbl dump_regs_tbl[MAX_DUMP_REGS];
+
#define COMBINE_DUMP_REG(upper, lower) (((u64) upper << 32) | lower)
struct msm_iommu_context_reg {
diff --git a/arch/arm/mach-msm/include/mach/iommu_hw-v1.h b/arch/arm/mach-msm/include/mach/iommu_hw-v1.h
index 1c20d04..04cd441 100644
--- a/arch/arm/mach-msm/include/mach/iommu_hw-v1.h
+++ b/arch/arm/mach-msm/include/mach/iommu_hw-v1.h
@@ -19,6 +19,8 @@
#define GET_GLOBAL_REG(reg, base) (readl_relaxed((base) + (reg)))
#define GET_CTX_REG(reg, base, ctx) \
(readl_relaxed((base) + CTX_OFFSET + (reg) + ((ctx) << CTX_SHIFT)))
+#define GET_CTX_REG_L(reg, base, ctx) \
+ (readll_relaxed((base) + CTX_OFFSET + (reg) + ((ctx) << CTX_SHIFT)))
#define SET_GLOBAL_REG(reg, base, val) writel_relaxed((val), ((base) + (reg)))
@@ -196,7 +198,7 @@
#define GET_CONTEXTIDR(b, c) GET_CTX_REG(CB_CONTEXTIDR, (b), (c))
#define GET_PRRR(b, c) GET_CTX_REG(CB_PRRR, (b), (c))
#define GET_NMRR(b, c) GET_CTX_REG(CB_NMRR, (b), (c))
-#define GET_PAR(b, c) GET_CTX_REG(CB_PAR, (b), (c))
+#define GET_PAR(b, c) GET_CTX_REG_L(CB_PAR, (b), (c))
#define GET_FSR(b, c) GET_CTX_REG(CB_FSR, (b), (c))
#define GET_FSRRESTORE(b, c) GET_CTX_REG(CB_FSRRESTORE, (b), (c))
#define GET_FAR(b, c) GET_CTX_REG(CB_FAR, (b), (c))
@@ -1307,6 +1309,7 @@
#define CB_PAR_TF (CB_PAR_TF_MASK << CB_PAR_TF_SHIFT)
#define CB_PAR_AFF (CB_PAR_AFF_MASK << CB_PAR_AFF_SHIFT)
#define CB_PAR_PF (CB_PAR_PF_MASK << CB_PAR_PF_SHIFT)
+#define CB_PAR_EF (CB_PAR_EF_MASK << CB_PAR_EF_SHIFT)
#define CB_PAR_TLBMCF (CB_PAR_TLBMCF_MASK << CB_PAR_TLBMCF_SHIFT)
#define CB_PAR_TLBLKF (CB_PAR_TLBLKF_MASK << CB_PAR_TLBLKF_SHIFT)
#define CB_PAR_ATOT (CB_PAR_ATOT_MASK << CB_PAR_ATOT_SHIFT)
@@ -1682,11 +1685,12 @@
#define CB_PAR_TF_MASK 0x01
#define CB_PAR_AFF_MASK 0x01
#define CB_PAR_PF_MASK 0x01
+#define CB_PAR_EF_MASK 0x01
#define CB_PAR_TLBMCF_MASK 0x01
#define CB_PAR_TLBLKF_MASK 0x01
-#define CB_PAR_ATOT_MASK 0x01
-#define CB_PAR_PLVL_MASK 0x03
-#define CB_PAR_STAGE_MASK 0x01
+#define CB_PAR_ATOT_MASK 0x01ULL
+#define CB_PAR_PLVL_MASK 0x03ULL
+#define CB_PAR_STAGE_MASK 0x01ULL
/* Primary Region Remap Register: CB_PRRR */
#define CB_PRRR_TR0_MASK 0x03
@@ -2052,11 +2056,12 @@
#define CB_PAR_TF_SHIFT 1
#define CB_PAR_AFF_SHIFT 2
#define CB_PAR_PF_SHIFT 3
+#define CB_PAR_EF_SHIFT 4
#define CB_PAR_TLBMCF_SHIFT 5
#define CB_PAR_TLBLKF_SHIFT 6
#define CB_PAR_ATOT_SHIFT 31
-#define CB_PAR_PLVL_SHIFT 0
-#define CB_PAR_STAGE_SHIFT 3
+#define CB_PAR_PLVL_SHIFT 32
+#define CB_PAR_STAGE_SHIFT 35
/* Primary Region Remap Register: CB_PRRR */
#define CB_PRRR_TR0_SHIFT 0
diff --git a/arch/arm/mach-msm/include/mach/msm_ipc_router.h b/arch/arm/mach-msm/include/mach/msm_ipc_router.h
index a87380c..f5f1954 100644
--- a/arch/arm/mach-msm/include/mach/msm_ipc_router.h
+++ b/arch/arm/mach-msm/include/mach/msm_ipc_router.h
@@ -29,6 +29,7 @@
enum msm_ipc_router_event {
MSM_IPC_ROUTER_READ_CB = 0,
MSM_IPC_ROUTER_WRITE_DONE,
+ MSM_IPC_ROUTER_RESUME_TX,
};
struct comm_mode_info {
diff --git a/arch/arm/mach-msm/include/mach/msm_qmi_interface.h b/arch/arm/mach-msm/include/mach/msm_qmi_interface.h
index 11867f3..1641e8c 100644
--- a/arch/arm/mach-msm/include/mach/msm_qmi_interface.h
+++ b/arch/arm/mach-msm/include/mach/msm_qmi_interface.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -20,6 +20,7 @@
#include <linux/socket.h>
#include <linux/gfp.h>
#include <linux/qmi_encdec.h>
+#include <linux/workqueue.h>
#define QMI_COMMON_TLV_TYPE 0
@@ -45,6 +46,8 @@
void *ind_cb_priv;
int handle_reset;
wait_queue_head_t reset_waitq;
+ struct list_head pending_txn_list;
+ struct delayed_work resume_tx_work;
};
enum qmi_result_type_v01 {
@@ -153,7 +156,8 @@
void *resp, unsigned int resp_len,
void (*resp_cb)(struct qmi_handle *handle,
unsigned int msg_id, void *msg,
- void *resp_cb_data),
+ void *resp_cb_data,
+ int stat),
void *resp_cb_data);
/**
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h
index 2cfb5ac..661d496 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -53,6 +53,12 @@
/* Encoder/decoder configuration block */
#define USM_PARAM_ID_ENCDEC_ENC_CFG_BLK 0x0001230D
+/* Max number of static located ports (bytes) */
+#define USM_MAX_PORT_NUMBER 8
+
+/* Max number of static located transparent data (bytes) */
+#define USM_MAX_CFG_DATA_SIZE 100
+
/* Parameter structures used in USM_STREAM_CMD_SET_ENCDEC_PARAM command */
/* common declarations */
struct usm_cfg_common {
@@ -60,26 +66,7 @@
u16 bits_per_sample;
u32 sample_rate;
u32 dev_id;
- u32 data_map;
-} __packed;
-
-/* Max number of static located transparent data (bytes) */
-#define USM_MAX_CFG_DATA_SIZE 100
-struct usm_encode_cfg_blk {
- u32 frames_per_buf;
- u32 format_id;
- /* <cfg_size> = sizeof(usm_cfg_common)+|tarnsp_data| */
- u32 cfg_size;
- struct usm_cfg_common cfg_common;
- /* Transparent configuration data for specific encoder */
- u8 transp_data[USM_MAX_CFG_DATA_SIZE];
-} __packed;
-
-struct usm_stream_cmd_encdec_cfg_blk {
- struct apr_hdr hdr;
- u32 param_id;
- u32 param_size;
- struct usm_encode_cfg_blk enc_blk;
+ u8 data_map[USM_MAX_PORT_NUMBER];
} __packed;
struct us_encdec_cfg {
@@ -89,16 +76,6 @@
u8 *params;
} __packed;
-struct usm_stream_media_format_update {
- struct apr_hdr hdr;
- u32 format_id;
- /* <cfg_size> = sizeof(usm_cfg_common)+|tarnsp_data| */
- u32 cfg_size;
- struct usm_cfg_common cfg_common;
- /* Transparent configuration data for specific encoder */
- u8 transp_data[USM_MAX_CFG_DATA_SIZE];
-} __packed;
-
/* Start/stop US signal detection */
#define USM_SESSION_CMD_SIGNAL_DETECT_MODE 0x00012719
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_a.h b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_a.h
index 4008698..782f3ae 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_a.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_a.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -56,4 +56,43 @@
#define USM_DATA_EVENT_WRITE_DONE 0x00011274
+/* Max number of static located ports (bytes) */
+#define USM_MAX_PORT_NUMBER_A 4
+
+/* Parameter structures used in USM_STREAM_CMD_SET_ENCDEC_PARAM command */
+/* common declarations */
+struct usm_cfg_common_a {
+ u16 ch_cfg;
+ u16 bits_per_sample;
+ u32 sample_rate;
+ u32 dev_id;
+ u8 data_map[USM_MAX_PORT_NUMBER_A];
+} __packed;
+
+struct usm_stream_media_format_update {
+ struct apr_hdr hdr;
+ u32 format_id;
+ /* <cfg_size> = sizeof(usm_cfg_common)+|transp_data| */
+ u32 cfg_size;
+ struct usm_cfg_common_a cfg_common;
+ /* Transparent configuration data for specific encoder */
+ u8 transp_data[USM_MAX_CFG_DATA_SIZE];
+} __packed;
+
+struct usm_encode_cfg_blk {
+ u32 frames_per_buf;
+ u32 format_id;
+ /* <cfg_size> = sizeof(usm_cfg_common)+|transp_data| */
+ u32 cfg_size;
+ struct usm_cfg_common_a cfg_common;
+ /* Transparent configuration data for specific encoder */
+ u8 transp_data[USM_MAX_CFG_DATA_SIZE];
+} __packed;
+
+struct usm_stream_cmd_encdec_cfg_blk {
+ struct apr_hdr hdr;
+ u32 param_id;
+ u32 param_size;
+ struct usm_encode_cfg_blk enc_blk;
+} __packed;
#endif /* __APR_US_A_H__ */
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_b.h b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_b.h
index 11de6ef..26e8369 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_b.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/apr_us_b.h
@@ -67,4 +67,30 @@
#define USM_DATA_EVENT_WRITE_DONE 0x00012727
+struct usm_stream_media_format_update {
+ struct apr_hdr hdr;
+ u32 format_id;
+ /* <cfg_size> = sizeof(usm_cfg_common)+|transp_data| */
+ u32 cfg_size;
+ struct usm_cfg_common cfg_common;
+ /* Transparent configuration data for specific encoder */
+ u8 transp_data[USM_MAX_CFG_DATA_SIZE];
+} __packed;
+
+struct usm_encode_cfg_blk {
+ u32 frames_per_buf;
+ u32 format_id;
+ /* <cfg_size> = sizeof(usm_cfg_common)+|transp_data| */
+ u32 cfg_size;
+ struct usm_cfg_common cfg_common;
+ /* Transparent configuration data for specific encoder */
+ u8 transp_data[USM_MAX_CFG_DATA_SIZE];
+} __packed;
+
+struct usm_stream_cmd_encdec_cfg_blk {
+ struct apr_hdr hdr;
+ u32 param_id;
+ u32 param_size;
+ struct usm_encode_cfg_blk enc_blk;
+} __packed;
#endif /* __APR_US_B_H__ */
diff --git a/arch/arm/mach-msm/include/mach/usb_bam.h b/arch/arm/mach-msm/include/mach/usb_bam.h
index a3e993d..b2dd49c 100644
--- a/arch/arm/mach-msm/include/mach/usb_bam.h
+++ b/arch/arm/mach-msm/include/mach/usb_bam.h
@@ -198,14 +198,6 @@
struct usb_bam_connect_ipa_params *ipa_params);
/**
- * Wait for Consumer granted from Resource Manager.
- *
- * @ipa_params - in/out parameters
- *
- */
-void usb_bam_wait_for_cons_granted(
- struct usb_bam_connect_ipa_params *ipa_params);
-/**
* Register a wakeup callback from peer BAM.
*
* @idx - Connection index.
diff --git a/arch/arm/mach-msm/include/mach/usb_bridge.h b/arch/arm/mach-msm/include/mach/usb_bridge.h
index c62cf01..b3b7d71 100644
--- a/arch/arm/mach-msm/include/mach/usb_bridge.h
+++ b/arch/arm/mach-msm/include/mach/usb_bridge.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -17,10 +17,8 @@
#include <linux/netdevice.h>
#include <linux/usb.h>
-/* bridge device 0: DUN
- * bridge device 1 : Tethered RMNET
- */
-#define MAX_BRIDGE_DEVICES 2
+#define MAX_BRIDGE_DEVICES 4
+#define BRIDGE_NAME_MAX_LEN 20
struct bridge_ops {
int (*send_pkt)(void *, void *, size_t actual);
@@ -37,11 +35,12 @@
/* context of the gadget port using bridge driver */
void *ctx;
- /* bridge device array index mapped to the gadget port array index.
- * data bridge[ch_id] <-- bridge --> gadget port[ch_id]
- */
+ /*to maps bridge driver instance*/
unsigned int ch_id;
+ /*to match against bridge xport name to get bridge driver instance*/
+ char *name;
+
/* flow control bits */
unsigned long flags;
@@ -101,7 +100,10 @@
int data_bridge_unthrottle_rx(unsigned int);
/* defined in control bridge */
-int ctrl_bridge_probe(struct usb_interface *, struct usb_host_endpoint *, int);
+int ctrl_bridge_init(void);
+void ctrl_bridge_exit(void);
+int ctrl_bridge_probe(struct usb_interface *, struct usb_host_endpoint *,
+ char *, int);
void ctrl_bridge_disconnect(unsigned int);
int ctrl_bridge_resume(unsigned int);
int ctrl_bridge_suspend(unsigned int);
diff --git a/arch/arm/mach-msm/include/mach/usb_gadget_xport.h b/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
index a183f0e..634a4dc 100644
--- a/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
+++ b/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
@@ -89,8 +89,8 @@
USB_GADGET_RMNET,
};
-#define NUM_RMNET_HSIC_PORTS 1
-#define NUM_DUN_HSIC_PORTS 1
+#define NUM_RMNET_HSIC_PORTS 2
+#define NUM_DUN_HSIC_PORTS 2
#define NUM_PORTS (NUM_RMNET_HSIC_PORTS \
+ NUM_DUN_HSIC_PORTS)
@@ -102,9 +102,11 @@
int ghsic_ctrl_connect(void *, int);
void ghsic_ctrl_disconnect(void *, int);
int ghsic_ctrl_setup(unsigned int, enum gadget_type);
+void ghsic_ctrl_set_port_name(const char *, const char *);
int ghsic_data_connect(void *, int);
void ghsic_data_disconnect(void *, int);
int ghsic_data_setup(unsigned int, enum gadget_type);
+void ghsic_data_set_port_name(const char *, const char *);
int ghsuart_ctrl_connect(void *, int);
void ghsuart_ctrl_disconnect(void *, int);
diff --git a/arch/arm/mach-msm/ipc_router.c b/arch/arm/mach-msm/ipc_router.c
index 9ec8395..32589f1 100644
--- a/arch/arm/mach-msm/ipc_router.c
+++ b/arch/arm/mach-msm/ipc_router.c
@@ -735,8 +735,14 @@
&rport_ptr->resume_tx_port_list, list) {
local_port =
msm_ipc_router_lookup_local_port(rtx_port->port_id);
- if (local_port)
+ if (local_port && local_port->notify)
+ local_port->notify(MSM_IPC_ROUTER_RESUME_TX,
+ local_port->priv);
+ else if (local_port)
post_pkt_to_port(local_port, pkt, 1);
+ else
+ pr_err("%s: Local Port %d not Found",
+ __func__, rtx_port->port_id);
list_del(&rtx_port->list);
kfree(rtx_port);
}
@@ -2216,6 +2222,8 @@
}
ret = msm_ipc_router_send_to(src, out_skb_head, dest);
+ if (ret == -EAGAIN)
+ return ret;
if (ret < 0) {
pr_err("%s: msm_ipc_router_send_to failed - ret: %d\n",
__func__, ret);
diff --git a/arch/arm/mach-msm/krait-regulator.c b/arch/arm/mach-msm/krait-regulator.c
index 7c1b8d6..b30cba4 100644
--- a/arch/arm/mach-msm/krait-regulator.c
+++ b/arch/arm/mach-msm/krait-regulator.c
@@ -131,6 +131,15 @@
#define VREF_LDO_BIT_POS 0
#define VREF_LDO_MASK KRAIT_MASK(6, 0)
+#define PWR_GATE_SWITCH_MODE_POS 4
+#define PWR_GATE_SWITCH_MODE_MASK KRAIT_MASK(6, 4)
+
+#define PWR_GATE_SWITCH_MODE_PC 0
+#define PWR_GATE_SWITCH_MODE_LDO 1
+#define PWR_GATE_SWITCH_MODE_BHS 2
+#define PWR_GATE_SWITCH_MODE_DT 3
+#define PWR_GATE_SWITCH_MODE_RET 4
+
#define LDO_HDROOM_MIN 50000
#define LDO_HDROOM_MAX 250000
@@ -144,6 +153,10 @@
#define LDO_DELTA_MAX 100000
#define MSM_L2_SAW_PHYS 0xf9012000
+#define MSM_MDD_BASE_PHYS 0xf908a800
+
+#define KPSS_VERSION_2P0 0x20000000
+
/**
* struct pmic_gang_vreg -
* @name: the string used to represent the gang
@@ -580,35 +593,55 @@
if (kvreg->mode == HS_MODE)
return 0;
/* enable bhs */
- krait_masked_write(kvreg, APC_PWR_GATE_CTL, BHS_EN_MASK, BHS_EN_MASK);
- /* complete the above write before the delay */
- mb();
- /* wait for the bhs to settle */
- udelay(BHS_SETTLING_DELAY_US);
+ if (version > KPSS_VERSION_2P0) {
+ krait_masked_write(kvreg, APC_PWR_GATE_MODE,
+ PWR_GATE_SWITCH_MODE_MASK,
+ PWR_GATE_SWITCH_MODE_BHS << PWR_GATE_SWITCH_MODE_POS);
- /* Turn on BHS segments */
- krait_masked_write(kvreg, APC_PWR_GATE_CTL,
- BHS_SEG_EN_MASK, BHS_SEG_EN_DEFAULT << BHS_SEG_EN_BIT_POS);
+ /* complete the writes before the delay */
+ mb();
- /* complete the above write before the delay */
- mb();
+ /* wait for the bhs to settle */
+ udelay(BHS_SETTLING_DELAY_US);
+ } else {
+ /* enable bhs */
+ krait_masked_write(kvreg, APC_PWR_GATE_CTL,
+ BHS_EN_MASK, BHS_EN_MASK);
- /*
- * wait for the bhs to settle - note that
- * after the voltage has settled both BHS and LDO are supplying power
- * to the krait. This avoids glitches during switching
- */
- udelay(BHS_SETTLING_DELAY_US);
+ /* complete the above write before the delay */
+ mb();
- /*
- * enable ldo bypass - the krait is powered still by LDO since
- * LDO is enabled
- */
- krait_masked_write(kvreg, APC_PWR_GATE_CTL, LDO_BYP_MASK, LDO_BYP_MASK);
+ /* wait for the bhs to settle */
+ udelay(BHS_SETTLING_DELAY_US);
- /* disable ldo - only the BHS provides voltage to the cpu after this */
- krait_masked_write(kvreg, APC_PWR_GATE_CTL,
+ /* Turn on BHS segments */
+ krait_masked_write(kvreg, APC_PWR_GATE_CTL, BHS_SEG_EN_MASK,
+ BHS_SEG_EN_DEFAULT << BHS_SEG_EN_BIT_POS);
+
+ /* complete the above write before the delay */
+ mb();
+
+ /*
+ * wait for the bhs to settle - note that
+ * after the voltage has settled both BHS and LDO are supplying
+ * power to the krait. This avoids glitches during switching
+ */
+ udelay(BHS_SETTLING_DELAY_US);
+
+ /*
+ * enable ldo bypass - the krait is powered still by LDO since
+ * LDO is enabled
+ */
+ krait_masked_write(kvreg, APC_PWR_GATE_CTL,
+ LDO_BYP_MASK, LDO_BYP_MASK);
+
+ /*
+ * disable ldo - only the BHS provides voltage to
+ * the cpu after this
+ */
+ krait_masked_write(kvreg, APC_PWR_GATE_CTL,
LDO_PWR_DWN_MASK, LDO_PWR_DWN_MASK);
+ }
kvreg->mode = HS_MODE;
pr_debug("%s using BHS\n", kvreg->name);
@@ -629,27 +662,39 @@
switch_to_using_hs(kvreg);
set_krait_ldo_uv(kvreg, kvreg->uV - kvreg->ldo_delta_uV);
+ if (version > KPSS_VERSION_2P0) {
+ krait_masked_write(kvreg, APC_PWR_GATE_MODE,
+ PWR_GATE_SWITCH_MODE_MASK,
+ PWR_GATE_SWITCH_MODE_LDO << PWR_GATE_SWITCH_MODE_POS);
- /*
- * enable ldo - note that both LDO and BHS are are supplying voltage to
- * the cpu after this. This avoids glitches during switching from BHS
- * to LDO.
- */
- krait_masked_write(kvreg, APC_PWR_GATE_CTL, LDO_PWR_DWN_MASK, 0);
+ /* complete the writes before the delay */
+ mb();
- /* complete the writes before the delay */
- mb();
+ /* wait for the ldo to settle */
+ udelay(LDO_SETTLING_DELAY_US);
+ } else {
+ /*
+ * enable ldo - note that both LDO and BHS are are supplying
+ * voltage to the cpu after this. This avoids glitches during
+ * switching from BHS to LDO.
+ */
+ krait_masked_write(kvreg, APC_PWR_GATE_CTL,
+ LDO_PWR_DWN_MASK, 0);
- /* wait for the ldo to settle */
- udelay(LDO_SETTLING_DELAY_US);
+ /* complete the writes before the delay */
+ mb();
- /*
- * disable BHS and disable LDO bypass seperate from enabling
- * the LDO above.
- */
- krait_masked_write(kvreg, APC_PWR_GATE_CTL,
- BHS_EN_MASK | LDO_BYP_MASK, 0);
- krait_masked_write(kvreg, APC_PWR_GATE_CTL, BHS_SEG_EN_MASK, 0);
+ /* wait for the ldo to settle */
+ udelay(LDO_SETTLING_DELAY_US);
+
+ /*
+ * disable BHS and disable LDO bypass seperate from enabling
+ * the LDO above.
+ */
+ krait_masked_write(kvreg, APC_PWR_GATE_CTL,
+ BHS_EN_MASK | LDO_BYP_MASK, 0);
+ krait_masked_write(kvreg, APC_PWR_GATE_CTL, BHS_SEG_EN_MASK, 0);
+ }
kvreg->mode = LDO_MODE;
pr_debug("%s using LDO\n", kvreg->name);
@@ -993,22 +1038,34 @@
DEFINE_SIMPLE_ATTRIBUTE(retention_fops,
get_retention_dbg_uV, set_retention_dbg_uV, "%llu\n");
+static void kvreg_ldo_voltage_init(struct krait_power_vreg *kvreg)
+{
+ set_krait_retention_uv(kvreg, kvreg->retention_uV);
+ set_krait_ldo_uv(kvreg, kvreg->ldo_default_uV);
+}
+
#define CPU_PWR_CTL_ONLINE_MASK 0x80
static void kvreg_hw_init(struct krait_power_vreg *kvreg)
{
- int online;
- /*
- * bhs_cnt value sets the ramp-up time from power collapse,
- * initialize the ramp up time
- */
- set_krait_retention_uv(kvreg, kvreg->retention_uV);
- set_krait_ldo_uv(kvreg, kvreg->ldo_default_uV);
-
/* setup the bandgap that configures the reference to the LDO */
writel_relaxed(0x00000190, kvreg->mdd_base + MDD_CONFIG_CTL);
/* Enable MDD */
writel_relaxed(0x00000002, kvreg->mdd_base + MDD_MODE);
mb();
+
+ if (version > KPSS_VERSION_2P0) {
+ /* Configure hardware sequencer delays. */
+ writel_relaxed(0x30430600, kvreg->reg_base + APC_PWR_GATE_DLY);
+
+ /* Enable the hardware sequencer in BHS mode. */
+ writel_relaxed(0x00000021, kvreg->reg_base + APC_PWR_GATE_MODE);
+ }
+}
+
+static void online_at_probe(struct krait_power_vreg *kvreg)
+{
+ int online;
+
online = CPU_PWR_CTL_ONLINE_MASK
& readl_relaxed(kvreg->reg_base + CPU_PWR_CTL);
kvreg->online_at_probe
@@ -1017,11 +1074,15 @@
static void glb_init(void __iomem *apcs_gcc_base)
{
- /* configure bi-modal switch */
- writel_relaxed(0x0008736E, apcs_gcc_base + PWR_GATE_CONFIG);
/* read kpss version */
version = readl_relaxed(apcs_gcc_base + VERSION);
pr_debug("version= 0x%x\n", version);
+
+ /* configure bi-modal switch */
+ if (version > KPSS_VERSION_2P0)
+ writel_relaxed(0x0308736E, apcs_gcc_base + PWR_GATE_CONFIG);
+ else
+ writel_relaxed(0x0008736E, apcs_gcc_base + PWR_GATE_CONFIG);
}
static int __devinit krait_power_probe(struct platform_device *pdev)
@@ -1179,6 +1240,14 @@
list_add_tail(&kvreg->link, &the_gang->krait_power_vregs);
mutex_unlock(&the_gang->krait_power_vregs_lock);
+ online_at_probe(kvreg);
+ kvreg_ldo_voltage_init(kvreg);
+
+ if (kvreg->cpu_num == 0)
+ kvreg_hw_init(kvreg);
+
+ per_cpu(krait_vregs, cpu_num) = kvreg;
+
kvreg->rdev = regulator_register(&kvreg->desc, &pdev->dev, init_data,
kvreg, pdev->dev.of_node);
if (IS_ERR(kvreg->rdev)) {
@@ -1187,8 +1256,6 @@
goto out;
}
- kvreg_hw_init(kvreg);
- per_cpu(krait_vregs, cpu_num) = kvreg;
dev_dbg(&pdev->dev, "id=%d, name=%s\n", pdev->id, kvreg->name);
return 0;
@@ -1426,10 +1493,29 @@
}
module_exit(krait_power_exit);
-void secondary_cpu_hs_init(void *base_ptr)
+#define GCC_BASE 0xF9011000
+
+/**
+ * secondary_cpu_hs_init - Initialize BHS and LDO registers
+ * for nonboot cpu
+ *
+ * @base_ptr: address pointer to APC registers of a cpu
+ * @cpu: the cpu being brought out of reset
+ *
+ * seconday_cpu_hs_init() is called when a secondary cpu
+ * is being brought online for the first time. It is not
+ * called for boot cpu. It initializes power related
+ * registers and makes the core run from BHS.
+ * It also ends up turning on MDD which is required when the
+ * core switches to LDO mode
+ */
+void secondary_cpu_hs_init(void *base_ptr, int cpu)
{
uint32_t reg_val;
void *l2_saw_base;
+ void *gcc_base_ptr;
+ void *mdd_base;
+ struct krait_power_vreg *kvreg;
/* Turn on the BHS, turn off LDO Bypass and power down LDO */
reg_val = BHS_CNT_DEFAULT << BHS_CNT_BIT_POS
@@ -1438,14 +1524,23 @@
| BHS_EN_MASK;
writel_relaxed(reg_val, base_ptr + APC_PWR_GATE_CTL);
- /* complete the above write before the delay */
- mb();
- /* wait for the bhs to settle */
- udelay(BHS_SETTLING_DELAY_US);
+ if (version == 0) {
+ gcc_base_ptr = ioremap_nocache(GCC_BASE, SZ_4K);
+ version = readl_relaxed(gcc_base_ptr + VERSION);
+ iounmap(gcc_base_ptr);
+ }
- /* Turn on BHS segments */
- reg_val |= BHS_SEG_EN_DEFAULT << BHS_SEG_EN_BIT_POS;
- writel_relaxed(reg_val, base_ptr + APC_PWR_GATE_CTL);
+ /* Turn on the BHS segments only for version < 2 */
+ if (version <= KPSS_VERSION_2P0) {
+ /* complete the above write before the delay */
+ mb();
+ /* wait for the bhs to settle */
+ udelay(BHS_SETTLING_DELAY_US);
+
+ /* Turn on BHS segments */
+ reg_val |= BHS_SEG_EN_DEFAULT << BHS_SEG_EN_BIT_POS;
+ writel_relaxed(reg_val, base_ptr + APC_PWR_GATE_CTL);
+ }
/* complete the above write before the delay */
mb();
@@ -1456,23 +1551,48 @@
reg_val |= LDO_BYP_MASK;
writel_relaxed(reg_val, base_ptr + APC_PWR_GATE_CTL);
- if (the_gang && the_gang->manage_phases)
- return;
+ kvreg = per_cpu(krait_vregs, cpu);
+ if (kvreg != NULL) {
+ kvreg_hw_init(kvreg);
+ } else {
+ /*
+ * This nonboot cpu has not been probed yet. This cpu was
+ * brought out of reset as a part of maxcpus >= 2. Initialize
+ * its MDD and APC_PWR_GATE_MODE register here
+ */
+ mdd_base = ioremap_nocache(MSM_MDD_BASE_PHYS + cpu * 0x10000,
+ SZ_4K);
+ /* setup the bandgap that configures the reference to the LDO */
+ writel_relaxed(0x00000190, mdd_base + MDD_CONFIG_CTL);
+ /* Enable MDD */
+ writel_relaxed(0x00000002, mdd_base + MDD_MODE);
+ mb();
+ iounmap(mdd_base);
- /*
- * If the driver has not yet started to manage phases then enable
- * max phases.
- */
- l2_saw_base = ioremap_nocache(MSM_L2_SAW_PHYS, SZ_4K);
- if (!l2_saw_base) {
- __WARN();
- return;
+ if (version > KPSS_VERSION_2P0) {
+ writel_relaxed(0x30430600, base_ptr + APC_PWR_GATE_DLY);
+ writel_relaxed(0x00000021,
+ base_ptr + APC_PWR_GATE_MODE);
+ }
+ mb();
}
- writel_relaxed(0x10003, l2_saw_base + 0x1c);
- mb();
- udelay(PHASE_SETTLING_TIME_US);
- iounmap(l2_saw_base);
+ if (!the_gang || !the_gang->manage_phases) {
+ /*
+ * If the driver has not yet started to manage phases then
+ * enable max phases.
+ */
+ l2_saw_base = ioremap_nocache(MSM_L2_SAW_PHYS, SZ_4K);
+ if (l2_saw_base) {
+ writel_relaxed(0x10003, l2_saw_base + 0x1c);
+ mb();
+ udelay(PHASE_SETTLING_TIME_US);
+
+ iounmap(l2_saw_base);
+ } else {
+ __WARN();
+ }
+ }
}
MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/msm_dcvs_scm.c b/arch/arm/mach-msm/msm_dcvs_scm.c
index 78d62ac..e03ac64 100644
--- a/arch/arm/mach-msm/msm_dcvs_scm.c
+++ b/arch/arm/mach-msm/msm_dcvs_scm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -17,6 +17,7 @@
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/memory_alloc.h>
+#include <asm/cacheflush.h>
#include <mach/memory.h>
#include <mach/scm.h>
#include <mach/msm_dcvs_scm.h>
@@ -83,6 +84,12 @@
}
EXPORT_SYMBOL(msm_dcvs_scm_init);
+static void __msm_dcvs_flush_cache(void *v, size_t size)
+{
+ __cpuc_flush_dcache_area(v, size);
+ outer_flush_range(virt_to_phys(v), virt_to_phys(v) + size);
+}
+
int msm_dcvs_scm_register_core(uint32_t core_id,
struct msm_dcvs_core_param *param)
{
@@ -99,6 +106,8 @@
reg_data.core_id = core_id;
reg_data.core_param_phy = virt_to_phys(p);
+ __msm_dcvs_flush_cache(p, sizeof(struct msm_dcvs_core_param));
+
ret = scm_call(SCM_SVC_DCVS, DCVS_CMD_REGISTER_CORE,
®_data, sizeof(reg_data), NULL, 0);
@@ -125,6 +134,8 @@
algo.core_id = core_id;
algo.algo_phy = virt_to_phys(p);
+ __msm_dcvs_flush_cache(p, sizeof(struct msm_algo_param));
+
ret = scm_call(SCM_SVC_DCVS, DCVS_CMD_SET_ALGO_PARAM,
&algo, sizeof(algo), NULL, 0);
@@ -150,6 +161,8 @@
algo.core_id = 0;
algo.algo_phy = virt_to_phys(p);
+ __msm_dcvs_flush_cache(p, sizeof(struct msm_algo_param));
+
ret = scm_call(SCM_SVC_DCVS, DCVS_CMD_SET_ALGO_PARAM,
&algo, sizeof(algo), NULL, 0);
@@ -197,6 +210,12 @@
sizeof(struct msm_dcvs_freq_entry)*pwr_param->num_freq);
memcpy(coefft, coeffs, sizeof(struct msm_dcvs_energy_curve_coeffs));
+ __msm_dcvs_flush_cache(pwrt, sizeof(struct msm_dcvs_power_params));
+ __msm_dcvs_flush_cache(freqt,
+ sizeof(struct msm_dcvs_freq_entry) * pwr_param->num_freq);
+ __msm_dcvs_flush_cache(coefft,
+ sizeof(struct msm_dcvs_energy_curve_coeffs));
+
pwr.core_id = core_id;
pwr.pwr_param_phy = virt_to_phys(pwrt);
pwr.freq_phy = virt_to_phys(freqt);
diff --git a/arch/arm/mach-msm/msm_qmi_interface.c b/arch/arm/mach-msm/msm_qmi_interface.c
index 9b3e500..8d96bd8 100644
--- a/arch/arm/mach-msm/msm_qmi_interface.c
+++ b/arch/arm/mach-msm/msm_qmi_interface.c
@@ -25,6 +25,8 @@
#include <linux/socket.h>
#include <linux/gfp.h>
#include <linux/qmi_encdec.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
#include <mach/msm_qmi_interface.h>
#include <mach/msm_ipc_router.h>
@@ -33,6 +35,8 @@
static LIST_HEAD(svc_event_nb_list);
static DEFINE_MUTEX(svc_event_nb_list_lock);
+static DEFINE_MUTEX(msm_qmi_init_lock);
+static struct workqueue_struct *msm_qmi_pending_workqueue;
struct elem_info qmi_response_type_v01_ei[] = {
{
@@ -87,12 +91,98 @@
spin_unlock_irqrestore(&handle->notify_lock, flags);
break;
+ case MSM_IPC_ROUTER_RESUME_TX:
+ queue_delayed_work(msm_qmi_pending_workqueue,
+ &handle->resume_tx_work,
+ msecs_to_jiffies(0));
+ break;
default:
break;
}
mutex_unlock(&handle->handle_lock);
}
+/**
+ * init_msm_qmi() - Init function for kernel space QMI
+ *
+ * This function is implemented to initialize the QMI resources that are common
+ * across kernel space QMI users. As it is not necessary for this init function
+ * to be module_init function it is called when the first handle of kernel space
+ * QMI gets created.
+ */
+static void init_msm_qmi(void)
+{
+ static bool msm_qmi_inited;
+
+ if (likely(msm_qmi_inited))
+ return;
+
+ mutex_lock(&msm_qmi_init_lock);
+ if (likely(msm_qmi_inited && msm_qmi_pending_workqueue)) {
+ mutex_unlock(&msm_qmi_init_lock);
+ return;
+ }
+ msm_qmi_inited = 1;
+ msm_qmi_pending_workqueue =
+ create_singlethread_workqueue("msm_qmi_rtx_q");
+ mutex_unlock(&msm_qmi_init_lock);
+}
+
+/**
+ * handle_resume_tx() - Handle the Resume_Tx event
+ * @work : Pointer to the work strcuture.
+ *
+ * This function handles the resume_tx event for any QMI client that
+ * exists in the kernel space. This function parses the pending_txn_list of
+ * the handle and attempts a send for each transaction in that list.
+ */
+static void handle_resume_tx(struct work_struct *work)
+{
+ struct delayed_work *rtx_work = to_delayed_work(work);
+ struct qmi_handle *handle =
+ container_of(rtx_work, struct qmi_handle, resume_tx_work);
+ struct qmi_txn *pend_txn, *temp_txn;
+ int ret;
+ uint16_t msg_id;
+
+ mutex_lock(&handle->handle_lock);
+ list_for_each_entry_safe(pend_txn, temp_txn,
+ &handle->pending_txn_list, list) {
+ ret = msm_ipc_router_send_msg(
+ (struct msm_ipc_port *)handle->src_port,
+ (struct msm_ipc_addr *)handle->dest_info,
+ pend_txn->enc_data, pend_txn->enc_data_len);
+
+ if (ret == -EAGAIN) {
+ mutex_unlock(&handle->handle_lock);
+ return;
+ }
+ msg_id = ((struct qmi_header *)pend_txn->enc_data)->msg_id;
+ kfree(pend_txn->enc_data);
+ if (ret < 0) {
+ if (pend_txn->type == QMI_ASYNC_TXN) {
+ pend_txn->resp_cb(pend_txn->handle,
+ msg_id, pend_txn->resp,
+ pend_txn->resp_cb_data,
+ ret);
+ list_del(&pend_txn->list);
+ kfree(pend_txn);
+ } else if (pend_txn->type == QMI_SYNC_TXN) {
+ pend_txn->send_stat = ret;
+ wake_up(&pend_txn->wait_q);
+ }
+ pr_err("%s: Sending transaction %d from port %d failed",
+ __func__, pend_txn->txn_id,
+ ((struct msm_ipc_port *)handle->src_port)->
+ this_port.port_id);
+ } else {
+ list_del(&pend_txn->list);
+ list_add_tail(&pend_txn->list, &handle->txn_list);
+ }
+ }
+ mutex_unlock(&handle->handle_lock);
+}
+
struct qmi_handle *qmi_handle_create(
void (*notify)(struct qmi_handle *handle,
enum qmi_event_type event, void *notify_priv),
@@ -118,20 +208,39 @@
temp_handle->src_port = port_ptr;
temp_handle->next_txn_id = 1;
INIT_LIST_HEAD(&temp_handle->txn_list);
+ INIT_LIST_HEAD(&temp_handle->pending_txn_list);
mutex_init(&temp_handle->handle_lock);
spin_lock_init(&temp_handle->notify_lock);
temp_handle->notify = notify;
temp_handle->notify_priv = notify_priv;
temp_handle->handle_reset = 0;
init_waitqueue_head(&temp_handle->reset_waitq);
+ INIT_DELAYED_WORK(&temp_handle->resume_tx_work, handle_resume_tx);
+ init_msm_qmi();
return temp_handle;
}
EXPORT_SYMBOL(qmi_handle_create);
static void clean_txn_info(struct qmi_handle *handle)
{
- struct qmi_txn *txn_handle, *temp_txn_handle;
+ struct qmi_txn *txn_handle, *temp_txn_handle, *pend_txn;
+ list_for_each_entry_safe(pend_txn, temp_txn_handle,
+ &handle->pending_txn_list, list) {
+ if (pend_txn->type == QMI_ASYNC_TXN) {
+ list_del(&pend_txn->list);
+ pend_txn->resp_cb(pend_txn->handle,
+ ((struct qmi_header *)
+ pend_txn->enc_data)->msg_id,
+ pend_txn->resp, pend_txn->resp_cb_data,
+ -ENETRESET);
+ kfree(pend_txn->enc_data);
+ kfree(pend_txn);
+ } else if (pend_txn->type == QMI_SYNC_TXN) {
+ kfree(pend_txn->enc_data);
+ wake_up(&pend_txn->wait_q);
+ }
+ }
list_for_each_entry_safe(txn_handle, temp_txn_handle,
&handle->txn_list, list) {
if (txn_handle->type == QMI_ASYNC_TXN) {
@@ -154,7 +263,7 @@
handle->handle_reset = 1;
clean_txn_info(handle);
mutex_unlock(&handle->handle_lock);
-
+ flush_delayed_work(&handle->resume_tx_work);
rc = wait_event_interruptible(handle->reset_waitq,
list_empty(&handle->txn_list));
@@ -194,7 +303,7 @@
struct msg_desc *resp_desc, void *resp, unsigned int resp_len,
void (*resp_cb)(struct qmi_handle *handle,
unsigned int msg_id, void *msg,
- void *resp_cb_data),
+ void *resp_cb_data, int stat),
void *resp_cb_data)
{
struct qmi_txn *txn_handle;
@@ -230,6 +339,8 @@
txn_handle->resp_received = 0;
txn_handle->resp_cb = resp_cb;
txn_handle->resp_cb_data = resp_cb_data;
+ txn_handle->enc_data = NULL;
+ txn_handle->enc_data_len = 0;
/* Encode the request msg */
encoded_req_len = req_desc->max_msg_len + QMI_HEADER_SIZE;
@@ -256,12 +367,35 @@
txn_handle->txn_id, req_desc->msg_id,
encoded_req_len);
encoded_req_len += QMI_HEADER_SIZE;
- list_add_tail(&txn_handle->list, &handle->txn_list);
+ /*
+ * Check if this port has transactions queued to its pending list
+ * and if there are any pending transactions then add the current
+ * transaction to the pending list rather than sending it. This avoids
+ * out-of-order message transfers.
+ */
+ if (!list_empty(&handle->pending_txn_list)) {
+ rc = -EAGAIN;
+ goto append_pend_txn;
+ }
+
+ list_add_tail(&txn_handle->list, &handle->txn_list);
/* Send the request */
rc = msm_ipc_router_send_msg((struct msm_ipc_port *)(handle->src_port),
(struct msm_ipc_addr *)handle->dest_info,
encoded_req, encoded_req_len);
+append_pend_txn:
+ if (rc == -EAGAIN) {
+ txn_handle->enc_data = encoded_req;
+ txn_handle->enc_data_len = encoded_req_len;
+ if (list_empty(&handle->pending_txn_list))
+ list_del(&txn_handle->list);
+ list_add_tail(&txn_handle->list, &handle->pending_txn_list);
+ if (ret_txn_handle)
+ *ret_txn_handle = txn_handle;
+ mutex_unlock(&handle->handle_lock);
+ return 0;
+ }
if (rc < 0) {
pr_err("%s: send_msg failed %d\n", __func__, rc);
goto encode_and_send_req_err3;
@@ -306,13 +440,15 @@
/* Wait for the response */
if (!timeout_ms) {
rc = wait_event_interruptible(txn_handle->wait_q,
- (txn_handle->resp_received ||
- handle->handle_reset));
+ (txn_handle->resp_received ||
+ handle->handle_reset ||
+ (txn_handle->send_stat < 0)));
} else {
rc = wait_event_interruptible_timeout(txn_handle->wait_q,
- (txn_handle->resp_received ||
- handle->handle_reset),
- msecs_to_jiffies(timeout_ms));
+ (txn_handle->resp_received ||
+ handle->handle_reset ||
+ (txn_handle->send_stat < 0)),
+ msecs_to_jiffies(timeout_ms));
if (rc == 0)
rc = -ETIMEDOUT;
}
@@ -324,6 +460,8 @@
rc = -ENETRESET;
if (rc >= 0)
rc = -EFAULT;
+ if (txn_handle->send_stat < 0)
+ rc = txn_handle->send_stat;
goto send_req_wait_err;
}
rc = 0;
@@ -344,7 +482,7 @@
void *resp, unsigned int resp_len,
void (*resp_cb)(struct qmi_handle *handle,
unsigned int msg_id, void *msg,
- void *resp_cb_data),
+ void *resp_cb_data, int stat),
void *resp_cb_data)
{
return qmi_encode_and_send_req(NULL, handle, QMI_ASYNC_TXN,
@@ -407,7 +545,7 @@
if (txn_handle->resp_cb)
txn_handle->resp_cb(txn_handle->handle, msg_id,
txn_handle->resp,
- txn_handle->resp_cb_data);
+ txn_handle->resp_cb_data, 0);
list_del(&txn_handle->list);
kfree(txn_handle);
rc = 0;
diff --git a/arch/arm/mach-msm/msm_qmi_interface_priv.h b/arch/arm/mach-msm/msm_qmi_interface_priv.h
index 58f1ce3..8eba0db 100644
--- a/arch/arm/mach-msm/msm_qmi_interface_priv.h
+++ b/arch/arm/mach-msm/msm_qmi_interface_priv.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -34,12 +34,15 @@
uint16_t txn_id;
enum txn_type type;
struct qmi_handle *handle;
+ void *enc_data;
+ unsigned int enc_data_len;
struct msg_desc *resp_desc;
void *resp;
unsigned int resp_len;
int resp_received;
+ int send_stat;
void (*resp_cb)(struct qmi_handle *handle, unsigned int msg_id,
- void *msg, void *resp_cb_data);
+ void *msg, void *resp_cb_data, int stat);
void *resp_cb_data;
wait_queue_head_t wait_q;
};
diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c
index f4ca4e3..796fba1 100644
--- a/arch/arm/mach-msm/platsmp.c
+++ b/arch/arm/mach-msm/platsmp.c
@@ -147,10 +147,11 @@
unsigned int cpu)
{
void *base_ptr = ioremap_nocache(base + (cpu * 0x10000), SZ_4K);
+
if (!base_ptr)
return -ENODEV;
- secondary_cpu_hs_init(base_ptr);
+ secondary_cpu_hs_init(base_ptr, cpu);
writel_relaxed(0x021, base_ptr+0x04);
mb();
diff --git a/arch/arm/mach-msm/pm-8x60.c b/arch/arm/mach-msm/pm-8x60.c
index 6799cbf..146dc0b 100644
--- a/arch/arm/mach-msm/pm-8x60.c
+++ b/arch/arm/mach-msm/pm-8x60.c
@@ -967,7 +967,7 @@
if (acc_sts & msm_pm_slp_sts[cpu].mask)
return 0;
udelay(100);
- WARN(++timeout == 10, "CPU%u didn't collape within 1ms\n",
+ WARN(++timeout == 20, "CPU%u didn't collape within 2ms\n",
cpu);
}
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c
index d37a325..1ea213a 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/usf.c
@@ -456,7 +456,7 @@
__func__, config->buf_num, config->stream_format,
config->port_cnt, config->params_data_size);
- pr_debug("%s: id[0]=%d, id[1]=%d, id[2]=%d, id[3]=%d, id[4]=%d\n",
+ pr_debug("%s: id[0]=%d, id[1]=%d, id[2]=%d, id[3]=%d, id[4]=%d,\n",
__func__,
config->port_id[0],
config->port_id[1],
@@ -464,6 +464,11 @@
config->port_id[3],
config->port_id[4]);
+ pr_debug("id[5]=%d, id[6]=%d, id[7]=%d\n",
+ config->port_id[5],
+ config->port_id[6],
+ config->port_id[7]);
+
/* q6usm allocation & configuration */
usf_xx->buffer_size = config->buf_size;
usf_xx->buffer_count = config->buf_num;
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/version_a/q6usm_a.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_a/q6usm_a.c
index 5d30eb1..80b1aaa 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/version_a/q6usm_a.c
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_a/q6usm_a.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -652,7 +652,7 @@
sizeof(struct usm_stream_cmd_encdec_cfg_blk);
uint32_t round_params_size = 0;
uint8_t is_allocated = 0;
-
+ size_t min_common_size;
if ((usc == NULL) || (us_cfg == NULL)) {
pr_err("%s: wrong input", __func__);
@@ -692,12 +692,13 @@
round_params_size;
enc_cfg->enc_blk.frames_per_buf = 1;
enc_cfg->enc_blk.format_id = int_format;
- enc_cfg->enc_blk.cfg_size = sizeof(struct usm_cfg_common)+
+ min_common_size = min(sizeof(struct usm_cfg_common),
+ sizeof(struct usm_cfg_common_a));
+ enc_cfg->enc_blk.cfg_size = min_common_size +
USM_MAX_CFG_DATA_SIZE +
round_params_size;
memcpy(&(enc_cfg->enc_blk.cfg_common), &(us_cfg->cfg_common),
- sizeof(struct usm_cfg_common));
-
+ min_common_size);
/* Transparent data copy */
memcpy(enc_cfg->enc_blk.transp_data, us_cfg->params,
us_cfg->params_size);
@@ -716,11 +717,15 @@
enc_cfg->enc_blk.transp_data[6],
enc_cfg->enc_blk.transp_data[7]
);
- pr_debug("%s: srate:%d, ch=%d, bps= %d; dmap:0x%x; dev_id=0x%x\n",
+ pr_debug("%s: srate:%d, ch=%d, bps= %d; dmap:[0x%x,0x%x,0x%x,0x%x];\n",
__func__, enc_cfg->enc_blk.cfg_common.sample_rate,
enc_cfg->enc_blk.cfg_common.ch_cfg,
enc_cfg->enc_blk.cfg_common.bits_per_sample,
- enc_cfg->enc_blk.cfg_common.data_map,
+ enc_cfg->enc_blk.cfg_common.data_map[0],
+ enc_cfg->enc_blk.cfg_common.data_map[1],
+ enc_cfg->enc_blk.cfg_common.data_map[2],
+ enc_cfg->enc_blk.cfg_common.data_map[3]);
+ pr_debug("dev_id=0x%x\n",
enc_cfg->enc_blk.cfg_common.dev_id);
rc = apr_send_pkt(usc->apr, (uint32_t *) enc_cfg);
@@ -757,7 +762,7 @@
uint32_t total_cfg_size = sizeof(struct usm_stream_media_format_update);
uint32_t round_params_size = 0;
uint8_t is_allocated = 0;
-
+ size_t min_common_size;
if ((usc == NULL) || (us_cfg == NULL)) {
pr_err("%s: wrong input", __func__);
@@ -794,11 +799,13 @@
dec_cfg->hdr.opcode = USM_DATA_CMD_MEDIA_FORMAT_UPDATE;
dec_cfg->format_id = int_format;
- dec_cfg->cfg_size = sizeof(struct usm_cfg_common) +
+ min_common_size = min(sizeof(struct usm_cfg_common),
+ sizeof(struct usm_cfg_common_a));
+ dec_cfg->cfg_size = min_common_size +
USM_MAX_CFG_DATA_SIZE +
round_params_size;
memcpy(&(dec_cfg->cfg_common), &(us_cfg->cfg_common),
- sizeof(struct usm_cfg_common));
+ min_common_size);
/* Transparent data copy */
memcpy(dec_cfg->transp_data, us_cfg->params, us_cfg->params_size);
pr_debug("%s: cfg_size[%d], params_size[%d]; parambytes[%d,%d,%d,%d]\n",
diff --git a/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c
index a3af3e78..fe7c8c2 100644
--- a/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c
+++ b/arch/arm/mach-msm/qdsp6v2/ultrasound/version_b/q6usm_b.c
@@ -620,13 +620,13 @@
uint32_t int_format = INVALID_FORMAT;
switch (ext_format) {
case FORMAT_USPS_EPOS:
- int_format = US_POINT_EPOS_FORMAT;
+ int_format = US_POINT_EPOS_FORMAT_V2;
break;
case FORMAT_USRAW:
- int_format = US_RAW_FORMAT;
+ int_format = US_RAW_FORMAT_V2;
break;
case FORMAT_USPROX:
- int_format = US_PROX_FORMAT;
+ int_format = US_PROX_FORMAT_V2;
break;
default:
pr_err("%s: Invalid format[%d]\n", __func__, ext_format);
@@ -757,11 +757,19 @@
enc_cfg->enc_blk.transp_data[6],
enc_cfg->enc_blk.transp_data[7]
);
- pr_debug("%s: srate:%d, ch=%d, bps= %d; dmap:0x%x; dev_id=0x%x\n",
+ pr_debug("%s: srate:%d, ch=%d, bps= %d;\n",
__func__, enc_cfg->enc_blk.cfg_common.sample_rate,
enc_cfg->enc_blk.cfg_common.ch_cfg,
- enc_cfg->enc_blk.cfg_common.bits_per_sample,
- enc_cfg->enc_blk.cfg_common.data_map,
+ enc_cfg->enc_blk.cfg_common.bits_per_sample);
+ pr_debug("dmap:[0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x]; dev_id=0x%x\n",
+ enc_cfg->enc_blk.cfg_common.data_map[0],
+ enc_cfg->enc_blk.cfg_common.data_map[1],
+ enc_cfg->enc_blk.cfg_common.data_map[2],
+ enc_cfg->enc_blk.cfg_common.data_map[3],
+ enc_cfg->enc_blk.cfg_common.data_map[4],
+ enc_cfg->enc_blk.cfg_common.data_map[5],
+ enc_cfg->enc_blk.cfg_common.data_map[6],
+ enc_cfg->enc_blk.cfg_common.data_map[7],
enc_cfg->enc_blk.cfg_common.dev_id);
rc = apr_send_pkt(usc->apr, (uint32_t *) enc_cfg);
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index cb4528f..7a0d23b 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -22,6 +22,7 @@
obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
obj-$(CONFIG_REGMAP) += regmap/
obj-$(CONFIG_SOC_BUS) += soc.o
+obj-$(CONFIG_PINCTRL) += pinctrl.o
obj-$(CONFIG_SYNC) += sync.o
obj-$(CONFIG_SW_SYNC) += sw_sync.o
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 1b1cbb5..b3a7d6e 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -24,6 +24,7 @@
#include <linux/wait.h>
#include <linux/async.h>
#include <linux/pm_runtime.h>
+#include <linux/pinctrl/devinfo.h>
#include "base.h"
#include "power/power.h"
@@ -257,6 +258,12 @@
WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv;
+
+ /* If using pinctrl, bind pins now before probing */
+ ret = pinctrl_bind_pins(dev);
+ if (ret)
+ goto probe_failed;
+
if (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
diff --git a/drivers/base/pinctrl.c b/drivers/base/pinctrl.c
new file mode 100644
index 0000000..67a274e
--- /dev/null
+++ b/drivers/base/pinctrl.c
@@ -0,0 +1,69 @@
+/*
+ * Driver core interface to the pinctrl subsystem.
+ *
+ * Copyright (C) 2012 ST-Ericsson SA
+ * Written on behalf of Linaro for ST-Ericsson
+ * Based on bits of regulator core, gpio core and clk core
+ *
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/device.h>
+#include <linux/pinctrl/devinfo.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/slab.h>
+
+/**
+ * pinctrl_bind_pins() - called by the device core before probe
+ * @dev: the device that is just about to probe
+ */
+int pinctrl_bind_pins(struct device *dev)
+{
+ int ret;
+
+ dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);
+ if (!dev->pins)
+ return -ENOMEM;
+
+ dev->pins->p = devm_pinctrl_get(dev);
+ if (IS_ERR(dev->pins->p)) {
+ dev_dbg(dev, "no pinctrl handle\n");
+ ret = PTR_ERR(dev->pins->p);
+ goto cleanup_alloc;
+ }
+
+ dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,
+ PINCTRL_STATE_DEFAULT);
+ if (IS_ERR(dev->pins->default_state)) {
+ dev_dbg(dev, "no default pinctrl state\n");
+ ret = 0;
+ goto cleanup_get;
+ }
+
+ ret = pinctrl_select_state(dev->pins->p, dev->pins->default_state);
+ if (ret) {
+ dev_dbg(dev, "failed to activate default pinctrl state\n");
+ goto cleanup_get;
+ }
+
+ return 0;
+
+ /*
+ * If no pinctrl handle or default state was found for this device,
+ * let's explicitly free the pin container in the device, there is
+ * no point in keeping it around.
+ */
+cleanup_get:
+ devm_pinctrl_put(dev->pins->p);
+cleanup_alloc:
+ devm_kfree(dev, dev->pins);
+ dev->pins = NULL;
+
+ /* Only return deferrals */
+ if (ret != -EPROBE_DEFER)
+ ret = 0;
+
+ return ret;
+}
diff --git a/drivers/coresight/coresight-etm.c b/drivers/coresight/coresight-etm.c
index 5a5c0cf..de86622 100644
--- a/drivers/coresight/coresight-etm.c
+++ b/drivers/coresight/coresight-etm.c
@@ -2154,14 +2154,20 @@
drvdata->cpu = count++;
- get_online_cpus();
etmdrvdata[drvdata->cpu] = drvdata;
+ /*
+ * This is safe wrt CPU_UP_PREPARE and CPU_STARTING hotplug callbacks
+ * on the secondary cores that may enable the clock and perform
+ * etm_os_unlock since they occur before the cpu online mask is updated
+ * for the cpu which is checked by this smp call.
+ */
if (!smp_call_function_single(drvdata->cpu, etm_os_unlock, drvdata, 1))
drvdata->os_unlock = true;
+
/*
- * Use CPU0 to populate read-only configuration data for ETM0. For
- * other ETMs copy it over from ETM0.
+ * OS unlock must have happened on cpu0 so use it to populate read-only
+ * configuration data for ETM0. For other ETMs copy it over from ETM0.
*/
if (drvdata->cpu == 0) {
register_hotcpu_notifier(&etm_cpu_notifier);
@@ -2172,8 +2178,6 @@
etm_copy_arch_data(drvdata);
}
- put_online_cpus();
-
if (etm_arch_supported(drvdata->arch) == false) {
ret = -EINVAL;
goto err1;
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index fda64e5..2fd8bef 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -22,6 +22,7 @@
#include <linux/hrtimer.h>
#include <linux/tick.h>
#include <linux/ktime.h>
+#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/input.h>
#include <linux/workqueue.h>
@@ -103,6 +104,11 @@
* when user is changing the governor or limits.
*/
struct mutex timer_mutex;
+
+ struct task_struct *sync_thread;
+ wait_queue_head_t sync_wq;
+ atomic_t src_sync_cpu;
+ atomic_t sync_enabled;
};
static DEFINE_PER_CPU(struct cpu_dbs_info_s, od_cpu_dbs_info);
@@ -125,14 +131,6 @@
static DEFINE_PER_CPU(struct dbs_work_struct, dbs_refresh_work);
-struct dbs_sync_work_struct {
- struct work_struct work;
- unsigned int src_cpu;
- unsigned int targ_cpu;
-};
-
-static DEFINE_PER_CPU(struct dbs_sync_work_struct, dbs_sync_work);
-
static struct dbs_tuners {
unsigned int sampling_rate;
unsigned int up_threshold;
@@ -1039,12 +1037,11 @@
static int dbs_migration_notify(struct notifier_block *nb,
unsigned long target_cpu, void *arg)
{
- struct dbs_sync_work_struct *sync_work =
- &per_cpu(dbs_sync_work, target_cpu);
- sync_work->src_cpu = (unsigned int)arg;
+ struct cpu_dbs_info_s *target_dbs_info =
+ &per_cpu(od_cpu_dbs_info, target_cpu);
- queue_work_on(target_cpu, dbs_wq,
- &per_cpu(dbs_sync_work, target_cpu).work);
+ atomic_set(&target_dbs_info->src_sync_cpu, (int)arg);
+ wake_up(&target_dbs_info->sync_wq);
return NOTIFY_OK;
}
@@ -1053,73 +1050,97 @@
.notifier_call = dbs_migration_notify,
};
-void dbs_synchronize(struct work_struct *work)
+static int sync_pending(struct cpu_dbs_info_s *this_dbs_info)
{
- struct cpufreq_policy *policy;
- struct cpu_dbs_info_s *this_dbs_info, *src_dbs_info;
- struct dbs_sync_work_struct *dbs_work;
- unsigned int cpu, src_cpu;
+ return atomic_read(&this_dbs_info->src_sync_cpu) >= 0;
+}
+
+static int dbs_sync_thread(void *data)
+{
+ int src_cpu, cpu = (int)data;
unsigned int src_freq, src_max_load;
+ struct cpu_dbs_info_s *this_dbs_info, *src_dbs_info;
+ struct cpufreq_policy *policy;
int delay;
- dbs_work = container_of(work, struct dbs_sync_work_struct, work);
- cpu = dbs_work->targ_cpu;
- src_cpu = dbs_work->src_cpu;
-
- get_online_cpus();
-
- /* Getting source cpu info */
- src_dbs_info = &per_cpu(od_cpu_dbs_info, src_cpu);
- if (src_dbs_info != NULL && src_dbs_info->cur_policy != NULL) {
- src_freq = src_dbs_info->cur_policy->cur;
- src_max_load = src_dbs_info->max_load;
- } else {
- src_freq = dbs_tuners_ins.sync_freq;
- src_max_load = 0;
- }
-
- if (lock_policy_rwsem_write(cpu) < 0)
- goto bail_acq_sema_failed;
-
this_dbs_info = &per_cpu(od_cpu_dbs_info, cpu);
- policy = this_dbs_info->cur_policy;
- if (!policy) {
- /* CPU not using ondemand governor */
- goto bail_incorrect_governor;
- }
- delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
+ while (1) {
+ wait_event(this_dbs_info->sync_wq,
+ sync_pending(this_dbs_info) ||
+ kthread_should_stop());
- if (policy->cur < src_freq) {
+ if (kthread_should_stop())
+ break;
- /* Cancelling the next ondemand sample */
- cancel_delayed_work_sync(&this_dbs_info->work);
+ get_online_cpus();
- /*
- * Arch specific cpufreq driver may fail.
- * Don't update governor frequency upon failure.
+ /* TODO: cur_policy is currently set for all CPUs when
+ * a policy is started but cleared only on the current
+ * CPU when the policy is stopped. If/when this is
+ * resolved, sync_enabled can be removed and
+ * cur_policy can be used instead.
*/
- if (__cpufreq_driver_target(policy, src_freq,
- CPUFREQ_RELATION_L) >= 0) {
- policy->cur = src_freq;
- if (src_max_load > this_dbs_info->max_load) {
- this_dbs_info->max_load = src_max_load;
- this_dbs_info->prev_load = src_max_load;
- }
+ if (!atomic_read(&this_dbs_info->sync_enabled)) {
+ atomic_set(&this_dbs_info->src_sync_cpu, -1);
+ put_online_cpus();
+ continue;
}
- /* Rescheduling the next ondemand sample */
- mutex_lock(&this_dbs_info->timer_mutex);
- schedule_delayed_work_on(cpu, &this_dbs_info->work,
- delay);
- mutex_unlock(&this_dbs_info->timer_mutex);
- }
-bail_incorrect_governor:
- unlock_policy_rwsem_write(cpu);
+ src_cpu = atomic_read(&this_dbs_info->src_sync_cpu);
+ src_dbs_info = &per_cpu(od_cpu_dbs_info, src_cpu);
+ if (src_dbs_info != NULL &&
+ src_dbs_info->cur_policy != NULL) {
+ src_freq = src_dbs_info->cur_policy->cur;
+ src_max_load = src_dbs_info->max_load;
+ } else {
+ src_freq = dbs_tuners_ins.sync_freq;
+ src_max_load = 0;
+ }
+ if (lock_policy_rwsem_write(cpu) < 0)
+ goto bail_acq_sema_failed;
+
+ policy = this_dbs_info->cur_policy;
+ if (!policy) {
+ /* CPU not using ondemand governor */
+ goto bail_incorrect_governor;
+ }
+ delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
+
+
+ if (policy->cur < src_freq) {
+ /* cancel the next ondemand sample */
+ cancel_delayed_work_sync(&this_dbs_info->work);
+
+ /*
+ * Arch specific cpufreq driver may fail.
+ * Don't update governor frequency upon failure.
+ */
+ if (__cpufreq_driver_target(policy, src_freq,
+ CPUFREQ_RELATION_L) >= 0) {
+ policy->cur = src_freq;
+ if (src_max_load > this_dbs_info->max_load) {
+ this_dbs_info->max_load = src_max_load;
+ this_dbs_info->prev_load = src_max_load;
+ }
+ }
+
+ /* reschedule the next ondemand sample */
+ mutex_lock(&this_dbs_info->timer_mutex);
+ queue_delayed_work_on(cpu, dbs_wq,
+ &this_dbs_info->work, delay);
+ mutex_unlock(&this_dbs_info->timer_mutex);
+ }
+
+bail_incorrect_governor:
+ unlock_policy_rwsem_write(cpu);
bail_acq_sema_failed:
- put_online_cpus();
- return;
+ put_online_cpus();
+ atomic_set(&this_dbs_info->src_sync_cpu, -1);
+ }
+
+ return 0;
}
static void dbs_input_event(struct input_handle *handle, unsigned int type,
@@ -1215,6 +1236,9 @@
if (dbs_tuners_ins.ignore_nice)
j_dbs_info->prev_cpu_nice =
kcpustat_cpu(j).cpustat[CPUTIME_NICE];
+ set_cpus_allowed(j_dbs_info->sync_thread,
+ *cpumask_of(j));
+ atomic_set(&j_dbs_info->sync_enabled, 1);
}
this_dbs_info->cpu = cpu;
this_dbs_info->rate_mult = 1;
@@ -1271,6 +1295,13 @@
mutex_lock(&dbs_mutex);
dbs_enable--;
+
+ for_each_cpu(j, policy->cpus) {
+ struct cpu_dbs_info_s *j_dbs_info;
+ j_dbs_info = &per_cpu(od_cpu_dbs_info, j);
+ atomic_set(&j_dbs_info->sync_enabled, 0);
+ }
+
/* If device is being removed, policy is no longer
* valid. */
this_dbs_info->cur_policy = NULL;
@@ -1342,17 +1373,17 @@
&per_cpu(od_cpu_dbs_info, i);
struct dbs_work_struct *dbs_work =
&per_cpu(dbs_refresh_work, i);
- struct dbs_sync_work_struct *dbs_sync =
- &per_cpu(dbs_sync_work, i);
mutex_init(&this_dbs_info->timer_mutex);
INIT_WORK(&dbs_work->work, dbs_refresh_callback);
dbs_work->cpu = i;
- INIT_WORK(&dbs_sync->work, dbs_synchronize);
- dbs_sync->src_cpu = 0;
- dbs_sync->targ_cpu = i;
+ atomic_set(&this_dbs_info->src_sync_cpu, -1);
+ init_waitqueue_head(&this_dbs_info->sync_wq);
+ this_dbs_info->sync_thread = kthread_run(dbs_sync_thread,
+ (void *)i,
+ "dbs_sync/%d", i);
}
return cpufreq_register_governor(&cpufreq_gov_ondemand);
@@ -1367,6 +1398,7 @@
struct cpu_dbs_info_s *this_dbs_info =
&per_cpu(od_cpu_dbs_info, i);
mutex_destroy(&this_dbs_info->timer_mutex);
+ kthread_stop(this_dbs_info->sync_thread);
}
destroy_workqueue(dbs_wq);
}
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index f8185df..dadf87c 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -69,6 +69,29 @@
}
/**
+ * devfreq_set_freq_limits() - Set min and max frequency from freq_table
+ * @devfreq: the devfreq instance
+ */
+static void devfreq_set_freq_limits(struct devfreq *devfreq)
+{
+ int idx;
+ unsigned long min = ~0, max = 0;
+
+ if (!devfreq->profile->freq_table)
+ return;
+
+ for (idx = 0; idx < devfreq->profile->max_state; idx++) {
+ if (min > devfreq->profile->freq_table[idx])
+ min = devfreq->profile->freq_table[idx];
+ if (max < devfreq->profile->freq_table[idx])
+ max = devfreq->profile->freq_table[idx];
+ }
+
+ devfreq->min_freq = min;
+ devfreq->max_freq = max;
+}
+
+/**
* devfreq_get_freq_level() - Lookup freq_table for the frequency
* @devfreq: the devfreq instance
* @freq: the target frequency
@@ -506,6 +529,7 @@
devfreq->profile->max_state,
GFP_KERNEL);
devfreq->last_stat_updated = jiffies;
+ devfreq_set_freq_limits(devfreq);
dev_set_name(&devfreq->dev, dev_name(dev));
err = device_register(&devfreq->dev);
diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c
index c72f942..bc7da1e 100644
--- a/drivers/devfreq/governor_performance.c
+++ b/drivers/devfreq/governor_performance.c
@@ -32,7 +32,7 @@
{
int ret = 0;
- if (event == DEVFREQ_GOV_START) {
+ if (event == DEVFREQ_GOV_START || event == DEVFREQ_GOV_RESUME) {
mutex_lock(&devfreq->lock);
ret = update_devfreq(devfreq);
mutex_unlock(&devfreq->lock);
diff --git a/drivers/devfreq/governor_powersave.c b/drivers/devfreq/governor_powersave.c
index 0c6bed5..6d43685 100644
--- a/drivers/devfreq/governor_powersave.c
+++ b/drivers/devfreq/governor_powersave.c
@@ -29,7 +29,7 @@
{
int ret = 0;
- if (event == DEVFREQ_GOV_START) {
+ if (event == DEVFREQ_GOV_START || event == DEVFREQ_GOV_RESUME) {
mutex_lock(&devfreq->lock);
ret = update_devfreq(devfreq);
mutex_unlock(&devfreq->lock);
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 5a75510..7db56a3 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1081,6 +1081,10 @@
}
}
+#ifdef CONFIG_PINCTRL
+ INIT_LIST_HEAD(&chip->pin_ranges);
+#endif
+
of_gpiochip_add(chip);
unlock:
@@ -1178,6 +1182,47 @@
}
EXPORT_SYMBOL_GPL(gpiochip_find);
+#ifdef CONFIG_PINCTRL
+int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
+ unsigned int pin_base, unsigned int npins)
+{
+ struct gpio_pin_range *pin_range;
+
+ pin_range = devm_kzalloc(chip->dev, sizeof(*pin_range), GFP_KERNEL);
+ if (!pin_range) {
+ pr_err("%s: GPIO chip: failed to allocate pin ranges\n",
+ chip->label);
+ return -ENOMEM;
+ }
+
+ pin_range->range.name = chip->label;
+ pin_range->range.base = chip->base;
+ pin_range->range.pin_base = pin_base;
+ pin_range->range.npins = npins;
+ pin_range->pctldev = pinctrl_find_and_add_gpio_range(pinctl_name,
+ &pin_range->range);
+
+ list_add_tail(&pin_range->node, &chip->pin_ranges);
+
+ return 0;
+}
+
+void gpiochip_remove_pin_ranges(struct gpio_chip *chip)
+{
+ struct gpio_pin_range *pin_range, *tmp;
+
+ list_for_each_entry_safe(pin_range, tmp, &chip->pin_ranges, node) {
+ list_del(&pin_range->node);
+ pinctrl_remove_gpio_range(pin_range->pctldev,
+ &pin_range->range);
+ }
+}
+#else
+void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
+ unsigned int pin_base, unsigned int npins) {}
+void gpiochip_remove_pin_ranges(struct gpio_chip *chip) {}
+#endif
+
/* These "optional" allocation calls help prevent drivers from stomping
* on each other, and help provide better diagnostics in debugfs.
* They're called even less than the "set direction" calls.
diff --git a/drivers/gpu/ion/ion_cma_heap.c b/drivers/gpu/ion/ion_cma_heap.c
index e7f7836..b24b2bd 100644
--- a/drivers/gpu/ion/ion_cma_heap.c
+++ b/drivers/gpu/ion/ion_cma_heap.c
@@ -76,10 +76,10 @@
if (!ION_IS_CACHED(flags))
info->cpu_addr = dma_alloc_writecombine(dev, len,
- &(info->handle), 0);
+ &(info->handle), GFP_KERNEL);
else
info->cpu_addr = dma_alloc_nonconsistent(dev, len,
- &(info->handle), 0);
+ &(info->handle), GFP_KERNEL);
if (!info->cpu_addr) {
dev_err(dev, "Fail to allocate buffer\n");
diff --git a/drivers/gpu/ion/ion_cma_secure_heap.c b/drivers/gpu/ion/ion_cma_secure_heap.c
index b3960b2..415c73e 100644
--- a/drivers/gpu/ion/ion_cma_secure_heap.c
+++ b/drivers/gpu/ion/ion_cma_secure_heap.c
@@ -83,7 +83,8 @@
return ION_CMA_ALLOCATE_FAILED;
}
- info->cpu_addr = dma_alloc_attrs(dev, len, &(info->handle), 0, &attrs);
+ info->cpu_addr = dma_alloc_attrs(dev, len, &(info->handle), GFP_KERNEL,
+ &attrs);
if (!info->cpu_addr) {
dev_err(dev, "Fail to allocate buffer\n");
diff --git a/drivers/gpu/ion/msm/msm_ion.c b/drivers/gpu/ion/msm/msm_ion.c
index 4a45313..4e3af1c 100644
--- a/drivers/gpu/ion/msm/msm_ion.c
+++ b/drivers/gpu/ion/msm/msm_ion.c
@@ -365,6 +365,9 @@
if (!ION_IS_CACHED(flags))
return 0;
+ if (flags & ION_FLAG_SECURE)
+ return 0;
+
table = ion_sg_table(client, handle);
if (IS_ERR_OR_NULL(table))
diff --git a/drivers/gpu/msm/kgsl_drm.c b/drivers/gpu/msm/kgsl_drm.c
index daeefd0..1fc7467 100644
--- a/drivers/gpu/msm/kgsl_drm.c
+++ b/drivers/gpu/msm/kgsl_drm.c
@@ -722,7 +722,7 @@
ret = -EINVAL;
else if (TYPE_IS_PMEM(priv->type) || TYPE_IS_MEM(priv->type)) {
if (priv->ion_handle) {
- args->ion_fd = ion_share_dma_buf(
+ args->ion_fd = ion_share_dma_buf_fd(
kgsl_drm_ion_client, priv->ion_handle);
if (args->ion_fd < 0) {
DRM_ERROR(
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 0bacc5e..9113605 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -287,7 +287,7 @@
addr < (p->gpuaddr + p->size)) {
kgsl_get_memory_usage(name, sizeof(name) - 1,
- p->flags),
+ p->flags);
KGSL_LOG_DUMP(iommu_dev->kgsldev,
"---- premature free ----\n");
KGSL_LOG_DUMP(iommu_dev->kgsldev,
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index de7b0e9..9570327 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -389,8 +389,13 @@
rc = qpnp_iadc_read_reg(QPNP_IADC_ATE_GAIN_CALIB_OFFSET,
&iadc->iadc_comp.sys_gain);
- if (rc < 0)
+ if (rc < 0) {
pr_err("full scale read failed with %d\n", rc);
+ return rc;
+ }
+
+ if (iadc->external_rsense)
+ iadc->iadc_comp.ext_rsense = true;
pr_debug("fab id = %u, revision = %u, sys gain = %u, external_rsense = %d\n",
iadc->iadc_comp.id,
diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c
index f54707c..204ad94 100644
--- a/drivers/input/misc/kxtj9.c
+++ b/drivers/input/misc/kxtj9.c
@@ -31,7 +31,9 @@
#include <linux/of_gpio.h>
#endif /* CONFIG_OF */
-#define NAME "kxtj9"
+#define ACCEL_INPUT_DEV_NAME "accelerometer"
+#define DEVICE_NAME "kxtj9"
+
#define G_MAX 8000
/* OUTPUT REGISTERS */
#define XOUT_L 0x06
@@ -421,19 +423,6 @@
tj9->enable = false;
}
-static int kxtj9_input_open(struct input_dev *input)
-{
- struct kxtj9_data *tj9 = input_get_drvdata(input);
-
- return kxtj9_enable(tj9);
-}
-
-static void kxtj9_input_close(struct input_dev *dev)
-{
- struct kxtj9_data *tj9 = input_get_drvdata(dev);
-
- kxtj9_disable(tj9);
-}
static void __devinit kxtj9_init_input_device(struct kxtj9_data *tj9,
struct input_dev *input_dev)
@@ -443,7 +432,7 @@
input_set_abs_params(input_dev, ABS_Y, -G_MAX, G_MAX, FUZZ, FLAT);
input_set_abs_params(input_dev, ABS_Z, -G_MAX, G_MAX, FUZZ, FLAT);
- input_dev->name = "kxtj9_accel";
+ input_dev->name = ACCEL_INPUT_DEV_NAME;
input_dev->id.bustype = BUS_I2C;
input_dev->dev.parent = &tj9->client->dev;
}
@@ -461,8 +450,6 @@
tj9->input_dev = input_dev;
- input_dev->open = kxtj9_input_open;
- input_dev->close = kxtj9_input_close;
input_set_drvdata(input_dev, tj9);
kxtj9_init_input_device(tj9, input_dev);
@@ -534,7 +521,7 @@
*/
/* Returns currently selected poll interval (in ms) */
-static ssize_t kxtj9_get_poll(struct device *dev,
+static ssize_t kxtj9_get_poll_delay(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -544,8 +531,9 @@
}
/* Allow users to select a new poll interval (in ms) */
-static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t kxtj9_set_poll_delay(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct kxtj9_data *tj9 = i2c_get_clientdata(client);
@@ -576,11 +564,12 @@
return count;
}
-static DEVICE_ATTR(poll, S_IRUGO|S_IWUSR, kxtj9_get_poll, kxtj9_set_poll);
+static DEVICE_ATTR(poll_delay, S_IRUGO|S_IWUSR|S_IWGRP,
+ kxtj9_get_poll_delay, kxtj9_set_poll_delay);
static struct attribute *kxtj9_attributes[] = {
&dev_attr_enable.attr,
- &dev_attr_poll.attr,
+ &dev_attr_poll_delay.attr,
NULL
};
@@ -679,7 +668,8 @@
goto out;
}
- retval = (retval != 0x07 && retval != 0x08) ? -EIO : 0;
+ retval = (retval != 0x05 && retval != 0x07 && retval != 0x08)
+ ? -EIO : 0;
out:
return retval;
@@ -950,7 +940,7 @@
static SIMPLE_DEV_PM_OPS(kxtj9_pm_ops, kxtj9_suspend, kxtj9_resume);
static const struct i2c_device_id kxtj9_id[] = {
- { NAME, 0 },
+ { DEVICE_NAME, 0 },
{ },
};
@@ -964,7 +954,7 @@
static struct i2c_driver kxtj9_driver = {
.driver = {
- .name = NAME,
+ .name = DEVICE_NAME,
.owner = THIS_MODULE,
.of_match_table = kxtj9_match_table,
.pm = &kxtj9_pm_ops,
diff --git a/drivers/input/misc/lis3dh_acc.c b/drivers/input/misc/lis3dh_acc.c
index ea1b079..03fabd0 100644
--- a/drivers/input/misc/lis3dh_acc.c
+++ b/drivers/input/misc/lis3dh_acc.c
@@ -1093,7 +1093,7 @@
static struct device_attribute attributes[] = {
- __ATTR(pollrate_ms, 0664, attr_get_polling_rate,
+ __ATTR(poll_delay, 0664, attr_get_polling_rate,
attr_set_polling_rate),
__ATTR(range, 0664, attr_get_range, attr_set_range),
__ATTR(enable, 0664, attr_get_enable, attr_set_enable),
@@ -1226,7 +1226,7 @@
acc->input_dev->open = lis3dh_acc_input_open;
acc->input_dev->close = lis3dh_acc_input_close;
- acc->input_dev->name = LIS3DH_ACC_DEV_NAME;
+ acc->input_dev->name = ACCEL_INPUT_DEV_NAME;
acc->input_dev->id.bustype = BUS_I2C;
acc->input_dev->dev.parent = &acc->client->dev;
diff --git a/drivers/iommu/msm_iommu-v1.c b/drivers/iommu/msm_iommu-v1.c
index a400b58..175328e 100644
--- a/drivers/iommu/msm_iommu-v1.c
+++ b/drivers/iommu/msm_iommu-v1.c
@@ -39,6 +39,7 @@
#define MSM_IOMMU_PGSIZES (SZ_4K | SZ_64K | SZ_1M | SZ_16M)
static DEFINE_MUTEX(msm_iommu_lock);
+struct dump_regs_tbl dump_regs_tbl[MAX_DUMP_REGS];
static int __enable_regulators(struct msm_iommu_drvdata *drvdata)
{
@@ -769,7 +770,7 @@
struct msm_iommu_priv *priv;
struct msm_iommu_drvdata *iommu_drvdata;
struct msm_iommu_ctx_drvdata *ctx_drvdata;
- unsigned int par;
+ u64 par;
void __iomem *base;
phys_addr_t ret = 0;
int ctx;
@@ -802,6 +803,23 @@
__disable_clocks(iommu_drvdata);
if (par & CB_PAR_F) {
+ unsigned int level = (par & CB_PAR_PLVL) >> CB_PAR_PLVL_SHIFT;
+ pr_err("IOMMU translation fault!\n");
+ pr_err("name = %s\n", iommu_drvdata->name);
+ pr_err("context = %s (%d)\n", ctx_drvdata->name,
+ ctx_drvdata->num);
+ pr_err("Interesting registers:\n");
+ pr_err("PAR = %16llx [%s%s%s%s%s%s%s%sPLVL%u %s]\n", par,
+ (par & CB_PAR_F) ? "F " : "",
+ (par & CB_PAR_TF) ? "TF " : "",
+ (par & CB_PAR_AFF) ? "AFF " : "",
+ (par & CB_PAR_PF) ? "PF " : "",
+ (par & CB_PAR_EF) ? "EF " : "",
+ (par & CB_PAR_TLBMCF) ? "TLBMCF " : "",
+ (par & CB_PAR_TLBLKF) ? "TLBLKF " : "",
+ (par & CB_PAR_ATOT) ? "ATOT " : "",
+ level,
+ (par & CB_PAR_STAGE) ? "S2 " : "S1 ");
ret = 0;
} else {
/* We are dealing with a supersection */
@@ -857,49 +875,13 @@
static void __print_ctx_regs(void __iomem *base, int ctx, unsigned int fsr)
{
- struct msm_iommu_context_reg regs[MAX_DUMP_REGS] = {
- [DUMP_REG_FAR0] = {
- .val = GET_FAR(base, ctx)
- },
- [DUMP_REG_FAR1] = {
- /* TODO: make GET_FAR 64-bit and take this from that */
- .val = 0
- },
- [DUMP_REG_PAR0] = {
- .val = GET_PAR(base, ctx)
- },
- [DUMP_REG_PAR1] = {
- /* TODO: make GET_PAR 64-bit and take this from that */
- .val = 0
- },
- [DUMP_REG_FSR] = {
- .val = fsr
- },
- [DUMP_REG_FSYNR0] = {
- .val = GET_FSYNR0(base, ctx)
- },
- [DUMP_REG_FSYNR1] = {
- .val = GET_FSYNR1(base, ctx)
- },
- [DUMP_REG_TTBR0] = {
- .val = GET_TTBR0(base, ctx)
- },
- [DUMP_REG_TTBR1] = {
- .val = GET_TTBR1(base, ctx)
- },
- [DUMP_REG_SCTLR] = {
- .val = GET_SCTLR(base, ctx)
- },
- [DUMP_REG_ACTLR] = {
- .val = GET_ACTLR(base, ctx)
- },
- [DUMP_REG_PRRR] = {
- .val = GET_PRRR(base, ctx)
- },
- [DUMP_REG_NMRR] = {
- .val = GET_NMRR(base, ctx)
- },
- };
+ struct msm_iommu_context_reg regs[MAX_DUMP_REGS];
+ unsigned int i;
+
+ for (i = DUMP_REG_FIRST; i < MAX_DUMP_REGS; ++i) {
+ regs[i].val = GET_CTX_REG(dump_regs_tbl[i].key, base, ctx);
+ regs[i].valid = 1;
+ }
print_ctx_regs(regs);
}
@@ -976,6 +958,29 @@
return __pa(priv->pt.fl_table);
}
+#define DUMP_REG_INIT(dump_reg, cb_reg) \
+ do { \
+ dump_regs_tbl[dump_reg].key = cb_reg; \
+ dump_regs_tbl[dump_reg].name = #cb_reg; \
+ } while (0)
+
+static void msm_iommu_build_dump_regs_table(void)
+{
+ DUMP_REG_INIT(DUMP_REG_FAR0, CB_FAR);
+ DUMP_REG_INIT(DUMP_REG_FAR1, CB_FAR + 4);
+ DUMP_REG_INIT(DUMP_REG_PAR0, CB_PAR);
+ DUMP_REG_INIT(DUMP_REG_PAR1, CB_PAR + 4);
+ DUMP_REG_INIT(DUMP_REG_FSR, CB_FSR);
+ DUMP_REG_INIT(DUMP_REG_FSYNR0, CB_FSYNR0);
+ DUMP_REG_INIT(DUMP_REG_FSYNR1, CB_FSYNR1);
+ DUMP_REG_INIT(DUMP_REG_TTBR0, CB_TTBR0);
+ DUMP_REG_INIT(DUMP_REG_TTBR1, CB_TTBR1);
+ DUMP_REG_INIT(DUMP_REG_SCTLR, CB_SCTLR);
+ DUMP_REG_INIT(DUMP_REG_ACTLR, CB_ACTLR);
+ DUMP_REG_INIT(DUMP_REG_PRRR, CB_PRRR);
+ DUMP_REG_INIT(DUMP_REG_NMRR, CB_NMRR);
+}
+
static struct iommu_ops msm_iommu_ops = {
.domain_init = msm_iommu_domain_init,
.domain_destroy = msm_iommu_domain_destroy,
@@ -995,6 +1000,8 @@
{
msm_iommu_pagetable_init();
bus_set_iommu(&platform_bus_type, &msm_iommu_ops);
+ msm_iommu_build_dump_regs_table();
+
return 0;
}
diff --git a/drivers/iommu/msm_iommu_sec.c b/drivers/iommu/msm_iommu_sec.c
index 50f6df4..8d2cc8f 100644
--- a/drivers/iommu/msm_iommu_sec.c
+++ b/drivers/iommu/msm_iommu_sec.c
@@ -124,41 +124,8 @@
return ret;
}
-static struct dump_regs_tbl {
- /*
- * To keep things context-bank-agnostic, we only store the CB
- * register offset in `key'
- */
- unsigned long key;
- const char *name;
- int offset;
-} dump_regs_tbl[MAX_DUMP_REGS];
-
#define EXTRACT_DUMP_REG_KEY(addr, ctx) (addr & ((1 << CTX_SHIFT) - 1))
-#define DUMP_REG_INIT(dump_reg, cb_reg) \
- do { \
- dump_regs_tbl[dump_reg].key = cb_reg; \
- dump_regs_tbl[dump_reg].name = #cb_reg; \
- } while (0)
-
-static void msm_iommu_sec_build_dump_regs_table(void)
-{
- DUMP_REG_INIT(DUMP_REG_FAR0, CB_FAR);
- DUMP_REG_INIT(DUMP_REG_FAR1, CB_FAR + 4);
- DUMP_REG_INIT(DUMP_REG_PAR0, CB_PAR);
- DUMP_REG_INIT(DUMP_REG_PAR1, CB_PAR + 4);
- DUMP_REG_INIT(DUMP_REG_FSR, CB_FSR);
- DUMP_REG_INIT(DUMP_REG_FSYNR0, CB_FSYNR0);
- DUMP_REG_INIT(DUMP_REG_FSYNR1, CB_FSYNR1);
- DUMP_REG_INIT(DUMP_REG_TTBR0, CB_TTBR0);
- DUMP_REG_INIT(DUMP_REG_TTBR1, CB_TTBR1);
- DUMP_REG_INIT(DUMP_REG_SCTLR, CB_SCTLR);
- DUMP_REG_INIT(DUMP_REG_ACTLR, CB_ACTLR);
- DUMP_REG_INIT(DUMP_REG_PRRR, CB_PRRR);
- DUMP_REG_INIT(DUMP_REG_NMRR, CB_NMRR);
-}
-
static int msm_iommu_reg_dump_to_regs(
struct msm_iommu_context_reg ctx_regs[],
struct msm_scm_fault_regs_dump *dump, int cb_num)
@@ -799,9 +766,6 @@
bus_set_iommu(&msm_iommu_sec_bus_type, &msm_iommu_ops);
ret = msm_iommu_sec_ptbl_init();
- if (ret)
- goto fail;
- msm_iommu_sec_build_dump_regs_table();
fail:
return ret;
}
diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c
index bac8678..fbcc243 100644
--- a/drivers/leds/leds-qpnp.c
+++ b/drivers/leds/leds-qpnp.c
@@ -97,6 +97,7 @@
#define FLASH_FAULT_DETECT(base) (base + 0x51)
#define FLASH_MAX_LEVEL 0x4F
+#define TORCH_MAX_LEVEL 0x0F
#define FLASH_NO_MASK 0x00
#define FLASH_MASK_1 0x20
@@ -159,6 +160,7 @@
#define QPNP_LED_PWM_FLAGS (PM_PWM_LUT_LOOP | PM_PWM_LUT_RAMP_UP)
#define QPNP_LUT_RAMP_STEP_DEFAULT 255
#define PWM_LUT_MAX_SIZE 63
+#define PWM_GPLED_LUT_MAX_SIZE 31
#define RGB_LED_DISABLE 0x00
#define MPP_MAX_LEVEL LED_FULL
@@ -299,6 +301,10 @@
* @pwm_channel - pwm channel to be configured for led
* @pwm_period_us - period for pwm, in us
* @mode - mode the led operates in
+ * @old_duty_pcts - storage for duty pcts that may need to be reused
+ * @default_mode - default mode of LED as set in device tree
+ * @use_blink - use blink sysfs entry
+ * @blinking - device is currently blinking w/LPG mode
*/
struct pwm_config_data {
struct lut_params lut_params;
@@ -306,9 +312,11 @@
int pwm_channel;
u32 pwm_period_us;
struct pwm_duty_cycles *duty_cycles;
+ int *old_duty_pcts;
u8 mode;
- u8 enable;
+ u8 default_mode;
bool use_blink;
+ bool blinking;
};
/**
@@ -414,6 +422,7 @@
* struct qpnp_led_data - internal led data structure
* @led_classdev - led class device
* @delayed_work - delayed work for turning off the LED
+ * @work - workqueue for led
* @id - led index
* @base_reg - base register given in device tree
* @lock - to protect the transactions
@@ -427,11 +436,12 @@
struct led_classdev cdev;
struct spmi_device *spmi_dev;
struct delayed_work dwork;
+ struct work_struct work;
int id;
u16 base;
u8 reg;
u8 num_leds;
- spinlock_t lock;
+ struct mutex lock;
struct wled_config_data *wled_cfg;
struct flash_config_data *flash_cfg;
struct kpdbl_config_data *kpdbl_cfg;
@@ -566,6 +576,14 @@
int duty_us;
if (led->cdev.brightness) {
+ if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
+ if (!led->mpp_cfg->pwm_cfg->blinking) {
+ led->mpp_cfg->pwm_cfg->mode =
+ led->mpp_cfg->pwm_cfg->default_mode;
+ led->mpp_cfg->pwm_mode =
+ led->mpp_cfg->pwm_cfg->default_mode;
+ }
+ }
if (led->mpp_cfg->pwm_mode == PWM_MODE) {
pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev);
duty_us = (led->mpp_cfg->pwm_cfg->pwm_period_us *
@@ -606,8 +624,13 @@
return rc;
}
} else {
- if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
+ if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
+ led->mpp_cfg->pwm_cfg->mode =
+ led->mpp_cfg->pwm_cfg->default_mode;
+ led->mpp_cfg->pwm_mode =
+ led->mpp_cfg->pwm_cfg->default_mode;
pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev);
+ }
rc = qpnp_led_masked_write(led,
LED_MPP_MODE_CTRL(led->base),
LED_MPP_MODE_MASK,
@@ -629,6 +652,8 @@
}
}
+ if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
+ led->mpp_cfg->pwm_cfg->blinking = false;
qpnp_dump_regs(led, mpp_debug_regs, ARRAY_SIZE(mpp_debug_regs));
return 0;
@@ -639,8 +664,12 @@
int rc;
int val = led->cdev.brightness;
- led->flash_cfg->current_prgm = (val * FLASH_MAX_LEVEL /
- led->max_current);
+ if (led->flash_cfg->torch_enable)
+ led->flash_cfg->current_prgm =
+ (val * TORCH_MAX_LEVEL / led->max_current);
+ else
+ led->flash_cfg->current_prgm =
+ (val * FLASH_MAX_LEVEL / led->max_current);
led->flash_cfg->current_prgm =
led->flash_cfg->current_prgm >> FLASH_CURRENT_PRGM_SHIFT;
@@ -691,7 +720,7 @@
qpnp_led_masked_write(led, FLASH_MAX_CURR(led->base),
FLASH_CURRENT_MASK,
- led->max_current);
+ TORCH_MAX_LEVEL);
if (rc) {
dev_err(&led->spmi_dev->dev,
"Max current reg write failed(%d)\n",
@@ -861,6 +890,9 @@
int rc;
if (led->cdev.brightness) {
+ if (!led->kpdbl_cfg->pwm_cfg->blinking)
+ led->kpdbl_cfg->pwm_cfg->mode =
+ led->kpdbl_cfg->pwm_cfg->default_mode;
rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
KPDBL_MODULE_EN_MASK, KPDBL_MODULE_EN);
duty_us = (led->kpdbl_cfg->pwm_cfg->pwm_period_us *
@@ -877,6 +909,8 @@
return rc;
}
} else {
+ led->kpdbl_cfg->pwm_cfg->mode =
+ led->kpdbl_cfg->pwm_cfg->default_mode;
pwm_disable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
KPDBL_MODULE_EN_MASK, KPDBL_MODULE_DIS);
@@ -887,6 +921,7 @@
}
}
+ led->kpdbl_cfg->pwm_cfg->blinking = false;
qpnp_dump_regs(led, kpdbl_debug_regs, ARRAY_SIZE(kpdbl_debug_regs));
return 0;
@@ -898,6 +933,9 @@
int rc;
if (led->cdev.brightness) {
+ if (!led->rgb_cfg->pwm_cfg->blinking)
+ led->rgb_cfg->pwm_cfg->mode =
+ led->rgb_cfg->pwm_cfg->default_mode;
if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
duty_us = (led->rgb_cfg->pwm_cfg->pwm_period_us *
led->cdev.brightness) / LED_FULL;
@@ -924,6 +962,8 @@
return rc;
}
} else {
+ led->rgb_cfg->pwm_cfg->mode =
+ led->rgb_cfg->pwm_cfg->default_mode;
pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev);
rc = qpnp_led_masked_write(led,
RGB_LED_EN_CTL(led->base),
@@ -935,6 +975,7 @@
}
}
+ led->rgb_cfg->pwm_cfg->blinking = false;
qpnp_dump_regs(led, rgb_pwm_debug_regs, ARRAY_SIZE(rgb_pwm_debug_regs));
return 0;
@@ -943,9 +984,7 @@
static void qpnp_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
- int rc, i;
struct qpnp_led_data *led;
- struct qpnp_led_data *led_array;
led = container_of(led_cdev, struct qpnp_led_data, cdev);
if (value < LED_OFF || value > led->cdev.max_brightness) {
@@ -953,6 +992,16 @@
return;
}
+ led->cdev.brightness = value;
+ schedule_work(&led->work);
+}
+
+static void __qpnp_led_work(struct qpnp_led_data *led,
+ enum led_brightness value)
+{
+ int rc, i;
+ struct qpnp_led_data *led_array;
+
if (led->id == QPNP_ID_FLASH1_LED0 || led->id == QPNP_ID_FLASH1_LED1) {
if (!led->flash_cfg->flash_on && value > 0) {
led_array = dev_get_drvdata(&led->spmi_dev->dev);
@@ -980,8 +1029,7 @@
}
}
- spin_lock(&led->lock);
- led->cdev.brightness = value;
+ mutex_lock(&led->lock);
switch (led->id) {
case QPNP_ID_WLED:
@@ -1021,7 +1069,7 @@
dev_err(&led->spmi_dev->dev, "Invalid LED(%d)\n", led->id);
break;
}
- spin_unlock(&led->lock);
+ mutex_unlock(&led->lock);
if (led->id == QPNP_ID_FLASH1_LED0 || led->id == QPNP_ID_FLASH1_LED1) {
if (led->flash_cfg->flash_on && !value) {
@@ -1050,6 +1098,16 @@
}
}
+static void qpnp_led_work(struct work_struct *work)
+{
+ struct qpnp_led_data *led = container_of(work,
+ struct qpnp_led_data, work);
+
+ __qpnp_led_work(led, led->cdev.brightness);
+
+ return;
+}
+
static int __devinit qpnp_led_set_max_brightness(struct qpnp_led_data *led)
{
switch (led->id) {
@@ -1341,28 +1399,424 @@
return 0;
}
+static ssize_t pwm_us_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct qpnp_led_data *led;
+ u32 pwm_us;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ ssize_t ret;
+ u32 previous_pwm_us;
+ struct pwm_config_data *pwm_cfg;
+
+ led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+ ret = kstrtou32(buf, 10, &pwm_us);
+ if (ret)
+ return ret;
+
+ switch (led->id) {
+ case QPNP_ID_LED_MPP:
+ pwm_cfg = led->mpp_cfg->pwm_cfg;
+ break;
+ case QPNP_ID_RGB_RED:
+ case QPNP_ID_RGB_GREEN:
+ case QPNP_ID_RGB_BLUE:
+ pwm_cfg = led->rgb_cfg->pwm_cfg;
+ break;
+ default:
+ dev_err(&led->spmi_dev->dev,
+ "Invalid LED id type for pwm_us\n");
+ return -EINVAL;
+ }
+
+ if (pwm_cfg->mode == LPG_MODE)
+ pwm_cfg->blinking = true;
+
+ previous_pwm_us = pwm_cfg->pwm_period_us;
+
+ pwm_cfg->pwm_period_us = pwm_us;
+ pwm_free(pwm_cfg->pwm_dev);
+ ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ if (ret) {
+ pwm_cfg->pwm_period_us = previous_pwm_us;
+ pwm_free(pwm_cfg->pwm_dev);
+ qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ dev_err(&led->spmi_dev->dev,
+ "Failed to initialize pwm with new pwm_us value\n");
+ return ret;
+ }
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ return count;
+}
+
+static ssize_t pause_lo_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct qpnp_led_data *led;
+ u32 pause_lo;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ ssize_t ret;
+ u32 previous_pause_lo;
+ struct pwm_config_data *pwm_cfg;
+
+ ret = kstrtou32(buf, 10, &pause_lo);
+ if (ret)
+ return ret;
+ led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+ switch (led->id) {
+ case QPNP_ID_LED_MPP:
+ pwm_cfg = led->mpp_cfg->pwm_cfg;
+ break;
+ case QPNP_ID_RGB_RED:
+ case QPNP_ID_RGB_GREEN:
+ case QPNP_ID_RGB_BLUE:
+ pwm_cfg = led->rgb_cfg->pwm_cfg;
+ break;
+ default:
+ dev_err(&led->spmi_dev->dev,
+ "Invalid LED id type for pause lo\n");
+ return -EINVAL;
+ }
+
+ if (pwm_cfg->mode == LPG_MODE)
+ pwm_cfg->blinking = true;
+
+ previous_pause_lo = pwm_cfg->lut_params.lut_pause_lo;
+
+ pwm_free(pwm_cfg->pwm_dev);
+ pwm_cfg->lut_params.lut_pause_lo = pause_lo;
+ ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ if (ret) {
+ pwm_cfg->lut_params.lut_pause_lo = previous_pause_lo;
+ pwm_free(pwm_cfg->pwm_dev);
+ qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ dev_err(&led->spmi_dev->dev,
+ "Failed to initialize pwm with new pause lo value\n");
+ return ret;
+ }
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ return count;
+}
+
+static ssize_t pause_hi_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct qpnp_led_data *led;
+ u32 pause_hi;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ ssize_t ret;
+ u32 previous_pause_hi;
+ struct pwm_config_data *pwm_cfg;
+
+ ret = kstrtou32(buf, 10, &pause_hi);
+ if (ret)
+ return ret;
+ led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+ switch (led->id) {
+ case QPNP_ID_LED_MPP:
+ pwm_cfg = led->mpp_cfg->pwm_cfg;
+ break;
+ case QPNP_ID_RGB_RED:
+ case QPNP_ID_RGB_GREEN:
+ case QPNP_ID_RGB_BLUE:
+ pwm_cfg = led->rgb_cfg->pwm_cfg;
+ break;
+ default:
+ dev_err(&led->spmi_dev->dev,
+ "Invalid LED id type for pause hi\n");
+ return -EINVAL;
+ }
+
+ if (pwm_cfg->mode == LPG_MODE)
+ pwm_cfg->blinking = true;
+
+ previous_pause_hi = pwm_cfg->lut_params.lut_pause_hi;
+
+ pwm_free(pwm_cfg->pwm_dev);
+ pwm_cfg->lut_params.lut_pause_hi = pause_hi;
+ ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ if (ret) {
+ pwm_cfg->lut_params.lut_pause_hi = previous_pause_hi;
+ pwm_free(pwm_cfg->pwm_dev);
+ qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ dev_err(&led->spmi_dev->dev,
+ "Failed to initialize pwm with new pause hi value\n");
+ return ret;
+ }
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ return count;
+}
+
+static ssize_t start_idx_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct qpnp_led_data *led;
+ u32 start_idx;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ ssize_t ret;
+ u32 previous_start_idx;
+ struct pwm_config_data *pwm_cfg;
+
+ ret = kstrtou32(buf, 10, &start_idx);
+ if (ret)
+ return ret;
+ led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+ switch (led->id) {
+ case QPNP_ID_LED_MPP:
+ pwm_cfg = led->mpp_cfg->pwm_cfg;
+ break;
+ case QPNP_ID_RGB_RED:
+ case QPNP_ID_RGB_GREEN:
+ case QPNP_ID_RGB_BLUE:
+ pwm_cfg = led->rgb_cfg->pwm_cfg;
+ break;
+ default:
+ dev_err(&led->spmi_dev->dev,
+ "Invalid LED id type for start idx\n");
+ return -EINVAL;
+ }
+
+ if (pwm_cfg->mode == LPG_MODE)
+ pwm_cfg->blinking = true;
+
+ previous_start_idx = pwm_cfg->duty_cycles->start_idx;
+ pwm_cfg->duty_cycles->start_idx = start_idx;
+ pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx;
+ pwm_free(pwm_cfg->pwm_dev);
+ ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ if (ret) {
+ pwm_cfg->duty_cycles->start_idx = previous_start_idx;
+ pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx;
+ pwm_free(pwm_cfg->pwm_dev);
+ qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ dev_err(&led->spmi_dev->dev,
+ "Failed to initialize pwm with new start idx value\n");
+ return ret;
+ }
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ return count;
+}
+
+static ssize_t ramp_step_ms_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct qpnp_led_data *led;
+ u32 ramp_step_ms;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ ssize_t ret;
+ u32 previous_ramp_step_ms;
+ struct pwm_config_data *pwm_cfg;
+
+ ret = kstrtou32(buf, 10, &ramp_step_ms);
+ if (ret)
+ return ret;
+ led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+ switch (led->id) {
+ case QPNP_ID_LED_MPP:
+ pwm_cfg = led->mpp_cfg->pwm_cfg;
+ break;
+ case QPNP_ID_RGB_RED:
+ case QPNP_ID_RGB_GREEN:
+ case QPNP_ID_RGB_BLUE:
+ pwm_cfg = led->rgb_cfg->pwm_cfg;
+ break;
+ default:
+ dev_err(&led->spmi_dev->dev,
+ "Invalid LED id type for ramp step\n");
+ return -EINVAL;
+ }
+
+ if (pwm_cfg->mode == LPG_MODE)
+ pwm_cfg->blinking = true;
+
+ previous_ramp_step_ms = pwm_cfg->lut_params.ramp_step_ms;
+
+ pwm_free(pwm_cfg->pwm_dev);
+ pwm_cfg->lut_params.ramp_step_ms = ramp_step_ms;
+ ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ if (ret) {
+ pwm_cfg->lut_params.ramp_step_ms = previous_ramp_step_ms;
+ pwm_free(pwm_cfg->pwm_dev);
+ qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ dev_err(&led->spmi_dev->dev,
+ "Failed to initialize pwm with new ramp step value\n");
+ return ret;
+ }
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ return count;
+}
+
+static ssize_t lut_flags_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct qpnp_led_data *led;
+ u32 lut_flags;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ ssize_t ret;
+ u32 previous_lut_flags;
+ struct pwm_config_data *pwm_cfg;
+
+ ret = kstrtou32(buf, 10, &lut_flags);
+ if (ret)
+ return ret;
+ led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+ switch (led->id) {
+ case QPNP_ID_LED_MPP:
+ pwm_cfg = led->mpp_cfg->pwm_cfg;
+ break;
+ case QPNP_ID_RGB_RED:
+ case QPNP_ID_RGB_GREEN:
+ case QPNP_ID_RGB_BLUE:
+ pwm_cfg = led->rgb_cfg->pwm_cfg;
+ break;
+ default:
+ dev_err(&led->spmi_dev->dev,
+ "Invalid LED id type for lut flags\n");
+ return -EINVAL;
+ }
+
+ if (pwm_cfg->mode == LPG_MODE)
+ pwm_cfg->blinking = true;
+
+ previous_lut_flags = pwm_cfg->lut_params.flags;
+
+ pwm_free(pwm_cfg->pwm_dev);
+ pwm_cfg->lut_params.flags = lut_flags;
+ ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ if (ret) {
+ pwm_cfg->lut_params.flags = previous_lut_flags;
+ pwm_free(pwm_cfg->pwm_dev);
+ qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ dev_err(&led->spmi_dev->dev,
+ "Failed to initialize pwm with new lut flags value\n");
+ return ret;
+ }
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ return count;
+}
+
+static ssize_t duty_pcts_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct qpnp_led_data *led;
+ int num_duty_pcts = 0;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ char *buffer;
+ ssize_t ret;
+ int i = 0;
+ int max_duty_pcts;
+ struct pwm_config_data *pwm_cfg;
+ u32 previous_num_duty_pcts;
+ int value;
+ int *previous_duty_pcts;
+
+ led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+ switch (led->id) {
+ case QPNP_ID_LED_MPP:
+ pwm_cfg = led->mpp_cfg->pwm_cfg;
+ max_duty_pcts = PWM_LUT_MAX_SIZE;
+ break;
+ case QPNP_ID_RGB_RED:
+ case QPNP_ID_RGB_GREEN:
+ case QPNP_ID_RGB_BLUE:
+ pwm_cfg = led->rgb_cfg->pwm_cfg;
+ max_duty_pcts = PWM_LUT_MAX_SIZE;
+ break;
+ default:
+ dev_err(&led->spmi_dev->dev,
+ "Invalid LED id type for duty pcts\n");
+ return -EINVAL;
+ }
+
+ if (pwm_cfg->mode == LPG_MODE)
+ pwm_cfg->blinking = true;
+
+ buffer = (char *)buf;
+
+ for (i = 0; i < max_duty_pcts; i++) {
+ if (buffer == NULL)
+ break;
+ ret = sscanf((const char *)buffer, "%u,%s", &value, buffer);
+ pwm_cfg->old_duty_pcts[i] = value;
+ num_duty_pcts++;
+ if (ret <= 1)
+ break;
+ }
+
+ if (num_duty_pcts >= max_duty_pcts) {
+ dev_err(&led->spmi_dev->dev,
+ "Number of duty pcts given exceeds max (%d)\n",
+ max_duty_pcts);
+ return -EINVAL;
+ }
+
+ previous_num_duty_pcts = pwm_cfg->duty_cycles->num_duty_pcts;
+ previous_duty_pcts = pwm_cfg->duty_cycles->duty_pcts;
+
+ pwm_cfg->duty_cycles->num_duty_pcts = num_duty_pcts;
+ pwm_cfg->duty_cycles->duty_pcts = pwm_cfg->old_duty_pcts;
+ pwm_cfg->old_duty_pcts = previous_duty_pcts;
+ pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts;
+
+ pwm_free(pwm_cfg->pwm_dev);
+ ret = qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ if (ret)
+ goto restore;
+
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ return count;
+
+restore:
+ dev_err(&led->spmi_dev->dev,
+ "Failed to initialize pwm with new duty pcts value\n");
+ pwm_cfg->duty_cycles->num_duty_pcts = previous_num_duty_pcts;
+ pwm_cfg->old_duty_pcts = pwm_cfg->duty_cycles->duty_pcts;
+ pwm_cfg->duty_cycles->duty_pcts = previous_duty_pcts;
+ pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts;
+ pwm_free(pwm_cfg->pwm_dev);
+ qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
+ return ret;
+}
+
static void led_blink(struct qpnp_led_data *led,
struct pwm_config_data *pwm_cfg)
{
- u8 previous_mode;
-
- previous_mode = pwm_cfg->mode;
if (pwm_cfg->use_blink) {
if (led->cdev.brightness) {
+ pwm_cfg->blinking = true;
if (led->id == QPNP_ID_LED_MPP)
led->mpp_cfg->pwm_mode = LPG_MODE;
pwm_cfg->mode = LPG_MODE;
- pwm_free(pwm_cfg->pwm_dev);
- qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
- qpnp_led_set(&led->cdev, led->cdev.brightness);
- if (led->id == QPNP_ID_LED_MPP)
- led->mpp_cfg->pwm_mode = previous_mode;
- pwm_cfg->mode = previous_mode;
} else {
- pwm_free(pwm_cfg->pwm_dev);
- qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
- qpnp_led_set(&led->cdev, led->cdev.brightness);
+ pwm_cfg->blinking = false;
+ pwm_cfg->mode = pwm_cfg->default_mode;
+ if (led->id == QPNP_ID_LED_MPP)
+ led->mpp_cfg->pwm_mode = pwm_cfg->default_mode;
}
+ pwm_free(pwm_cfg->pwm_dev);
+ qpnp_pwm_init(pwm_cfg, led->spmi_dev, led->cdev.name);
+ qpnp_led_set(&led->cdev, led->cdev.brightness);
}
}
@@ -1399,6 +1853,13 @@
static DEVICE_ATTR(led_mode, 0664, NULL, led_mode_store);
static DEVICE_ATTR(strobe, 0664, NULL, led_strobe_type_store);
+static DEVICE_ATTR(pwm_us, 0664, NULL, pwm_us_store);
+static DEVICE_ATTR(pause_lo, 0664, NULL, pause_lo_store);
+static DEVICE_ATTR(pause_hi, 0664, NULL, pause_hi_store);
+static DEVICE_ATTR(start_idx, 0664, NULL, start_idx_store);
+static DEVICE_ATTR(ramp_step_ms, 0664, NULL, ramp_step_ms_store);
+static DEVICE_ATTR(lut_flags, 0664, NULL, lut_flags_store);
+static DEVICE_ATTR(duty_pcts, 0664, NULL, duty_pcts_store);
static DEVICE_ATTR(blink, 0664, NULL, blink_store);
static struct attribute *led_attrs[] = {
@@ -1411,11 +1872,34 @@
.attrs = led_attrs,
};
+static struct attribute *pwm_attrs[] = {
+ &dev_attr_pwm_us.attr,
+ NULL
+};
+
+static struct attribute *lpg_attrs[] = {
+ &dev_attr_pause_lo.attr,
+ &dev_attr_pause_hi.attr,
+ &dev_attr_start_idx.attr,
+ &dev_attr_ramp_step_ms.attr,
+ &dev_attr_lut_flags.attr,
+ &dev_attr_duty_pcts.attr,
+ NULL
+};
+
static struct attribute *blink_attrs[] = {
&dev_attr_blink.attr,
NULL
};
+static const struct attribute_group pwm_attr_group = {
+ .attrs = pwm_attrs,
+};
+
+static const struct attribute_group lpg_attr_group = {
+ .attrs = lpg_attrs,
+};
+
static const struct attribute_group blink_attr_group = {
.attrs = blink_attrs,
};
@@ -1840,11 +2324,18 @@
return -EINVAL;
}
+ led->flash_cfg->torch_enable =
+ of_property_read_bool(node, "qcom,torch-enable");
+
rc = of_property_read_u32(node, "qcom,current", &val);
- if (!rc)
- led->flash_cfg->current_prgm = (val *
+ if (!rc) {
+ if (led->flash_cfg->torch_enable)
+ led->flash_cfg->current_prgm = (val *
+ TORCH_MAX_LEVEL / led->max_current);
+ else
+ led->flash_cfg->current_prgm = (val *
FLASH_MAX_LEVEL / led->max_current);
- else
+ } else
return -EINVAL;
rc = of_property_read_u32(node, "qcom,headroom", &val);
@@ -1883,9 +2374,6 @@
led->flash_cfg->safety_timer =
of_property_read_bool(node, "qcom,safety-timer");
- led->flash_cfg->torch_enable =
- of_property_read_bool(node, "qcom,torch-enable");
-
return 0;
}
@@ -1942,12 +2430,23 @@
pwm_cfg->duty_cycles->duty_pcts =
devm_kzalloc(&spmi_dev->dev,
- sizeof(int) * pwm_cfg->duty_cycles->num_duty_pcts,
+ sizeof(int) * PWM_LUT_MAX_SIZE,
GFP_KERNEL);
if (!pwm_cfg->duty_cycles->duty_pcts) {
dev_err(&spmi_dev->dev,
"Unable to allocate memory\n");
- rc = -ENOMEM;
+ rc = -ENOMEM;
+ goto bad_lpg_params;
+ }
+
+ pwm_cfg->old_duty_pcts =
+ devm_kzalloc(&spmi_dev->dev,
+ sizeof(int) * PWM_LUT_MAX_SIZE,
+ GFP_KERNEL);
+ if (!pwm_cfg->old_duty_pcts) {
+ dev_err(&spmi_dev->dev,
+ "Unable to allocate memory\n");
+ rc = -ENOMEM;
goto bad_lpg_params;
}
@@ -2062,6 +2561,7 @@
return -ENOMEM;
}
led->kpdbl_cfg->pwm_cfg->mode = led_mode;
+ led->kpdbl_cfg->pwm_cfg->default_mode = led_mode;
} else
return rc;
@@ -2130,6 +2630,7 @@
return -ENOMEM;
}
led->rgb_cfg->pwm_cfg->mode = led_mode;
+ led->rgb_cfg->pwm_cfg->default_mode = led_mode;
} else
return rc;
@@ -2196,6 +2697,7 @@
return -ENOMEM;
}
led->mpp_cfg->pwm_cfg->mode = led_mode;
+ led->mpp_cfg->pwm_cfg->default_mode = led_mode;
} else
return rc;
@@ -2331,7 +2833,8 @@
goto fail_id_check;
}
- spin_lock_init(&led->lock);
+ mutex_init(&led->lock);
+ INIT_WORK(&led->work, qpnp_led_work);
rc = qpnp_led_initialize(led);
if (rc < 0)
@@ -2360,27 +2863,60 @@
if (led->id == QPNP_ID_LED_MPP) {
if (!led->mpp_cfg->pwm_cfg)
break;
+ if (led->mpp_cfg->pwm_cfg->mode == PWM_MODE) {
+ rc = sysfs_create_group(&led->cdev.dev->kobj,
+ &pwm_attr_group);
+ if (rc)
+ goto fail_id_check;
+ }
if (led->mpp_cfg->pwm_cfg->use_blink) {
rc = sysfs_create_group(&led->cdev.dev->kobj,
&blink_attr_group);
if (rc)
goto fail_id_check;
+
+ rc = sysfs_create_group(&led->cdev.dev->kobj,
+ &lpg_attr_group);
+ if (rc)
+ goto fail_id_check;
+ } else if (led->mpp_cfg->pwm_cfg->mode == LPG_MODE) {
+ rc = sysfs_create_group(&led->cdev.dev->kobj,
+ &lpg_attr_group);
+ if (rc)
+ goto fail_id_check;
}
} else if ((led->id == QPNP_ID_RGB_RED) ||
(led->id == QPNP_ID_RGB_GREEN) ||
(led->id == QPNP_ID_RGB_BLUE)) {
+ if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
+ rc = sysfs_create_group(&led->cdev.dev->kobj,
+ &pwm_attr_group);
+ if (rc)
+ goto fail_id_check;
+ }
if (led->rgb_cfg->pwm_cfg->use_blink) {
rc = sysfs_create_group(&led->cdev.dev->kobj,
&blink_attr_group);
if (rc)
goto fail_id_check;
+
+ rc = sysfs_create_group(&led->cdev.dev->kobj,
+ &lpg_attr_group);
+ if (rc)
+ goto fail_id_check;
+ } else if (led->rgb_cfg->pwm_cfg->mode == LPG_MODE) {
+ rc = sysfs_create_group(&led->cdev.dev->kobj,
+ &lpg_attr_group);
+ if (rc)
+ goto fail_id_check;
}
}
/* configure default state */
if (led->default_on) {
led->cdev.brightness = led->cdev.max_brightness;
- qpnp_led_set(&led->cdev, led->cdev.brightness);
+ __qpnp_led_work(led, led->cdev.brightness);
+ schedule_work(&led->work);
if (led->turn_off_delay_ms > 0)
qpnp_led_turn_off(led);
} else
@@ -2392,8 +2928,11 @@
return 0;
fail_id_check:
- for (i = 0; i < parsed_leds; i++)
+ for (i = 0; i < parsed_leds; i++) {
+ mutex_destroy(&led_array[i].lock);
led_classdev_unregister(&led_array[i].cdev);
+ }
+
return rc;
}
@@ -2403,6 +2942,8 @@
int i, parsed_leds = led_array->num_leds;
for (i = 0; i < parsed_leds; i++) {
+ cancel_work_sync(&led_array[i].work);
+ mutex_destroy(&led_array[i].lock);
led_classdev_unregister(&led_array[i].cdev);
switch (led_array[i].id) {
case QPNP_ID_WLED:
@@ -2418,6 +2959,35 @@
case QPNP_ID_RGB_RED:
case QPNP_ID_RGB_GREEN:
case QPNP_ID_RGB_BLUE:
+ if (led_array[i].rgb_cfg->pwm_cfg->mode == PWM_MODE)
+ sysfs_remove_group(&led_array[i].cdev.dev->\
+ kobj, &pwm_attr_group);
+ if (led_array[i].rgb_cfg->pwm_cfg->use_blink) {
+ sysfs_remove_group(&led_array[i].cdev.dev->\
+ kobj, &blink_attr_group);
+ sysfs_remove_group(&led_array[i].cdev.dev->\
+ kobj, &lpg_attr_group);
+ } else if (led_array[i].rgb_cfg->pwm_cfg->mode\
+ == LPG_MODE)
+ sysfs_remove_group(&led_array[i].cdev.dev->\
+ kobj, &lpg_attr_group);
+ break;
+ case QPNP_ID_LED_MPP:
+ if (!led_array[i].mpp_cfg->pwm_cfg)
+ break;
+ if (led_array[i].mpp_cfg->pwm_cfg->mode == PWM_MODE)
+ sysfs_remove_group(&led_array[i].cdev.dev->\
+ kobj, &pwm_attr_group);
+ if (led_array[i].mpp_cfg->pwm_cfg->use_blink) {
+ sysfs_remove_group(&led_array[i].cdev.dev->\
+ kobj, &blink_attr_group);
+ sysfs_remove_group(&led_array[i].cdev.dev->\
+ kobj, &lpg_attr_group);
+ } else if (led_array[i].mpp_cfg->pwm_cfg->mode\
+ == LPG_MODE)
+ sysfs_remove_group(&led_array[i].cdev.dev->\
+ kobj, &lpg_attr_group);
+ break;
default:
dev_err(&led_array[i].spmi_dev->dev,
"Invalid LED(%d)\n",
diff --git a/drivers/media/platform/msm/camera_v2/Kconfig b/drivers/media/platform/msm/camera_v2/Kconfig
index 3a30970..ec2e381 100644
--- a/drivers/media/platform/msm/camera_v2/Kconfig
+++ b/drivers/media/platform/msm/camera_v2/Kconfig
@@ -164,6 +164,15 @@
1280 * 270. It does not support auto focus. It supports
few special effects like mono.
+config GC0339
+ bool "Sensor GC0339 (BAYER .3M)"
+ depends on MSMB_CAMERA
+ ---help---
+ gc0339 is a Galaxycore .3 MP Bayer Sensor.
+ It supports 1 or 2 mipi lanes.
+ Preview and snapshot resolution shall be 640*480 at 30 fps,
+ It does not support auto focus.
+
config OV8825
bool "OmniVision OV8825 (BAYER 8MP)"
depends on MSMB_CAMERA
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
index 2ff70d3..cb8d821 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
@@ -34,6 +34,7 @@
#define VFE40_8974V2_VERSION 0x1001001A
#define VFE40_8974V3_VERSION 0x1001001B
#define VFE40_8x26_VERSION 0x20000013
+#define VFE40_8x26V2_VERSION 0x20010014
#define VFE40_BURST_LEN 3
#define VFE40_STATS_BURST_LEN 2
@@ -93,8 +94,10 @@
static void msm_vfe40_init_qos_parms(struct vfe_device *vfe_dev)
{
void __iomem *vfebase = vfe_dev->vfe_base;
+
if (vfe_dev->vfe_hw_version == VFE40_8974V1_VERSION ||
- vfe_dev->vfe_hw_version == VFE40_8x26_VERSION) {
+ vfe_dev->vfe_hw_version == VFE40_8x26_VERSION ||
+ vfe_dev->vfe_hw_version == VFE40_8x26V2_VERSION) {
msm_camera_io_w(0xAAAAAAAA, vfebase + VFE40_BUS_BDG_QOS_CFG_0);
msm_camera_io_w(0xAAAAAAAA, vfebase + VFE40_BUS_BDG_QOS_CFG_1);
msm_camera_io_w(0xAAAAAAAA, vfebase + VFE40_BUS_BDG_QOS_CFG_2);
@@ -237,6 +240,7 @@
msm_vfe40_init_vbif_parms_8974_v2(vfe_dev);
break;
case VFE40_8x26_VERSION:
+ case VFE40_8x26V2_VERSION:
msm_vfe40_init_vbif_parms_8x26(vfe_dev);
break;
default:
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
index df72328..d238649 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
@@ -24,6 +24,9 @@
#include <linux/proc_fs.h>
#include <linux/msm_ion.h>
#include <linux/iommu.h>
+#include <linux/timer.h>
+#include <linux/kernel.h>
+#include <linux/workqueue.h>
#include <mach/iommu_domains.h>
#include <mach/iommu.h>
#include <mach/vreg.h>
@@ -36,6 +39,7 @@
#include "msm_cpp.h"
#include "msm_isp_util.h"
#include "msm_camera_io_util.h"
+#include <linux/debugfs.h>
#define MSM_CPP_DRV_NAME "msm_cpp"
@@ -43,6 +47,23 @@
#define CONFIG_MSM_CPP_DBG 0
+#define CPP_CMD_TIMEOUT_MS 300
+
+struct msm_cpp_timer_data_t {
+ struct cpp_device *cpp_dev;
+ struct msm_cpp_frame_info_t *processed_frame;
+};
+
+struct msm_cpp_timer_t {
+ uint8_t used;
+ struct msm_cpp_timer_data_t data;
+ struct timer_list cpp_timer;
+};
+
+struct msm_cpp_timer_t cpp_timers[2];
+static int del_timer_idx;
+static int set_timer_idx;
+
/* dump the frame command before writing to the hardware */
#define MSM_CPP_DUMP_FRM_CMD 0
@@ -109,6 +130,11 @@
{"micro_iface_clk", -1},
};
static int msm_cpp_notify_frame_done(struct cpp_device *cpp_dev);
+static void cpp_load_fw(struct cpp_device *cpp_dev, char *fw_name_bin);
+static void cpp_timer_callback(unsigned long data);
+
+uint8_t induce_error;
+static int msm_cpp_enable_debugfs(struct cpp_device *cpp_dev);
static void msm_cpp_write(u32 data, void __iomem *cpp_base)
{
@@ -472,7 +498,6 @@
tx_fifo[i] = msm_camera_io_r(cpp_dev->base +
MSM_CPP_MICRO_FIFO_TX_DATA);
}
-
spin_lock_irqsave(&cpp_dev->tasklet_lock, flags);
queue_cmd = &cpp_dev->tasklet_queue_cmd[cpp_dev->taskletq_idx];
if (queue_cmd->cmd_used) {
@@ -494,6 +519,36 @@
spin_unlock_irqrestore(&cpp_dev->tasklet_lock, flags);
tasklet_schedule(&cpp_dev->cpp_tasklet);
+ } else if (irq_status & 0x7C0) {
+ pr_err("%s: fatal error: 0x%x\n", __func__, irq_status);
+ pr_err("%s: DEBUG_SP: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x40));
+ pr_err("%s: DEBUG_T: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x44));
+ pr_err("%s: DEBUG_N: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x48));
+ pr_err("%s: DEBUG_R: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x4C));
+ pr_err("%s: DEBUG_OPPC: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x50));
+ pr_err("%s: DEBUG_MO: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x54));
+ pr_err("%s: DEBUG_TIMER0: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x60));
+ pr_err("%s: DEBUG_TIMER1: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x64));
+ pr_err("%s: DEBUG_GPI: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x70));
+ pr_err("%s: DEBUG_GPO: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x74));
+ pr_err("%s: DEBUG_T0: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x80));
+ pr_err("%s: DEBUG_R0: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x84));
+ pr_err("%s: DEBUG_T1: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x88));
+ pr_err("%s: DEBUG_R1: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x8C));
}
msm_camera_io_w(irq_status, cpp_dev->base + MSM_CPP_MICRO_IRQGEN_CLR);
return IRQ_HANDLED;
@@ -509,6 +564,7 @@
uint32_t tx_fifo[MSM_CPP_TX_FIFO_LEVEL];
struct cpp_device *cpp_dev = (struct cpp_device *) data;
struct msm_cpp_tasklet_queue_cmd *queue_cmd;
+ struct msm_cpp_timer_t *timer = NULL;
while (atomic_read(&cpp_dev->irq_cnt)) {
spin_lock_irqsave(&cpp_dev->tasklet_lock, flags);
@@ -535,6 +591,25 @@
msg_id = tx_fifo[i+2];
if (msg_id == MSM_CPP_MSG_ID_FRAME_ACK) {
CPP_DBG("Frame done!!\n");
+ /* delete CPP timer */
+ CPP_DBG("delete timer %d.\n",
+ del_timer_idx);
+ timer = &cpp_timers[del_timer_idx];
+ del_timer(&timer->cpp_timer);
+ timer->used = 0;
+ timer->data.processed_frame = NULL;
+ del_timer_idx = 1 - del_timer_idx;
+ msm_cpp_notify_frame_done(cpp_dev);
+ } else if (msg_id ==
+ MSM_CPP_MSG_ID_FRAME_NACK) {
+ pr_err("NACK error from hw!!\n");
+ CPP_DBG("delete timer %d.\n",
+ del_timer_idx);
+ timer = &cpp_timers[del_timer_idx];
+ del_timer(&timer->cpp_timer);
+ timer->used = 0;
+ timer->data.processed_frame = NULL;
+ del_timer_idx = 1 - del_timer_idx;
msm_cpp_notify_frame_done(cpp_dev);
}
i += cmd_len + 2;
@@ -543,48 +618,6 @@
}
}
-static void msm_cpp_boot_hw(struct cpp_device *cpp_dev)
-{
- disable_irq(cpp_dev->irq->start);
-
- msm_camera_io_w(0x1, cpp_dev->base + MSM_CPP_MICRO_CLKEN_CTL);
- msm_camera_io_w(0x1, cpp_dev->base +
- MSM_CPP_MICRO_BOOT_START);
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
-
- /*Trigger MC to jump to start address*/
- msm_cpp_write(MSM_CPP_CMD_EXEC_JUMP, cpp_dev->base);
- msm_cpp_write(MSM_CPP_JUMP_ADDRESS, cpp_dev->base);
-
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
- msm_cpp_poll(cpp_dev->base, 0x1);
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_JUMP_ACK);
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_TRAILER);
-
- /*Get Bootloader Version*/
- msm_cpp_write(MSM_CPP_CMD_GET_BOOTLOADER_VER, cpp_dev->base);
- pr_info("MC Bootloader Version: 0x%x\n",
- msm_cpp_read(cpp_dev->base));
-
- /*Get Firmware Version*/
- msm_cpp_write(MSM_CPP_CMD_GET_FW_VER, cpp_dev->base);
- msm_cpp_write(MSM_CPP_MSG_ID_CMD, cpp_dev->base);
- msm_cpp_write(0x1, cpp_dev->base);
- msm_cpp_write(MSM_CPP_CMD_GET_FW_VER, cpp_dev->base);
- msm_cpp_write(MSM_CPP_MSG_ID_TRAILER, cpp_dev->base);
-
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
- msm_cpp_poll(cpp_dev->base, 0x2);
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_FW_VER);
- pr_info("CPP FW Version: 0x%x\n", msm_cpp_read(cpp_dev->base));
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_TRAILER);
- enable_irq(cpp_dev->irq->start);
- msm_camera_io_w_mb(0x8, cpp_dev->base +
- MSM_CPP_MICRO_IRQGEN_MASK);
- msm_camera_io_w_mb(0xFFFF, cpp_dev->base +
- MSM_CPP_MICRO_IRQGEN_CLR);
-}
-
static int cpp_init_hardware(struct cpp_device *cpp_dev)
{
int rc = 0;
@@ -663,8 +696,15 @@
cpp_dev->taskletq_idx = 0;
atomic_set(&cpp_dev->irq_cnt, 0);
msm_cpp_create_buff_queue(cpp_dev, MSM_CPP_MAX_BUFF_QUEUE);
- if (cpp_dev->is_firmware_loaded == 1)
- msm_cpp_boot_hw(cpp_dev);
+ if (cpp_dev->is_firmware_loaded == 1) {
+ disable_irq(cpp_dev->irq->start);
+ cpp_load_fw(cpp_dev, NULL);
+ enable_irq(cpp_dev->irq->start);
+ msm_camera_io_w_mb(0x7C8, cpp_dev->base +
+ MSM_CPP_MICRO_IRQGEN_MASK);
+ msm_camera_io_w_mb(0xFFFF, cpp_dev->base +
+ MSM_CPP_MICRO_IRQGEN_CLR);
+ }
return rc;
req_irq_fail:
iounmap(cpp_dev->cpp_hw_base);
@@ -713,47 +753,44 @@
const struct firmware *fw = NULL;
struct device *dev = &cpp_dev->pdev->dev;
- pr_debug("%s: FW file: %s\n", __func__, fw_name_bin);
- rc = request_firmware(&fw, fw_name_bin, dev);
- if (rc) {
- dev_err(dev, "Failed to locate blob %s from device %p, Error: %d\n",
- fw_name_bin, dev, rc);
- }
-
- CPP_DBG("HW Ver:0x%x\n",
- msm_camera_io_r(cpp_dev->base +
- MSM_CPP_MICRO_HW_VERSION));
-
+ msm_camera_io_w(0x1, cpp_dev->base + MSM_CPP_MICRO_CLKEN_CTL);
msm_camera_io_w(0x1, cpp_dev->base +
- MSM_CPP_MICRO_BOOT_START);
- /*Enable MC clock*/
- msm_camera_io_w(0x1, cpp_dev->base +
- MSM_CPP_MICRO_CLKEN_CTL);
-
+ MSM_CPP_MICRO_BOOT_START);
msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
- /*Start firmware loading*/
- msm_cpp_write(MSM_CPP_CMD_FW_LOAD, cpp_dev->base);
- msm_cpp_write(MSM_CPP_END_ADDRESS, cpp_dev->base);
- msm_cpp_write(MSM_CPP_START_ADDRESS, cpp_dev->base);
- if (NULL != fw)
- ptr_bin = (uint32_t *)fw->data;
+ if (fw_name_bin) {
+ pr_debug("%s: FW file: %s\n", __func__, fw_name_bin);
+ rc = request_firmware(&fw, fw_name_bin, dev);
+ if (rc) {
+ dev_err(dev,
+ "Fail to loc blob %s from dev %p, Error: %d\n",
+ fw_name_bin, dev, rc);
+ }
+ if (NULL != fw)
+ ptr_bin = (uint32_t *)fw->data;
- if (ptr_bin == NULL) {
- pr_err("ptr_bin is NULL\n");
- } else {
- for (i = 0; i < fw->size/4; i++) {
- if (ptr_bin) {
+ msm_camera_io_w(0x1, cpp_dev->base +
+ MSM_CPP_MICRO_BOOT_START);
+ msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
+ msm_camera_io_w(0xFFFFFFFF, cpp_dev->base +
+ MSM_CPP_MICRO_IRQGEN_CLR);
+
+ /*Start firmware loading*/
+ msm_cpp_write(MSM_CPP_CMD_FW_LOAD, cpp_dev->base);
+ msm_cpp_write(MSM_CPP_END_ADDRESS, cpp_dev->base);
+ msm_cpp_write(MSM_CPP_START_ADDRESS, cpp_dev->base);
+
+ if (ptr_bin) {
+ for (i = 0; i < fw->size/4; i++) {
msm_cpp_write(*ptr_bin, cpp_dev->base);
ptr_bin++;
}
}
+ if (fw)
+ release_firmware(fw);
+ msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_OK);
+ msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
}
- if (fw)
- release_firmware(fw);
-
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_OK);
- msm_cpp_poll(cpp_dev->base, MSM_CPP_MSG_ID_CMD);
/*Trigger MC to jump to start address*/
msm_cpp_write(MSM_CPP_CMD_EXEC_JUMP, cpp_dev->base);
@@ -817,7 +854,6 @@
cpp_dev->cpp_open_cnt++;
if (cpp_dev->cpp_open_cnt == 1) {
cpp_init_hardware(cpp_dev);
- iommu_attach_device(cpp_dev->domain, cpp_dev->iommu_ctx);
cpp_init_mem(cpp_dev);
cpp_dev->state = CPP_STATE_IDLE;
}
@@ -852,9 +888,38 @@
cpp_dev->cpp_open_cnt--;
if (cpp_dev->cpp_open_cnt == 0) {
+ pr_err("%s: irq_status: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x4));
+ pr_err("%s: DEBUG_SP: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x40));
+ pr_err("%s: DEBUG_T: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x44));
+ pr_err("%s: DEBUG_N: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x48));
+ pr_err("%s: DEBUG_R: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x4C));
+ pr_err("%s: DEBUG_OPPC: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x50));
+ pr_err("%s: DEBUG_MO: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x54));
+ pr_err("%s: DEBUG_TIMER0: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x60));
+ pr_err("%s: DEBUG_TIMER1: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x64));
+ pr_err("%s: DEBUG_GPI: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x70));
+ pr_err("%s: DEBUG_GPO: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x74));
+ pr_err("%s: DEBUG_T0: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x80));
+ pr_err("%s: DEBUG_R0: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x84));
+ pr_err("%s: DEBUG_T1: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x88));
+ pr_err("%s: DEBUG_R1: 0x%x\n", __func__,
+ msm_camera_io_r(cpp_dev->cpp_hw_base + 0x8C));
msm_camera_io_w(0x0, cpp_dev->base + MSM_CPP_MICRO_CLKEN_CTL);
cpp_deinit_mem(cpp_dev);
- iommu_detach_device(cpp_dev->domain, cpp_dev->iommu_ctx);
cpp_release_hardware(cpp_dev);
cpp_dev->state = CPP_STATE_OFF;
}
@@ -969,23 +1034,140 @@
}
#endif
+void msm_cpp_do_timeout_work(struct work_struct *work)
+{
+ int ret;
+ uint32_t i = 0;
+ struct msm_cpp_frame_info_t *this_frame =
+ cpp_timers[del_timer_idx].data.processed_frame;
+ struct msm_cpp_frame_info_t *second_frame = NULL;
+
+ pr_err("cpp_timer_callback called idx:%d. (jiffies=%lu)\n",
+ del_timer_idx, jiffies);
+ pr_err("fatal: cpp_timer expired for identity=0x%x, frame_id=%03d",
+ this_frame->identity, this_frame->frame_id);
+ cpp_timers[del_timer_idx].used = 0;
+ cpp_timers[del_timer_idx].data.processed_frame = NULL;
+ del_timer_idx = 1 - del_timer_idx;
+
+ if (cpp_timers[del_timer_idx].used == 1) {
+ pr_err("deleting cpp_timer %d.\n", del_timer_idx);
+ del_timer(&cpp_timers[del_timer_idx].cpp_timer);
+ cpp_timers[del_timer_idx].used = 0;
+ second_frame = cpp_timers[del_timer_idx].data.processed_frame;
+ cpp_timers[del_timer_idx].data.processed_frame = NULL;
+ del_timer_idx = 1 - del_timer_idx;
+ }
+
+ disable_irq(cpp_timers[del_timer_idx].data.cpp_dev->irq->start);
+ pr_err("Reloading firmware\n");
+ cpp_load_fw(cpp_timers[del_timer_idx].data.cpp_dev, NULL);
+ pr_err("Firmware loading done\n");
+ enable_irq(cpp_timers[del_timer_idx].data.cpp_dev->irq->start);
+ msm_camera_io_w_mb(0x8, cpp_timers[del_timer_idx].data.cpp_dev->base +
+ MSM_CPP_MICRO_IRQGEN_MASK);
+ msm_camera_io_w_mb(0xFFFF,
+ cpp_timers[del_timer_idx].data.cpp_dev->base +
+ MSM_CPP_MICRO_IRQGEN_CLR);
+
+ cpp_timers[set_timer_idx].data.processed_frame = this_frame;
+ cpp_timers[set_timer_idx].used = 1;
+ pr_err("ReInstalling cpp_timer %d\n", set_timer_idx);
+ setup_timer(&cpp_timers[set_timer_idx].cpp_timer, cpp_timer_callback,
+ (unsigned long)&cpp_timers[0]);
+ pr_err("Starting timer to fire in %d ms. (jiffies=%lu)\n",
+ CPP_CMD_TIMEOUT_MS, jiffies);
+ ret = mod_timer(&cpp_timers[set_timer_idx].cpp_timer,
+ jiffies + msecs_to_jiffies(CPP_CMD_TIMEOUT_MS));
+ if (ret)
+ pr_err("error in mod_timer\n");
+
+ set_timer_idx = 1 - set_timer_idx;
+ pr_err("Rescheduling for identity=0x%x, frame_id=%03d",
+ this_frame->identity, this_frame->frame_id);
+ msm_cpp_write(0x6, cpp_timers[set_timer_idx].data.cpp_dev->base);
+ msm_cpp_dump_frame_cmd(this_frame->cpp_cmd_msg,
+ this_frame->msg_len);
+ for (i = 0; i < this_frame->msg_len; i++)
+ msm_cpp_write(this_frame->cpp_cmd_msg[i],
+ cpp_timers[set_timer_idx].data.cpp_dev->base);
+
+
+ if (second_frame != NULL) {
+ cpp_timers[set_timer_idx].data.processed_frame = second_frame;
+ cpp_timers[set_timer_idx].used = 1;
+ pr_err("ReInstalling cpp_timer %d\n", set_timer_idx);
+ setup_timer(&cpp_timers[set_timer_idx].cpp_timer,
+ cpp_timer_callback, (unsigned long)&cpp_timers[0]);
+ pr_err("Starting timer to fire in %d ms. (jiffies=%lu)\n",
+ CPP_CMD_TIMEOUT_MS, jiffies);
+ ret = mod_timer(&cpp_timers[set_timer_idx].cpp_timer,
+ jiffies + msecs_to_jiffies(CPP_CMD_TIMEOUT_MS));
+ if (ret)
+ pr_err("error in mod_timer\n");
+
+ set_timer_idx = 1 - set_timer_idx;
+ pr_err("Rescheduling for identity=0x%x, frame_id=%03d",
+ second_frame->identity, second_frame->frame_id);
+ msm_cpp_write(0x6,
+ cpp_timers[set_timer_idx].data.cpp_dev->base);
+ msm_cpp_dump_frame_cmd(second_frame->cpp_cmd_msg,
+ second_frame->msg_len);
+ for (i = 0; i < second_frame->msg_len; i++)
+ msm_cpp_write(second_frame->cpp_cmd_msg[i],
+ cpp_timers[set_timer_idx].data.cpp_dev->base);
+ }
+}
+
+void cpp_timer_callback(unsigned long data)
+{
+ struct msm_cpp_work_t *work =
+ cpp_timers[set_timer_idx].data.cpp_dev->work;
+ queue_work(cpp_timers[set_timer_idx].data.cpp_dev->timer_wq,
+ (struct work_struct *)work);
+}
+
static int msm_cpp_send_frame_to_hardware(struct cpp_device *cpp_dev,
struct msm_queue_cmd *frame_qcmd)
{
uint32_t i;
int32_t rc = -EAGAIN;
+ int ret;
struct msm_cpp_frame_info_t *process_frame;
if (cpp_dev->processing_q.len < MAX_CPP_PROCESSING_FRAME) {
process_frame = frame_qcmd->command;
msm_enqueue(&cpp_dev->processing_q,
&frame_qcmd->list_frame);
+
+ cpp_timers[set_timer_idx].data.processed_frame = process_frame;
+ cpp_timers[set_timer_idx].used = 1;
+ /* install timer for cpp timeout */
+ CPP_DBG("Installing cpp_timer %d\n", set_timer_idx);
+ setup_timer(&cpp_timers[set_timer_idx].cpp_timer,
+ cpp_timer_callback, (unsigned long)&cpp_timers[0]);
+ CPP_DBG("Starting timer to fire in %d ms. (jiffies=%lu)\n",
+ CPP_CMD_TIMEOUT_MS, jiffies);
+ ret = mod_timer(&cpp_timers[set_timer_idx].cpp_timer,
+ jiffies + msecs_to_jiffies(CPP_CMD_TIMEOUT_MS));
+ if (ret)
+ pr_err("error in mod_timer\n");
+
+ set_timer_idx = 1 - set_timer_idx;
+
msm_cpp_write(0x6, cpp_dev->base);
msm_cpp_dump_frame_cmd(process_frame->cpp_cmd_msg,
process_frame->msg_len);
- for (i = 0; i < process_frame->msg_len; i++)
- msm_cpp_write(process_frame->cpp_cmd_msg[i],
- cpp_dev->base);
+ for (i = 0; i < process_frame->msg_len; i++) {
+ if ((induce_error) && (i == 1)) {
+ pr_err("Induce error\n");
+ msm_cpp_write(process_frame->cpp_cmd_msg[i]-1,
+ cpp_dev->base);
+ induce_error--;
+ } else
+ msm_cpp_write(process_frame->cpp_cmd_msg[i],
+ cpp_dev->base);
+ }
do_gettimeofday(&(process_frame->in_time));
rc = 0;
}
@@ -1097,7 +1279,7 @@
&buff_mgr_info);
if (rc < 0) {
rc = -EAGAIN;
- pr_err("error getting buffer rc:%d\n", rc);
+ pr_debug("error getting buffer rc:%d\n", rc);
goto ERROR2;
}
new_frame->output_buffer_info[1].index = buff_mgr_info.index;
@@ -1184,7 +1366,6 @@
struct cpp_device *cpp_dev = v4l2_get_subdevdata(sd);
struct msm_camera_v4l2_ioctl_t *ioctl_ptr = arg;
int rc = 0;
- char *fw_name_bin;
if (ioctl_ptr == NULL) {
pr_err("ioctl_ptr is null\n");
@@ -1206,8 +1387,11 @@
case VIDIOC_MSM_CPP_LOAD_FIRMWARE: {
if (cpp_dev->is_firmware_loaded == 0) {
- fw_name_bin = kzalloc(ioctl_ptr->len+1, GFP_KERNEL);
- if (!fw_name_bin) {
+ kfree(cpp_dev->fw_name_bin);
+ cpp_dev->fw_name_bin = NULL;
+ cpp_dev->fw_name_bin = kzalloc(ioctl_ptr->len+1,
+ GFP_KERNEL);
+ if (!cpp_dev->fw_name_bin) {
pr_err("%s:%d: malloc error\n", __func__,
__LINE__);
mutex_unlock(&cpp_dev->mutex);
@@ -1222,24 +1406,24 @@
pr_err("ioctl_ptr->len is 0\n");
return -EINVAL;
}
- rc = (copy_from_user(fw_name_bin,
+ rc = (copy_from_user(cpp_dev->fw_name_bin,
(void __user *)ioctl_ptr->ioctl_ptr,
ioctl_ptr->len) ? -EFAULT : 0);
if (rc) {
ERR_COPY_FROM_USER();
- kfree(fw_name_bin);
+ kfree(cpp_dev->fw_name_bin);
+ cpp_dev->fw_name_bin = NULL;
mutex_unlock(&cpp_dev->mutex);
return -EINVAL;
}
- *(fw_name_bin+ioctl_ptr->len) = '\0';
+ *(cpp_dev->fw_name_bin+ioctl_ptr->len) = '\0';
if (cpp_dev == NULL) {
pr_err("cpp_dev is null\n");
return -EINVAL;
}
disable_irq(cpp_dev->irq->start);
- cpp_load_fw(cpp_dev, fw_name_bin);
- kfree(fw_name_bin);
+ cpp_load_fw(cpp_dev, cpp_dev->fw_name_bin);
enable_irq(cpp_dev->irq->start);
cpp_dev->is_firmware_loaded = 1;
}
@@ -1467,7 +1651,6 @@
return msm_register_domain(&cpp_fw_layout);
}
-
static int __devinit cpp_probe(struct platform_device *pdev)
{
struct cpp_device *cpp_dev;
@@ -1582,23 +1765,35 @@
cpp_dev->msm_sd.sd.entity.revision = cpp_dev->msm_sd.sd.devnode->num;
cpp_dev->state = CPP_STATE_BOOT;
cpp_init_hardware(cpp_dev);
+ iommu_attach_device(cpp_dev->domain, cpp_dev->iommu_ctx);
msm_camera_io_w(0x0, cpp_dev->base +
MSM_CPP_MICRO_IRQGEN_MASK);
msm_camera_io_w(0xFFFF, cpp_dev->base +
MSM_CPP_MICRO_IRQGEN_CLR);
-
+ msm_camera_io_w(0x80000000, cpp_dev->base + 0xF0);
cpp_release_hardware(cpp_dev);
cpp_dev->state = CPP_STATE_OFF;
+ msm_cpp_enable_debugfs(cpp_dev);
msm_queue_init(&cpp_dev->eventData_q, "eventdata");
msm_queue_init(&cpp_dev->processing_q, "frame");
INIT_LIST_HEAD(&cpp_dev->tasklet_q);
tasklet_init(&cpp_dev->cpp_tasklet, msm_cpp_do_tasklet,
(unsigned long)cpp_dev);
+ cpp_dev->timer_wq = create_workqueue("msm_cpp_workqueue");
+ cpp_dev->work = kmalloc(sizeof(struct msm_cpp_work_t),
+ GFP_KERNEL);
+ INIT_WORK((struct work_struct *)cpp_dev->work, msm_cpp_do_timeout_work);
cpp_dev->cpp_open_cnt = 0;
cpp_dev->is_firmware_loaded = 0;
+ cpp_timers[0].data.cpp_dev = cpp_dev;
+ cpp_timers[1].data.cpp_dev = cpp_dev;
+ cpp_timers[0].used = 0;
+ cpp_timers[1].used = 0;
+ cpp_dev->fw_name_bin = NULL;
return rc;
+
ERROR3:
release_mem_region(cpp_dev->mem->start, resource_size(cpp_dev->mem));
ERROR2:
@@ -1628,6 +1823,7 @@
return 0;
}
+ iommu_detach_device(cpp_dev->domain, cpp_dev->iommu_ctx);
msm_sd_unregister(&cpp_dev->msm_sd);
release_mem_region(cpp_dev->mem->start, resource_size(cpp_dev->mem));
release_mem_region(cpp_dev->vbif_mem->start,
@@ -1635,6 +1831,8 @@
release_mem_region(cpp_dev->cpp_hw_mem->start,
resource_size(cpp_dev->cpp_hw_mem));
mutex_destroy(&cpp_dev->mutex);
+ kfree(cpp_dev->work);
+ destroy_workqueue(cpp_dev->timer_wq);
kfree(cpp_dev->cpp_clk);
kfree(cpp_dev);
return 0;
@@ -1660,6 +1858,30 @@
platform_driver_unregister(&cpp_driver);
}
+static int msm_cpp_debugfs_error_s(void *data, u64 val)
+{
+ pr_err("setting error inducement");
+ induce_error = val;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(cpp_debugfs_error, NULL,
+ msm_cpp_debugfs_error_s, "%llu\n");
+
+static int msm_cpp_enable_debugfs(struct cpp_device *cpp_dev)
+{
+ struct dentry *debugfs_base;
+ debugfs_base = debugfs_create_dir("msm_cpp", NULL);
+ if (!debugfs_base)
+ return -ENOMEM;
+
+ if (!debugfs_create_file("error", S_IRUGO | S_IWUSR, debugfs_base,
+ (void *)cpp_dev, &cpp_debugfs_error))
+ return -ENOMEM;
+
+ return 0;
+}
+
module_init(msm_cpp_init_module);
module_exit(msm_cpp_exit_module);
MODULE_DESCRIPTION("MSM CPP driver");
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
index 36a5fa5..0a70d37 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h
@@ -147,6 +147,11 @@
struct list_head native_buff_head;
};
+struct msm_cpp_work_t {
+ struct work_struct my_work;
+ struct cpp_device *cpp_dev;
+};
+
struct cpp_device {
struct platform_device *pdev;
struct msm_sd_subdev msm_sd;
@@ -165,6 +170,9 @@
struct mutex mutex;
enum cpp_state state;
uint8_t is_firmware_loaded;
+ char *fw_name_bin;
+ struct workqueue_struct *timer_wq;
+ struct msm_cpp_work_t *work;
int domain_num;
struct iommu_domain *domain;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/Makefile b/drivers/media/platform/msm/camera_v2/sensor/Makefile
index b315959..40931ef 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/Makefile
+++ b/drivers/media/platform/msm/camera_v2/sensor/Makefile
@@ -10,5 +10,7 @@
obj-$(CONFIG_OV8825) += ov8825.o
obj-$(CONFIG_OV2720) += ov2720.o
obj-$(CONFIG_OV9724) += ov9724.o
+obj-$(CONFIG_HI256) += hi256.o
obj-$(CONFIG_MT9M114) += mt9m114.o
obj-$(CONFIG_SP1628) += sp1628.o
+obj-$(CONFIG_GC0339) += gc0339.o
diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
index 676862f..a6c5639 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
@@ -111,9 +111,30 @@
msm_camera_io_w(1 << master, cci_dev->base + CCI_HALT_REQ_ADDR);
rc = wait_for_completion_interruptible_timeout(
&cci_dev->cci_master_info[master].reset_complete, CCI_TIMEOUT);
- if (rc <= 0)
- pr_err("%s: wait_for_completion_interruptible_timeout %d\n",
- __func__, __LINE__);
+ if (rc < 0) {
+ pr_err("%s:%d wait failed\n", __func__, __LINE__);
+ } else if (rc == 0) {
+ pr_err("%s:%d wait timeout\n", __func__, __LINE__);
+
+ /* Set reset pending flag to TRUE */
+ cci_dev->cci_master_info[master].reset_pending = TRUE;
+
+ /* Set proper mask to RESET CMD address based on MASTER */
+ if (master == MASTER_0)
+ msm_camera_io_w(CCI_M0_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+ else
+ msm_camera_io_w(CCI_M1_RESET_RMSK,
+ cci_dev->base + CCI_RESET_CMD_ADDR);
+
+ /* wait for reset done irq */
+ rc = wait_for_completion_interruptible_timeout(
+ &cci_dev->cci_master_info[master].reset_complete,
+ CCI_TIMEOUT);
+ if (rc <= 0)
+ pr_err("%s:%d wait failed %d\n", __func__, __LINE__,
+ rc);
+ }
return;
}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c b/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c
index 3dd3a4e..e096e96 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c
@@ -130,12 +130,6 @@
pr_err("%s failed e_ctrl is NULL\n", __func__);
return -EINVAL;
}
- if (e_ctrl->eeprom_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
- rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_util(
- &e_ctrl->i2c_client, MSM_CCI_INIT);
- if (rc < 0)
- pr_err("%s cci_init failed\n", __func__);
- }
CDBG("%s X\n", __func__);
return rc;
}
@@ -149,12 +143,6 @@
pr_err("%s failed e_ctrl is NULL\n", __func__);
return -EINVAL;
}
- if (e_ctrl->eeprom_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
- rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_util(
- &e_ctrl->i2c_client, MSM_CCI_RELEASE);
- if (rc < 0)
- pr_err("%s cci_init failed\n", __func__);
- }
CDBG("%s X\n", __func__);
return rc;
}
@@ -445,14 +433,18 @@
struct msm_eeprom_board_info *eb_info;
struct msm_camera_power_ctrl_t *power_info =
&e_ctrl->eboard_info->power_info;
- struct spi_device *spi = e_ctrl->i2c_client.spi_client->spi_master;
- struct device_node *of_node = spi->dev.of_node;
+ struct device_node *of_node = NULL;
struct msm_camera_gpio_conf *gconf = NULL;
uint16_t gpio_array_size = 0;
uint16_t *gpio_array = NULL;
eb_info = e_ctrl->eboard_info;
- rc = msm_camera_get_dt_power_setting_data(spi->dev.of_node,
+ if (e_ctrl->eeprom_device_type == MSM_CAMERA_SPI_DEVICE)
+ of_node = e_ctrl->i2c_client.
+ spi_client->spi_master->dev.of_node;
+ else if (e_ctrl->eeprom_device_type == MSM_CAMERA_PLATFORM_DEVICE)
+ of_node = e_ctrl->pdev->dev.of_node;
+ rc = msm_camera_get_dt_power_setting_data(of_node,
&power_info->power_setting, &power_info->power_setting_size);
if (rc)
return rc;
@@ -787,26 +779,35 @@
goto board_free;
}
- if (e_ctrl->eeprom_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
- rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_util(
- &e_ctrl->i2c_client, MSM_CCI_INIT);
- if (rc < 0)
- pr_err("%s cci_init failed\n", __func__);
- }
+ rc = msm_eeprom_get_dt_data(e_ctrl);
+ if (rc)
+ goto board_free;
rc = msm_eeprom_alloc_memory_map(e_ctrl, of_node);
if (rc)
goto board_free;
+ rc = msm_camera_power_up(power_info, e_ctrl->eeprom_device_type,
+ &e_ctrl->i2c_client);
+ if (rc) {
+ pr_err("failed rc %d\n", rc);
+ goto memdata_free;
+ }
rc = read_eeprom_memory(e_ctrl);
if (rc < 0) {
pr_err("%s read_eeprom_memory failed\n", __func__);
- goto memdata_free;
+ goto power_down;
}
pr_err("%s line %d\n", __func__, __LINE__);
for (j = 0; j < e_ctrl->num_bytes; j++)
CDBG("memory_data[%d] = 0x%X\n", j, e_ctrl->memory_data[j]);
+ rc = msm_camera_power_down(power_info, e_ctrl->eeprom_device_type,
+ &e_ctrl->i2c_client);
+ if (rc) {
+ pr_err("failed rc %d\n", rc);
+ goto memdata_free;
+ }
v4l2_subdev_init(&e_ctrl->msm_sd.sd,
e_ctrl->eeprom_v4l2_subdev_ops);
v4l2_set_subdevdata(&e_ctrl->msm_sd.sd, e_ctrl);
@@ -821,16 +822,13 @@
msm_sd_register(&e_ctrl->msm_sd);
- if (e_ctrl->eeprom_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
- rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_util(
- &e_ctrl->i2c_client, MSM_CCI_RELEASE);
- if (rc < 0)
- pr_err("%s cci_init failed\n", __func__);
- }
e_ctrl->is_supported = 1;
CDBG("%s X\n", __func__);
return rc;
+power_down:
+ msm_camera_power_down(power_info, e_ctrl->eeprom_device_type,
+ &e_ctrl->i2c_client);
memdata_free:
kfree(e_ctrl->memory_data);
kfree(eb_info->eeprom_map);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/gc0339.c b/drivers/media/platform/msm/camera_v2/sensor/gc0339.c
new file mode 100644
index 0000000..8cba04c
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/gc0339.c
@@ -0,0 +1,676 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <mach/gpiomux.h>
+#include "msm_sensor.h"
+#include "msm_cci.h"
+#include "msm_camera_io_util.h"
+#include "msm_camera_i2c_mux.h"
+
+
+#define GC0339_SENSOR_NAME "gc0339"
+DEFINE_MSM_MUTEX(gc0339_mut);
+
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+
+static struct msm_sensor_ctrl_t gc0339_s_ctrl;
+
+static struct msm_sensor_power_setting gc0339_power_setting[] = {
+
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VIO,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VDIG,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VANA,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_STANDBY,
+ .config_val = GPIO_OUT_HIGH,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_CLK,
+ .seq_val = SENSOR_CAM_MCLK,
+ .config_val = 24000000,
+ .delay = 5,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_STANDBY,
+ .config_val = GPIO_OUT_LOW,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_RESET,
+ .config_val = GPIO_OUT_HIGH,
+ .delay = 1,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_RESET,
+ .config_val = GPIO_OUT_LOW,
+ .delay = 1,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_RESET,
+ .config_val = GPIO_OUT_HIGH,
+ .delay = 1,
+ },
+};
+
+static struct v4l2_subdev_info gc0339_subdev_info[] = {
+ {
+ .code = V4L2_MBUS_FMT_SBGGR10_1X10,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 0,
+ },
+};
+
+static int32_t msm_gc0339_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ return msm_sensor_i2c_probe(client, id, &gc0339_s_ctrl);
+}
+
+static const struct i2c_device_id gc0339_i2c_id[] = {
+ {GC0339_SENSOR_NAME, (kernel_ulong_t)&gc0339_s_ctrl},
+ { }
+};
+
+static struct i2c_driver gc0339_i2c_driver = {
+ .id_table = gc0339_i2c_id,
+ .probe = msm_gc0339_i2c_probe,
+ .driver = {
+ .name = GC0339_SENSOR_NAME,
+ },
+};
+
+static struct msm_camera_i2c_client gc0339_sensor_i2c_client = {
+ .addr_type = MSM_CAMERA_I2C_BYTE_ADDR,
+};
+
+int32_t gc0339_power_up(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0, index = 0;
+ struct msm_sensor_power_setting_array *power_setting_array = NULL;
+ struct msm_sensor_power_setting *power_setting = NULL;
+ struct msm_camera_sensor_board_info *data = s_ctrl->sensordata;
+
+ CDBG("%s:%d\n", __func__, __LINE__);
+ power_setting_array = &s_ctrl->power_setting_array;
+
+ if (data->gpio_conf->cam_gpiomux_conf_tbl != NULL) {
+ pr_err("%s:%d mux install\n", __func__, __LINE__);
+ msm_gpiomux_install(
+ (struct msm_gpiomux_config *)
+ data->gpio_conf->cam_gpiomux_conf_tbl,
+ data->gpio_conf->cam_gpiomux_conf_tbl_size);
+ }
+
+ rc = msm_camera_request_gpio_table(
+ data->gpio_conf->cam_gpio_req_tbl,
+ data->gpio_conf->cam_gpio_req_tbl_size, 1);
+ if (rc < 0) {
+ pr_err("%s: request gpio failed\n", __func__);
+ return rc;
+ }
+ for (index = 0; index < power_setting_array->size; index++) {
+ CDBG("%s index %d\n", __func__, index);
+ power_setting = &power_setting_array->power_setting[index];
+ CDBG("%s type %d\n", __func__, power_setting->seq_type);
+ switch (power_setting->seq_type) {
+ case SENSOR_CLK:
+ if (power_setting->seq_val >= s_ctrl->clk_info_size) {
+ pr_err("%s clk index %d >= max %d\n", __func__,
+ power_setting->seq_val,
+ s_ctrl->clk_info_size);
+ goto power_up_failed;
+ }
+ if (power_setting->config_val)
+ s_ctrl->clk_info[power_setting->seq_val].
+ clk_rate = power_setting->config_val;
+
+ rc = msm_cam_clk_enable(s_ctrl->dev,
+ &s_ctrl->clk_info[0],
+ (struct clk **)&power_setting->data[0],
+ s_ctrl->clk_info_size,
+ 1);
+ if (rc < 0) {
+ pr_err("%s: clk enable failed\n",
+ __func__);
+ goto power_up_failed;
+ }
+ break;
+ case SENSOR_GPIO:
+ if (power_setting->seq_val >= SENSOR_GPIO_MAX ||
+ !data->gpio_conf->gpio_num_info) {
+ pr_err("%s gpio index %d >= max %d\n", __func__,
+ power_setting->seq_val,
+ SENSOR_GPIO_MAX);
+ goto power_up_failed;
+ }
+ pr_debug("%s:%d gpio set val %d\n", __func__, __LINE__,
+ data->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val]);
+ if (data->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val])
+ gpio_set_value_cansleep(
+ data->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val],
+ power_setting->config_val);
+ break;
+ case SENSOR_VREG:
+ if (power_setting->seq_val >= CAM_VREG_MAX) {
+ pr_err("%s vreg index %d >= max %d\n", __func__,
+ power_setting->seq_val,
+ SENSOR_GPIO_MAX);
+ goto power_up_failed;
+ }
+ msm_camera_config_single_vreg(s_ctrl->dev,
+ &data->cam_vreg[power_setting->seq_val],
+ (struct regulator **)&power_setting->data[0],
+ 1);
+ break;
+ default:
+ pr_err("%s error power seq type %d\n", __func__,
+ power_setting->seq_type);
+ break;
+ }
+ if (power_setting->delay > 20) {
+ msleep(power_setting->delay);
+ } else if (power_setting->delay) {
+ usleep_range(power_setting->delay * 1000,
+ (power_setting->delay * 1000) + 1000);
+ }
+ }
+
+ if (s_ctrl->sensor_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_util(
+ s_ctrl->sensor_i2c_client, MSM_CCI_INIT);
+ if (rc < 0) {
+ pr_err("%s cci_init failed\n", __func__);
+ goto power_up_failed;
+ }
+ }
+ if (s_ctrl->func_tbl->sensor_match_id)
+ rc = s_ctrl->func_tbl->sensor_match_id(s_ctrl);
+ else
+ rc = msm_sensor_match_id(s_ctrl);
+ if (rc < 0) {
+ pr_err("%s:%d match id failed rc %d\n", __func__, __LINE__, rc);
+ goto power_up_failed;
+ }
+
+ CDBG("%s exit\n", __func__);
+ return 0;
+power_up_failed:
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ if (s_ctrl->sensor_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
+ s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_util(
+ s_ctrl->sensor_i2c_client, MSM_CCI_RELEASE);
+ }
+
+ for (index--; index >= 0; index--) {
+ CDBG("%s index %d\n", __func__, index);
+ power_setting = &power_setting_array->power_setting[index];
+ CDBG("%s type %d\n", __func__, power_setting->seq_type);
+ switch (power_setting->seq_type) {
+ case SENSOR_CLK:
+ msm_cam_clk_enable(s_ctrl->dev,
+ &s_ctrl->clk_info[0],
+ (struct clk **)&power_setting->data[0],
+ s_ctrl->clk_info_size,
+ 0);
+ break;
+ case SENSOR_GPIO:
+ gpio_set_value_cansleep(
+ data->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val], GPIOF_OUT_INIT_LOW);
+ break;
+ case SENSOR_VREG:
+ msm_camera_config_single_vreg(s_ctrl->dev,
+ &data->cam_vreg[power_setting->seq_val],
+ (struct regulator **)&power_setting->data[0],
+ 0);
+ break;
+ default:
+ pr_err("%s error power seq type %d\n", __func__,
+ power_setting->seq_type);
+ break;
+ }
+ if (power_setting->delay > 20) {
+ msleep(power_setting->delay);
+ } else if (power_setting->delay) {
+ usleep_range(power_setting->delay * 1000,
+ (power_setting->delay * 1000) + 1000);
+ }
+ }
+ msm_camera_request_gpio_table(
+ data->gpio_conf->cam_gpio_req_tbl,
+ data->gpio_conf->cam_gpio_req_tbl_size, 0);
+ return rc;
+}
+
+int32_t gc0339_power_down(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t index = 0;
+ struct msm_sensor_power_setting_array *power_setting_array = NULL;
+ struct msm_sensor_power_setting *power_setting = NULL;
+ struct msm_camera_sensor_board_info *data = s_ctrl->sensordata;
+
+ CDBG("%s:%d\n", __func__, __LINE__);
+ power_setting_array = &s_ctrl->power_setting_array;
+
+ if (s_ctrl->sensor_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
+ s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_util(
+ s_ctrl->sensor_i2c_client, MSM_CCI_RELEASE);
+ }
+
+ for (index = (power_setting_array->size - 1); index >= 0; index--) {
+ CDBG("%s index %d\n", __func__, index);
+ power_setting = &power_setting_array->power_setting[index];
+ CDBG("%s type %d\n", __func__, power_setting->seq_type);
+ switch (power_setting->seq_type) {
+ case SENSOR_CLK:
+ msm_cam_clk_enable(s_ctrl->dev,
+ &s_ctrl->clk_info[0],
+ (struct clk **)&power_setting->data[0],
+ s_ctrl->clk_info_size,
+ 0);
+ break;
+ case SENSOR_GPIO:
+ if (power_setting->seq_val >= SENSOR_GPIO_MAX ||
+ !data->gpio_conf->gpio_num_info) {
+ pr_err("%s gpio index %d >= max %d\n", __func__,
+ power_setting->seq_val,
+ SENSOR_GPIO_MAX);
+ continue;
+ }
+ gpio_set_value_cansleep(
+ data->gpio_conf->gpio_num_info->gpio_num
+ [power_setting->seq_val], GPIOF_OUT_INIT_LOW);
+ break;
+ case SENSOR_VREG:
+ if (power_setting->seq_val >= CAM_VREG_MAX) {
+ pr_err("%s vreg index %d >= max %d\n", __func__,
+ power_setting->seq_val,
+ SENSOR_GPIO_MAX);
+ continue;
+ }
+ msm_camera_config_single_vreg(s_ctrl->dev,
+ &data->cam_vreg[power_setting->seq_val],
+ (struct regulator **)&power_setting->data[0],
+ 0);
+ break;
+ default:
+ pr_err("%s error power seq type %d\n", __func__,
+ power_setting->seq_type);
+ break;
+ }
+ if (power_setting->delay > 20) {
+ msleep(power_setting->delay);
+ } else if (power_setting->delay) {
+ usleep_range(power_setting->delay * 1000,
+ (power_setting->delay * 1000) + 1000);
+ }
+ }
+ msm_camera_request_gpio_table(
+ data->gpio_conf->cam_gpio_req_tbl,
+ data->gpio_conf->cam_gpio_req_tbl_size, 0);
+ CDBG("%s exit\n", __func__);
+ return 0;
+}
+
+int32_t gc0339_match_id(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0;
+ uint16_t chipid = 0;
+ s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write(
+ s_ctrl->sensor_i2c_client,
+ 0xfc,
+ 0x10, MSM_CAMERA_I2C_BYTE_DATA);
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_read(
+ s_ctrl->sensor_i2c_client,
+ s_ctrl->sensordata->slave_info->sensor_id_reg_addr,
+ &chipid, MSM_CAMERA_I2C_BYTE_DATA);
+ if (rc < 0) {
+ pr_err("%s: %s: read id failed\n", __func__,
+ s_ctrl->sensordata->sensor_name);
+ return rc;
+ }
+
+ if (chipid != s_ctrl->sensordata->slave_info->sensor_id) {
+ pr_err("msm_sensor_match_id chip id doesnot match\n");
+ return -ENODEV;
+ }
+ return rc;
+}
+
+int32_t gc0339_config(struct msm_sensor_ctrl_t *s_ctrl,
+ void __user *argp)
+{
+ struct sensorb_cfg_data *cdata = (struct sensorb_cfg_data *)argp;
+ long rc = 0;
+ int32_t i = 0;
+ mutex_lock(s_ctrl->msm_sensor_mutex);
+ switch (cdata->cfgtype) {
+ case CFG_GET_SENSOR_INFO:
+ memcpy(cdata->cfg.sensor_info.sensor_name,
+ s_ctrl->sensordata->sensor_name,
+ sizeof(cdata->cfg.sensor_info.sensor_name));
+ cdata->cfg.sensor_info.session_id =
+ s_ctrl->sensordata->sensor_info->session_id;
+ for (i = 0; i < SUB_MODULE_MAX; i++)
+ cdata->cfg.sensor_info.subdev_id[i] =
+ s_ctrl->sensordata->sensor_info->subdev_id[i];
+ CDBG("%s:%d sensor name %s\n", __func__, __LINE__,
+ cdata->cfg.sensor_info.sensor_name);
+ CDBG("%s:%d session id %d\n", __func__, __LINE__,
+ cdata->cfg.sensor_info.session_id);
+ for (i = 0; i < SUB_MODULE_MAX; i++)
+ CDBG("%s:%d subdev_id[%d] %d\n", __func__, __LINE__, i,
+ cdata->cfg.sensor_info.subdev_id[i]);
+
+ break;
+ case CFG_GET_SENSOR_INIT_PARAMS:
+ cdata->cfg.sensor_init_params =
+ *s_ctrl->sensordata->sensor_init_params;
+ CDBG("%s:%d init params mode %d pos %d mount %d\n", __func__,
+ __LINE__,
+ cdata->cfg.sensor_init_params.modes_supported,
+ cdata->cfg.sensor_init_params.position,
+ cdata->cfg.sensor_init_params.sensor_mount_angle);
+ break;
+ case CFG_SET_SLAVE_INFO: {
+ struct msm_camera_sensor_slave_info sensor_slave_info;
+ struct msm_sensor_power_setting_array *power_setting_array;
+ int slave_index = 0;
+ if (copy_from_user(&sensor_slave_info,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_sensor_slave_info))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ /* Update sensor slave address */
+ if (sensor_slave_info.slave_addr) {
+ s_ctrl->sensor_i2c_client->cci_client->sid =
+ sensor_slave_info.slave_addr >> 1;
+ }
+
+ /* Update sensor address type */
+ s_ctrl->sensor_i2c_client->addr_type =
+ sensor_slave_info.addr_type;
+
+ /* Update power up / down sequence */
+ s_ctrl->power_setting_array =
+ sensor_slave_info.power_setting_array;
+ power_setting_array = &s_ctrl->power_setting_array;
+ power_setting_array->power_setting = kzalloc(
+ power_setting_array->size *
+ sizeof(struct msm_sensor_power_setting), GFP_KERNEL);
+ if (!power_setting_array->power_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(power_setting_array->power_setting,
+ (void *)
+ sensor_slave_info.power_setting_array.power_setting,
+ power_setting_array->size *
+ sizeof(struct msm_sensor_power_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ s_ctrl->free_power_setting = true;
+ CDBG("%s sensor id %x\n", __func__,
+ sensor_slave_info.slave_addr);
+ CDBG("%s sensor addr type %d\n", __func__,
+ sensor_slave_info.addr_type);
+ CDBG("%s sensor reg %x\n", __func__,
+ sensor_slave_info.sensor_id_info.sensor_id_reg_addr);
+ CDBG("%s sensor id %x\n", __func__,
+ sensor_slave_info.sensor_id_info.sensor_id);
+ for (slave_index = 0; slave_index <
+ power_setting_array->size; slave_index++) {
+ CDBG("%s i %d power setting %d %d %ld %d\n", __func__,
+ slave_index,
+ power_setting_array->power_setting[slave_index].
+ seq_type,
+ power_setting_array->power_setting[slave_index].
+ seq_val,
+ power_setting_array->power_setting[slave_index].
+ config_val,
+ power_setting_array->power_setting[slave_index].
+ delay);
+ }
+ break;
+ }
+ case CFG_WRITE_I2C_ARRAY: {
+ struct msm_camera_i2c_reg_setting conf_array;
+ struct msm_camera_i2c_reg_array *reg_setting = NULL;
+
+ if (copy_from_user(&conf_array,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = kzalloc(conf_array.size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(reg_setting, (void *)conf_array.reg_setting,
+ conf_array.size *
+ sizeof(struct msm_camera_i2c_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+
+ conf_array.reg_setting = reg_setting;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write_table(
+ s_ctrl->sensor_i2c_client, &conf_array);
+ kfree(reg_setting);
+ break;
+ }
+ case CFG_WRITE_I2C_SEQ_ARRAY: {
+ struct msm_camera_i2c_seq_reg_setting conf_array;
+ struct msm_camera_i2c_seq_reg_array *reg_setting = NULL;
+
+ if (copy_from_user(&conf_array,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_seq_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = kzalloc(conf_array.size *
+ (sizeof(struct msm_camera_i2c_seq_reg_array)),
+ GFP_KERNEL);
+ if (!reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(reg_setting, (void *)conf_array.reg_setting,
+ conf_array.size *
+ sizeof(struct msm_camera_i2c_seq_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+
+ conf_array.reg_setting = reg_setting;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write_seq_table(s_ctrl->sensor_i2c_client,
+ &conf_array);
+ kfree(reg_setting);
+ break;
+ }
+
+ case CFG_POWER_UP:
+ if (s_ctrl->func_tbl->sensor_power_up)
+ rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
+ else
+ rc = -EFAULT;
+ break;
+
+ case CFG_POWER_DOWN:
+ if (s_ctrl->func_tbl->sensor_power_down)
+ rc = s_ctrl->func_tbl->sensor_power_down(
+ s_ctrl);
+ else
+ rc = -EFAULT;
+ break;
+
+ case CFG_SET_STOP_STREAM_SETTING: {
+ struct msm_camera_i2c_reg_setting *stop_setting =
+ &s_ctrl->stop_setting;
+ struct msm_camera_i2c_reg_array *reg_setting = NULL;
+ if (copy_from_user(stop_setting,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = stop_setting->reg_setting;
+ stop_setting->reg_setting = kzalloc(stop_setting->size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!stop_setting->reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(stop_setting->reg_setting,
+ (void *)reg_setting,
+ stop_setting->size *
+ sizeof(struct msm_camera_i2c_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(stop_setting->reg_setting);
+ stop_setting->reg_setting = NULL;
+ stop_setting->size = 0;
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(s_ctrl->msm_sensor_mutex);
+ return rc;
+}
+
+static struct msm_sensor_fn_t gc0339_sensor_fn_t = {
+ .sensor_power_up = gc0339_power_up,
+ .sensor_power_down = gc0339_power_down,
+ .sensor_match_id = gc0339_match_id,
+ .sensor_config = gc0339_config,
+};
+
+
+static struct msm_sensor_ctrl_t gc0339_s_ctrl = {
+ .sensor_i2c_client = &gc0339_sensor_i2c_client,
+ .power_setting_array.power_setting = gc0339_power_setting,
+ .power_setting_array.size = ARRAY_SIZE(gc0339_power_setting),
+ .msm_sensor_mutex = &gc0339_mut,
+ .sensor_v4l2_subdev_info = gc0339_subdev_info,
+ .sensor_v4l2_subdev_info_size = ARRAY_SIZE(gc0339_subdev_info),
+ .func_tbl = &gc0339_sensor_fn_t,
+};
+
+static const struct of_device_id gc0339_dt_match[] = {
+ {.compatible = "shinetech,gc0339", .data = &gc0339_s_ctrl},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, gc0339_dt_match);
+
+static struct platform_driver gc0339_platform_driver = {
+ .driver = {
+ .name = "shinetech,gc0339",
+ .owner = THIS_MODULE,
+ .of_match_table = gc0339_dt_match,
+ },
+};
+
+static int32_t gc0339_platform_probe(struct platform_device *pdev)
+{
+ int32_t rc = 0;
+ const struct of_device_id *match;
+
+ match = of_match_device(gc0339_dt_match, &pdev->dev);
+ rc = msm_sensor_platform_probe(pdev, match->data);
+ return rc;
+}
+
+static int __init gc0339_init_module(void)
+{
+ int32_t rc = 0;
+
+ rc = platform_driver_probe(&gc0339_platform_driver,
+ gc0339_platform_probe);
+ if (!rc)
+ return rc;
+ return i2c_add_driver(&gc0339_i2c_driver);
+}
+
+static void __exit gc0339_exit_module(void)
+{
+ if (gc0339_s_ctrl.pdev) {
+ msm_sensor_free_sensor_data(&gc0339_s_ctrl);
+ platform_driver_unregister(&gc0339_platform_driver);
+ } else
+ i2c_del_driver(&gc0339_i2c_driver);
+ return;
+}
+
+module_init(gc0339_init_module);
+module_exit(gc0339_exit_module);
+MODULE_DESCRIPTION("gc0339");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera_v2/sensor/hi256.c b/drivers/media/platform/msm/camera_v2/sensor/hi256.c
new file mode 100644
index 0000000..f1df703
--- /dev/null
+++ b/drivers/media/platform/msm/camera_v2/sensor/hi256.c
@@ -0,0 +1,1413 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include "msm_sensor.h"
+#include "msm_cci.h"
+#include "msm_camera_io_util.h"
+#define HI256_SENSOR_NAME "hi256"
+#define PLATFORM_DRIVER_NAME "msm_camera_hi256"
+
+#define CONFIG_MSMB_CAMERA_DEBUG
+#undef CDBG
+#ifdef CONFIG_MSMB_CAMERA_DEBUG
+#define CDBG(fmt, args...) pr_err(fmt, ##args)
+#else
+#define CDBG(fmt, args...) do { } while (0)
+#endif
+
+
+DEFINE_MSM_MUTEX(hi256_mut);
+static struct msm_sensor_ctrl_t hi256_s_ctrl;
+
+static struct msm_sensor_power_setting hi256_power_setting[] = {
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_STANDBY,
+ .config_val = GPIO_OUT_LOW,
+ .delay = 20,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_STANDBY,
+ .config_val = GPIO_OUT_HIGH,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VIO,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VANA,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_VREG,
+ .seq_val = CAM_VDIG,
+ .config_val = 0,
+ .delay = 0,
+ },
+ {
+ .seq_type = SENSOR_CLK,
+ .seq_val = SENSOR_CAM_MCLK,
+ .config_val = 24000000,
+ .delay = 10,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_STANDBY,
+ .config_val = GPIO_OUT_LOW,
+ .delay = 20,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_RESET,
+ .config_val = GPIO_OUT_LOW,
+ .delay = 10,
+ },
+ {
+ .seq_type = SENSOR_GPIO,
+ .seq_val = SENSOR_GPIO_RESET,
+ .config_val = GPIO_OUT_HIGH,
+ .delay = 30,
+ },
+ {
+ .seq_type = SENSOR_I2C_MUX,
+ .seq_val = 0,
+ .config_val = 0,
+ .delay = 0,
+ },
+};
+
+static struct msm_camera_i2c_reg_conf hi256_uxga_settings[] = {
+ {0x03, 0x00},
+ {0x01, 0xf1},
+ {0x03, 0x20},
+ {0x10, 0x1c},
+ {0x03, 0x22},
+ {0x10, 0x69},
+ {0x03, 0x00},
+ {0x12, 0x00},
+ {0x20, 0x00},
+ {0x21, 0x0a},
+ {0x22, 0x00},
+ {0x23, 0x0a},
+ {0x40, 0x01},
+ {0x41, 0x68},
+ {0x42, 0x00},
+ {0x43, 0x12},
+ {0x03, 0x10},
+ {0x3f, 0x00},
+ {0x03, 0x12},
+ {0x20, 0x0f},
+ {0x21, 0x0f},
+ {0x90, 0x5d},
+ {0x03, 0x13},
+ {0x80, 0xfd},
+ {0x03, 0x00},
+ {0x10, 0x00},
+ {0x03, 0x48},
+ {0x72, 0x81},
+ {0x30, 0x0c},
+ {0x31, 0x80},
+ {0x03, 0x00},
+ {0x01, 0xf0},
+};
+
+static struct msm_camera_i2c_reg_conf hi256_start_settings[] = {
+ {0x03, 0x00},
+ {0x01, 0xf0},
+};
+
+static struct msm_camera_i2c_reg_conf hi256_stop_settings[] = {
+ {0x03, 0x00},
+ {0x01, 0xf1},
+};
+
+static struct msm_camera_i2c_reg_conf hi256_recommend_settings[] = {
+ {0x01, 0xf1},
+ {0x01, 0xf3},
+ {0x01, 0xf1},
+
+ {0x08, 0x0f},
+ {0x0a, 0x00},
+
+ {0x03, 0x20},
+ {0x10, 0x1c},
+ {0x03, 0x22},
+ {0x10, 0x69},
+
+ {0x03, 0x00},
+ {0x10, 0x13},
+ {0x11, 0x93},
+ {0x12, 0x00},
+ {0x0b, 0xaa},
+ {0x0c, 0xaa},
+ {0x0d, 0xaa},
+ {0x20, 0x00},
+ {0x21, 0x06},
+ {0x22, 0x00},
+ {0x23, 0x05},
+ {0x24, 0x04},
+ {0x25, 0xb0},
+ {0x26, 0x06},
+ {0x27, 0x40},
+ {0x40, 0x01},
+ {0x41, 0x78},
+ {0x42, 0x00},
+ {0x43, 0x14},
+ {0x45, 0x04},
+ {0x46, 0x18},
+ {0x47, 0xd8},
+ {0x80, 0x2e},
+ {0x81, 0x7e},
+ {0x82, 0x90},
+ {0x83, 0x00},
+ {0x84, 0x0c},
+ {0x85, 0x00},
+ {0x90, 0x0c},
+ {0x91, 0x0c},
+ {0x92, 0x78},
+ {0x93, 0x70},
+ {0x94, 0xff},
+ {0x95, 0xff},
+ {0x96, 0xdc},
+ {0x97, 0xfe},
+ {0x98, 0x38},
+ {0xa0, 0x45},
+ {0xa2, 0x45},
+ {0xa4, 0x45},
+ {0xa6, 0x45},
+ {0xa8, 0x45},
+ {0xaa, 0x45},
+ {0xac, 0x45},
+ {0xae, 0x45},
+ {0x99, 0x43},
+ {0x9a, 0x43},
+ {0x9b, 0x43},
+ {0x9c, 0x43},
+ {0x03, 0x02},
+ {0x12, 0x03},
+ {0x13, 0x03},
+ {0x15, 0x00},
+ {0x16, 0x00},
+ {0x17, 0x8C},
+ {0x18, 0x4c},
+ {0x19, 0x00},
+ {0x1a, 0x39},
+ {0x1c, 0x09},
+ {0x1d, 0x40},
+ {0x1e, 0x30},
+ {0x1f, 0x10},
+ {0x20, 0x77},
+ {0x21, 0x6d},
+ {0x22, 0x77},
+ {0x23, 0x30},
+ {0x24, 0x77},
+ {0x27, 0x3c},
+ {0x2b, 0x80},
+ {0x2e, 0x00},
+ {0x2f, 0x00},
+ {0x30, 0x05},
+ {0x50, 0x20},
+ {0x52, 0x01},
+ {0x53, 0xc1},
+ {0x55, 0x1c},
+ {0x56, 0x11},
+ {0x58, 0x22},
+ {0x59, 0x20},
+ {0x5d, 0xa2},
+ {0x5e, 0x5a},
+ {0x60, 0x87},
+ {0x61, 0x99},
+ {0x62, 0x88},
+ {0x63, 0x97},
+ {0x64, 0x88},
+ {0x65, 0x97},
+ {0x67, 0x0c},
+ {0x68, 0x0c},
+ {0x69, 0x0c},
+ {0x72, 0x89},
+ {0x73, 0x96},
+ {0x74, 0x89},
+ {0x75, 0x96},
+ {0x76, 0x89},
+ {0x77, 0x96},
+ {0x7c, 0x85},
+ {0x7d, 0xaf},
+ {0x80, 0x01},
+ {0x81, 0x7f},
+ {0x82, 0x13},
+ {0x83, 0x24},
+ {0x84, 0x7d},
+ {0x85, 0x81},
+ {0x86, 0x7d},
+ {0x87, 0x81},
+ {0x92, 0x48},
+ {0x93, 0x54},
+ {0x94, 0x7d},
+ {0x95, 0x81},
+ {0x96, 0x7d},
+ {0x97, 0x81},
+ {0xa0, 0x02},
+ {0xa1, 0x7b},
+ {0xa2, 0x02},
+ {0xa3, 0x7b},
+ {0xa4, 0x7b},
+ {0xa5, 0x02},
+ {0xa6, 0x7b},
+ {0xa7, 0x02},
+ {0xa8, 0x85},
+ {0xa9, 0x8c},
+ {0xaa, 0x85},
+ {0xab, 0x8c},
+ {0xac, 0x10},
+ {0xad, 0x16},
+ {0xae, 0x10},
+ {0xaf, 0x16},
+ {0xb0, 0x99},
+ {0xb1, 0xa3},
+ {0xb2, 0xa4},
+ {0xb3, 0xae},
+ {0xb4, 0x9b},
+ {0xb5, 0xa2},
+ {0xb6, 0xa6},
+ {0xb7, 0xac},
+ {0xb8, 0x9b},
+ {0xb9, 0x9f},
+ {0xba, 0xa6},
+ {0xbb, 0xaa},
+ {0xbc, 0x9b},
+ {0xbd, 0x9f},
+ {0xbe, 0xa6},
+ {0xbf, 0xaa},
+ {0xc4, 0x2c},
+ {0xc5, 0x43},
+ {0xc6, 0x63},
+ {0xc7, 0x79},
+ {0xc8, 0x2d},
+ {0xc9, 0x42},
+ {0xca, 0x2d},
+ {0xcb, 0x42},
+ {0xcc, 0x64},
+ {0xcd, 0x78},
+ {0xce, 0x64},
+ {0xcf, 0x78},
+ {0xd0, 0x0a},
+ {0xd1, 0x09},
+ {0xd4, 0x0c},
+ {0xd5, 0x0c},
+ {0xd6, 0x78},
+ {0xd7, 0x70},
+ {0xe0, 0xc4},
+ {0xe1, 0xc4},
+ {0xe2, 0xc4},
+ {0xe3, 0xc4},
+ {0xe4, 0x00},
+ {0xe8, 0x80},
+ {0xe9, 0x40},
+ {0xea, 0x7f},
+ {0xf0, 0xc1},
+ {0xf1, 0xc1},
+ {0xf2, 0xc1},
+ {0xf3, 0xc1},
+ {0xf4, 0xc1},
+ {0x03, 0x03},
+ {0x10, 0x10},
+ {0x03, 0x10},
+ {0x10, 0x03},
+ {0x12, 0x30},
+ {0x13, 0x02},
+ {0x20, 0x00},
+ {0x30, 0x00},
+ {0x31, 0x00},
+ {0x32, 0x00},
+ {0x33, 0x00},
+ {0x34, 0x30},
+ {0x35, 0x00},
+ {0x36, 0x00},
+ {0x38, 0x00},
+ {0x3e, 0x58},
+ {0x3f, 0x00},
+ {0x40, 0x80},
+ {0x41, 0x00},
+ {0x48, 0x95},
+ {0x60, 0x67},
+ {0x61, 0x88},
+ {0x62, 0x90},
+ {0x63, 0x50},
+ {0x64, 0x41},
+ {0x66, 0x42},
+ {0x67, 0x20},
+ {0x6a, 0x71},
+ {0x6b, 0x84},
+ {0x6c, 0x72},
+ {0x6d, 0x83},
+ {0x03, 0x11},
+ {0x10, 0x7f},
+ {0x11, 0x40},
+ {0x12, 0x0a},
+ {0x13, 0xbb},
+ {0x26, 0x31},
+ {0x27, 0x34},
+ {0x28, 0x0f},
+ {0x29, 0x10},
+ {0x2b, 0x30},
+ {0x2c, 0x32},
+ {0x30, 0x70},
+ {0x31, 0x10},
+ {0x32, 0x58},
+ {0x33, 0x09},
+ {0x34, 0x06},
+ {0x35, 0x03},
+ {0x36, 0x70},
+ {0x37, 0x18},
+ {0x38, 0x58},
+ {0x39, 0x09},
+ {0x3a, 0x06},
+ {0x3b, 0x03},
+ {0x3c, 0x80},
+ {0x3d, 0x18},
+ {0x3e, 0x80},
+ {0x3f, 0x0c},
+ {0x40, 0x05},
+ {0x41, 0x06},
+ {0x42, 0x80},
+ {0x43, 0x18},
+ {0x44, 0x80},
+ {0x45, 0x0c},
+ {0x46, 0x05},
+ {0x47, 0x06},
+ {0x48, 0x90},
+ {0x49, 0x40},
+ {0x4a, 0x80},
+ {0x4b, 0x13},
+ {0x4c, 0x10},
+ {0x4d, 0x11},
+ {0x4e, 0x80},
+ {0x4f, 0x30},
+ {0x50, 0x80},
+ {0x51, 0x13},
+ {0x52, 0x10},
+ {0x53, 0x13},
+ {0x54, 0x11},
+ {0x55, 0x17},
+ {0x56, 0x20},
+ {0x57, 0x01},
+ {0x58, 0x00},
+ {0x59, 0x00},
+ {0x5a, 0x18},
+ {0x5b, 0x00},
+ {0x5c, 0x00},
+ {0x60, 0x3f},
+ {0x62, 0x60},
+ {0x70, 0x06},
+ {0x03, 0x12},
+ {0x20, 0x00},
+ {0x21, 0x00},
+ {0x25, 0x00},
+ {0x28, 0x00},
+ {0x29, 0x00},
+ {0x2a, 0x00},
+ {0x30, 0x50},
+ {0x31, 0x18},
+ {0x32, 0x32},
+ {0x33, 0x40},
+ {0x34, 0x50},
+ {0x35, 0x70},
+ {0x36, 0xa0},
+ {0x40, 0xa0},
+ {0x41, 0x40},
+ {0x42, 0xa0},
+ {0x43, 0x90},
+ {0x44, 0x90},
+ {0x45, 0x80},
+ {0x46, 0xb0},
+ {0x47, 0x55},
+ {0x48, 0xa0},
+ {0x49, 0x90},
+ {0x4a, 0x90},
+ {0x4b, 0x80},
+ {0x4c, 0xb0},
+ {0x4d, 0x40},
+ {0x4e, 0x90},
+ {0x4f, 0x60},
+ {0x50, 0xa0},
+ {0x51, 0x80},
+ {0x52, 0xb0},
+ {0x53, 0x40},
+ {0x54, 0x90},
+ {0x55, 0x60},
+ {0x56, 0xa0},
+ {0x57, 0x80},
+ {0x58, 0x90},
+ {0x59, 0x40},
+ {0x5a, 0xd0},
+ {0x5b, 0xd0},
+ {0x5c, 0xe0},
+ {0x5d, 0x80},
+ {0x5e, 0x88},
+ {0x5f, 0x40},
+ {0x60, 0xe0},
+ {0x61, 0xe0},
+ {0x62, 0xe0},
+ {0x63, 0x80},
+ {0x70, 0x15},
+ {0x71, 0x01},
+ {0x72, 0x18},
+ {0x73, 0x01},
+ {0x74, 0x25},
+ {0x75, 0x15},
+ {0x80, 0x20},
+ {0x81, 0x40},
+ {0x82, 0x65},
+ {0x85, 0x1a},
+ {0x88, 0x00},
+ {0x89, 0x00},
+ {0x90, 0x5d},
+ {0xD0, 0x0c},
+ {0xD1, 0x80},
+ {0xD2, 0x17},
+ {0xD3, 0x00},
+ {0xD4, 0x00},
+ {0xD5, 0x0f},
+ {0xD6, 0xff},
+ {0xD7, 0xff},
+ {0x3b, 0x06},
+ {0x3c, 0x06},
+ {0xc5, 0x00},
+ {0xc6, 0x00},
+ {0x03, 0x13},
+ {0x10, 0xcb},
+ {0x11, 0x7b},
+ {0x12, 0x07},
+ {0x14, 0x00},
+ {0x20, 0x15},
+ {0x21, 0x13},
+ {0x22, 0x33},
+ {0x23, 0x05},
+ {0x24, 0x09},
+ {0x25, 0x0a},
+ {0x26, 0x18},
+ {0x27, 0x30},
+ {0x29, 0x12},
+ {0x2a, 0x50},
+ {0x2b, 0x02},
+ {0x2c, 0x02},
+ {0x25, 0x06},
+ {0x2d, 0x0c},
+ {0x2e, 0x12},
+ {0x2f, 0x12},
+ {0x50, 0x10},
+ {0x51, 0x14},
+ {0x52, 0x12},
+ {0x53, 0x0c},
+ {0x54, 0x0f},
+ {0x55, 0x0c},
+ {0x56, 0x10},
+ {0x57, 0x13},
+ {0x58, 0x12},
+ {0x59, 0x0c},
+ {0x5a, 0x0f},
+ {0x5b, 0x0c},
+ {0x5c, 0x25},
+ {0x5d, 0x25},
+ {0x5e, 0x25},
+ {0x5f, 0x25},
+ {0x60, 0x25},
+ {0x61, 0x25},
+ {0x62, 0x25},
+ {0x63, 0x25},
+ {0x64, 0x25},
+ {0x65, 0x25},
+ {0x66, 0x25},
+ {0x67, 0x25},
+ {0x68, 0x07},
+ {0x69, 0x07},
+ {0x6a, 0x07},
+ {0x6b, 0x05},
+ {0x6c, 0x05},
+ {0x6d, 0x05},
+ {0x6e, 0x07},
+ {0x6f, 0x07},
+ {0x70, 0x07},
+ {0x71, 0x05},
+ {0x72, 0x05},
+ {0x73, 0x05},
+ {0x80, 0x01},
+ {0x81, 0x1f},
+ {0x82, 0x05},
+ {0x83, 0x31},
+ {0x90, 0x05},
+ {0x91, 0x05},
+ {0x92, 0x33},
+ {0x93, 0x30},
+ {0x94, 0x03},
+ {0x95, 0x14},
+ {0x97, 0x20},
+ {0x99, 0x20},
+ {0xa0, 0x01},
+ {0xa1, 0x02},
+ {0xa2, 0x01},
+ {0xa3, 0x02},
+ {0xa4, 0x05},
+ {0xa5, 0x05},
+ {0xa6, 0x07},
+ {0xa7, 0x08},
+ {0xa8, 0x07},
+ {0xa9, 0x08},
+ {0xaa, 0x07},
+ {0xab, 0x08},
+ {0xb0, 0x22},
+ {0xb1, 0x2a},
+ {0xb2, 0x28},
+ {0xb3, 0x22},
+ {0xb4, 0x2a},
+ {0xb5, 0x28},
+ {0xb6, 0x22},
+ {0xb7, 0x2a},
+ {0xb8, 0x28},
+ {0xb9, 0x22},
+ {0xba, 0x2a},
+ {0xbb, 0x28},
+ {0xbc, 0x25},
+ {0xbd, 0x2a},
+ {0xbe, 0x27},
+ {0xbf, 0x25},
+ {0xc0, 0x2a},
+ {0xc1, 0x27},
+ {0xc2, 0x1e},
+ {0xc3, 0x24},
+ {0xc4, 0x20},
+ {0xc5, 0x1e},
+ {0xc6, 0x24},
+ {0xc7, 0x20},
+ {0xc8, 0x18},
+ {0xc9, 0x20},
+ {0xca, 0x1e},
+ {0xcb, 0x18},
+ {0xcc, 0x20},
+ {0xcd, 0x1e},
+ {0xce, 0x18},
+ {0xcf, 0x20},
+ {0xd0, 0x1e},
+ {0xd1, 0x18},
+ {0xd2, 0x20},
+ {0xd3, 0x1e},
+ {0x03, 0x14},
+ {0x10, 0x11},
+ {0x14, 0x80},
+ {0x15, 0x80},
+ {0x16, 0x80},
+ {0x17, 0x80},
+ {0x18, 0x80},
+ {0x19, 0x80},
+ {0x20, 0x80},
+ {0x21, 0x80},
+ {0x22, 0x80},
+ {0x23, 0x80},
+ {0x24, 0x80},
+ {0x30, 0xc8},
+ {0x31, 0x2b},
+ {0x32, 0x00},
+ {0x33, 0x00},
+ {0x34, 0x90},
+ {0x40, 0x32},
+ {0x50, 0x21},
+ {0x60, 0x19},
+ {0x70, 0x21},
+ {0x03, 0x15},
+ {0x10, 0x0f},
+ {0x14, 0x46},
+ {0x15, 0x36},
+ {0x16, 0x26},
+ {0x17, 0x2f},
+ {0x30, 0x8f},
+ {0x31, 0x59},
+ {0x32, 0x0a},
+ {0x33, 0x15},
+ {0x34, 0x5b},
+ {0x35, 0x06},
+ {0x36, 0x07},
+ {0x37, 0x40},
+ {0x38, 0x87},
+ {0x40, 0x94},
+ {0x41, 0x20},
+ {0x42, 0x89},
+ {0x43, 0x84},
+ {0x44, 0x03},
+ {0x45, 0x01},
+ {0x46, 0x88},
+ {0x47, 0x9c},
+ {0x48, 0x28},
+ {0x50, 0x02},
+ {0x51, 0x82},
+ {0x52, 0x00},
+ {0x53, 0x07},
+ {0x54, 0x11},
+ {0x55, 0x98},
+ {0x56, 0x00},
+ {0x57, 0x0b},
+ {0x58, 0x8b},
+ {0x80, 0x03},
+ {0x85, 0x40},
+ {0x87, 0x02},
+ {0x88, 0x00},
+ {0x89, 0x00},
+ {0x8a, 0x00},
+ {0x03, 0x16},
+ {0x10, 0x31},
+ {0x18, 0x5e},
+ {0x19, 0x5d},
+ {0x1a, 0x0e},
+ {0x1b, 0x01},
+ {0x1c, 0xdc},
+ {0x1d, 0xfe},
+ {0x30, 0x00},
+ {0x31, 0x0a},
+ {0x32, 0x1f},
+ {0x33, 0x33},
+ {0x34, 0x53},
+ {0x35, 0x6c},
+ {0x36, 0x81},
+ {0x37, 0x94},
+ {0x38, 0xa4},
+ {0x39, 0xb3},
+ {0x3a, 0xc0},
+ {0x3b, 0xcb},
+ {0x3c, 0xd5},
+ {0x3d, 0xde},
+ {0x3e, 0xe6},
+ {0x3f, 0xee},
+ {0x40, 0xf5},
+ {0x41, 0xfc},
+ {0x42, 0xff},
+ {0x50, 0x00},
+ {0x51, 0x08},
+ {0x52, 0x1e},
+ {0x53, 0x36},
+ {0x54, 0x5a},
+ {0x55, 0x75},
+ {0x56, 0x8d},
+ {0x57, 0xa1},
+ {0x58, 0xb2},
+ {0x59, 0xbe},
+ {0x5a, 0xc9},
+ {0x5b, 0xd2},
+ {0x5c, 0xdb},
+ {0x5d, 0xe3},
+ {0x5e, 0xeb},
+ {0x5f, 0xf0},
+ {0x60, 0xf5},
+ {0x61, 0xf7},
+ {0x62, 0xf8},
+ {0x70, 0x00},
+ {0x71, 0x08},
+ {0x72, 0x17},
+ {0x73, 0x2f},
+ {0x74, 0x53},
+ {0x75, 0x6c},
+ {0x76, 0x81},
+ {0x77, 0x94},
+ {0x78, 0xa4},
+ {0x79, 0xb3},
+ {0x7a, 0xc0},
+ {0x7b, 0xcb},
+ {0x7c, 0xd5},
+ {0x7d, 0xde},
+ {0x7e, 0xe6},
+ {0x7f, 0xee},
+ {0x80, 0xf4},
+ {0x81, 0xfa},
+ {0x82, 0xff},
+ {0x03, 0x17},
+ {0x10, 0xf7},
+ {0xC4, 0x66},
+ {0xC5, 0x55},
+ {0x03, 0x20},
+ {0x11, 0x1c},
+ {0x18, 0x30},
+ {0x1a, 0x08},
+ {0x20, 0x05},
+ {0x21, 0x30},
+ {0x22, 0x10},
+ {0x23, 0x00},
+ {0x24, 0x00},
+ {0x28, 0xe7},
+ {0x29, 0x0d},
+ {0x2a, 0xf0},
+ {0x2b, 0x34},
+ {0x30, 0x78},
+ {0x2c, 0xc2},
+ {0x2d, 0xff},
+ {0x2e, 0x33},
+ {0x30, 0x78},
+ {0x32, 0x03},
+ {0x33, 0x2e},
+ {0x34, 0x30},
+ {0x35, 0xd4},
+ {0x36, 0xfe},
+ {0x37, 0x32},
+ {0x38, 0x04},
+ {0x39, 0x22},
+ {0x3a, 0xde},
+ {0x3b, 0x22},
+ {0x3c, 0xde},
+ {0x50, 0x45},
+ {0x51, 0x88},
+ {0x56, 0x03},
+ {0x57, 0xf7},
+ {0x58, 0x14},
+ {0x59, 0x88},
+ {0x5a, 0x04},
+ {0x60, 0xaa},
+ {0x61, 0xaa},
+ {0x62, 0xaa},
+ {0x63, 0xaa},
+ {0x64, 0xaa},
+ {0x65, 0xaa},
+ {0x66, 0xab},
+ {0x67, 0xEa},
+ {0x68, 0xab},
+ {0x69, 0xEa},
+ {0x6a, 0xaa},
+ {0x6b, 0xaa},
+ {0x6c, 0xaa},
+ {0x6d, 0xaa},
+ {0x6e, 0xaa},
+ {0x6f, 0xaa},
+ {0x70, 0x76},
+ {0x71, 0x80},
+ {0x76, 0x43},
+ {0x77, 0x04},
+ {0x78, 0x23},
+ {0x79, 0x46},
+ {0x7a, 0x23},
+ {0x7b, 0x22},
+ {0x7d, 0x23},
+ {0x83, 0x01},
+ {0x84, 0x5f},
+ {0x85, 0x90},
+ {0x86, 0x01},
+ {0x87, 0x2c},
+ {0x88, 0x05},
+ {0x89, 0x7e},
+ {0x8a, 0x40},
+ {0x8B, 0x75},
+ {0x8C, 0x30},
+ {0x8D, 0x61},
+ {0x8E, 0x44},
+ {0x9c, 0x08},
+ {0x9d, 0x34},
+ {0x9e, 0x01},
+ {0x9f, 0x2c},
+ {0xb0, 0x18},
+ {0xb1, 0x14},
+ {0xb2, 0x80},
+ {0xb3, 0x18},
+ {0xb4, 0x1a},
+ {0xb5, 0x44},
+ {0xb6, 0x2f},
+ {0xb7, 0x28},
+ {0xb8, 0x25},
+ {0xb9, 0x22},
+ {0xba, 0x21},
+ {0xbb, 0x20},
+ {0xbc, 0x32},
+ {0xbd, 0x30},
+ {0xc0, 0x10},
+ {0xc1, 0x2b},
+ {0xc2, 0x2b},
+ {0xc3, 0x2b},
+ {0xc4, 0x08},
+ {0xc8, 0x40},
+ {0xc9, 0x40},
+ {0x03, 0x22},
+ {0x10, 0xfd},
+ {0x11, 0x2e},
+ {0x19, 0x01},
+ {0x20, 0x10},
+ {0x21, 0x80},
+ {0x24, 0x01},
+ {0x30, 0x80},
+ {0x31, 0x80},
+ {0x38, 0x11},
+ {0x39, 0x34},
+ {0x40, 0xfa},
+ {0x41, 0x44},
+ {0x42, 0x43},
+ {0x43, 0xf6},
+ {0x44, 0x44},
+ {0x45, 0x33},
+ {0x46, 0x00},
+ {0x50, 0xb2},
+ {0x51, 0x81},
+ {0x52, 0x98},
+ {0x80, 0x38},
+ {0x81, 0x20},
+ {0x82, 0x38},
+ {0x83, 0x5e},
+ {0x84, 0x18},
+ {0x85, 0x58},
+ {0x86, 0x20},
+ {0x87, 0x49},
+ {0x88, 0x33},
+ {0x89, 0x37},
+ {0x8a, 0x2a},
+ {0x8b, 0x41},
+ {0x8c, 0x39},
+ {0x8d, 0x34},
+ {0x8e, 0x29},
+ {0x8f, 0x53},
+ {0x90, 0x52},
+ {0x91, 0x51},
+ {0x92, 0x4e},
+ {0x93, 0x46},
+ {0x94, 0x3d},
+ {0x95, 0x34},
+ {0x96, 0x2e},
+ {0x97, 0x29},
+ {0x98, 0x22},
+ {0x99, 0x1c},
+ {0x9a, 0x18},
+ {0x9b, 0x77},
+ {0x9c, 0x77},
+ {0x9d, 0x48},
+ {0x9e, 0x38},
+ {0x9f, 0x30},
+ {0xa0, 0x60},
+ {0xa1, 0x34},
+ {0xa2, 0x6f},
+ {0xa3, 0xff},
+ {0xa4, 0x14},
+ {0xa5, 0x2c},
+ {0xa6, 0xcf},
+ {0xad, 0x40},
+ {0xae, 0x4a},
+ {0xaf, 0x28},
+ {0xb0, 0x26},
+ {0xb1, 0x00},
+ {0xb4, 0xea},
+ {0xb8, 0xa0},
+ {0xb9, 0x00},
+ {0x03, 0x48},
+ {0x70, 0x03},
+ {0x71, 0x30},
+ {0x72, 0x81},
+ {0x73, 0x10},
+ {0x70, 0x85},
+ {0x03, 0x48},
+ {0x03, 0x48},
+ {0x03, 0x48},
+ {0x03, 0x48},
+ {0x70, 0x95},
+ {0x10, 0x1c},
+ {0x11, 0x10},
+ {0x12, 0x00},
+ {0x14, 0x00},
+ {0x16, 0x04},
+ {0x18, 0x80},
+ {0x19, 0x00},
+ {0x1a, 0xa0},
+ {0x1b, 0x0d},
+ {0x1c, 0x01},
+ {0x1d, 0x0a},
+ {0x1e, 0x07},
+ {0x1f, 0x0b},
+ {0x23, 0x01},
+ {0x24, 0x1e},
+ {0x25, 0x00},
+ {0x26, 0x00},
+ {0x27, 0x08},
+ {0x28, 0x00},
+ {0x30, 0x06},
+ {0x31, 0x40},
+ {0x32, 0x13},
+ {0x33, 0x0c},
+ {0x34, 0x04},
+ {0x35, 0x06},
+ {0x36, 0x01},
+ {0x37, 0x06},
+ {0x39, 0x4f},
+ {0x03, 0x20},
+ {0x10, 0x9c},
+ {0x03, 0x22},
+ {0x10, 0xe9},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x03, 0x00},
+ {0x0e, 0x03},
+ {0x0e, 0x73},
+ {0x03, 0x00},
+ {0x01, 0xf0},
+};
+
+static struct v4l2_subdev_info hi256_subdev_info[] = {
+ {
+ .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 0,
+ },
+};
+
+static struct msm_camera_i2c_reg_conf hi256_svga_settings[] = {
+ {0x03, 0x20},
+ {0x10, 0x1c},
+ {0x03, 0x22},
+ {0x10, 0x69},
+ {0x03, 0x00},
+ {0x10, 0x13},
+ {0x12, 0x00},
+ {0x20, 0x00},
+ {0x21, 0x04},
+ {0x22, 0x00},
+ {0x23, 0x07},
+ {0x40, 0x01},
+ {0x41, 0x78},
+ {0x42, 0x00},
+ {0x43, 0x14},
+ {0x03, 0x10},
+ {0x3f, 0x02},
+ {0x03, 0x12},
+ {0x20, 0x0f},
+ {0x21, 0x0f},
+ {0x90, 0x5d},
+ {0x03, 0x13},
+ {0x80, 0x00},
+ {0x03, 0x48},
+ {0x72, 0x81},
+ {0x30, 0x06},
+ {0x31, 0x40},
+ {0x03, 0x20},
+ {0x88, 0x05},
+ {0x89, 0x7e},
+ {0x8a, 0x40},
+ {0x03, 0x20},
+ {0x10, 0x9c},
+ {0x03, 0x22},
+ {0x10, 0xe9},
+};
+
+
+static const struct i2c_device_id hi256_i2c_id[] = {
+ {HI256_SENSOR_NAME, (kernel_ulong_t)&hi256_s_ctrl},
+ { }
+};
+
+static int32_t msm_hi256_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ return msm_sensor_i2c_probe(client, id, &hi256_s_ctrl);
+}
+
+static struct i2c_driver hi256_i2c_driver = {
+ .id_table = hi256_i2c_id,
+ .probe = msm_hi256_i2c_probe,
+ .driver = {
+ .name = HI256_SENSOR_NAME,
+ },
+};
+
+static struct msm_camera_i2c_client hi256_sensor_i2c_client = {
+ .addr_type = MSM_CAMERA_I2C_BYTE_ADDR,
+};
+
+static const struct of_device_id hi256_dt_match[] = {
+ {.compatible = "shinetech,hi256", .data = &hi256_s_ctrl},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, hi256_dt_match);
+
+static struct platform_driver hi256_platform_driver = {
+ .driver = {
+ .name = "shinetech,hi256",
+ .owner = THIS_MODULE,
+ .of_match_table = hi256_dt_match,
+ },
+};
+
+static void hi256_i2c_write_table(struct msm_sensor_ctrl_t *s_ctrl,
+ struct msm_camera_i2c_reg_conf *table,
+ int num)
+{
+ int i = 0;
+ int rc = 0;
+ for (i = 0; i < num; ++i) {
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write(
+ s_ctrl->sensor_i2c_client, table->reg_addr,
+ table->reg_data,
+ MSM_CAMERA_I2C_BYTE_DATA);
+ if (rc < 0) {
+ msleep(100);
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write(
+ s_ctrl->sensor_i2c_client, table->reg_addr,
+ table->reg_data,
+ MSM_CAMERA_I2C_BYTE_DATA);
+ }
+ table++;
+ }
+
+}
+
+static int32_t hi256_platform_probe(struct platform_device *pdev)
+{
+ int32_t rc;
+ const struct of_device_id *match;
+ match = of_match_device(hi256_dt_match, &pdev->dev);
+ rc = msm_sensor_platform_probe(pdev, match->data);
+ return rc;
+}
+
+static int __init hi256_init_module(void)
+{
+ int32_t rc;
+ pr_info("%s:%d\n", __func__, __LINE__);
+ rc = platform_driver_probe(&hi256_platform_driver,
+ hi256_platform_probe);
+ if (!rc)
+ return rc;
+ return i2c_add_driver(&hi256_i2c_driver);
+}
+
+static void __exit hi256_exit_module(void)
+{
+ pr_info("%s:%d\n", __func__, __LINE__);
+ if (hi256_s_ctrl.pdev) {
+ msm_sensor_free_sensor_data(&hi256_s_ctrl);
+ platform_driver_unregister(&hi256_platform_driver);
+ } else
+ i2c_del_driver(&hi256_i2c_driver);
+ return;
+}
+
+static int32_t hi256_sensor_match_id(struct msm_sensor_ctrl_t *s_ctrl)
+{
+ int32_t rc = 0;
+ uint16_t chipid = 0;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_read(
+ s_ctrl->sensor_i2c_client,
+ s_ctrl->sensordata->slave_info->sensor_id_reg_addr,
+ &chipid, MSM_CAMERA_I2C_BYTE_DATA);
+ if (rc < 0) {
+ pr_err("%s: %s: hi256 read id failed\n", __func__,
+ s_ctrl->sensordata->sensor_name);
+ return rc;
+ }
+
+ CDBG("%s: read id: %x expected id %x:\n", __func__, chipid,
+ s_ctrl->sensordata->slave_info->sensor_id);
+ if (chipid != s_ctrl->sensordata->slave_info->sensor_id) {
+ pr_err("msm_sensor_match_id chip id doesnot match\n");
+ return -ENODEV;
+ }
+ return rc;
+}
+
+int32_t hi256_sensor_config(struct msm_sensor_ctrl_t *s_ctrl,
+ void __user *argp)
+{
+ struct sensorb_cfg_data *cdata = (struct sensorb_cfg_data *)argp;
+ long rc = 0;
+ int32_t i = 0;
+ mutex_lock(s_ctrl->msm_sensor_mutex);
+ CDBG("%s:%d %s cfgtype = %d\n", __func__, __LINE__,
+ s_ctrl->sensordata->sensor_name, cdata->cfgtype);
+ switch (cdata->cfgtype) {
+ case CFG_GET_SENSOR_INFO:
+ memcpy(cdata->cfg.sensor_info.sensor_name,
+ s_ctrl->sensordata->sensor_name,
+ sizeof(cdata->cfg.sensor_info.sensor_name));
+ cdata->cfg.sensor_info.session_id =
+ s_ctrl->sensordata->sensor_info->session_id;
+ for (i = 0; i < SUB_MODULE_MAX; i++)
+ cdata->cfg.sensor_info.subdev_id[i] =
+ s_ctrl->sensordata->sensor_info->subdev_id[i];
+ CDBG("%s:%d sensor name %s\n", __func__, __LINE__,
+ cdata->cfg.sensor_info.sensor_name);
+ CDBG("%s:%d session id %d\n", __func__, __LINE__,
+ cdata->cfg.sensor_info.session_id);
+ for (i = 0; i < SUB_MODULE_MAX; i++)
+ CDBG("%s:%d subdev_id[%d] %d\n", __func__, __LINE__, i,
+ cdata->cfg.sensor_info.subdev_id[i]);
+ break;
+ case CFG_SET_INIT_SETTING:
+ CDBG("init setting");
+ hi256_i2c_write_table(s_ctrl,
+ &hi256_recommend_settings[0],
+ ARRAY_SIZE(hi256_recommend_settings));
+ CDBG("init setting X");
+ break;
+ case CFG_SET_RESOLUTION: {
+ int val = 0;
+ if (copy_from_user(&val,
+ (void *)cdata->cfg.setting, sizeof(int))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ if (val == 0)
+ hi256_i2c_write_table(s_ctrl, &hi256_uxga_settings[0],
+ ARRAY_SIZE(hi256_uxga_settings));
+ else if (val == 1)
+ hi256_i2c_write_table(s_ctrl, &hi256_svga_settings[0],
+ ARRAY_SIZE(hi256_svga_settings));
+ break;
+ }
+ case CFG_SET_STOP_STREAM:
+ hi256_i2c_write_table(s_ctrl,
+ &hi256_stop_settings[0],
+ ARRAY_SIZE(hi256_stop_settings));
+ break;
+ case CFG_SET_START_STREAM:
+ hi256_i2c_write_table(s_ctrl,
+ &hi256_start_settings[0],
+ ARRAY_SIZE(hi256_start_settings));
+ break;
+ case CFG_GET_SENSOR_INIT_PARAMS:
+ cdata->cfg.sensor_init_params =
+ *s_ctrl->sensordata->sensor_init_params;
+ CDBG("%s:%d init params mode %d pos %d mount %d\n", __func__,
+ __LINE__,
+ cdata->cfg.sensor_init_params.modes_supported,
+ cdata->cfg.sensor_init_params.position,
+ cdata->cfg.sensor_init_params.sensor_mount_angle);
+ break;
+ case CFG_SET_SLAVE_INFO: {
+ struct msm_camera_sensor_slave_info sensor_slave_info;
+ struct msm_sensor_power_setting_array *power_setting_array;
+ int slave_index = 0;
+ if (copy_from_user(&sensor_slave_info,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_sensor_slave_info))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ /* Update sensor slave address */
+ if (sensor_slave_info.slave_addr) {
+ s_ctrl->sensor_i2c_client->cci_client->sid =
+ sensor_slave_info.slave_addr >> 1;
+ }
+
+ /* Update sensor address type */
+ s_ctrl->sensor_i2c_client->addr_type =
+ sensor_slave_info.addr_type;
+
+ /* Update power up / down sequence */
+ s_ctrl->power_setting_array =
+ sensor_slave_info.power_setting_array;
+ power_setting_array = &s_ctrl->power_setting_array;
+ power_setting_array->power_setting = kzalloc(
+ power_setting_array->size *
+ sizeof(struct msm_sensor_power_setting), GFP_KERNEL);
+ if (!power_setting_array->power_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(power_setting_array->power_setting,
+ (void *)
+ sensor_slave_info.power_setting_array.power_setting,
+ power_setting_array->size *
+ sizeof(struct msm_sensor_power_setting))) {
+ kfree(power_setting_array->power_setting);
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+ s_ctrl->free_power_setting = true;
+ CDBG("%s sensor id %x\n", __func__,
+ sensor_slave_info.slave_addr);
+ CDBG("%s sensor addr type %d\n", __func__,
+ sensor_slave_info.addr_type);
+ CDBG("%s sensor reg %x\n", __func__,
+ sensor_slave_info.sensor_id_info.sensor_id_reg_addr);
+ CDBG("%s sensor id %x\n", __func__,
+ sensor_slave_info.sensor_id_info.sensor_id);
+ for (slave_index = 0; slave_index <
+ power_setting_array->size; slave_index++) {
+ CDBG("%s i %d power setting %d %d %ld %d\n", __func__,
+ slave_index,
+ power_setting_array->power_setting[slave_index].
+ seq_type,
+ power_setting_array->power_setting[slave_index].
+ seq_val,
+ power_setting_array->power_setting[slave_index].
+ config_val,
+ power_setting_array->power_setting[slave_index].
+ delay);
+ }
+ kfree(power_setting_array->power_setting);
+ break;
+ }
+ case CFG_WRITE_I2C_ARRAY: {
+ struct msm_camera_i2c_reg_setting conf_array;
+ struct msm_camera_i2c_reg_array *reg_setting = NULL;
+
+ if (copy_from_user(&conf_array,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = kzalloc(conf_array.size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(reg_setting, (void *)conf_array.reg_setting,
+ conf_array.size *
+ sizeof(struct msm_camera_i2c_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+
+ conf_array.reg_setting = reg_setting;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->i2c_write_table(
+ s_ctrl->sensor_i2c_client, &conf_array);
+ kfree(reg_setting);
+ break;
+ }
+ case CFG_WRITE_I2C_SEQ_ARRAY: {
+ struct msm_camera_i2c_seq_reg_setting conf_array;
+ struct msm_camera_i2c_seq_reg_array *reg_setting = NULL;
+
+ if (copy_from_user(&conf_array,
+ (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_seq_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = kzalloc(conf_array.size *
+ (sizeof(struct msm_camera_i2c_seq_reg_array)),
+ GFP_KERNEL);
+ if (!reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(reg_setting, (void *)conf_array.reg_setting,
+ conf_array.size *
+ sizeof(struct msm_camera_i2c_seq_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(reg_setting);
+ rc = -EFAULT;
+ break;
+ }
+
+ conf_array.reg_setting = reg_setting;
+ rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
+ i2c_write_seq_table(s_ctrl->sensor_i2c_client,
+ &conf_array);
+ kfree(reg_setting);
+ break;
+ }
+
+ case CFG_POWER_UP:
+ if (s_ctrl->func_tbl->sensor_power_up)
+ rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl);
+ else
+ rc = -EFAULT;
+ break;
+
+ case CFG_POWER_DOWN:
+ if (s_ctrl->func_tbl->sensor_power_down)
+ rc = s_ctrl->func_tbl->sensor_power_down(s_ctrl);
+ else
+ rc = -EFAULT;
+ break;
+
+ case CFG_SET_STOP_STREAM_SETTING: {
+ struct msm_camera_i2c_reg_setting *stop_setting =
+ &s_ctrl->stop_setting;
+ struct msm_camera_i2c_reg_array *reg_setting = NULL;
+ if (copy_from_user(stop_setting, (void *)cdata->cfg.setting,
+ sizeof(struct msm_camera_i2c_reg_setting))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -EFAULT;
+ break;
+ }
+
+ reg_setting = stop_setting->reg_setting;
+ stop_setting->reg_setting = kzalloc(stop_setting->size *
+ (sizeof(struct msm_camera_i2c_reg_array)), GFP_KERNEL);
+ if (!stop_setting->reg_setting) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ rc = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(stop_setting->reg_setting,
+ (void *)reg_setting, stop_setting->size *
+ sizeof(struct msm_camera_i2c_reg_array))) {
+ pr_err("%s:%d failed\n", __func__, __LINE__);
+ kfree(stop_setting->reg_setting);
+ stop_setting->reg_setting = NULL;
+ stop_setting->size = 0;
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ mutex_unlock(s_ctrl->msm_sensor_mutex);
+
+ return rc;
+}
+
+static struct msm_sensor_fn_t hi256_sensor_func_tbl = {
+ .sensor_config = hi256_sensor_config,
+ .sensor_power_up = msm_sensor_power_up,
+ .sensor_power_down = msm_sensor_power_down,
+ .sensor_match_id = hi256_sensor_match_id,
+};
+
+static struct msm_sensor_ctrl_t hi256_s_ctrl = {
+ .sensor_i2c_client = &hi256_sensor_i2c_client,
+ .power_setting_array.power_setting = hi256_power_setting,
+ .power_setting_array.size = ARRAY_SIZE(hi256_power_setting),
+ .msm_sensor_mutex = &hi256_mut,
+ .sensor_v4l2_subdev_info = hi256_subdev_info,
+ .sensor_v4l2_subdev_info_size = ARRAY_SIZE(hi256_subdev_info),
+ .func_tbl = &hi256_sensor_func_tbl,
+};
+
+module_init(hi256_init_module);
+module_exit(hi256_exit_module);
+MODULE_DESCRIPTION("Hi256 2MP YUV sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index bd05180..77f838c 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -1305,6 +1305,25 @@
return rc;
}
+static void msm_vidc_print_running_insts(struct msm_vidc_core *core)
+{
+ struct msm_vidc_inst *temp;
+ dprintk(VIDC_ERR, "Running instances:\n");
+ dprintk(VIDC_ERR, "%4s|%4s|%4s|%4s\n", "type", "w", "h", "fps");
+ list_for_each_entry(temp, &core->instances, list) {
+ mutex_lock(&temp->lock);
+ if (temp->state >= MSM_VIDC_OPEN_DONE &&
+ temp->state < MSM_VIDC_STOP_DONE) {
+ dprintk(VIDC_ERR, "%4d|%4d|%4d|%4d\n",
+ temp->session_type,
+ temp->prop.width,
+ temp->prop.height,
+ temp->prop.fps);
+ }
+ mutex_unlock(&temp->lock);
+ }
+}
+
static int msm_vidc_load_resources(int flipped_state,
struct msm_vidc_inst *inst)
{
@@ -1327,24 +1346,9 @@
num_mbs_per_sec = msm_comm_get_load(inst->core, MSM_VIDC_DECODER);
num_mbs_per_sec += msm_comm_get_load(inst->core, MSM_VIDC_ENCODER);
if (num_mbs_per_sec > inst->core->resources.max_load) {
- struct msm_vidc_inst *temp;
-
dprintk(VIDC_ERR, "HW is overloaded, needed: %d max: %d\n",
num_mbs_per_sec, inst->core->resources.max_load);
- dprintk(VIDC_ERR, "Running instances:\n");
- dprintk(VIDC_ERR, "%4s|%4s|%4s|%4s\n", "type", "w", "h", "fps");
- list_for_each_entry(temp, &inst->core->instances, list) {
- mutex_lock(&temp->lock);
- if (temp->state >= MSM_VIDC_OPEN_DONE &&
- temp->state < MSM_VIDC_STOP_DONE) {
- dprintk(VIDC_ERR, "%4d|%4d|%4d|%4d\n",
- temp->session_type,
- temp->prop.width,
- temp->prop.height,
- temp->prop.fps);
- }
- mutex_unlock(&temp->lock);
- }
+ msm_vidc_print_running_insts(inst->core);
inst->state = MSM_VIDC_CORE_INVALID;
msm_comm_recover_from_session_error(inst);
return -ENOMEM;
@@ -2479,6 +2483,29 @@
return rc;
}
+static int msm_vidc_load_supported(struct msm_vidc_inst *inst)
+{
+ int num_mbs_per_sec = 0;
+
+ if (inst->state == MSM_VIDC_OPEN_DONE) {
+ num_mbs_per_sec = msm_comm_get_load(inst->core,
+ MSM_VIDC_DECODER);
+ num_mbs_per_sec += msm_comm_get_load(inst->core,
+ MSM_VIDC_ENCODER);
+ if (num_mbs_per_sec > inst->core->resources.max_load) {
+ dprintk(VIDC_ERR,
+ "H/w is overloaded. needed: %d max: %d\n",
+ num_mbs_per_sec,
+ inst->core->resources.max_load);
+ mutex_lock(&inst->sync_lock);
+ msm_vidc_print_running_insts(inst->core);
+ mutex_unlock(&inst->sync_lock);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
int msm_vidc_check_session_supported(struct msm_vidc_inst *inst)
{
struct msm_vidc_core_capability *capability;
@@ -2492,7 +2519,8 @@
capability = &inst->capability;
hdev = inst->core->device;
- if (inst->capability.capability_set) {
+ rc = msm_vidc_load_supported(inst);
+ if (!rc && inst->capability.capability_set) {
rc = call_hfi_op(hdev, capability_check,
inst->fmts[OUTPUT_PORT]->fourcc,
inst->prop.width, &capability->width.max,
diff --git a/drivers/media/platform/msm/vidc/q6_hfi.c b/drivers/media/platform/msm/vidc/q6_hfi.c
index 0678fc2..bc3b93d 100644
--- a/drivers/media/platform/msm/vidc/q6_hfi.c
+++ b/drivers/media/platform/msm/vidc/q6_hfi.c
@@ -400,9 +400,11 @@
static int q6_hfi_apr_callback(struct apr_client_data *data, void *priv)
{
struct q6_hfi_device *device = priv;
+ struct hfi_msg_event_notify_packet pkt = {0};
+ void *payload = NULL;
int rc = 0;
- if (!data || !device || !data->payload_size) {
+ if (!data || !device) {
dprintk(VIDC_ERR, "%s - Invalid arguments", __func__);
return -EINVAL;
}
@@ -410,7 +412,23 @@
dprintk(VIDC_DBG, "%s opcode = %u payload size = %u", __func__,
data->opcode, data->payload_size);
- rc = q6_hfi_iface_eventq_write(device, data->payload);
+ if (data->opcode == RESET_EVENTS) {
+ dprintk(VIDC_ERR, "%s Received subsystem reset event: %d",
+ __func__, data->reset_event);
+ pkt.packet_type = HFI_MSG_EVENT_NOTIFY;
+ pkt.size = sizeof(pkt);
+ pkt.event_id = HFI_EVENT_SYS_ERROR;
+ pkt.event_data1 = data->opcode;
+ pkt.event_data2 = data->reset_event;
+ payload = &pkt;
+ } else if (data->payload_size > 0) {
+ payload = data->payload;
+ } else {
+ dprintk(VIDC_ERR, "%s - Invalid payload size", __func__);
+ return -EINVAL;
+ }
+
+ rc = q6_hfi_iface_eventq_write(device, payload);
if (rc) {
dprintk(VIDC_ERR, "%s failed to write to event queue",
__func__);
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index c60537a..6ea2346 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -41,6 +41,7 @@
#include <mach/subsystem_restart.h>
#include <mach/socinfo.h>
#include <mach/qseecomi.h>
+#include <asm/cacheflush.h>
#include "qseecom_legacy.h"
#include "qseecom_kernel.h"
@@ -515,7 +516,10 @@
qseecom.send_resp_flag = 0;
send_data_rsp.qsee_cmd_id = QSEOS_LISTENER_DATA_RSP_COMMAND;
send_data_rsp.listener_id = lstnr ;
-
+ if (ptr_svc)
+ msm_ion_do_cache_op(qseecom.ion_clnt, ptr_svc->ihandle,
+ ptr_svc->sb_virt, ptr_svc->sb_length,
+ ION_IOC_CLEAN_INV_CACHES);
ret = scm_call(SCM_SVC_TZSCHEDULER, 1,
(const void *)&send_data_rsp,
sizeof(send_data_rsp), resp,
@@ -641,6 +645,8 @@
load_req.mdt_len = load_img_req.mdt_len;
load_req.img_len = load_img_req.img_len;
load_req.phy_addr = pa;
+ msm_ion_do_cache_op(qseecom.ion_clnt, ihandle, NULL, len,
+ ION_IOC_CLEAN_INV_CACHES);
/* SCM_CALL to load the app and get the app_id back */
ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
@@ -880,10 +886,15 @@
pr_err("Unsupported cmd_id %d\n", req.cmd_id);
return -EINVAL;
}
-
+ msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle,
+ data->client.sb_virt, data->client.sb_length,
+ ION_IOC_CLEAN_INV_CACHES);
ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *) &send_svc_ireq,
sizeof(send_svc_ireq),
&resp, sizeof(resp));
+ msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle,
+ data->client.sb_virt, data->client.sb_length,
+ ION_IOC_INV_CACHES);
if (ret) {
pr_err("qseecom_scm_call failed with err: %d\n", ret);
return ret;
@@ -952,6 +963,11 @@
(uint32_t)req->resp_buf));
send_data_req.rsp_len = req->resp_len;
+ msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle,
+ data->client.sb_virt,
+ (req->cmd_req_len + req->resp_len),
+ ION_IOC_CLEAN_INV_CACHES);
+
ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *) &send_data_req,
sizeof(send_data_req),
&resp, sizeof(resp));
@@ -974,6 +990,9 @@
ret = -EINVAL;
}
}
+ msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle,
+ data->client.sb_virt, data->client.sb_length,
+ ION_IOC_INV_CACHES);
return ret;
}
@@ -1005,6 +1024,8 @@
char *field;
int ret = 0;
int i = 0;
+ uint32_t len = 0;
+ struct scatterlist *sg;
for (i = 0; i < MAX_ION_FD; i++) {
struct sg_table *sg_ptr = NULL;
@@ -1035,6 +1056,7 @@
sg_ptr->nents, QSEECOM_MAX_SG_ENTRY);
goto err;
}
+ sg = sg_ptr->sgl;
if (sg_ptr->nents == 1) {
uint32_t *update;
update = (uint32_t *) field;
@@ -1043,12 +1065,11 @@
else
*update = (uint32_t)sg_dma_address(
sg_ptr->sgl);
+ len += (uint32_t)sg->length;
} else {
struct qseecom_sg_entry *update;
- struct scatterlist *sg;
int j = 0;
update = (struct qseecom_sg_entry *) field;
- sg = sg_ptr->sgl;
for (j = 0; j < sg_ptr->nents; j++) {
if (cleanup) {
update->phys_addr = 0;
@@ -1058,10 +1079,19 @@
sg_dma_address(sg);
update->len = sg->length;
}
+ len += sg->length;
update++;
sg = sg_next(sg);
}
}
+ if (cleanup)
+ msm_ion_do_cache_op(qseecom.ion_clnt,
+ ihandle, NULL, len,
+ ION_IOC_INV_CACHES);
+ else
+ msm_ion_do_cache_op(qseecom.ion_clnt,
+ ihandle, NULL, len,
+ ION_IOC_CLEAN_INV_CACHES);
/* Deallocate the handle */
if (!IS_ERR_OR_NULL(ihandle))
ion_free(qseecom.ion_clnt, ihandle);
@@ -1288,6 +1318,7 @@
return -EIO;
}
+ __cpuc_flush_dcache_area((void *)img_data, fw_size);
/* SCM_CALL to load the image */
ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
sizeof(struct qseecom_load_app_ireq),
@@ -1354,6 +1385,7 @@
return -EIO;
}
+ __cpuc_flush_dcache_area((void *)img_data, fw_size);
/* SCM_CALL to load the image */
ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
sizeof(struct qseecom_load_lib_image_ireq),
@@ -1985,7 +2017,8 @@
ret = -EIO;
goto qseecom_load_external_elf_set_cpu_err;
}
-
+ msm_ion_do_cache_op(qseecom.ion_clnt, ihandle, NULL, len,
+ ION_IOC_CLEAN_INV_CACHES);
/* SCM_CALL to load the external elf */
ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
sizeof(struct qseecom_load_app_ireq),
@@ -2498,7 +2531,7 @@
ret = scm_call(SCM_SVC_ES, SCM_SAVE_PARTITION_HASH_ID,
(void *) &req, sizeof(req), NULL, 0);
if (ret) {
- pr_err("scm_call failed");
+ pr_err("qseecom_scm_call failed");
return ret;
}
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index f01ddab..d975543 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -3130,11 +3130,11 @@
/* Silent the block layer */
if (md) {
- rc = mmc_queue_suspend(&md->queue);
+ rc = mmc_queue_suspend(&md->queue, 1);
if (rc)
goto suspend_error;
list_for_each_entry(part_md, &md->part, part) {
- rc = mmc_queue_suspend(&part_md->queue);
+ rc = mmc_queue_suspend(&part_md->queue, 1);
if (rc)
goto suspend_error;
}
@@ -3161,11 +3161,11 @@
int rc = 0;
if (md) {
- rc = mmc_queue_suspend(&md->queue);
+ rc = mmc_queue_suspend(&md->queue, 0);
if (rc)
goto out;
list_for_each_entry(part_md, &md->part, part) {
- rc = mmc_queue_suspend(&part_md->queue);
+ rc = mmc_queue_suspend(&part_md->queue, 0);
if (rc)
goto out_resume;
}
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 0e024dd..507cd5b 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -438,12 +438,13 @@
/**
* mmc_queue_suspend - suspend a MMC request queue
* @mq: MMC queue to suspend
+ * @wait: Wait till MMC request queue is empty
*
* Stop the block request queue, and wait for our thread to
* complete any outstanding requests. This ensures that we
* won't suspend while a request is being processed.
*/
-int mmc_queue_suspend(struct mmc_queue *mq)
+int mmc_queue_suspend(struct mmc_queue *mq, int wait)
{
struct request_queue *q = mq->queue;
unsigned long flags;
@@ -457,7 +458,7 @@
spin_unlock_irqrestore(q->queue_lock, flags);
rc = down_trylock(&mq->thread_sem);
- if (rc) {
+ if (rc && !wait) {
/*
* Failed to take the lock so better to abort the
* suspend because mmcqd thread is processing requests.
@@ -467,6 +468,9 @@
blk_start_queue(q);
spin_unlock_irqrestore(q->queue_lock, flags);
rc = -EBUSY;
+ } else if (rc && wait) {
+ down(&mq->thread_sem);
+ rc = 0;
}
}
return rc;
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index 9280d1b..d1fe01c 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -60,7 +60,7 @@
extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *,
const char *);
extern void mmc_cleanup_queue(struct mmc_queue *);
-extern int mmc_queue_suspend(struct mmc_queue *);
+extern int mmc_queue_suspend(struct mmc_queue *, int);
extern void mmc_queue_resume(struct mmc_queue *);
extern unsigned int mmc_queue_map_sg(struct mmc_queue *,
diff --git a/drivers/net/usb/rmnet_usb_ctrl.h b/drivers/net/usb/rmnet_usb.h
similarity index 61%
rename from drivers/net/usb/rmnet_usb_ctrl.h
rename to drivers/net/usb/rmnet_usb.h
index f0c169f..36002df 100644
--- a/drivers/net/usb/rmnet_usb_ctrl.h
+++ b/drivers/net/usb/rmnet_usb.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -10,8 +10,8 @@
* GNU General Public License for more details.
*/
-#ifndef __RMNET_USB_CTRL_H
-#define __RMNET_USB_CTRL_H
+#ifndef __RMNET_USB_H
+#define __RMNET_USB_H
#include <linux/mutex.h>
#include <linux/usb.h>
@@ -19,7 +19,36 @@
#include <linux/usb/ch9.h>
#include <linux/usb/cdc.h>
-#define CTRL_DEV_MAX_LEN 10
+#define MAX_RMNET_DEVS 4
+#define MAX_RMNET_INSTS_PER_DEV 17
+#define TOTAL_RMNET_DEV_COUNT (MAX_RMNET_DEVS * MAX_RMNET_INSTS_PER_DEV)
+
+#define CTRL_DEV_MAX_LEN 10
+
+#define RMNET_CTRL_DEV_OPEN 0
+#define RMNET_CTRL_DEV_READY 1
+#define RMNET_CTRL_DEV_MUX_EN 2
+
+/*MUX header bit masks*/
+#define MUX_CTRL_MASK 0x1
+#define MUX_PAD_SHIFT 0x2
+
+/*max padding bytes for n byte alignment*/
+#define MAX_PAD_BYTES(n) (n-1)
+
+/*
+ *MUX Header Format
+ *BIT 0 : Mux type 0: Data, 1: control
+ *BIT 1: Reserved
+ *BIT 2-7: Pad bytes
+ *BIT 8-15: Mux ID
+ *BIT 16-31: PACKET_LEN_WITH_PADDING (Bytes)
+ */
+struct mux_hdr {
+ __u8 padding_info;
+ __u8 mux_id;
+ __le16 pkt_len_w_padding;
+} __packed;
struct rmnet_ctrl_dev {
@@ -28,6 +57,10 @@
struct cdev cdev;
struct device *devicep;
+ unsigned ch_id;
+
+ /*to identify the usb device*/
+ unsigned id;
struct usb_interface *intf;
unsigned int int_pipe;
@@ -48,24 +81,17 @@
struct workqueue_struct *wq;
struct work_struct get_encap_work;
- unsigned is_opened;
+ unsigned long status;
- bool is_connected;
+ bool claimed;
+
+ unsigned int mdm_wait_timeout;
/*input control lines (DSR, CTS, CD, RI)*/
unsigned int cbits_tolocal;
-
/*output control lines (DTR, RTS)*/
unsigned int cbits_tomdm;
- /*
- * track first resp available from mdm when it boots up
- * to avoid bigger timeout value used by qmuxd
- */
- bool resp_available;
-
- unsigned int mdm_wait_timeout;
-
/*counters*/
unsigned int snd_encap_cmd_cnt;
unsigned int get_encap_resp_cnt;
@@ -76,15 +102,16 @@
unsigned int zlp_cnt;
};
-extern struct rmnet_ctrl_dev *ctrl_dev[];
+extern struct workqueue_struct *usbnet_wq;
extern int rmnet_usb_ctrl_start_rx(struct rmnet_ctrl_dev *);
extern int rmnet_usb_ctrl_suspend(struct rmnet_ctrl_dev *dev);
-extern int rmnet_usb_ctrl_init(void);
-extern void rmnet_usb_ctrl_exit(void);
+extern int rmnet_usb_ctrl_init(int num_devs, int insts_per_dev);
+extern void rmnet_usb_ctrl_exit(int num_devs, int insts_per_dev);
extern int rmnet_usb_ctrl_probe(struct usb_interface *intf,
- struct usb_host_endpoint *status,
- struct rmnet_ctrl_dev *dev);
+ struct usb_host_endpoint *int_in,
+ unsigned long rmnet_devnum,
+ unsigned long *data);
extern void rmnet_usb_ctrl_disconnect(struct rmnet_ctrl_dev *);
#endif /* __RMNET_USB_H*/
diff --git a/drivers/net/usb/rmnet_usb_ctrl.c b/drivers/net/usb/rmnet_usb_ctrl.c
index 283c6b0..0772592 100644
--- a/drivers/net/usb/rmnet_usb_ctrl.c
+++ b/drivers/net/usb/rmnet_usb_ctrl.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -19,10 +19,11 @@
#include <linux/poll.h>
#include <linux/ratelimit.h>
#include <linux/debugfs.h>
-#include "rmnet_usb_ctrl.h"
+#include "rmnet_usb.h"
-#define DEVICE_NAME "hsicctl"
-#define NUM_CTRL_CHANNELS 4
+static char *rmnet_dev_names[MAX_RMNET_DEVS] = {"hsicctl"};
+module_param_array(rmnet_dev_names, charp, NULL, S_IRUGO | S_IWUSR);
+
#define DEFAULT_READ_URB_LENGTH 0x1000
#define UNLINK_TIMEOUT_MS 500 /*random value*/
@@ -93,13 +94,19 @@
pr_info(x); \
} while (0)
-struct rmnet_ctrl_dev *ctrl_dev[NUM_CTRL_CHANNELS];
-struct class *ctrldev_classp;
-static dev_t ctrldev_num;
+/* passed in rmnet_usb_ctrl_init */
+static int num_devs;
+static int insts_per_dev;
+
+/* dynamically allocated 2-D array of num_devs*insts_per_dev ctrl_devs */
+static struct rmnet_ctrl_dev **ctrl_devs;
+static struct class *ctrldev_classp[MAX_RMNET_DEVS];
+static dev_t ctrldev_num[MAX_RMNET_DEVS];
struct ctrl_pkt {
size_t data_size;
void *data;
+ void *ctxt;
};
struct ctrl_pkt_list_elem {
@@ -109,18 +116,56 @@
static void resp_avail_cb(struct urb *);
-static int is_dev_connected(struct rmnet_ctrl_dev *dev)
+static int rmnet_usb_ctrl_dmux(struct ctrl_pkt_list_elem *clist)
{
- if (dev) {
- mutex_lock(&dev->dev_lock);
- if (!dev->is_connected) {
- mutex_unlock(&dev->dev_lock);
- return 0;
- }
- mutex_unlock(&dev->dev_lock);
- return 1;
+ struct mux_hdr *hdr;
+ size_t pad_len;
+ size_t total_len;
+ unsigned int mux_id;
+
+ hdr = (struct mux_hdr *)clist->cpkt.data;
+ pad_len = hdr->padding_info >> MUX_PAD_SHIFT;
+ if (pad_len > MAX_PAD_BYTES(4)) {
+ pr_err_ratelimited("%s: Invalid pad len %d\n", __func__,
+ pad_len);
+ return -EINVAL;
}
- return 0;
+
+ mux_id = hdr->mux_id;
+ if (!mux_id || mux_id > insts_per_dev) {
+ pr_err_ratelimited("%s: Invalid mux id %d\n", __func__, mux_id);
+ return -EINVAL;
+ }
+
+ total_len = le16_to_cpu(hdr->pkt_len_w_padding);
+ if (!total_len || !(total_len - pad_len)) {
+ pr_err_ratelimited("%s: Invalid pkt length %d\n", __func__,
+ total_len);
+ return -EINVAL;
+ }
+
+ clist->cpkt.data_size = total_len - pad_len;
+
+ return mux_id - 1;
+}
+
+static void rmnet_usb_ctrl_mux(unsigned int id, struct ctrl_pkt *cpkt)
+{
+ struct mux_hdr *hdr;
+ size_t len;
+ size_t pad_len = 0;
+
+ hdr = (struct mux_hdr *)cpkt->data;
+ hdr->mux_id = id + 1;
+ len = cpkt->data_size - sizeof(struct mux_hdr) - MAX_PAD_BYTES(4);
+
+ /*add padding if len is not 4 byte aligned*/
+ pad_len = ALIGN(len, 4) - len;
+
+ hdr->pkt_len_w_padding = cpu_to_le16(len + pad_len);
+ hdr->padding_info = (pad_len << MUX_PAD_SHIFT) | MUX_CTRL_MASK;
+
+ cpkt->data_size = sizeof(struct mux_hdr) + hdr->pkt_len_w_padding;
}
static void get_encap_work(struct work_struct *w)
@@ -130,6 +175,9 @@
container_of(w, struct rmnet_ctrl_dev, get_encap_work);
int status;
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status))
+ return;
+
udev = interface_to_usbdev(dev->intf);
status = usb_autopm_get_interface(dev->intf);
@@ -216,14 +264,17 @@
case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
dev->resp_avail_cnt++;
- usb_mark_last_busy(udev);
- queue_work(dev->wq, &dev->get_encap_work);
-
- if (!dev->resp_available) {
- dev->resp_available = true;
+ /* If MUX is not enabled, wakeup up the open process
+ * upon first notify response available.
+ */
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status)) {
+ set_bit(RMNET_CTRL_DEV_READY, &dev->status);
wake_up(&dev->open_wait_queue);
}
+ usb_mark_last_busy(udev);
+ queue_work(dev->wq, &dev->get_encap_work);
+
return;
default:
dev_err(dev->devicep,
@@ -246,9 +297,9 @@
{
struct usb_device *udev;
struct ctrl_pkt_list_elem *list_elem = NULL;
- struct rmnet_ctrl_dev *dev = urb->context;
+ struct rmnet_ctrl_dev *rx_dev, *dev = urb->context;
void *cpkt;
- int status = 0;
+ int ch_id, status = 0;
size_t cpkt_size = 0;
udev = interface_to_usbdev(dev->intf);
@@ -258,7 +309,6 @@
switch (urb->status) {
case 0:
/*success*/
- dev->get_encap_resp_cnt++;
break;
/*do not resubmit*/
@@ -303,11 +353,27 @@
}
memcpy(list_elem->cpkt.data, cpkt, cpkt_size);
list_elem->cpkt.data_size = cpkt_size;
- spin_lock(&dev->rx_lock);
- list_add_tail(&list_elem->list, &dev->rx_list);
- spin_unlock(&dev->rx_lock);
- wake_up(&dev->read_wait_queue);
+ rx_dev = dev;
+
+ if (test_bit(RMNET_CTRL_DEV_MUX_EN, &dev->status)) {
+ ch_id = rmnet_usb_ctrl_dmux(list_elem);
+ if (ch_id < 0) {
+ kfree(list_elem->cpkt.data);
+ kfree(list_elem);
+ goto resubmit_int_urb;
+ }
+
+ rx_dev = &ctrl_devs[dev->id][ch_id];
+ }
+
+ rx_dev->get_encap_resp_cnt++;
+
+ spin_lock(&rx_dev->rx_lock);
+ list_add_tail(&list_elem->list, &rx_dev->rx_list);
+ spin_unlock(&rx_dev->rx_lock);
+
+ wake_up(&rx_dev->read_wait_queue);
resubmit_int_urb:
/*check if it is already submitted in resume*/
@@ -340,8 +406,6 @@
static int rmnet_usb_ctrl_alloc_rx(struct rmnet_ctrl_dev *dev)
{
- int retval = -ENOMEM;
-
dev->rcvurb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->rcvurb) {
pr_err("%s: Error allocating read urb\n", __func__);
@@ -367,14 +431,14 @@
kfree(dev->rcvbuf);
kfree(dev->in_ctlreq);
- return retval;
+ return -ENOMEM;
}
static int rmnet_usb_ctrl_write_cmd(struct rmnet_ctrl_dev *dev)
{
struct usb_device *udev;
- if (!is_dev_connected(dev))
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status))
return -ENODEV;
udev = interface_to_usbdev(dev->intf);
@@ -389,7 +453,8 @@
static void ctrl_write_callback(struct urb *urb)
{
- struct rmnet_ctrl_dev *dev = urb->context;
+ struct ctrl_pkt *cpkt = urb->context;
+ struct rmnet_ctrl_dev *dev = cpkt->ctxt;
if (urb->status) {
dev->tx_ctrl_err_cnt++;
@@ -400,18 +465,19 @@
kfree(urb->setup_packet);
kfree(urb->transfer_buffer);
usb_free_urb(urb);
+ kfree(cpkt);
usb_autopm_put_interface_async(dev->intf);
}
-static int rmnet_usb_ctrl_write(struct rmnet_ctrl_dev *dev, char *buf,
- size_t size)
+static int rmnet_usb_ctrl_write(struct rmnet_ctrl_dev *dev,
+ struct ctrl_pkt *cpkt, size_t size)
{
int result;
struct urb *sndurb;
struct usb_ctrlrequest *out_ctlreq;
struct usb_device *udev;
- if (!is_dev_connected(dev))
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status))
return -ENETRESET;
udev = interface_to_usbdev(dev->intf);
@@ -435,12 +501,12 @@
out_ctlreq->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
out_ctlreq->wValue = 0;
out_ctlreq->wIndex = dev->intf->cur_altsetting->desc.bInterfaceNumber;
- out_ctlreq->wLength = cpu_to_le16(size);
+ out_ctlreq->wLength = cpu_to_le16(cpkt->data_size);
usb_fill_control_urb(sndurb, udev,
usb_sndctrlpipe(udev, 0),
- (unsigned char *)out_ctlreq, (void *)buf, size,
- ctrl_write_callback, dev);
+ (unsigned char *)out_ctlreq, (void *)cpkt->data,
+ cpkt->data_size, ctrl_write_callback, cpkt);
result = usb_autopm_get_interface(dev->intf);
if (result < 0) {
@@ -483,16 +549,15 @@
if (!dev)
return -ENODEV;
- if (dev->is_opened)
+ if (test_bit(RMNET_CTRL_DEV_OPEN, &dev->status))
goto already_opened;
- /*block open to get first response available from mdm*/
- if (dev->mdm_wait_timeout && !dev->resp_available) {
+ if (dev->mdm_wait_timeout &&
+ !test_bit(RMNET_CTRL_DEV_READY, &dev->status)) {
retval = wait_event_interruptible_timeout(
- dev->open_wait_queue,
- dev->resp_available,
- msecs_to_jiffies(dev->mdm_wait_timeout *
- 1000));
+ dev->open_wait_queue,
+ test_bit(RMNET_CTRL_DEV_READY, &dev->status),
+ msecs_to_jiffies(dev->mdm_wait_timeout * 1000));
if (retval == 0) {
dev_err(dev->devicep, "%s: Timeout opening %s\n",
__func__, dev->name);
@@ -504,15 +569,13 @@
}
}
- if (!dev->resp_available) {
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status)) {
dev_dbg(dev->devicep, "%s: Connection timedout opening %s\n",
__func__, dev->name);
return -ETIMEDOUT;
}
- mutex_lock(&dev->dev_lock);
- dev->is_opened = 1;
- mutex_unlock(&dev->dev_lock);
+ set_bit(RMNET_CTRL_DEV_OPEN, &dev->status);
file->private_data = dev;
@@ -547,9 +610,7 @@
}
spin_unlock_irqrestore(&dev->rx_lock, flag);
- mutex_lock(&dev->dev_lock);
- dev->is_opened = 0;
- mutex_unlock(&dev->dev_lock);
+ clear_bit(RMNET_CTRL_DEV_OPEN, &dev->status);
time = usb_wait_anchor_empty_timeout(&dev->tx_submitted,
UNLINK_TIMEOUT_MS);
@@ -571,7 +632,7 @@
return POLLERR;
poll_wait(file, &dev->read_wait_queue, wait);
- if (!is_dev_connected(dev)) {
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status)) {
dev_dbg(dev->devicep, "%s: Device not connected\n",
__func__);
return POLLERR;
@@ -588,6 +649,7 @@
{
int retval = 0;
int bytes_to_read;
+ unsigned int hdr_len = 0;
struct rmnet_ctrl_dev *dev;
struct ctrl_pkt_list_elem *list_elem = NULL;
unsigned long flags;
@@ -599,7 +661,7 @@
DBG("%s: Read from %s\n", __func__, dev->name);
ctrl_read:
- if (!is_dev_connected(dev)) {
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status)) {
dev_dbg(dev->devicep, "%s: Device not connected\n",
__func__);
return -ENETRESET;
@@ -609,8 +671,8 @@
spin_unlock_irqrestore(&dev->rx_lock, flags);
retval = wait_event_interruptible(dev->read_wait_queue,
- !list_empty(&dev->rx_list) ||
- !is_dev_connected(dev));
+ !list_empty(&dev->rx_list) ||
+ !test_bit(RMNET_CTRL_DEV_READY, &dev->status));
if (retval < 0)
return retval;
@@ -628,7 +690,10 @@
}
spin_unlock_irqrestore(&dev->rx_lock, flags);
- if (copy_to_user(buf, list_elem->cpkt.data, bytes_to_read)) {
+ if (test_bit(RMNET_CTRL_DEV_MUX_EN, &dev->status))
+ hdr_len = sizeof(struct mux_hdr);
+
+ if (copy_to_user(buf, list_elem->cpkt.data + hdr_len, bytes_to_read)) {
dev_err(dev->devicep,
"%s: copy_to_user failed for %s\n",
__func__, dev->name);
@@ -651,7 +716,10 @@
size_t size, loff_t *pos)
{
int status;
+ size_t total_len;
void *wbuf;
+ void *actual_data;
+ struct ctrl_pkt *cpkt;
struct rmnet_ctrl_dev *dev = file->private_data;
if (!dev)
@@ -660,26 +728,46 @@
if (size <= 0)
return -EINVAL;
- if (!is_dev_connected(dev))
+ if (!test_bit(RMNET_CTRL_DEV_READY, &dev->status))
return -ENETRESET;
DBG("%s: Writing %i bytes on %s\n", __func__, size, dev->name);
- wbuf = kmalloc(size , GFP_KERNEL);
+ total_len = size;
+
+ if (test_bit(RMNET_CTRL_DEV_MUX_EN, &dev->status))
+ total_len += sizeof(struct mux_hdr) + MAX_PAD_BYTES(4);
+
+ wbuf = kmalloc(total_len , GFP_KERNEL);
if (!wbuf)
return -ENOMEM;
- status = copy_from_user(wbuf , buf, size);
+ cpkt = kmalloc(sizeof(struct ctrl_pkt), GFP_KERNEL);
+ if (!cpkt) {
+ kfree(wbuf);
+ return -ENOMEM;
+ }
+ actual_data = cpkt->data = wbuf;
+ cpkt->data_size = total_len;
+ cpkt->ctxt = dev;
+
+ if (test_bit(RMNET_CTRL_DEV_MUX_EN, &dev->status)) {
+ actual_data = wbuf + sizeof(struct mux_hdr);
+ rmnet_usb_ctrl_mux(dev->ch_id, cpkt);
+ }
+
+ status = copy_from_user(actual_data, buf, size);
if (status) {
dev_err(dev->devicep,
"%s: Unable to copy data from userspace %d\n",
__func__, status);
kfree(wbuf);
+ kfree(cpkt);
return status;
}
DUMP_BUFFER("Write: ", size, buf);
- status = rmnet_usb_ctrl_write(dev, wbuf, size);
+ status = rmnet_usb_ctrl_write(dev, cpkt, size);
if (status == size)
return size;
@@ -780,51 +868,43 @@
};
int rmnet_usb_ctrl_probe(struct usb_interface *intf,
- struct usb_host_endpoint *int_in, struct rmnet_ctrl_dev *dev)
+ struct usb_host_endpoint *int_in,
+ unsigned long rmnet_devnum,
+ unsigned long *data)
{
+ struct rmnet_ctrl_dev *dev = NULL;
u16 wMaxPacketSize;
struct usb_endpoint_descriptor *ep;
- struct usb_device *udev;
+ struct usb_device *udev = interface_to_usbdev(intf);
int interval;
- int ret = 0;
+ int ret = 0, n;
- udev = interface_to_usbdev(intf);
+ /* Find next available ctrl_dev */
+ for (n = 0; n < insts_per_dev; n++) {
+ dev = &ctrl_devs[rmnet_devnum][n];
+ if (!dev->claimed)
+ break;
+ }
- if (!dev) {
- pr_err("%s: Ctrl device not found\n", __func__);
+ if (!dev || n == insts_per_dev) {
+ pr_err("%s: No available ctrl devices for %lu\n", __func__,
+ rmnet_devnum);
return -ENODEV;
}
+
dev->int_pipe = usb_rcvintpipe(udev,
int_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
- mutex_lock(&dev->dev_lock);
dev->intf = intf;
- /*TBD: for now just update CD status*/
- dev->cbits_tolocal = ACM_CTRL_CD;
+ dev->id = rmnet_devnum;
- /*send DTR high to modem*/
- dev->cbits_tomdm = ACM_CTRL_DTR;
- mutex_unlock(&dev->dev_lock);
-
- dev->resp_available = false;
dev->snd_encap_cmd_cnt = 0;
dev->get_encap_resp_cnt = 0;
dev->resp_avail_cnt = 0;
dev->tx_ctrl_err_cnt = 0;
dev->set_ctrl_line_state_cnt = 0;
- ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
- USB_CDC_REQ_SET_CONTROL_LINE_STATE,
- (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE),
- dev->cbits_tomdm,
- dev->intf->cur_altsetting->desc.bInterfaceNumber,
- NULL, 0, USB_CTRL_SET_TIMEOUT);
- if (ret < 0)
- return ret;
-
- dev->set_ctrl_line_state_cnt++;
-
dev->inturb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->inturb) {
dev_err(dev->devicep, "Error allocating int urb\n");
@@ -861,22 +941,39 @@
usb_mark_last_busy(udev);
ret = rmnet_usb_ctrl_start_rx(dev);
- if (!ret)
- dev->is_connected = true;
+ if (ret) {
+ usb_free_urb(dev->inturb);
+ kfree(dev->intbuf);
+ return ret;
+ }
- return ret;
+ dev->claimed = true;
+
+ /*mux info is passed to data parameter*/
+ if (*data)
+ set_bit(RMNET_CTRL_DEV_MUX_EN, &dev->status);
+
+ *data = (unsigned long)dev;
+
+ /* If MUX is enabled, wakeup the open process here */
+ if (test_bit(RMNET_CTRL_DEV_MUX_EN, &dev->status)) {
+ set_bit(RMNET_CTRL_DEV_READY, &dev->status);
+ wake_up(&dev->open_wait_queue);
+ }
+
+ return 0;
}
void rmnet_usb_ctrl_disconnect(struct rmnet_ctrl_dev *dev)
{
+ dev->claimed = false;
+
+ clear_bit(RMNET_CTRL_DEV_READY, &dev->status);
mutex_lock(&dev->dev_lock);
-
/*TBD: for now just update CD status*/
dev->cbits_tolocal = ~ACM_CTRL_CD;
-
dev->cbits_tomdm = ~ACM_CTRL_DTR;
- dev->is_connected = false;
mutex_unlock(&dev->dev_lock);
wake_up(&dev->read_wait_queue);
@@ -901,50 +998,53 @@
struct rmnet_ctrl_dev *dev;
char *buf;
int ret;
- int i;
+ int i, n;
int temp = 0;
buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
- for (i = 0; i < NUM_CTRL_CHANNELS; i++) {
- dev = ctrl_dev[i];
- if (!dev)
- continue;
-
- temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
- "\n#ctrl_dev: %p Name: %s#\n"
- "snd encap cmd cnt %u\n"
- "resp avail cnt: %u\n"
- "get encap resp cnt: %u\n"
- "set ctrl line state cnt: %u\n"
- "tx_err_cnt: %u\n"
- "cbits_tolocal: %d\n"
- "cbits_tomdm: %d\n"
- "mdm_wait_timeout: %u\n"
- "zlp_cnt: %u\n"
- "get_encap_failure_cnt %u\n"
- "dev opened: %s\n",
- dev, dev->name,
- dev->snd_encap_cmd_cnt,
- dev->resp_avail_cnt,
- dev->get_encap_resp_cnt,
- dev->set_ctrl_line_state_cnt,
- dev->tx_ctrl_err_cnt,
- dev->cbits_tolocal,
- dev->cbits_tomdm,
- dev->mdm_wait_timeout,
- dev->zlp_cnt,
- dev->get_encap_failure_cnt,
- dev->is_opened ? "OPEN" : "CLOSE");
-
+ for (i = 0; i < num_devs; i++) {
+ for (n = 0; n < insts_per_dev; n++) {
+ dev = &ctrl_devs[i][n];
+ temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+ "\n#ctrl_dev: %p Name: %s#\n"
+ "snd encap cmd cnt %u\n"
+ "resp avail cnt: %u\n"
+ "get encap resp cnt: %u\n"
+ "set ctrl line state cnt: %u\n"
+ "tx_err_cnt: %u\n"
+ "cbits_tolocal: %d\n"
+ "cbits_tomdm: %d\n"
+ "mdm_wait_timeout: %u\n"
+ "zlp_cnt: %u\n"
+ "get_encap_failure_cnt %u\n"
+ "RMNET_CTRL_DEV_MUX_EN: %d\n"
+ "RMNET_CTRL_DEV_OPEN: %d\n"
+ "RMNET_CTRL_DEV_READY: %d\n",
+ dev, dev->name,
+ dev->snd_encap_cmd_cnt,
+ dev->resp_avail_cnt,
+ dev->get_encap_resp_cnt,
+ dev->set_ctrl_line_state_cnt,
+ dev->tx_ctrl_err_cnt,
+ dev->cbits_tolocal,
+ dev->cbits_tomdm,
+ dev->mdm_wait_timeout,
+ dev->zlp_cnt,
+ dev->get_encap_failure_cnt,
+ test_bit(RMNET_CTRL_DEV_MUX_EN,
+ &dev->status),
+ test_bit(RMNET_CTRL_DEV_OPEN,
+ &dev->status),
+ test_bit(RMNET_CTRL_DEV_READY,
+ &dev->status));
+ }
}
ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
-
kfree(buf);
-
return ret;
}
@@ -952,19 +1052,19 @@
buf, size_t count, loff_t *ppos)
{
struct rmnet_ctrl_dev *dev;
- int i;
+ int i, n;
- for (i = 0; i < NUM_CTRL_CHANNELS; i++) {
- dev = ctrl_dev[i];
- if (!dev)
- continue;
+ for (i = 0; i < num_devs; i++) {
+ for (n = 0; n < insts_per_dev; n++) {
+ dev = &ctrl_devs[i][n];
- dev->snd_encap_cmd_cnt = 0;
- dev->resp_avail_cnt = 0;
- dev->get_encap_resp_cnt = 0;
- dev->set_ctrl_line_state_cnt = 0;
- dev->tx_ctrl_err_cnt = 0;
- dev->zlp_cnt = 0;
+ dev->snd_encap_cmd_cnt = 0;
+ dev->resp_avail_cnt = 0;
+ dev->get_encap_resp_cnt = 0;
+ dev->set_ctrl_line_state_cnt = 0;
+ dev->tx_ctrl_err_cnt = 0;
+ dev->zlp_cnt = 0;
+ }
}
return count;
}
@@ -999,144 +1099,152 @@
static void rmnet_usb_ctrl_debugfs_exit(void) { }
#endif
-int rmnet_usb_ctrl_init(void)
+int rmnet_usb_ctrl_init(int no_rmnet_devs, int no_rmnet_insts_per_dev)
{
struct rmnet_ctrl_dev *dev;
- int n;
+ int i, n;
int status;
- for (n = 0; n < NUM_CTRL_CHANNELS; ++n) {
+ num_devs = no_rmnet_devs;
+ insts_per_dev = no_rmnet_insts_per_dev;
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev) {
- status = -ENOMEM;
- goto error0;
- }
- /*for debug purpose*/
- snprintf(dev->name, CTRL_DEV_MAX_LEN, "hsicctl%d", n);
+ ctrl_devs = kzalloc(num_devs * sizeof(*ctrl_devs), GFP_KERNEL);
+ if (!ctrl_devs)
+ return -ENOMEM;
- dev->wq = create_singlethread_workqueue(dev->name);
- if (!dev->wq) {
- pr_err("unable to allocate workqueue");
- kfree(dev);
- goto error0;
- }
+ for (i = 0; i < num_devs; i++) {
+ ctrl_devs[i] = kzalloc(insts_per_dev * sizeof(*ctrl_devs[i]),
+ GFP_KERNEL);
+ if (!ctrl_devs[i])
+ return -ENOMEM;
- mutex_init(&dev->dev_lock);
- spin_lock_init(&dev->rx_lock);
- init_waitqueue_head(&dev->read_wait_queue);
- init_waitqueue_head(&dev->open_wait_queue);
- INIT_LIST_HEAD(&dev->rx_list);
- init_usb_anchor(&dev->tx_submitted);
- init_usb_anchor(&dev->rx_submitted);
- INIT_WORK(&dev->get_encap_work, get_encap_work);
-
- status = rmnet_usb_ctrl_alloc_rx(dev);
- if (status < 0) {
- kfree(dev);
- goto error0;
- }
-
- ctrl_dev[n] = dev;
- }
-
- status = alloc_chrdev_region(&ctrldev_num, 0, NUM_CTRL_CHANNELS,
- DEVICE_NAME);
- if (IS_ERR_VALUE(status)) {
- pr_err("ERROR:%s: alloc_chrdev_region() ret %i.\n",
- __func__, status);
- goto error0;
- }
-
- ctrldev_classp = class_create(THIS_MODULE, DEVICE_NAME);
- if (IS_ERR(ctrldev_classp)) {
- pr_err("ERROR:%s: class_create() ENOMEM\n", __func__);
- status = -ENOMEM;
- goto error1;
- }
- for (n = 0; n < NUM_CTRL_CHANNELS; ++n) {
- cdev_init(&ctrl_dev[n]->cdev, &ctrldev_fops);
- ctrl_dev[n]->cdev.owner = THIS_MODULE;
-
- status = cdev_add(&ctrl_dev[n]->cdev, (ctrldev_num + n), 1);
-
+ status = alloc_chrdev_region(&ctrldev_num[i], 0, insts_per_dev,
+ rmnet_dev_names[i]);
if (IS_ERR_VALUE(status)) {
- pr_err("%s: cdev_add() ret %i\n", __func__, status);
- kfree(ctrl_dev[n]);
- goto error2;
+ pr_err("ERROR:%s: alloc_chrdev_region() ret %i.\n",
+ __func__, status);
+ return status;
}
- ctrl_dev[n]->devicep =
- device_create(ctrldev_classp, NULL,
- (ctrldev_num + n), NULL,
- DEVICE_NAME "%d", n);
+ ctrldev_classp[i] = class_create(THIS_MODULE,
+ rmnet_dev_names[i]);
+ if (IS_ERR(ctrldev_classp[i])) {
+ pr_err("ERROR:%s: class_create() ENOMEM\n", __func__);
+ status = PTR_ERR(ctrldev_classp[i]);
+ return status;
+ }
- if (IS_ERR(ctrl_dev[n]->devicep)) {
- pr_err("%s: device_create() ENOMEM\n", __func__);
- status = -ENOMEM;
- cdev_del(&ctrl_dev[n]->cdev);
- kfree(ctrl_dev[n]);
- goto error2;
+ for (n = 0; n < insts_per_dev; n++) {
+ dev = &ctrl_devs[i][n];
+
+ /*for debug purpose*/
+ snprintf(dev->name, CTRL_DEV_MAX_LEN, "%s%d",
+ rmnet_dev_names[i], n);
+
+ dev->wq = create_singlethread_workqueue(dev->name);
+ if (!dev->wq) {
+ pr_err("unable to allocate workqueue");
+ kfree(dev);
+ return -ENOMEM;
+ }
+
+ dev->ch_id = n;
+
+ mutex_init(&dev->dev_lock);
+ spin_lock_init(&dev->rx_lock);
+ init_waitqueue_head(&dev->read_wait_queue);
+ init_waitqueue_head(&dev->open_wait_queue);
+ INIT_LIST_HEAD(&dev->rx_list);
+ init_usb_anchor(&dev->tx_submitted);
+ init_usb_anchor(&dev->rx_submitted);
+ INIT_WORK(&dev->get_encap_work, get_encap_work);
+
+ cdev_init(&dev->cdev, &ctrldev_fops);
+ dev->cdev.owner = THIS_MODULE;
+
+ status = cdev_add(&dev->cdev, (ctrldev_num[i] + n), 1);
+ if (status) {
+ pr_err("%s: cdev_add() ret %i\n", __func__,
+ status);
+ destroy_workqueue(dev->wq);
+ kfree(dev);
+ return status;
+ }
+
+ dev->devicep = device_create(ctrldev_classp[i], NULL,
+ (ctrldev_num[i] + n), NULL,
+ "%s%d", rmnet_dev_names[i],
+ n);
+ if (IS_ERR(dev->devicep)) {
+ pr_err("%s: device_create() returned %ld\n",
+ __func__, PTR_ERR(dev->devicep));
+ cdev_del(&dev->cdev);
+ destroy_workqueue(dev->wq);
+ kfree(dev);
+ return PTR_ERR(dev->devicep);
+ }
+
+ /*create /sys/class/hsicctl/hsicctlx/modem_wait*/
+ status = device_create_file(dev->devicep,
+ &dev_attr_modem_wait);
+ if (status) {
+ device_destroy(dev->devicep->class,
+ dev->devicep->devt);
+ cdev_del(&dev->cdev);
+ destroy_workqueue(dev->wq);
+ kfree(dev);
+ return status;
+ }
+ dev_set_drvdata(dev->devicep, dev);
+
+ status = rmnet_usb_ctrl_alloc_rx(dev);
+ if (status) {
+ device_remove_file(dev->devicep,
+ &dev_attr_modem_wait);
+ device_destroy(dev->devicep->class,
+ dev->devicep->devt);
+ cdev_del(&dev->cdev);
+ destroy_workqueue(dev->wq);
+ kfree(dev);
+ return status;
+ }
}
- /*create /sys/class/hsicctl/hsicctlx/modem_wait*/
- status = device_create_file(ctrl_dev[n]->devicep,
- &dev_attr_modem_wait);
- if (status) {
- device_destroy(ctrldev_classp,
- MKDEV(MAJOR(ctrldev_num), n));
- cdev_del(&ctrl_dev[n]->cdev);
- kfree(ctrl_dev[n]);
- goto error2;
- }
- dev_set_drvdata(ctrl_dev[n]->devicep, ctrl_dev[n]);
}
rmnet_usb_ctrl_debugfs_init();
pr_info("rmnet usb ctrl Initialized.\n");
return 0;
-
-error2:
- while (--n >= 0) {
- cdev_del(&ctrl_dev[n]->cdev);
- device_destroy(ctrldev_classp,
- MKDEV(MAJOR(ctrldev_num), n));
- }
-
- class_destroy(ctrldev_classp);
- n = NUM_CTRL_CHANNELS;
-error1:
- unregister_chrdev_region(MAJOR(ctrldev_num), NUM_CTRL_CHANNELS);
-error0:
- while (--n >= 0)
- kfree(ctrl_dev[n]);
-
- return status;
}
-void rmnet_usb_ctrl_exit(void)
+static void free_rmnet_ctrl_dev(struct rmnet_ctrl_dev *dev)
{
- int i;
+ kfree(dev->in_ctlreq);
+ kfree(dev->rcvbuf);
+ kfree(dev->intbuf);
+ usb_free_urb(dev->rcvurb);
+ usb_free_urb(dev->inturb);
+ device_remove_file(dev->devicep, &dev_attr_modem_wait);
+ cdev_del(&dev->cdev);
+ destroy_workqueue(dev->wq);
+ device_destroy(dev->devicep->class,
+ dev->devicep->devt);
+}
- for (i = 0; i < NUM_CTRL_CHANNELS; ++i) {
- if (!ctrl_dev[i])
- return;
+void rmnet_usb_ctrl_exit(int no_rmnet_devs, int no_rmnet_insts_per_dev)
+{
+ int i, n;
- kfree(ctrl_dev[i]->in_ctlreq);
- kfree(ctrl_dev[i]->rcvbuf);
- kfree(ctrl_dev[i]->intbuf);
- usb_free_urb(ctrl_dev[i]->rcvurb);
- usb_free_urb(ctrl_dev[i]->inturb);
-#if defined(DEBUG)
- device_remove_file(ctrl_dev[i]->devicep, &dev_attr_modem_wait);
-#endif
- cdev_del(&ctrl_dev[i]->cdev);
- destroy_workqueue(ctrl_dev[i]->wq);
- kfree(ctrl_dev[i]);
- ctrl_dev[i] = NULL;
- device_destroy(ctrldev_classp, MKDEV(MAJOR(ctrldev_num), i));
+ for (i = 0; i < no_rmnet_devs; i++) {
+ for (n = 0; n < no_rmnet_insts_per_dev; n++)
+ free_rmnet_ctrl_dev(&ctrl_devs[i][n]);
+
+ kfree(ctrl_devs[i]);
+
+ class_destroy(ctrldev_classp[i]);
+ if (ctrldev_num[i])
+ unregister_chrdev_region(ctrldev_num[i], insts_per_dev);
}
- class_destroy(ctrldev_classp);
- unregister_chrdev_region(MAJOR(ctrldev_num), NUM_CTRL_CHANNELS);
+ kfree(ctrl_devs);
rmnet_usb_ctrl_debugfs_exit();
}
diff --git a/drivers/net/usb/rmnet_usb_data.c b/drivers/net/usb/rmnet_usb_data.c
index 2a57b36..f9bb5c8 100644
--- a/drivers/net/usb/rmnet_usb_data.c
+++ b/drivers/net/usb/rmnet_usb_data.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -17,14 +17,48 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/usb.h>
+#include <linux/ratelimit.h>
#include <linux/usb/usbnet.h>
#include <linux/msm_rmnet.h>
-#include "rmnet_usb_ctrl.h"
+#include "rmnet_usb.h"
#define RMNET_DATA_LEN 2000
-#define HEADROOM_FOR_QOS 8
+#define RMNET_HEADROOM_W_MUX (sizeof(struct mux_hdr) + \
+ sizeof(struct QMI_QOS_HDR_S))
+#define RMNET_HEADROOM sizeof(struct QMI_QOS_HDR_S)
+#define RMNET_TAILROOM MAX_PAD_BYTES(4);
+static unsigned int no_rmnet_devs = 1;
+module_param(no_rmnet_devs, uint, S_IRUGO | S_IWUSR);
+
+unsigned int no_rmnet_insts_per_dev = 4;
+module_param(no_rmnet_insts_per_dev, uint, S_IRUGO | S_IWUSR);
+
+/*
+ * To support mux on multiple devices, bit position represents device
+ * and value represnts if mux is enabled or disabled.
+ * e.g. bit 0: mdm over HSIC, bit1: mdm over hsusb
+ */
+static unsigned long mux_enabled;
+module_param(mux_enabled, ulong, S_IRUGO | S_IWUSR);
+
+static unsigned int no_fwd_rmnet_links;
+module_param(no_fwd_rmnet_links, uint, S_IRUGO | S_IWUSR);
+
+struct usbnet *unet_list[TOTAL_RMNET_DEV_COUNT];
+
+/* net device name prefixes, indexed by driver_info->data */
+static const char * const rmnet_names[] = {
+ "rmnet_usb%d",
+ "rmnet2_usb%d",
+};
+
+/* net device reverse link name prefixes, indexed by driver_info->data */
+static const char * const rev_rmnet_names[] = {
+ "rev_rmnet_usb%d",
+ "rev_rmnet2_usb%d",
+};
static int data_msg_dbg_mask;
enum {
@@ -80,43 +114,121 @@
#define DBG1(x...) DBG(DEBUG_MASK_LVL1, x)
#define DBG2(x...) DBG(DEBUG_MASK_LVL2, x)
-static void rmnet_usb_setup(struct net_device *);
+static int rmnet_data_start(void);
+static bool rmnet_data_init;
+
+static int rmnet_init(const char *val, const struct kernel_param *kp)
+{
+ int ret = 0;
+
+ if (rmnet_data_init) {
+ pr_err("dynamic setting rmnet params currently unsupported\n");
+ return -EINVAL;
+ }
+
+ ret = param_set_bool(val, kp);
+ if (ret)
+ return ret;
+
+ rmnet_data_start();
+
+ return ret;
+}
+
+static struct kernel_param_ops rmnet_init_ops = {
+ .set = rmnet_init,
+ .get = param_get_bool,
+};
+module_param_cb(rmnet_data_init, &rmnet_init_ops, &rmnet_data_init,
+ S_IRUGO | S_IWUSR);
+
+static void rmnet_usb_setup(struct net_device *, int mux_enabled);
static int rmnet_ioctl(struct net_device *, struct ifreq *, int);
static int rmnet_usb_suspend(struct usb_interface *iface, pm_message_t message)
{
- struct usbnet *unet;
+ struct usbnet *unet = usb_get_intfdata(iface);
struct rmnet_ctrl_dev *dev;
+ int i, n, rdev_cnt, unet_id;
+ int retval = 0;
- unet = usb_get_intfdata(iface);
+ rdev_cnt = unet->data[4] ? no_rmnet_insts_per_dev : 1;
- dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ for (n = 0; n < rdev_cnt; n++) {
+ unet_id = n + unet->driver_info->data * no_rmnet_insts_per_dev;
+ unet =
+ unet->data[4] ? unet_list[unet_id] : usb_get_intfdata(iface);
- if (work_busy(&dev->get_encap_work))
- return -EBUSY;
+ dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ spin_lock_irq(&unet->txq.lock);
+ if (work_busy(&dev->get_encap_work) || unet->txq.qlen) {
+ spin_unlock_irq(&unet->txq.lock);
+ retval = -EBUSY;
+ goto abort_suspend;
+ }
- if (usbnet_suspend(iface, message))
- return -EBUSY;
+ set_bit(EVENT_DEV_ASLEEP, &unet->flags);
+ spin_unlock_irq(&unet->txq.lock);
- usb_kill_anchored_urbs(&dev->rx_submitted);
+ usb_kill_anchored_urbs(&dev->rx_submitted);
+ if (work_busy(&dev->get_encap_work)) {
+ spin_lock_irq(&unet->txq.lock);
+ clear_bit(EVENT_DEV_ASLEEP, &unet->flags);
+ spin_unlock_irq(&unet->txq.lock);
+ retval = -EBUSY;
+ goto abort_suspend;
+ }
+ }
+
+ for (n = 0; n < rdev_cnt; n++) {
+ unet_id = n + unet->driver_info->data * no_rmnet_insts_per_dev;
+ unet =
+ unet->data[4] ? unet_list[unet_id] : usb_get_intfdata(iface);
+
+ dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ netif_device_detach(unet->net);
+ usbnet_terminate_urbs(unet);
+ netif_device_attach(unet->net);
+ }
return 0;
+
+abort_suspend:
+ for (i = 0; i < n; i++) {
+ unet_id = i + unet->driver_info->data * no_rmnet_insts_per_dev;
+ unet =
+ unet->data[4] ? unet_list[unet_id] : usb_get_intfdata(iface);
+
+ dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ rmnet_usb_ctrl_start_rx(dev);
+ spin_lock_irq(&unet->txq.lock);
+ clear_bit(EVENT_DEV_ASLEEP, &unet->flags);
+ spin_unlock_irq(&unet->txq.lock);
+ }
+ return retval;
}
static int rmnet_usb_resume(struct usb_interface *iface)
{
- int retval = 0;
- struct usbnet *unet;
+ struct usbnet *unet = usb_get_intfdata(iface);
struct rmnet_ctrl_dev *dev;
+ int n, rdev_cnt, unet_id;
- unet = usb_get_intfdata(iface);
+ rdev_cnt = unet->data[4] ? no_rmnet_insts_per_dev : 1;
- dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ for (n = 0; n < rdev_cnt; n++) {
+ unet_id = n + unet->driver_info->data * no_rmnet_insts_per_dev;
+ unet =
+ unet->data[4] ? unet_list[unet_id] : usb_get_intfdata(iface);
- usbnet_resume(iface);
- retval = rmnet_usb_ctrl_start_rx(dev);
+ dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ rmnet_usb_ctrl_start_rx(dev);
+ usb_set_intfdata(iface, unet);
+ unet->suspend_count = 1;
+ usbnet_resume(iface);
+ }
- return retval;
+ return 0;
}
static int rmnet_usb_bind(struct usbnet *usbnet, struct usb_interface *iface)
@@ -125,9 +237,13 @@
struct usb_host_endpoint *bulk_in = NULL;
struct usb_host_endpoint *bulk_out = NULL;
struct usb_host_endpoint *int_in = NULL;
+ struct driver_info *info = usbnet->driver_info;
int status = 0;
int i;
int numends;
+ bool mux;
+
+ mux = test_bit(info->data, &mux_enabled);
numends = iface->cur_altsetting->desc.bNumEndpoints;
for (i = 0; i < numends; i++) {
@@ -158,13 +274,72 @@
usbnet->status = int_in;
/*change name of net device to rmnet_usbx here*/
- strlcpy(usbnet->net->name, "rmnet_usb%d", IFNAMSIZ);
+ if (mux && (info->in > no_fwd_rmnet_links))
+ strlcpy(usbnet->net->name, rev_rmnet_names[info->data],
+ IFNAMSIZ);
+ else
+ strlcpy(usbnet->net->name, rmnet_names[info->data],
+ IFNAMSIZ);
- /*TBD: update rx_urb_size, curently set to eth frame len by usbnet*/
+ if (mux)
+ usbnet->rx_urb_size = usbnet->hard_mtu + sizeof(struct mux_hdr)
+ + MAX_PAD_BYTES(4);
out:
return status;
}
+static int rmnet_usb_data_dmux(struct sk_buff *skb, struct urb *rx_urb)
+{
+ struct mux_hdr *hdr;
+ size_t pad_len;
+ size_t total_len;
+ unsigned int mux_id;
+
+ hdr = (struct mux_hdr *)skb->data;
+ mux_id = hdr->mux_id;
+ if (!mux_id || mux_id > no_rmnet_insts_per_dev) {
+ pr_err_ratelimited("%s: Invalid data channel id %u.\n",
+ __func__, mux_id);
+ return -EINVAL;
+ }
+
+ pad_len = hdr->padding_info >> MUX_PAD_SHIFT;
+ if (pad_len > MAX_PAD_BYTES(4)) {
+ pr_err_ratelimited("%s: Invalid pad len %d\n",
+ __func__, pad_len);
+ return -EINVAL;
+ }
+
+ total_len = le16_to_cpu(hdr->pkt_len_w_padding);
+ if (!total_len || !(total_len - pad_len)) {
+ pr_err_ratelimited("%s: Invalid pkt length %d\n", __func__,
+ total_len);
+ return -EINVAL;
+ }
+
+ skb->data = (unsigned char *)(hdr + 1);
+ skb_reset_tail_pointer(skb);
+ rx_urb->actual_length = total_len - pad_len;
+
+ return mux_id - 1;
+}
+
+static void rmnet_usb_data_mux(struct sk_buff *skb, unsigned int id)
+{
+ struct mux_hdr *hdr;
+ size_t len;
+
+ hdr = (struct mux_hdr *)skb_push(skb, sizeof(struct mux_hdr));
+ hdr->mux_id = id + 1;
+ len = skb->len - sizeof(struct mux_hdr);
+
+ /*add padding if len is not 4 byte aligned*/
+ skb_put(skb, ALIGN(len, 4) - len);
+
+ hdr->pkt_len_w_padding = cpu_to_le16(skb->len - sizeof(struct mux_hdr));
+ hdr->padding_info = (ALIGN(len, 4) - len) << MUX_PAD_SHIFT;
+}
+
static struct sk_buff *rmnet_usb_tx_fixup(struct usbnet *dev,
struct sk_buff *skb, gfp_t flags)
{
@@ -178,6 +353,9 @@
qmih->flow_id = skb->mark;
}
+ if (dev->data[4])
+ rmnet_usb_data_mux(skb, dev->data[3]);
+
DBG1("[%s] Tx packet #%lu len=%d mark=0x%x\n",
dev->net->name, dev->net->stats.tx_packets, skb->len, skb->mark);
@@ -206,10 +384,35 @@
return protocol;
}
-static int rmnet_usb_rx_fixup(struct usbnet *dev,
- struct sk_buff *skb)
+static void rmnet_usb_rx_complete(struct urb *rx_urb)
{
+ struct sk_buff *skb = (struct sk_buff *) rx_urb->context;
+ struct skb_data *entry = (struct skb_data *) skb->cb;
+ struct usbnet *dev = entry->dev;
+ unsigned int unet_offset;
+ unsigned int unet_id;
+ int mux_id;
+ unet_offset = dev->driver_info->data * no_rmnet_insts_per_dev;
+
+ if (!rx_urb->status && dev->data[4]) {
+ mux_id = rmnet_usb_data_dmux(skb, rx_urb);
+ if (mux_id < 0) {
+ /*resubmit urb and free skb in rx_complete*/
+ rx_urb->status = -EINVAL;
+ } else {
+ /*map urb to actual network iface based on mux id*/
+ unet_id = unet_offset + mux_id;
+ skb->dev = unet_list[unet_id]->net;
+ entry->dev = unet_list[unet_id];
+ }
+ }
+
+ rx_complete(rx_urb);
+}
+
+static int rmnet_usb_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
if (test_bit(RMNET_MODE_LLP_IP, &dev->data[0]))
skb->protocol = rmnet_ip_type_trans(skb, dev->net);
else /*set zero for eth mode*/
@@ -270,7 +473,6 @@
.ndo_validate_addr = 0,
};
-
static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct usbnet *unet = netdev_priv(dev);
@@ -306,7 +508,6 @@
dev->mtu = prev_mtu;
dev->addr_len = 0;
dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
- dev->needed_headroom = HEADROOM_FOR_QOS;
dev->netdev_ops = &rmnet_usb_ops_ip;
clear_bit(RMNET_MODE_LLP_ETH, &unet->data[0]);
set_bit(RMNET_MODE_LLP_IP, &unet->data[0]);
@@ -365,7 +566,7 @@
return rc;
}
-static void rmnet_usb_setup(struct net_device *dev)
+static void rmnet_usb_setup(struct net_device *dev, int mux_enabled)
{
/* Using Ethernet mode by default */
dev->netdev_ops = &rmnet_usb_ops_ether;
@@ -373,7 +574,15 @@
/* set this after calling ether_setup */
dev->mtu = RMNET_DATA_LEN;
- dev->needed_headroom = HEADROOM_FOR_QOS;
+ if (mux_enabled) {
+ dev->needed_headroom = RMNET_HEADROOM_W_MUX;
+
+ /*max pad bytes for 4 byte alignment*/
+ dev->needed_tailroom = RMNET_TAILROOM;
+ } else {
+ dev->needed_headroom = RMNET_HEADROOM;
+ }
+
random_ether_addr(dev->dev_addr);
dev->watchdog_timeo = 1000; /* 10 seconds? */
}
@@ -403,7 +612,6 @@
seq_printf(s, "tx errors: %lu\n", unet->net->stats.tx_errors);
seq_printf(s, "tx packets: %lu\n", unet->net->stats.tx_packets);
seq_printf(s, "tx bytes: %lu\n", unet->net->stats.tx_bytes);
- seq_printf(s, "suspend count: %d\n", unet->suspend_count);
seq_printf(s, "EVENT_DEV_OPEN: %d\n",
test_bit(EVENT_DEV_OPEN, &unet->flags));
seq_printf(s, "EVENT_TX_HALT: %d\n",
@@ -458,21 +666,25 @@
{
struct dentry *root = (struct dentry *)unet->data[2];
- debugfs_remove_recursive(root);
- unet->data[2] = 0;
+ if (root) {
+ debugfs_remove_recursive(root);
+ unet->data[2] = 0;
+ }
}
static int rmnet_usb_probe(struct usb_interface *iface,
const struct usb_device_id *prod)
{
struct usbnet *unet;
- struct driver_info *info;
+ struct driver_info *info = (struct driver_info *)prod->driver_info;
struct usb_device *udev;
- unsigned int iface_num;
- static int first_rmnet_iface_num = -EINVAL;
int status = 0;
+ unsigned int i, unet_id, rdev_cnt, n = 0;
+ bool mux;
+ struct rmnet_ctrl_dev *dev;
- iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
+ udev = interface_to_usbdev(iface);
+
if (iface->num_altsetting != 1) {
dev_err(&iface->dev, "%s invalid num_altsetting %u\n",
__func__, iface->num_altsetting);
@@ -480,45 +692,61 @@
goto out;
}
- info = (struct driver_info *)prod->driver_info;
- if (!test_bit(iface_num, &info->data))
- return -ENODEV;
+ mux = test_bit(info->data, &mux_enabled);
+ rdev_cnt = mux ? no_rmnet_insts_per_dev : 1;
+ info->in = 0;
- status = usbnet_probe(iface, prod);
- if (status < 0) {
- dev_err(&iface->dev, "usbnet_probe failed %d\n", status);
- goto out;
+ for (n = 0; n < rdev_cnt; n++) {
+
+ /* Use this filed to increment device count this will be
+ * used by bind to determin the forward link and reverse
+ * link network interface names.
+ */
+ info->in++;
+ status = usbnet_probe(iface, prod);
+ if (status < 0) {
+ dev_err(&iface->dev, "usbnet_probe failed %d\n",
+ status);
+ goto out;
+ }
+
+ unet_id = n + info->data * no_rmnet_insts_per_dev;
+
+ unet_list[unet_id] = unet = usb_get_intfdata(iface);
+
+ /*store mux id for later access*/
+ unet->data[3] = n;
+
+ /*save mux info for control and usbnet devices*/
+ unet->data[1] = unet->data[4] = mux;
+
+ /*set rmnet operation mode to eth by default*/
+ set_bit(RMNET_MODE_LLP_ETH, &unet->data[0]);
+
+ /*update net device*/
+ rmnet_usb_setup(unet->net, mux);
+
+ /*create /sys/class/net/rmnet_usbx/dbg_mask*/
+ status = device_create_file(&unet->net->dev,
+ &dev_attr_dbg_mask);
+ if (status) {
+ usbnet_disconnect(iface);
+ goto out;
+ }
+
+ status = rmnet_usb_ctrl_probe(iface, unet->status, info->data,
+ &unet->data[1]);
+ if (status) {
+ device_remove_file(&unet->net->dev, &dev_attr_dbg_mask);
+ usbnet_disconnect(iface);
+ goto out;
+ }
+
+ status = rmnet_usb_data_debugfs_init(unet);
+ if (status)
+ dev_dbg(&iface->dev,
+ "mode debugfs file is not available\n");
}
- unet = usb_get_intfdata(iface);
-
- /*set rmnet operation mode to eth by default*/
- set_bit(RMNET_MODE_LLP_ETH, &unet->data[0]);
-
- /*update net device*/
- rmnet_usb_setup(unet->net);
-
- /*create /sys/class/net/rmnet_usbx/dbg_mask*/
- status = device_create_file(&unet->net->dev, &dev_attr_dbg_mask);
- if (status)
- goto out;
-
- if (first_rmnet_iface_num == -EINVAL)
- first_rmnet_iface_num = iface_num;
-
- /*save control device intstance */
- unet->data[1] = (unsigned long)ctrl_dev \
- [iface_num - first_rmnet_iface_num];
-
- status = rmnet_usb_ctrl_probe(iface, unet->status,
- (struct rmnet_ctrl_dev *)unet->data[1]);
- if (status)
- goto out;
-
- status = rmnet_usb_data_debugfs_init(unet);
- if (status)
- dev_dbg(&iface->dev, "mode debugfs file is not available\n");
-
- udev = unet->udev;
usb_enable_autosuspend(udev);
@@ -528,83 +756,123 @@
device_set_wakeup_enable(&udev->parent->dev, 1);
}
+ return 0;
+
out:
+ for (i = 0; i < n; i++) {
+ /* This cleanup happens only for MUX case */
+ unet_id = i + info->data * no_rmnet_insts_per_dev;
+ unet = unet_list[unet_id];
+ dev = (struct rmnet_ctrl_dev *)unet->data[1];
+
+ rmnet_usb_data_debugfs_cleanup(unet);
+ rmnet_usb_ctrl_disconnect(dev);
+ device_remove_file(&unet->net->dev, &dev_attr_dbg_mask);
+ usb_set_intfdata(iface, unet_list[unet_id]);
+ usbnet_disconnect(iface);
+ unet_list[unet_id] = NULL;
+ }
+
return status;
}
static void rmnet_usb_disconnect(struct usb_interface *intf)
{
- struct usbnet *unet;
+ struct usbnet *unet = usb_get_intfdata(intf);
struct rmnet_ctrl_dev *dev;
+ unsigned int n, rdev_cnt, unet_id;
- unet = usb_get_intfdata(intf);
- if (!unet) {
- dev_err(&intf->dev, "%s:data device not found\n", __func__);
- return;
- }
+ rdev_cnt = unet->data[4] ? no_rmnet_insts_per_dev : 1;
device_set_wakeup_enable(&unet->udev->dev, 0);
- rmnet_usb_data_debugfs_cleanup(unet);
- dev = (struct rmnet_ctrl_dev *)unet->data[1];
- if (!dev) {
- dev_err(&intf->dev, "%s:ctrl device not found\n", __func__);
- return;
+ for (n = 0; n < rdev_cnt; n++) {
+ unet_id = n + unet->driver_info->data * no_rmnet_insts_per_dev;
+ unet =
+ unet->data[4] ? unet_list[unet_id] : usb_get_intfdata(intf);
+ device_remove_file(&unet->net->dev, &dev_attr_dbg_mask);
+
+ dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ rmnet_usb_ctrl_disconnect(dev);
+ unet->data[0] = 0;
+ unet->data[1] = 0;
+ rmnet_usb_data_debugfs_cleanup(unet);
+ usb_set_intfdata(intf, unet);
+ usbnet_disconnect(intf);
+ unet_list[unet_id] = NULL;
}
- unet->data[0] = 0;
- unet->data[1] = 0;
- rmnet_usb_ctrl_disconnect(dev);
- device_remove_file(&unet->net->dev, &dev_attr_dbg_mask);
- usbnet_disconnect(intf);
}
-/*bit position represents interface number*/
-#define PID9034_IFACE_MASK 0xF0
-#define PID9048_IFACE_MASK 0x1E0
-#define PID904C_IFACE_MASK 0x1C0
-
-static const struct driver_info rmnet_info_pid9034 = {
+static struct driver_info rmnet_info = {
.description = "RmNET net device",
.flags = FLAG_SEND_ZLP,
.bind = rmnet_usb_bind,
.tx_fixup = rmnet_usb_tx_fixup,
.rx_fixup = rmnet_usb_rx_fixup,
+ .rx_complete = rmnet_usb_rx_complete,
.manage_power = rmnet_usb_manage_power,
- .data = PID9034_IFACE_MASK,
+ .data = 0,
};
-static const struct driver_info rmnet_info_pid9048 = {
+static struct driver_info rmnet_usb_info = {
.description = "RmNET net device",
.flags = FLAG_SEND_ZLP,
.bind = rmnet_usb_bind,
.tx_fixup = rmnet_usb_tx_fixup,
.rx_fixup = rmnet_usb_rx_fixup,
+ .rx_complete = rmnet_usb_rx_complete,
.manage_power = rmnet_usb_manage_power,
- .data = PID9048_IFACE_MASK,
-};
-
-static const struct driver_info rmnet_info_pid904c = {
- .description = "RmNET net device",
- .flags = FLAG_SEND_ZLP,
- .bind = rmnet_usb_bind,
- .tx_fixup = rmnet_usb_tx_fixup,
- .rx_fixup = rmnet_usb_rx_fixup,
- .manage_power = rmnet_usb_manage_power,
- .data = PID904C_IFACE_MASK,
+ .data = 1,
};
static const struct usb_device_id vidpids[] = {
- {
- USB_DEVICE(0x05c6, 0x9034), /* MDM9x15*/
- .driver_info = (unsigned long)&rmnet_info_pid9034,
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9034, 4),
+ .driver_info = (unsigned long)&rmnet_info,
},
- {
- USB_DEVICE(0x05c6, 0x9048), /* MDM9x15*/
- .driver_info = (unsigned long)&rmnet_info_pid9048,
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9034, 5),
+ .driver_info = (unsigned long)&rmnet_info,
},
- {
- USB_DEVICE(0x05c6, 0x904c), /* MDM9x15*/
- .driver_info = (unsigned long)&rmnet_info_pid904c,
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9034, 6),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9034, 7),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9048, 5),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9048, 6),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9048, 7),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9048, 8),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x904c, 6),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x904c, 7),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x904c, 8),
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9075, 6), /*mux over hsic mdm*/
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9079, 5),
+ .driver_info = (unsigned long)&rmnet_usb_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9079, 6),
+ .driver_info = (unsigned long)&rmnet_usb_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9079, 7),
+ .driver_info = (unsigned long)&rmnet_usb_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05c6, 0x9079, 8),
+ .driver_info = (unsigned long)&rmnet_usb_info,
},
{ }, /* Terminating entry */
@@ -622,31 +890,36 @@
.supports_autosuspend = true,
};
-static int __init rmnet_usb_init(void)
+static int rmnet_data_start(void)
{
int retval;
+ if (no_rmnet_devs > MAX_RMNET_DEVS) {
+ pr_err("ERROR:%s: param no_rmnet_devs(%d) > than maximum(%d)",
+ __func__, no_rmnet_devs, MAX_RMNET_DEVS);
+ return -EINVAL;
+ }
+
+ /* initialize ctrl devices */
+ retval = rmnet_usb_ctrl_init(no_rmnet_devs, no_rmnet_insts_per_dev);
+ if (retval) {
+ err("rmnet_usb_cmux_init failed: %d", retval);
+ return retval;
+ }
+
retval = usb_register(&rmnet_usb);
if (retval) {
err("usb_register failed: %d", retval);
return retval;
}
- /* initialize rmnet ctrl device here*/
- retval = rmnet_usb_ctrl_init();
- if (retval) {
- usb_deregister(&rmnet_usb);
- err("rmnet_usb_cmux_init failed: %d", retval);
- return retval;
- }
- return 0;
+ return retval;
}
-module_init(rmnet_usb_init);
static void __exit rmnet_usb_exit(void)
{
- rmnet_usb_ctrl_exit();
usb_deregister(&rmnet_usb);
+ rmnet_usb_ctrl_exit(no_rmnet_devs, no_rmnet_insts_per_dev);
}
module_exit(rmnet_usb_exit);
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index fa0c568..b42050c 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -86,7 +86,7 @@
static const char driver_name [] = "usbnet";
-static struct workqueue_struct *usbnet_wq;
+struct workqueue_struct *usbnet_wq;
static DECLARE_WAIT_QUEUE_HEAD(unlink_wakeup);
@@ -343,12 +343,11 @@
/*-------------------------------------------------------------------------*/
-static void rx_complete (struct urb *urb);
-
static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
{
struct sk_buff *skb;
struct skb_data *entry;
+ usb_complete_t complete_fn;
int retval = 0;
unsigned long lockflags;
size_t size = dev->rx_urb_size;
@@ -366,8 +365,13 @@
entry->dev = dev;
entry->length = 0;
+ if (dev->driver_info->rx_complete)
+ complete_fn = dev->driver_info->rx_complete;
+ else
+ complete_fn = rx_complete;
+
usb_fill_bulk_urb (urb, dev->udev, dev->in,
- skb->data, size, rx_complete, skb);
+ skb->data, size, complete_fn, skb);
spin_lock_irqsave (&dev->rxq.lock, lockflags);
@@ -441,7 +445,7 @@
/*-------------------------------------------------------------------------*/
-static void rx_complete (struct urb *urb)
+void rx_complete(struct urb *urb)
{
struct sk_buff *skb = (struct sk_buff *) urb->context;
struct skb_data *entry = (struct skb_data *) skb->cb;
@@ -527,6 +531,7 @@
}
netif_dbg(dev, rx_err, dev->net, "no read resubmitted\n");
}
+EXPORT_SYMBOL_GPL(rx_complete);
static void intr_complete (struct urb *urb)
{
@@ -662,7 +667,7 @@
/*-------------------------------------------------------------------------*/
// precondition: never called in_interrupt
-static void usbnet_terminate_urbs(struct usbnet *dev)
+void usbnet_terminate_urbs(struct usbnet *dev)
{
DECLARE_WAITQUEUE(wait, current);
int temp;
@@ -687,6 +692,7 @@
dev->wait = NULL;
remove_wait_queue(&unlink_wakeup, &wait);
}
+EXPORT_SYMBOL_GPL(usbnet_terminate_urbs);
int usbnet_stop (struct net_device *net)
{
diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig
index 5af95927..3039b0d 100644
--- a/drivers/nfc/Kconfig
+++ b/drivers/nfc/Kconfig
@@ -39,3 +39,12 @@
into the kernel or say M to compile it as module.
endmenu
+
+config NFC_QNCI
+ bool "Qualcomm NCI based NFC Controller Driver for qca199x"
+ depends on I2C
+ select CRC_CCITT
+ help
+ This enables the NFC driver for QCA199x based devices.
+ This is for i2c connected version. NCI protocol logic
+ resides in the usermode and it has no other NFC dependencies.
diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile
index ab99e85..35a71e8 100644
--- a/drivers/nfc/Makefile
+++ b/drivers/nfc/Makefile
@@ -5,5 +5,6 @@
obj-$(CONFIG_PN544_NFC) += pn544.o
obj-$(CONFIG_NFC_PN533) += pn533.o
obj-$(CONFIG_NFC_WILINK) += nfcwilink.o
+obj-$(CONFIG_NFC_QNCI) += nfc-nci.o
ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
diff --git a/drivers/nfc/nfc-nci.c b/drivers/nfc/nfc-nci.c
new file mode 100644
index 0000000..9e9a4ea
--- /dev/null
+++ b/drivers/nfc/nfc-nci.c
@@ -0,0 +1,764 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/spinlock.h>
+#include <linux/poll.h>
+#include <linux/of_gpio.h>
+#include <linux/clk.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+#include "nfc-nci.h"
+
+
+struct qca199x_platform_data {
+ unsigned int irq_gpio;
+ unsigned int dis_gpio;
+ unsigned int ven_gpio;
+ unsigned int reg;
+};
+
+static struct of_device_id msm_match_table[] = {
+ {.compatible = "qcom,nfc-nci"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_match_table);
+
+#define MAX_BUFFER_SIZE (780)
+/* Read data */
+#define PACKET_HEADER_SIZE_NCI (4)
+#define PACKET_TYPE_NCI (16)
+#define MAX_PACKET_SIZE (PACKET_HEADER_SIZE_NCI + 255)
+#define MAX_QCA_REG (116)
+
+static int nfc_i2c_write(struct i2c_client *client, u8 *buf, int len);
+
+struct qca199x_dev {
+ wait_queue_head_t read_wq;
+ struct mutex read_mutex;
+ struct i2c_client *client;
+ struct miscdevice qca199x_device;
+ unsigned int irq_gpio;
+ unsigned int dis_gpio;
+ unsigned int ven_gpio;
+ bool irq_enabled;
+ spinlock_t irq_enabled_lock;
+ unsigned int count_irq;
+};
+
+/*
+ * To allow filtering of nfc logging from user. This is set via
+ * IOCTL NFC_KERNEL_LOGGING_MODE.
+ */
+static int logging_level;
+
+static void qca199x_init_stat(struct qca199x_dev *qca199x_dev)
+{
+ qca199x_dev->count_irq = 0;
+}
+
+static void qca199x_disable_irq(struct qca199x_dev *qca199x_dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&qca199x_dev->irq_enabled_lock, flags);
+ if (qca199x_dev->irq_enabled) {
+ disable_irq_nosync(qca199x_dev->client->irq);
+ qca199x_dev->irq_enabled = false;
+ }
+ spin_unlock_irqrestore(&qca199x_dev->irq_enabled_lock, flags);
+}
+
+static void qca199x_enable_irq(struct qca199x_dev *qca199x_dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&qca199x_dev->irq_enabled_lock, flags);
+ if (!qca199x_dev->irq_enabled) {
+ qca199x_dev->irq_enabled = true;
+ enable_irq(qca199x_dev->client->irq);
+ }
+ spin_unlock_irqrestore(&qca199x_dev->irq_enabled_lock, flags);
+}
+
+static irqreturn_t qca199x_dev_irq_handler(int irq, void *dev_id)
+{
+ struct qca199x_dev *qca199x_dev = dev_id;
+ unsigned long flags;
+
+
+ spin_lock_irqsave(&qca199x_dev->irq_enabled_lock, flags);
+ qca199x_dev->count_irq++;
+ spin_unlock_irqrestore(&qca199x_dev->irq_enabled_lock, flags);
+ wake_up(&qca199x_dev->read_wq);
+
+ return IRQ_HANDLED;
+}
+
+static unsigned int nfc_poll(struct file *filp, poll_table *wait)
+{
+ struct qca199x_dev *qca199x_dev = filp->private_data;
+ unsigned int mask = 0;
+ unsigned long flags;
+
+
+ poll_wait(filp, &qca199x_dev->read_wq, wait);
+
+ spin_lock_irqsave(&qca199x_dev->irq_enabled_lock, flags);
+ if (qca199x_dev->count_irq > 0) {
+ qca199x_dev->count_irq--;
+ mask |= POLLIN | POLLRDNORM;
+ }
+ spin_unlock_irqrestore(&qca199x_dev->irq_enabled_lock, flags);
+
+
+ return mask;
+}
+
+static ssize_t nfc_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *offset)
+{
+ struct qca199x_dev *qca199x_dev = filp->private_data;
+ unsigned char tmp[MAX_BUFFER_SIZE];
+ unsigned char len[PAYLOAD_HEADER_LENGTH];
+ int total, length, ret;
+
+ total = 0;
+ length = 0;
+ if (count > MAX_BUFFER_SIZE)
+ count = MAX_BUFFER_SIZE;
+
+ mutex_lock(&qca199x_dev->read_mutex);
+ /* Read the header */
+ ret = i2c_master_recv(qca199x_dev->client, len, PAYLOAD_HEADER_LENGTH);
+ if (ret != PAYLOAD_HEADER_LENGTH)
+ goto err;
+ length = len[PAYLOAD_HEADER_LENGTH - 1];
+
+ /** make sure full packet fits in the buffer **/
+ if ((length > 0) && ((length + PAYLOAD_HEADER_LENGTH) <= count)) {
+ /* Read the packet */
+ ret = i2c_master_recv(qca199x_dev->client, tmp, (length +
+ PAYLOAD_HEADER_LENGTH));
+ if (ret < 0)
+ goto err;
+ total = (length + PAYLOAD_HEADER_LENGTH);
+ }
+ mutex_unlock(&qca199x_dev->read_mutex);
+ if (total > 0) {
+ if ((total > count) || copy_to_user(buf, tmp, total)) {
+ dev_err(&qca199x_dev->client->dev,
+ "failed to copy to user space, total = %d\n",
+ total);
+ total = -EFAULT;
+ }
+ }
+err:
+ if (ret < 0)
+ mutex_unlock(&qca199x_dev->read_mutex);
+
+ return total;
+}
+
+static ssize_t nfc_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *offset)
+{
+ struct qca199x_dev *qca199x_dev = filp->private_data;
+ char tmp[MAX_BUFFER_SIZE];
+ int ret;
+
+ if (count > MAX_BUFFER_SIZE) {
+ dev_err(&qca199x_dev->client->dev, "out of memory\n");
+ return -ENOMEM;
+ }
+ if (copy_from_user(tmp, buf, count)) {
+ dev_err(&qca199x_dev->client->dev,
+ "nfc-nci write: failed to copy from user space\n");
+ return -EFAULT;
+ }
+ mutex_lock(&qca199x_dev->read_mutex);
+ ret = i2c_master_send(qca199x_dev->client, tmp, count);
+ if (ret != count) {
+ dev_err(&qca199x_dev->client->dev,
+ "NFC: failed to write %d\n", ret);
+ ret = -EIO;
+ }
+ mutex_unlock(&qca199x_dev->read_mutex);
+
+ return ret;
+}
+
+static int nfc_open(struct inode *inode, struct file *filp)
+{
+ int ret = 0;
+
+ struct qca199x_dev *qca199x_dev = container_of(filp->private_data,
+ struct qca199x_dev,
+ qca199x_device);
+
+ filp->private_data = qca199x_dev;
+ qca199x_init_stat(qca199x_dev);
+ qca199x_enable_irq(qca199x_dev);
+ dev_dbg(&qca199x_dev->client->dev,
+ "%d,%d\n", imajor(inode), iminor(inode));
+ return ret;
+}
+
+/*
+ * Wake/Sleep Mode
+ */
+int nfcc_wake(int level, struct nfc_info *info)
+{
+ int r = 0;
+ unsigned char raw_nci_sleep[] = {0x2F, 0x03, 0x00};
+ /* Change slave address to 0xE */
+ unsigned char raw_nci_wake[] = {0x10, 0x0F};
+ unsigned short slave_addr = 0xE;
+ unsigned short curr_addr;
+
+ struct i2c_client *client = info->i2c_dev;
+
+ dev_dbg(&client->dev, "nfcc_wake: %s: info: %p\n", __func__, info);
+
+ if (level == NFCC_SLEEP) {
+ r = nfc_i2c_write(client, &raw_nci_sleep[0],
+ sizeof(raw_nci_sleep));
+
+ if (r != sizeof(raw_nci_sleep))
+ return -EMSGSIZE;
+ info->state = NFCC_STATE_NORMAL_SLEEP;
+ } else {
+ curr_addr = client->addr;
+ client->addr = slave_addr;
+ r = nfc_i2c_write(client, &raw_nci_wake[0],
+ sizeof(raw_nci_wake));
+ /* Restore original NFCC slave I2C address */
+ client->addr = curr_addr;
+
+ if (r != sizeof(raw_nci_sleep))
+ return -EMSGSIZE;
+
+ info->state = NFCC_STATE_NORMAL_WAKE;
+ }
+ msleep(20);
+ return r;
+}
+
+/*
+ * Inside nfc_ioctl_power_states
+ *
+ * @brief ioctl functions
+ *
+ *
+ * Device control
+ * remove control via ioctl
+ * (arg = 0): NFC_DISABLE GPIO = 0
+ * (arg = 1): NFC_DISABLE GPIO = 1
+ * NOT USED (arg = 2): FW_DL GPIO = 0
+ * NOT USED (arg = 3): FW_DL GPIO = 1
+ * (arg = 4): NFCC_WAKE = 1
+ * (arg = 5): NFCC_WAKE = 0
+ *
+ *
+ */
+int nfc_ioctl_power_states(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ int r = 0;
+ struct qca199x_dev *qca199x_dev = filp->private_data;
+ struct nfc_info *info = container_of(filp->private_data,
+ struct nfc_info, miscdev);
+
+ struct i2c_client *client = info->i2c_dev;
+
+ r = gpio_request(qca199x_dev->dis_gpio, "nfc_reset_gpio");
+ if (r) {
+ dev_err(&client->dev, "unable to request gpio [%d]\n",
+ qca199x_dev->dis_gpio);
+ goto err_req;
+ }
+ gpio_set_value(qca199x_dev->dis_gpio, 0);
+ r = gpio_direction_output(qca199x_dev->dis_gpio, 1);
+ if (r) {
+ dev_err(&client->dev, "unable to set direction for gpio [%d]\n",
+ qca199x_dev->irq_gpio);
+ goto err_req;
+ }
+
+ if (arg == 0) {
+ gpio_set_value(qca199x_dev->dis_gpio, 0);
+ msleep(20);
+ } else if (arg == 1) {
+ gpio_set_value(qca199x_dev->dis_gpio, 1);
+ msleep(20);
+ } else if (arg == 2) {
+ msleep(20);
+ } else if (arg == 3) {
+ msleep(20);
+ } else if (arg == 4) {
+ nfcc_wake(NFCC_WAKE, info);
+ msleep(20);
+ } else if (arg == 5) {
+ nfcc_wake(NFCC_SLEEP, info);
+ msleep(20);
+ } else {
+ r = -ENOIOCTLCMD;
+ }
+
+err_req:
+ return r;
+}
+
+/*
+ * Inside nfc_ioctl_kernel_logging
+ *
+ * @brief nfc_ioctl_kernel_logging
+ *
+ * (arg = 0) ; NO_LOGGING
+ * (arg = 1) ; COMMS_LOGGING - BASIC LOGGING - Mainly just comms over I2C
+ * (arg = 2) ; FULL_LOGGING - ENABLE ALL - DBG messages for handlers etc.
+ * ; ! Be aware as amount of logging could impact behaviour !
+ *
+ *
+ */
+int nfc_ioctl_kernel_logging(unsigned long arg, struct file *filp)
+{
+ int retval = 0;
+ struct qca199x_dev *qca199x_dev = container_of(filp->private_data,
+ struct qca199x_dev,
+ qca199x_device);
+ if (arg == 0) {
+ dev_dbg(&qca199x_dev->client->dev,
+ "nfc_ioctl_kernel_logging : level = NO_LOGGING\n");
+ logging_level = 0;
+ } else if (arg == 1) {
+ dev_dbg(&qca199x_dev->client->dev,
+ "nfc_ioctl_kernel_logging: level = COMMS_LOGGING only\n");
+ logging_level = 1;
+ } else if (arg == 2) {
+ dev_dbg(&qca199x_dev->client->dev,
+ "nfc_ioctl_kernel_logging: level = FULL_LOGGING\n");
+ logging_level = 2;
+ }
+ return retval;
+}
+
+static long nfc_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg)
+{
+ int r = 0;
+
+ switch (cmd) {
+
+ case NFC_SET_PWR:
+ nfc_ioctl_power_states(pfile, cmd, arg);
+ break;
+ case NFCC_MODE:
+ break;
+ case NFC_KERNEL_LOGGING_MODE:
+ nfc_ioctl_kernel_logging(arg, pfile);
+ break;
+ case SET_RX_BLOCK:
+ break;
+ case SET_EMULATOR_TEST_POINT:
+ break;
+ default:
+ r = -ENOIOCTLCMD;
+ }
+ return r;
+}
+
+static const struct file_operations nfc_dev_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .poll = nfc_poll,
+ .read = nfc_read,
+ .write = nfc_write,
+ .open = nfc_open,
+ .unlocked_ioctl = nfc_ioctl
+};
+
+void dumpqca1990(struct i2c_client *client)
+{
+ int r = 0;
+ int i = 0;
+ unsigned char raw_reg_rd = {0x0};
+ unsigned short temp_addr;
+
+ temp_addr = client->addr;
+ client->addr = 0x0E;
+
+ for (i = 0; i < MAX_QCA_REG; i++) {
+ raw_reg_rd = i;
+ if (((i >= 0x0) && (i < 0x4)) || ((i > 0x7) && (i < 0xA)) ||
+ ((i > 0xF) && (i < 0x12)) || ((i > 0x39) && (i < 0x4d)) ||
+ ((i > 0x69) && (i < 0x74)) || (i == 0x18) || (i == 0x30) ||
+ (i == 0x58)) {
+ r = nfc_i2c_write(client, &raw_reg_rd, 1);
+ msleep(20);
+ r = i2c_master_recv(client, &raw_reg_rd, 1);
+ }
+ }
+ client->addr = temp_addr;
+}
+
+static int nfc_i2c_write(struct i2c_client *client, u8 *buf, int len)
+{
+ int r;
+
+ r = i2c_master_send(client, buf, len);
+ dev_dbg(&client->dev, "send: %d\n", r);
+ if (r == -EREMOTEIO) { /* Retry, chip was in standby */
+ usleep_range(6000, 10000);
+ r = i2c_master_send(client, buf, len);
+ dev_dbg(&client->dev, "send2: %d\n", r);
+ }
+ if (r != len)
+ return -EREMOTEIO;
+
+ return r;
+}
+
+int nfcc_initialise(struct i2c_client *client, unsigned short curr_addr)
+{
+ int r = 0;
+ unsigned char raw_1p8_CONTROL_011[] = {0x11, XTAL_CLOCK};
+ unsigned char raw_1P8_CONTROL_010[] = {0x10, PWR_EN};
+ unsigned char raw_1P8_X0_0B0[] = {0xB0, (FREQ_SEL)};
+ unsigned char raw_slave1[] = {0x09, NCI_I2C_SLAVE};
+ unsigned char raw_slave2[] = {0x8, 0x10};
+ unsigned char raw_s73[] = {0x73, 0x02};
+ unsigned char raw_slave1_rd = {0x0};
+ unsigned char raw_1P8_PAD_CFG_CLK_REQ[] = {0xA5, 0x1};
+ unsigned char buf[4];
+
+ /* Set I2C address to enable configuration of QCA1990 */
+ client->addr = curr_addr;
+ RAW(s73, 0x02);
+
+ r = nfc_i2c_write(client, &raw_s73[0], sizeof(raw_s73));
+ usleep(1000);
+ RAW(1p8_CONTROL_011, XTAL_CLOCK | 0x01);
+
+ r = nfc_i2c_write(client, &raw_1p8_CONTROL_011[0],
+ sizeof(raw_1p8_CONTROL_011));
+ usleep(1000);
+ RAW(1P8_CONTROL_010, (0x8));
+ r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
+ sizeof(raw_1P8_CONTROL_010));
+
+ usleep(10000); /* 10ms wait */
+ RAW(1P8_CONTROL_010, (0xC));
+ r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
+ sizeof(raw_1P8_CONTROL_010));
+ usleep(100); /* 100uS wait */
+ RAW(1P8_X0_0B0, (FREQ_SEL_19));
+ r = nfc_i2c_write(client, &raw_1P8_X0_0B0[0], sizeof(raw_1P8_X0_0B0));
+ usleep(1000);
+
+ /* PWR_EN = 1 */
+ RAW(1P8_CONTROL_010, (0xd));
+ r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
+ sizeof(raw_1P8_CONTROL_010));
+ usleep(20000); /* 20ms wait */
+ /* LS_EN = 1 */
+ RAW(1P8_CONTROL_010, 0xF);
+ r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0],
+ sizeof(raw_1P8_CONTROL_010));
+ usleep(20000); /* 20ms wait */
+
+ /* Enable the PMIC clock */
+ RAW(1P8_PAD_CFG_CLK_REQ, (0x1));
+ r = nfc_i2c_write(client, &raw_1P8_PAD_CFG_CLK_REQ[0],
+ sizeof(raw_1P8_PAD_CFG_CLK_REQ));
+ usleep(1000);
+
+ RAW(slave2, 0x10);
+ r = nfc_i2c_write(client, &raw_slave2[0], sizeof(raw_slave2));
+ usleep(1000);
+ {
+ r = i2c_master_send(client, buf, 1);
+ memset(buf, 0xAA, sizeof(buf));
+ r = i2c_master_recv(client, buf, 1);
+ }
+ RAW(slave1, NCI_I2C_SLAVE);
+ r = nfc_i2c_write(client, &raw_slave1[0], sizeof(raw_slave1));
+ usleep(1000);
+
+ /* QCA199x NFCC CPU should now boot... */
+ r = i2c_master_recv(client, &raw_slave1_rd, 1);
+ /* Talk on NCI slave address NCI_I2C_SLAVE 0x2C*/
+ client->addr = NCI_I2C_SLAVE;
+
+ return r;
+}
+
+static int nfc_parse_dt(struct device *dev, struct qca199x_platform_data *pdata)
+{
+ int r = 0;
+ struct device_node *np = dev->of_node;
+
+ r = of_property_read_u32(np, "reg", &pdata->reg);
+ if (r)
+ return -EINVAL;
+
+ r = of_property_read_u32(np, "qcom,clk-gpio", &pdata->ven_gpio);
+ if (r)
+ return -EINVAL;
+
+ pdata->dis_gpio = of_get_named_gpio(np, "qcom,dis-gpio", 0);
+ if ((!gpio_is_valid(pdata->dis_gpio)))
+ return -EINVAL;
+
+ pdata->irq_gpio = of_get_named_gpio(np, "qcom,irq-gpio", 0);
+ if ((!gpio_is_valid(pdata->irq_gpio)))
+ return -EINVAL;
+
+ return r;
+}
+
+static int qca199x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int r = 0;
+ int irqn = 0;
+ struct clk *nfc_clk;
+ struct device_node *node = client->dev.of_node;
+ struct qca199x_platform_data *platform_data;
+ struct qca199x_dev *qca199x_dev;
+
+ if (client->dev.of_node) {
+ platform_data = devm_kzalloc(&client->dev,
+ sizeof(struct qca199x_platform_data), GFP_KERNEL);
+ if (!platform_data) {
+ dev_err(&client->dev,
+ "nfc-nci probe: Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+ r = nfc_parse_dt(&client->dev, platform_data);
+ if (r)
+ return r;
+ } else {
+ platform_data = client->dev.platform_data;
+ }
+ if (!platform_data)
+ return -EINVAL;
+ dev_dbg(&client->dev,
+ "nfc-nci probe: %s, inside nfc-nci flags = %x\n",
+ __func__, client->flags);
+ if (platform_data == NULL) {
+ dev_err(&client->dev, "nfc-nci probe: failed\n");
+ return -ENODEV;
+ }
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "nfc-nci probe: need I2C_FUNC_I2C\n");
+ return -ENODEV;
+ }
+ qca199x_dev = kzalloc(sizeof(*qca199x_dev), GFP_KERNEL);
+ if (qca199x_dev == NULL) {
+ dev_err(&client->dev,
+ "nfc-nci probe: failed to allocate memory for module data\n");
+ return -ENOMEM;
+ }
+ if (gpio_is_valid(platform_data->irq_gpio)) {
+ r = gpio_request(platform_data->irq_gpio, "nfc_irq_gpio");
+ if (r) {
+ dev_err(&client->dev, "unable to request gpio [%d]\n",
+ platform_data->irq_gpio);
+ goto err_irq;
+ }
+ r = gpio_direction_input(platform_data->irq_gpio);
+ if (r) {
+
+ dev_err(&client->dev,
+ "unable to set direction for gpio [%d]\n",
+ platform_data->irq_gpio);
+ goto err_irq;
+ }
+ gpio_to_irq(0);
+ irqn = gpio_to_irq(platform_data->irq_gpio);
+ if (irqn < 0) {
+ r = irqn;
+ goto err_irq;
+ }
+ client->irq = irqn;
+
+ } else {
+ dev_err(&client->dev, "irq gpio not provided\n");
+ goto err_free_dev;
+ }
+ if (gpio_is_valid(platform_data->dis_gpio)) {
+ r = gpio_request(platform_data->dis_gpio, "nfc_reset_gpio");
+ if (r) {
+ dev_err(&client->dev,
+ "NFC: unable to request gpio [%d]\n",
+ platform_data->dis_gpio);
+ goto err_dis_gpio;
+ }
+ r = gpio_direction_output(platform_data->dis_gpio, 1);
+ if (r) {
+ dev_err(&client->dev,
+ "NFC: unable to set direction for gpio [%d]\n",
+ platform_data->dis_gpio);
+ goto err_dis_gpio;
+ }
+ } else {
+ dev_err(&client->dev, "dis gpio not provided\n");
+ goto err_irq;
+ }
+
+ nfc_clk = clk_get(&client->dev, "ref_clk");
+
+ if (nfc_clk == NULL)
+ goto err_dis_gpio;
+
+ r = clk_prepare_enable(nfc_clk);
+ if (r)
+ goto err_dis_gpio;
+
+ platform_data->ven_gpio = of_get_named_gpio(node,
+ "qcom,clk-gpio", 0);
+
+ if (gpio_is_valid(platform_data->ven_gpio)) {
+ r = gpio_request(platform_data->ven_gpio, "nfc_ven_gpio");
+ if (r) {
+ dev_err(&client->dev, "unable to request gpio [%d]\n",
+ platform_data->irq_gpio);
+ goto err_ven_gpio;
+ }
+ r = gpio_direction_input(platform_data->ven_gpio);
+ if (r) {
+
+ dev_err(&client->dev,
+ "unable to set direction for gpio [%d]\n",
+ platform_data->irq_gpio);
+ goto err_ven_gpio;
+ }
+
+ } else {
+
+ dev_err(&client->dev, "ven gpio not provided\n");
+ goto err_dis_gpio;
+ }
+ qca199x_dev->dis_gpio = platform_data->dis_gpio;
+ qca199x_dev->irq_gpio = platform_data->irq_gpio;
+ qca199x_dev->ven_gpio = platform_data->ven_gpio;
+ qca199x_dev->client = client;
+
+ /* init mutex and queues */
+ init_waitqueue_head(&qca199x_dev->read_wq);
+ mutex_init(&qca199x_dev->read_mutex);
+ spin_lock_init(&qca199x_dev->irq_enabled_lock);
+
+ qca199x_dev->qca199x_device.minor = MISC_DYNAMIC_MINOR;
+ qca199x_dev->qca199x_device.name = "nfc-nci";
+ qca199x_dev->qca199x_device.fops = &nfc_dev_fops;
+
+ r = misc_register(&qca199x_dev->qca199x_device);
+ if (r) {
+ dev_err(&client->dev, "misc_register failed\n");
+ goto err_misc_register;
+ }
+
+ logging_level = 0;
+ /* request irq. The irq is set whenever the chip has data available
+ * for reading. It is cleared when all data has been read.
+ */
+ nfcc_initialise(client, platform_data->reg);
+
+ qca199x_dev->irq_enabled = true;
+ r = request_irq(client->irq, qca199x_dev_irq_handler,
+ IRQF_TRIGGER_RISING, client->name, qca199x_dev);
+ if (r) {
+ dev_err(&client->dev, "nfc-nci probe: request_irq failed\n");
+ goto err_request_irq_failed;
+ }
+ qca199x_disable_irq(qca199x_dev);
+ i2c_set_clientdata(client, qca199x_dev);
+ dev_dbg(&client->dev,
+ "nfc-nci probe: %s, probing qca1990 exited successfully\n",
+ __func__);
+ return 0;
+
+err_request_irq_failed:
+ misc_deregister(&qca199x_dev->qca199x_device);
+err_misc_register:
+ mutex_destroy(&qca199x_dev->read_mutex);
+err_ven_gpio:
+ gpio_free(platform_data->ven_gpio);
+err_dis_gpio:
+ gpio_free(platform_data->dis_gpio);
+err_irq:
+ gpio_free(platform_data->irq_gpio);
+err_free_dev:
+ kfree(qca199x_dev);
+ return r;
+}
+
+static int qca199x_remove(struct i2c_client *client)
+{
+ struct qca199x_dev *qca199x_dev;
+
+ qca199x_dev = i2c_get_clientdata(client);
+ free_irq(client->irq, qca199x_dev);
+ misc_deregister(&qca199x_dev->qca199x_device);
+ mutex_destroy(&qca199x_dev->read_mutex);
+ gpio_free(qca199x_dev->irq_gpio);
+ gpio_free(qca199x_dev->dis_gpio);
+ gpio_free(qca199x_dev->ven_gpio);
+ kfree(qca199x_dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id qca199x_id[] = {
+ {"qca199x-i2c", 0},
+ {}
+};
+
+static struct i2c_driver qca199x = {
+ .id_table = qca199x_id,
+ .probe = qca199x_probe,
+ .remove = qca199x_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "nfc-nci",
+ .of_match_table = msm_match_table,
+ },
+};
+
+/*
+ * module load/unload record keeping
+ */
+static int __init qca199x_dev_init(void)
+{
+ return i2c_add_driver(&qca199x);
+}
+module_init(qca199x_dev_init);
+
+static void __exit qca199x_dev_exit(void)
+{
+ i2c_del_driver(&qca199x);
+}
+module_exit(qca199x_dev_exit);
+
+MODULE_DESCRIPTION("NFC QCA199x");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/nfc/nfc-nci.h b/drivers/nfc/nfc-nci.h
new file mode 100644
index 0000000..4398df7
--- /dev/null
+++ b/drivers/nfc/nfc-nci.h
@@ -0,0 +1,222 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __NFC_NCI_H
+#define __NFC_NCI_H
+
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <linux/version.h>
+
+#include <linux/semaphore.h>
+#include <linux/completion.h>
+
+#include <linux/ioctl.h>
+#include <linux/cdev.h>
+#include <linux/miscdevice.h>
+
+struct nfc_device {
+ struct cdev cdev;
+ struct class *char_class;
+};
+
+enum ehandler_mode {
+ UNSOLICITED_READ_MODE = 0,
+ SOLICITED_READ_MODE
+};
+
+enum ekernel_logging_mode {
+ LEVEL_0 = 0, /* For Basic Comms, such asNCI TX/TX to NFCC */
+ LEVEL_1, /* Other Debug e.g. Notifications, ISR hit, etc ..*/
+ LEVEL_2,
+ LEVEL_3,
+ LEVEL_4,
+ LEVEL_5
+};
+
+struct DeviceMode {
+ enum ehandler_mode handle_flavour;
+} tDeviceMode;
+
+#define NFC_DRIVER_NAME "nfc-nci"
+#define NFC_I2C_DRIVER_NAME "NCI NFC I2C Interface",
+
+#define NCI_I2C_SLAVE (0x2C)
+#define NFC_I2C_BUS 3 /* 6, 10, 4, 5 */
+#define NFC_SET_PWR _IOW(0xE9, 0x01, unsigned int)
+#define NFCC_MODE _IOW(0xE9, 0x02, unsigned int)
+#define NFC_KERNEL_LOGGING_MODE _IOW(0xE9, 0x03, unsigned int)
+#define SET_RX_BLOCK _IOW(0xE9, 0x04, unsigned int)
+#define SET_EMULATOR_TEST_POINT _IOW(0xE9, 0x05, unsigned int)
+
+#define NFC_MAX_I2C_TRANSFER (0x0400)
+#define NFC_MSG_MAX_SIZE (0x21)
+
+#define NFC_RX_BUFFER_CNT_START (0x0)
+
+#define NFC_RX_BUFFER_BLOCK_SIZE (0x120) /* Bytes per Block */
+#define NFC_RX_BUFFER_PAGE_SIZE (0x1000) /* Page size Bytes */
+#define NFC_RX_BUFFER_PAGES (0x8)
+#define NFC_RX_ORDER_FREE_PAGES (0x3) /* Free 8 Pages */
+
+/* The total no. of Blocks */
+#define NFC_RX_BUFFER_CNT_LIMIT (unsigned short)( \
+ ( \
+ ((NFC_RX_BUFFER_PAGE_SIZE) *\
+ (NFC_RX_BUFFER_PAGES))/\
+ (NFC_RX_BUFFER_BLOCK_SIZE)\
+ ) \
+ ) \
+
+#define PAYLOAD_HEADER_LENGTH (0x3)
+#define PAYLOAD_LENGTH_MAX (256)
+#define BYTE (0x8)
+#define NCI_IDENTIFIER (0x10)
+
+/** Power Management Related **/
+
+#define NFCC_WAKE (0x01)
+#define NFCC_SLEEP (0x00)
+
+#define XTAL_CLOCK (0X00)
+#define REFERENCE_CLOCK (0X01)
+
+/* LDO Trim Settings */
+#define IPTAT_TRIM (0x1F)
+#define V1P1_TRIM (0x0F)
+#define V1P8_TRIM (0x0F)
+#define VBATT_OK_THRESHOLD (0x07)
+
+#define PWR_EN (0x08) /* Enable 1.1V LDO Regulator */
+#define LS_EN (0x04) /* Enable 1.1V->1.8V Level Shifters */
+
+/* Write '1' to cause wake event to NFCC. If set NFCC will not go to SLEEP */
+#define NCI_WAKE (0x02)
+
+#define NCI_ENA (0x01) /* Write '1' to enable PLL */
+#define FREQ_SEL (0x00) /* XO Frequency Select */
+#define FREQ_SEL_13 (0x00) /* XO Frequency Select = 13.56MHz */
+#define FREQ_SEL_19 (0x01) /* XO Frequency Select = 19.20 MHz */
+#define FREQ_SEL_26 (0x02) /* XO Frequency Select = 26.00 MHz */
+#define FREQ_SEL_27 (0x03) /* XO Frequency Select = 27.12 MHz */
+#define FREQ_SEL_37 (0x04) /* XO Frequency Select = 37.40 MHz */
+#define FREQ_SEL_38 (0x05) /* XO Frequency Select = 38.40 MHz */
+#define FREQ_SEL_40 (0x06) /* XO Frequency Select = 40.00 MHz */
+#define FREQ_SEL_48 (0x07) /* XO Frequency Select = 48.00 MHz */
+#define FREQ_SEL_27 (0x03) /* XO Frequency Select */
+
+
+#define QUALIFY_REFCLK (0x80)
+#define QUALIFY_OSC (0x40)
+#define LOCALBIASXTAL (0x20)
+#define BIAS2X_FORCE (0x10)
+#define BIAS2X (0x08)
+#define LBIAS2X (0x04)
+#define SMALLRF (0x02)
+#define SMALLRBIAS (0x01)
+
+/* Select as appropriate */
+#define CRYSTAL_OSC ((QUALIFY_REFCLK) | (QUALIFY_OSC) | \
+ (LOCALBIASXTAL) | (BIAS2X_FORCE) | \
+ (BIAS2X) | (LBIAS2X) | (SMALLRF) | (SMALLRBIAS))
+
+#define CDACIN (0x3F) /* Tuning range for load capacitor at X1*/
+#define CDACOUT (0x3F) /* Tuning range for load capacitor at X2*/
+
+#define RAW(reg, value) (raw_##reg[1] = value)
+
+/* Logging macro with threshold control */
+#define PRINTK(LEVEL, THRESHOLD, pString, ...) ( \
+ if (LEVEL > THRESHOLD) { \
+ pr_info(pString, ##__VA_ARGS__); \
+ } \
+ )
+
+/* board config */
+struct nfc_platform_data {
+ int (*request_resources) (struct i2c_client *client);
+ void (*free_resources) (void);
+ void (*enable) (int fw);
+ int (*test) (void);
+ void (*disable) (void);
+};
+/*
+ * Internal NFCC Hardware states. At present these may not be possible to
+ * detect in software as possibly no power when
+ * in monitor state! Also, need to detect DISABLE control GPIO from PMIC.
+ */
+enum nfcc_hardware_state {
+ NFCC_STATE_MONITOR, /* VBAT < h/w Critcal Voltage */
+ /* VBAT > H/W Critical Voltage;
+ Lowest Power Mode - DISABLE = 1; only
+ possible when phone is ON */
+ NFCC_STATE_HPD,
+ /* VBAT > H/W Critical Voltage; DISABLE = 0;
+ Only possible when phone is ON */
+ NFCC_STSTE_ULPM,
+ /* VBAT > H/W Critical Voltage; DISABLE = 0;
+ Powered by PMIC & VBAT; 1.8V I/O supply on; VDDPX available, boot is
+ initiated by host over I2C */
+ NFCC_STATE_NORMAL_REGION1,
+ /* VBAT > H/W Critical Voltage; DISABLE = 0;
+ Powered by VBAT; 1.8V I/O supply on; VDDPX available, boot is initiated
+ by host over I2C */
+ NFCC_STATE_NORMAL_REGION2,
+};
+
+/* We assume here that VBATT > h/w Critical Voltage */
+enum nfcc_state {
+ /* Assume In ULPM state, ready for initialisation, cannot detect for
+ Monitor or HPD states */
+ NFCC_STATE_COLD,
+ /* (VDDPX==1) && (Following I2C initialisation). In Region 1 or Region2
+ state WAKE */
+ NFCC_STATE_NORMAL_WAKE,
+ /* (VDDPX==1) && (Following I2C initialisation). In Region 1 or Region2
+ state SLEEP */
+ NFCC_STATE_NORMAL_SLEEP,
+};
+
+
+enum nfcc_irq {
+ NFCC_NO_INT,
+ NFCC_INT,
+};
+
+
+struct nfc_info {
+ struct miscdevice miscdev;
+ struct i2c_client *i2c_dev;
+ struct regulator_bulk_data regs[3];
+ enum nfcc_state state;
+ wait_queue_head_t read_wait;
+ loff_t read_offset;
+ struct mutex read_mutex;
+ struct mutex mutex;
+ u8 *buf;
+ size_t buflen;
+ spinlock_t irq_enabled_lock;
+ unsigned int count_irq;
+ enum nfcc_irq read_irq;
+};
+
+
+struct nfc_i2c_platform_data {
+ unsigned int nfc_irq_gpio;
+ unsigned int nfc_clk_en_gpio;
+ unsigned int dis_gpio;
+ unsigned int irq_gpio;
+ unsigned int ven_gpio;
+ unsigned int firm_gpio;
+ unsigned int reg;
+};
+#endif
diff --git a/drivers/of/gpio.c b/drivers/of/gpio.c
index 94e76d8..7b41fd8 100644
--- a/drivers/of/gpio.c
+++ b/drivers/of/gpio.c
@@ -18,6 +18,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
+#include <linux/pinctrl/pinctrl.h>
#include <linux/slab.h>
/**
@@ -206,6 +207,53 @@
}
EXPORT_SYMBOL(of_mm_gpiochip_add);
+#ifdef CONFIG_PINCTRL
+void of_gpiochip_add_pin_range(struct gpio_chip *chip)
+{
+ struct device_node *np = chip->of_node;
+ struct of_phandle_args pinspec;
+ struct pinctrl_dev *pctldev;
+ int index = 0, ret;
+
+ if (!np)
+ return;
+
+ do {
+ ret = of_parse_phandle_with_args(np, "gpio-ranges",
+ "#gpio-range-cells", index, &pinspec);
+ if (ret)
+ break;
+
+ pctldev = of_pinctrl_get(pinspec.np);
+ if (!pctldev)
+ break;
+
+ ret = gpiochip_add_pin_range(chip,
+ pinctrl_dev_get_name(pctldev),
+ pinspec.args[0],
+ pinspec.args[1]);
+
+ if (ret)
+ break;
+
+ } while (index++);
+}
+
+void of_gpiochip_remove_pin_range(struct gpio_chip *chip)
+{
+ struct gpio_pin_range *pin_range, *tmp;
+
+ list_for_each_entry_safe(pin_range, tmp, &chip->pin_ranges, node) {
+ list_del(&pin_range->node);
+ pinctrl_remove_gpio_range(pin_range->pctldev,
+ &pin_range->range);
+ }
+}
+#else
+void of_gpiochip_add_pin_range(struct gpio_chip *chip) {}
+void of_gpiochip_remove_pin_range(struct gpio_chip *chip) {}
+#endif
+
void of_gpiochip_add(struct gpio_chip *chip)
{
if ((!chip->of_node) && (chip->dev))
@@ -219,11 +267,14 @@
chip->of_xlate = of_gpio_simple_xlate;
}
+ of_gpiochip_add_pin_range(chip);
of_node_get(chip->of_node);
}
void of_gpiochip_remove(struct gpio_chip *chip)
{
+ of_gpiochip_remove_pin_range(chip);
+
if (chip->of_node)
of_node_put(chip->of_node);
}
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index abfb964..3671ac2 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -4,7 +4,6 @@
config PINCTRL
bool
- depends on EXPERIMENTAL
if PINCTRL
@@ -35,19 +34,16 @@
bool "MMP2 pin controller driver"
depends on ARCH_MMP
select PINCTRL_PXA3xx
- select PINCONF
config PINCTRL_PXA168
bool "PXA168 pin controller driver"
depends on ARCH_MMP
select PINCTRL_PXA3xx
- select PINCONF
config PINCTRL_PXA910
bool "PXA910 pin controller driver"
depends on ARCH_MMP
select PINCTRL_PXA3xx
- select PINCONF
config PINCTRL_SIRF
bool "CSR SiRFprimaII pin controller driver"
@@ -56,17 +52,15 @@
config PINCTRL_TEGRA
bool
+ select PINMUX
+ select PINCONF
config PINCTRL_TEGRA20
bool
- select PINMUX
- select PINCONF
select PINCTRL_TEGRA
config PINCTRL_TEGRA30
bool
- select PINMUX
- select PINCONF
select PINCTRL_TEGRA
config PINCTRL_U300
@@ -77,7 +71,7 @@
config PINCTRL_COH901
bool "ST-Ericsson U300 COH 901 335/571 GPIO"
- depends on GPIOLIB && ARCH_U300 && PINMUX_U300
+ depends on GPIOLIB && ARCH_U300 && PINCTRL_U300
help
Say yes here to support GPIO interface on ST-Ericsson U300.
The names of the two IP block variants supported are
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 6d4150b..8e3c95a 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -5,6 +5,9 @@
obj-$(CONFIG_PINCTRL) += core.o
obj-$(CONFIG_PINMUX) += pinmux.o
obj-$(CONFIG_PINCONF) += pinconf.o
+ifeq ($(CONFIG_OF),y)
+obj-$(CONFIG_PINCTRL) += devicetree.o
+endif
obj-$(CONFIG_GENERIC_PINCONF) += pinconf-generic.o
obj-$(CONFIG_PINCTRL_PXA3xx) += pinctrl-pxa3xx.o
obj-$(CONFIG_PINCTRL_MMP2) += pinctrl-mmp2.o
diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index df6296c..b0de6e7 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -14,6 +14,7 @@
#define pr_fmt(fmt) "pinctrl core: " fmt
#include <linux/kernel.h>
+#include <linux/kref.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/device.h>
@@ -23,41 +24,42 @@
#include <linux/sysfs.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/machine.h>
#include "core.h"
+#include "devicetree.h"
#include "pinmux.h"
#include "pinconf.h"
-/**
- * struct pinctrl_maps - a list item containing part of the mapping table
- * @node: mapping table list node
- * @maps: array of mapping table entries
- * @num_maps: the number of entries in @maps
- */
-struct pinctrl_maps {
- struct list_head node;
- struct pinctrl_map const *maps;
- unsigned num_maps;
-};
+
+static bool pinctrl_dummy_state;
/* Mutex taken by all entry points */
DEFINE_MUTEX(pinctrl_mutex);
/* Global list of pin control devices (struct pinctrl_dev) */
-static LIST_HEAD(pinctrldev_list);
+LIST_HEAD(pinctrldev_list);
/* List of pin controller handles (struct pinctrl) */
static LIST_HEAD(pinctrl_list);
/* List of pinctrl maps (struct pinctrl_maps) */
-static LIST_HEAD(pinctrl_maps);
+LIST_HEAD(pinctrl_maps);
-#define for_each_maps(_maps_node_, _i_, _map_) \
- list_for_each_entry(_maps_node_, &pinctrl_maps, node) \
- for (_i_ = 0, _map_ = &_maps_node_->maps[_i_]; \
- _i_ < _maps_node_->num_maps; \
- i++, _map_ = &_maps_node_->maps[_i_])
+
+/**
+ * pinctrl_provide_dummies() - indicate if pinctrl provides dummy state support
+ *
+ * Usually this function is called by platforms without pinctrl driver support
+ * but run with some shared drivers using pinctrl APIs.
+ * After calling this function, the pinctrl core will return successfully
+ * with creating a dummy state for the driver to keep going smoothly.
+ */
+void pinctrl_provide_dummies(void)
+{
+ pinctrl_dummy_state = true;
+}
const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev)
{
@@ -66,6 +68,12 @@
}
EXPORT_SYMBOL_GPL(pinctrl_dev_get_name);
+const char *pinctrl_dev_get_devname(struct pinctrl_dev *pctldev)
+{
+ return dev_name(pctldev->dev);
+}
+EXPORT_SYMBOL_GPL(pinctrl_dev_get_devname);
+
void *pinctrl_dev_get_drvdata(struct pinctrl_dev *pctldev)
{
return pctldev->driver_data;
@@ -124,6 +132,25 @@
}
/**
+ * pin_get_name_from_id() - look up a pin name from a pin id
+ * @pctldev: the pin control device to lookup the pin on
+ * @name: the name of the pin to look up
+ */
+const char *pin_get_name(struct pinctrl_dev *pctldev, const unsigned pin)
+{
+ const struct pin_desc *desc;
+
+ desc = pin_desc_get(pctldev, pin);
+ if (desc == NULL) {
+ dev_err(pctldev->dev, "failed to get pin(%d) name\n",
+ pin);
+ return NULL;
+ }
+
+ return desc->name;
+}
+
+/**
* pin_is_valid() - check if pin exists on controller
* @pctldev: the pin control device to check the pin on
* @pin: pin to check, use the local pin controller index number
@@ -194,8 +221,10 @@
pindesc->name = name;
} else {
pindesc->name = kasprintf(GFP_KERNEL, "PIN%u", number);
- if (pindesc->name == NULL)
+ if (pindesc->name == NULL) {
+ kfree(pindesc);
return -ENOMEM;
+ }
pindesc->dynamic_name = true;
}
@@ -255,7 +284,8 @@
*
* Find the pin controller handling a certain GPIO pin from the pinspace of
* the GPIO subsystem, return the device and the matching GPIO range. Returns
- * negative if the GPIO range could not be found in any device.
+ * -EPROBE_DEFER if the GPIO range could not be found in any device since it
+ * may still have not been registered.
*/
static int pinctrl_get_device_gpio_range(unsigned gpio,
struct pinctrl_dev **outdev,
@@ -275,7 +305,7 @@
}
}
- return -EINVAL;
+ return -EPROBE_DEFER;
}
/**
@@ -295,6 +325,59 @@
}
EXPORT_SYMBOL_GPL(pinctrl_add_gpio_range);
+void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *ranges,
+ unsigned nranges)
+{
+ int i;
+
+ for (i = 0; i < nranges; i++)
+ pinctrl_add_gpio_range(pctldev, &ranges[i]);
+}
+EXPORT_SYMBOL_GPL(pinctrl_add_gpio_ranges);
+
+struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
+ struct pinctrl_gpio_range *range)
+{
+ struct pinctrl_dev *pctldev = get_pinctrl_dev_from_devname(devname);
+
+ /*
+ * If we can't find this device, let's assume that is because
+ * it has not probed yet, so the driver trying to register this
+ * range need to defer probing.
+ */
+ if (!pctldev)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ pinctrl_add_gpio_range(pctldev, range);
+ return pctldev;
+}
+EXPORT_SYMBOL_GPL(pinctrl_find_and_add_gpio_range);
+
+/**
+ * pinctrl_find_gpio_range_from_pin() - locate the GPIO range for a pin
+ * @pctldev: the pin controller device to look in
+ * @pin: a controller-local number to find the range for
+ */
+struct pinctrl_gpio_range *
+pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev,
+ unsigned int pin)
+{
+ struct pinctrl_gpio_range *range = NULL;
+
+ /* Loop over the ranges */
+ list_for_each_entry(range, &pctldev->gpio_ranges, node) {
+ /* Check if we're in the valid range */
+ if (pin >= range->pin_base &&
+ pin < range->pin_base + range->npins) {
+ return range;
+ }
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(pinctrl_find_gpio_range_from_pin);
+
/**
* pinctrl_remove_gpio_range() - remove a range of GPIOs fro a pin controller
* @pctldev: pin controller device to remove the range from
@@ -318,9 +401,10 @@
const char *pin_group)
{
const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
+ unsigned ngroups = pctlops->get_groups_count(pctldev);
unsigned group_selector = 0;
- while (pctlops->list_groups(pctldev, group_selector) >= 0) {
+ while (group_selector < ngroups) {
const char *gname = pctlops->get_group_name(pctldev,
group_selector);
if (!strcmp(gname, pin_group)) {
@@ -360,7 +444,7 @@
ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range);
if (ret) {
mutex_unlock(&pinctrl_mutex);
- return -EINVAL;
+ return ret;
}
/* Convert to the pin controllers number space */
@@ -516,13 +600,21 @@
setting->pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name);
if (setting->pctldev == NULL) {
- dev_err(p->dev, "unknown pinctrl device %s in map entry",
- map->ctrl_dev_name);
kfree(setting);
- /* Eventually, this should trigger deferred probe */
- return -ENODEV;
+ /* Do not defer probing of hogs (circular loop) */
+ if (!strcmp(map->ctrl_dev_name, map->dev_name))
+ return -ENODEV;
+ /*
+ * OK let us guess that the driver is not there yet, and
+ * let's defer obtaining this pinctrl handle to later...
+ */
+ dev_info(p->dev, "unknown pinctrl device %s in map entry, deferring probe",
+ map->ctrl_dev_name);
+ return -EPROBE_DEFER;
}
+ setting->dev_name = map->dev_name;
+
switch (map->type) {
case PIN_MAP_TYPE_MUX_GROUP:
ret = pinmux_map_to_setting(map, setting);
@@ -579,6 +671,13 @@
}
p->dev = dev;
INIT_LIST_HEAD(&p->states);
+ INIT_LIST_HEAD(&p->dt_maps);
+
+ ret = pinctrl_dt_to_map(p);
+ if (ret < 0) {
+ kfree(p);
+ return ERR_PTR(ret);
+ }
devname = dev_name(dev);
@@ -589,13 +688,33 @@
continue;
ret = add_setting(p, map);
- if (ret < 0) {
+ /*
+ * At this point the adding of a setting may:
+ *
+ * - Defer, if the pinctrl device is not yet available
+ * - Fail, if the pinctrl device is not yet available,
+ * AND the setting is a hog. We cannot defer that, since
+ * the hog will kick in immediately after the device
+ * is registered.
+ *
+ * If the error returned was not -EPROBE_DEFER then we
+ * accumulate the errors to see if we end up with
+ * an -EPROBE_DEFER later, as that is the worst case.
+ */
+ if (ret == -EPROBE_DEFER) {
pinctrl_put_locked(p, false);
return ERR_PTR(ret);
}
}
+ if (ret < 0) {
+ /* If some other error than deferral occured, return here */
+ pinctrl_put_locked(p, false);
+ return ERR_PTR(ret);
+ }
- /* Add the pinmux to the global list */
+ kref_init(&p->users);
+
+ /* Add the pinctrl handle to the global list */
list_add_tail(&p->node, &pinctrl_list);
return p;
@@ -608,15 +727,19 @@
if (WARN_ON(!dev))
return ERR_PTR(-EINVAL);
+ /*
+ * See if somebody else (such as the device core) has already
+ * obtained a handle to the pinctrl for this device. In that case,
+ * return another pointer to it.
+ */
p = find_pinctrl(dev);
- if (p != NULL)
- return ERR_PTR(-EBUSY);
-
- p = create_pinctrl(dev);
- if (IS_ERR(p))
+ if (p != NULL) {
+ dev_dbg(dev, "obtain a copy of previously claimed pinctrl\n");
+ kref_get(&p->users);
return p;
+ }
- return p;
+ return create_pinctrl(dev);
}
/**
@@ -662,19 +785,32 @@
kfree(state);
}
+ pinctrl_dt_free_maps(p);
+
if (inlist)
list_del(&p->node);
kfree(p);
}
/**
- * pinctrl_put() - release a previously claimed pinctrl handle
+ * pinctrl_release() - release the pinctrl handle
+ * @kref: the kref in the pinctrl being released
+ */
+static void pinctrl_release(struct kref *kref)
+{
+ struct pinctrl *p = container_of(kref, struct pinctrl, users);
+
+ pinctrl_put_locked(p, true);
+}
+
+/**
+ * pinctrl_put() - decrease use count on a previously claimed pinctrl handle
* @p: the pinctrl handle to release
*/
void pinctrl_put(struct pinctrl *p)
{
mutex_lock(&pinctrl_mutex);
- pinctrl_put_locked(p, true);
+ kref_put(&p->users, pinctrl_release);
mutex_unlock(&pinctrl_mutex);
}
EXPORT_SYMBOL_GPL(pinctrl_put);
@@ -685,8 +821,15 @@
struct pinctrl_state *state;
state = find_state(p, name);
- if (!state)
- return ERR_PTR(-ENODEV);
+ if (!state) {
+ if (pinctrl_dummy_state) {
+ /* create dummy state */
+ dev_dbg(p->dev, "using pinctrl dummy state (%s)\n",
+ name);
+ state = create_state(p, name);
+ } else
+ state = ERR_PTR(-ENODEV);
+ }
return state;
}
@@ -787,15 +930,63 @@
}
EXPORT_SYMBOL_GPL(pinctrl_select_state);
+static void devm_pinctrl_release(struct device *dev, void *res)
+{
+ pinctrl_put(*(struct pinctrl **)res);
+}
+
/**
- * pinctrl_register_mappings() - register a set of pin controller mappings
- * @maps: the pincontrol mappings table to register. This should probably be
- * marked with __initdata so it can be discarded after boot. This
- * function will perform a shallow copy for the mapping entries.
- * @num_maps: the number of maps in the mapping table
+ * struct devm_pinctrl_get() - Resource managed pinctrl_get()
+ * @dev: the device to obtain the handle for
+ *
+ * If there is a need to explicitly destroy the returned struct pinctrl,
+ * devm_pinctrl_put() should be used, rather than plain pinctrl_put().
*/
-int pinctrl_register_mappings(struct pinctrl_map const *maps,
- unsigned num_maps)
+struct pinctrl *devm_pinctrl_get(struct device *dev)
+{
+ struct pinctrl **ptr, *p;
+
+ ptr = devres_alloc(devm_pinctrl_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ p = pinctrl_get(dev);
+ if (!IS_ERR(p)) {
+ *ptr = p;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return p;
+}
+EXPORT_SYMBOL_GPL(devm_pinctrl_get);
+
+static int devm_pinctrl_match(struct device *dev, void *res, void *data)
+{
+ struct pinctrl **p = res;
+
+ return *p == data;
+}
+
+/**
+ * devm_pinctrl_put() - Resource managed pinctrl_put()
+ * @p: the pinctrl handle to release
+ *
+ * Deallocate a struct pinctrl obtained via devm_pinctrl_get(). Normally
+ * this function will not need to be called and the resource management
+ * code will ensure that the resource is freed.
+ */
+void devm_pinctrl_put(struct pinctrl *p)
+{
+ WARN_ON(devres_destroy(p->dev, devm_pinctrl_release,
+ devm_pinctrl_match, p));
+ pinctrl_put(p);
+}
+EXPORT_SYMBOL_GPL(devm_pinctrl_put);
+
+int pinctrl_register_map(struct pinctrl_map const *maps, unsigned num_maps,
+ bool dup, bool locked)
{
int i, ret;
struct pinctrl_maps *maps_node;
@@ -829,13 +1020,13 @@
case PIN_MAP_TYPE_MUX_GROUP:
ret = pinmux_validate_map(&maps[i], i);
if (ret < 0)
- return 0;
+ return ret;
break;
case PIN_MAP_TYPE_CONFIGS_PIN:
case PIN_MAP_TYPE_CONFIGS_GROUP:
ret = pinconf_validate_map(&maps[i], i);
if (ret < 0)
- return 0;
+ return ret;
break;
default:
pr_err("failed to register map %s (%d): invalid type given\n",
@@ -851,20 +1042,76 @@
}
maps_node->num_maps = num_maps;
- maps_node->maps = kmemdup(maps, sizeof(*maps) * num_maps, GFP_KERNEL);
- if (!maps_node->maps) {
- pr_err("failed to duplicate mapping table\n");
- kfree(maps_node);
- return -ENOMEM;
+ if (dup) {
+ maps_node->maps = kmemdup(maps, sizeof(*maps) * num_maps,
+ GFP_KERNEL);
+ if (!maps_node->maps) {
+ pr_err("failed to duplicate mapping table\n");
+ kfree(maps_node);
+ return -ENOMEM;
+ }
+ } else {
+ maps_node->maps = maps;
}
- mutex_lock(&pinctrl_mutex);
+ if (!locked)
+ mutex_lock(&pinctrl_mutex);
list_add_tail(&maps_node->node, &pinctrl_maps);
- mutex_unlock(&pinctrl_mutex);
+ if (!locked)
+ mutex_unlock(&pinctrl_mutex);
return 0;
}
+/**
+ * pinctrl_register_mappings() - register a set of pin controller mappings
+ * @maps: the pincontrol mappings table to register. This should probably be
+ * marked with __initdata so it can be discarded after boot. This
+ * function will perform a shallow copy for the mapping entries.
+ * @num_maps: the number of maps in the mapping table
+ */
+int pinctrl_register_mappings(struct pinctrl_map const *maps,
+ unsigned num_maps)
+{
+ return pinctrl_register_map(maps, num_maps, true, false);
+}
+
+void pinctrl_unregister_map(struct pinctrl_map const *map)
+{
+ struct pinctrl_maps *maps_node;
+
+ list_for_each_entry(maps_node, &pinctrl_maps, node) {
+ if (maps_node->maps == map) {
+ list_del(&maps_node->node);
+ return;
+ }
+ }
+}
+
+/**
+ * pinctrl_force_sleep() - turn a given controller device into sleep state
+ * @pctldev: pin controller device
+ */
+int pinctrl_force_sleep(struct pinctrl_dev *pctldev)
+{
+ if (!IS_ERR(pctldev->p) && !IS_ERR(pctldev->hog_sleep))
+ return pinctrl_select_state(pctldev->p, pctldev->hog_sleep);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pinctrl_force_sleep);
+
+/**
+ * pinctrl_force_default() - turn a given controller device into default state
+ * @pctldev: pin controller device
+ */
+int pinctrl_force_default(struct pinctrl_dev *pctldev)
+{
+ if (!IS_ERR(pctldev->p) && !IS_ERR(pctldev->hog_default))
+ return pinctrl_select_state(pctldev->p, pctldev->hog_default);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pinctrl_force_default);
+
#ifdef CONFIG_DEBUG_FS
static int pinctrl_pins_show(struct seq_file *s, void *what)
@@ -906,15 +1153,17 @@
{
struct pinctrl_dev *pctldev = s->private;
const struct pinctrl_ops *ops = pctldev->desc->pctlops;
- unsigned selector = 0;
+ unsigned ngroups, selector = 0;
+ ngroups = ops->get_groups_count(pctldev);
mutex_lock(&pinctrl_mutex);
seq_puts(s, "registered pin groups:\n");
- while (ops->list_groups(pctldev, selector) >= 0) {
+ while (selector < ngroups) {
const unsigned *pins;
unsigned num_pins;
const char *gname = ops->get_group_name(pctldev, selector);
+ const char *pname;
int ret;
int i;
@@ -924,10 +1173,16 @@
seq_printf(s, "%s [ERROR GETTING PINS]\n",
gname);
else {
- seq_printf(s, "group: %s, pins = [ ", gname);
- for (i = 0; i < num_pins; i++)
- seq_printf(s, "%d ", pins[i]);
- seq_puts(s, "]\n");
+ seq_printf(s, "group: %s\n", gname);
+ for (i = 0; i < num_pins; i++) {
+ pname = pin_get_name(pctldev, pins[i]);
+ if (WARN_ON(!pname)) {
+ mutex_unlock(&pinctrl_mutex);
+ return -EINVAL;
+ }
+ seq_printf(s, "pin %d (%s)\n", pins[i], pname);
+ }
+ seq_puts(s, "\n");
}
selector++;
}
@@ -1226,11 +1481,14 @@
const struct pinctrl_ops *ops = pctldev->desc->pctlops;
if (!ops ||
- !ops->list_groups ||
+ !ops->get_groups_count ||
!ops->get_group_name ||
!ops->get_group_pins)
return -EINVAL;
+ if (ops->dt_node_to_map && !ops->dt_free_map)
+ return -EINVAL;
+
return 0;
}
@@ -1246,9 +1504,9 @@
struct pinctrl_dev *pctldev;
int ret;
- if (pctldesc == NULL)
+ if (!pctldesc)
return NULL;
- if (pctldesc->name == NULL)
+ if (!pctldesc->name)
return NULL;
pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL);
@@ -1266,39 +1524,28 @@
pctldev->dev = dev;
/* check core ops for sanity */
- ret = pinctrl_check_ops(pctldev);
- if (ret) {
- pr_err("%s pinctrl ops lacks necessary functions\n",
- pctldesc->name);
+ if (pinctrl_check_ops(pctldev)) {
+ dev_err(dev, "pinctrl ops lacks necessary functions\n");
goto out_err;
}
/* If we're implementing pinmuxing, check the ops for sanity */
if (pctldesc->pmxops) {
- ret = pinmux_check_ops(pctldev);
- if (ret) {
- pr_err("%s pinmux ops lacks necessary functions\n",
- pctldesc->name);
+ if (pinmux_check_ops(pctldev))
goto out_err;
- }
}
/* If we're implementing pinconfig, check the ops for sanity */
if (pctldesc->confops) {
- ret = pinconf_check_ops(pctldev);
- if (ret) {
- pr_err("%s pin config ops lacks necessary functions\n",
- pctldesc->name);
+ if (pinconf_check_ops(pctldev))
goto out_err;
- }
}
/* Register all the pins */
- pr_debug("try to register %d pins on %s...\n",
- pctldesc->npins, pctldesc->name);
+ dev_dbg(dev, "try to register %d pins ...\n", pctldesc->npins);
ret = pinctrl_register_pins(pctldev, pctldesc->pins, pctldesc->npins);
if (ret) {
- pr_err("error during pin registration\n");
+ dev_err(dev, "error during pin registration\n");
pinctrl_free_pindescs(pctldev, pctldesc->pins,
pctldesc->npins);
goto out_err;
@@ -1310,11 +1557,23 @@
pctldev->p = pinctrl_get_locked(pctldev->dev);
if (!IS_ERR(pctldev->p)) {
- struct pinctrl_state *s =
+ pctldev->hog_default =
pinctrl_lookup_state_locked(pctldev->p,
PINCTRL_STATE_DEFAULT);
- if (!IS_ERR(s))
- pinctrl_select_state_locked(pctldev->p, s);
+ if (IS_ERR(pctldev->hog_default)) {
+ dev_dbg(dev, "failed to lookup the default state\n");
+ } else {
+ if (pinctrl_select_state_locked(pctldev->p,
+ pctldev->hog_default))
+ dev_err(dev,
+ "failed to select default state\n");
+ }
+
+ pctldev->hog_sleep =
+ pinctrl_lookup_state_locked(pctldev->p,
+ PINCTRL_STATE_SLEEP);
+ if (IS_ERR(pctldev->hog_sleep))
+ dev_dbg(dev, "failed to lookup the sleep state\n");
}
mutex_unlock(&pinctrl_mutex);
@@ -1337,6 +1596,7 @@
*/
void pinctrl_unregister(struct pinctrl_dev *pctldev)
{
+ struct pinctrl_gpio_range *range, *n;
if (pctldev == NULL)
return;
@@ -1352,6 +1612,10 @@
/* Destroy descriptor tree */
pinctrl_free_pindescs(pctldev, pctldev->desc->pins,
pctldev->desc->npins);
+ /* remove gpio ranges map */
+ list_for_each_entry_safe(range, n, &pctldev->gpio_ranges, node)
+ list_del(&range->node);
+
kfree(pctldev);
mutex_unlock(&pinctrl_mutex);
diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h
index 17ecf65..ee72f1f 100644
--- a/drivers/pinctrl/core.h
+++ b/drivers/pinctrl/core.h
@@ -9,6 +9,7 @@
* License terms: GNU General Public License (GPL) version 2
*/
+#include <linux/kref.h>
#include <linux/mutex.h>
#include <linux/radix-tree.h>
#include <linux/pinctrl/pinconf.h>
@@ -30,6 +31,8 @@
* @driver_data: driver data for drivers registering to the pin controller
* subsystem
* @p: result of pinctrl_get() for this device
+ * @hog_default: default state for pins hogged by this device
+ * @hog_sleep: sleep state for pins hogged by this device
* @device_root: debugfs root for this device
*/
struct pinctrl_dev {
@@ -41,6 +44,8 @@
struct module *owner;
void *driver_data;
struct pinctrl *p;
+ struct pinctrl_state *hog_default;
+ struct pinctrl_state *hog_sleep;
#ifdef CONFIG_DEBUG_FS
struct dentry *device_root;
#endif
@@ -52,12 +57,17 @@
* @dev: the device using this pin control handle
* @states: a list of states for this device
* @state: the current state
+ * @dt_maps: the mapping table chunks dynamically parsed from device tree for
+ * this device, if any
+ * @users: reference count
*/
struct pinctrl {
struct list_head node;
struct device *dev;
struct list_head states;
struct pinctrl_state *state;
+ struct list_head dt_maps;
+ struct kref users;
};
/**
@@ -100,13 +110,16 @@
* struct pinctrl_setting - an individual mux or config setting
* @node: list node for struct pinctrl_settings's @settings field
* @type: the type of setting
- * @pctldev: pin control device handling to be programmed
+ * @pctldev: pin control device handling to be programmed. Not used for
+ * PIN_MAP_TYPE_DUMMY_STATE.
+ * @dev_name: the name of the device using this state
* @data: Data specific to the setting type
*/
struct pinctrl_setting {
struct list_head node;
enum pinctrl_map_type type;
struct pinctrl_dev *pctldev;
+ const char *dev_name;
union {
struct pinctrl_setting_mux mux;
struct pinctrl_setting_configs configs;
@@ -142,8 +155,21 @@
#endif
};
+/**
+ * struct pinctrl_maps - a list item containing part of the mapping table
+ * @node: mapping table list node
+ * @maps: array of mapping table entries
+ * @num_maps: the number of entries in @maps
+ */
+struct pinctrl_maps {
+ struct list_head node;
+ struct pinctrl_map const *maps;
+ unsigned num_maps;
+};
+
struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *dev_name);
int pin_get_from_name(struct pinctrl_dev *pctldev, const char *name);
+const char *pin_get_name(struct pinctrl_dev *pctldev, const unsigned pin);
int pinctrl_get_group_selector(struct pinctrl_dev *pctldev,
const char *pin_group);
@@ -153,4 +179,19 @@
return radix_tree_lookup(&pctldev->pin_desc_tree, pin);
}
+int pinctrl_register_map(struct pinctrl_map const *maps, unsigned num_maps,
+ bool dup, bool locked);
+void pinctrl_unregister_map(struct pinctrl_map const *map);
+
+extern int pinctrl_force_sleep(struct pinctrl_dev *pctldev);
+extern int pinctrl_force_default(struct pinctrl_dev *pctldev);
+
extern struct mutex pinctrl_mutex;
+extern struct list_head pinctrldev_list;
+extern struct list_head pinctrl_maps;
+
+#define for_each_maps(_maps_node_, _i_, _map_) \
+ list_for_each_entry(_maps_node_, &pinctrl_maps, node) \
+ for (_i_ = 0, _map_ = &_maps_node_->maps[_i_]; \
+ _i_ < _maps_node_->num_maps; \
+ _i_++, _map_ = &_maps_node_->maps[_i_])
diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c
new file mode 100644
index 0000000..fd40a11
--- /dev/null
+++ b/drivers/pinctrl/devicetree.c
@@ -0,0 +1,265 @@
+/*
+ * Device tree integration for the pin control subsystem
+ *
+ * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/device.h>
+#include <linux/of.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/slab.h>
+
+#include "core.h"
+#include "devicetree.h"
+
+/**
+ * struct pinctrl_dt_map - mapping table chunk parsed from device tree
+ * @node: list node for struct pinctrl's @dt_maps field
+ * @pctldev: the pin controller that allocated this struct, and will free it
+ * @maps: the mapping table entries
+ */
+struct pinctrl_dt_map {
+ struct list_head node;
+ struct pinctrl_dev *pctldev;
+ struct pinctrl_map *map;
+ unsigned num_maps;
+};
+
+static void dt_free_map(struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, unsigned num_maps)
+{
+ if (pctldev) {
+ struct pinctrl_ops *ops = pctldev->desc->pctlops;
+ ops->dt_free_map(pctldev, map, num_maps);
+ } else {
+ /* There is no pctldev for PIN_MAP_TYPE_DUMMY_STATE */
+ kfree(map);
+ }
+}
+
+void pinctrl_dt_free_maps(struct pinctrl *p)
+{
+ struct pinctrl_dt_map *dt_map, *n1;
+
+ list_for_each_entry_safe(dt_map, n1, &p->dt_maps, node) {
+ pinctrl_unregister_map(dt_map->map);
+ list_del(&dt_map->node);
+ dt_free_map(dt_map->pctldev, dt_map->map,
+ dt_map->num_maps);
+ kfree(dt_map);
+ }
+
+ of_node_put(p->dev->of_node);
+}
+
+static int dt_remember_or_free_map(struct pinctrl *p, const char *statename,
+ struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, unsigned num_maps)
+{
+ int i;
+ struct pinctrl_dt_map *dt_map;
+
+ /* Initialize common mapping table entry fields */
+ for (i = 0; i < num_maps; i++) {
+ map[i].dev_name = dev_name(p->dev);
+ map[i].name = statename;
+ if (pctldev)
+ map[i].ctrl_dev_name = dev_name(pctldev->dev);
+ }
+
+ /* Remember the converted mapping table entries */
+ dt_map = kzalloc(sizeof(*dt_map), GFP_KERNEL);
+ if (!dt_map) {
+ dev_err(p->dev, "failed to alloc struct pinctrl_dt_map\n");
+ dt_free_map(pctldev, map, num_maps);
+ return -ENOMEM;
+ }
+
+ dt_map->pctldev = pctldev;
+ dt_map->map = map;
+ dt_map->num_maps = num_maps;
+ list_add_tail(&dt_map->node, &p->dt_maps);
+
+ return pinctrl_register_map(map, num_maps, false, true);
+}
+
+static struct pinctrl_dev *find_pinctrl_by_of_node(struct device_node *np)
+{
+ struct pinctrl_dev *pctldev;
+
+ list_for_each_entry(pctldev, &pinctrldev_list, node)
+ if (pctldev->dev->of_node == np)
+ return pctldev;
+
+ return NULL;
+}
+
+struct pinctrl_dev *of_pinctrl_get(struct device_node *np)
+{
+ struct pinctrl_dev *pctldev;
+
+ pctldev = find_pinctrl_by_of_node(np);
+ if (!pctldev)
+ return NULL;
+
+ return pctldev;
+}
+
+static int dt_to_map_one_config(struct pinctrl *p, const char *statename,
+ struct device_node *np_config)
+{
+ struct device_node *np_pctldev;
+ struct pinctrl_dev *pctldev;
+ struct pinctrl_ops *ops;
+ int ret;
+ struct pinctrl_map *map;
+ unsigned num_maps;
+
+ /* Find the pin controller containing np_config */
+ np_pctldev = of_node_get(np_config);
+ for (;;) {
+ np_pctldev = of_get_next_parent(np_pctldev);
+ if (!np_pctldev || of_node_is_root(np_pctldev)) {
+ dev_info(p->dev, "could not find pctldev for node %s, deferring probe\n",
+ np_config->full_name);
+ of_node_put(np_pctldev);
+ /* OK let's just assume this will appear later then */
+ return -EPROBE_DEFER;
+ }
+ pctldev = find_pinctrl_by_of_node(np_pctldev);
+ if (pctldev)
+ break;
+ /* Do not defer probing of hogs (circular loop) */
+ if (np_pctldev == p->dev->of_node) {
+ of_node_put(np_pctldev);
+ return -ENODEV;
+ }
+ }
+ of_node_put(np_pctldev);
+
+ /*
+ * Call pinctrl driver to parse device tree node, and
+ * generate mapping table entries
+ */
+ ops = pctldev->desc->pctlops;
+ if (!ops->dt_node_to_map) {
+ dev_err(p->dev, "pctldev %s doesn't support DT\n",
+ dev_name(pctldev->dev));
+ return -ENODEV;
+ }
+ ret = ops->dt_node_to_map(pctldev, np_config, &map, &num_maps);
+ if (ret < 0)
+ return ret;
+
+ /* Stash the mapping table chunk away for later use */
+ return dt_remember_or_free_map(p, statename, pctldev, map, num_maps);
+}
+
+static int dt_remember_dummy_state(struct pinctrl *p, const char *statename)
+{
+ struct pinctrl_map *map;
+
+ map = kzalloc(sizeof(*map), GFP_KERNEL);
+ if (!map) {
+ dev_err(p->dev, "failed to alloc struct pinctrl_map\n");
+ return -ENOMEM;
+ }
+
+ /* There is no pctldev for PIN_MAP_TYPE_DUMMY_STATE */
+ map->type = PIN_MAP_TYPE_DUMMY_STATE;
+
+ return dt_remember_or_free_map(p, statename, NULL, map, 1);
+}
+
+int pinctrl_dt_to_map(struct pinctrl *p)
+{
+ struct device_node *np = p->dev->of_node;
+ int state, ret;
+ char *propname;
+ struct property *prop;
+ const char *statename;
+ const __be32 *list;
+ int size, config;
+ phandle phandle;
+ struct device_node *np_config;
+
+ /* CONFIG_OF enabled, p->dev not instantiated from DT */
+ if (!np) {
+ dev_dbg(p->dev, "no of_node; not parsing pinctrl DT\n");
+ return 0;
+ }
+
+ /* We may store pointers to property names within the node */
+ of_node_get(np);
+
+ /* For each defined state ID */
+ for (state = 0; ; state++) {
+ /* Retrieve the pinctrl-* property */
+ propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state);
+ prop = of_find_property(np, propname, &size);
+ kfree(propname);
+ if (!prop)
+ break;
+ list = prop->value;
+ size /= sizeof(*list);
+
+ /* Determine whether pinctrl-names property names the state */
+ ret = of_property_read_string_index(np, "pinctrl-names",
+ state, &statename);
+ /*
+ * If not, statename is just the integer state ID. But rather
+ * than dynamically allocate it and have to free it later,
+ * just point part way into the property name for the string.
+ */
+ if (ret < 0) {
+ /* strlen("pinctrl-") == 8 */
+ statename = prop->name + 8;
+ }
+
+ /* For every referenced pin configuration node in it */
+ for (config = 0; config < size; config++) {
+ phandle = be32_to_cpup(list++);
+
+ /* Look up the pin configuration node */
+ np_config = of_find_node_by_phandle(phandle);
+ if (!np_config) {
+ dev_err(p->dev,
+ "prop %s index %i invalid phandle\n",
+ prop->name, config);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Parse the node */
+ ret = dt_to_map_one_config(p, statename, np_config);
+ of_node_put(np_config);
+ if (ret < 0)
+ goto err;
+ }
+
+ /* No entries in DT? Generate a dummy state table entry */
+ if (!size) {
+ ret = dt_remember_dummy_state(p, statename);
+ if (ret < 0)
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ pinctrl_dt_free_maps(p);
+ return ret;
+}
diff --git a/drivers/pinctrl/devicetree.h b/drivers/pinctrl/devicetree.h
new file mode 100644
index 0000000..760bc49
--- /dev/null
+++ b/drivers/pinctrl/devicetree.h
@@ -0,0 +1,35 @@
+/*
+ * Internal interface to pinctrl device tree integration
+ *
+ * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#ifdef CONFIG_OF
+
+void pinctrl_dt_free_maps(struct pinctrl *p);
+int pinctrl_dt_to_map(struct pinctrl *p);
+
+#else
+
+static inline int pinctrl_dt_to_map(struct pinctrl *p)
+{
+ return 0;
+}
+
+static inline void pinctrl_dt_free_maps(struct pinctrl *p)
+{
+}
+
+#endif
diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c
index 33fbaea..06c304a 100644
--- a/drivers/pinctrl/pinconf-generic.c
+++ b/drivers/pinctrl/pinconf-generic.c
@@ -41,10 +41,13 @@
PCONFDUMP(PIN_CONFIG_DRIVE_PUSH_PULL, "output drive push pull", NULL),
PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_DRAIN, "output drive open drain", NULL),
PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_SOURCE, "output drive open source", NULL),
+ PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT_ENABLE, "input schmitt enabled", NULL),
PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT, "input schmitt trigger", NULL),
PCONFDUMP(PIN_CONFIG_INPUT_DEBOUNCE, "input debounce", "time units"),
PCONFDUMP(PIN_CONFIG_POWER_SOURCE, "pin power source", "selector"),
+ PCONFDUMP(PIN_CONFIG_SLEW_RATE, "slew rate", NULL),
PCONFDUMP(PIN_CONFIG_LOW_POWER_MODE, "pin low power", "mode"),
+ PCONFDUMP(PIN_CONFIG_OUTPUT, "pin output", "level"),
};
void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev,
diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c
index 7321e86..d611ecf 100644
--- a/drivers/pinctrl/pinconf.c
+++ b/drivers/pinctrl/pinconf.c
@@ -28,11 +28,17 @@
const struct pinconf_ops *ops = pctldev->desc->confops;
/* We must be able to read out pin status */
- if (!ops->pin_config_get && !ops->pin_config_group_get)
+ if (!ops->pin_config_get && !ops->pin_config_group_get) {
+ dev_err(pctldev->dev,
+ "pinconf must be able to read out pin status\n");
return -EINVAL;
+ }
/* We have to be able to config the pins in SOME way */
- if (!ops->pin_config_set && !ops->pin_config_group_set)
+ if (!ops->pin_config_set && !ops->pin_config_group_set) {
+ dev_err(pctldev->dev,
+ "pinconf has to be able to set a pins config\n");
return -EINVAL;
+ }
return 0;
}
@@ -44,9 +50,9 @@
return -EINVAL;
}
- if (map->data.configs.num_configs &&
+ if (!map->data.configs.num_configs ||
!map->data.configs.configs) {
- pr_err("failed to register map %s (%d): no configs ptr given\n",
+ pr_err("failed to register map %s (%d): no configs given\n",
map->name, i);
return -EINVAL;
}
@@ -379,8 +385,16 @@
void pinconf_show_map(struct seq_file *s, struct pinctrl_map const *map)
{
+ struct pinctrl_dev *pctldev;
+ const struct pinconf_ops *confops;
int i;
+ pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name);
+ if (pctldev)
+ confops = pctldev->desc->confops;
+ else
+ confops = NULL;
+
switch (map->type) {
case PIN_MAP_TYPE_CONFIGS_PIN:
seq_printf(s, "pin ");
@@ -394,8 +408,15 @@
seq_printf(s, "%s\n", map->data.configs.group_or_pin);
- for (i = 0; i < map->data.configs.num_configs; i++)
- seq_printf(s, "config %08lx\n", map->data.configs.configs[i]);
+ for (i = 0; i < map->data.configs.num_configs; i++) {
+ seq_printf(s, "config ");
+ if (confops && confops->pin_config_config_dbg_show)
+ confops->pin_config_config_dbg_show(pctldev, s,
+ map->data.configs.configs[i]);
+ else
+ seq_printf(s, "%08lx", map->data.configs.configs[i]);
+ seq_printf(s, "\n");
+ }
}
void pinconf_show_setting(struct seq_file *s,
@@ -403,6 +424,7 @@
{
struct pinctrl_dev *pctldev = setting->pctldev;
const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
+ const struct pinconf_ops *confops = pctldev->desc->confops;
struct pin_desc *desc;
int i;
@@ -428,8 +450,15 @@
* FIXME: We should really get the pin controler to dump the config
* values, so they can be decoded to something meaningful.
*/
- for (i = 0; i < setting->data.configs.num_configs; i++)
- seq_printf(s, " %08lx", setting->data.configs.configs[i]);
+ for (i = 0; i < setting->data.configs.num_configs; i++) {
+ seq_printf(s, " ");
+ if (confops && confops->pin_config_config_dbg_show)
+ confops->pin_config_config_dbg_show(pctldev, s,
+ setting->data.configs.configs[i]);
+ else
+ seq_printf(s, "%08lx",
+ setting->data.configs.configs[i]);
+ }
seq_printf(s, "\n");
}
@@ -448,10 +477,14 @@
static int pinconf_pins_show(struct seq_file *s, void *what)
{
struct pinctrl_dev *pctldev = s->private;
+ const struct pinconf_ops *ops = pctldev->desc->confops;
unsigned i, pin;
+ if (!ops || !ops->pin_config_get)
+ return 0;
+
seq_puts(s, "Pin config settings per pin\n");
- seq_puts(s, "Format: pin (name): pinmux setting array\n");
+ seq_puts(s, "Format: pin (name): configs\n");
mutex_lock(&pinctrl_mutex);
@@ -495,17 +528,16 @@
struct pinctrl_dev *pctldev = s->private;
const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
const struct pinconf_ops *ops = pctldev->desc->confops;
+ unsigned ngroups = pctlops->get_groups_count(pctldev);
unsigned selector = 0;
if (!ops || !ops->pin_config_group_get)
return 0;
seq_puts(s, "Pin config settings per pin group\n");
- seq_puts(s, "Format: group (name): pinmux setting array\n");
+ seq_puts(s, "Format: group (name): configs\n");
- mutex_lock(&pinctrl_mutex);
-
- while (pctlops->list_groups(pctldev, selector) >= 0) {
+ while (selector < ngroups) {
const char *gname = pctlops->get_group_name(pctldev, selector);
seq_printf(s, "%u (%s):", selector, gname);
@@ -515,8 +547,6 @@
selector++;
}
- mutex_unlock(&pinctrl_mutex);
-
return 0;
}
@@ -544,6 +574,207 @@
.release = single_release,
};
+/* 32bit read/write ressources */
+#define MAX_NAME_LEN 16
+char dbg_pinname[MAX_NAME_LEN]; /* shared: name of the state of the pin*/
+char dbg_state_name[MAX_NAME_LEN]; /* shared: state of the pin*/
+static u32 dbg_config; /* shared: config to be read/set for the pin & state*/
+
+static int pinconf_dbg_pinname_print(struct seq_file *s, void *d)
+{
+ if (strlen(dbg_pinname))
+ seq_printf(s, "%s\n", dbg_pinname);
+ else
+ seq_printf(s, "No pin name set\n");
+ return 0;
+}
+
+static int pinconf_dbg_pinname_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pinconf_dbg_pinname_print, inode->i_private);
+}
+
+static int pinconf_dbg_pinname_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ int err;
+
+ if (count > MAX_NAME_LEN)
+ return -EINVAL;
+
+ err = sscanf(user_buf, "%15s", dbg_pinname);
+
+ if (err != 1)
+ return -EINVAL;
+
+ return count;
+}
+
+static const struct file_operations pinconf_dbg_pinname_fops = {
+ .open = pinconf_dbg_pinname_open,
+ .write = pinconf_dbg_pinname_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int pinconf_dbg_state_print(struct seq_file *s, void *d)
+{
+ if (strlen(dbg_state_name))
+ seq_printf(s, "%s\n", dbg_state_name);
+ else
+ seq_printf(s, "No pin state set\n");
+ return 0;
+}
+
+static int pinconf_dbg_state_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pinconf_dbg_state_print, inode->i_private);
+}
+
+static int pinconf_dbg_state_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ int err;
+
+ if (count > MAX_NAME_LEN)
+ return -EINVAL;
+
+ err = sscanf(user_buf, "%15s", dbg_state_name);
+
+ if (err != 1)
+ return -EINVAL;
+
+ return count;
+}
+
+static const struct file_operations pinconf_dbg_pinstate_fops = {
+ .open = pinconf_dbg_state_open,
+ .write = pinconf_dbg_state_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+/**
+ * pinconf_dbg_config_print() - display the pinctrl config from the pinctrl
+ * map, of a pin/state pair based on pinname and state that have been
+ * selected with the debugfs entries pinconf-name and pinconf-state
+ * @s: contains the 32bits config to be written
+ * @d: not used
+ */
+static int pinconf_dbg_config_print(struct seq_file *s, void *d)
+{
+ struct pinctrl_maps *maps_node;
+ struct pinctrl_map const *map;
+ struct pinctrl_dev *pctldev = NULL;
+ struct pinconf_ops *confops = NULL;
+ int i, j;
+ bool found = false;
+
+ mutex_lock(&pinctrl_mutex);
+
+ /* Parse the pinctrl map and look for the elected pin/state */
+ for_each_maps(maps_node, i, map) {
+ if (map->type != PIN_MAP_TYPE_CONFIGS_PIN)
+ continue;
+
+ if (strncmp(map->name, dbg_state_name, MAX_NAME_LEN) > 0)
+ continue;
+
+ for (j = 0; j < map->data.configs.num_configs; j++) {
+ if (0 == strncmp(map->data.configs.group_or_pin,
+ dbg_pinname, MAX_NAME_LEN)) {
+ /* We found the right pin / state, read the
+ * config and store the pctldev */
+ dbg_config = map->data.configs.configs[j];
+ pctldev = get_pinctrl_dev_from_devname
+ (map->ctrl_dev_name);
+ found = true;
+ break;
+ }
+ }
+ }
+
+ mutex_unlock(&pinctrl_mutex);
+
+ if (found) {
+ seq_printf(s, "Config of %s in state %s: 0x%08X\n", dbg_pinname,
+ dbg_state_name, dbg_config);
+
+ if (pctldev)
+ confops = pctldev->desc->confops;
+
+ if (confops && confops->pin_config_config_dbg_show)
+ confops->pin_config_config_dbg_show(pctldev,
+ s, dbg_config);
+ } else {
+ seq_printf(s, "No pin found for defined name/state\n");
+ }
+
+ return 0;
+}
+
+static int pinconf_dbg_config_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pinconf_dbg_config_print, inode->i_private);
+}
+
+/**
+ * pinconf_dbg_config_write() - overwrite the pinctrl config in thepinctrl
+ * map, of a pin/state pair based on pinname and state that have been
+ * selected with the debugfs entries pinconf-name and pinconf-state
+ */
+static int pinconf_dbg_config_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ int err;
+ unsigned long config;
+ struct pinctrl_maps *maps_node;
+ struct pinctrl_map const *map;
+ int i, j;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &config);
+
+ if (err)
+ return err;
+
+ dbg_config = config;
+
+ mutex_lock(&pinctrl_mutex);
+
+ /* Parse the pinctrl map and look for the selected pin/state */
+ for_each_maps(maps_node, i, map) {
+ if (map->type != PIN_MAP_TYPE_CONFIGS_PIN)
+ continue;
+
+ if (strncmp(map->name, dbg_state_name, MAX_NAME_LEN) > 0)
+ continue;
+
+ /* we found the right pin / state, so overwrite config */
+ for (j = 0; j < map->data.configs.num_configs; j++) {
+ if (strncmp(map->data.configs.group_or_pin, dbg_pinname,
+ MAX_NAME_LEN) == 0)
+ map->data.configs.configs[j] = dbg_config;
+ }
+ }
+
+ mutex_unlock(&pinctrl_mutex);
+
+ return count;
+}
+
+static const struct file_operations pinconf_dbg_pinconfig_fops = {
+ .open = pinconf_dbg_config_open,
+ .write = pinconf_dbg_config_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
void pinconf_init_device_debugfs(struct dentry *devroot,
struct pinctrl_dev *pctldev)
{
@@ -551,6 +782,12 @@
devroot, pctldev, &pinconf_pins_ops);
debugfs_create_file("pinconf-groups", S_IFREG | S_IRUGO,
devroot, pctldev, &pinconf_groups_ops);
+ debugfs_create_file("pinconf-name", (S_IRUGO | S_IWUSR | S_IWGRP),
+ devroot, pctldev, &pinconf_dbg_pinname_fops);
+ debugfs_create_file("pinconf-state", (S_IRUGO | S_IWUSR | S_IWGRP),
+ devroot, pctldev, &pinconf_dbg_pinstate_fops);
+ debugfs_create_file("pinconf-config", (S_IRUGO | S_IWUSR | S_IWGRP),
+ devroot, pctldev, &pinconf_dbg_pinconfig_fops);
}
#endif
diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h
index 54510de..bfda73d 100644
--- a/drivers/pinctrl/pinconf.h
+++ b/drivers/pinctrl/pinconf.h
@@ -19,11 +19,6 @@
struct pinctrl_setting *setting);
void pinconf_free_setting(struct pinctrl_setting const *setting);
int pinconf_apply_setting(struct pinctrl_setting const *setting);
-void pinconf_show_map(struct seq_file *s, struct pinctrl_map const *map);
-void pinconf_show_setting(struct seq_file *s,
- struct pinctrl_setting const *setting);
-void pinconf_init_device_debugfs(struct dentry *devroot,
- struct pinctrl_dev *pctldev);
/*
* You will only be interested in these if you're using PINCONF
@@ -61,6 +56,18 @@
return 0;
}
+#endif
+
+#if defined(CONFIG_PINCONF) && defined(CONFIG_DEBUG_FS)
+
+void pinconf_show_map(struct seq_file *s, struct pinctrl_map const *map);
+void pinconf_show_setting(struct seq_file *s,
+ struct pinctrl_setting const *setting);
+void pinconf_init_device_debugfs(struct dentry *devroot,
+ struct pinctrl_dev *pctldev);
+
+#else
+
static inline void pinconf_show_map(struct seq_file *s,
struct pinctrl_map const *map)
{
@@ -83,7 +90,7 @@
* pin config.
*/
-#ifdef CONFIG_GENERIC_PINCONF
+#if defined(CONFIG_GENERIC_PINCONF) && defined(CONFIG_DEBUG_FS)
void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev,
struct seq_file *s, unsigned pin);
diff --git a/drivers/pinctrl/pinctrl-pxa3xx.c b/drivers/pinctrl/pinctrl-pxa3xx.c
index 079dce0..7644e42 100644
--- a/drivers/pinctrl/pinctrl-pxa3xx.c
+++ b/drivers/pinctrl/pinctrl-pxa3xx.c
@@ -25,20 +25,18 @@
.pin_base = 0,
};
-static int pxa3xx_list_groups(struct pinctrl_dev *pctrldev, unsigned selector)
+static int pxa3xx_get_groups_count(struct pinctrl_dev *pctrldev)
{
struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev);
- if (selector >= info->num_grps)
- return -EINVAL;
- return 0;
+
+ return info->num_grps;
}
static const char *pxa3xx_get_group_name(struct pinctrl_dev *pctrldev,
unsigned selector)
{
struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev);
- if (selector >= info->num_grps)
- return NULL;
+
return info->grps[selector].name;
}
@@ -48,25 +46,23 @@
unsigned *num_pins)
{
struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev);
- if (selector >= info->num_grps)
- return -EINVAL;
+
*pins = info->grps[selector].pins;
*num_pins = info->grps[selector].npins;
return 0;
}
static struct pinctrl_ops pxa3xx_pctrl_ops = {
- .list_groups = pxa3xx_list_groups,
+ .get_groups_count = pxa3xx_get_groups_count,
.get_group_name = pxa3xx_get_group_name,
.get_group_pins = pxa3xx_get_group_pins,
};
-static int pxa3xx_pmx_list_func(struct pinctrl_dev *pctrldev, unsigned func)
+static int pxa3xx_pmx_get_funcs_count(struct pinctrl_dev *pctrldev)
{
struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev);
- if (func >= info->num_funcs)
- return -EINVAL;
- return 0;
+
+ return info->num_funcs;
}
static const char *pxa3xx_pmx_get_func_name(struct pinctrl_dev *pctrldev,
@@ -170,7 +166,7 @@
}
static struct pinmux_ops pxa3xx_pmx_ops = {
- .list_functions = pxa3xx_pmx_list_func,
+ .get_functions_count = pxa3xx_pmx_get_funcs_count,
.get_function_name = pxa3xx_pmx_get_func_name,
.get_function_groups = pxa3xx_pmx_get_groups,
.enable = pxa3xx_pmx_enable,
diff --git a/drivers/pinctrl/pinctrl-sirf.c b/drivers/pinctrl/pinctrl-sirf.c
index 6b3534c..ba15b1a 100644
--- a/drivers/pinctrl/pinctrl-sirf.c
+++ b/drivers/pinctrl/pinctrl-sirf.c
@@ -853,18 +853,14 @@
SIRFSOC_PIN_GROUP("gpsgrp", gps_pins),
};
-static int sirfsoc_list_groups(struct pinctrl_dev *pctldev, unsigned selector)
+static int sirfsoc_get_groups_count(struct pinctrl_dev *pctldev)
{
- if (selector >= ARRAY_SIZE(sirfsoc_pin_groups))
- return -EINVAL;
- return 0;
+ return ARRAY_SIZE(sirfsoc_pin_groups);
}
static const char *sirfsoc_get_group_name(struct pinctrl_dev *pctldev,
unsigned selector)
{
- if (selector >= ARRAY_SIZE(sirfsoc_pin_groups))
- return NULL;
return sirfsoc_pin_groups[selector].name;
}
@@ -872,8 +868,6 @@
const unsigned **pins,
unsigned *num_pins)
{
- if (selector >= ARRAY_SIZE(sirfsoc_pin_groups))
- return -EINVAL;
*pins = sirfsoc_pin_groups[selector].pins;
*num_pins = sirfsoc_pin_groups[selector].num_pins;
return 0;
@@ -886,7 +880,7 @@
}
static struct pinctrl_ops sirfsoc_pctrl_ops = {
- .list_groups = sirfsoc_list_groups,
+ .get_groups_count = sirfsoc_get_groups_count,
.get_group_name = sirfsoc_get_group_name,
.get_group_pins = sirfsoc_get_group_pins,
.pin_dbg_show = sirfsoc_pin_dbg_show,
@@ -1033,11 +1027,9 @@
sirfsoc_pinmux_endisable(spmx, selector, false);
}
-static int sirfsoc_pinmux_list_funcs(struct pinctrl_dev *pmxdev, unsigned selector)
+static int sirfsoc_pinmux_get_funcs_count(struct pinctrl_dev *pmxdev)
{
- if (selector >= ARRAY_SIZE(sirfsoc_pmx_functions))
- return -EINVAL;
- return 0;
+ return ARRAY_SIZE(sirfsoc_pmx_functions);
}
static const char *sirfsoc_pinmux_get_func_name(struct pinctrl_dev *pctldev,
@@ -1074,9 +1066,9 @@
}
static struct pinmux_ops sirfsoc_pinmux_ops = {
- .list_functions = sirfsoc_pinmux_list_funcs,
.enable = sirfsoc_pinmux_enable,
.disable = sirfsoc_pinmux_disable,
+ .get_functions_count = sirfsoc_pinmux_get_funcs_count,
.get_function_name = sirfsoc_pinmux_get_func_name,
.get_function_groups = sirfsoc_pinmux_get_groups,
.gpio_request_enable = sirfsoc_pinmux_request_gpio,
diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c
index 9b32968..c4c47c5 100644
--- a/drivers/pinctrl/pinctrl-tegra.c
+++ b/drivers/pinctrl/pinctrl-tegra.c
@@ -53,15 +53,11 @@
writel(val, pmx->regs[bank] + reg);
}
-static int tegra_pinctrl_list_groups(struct pinctrl_dev *pctldev,
- unsigned group)
+static int tegra_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
{
struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- if (group >= pmx->soc->ngroups)
- return -EINVAL;
-
- return 0;
+ return pmx->soc->ngroups;
}
static const char *tegra_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
@@ -69,9 +65,6 @@
{
struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- if (group >= pmx->soc->ngroups)
- return NULL;
-
return pmx->soc->groups[group].name;
}
@@ -82,9 +75,6 @@
{
struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- if (group >= pmx->soc->ngroups)
- return -EINVAL;
-
*pins = pmx->soc->groups[group].pins;
*num_pins = pmx->soc->groups[group].npins;
@@ -99,21 +89,17 @@
}
static struct pinctrl_ops tegra_pinctrl_ops = {
- .list_groups = tegra_pinctrl_list_groups,
+ .get_groups_count = tegra_pinctrl_get_groups_count,
.get_group_name = tegra_pinctrl_get_group_name,
.get_group_pins = tegra_pinctrl_get_group_pins,
.pin_dbg_show = tegra_pinctrl_pin_dbg_show,
};
-static int tegra_pinctrl_list_funcs(struct pinctrl_dev *pctldev,
- unsigned function)
+static int tegra_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev)
{
struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- if (function >= pmx->soc->nfunctions)
- return -EINVAL;
-
- return 0;
+ return pmx->soc->nfunctions;
}
static const char *tegra_pinctrl_get_func_name(struct pinctrl_dev *pctldev,
@@ -121,9 +107,6 @@
{
struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- if (function >= pmx->soc->nfunctions)
- return NULL;
-
return pmx->soc->functions[function].name;
}
@@ -134,9 +117,6 @@
{
struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
- if (function >= pmx->soc->nfunctions)
- return -EINVAL;
-
*groups = pmx->soc->functions[function].groups;
*num_groups = pmx->soc->functions[function].ngroups;
@@ -151,8 +131,6 @@
int i;
u32 val;
- if (group >= pmx->soc->ngroups)
- return -EINVAL;
g = &pmx->soc->groups[group];
if (g->mux_reg < 0)
@@ -180,8 +158,6 @@
const struct tegra_pingroup *g;
u32 val;
- if (group >= pmx->soc->ngroups)
- return;
g = &pmx->soc->groups[group];
if (g->mux_reg < 0)
@@ -194,7 +170,7 @@
}
static struct pinmux_ops tegra_pinmux_ops = {
- .list_functions = tegra_pinctrl_list_funcs,
+ .get_functions_count = tegra_pinctrl_get_funcs_count,
.get_function_name = tegra_pinctrl_get_func_name,
.get_function_groups = tegra_pinctrl_get_func_groups,
.enable = tegra_pinctrl_enable,
@@ -324,8 +300,6 @@
s16 reg;
u32 val, mask;
- if (group >= pmx->soc->ngroups)
- return -EINVAL;
g = &pmx->soc->groups[group];
ret = tegra_pinconf_reg(pmx, g, param, &bank, ®, &bit, &width);
@@ -353,8 +327,6 @@
s16 reg;
u32 val, mask;
- if (group >= pmx->soc->ngroups)
- return -EINVAL;
g = &pmx->soc->groups[group];
ret = tegra_pinconf_reg(pmx, g, param, &bank, ®, &bit, &width);
@@ -525,7 +497,6 @@
{
struct tegra_pmx *pmx = platform_get_drvdata(pdev);
- pinctrl_remove_gpio_range(pmx->pctl, &tegra_pinctrl_gpio_range);
pinctrl_unregister(pmx->pctl);
return 0;
diff --git a/drivers/pinctrl/pinctrl-u300.c b/drivers/pinctrl/pinctrl-u300.c
index 26eb8cc..10de43c 100644
--- a/drivers/pinctrl/pinctrl-u300.c
+++ b/drivers/pinctrl/pinctrl-u300.c
@@ -836,18 +836,14 @@
},
};
-static int u300_list_groups(struct pinctrl_dev *pctldev, unsigned selector)
+static int u300_get_groups_count(struct pinctrl_dev *pctldev)
{
- if (selector >= ARRAY_SIZE(u300_pin_groups))
- return -EINVAL;
- return 0;
+ return ARRAY_SIZE(u300_pin_groups);
}
static const char *u300_get_group_name(struct pinctrl_dev *pctldev,
unsigned selector)
{
- if (selector >= ARRAY_SIZE(u300_pin_groups))
- return NULL;
return u300_pin_groups[selector].name;
}
@@ -855,8 +851,6 @@
const unsigned **pins,
unsigned *num_pins)
{
- if (selector >= ARRAY_SIZE(u300_pin_groups))
- return -EINVAL;
*pins = u300_pin_groups[selector].pins;
*num_pins = u300_pin_groups[selector].num_pins;
return 0;
@@ -869,7 +863,7 @@
}
static struct pinctrl_ops u300_pctrl_ops = {
- .list_groups = u300_list_groups,
+ .get_groups_count = u300_get_groups_count,
.get_group_name = u300_get_group_name,
.get_group_pins = u300_get_group_pins,
.pin_dbg_show = u300_pin_dbg_show,
@@ -991,11 +985,9 @@
u300_pmx_endisable(upmx, selector, false);
}
-static int u300_pmx_list_funcs(struct pinctrl_dev *pctldev, unsigned selector)
+static int u300_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
{
- if (selector >= ARRAY_SIZE(u300_pmx_functions))
- return -EINVAL;
- return 0;
+ return ARRAY_SIZE(u300_pmx_functions);
}
static const char *u300_pmx_get_func_name(struct pinctrl_dev *pctldev,
@@ -1014,7 +1006,7 @@
}
static struct pinmux_ops u300_pmx_ops = {
- .list_functions = u300_pmx_list_funcs,
+ .get_functions_count = u300_pmx_get_funcs_count,
.get_function_name = u300_pmx_get_func_name,
.get_function_groups = u300_pmx_get_groups,
.enable = u300_pmx_enable,
@@ -1185,8 +1177,6 @@
struct u300_pmx *upmx = platform_get_drvdata(pdev);
int i;
- for (i = 0; i < ARRAY_SIZE(u300_gpio_ranges); i++)
- pinctrl_remove_gpio_range(upmx->pctl, &u300_gpio_ranges[i]);
pinctrl_unregister(upmx->pctl);
iounmap(upmx->virtbase);
release_mem_region(upmx->phybase, upmx->physize);
diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c
index 4e62783..bd83c8b 100644
--- a/drivers/pinctrl/pinmux.c
+++ b/drivers/pinctrl/pinmux.c
@@ -33,22 +33,25 @@
int pinmux_check_ops(struct pinctrl_dev *pctldev)
{
const struct pinmux_ops *ops = pctldev->desc->pmxops;
+ unsigned nfuncs;
unsigned selector = 0;
/* Check that we implement required operations */
- if (!ops->list_functions ||
+ if (!ops ||
+ !ops->get_functions_count ||
!ops->get_function_name ||
!ops->get_function_groups ||
- !ops->enable ||
- !ops->disable)
+ !ops->enable) {
+ dev_err(pctldev->dev, "pinmux ops lacks necessary functions\n");
return -EINVAL;
-
+ }
/* Check that all functions registered have names */
- while (ops->list_functions(pctldev, selector) >= 0) {
+ nfuncs = ops->get_functions_count(pctldev);
+ while (selector < nfuncs) {
const char *fname = ops->get_function_name(pctldev,
selector);
if (!fname) {
- pr_err("pinmux ops has no name for function%u\n",
+ dev_err(pctldev->dev, "pinmux ops has no name for function%u\n",
selector);
return -EINVAL;
}
@@ -85,20 +88,23 @@
const struct pinmux_ops *ops = pctldev->desc->pmxops;
int status = -EINVAL;
- dev_dbg(pctldev->dev, "request pin %d for %s\n", pin, owner);
-
desc = pin_desc_get(pctldev, pin);
if (desc == NULL) {
dev_err(pctldev->dev,
- "pin is not registered so it cannot be requested\n");
+ "pin %d is not registered so it cannot be requested\n",
+ pin);
goto out;
}
+ dev_dbg(pctldev->dev, "request pin %d (%s) for %s\n",
+ pin, desc->name, owner);
+
if (gpio_range) {
/* There's no need to support multiple GPIO requests */
if (desc->gpio_owner) {
dev_err(pctldev->dev,
- "pin already requested\n");
+ "pin %s already requested by %s; cannot claim for %s\n",
+ desc->name, desc->gpio_owner, owner);
goto out;
}
@@ -106,7 +112,8 @@
} else {
if (desc->mux_usecount && strcmp(desc->mux_owner, owner)) {
dev_err(pctldev->dev,
- "pin already requested\n");
+ "pin %s already requested by %s; cannot claim for %s\n",
+ desc->name, desc->mux_owner, owner);
goto out;
}
@@ -139,8 +146,7 @@
status = 0;
if (status) {
- dev_err(pctldev->dev, "->request on device %s failed for pin %d\n",
- pctldev->desc->name, pin);
+ dev_err(pctldev->dev, "request() failed for pin %d\n", pin);
module_put(pctldev->owner);
}
@@ -157,7 +163,7 @@
out:
if (status)
dev_err(pctldev->dev, "pin-%d (%s) status %d\n",
- pin, owner, status);
+ pin, owner, status);
return status;
}
@@ -188,6 +194,11 @@
}
if (!gpio_range) {
+ /*
+ * A pin should not be freed more times than allocated.
+ */
+ if (WARN_ON(!desc->mux_usecount))
+ return NULL;
desc->mux_usecount--;
if (desc->mux_usecount)
return NULL;
@@ -226,14 +237,11 @@
struct pinctrl_gpio_range *range,
unsigned pin, unsigned gpio)
{
- char gpiostr[16];
const char *owner;
int ret;
/* Conjure some name stating what chip and pin this is taken by */
- snprintf(gpiostr, 15, "%s:%d", range->name, gpio);
-
- owner = kstrdup(gpiostr, GFP_KERNEL);
+ owner = kasprintf(GFP_KERNEL, "%s:%d", range->name, gpio);
if (!owner)
return -EINVAL;
@@ -287,10 +295,11 @@
const char *function)
{
const struct pinmux_ops *ops = pctldev->desc->pmxops;
+ unsigned nfuncs = ops->get_functions_count(pctldev);
unsigned selector = 0;
/* See if this pctldev has this function */
- while (ops->list_functions(pctldev, selector) >= 0) {
+ while (selector < nfuncs) {
const char *fname = ops->get_function_name(pctldev,
selector);
@@ -310,27 +319,38 @@
{
struct pinctrl_dev *pctldev = setting->pctldev;
const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
- const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
char const * const *groups;
unsigned num_groups;
int ret;
const char *group;
int i;
- const unsigned *pins;
- unsigned num_pins;
- setting->data.mux.func =
- pinmux_func_name_to_selector(pctldev, map->data.mux.function);
- if (setting->data.mux.func < 0)
- return setting->data.mux.func;
+ if (!pmxops) {
+ dev_err(pctldev->dev, "does not support mux function\n");
+ return -EINVAL;
+ }
+
+ ret = pinmux_func_name_to_selector(pctldev, map->data.mux.function);
+ if (ret < 0) {
+ dev_err(pctldev->dev, "invalid function %s in map table\n",
+ map->data.mux.function);
+ return ret;
+ }
+ setting->data.mux.func = ret;
ret = pmxops->get_function_groups(pctldev, setting->data.mux.func,
&groups, &num_groups);
- if (ret < 0)
+ if (ret < 0) {
+ dev_err(pctldev->dev, "can't query groups for function %s\n",
+ map->data.mux.function);
return ret;
- if (!num_groups)
+ }
+ if (!num_groups) {
+ dev_err(pctldev->dev,
+ "function %s can't be selected on any group\n",
+ map->data.mux.function);
return -EINVAL;
-
+ }
if (map->data.mux.group) {
bool found = false;
group = map->data.mux.group;
@@ -340,63 +360,30 @@
break;
}
}
- if (!found)
+ if (!found) {
+ dev_err(pctldev->dev,
+ "invalid group \"%s\" for function \"%s\"\n",
+ group, map->data.mux.function);
return -EINVAL;
+ }
} else {
group = groups[0];
}
- setting->data.mux.group = pinctrl_get_group_selector(pctldev, group);
- if (setting->data.mux.group < 0)
- return setting->data.mux.group;
-
- ret = pctlops->get_group_pins(pctldev, setting->data.mux.group, &pins,
- &num_pins);
- if (ret) {
- dev_err(pctldev->dev,
- "could not get pins for device %s group selector %d\n",
- pinctrl_dev_get_name(pctldev), setting->data.mux.group);
- return -ENODEV;
+ ret = pinctrl_get_group_selector(pctldev, group);
+ if (ret < 0) {
+ dev_err(pctldev->dev, "invalid group %s in map table\n",
+ map->data.mux.group);
+ return ret;
}
-
- /* Try to allocate all pins in this group, one by one */
- for (i = 0; i < num_pins; i++) {
- ret = pin_request(pctldev, pins[i], map->dev_name, NULL);
- if (ret) {
- dev_err(pctldev->dev,
- "could not get request pin %d on device %s\n",
- pins[i], pinctrl_dev_get_name(pctldev));
- /* On error release all taken pins */
- i--; /* this pin just failed */
- for (; i >= 0; i--)
- pin_free(pctldev, pins[i], NULL);
- return -ENODEV;
- }
- }
+ setting->data.mux.group = ret;
return 0;
}
void pinmux_free_setting(struct pinctrl_setting const *setting)
{
- struct pinctrl_dev *pctldev = setting->pctldev;
- const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
- const unsigned *pins;
- unsigned num_pins;
- int ret;
- int i;
-
- ret = pctlops->get_group_pins(pctldev, setting->data.mux.group,
- &pins, &num_pins);
- if (ret) {
- dev_err(pctldev->dev,
- "could not get pins for device %s group selector %d\n",
- pinctrl_dev_get_name(pctldev), setting->data.mux.group);
- return;
- }
-
- for (i = 0; i < num_pins; i++)
- pin_free(pctldev, pins[i], NULL);
+ /* This function is currently unused */
}
int pinmux_enable_setting(struct pinctrl_setting const *setting)
@@ -420,6 +407,18 @@
num_pins = 0;
}
+ /* Try to allocate all pins in this group, one by one */
+ for (i = 0; i < num_pins; i++) {
+ ret = pin_request(pctldev, pins[i], setting->dev_name, NULL);
+ if (ret) {
+ dev_err(pctldev->dev,
+ "could not request pin %d on device %s\n",
+ pins[i], pinctrl_dev_get_name(pctldev));
+ goto err_pin_request;
+ }
+ }
+
+ /* Now that we have acquired the pins, encode the mux setting */
for (i = 0; i < num_pins; i++) {
desc = pin_desc_get(pctldev, pins[i]);
if (desc == NULL) {
@@ -431,8 +430,26 @@
desc->mux_setting = &(setting->data.mux);
}
- return ops->enable(pctldev, setting->data.mux.func,
- setting->data.mux.group);
+ ret = ops->enable(pctldev, setting->data.mux.func,
+ setting->data.mux.group);
+
+ if (ret)
+ goto err_enable;
+
+ return 0;
+
+err_enable:
+ for (i = 0; i < num_pins; i++) {
+ desc = pin_desc_get(pctldev, pins[i]);
+ if (desc)
+ desc->mux_setting = NULL;
+ }
+err_pin_request:
+ /* On error release all taken pins */
+ while (--i >= 0)
+ pin_free(pctldev, pins[i], NULL);
+
+ return ret;
}
void pinmux_disable_setting(struct pinctrl_setting const *setting)
@@ -456,6 +473,7 @@
num_pins = 0;
}
+ /* Flag the descs that no setting is active */
for (i = 0; i < num_pins; i++) {
desc = pin_desc_get(pctldev, pins[i]);
if (desc == NULL) {
@@ -467,7 +485,12 @@
desc->mux_setting = NULL;
}
- ops->disable(pctldev, setting->data.mux.func, setting->data.mux.group);
+ /* And release the pins */
+ for (i = 0; i < num_pins; i++)
+ pin_free(pctldev, pins[i], NULL);
+
+ if (ops->disable)
+ ops->disable(pctldev, setting->data.mux.func, setting->data.mux.group);
}
#ifdef CONFIG_DEBUG_FS
@@ -477,11 +500,15 @@
{
struct pinctrl_dev *pctldev = s->private;
const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
+ unsigned nfuncs;
unsigned func_selector = 0;
- mutex_lock(&pinctrl_mutex);
+ if (!pmxops)
+ return 0;
- while (pmxops->list_functions(pctldev, func_selector) >= 0) {
+ mutex_lock(&pinctrl_mutex);
+ nfuncs = pmxops->get_functions_count(pctldev);
+ while (func_selector < nfuncs) {
const char *func = pmxops->get_function_name(pctldev,
func_selector);
const char * const *groups;
@@ -515,6 +542,9 @@
const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
unsigned i, pin;
+ if (!pmxops)
+ return 0;
+
seq_puts(s, "Pinmux settings per pin\n");
seq_puts(s, "Format: pin (name): mux_owner gpio_owner hog?\n");
diff --git a/drivers/pinctrl/pinmux.h b/drivers/pinctrl/pinmux.h
index 6fc4700..d1a98b1 100644
--- a/drivers/pinctrl/pinmux.h
+++ b/drivers/pinctrl/pinmux.h
@@ -31,12 +31,6 @@
int pinmux_enable_setting(struct pinctrl_setting const *setting);
void pinmux_disable_setting(struct pinctrl_setting const *setting);
-void pinmux_show_map(struct seq_file *s, struct pinctrl_map const *map);
-void pinmux_show_setting(struct seq_file *s,
- struct pinctrl_setting const *setting);
-void pinmux_init_device_debugfs(struct dentry *devroot,
- struct pinctrl_dev *pctldev);
-
#else
static inline int pinmux_check_ops(struct pinctrl_dev *pctldev)
@@ -89,6 +83,18 @@
{
}
+#endif
+
+#if defined(CONFIG_PINMUX) && defined(CONFIG_DEBUG_FS)
+
+void pinmux_show_map(struct seq_file *s, struct pinctrl_map const *map);
+void pinmux_show_setting(struct seq_file *s,
+ struct pinctrl_setting const *setting);
+void pinmux_init_device_debugfs(struct dentry *devroot,
+ struct pinctrl_dev *pctldev);
+
+#else
+
static inline void pinmux_show_map(struct seq_file *s,
struct pinctrl_map const *map)
{
diff --git a/drivers/platform/msm/ipa/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_debugfs.c
index aaf5cc0..87ddf59 100644
--- a/drivers/platform/msm/ipa/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_debugfs.c
@@ -346,11 +346,18 @@
uint32_t mask[4];
int i;
- if (attrib->attrib_mask & IPA_FLT_TOS) {
- nbytes = scnprintf(buff + cnt, sz - cnt, "tos:%d ",
- attrib->u.v4.tos);
+
+ if (attrib->attrib_mask & IPA_FLT_TOS_MASKED) {
+ nbytes = scnprintf(buff + cnt, sz - cnt, "tos_value:%d ",
+ attrib->tos_value);
cnt += nbytes;
}
+ if (attrib->attrib_mask & IPA_FLT_TOS_MASKED) {
+ nbytes = scnprintf(buff + cnt, sz - cnt, "tos_mask:%d ",
+ attrib->tos_mask);
+ cnt += nbytes;
+ }
+
if (attrib->attrib_mask & IPA_FLT_PROTOCOL) {
nbytes = scnprintf(buff + cnt, sz - cnt, "protocol:%d ",
attrib->u.v4.protocol);
diff --git a/drivers/platform/msm/ipa/ipa_rm.c b/drivers/platform/msm/ipa/ipa_rm.c
index e057c5a..64c77a0 100644
--- a/drivers/platform/msm/ipa/ipa_rm.c
+++ b/drivers/platform/msm/ipa/ipa_rm.c
@@ -119,12 +119,12 @@
{
int result;
- read_lock(&ipa_rm_ctx->lock);
+ write_lock(&ipa_rm_ctx->lock);
result = ipa_rm_dep_graph_add_dependency(
ipa_rm_ctx->dep_graph,
resource_name,
depends_on_name);
- read_unlock(&ipa_rm_ctx->lock);
+ write_unlock(&ipa_rm_ctx->lock);
return result;
}
EXPORT_SYMBOL(ipa_rm_add_dependency);
@@ -145,12 +145,12 @@
enum ipa_rm_resource_name depends_on_name)
{
int result;
- read_lock(&ipa_rm_ctx->lock);
+ write_lock(&ipa_rm_ctx->lock);
result = ipa_rm_dep_graph_delete_dependency(
ipa_rm_ctx->dep_graph,
resource_name,
depends_on_name);
- read_unlock(&ipa_rm_ctx->lock);
+ write_unlock(&ipa_rm_ctx->lock);
return result;
}
EXPORT_SYMBOL(ipa_rm_delete_dependency);
diff --git a/drivers/platform/msm/ipa/ipa_rm_resource.c b/drivers/platform/msm/ipa/ipa_rm_resource.c
index 7142dc7..dba4430 100644
--- a/drivers/platform/msm/ipa/ipa_rm_resource.c
+++ b/drivers/platform/msm/ipa/ipa_rm_resource.c
@@ -720,6 +720,7 @@
producer->resource.name,
IPA_RM_RESOURCE_GRANTED,
true);
+ result = 0;
}
unlock_and_bail:
spin_unlock_irqrestore(&producer->resource.state_lock, flags);
diff --git a/drivers/platform/msm/ipa/ipa_rt.c b/drivers/platform/msm/ipa/ipa_rt.c
index 6430c07..5c27c40 100644
--- a/drivers/platform/msm/ipa/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_rt.c
@@ -67,13 +67,31 @@
}
rule_hdr->u.hdr.pipe_dest_idx = pipe_idx;
rule_hdr->u.hdr.system = !ipa_ctx->hdr_tbl_lcl;
- if (entry->hdr)
+ if (entry->hdr) {
rule_hdr->u.hdr.hdr_offset =
entry->hdr->offset_entry->offset >> 2;
- else
+ } else {
rule_hdr->u.hdr.hdr_offset = 0;
-
+ }
buf += sizeof(struct ipa_rt_rule_hw_hdr);
+ if ((ip == IPA_IP_v4) &&
+ (entry->rule.attrib.attrib_mask & IPA_FLT_TOS)) {
+ entry->rule.attrib.tos_value =
+ (entry->rule.attrib.u.v4.tos << 5);
+ entry->rule.attrib.tos_mask = 0xe0;
+ entry->rule.attrib.attrib_mask &= ~IPA_FLT_TOS;
+ entry->rule.attrib.attrib_mask |= IPA_FLT_TOS_MASKED;
+ }
+
+ if ((ip == IPA_IP_v6) &&
+ (entry->rule.attrib.attrib_mask & IPA_FLT_TC)) {
+ entry->rule.attrib.tos_value =
+ (entry->rule.attrib.u.v6.tc << 5);
+ entry->rule.attrib.tos_mask = 0xe0;
+ entry->rule.attrib.attrib_mask &= ~IPA_FLT_TC;
+ entry->rule.attrib.attrib_mask |= IPA_FLT_TOS_MASKED;
+ }
+
if (ipa_generate_hw_rule(ip, &rule->attrib, &buf, &en_rule)) {
IPAERR("fail to generate hw rule\n");
return -EPERM;
diff --git a/drivers/platform/msm/ipa/ipa_utils.c b/drivers/platform/msm/ipa/ipa_utils.c
index 23de300..21a6dc4 100644
--- a/drivers/platform/msm/ipa/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_utils.c
@@ -237,6 +237,20 @@
*buf = ipa_pad_to_32(*buf);
}
+ if (attrib->attrib_mask & IPA_FLT_TOS_MASKED) {
+ if (ipa_ofst_meq32[ofst_meq32] == -1) {
+ IPAERR("ran out of meq32 eq\n");
+ return -EPERM;
+ }
+ *en_rule |= ipa_ofst_meq32[ofst_meq32];
+ /* 0 => offset of TOS in v4 header */
+ *buf = ipa_write_8(0, *buf);
+ *buf = ipa_write_32((attrib->tos_mask << 16), *buf);
+ *buf = ipa_write_32(attrib->tos_value, *buf);
+ *buf = ipa_pad_to_32(*buf);
+ ofst_meq32++;
+ }
+
if (attrib->attrib_mask & IPA_FLT_PROTOCOL) {
*en_rule |= IPA_PROTOCOL_EQ;
*buf = ipa_write_8(attrib->u.v4.protocol, *buf);
@@ -568,6 +582,20 @@
*buf = ipa_pad_to_32(*buf);
}
+ if (attrib->attrib_mask & IPA_FLT_TOS_MASKED) {
+ if (ipa_ofst_meq32[ofst_meq32] == -1) {
+ IPAERR("ran out of meq32 eq\n");
+ return -EPERM;
+ }
+ *en_rule |= ipa_ofst_meq32[ofst_meq32];
+ /* 0 => offset of TOS in v4 header */
+ *buf = ipa_write_8(0, *buf);
+ *buf = ipa_write_32((attrib->tos_mask << 20), *buf);
+ *buf = ipa_write_32(attrib->tos_value, *buf);
+ *buf = ipa_pad_to_32(*buf);
+ ofst_meq32++;
+ }
+
if (attrib->attrib_mask & IPA_FLT_FLOW_LABEL) {
*en_rule |= IPA_FLT_FLOW_LABEL;
/* FIXME FL is only 20 bits */
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index b390280..fae09fc 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -153,7 +153,6 @@
bool prod_stopped;
struct completion prod_avail[MAX_BAMS];
- struct completion cons_avail[MAX_BAMS];
struct completion cons_released[MAX_BAMS];
struct completion prod_released[MAX_BAMS];
@@ -878,7 +877,6 @@
spin_lock(&usb_bam_ipa_handshake_info_lock);
info.cur_cons_state[cur_bam] = IPA_RM_RESOURCE_GRANTED;
- complete_all(&info.cons_avail[cur_bam]);
spin_lock(&usb_bam_lock);
@@ -1024,7 +1022,7 @@
}
}
-static void wait_for_prod_granted(enum usb_bam cur_bam, bool start_cons)
+static void wait_for_prod_granted(enum usb_bam cur_bam)
{
int ret;
@@ -1038,8 +1036,6 @@
__func__);
init_completion(&info.prod_avail[cur_bam]);
- if (start_cons)
- init_completion(&info.cons_avail[cur_bam]);
ret = ipa_rm_request_resource(ipa_rm_resource_prod[cur_bam]);
if (!ret) {
@@ -1056,22 +1052,14 @@
pr_err("%s: ipa_rm_request_resource ret =%d\n", __func__, ret);
}
-void wait_for_cons_granted(enum usb_bam cur_bam)
+void notify_usb_connected(enum usb_bam cur_bam)
{
- pr_debug("%s: Waiting for CONS\n", __func__);
- if (info.cur_cons_state[cur_bam] != IPA_RM_RESOURCE_GRANTED) {
- if (!wait_for_completion_timeout(&info.cons_avail[cur_bam],
- USB_BAM_TIMEOUT))
- pr_err("%s: Timeout wainting for CONS_REQUEST\n",
- __func__);
- pr_err("%s: Finished waiting for CONS\n", __func__);
- }
+ pr_debug("%s: enter\n", __func__);
spin_lock(&usb_bam_ipa_handshake_info_lock);
if (cur_bam == HSUSB_BAM)
info.connect_complete = 1;
spin_unlock(&usb_bam_ipa_handshake_info_lock);
- pr_debug("%s: CONS is granted\n", __func__);
if (info.cur_cons_state[HSUSB_BAM] == IPA_RM_RESOURCE_GRANTED) {
pr_debug("%s: Notify CONS_GRANTED\n", __func__);
@@ -1080,20 +1068,6 @@
}
}
-void usb_bam_wait_for_cons_granted(
- struct usb_bam_connect_ipa_params *ipa_params)
-{
- struct usb_bam_pipe_connect *pipe_connect;
- enum usb_bam cur_bam;
- u8 src_idx;
-
- src_idx = ipa_params->src_idx;
- pipe_connect = &usb_bam_connections[src_idx];
- cur_bam = pipe_connect->bam_type;
-
- wait_for_cons_granted(cur_bam);
-}
-
static void wait_for_prod_release(enum usb_bam cur_bam)
{
int ret;
@@ -1270,8 +1244,8 @@
info.lpm_wait_handshake[HSUSB_BAM] = true;
spin_unlock(&usb_bam_ipa_handshake_info_lock);
- wait_for_prod_granted(HSUSB_BAM, true);
- wait_for_cons_granted(HSUSB_BAM);
+ wait_for_prod_granted(HSUSB_BAM);
+ notify_usb_connected(HSUSB_BAM);
if (info.cons_stopped) {
ipa_resume_pipes();
if (info.start) {
@@ -1339,7 +1313,7 @@
usb_bam_resume_hsic_host();
/* Ensure getting the producer resource */
- wait_for_prod_granted(HSIC_BAM, false);
+ wait_for_prod_granted(HSIC_BAM);
}
void msm_bam_hsic_notify_on_resume(void)
@@ -1500,7 +1474,7 @@
if (ipa_params->dir == USB_TO_PEER_PERIPHERAL) {
pr_debug("%s: Starting connect sequence\n", __func__);
- wait_for_prod_granted(cur_bam, true);
+ wait_for_prod_granted(cur_bam);
}
ret = connect_pipe_ipa(idx, ipa_params);
@@ -1524,7 +1498,7 @@
ctx.pipes_enabled_per_bam[cur_bam] += 1;
spin_unlock(&usb_bam_lock);
if (ipa_params->dir == PEER_PERIPHERAL_TO_USB && cur_bam == HSUSB_BAM)
- wait_for_cons_granted(cur_bam);
+ notify_usb_connected(cur_bam);
if (cur_bam == HSUSB_BAM)
mutex_unlock(&info.suspend_resume_mutex);
@@ -1586,7 +1560,7 @@
if (pipe_connect->peer_bam == IPA_P_BAM &&
pipe_connect->bam_type == HSIC_BAM &&
info.cur_prod_state[HSIC_BAM] != IPA_RM_RESOURCE_GRANTED) {
- wait_for_prod_granted(HSIC_BAM, false);
+ wait_for_prod_granted(HSIC_BAM);
}
/*
@@ -1618,7 +1592,7 @@
if (pipe_connect->bam_type == HSUSB_BAM) {
/* A2 wakeup not from LPM (CONS was up) */
- wait_for_prod_granted(pipe_connect->bam_type, true);
+ wait_for_prod_granted(pipe_connect->bam_type);
if (info.start) {
pr_debug("%s: Enqueue PROD transfer", __func__);
info.start(info.start_stop_param,
@@ -2568,8 +2542,6 @@
ctx.is_bam_inactivity[i] = false;
init_completion(&info.prod_avail[i]);
complete(&info.prod_avail[i]);
- init_completion(&info.cons_avail[i]);
- complete(&info.cons_avail[i]);
init_completion(&info.cons_released[i]);
complete(&info.cons_released[i]);
init_completion(&info.prod_released[i]);
diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c
index 14cba58..12aef1c 100644
--- a/drivers/power/qpnp-bms.c
+++ b/drivers/power/qpnp-bms.c
@@ -45,8 +45,10 @@
#define BMS1_OCV_USE_LIMIT_CTL 0x4C
/* Delay control */
#define BMS1_S1_DELAY_CTL 0x5A
-/* CC interrupt threshold */
-#define BMS1_CC_THR0 0x7A
+/* OCV interrupt threshold */
+#define BMS1_OCV_THR0 0x50
+/* SW CC interrupt threshold */
+#define BMS1_SW_CC_THR0 0xA0
/* OCV for r registers */
#define BMS1_OCV_FOR_R_DATA0 0x80
#define BMS1_VSENSE_FOR_R_DATA0 0x82
@@ -120,6 +122,16 @@
int chargecycles;
};
+struct bms_irq {
+ unsigned int irq;
+ unsigned long disabled;
+};
+
+struct bms_wakeup_source {
+ struct wakeup_source source;
+ unsigned long disabled;
+};
+
struct qpnp_bms_chip {
struct device *dev;
struct power_supply bms_psy;
@@ -176,7 +188,7 @@
int low_soc_calc_threshold;
int low_soc_calculate_soc_ms;
int calculate_soc_ms;
- struct wake_lock soc_wake_lock;
+ struct bms_wakeup_source soc_wake_source;
struct wake_lock cv_wake_lock;
uint16_t ocv_reading_at_100;
@@ -248,6 +260,8 @@
u8 charge_increase;
int fcc_new_sysfs;
int fcc_update_complete;
+ struct bms_irq sw_cc_thr_irq;
+ struct bms_irq ocv_thr_irq;
};
static struct of_device_id qpnp_bms_match_table[] = {
@@ -371,6 +385,38 @@
return qpnp_masked_write_base(chip, chip->base + addr, mask, val);
}
+static void bms_stay_awake(struct bms_wakeup_source *source)
+{
+ if (__test_and_clear_bit(0, &source->disabled)) {
+ __pm_stay_awake(&source->source);
+ pr_debug("enabled source %s\n", source->source.name);
+ }
+}
+
+static void bms_relax(struct bms_wakeup_source *source)
+{
+ if (!__test_and_set_bit(0, &source->disabled)) {
+ __pm_relax(&source->source);
+ pr_debug("disabled source %s\n", source->source.name);
+ }
+}
+
+static void enable_bms_irq(struct bms_irq *irq)
+{
+ if (__test_and_clear_bit(0, &irq->disabled)) {
+ enable_irq(irq->irq);
+ pr_debug("enabled irq %d\n", irq->irq);
+ }
+}
+
+static void disable_bms_irq(struct bms_irq *irq)
+{
+ if (!__test_and_set_bit(0, &irq->disabled)) {
+ disable_irq(irq->irq);
+ pr_debug("disabled irq %d\n", irq->irq);
+ }
+}
+
#define HOLD_OREG_DATA BIT(0)
static int lock_output_data(struct qpnp_bms_chip *chip)
{
@@ -412,7 +458,6 @@
#define VADC_CALIB_UV 625000
#define VBATT_MUL_FACTOR 3
-
static int adjust_vbatt_reading(struct qpnp_bms_chip *chip, int reading_uv)
{
s64 numerator, denominator;
@@ -490,6 +535,30 @@
return result_uv;
}
+static s64 cc_reverse_adjust_for_gain(s64 uv)
+{
+ struct qpnp_iadc_calib calibration;
+ int gain;
+ s64 result_uv;
+
+ qpnp_iadc_get_gain_and_offset(&calibration);
+ gain = (int)calibration.gain_raw - (int)calibration.offset_raw;
+
+ pr_debug("reverse adjusting_uv = %lld\n", uv);
+ if (gain == 0) {
+ pr_debug("gain is %d, not adjusting\n", gain);
+ return uv;
+ }
+ pr_debug("adjusting by factor: %hu/%lld = %lld%%\n",
+ gain, QPNP_ADC_GAIN_IDEAL,
+ div64_s64((s64)gain * 100LL,
+ (s64)QPNP_ADC_GAIN_IDEAL));
+
+ result_uv = div64_s64(uv * (s64)gain, QPNP_ADC_GAIN_IDEAL);
+ pr_debug("result_uv = %lld\n", result_uv);
+ return result_uv;
+}
+
static int convert_vsense_to_uv(struct qpnp_bms_chip *chip,
int16_t reading)
{
@@ -565,6 +634,20 @@
}
#define CC_36_BIT_MASK 0xFFFFFFFFFLL
+static uint64_t convert_s64_to_s36(int64_t raw64)
+{
+ return (uint64_t) raw64 & CC_36_BIT_MASK;
+}
+
+#define SIGN_EXTEND_36_TO_64_MASK (-1LL ^ CC_36_BIT_MASK)
+static int64_t convert_s36_to_s64(uint64_t raw36)
+{
+ raw36 = raw36 & CC_36_BIT_MASK;
+ /* convert 36 bit signed value into 64 signed value */
+ return (raw36 >> 35) == 0LL ?
+ raw36 : (SIGN_EXTEND_36_TO_64_MASK | raw36);
+}
+
static int read_cc_raw(struct qpnp_bms_chip *chip, int64_t *reading,
int cc_type)
{
@@ -582,12 +665,7 @@
return -ENXIO;
}
- raw_reading = raw_reading & CC_36_BIT_MASK;
- /* convert 36 bit signed value into 64 signed value */
- *reading = (raw_reading >> 35) == 0LL ?
- raw_reading : ((-1LL ^ CC_36_BIT_MASK) | raw_reading);
- pr_debug("before conversion: %llx, after conversion: %llx\n",
- raw_reading, *reading);
+ *reading = convert_s36_to_s64(raw_reading);
return 0;
}
@@ -1282,6 +1360,59 @@
return rc;
}
+/* Returns estimated battery resistance */
+static int get_prop_bms_batt_resistance(struct qpnp_bms_chip *chip)
+{
+ return chip->rbatt_mohm * 1000;
+}
+
+/* Returns instantaneous current in uA */
+static int get_prop_bms_current_now(struct qpnp_bms_chip *chip)
+{
+ int rc, result_ua;
+
+ rc = get_battery_current(chip, &result_ua);
+ if (rc) {
+ pr_err("failed to get current: %d\n", rc);
+ return rc;
+ }
+ return result_ua;
+}
+
+/* Returns coulomb counter in uAh */
+static int get_prop_bms_charge_counter(struct qpnp_bms_chip *chip)
+{
+ int64_t cc_raw;
+
+ mutex_lock(&chip->bms_output_lock);
+ lock_output_data(chip);
+ read_cc_raw(chip, &cc_raw, false);
+ unlock_output_data(chip);
+ mutex_unlock(&chip->bms_output_lock);
+
+ return calculate_cc(chip, cc_raw, CC, NORESET);
+}
+
+/* Returns shadow coulomb counter in uAh */
+static int get_prop_bms_charge_counter_shadow(struct qpnp_bms_chip *chip)
+{
+ int64_t cc_raw;
+
+ mutex_lock(&chip->bms_output_lock);
+ lock_output_data(chip);
+ read_cc_raw(chip, &cc_raw, true);
+ unlock_output_data(chip);
+ mutex_unlock(&chip->bms_output_lock);
+
+ return calculate_cc(chip, cc_raw, SHDW_CC, NORESET);
+}
+
+/* Returns full charge design in uAh */
+static int get_prop_bms_charge_full_design(struct qpnp_bms_chip *chip)
+{
+ return chip->fcc_mah * 1000;
+}
+
static int calculate_delta_time(unsigned long *time_stamp, int *delta_time_s)
{
unsigned long now_tm_sec = 0;
@@ -1920,6 +2051,92 @@
}
}
+static int64_t convert_cc_uah_to_raw(struct qpnp_bms_chip *chip, int64_t cc_uah)
+{
+ int64_t cc_uv, cc_pvh, cc_raw;
+
+ cc_pvh = cc_uah * chip->r_sense_uohm;
+ cc_uv = div_s64(cc_pvh * SLEEP_CLK_HZ * SECONDS_PER_HOUR,
+ CC_READING_TICKS * 1000000LL);
+ cc_raw = div_s64(cc_uv * CC_READING_RESOLUTION_D,
+ CC_READING_RESOLUTION_N);
+ return cc_raw;
+}
+
+#define CC_STEP_INCREMENT_UAH 1500
+#define OCV_STEP_INCREMENT 0x10
+static void configure_soc_wakeup(struct qpnp_bms_chip *chip,
+ struct soc_params *params,
+ int batt_temp, int target_soc)
+{
+ int target_ocv_uv;
+ int64_t target_cc_uah, cc_raw_64, current_shdw_cc_raw_64;
+ int64_t current_shdw_cc_uah, iadc_comp_factor;
+ uint64_t cc_raw, current_shdw_cc_raw;
+ int16_t ocv_raw, current_ocv_raw;
+
+ current_shdw_cc_raw = 0;
+ mutex_lock(&chip->bms_output_lock);
+ lock_output_data(chip);
+ qpnp_read_wrapper(chip, (u8 *)¤t_ocv_raw,
+ chip->base + BMS1_OCV_FOR_SOC_DATA0, 2);
+ unlock_output_data(chip);
+ mutex_unlock(&chip->bms_output_lock);
+ current_shdw_cc_uah = get_prop_bms_charge_counter_shadow(chip);
+ current_shdw_cc_raw_64 = convert_cc_uah_to_raw(chip,
+ current_shdw_cc_uah);
+
+ /*
+ * Calculate the target shadow coulomb counter threshold for when
+ * the SoC changes.
+ *
+ * Since the BMS driver resets the shadow coulomb counter every
+ * 20 seconds when the device is awake, calculate the threshold as
+ * a delta from the current shadow coulomb count.
+ */
+ target_cc_uah = (100 - target_soc)
+ * (params->fcc_uah - params->uuc_uah)
+ / 100 - current_shdw_cc_uah;
+ if (target_cc_uah < 0) {
+ /*
+ * If the target cc is below 0, that means we have already
+ * passed the point where SoC should have fallen.
+ * Set a wakeup in a few more mAh and check back again
+ */
+ target_cc_uah = CC_STEP_INCREMENT_UAH;
+ }
+ iadc_comp_factor = 100000;
+ qpnp_iadc_comp_result(&iadc_comp_factor);
+ target_cc_uah = div64_s64(target_cc_uah * 100000, iadc_comp_factor);
+ target_cc_uah = cc_reverse_adjust_for_gain(target_cc_uah);
+ cc_raw_64 = convert_cc_uah_to_raw(chip, target_cc_uah);
+ cc_raw = convert_s64_to_s36(cc_raw_64);
+
+ find_ocv_for_soc(chip, params, batt_temp, target_soc, &target_ocv_uv);
+ ocv_raw = convert_vbatt_uv_to_raw(chip, target_ocv_uv);
+
+ /*
+ * If the current_ocv_raw was updated since reaching 100% and is lower
+ * than the calculated target ocv threshold, set the new target
+ * threshold 1.5mAh lower in order to check if the SoC changed yet.
+ */
+ if (current_ocv_raw != chip->ocv_reading_at_100
+ && current_ocv_raw < ocv_raw)
+ ocv_raw = current_ocv_raw - OCV_STEP_INCREMENT;
+
+ qpnp_write_wrapper(chip, (u8 *)&cc_raw,
+ chip->base + BMS1_SW_CC_THR0, 5);
+ qpnp_write_wrapper(chip, (u8 *)&ocv_raw,
+ chip->base + BMS1_OCV_THR0, 2);
+
+ pr_debug("current sw_cc_raw = 0x%llx, current ocv = 0x%hx\n",
+ current_shdw_cc_raw, (uint16_t)current_ocv_raw);
+ pr_debug("target_cc_uah = %lld, raw64 = 0x%llx, raw 36 = 0x%llx, ocv_raw = 0x%hx\n",
+ target_cc_uah,
+ (uint64_t)cc_raw_64, cc_raw,
+ (uint16_t)ocv_raw);
+}
+
#define SLEEP_RECALC_INTERVAL 3
static int calculate_state_of_charge(struct qpnp_bms_chip *chip,
struct raw_soc_params *raw,
@@ -2022,7 +2239,13 @@
/* always clamp soc due to BMS hw/sw immaturities */
new_calculated_soc = clamp_soc_based_on_voltage(chip,
new_calculated_soc);
-
+ /*
+ * If the battery is full, configure the cc threshold so the system
+ * wakes up after SoC changes
+ */
+ if (is_battery_full(chip))
+ configure_soc_wakeup(chip, ¶ms,
+ batt_temp, bound_soc(new_calculated_soc - 1));
done_calculating:
mutex_lock(&chip->last_soc_mutex);
previous_soc = chip->calculated_soc;
@@ -2101,8 +2324,7 @@
struct qpnp_vadc_result result;
struct raw_soc_params raw;
- if (!wake_lock_active(&chip->soc_wake_lock))
- wake_lock(&chip->soc_wake_lock);
+ bms_stay_awake(&chip->soc_wake_source);
mutex_lock(&chip->vbat_monitor_mutex);
qpnp_adc_tm_channel_measure(&chip->vbat_monitor_params);
mutex_unlock(&chip->vbat_monitor_mutex);
@@ -2127,7 +2349,7 @@
mutex_unlock(&chip->last_ocv_uv_mutex);
}
}
- wake_unlock(&chip->soc_wake_lock);
+ bms_relax(&chip->soc_wake_source);
return soc;
}
@@ -2681,8 +2903,20 @@
pr_debug("charging ended\n");
charging_ended(chip);
}
+
+ if (status == POWER_SUPPLY_STATUS_FULL) {
+ pr_debug("battery full\n");
+ enable_bms_irq(&chip->ocv_thr_irq);
+ enable_bms_irq(&chip->sw_cc_thr_irq);
+ } else if (chip->battery_status
+ == POWER_SUPPLY_STATUS_FULL) {
+ pr_debug("battery not full any more\n");
+ disable_bms_irq(&chip->ocv_thr_irq);
+ disable_bms_irq(&chip->sw_cc_thr_irq);
+ }
+
chip->battery_status = status;
- /* a new battery was inserted or removed, so force a soc
+ /* battery charge status has changed, so force a soc
* recalculation to update the SoC */
schedule_work(&chip->recalc_work);
}
@@ -2716,59 +2950,6 @@
return report_state_of_charge(chip);
}
-/* Returns estimated battery resistance */
-static int get_prop_bms_batt_resistance(struct qpnp_bms_chip *chip)
-{
- return chip->rbatt_mohm * 1000;
-}
-
-/* Returns instantaneous current in uA */
-static int get_prop_bms_current_now(struct qpnp_bms_chip *chip)
-{
- int rc, result_ua;
-
- rc = get_battery_current(chip, &result_ua);
- if (rc) {
- pr_err("failed to get current: %d\n", rc);
- return rc;
- }
- return result_ua;
-}
-
-/* Returns coulomb counter in uAh */
-static int get_prop_bms_charge_counter(struct qpnp_bms_chip *chip)
-{
- int64_t cc_raw;
-
- mutex_lock(&chip->bms_output_lock);
- lock_output_data(chip);
- read_cc_raw(chip, &cc_raw, CC);
- unlock_output_data(chip);
- mutex_unlock(&chip->bms_output_lock);
-
- return calculate_cc(chip, cc_raw, CC, NORESET);
-}
-
-/* Returns shadow coulomb counter in uAh */
-static int get_prop_bms_charge_counter_shadow(struct qpnp_bms_chip *chip)
-{
- int64_t cc_raw;
-
- mutex_lock(&chip->bms_output_lock);
- lock_output_data(chip);
- read_cc_raw(chip, &cc_raw, SHDW_CC);
- unlock_output_data(chip);
- mutex_unlock(&chip->bms_output_lock);
-
- return calculate_cc(chip, cc_raw, SHDW_CC, NORESET);
-}
-
-/* Returns full charge design in uAh */
-static int get_prop_bms_charge_full_design(struct qpnp_bms_chip *chip)
-{
- return chip->fcc_mah * 1000;
-}
-
static void qpnp_bms_external_power_changed(struct power_supply *psy)
{
struct qpnp_bms_chip *chip = container_of(psy, struct qpnp_bms_chip,
@@ -3019,6 +3200,26 @@
chip->shutdown_soc_invalid);
}
+static irqreturn_t bms_ocv_thr_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_bms_chip *chip = _chip;
+
+ pr_debug("ocv_thr irq triggered\n");
+ bms_stay_awake(&chip->soc_wake_source);
+ schedule_work(&chip->recalc_work);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t bms_sw_cc_thr_irq_handler(int irq, void *_chip)
+{
+ struct qpnp_bms_chip *chip = _chip;
+
+ pr_debug("sw_cc_thr irq triggered\n");
+ bms_stay_awake(&chip->soc_wake_source);
+ schedule_work(&chip->recalc_work);
+ return IRQ_HANDLED;
+}
+
#define PALLADIUM_ID_MIN 0x7F40
#define PALLADIUM_ID_MAX 0x7F5A
#define DESAY_5200_ID_MIN 0x7F7F
@@ -3199,6 +3400,35 @@
chip->first_time_calc_uuc = 1;
}
+#define SPMI_SETUP_IRQ(irq_name) \
+do { \
+ chip->irq_name##_irq.irq = spmi_get_irq_byname(chip->spmi, \
+ resource, #irq_name); \
+ if (chip->irq_name##_irq.irq < 0) { \
+ pr_err("Unable to get " #irq_name " irq\n"); \
+ return -ENXIO; \
+ } \
+ rc = devm_request_irq(chip->dev, chip->irq_name##_irq.irq, \
+ bms_##irq_name##_irq_handler, \
+ IRQF_TRIGGER_RISING, #irq_name, chip); \
+ if (rc < 0) { \
+ pr_err("Unable to request " #irq_name " irq: %d\n", rc);\
+ return -ENXIO; \
+ } \
+} while (0)
+
+static int bms_setup_irqs(struct qpnp_bms_chip *chip,
+ struct spmi_resource *resource)
+{
+ int rc;
+
+ SPMI_SETUP_IRQ(sw_cc_thr);
+ enable_irq_wake(chip->sw_cc_thr_irq.irq);
+ SPMI_SETUP_IRQ(ocv_thr);
+ enable_irq_wake(chip->ocv_thr_irq.irq);
+ return 0;
+}
+
#define REG_OFFSET_PERP_TYPE 0x04
#define REG_OFFSET_PERP_SUBTYPE 0x05
#define BMS_BMS_TYPE 0xD
@@ -3246,6 +3476,11 @@
if (type == BMS_BMS_TYPE && subtype == BMS_BMS1_SUBTYPE) {
chip->base = resource->start;
+ rc = bms_setup_irqs(chip, spmi_resource);
+ if (rc) {
+ pr_err("Could not register irqs\n");
+ return rc;
+ }
} else if (type == BMS_IADC_TYPE
&& (subtype == BMS_IADC1_SUBTYPE
|| subtype == BMS_IADC2_SUBTYPE)) {
@@ -3509,8 +3744,7 @@
mutex_init(&chip->soc_invalidation_mutex);
mutex_init(&chip->last_soc_mutex);
- wake_lock_init(&chip->soc_wake_lock, WAKE_LOCK_SUSPEND,
- "qpnp_soc_lock");
+ wakeup_source_init(&chip->soc_wake_source.source, "qpnp_soc_wake");
wake_lock_init(&chip->low_voltage_wake_lock, WAKE_LOCK_SUSPEND,
"qpnp_low_voltage_lock");
wake_lock_init(&chip->cv_wake_lock, WAKE_LOCK_SUSPEND,
@@ -3588,7 +3822,7 @@
power_supply_unregister(&chip->bms_psy);
error_setup:
dev_set_drvdata(&spmi->dev, NULL);
- wake_lock_destroy(&chip->soc_wake_lock);
+ wakeup_source_trash(&chip->soc_wake_source.source);
wake_lock_destroy(&chip->low_voltage_wake_lock);
wake_lock_destroy(&chip->cv_wake_lock);
error_resource:
@@ -3640,9 +3874,8 @@
- (int)(time_since_last_recalc * 1000));
}
- if (!wake_lock_active(&chip->soc_wake_lock)
- && time_until_next_recalc == 0)
- wake_lock(&chip->soc_wake_lock);
+ if (time_until_next_recalc == 0)
+ bms_stay_awake(&chip->soc_wake_source);
schedule_delayed_work(&chip->calculate_soc_delayed_work,
round_jiffies_relative(msecs_to_jiffies
(time_until_next_recalc)));
diff --git a/drivers/slimbus/slim-msm-ctrl.c b/drivers/slimbus/slim-msm-ctrl.c
index 4a3ea76..97d47db 100644
--- a/drivers/slimbus/slim-msm-ctrl.c
+++ b/drivers/slimbus/slim-msm-ctrl.c
@@ -325,35 +325,7 @@
}
pstat = readl_relaxed(PGD_THIS_EE(PGD_PORT_INT_ST_EEn, dev->ver));
if (pstat != 0) {
- int i = 0;
- for (i = dev->pipe_b; i < MSM_SLIM_NPORTS; i++) {
- if (pstat & 1 << i) {
- u32 val = readl_relaxed(PGD_PORT(PGD_PORT_STATn,
- i, dev->ver));
- if (val & (1 << 19)) {
- dev->ctrl.ports[i].err =
- SLIM_P_DISCONNECT;
- dev->pipes[i-dev->pipe_b].connected =
- false;
- /*
- * SPS will call completion since
- * ERROR flags are registered
- */
- } else if (val & (1 << 2))
- dev->ctrl.ports[i].err =
- SLIM_P_OVERFLOW;
- else if (val & (1 << 3))
- dev->ctrl.ports[i].err =
- SLIM_P_UNDERFLOW;
- }
- writel_relaxed(1, PGD_THIS_EE(PGD_PORT_INT_CL_EEn,
- dev->ver));
- }
- /*
- * Guarantee that port interrupt bit(s) clearing writes go
- * through before exiting ISR
- */
- mb();
+ return msm_slim_port_irq_handler(dev, pstat);
}
return IRQ_HANDLED;
@@ -446,16 +418,13 @@
if (mc != SLIM_MSG_MC_DISCONNECT_PORT)
dev->err = msm_slim_connect_pipe_port(dev, *puc);
else {
- struct msm_slim_endp *endpoint = &dev->pipes[*puc];
- struct sps_register_event sps_event;
- memset(&sps_event, 0, sizeof(sps_event));
- sps_register_event(endpoint->sps, &sps_event);
- sps_disconnect(endpoint->sps);
/*
* Remove channel disconnects master-side ports from
* channel. No need to send that again on the bus
+ * Only disable port
*/
- dev->pipes[*puc].connected = false;
+ writel_relaxed(0, PGD_PORT(PGD_PORT_CFGn,
+ (*puc + dev->port_b), dev->ver));
mutex_unlock(&dev->tx_lock);
if (msgv >= 0)
msm_slim_put_ctrl(dev);
@@ -468,7 +437,7 @@
msm_slim_put_ctrl(dev);
return dev->err;
}
- *(puc) = *(puc) + dev->pipe_b;
+ *(puc) = *(puc) + dev->port_b;
}
if (txn->mt == SLIM_MSG_MT_CORE &&
mc == SLIM_MSG_MC_BEGIN_RECONFIGURATION)
@@ -1258,7 +1227,8 @@
dev->ctrl.set_laddr = msm_set_laddr;
dev->ctrl.xfer_msg = msm_xfer_msg;
dev->ctrl.wakeup = msm_clk_pause_wakeup;
- dev->ctrl.config_port = msm_config_port;
+ dev->ctrl.alloc_port = msm_alloc_port;
+ dev->ctrl.dealloc_port = msm_dealloc_port;
dev->ctrl.port_xfer = msm_slim_port_xfer;
dev->ctrl.port_xfer_status = msm_slim_port_xfer_status;
/* Reserve some messaging BW for satellite-apps driver communication */
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index 6962d53..509c1e8 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -89,6 +89,7 @@
struct msm_slim_ctrl *dev = (struct msm_slim_ctrl *)d;
void __iomem *ngd = dev->base + NGD_BASE(dev->ctrl.nr, dev->ver);
u32 stat = readl_relaxed(ngd + NGD_INT_STAT);
+ u32 pstat;
if (stat & NGD_INT_TX_MSG_SENT) {
writel_relaxed(NGD_INT_TX_MSG_SENT, ngd + NGD_INT_CLR);
@@ -147,6 +148,10 @@
mb();
dev_err(dev->dev, "NGD IE VE change");
}
+
+ pstat = readl_relaxed(PGD_THIS_EE(PGD_PORT_INT_ST_EEn, dev->ver));
+ if (pstat != 0)
+ return msm_slim_port_irq_handler(dev, pstat);
return IRQ_HANDLED;
}
@@ -357,19 +362,16 @@
txn->mc == SLIM_USR_MC_CONNECT_SINK ||
txn->mc == SLIM_USR_MC_DISCONNECT_PORT) && txn->wbuf &&
wbuf[0] == dev->pgdla) {
- if (txn->mc != SLIM_MSG_MC_DISCONNECT_PORT)
+ if (txn->mc != SLIM_USR_MC_DISCONNECT_PORT)
dev->err = msm_slim_connect_pipe_port(dev, wbuf[1]);
else {
- struct msm_slim_endp *endpoint = &dev->pipes[wbuf[1]];
- struct sps_register_event sps_event;
- memset(&sps_event, 0, sizeof(sps_event));
- sps_register_event(endpoint->sps, &sps_event);
- sps_disconnect(endpoint->sps);
/*
* Remove channel disconnects master-side ports from
* channel. No need to send that again on the bus
+ * Only disable port
*/
- dev->pipes[wbuf[1]].connected = false;
+ writel_relaxed(0, PGD_PORT(PGD_PORT_CFGn,
+ (wbuf[1] + dev->port_b), dev->ver));
mutex_unlock(&dev->tx_lock);
msm_slim_put_ctrl(dev);
return 0;
@@ -1083,7 +1085,8 @@
dev->ctrl.allocbw = ngd_allocbw;
dev->ctrl.xfer_msg = ngd_xfer_msg;
dev->ctrl.wakeup = ngd_clk_pause_wakeup;
- dev->ctrl.config_port = msm_config_port;
+ dev->ctrl.alloc_port = msm_alloc_port;
+ dev->ctrl.dealloc_port = msm_dealloc_port;
dev->ctrl.port_xfer = msm_slim_port_xfer;
dev->ctrl.port_xfer_status = msm_slim_port_xfer_status;
dev->bam_mem = bam_mem;
diff --git a/drivers/slimbus/slim-msm.c b/drivers/slimbus/slim-msm.c
index 0166196..a63ee76 100644
--- a/drivers/slimbus/slim-msm.c
+++ b/drivers/slimbus/slim-msm.c
@@ -73,6 +73,52 @@
#endif
}
+irqreturn_t msm_slim_port_irq_handler(struct msm_slim_ctrl *dev, u32 pstat)
+{
+ int i;
+ u32 int_en = readl_relaxed(PGD_THIS_EE(PGD_PORT_INT_EN_EEn,
+ dev->ver));
+ /*
+ * different port-interrupt than what we enabled, ignore.
+ * This may happen if overflow/underflow is reported, but
+ * was disabled due to unavailability of buffers provided by
+ * client.
+ */
+ if ((pstat & int_en) == 0)
+ return IRQ_HANDLED;
+ for (i = dev->port_b; i < MSM_SLIM_NPORTS; i++) {
+ if (pstat & (1 << i)) {
+ u32 val = readl_relaxed(PGD_PORT(PGD_PORT_STATn,
+ i, dev->ver));
+ if (val & MSM_PORT_OVERFLOW) {
+ dev->ctrl.ports[i-dev->port_b].err =
+ SLIM_P_OVERFLOW;
+ } else if (val & MSM_PORT_UNDERFLOW) {
+ dev->ctrl.ports[i-dev->port_b].err =
+ SLIM_P_UNDERFLOW;
+ }
+ }
+ }
+ /*
+ * Disable port interrupt here. Re-enable when more
+ * buffers are provided for this port.
+ */
+ writel_relaxed((int_en & (~pstat)),
+ PGD_THIS_EE(PGD_PORT_INT_EN_EEn,
+ dev->ver));
+ /* clear port interrupts */
+ writel_relaxed(pstat, PGD_THIS_EE(PGD_PORT_INT_CL_EEn,
+ dev->ver));
+ pr_info("disabled overflow/underflow for port 0x%x", pstat);
+
+ /*
+ * Guarantee that port interrupt bit(s) clearing writes go
+ * through before exiting ISR
+ */
+ mb();
+ return IRQ_HANDLED;
+}
+
int msm_slim_init_endpoint(struct msm_slim_ctrl *dev, struct msm_slim_endp *ep)
{
int ret;
@@ -138,17 +184,27 @@
void msm_hw_set_port(struct msm_slim_ctrl *dev, u8 pn)
{
u32 set_cfg = DEF_WATERMARK | DEF_ALIGN | DEF_PACK | ENABLE_PORT;
- u32 int_port = readl_relaxed(PGD_THIS_EE(PGD_PORT_INT_EN_EEn,
- dev->ver));
writel_relaxed(set_cfg, PGD_PORT(PGD_PORT_CFGn, pn, dev->ver));
writel_relaxed(DEF_BLKSZ, PGD_PORT(PGD_PORT_BLKn, pn, dev->ver));
writel_relaxed(DEF_TRANSZ, PGD_PORT(PGD_PORT_TRANn, pn, dev->ver));
- writel_relaxed((int_port | 1 << pn) , PGD_THIS_EE(PGD_PORT_INT_EN_EEn,
- dev->ver));
/* Make sure that port registers are updated before returning */
mb();
}
+static void msm_slim_disconn_pipe_port(struct msm_slim_ctrl *dev, u8 pn)
+{
+ struct msm_slim_endp *endpoint = &dev->pipes[pn];
+ struct sps_register_event sps_event;
+ writel_relaxed(0, PGD_PORT(PGD_PORT_CFGn, (pn + dev->port_b),
+ dev->ver));
+ /* Make sure port register is updated */
+ mb();
+ memset(&sps_event, 0, sizeof(sps_event));
+ sps_register_event(endpoint->sps, &sps_event);
+ sps_disconnect(endpoint->sps);
+ dev->pipes[pn].connected = false;
+}
+
int msm_slim_connect_pipe_port(struct msm_slim_ctrl *dev, u8 pn)
{
struct msm_slim_endp *endpoint = &dev->pipes[pn];
@@ -162,16 +218,26 @@
cfg->options = SPS_O_DESC_DONE | SPS_O_ERROR |
SPS_O_ACK_TRANSFERS | SPS_O_AUTO_ENABLE;
- if (dev->pipes[pn].connected) {
- ret = sps_set_config(dev->pipes[pn].sps, cfg);
- if (ret) {
- dev_err(dev->dev, "sps pipe-port set config erro:%x\n",
- ret);
- return ret;
+ if (dev->pipes[pn].connected &&
+ dev->ctrl.ports[pn].state == SLIM_P_CFG) {
+ return -EISCONN;
+ } else if (dev->pipes[pn].connected) {
+ writel_relaxed(0, PGD_PORT(PGD_PORT_CFGn, (pn + dev->port_b),
+ dev->ver));
+ /* Make sure port disabling goes through */
+ mb();
+ /* Is pipe already connected in desired direction */
+ if ((dev->ctrl.ports[pn].flow == SLIM_SRC &&
+ cfg->mode == SPS_MODE_DEST) ||
+ (dev->ctrl.ports[pn].flow == SLIM_SINK &&
+ cfg->mode == SPS_MODE_SRC)) {
+ msm_hw_set_port(dev, pn + dev->port_b);
+ return 0;
}
+ msm_slim_disconn_pipe_port(dev, pn);
}
- stat = readl_relaxed(PGD_PORT(PGD_PORT_STATn, (pn + dev->pipe_b),
+ stat = readl_relaxed(PGD_PORT(PGD_PORT_STATn, (pn + dev->port_b),
dev->ver));
if (dev->ctrl.ports[pn].flow == SLIM_SRC) {
cfg->destination = dev->bam.hdl;
@@ -191,17 +257,21 @@
cfg->mode = SPS_MODE_SRC;
}
/* Space for desciptor FIFOs */
- cfg->desc.size = MSM_SLIM_DESC_NUM * sizeof(struct sps_iovec);
- cfg->config = SPS_CONFIG_DEFAULT;
- ret = sps_connect(dev->pipes[pn].sps, cfg);
+ ret = msm_slim_sps_mem_alloc(dev, &cfg->desc,
+ MSM_SLIM_DESC_NUM * sizeof(struct sps_iovec));
+ if (ret)
+ pr_err("mem alloc for descr failed:%d", ret);
+ else
+ ret = sps_connect(dev->pipes[pn].sps, cfg);
+
if (!ret) {
dev->pipes[pn].connected = true;
- msm_hw_set_port(dev, pn + dev->pipe_b);
+ msm_hw_set_port(dev, pn + dev->port_b);
}
return ret;
}
-int msm_config_port(struct slim_controller *ctrl, u8 pn)
+int msm_alloc_port(struct slim_controller *ctrl, u8 pn)
{
struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
struct msm_slim_endp *endpoint;
@@ -209,7 +279,7 @@
if (ctrl->ports[pn].req == SLIM_REQ_HALF_DUP ||
ctrl->ports[pn].req == SLIM_REQ_MULTI_CH)
return -EPROTONOSUPPORT;
- if (pn >= (MSM_SLIM_NPORTS - dev->pipe_b))
+ if (pn >= (MSM_SLIM_NPORTS - dev->port_b))
return -ENODEV;
endpoint = &dev->pipes[pn];
@@ -218,6 +288,22 @@
return ret;
}
+void msm_dealloc_port(struct slim_controller *ctrl, u8 pn)
+{
+ struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+ struct msm_slim_endp *endpoint;
+ if (pn >= (MSM_SLIM_NPORTS - dev->port_b))
+ return;
+ endpoint = &dev->pipes[pn];
+ if (dev->pipes[pn].connected)
+ msm_slim_disconn_pipe_port(dev, pn);
+ if (endpoint->sps) {
+ struct sps_connect *config = &endpoint->config;
+ msm_slim_free_endpoint(endpoint);
+ msm_slim_sps_mem_free(dev, &config->desc);
+ }
+}
+
enum slim_port_err msm_slim_port_xfer_status(struct slim_controller *ctr,
u8 pn, u8 **done_buf, u32 *done_len)
{
@@ -241,6 +327,25 @@
return SLIM_P_INPROGRESS;
}
+static void msm_slim_port_cb(struct sps_event_notify *ev)
+{
+
+ struct completion *comp = ev->data.transfer.user;
+ struct sps_iovec *iovec = &ev->data.transfer.iovec;
+
+ if (ev->event_id == SPS_EVENT_DESC_DONE) {
+
+ pr_debug("desc done iovec = (0x%x 0x%x 0x%x)\n",
+ iovec->addr, iovec->size, iovec->flags);
+
+ } else {
+ pr_err("%s: ERR event %d\n",
+ __func__, ev->event_id);
+ }
+ if (comp)
+ complete(comp);
+}
+
int msm_slim_port_xfer(struct slim_controller *ctrl, u8 pn, u8 *iobuf,
u32 len, struct completion *comp)
{
@@ -251,20 +356,29 @@
return -ENODEV;
- ctrl->ports[pn].xcomp = comp;
sreg.options = (SPS_EVENT_DESC_DONE|SPS_EVENT_ERROR);
sreg.mode = SPS_TRIGGER_WAIT;
- sreg.xfer_done = comp;
- sreg.callback = NULL;
- sreg.user = &ctrl->ports[pn];
+ sreg.xfer_done = NULL;
+ sreg.callback = msm_slim_port_cb;
+ sreg.user = NULL;
ret = sps_register_event(dev->pipes[pn].sps, &sreg);
if (ret) {
dev_dbg(dev->dev, "sps register event error:%x\n", ret);
return ret;
}
- ret = sps_transfer_one(dev->pipes[pn].sps, (u32)iobuf, len, NULL,
+ ret = sps_transfer_one(dev->pipes[pn].sps, (u32)iobuf, len, comp,
SPS_IOVEC_FLAG_INT);
dev_dbg(dev->dev, "sps submit xfer error code:%x\n", ret);
+ if (!ret) {
+ /* Enable port interrupts */
+ u32 int_port = readl_relaxed(PGD_THIS_EE(PGD_PORT_INT_EN_EEn,
+ dev->ver));
+ if (!(int_port & (1 << (dev->port_b + pn))))
+ writel_relaxed((int_port | (1 << (dev->port_b + pn))),
+ PGD_THIS_EE(PGD_PORT_INT_EN_EEn, dev->ver));
+ /* Make sure that port registers are updated before returning */
+ mb();
+ }
return ret;
}
@@ -680,7 +794,7 @@
if ((sec_props.ees[dev->ee].pipe_mask >> i) & 0x1)
break;
}
- dev->pipe_b = i - 7;
+ dev->port_b = i - 7;
/* Register the BAM device with the SPS driver */
ret = sps_register_bam_device(&bam_props, &bam_handle);
@@ -750,6 +864,12 @@
if (dev->use_tx_msgqs >= MSM_MSGQ_ENABLED)
msm_slim_remove_ep(dev, &dev->tx_msgq, &dev->use_tx_msgqs);
if (dereg) {
+ int i;
+ for (i = dev->port_b; i < MSM_SLIM_NPORTS; i++) {
+ if (dev->pipes[i - dev->port_b].connected)
+ msm_dealloc_port(&dev->ctrl,
+ i - dev->port_b);
+ }
sps_deregister_bam_device(dev->bam.hdl);
dev->bam.hdl = 0L;
}
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
index f8f625e..896e196 100644
--- a/drivers/slimbus/slim-msm.h
+++ b/drivers/slimbus/slim-msm.h
@@ -13,6 +13,7 @@
#ifndef _SLIM_MSM_H
#define _SLIM_MSM_H
+#include <linux/irq.h>
#include <linux/kthread.h>
#include <mach/msm_qmi_interface.h>
@@ -152,6 +153,12 @@
PGD_VE_STAT_V1 = 0x1710,
};
+enum msm_slim_port_status {
+ MSM_PORT_OVERFLOW = 1 << 2,
+ MSM_PORT_UNDERFLOW = 1 << 3,
+ MSM_PORT_DISCONNECT = 1 << 19,
+};
+
enum msm_ctrl_state {
MSM_CTRL_AWAKE,
MSM_CTRL_SLEEPING,
@@ -177,7 +184,6 @@
struct sps_connect config;
struct sps_register_event event;
struct sps_mem_buffer buf;
- struct completion *xcomp;
bool connected;
};
@@ -224,7 +230,7 @@
u8 pgdla;
enum msm_slim_msgq use_rx_msgqs;
enum msm_slim_msgq use_tx_msgqs;
- int pipe_b;
+ int port_b;
struct completion reconf;
bool reconf_busy;
bool chan_active;
@@ -271,11 +277,13 @@
int msm_slim_rx_dequeue(struct msm_slim_ctrl *dev, u8 *buf);
int msm_slim_get_ctrl(struct msm_slim_ctrl *dev);
void msm_slim_put_ctrl(struct msm_slim_ctrl *dev);
+irqreturn_t msm_slim_port_irq_handler(struct msm_slim_ctrl *dev, u32 pstat);
int msm_slim_init_endpoint(struct msm_slim_ctrl *dev, struct msm_slim_endp *ep);
void msm_slim_free_endpoint(struct msm_slim_endp *ep);
void msm_hw_set_port(struct msm_slim_ctrl *dev, u8 pn);
+int msm_alloc_port(struct slim_controller *ctrl, u8 pn);
+void msm_dealloc_port(struct slim_controller *ctrl, u8 pn);
int msm_slim_connect_pipe_port(struct msm_slim_ctrl *dev, u8 pn);
-int msm_config_port(struct slim_controller *ctrl, u8 pn);
enum slim_port_err msm_slim_port_xfer_status(struct slim_controller *ctr,
u8 pn, u8 **done_buf, u32 *done_len);
int msm_slim_port_xfer(struct slim_controller *ctrl, u8 pn, u8 *iobuf,
diff --git a/drivers/slimbus/slimbus.c b/drivers/slimbus/slimbus.c
index e9f056e..201470f 100644
--- a/drivers/slimbus/slimbus.c
+++ b/drivers/slimbus/slimbus.c
@@ -1089,8 +1089,11 @@
}
break;
}
- if (i >= ctrl->nports)
+ if (i >= ctrl->nports) {
ret = -EDQUOT;
+ goto alloc_err;
+ }
+ ret = 0;
for (j = i; j < i + nphysp; j++) {
ctrl->ports[j].state = SLIM_P_UNCFG;
ctrl->ports[j].req = req;
@@ -1098,7 +1101,8 @@
ctrl->ports[j].flow = SLIM_SINK;
else
ctrl->ports[j].flow = SLIM_SRC;
- ret = ctrl->config_port(ctrl, j);
+ if (ctrl->alloc_port)
+ ret = ctrl->alloc_port(ctrl, j);
if (ret) {
for (; j >= i; j--)
ctrl->ports[j].state = SLIM_P_FREE;
@@ -1126,17 +1130,26 @@
for (i = 0; i < nports; i++) {
u8 pn;
pn = SLIM_HDL_TO_PORT(hdl[i]);
- if (ctrl->ports[pn].state == SLIM_P_CFG) {
- int j;
- dev_err(&ctrl->dev, "Can't dealloc connected port:%d",
- i);
+
+ if (pn >= ctrl->nports || ctrl->ports[pn].state == SLIM_P_CFG) {
+ int j, ret;
+ if (pn >= ctrl->nports) {
+ dev_err(&ctrl->dev, "invalid port number");
+ ret = -EINVAL;
+ } else {
+ dev_err(&ctrl->dev,
+ "Can't dealloc connected port:%d", i);
+ ret = -EISCONN;
+ }
for (j = i - 1; j >= 0; j--) {
pn = SLIM_HDL_TO_PORT(hdl[j]);
ctrl->ports[pn].state = SLIM_P_UNCFG;
}
mutex_unlock(&ctrl->m_ctrl);
- return -EISCONN;
+ return ret;
}
+ if (ctrl->dealloc_port)
+ ctrl->dealloc_port(ctrl, pn);
ctrl->ports[pn].state = SLIM_P_FREE;
}
mutex_unlock(&ctrl->m_ctrl);
@@ -1215,6 +1228,8 @@
* Channel specified in chanh needs to be allocated first.
* Returns -EALREADY if source is already configured for this channel.
* Returns -ENOTCONN if channel is not allocated
+ * Returns -EINVAL if invalid direction is specified for non-manager port,
+ * or if the manager side port number is out of bounds, or in incorrect state
*/
int slim_connect_src(struct slim_device *sb, u32 srch, u16 chanh)
{
@@ -1223,12 +1238,23 @@
u8 chan = SLIM_HDL_TO_CHIDX(chanh);
struct slim_ich *slc = &ctrl->chans[chan];
enum slim_port_flow flow = SLIM_HDL_TO_FLOW(srch);
+ u8 la = SLIM_HDL_TO_LA(srch);
- if (flow != SLIM_SRC)
+ /* manager ports don't have direction when they are allocated */
+ if (la != SLIM_LA_MANAGER && flow != SLIM_SRC)
return -EINVAL;
mutex_lock(&ctrl->sched.m_reconf);
+ if (la == SLIM_LA_MANAGER) {
+ u8 pn = SLIM_HDL_TO_PORT(srch);
+ if (pn >= ctrl->nports ||
+ ctrl->ports[pn].state != SLIM_P_UNCFG) {
+ ret = -EINVAL;
+ goto connect_src_err;
+ }
+ }
+
if (slc->state == SLIM_CH_FREE) {
ret = -ENOTCONN;
goto connect_src_err;
@@ -1264,6 +1290,9 @@
* Channel specified in chanh needs to be allocated first.
* Returns -EALREADY if sink is already configured for this channel.
* Returns -ENOTCONN if channel is not allocated
+ * Returns -EINVAL if invalid parameters are passed, or invalid direction is
+ * specified for non-manager port, or if the manager side port number is out of
+ * bounds, or in incorrect state
*/
int slim_connect_sink(struct slim_device *sb, u32 *sinkh, int nsink, u16 chanh)
{
@@ -1290,8 +1319,14 @@
for (j = 0; j < nsink; j++) {
enum slim_port_flow flow = SLIM_HDL_TO_FLOW(sinkh[j]);
- if (flow != SLIM_SINK)
+ u8 la = SLIM_HDL_TO_LA(sinkh[j]);
+ u8 pn = SLIM_HDL_TO_PORT(sinkh[j]);
+ if (la != SLIM_LA_MANAGER && flow != SLIM_SINK)
ret = -EINVAL;
+ else if (la == SLIM_LA_MANAGER &&
+ (pn >= ctrl->nports ||
+ ctrl->ports[pn].state != SLIM_P_UNCFG))
+ ret = -EINVAL;
else
ret = connect_port_ch(ctrl, chan, sinkh[j], SLIM_SINK);
if (ret) {
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 36a43c3..38b1967 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -671,6 +671,9 @@
#define MAX_XPORT_STR_LEN 50
static char rmnet_transports[MAX_XPORT_STR_LEN];
+/*rmnet transport name string - "rmnet_hsic[,rmnet_hsusb]" */
+static char rmnet_xport_names[MAX_XPORT_STR_LEN];
+
static void rmnet_function_cleanup(struct android_usb_function *f)
{
frmnet_cleanup();
@@ -683,18 +686,28 @@
int err = 0;
char *ctrl_name;
char *data_name;
+ char *tname = NULL;
char buf[MAX_XPORT_STR_LEN], *b;
+ char xport_name_buf[MAX_XPORT_STR_LEN], *tb;
static int rmnet_initialized, ports;
if (!rmnet_initialized) {
rmnet_initialized = 1;
strlcpy(buf, rmnet_transports, sizeof(buf));
b = strim(buf);
+
+ strlcpy(xport_name_buf, rmnet_xport_names,
+ sizeof(xport_name_buf));
+ tb = strim(xport_name_buf);
+
while (b) {
ctrl_name = strsep(&b, ",");
data_name = strsep(&b, ",");
if (ctrl_name && data_name) {
- err = frmnet_init_port(ctrl_name, data_name);
+ if (tb)
+ tname = strsep(&tb, ",");
+ err = frmnet_init_port(ctrl_name, data_name,
+ tname);
if (err) {
pr_err("rmnet: Cannot open ctrl port:"
"'%s' data port:'%s'\n",
@@ -738,12 +751,34 @@
return size;
}
+static ssize_t rmnet_xport_names_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", rmnet_xport_names);
+}
+
+static ssize_t rmnet_xport_names_store(
+ struct device *device, struct device_attribute *attr,
+ const char *buff, size_t size)
+{
+ strlcpy(rmnet_xport_names, buff, sizeof(rmnet_xport_names));
+
+ return size;
+}
+
static struct device_attribute dev_attr_rmnet_transports =
__ATTR(transports, S_IRUGO | S_IWUSR,
rmnet_transports_show,
rmnet_transports_store);
+
+static struct device_attribute dev_attr_rmnet_xport_names =
+ __ATTR(transport_names, S_IRUGO | S_IWUSR,
+ rmnet_xport_names_show,
+ rmnet_xport_names_store);
+
static struct device_attribute *rmnet_function_attributes[] = {
&dev_attr_rmnet_transports,
+ &dev_attr_rmnet_xport_names,
NULL };
static struct android_usb_function rmnet_function = {
@@ -1199,9 +1234,33 @@
return size;
}
+/*enabled FSERIAL transport names - "serial_hsic[,serial_hsusb]"*/
+static char serial_xport_names[32];
+static ssize_t serial_xport_names_store(
+ struct device *device, struct device_attribute *attr,
+ const char *buff, size_t size)
+{
+ strlcpy(serial_xport_names, buff, sizeof(serial_xport_names));
+
+ return size;
+}
+
+static ssize_t serial_xport_names_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", serial_xport_names);
+}
+
static DEVICE_ATTR(transports, S_IWUSR, NULL, serial_transports_store);
-static struct device_attribute *serial_function_attributes[] =
- { &dev_attr_transports, NULL };
+static struct device_attribute dev_attr_serial_xport_names =
+ __ATTR(transport_names, S_IRUGO | S_IWUSR,
+ serial_xport_names_show,
+ serial_xport_names_store);
+
+static struct device_attribute *serial_function_attributes[] = {
+ &dev_attr_transports,
+ &dev_attr_serial_xport_names,
+ NULL };
static void serial_function_cleanup(struct android_usb_function *f)
{
@@ -1211,8 +1270,8 @@
static int serial_function_bind_config(struct android_usb_function *f,
struct usb_configuration *c)
{
- char *name;
- char buf[32], *b;
+ char *name, *xport_name = NULL;
+ char buf[32], *b, xport_name_buf[32], *tb;
int err = -1, i;
static int serial_initialized = 0, ports = 0;
@@ -1223,11 +1282,16 @@
strlcpy(buf, serial_transports, sizeof(buf));
b = strim(buf);
+ strlcpy(xport_name_buf, serial_xport_names, sizeof(xport_name_buf));
+ tb = strim(xport_name_buf);
+
while (b) {
name = strsep(&b, ",");
if (name) {
- err = gserial_init_port(ports, name);
+ if (tb)
+ xport_name = strsep(&tb, ",");
+ err = gserial_init_port(ports, name, xport_name);
if (err) {
pr_err("serial: Cannot open port '%s'", name);
goto out;
diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c
index 88074b9..a9c073b 100644
--- a/drivers/usb/gadget/ci13xxx_msm.c
+++ b/drivers/usb/gadget/ci13xxx_msm.c
@@ -69,6 +69,26 @@
ULPI_CLR(ULPI_MISC_A));
}
+/* Link power management will reduce power consumption by
+ * short time HW suspend/resume.
+ */
+static void ci13xxx_msm_set_l1(struct ci13xxx *udc)
+{
+ int temp;
+ struct device *dev = udc->gadget.dev.parent;
+
+ dev_dbg(dev, "Enable link power management\n");
+
+ /* Enable remote wakeup and L1 for IN EPs */
+ writel_relaxed(0xffff0000, USB_L1_EP_CTRL);
+
+ temp = readl_relaxed(USB_L1_CONFIG);
+ temp |= L1_CONFIG_LPM_EN | L1_CONFIG_REMOTE_WAKEUP |
+ L1_CONFIG_GATE_SYS_CLK | L1_CONFIG_PHY_LPM |
+ L1_CONFIG_PLL;
+ writel_relaxed(temp, USB_L1_CONFIG);
+}
+
static void ci13xxx_msm_connect(void)
{
struct ci13xxx *udc = _udc;
@@ -109,6 +129,9 @@
writel_relaxed(0, USB_AHBBURST);
writel_relaxed(0x08, USB_AHBMODE);
+ if (udc->gadget.l1_supported)
+ ci13xxx_msm_set_l1(udc);
+
if (phy && (phy->flags & ENABLE_SECONDARY_PHY)) {
int temp;
@@ -234,6 +257,7 @@
struct resource *res;
int ret;
struct ci13xxx_platform_data *pdata = pdev->dev.platform_data;
+ bool is_l1_supported = false;
dev_dbg(&pdev->dev, "ci13xxx_msm_probe\n");
@@ -245,6 +269,8 @@
else
ci13xxx_msm_udc_driver.nz_itc =
1 << (pdata->log2_itc-1);
+
+ is_l1_supported = pdata->l1_supported;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -265,6 +291,8 @@
goto iounmap;
}
+ _udc->gadget.l1_supported = is_l1_supported;
+
_udc_ctxt.irq = platform_get_irq(pdev, 0);
if (_udc_ctxt.irq < 0) {
dev_err(&pdev->dev, "IRQ not found\n");
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index de4a233..27d67e3 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -515,7 +515,8 @@
/*
* A SuperSpeed device shall include the USB2.0 extension descriptor
- * and shall support LPM when operating in USB2.0 HS mode.
+ * and shall support LPM when operating in USB2.0 HS mode, as well as
+ * a HS device when operating in USB2.1 HS mode.
*/
usb_ext = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
bos->bNumDeviceCaps++;
@@ -525,33 +526,37 @@
usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT;
usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT);
- /*
- * The Superspeed USB Capability descriptor shall be implemented by all
- * SuperSpeed devices.
- */
- ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
- bos->bNumDeviceCaps++;
- le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE);
- ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE;
- ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
- ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE;
- ss_cap->bmAttributes = 0; /* LTM is not supported yet */
- ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION |
- USB_FULL_SPEED_OPERATION |
- USB_HIGH_SPEED_OPERATION |
- USB_5GBPS_OPERATION);
- ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
+ if (gadget_is_superspeed(cdev->gadget)) {
+ /*
+ * The Superspeed USB Capability descriptor shall be
+ * implemented by all SuperSpeed devices.
+ */
+ ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
+ bos->bNumDeviceCaps++;
+ le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE);
+ ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE;
+ ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
+ ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE;
+ ss_cap->bmAttributes = 0; /* LTM is not supported yet */
+ ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION |
+ USB_FULL_SPEED_OPERATION |
+ USB_HIGH_SPEED_OPERATION |
+ USB_5GBPS_OPERATION);
+ ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
- /* Get Controller configuration */
- if (cdev->gadget->ops->get_config_params)
- cdev->gadget->ops->get_config_params(&dcd_config_params);
- else {
- dcd_config_params.bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT;
- dcd_config_params.bU2DevExitLat =
- cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
+ /* Get Controller configuration */
+ if (cdev->gadget->ops->get_config_params)
+ cdev->gadget->ops->get_config_params
+ (&dcd_config_params);
+ else {
+ dcd_config_params.bU1devExitLat =
+ USB_DEFAULT_U1_DEV_EXIT_LAT;
+ dcd_config_params.bU2DevExitLat =
+ cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
+ }
+ ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
+ ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
}
- ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
- ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
return le16_to_cpu(bos->wTotalLength);
}
@@ -1124,6 +1129,9 @@
cdev->desc.bcdUSB = cpu_to_le16(0x0210);
DBG(cdev, "Config SS device in HS\n");
}
+ } else if (gadget->l1_supported) {
+ cdev->desc.bcdUSB = cpu_to_le16(0x0210);
+ DBG(cdev, "Config HS device with LPM(L1)\n");
}
value = min(w_length, (u16) sizeof cdev->desc);
@@ -1164,7 +1172,8 @@
value = min(w_length, (u16) value);
break;
case USB_DT_BOS:
- if (gadget_is_superspeed(gadget)) {
+ if (gadget_is_superspeed(gadget) ||
+ gadget->l1_supported) {
value = bos_desc(cdev);
value = min(w_length, (u16) value);
}
@@ -1371,6 +1380,10 @@
reset_config(cdev);
if (composite->disconnect)
composite->disconnect(cdev);
+ if (cdev->delayed_status != 0) {
+ INFO(cdev, "delayed status mismatch..resetting\n");
+ cdev->delayed_status = 0;
+ }
spin_unlock_irqrestore(&cdev->lock, flags);
}
diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c
index 6dcc3b4..6bfa203 100644
--- a/drivers/usb/gadget/f_rmnet.c
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -1230,7 +1230,8 @@
no_data_hsuart_ports = 0;
}
-static int frmnet_init_port(const char *ctrl_name, const char *data_name)
+static int frmnet_init_port(const char *ctrl_name, const char *data_name,
+ const char *port_name)
{
struct f_rmnet *dev;
struct rmnet_ports *rmnet_port;
@@ -1272,6 +1273,7 @@
no_ctrl_qti_ports++;
break;
case USB_GADGET_XPORT_HSIC:
+ ghsic_ctrl_set_port_name(port_name, ctrl_name);
rmnet_port->ctrl_xport_num = no_ctrl_hsic_ports;
no_ctrl_hsic_ports++;
break;
@@ -1299,6 +1301,7 @@
no_data_bam2bam_ports++;
break;
case USB_GADGET_XPORT_HSIC:
+ ghsic_data_set_port_name(port_name, data_name);
rmnet_port->data_xport_num = no_data_hsic_ports;
no_data_hsic_ports++;
break;
diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c
index 74dba07..57cbc03 100644
--- a/drivers/usb/gadget/f_serial.c
+++ b/drivers/usb/gadget/f_serial.c
@@ -985,9 +985,11 @@
/**
* gserial_init_port - bind a gserial_port to its transport
*/
-static int gserial_init_port(int port_num, const char *name)
+static int gserial_init_port(int port_num, const char *name,
+ const char *port_name)
{
enum transport_type transport;
+ int ret = 0;
if (port_num >= GSERIAL_NO_PORTS)
return -ENODEV;
@@ -1013,6 +1015,9 @@
no_smd_ports++;
break;
case USB_GADGET_XPORT_HSIC:
+ ghsic_ctrl_set_port_name(port_name, name);
+ ghsic_data_set_port_name(port_name, name);
+
/*client port number will be updated in gport_setup*/
no_hsic_sports++;
break;
@@ -1028,5 +1033,5 @@
nr_ports++;
- return 0;
+ return ret;
}
diff --git a/drivers/usb/gadget/u_bam.c b/drivers/usb/gadget/u_bam.c
index 022e641..a775459 100644
--- a/drivers/usb/gadget/u_bam.c
+++ b/drivers/usb/gadget/u_bam.c
@@ -730,11 +730,11 @@
int ret;
if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
- teth_bridge_disconnect();
ret = usb_bam_disconnect_ipa(&d->ipa_params);
if (ret)
pr_err("%s: usb_bam_disconnect_ipa failed: err:%d\n",
__func__, ret);
+ teth_bridge_disconnect();
}
}
diff --git a/drivers/usb/gadget/u_bam_data.c b/drivers/usb/gadget/u_bam_data.c
index d470edf..3322da5 100644
--- a/drivers/usb/gadget/u_bam_data.c
+++ b/drivers/usb/gadget/u_bam_data.c
@@ -177,13 +177,14 @@
int ret;
if (d->trans == USB_GADGET_XPORT_BAM2BAM_IPA) {
- if (d->func_type == USB_FUNC_MBIM)
- teth_bridge_disconnect();
if (d->func_type == USB_FUNC_ECM)
ecm_ipa_disconnect(d->ipa_params.priv);
ret = usb_bam_disconnect_ipa(&d->ipa_params);
if (ret)
pr_err("usb_bam_disconnect_ipa failed: err:%d\n", ret);
+ if (d->func_type == USB_FUNC_MBIM)
+ teth_bridge_disconnect();
+
}
}
diff --git a/drivers/usb/gadget/u_ctrl_hsic.c b/drivers/usb/gadget/u_ctrl_hsic.c
index 0f93ad4..6143d1b 100644
--- a/drivers/usb/gadget/u_ctrl_hsic.c
+++ b/drivers/usb/gadget/u_ctrl_hsic.c
@@ -36,12 +36,6 @@
static unsigned int no_ctrl_ports;
-static const char *ctrl_bridge_names[] = {
- "dun_ctrl_hsic0",
- "rmnet_ctrl_hsic0"
-};
-
-#define CTRL_BRIDGE_NAME_MAX_LEN 20
#define READ_BUF_LEN 1024
#define CH_OPENED 0
@@ -83,6 +77,7 @@
static struct {
struct gctrl_port *port;
struct platform_driver pdrv;
+ char port_name[BRIDGE_NAME_MAX_LEN];
} gctrl_ports[NUM_PORTS];
static int ghsic_ctrl_receive(void *dev, void *buf, size_t actual)
@@ -336,19 +331,35 @@
gser->send_modem_ctrl_bits(gser, ctrl_bits);
}
+static int ghsic_ctrl_get_port_id(const char *pdev_name)
+{
+ struct gctrl_port *port;
+ int i;
+
+ for (i = 0; i < no_ctrl_ports; i++) {
+ port = gctrl_ports[i].port;
+ if (!strncmp(port->brdg.name, pdev_name, BRIDGE_NAME_MAX_LEN))
+ return i;
+ }
+
+ return -EINVAL;
+}
+
static int ghsic_ctrl_probe(struct platform_device *pdev)
{
struct gctrl_port *port;
unsigned long flags;
+ int id;
pr_debug("%s: name:%s\n", __func__, pdev->name);
- if (pdev->id >= no_ctrl_ports) {
- pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+ id = ghsic_ctrl_get_port_id(pdev->name);
+ if (id < 0 || id >= no_ctrl_ports) {
+ pr_err("%s: invalid port: %d\n", __func__, id);
return -EINVAL;
}
- port = gctrl_ports[pdev->id].port;
+ port = gctrl_ports[id].port;
set_bit(CH_READY, &port->bridge_sts);
/* if usb is online, start read */
@@ -366,15 +377,17 @@
struct gserial *gser = NULL;
struct grmnet *gr = NULL;
unsigned long flags;
+ int id;
pr_debug("%s: name:%s\n", __func__, pdev->name);
- if (pdev->id >= no_ctrl_ports) {
- pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+ id = ghsic_ctrl_get_port_id(pdev->name);
+ if (id < 0 || id >= no_ctrl_ports) {
+ pr_err("%s: invalid port: %d\n", __func__, id);
return -EINVAL;
}
- port = gctrl_ports[pdev->id].port;
+ port = gctrl_ports[id].port;
spin_lock_irqsave(&port->port_lock, flags);
if (!port->port_usb) {
@@ -421,15 +434,17 @@
{
struct gctrl_port *port;
struct platform_driver *pdrv;
+ char *name;
port = kzalloc(sizeof(struct gctrl_port), GFP_KERNEL);
if (!port)
return -ENOMEM;
- port->wq = create_singlethread_workqueue(ctrl_bridge_names[portno]);
+ name = gctrl_ports[portno].port_name;
+
+ port->wq = create_singlethread_workqueue(name);
if (!port->wq) {
- pr_err("%s: Unable to create workqueue:%s\n",
- __func__, ctrl_bridge_names[portno]);
+ pr_err("%s: Unable to create workqueue:%s\n", __func__, name);
return -ENOMEM;
}
@@ -441,7 +456,7 @@
INIT_WORK(&port->connect_w, ghsic_ctrl_connect_w);
INIT_WORK(&port->disconnect_w, gctrl_disconnect_w);
- port->brdg.ch_id = portno;
+ port->brdg.name = name;
port->brdg.ctx = port;
port->brdg.ops.send_pkt = ghsic_ctrl_receive;
if (port->gtype == USB_GADGET_SERIAL)
@@ -451,7 +466,7 @@
pdrv = &gctrl_ports[portno].pdrv;
pdrv->probe = ghsic_ctrl_probe;
pdrv->remove = ghsic_ctrl_remove;
- pdrv->driver.name = ctrl_bridge_names[portno];
+ pdrv->driver.name = name;
pdrv->driver.owner = THIS_MODULE;
platform_driver_register(pdrv);
@@ -461,6 +476,31 @@
return 0;
}
+/*portname will be used to find the bridge channel index*/
+void ghsic_ctrl_set_port_name(const char *name, const char *xport_type)
+{
+ static unsigned int port_num;
+
+ if (port_num >= NUM_PORTS) {
+ pr_err("%s: setting xport name for invalid port num %d\n",
+ __func__, port_num);
+ return;
+ }
+
+ /*if no xport name is passed set it to xport type e.g. hsic*/
+ if (!name)
+ strlcpy(gctrl_ports[port_num].port_name, xport_type,
+ BRIDGE_NAME_MAX_LEN);
+ else
+ strlcpy(gctrl_ports[port_num].port_name, name,
+ BRIDGE_NAME_MAX_LEN);
+
+ /*append _ctrl to get ctrl bridge name e.g. serial_hsic_ctrl*/
+ strlcat(gctrl_ports[port_num].port_name, "_ctrl", BRIDGE_NAME_MAX_LEN);
+
+ port_num++;
+}
+
int ghsic_ctrl_setup(unsigned int num_ports, enum gadget_type gtype)
{
int first_port_id = no_ctrl_ports;
diff --git a/drivers/usb/gadget/u_data_hsic.c b/drivers/usb/gadget/u_data_hsic.c
index 3932bbc..92653db 100644
--- a/drivers/usb/gadget/u_data_hsic.c
+++ b/drivers/usb/gadget/u_data_hsic.c
@@ -25,13 +25,6 @@
static unsigned int no_data_ports;
-static const char *data_bridge_names[] = {
- "dun_data_hsic0",
- "rmnet_data_hsic0"
-};
-
-#define DATA_BRIDGE_NAME_MAX_LEN 20
-
#define GHSIC_DATA_RMNET_RX_Q_SIZE 50
#define GHSIC_DATA_RMNET_TX_Q_SIZE 300
#define GHSIC_DATA_SERIAL_RX_Q_SIZE 10
@@ -130,6 +123,7 @@
static struct {
struct gdata_port *port;
struct platform_driver pdrv;
+ char port_name[BRIDGE_NAME_MAX_LEN];
} gdata_ports[NUM_PORTS];
static unsigned int get_timestamp(void);
@@ -150,22 +144,25 @@
static int ghsic_data_alloc_requests(struct usb_ep *ep, struct list_head *head,
int num,
void (*cb)(struct usb_ep *ep, struct usb_request *),
- gfp_t flags)
+ spinlock_t *lock)
{
int i;
struct usb_request *req;
+ unsigned long flags;
pr_debug("%s: ep:%s head:%p num:%d cb:%p", __func__,
ep->name, head, num, cb);
for (i = 0; i < num; i++) {
- req = usb_ep_alloc_request(ep, flags);
+ req = usb_ep_alloc_request(ep, GFP_KERNEL);
if (!req) {
pr_debug("%s: req allocated:%d\n", __func__, i);
return list_empty(head) ? -ENOMEM : 0;
}
req->complete = cb;
+ spin_lock_irqsave(lock, flags);
list_add(&req->list, head);
+ spin_unlock_irqrestore(lock, flags);
}
return 0;
@@ -438,20 +435,23 @@
req = list_first_entry(&port->rx_idle,
struct usb_request, list);
+ list_del(&req->list);
+ spin_unlock_irqrestore(&port->rx_lock, flags);
created = get_timestamp();
- skb = alloc_skb(ghsic_data_rx_req_size, GFP_ATOMIC);
- if (!skb)
+ skb = alloc_skb(ghsic_data_rx_req_size, GFP_KERNEL);
+ if (!skb) {
+ spin_lock_irqsave(&port->rx_lock, flags);
+ list_add(&req->list, &port->rx_idle);
break;
+ }
info = (struct timestamp_info *)skb->cb;
info->created = created;
- list_del(&req->list);
req->buf = skb->data;
req->length = ghsic_data_rx_req_size;
req->context = skb;
info->rx_queued = get_timestamp();
- spin_unlock_irqrestore(&port->rx_lock, flags);
ret = usb_ep_queue(ep, req, GFP_KERNEL);
spin_lock_irqsave(&port->rx_lock, flags);
if (ret) {
@@ -472,7 +472,7 @@
static void ghsic_data_start_io(struct gdata_port *port)
{
unsigned long flags;
- struct usb_ep *ep;
+ struct usb_ep *ep_out, *ep_in;
int ret;
pr_debug("%s: port:%p\n", __func__, port);
@@ -481,37 +481,37 @@
return;
spin_lock_irqsave(&port->rx_lock, flags);
- ep = port->out;
- if (!ep) {
- spin_unlock_irqrestore(&port->rx_lock, flags);
+ ep_out = port->out;
+ spin_unlock_irqrestore(&port->rx_lock, flags);
+ if (!ep_out)
return;
- }
- ret = ghsic_data_alloc_requests(ep, &port->rx_idle,
- port->rx_q_size, ghsic_data_epout_complete, GFP_ATOMIC);
+ ret = ghsic_data_alloc_requests(ep_out, &port->rx_idle,
+ port->rx_q_size, ghsic_data_epout_complete, &port->rx_lock);
if (ret) {
pr_err("%s: rx req allocation failed\n", __func__);
+ return;
+ }
+
+ spin_lock_irqsave(&port->tx_lock, flags);
+ ep_in = port->in;
+ spin_unlock_irqrestore(&port->tx_lock, flags);
+ if (!ep_in) {
+ spin_lock_irqsave(&port->rx_lock, flags);
+ ghsic_data_free_requests(ep_out, &port->rx_idle);
spin_unlock_irqrestore(&port->rx_lock, flags);
return;
}
- spin_unlock_irqrestore(&port->rx_lock, flags);
- spin_lock_irqsave(&port->tx_lock, flags);
- ep = port->in;
- if (!ep) {
- spin_unlock_irqrestore(&port->tx_lock, flags);
- return;
- }
-
- ret = ghsic_data_alloc_requests(ep, &port->tx_idle,
- port->tx_q_size, ghsic_data_epin_complete, GFP_ATOMIC);
+ ret = ghsic_data_alloc_requests(ep_in, &port->tx_idle,
+ port->tx_q_size, ghsic_data_epin_complete, &port->tx_lock);
if (ret) {
pr_err("%s: tx req allocation failed\n", __func__);
- ghsic_data_free_requests(ep, &port->rx_idle);
- spin_unlock_irqrestore(&port->tx_lock, flags);
+ spin_lock_irqsave(&port->rx_lock, flags);
+ ghsic_data_free_requests(ep_out, &port->rx_idle);
+ spin_unlock_irqrestore(&port->rx_lock, flags);
return;
}
- spin_unlock_irqrestore(&port->tx_lock, flags);
/* queue out requests */
ghsic_data_start_rx(port);
@@ -586,19 +586,35 @@
spin_unlock_irqrestore(&port->rx_lock, flags);
}
+static int ghsic_data_get_port_id(const char *pdev_name)
+{
+ struct gdata_port *port;
+ int i;
+
+ for (i = 0; i < no_data_ports; i++) {
+ port = gdata_ports[i].port;
+ if (!strncmp(port->brdg.name, pdev_name, BRIDGE_NAME_MAX_LEN))
+ return i;
+ }
+
+ return -EINVAL;
+}
+
static int ghsic_data_probe(struct platform_device *pdev)
{
struct gdata_port *port;
+ int id;
- pr_debug("%s: name:%s no_data_ports= %d\n",
- __func__, pdev->name, no_data_ports);
+ pr_debug("%s: name:%s no_data_ports= %d\n", __func__, pdev->name,
+ no_data_ports);
- if (pdev->id >= no_data_ports) {
- pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+ id = ghsic_data_get_port_id(pdev->name);
+ if (id < 0 || id >= no_data_ports) {
+ pr_err("%s: invalid port: %d\n", __func__, id);
return -EINVAL;
}
- port = gdata_ports[pdev->id].port;
+ port = gdata_ports[id].port;
set_bit(CH_READY, &port->bridge_sts);
/* if usb is online, try opening bridge */
@@ -614,15 +630,17 @@
struct gdata_port *port;
struct usb_ep *ep_in;
struct usb_ep *ep_out;
+ int id;
pr_debug("%s: name:%s\n", __func__, pdev->name);
- if (pdev->id >= no_data_ports) {
- pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+ id = ghsic_data_get_port_id(pdev->name);
+ if (id < 0 || id >= no_data_ports) {
+ pr_err("%s: invalid port: %d\n", __func__, id);
return -EINVAL;
}
- port = gdata_ports[pdev->id].port;
+ port = gdata_ports[id].port;
ep_in = port->in;
if (ep_in)
@@ -632,6 +650,9 @@
if (ep_out)
usb_ep_fifo_flush(ep_out);
+ /* cancel pending writes to MDM */
+ cancel_work_sync(&port->write_tomdm_w);
+
ghsic_data_free_buffers(port);
cancel_work_sync(&port->connect_w);
@@ -658,15 +679,17 @@
{
struct gdata_port *port;
struct platform_driver *pdrv;
+ char *name;
port = kzalloc(sizeof(struct gdata_port), GFP_KERNEL);
if (!port)
return -ENOMEM;
- port->wq = create_singlethread_workqueue(data_bridge_names[port_num]);
+ name = gdata_ports[port_num].port_name;
+
+ port->wq = create_singlethread_workqueue(name);
if (!port->wq) {
- pr_err("%s: Unable to create workqueue:%s\n",
- __func__, data_bridge_names[port_num]);
+ pr_err("%s: Unable to create workqueue:%s\n", __func__, name);
kfree(port);
return -ENOMEM;
}
@@ -688,7 +711,7 @@
skb_queue_head_init(&port->rx_skb_q);
port->gtype = gtype;
- port->brdg.ch_id = port_num;
+ port->brdg.name = name;
port->brdg.ctx = port;
port->brdg.ops.send_pkt = ghsic_data_receive;
port->brdg.ops.unthrottle_tx = ghsic_data_unthrottle_tx;
@@ -697,7 +720,7 @@
pdrv = &gdata_ports[port_num].pdrv;
pdrv->probe = ghsic_data_probe;
pdrv->remove = ghsic_data_remove;
- pdrv->driver.name = data_bridge_names[port_num];
+ pdrv->driver.name = name;
pdrv->driver.owner = THIS_MODULE;
platform_driver_register(pdrv);
@@ -846,7 +869,7 @@
}
#if defined(CONFIG_DEBUG_FS)
-#define DEBUG_BUF_SIZE 1024
+#define DEBUG_DATA_BUF_SIZE 4096
static unsigned int record_timestamp;
module_param(record_timestamp, uint, S_IRUGO | S_IWUSR);
@@ -917,7 +940,7 @@
if (!record_timestamp)
return 0;
- buf = kzalloc(sizeof(char) * 4 * DEBUG_BUF_SIZE, GFP_KERNEL);
+ buf = kzalloc(sizeof(char) * DEBUG_DATA_BUF_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -927,7 +950,7 @@
for (dbg_inc(&i); i != dbg_data.idx; dbg_inc(&i)) {
if (!strnlen(dbg_data.buf[i], DBG_DATA_MSG))
continue;
- j += scnprintf(buf + j, (4 * DEBUG_BUF_SIZE) - j,
+ j += scnprintf(buf + j, DEBUG_DATA_BUF_SIZE - j,
"%s\n", dbg_data.buf[i]);
}
@@ -955,7 +978,7 @@
int i;
int temp = 0;
- buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+ buf = kzalloc(sizeof(char) * DEBUG_DATA_BUF_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -966,7 +989,7 @@
pdrv = &gdata_ports[i].pdrv;
spin_lock_irqsave(&port->rx_lock, flags);
- temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+ temp += scnprintf(buf + temp, DEBUG_DATA_BUF_SIZE - temp,
"\nName: %s\n"
"#PORT:%d port#: %p\n"
"data_ch_open: %d\n"
@@ -991,7 +1014,7 @@
spin_unlock_irqrestore(&port->rx_lock, flags);
spin_lock_irqsave(&port->tx_lock, flags);
- temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+ temp += scnprintf(buf + temp, DEBUG_DATA_BUF_SIZE - temp,
"\n******DL INFO******\n\n"
"dpkts_to_usbhost: %lu\n"
"tx_buf_len: %u\n"
@@ -1094,6 +1117,31 @@
#endif
+/*portname will be used to find the bridge channel index*/
+void ghsic_data_set_port_name(const char *name, const char *xport_type)
+{
+ static unsigned int port_num;
+
+ if (port_num >= NUM_PORTS) {
+ pr_err("%s: setting xport name for invalid port num %d\n",
+ __func__, port_num);
+ return;
+ }
+
+ /*if no xport name is passed set it to xport type e.g. hsic*/
+ if (!name)
+ strlcpy(gdata_ports[port_num].port_name, xport_type,
+ BRIDGE_NAME_MAX_LEN);
+ else
+ strlcpy(gdata_ports[port_num].port_name, name,
+ BRIDGE_NAME_MAX_LEN);
+
+ /*append _data to get data bridge name: e.g. serial_hsic_data*/
+ strlcat(gdata_ports[port_num].port_name, "_data", BRIDGE_NAME_MAX_LEN);
+
+ port_num++;
+}
+
int ghsic_data_setup(unsigned num_ports, enum gadget_type gtype)
{
int first_port_id = no_data_ports;
diff --git a/drivers/usb/host/ehci-msm2.c b/drivers/usb/host/ehci-msm2.c
index 6727996..60788f5 100644
--- a/drivers/usb/host/ehci-msm2.c
+++ b/drivers/usb/host/ehci-msm2.c
@@ -36,6 +36,7 @@
#include <mach/clk.h>
#include <mach/msm_xo.h>
#include <mach/msm_iomap.h>
+#include <linux/debugfs.h>
#define MSM_USB_BASE (hcd->regs)
@@ -896,6 +897,135 @@
return 0;
}
+#if defined(CONFIG_DEBUG_FS)
+static u32 addr;
+#define BUF_SIZE 32
+static ssize_t debug_read_phy_data(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct msm_hcd *mhcd = file->private_data;
+ char *kbuf;
+ size_t c = 0;
+ u32 data = 0;
+ int ret = 0;
+
+ kbuf = kzalloc(sizeof(char) * BUF_SIZE, GFP_KERNEL);
+ pm_runtime_get(mhcd->dev);
+ data = msm_ulpi_read(mhcd, addr);
+ pm_runtime_put(mhcd->dev);
+ if (data < 0) {
+ dev_err(mhcd->dev,
+ "%s(): ulpi read timeout\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ c = scnprintf(kbuf, BUF_SIZE, "addr: 0x%x: data: 0x%x\n", addr, data);
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, kbuf, c);
+
+ kfree(kbuf);
+
+ return ret;
+}
+
+static ssize_t debug_write_phy_data(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct msm_hcd *mhcd = file->private_data;
+ char kbuf[10];
+ u32 data = 0;
+
+ memset(kbuf, 0, 10);
+
+ if (copy_from_user(kbuf, buf, count > 10 ? 10 : count))
+ return -EFAULT;
+
+ if (sscanf(kbuf, "%x", &data) != 1)
+ return -EINVAL;
+
+ pm_runtime_get(mhcd->dev);
+ if (msm_ulpi_write(mhcd, data, addr) < 0) {
+ dev_err(mhcd->dev,
+ "%s(): ulpi write timeout\n", __func__);
+ return -ETIMEDOUT;
+ }
+ pm_runtime_put(mhcd->dev);
+
+ return count;
+}
+
+static ssize_t debug_phy_write_addr(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char kbuf[10];
+ u32 temp;
+
+ memset(kbuf, 0, 10);
+
+ if (copy_from_user(kbuf, buf, count > 10 ? 10 : count))
+ return -EFAULT;
+
+ if (sscanf(kbuf, "%x", &temp) != 1)
+ return -EINVAL;
+
+ if (temp > 0x3F)
+ return -EINVAL;
+
+ addr = temp;
+
+ return count;
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+const struct file_operations debug_rw_phy_ops = {
+ .open = debug_open,
+ .read = debug_read_phy_data,
+ .write = debug_write_phy_data,
+};
+
+const struct file_operations debug_write_phy_ops = {
+ .open = debug_open,
+ .write = debug_phy_write_addr,
+};
+
+static struct dentry *dent_ehci;
+
+static int ehci_debugfs_init(struct msm_hcd *mhcd)
+{
+ struct dentry *debug_phy_data;
+ struct dentry *debug_phy_addr;
+
+ dent_ehci = debugfs_create_dir(dev_name(mhcd->dev), 0);
+ if (IS_ERR(dent_ehci))
+ return -ENOENT;
+
+ debug_phy_data = debugfs_create_file("phy_reg_data", 0666,
+ dent_ehci, mhcd, &debug_rw_phy_ops);
+ if (!debug_phy_data) {
+ debugfs_remove(dent_ehci);
+ return -ENOENT;
+ }
+
+ debug_phy_addr = debugfs_create_file("phy_reg_addr", 0666,
+ dent_ehci, mhcd, &debug_write_phy_ops);
+ if (!debug_phy_addr) {
+ debugfs_remove_recursive(dent_ehci);
+ return -ENOENT;
+ }
+ return 0;
+}
+#else
+static int ehci_debugfs_init(struct msm_hcd *mhcd)
+{
+ return 0;
+}
+#endif
+
static struct hc_driver msm_hc2_driver = {
.description = hcd_name,
.product_desc = "Qualcomm EHCI Host Controller",
@@ -1205,6 +1335,9 @@
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
+ if (ehci_debugfs_init(mhcd) < 0)
+ dev_err(mhcd->dev, "%s: debugfs init failed\n", __func__);
+
return 0;
vbus_deinit:
@@ -1275,6 +1408,9 @@
iounmap(hcd->regs);
usb_put_hcd(hcd);
+#if defined(CONFIG_DEBUG_FS)
+ debugfs_remove_recursive(dent_ehci);
+#endif
return 0;
}
diff --git a/drivers/usb/misc/diag_bridge.c b/drivers/usb/misc/diag_bridge.c
index 1ee1c8e..22e2a25 100644
--- a/drivers/usb/misc/diag_bridge.c
+++ b/drivers/usb/misc/diag_bridge.c
@@ -45,6 +45,7 @@
struct diag_bridge_ops *ops;
struct platform_device *pdev;
unsigned default_autosusp_delay;
+ int id;
/* debugging counters */
unsigned long bytes_to_host;
@@ -92,7 +93,7 @@
static void diag_bridge_delete(struct kref *kref)
{
struct diag_bridge *dev = container_of(kref, struct diag_bridge, kref);
- int id = dev->pdev->id;
+ int id = dev->id;
usb_put_dev(dev->udev);
__dev[id] = 0;
@@ -470,6 +471,7 @@
return -ENOMEM;
}
__dev[devid] = dev;
+ dev->id = devid;
dev->udev = usb_get_dev(interface_to_usbdev(ifc));
dev->ifc = ifc;
diff --git a/drivers/usb/misc/mdm_ctrl_bridge.c b/drivers/usb/misc/mdm_ctrl_bridge.c
index 2a61501..75fce2f 100644
--- a/drivers/usb/misc/mdm_ctrl_bridge.c
+++ b/drivers/usb/misc/mdm_ctrl_bridge.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -26,11 +26,6 @@
#include <asm/unaligned.h>
#include <mach/usb_bridge.h>
-static const char *ctrl_bridge_names[] = {
- "dun_ctrl_hsic0",
- "rmnet_ctrl_hsic0"
-};
-
/* polling interval for Interrupt ep */
#define HS_INTERVAL 7
#define FS_LS_INTERVAL 3
@@ -40,10 +35,18 @@
#define SUSPENDED BIT(0)
+enum ctrl_bridge_rx_state {
+ RX_IDLE, /* inturb is not queued */
+ RX_WAIT, /* inturb is queued and waiting for data */
+ RX_BUSY, /* inturb is completed. processing RX */
+};
+
struct ctrl_bridge {
struct usb_device *udev;
struct usb_interface *intf;
+ char *name;
+
unsigned int int_pipe;
struct urb *inturb;
void *intbuf;
@@ -66,6 +69,9 @@
/* output control lines (DTR, RTS) */
unsigned int cbits_tomdm;
+ spinlock_t lock;
+ enum ctrl_bridge_rx_state rx_state;
+
/* counters */
unsigned int snd_encap_cmd;
unsigned int get_encap_res;
@@ -76,8 +82,19 @@
static struct ctrl_bridge *__dev[MAX_BRIDGE_DEVICES];
-/* counter used for indexing ctrl bridge devices */
-static int ch_id;
+static int get_ctrl_bridge_chid(char *xport_name)
+{
+ struct ctrl_bridge *dev;
+ int i;
+
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
+ dev = __dev[i];
+ if (!strncmp(dev->name, xport_name, BRIDGE_NAME_MAX_LEN))
+ return i;
+ }
+
+ return -ENODEV;
+}
unsigned int ctrl_bridge_get_cbits_tohost(unsigned int id)
{
@@ -125,12 +142,43 @@
}
EXPORT_SYMBOL(ctrl_bridge_set_cbits);
+static int ctrl_bridge_start_read(struct ctrl_bridge *dev, gfp_t gfp_flags)
+{
+ int retval = 0;
+ unsigned long flags;
+
+ if (!dev->inturb) {
+ dev_err(&dev->intf->dev, "%s: inturb is NULL\n", __func__);
+ return -ENODEV;
+ }
+
+ retval = usb_submit_urb(dev->inturb, gfp_flags);
+ if (retval < 0 && retval != -EPERM) {
+ dev_err(&dev->intf->dev,
+ "%s error submitting int urb %d\n",
+ __func__, retval);
+ }
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (retval)
+ dev->rx_state = RX_IDLE;
+ else
+ dev->rx_state = RX_WAIT;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return retval;
+}
+
static void resp_avail_cb(struct urb *urb)
{
struct ctrl_bridge *dev = urb->context;
- int status = 0;
int resubmit_urb = 1;
struct bridge *brdg = dev->brdg;
+ unsigned long flags;
+
+ /*usb device disconnect*/
+ if (urb->dev->state == USB_STATE_NOTATTACHED)
+ return;
switch (urb->status) {
case 0:
@@ -158,15 +206,14 @@
if (resubmit_urb) {
/*re- submit int urb to check response available*/
- usb_anchor_urb(dev->inturb, &dev->tx_submitted);
- status = usb_submit_urb(dev->inturb, GFP_ATOMIC);
- if (status) {
- dev_err(&dev->intf->dev,
- "%s: Error re-submitting Int URB %d\n",
- __func__, status);
- usb_unanchor_urb(dev->inturb);
- }
+ ctrl_bridge_start_read(dev, GFP_ATOMIC);
+ } else {
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->rx_state = RX_IDLE;
+ spin_unlock_irqrestore(&dev->lock, flags);
}
+
+ usb_autopm_put_interface_async(dev->intf);
}
static void notification_available_cb(struct urb *urb)
@@ -177,6 +224,15 @@
struct bridge *brdg = dev->brdg;
unsigned int ctrl_bits;
unsigned char *data;
+ unsigned long flags;
+
+ /*usb device disconnect*/
+ if (urb->dev->state == USB_STATE_NOTATTACHED)
+ return;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->rx_state = RX_IDLE;
+ spin_unlock_irqrestore(&dev->lock, flags);
switch (urb->status) {
case 0:
@@ -204,7 +260,11 @@
switch (ctrl->bNotificationType) {
case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->rx_state = RX_BUSY;
+ spin_unlock_irqrestore(&dev->lock, flags);
dev->resp_avail++;
+ usb_autopm_get_interface_no_resume(dev->intf);
usb_fill_control_urb(dev->readurb, dev->udev,
usb_rcvctrlpipe(dev->udev, 0),
(unsigned char *)dev->in_ctlreq,
@@ -212,13 +272,12 @@
DEFAULT_READ_URB_LENGTH,
resp_avail_cb, dev);
- usb_anchor_urb(dev->readurb, &dev->tx_submitted);
status = usb_submit_urb(dev->readurb, GFP_ATOMIC);
if (status) {
dev_err(&dev->intf->dev,
"%s: Error submitting Read URB %d\n",
__func__, status);
- usb_unanchor_urb(dev->readurb);
+ usb_autopm_put_interface_async(dev->intf);
goto resubmit_int_urb;
}
return;
@@ -242,56 +301,28 @@
}
resubmit_int_urb:
- usb_anchor_urb(urb, &dev->tx_submitted);
- status = usb_submit_urb(urb, GFP_ATOMIC);
- if (status) {
- dev_err(&dev->intf->dev, "%s: Error re-submitting Int URB %d\n",
- __func__, status);
- usb_unanchor_urb(urb);
- }
-}
-
-static int ctrl_bridge_start_read(struct ctrl_bridge *dev)
-{
- int retval = 0;
-
- if (!dev->inturb) {
- dev_err(&dev->intf->dev, "%s: inturb is NULL\n", __func__);
- return -ENODEV;
- }
-
- if (!dev->inturb->anchor) {
- usb_anchor_urb(dev->inturb, &dev->tx_submitted);
- retval = usb_submit_urb(dev->inturb, GFP_KERNEL);
- if (retval < 0) {
- dev_err(&dev->intf->dev,
- "%s error submitting int urb %d\n",
- __func__, retval);
- usb_unanchor_urb(dev->inturb);
- }
- }
-
- return retval;
+ ctrl_bridge_start_read(dev, GFP_ATOMIC);
}
int ctrl_bridge_open(struct bridge *brdg)
{
struct ctrl_bridge *dev;
+ int ch_id;
if (!brdg) {
err("bridge is null\n");
return -EINVAL;
}
- if (brdg->ch_id >= MAX_BRIDGE_DEVICES)
- return -EINVAL;
-
- dev = __dev[brdg->ch_id];
- if (!dev) {
- err("dev is null\n");
- return -ENODEV;
+ ch_id = get_ctrl_bridge_chid(brdg->name);
+ if (ch_id < 0 || ch_id >= MAX_BRIDGE_DEVICES) {
+ err("%s: %s dev not found\n", __func__, brdg->name);
+ return ch_id;
}
+ brdg->ch_id = ch_id;
+
+ dev = __dev[ch_id];
dev->brdg = brdg;
dev->snd_encap_cmd = 0;
dev->get_encap_res = 0;
@@ -337,7 +368,13 @@
kfree(urb->transfer_buffer);
kfree(urb->setup_packet);
usb_free_urb(urb);
- usb_autopm_put_interface_async(dev->intf);
+
+ /* if we are here after device disconnect
+ * usb_unbind_interface() takes care of
+ * residual pm_autopm_get_interface_* calls
+ */
+ if (urb->dev->state != USB_STATE_NOTATTACHED)
+ usb_autopm_put_interface_async(dev->intf);
}
int ctrl_bridge_write(unsigned int id, char *data, size_t size)
@@ -346,6 +383,7 @@
struct urb *writeurb;
struct usb_ctrlrequest *out_ctlreq;
struct ctrl_bridge *dev;
+ unsigned long flags;
if (id >= MAX_BRIDGE_DEVICES) {
result = -EINVAL;
@@ -414,12 +452,15 @@
goto free_ctrlreq;
}
+ spin_lock_irqsave(&dev->lock, flags);
if (test_bit(SUSPENDED, &dev->flags)) {
usb_anchor_urb(writeurb, &dev->tx_deferred);
+ spin_unlock_irqrestore(&dev->lock, flags);
goto deferred;
}
usb_anchor_urb(writeurb, &dev->tx_submitted);
+ spin_unlock_irqrestore(&dev->lock, flags);
result = usb_submit_urb(writeurb, GFP_ATOMIC);
if (result < 0) {
dev_err(&dev->intf->dev, "%s: submit URB error %d\n",
@@ -446,6 +487,7 @@
int ctrl_bridge_suspend(unsigned int id)
{
struct ctrl_bridge *dev;
+ unsigned long flags;
if (id >= MAX_BRIDGE_DEVICES)
return -EINVAL;
@@ -454,8 +496,27 @@
if (!dev)
return -ENODEV;
+ spin_lock_irqsave(&dev->lock, flags);
+ if (!usb_anchor_empty(&dev->tx_submitted) || dev->rx_state == RX_BUSY) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -EBUSY;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ usb_kill_urb(dev->inturb);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->rx_state != RX_IDLE) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -EBUSY;
+ }
+ if (!usb_anchor_empty(&dev->tx_submitted)) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ ctrl_bridge_start_read(dev, GFP_KERNEL);
+ return -EBUSY;
+ }
set_bit(SUSPENDED, &dev->flags);
- usb_kill_anchored_urbs(&dev->tx_submitted);
+ spin_unlock_irqrestore(&dev->lock, flags);
return 0;
}
@@ -464,6 +525,8 @@
{
struct ctrl_bridge *dev;
struct urb *urb;
+ unsigned long flags;
+ int ret;
if (id >= MAX_BRIDGE_DEVICES)
return -EINVAL;
@@ -472,12 +535,19 @@
if (!dev)
return -ENODEV;
- if (!test_and_clear_bit(SUSPENDED, &dev->flags))
+ if (!test_bit(SUSPENDED, &dev->flags))
return 0;
+ spin_lock_irqsave(&dev->lock, flags);
/* submit pending write requests */
while ((urb = usb_get_from_anchor(&dev->tx_deferred))) {
- int ret;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ /*
+ * usb_get_from_anchor() does not drop the
+ * ref count incremented by the usb_anchro_urb()
+ * called in Tx submission path. Let us do it.
+ */
+ usb_put_urb(urb);
usb_anchor_urb(urb, &dev->tx_submitted);
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret < 0) {
@@ -487,9 +557,12 @@
usb_free_urb(urb);
usb_autopm_put_interface_async(dev->intf);
}
+ spin_lock_irqsave(&dev->lock, flags);
}
+ clear_bit(SUSPENDED, &dev->flags);
+ spin_unlock_irqrestore(&dev->lock, flags);
- return ctrl_bridge_start_read(dev);
+ return ctrl_bridge_start_read(dev, GFP_KERNEL);
}
#if defined(CONFIG_DEBUG_FS)
@@ -507,7 +580,7 @@
if (!buf)
return -ENOMEM;
- for (i = 0; i < ch_id; i++) {
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
dev = __dev[i];
if (!dev)
continue;
@@ -522,7 +595,7 @@
"cbits_tomdm: %d\n"
"cbits_tohost: %d\n"
"suspended: %d\n",
- dev->pdev->name, dev,
+ dev->name, dev,
dev->snd_encap_cmd,
dev->get_encap_res,
dev->resp_avail,
@@ -546,7 +619,7 @@
struct ctrl_bridge *dev;
int i;
- for (i = 0; i < ch_id; i++) {
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
dev = __dev[i];
if (!dev)
continue;
@@ -593,7 +666,7 @@
int
ctrl_bridge_probe(struct usb_interface *ifc, struct usb_host_endpoint *int_in,
- int id)
+ char *name, int id)
{
struct ctrl_bridge *dev;
struct usb_device *udev;
@@ -604,28 +677,28 @@
udev = interface_to_usbdev(ifc);
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = __dev[id];
if (!dev) {
- dev_err(&ifc->dev, "%s: unable to allocate dev\n",
- __func__);
- return -ENOMEM;
- }
- dev->pdev = platform_device_alloc(ctrl_bridge_names[id], id);
- if (!dev->pdev) {
- dev_err(&ifc->dev, "%s: unable to allocate platform device\n",
- __func__);
- retval = -ENOMEM;
- goto nomem;
+ pr_err("%s:device not found\n", __func__);
+ return -ENODEV;
}
+ dev->name = name;
+
+ dev->pdev = platform_device_alloc(name, -1);
+ if (!dev->pdev) {
+ retval = -ENOMEM;
+ dev_err(&ifc->dev, "%s: unable to allocate platform device\n",
+ __func__);
+ goto free_name;
+ }
+
+ dev->flags = 0;
dev->udev = udev;
dev->int_pipe = usb_rcvintpipe(udev,
int_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
dev->intf = ifc;
- init_usb_anchor(&dev->tx_submitted);
- init_usb_anchor(&dev->tx_deferred);
-
/*use max pkt size from ep desc*/
ep = &dev->intf->cur_altsetting->endpoint[0].desc;
@@ -633,7 +706,7 @@
if (!dev->inturb) {
dev_err(&ifc->dev, "%s: error allocating int urb\n", __func__);
retval = -ENOMEM;
- goto pdev_del;
+ goto pdev_put;
}
wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize);
@@ -685,14 +758,24 @@
dev->intf->cur_altsetting->desc.bInterfaceNumber;
dev->in_ctlreq->wLength = cpu_to_le16(DEFAULT_READ_URB_LENGTH);
- __dev[id] = dev;
+ retval = platform_device_add(dev->pdev);
+ if (retval) {
+ dev_err(&ifc->dev, "%s:fail to add pdev\n", __func__);
+ goto free_ctrlreq;
+ }
- platform_device_add(dev->pdev);
+ retval = ctrl_bridge_start_read(dev, GFP_KERNEL);
+ if (retval) {
+ dev_err(&ifc->dev, "%s:fail to start reading\n", __func__);
+ goto pdev_del;
+ }
- ch_id++;
+ return 0;
- return ctrl_bridge_start_read(dev);
-
+pdev_del:
+ platform_device_del(dev->pdev);
+free_ctrlreq:
+ kfree(dev->in_ctlreq);
free_rbuf:
kfree(dev->readbuf);
free_rurb:
@@ -701,10 +784,10 @@
kfree(dev->intbuf);
free_inturb:
usb_free_urb(dev->inturb);
-pdev_del:
- platform_device_unregister(dev->pdev);
-nomem:
- kfree(dev);
+pdev_put:
+ platform_device_put(dev->pdev);
+free_name:
+ dev->name = "none";
return retval;
}
@@ -715,9 +798,18 @@
dev_dbg(&dev->intf->dev, "%s:\n", __func__);
+ /*set device name to none to get correct channel id
+ * at the time of bridge open
+ */
+ dev->name = "none";
+
platform_device_unregister(dev->pdev);
- usb_unlink_anchored_urbs(&dev->tx_submitted);
+ usb_scuttle_anchored_urbs(&dev->tx_deferred);
+ usb_kill_anchored_urbs(&dev->tx_submitted);
+
+ usb_kill_urb(dev->inturb);
+ usb_kill_urb(dev->readurb);
kfree(dev->in_ctlreq);
kfree(dev->readbuf);
@@ -725,26 +817,54 @@
usb_free_urb(dev->readurb);
usb_free_urb(dev->inturb);
-
- __dev[id] = NULL;
- ch_id--;
-
- kfree(dev);
}
-static int __init ctrl_bridge_init(void)
+int ctrl_bridge_init(void)
{
+ struct ctrl_bridge *dev;
+ int i;
+ int retval = 0;
+
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ pr_err("%s: unable to allocate dev\n", __func__);
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ /*transport name will be set during probe*/
+ dev->name = "none";
+
+ spin_lock_init(&dev->lock);
+ init_usb_anchor(&dev->tx_submitted);
+ init_usb_anchor(&dev->tx_deferred);
+
+ __dev[i] = dev;
+ }
+
ctrl_bridge_debugfs_init();
return 0;
-}
-module_init(ctrl_bridge_init);
-static void __exit ctrl_bridge_exit(void)
+error:
+ while (--i >= 0) {
+ kfree(__dev[i]);
+ __dev[i] = NULL;
+ }
+
+ return retval;
+}
+
+void ctrl_bridge_exit(void)
{
- ctrl_bridge_debugfs_exit();
-}
-module_exit(ctrl_bridge_exit);
+ int i;
-MODULE_DESCRIPTION("Qualcomm modem control bridge driver");
-MODULE_LICENSE("GPL v2");
+ ctrl_bridge_debugfs_exit();
+
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
+ kfree(__dev[i]);
+ __dev[i] = NULL;
+ }
+}
diff --git a/drivers/usb/misc/mdm_data_bridge.c b/drivers/usb/misc/mdm_data_bridge.c
index fcbf0e1..eea217a 100644
--- a/drivers/usb/misc/mdm_data_bridge.c
+++ b/drivers/usb/misc/mdm_data_bridge.c
@@ -29,11 +29,37 @@
#define FLOW_CTRL_DISABLE 300
#define FLOW_CTRL_SUPPORT 1
-static const char *data_bridge_names[] = {
- "dun_data_hsic0",
- "rmnet_data_hsic0"
+#define BRIDGE_DATA_IDX 0
+#define BRIDGE_CTRL_IDX 1
+
+/*for xport : HSIC*/
+static const char * const serial_hsic_bridge_names[] = {
+ "serial_hsic_data",
+ "serial_hsic_ctrl",
};
+static const char * const rmnet_hsic_bridge_names[] = {
+ "rmnet_hsic_data",
+ "rmnet_hsic_ctrl",
+};
+
+/*for xport : HSUSB*/
+static const char * const serial_hsusb_bridge_names[] = {
+ "serial_hsusb_data",
+ "serial_hsusb_ctrl",
+};
+
+static const char * const rmnet_hsusb_bridge_names[] = {
+ "rmnet_hsusb_data",
+ "rmnet_hsusb_ctrl",
+};
+
+/* since driver supports multiple instances, on smp systems
+ * probe might get called from multiple cores, hence use lock
+ * to identify unclaimed bridge device instance
+ */
+static DEFINE_MUTEX(brdg_claim_lock);
+
static struct workqueue_struct *bridge_wq;
static unsigned int fctrl_support = FLOW_CTRL_SUPPORT;
@@ -54,14 +80,16 @@
static unsigned tx_urb_mult = 20;
module_param(tx_urb_mult, uint, S_IRUGO|S_IWUSR);
-#define TX_HALT BIT(0)
-#define RX_HALT BIT(1)
-#define SUSPENDED BIT(2)
+#define TX_HALT 0
+#define RX_HALT 1
+#define SUSPENDED 2
+#define CLAIMED 3
struct data_bridge {
struct usb_interface *intf;
struct usb_device *udev;
int id;
+ char *name;
unsigned int bulk_in;
unsigned int bulk_out;
@@ -71,9 +99,6 @@
struct usb_anchor tx_active;
struct usb_anchor rx_active;
- /* keep track of outgoing URBs during suspend */
- struct usb_anchor delayed;
-
struct list_head rx_idle;
struct sk_buff_head rx_done;
@@ -102,14 +127,45 @@
static struct data_bridge *__dev[MAX_BRIDGE_DEVICES];
-/* counter used for indexing data bridge devices */
-static int ch_id;
-
static unsigned int get_timestamp(void);
static void dbg_timestamp(char *, struct sk_buff *);
static int submit_rx_urb(struct data_bridge *dev, struct urb *urb,
gfp_t flags);
+/* Find an unclaimed bridge device instance */
+static int get_bridge_dev_idx(void)
+{
+ struct data_bridge *dev;
+ int i;
+
+ mutex_lock(&brdg_claim_lock);
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
+ dev = __dev[i];
+ if (!test_bit(CLAIMED, &dev->flags)) {
+ set_bit(CLAIMED, &dev->flags);
+ mutex_unlock(&brdg_claim_lock);
+ return i;
+ }
+ }
+ mutex_unlock(&brdg_claim_lock);
+
+ return -ENODEV;
+}
+
+static int get_data_bridge_chid(char *xport_name)
+{
+ struct data_bridge *dev;
+ int i;
+
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
+ dev = __dev[i];
+ if (!strncmp(dev->name, xport_name, BRIDGE_NAME_MAX_LEN))
+ return i;
+ }
+
+ return -ENODEV;
+}
+
static inline bool rx_halted(struct data_bridge *dev)
{
return test_bit(RX_HALT, &dev->flags);
@@ -120,6 +176,22 @@
return test_bit(RX_THROTTLED, &brdg->flags);
}
+static void free_rx_urbs(struct data_bridge *dev)
+{
+ struct list_head *head;
+ struct urb *rx_urb;
+ unsigned long flags;
+
+ head = &dev->rx_idle;
+ spin_lock_irqsave(&dev->rx_done.lock, flags);
+ while (!list_empty(head)) {
+ rx_urb = list_entry(head->next, struct urb, urb_list);
+ list_del(&rx_urb->urb_list);
+ usb_free_urb(rx_urb);
+ }
+ spin_unlock_irqrestore(&dev->rx_done.lock, flags);
+}
+
int data_bridge_unthrottle_rx(unsigned int id)
{
struct data_bridge *dev;
@@ -193,6 +265,10 @@
struct data_bridge *dev = info->dev;
bool queue = 0;
+ /*usb device disconnect*/
+ if (urb->dev->state == USB_STATE_NOTATTACHED)
+ urb->status = -ECONNRESET;
+
brdg = dev->brdg;
skb_put(skb, urb->actual_length);
@@ -280,35 +356,45 @@
{
int i;
struct urb *rx_urb;
+ int retval = 0;
for (i = 0; i < max_rx_urbs; i++) {
rx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!rx_urb)
- return -ENOMEM;
+ if (!rx_urb) {
+ retval = -ENOMEM;
+ goto free_urbs;
+ }
list_add_tail(&rx_urb->urb_list, &dev->rx_idle);
}
- return 0;
+
+ return 0;
+
+free_urbs:
+ free_rx_urbs(dev);
+ return retval;
}
int data_bridge_open(struct bridge *brdg)
{
struct data_bridge *dev;
+ int ch_id;
if (!brdg) {
err("bridge is null\n");
return -EINVAL;
}
- if (brdg->ch_id >= MAX_BRIDGE_DEVICES)
- return -EINVAL;
-
- dev = __dev[brdg->ch_id];
- if (!dev) {
- err("dev is null\n");
- return -ENODEV;
+ ch_id = get_data_bridge_chid(brdg->name);
+ if (ch_id < 0 || ch_id >= MAX_BRIDGE_DEVICES) {
+ err("%s: %s dev not found\n", __func__, brdg->name);
+ return ch_id;
}
+ brdg->ch_id = ch_id;
+
+ dev = __dev[ch_id];
+
dev_dbg(&dev->intf->dev, "%s: dev:%p\n", __func__, dev);
dev->brdg = brdg;
@@ -346,9 +432,8 @@
cancel_work_sync(&dev->kevent);
cancel_work_sync(&dev->process_rx_w);
- usb_unlink_anchored_urbs(&dev->tx_active);
- usb_unlink_anchored_urbs(&dev->rx_active);
- usb_unlink_anchored_urbs(&dev->delayed);
+ usb_kill_anchored_urbs(&dev->tx_active);
+ usb_kill_anchored_urbs(&dev->rx_active);
spin_lock_irqsave(&dev->rx_done.lock, flags);
while ((skb = __skb_dequeue(&dev->rx_done)))
@@ -457,7 +542,12 @@
brdg->ops.unthrottle_tx(brdg->ctx);
}
- usb_autopm_put_interface_async(dev->intf);
+ /* if we are here after device disconnect
+ * usb_unbind_interface() takes care of
+ * residual pm_autopm_get_interface_* calls
+ */
+ if (urb->dev->state != USB_STATE_NOTATTACHED)
+ usb_autopm_put_interface_async(dev->intf);
}
int data_bridge_write(unsigned int id, struct sk_buff *skb)
@@ -502,11 +592,6 @@
txurb->transfer_flags |= URB_ZERO_PACKET;
- if (test_bit(SUSPENDED, &dev->flags)) {
- usb_anchor_urb(txurb, &dev->delayed);
- goto free_urb;
- }
-
pending = atomic_inc_return(&dev->pending_txurbs);
usb_anchor_urb(txurb, &dev->tx_active);
@@ -546,110 +631,66 @@
}
EXPORT_SYMBOL(data_bridge_write);
-static int data_bridge_resume(struct data_bridge *dev)
+static int bridge_resume(struct usb_interface *iface)
{
- struct urb *urb;
- int retval;
+ int retval = 0;
+ struct data_bridge *dev = usb_get_intfdata(iface);
- if (!test_and_clear_bit(SUSPENDED, &dev->flags))
- return 0;
-
- while ((urb = usb_get_from_anchor(&dev->delayed))) {
- usb_anchor_urb(urb, &dev->tx_active);
- atomic_inc(&dev->pending_txurbs);
- retval = usb_submit_urb(urb, GFP_ATOMIC);
- if (retval < 0) {
- atomic_dec(&dev->pending_txurbs);
- usb_unanchor_urb(urb);
-
- /* TODO: need to free urb data */
- usb_scuttle_anchored_urbs(&dev->delayed);
- break;
- }
- dev->to_modem++;
- dev->txurb_drp_cnt--;
- }
+ clear_bit(SUSPENDED, &dev->flags);
if (dev->brdg)
queue_work(dev->wq, &dev->process_rx_w);
- return 0;
-}
-
-static int bridge_resume(struct usb_interface *iface)
-{
- int retval = 0;
- int oldstate;
- struct data_bridge *dev = usb_get_intfdata(iface);
-
- oldstate = iface->dev.power.power_state.event;
- iface->dev.power.power_state.event = PM_EVENT_ON;
-
- if (oldstate & PM_EVENT_SUSPEND) {
- retval = data_bridge_resume(dev);
- if (!retval)
- retval = ctrl_bridge_resume(dev->id);
- }
+ retval = ctrl_bridge_resume(dev->id);
return retval;
}
-static int data_bridge_suspend(struct data_bridge *dev, pm_message_t message)
-{
- if (atomic_read(&dev->pending_txurbs) &&
- (message.event & PM_EVENT_AUTO))
- return -EBUSY;
-
- set_bit(SUSPENDED, &dev->flags);
-
- usb_kill_anchored_urbs(&dev->tx_active);
- usb_kill_anchored_urbs(&dev->rx_active);
-
- return 0;
-}
-
static int bridge_suspend(struct usb_interface *intf, pm_message_t message)
{
int retval;
struct data_bridge *dev = usb_get_intfdata(intf);
- retval = data_bridge_suspend(dev, message);
- if (!retval) {
- retval = ctrl_bridge_suspend(dev->id);
- intf->dev.power.power_state.event = message.event;
- }
+ if (atomic_read(&dev->pending_txurbs))
+ return -EBUSY;
- return retval;
+ retval = ctrl_bridge_suspend(dev->id);
+ if (retval)
+ return retval;
+
+ set_bit(SUSPENDED, &dev->flags);
+ usb_kill_anchored_urbs(&dev->rx_active);
+
+ return 0;
}
static int data_bridge_probe(struct usb_interface *iface,
struct usb_host_endpoint *bulk_in,
- struct usb_host_endpoint *bulk_out, int id)
+ struct usb_host_endpoint *bulk_out, char *name, int id)
{
struct data_bridge *dev;
+ int retval;
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = __dev[id];
if (!dev) {
- err("%s: unable to allocate dev\n", __func__);
- return -ENOMEM;
+ err("%s: device not found\n", __func__);
+ return -ENODEV;
}
- dev->pdev = platform_device_alloc(data_bridge_names[id], id);
+ dev->pdev = platform_device_alloc(name, -1);
if (!dev->pdev) {
err("%s: unable to allocate platform device\n", __func__);
kfree(dev);
return -ENOMEM;
}
- init_usb_anchor(&dev->tx_active);
- init_usb_anchor(&dev->rx_active);
- init_usb_anchor(&dev->delayed);
+ /*clear all bits except claimed bit*/
+ clear_bit(RX_HALT, &dev->flags);
+ clear_bit(TX_HALT, &dev->flags);
+ clear_bit(SUSPENDED, &dev->flags);
- INIT_LIST_HEAD(&dev->rx_idle);
- skb_queue_head_init(&dev->rx_done);
-
- dev->wq = bridge_wq;
dev->id = id;
+ dev->name = name;
dev->udev = interface_to_usbdev(iface);
dev->intf = iface;
@@ -661,13 +702,12 @@
usb_set_intfdata(iface, dev);
- INIT_WORK(&dev->kevent, defer_kevent);
- INIT_WORK(&dev->process_rx_w, data_bridge_process_rx);
-
- __dev[id] = dev;
-
/*allocate list of rx urbs*/
- data_bridge_prepare_rx(dev);
+ retval = data_bridge_prepare_rx(dev);
+ if (retval) {
+ platform_device_put(dev->pdev);
+ return retval;
+ }
platform_device_add(dev->pdev);
@@ -675,7 +715,7 @@
}
#if defined(CONFIG_DEBUG_FS)
-#define DEBUG_BUF_SIZE 1024
+#define DEBUG_BUF_SIZE 4096
static unsigned int record_timestamp;
module_param(record_timestamp, uint, S_IRUGO | S_IWUSR);
@@ -746,7 +786,7 @@
if (!record_timestamp)
return 0;
- buf = kzalloc(sizeof(char) * 4 * DEBUG_BUF_SIZE, GFP_KERNEL);
+ buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -756,7 +796,7 @@
for (dbg_inc(&i); i != dbg_data.idx; dbg_inc(&i)) {
if (!strnlen(dbg_data.buf[i], DBG_DATA_MSG))
continue;
- j += scnprintf(buf + j, (4 * DEBUG_BUF_SIZE) - j,
+ j += scnprintf(buf + j, DEBUG_BUF_SIZE - j,
"%s\n", dbg_data.buf[i]);
}
@@ -786,7 +826,7 @@
if (!buf)
return -ENOMEM;
- for (i = 0; i < ch_id; i++) {
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
dev = __dev[i];
if (!dev)
continue;
@@ -806,7 +846,7 @@
"suspended: %d\n"
"TX_HALT: %d\n"
"RX_HALT: %d\n",
- dev->pdev->name, dev,
+ dev->name, dev,
atomic_read(&dev->pending_txurbs),
dev->txurb_drp_cnt,
dev->to_host,
@@ -836,7 +876,7 @@
struct data_bridge *dev;
int i;
- for (i = 0; i < ch_id; i++) {
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
dev = __dev[i];
if (!dev)
continue;
@@ -913,9 +953,8 @@
int i;
int status = 0;
int numends;
- unsigned int iface_num;
-
- iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
+ int ch_id;
+ char **bname = (char **)id->driver_info;
if (iface->num_altsetting != 1) {
err("%s invalid num_altsetting %u\n",
@@ -923,9 +962,6 @@
return -EINVAL;
}
- if (!test_bit(iface_num, &id->driver_info))
- return -ENODEV;
-
udev = interface_to_usbdev(iface);
usb_get_dev(udev);
@@ -953,27 +989,32 @@
goto out;
}
- status = data_bridge_probe(iface, bulk_in, bulk_out, ch_id);
+ ch_id = get_bridge_dev_idx();
+ if (ch_id < 0) {
+ err("%s all bridge channels claimed. Probe failed\n", __func__);
+ return -ENODEV;
+ }
+
+ status = data_bridge_probe(iface, bulk_in, bulk_out,
+ bname[BRIDGE_DATA_IDX], ch_id);
if (status < 0) {
dev_err(&iface->dev, "data_bridge_probe failed %d\n", status);
goto out;
}
- status = ctrl_bridge_probe(iface, int_in, ch_id);
+ status = ctrl_bridge_probe(iface, int_in, bname[BRIDGE_CTRL_IDX],
+ ch_id);
if (status < 0) {
dev_err(&iface->dev, "ctrl_bridge_probe failed %d\n", status);
- goto free_data_bridge;
+ goto error;
}
- ch_id++;
-
return 0;
-free_data_bridge:
+error:
platform_device_unregister(__dev[ch_id]->pdev);
+ free_rx_urbs(__dev[ch_id]);
usb_set_intfdata(iface, NULL);
- kfree(__dev[ch_id]);
- __dev[ch_id] = NULL;
out:
usb_put_dev(udev);
@@ -983,57 +1024,65 @@
static void bridge_disconnect(struct usb_interface *intf)
{
struct data_bridge *dev = usb_get_intfdata(intf);
- struct list_head *head;
- struct urb *rx_urb;
- unsigned long flags;
if (!dev) {
err("%s: data device not found\n", __func__);
return;
}
- ch_id--;
+ /*set device name to none to get correct channel id
+ * at the time of bridge open
+ */
+ dev->name = "none";
+
ctrl_bridge_disconnect(dev->id);
platform_device_unregister(dev->pdev);
usb_set_intfdata(intf, NULL);
- __dev[dev->id] = NULL;
- /*free rx urbs*/
- head = &dev->rx_idle;
- spin_lock_irqsave(&dev->rx_done.lock, flags);
- while (!list_empty(head)) {
- rx_urb = list_entry(head->next, struct urb, urb_list);
- list_del(&rx_urb->urb_list);
- usb_free_urb(rx_urb);
- }
- spin_unlock_irqrestore(&dev->rx_done.lock, flags);
+ free_rx_urbs(dev);
usb_put_dev(dev->udev);
- kfree(dev);
+
+ clear_bit(CLAIMED, &dev->flags);
}
-/*bit position represents interface number*/
-#define PID9001_IFACE_MASK 0xC
-#define PID9034_IFACE_MASK 0xC
-#define PID9048_IFACE_MASK 0x18
-#define PID904C_IFACE_MASK 0x28
-#define PID9075_IFACE_MASK 0x28
-
+/*driver info stores data/ctrl bridge name used to match bridge xport name*/
static const struct usb_device_id bridge_ids[] = {
- { USB_DEVICE(0x5c6, 0x9001),
- .driver_info = PID9001_IFACE_MASK,
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9001, 2),
+ .driver_info = (unsigned long)serial_hsic_bridge_names,
},
- { USB_DEVICE(0x5c6, 0x9034),
- .driver_info = PID9034_IFACE_MASK,
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9001, 3),
+ .driver_info = (unsigned long)rmnet_hsic_bridge_names,
},
- { USB_DEVICE(0x5c6, 0x9048),
- .driver_info = PID9048_IFACE_MASK,
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9034, 2),
+ .driver_info = (unsigned long)serial_hsic_bridge_names,
},
- { USB_DEVICE(0x5c6, 0x904c),
- .driver_info = PID904C_IFACE_MASK,
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9034, 3),
+ .driver_info = (unsigned long)rmnet_hsic_bridge_names,
},
- { USB_DEVICE(0x5c6, 0x9075),
- .driver_info = PID9075_IFACE_MASK,
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9048, 3),
+ .driver_info = (unsigned long)serial_hsic_bridge_names,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9048, 4),
+ .driver_info = (unsigned long)rmnet_hsic_bridge_names,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x904c, 3),
+ .driver_info = (unsigned long)serial_hsic_bridge_names,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x904c, 5),
+ .driver_info = (unsigned long)rmnet_hsic_bridge_names,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9075, 3),
+ .driver_info = (unsigned long)serial_hsic_bridge_names,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9075, 5),
+ .driver_info = (unsigned long)rmnet_hsic_bridge_names,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9079, 3),
+ .driver_info = (unsigned long)serial_hsusb_bridge_names,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9079, 4),
+ .driver_info = (unsigned long)rmnet_hsusb_bridge_names,
},
{ } /* Terminating entry */
@@ -1052,31 +1101,83 @@
static int __init bridge_init(void)
{
- int ret;
+ struct data_bridge *dev;
+ int ret;
+ int i = 0;
+
+ ret = ctrl_bridge_init();
+ if (ret)
+ return ret;
+
+ bridge_wq = create_singlethread_workqueue("mdm_bridge");
+ if (!bridge_wq) {
+ pr_err("%s: Unable to create workqueue:bridge\n", __func__);
+ ret = -ENOMEM;
+ goto free_ctrl;
+ }
+
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ err("%s: unable to allocate dev\n", __func__);
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ dev->wq = bridge_wq;
+
+ /*transport name will be set during probe*/
+ dev->name = "none";
+
+ init_usb_anchor(&dev->tx_active);
+ init_usb_anchor(&dev->rx_active);
+
+ INIT_LIST_HEAD(&dev->rx_idle);
+
+ skb_queue_head_init(&dev->rx_done);
+
+ INIT_WORK(&dev->kevent, defer_kevent);
+ INIT_WORK(&dev->process_rx_w, data_bridge_process_rx);
+
+ __dev[i] = dev;
+ }
ret = usb_register(&bridge_driver);
if (ret) {
err("%s: unable to register mdm_bridge driver", __func__);
- return ret;
- }
-
- bridge_wq = create_singlethread_workqueue("mdm_bridge");
- if (!bridge_wq) {
- usb_deregister(&bridge_driver);
- pr_err("%s: Unable to create workqueue:bridge\n", __func__);
- return -ENOMEM;
+ goto error;
}
data_bridge_debugfs_init();
return 0;
+
+error:
+ while (--i >= 0) {
+ kfree(__dev[i]);
+ __dev[i] = NULL;
+ }
+ destroy_workqueue(bridge_wq);
+free_ctrl:
+ ctrl_bridge_exit();
+ return ret;
}
static void __exit bridge_exit(void)
{
+ int i;
+
+ usb_deregister(&bridge_driver);
data_bridge_debugfs_exit();
destroy_workqueue(bridge_wq);
- usb_deregister(&bridge_driver);
+
+ for (i = 0; i < MAX_BRIDGE_DEVICES; i++) {
+ kfree(__dev[i]);
+ __dev[i] = NULL;
+ }
+
+ ctrl_bridge_exit();
}
module_init(bridge_init);
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index a074e2d..c0b2771 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -851,6 +851,24 @@
return 0;
}
+static void msm_otg_bus_vote(struct msm_otg *motg, enum usb_bus_vote vote)
+{
+ int ret;
+ struct msm_otg_platform_data *pdata = motg->pdata;
+
+ /* Check if target allows min_vote to be same as no_vote */
+ if (vote >= pdata->bus_scale_table->num_usecases)
+ vote = USB_NO_PERF_VOTE;
+
+ if (motg->bus_perf_client) {
+ ret = msm_bus_scale_client_update_request(
+ motg->bus_perf_client, vote);
+ if (ret)
+ dev_err(motg->phy.dev, "%s: Failed to vote (%d)\n"
+ "for bus bw %d\n", __func__, vote, ret);
+ }
+}
+
#define PHY_SUSPEND_TIMEOUT_USEC (500 * 1000)
#define PHY_RESUME_TIMEOUT_USEC (100 * 1000)
@@ -1040,6 +1058,8 @@
if (bus)
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags);
+ msm_otg_bus_vote(motg, USB_NO_PERF_VOTE);
+
atomic_set(&motg->in_lpm, 1);
/* Enable ASYNC IRQ (if present) during LPM */
if (motg->async_irq)
@@ -1068,6 +1088,9 @@
disable_irq(motg->irq);
wake_lock(&motg->wlock);
+ /* Some platforms require BUS vote to enable/disable clocks */
+ msm_otg_bus_vote(motg, USB_MIN_PERF_VOTE);
+
/* Vote for TCXO when waking up the phy */
if (motg->lpm_flags & XO_SHUTDOWN) {
if (!IS_ERR(motg->xo_clk)) {
@@ -1550,7 +1573,6 @@
static void msm_otg_start_peripheral(struct usb_otg *otg, int on)
{
- int ret;
struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy);
struct msm_otg_platform_data *pdata = motg->pdata;
@@ -1568,29 +1590,18 @@
pdata->setup_gpio(OTG_STATE_B_PERIPHERAL);
/* Configure BUS performance parameters for MAX bandwidth */
- if (motg->bus_perf_client && debug_bus_voting_enabled) {
- ret = msm_bus_scale_client_update_request(
- motg->bus_perf_client, 1);
- if (ret)
- dev_err(motg->phy.dev, "%s: Failed to vote for "
- "bus bandwidth %d\n", __func__, ret);
- }
+ if (debug_bus_voting_enabled)
+ msm_otg_bus_vote(motg, USB_MAX_PERF_VOTE);
+
usb_gadget_vbus_connect(otg->gadget);
} else {
dev_dbg(otg->phy->dev, "gadget off\n");
usb_gadget_vbus_disconnect(otg->gadget);
/* Configure BUS performance parameters to default */
- if (motg->bus_perf_client) {
- ret = msm_bus_scale_client_update_request(
- motg->bus_perf_client, 0);
- if (ret)
- dev_err(motg->phy.dev, "%s: Failed to devote "
- "for bus bw %d\n", __func__, ret);
- }
+ msm_otg_bus_vote(motg, USB_MIN_PERF_VOTE);
if (pdata->setup_gpio)
pdata->setup_gpio(OTG_STATE_UNDEFINED);
}
-
}
static int msm_otg_set_peripheral(struct usb_otg *otg,
@@ -3469,7 +3480,6 @@
size_t count, loff_t *ppos)
{
char buf[8];
- int ret;
struct seq_file *s = file->private_data;
struct msm_otg *motg = s->private;
@@ -3483,13 +3493,7 @@
debug_bus_voting_enabled = true;
} else {
debug_bus_voting_enabled = false;
- if (motg->bus_perf_client) {
- ret = msm_bus_scale_client_update_request(
- motg->bus_perf_client, 0);
- if (ret)
- dev_err(motg->phy.dev, "%s: Failed to devote "
- "for bus bw %d\n", __func__, ret);
- }
+ msm_otg_bus_vote(motg, USB_MIN_PERF_VOTE);
}
return count;
@@ -3712,6 +3716,7 @@
ofdev->dev.platform_data;
ci_pdata.log2_itc = otg_pdata->log2_itc;
ci_pdata.usb_core_id = 0;
+ ci_pdata.l1_supported = otg_pdata->l1_supported;
retval = platform_device_add_data(pdev, &ci_pdata,
sizeof(ci_pdata));
if (retval)
@@ -3843,6 +3848,9 @@
if (pdata->pmic_id_irq < 0)
pdata->pmic_id_irq = 0;
+ pdata->l1_supported = of_property_read_bool(node,
+ "qcom,hsusb-l1-supported");
+
return pdata;
}
@@ -3881,17 +3889,17 @@
pdata = pdev->dev.platform_data;
}
- motg = kzalloc(sizeof(struct msm_otg), GFP_KERNEL);
+ motg = devm_kzalloc(&pdev->dev, sizeof(struct msm_otg), GFP_KERNEL);
if (!motg) {
dev_err(&pdev->dev, "unable to allocate msm_otg\n");
return -ENOMEM;
}
- motg->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
+ motg->phy.otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg),
+ GFP_KERNEL);
if (!motg->phy.otg) {
dev_err(&pdev->dev, "unable to allocate usb_otg\n");
- ret = -ENOMEM;
- goto free_motg;
+ return -ENOMEM;
}
the_msm_otg = motg;
@@ -3899,6 +3907,19 @@
phy = &motg->phy;
phy->dev = &pdev->dev;
+ if (motg->pdata->bus_scale_table) {
+ motg->bus_perf_client =
+ msm_bus_scale_register_client(motg->pdata->bus_scale_table);
+ if (!motg->bus_perf_client) {
+ dev_err(motg->phy.dev, "%s: Failed to register BUS\n"
+ "scaling client!!\n", __func__);
+ } else {
+ debug_bus_voting_enabled = true;
+ /* Some platforms require BUS vote to control clocks */
+ msm_otg_bus_vote(motg, USB_MIN_PERF_VOTE);
+ }
+ }
+
/*
* ACA ID_GND threshold range is overlapped with OTG ID_FLOAT. Hence
* PHY treat ACA ID_GND as float and no interrupt is generated. But
@@ -3907,7 +3928,7 @@
if (aca_enabled() && motg->pdata->otg_control != OTG_PMIC_CONTROL) {
dev_err(&pdev->dev, "ACA can not be enabled without PMIC\n");
ret = -EINVAL;
- goto free_otg;
+ goto devote_bus_bw;
}
/* initialize reset counter */
@@ -4202,16 +4223,6 @@
pm_runtime_use_autosuspend(&pdev->dev);
}
- if (motg->pdata->bus_scale_table) {
- motg->bus_perf_client =
- msm_bus_scale_register_client(motg->pdata->bus_scale_table);
- if (!motg->bus_perf_client)
- dev_err(motg->phy.dev, "%s: Failed to register BUS "
- "scaling client!!\n", __func__);
- else
- debug_bus_voting_enabled = true;
- }
-
motg->usb_psy.name = "usb";
motg->usb_psy.type = POWER_SUPPLY_TYPE_USB;
motg->usb_psy.supplied_to = otg_pm_power_supplied_to;
@@ -4279,10 +4290,12 @@
clk_put(motg->clk);
if (!IS_ERR(motg->phy_reset_clk))
clk_put(motg->phy_reset_clk);
-free_otg:
- kfree(motg->phy.otg);
-free_motg:
- kfree(motg);
+devote_bus_bw:
+ if (motg->bus_perf_client) {
+ msm_otg_bus_vote(motg, USB_NO_PERF_VOTE);
+ msm_bus_scale_unregister_client(motg->bus_perf_client);
+ }
+
return ret;
}
@@ -4363,11 +4376,11 @@
clk_put(motg->clk);
clk_put(motg->core_clk);
- if (motg->bus_perf_client)
+ if (motg->bus_perf_client) {
+ msm_otg_bus_vote(motg, USB_NO_PERF_VOTE);
msm_bus_scale_unregister_client(motg->bus_perf_client);
+ }
- kfree(motg->phy.otg);
- kfree(motg);
return 0;
}
diff --git a/drivers/video/msm/mdss/mdp3.c b/drivers/video/msm/mdss/mdp3.c
index e5b6603..aecb19d 100644
--- a/drivers/video/msm/mdss/mdp3.c
+++ b/drivers/video/msm/mdss/mdp3.c
@@ -826,20 +826,16 @@
struct ion_client *iclient = mdp3_res->ion_client;
int dom = (mdp3_res->domains + MDP3_IOMMU_DOMAIN)->domain_idx;
- if (!data->srcp_file) {
- pr_debug("No img to put\n");
- return 0;
- }
- if (data->flags & MDP_BLIT_SRC_GEM) {
- pr_debug("memory source MDP_BLIT_SRC_GEM\n");
- } else if (data->flags & MDP_MEMORY_ID_TYPE_FB) {
- pr_debug("fb mem buf=0x%x\n", data->addr);
+ if (data->flags & MDP_MEMORY_ID_TYPE_FB) {
+ pr_info("mdp3_put_img fb mem buf=0x%x\n", data->addr);
fput_light(data->srcp_file, data->p_need);
data->srcp_file = NULL;
- } else {
+ } else if (!IS_ERR_OR_NULL(data->srcp_ihdl)) {
ion_unmap_iommu(iclient, data->srcp_ihdl, dom, 0);
ion_free(iclient, data->srcp_ihdl);
data->srcp_ihdl = NULL;
+ } else {
+ return -EINVAL;
}
return 0;
}
@@ -855,16 +851,9 @@
start = (unsigned long *) &data->addr;
len = (unsigned long *) &data->len;
- data->flags |= img->flags;
+ data->flags = img->flags;
data->p_need = 0;
- if (img->flags & MDP_BLIT_SRC_GEM) {
- data->srcp_file = NULL;
- ret = kgsl_gem_obj_addr(img->memory_id, (int) img->priv,
- &data->addr, &data->len);
- if (!ret)
- goto done;
- }
if (img->flags & MDP_MEMORY_ID_TYPE_FB) {
file = fget_light(img->memory_id, &data->p_need);
if (file == NULL) {
@@ -889,8 +878,7 @@
data->srcp_file = file;
if (!ret)
goto done;
- }
- if (iclient) {
+ } else if (iclient) {
data->srcp_ihdl = ion_import_dma_buf(iclient, img->memory_id);
if (IS_ERR_OR_NULL(data->srcp_ihdl)) {
pr_err("error on ion_import_fd\n");
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.c b/drivers/video/msm/mdss/mdp3_ctrl.c
index 339b888..f43d1ed 100644
--- a/drivers/video/msm/mdss/mdp3_ctrl.c
+++ b/drivers/video/msm/mdss/mdp3_ctrl.c
@@ -30,6 +30,8 @@
static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd);
static int mdp3_overlay_unset(struct msm_fb_data_type *mfd, int ndx);
+static int mdp3_histogram_stop(struct mdp3_session_data *session,
+ u32 block);
static void mdp3_bufq_init(struct mdp3_buffer_queue *bufq)
{
@@ -495,6 +497,8 @@
pr_debug("mdp3_ctrl_off stop mdp3 dma engine\n");
+ mdp3_histogram_stop(mdp3_session, MDP_BLOCK_DMA_P);
+
rc = mdp3_session->dma->stop(mdp3_session->dma, mdp3_session->intf);
if (rc)
@@ -521,6 +525,7 @@
off_error:
mdp3_session->status = 0;
+ mdp3_bufq_deinit(&mdp3_session->bufq_out);
mutex_unlock(&mdp3_session->lock);
if (mdp3_session->overlay.id != MSMFB_NEW_REQUEST)
mdp3_overlay_unset(mfd, mdp3_session->overlay.id);
@@ -572,14 +577,11 @@
int rc = 0;
struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
- mdp3_ctrl_pan_display(mfd);
-
mutex_lock(&mdp3_session->lock);
if (mdp3_session->overlay.id == ndx && ndx == 1) {
mdp3_session->overlay.id = MSMFB_NEW_REQUEST;
mdp3_bufq_deinit(&mdp3_session->bufq_in);
- mdp3_bufq_deinit(&mdp3_session->bufq_out);
} else {
rc = -EINVAL;
}
@@ -661,12 +663,14 @@
mdp3_bufq_push(&mdp3_session->bufq_out, data);
}
- if (mdp3_bufq_count(&mdp3_session->bufq_out) > 1) {
+ if (mdp3_bufq_count(&mdp3_session->bufq_out) > 2) {
data = mdp3_bufq_pop(&mdp3_session->bufq_out);
mdp3_put_img(data);
}
-
mutex_unlock(&mdp3_session->lock);
+
+ mdss_fb_update_notify_update(mfd);
+
return rc;
}
@@ -709,7 +713,7 @@
mdp3_session->intf);
} else {
pr_debug("mdp3_ctrl_pan_display no memory, stop interface");
- mdp3_session->intf->stop(mdp3_session->intf);
+ mdp3_session->dma->stop(mdp3_session->dma, mdp3_session->intf);
}
pan_error:
mutex_unlock(&mdp3_session->lock);
@@ -738,6 +742,288 @@
return ret;
}
+static int mdp3_histogram_start(struct mdp3_session_data *session,
+ struct mdp_histogram_start_req *req)
+{
+ int ret;
+ struct mdp3_dma_histogram_config histo_config;
+
+ pr_debug("mdp3_histogram_start\n");
+ if (req->block != MDP_BLOCK_DMA_P ||
+ req->num_bins != MDP_HISTOGRAM_BIN_NUM) {
+ pr_err("mdp3_histogram_start invalid request\n");
+ return -EINVAL;
+ }
+
+ if (!session->dma->histo_op ||
+ !session->dma->config_histo) {
+ pr_err("mdp3_histogram_start not supported\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&session->histo_lock);
+
+ if (session->histo_status) {
+ pr_err("mdp3_histogram_start already started\n");
+ ret = -EBUSY;
+ goto histogram_start_err;
+ }
+
+ ret = session->dma->histo_op(session->dma, MDP3_DMA_HISTO_OP_RESET);
+ if (ret) {
+ pr_err("mdp3_histogram_start reset error\n");
+ goto histogram_start_err;
+ }
+
+ histo_config.frame_count = req->frame_cnt;
+ histo_config.bit_mask = req->bit_mask;
+ histo_config.auto_clear_en = 1;
+ histo_config.bit_mask_polarity = 0;
+ ret = session->dma->config_histo(session->dma, &histo_config);
+ if (ret) {
+ pr_err("mdp3_histogram_start config error\n");
+ goto histogram_start_err;
+ }
+
+ ret = session->dma->histo_op(session->dma, MDP3_DMA_HISTO_OP_START);
+ if (ret) {
+ pr_err("mdp3_histogram_start config error\n");
+ goto histogram_start_err;
+ }
+
+ session->histo_status = 1;
+
+histogram_start_err:
+ mutex_unlock(&session->histo_lock);
+ return ret;
+}
+
+static int mdp3_histogram_stop(struct mdp3_session_data *session,
+ u32 block)
+{
+ int ret;
+ pr_debug("mdp3_histogram_stop\n");
+
+ if (!session->dma->histo_op || block != MDP_BLOCK_DMA_P) {
+ pr_err("mdp3_histogram_stop not supported\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&session->histo_lock);
+
+ if (!session->histo_status) {
+ ret = 0;
+ goto histogram_stop_err;
+ }
+
+ ret = session->dma->histo_op(session->dma, MDP3_DMA_HISTO_OP_CANCEL);
+ if (ret)
+ pr_err("mdp3_histogram_stop error\n");
+
+ session->histo_status = 0;
+
+histogram_stop_err:
+ mutex_unlock(&session->histo_lock);
+ return ret;
+}
+
+static int mdp3_histogram_collect(struct mdp3_session_data *session,
+ struct mdp_histogram_data *hist)
+{
+ int ret;
+ struct mdp3_dma_histogram_data *mdp3_histo;
+
+ if (!session->dma->get_histo) {
+ pr_err("mdp3_histogram_collect not supported\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&session->histo_lock);
+
+ if (!session->histo_status) {
+ pr_err("mdp3_histogram_collect not started\n");
+ mutex_unlock(&session->histo_lock);
+ return -EPERM;
+ }
+
+ mutex_unlock(&session->histo_lock);
+
+ ret = session->dma->get_histo(session->dma);
+ if (ret) {
+ pr_err("mdp3_histogram_collect error = %d\n", ret);
+ return ret;
+ }
+
+ mdp3_histo = &session->dma->histo_data;
+
+ ret = copy_to_user(hist->c0, mdp3_histo->r_data,
+ sizeof(uint32_t) * MDP_HISTOGRAM_BIN_NUM);
+ if (ret)
+ return ret;
+
+ ret = copy_to_user(hist->c1, mdp3_histo->g_data,
+ sizeof(uint32_t) * MDP_HISTOGRAM_BIN_NUM);
+ if (ret)
+ return ret;
+
+ ret = copy_to_user(hist->c2, mdp3_histo->b_data,
+ sizeof(uint32_t) * MDP_HISTOGRAM_BIN_NUM);
+ if (ret)
+ return ret;
+
+ ret = copy_to_user(hist->extra_info, mdp3_histo->extra,
+ sizeof(uint32_t) * 2);
+ if (ret)
+ return ret;
+
+ hist->bin_cnt = MDP_HISTOGRAM_BIN_NUM;
+ hist->block = MDP_BLOCK_DMA_P;
+ return ret;
+}
+
+static int mdp3_bl_scale_config(struct msm_fb_data_type *mfd,
+ struct mdp_bl_scale_data *data)
+{
+ int ret = 0;
+ int curr_bl;
+ mutex_lock(&mfd->bl_lock);
+ curr_bl = mfd->bl_level;
+ mfd->bl_scale = data->scale;
+ mfd->bl_min_lvl = data->min_lvl;
+ pr_debug("update scale = %d, min_lvl = %d\n", mfd->bl_scale,
+ mfd->bl_min_lvl);
+
+ /* update current backlight to use new scaling*/
+ mdss_fb_set_backlight(mfd, curr_bl);
+ mutex_unlock(&mfd->bl_lock);
+ return ret;
+}
+
+static int mdp3_pp_ioctl(struct msm_fb_data_type *mfd,
+ void __user *argp)
+{
+ int ret = -EINVAL;
+ struct msmfb_mdp_pp mdp_pp;
+
+ ret = copy_from_user(&mdp_pp, argp, sizeof(mdp_pp));
+ if (ret)
+ return ret;
+
+ switch (mdp_pp.op) {
+ case mdp_bl_scale_cfg:
+ ret = mdp3_bl_scale_config(mfd, (struct mdp_bl_scale_data *)
+ &mdp_pp.data.bl_scale_data);
+ break;
+ default:
+ pr_err("Unsupported request to MDP_PP IOCTL.\n");
+ ret = -EINVAL;
+ break;
+ }
+ if (!ret)
+ ret = copy_to_user(argp, &mdp_pp, sizeof(struct msmfb_mdp_pp));
+ return ret;
+}
+
+static int mdp3_histo_ioctl(struct msm_fb_data_type *mfd, u32 cmd,
+ void __user *argp)
+{
+ int ret = -ENOSYS;
+ struct mdp_histogram_data hist;
+ struct mdp_histogram_start_req hist_req;
+ u32 block;
+ struct mdp3_session_data *mdp3_session;
+
+ if (!mfd || !mfd->mdp.private1)
+ return -EINVAL;
+
+ mdp3_session = mfd->mdp.private1;
+
+ switch (cmd) {
+ case MSMFB_HISTOGRAM_START:
+ ret = copy_from_user(&hist_req, argp, sizeof(hist_req));
+ if (ret)
+ return ret;
+
+ ret = mdp3_histogram_start(mdp3_session, &hist_req);
+ break;
+
+ case MSMFB_HISTOGRAM_STOP:
+ ret = copy_from_user(&block, argp, sizeof(int));
+ if (ret)
+ return ret;
+
+ ret = mdp3_histogram_stop(mdp3_session, block);
+ break;
+
+ case MSMFB_HISTOGRAM:
+ ret = copy_from_user(&hist, argp, sizeof(hist));
+ if (ret)
+ return ret;
+
+ ret = mdp3_histogram_collect(mdp3_session, &hist);
+ if (!ret)
+ ret = copy_to_user(argp, &hist, sizeof(hist));
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static int mdp3_ctrl_lut_update(struct msm_fb_data_type *mfd,
+ struct fb_cmap *cmap)
+{
+ int rc = 0;
+ struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
+ struct mdp3_dma_lut_config lut_config;
+ struct mdp3_dma_lut lut;
+ static u16 r[MDP_LUT_SIZE];
+ static u16 g[MDP_LUT_SIZE];
+ static u16 b[MDP_LUT_SIZE];
+
+ if (!mdp3_session->dma->config_lut)
+ return -EINVAL;
+
+ if (cmap->start + cmap->len > MDP_LUT_SIZE) {
+ pr_err("mdp3_ctrl_lut_update invalid arguments\n");
+ return -EINVAL;
+ }
+
+ rc = copy_from_user(r + cmap->start,
+ cmap->red, sizeof(u16)*cmap->len);
+ rc |= copy_from_user(g + cmap->start,
+ cmap->green, sizeof(u16)*cmap->len);
+ rc |= copy_from_user(b + cmap->start,
+ cmap->blue, sizeof(u16)*cmap->len);
+ if (rc)
+ return rc;
+
+ lut_config.lut_enable = 7;
+ lut_config.lut_sel = mdp3_session->lut_sel;
+ lut_config.lut_position = 0;
+ lut.color0_lut = r;
+ lut.color1_lut = g;
+ lut.color2_lut = b;
+
+ mutex_lock(&mdp3_session->lock);
+
+ if (!mdp3_session->status) {
+ pr_err("%s, display off!\n", __func__);
+ mutex_unlock(&mdp3_session->lock);
+ return -EPERM;
+ }
+
+ rc = mdp3_session->dma->config_lut(mdp3_session->dma, &lut_config,
+ &lut);
+ if (rc)
+ pr_err("mdp3_ctrl_lut_update failed\n");
+
+ mdp3_session->lut_sel = (mdp3_session->lut_sel + 1) % 2;
+
+ mutex_unlock(&mdp3_session->lock);
+ return rc;
+}
+
static int mdp3_ctrl_ioctl_handler(struct msm_fb_data_type *mfd,
u32 cmd, void __user *argp)
{
@@ -754,10 +1040,19 @@
if (!mdp3_session->status) {
pr_err("mdp3_ctrl_ioctl_handler, display off!\n");
- return -EINVAL;
+ return -EPERM;
}
switch (cmd) {
+ case MSMFB_MDP_PP:
+ rc = mdp3_pp_ioctl(mfd, argp);
+ break;
+ case MSMFB_HISTOGRAM_START:
+ case MSMFB_HISTOGRAM_STOP:
+ case MSMFB_HISTOGRAM:
+ rc = mdp3_histo_ioctl(mfd, cmd, argp);
+ break;
+
case MSMFB_VSYNC_CTRL:
case MSMFB_OVERLAY_VSYNC_CTRL:
if (!copy_from_user(&val, argp, sizeof(val))) {
@@ -836,6 +1131,7 @@
mdp3_interface->dma_fnc = mdp3_ctrl_pan_display;
mdp3_interface->ioctl_handler = mdp3_ctrl_ioctl_handler;
mdp3_interface->kickoff_fnc = mdp3_ctrl_display_commit_kickoff;
+ mdp3_interface->lut_update = mdp3_ctrl_lut_update;
mdp3_session = kmalloc(sizeof(struct mdp3_session_data), GFP_KERNEL);
if (!mdp3_session) {
@@ -845,6 +1141,7 @@
memset(mdp3_session, 0, sizeof(struct mdp3_session_data));
mutex_init(&mdp3_session->lock);
INIT_WORK(&mdp3_session->vsync_work, mdp3_dispatch_vsync);
+ mutex_init(&mdp3_session->histo_lock);
mdp3_session->dma = mdp3_get_dma_pipe(MDP3_DMA_CAP_ALL);
if (!mdp3_session->dma) {
rc = -ENODEV;
@@ -864,6 +1161,8 @@
mdp3_session->overlay.id = MSMFB_NEW_REQUEST;
mdp3_bufq_init(&mdp3_session->bufq_in);
mdp3_bufq_init(&mdp3_session->bufq_out);
+ mdp3_session->histo_status = 0;
+ mdp3_session->lut_sel = 0;
init_timer(&mdp3_session->vsync_timer);
mdp3_session->vsync_timer.function = mdp3_vsync_timer_func;
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.h b/drivers/video/msm/mdss/mdp3_ctrl.h
index 0c2b0cd..0bbc2ac 100644
--- a/drivers/video/msm/mdss/mdp3_ctrl.h
+++ b/drivers/video/msm/mdss/mdp3_ctrl.h
@@ -47,6 +47,9 @@
struct mdp3_buffer_queue bufq_in;
struct mdp3_buffer_queue bufq_out;
struct work_struct vsync_work;
+ int histo_status;
+ struct mutex histo_lock;
+ int lut_sel;
};
int mdp3_ctrl_init(struct msm_fb_data_type *mfd);
diff --git a/drivers/video/msm/mdss/mdp3_dma.c b/drivers/video/msm/mdss/mdp3_dma.c
index 3a46034..3b65405 100644
--- a/drivers/video/msm/mdss/mdp3_dma.c
+++ b/drivers/video/msm/mdss/mdp3_dma.c
@@ -19,6 +19,10 @@
#define DMA_STOP_POLL_SLEEP_US 1000
#define DMA_STOP_POLL_TIMEOUT_US 16000
+#define DMA_HISTO_RESET_TIMEOUT_MS 40
+#define DMA_LUT_CONFIG_MASK 0xfffffbe8
+#define DMA_CCS_CONFIG_MASK 0xfffffc17
+#define HIST_WAIT_TIMEOUT(frame) ((75 * HZ * (frame)) / 1000)
static void mdp3_vsync_intr_handler(int type, void *arg)
{
@@ -45,12 +49,47 @@
mdp3_irq_disable_nosync(type);
}
+static void mdp3_hist_done_intr_handler(int type, void *arg)
+{
+ struct mdp3_dma *dma = (struct mdp3_dma *)arg;
+ u32 isr, mask;
+
+ isr = MDP3_REG_READ(MDP3_REG_DMA_P_HIST_INTR_STATUS);
+ mask = MDP3_REG_READ(MDP3_REG_DMA_P_HIST_INTR_ENABLE);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_INTR_CLEAR, isr);
+
+ isr &= mask;
+ if (isr == 0)
+ return;
+
+ if (isr & MDP3_DMA_P_HIST_INTR_HIST_DONE_BIT) {
+ spin_lock(&dma->histo_lock);
+ dma->histo_state = MDP3_DMA_HISTO_STATE_READY;
+ complete(&dma->histo_comp);
+ spin_unlock(&dma->histo_lock);
+ }
+ if (isr & MDP3_DMA_P_HIST_INTR_RESET_DONE_BIT) {
+ spin_lock(&dma->histo_lock);
+ dma->histo_state = MDP3_DMA_HISTO_STATE_IDLE;
+ complete(&dma->histo_comp);
+ spin_unlock(&dma->histo_lock);
+ }
+}
+
void mdp3_dma_callback_enable(struct mdp3_dma *dma, int type)
{
int irq_bit;
pr_debug("mdp3_dma_callback_enable type=%d\n", type);
+ if (dma->dma_sel == MDP3_DMA_P) {
+ if (type & MDP3_DMA_CALLBACK_TYPE_HIST_RESET_DONE)
+ mdp3_irq_enable(MDP3_INTR_DMA_P_HISTO);
+
+ if (type & MDP3_DMA_CALLBACK_TYPE_HIST_DONE)
+ mdp3_irq_enable(MDP3_INTR_DMA_P_HISTO);
+ }
+
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC) {
if (type & MDP3_DMA_CALLBACK_TYPE_VSYNC)
@@ -79,6 +118,14 @@
pr_debug("mdp3_dma_callback_disable type=%d\n", type);
+ if (dma->dma_sel == MDP3_DMA_P) {
+ if (type & MDP3_DMA_CALLBACK_TYPE_HIST_RESET_DONE)
+ mdp3_irq_disable(MDP3_INTR_DMA_P_HISTO);
+
+ if (type & MDP3_DMA_CALLBACK_TYPE_HIST_DONE)
+ mdp3_irq_disable(MDP3_INTR_DMA_P_HISTO);
+ }
+
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC) {
if (type & MDP3_DMA_CALLBACK_TYPE_VSYNC)
@@ -101,7 +148,7 @@
static int mdp3_dma_callback_setup(struct mdp3_dma *dma)
{
- int rc;
+ int rc = 0;
struct mdp3_intr_cb vsync_cb = {
.cb = mdp3_vsync_intr_handler,
.data = dma,
@@ -112,14 +159,22 @@
.data = dma,
};
+ struct mdp3_intr_cb hist_cb = {
+ .cb = mdp3_hist_done_intr_handler,
+ .data = dma,
+ };
+
+ if (dma->dma_sel == MDP3_DMA_P)
+ rc = mdp3_set_intr_callback(MDP3_INTR_DMA_P_HISTO, &hist_cb);
+
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_LCDC)
- rc = mdp3_set_intr_callback(MDP3_INTR_LCDC_START_OF_FRAME,
+ rc |= mdp3_set_intr_callback(MDP3_INTR_LCDC_START_OF_FRAME,
&vsync_cb);
else if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
int irq_bit = MDP3_INTR_SYNC_PRIMARY_LINE;
irq_bit += dma->dma_sel;
- rc = mdp3_set_intr_callback(irq_bit, &vsync_cb);
+ rc |= mdp3_set_intr_callback(irq_bit, &vsync_cb);
irq_bit = MDP3_INTR_DMA_P_DONE;
if (dma->dma_sel == MDP3_DMA_S)
irq_bit = MDP3_INTR_DMA_S_DONE;
@@ -128,6 +183,7 @@
pr_err("mdp3_dma_callback_setup not suppported interface\n");
rc = -ENODEV;
}
+
return rc;
}
@@ -291,24 +347,54 @@
return 0;
}
-static int mdp3_dmap_ccs_config(struct mdp3_dma *dma,
- struct mdp3_dma_color_correct_config *config,
- struct mdp3_dma_ccs *ccs,
+static int mdp3_dmap_lut_config(struct mdp3_dma *dma,
+ struct mdp3_dma_lut_config *config,
struct mdp3_dma_lut *lut)
{
+ u32 cc_config, addr, color;
int i;
- u32 addr, cc_config, color;
- cc_config = config->lut_enable;
- if (config->ccs_enable)
- cc_config |= BIT(3);
+ if (config->lut_enable && lut) {
+ addr = MDP3_REG_DMA_P_CSC_LUT1;
+ if (config->lut_sel)
+ addr = MDP3_REG_DMA_P_CSC_LUT2;
+
+ for (i = 0; i < MDP_LUT_SIZE; i++) {
+ color = lut->color0_lut[i] & 0xff;
+ color |= (lut->color1_lut[i] & 0xff) << 8;
+ color |= (lut->color2_lut[i] & 0xff) << 16;
+ MDP3_REG_WRITE(addr, color);
+ addr += 4;
+ }
+ }
+
+ cc_config = MDP3_REG_READ(MDP3_REG_DMA_P_COLOR_CORRECT_CONFIG);
+ cc_config &= DMA_LUT_CONFIG_MASK;
+ cc_config |= config->lut_enable;
cc_config |= config->lut_position << 4;
+ cc_config |= config->lut_sel << 10;
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_COLOR_CORRECT_CONFIG, cc_config);
+ wmb();
+
+ dma->lut_config = *config;
+ return 0;
+}
+
+static int mdp3_dmap_ccs_config(struct mdp3_dma *dma,
+ struct mdp3_dma_color_correct_config *config,
+ struct mdp3_dma_ccs *ccs)
+{
+ int i;
+ u32 cc_config, addr;
+
+ cc_config = MDP3_REG_READ(MDP3_REG_DMA_P_COLOR_CORRECT_CONFIG);
+ cc_config &= DMA_CCS_CONFIG_MASK;
+ cc_config |= BIT(3);
cc_config |= config->ccs_sel << 5;
cc_config |= config->pre_bias_sel << 6;
cc_config |= config->post_bias_sel << 7;
cc_config |= config->pre_limit_sel << 8;
cc_config |= config->post_limit_sel << 9;
- cc_config |= config->lut_sel << 10;
MDP3_REG_WRITE(MDP3_REG_DMA_P_COLOR_CORRECT_CONFIG, cc_config);
@@ -394,30 +480,6 @@
}
}
- if (config->lut_enable && lut) {
- if (lut->color0_lut1 && lut->color1_lut1 && lut->color2_lut1) {
- addr = MDP3_REG_DMA_P_CSC_LUT1;
- for (i = 0; i < 256; i++) {
- color = lut->color0_lut1[i];
- color |= lut->color1_lut1[i] << 8;
- color |= lut->color2_lut1[i] << 16;
- MDP3_REG_WRITE(addr, color);
- addr += 4;
- }
- }
-
- if (lut->color0_lut2 && lut->color1_lut2 && lut->color2_lut2) {
- addr = MDP3_REG_DMA_P_CSC_LUT2;
- for (i = 0; i < 256; i++) {
- color = lut->color0_lut2[i];
- color |= lut->color1_lut2[i] << 8;
- color |= lut->color2_lut2[i] << 16;
- MDP3_REG_WRITE(addr, color);
- addr += 4;
- }
- }
- }
-
dma->ccs_config = *config;
return 0;
}
@@ -425,18 +487,28 @@
static int mdp3_dmap_histo_config(struct mdp3_dma *dma,
struct mdp3_dma_histogram_config *histo_config)
{
- u32 hist_bit_mask, hist_control;
+ unsigned long flag;
+ u32 histo_bit_mask, histo_control;
+ u32 histo_isr_mask = MDP3_DMA_P_HIST_INTR_HIST_DONE_BIT |
+ MDP3_DMA_P_HIST_INTR_RESET_DONE_BIT;
+
+ spin_lock_irqsave(&dma->histo_lock, flag);
if (histo_config->bit_mask_polarity)
- hist_bit_mask = BIT(31);
- hist_bit_mask |= histo_config->bit_mask;
+ histo_bit_mask = BIT(31);
+ histo_bit_mask |= histo_config->bit_mask;
if (histo_config->auto_clear_en)
- hist_control = BIT(0);
+ histo_control = BIT(0);
MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_FRAME_CNT,
histo_config->frame_count);
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_BIT_MASK, hist_bit_mask);
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_CONTROL, hist_control);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_BIT_MASK, histo_bit_mask);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_CONTROL, histo_control);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_INTR_ENABLE, histo_isr_mask);
+
+ spin_unlock_irqrestore(&dma->histo_lock, flag);
+
+ dma->histogram_config = *histo_config;
return 0;
}
@@ -518,78 +590,174 @@
return 0;
}
-static int mdp3_dmap_histo_get(struct mdp3_dma *dma,
- struct mdp3_dma_histogram_data *data)
+static int mdp3_dmap_histo_get(struct mdp3_dma *dma)
{
- int i;
- u32 addr, extra;
+ int i, state, timeout, ret;
+ u32 addr;
+ unsigned long flag;
+
+ spin_lock_irqsave(&dma->histo_lock, flag);
+ state = dma->histo_state;
+ spin_unlock_irqrestore(&dma->histo_lock, flag);
+
+ if (state != MDP3_DMA_HISTO_STATE_START &&
+ state != MDP3_DMA_HISTO_STATE_READY) {
+ pr_err("mdp3_dmap_histo_get invalid state %d\n", state);
+ return -EINVAL;
+ }
+
+ timeout = HIST_WAIT_TIMEOUT(dma->histogram_config.frame_count);
+ ret = wait_for_completion_killable_timeout(&dma->histo_comp, timeout);
+
+ if (ret == 0) {
+ pr_err("mdp3_dmap_histo_get time out\n");
+ ret = -ETIMEDOUT;
+ } else if (ret < 0) {
+ pr_err("mdp3_dmap_histo_get interrupted\n");
+ }
+
+ if (ret < 0)
+ return ret;
+
+ if (dma->histo_state != MDP3_DMA_HISTO_STATE_READY) {
+ pr_err("mdp3_dmap_histo_get after dma shut down\n");
+ return -EPERM;
+ }
addr = MDP3_REG_DMA_P_HIST_R_DATA;
- for (i = 0; i < 32; i++) {
- data->r_data[i] = MDP3_REG_READ(addr);
+ for (i = 0; i < MDP_HISTOGRAM_BIN_NUM; i++) {
+ dma->histo_data.r_data[i] = MDP3_REG_READ(addr);
addr += 4;
}
addr = MDP3_REG_DMA_P_HIST_G_DATA;
- for (i = 0; i < 32; i++) {
- data->g_data[i] = MDP3_REG_READ(addr);
+ for (i = 0; i < MDP_HISTOGRAM_BIN_NUM; i++) {
+ dma->histo_data.g_data[i] = MDP3_REG_READ(addr);
addr += 4;
}
addr = MDP3_REG_DMA_P_HIST_B_DATA;
- for (i = 0; i < 32; i++) {
- data->b_data[i] = MDP3_REG_READ(addr);
+ for (i = 0; i < MDP_HISTOGRAM_BIN_NUM; i++) {
+ dma->histo_data.b_data[i] = MDP3_REG_READ(addr);
addr += 4;
}
- extra = MDP3_REG_READ(MDP3_REG_DMA_P_HIST_EXTRA_INFO_0);
- data->r_min_value = (extra & 0x1F0000) >> 16;
- data->r_max_value = (extra & 0x1F000000) >> 24;
- extra = MDP3_REG_READ(MDP3_REG_DMA_P_HIST_EXTRA_INFO_1);
- data->g_min_value = extra & 0x1F;
- data->g_max_value = (extra & 0x1F00) >> 8;
- data->b_min_value = (extra & 0x1F0000) >> 16;
- data->b_max_value = (extra & 0x1F000000) >> 24;
+ dma->histo_data.extra[0] =
+ MDP3_REG_READ(MDP3_REG_DMA_P_HIST_EXTRA_INFO_0);
+ dma->histo_data.extra[1] =
+ MDP3_REG_READ(MDP3_REG_DMA_P_HIST_EXTRA_INFO_1);
+
+ spin_lock_irqsave(&dma->histo_lock, flag);
+ init_completion(&dma->histo_comp);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_START, 1);
+ wmb();
+ dma->histo_state = MDP3_DMA_HISTO_STATE_START;
+ spin_unlock_irqrestore(&dma->histo_lock, flag);
+
+ return 0;
+}
+
+static int mdp3_dmap_histo_start(struct mdp3_dma *dma)
+{
+ unsigned long flag;
+
+ if (dma->histo_state != MDP3_DMA_HISTO_STATE_IDLE)
+ return -EINVAL;
+
+ spin_lock_irqsave(&dma->histo_lock, flag);
+
+ init_completion(&dma->histo_comp);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_START, 1);
+ wmb();
+ dma->histo_state = MDP3_DMA_HISTO_STATE_START;
+
+ spin_unlock_irqrestore(&dma->histo_lock, flag);
+
+ mdp3_dma_callback_enable(dma, MDP3_DMA_CALLBACK_TYPE_HIST_DONE);
+ return 0;
+
+}
+
+static int mdp3_dmap_histo_reset(struct mdp3_dma *dma)
+{
+ unsigned long flag;
+ int ret;
+ u32 cgc;
+
+ if (dma->histo_state == MDP3_DMA_HISTO_STATE_START)
+ return -EINVAL;
+
+ spin_lock_irqsave(&dma->histo_lock, flag);
+
+ init_completion(&dma->histo_comp);
+ cgc = MDP3_REG_READ(MDP3_REG_CGC_EN);
+ cgc &= ~BIT(10);
+ MDP3_REG_WRITE(MDP3_REG_CGC_EN, cgc);
+
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_INTR_ENABLE, BIT(0)|BIT(1));
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_RESET_SEQ_START, 1);
+ wmb();
+ dma->histo_state = MDP3_DMA_HISTO_STATE_RESET;
+
+ spin_unlock_irqrestore(&dma->histo_lock, flag);
+
+ mdp3_dma_callback_enable(dma, MDP3_DMA_CALLBACK_TYPE_HIST_RESET_DONE);
+ ret = wait_for_completion_killable_timeout(&dma->histo_comp,
+ msecs_to_jiffies(DMA_HISTO_RESET_TIMEOUT_MS));
+
+ if (ret == 0) {
+ pr_err("mdp3_dmap_histo_reset time out\n");
+ ret = -ETIMEDOUT;
+ } else if (ret < 0) {
+ pr_err("mdp3_dmap_histo_reset interrupted\n");
+ } else {
+ ret = 0;
+ }
+ mdp3_dma_callback_disable(dma, MDP3_DMA_CALLBACK_TYPE_HIST_RESET_DONE);
+ cgc |= BIT(10);
+ MDP3_REG_WRITE(MDP3_REG_CGC_EN, cgc);
+
+ return ret;
+}
+
+static int mdp3_dmap_histo_stop(struct mdp3_dma *dma)
+{
+ unsigned long flag;
+ int cb_type = MDP3_DMA_CALLBACK_TYPE_HIST_RESET_DONE |
+ MDP3_DMA_CALLBACK_TYPE_HIST_DONE;
+
+ spin_lock_irqsave(&dma->histo_lock, flag);
+
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_CANCEL_REQ, 1);
+ MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_INTR_ENABLE, 0);
+ wmb();
+ dma->histo_state = MDP3_DMA_HISTO_STATE_IDLE;
+
+ spin_unlock_irqrestore(&dma->histo_lock, flag);
+
+ mdp3_dma_callback_disable(dma, cb_type);
return 0;
}
static int mdp3_dmap_histo_op(struct mdp3_dma *dma, u32 op)
{
+ int ret;
+
switch (op) {
case MDP3_DMA_HISTO_OP_START:
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_START, 1);
+ ret = mdp3_dmap_histo_start(dma);
break;
case MDP3_DMA_HISTO_OP_STOP:
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_STOP_REQ, 1);
- break;
case MDP3_DMA_HISTO_OP_CANCEL:
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_CANCEL_REQ, 1);
+ ret = mdp3_dmap_histo_stop(dma);
break;
case MDP3_DMA_HISTO_OP_RESET:
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_RESET_SEQ_START, 1);
+ ret = mdp3_dmap_histo_reset(dma);
break;
default:
- return -EINVAL;
+ ret = -EINVAL;
}
- return 0;
-}
-
-static int mdp3_dmap_histo_intr_status(struct mdp3_dma *dma, int *status)
-{
- *status = MDP3_REG_READ(MDP3_REG_DMA_P_HIST_INTR_STATUS);
- return 0;
-}
-
-static int mdp3_dmap_histo_intr_enable(struct mdp3_dma *dma, u32 mask)
-{
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_INTR_ENABLE, mask);
- return 0;
-}
-
-static int mdp3_dmap_histo_intr_clear(struct mdp3_dma *dma, u32 mask)
-{
- MDP3_REG_WRITE(MDP3_REG_DMA_P_HIST_INTR_CLEAR, mask);
- return 0;
+ return ret;
}
static int mdp3_dma_start(struct mdp3_dma *dma, struct mdp3_intf *intf)
@@ -648,6 +816,7 @@
mdp3_dma_callback_disable(dma, MDP3_DMA_CALLBACK_TYPE_VSYNC |
MDP3_DMA_CALLBACK_TYPE_DMA_DONE);
+ init_completion(&dma->dma_comp);
return ret;
}
@@ -667,13 +836,11 @@
dma->config_cursor = mdp3_dmap_cursor_config;
dma->config_ccs = mdp3_dmap_ccs_config;
dma->config_histo = mdp3_dmap_histo_config;
+ dma->config_lut = mdp3_dmap_lut_config;
dma->update = mdp3_dmap_update;
dma->update_cursor = mdp3_dmap_cursor_update;
dma->get_histo = mdp3_dmap_histo_get;
dma->histo_op = mdp3_dmap_histo_op;
- dma->histo_intr_status = mdp3_dmap_histo_intr_status;
- dma->histo_intr_enable = mdp3_dmap_histo_intr_enable;
- dma->histo_intr_clear = mdp3_dmap_histo_intr_clear;
dma->vsync_enable = mdp3_dma_vsync_enable;
dma->start = mdp3_dma_start;
dma->stop = mdp3_dma_stop;
@@ -686,13 +853,11 @@
dma->config_cursor = NULL;
dma->config_ccs = NULL;
dma->config_histo = NULL;
+ dma->config_lut = NULL;
dma->update = mdp3_dmas_update;
dma->update_cursor = NULL;
dma->get_histo = NULL;
dma->histo_op = NULL;
- dma->histo_intr_status = NULL;
- dma->histo_intr_enable = NULL;
- dma->histo_intr_clear = NULL;
dma->vsync_enable = mdp3_dma_vsync_enable;
dma->start = mdp3_dma_start;
dma->stop = mdp3_dma_stop;
@@ -704,10 +869,13 @@
}
spin_lock_init(&dma->dma_lock);
+ spin_lock_init(&dma->histo_lock);
init_completion(&dma->vsync_comp);
init_completion(&dma->dma_comp);
+ init_completion(&dma->histo_comp);
dma->vsync_client.handler = NULL;
dma->vsync_client.arg = NULL;
+ dma->histo_state = MDP3_DMA_HISTO_STATE_IDLE;
memset(&dma->cursor, 0, sizeof(dma->cursor));
memset(&dma->ccs_config, 0, sizeof(dma->ccs_config));
diff --git a/drivers/video/msm/mdss/mdp3_dma.h b/drivers/video/msm/mdss/mdp3_dma.h
index c7b5f25..518bd58 100644
--- a/drivers/video/msm/mdss/mdp3_dma.h
+++ b/drivers/video/msm/mdss/mdp3_dma.h
@@ -16,6 +16,9 @@
#include <linux/sched.h>
+#define MDP_HISTOGRAM_BIN_NUM 32
+#define MDP_LUT_SIZE 256
+
enum {
MDP3_DMA_P,
MDP3_DMA_S,
@@ -119,8 +122,18 @@
};
enum {
+ MDP3_DMA_HISTO_STATE_UNKNOWN,
+ MDP3_DMA_HISTO_STATE_IDLE,
+ MDP3_DMA_HISTO_STATE_RESET,
+ MDP3_DMA_HISTO_STATE_START,
+ MDP3_DMA_HISTO_STATE_READY,
+};
+
+enum {
MDP3_DMA_CALLBACK_TYPE_VSYNC = 0x01,
MDP3_DMA_CALLBACK_TYPE_DMA_DONE = 0x02,
+ MDP3_DMA_CALLBACK_TYPE_HIST_RESET_DONE = 0x04,
+ MDP3_DMA_CALLBACK_TYPE_HIST_DONE = 0x08,
};
struct mdp3_dma_source {
@@ -176,24 +189,24 @@
};
struct mdp3_dma_lut {
- uint8_t *color0_lut1;
- uint8_t *color1_lut1;
- uint8_t *color2_lut1;
- uint8_t *color0_lut2;
- uint8_t *color1_lut2;
- uint8_t *color2_lut2;
+ u16 *color0_lut;
+ u16 *color1_lut;
+ u16 *color2_lut;
+};
+
+struct mdp3_dma_lut_config {
+ int lut_enable;
+ u32 lut_sel;
+ u32 lut_position;
};
struct mdp3_dma_color_correct_config {
int ccs_enable;
- int lut_enable;
- u32 lut_sel;
u32 post_limit_sel;
u32 pre_limit_sel;
u32 post_bias_sel;
u32 pre_bias_sel;
u32 ccs_sel;
- u32 lut_position;
};
struct mdp3_dma_histogram_config {
@@ -204,15 +217,10 @@
};
struct mdp3_dma_histogram_data {
- uint8_t r_max_value;
- uint8_t r_min_value;
- uint8_t b_max_value;
- uint8_t b_min_value;
- uint8_t g_max_value;
- uint8_t g_min_value;
- uint8_t r_data[32];
- uint8_t g_data[32];
- uint8_t b_data[32];
+ u32 r_data[MDP_HISTOGRAM_BIN_NUM];
+ u32 g_data[MDP_HISTOGRAM_BIN_NUM];
+ u32 b_data[MDP_HISTOGRAM_BIN_NUM];
+ u32 extra[2];
};
struct mdp3_vsync_notification {
@@ -229,8 +237,10 @@
int available;
spinlock_t dma_lock;
+ spinlock_t histo_lock;
struct completion vsync_comp;
struct completion dma_comp;
+ struct completion histo_comp;
struct mdp3_vsync_notification vsync_client;
struct mdp3_dma_output_config output_config;
@@ -238,7 +248,10 @@
struct mdp3_dma_cursor cursor;
struct mdp3_dma_color_correct_config ccs_config;
+ struct mdp3_dma_lut_config lut_config;
struct mdp3_dma_histogram_config histogram_config;
+ int histo_state;
+ struct mdp3_dma_histogram_data histo_data;
int (*start)(struct mdp3_dma *dma, struct mdp3_intf *intf);
@@ -249,27 +262,22 @@
int (*config_ccs)(struct mdp3_dma *dma,
struct mdp3_dma_color_correct_config *config,
- struct mdp3_dma_ccs *ccs,
+ struct mdp3_dma_ccs *ccs);
+
+ int (*config_lut)(struct mdp3_dma *dma,
+ struct mdp3_dma_lut_config *config,
struct mdp3_dma_lut *lut);
int (*update)(struct mdp3_dma *dma, void *buf, struct mdp3_intf *intf);
int (*update_cursor)(struct mdp3_dma *dma, int x, int y);
- int (*get_histo)(struct mdp3_dma *dma,
- struct mdp3_dma_histogram_data *data);
+ int (*get_histo)(struct mdp3_dma *dma);
int (*config_histo)(struct mdp3_dma *dma,
struct mdp3_dma_histogram_config *histo_config);
- int (*histo_op)(struct mdp3_dma *dma,
- u32 op);
-
- int (*histo_intr_status)(struct mdp3_dma *dma, int *status);
-
- int (*histo_intr_enable)(struct mdp3_dma *dma, u32 mask);
-
- int (*histo_intr_clear)(struct mdp3_dma *dma, u32 mask);
+ int (*histo_op)(struct mdp3_dma *dma, u32 op);
void (*vsync_enable)(struct mdp3_dma *dma,
struct mdp3_vsync_notification *vsync_client);
diff --git a/drivers/video/msm/mdss/mdp3_hwio.h b/drivers/video/msm/mdss/mdp3_hwio.h
index f10206f..4afa37c 100644
--- a/drivers/video/msm/mdss/mdp3_hwio.h
+++ b/drivers/video/msm/mdss/mdp3_hwio.h
@@ -60,6 +60,9 @@
#define MDP3_REG_EBI2_LCD0 0x003c
#define MDP3_REG_EBI2_LCD0_YSTRIDE 0x0050
+/*clock control*/
+#define MDP3_REG_CGC_EN 0x0100
+
/*DMA_P*/
#define MDP3_REG_DMA_P_CONFIG 0x90000
#define MDP3_REG_DMA_P_SIZE 0x90004
diff --git a/drivers/video/msm/mdss/mdss_dsi_host.c b/drivers/video/msm/mdss/mdss_dsi_host.c
index 72e3c64..60b8b2e 100644
--- a/drivers/video/msm/mdss/mdss_dsi_host.c
+++ b/drivers/video/msm/mdss/mdss_dsi_host.c
@@ -1638,7 +1638,7 @@
if (status) {
MIPI_OUTP(base + 0x0068, status);
- pr_debug("%s: status=%x\n", __func__, status);
+ pr_err("%s: status=%x\n", __func__, status);
}
}
@@ -1649,7 +1649,7 @@
status = MIPI_INP(base + 0x00c0);/* DSI_TIMEOUT_STATUS */
if (status & 0x0111) {
MIPI_OUTP(base + 0x00c0, status);
- pr_debug("%s: status=%x\n", __func__, status);
+ pr_err("%s: status=%x\n", __func__, status);
}
}
@@ -1661,7 +1661,7 @@
if (status & 0x011111) {
MIPI_OUTP(base + 0x00b4, status);
- pr_debug("%s: status=%x\n", __func__, status);
+ pr_err("%s: status=%x\n", __func__, status);
}
}
@@ -1673,7 +1673,7 @@
if (status & 0x44444489) {
MIPI_OUTP(base + 0x000c, status);
- pr_debug("%s: status=%x\n", __func__, status);
+ pr_err("%s: status=%x\n", __func__, status);
}
}
@@ -1685,7 +1685,7 @@
if (status & 0x80000000) {
MIPI_OUTP(base + 0x0008, status);
- pr_debug("%s: status=%x\n", __func__, status);
+ pr_err("%s: status=%x\n", __func__, status);
}
}
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index ab4ae4b..52224e2 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -312,6 +312,8 @@
u8 vert_deci;
struct mdss_mdp_img_rect src;
struct mdss_mdp_img_rect dst;
+ u32 phase_step_x;
+ u32 phase_step_y;
struct mdss_mdp_format_params *src_fmt;
struct mdss_mdp_plane_sizes src_planes;
@@ -545,6 +547,7 @@
int mdss_mdp_put_img(struct mdss_mdp_img_data *data);
int mdss_mdp_get_img(struct msmfb_data *img, struct mdss_mdp_img_data *data);
u32 mdss_get_panel_framerate(struct msm_fb_data_type *mfd);
+int mdss_mdp_calc_phase_step(u32 src, u32 dst, u32 *out_phase);
int mdss_mdp_wb_kickoff(struct msm_fb_data_type *mfd);
int mdss_mdp_wb_ioctl_handler(struct msm_fb_data_type *mfd, u32 cmd, void *arg);
diff --git a/drivers/video/msm/mdss/mdss_mdp_hwio.h b/drivers/video/msm/mdss/mdss_mdp_hwio.h
index 741c7a7..4779d20 100644
--- a/drivers/video/msm/mdss/mdss_mdp_hwio.h
+++ b/drivers/video/msm/mdss/mdss_mdp_hwio.h
@@ -340,6 +340,7 @@
#define MDSS_MDP_REG_WB_DST_MATRIX_ROW1 0x034
#define MDSS_MDP_REG_WB_DST_MATRIX_ROW2 0x038
#define MDSS_MDP_REG_WB_DST_MATRIX_ROW3 0x03C
+#define MDSS_MDP_REG_WB_DST_WRITE_CONFIG 0x048
#define MDSS_MDP_REG_WB_ROTATION_DNSCALER 0x050
#define MDSS_MDP_REG_WB_N16_INIT_PHASE_X_C03 0x060
#define MDSS_MDP_REG_WB_N16_INIT_PHASE_X_C12 0x064
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
index 503d8e2..b7fe1bd 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_writeback.c
@@ -205,6 +205,7 @@
mdp_wb_write(ctx, MDSS_MDP_REG_WB_DST_YSTRIDE0, ystride0);
mdp_wb_write(ctx, MDSS_MDP_REG_WB_DST_YSTRIDE1, ystride1);
mdp_wb_write(ctx, MDSS_MDP_REG_WB_OUT_SIZE, outsize);
+ mdp_wb_write(ctx, MDSS_MDP_REG_WB_DST_WRITE_CONFIG, 0x58);
return 0;
}
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index 43de734..a78a10a 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -180,6 +180,11 @@
pr_err("BWC: unequal src img and rect w,h\n");
return -EINVAL;
}
+
+ if (req->flags & MDP_DECIMATION_EN) {
+ pr_err("Can't enable BWC decode && decimate\n");
+ return -EINVAL;
+ }
}
if (req->flags & MDP_DEINTERLACE) {
@@ -315,6 +320,30 @@
return 0;
}
+static int __mdss_mdp_overlay_setup_scaling(struct mdss_mdp_pipe *pipe)
+{
+ u32 src;
+ int rc;
+
+ src = pipe->src.w >> pipe->horz_deci;
+ rc = mdss_mdp_calc_phase_step(src, pipe->dst.w, &pipe->phase_step_x);
+ if (rc) {
+ pr_err("Horizontal scaling calculation failed=%d! %d->%d\n",
+ rc, src, pipe->dst.w);
+ return rc;
+ }
+
+ src = pipe->src.h >> pipe->vert_deci;
+ rc = mdss_mdp_calc_phase_step(src, pipe->dst.h, &pipe->phase_step_y);
+ if (rc) {
+ pr_err("Vertical scaling calculation failed=%d! %d->%d\n",
+ rc, src, pipe->dst.h);
+ return rc;
+ }
+
+ return 0;
+}
+
static int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd,
struct mdp_overlay *req,
struct mdss_mdp_pipe **ppipe)
@@ -535,6 +564,10 @@
goto exit_fail;
}
+ ret = __mdss_mdp_overlay_setup_scaling(pipe);
+ if (ret)
+ goto exit_fail;
+
ret = mdss_mdp_smp_reserve(pipe);
if (ret) {
pr_debug("mdss_mdp_smp_reserve failed. ret=%d\n", ret);
@@ -833,9 +866,14 @@
static int mdss_mdp_overlay_unset(struct msm_fb_data_type *mfd, int ndx)
{
int ret = 0;
- struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+ struct mdss_overlay_private *mdp5_data;
- if (!mfd || !mdp5_data->ctl)
+ if (!mfd)
+ return -ENODEV;
+
+ mdp5_data = mfd_to_mdp5_data(mfd);
+
+ if (!mdp5_data || !mdp5_data->ctl)
return -ENODEV;
ret = mutex_lock_interruptible(&mdp5_data->ov_lock);
@@ -1158,14 +1196,18 @@
struct mdss_mdp_data data;
struct mdss_mdp_pipe *pipe;
struct fb_info *fbi;
- struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
+ struct mdss_overlay_private *mdp5_data;
u32 offset;
int bpp, ret;
- if (!mfd || !mdp5_data->ctl)
+ if (!mfd)
return;
fbi = mfd->fbi;
+ mdp5_data = mfd_to_mdp5_data(mfd);
+
+ if (!mdp5_data || !mdp5_data->ctl)
+ return;
if (!fbi->fix.smem_start || fbi->fix.smem_len == 0 ||
mdp5_data->borderfill_enable) {
@@ -1848,14 +1890,17 @@
static int mdss_mdp_overlay_on(struct msm_fb_data_type *mfd)
{
int rc;
- struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
-
+ struct mdss_overlay_private *mdp5_data;
if (!mfd)
return -ENODEV;
if (mfd->key != MFD_KEY)
return -EINVAL;
+ mdp5_data = mfd_to_mdp5_data(mfd);
+ if (!mdp5_data)
+ return -EINVAL;
+
if (!mdp5_data->ctl) {
struct mdss_mdp_ctl *ctl;
struct mdss_panel_data *pdata;
@@ -1910,15 +1955,16 @@
static int mdss_mdp_overlay_off(struct msm_fb_data_type *mfd)
{
int rc;
- struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
-
+ struct mdss_overlay_private *mdp5_data;
if (!mfd)
return -ENODEV;
if (mfd->key != MFD_KEY)
return -EINVAL;
- if (!mdp5_data->ctl) {
+ mdp5_data = mfd_to_mdp5_data(mfd);
+
+ if (!mdp5_data || !mdp5_data->ctl) {
pr_err("ctl not initialized\n");
return -ENODEV;
}
@@ -1926,6 +1972,7 @@
if (!mdp5_data->ctl->power_on)
return 0;
+ mdss_mdp_overlay_free_fb_pipe(mfd);
if (!mfd->ref_cnt) {
mdss_mdp_overlay_release_all(mfd);
} else {
diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c
index 963d3fb..75b6056 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pp.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pp.c
@@ -607,39 +607,6 @@
return 0;
}
-static int mdss_mdp_leading_zero(u32 num)
-{
- u32 bit = 0x80000000;
- int i;
-
- for (i = 0; i < 32; i++) {
- if (bit & num)
- return i;
- bit >>= 1;
- }
-
- return i;
-}
-
-static u32 mdss_mdp_scale_phase_step(int f_num, u32 src, u32 dst)
-{
- u32 val, s;
- int n;
-
- n = mdss_mdp_leading_zero(src);
- if (n > f_num)
- n = f_num;
- s = src << n; /* maximum to reduce lose of resolution */
- val = s / dst;
- if (n < f_num) {
- n = f_num - n;
- val <<= n;
- val |= ((s % dst) << n) / dst;
- }
-
- return val;
-}
-
static int mdss_mdp_scale_setup(struct mdss_mdp_pipe *pipe)
{
u32 scale_config = 0;
@@ -705,13 +672,14 @@
}
scale_config |= MDSS_MDP_SCALEY_EN;
+ phasey_step = pipe->phase_step_y;
if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG) {
- u32 chr_dst_h = pipe->dst.h;
+ u32 chroma_shift = 0;
if (!pipe->vert_deci &&
((chroma_sample == MDSS_MDP_CHROMA_420) ||
(chroma_sample == MDSS_MDP_CHROMA_H1V2)))
- chr_dst_h *= 2; /* 2x upsample chroma */
+ chroma_shift = 1; /* 2x upsample chroma */
if (src_h <= pipe->dst.h) {
scale_config |= /* G/Y, A */
@@ -722,17 +690,14 @@
(MDSS_MDP_SCALE_FILTER_PCMN << 10) |
(MDSS_MDP_SCALE_FILTER_PCMN << 18);
- if (src_h <= chr_dst_h)
+ if ((src_h >> chroma_shift) <= pipe->dst.h)
scale_config |= /* CrCb */
(MDSS_MDP_SCALE_FILTER_BIL << 14);
else
scale_config |= /* CrCb */
(MDSS_MDP_SCALE_FILTER_PCMN << 14);
- phasey_step = mdss_mdp_scale_phase_step(
- PHASE_STEP_SHIFT, src_h, chr_dst_h);
-
- writel_relaxed(phasey_step, pipe->base +
+ writel_relaxed(phasey_step >> chroma_shift, pipe->base +
MDSS_MDP_REG_VIG_QSEED2_C12_PHASESTEPY);
} else {
if (src_h <= pipe->dst.h)
@@ -744,9 +709,6 @@
(MDSS_MDP_SCALE_FILTER_PCMN << 10) |
(MDSS_MDP_SCALE_FILTER_PCMN << 18);
}
-
- phasey_step = mdss_mdp_scale_phase_step(
- PHASE_STEP_SHIFT, src_h, pipe->dst.h);
}
if ((src_w != pipe->dst.w) ||
@@ -762,14 +724,15 @@
}
scale_config |= MDSS_MDP_SCALEX_EN;
+ phasex_step = pipe->phase_step_x;
if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG) {
- u32 chr_dst_w = pipe->dst.w;
+ u32 chroma_shift = 0;
if (!pipe->horz_deci &&
((chroma_sample == MDSS_MDP_CHROMA_420) ||
(chroma_sample == MDSS_MDP_CHROMA_H2V1)))
- chr_dst_w *= 2; /* 2x upsample chroma */
+ chroma_shift = 1; /* 2x upsample chroma */
if (src_w <= pipe->dst.w) {
scale_config |= /* G/Y, A */
@@ -780,16 +743,14 @@
(MDSS_MDP_SCALE_FILTER_PCMN << 8) |
(MDSS_MDP_SCALE_FILTER_PCMN << 16);
- if (src_w <= chr_dst_w)
+ if ((src_w >> chroma_shift) <= pipe->dst.w)
scale_config |= /* CrCb */
(MDSS_MDP_SCALE_FILTER_BIL << 12);
else
scale_config |= /* CrCb */
(MDSS_MDP_SCALE_FILTER_PCMN << 12);
- phasex_step = mdss_mdp_scale_phase_step(
- PHASE_STEP_SHIFT, src_w, chr_dst_w);
- writel_relaxed(phasex_step, pipe->base +
+ writel_relaxed(phasex_step >> chroma_shift, pipe->base +
MDSS_MDP_REG_VIG_QSEED2_C12_PHASESTEPX);
} else {
if (src_w <= pipe->dst.w)
@@ -801,9 +762,6 @@
(MDSS_MDP_SCALE_FILTER_PCMN << 8) |
(MDSS_MDP_SCALE_FILTER_PCMN << 16);
}
-
- phasex_step = mdss_mdp_scale_phase_step(
- PHASE_STEP_SHIFT, src_w, pipe->dst.w);
}
writel_relaxed(scale_config, pipe->base +
diff --git a/drivers/video/msm/mdss/mdss_mdp_util.c b/drivers/video/msm/mdss/mdss_mdp_util.c
index b29b0eb..c7beb0e 100644
--- a/drivers/video/msm/mdss/mdss_mdp_util.c
+++ b/drivers/video/msm/mdss/mdss_mdp_util.c
@@ -561,3 +561,23 @@
return ret;
}
+
+int mdss_mdp_calc_phase_step(u32 src, u32 dst, u32 *out_phase)
+{
+ u32 unit, residue;
+
+ if (dst == 0)
+ return -EINVAL;
+
+ unit = 1 << PHASE_STEP_SHIFT;
+ *out_phase = mult_frac(src, unit, dst);
+
+ /* check if overflow is possible */
+ if (src > dst) {
+ residue = *out_phase & (unit - 1);
+ if (residue && ((residue * dst) < (unit - residue)))
+ return -EOVERFLOW;
+ }
+
+ return 0;
+}
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 5f52690..03efb50 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -5,6 +5,7 @@
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/of.h>
+#include <linux/pinctrl/pinctrl.h>
#ifdef CONFIG_GPIOLIB
@@ -47,6 +48,26 @@
struct module;
struct device_node;
+#ifdef CONFIG_PINCTRL
+/**
+ * struct gpio_pin_range - pin range controlled by a gpio chip
+ * @head: list for maintaining set of pin ranges, used internally
+ * @pctldev: pinctrl device which handles corresponding pins
+ * @range: actual range of pins controlled by a gpio controller
+ */
+
+struct gpio_pin_range {
+ struct list_head node;
+ struct pinctrl_dev *pctldev;
+ struct pinctrl_gpio_range range;
+};
+
+int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
+ unsigned int pin_base, unsigned int npins);
+void gpiochip_remove_pin_ranges(struct gpio_chip *chip);
+
+#endif
+
/**
* struct gpio_chip - abstract a GPIO controller
* @label: for diagnostics
@@ -132,6 +153,15 @@
int (*of_xlate)(struct gpio_chip *gc,
const struct of_phandle_args *gpiospec, u32 *flags);
#endif
+#ifdef CONFIG_PINCTRL
+ /*
+ * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally
+ * describe the actual pin range which they serve in an SoC. This
+ * information would be used by pinctrl subsystem to configure
+ * corresponding pins for gpio usage.
+ */
+ struct list_head pin_ranges;
+#endif
};
extern const char *gpiochip_is_requested(struct gpio_chip *chip,
diff --git a/include/linux/compaction.h b/include/linux/compaction.h
index 51a90b7..ee1c2ff 100644
--- a/include/linux/compaction.h
+++ b/include/linux/compaction.h
@@ -22,8 +22,9 @@
extern int fragmentation_index(struct zone *zone, unsigned int order);
extern unsigned long try_to_compact_pages(struct zonelist *zonelist,
int order, gfp_t gfp_mask, nodemask_t *mask,
- bool sync);
+ bool sync, bool *contended);
extern int compact_pgdat(pg_data_t *pgdat, int order);
+extern void reset_isolation_suitable(pg_data_t *pgdat);
extern unsigned long compaction_suitable(struct zone *zone, int order);
/* Do not skip compaction more than 64 times */
@@ -61,10 +62,20 @@
return zone->compact_considered < (1UL << zone->compact_defer_shift);
}
+/* Returns true if restarting compaction after many failures */
+static inline bool compaction_restarting(struct zone *zone, int order)
+{
+ if (order < zone->compact_order_failed)
+ return false;
+
+ return zone->compact_defer_shift == COMPACT_MAX_DEFER_SHIFT &&
+ zone->compact_considered >= 1UL << zone->compact_defer_shift;
+}
+
#else
static inline unsigned long try_to_compact_pages(struct zonelist *zonelist,
int order, gfp_t gfp_mask, nodemask_t *nodemask,
- bool sync)
+ bool sync, bool *contended)
{
return COMPACT_CONTINUE;
}
@@ -74,6 +85,10 @@
return COMPACT_CONTINUE;
}
+static inline void reset_isolation_suitable(pg_data_t *pgdat)
+{
+}
+
static inline unsigned long compaction_suitable(struct zone *zone, int order)
{
return COMPACT_SKIPPED;
diff --git a/include/linux/device.h b/include/linux/device.h
index 810337c..35862c3 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -21,6 +21,7 @@
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/mutex.h>
+#include <linux/pinctrl/devinfo.h>
#include <linux/pm.h>
#include <linux/atomic.h>
#include <asm/device.h>
@@ -595,6 +596,8 @@
* @pm_domain: Provide callbacks that are executed during system suspend,
* hibernation, system resume and during runtime PM transitions
* along with subsystem-level and driver-level callbacks.
+ * @pins: For device pin management.
+ * See Documentation/pinctrl.txt for details.
* @numa_node: NUMA node this device is close to.
* @dma_mask: Dma mask (if dma'ble device).
* @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all
@@ -646,6 +649,10 @@
struct dev_pm_info power;
struct dev_pm_domain *pm_domain;
+#ifdef CONFIG_PINCTRL
+ struct dev_pin_info *pins;
+#endif
+
#ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
#endif
diff --git a/include/linux/gpio.h b/include/linux/gpio.h
index 6155ecf..eda50bd 100644
--- a/include/linux/gpio.h
+++ b/include/linux/gpio.h
@@ -172,6 +172,21 @@
return -EINVAL;
}
-#endif
+#ifdef CONFIG_PINCTRL
+
+static inline int
+gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
+ unsigned int pin_base, unsigned int npins)
+{
+}
+
+static inline void
+gpiochip_remove_pin_ranges(struct gpio_chip *chip)
+{
+}
+
+#endif /* CONFIG_PINCTRL */
+
+#endif /* ! CONFIG_GENERIC_GPIO */
#endif /* __LINUX_GPIO_H */
diff --git a/include/linux/input/lis3dh.h b/include/linux/input/lis3dh.h
index f081b06..7c0172f 100644
--- a/include/linux/input/lis3dh.h
+++ b/include/linux/input/lis3dh.h
@@ -37,7 +37,7 @@
#define LIS3DH_ACC_I2C_SAD_L ((LIS3DH_ACC_I2C_SADROOT<<1)|SAD0L)
#define LIS3DH_ACC_I2C_SAD_H ((LIS3DH_ACC_I2C_SADROOT<<1)|SAD0H)
#define LIS3DH_ACC_DEV_NAME "lis3dh_acc"
-
+#define ACCEL_INPUT_DEV_NAME "accelerometer"
/************************************************/
/* Accelerometer defines section */
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index f94efd2..14d38a5 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -66,7 +66,6 @@
struct lruvec *mem_cgroup_lru_add_list(struct zone *, struct page *,
enum lru_list);
void mem_cgroup_lru_del_list(struct page *, enum lru_list);
-void mem_cgroup_lru_del(struct page *);
struct lruvec *mem_cgroup_lru_move_lists(struct zone *, struct page *,
enum lru_list, enum lru_list);
@@ -79,6 +78,8 @@
extern void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
int order);
+bool __mem_cgroup_same_or_subtree(const struct mem_cgroup *root_memcg,
+ struct mem_cgroup *memcg);
int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *memcg);
extern struct mem_cgroup *try_get_mem_cgroup_from_page(struct page *page);
@@ -92,10 +93,13 @@
int mm_match_cgroup(const struct mm_struct *mm, const struct mem_cgroup *cgroup)
{
struct mem_cgroup *memcg;
+ int match;
+
rcu_read_lock();
memcg = mem_cgroup_from_task(rcu_dereference((mm)->owner));
+ match = __mem_cgroup_same_or_subtree(cgroup, memcg);
rcu_read_unlock();
- return cgroup == memcg;
+ return match;
}
extern struct cgroup_subsys_state *mem_cgroup_css(struct mem_cgroup *memcg);
@@ -262,10 +266,6 @@
{
}
-static inline void mem_cgroup_lru_del(struct page *page)
-{
-}
-
static inline struct lruvec *mem_cgroup_lru_move_lists(struct zone *zone,
struct page *page,
enum lru_list from,
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 3cc3062..b98b4b9 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -344,17 +344,6 @@
/* Architecture-specific MM context */
mm_context_t context;
- /* Swap token stuff */
- /*
- * Last value of global fault stamp as seen by this process.
- * In other words, this value gives an indication of how long
- * it has been since this task got the token.
- * Look at mm/thrash.c
- */
- unsigned int faultstamp;
- unsigned int token_priority;
- unsigned int last_interval;
-
unsigned long flags; /* Must use atomic bitops to access the bits */
struct core_state *core_state; /* coredumping support */
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index d63232a..7539e03 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -204,16 +204,14 @@
#define LRU_ALL_EVICTABLE (LRU_ALL_FILE | LRU_ALL_ANON)
#define LRU_ALL ((1 << NR_LRU_LISTS) - 1)
-/* Isolate inactive pages */
-#define ISOLATE_INACTIVE ((__force isolate_mode_t)0x1)
-/* Isolate active pages */
-#define ISOLATE_ACTIVE ((__force isolate_mode_t)0x2)
/* Isolate clean file */
-#define ISOLATE_CLEAN ((__force isolate_mode_t)0x4)
+#define ISOLATE_CLEAN ((__force isolate_mode_t)0x1)
/* Isolate unmapped file */
-#define ISOLATE_UNMAPPED ((__force isolate_mode_t)0x8)
+#define ISOLATE_UNMAPPED ((__force isolate_mode_t)0x2)
/* Isolate for asynchronous migration */
-#define ISOLATE_ASYNC_MIGRATE ((__force isolate_mode_t)0x10)
+#define ISOLATE_ASYNC_MIGRATE ((__force isolate_mode_t)0x4)
+/* Isolate unevictable pages */
+#define ISOLATE_UNEVICTABLE ((__force isolate_mode_t)0x8)
/* LRU Isolation modes. */
typedef unsigned __bitwise__ isolate_mode_t;
@@ -378,6 +376,14 @@
*/
spinlock_t lock;
int all_unreclaimable; /* All pages pinned */
+#if defined CONFIG_COMPACTION || defined CONFIG_CMA
+ /* Set to true when the PG_migrate_skip bits should be cleared */
+ bool compact_blockskip_flush;
+
+ /* pfns where compaction scanners should start */
+ unsigned long compact_cached_free_pfn;
+ unsigned long compact_cached_migrate_pfn;
+#endif
#ifdef CONFIG_MEMORY_HOTPLUG
/* see spanned/present_pages for more description */
seqlock_t span_seqlock;
diff --git a/include/linux/msm_ipa.h b/include/linux/msm_ipa.h
index 5151654..b2229d3 100644
--- a/include/linux/msm_ipa.h
+++ b/include/linux/msm_ipa.h
@@ -87,6 +87,7 @@
#define IPA_FLT_NEXT_HDR (1ul << 13)
#define IPA_FLT_META_DATA (1ul << 14)
#define IPA_FLT_FRAGMENT (1ul << 15)
+#define IPA_FLT_TOS_MASKED (1ul << 16)
/**
* enum ipa_client_type - names for the various IPA "clients"
@@ -243,6 +244,8 @@
uint16_t dst_port_hi;
uint8_t type;
uint8_t code;
+ uint8_t tos_value;
+ uint8_t tos_mask;
uint32_t spi;
uint16_t src_port;
uint16_t dst_port;
diff --git a/include/linux/pageblock-flags.h b/include/linux/pageblock-flags.h
index 19ef95d..eed27f4 100644
--- a/include/linux/pageblock-flags.h
+++ b/include/linux/pageblock-flags.h
@@ -30,6 +30,9 @@
PB_migrate,
PB_migrate_end = PB_migrate + 3 - 1,
/* 3 bits required for migrate types */
+#ifdef CONFIG_COMPACTION
+ PB_migrate_skip,/* If set the block is skipped by compaction */
+#endif /* CONFIG_COMPACTION */
NR_PAGEBLOCK_BITS
};
@@ -65,10 +68,22 @@
void set_pageblock_flags_group(struct page *page, unsigned long flags,
int start_bitidx, int end_bitidx);
+#ifdef CONFIG_COMPACTION
+#define get_pageblock_skip(page) \
+ get_pageblock_flags_group(page, PB_migrate_skip, \
+ PB_migrate_skip + 1)
+#define clear_pageblock_skip(page) \
+ set_pageblock_flags_group(page, 0, PB_migrate_skip, \
+ PB_migrate_skip + 1)
+#define set_pageblock_skip(page) \
+ set_pageblock_flags_group(page, 1, PB_migrate_skip, \
+ PB_migrate_skip + 1)
+#endif /* CONFIG_COMPACTION */
+
#define get_pageblock_flags(page) \
- get_pageblock_flags_group(page, 0, NR_PAGEBLOCK_BITS-1)
+ get_pageblock_flags_group(page, 0, PB_migrate_end)
#define set_pageblock_flags(page, flags) \
set_pageblock_flags_group(page, flags, \
- 0, NR_PAGEBLOCK_BITS-1)
+ 0, PB_migrate_end)
#endif /* PAGEBLOCK_FLAGS_H */
diff --git a/include/linux/pinctrl/consumer.h b/include/linux/pinctrl/consumer.h
index 191e726..d281645 100644
--- a/include/linux/pinctrl/consumer.h
+++ b/include/linux/pinctrl/consumer.h
@@ -20,6 +20,7 @@
/* This struct is private to the core and should be regarded as a cookie */
struct pinctrl;
struct pinctrl_state;
+struct device;
#ifdef CONFIG_PINCTRL
@@ -36,6 +37,9 @@
const char *name);
extern int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *s);
+extern struct pinctrl * __must_check devm_pinctrl_get(struct device *dev);
+extern void devm_pinctrl_put(struct pinctrl *p);
+
#else /* !CONFIG_PINCTRL */
static inline int pinctrl_request_gpio(unsigned gpio)
@@ -79,6 +83,15 @@
return 0;
}
+static inline struct pinctrl * __must_check devm_pinctrl_get(struct device *dev)
+{
+ return NULL;
+}
+
+static inline void devm_pinctrl_put(struct pinctrl *p)
+{
+}
+
#endif /* CONFIG_PINCTRL */
static inline struct pinctrl * __must_check pinctrl_get_select(
@@ -113,6 +126,38 @@
return pinctrl_get_select(dev, PINCTRL_STATE_DEFAULT);
}
+static inline struct pinctrl * __must_check devm_pinctrl_get_select(
+ struct device *dev, const char *name)
+{
+ struct pinctrl *p;
+ struct pinctrl_state *s;
+ int ret;
+
+ p = devm_pinctrl_get(dev);
+ if (IS_ERR(p))
+ return p;
+
+ s = pinctrl_lookup_state(p, name);
+ if (IS_ERR(s)) {
+ devm_pinctrl_put(p);
+ return ERR_CAST(s);
+ }
+
+ ret = pinctrl_select_state(p, s);
+ if (ret < 0) {
+ devm_pinctrl_put(p);
+ return ERR_PTR(ret);
+ }
+
+ return p;
+}
+
+static inline struct pinctrl * __must_check devm_pinctrl_get_select_default(
+ struct device *dev)
+{
+ return devm_pinctrl_get_select(dev, PINCTRL_STATE_DEFAULT);
+}
+
#ifdef CONFIG_PINCONF
extern int pin_config_get(const char *dev_name, const char *name,
diff --git a/include/linux/pinctrl/devinfo.h b/include/linux/pinctrl/devinfo.h
new file mode 100644
index 0000000..6e5f8a9
--- /dev/null
+++ b/include/linux/pinctrl/devinfo.h
@@ -0,0 +1,45 @@
+/*
+ * Per-device information from the pin control system.
+ * This is the stuff that get included into the device
+ * core.
+ *
+ * Copyright (C) 2012 ST-Ericsson SA
+ * Written on behalf of Linaro for ST-Ericsson
+ * This interface is used in the core to keep track of pins.
+ *
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef PINCTRL_DEVINFO_H
+#define PINCTRL_DEVINFO_H
+
+#ifdef CONFIG_PINCTRL
+
+/* The device core acts as a consumer toward pinctrl */
+#include <linux/pinctrl/consumer.h>
+
+/**
+ * struct dev_pin_info - pin state container for devices
+ * @p: pinctrl handle for the containing device
+ * @default_state: the default state for the handle, if found
+ */
+struct dev_pin_info {
+ struct pinctrl *p;
+ struct pinctrl_state *default_state;
+};
+
+extern int pinctrl_bind_pins(struct device *dev);
+
+#else
+
+/* Stubs if we're not using pinctrl */
+
+static inline int pinctrl_bind_pins(struct device *dev)
+{
+ return 0;
+}
+
+#endif /* CONFIG_PINCTRL */
+#endif /* PINCTRL_DEVINFO_H */
diff --git a/include/linux/pinctrl/machine.h b/include/linux/pinctrl/machine.h
index e4d1de7..7d22ab0 100644
--- a/include/linux/pinctrl/machine.h
+++ b/include/linux/pinctrl/machine.h
@@ -154,7 +154,7 @@
extern int pinctrl_register_mappings(struct pinctrl_map const *map,
unsigned num_maps);
-
+extern void pinctrl_provide_dummies(void);
#else
static inline int pinctrl_register_mappings(struct pinctrl_map const *map,
@@ -163,5 +163,8 @@
return 0;
}
-#endif /* !CONFIG_PINMUX */
+static inline void pinctrl_provide_dummies(void)
+{
+}
+#endif /* !CONFIG_PINCTRL */
#endif
diff --git a/include/linux/pinctrl/pinconf-generic.h b/include/linux/pinctrl/pinconf-generic.h
index 4f0abb9..72474e1 100644
--- a/include/linux/pinctrl/pinconf-generic.h
+++ b/include/linux/pinctrl/pinconf-generic.h
@@ -46,11 +46,15 @@
* @PIN_CONFIG_DRIVE_OPEN_SOURCE: the pin will be driven with open source
* (open emitter). Sending this config will enabale open drain mode, the
* argument is ignored.
+ * @PIN_CONFIG_DRIVE_STRENGTH: the pin will output the current passed as
+ * argument. The argument is in mA.
+ * @PIN_CONFIG_INPUT_SCHMITT_ENABLE: control schmitt-trigger mode on the pin.
+ * If the argument != 0, schmitt-trigger mode is enabled. If it's 0,
+ * schmitt-trigger mode is disabled.
* @PIN_CONFIG_INPUT_SCHMITT: this will configure an input pin to run in
* schmitt-trigger mode. If the schmitt-trigger has adjustable hysteresis,
* the threshold value is given on a custom format as argument when
- * setting pins to this mode. The argument zero turns the schmitt trigger
- * off.
+ * setting pins to this mode.
* @PIN_CONFIG_INPUT_DEBOUNCE: this will configure the pin to debounce mode,
* which means it will wait for signals to settle when reading inputs. The
* argument gives the debounce time on a custom format. Setting the
@@ -58,10 +62,15 @@
* @PIN_CONFIG_POWER_SOURCE: if the pin can select between different power
* supplies, the argument to this parameter (on a custom format) tells
* the driver which alternative power source to use.
+ * @PIN_CONFIG_SLEW_RATE: if the pin can select slew rate, the argument to
+ * this parameter (on a custom format) tells the driver which alternative
+ * slew rate to use.
* @PIN_CONFIG_LOW_POWER_MODE: this will configure the pin for low power
* operation, if several modes of operation are supported these can be
* passed in the argument on a custom form, else just use argument 1
* to indicate low power mode, argument 0 turns low power mode off.
+ * @PIN_CONFIG_OUTPUT: this will configure the pin in output, use argument
+ * 1 to indicate high level, argument 0 to indicate low level.
* @PIN_CONFIG_END: this is the last enumerator for pin configurations, if
* you need to pass in custom configurations to the pin controller, use
* PIN_CONFIG_END+1 as the base offset.
@@ -74,10 +83,14 @@
PIN_CONFIG_DRIVE_PUSH_PULL,
PIN_CONFIG_DRIVE_OPEN_DRAIN,
PIN_CONFIG_DRIVE_OPEN_SOURCE,
+ PIN_CONFIG_DRIVE_STRENGTH,
+ PIN_CONFIG_INPUT_SCHMITT_ENABLE,
PIN_CONFIG_INPUT_SCHMITT,
PIN_CONFIG_INPUT_DEBOUNCE,
PIN_CONFIG_POWER_SOURCE,
+ PIN_CONFIG_SLEW_RATE,
PIN_CONFIG_LOW_POWER_MODE,
+ PIN_CONFIG_OUTPUT,
PIN_CONFIG_END = 0x7FFF,
};
diff --git a/include/linux/pinctrl/pinconf.h b/include/linux/pinctrl/pinconf.h
index ec431f0..e7a7201 100644
--- a/include/linux/pinctrl/pinconf.h
+++ b/include/linux/pinctrl/pinconf.h
@@ -25,7 +25,6 @@
* @pin_config_get: get the config of a certain pin, if the requested config
* is not available on this controller this should return -ENOTSUPP
* and if it is available but disabled it should return -EINVAL
- * @pin_config_get: get the config of a certain pin
* @pin_config_set: configure an individual pin
* @pin_config_group_get: get configurations for an entire pin group
* @pin_config_group_set: configure all pins in a group
@@ -33,6 +32,8 @@
* per-device info for a certain pin in debugfs
* @pin_config_group_dbg_show: optional debugfs display hook that will provide
* per-device info for a certain group in debugfs
+ * @pin_config_config_dbg_show: optional debugfs display hook that will decode
+ * and display a driver's pin configuration parameter
*/
struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONF
@@ -56,6 +57,9 @@
void (*pin_config_group_dbg_show) (struct pinctrl_dev *pctldev,
struct seq_file *s,
unsigned selector);
+ void (*pin_config_config_dbg_show) (struct pinctrl_dev *pctldev,
+ struct seq_file *s,
+ unsigned long config);
};
#endif
diff --git a/include/linux/pinctrl/pinctrl-state.h b/include/linux/pinctrl/pinctrl-state.h
index 3920e28..b5919f8 100644
--- a/include/linux/pinctrl/pinctrl-state.h
+++ b/include/linux/pinctrl/pinctrl-state.h
@@ -2,5 +2,23 @@
* Standard pin control state definitions
*/
+/**
+ * @PINCTRL_STATE_DEFAULT: the state the pinctrl handle shall be put
+ * into as default, usually this means the pins are up and ready to
+ * be used by the device driver. This state is commonly used by
+ * hogs to configure muxing and pins at boot, and also as a state
+ * to go into when returning from sleep and idle in
+ * .pm_runtime_resume() or ordinary .resume() for example.
+ * @PINCTRL_STATE_IDLE: the state the pinctrl handle shall be put into
+ * when the pins are idle. This is a state where the system is relaxed
+ * but not fully sleeping - some power may be on but clocks gated for
+ * example. Could typically be set from a pm_runtime_suspend() or
+ * pm_runtime_idle() operation.
+ * @PINCTRL_STATE_SLEEP: the state the pinctrl handle shall be put into
+ * when the pins are sleeping. This is a state where the system is in
+ * its lowest sleep state. Could typically be set from an
+ * ordinary .suspend() function.
+ */
#define PINCTRL_STATE_DEFAULT "default"
#define PINCTRL_STATE_IDLE "idle"
+#define PINCTRL_STATE_SLEEP "sleep"
diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h
index 4e9f078..d19ad40 100644
--- a/include/linux/pinctrl/pinctrl.h
+++ b/include/linux/pinctrl/pinctrl.h
@@ -21,9 +21,11 @@
struct device;
struct pinctrl_dev;
+struct pinctrl_map;
struct pinmux_ops;
struct pinconf_ops;
struct gpio_chip;
+struct device_node;
/**
* struct pinctrl_pin_desc - boards/machines provide information on their
@@ -64,17 +66,24 @@
/**
* struct pinctrl_ops - global pin control operations, to be implemented by
* pin controller drivers.
- * @list_groups: list the number of selectable named groups available
- * in this pinmux driver, the core will begin on 0 and call this
- * repeatedly as long as it returns >= 0 to enumerate the groups
+ * @get_groups_count: Returns the count of total number of groups registered.
* @get_group_name: return the group name of the pin group
* @get_group_pins: return an array of pins corresponding to a certain
* group selector @pins, and the size of the array in @num_pins
* @pin_dbg_show: optional debugfs display hook that will provide per-device
* info for a certain pin in debugfs
+ * @dt_node_to_map: parse a device tree "pin configuration node", and create
+ * mapping table entries for it. These are returned through the @map and
+ * @num_maps output parameters. This function is optional, and may be
+ * omitted for pinctrl drivers that do not support device tree.
+ * @dt_free_map: free mapping table entries created via @dt_node_to_map. The
+ * top-level @map pointer must be freed, along with any dynamically
+ * allocated members of the mapping table entries themselves. This
+ * function is optional, and may be omitted for pinctrl drivers that do
+ * not support device tree.
*/
struct pinctrl_ops {
- int (*list_groups) (struct pinctrl_dev *pctldev, unsigned selector);
+ int (*get_groups_count) (struct pinctrl_dev *pctldev);
const char *(*get_group_name) (struct pinctrl_dev *pctldev,
unsigned selector);
int (*get_group_pins) (struct pinctrl_dev *pctldev,
@@ -83,6 +92,11 @@
unsigned *num_pins);
void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s,
unsigned offset);
+ int (*dt_node_to_map) (struct pinctrl_dev *pctldev,
+ struct device_node *np_config,
+ struct pinctrl_map **map, unsigned *num_maps);
+ void (*dt_free_map) (struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, unsigned num_maps);
};
/**
@@ -117,9 +131,30 @@
extern bool pin_is_valid(struct pinctrl_dev *pctldev, int pin);
extern void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range);
+extern void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *ranges,
+ unsigned nranges);
extern void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range);
+
+extern struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
+ struct pinctrl_gpio_range *range);
+extern struct pinctrl_gpio_range *
+pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev,
+ unsigned int pin);
+
+#ifdef CONFIG_OF
+extern struct pinctrl_dev *of_pinctrl_get(struct device_node *np);
+#else
+static inline
+struct pinctrl_dev *of_pinctrl_get(struct device_node *np)
+{
+ return NULL;
+}
+#endif /* CONFIG_OF */
+
extern const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev);
+extern const char *pinctrl_dev_get_devname(struct pinctrl_dev *pctldev);
extern void *pinctrl_dev_get_drvdata(struct pinctrl_dev *pctldev);
#else
diff --git a/include/linux/pinctrl/pinmux.h b/include/linux/pinctrl/pinmux.h
index 47e9237..1818dcb 100644
--- a/include/linux/pinctrl/pinmux.h
+++ b/include/linux/pinctrl/pinmux.h
@@ -23,15 +23,14 @@
/**
* struct pinmux_ops - pinmux operations, to be implemented by pin controller
* drivers that support pinmuxing
- * @request: called by the core to see if a certain pin can be made available
+ * @request: called by the core to see if a certain pin can be made
* available for muxing. This is called by the core to acquire the pins
* before selecting any actual mux setting across a function. The driver
* is allowed to answer "no" by returning a negative error code
* @free: the reverse function of the request() callback, frees a pin after
* being requested
- * @list_functions: list the number of selectable named functions available
- * in this pinmux driver, the core will begin on 0 and call this
- * repeatedly as long as it returns >= 0 to enumerate mux settings
+ * @get_functions_count: returns number of selectable named functions available
+ * in this pinmux driver
* @get_function_name: return the function name of the muxing selector,
* called by the core to figure out which mux setting it shall map a
* certain device to
@@ -62,7 +61,7 @@
struct pinmux_ops {
int (*request) (struct pinctrl_dev *pctldev, unsigned offset);
int (*free) (struct pinctrl_dev *pctldev, unsigned offset);
- int (*list_functions) (struct pinctrl_dev *pctldev, unsigned selector);
+ int (*get_functions_count) (struct pinctrl_dev *pctldev);
const char *(*get_function_name) (struct pinctrl_dev *pctldev,
unsigned selector);
int (*get_function_groups) (struct pinctrl_dev *pctldev,
diff --git a/include/linux/regulator/krait-regulator.h b/include/linux/regulator/krait-regulator.h
index 2683fd7..bd2ca82 100644
--- a/include/linux/regulator/krait-regulator.h
+++ b/include/linux/regulator/krait-regulator.h
@@ -26,14 +26,14 @@
#ifdef CONFIG_ARCH_MSM8974
int __init krait_power_init(void);
-void secondary_cpu_hs_init(void *base_ptr);
+void secondary_cpu_hs_init(void *base_ptr, int cpu);
#else
static inline int __init krait_power_init(void)
{
return -ENOSYS;
}
-static inline void secondary_cpu_hs_init(void *base_ptr) {}
+static inline void secondary_cpu_hs_init(void *base_ptr, int cpu) {}
#endif
#endif
diff --git a/include/linux/rmap.h b/include/linux/rmap.h
index fd07c45..a5ddc60 100644
--- a/include/linux/rmap.h
+++ b/include/linux/rmap.h
@@ -67,6 +67,17 @@
struct list_head same_anon_vma; /* locked by anon_vma->mutex */
};
+enum ttu_flags {
+ TTU_UNMAP = 0, /* unmap mode */
+ TTU_MIGRATION = 1, /* migration mode */
+ TTU_MUNLOCK = 2, /* munlock mode */
+ TTU_ACTION_MASK = 0xff,
+
+ TTU_IGNORE_MLOCK = (1 << 8), /* ignore mlock */
+ TTU_IGNORE_ACCESS = (1 << 9), /* don't age */
+ TTU_IGNORE_HWPOISON = (1 << 10),/* corrupted page is recoverable */
+};
+
#ifdef CONFIG_MMU
static inline void get_anon_vma(struct anon_vma *anon_vma)
{
@@ -161,16 +172,6 @@
int page_referenced_one(struct page *, struct vm_area_struct *,
unsigned long address, unsigned int *mapcount, unsigned long *vm_flags);
-enum ttu_flags {
- TTU_UNMAP = 0, /* unmap mode */
- TTU_MIGRATION = 1, /* migration mode */
- TTU_MUNLOCK = 2, /* munlock mode */
- TTU_ACTION_MASK = 0xff,
-
- TTU_IGNORE_MLOCK = (1 << 8), /* ignore mlock */
- TTU_IGNORE_ACCESS = (1 << 9), /* don't age */
- TTU_IGNORE_HWPOISON = (1 << 10),/* corrupted page is recoverable */
-};
#define TTU_ACTION(x) ((x) & TTU_ACTION_MASK)
bool is_vma_temporary_stack(struct vm_area_struct *vma);
diff --git a/include/linux/slimbus/slimbus.h b/include/linux/slimbus/slimbus.h
index 5c5b777..132135e 100644
--- a/include/linux/slimbus/slimbus.h
+++ b/include/linux/slimbus/slimbus.h
@@ -511,8 +511,11 @@
* @wakeup: This function pointer implements controller-specific procedure
* to wake it up from clock-pause. Framework will call this to bring
* the controller out of clock pause.
- * @config_port: Configure a port and make it ready for data transfer. This is
- * called by framework after connect_port message is sent successfully.
+ * @alloc_port: Allocate a port and make it ready for data transfer. This is
+ * called by framework to make sure controller can take necessary steps
+ * to initialize its port
+ * @dealloc_port: Counter-part of alloc_port. This is called by framework so
+ * that controller can free resources associated with this port
* @framer_handover: If this controller has multiple framers, this API will
* be called to switch between framers if controller desires to change
* the active framer.
@@ -557,7 +560,9 @@
int (*get_laddr)(struct slim_controller *ctrl,
const u8 *ea, u8 elen, u8 *laddr);
int (*wakeup)(struct slim_controller *ctrl);
- int (*config_port)(struct slim_controller *ctrl,
+ int (*alloc_port)(struct slim_controller *ctrl,
+ u8 port);
+ void (*dealloc_port)(struct slim_controller *ctrl,
u8 port);
int (*framer_handover)(struct slim_controller *ctrl,
struct slim_framer *new_framer);
@@ -795,6 +800,8 @@
* Channel specified in chanh needs to be allocated first.
* Returns -EALREADY if source is already configured for this channel.
* Returns -ENOTCONN if channel is not allocated
+ * Returns -EINVAL if invalid direction is specified for non-manager port,
+ * or if the manager side port number is out of bounds, or in incorrect state
*/
extern int slim_connect_src(struct slim_device *sb, u32 srch, u16 chanh);
@@ -808,6 +815,9 @@
* Channel specified in chanh needs to be allocated first.
* Returns -EALREADY if sink is already configured for this channel.
* Returns -ENOTCONN if channel is not allocated
+ * Returns -EINVAL if invalid parameters are passed, or invalid direction is
+ * specified for non-manager port, or if the manager side port number is out of
+ * bounds, or in incorrect state
*/
extern int slim_connect_sink(struct slim_device *sb, u32 *sinkh, int nsink,
u16 chanh);
diff --git a/include/linux/swap.h b/include/linux/swap.h
index b1fd5c7..d5bd6ee 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -251,7 +251,7 @@
/* linux/mm/vmscan.c */
extern unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
gfp_t gfp_mask, nodemask_t *mask);
-extern int __isolate_lru_page(struct page *page, isolate_mode_t mode, int file);
+extern int __isolate_lru_page(struct page *page, isolate_mode_t mode);
extern unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *mem,
gfp_t gfp_mask, bool noswap);
extern unsigned long mem_cgroup_shrink_node_zone(struct mem_cgroup *mem,
@@ -355,23 +355,6 @@
extern int try_to_free_swap(struct page *);
struct backing_dev_info;
-/* linux/mm/thrash.c */
-extern struct mm_struct *swap_token_mm;
-extern void grab_swap_token(struct mm_struct *);
-extern void __put_swap_token(struct mm_struct *);
-extern void disable_swap_token(struct mem_cgroup *memcg);
-
-static inline int has_swap_token(struct mm_struct *mm)
-{
- return (mm == swap_token_mm);
-}
-
-static inline void put_swap_token(struct mm_struct *mm)
-{
- if (has_swap_token(mm))
- __put_swap_token(mm);
-}
-
#ifdef CONFIG_CGROUP_MEM_RES_CTLR
extern void
mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout);
@@ -476,24 +459,6 @@
return entry;
}
-/* linux/mm/thrash.c */
-static inline void put_swap_token(struct mm_struct *mm)
-{
-}
-
-static inline void grab_swap_token(struct mm_struct *mm)
-{
-}
-
-static inline int has_swap_token(struct mm_struct *mm)
-{
- return 0;
-}
-
-static inline void disable_swap_token(struct mem_cgroup *memcg)
-{
-}
-
static inline void
mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent)
{
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 82044f7..fea832e 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -545,6 +545,7 @@
const char *name;
struct device dev;
u8 usb_core_id;
+ bool l1_supported;
};
static inline void set_gadget_data(struct usb_gadget *gadget, void *data)
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 04b99b7..44c6d7f 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -42,6 +42,20 @@
#define MSM_VENDOR_ID BIT(16)
/**
+ * Requested USB votes for BUS bandwidth
+ *
+ * USB_NO_PERF_VOTE BUS Vote for inactive USB session or disconnect
+ * USB_MAX_PERF_VOTE Maximum BUS bandwidth vote
+ * USB_MIN_PERF_VOTE Minimum BUS bandwidth vote (for some hw same as NO_PERF)
+ *
+ */
+enum usb_bus_vote {
+ USB_NO_PERF_VOTE = 0,
+ USB_MAX_PERF_VOTE,
+ USB_MIN_PERF_VOTE,
+};
+
+/**
* Supported USB modes
*
* USB_PERIPHERAL Only peripheral mode is supported.
@@ -207,6 +221,7 @@
* @log2_itc: value of 2^(log2_itc-1) will be used as the
* interrupt threshold (ITC), when log2_itc is
* between 1 to 7.
+ * @l1_supported: enable link power management support.
*/
struct msm_otg_platform_data {
int *phy_init_seq;
@@ -231,6 +246,7 @@
struct msm_bus_scale_pdata *bus_scale_table;
const char *mhl_dev_name;
int log2_itc;
+ bool l1_supported;
};
/* phy related flags */
@@ -414,6 +430,7 @@
*/
int log2_itc;
void *prv_data;
+ bool l1_supported;
};
struct msm_hsic_host_platform_data {
diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h
index 0d57d93..f3f4c3b 100644
--- a/include/linux/usb/msm_hsusb_hw.h
+++ b/include/linux/usb/msm_hsusb_hw.h
@@ -39,6 +39,15 @@
#define USB_USBINTR (MSM_USB_BASE + 0x0148)
#define USB_FRINDEX (MSM_USB_BASE + 0x014C)
+#define USB_L1_EP_CTRL (MSM_USB_BASE + 0x0250)
+#define USB_L1_CONFIG (MSM_USB_BASE + 0x0254)
+
+#define L1_CONFIG_LPM_EN BIT(4)
+#define L1_CONFIG_REMOTE_WAKEUP BIT(5)
+#define L1_CONFIG_GATE_SYS_CLK BIT(7)
+#define L1_CONFIG_PHY_LPM BIT(10)
+#define L1_CONFIG_PLL BIT(11)
+
#define PORTSC_PHCD (1 << 23) /* phy suspend mode */
#define PORTSC_PTS_MASK (3 << 30)
#define PORTSC_PTS_ULPI (3 << 30)
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index 72f5c96..642c4fe 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -131,6 +131,9 @@
/* link reset handling, called from defer_kevent */
int (*link_reset)(struct usbnet *);
+ /*in case if usbnet wrapper wants to override rx_complete()*/
+ void (*rx_complete) (struct urb *);
+
/* fixup rx packet (strip framing) */
int (*rx_fixup)(struct usbnet *dev, struct sk_buff *skb);
@@ -228,5 +231,7 @@
extern void usbnet_set_msglevel(struct net_device *, u32);
extern void usbnet_get_drvinfo(struct net_device *, struct ethtool_drvinfo *);
extern int usbnet_nway_reset(struct net_device *net);
+extern void usbnet_terminate_urbs(struct usbnet *dev);
+extern void rx_complete(struct urb *urb);
#endif /* __LINUX_USB_USBNET_H */
diff --git a/include/linux/vm_event_item.h b/include/linux/vm_event_item.h
index 06f8e38..8bbb324 100644
--- a/include/linux/vm_event_item.h
+++ b/include/linux/vm_event_item.h
@@ -37,8 +37,12 @@
KSWAPD_LOW_WMARK_HIT_QUICKLY, KSWAPD_HIGH_WMARK_HIT_QUICKLY,
KSWAPD_SKIP_CONGESTION_WAIT,
PAGEOUTRUN, ALLOCSTALL, PGROTATED,
+#ifdef CONFIG_MIGRATION
+ PGMIGRATE_SUCCESS, PGMIGRATE_FAIL,
+#endif
#ifdef CONFIG_COMPACTION
- COMPACTBLOCKS, COMPACTPAGES, COMPACTPAGEFAILED,
+ COMPACTMIGRATE_SCANNED, COMPACTFREE_SCANNED,
+ COMPACTISOLATED,
COMPACTSTALL, COMPACTFAIL, COMPACTSUCCESS,
#endif
#ifdef CONFIG_HUGETLB_PAGE
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
index 0d3b5a0..7e98ef3 100644
--- a/include/sound/apr_audio-v2.h
+++ b/include/sound/apr_audio-v2.h
@@ -6930,8 +6930,8 @@
} __packed;
/* Ultrasound supported formats */
-#define US_POINT_EPOS_FORMAT 0x00012310
-#define US_RAW_FORMAT 0x0001127C
-#define US_PROX_FORMAT 0x0001272B
+#define US_POINT_EPOS_FORMAT_V2 0x0001272D
+#define US_RAW_FORMAT_V2 0x0001272C
+#define US_PROX_FORMAT_V2 0x0001272E
#endif /*_APR_AUDIO_V2_H_ */
diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h
index f64560e..bab3b87 100644
--- a/include/trace/events/vmscan.h
+++ b/include/trace/events/vmscan.h
@@ -13,7 +13,7 @@
#define RECLAIM_WB_ANON 0x0001u
#define RECLAIM_WB_FILE 0x0002u
#define RECLAIM_WB_MIXED 0x0010u
-#define RECLAIM_WB_SYNC 0x0004u
+#define RECLAIM_WB_SYNC 0x0004u /* Unused, all reclaim async */
#define RECLAIM_WB_ASYNC 0x0008u
#define show_reclaim_flags(flags) \
@@ -25,15 +25,15 @@
{RECLAIM_WB_ASYNC, "RECLAIM_WB_ASYNC"} \
) : "RECLAIM_WB_NONE"
-#define trace_reclaim_flags(page, sync) ( \
+#define trace_reclaim_flags(page) ( \
(page_is_file_cache(page) ? RECLAIM_WB_FILE : RECLAIM_WB_ANON) | \
- (sync & RECLAIM_MODE_SYNC ? RECLAIM_WB_SYNC : RECLAIM_WB_ASYNC) \
+ (RECLAIM_WB_ASYNC) \
)
-#define trace_shrink_flags(file, sync) ( \
- (sync & RECLAIM_MODE_SYNC ? RECLAIM_WB_MIXED : \
- (file ? RECLAIM_WB_FILE : RECLAIM_WB_ANON)) | \
- (sync & RECLAIM_MODE_SYNC ? RECLAIM_WB_SYNC : RECLAIM_WB_ASYNC) \
+#define trace_shrink_flags(file) \
+ ( \
+ (file ? RECLAIM_WB_FILE : RECLAIM_WB_ANON) | \
+ (RECLAIM_WB_ASYNC) \
)
TRACE_EVENT(mm_vmscan_kswapd_sleep,
@@ -263,22 +263,16 @@
unsigned long nr_requested,
unsigned long nr_scanned,
unsigned long nr_taken,
- unsigned long nr_lumpy_taken,
- unsigned long nr_lumpy_dirty,
- unsigned long nr_lumpy_failed,
isolate_mode_t isolate_mode,
int file),
- TP_ARGS(order, nr_requested, nr_scanned, nr_taken, nr_lumpy_taken, nr_lumpy_dirty, nr_lumpy_failed, isolate_mode, file),
+ TP_ARGS(order, nr_requested, nr_scanned, nr_taken, isolate_mode, file),
TP_STRUCT__entry(
__field(int, order)
__field(unsigned long, nr_requested)
__field(unsigned long, nr_scanned)
__field(unsigned long, nr_taken)
- __field(unsigned long, nr_lumpy_taken)
- __field(unsigned long, nr_lumpy_dirty)
- __field(unsigned long, nr_lumpy_failed)
__field(isolate_mode_t, isolate_mode)
__field(int, file)
),
@@ -288,22 +282,16 @@
__entry->nr_requested = nr_requested;
__entry->nr_scanned = nr_scanned;
__entry->nr_taken = nr_taken;
- __entry->nr_lumpy_taken = nr_lumpy_taken;
- __entry->nr_lumpy_dirty = nr_lumpy_dirty;
- __entry->nr_lumpy_failed = nr_lumpy_failed;
__entry->isolate_mode = isolate_mode;
__entry->file = file;
),
- TP_printk("isolate_mode=%d order=%d nr_requested=%lu nr_scanned=%lu nr_taken=%lu contig_taken=%lu contig_dirty=%lu contig_failed=%lu file=%d",
+ TP_printk("isolate_mode=%d order=%d nr_requested=%lu nr_scanned=%lu nr_taken=%lu file=%d",
__entry->isolate_mode,
__entry->order,
__entry->nr_requested,
__entry->nr_scanned,
__entry->nr_taken,
- __entry->nr_lumpy_taken,
- __entry->nr_lumpy_dirty,
- __entry->nr_lumpy_failed,
__entry->file)
);
@@ -313,13 +301,10 @@
unsigned long nr_requested,
unsigned long nr_scanned,
unsigned long nr_taken,
- unsigned long nr_lumpy_taken,
- unsigned long nr_lumpy_dirty,
- unsigned long nr_lumpy_failed,
isolate_mode_t isolate_mode,
int file),
- TP_ARGS(order, nr_requested, nr_scanned, nr_taken, nr_lumpy_taken, nr_lumpy_dirty, nr_lumpy_failed, isolate_mode, file)
+ TP_ARGS(order, nr_requested, nr_scanned, nr_taken, isolate_mode, file)
);
@@ -329,13 +314,10 @@
unsigned long nr_requested,
unsigned long nr_scanned,
unsigned long nr_taken,
- unsigned long nr_lumpy_taken,
- unsigned long nr_lumpy_dirty,
- unsigned long nr_lumpy_failed,
isolate_mode_t isolate_mode,
int file),
- TP_ARGS(order, nr_requested, nr_scanned, nr_taken, nr_lumpy_taken, nr_lumpy_dirty, nr_lumpy_failed, isolate_mode, file)
+ TP_ARGS(order, nr_requested, nr_scanned, nr_taken, isolate_mode, file)
);
@@ -395,88 +377,6 @@
show_reclaim_flags(__entry->reclaim_flags))
);
-TRACE_EVENT(replace_swap_token,
- TP_PROTO(struct mm_struct *old_mm,
- struct mm_struct *new_mm),
-
- TP_ARGS(old_mm, new_mm),
-
- TP_STRUCT__entry(
- __field(struct mm_struct*, old_mm)
- __field(unsigned int, old_prio)
- __field(struct mm_struct*, new_mm)
- __field(unsigned int, new_prio)
- ),
-
- TP_fast_assign(
- __entry->old_mm = old_mm;
- __entry->old_prio = old_mm ? old_mm->token_priority : 0;
- __entry->new_mm = new_mm;
- __entry->new_prio = new_mm->token_priority;
- ),
-
- TP_printk("old_token_mm=%p old_prio=%u new_token_mm=%p new_prio=%u",
- __entry->old_mm, __entry->old_prio,
- __entry->new_mm, __entry->new_prio)
-);
-
-DECLARE_EVENT_CLASS(put_swap_token_template,
- TP_PROTO(struct mm_struct *swap_token_mm),
-
- TP_ARGS(swap_token_mm),
-
- TP_STRUCT__entry(
- __field(struct mm_struct*, swap_token_mm)
- ),
-
- TP_fast_assign(
- __entry->swap_token_mm = swap_token_mm;
- ),
-
- TP_printk("token_mm=%p", __entry->swap_token_mm)
-);
-
-DEFINE_EVENT(put_swap_token_template, put_swap_token,
- TP_PROTO(struct mm_struct *swap_token_mm),
- TP_ARGS(swap_token_mm)
-);
-
-DEFINE_EVENT_CONDITION(put_swap_token_template, disable_swap_token,
- TP_PROTO(struct mm_struct *swap_token_mm),
- TP_ARGS(swap_token_mm),
- TP_CONDITION(swap_token_mm != NULL)
-);
-
-TRACE_EVENT_CONDITION(update_swap_token_priority,
- TP_PROTO(struct mm_struct *mm,
- unsigned int old_prio,
- struct mm_struct *swap_token_mm),
-
- TP_ARGS(mm, old_prio, swap_token_mm),
-
- TP_CONDITION(mm->token_priority != old_prio),
-
- TP_STRUCT__entry(
- __field(struct mm_struct*, mm)
- __field(unsigned int, old_prio)
- __field(unsigned int, new_prio)
- __field(struct mm_struct*, swap_token_mm)
- __field(unsigned int, swap_token_prio)
- ),
-
- TP_fast_assign(
- __entry->mm = mm;
- __entry->old_prio = old_prio;
- __entry->new_prio = mm->token_priority;
- __entry->swap_token_mm = swap_token_mm;
- __entry->swap_token_prio = swap_token_mm ? swap_token_mm->token_priority : 0;
- ),
-
- TP_printk("mm=%p old_prio=%u new_prio=%u swap_token_mm=%p token_prio=%u",
- __entry->mm, __entry->old_prio, __entry->new_prio,
- __entry->swap_token_mm, __entry->swap_token_prio)
-);
-
#endif /* _TRACE_VMSCAN_H */
/* This part must be outside protection */
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 2f0d7542..01f68c4 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -4902,7 +4902,7 @@
* @root: the css supporsed to be an ancestor of the child.
*
* Returns true if "root" is an ancestor of "child" in its hierarchy. Because
- * this function reads css->id, this use rcu_dereference() and rcu_read_lock().
+ * this function reads css->id, the caller must hold rcu_read_lock().
* But, considering usual usage, the csses should be valid objects after test.
* Assuming that the caller will do some action to the child if this returns
* returns true, the caller must take "child";s reference count.
@@ -4914,18 +4914,18 @@
{
struct css_id *child_id;
struct css_id *root_id;
- bool ret = true;
- rcu_read_lock();
child_id = rcu_dereference(child->id);
+ if (!child_id)
+ return false;
root_id = rcu_dereference(root->id);
- if (!child_id
- || !root_id
- || (child_id->depth < root_id->depth)
- || (child_id->stack[root_id->depth] != root_id->id))
- ret = false;
- rcu_read_unlock();
- return ret;
+ if (!root_id)
+ return false;
+ if (child_id->depth < root_id->depth)
+ return false;
+ if (child_id->stack[root_id->depth] != root_id->id)
+ return false;
+ return true;
}
void free_css_id(struct cgroup_subsys *ss, struct cgroup_subsys_state *css)
diff --git a/kernel/events/core.c b/kernel/events/core.c
index fd126f8..aafa4c1 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -5118,7 +5118,7 @@
static int perf_swevent_init(struct perf_event *event)
{
- int event_id = event->attr.config;
+ u64 event_id = event->attr.config;
if (event->attr.type != PERF_TYPE_SOFTWARE)
return -ENOENT;
diff --git a/kernel/fork.c b/kernel/fork.c
index 0de735c..a8bf721 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -597,7 +597,6 @@
list_del(&mm->mmlist);
spin_unlock(&mmlist_lock);
}
- put_swap_token(mm);
if (mm->binfmt)
module_put(mm->binfmt->module);
mmdrop(mm);
@@ -815,10 +814,6 @@
memcpy(mm, oldmm, sizeof(*mm));
mm_init_cpumask(mm);
- /* Initializing for Swap token stuff */
- mm->token_priority = 0;
- mm->last_interval = 0;
-
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
mm->pmd_huge_pte = NULL;
#endif
@@ -896,10 +891,6 @@
goto fail_nomem;
good_mm:
- /* Initializing for Swap token stuff */
- mm->token_priority = 0;
- mm->last_interval = 0;
-
tsk->mm = mm;
tsk->active_mm = mm;
return 0;
diff --git a/mm/Makefile b/mm/Makefile
index 8aada89..ccecbf9 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -25,7 +25,7 @@
obj-$(CONFIG_HAVE_MEMBLOCK) += memblock.o
obj-$(CONFIG_BOUNCE) += bounce.o
-obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o thrash.o
+obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o
obj-$(CONFIG_HAS_DMA) += dmapool.o
obj-$(CONFIG_HUGETLBFS) += hugetlb.o
obj-$(CONFIG_NUMA) += mempolicy.o
diff --git a/mm/compaction.c b/mm/compaction.c
index 353f1c5..673142d 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -16,6 +16,21 @@
#include <linux/sysfs.h>
#include "internal.h"
+#ifdef CONFIG_COMPACTION
+static inline void count_compact_event(enum vm_event_item item)
+{
+ count_vm_event(item);
+}
+
+static inline void count_compact_events(enum vm_event_item item, long delta)
+{
+ count_vm_events(item, delta);
+}
+#else
+#define count_compact_event(item) do { } while (0)
+#define count_compact_events(item, delta) do { } while (0)
+#endif
+
#if defined CONFIG_COMPACTION || defined CONFIG_CMA
#define CREATE_TRACE_POINTS
@@ -50,44 +65,228 @@
return is_migrate_cma(migratetype) || migratetype == MIGRATE_MOVABLE;
}
+#ifdef CONFIG_COMPACTION
+/* Returns true if the pageblock should be scanned for pages to isolate. */
+static inline bool isolation_suitable(struct compact_control *cc,
+ struct page *page)
+{
+ if (cc->ignore_skip_hint)
+ return true;
+
+ return !get_pageblock_skip(page);
+}
+
+/*
+ * This function is called to clear all cached information on pageblocks that
+ * should be skipped for page isolation when the migrate and free page scanner
+ * meet.
+ */
+static void __reset_isolation_suitable(struct zone *zone)
+{
+ unsigned long start_pfn = zone->zone_start_pfn;
+ unsigned long end_pfn = zone->zone_start_pfn + zone->spanned_pages;
+ unsigned long pfn;
+
+ zone->compact_cached_migrate_pfn = start_pfn;
+ zone->compact_cached_free_pfn = end_pfn;
+ zone->compact_blockskip_flush = false;
+
+ /* Walk the zone and mark every pageblock as suitable for isolation */
+ for (pfn = start_pfn; pfn < end_pfn; pfn += pageblock_nr_pages) {
+ struct page *page;
+
+ cond_resched();
+
+ if (!pfn_valid(pfn))
+ continue;
+
+ page = pfn_to_page(pfn);
+ if (zone != page_zone(page))
+ continue;
+
+ clear_pageblock_skip(page);
+ }
+}
+
+void reset_isolation_suitable(pg_data_t *pgdat)
+{
+ int zoneid;
+
+ for (zoneid = 0; zoneid < MAX_NR_ZONES; zoneid++) {
+ struct zone *zone = &pgdat->node_zones[zoneid];
+ if (!populated_zone(zone))
+ continue;
+
+ /* Only flush if a full compaction finished recently */
+ if (zone->compact_blockskip_flush)
+ __reset_isolation_suitable(zone);
+ }
+}
+
+/*
+ * If no pages were isolated then mark this pageblock to be skipped in the
+ * future. The information is later cleared by __reset_isolation_suitable().
+ */
+static void update_pageblock_skip(struct compact_control *cc,
+ struct page *page, unsigned long nr_isolated,
+ bool migrate_scanner)
+{
+ struct zone *zone = cc->zone;
+ if (!page)
+ return;
+
+ if (!nr_isolated) {
+ unsigned long pfn = page_to_pfn(page);
+ set_pageblock_skip(page);
+
+ /* Update where compaction should restart */
+ if (migrate_scanner) {
+ if (!cc->finished_update_migrate &&
+ pfn > zone->compact_cached_migrate_pfn)
+ zone->compact_cached_migrate_pfn = pfn;
+ } else {
+ if (!cc->finished_update_free &&
+ pfn < zone->compact_cached_free_pfn)
+ zone->compact_cached_free_pfn = pfn;
+ }
+ }
+}
+#else
+static inline bool isolation_suitable(struct compact_control *cc,
+ struct page *page)
+{
+ return true;
+}
+
+static void update_pageblock_skip(struct compact_control *cc,
+ struct page *page, unsigned long nr_isolated,
+ bool migrate_scanner)
+{
+}
+#endif /* CONFIG_COMPACTION */
+
+static inline bool should_release_lock(spinlock_t *lock)
+{
+ return need_resched() || spin_is_contended(lock);
+}
+
+/*
+ * Compaction requires the taking of some coarse locks that are potentially
+ * very heavily contended. Check if the process needs to be scheduled or
+ * if the lock is contended. For async compaction, back out in the event
+ * if contention is severe. For sync compaction, schedule.
+ *
+ * Returns true if the lock is held.
+ * Returns false if the lock is released and compaction should abort
+ */
+static bool compact_checklock_irqsave(spinlock_t *lock, unsigned long *flags,
+ bool locked, struct compact_control *cc)
+{
+ if (should_release_lock(lock)) {
+ if (locked) {
+ spin_unlock_irqrestore(lock, *flags);
+ locked = false;
+ }
+
+ /* async aborts if taking too long or contended */
+ if (!cc->sync) {
+ cc->contended = true;
+ return false;
+ }
+
+ cond_resched();
+ }
+
+ if (!locked)
+ spin_lock_irqsave(lock, *flags);
+ return true;
+}
+
+static inline bool compact_trylock_irqsave(spinlock_t *lock,
+ unsigned long *flags, struct compact_control *cc)
+{
+ return compact_checklock_irqsave(lock, flags, false, cc);
+}
+
+/* Returns true if the page is within a block suitable for migration to */
+static bool suitable_migration_target(struct page *page)
+{
+ int migratetype = get_pageblock_migratetype(page);
+
+ /* Don't interfere with memory hot-remove or the min_free_kbytes blocks */
+ if (migratetype == MIGRATE_ISOLATE || migratetype == MIGRATE_RESERVE)
+ return false;
+
+ /* If the page is a large free page, then allow migration */
+ if (PageBuddy(page) && page_order(page) >= pageblock_order)
+ return true;
+
+ /* If the block is MIGRATE_MOVABLE or MIGRATE_CMA, allow migration */
+ if (migrate_async_suitable(migratetype))
+ return true;
+
+ /* Otherwise skip the block */
+ return false;
+}
+
/*
* Isolate free pages onto a private freelist. Caller must hold zone->lock.
* If @strict is true, will abort returning 0 on any invalid PFNs or non-free
* pages inside of the pageblock (even though it may still end up isolating
* some pages).
*/
-static unsigned long isolate_freepages_block(unsigned long blockpfn,
+static unsigned long isolate_freepages_block(struct compact_control *cc,
+ unsigned long blockpfn,
unsigned long end_pfn,
struct list_head *freelist,
bool strict)
{
int nr_scanned = 0, total_isolated = 0;
- struct page *cursor;
+ struct page *cursor, *valid_page = NULL;
+ unsigned long nr_strict_required = end_pfn - blockpfn;
+ unsigned long flags;
+ bool locked = false;
cursor = pfn_to_page(blockpfn);
- /* Isolate free pages. This assumes the block is valid */
+ /* Isolate free pages. */
for (; blockpfn < end_pfn; blockpfn++, cursor++) {
int isolated, i;
struct page *page = cursor;
- if (!pfn_valid_within(blockpfn)) {
- if (strict)
- return 0;
- continue;
- }
nr_scanned++;
-
- if (!PageBuddy(page)) {
- if (strict)
- return 0;
+ if (!pfn_valid_within(blockpfn))
continue;
- }
+ if (!valid_page)
+ valid_page = page;
+ if (!PageBuddy(page))
+ continue;
+
+ /*
+ * The zone lock must be held to isolate freepages.
+ * Unfortunately this is a very coarse lock and can be
+ * heavily contended if there are parallel allocations
+ * or parallel compactions. For async compaction do not
+ * spin on the lock and we acquire the lock as late as
+ * possible.
+ */
+ locked = compact_checklock_irqsave(&cc->zone->lock, &flags,
+ locked, cc);
+ if (!locked)
+ break;
+
+ /* Recheck this is a suitable migration target under lock */
+ if (!strict && !suitable_migration_target(page))
+ break;
+
+ /* Recheck this is a buddy page under lock */
+ if (!PageBuddy(page))
+ continue;
/* Found a free page, break it into order-0 pages */
isolated = split_free_page(page);
if (!isolated && strict)
- return 0;
+ break;
total_isolated += isolated;
for (i = 0; i < isolated; i++) {
list_add(&page->lru, freelist);
@@ -102,6 +301,25 @@
}
trace_mm_compaction_isolate_freepages(nr_scanned, total_isolated);
+
+ /*
+ * If strict isolation is requested by CMA then check that all the
+ * pages requested were isolated. If there were any failures, 0 is
+ * returned and CMA will fail.
+ */
+ if (strict && nr_strict_required > total_isolated)
+ total_isolated = 0;
+
+ if (locked)
+ spin_unlock_irqrestore(&cc->zone->lock, flags);
+
+ /* Update the pageblock-skip if the whole pageblock was scanned */
+ if (blockpfn == end_pfn)
+ update_pageblock_skip(cc, valid_page, total_isolated, false);
+
+ count_compact_events(COMPACTFREE_SCANNED, nr_scanned);
+ if (total_isolated)
+ count_compact_events(COMPACTISOLATED, total_isolated);
return total_isolated;
}
@@ -119,17 +337,14 @@
* a free page).
*/
unsigned long
-isolate_freepages_range(unsigned long start_pfn, unsigned long end_pfn)
+isolate_freepages_range(struct compact_control *cc,
+ unsigned long start_pfn, unsigned long end_pfn)
{
- unsigned long isolated, pfn, block_end_pfn, flags;
- struct zone *zone = NULL;
+ unsigned long isolated, pfn, block_end_pfn;
LIST_HEAD(freelist);
- if (pfn_valid(start_pfn))
- zone = page_zone(pfn_to_page(start_pfn));
-
for (pfn = start_pfn; pfn < end_pfn; pfn += isolated) {
- if (!pfn_valid(pfn) || zone != page_zone(pfn_to_page(pfn)))
+ if (!pfn_valid(pfn) || cc->zone != page_zone(pfn_to_page(pfn)))
break;
/*
@@ -139,10 +354,8 @@
block_end_pfn = ALIGN(pfn + 1, pageblock_nr_pages);
block_end_pfn = min(block_end_pfn, end_pfn);
- spin_lock_irqsave(&zone->lock, flags);
- isolated = isolate_freepages_block(pfn, block_end_pfn,
+ isolated = isolate_freepages_block(cc, pfn, block_end_pfn,
&freelist, true);
- spin_unlock_irqrestore(&zone->lock, flags);
/*
* In strict mode, isolate_freepages_block() returns 0 if
@@ -173,7 +386,7 @@
}
/* Update the number of anon and file isolated pages in the zone */
-static void acct_isolated(struct zone *zone, struct compact_control *cc)
+static void acct_isolated(struct zone *zone, bool locked, struct compact_control *cc)
{
struct page *page;
unsigned int count[2] = { 0, };
@@ -181,8 +394,14 @@
list_for_each_entry(page, &cc->migratepages, lru)
count[!!page_is_file_cache(page)]++;
- __mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]);
- __mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]);
+ /* If locked we can use the interrupt unsafe versions */
+ if (locked) {
+ __mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]);
+ __mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]);
+ } else {
+ mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]);
+ mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]);
+ }
}
/* Similar to reclaim, but different enough that they don't share logic */
@@ -206,6 +425,7 @@
* @cc: Compaction control structure.
* @low_pfn: The first PFN of the range.
* @end_pfn: The one-past-the-last PFN of the range.
+ * @unevictable: true if it allows to isolate unevictable pages
*
* Isolate all pages that can be migrated from the range specified by
* [low_pfn, end_pfn). Returns zero if there is a fatal signal
@@ -221,12 +441,15 @@
*/
unsigned long
isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
- unsigned long low_pfn, unsigned long end_pfn)
+ unsigned long low_pfn, unsigned long end_pfn, bool unevictable)
{
unsigned long last_pageblock_nr = 0, pageblock_nr;
unsigned long nr_scanned = 0, nr_isolated = 0;
struct list_head *migratelist = &cc->migratepages;
- isolate_mode_t mode = ISOLATE_ACTIVE|ISOLATE_INACTIVE;
+ isolate_mode_t mode = 0;
+ unsigned long flags;
+ bool locked = false;
+ struct page *page = NULL, *valid_page = NULL;
/*
* Ensure that there are not too many pages isolated from the LRU
@@ -246,25 +469,14 @@
/* Time to isolate some pages for migration */
cond_resched();
- spin_lock_irq(&zone->lru_lock);
for (; low_pfn < end_pfn; low_pfn++) {
- struct page *page;
- bool locked = true;
-
/* give a chance to irqs before checking need_resched() */
- if (!((low_pfn+1) % SWAP_CLUSTER_MAX)) {
- spin_unlock_irq(&zone->lru_lock);
- locked = false;
+ if (locked && !((low_pfn+1) % SWAP_CLUSTER_MAX)) {
+ if (should_release_lock(&zone->lru_lock)) {
+ spin_unlock_irqrestore(&zone->lru_lock, flags);
+ locked = false;
+ }
}
- if (need_resched() || spin_is_contended(&zone->lru_lock)) {
- if (locked)
- spin_unlock_irq(&zone->lru_lock);
- cond_resched();
- spin_lock_irq(&zone->lru_lock);
- if (fatal_signal_pending(current))
- break;
- } else if (!locked)
- spin_lock_irq(&zone->lru_lock);
/*
* migrate_pfn does not necessarily start aligned to a
@@ -293,6 +505,14 @@
if (page_zone(page) != zone)
continue;
+ if (!valid_page)
+ valid_page = page;
+
+ /* If isolation recently failed, do not retry */
+ pageblock_nr = low_pfn >> pageblock_order;
+ if (!isolation_suitable(cc, page))
+ goto next_pageblock;
+
/* Skip if free */
if (PageBuddy(page))
continue;
@@ -302,24 +522,43 @@
* migration is optimistic to see if the minimum amount of work
* satisfies the allocation
*/
- pageblock_nr = low_pfn >> pageblock_order;
if (!cc->sync && last_pageblock_nr != pageblock_nr &&
!migrate_async_suitable(get_pageblock_migratetype(page))) {
- low_pfn += pageblock_nr_pages;
- low_pfn = ALIGN(low_pfn, pageblock_nr_pages) - 1;
- last_pageblock_nr = pageblock_nr;
- continue;
+ cc->finished_update_migrate = true;
+ goto next_pageblock;
}
+ /* Check may be lockless but that's ok as we recheck later */
if (!PageLRU(page))
continue;
/*
- * PageLRU is set, and lru_lock excludes isolation,
- * splitting and collapsing (collapsing has already
- * happened if PageLRU is set).
+ * PageLRU is set. lru_lock normally excludes isolation
+ * splitting and collapsing (collapsing has already happened
+ * if PageLRU is set) but the lock is not necessarily taken
+ * here and it is wasteful to take it just to check transhuge.
+ * Check TransHuge without lock and skip the whole pageblock if
+ * it's either a transhuge or hugetlbfs page, as calling
+ * compound_order() without preventing THP from splitting the
+ * page underneath us may return surprising results.
*/
if (PageTransHuge(page)) {
+ if (!locked)
+ goto next_pageblock;
+ low_pfn += (1 << compound_order(page)) - 1;
+ continue;
+ }
+
+ /* Check if it is ok to still hold the lock */
+ locked = compact_checklock_irqsave(&zone->lru_lock, &flags,
+ locked, cc);
+ if (!locked || fatal_signal_pending(current))
+ break;
+
+ /* Recheck PageLRU and PageTransHuge under lock */
+ if (!PageLRU(page))
+ continue;
+ if (PageTransHuge(page)) {
low_pfn += (1 << compound_order(page)) - 1;
continue;
}
@@ -327,13 +566,17 @@
if (!cc->sync)
mode |= ISOLATE_ASYNC_MIGRATE;
+ if (unevictable)
+ mode |= ISOLATE_UNEVICTABLE;
+
/* Try isolate the page */
- if (__isolate_lru_page(page, mode, 0) != 0)
+ if (__isolate_lru_page(page, mode) != 0)
continue;
VM_BUG_ON(PageTransCompound(page));
/* Successfully isolated */
+ cc->finished_update_migrate = true;
del_page_from_lru_list(zone, page, page_lru(page));
list_add(&page->lru, migratelist);
cc->nr_migratepages++;
@@ -344,42 +587,35 @@
++low_pfn;
break;
}
+
+ continue;
+
+next_pageblock:
+ low_pfn += pageblock_nr_pages;
+ low_pfn = ALIGN(low_pfn, pageblock_nr_pages) - 1;
+ last_pageblock_nr = pageblock_nr;
}
- acct_isolated(zone, cc);
+ acct_isolated(zone, locked, cc);
- spin_unlock_irq(&zone->lru_lock);
+ if (locked)
+ spin_unlock_irqrestore(&zone->lru_lock, flags);
+
+ /* Update the pageblock-skip if the whole pageblock was scanned */
+ if (low_pfn == end_pfn)
+ update_pageblock_skip(cc, valid_page, nr_isolated, true);
trace_mm_compaction_isolate_migratepages(nr_scanned, nr_isolated);
+ count_compact_events(COMPACTMIGRATE_SCANNED, nr_scanned);
+ if (nr_isolated)
+ count_compact_events(COMPACTISOLATED, nr_isolated);
+
return low_pfn;
}
#endif /* CONFIG_COMPACTION || CONFIG_CMA */
#ifdef CONFIG_COMPACTION
-
-/* Returns true if the page is within a block suitable for migration to */
-static bool suitable_migration_target(struct page *page)
-{
-
- int migratetype = get_pageblock_migratetype(page);
-
- /* Don't interfere with memory hot-remove or the min_free_kbytes blocks */
- if (migratetype == MIGRATE_ISOLATE || migratetype == MIGRATE_RESERVE)
- return false;
-
- /* If the page is a large free page, then allow migration */
- if (PageBuddy(page) && page_order(page) >= pageblock_order)
- return true;
-
- /* If the block is MIGRATE_MOVABLE or MIGRATE_CMA, allow migration */
- if (migrate_async_suitable(migratetype))
- return true;
-
- /* Otherwise skip the block */
- return false;
-}
-
/*
* Based on information in the current compact_control, find blocks
* suitable for isolating free pages from and then isolate them.
@@ -389,7 +625,6 @@
{
struct page *page;
unsigned long high_pfn, low_pfn, pfn, zone_end_pfn, end_pfn;
- unsigned long flags;
int nr_freepages = cc->nr_freepages;
struct list_head *freelist = &cc->freepages;
@@ -437,29 +672,34 @@
if (!suitable_migration_target(page))
continue;
- /*
- * Found a block suitable for isolating free pages from. Now
- * we disabled interrupts, double check things are ok and
- * isolate the pages. This is to minimise the time IRQs
- * are disabled
- */
+ /* If isolation recently failed, do not retry */
+ if (!isolation_suitable(cc, page))
+ continue;
+
+ /* Found a block suitable for isolating free pages from */
isolated = 0;
- spin_lock_irqsave(&zone->lock, flags);
- if (suitable_migration_target(page)) {
- end_pfn = min(pfn + pageblock_nr_pages, zone_end_pfn);
- isolated = isolate_freepages_block(pfn, end_pfn,
- freelist, false);
- nr_freepages += isolated;
- }
- spin_unlock_irqrestore(&zone->lock, flags);
+
+ /*
+ * As pfn may not start aligned, pfn+pageblock_nr_page
+ * may cross a MAX_ORDER_NR_PAGES boundary and miss
+ * a pfn_valid check. Ensure isolate_freepages_block()
+ * only scans within a pageblock
+ */
+ end_pfn = ALIGN(pfn + 1, pageblock_nr_pages);
+ end_pfn = min(end_pfn, zone_end_pfn);
+ isolated = isolate_freepages_block(cc, pfn, end_pfn,
+ freelist, false);
+ nr_freepages += isolated;
/*
* Record the highest PFN we isolated pages from. When next
* looking for free pages, the search will restart here as
* page migration may have returned some pages to the allocator
*/
- if (isolated)
+ if (isolated) {
+ cc->finished_update_free = true;
high_pfn = max(high_pfn, pfn);
+ }
}
/* split_free_page does not map the pages */
@@ -544,8 +784,8 @@
}
/* Perform the isolation */
- low_pfn = isolate_migratepages_range(zone, cc, low_pfn, end_pfn);
- if (!low_pfn)
+ low_pfn = isolate_migratepages_range(zone, cc, low_pfn, end_pfn, false);
+ if (!low_pfn || cc->contended)
return ISOLATE_ABORT;
cc->migrate_pfn = low_pfn;
@@ -563,8 +803,18 @@
return COMPACT_PARTIAL;
/* Compaction run completes if the migrate and free scanner meet */
- if (cc->free_pfn <= cc->migrate_pfn)
+ if (cc->free_pfn <= cc->migrate_pfn) {
+ /*
+ * Mark that the PG_migrate_skip information should be cleared
+ * by kswapd when it goes to sleep. kswapd does not set the
+ * flag itself as the decision to be clear should be directly
+ * based on an allocation request.
+ */
+ if (!current_is_kswapd())
+ zone->compact_blockskip_flush = true;
+
return COMPACT_COMPLETE;
+ }
/*
* order == -1 is expected when compacting via
@@ -582,12 +832,14 @@
/* Direct compactor: Is a suitable page free? */
for (order = cc->order; order < MAX_ORDER; order++) {
+ struct free_area *area = &zone->free_area[order];
+
/* Job done if page is free of the right migratetype */
- if (!list_empty(&zone->free_area[order].free_list[cc->migratetype]))
+ if (!list_empty(&area->free_list[cc->migratetype]))
return COMPACT_PARTIAL;
/* Job done if allocation would set block type */
- if (order >= pageblock_order && zone->free_area[order].nr_free)
+ if (cc->order >= pageblock_order && area->nr_free)
return COMPACT_PARTIAL;
}
@@ -647,6 +899,8 @@
static int compact_zone(struct zone *zone, struct compact_control *cc)
{
int ret;
+ unsigned long start_pfn = zone->zone_start_pfn;
+ unsigned long end_pfn = zone->zone_start_pfn + zone->spanned_pages;
ret = compaction_suitable(zone, cc->order);
switch (ret) {
@@ -659,10 +913,29 @@
;
}
- /* Setup to move all movable pages to the end of the zone */
- cc->migrate_pfn = zone->zone_start_pfn;
- cc->free_pfn = cc->migrate_pfn + zone->spanned_pages;
- cc->free_pfn &= ~(pageblock_nr_pages-1);
+ /*
+ * Setup to move all movable pages to the end of the zone. Used cached
+ * information on where the scanners should start but check that it
+ * is initialised by ensuring the values are within zone boundaries.
+ */
+ cc->migrate_pfn = zone->compact_cached_migrate_pfn;
+ cc->free_pfn = zone->compact_cached_free_pfn;
+ if (cc->free_pfn < start_pfn || cc->free_pfn > end_pfn) {
+ cc->free_pfn = end_pfn & ~(pageblock_nr_pages-1);
+ zone->compact_cached_free_pfn = cc->free_pfn;
+ }
+ if (cc->migrate_pfn < start_pfn || cc->migrate_pfn > end_pfn) {
+ cc->migrate_pfn = start_pfn;
+ zone->compact_cached_migrate_pfn = cc->migrate_pfn;
+ }
+
+ /*
+ * Clear pageblock skip if there were failures recently and compaction
+ * is about to be retried after being deferred. kswapd does not do
+ * this reset as it'll reset the cached information when going to sleep.
+ */
+ if (compaction_restarting(zone, cc->order) && !current_is_kswapd())
+ __reset_isolation_suitable(zone);
migrate_prep_local();
@@ -673,6 +946,8 @@
switch (isolate_migratepages(zone, cc)) {
case ISOLATE_ABORT:
ret = COMPACT_PARTIAL;
+ putback_lru_pages(&cc->migratepages);
+ cc->nr_migratepages = 0;
goto out;
case ISOLATE_NONE:
continue;
@@ -687,10 +962,6 @@
update_nr_listpages(cc);
nr_remaining = cc->nr_migratepages;
- count_vm_event(COMPACTBLOCKS);
- count_vm_events(COMPACTPAGES, nr_migrate - nr_remaining);
- if (nr_remaining)
- count_vm_events(COMPACTPAGEFAILED, nr_remaining);
trace_mm_compaction_migratepages(nr_migrate - nr_remaining,
nr_remaining);
@@ -698,8 +969,11 @@
if (err) {
putback_lru_pages(&cc->migratepages);
cc->nr_migratepages = 0;
+ if (err == -ENOMEM) {
+ ret = COMPACT_PARTIAL;
+ goto out;
+ }
}
-
}
out:
@@ -712,8 +986,9 @@
static unsigned long compact_zone_order(struct zone *zone,
int order, gfp_t gfp_mask,
- bool sync)
+ bool sync, bool *contended)
{
+ unsigned long ret;
struct compact_control cc = {
.nr_freepages = 0,
.nr_migratepages = 0,
@@ -725,7 +1000,13 @@
INIT_LIST_HEAD(&cc.freepages);
INIT_LIST_HEAD(&cc.migratepages);
- return compact_zone(zone, &cc);
+ ret = compact_zone(zone, &cc);
+
+ VM_BUG_ON(!list_empty(&cc.freepages));
+ VM_BUG_ON(!list_empty(&cc.migratepages));
+
+ *contended = cc.contended;
+ return ret;
}
int sysctl_extfrag_threshold = 500;
@@ -737,12 +1018,14 @@
* @gfp_mask: The GFP mask of the current allocation
* @nodemask: The allowed nodes to allocate from
* @sync: Whether migration is synchronous or not
+ * @contended: Return value that is true if compaction was aborted due to lock contention
+ * @page: Optionally capture a free page of the requested order during compaction
*
* This is the main entry point for direct page compaction.
*/
unsigned long try_to_compact_pages(struct zonelist *zonelist,
int order, gfp_t gfp_mask, nodemask_t *nodemask,
- bool sync)
+ bool sync, bool *contended)
{
enum zone_type high_zoneidx = gfp_zone(gfp_mask);
int may_enter_fs = gfp_mask & __GFP_FS;
@@ -752,15 +1035,11 @@
int rc = COMPACT_SKIPPED;
int alloc_flags = 0;
- /*
- * Check whether it is worth even starting compaction. The order check is
- * made because an assumption is made that the page allocator can satisfy
- * the "cheaper" orders without taking special steps
- */
+ /* Check if the GFP flags allow compaction */
if (!order || !may_enter_fs || !may_perform_io)
return rc;
- count_vm_event(COMPACTSTALL);
+ count_compact_event(COMPACTSTALL);
#ifdef CONFIG_CMA
if (allocflags_to_migratetype(gfp_mask) == MIGRATE_MOVABLE)
@@ -771,7 +1050,8 @@
nodemask) {
int status;
- status = compact_zone_order(zone, order, gfp_mask, sync);
+ status = compact_zone_order(zone, order, gfp_mask, sync,
+ contended);
rc = max(status, rc);
/* If a normal allocation would succeed, stop compacting */
@@ -808,7 +1088,7 @@
if (cc->order > 0) {
int ok = zone_watermark_ok(zone, cc->order,
low_wmark_pages(zone), 0, 0);
- if (ok && cc->order > zone->compact_order_failed)
+ if (ok && cc->order >= zone->compact_order_failed)
zone->compact_order_failed = cc->order + 1;
/* Currently async compaction is never deferred. */
else if (!ok && cc->sync)
@@ -843,7 +1123,7 @@
}
/* Compact all nodes in the system */
-static int compact_nodes(void)
+static void compact_nodes(void)
{
int nid;
@@ -852,8 +1132,6 @@
for_each_online_node(nid)
compact_node(nid);
-
- return COMPACT_COMPLETE;
}
/* The written value is actually unused, all memory is compacted */
@@ -864,7 +1142,7 @@
void __user *buffer, size_t *length, loff_t *ppos)
{
if (write)
- return compact_nodes();
+ compact_nodes();
return 0;
}
diff --git a/mm/internal.h b/mm/internal.h
index 8c6fd44..3439ef4 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -120,17 +120,24 @@
unsigned long free_pfn; /* isolate_freepages search base */
unsigned long migrate_pfn; /* isolate_migratepages search base */
bool sync; /* Synchronous migration */
+ bool ignore_skip_hint; /* Scan blocks even if marked skip */
+ bool finished_update_free; /* True when the zone cached pfns are
+ * no longer being updated
+ */
+ bool finished_update_migrate;
int order; /* order a direct compactor needs */
int migratetype; /* MOVABLE, RECLAIMABLE etc */
struct zone *zone;
+ bool contended; /* True if a lock was contended */
};
unsigned long
-isolate_freepages_range(unsigned long start_pfn, unsigned long end_pfn);
+isolate_freepages_range(struct compact_control *cc,
+ unsigned long start_pfn, unsigned long end_pfn);
unsigned long
isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
- unsigned long low_pfn, unsigned long end_pfn);
+ unsigned long low_pfn, unsigned long end_pfn, bool unevictable);
#endif
@@ -356,3 +363,6 @@
#define ALLOC_HIGH 0x20 /* __GFP_HIGH set */
#define ALLOC_CPUSET 0x40 /* check for correct cpuset */
#define ALLOC_CMA 0x80 /* allow allocations from CMA areas */
+
+unsigned long reclaim_clean_pages_from_list(struct zone *zone,
+ struct list_head *page_list);
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 7685d4a..d436634 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -1116,11 +1116,6 @@
mz->lru_size[lru] -= 1 << compound_order(page);
}
-void mem_cgroup_lru_del(struct page *page)
-{
- mem_cgroup_lru_del_list(page, page_lru(page));
-}
-
/**
* mem_cgroup_lru_move_lists - account for moving a page between lrus
* @zone: zone of the page
@@ -1149,15 +1144,25 @@
* Checks whether given mem is same or in the root_mem_cgroup's
* hierarchy subtree
*/
-static bool mem_cgroup_same_or_subtree(const struct mem_cgroup *root_memcg,
- struct mem_cgroup *memcg)
+bool __mem_cgroup_same_or_subtree(const struct mem_cgroup *root_memcg,
+ struct mem_cgroup *memcg)
{
- if (root_memcg != memcg) {
- return (root_memcg->use_hierarchy &&
- css_is_ancestor(&memcg->css, &root_memcg->css));
- }
+ if (root_memcg == memcg)
+ return true;
+ if (!root_memcg->use_hierarchy)
+ return false;
+ return css_is_ancestor(&memcg->css, &root_memcg->css);
+}
- return true;
+static bool mem_cgroup_same_or_subtree(const struct mem_cgroup *root_memcg,
+ struct mem_cgroup *memcg)
+{
+ bool ret;
+
+ rcu_read_lock();
+ ret = __mem_cgroup_same_or_subtree(root_memcg, memcg);
+ rcu_read_unlock();
+ return ret;
}
int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *memcg)
@@ -5610,7 +5615,6 @@
if (mm) {
if (mc.to)
mem_cgroup_move_charge(mm);
- put_swap_token(mm);
mmput(mm);
}
if (mc.to)
diff --git a/mm/memory.c b/mm/memory.c
index 2111354..b6ab040 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -2935,7 +2935,6 @@
delayacct_set_flag(DELAYACCT_PF_SWAPIN);
page = lookup_swap_cache(entry);
if (!page) {
- grab_swap_token(mm); /* Contend for token _before_ read-in */
page = swapin_readahead(entry,
GFP_HIGHUSER_MOVABLE, vma, address);
if (!page) {
@@ -2965,6 +2964,7 @@
}
locked = lock_page_or_retry(page, mm, flags);
+
delayacct_clear_flag(DELAYACCT_PF_SWAPIN);
if (!locked) {
ret |= VM_FAULT_RETRY;
diff --git a/mm/migrate.c b/mm/migrate.c
index 79a791f..4f5c02e 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -969,6 +969,7 @@
{
int retry = 1;
int nr_failed = 0;
+ int nr_succeeded = 0;
int pass = 0;
struct page *page;
struct page *page2;
@@ -997,6 +998,7 @@
trace_migrate_retry(retry);
break;
case 0:
+ nr_succeeded++;
break;
default:
/* Permanent failure */
@@ -1007,6 +1009,10 @@
}
rc = 0;
out:
+ if (nr_succeeded)
+ count_vm_events(PGMIGRATE_SUCCESS, nr_succeeded);
+ if (nr_failed)
+ count_vm_events(PGMIGRATE_FAIL, nr_failed);
if (!swapwrite)
current->flags &= ~PF_SWAPWRITE;
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 8a93508..5148c1a 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1441,6 +1441,45 @@
set_page_refcounted(page + i);
}
+static int __isolate_free_page(struct page *page, unsigned int order)
+{
+ unsigned long watermark;
+ struct zone *zone;
+ int mt;
+
+ BUG_ON(!PageBuddy(page));
+
+ zone = page_zone(page);
+ mt = get_pageblock_migratetype(page);
+
+ if (mt != MIGRATE_ISOLATE && !is_migrate_cma(mt)) {
+ /* Obey watermarks as if the page was being allocated */
+ watermark = low_wmark_pages(zone) + (1 << order);
+ if (!zone_watermark_ok(zone, 0, watermark, 0, 0))
+ return 0;
+
+ __mod_zone_freepage_state(zone, -(1UL << order), mt);
+ }
+
+ /* Remove page from free list */
+ list_del(&page->lru);
+ zone->free_area[order].nr_free--;
+ rmv_page_order(page);
+
+ /* Set the pageblock if the isolated page is at least a pageblock */
+ if (order >= pageblock_order - 1) {
+ struct page *endpage = page + (1 << order) - 1;
+ for (; page < endpage; page += pageblock_nr_pages) {
+ mt = get_pageblock_migratetype(page);
+ if (mt != MIGRATE_ISOLATE && !is_migrate_cma(mt))
+ set_pageblock_migratetype(page,
+ MIGRATE_MOVABLE);
+ }
+ }
+
+ return 1UL << order;
+}
+
/*
* Similar to split_page except the page is already free. As this is only
* being used for migration, the migratetype of the block also changes.
@@ -1454,45 +1493,18 @@
int split_free_page(struct page *page)
{
unsigned int order;
- unsigned long watermark;
- struct zone *zone;
- int mt;
+ int nr_pages;
- BUG_ON(!PageBuddy(page));
-
- zone = page_zone(page);
order = page_order(page);
- mt = get_pageblock_migratetype(page);
- /* Obey watermarks as if the page was being allocated */
- watermark = low_wmark_pages(zone) + (1 << order);
- if (!is_migrate_cma(mt) && mt != MIGRATE_ISOLATE &&
- !zone_watermark_ok(zone, 0, watermark, 0, 0))
+ nr_pages = __isolate_free_page(page, order);
+ if (!nr_pages)
return 0;
- /* Remove page from free list */
- list_del(&page->lru);
- zone->free_area[order].nr_free--;
- rmv_page_order(page);
-
- if (unlikely(mt != MIGRATE_ISOLATE))
- __mod_zone_freepage_state(zone, -(1UL << order), mt);
-
/* Split into individual pages */
set_page_refcounted(page);
split_page(page, order);
-
- if (order >= pageblock_order - 1) {
- struct page *endpage = page + (1 << order) - 1;
- for (; page < endpage; page += pageblock_nr_pages) {
- mt = get_pageblock_migratetype(page);
- if (mt != MIGRATE_ISOLATE && !is_migrate_cma(mt))
- set_pageblock_migratetype(page,
- MIGRATE_MOVABLE);
- }
- }
-
- return 1 << order;
+ return nr_pages;
}
/*
@@ -2134,11 +2146,9 @@
struct zonelist *zonelist, enum zone_type high_zoneidx,
nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone,
int migratetype, bool sync_migration,
- bool *deferred_compaction,
+ bool *contended_compaction, bool *deferred_compaction,
unsigned long *did_some_progress)
{
- struct page *page;
-
if (!order)
return NULL;
@@ -2149,9 +2159,12 @@
current->flags |= PF_MEMALLOC;
*did_some_progress = try_to_compact_pages(zonelist, order, gfp_mask,
- nodemask, sync_migration);
+ nodemask, sync_migration,
+ contended_compaction);
current->flags &= ~PF_MEMALLOC;
+
if (*did_some_progress != COMPACT_SKIPPED) {
+ struct page *page;
/* Page migration frees to the PCP lists but we want merging */
drain_pages(get_cpu());
@@ -2162,6 +2175,7 @@
alloc_flags, preferred_zone,
migratetype);
if (page) {
+ preferred_zone->compact_blockskip_flush = false;
preferred_zone->compact_considered = 0;
preferred_zone->compact_defer_shift = 0;
if (order >= preferred_zone->compact_order_failed)
@@ -2195,7 +2209,7 @@
struct zonelist *zonelist, enum zone_type high_zoneidx,
nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone,
int migratetype, bool sync_migration,
- bool *deferred_compaction,
+ bool *contended_compaction, bool *deferred_compaction,
unsigned long *did_some_progress)
{
return NULL;
@@ -2362,6 +2376,7 @@
unsigned long did_some_progress;
bool sync_migration = false;
bool deferred_compaction = false;
+ bool contended_compaction = false;
/*
* In the slowpath, we sanity check order to avoid ever trying to
@@ -2443,6 +2458,7 @@
nodemask,
alloc_flags, preferred_zone,
migratetype, sync_migration,
+ &contended_compaction,
&deferred_compaction,
&did_some_progress);
if (page)
@@ -2452,10 +2468,11 @@
/*
* If compaction is deferred for high-order allocations, it is because
* sync compaction recently failed. In this is the case and the caller
- * has requested the system not be heavily disrupted, fail the
- * allocation now instead of entering direct reclaim
+ * requested a movable allocation that does not heavily disrupt the
+ * system then fail the allocation instead of entering direct reclaim.
*/
- if (deferred_compaction && (gfp_mask & __GFP_NO_KSWAPD))
+ if ((deferred_compaction || contended_compaction) &&
+ (gfp_mask & __GFP_NO_KSWAPD))
goto nopage;
/* Try direct reclaim and then allocating */
@@ -2526,6 +2543,7 @@
nodemask,
alloc_flags, preferred_zone,
migratetype, sync_migration,
+ &contended_compaction,
&deferred_compaction,
&did_some_progress);
if (page)
@@ -5592,26 +5610,28 @@
}
/*
- * This is designed as sub function...plz see page_isolation.c also.
- * set/clear page block's type to be ISOLATE.
- * page allocater never alloc memory from ISOLATE block.
+ * This function checks whether pageblock includes unmovable pages or not.
+ * If @count is not zero, it is okay to include less @count unmovable pages
+ *
+ * PageLRU check wihtout isolation or lru_lock could race so that
+ * MIGRATE_MOVABLE block might include unmovable pages. It means you can't
+ * expect this function should be exact.
*/
-
-static int
-__count_immobile_pages(struct zone *zone, struct page *page, int count)
+static bool
+__has_unmovable_pages(struct zone *zone, struct page *page, int count)
{
unsigned long pfn, iter, found;
int mt;
/*
* For avoiding noise data, lru_add_drain_all() should be called
- * If ZONE_MOVABLE, the zone never contains immobile pages
+ * If ZONE_MOVABLE, the zone never contains unmovable pages
*/
if (zone_idx(zone) == ZONE_MOVABLE)
- return true;
+ return false;
mt = get_pageblock_migratetype(page);
if (mt == MIGRATE_MOVABLE || is_migrate_cma(mt))
- return true;
+ return false;
pfn = page_to_pfn(page);
for (found = 0, iter = 0; iter < pageblock_nr_pages; iter++) {
@@ -5621,11 +5641,18 @@
continue;
page = pfn_to_page(check);
- if (!page_count(page)) {
+ /*
+ * We can't use page_count without pin a page
+ * because another CPU can free compound page.
+ * This check already skips compound tails of THP
+ * because their page->_count is zero at all time.
+ */
+ if (!atomic_read(&page->_count)) {
if (PageBuddy(page))
iter += (1 << page_order(page)) - 1;
continue;
}
+
if (!PageLRU(page))
found++;
/*
@@ -5642,9 +5669,9 @@
* page at boot.
*/
if (found > count)
- return false;
+ return true;
}
- return true;
+ return false;
}
bool is_pageblock_removable_nolock(struct page *page)
@@ -5668,7 +5695,7 @@
zone->zone_start_pfn + zone->spanned_pages <= pfn)
return false;
- return __count_immobile_pages(zone, page, 0);
+ return !__has_unmovable_pages(zone, page, 0);
}
int set_migratetype_isolate(struct page *page)
@@ -5707,12 +5734,12 @@
* FIXME: Now, memory hotplug doesn't call shrink_slab() by itself.
* We just check MOVABLE pages.
*/
- if (__count_immobile_pages(zone, page, arg.pages_found))
+ if (!__has_unmovable_pages(zone, page, arg.pages_found))
ret = 0;
-
/*
- * immobile means "not-on-lru" paes. If immobile is larger than
- * removable-by-driver pages reported by notifier, we'll fail.
+ * Unmovable means "not-on-lru" pages. If Unmovable pages are
+ * larger than removable-by-driver pages reported by notifier,
+ * we'll fail.
*/
out:
@@ -5775,34 +5802,27 @@
}
/* [start, end) must belong to a single zone. */
-static int __alloc_contig_migrate_range(unsigned long start, unsigned long end)
+static int __alloc_contig_migrate_range(struct compact_control *cc,
+ unsigned long start, unsigned long end)
{
/* This function is based on compact_zone() from compaction.c. */
-
+ unsigned long nr_reclaimed;
unsigned long pfn = start;
unsigned int tries = 0;
int ret = 0;
- struct compact_control cc = {
- .nr_migratepages = 0,
- .order = -1,
- .zone = page_zone(pfn_to_page(start)),
- .sync = true,
- };
- INIT_LIST_HEAD(&cc.migratepages);
-
migrate_prep();
- while (pfn < end || !list_empty(&cc.migratepages)) {
+ while (pfn < end || !list_empty(&cc->migratepages)) {
if (fatal_signal_pending(current)) {
ret = -EINTR;
break;
}
- if (list_empty(&cc.migratepages)) {
- cc.nr_migratepages = 0;
- pfn = isolate_migratepages_range(cc.zone, &cc,
- pfn, end);
+ if (list_empty(&cc->migratepages)) {
+ cc->nr_migratepages = 0;
+ pfn = isolate_migratepages_range(cc->zone, cc,
+ pfn, end, true);
if (!pfn) {
ret = -EINTR;
break;
@@ -5813,12 +5833,16 @@
break;
}
- ret = migrate_pages(&cc.migratepages,
+ nr_reclaimed = reclaim_clean_pages_from_list(cc->zone, &cc->migratepages);
+
+ cc->nr_migratepages -= nr_reclaimed;
+
+ ret = migrate_pages(&cc->migratepages,
__alloc_contig_migrate_alloc,
0, false, MIGRATE_SYNC);
}
- putback_lru_pages(&cc.migratepages);
+ putback_lru_pages(&cc->migratepages);
return ret > 0 ? 0 : ret;
}
@@ -5849,6 +5873,15 @@
unsigned long outer_start, outer_end;
int ret = 0, order;
+ struct compact_control cc = {
+ .nr_migratepages = 0,
+ .order = -1,
+ .zone = page_zone(pfn_to_page(start)),
+ .sync = true,
+ .ignore_skip_hint = true,
+ };
+ INIT_LIST_HEAD(&cc.migratepages);
+
/*
* What we do here is we mark all pageblocks in range as
* MIGRATE_ISOLATE. Because pageblock and max order pages may
@@ -5880,7 +5913,7 @@
zone->cma_alloc = 1;
- ret = __alloc_contig_migrate_range(start, end);
+ ret = __alloc_contig_migrate_range(&cc, start, end);
if (ret)
goto done;
@@ -5924,7 +5957,7 @@
/* Grab isolated pages from freelists. */
- outer_end = isolate_freepages_range(outer_start, end);
+ outer_end = isolate_freepages_range(&cc, outer_start, end);
if (!outer_end) {
ret = -EBUSY;
goto done;
@@ -5945,8 +5978,15 @@
void free_contig_range(unsigned long pfn, unsigned nr_pages)
{
- for (; nr_pages--; ++pfn)
- __free_page(pfn_to_page(pfn));
+ unsigned int count = 0;
+
+ for (; nr_pages--; pfn++) {
+ struct page *page = pfn_to_page(pfn);
+
+ count += page_count(page) != 1;
+ __free_page(page);
+ }
+ WARN(count != 0, "%d pages are still in use!\n", count);
}
#endif
diff --git a/mm/rmap.c b/mm/rmap.c
index 5b5ad58..0f3b7cd 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -755,12 +755,6 @@
pte_unmap_unlock(pte, ptl);
}
- /* Pretend the page is referenced if the task has the
- swap token and is in the middle of a page fault. */
- if (mm != current->mm && has_swap_token(mm) &&
- rwsem_is_locked(&mm->mmap_sem))
- referenced++;
-
(*mapcount)--;
if (referenced)
diff --git a/mm/thrash.c b/mm/thrash.c
deleted file mode 100644
index 57ad495..0000000
--- a/mm/thrash.c
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * mm/thrash.c
- *
- * Copyright (C) 2004, Red Hat, Inc.
- * Copyright (C) 2004, Rik van Riel <riel@redhat.com>
- * Released under the GPL, see the file COPYING for details.
- *
- * Simple token based thrashing protection, using the algorithm
- * described in: http://www.cse.ohio-state.edu/hpcs/WWW/HTML/publications/abs05-1.html
- *
- * Sep 2006, Ashwin Chaugule <ashwin.chaugule@celunite.com>
- * Improved algorithm to pass token:
- * Each task has a priority which is incremented if it contended
- * for the token in an interval less than its previous attempt.
- * If the token is acquired, that task's priority is boosted to prevent
- * the token from bouncing around too often and to let the task make
- * some progress in its execution.
- */
-
-#include <linux/jiffies.h>
-#include <linux/mm.h>
-#include <linux/sched.h>
-#include <linux/swap.h>
-#include <linux/memcontrol.h>
-
-#include <trace/events/vmscan.h>
-
-#define TOKEN_AGING_INTERVAL (0xFF)
-
-static DEFINE_SPINLOCK(swap_token_lock);
-struct mm_struct *swap_token_mm;
-static struct mem_cgroup *swap_token_memcg;
-
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
-static struct mem_cgroup *swap_token_memcg_from_mm(struct mm_struct *mm)
-{
- struct mem_cgroup *memcg;
-
- memcg = try_get_mem_cgroup_from_mm(mm);
- if (memcg)
- css_put(mem_cgroup_css(memcg));
-
- return memcg;
-}
-#else
-static struct mem_cgroup *swap_token_memcg_from_mm(struct mm_struct *mm)
-{
- return NULL;
-}
-#endif
-
-void grab_swap_token(struct mm_struct *mm)
-{
- int current_interval;
- unsigned int old_prio = mm->token_priority;
- static unsigned int global_faults;
- static unsigned int last_aging;
-
- global_faults++;
-
- current_interval = global_faults - mm->faultstamp;
-
- if (!spin_trylock(&swap_token_lock))
- return;
-
- /* First come first served */
- if (!swap_token_mm)
- goto replace_token;
-
- /*
- * Usually, we don't need priority aging because long interval faults
- * makes priority decrease quickly. But there is one exception. If the
- * token owner task is sleeping, it never make long interval faults.
- * Thus, we need a priority aging mechanism instead. The requirements
- * of priority aging are
- * 1) An aging interval is reasonable enough long. Too short aging
- * interval makes quick swap token lost and decrease performance.
- * 2) The swap token owner task have to get priority aging even if
- * it's under sleep.
- */
- if ((global_faults - last_aging) > TOKEN_AGING_INTERVAL) {
- swap_token_mm->token_priority /= 2;
- last_aging = global_faults;
- }
-
- if (mm == swap_token_mm) {
- mm->token_priority += 2;
- goto update_priority;
- }
-
- if (current_interval < mm->last_interval)
- mm->token_priority++;
- else {
- if (likely(mm->token_priority > 0))
- mm->token_priority--;
- }
-
- /* Check if we deserve the token */
- if (mm->token_priority > swap_token_mm->token_priority)
- goto replace_token;
-
-update_priority:
- trace_update_swap_token_priority(mm, old_prio, swap_token_mm);
-
-out:
- mm->faultstamp = global_faults;
- mm->last_interval = current_interval;
- spin_unlock(&swap_token_lock);
- return;
-
-replace_token:
- mm->token_priority += 2;
- trace_replace_swap_token(swap_token_mm, mm);
- swap_token_mm = mm;
- swap_token_memcg = swap_token_memcg_from_mm(mm);
- last_aging = global_faults;
- goto out;
-}
-
-/* Called on process exit. */
-void __put_swap_token(struct mm_struct *mm)
-{
- spin_lock(&swap_token_lock);
- if (likely(mm == swap_token_mm)) {
- trace_put_swap_token(swap_token_mm);
- swap_token_mm = NULL;
- swap_token_memcg = NULL;
- }
- spin_unlock(&swap_token_lock);
-}
-
-static bool match_memcg(struct mem_cgroup *a, struct mem_cgroup *b)
-{
- if (!a)
- return true;
- if (!b)
- return true;
- if (a == b)
- return true;
- return false;
-}
-
-void disable_swap_token(struct mem_cgroup *memcg)
-{
- /* memcg reclaim don't disable unrelated mm token. */
- if (match_memcg(memcg, swap_token_memcg)) {
- spin_lock(&swap_token_lock);
- if (match_memcg(memcg, swap_token_memcg)) {
- trace_disable_swap_token(swap_token_mm);
- swap_token_mm = NULL;
- swap_token_memcg = NULL;
- }
- spin_unlock(&swap_token_lock);
- }
-}
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 33dc256..c69f5e2 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -53,24 +53,6 @@
#define CREATE_TRACE_POINTS
#include <trace/events/vmscan.h>
-/*
- * reclaim_mode determines how the inactive list is shrunk
- * RECLAIM_MODE_SINGLE: Reclaim only order-0 pages
- * RECLAIM_MODE_ASYNC: Do not block
- * RECLAIM_MODE_SYNC: Allow blocking e.g. call wait_on_page_writeback
- * RECLAIM_MODE_LUMPYRECLAIM: For high-order allocations, take a reference
- * page from the LRU and reclaim all pages within a
- * naturally aligned range
- * RECLAIM_MODE_COMPACTION: For high-order allocations, reclaim a number of
- * order-0 pages and then compact the zone
- */
-typedef unsigned __bitwise__ reclaim_mode_t;
-#define RECLAIM_MODE_SINGLE ((__force reclaim_mode_t)0x01u)
-#define RECLAIM_MODE_ASYNC ((__force reclaim_mode_t)0x02u)
-#define RECLAIM_MODE_SYNC ((__force reclaim_mode_t)0x04u)
-#define RECLAIM_MODE_LUMPYRECLAIM ((__force reclaim_mode_t)0x08u)
-#define RECLAIM_MODE_COMPACTION ((__force reclaim_mode_t)0x10u)
-
struct scan_control {
/* Incremented by the number of inactive pages that were scanned */
unsigned long nr_scanned;
@@ -96,11 +78,8 @@
int order;
- /*
- * Intend to reclaim enough continuous memory rather than reclaim
- * enough amount of memory. i.e, mode for high order allocation.
- */
- reclaim_mode_t reclaim_mode;
+ /* Scan (total_size >> priority) pages at once */
+ int priority;
/*
* The memory cgroup that hit its limit and as a result is the
@@ -164,26 +143,16 @@
{
return !sc->target_mem_cgroup;
}
-
-static bool scanning_global_lru(struct mem_cgroup_zone *mz)
-{
- return !mz->mem_cgroup;
-}
#else
static bool global_reclaim(struct scan_control *sc)
{
return true;
}
-
-static bool scanning_global_lru(struct mem_cgroup_zone *mz)
-{
- return true;
-}
#endif
static struct zone_reclaim_stat *get_reclaim_stat(struct mem_cgroup_zone *mz)
{
- if (!scanning_global_lru(mz))
+ if (!mem_cgroup_disabled())
return mem_cgroup_get_reclaim_stat(mz->mem_cgroup, mz->zone);
return &mz->zone->reclaim_stat;
@@ -192,7 +161,7 @@
static unsigned long zone_nr_lru_pages(struct mem_cgroup_zone *mz,
enum lru_list lru)
{
- if (!scanning_global_lru(mz))
+ if (!mem_cgroup_disabled())
return mem_cgroup_zone_nr_lru_pages(mz->mem_cgroup,
zone_to_nid(mz->zone),
zone_idx(mz->zone),
@@ -364,39 +333,6 @@
return ret;
}
-static void set_reclaim_mode(int priority, struct scan_control *sc,
- bool sync)
-{
- reclaim_mode_t syncmode = sync ? RECLAIM_MODE_SYNC : RECLAIM_MODE_ASYNC;
-
- /*
- * Initially assume we are entering either lumpy reclaim or
- * reclaim/compaction.Depending on the order, we will either set the
- * sync mode or just reclaim order-0 pages later.
- */
- if (COMPACTION_BUILD)
- sc->reclaim_mode = RECLAIM_MODE_COMPACTION;
- else
- sc->reclaim_mode = RECLAIM_MODE_LUMPYRECLAIM;
-
- /*
- * Avoid using lumpy reclaim or reclaim/compaction if possible by
- * restricting when its set to either costly allocations or when
- * under memory pressure
- */
- if (sc->order > PAGE_ALLOC_COSTLY_ORDER)
- sc->reclaim_mode |= syncmode;
- else if (sc->order && priority < DEF_PRIORITY - 2)
- sc->reclaim_mode |= syncmode;
- else
- sc->reclaim_mode = RECLAIM_MODE_SINGLE | RECLAIM_MODE_ASYNC;
-}
-
-static void reset_reclaim_mode(struct scan_control *sc)
-{
- sc->reclaim_mode = RECLAIM_MODE_SINGLE | RECLAIM_MODE_ASYNC;
-}
-
static inline int is_page_cache_freeable(struct page *page)
{
/*
@@ -416,10 +352,6 @@
return 1;
if (bdi == current->backing_dev_info)
return 1;
-
- /* lumpy reclaim for hugepage often need a lot of write */
- if (sc->order > PAGE_ALLOC_COSTLY_ORDER)
- return 1;
return 0;
}
@@ -523,8 +455,7 @@
/* synchronous write or broken a_ops? */
ClearPageReclaim(page);
}
- trace_mm_vmscan_writepage(page,
- trace_reclaim_flags(page, sc->reclaim_mode));
+ trace_mm_vmscan_writepage(page, trace_reclaim_flags(page));
inc_zone_page_state(page, NR_VMSCAN_WRITE);
return PAGE_SUCCESS;
}
@@ -701,19 +632,15 @@
};
static enum page_references page_check_references(struct page *page,
- struct mem_cgroup_zone *mz,
struct scan_control *sc)
{
int referenced_ptes, referenced_page;
unsigned long vm_flags;
- referenced_ptes = page_referenced(page, 1, mz->mem_cgroup, &vm_flags);
+ referenced_ptes = page_referenced(page, 1, sc->target_mem_cgroup,
+ &vm_flags);
referenced_page = TestClearPageReferenced(page);
- /* Lumpy reclaim - ignore references */
- if (sc->reclaim_mode & RECLAIM_MODE_LUMPYRECLAIM)
- return PAGEREF_RECLAIM;
-
/*
* Mlock lost the isolation race with us. Let try_to_unmap()
* move the page to the unevictable list.
@@ -763,11 +690,12 @@
* shrink_page_list() returns the number of reclaimed pages
*/
static unsigned long shrink_page_list(struct list_head *page_list,
- struct mem_cgroup_zone *mz,
+ struct zone *zone,
struct scan_control *sc,
- int priority,
+ enum ttu_flags ttu_flags,
unsigned long *ret_nr_dirty,
- unsigned long *ret_nr_writeback)
+ unsigned long *ret_nr_writeback,
+ bool force_reclaim)
{
LIST_HEAD(ret_pages);
LIST_HEAD(free_pages);
@@ -780,10 +708,10 @@
cond_resched();
while (!list_empty(page_list)) {
- enum page_references references;
struct address_space *mapping;
struct page *page;
int may_enter_fs;
+ enum page_references references = PAGEREF_RECLAIM_CLEAN;
cond_resched();
@@ -794,7 +722,7 @@
goto keep;
VM_BUG_ON(PageActive(page));
- VM_BUG_ON(page_zone(page) != mz->zone);
+ VM_BUG_ON(page_zone(page) != zone);
sc->nr_scanned++;
@@ -813,22 +741,13 @@
if (PageWriteback(page)) {
nr_writeback++;
- /*
- * Synchronous reclaim cannot queue pages for
- * writeback due to the possibility of stack overflow
- * but if it encounters a page under writeback, wait
- * for the IO to complete.
- */
- if ((sc->reclaim_mode & RECLAIM_MODE_SYNC) &&
- may_enter_fs)
- wait_on_page_writeback(page);
- else {
- unlock_page(page);
- goto keep_lumpy;
- }
+ unlock_page(page);
+ goto keep;
}
- references = page_check_references(page, mz, sc);
+ if (!force_reclaim)
+ references = page_check_references(page, sc);
+
switch (references) {
case PAGEREF_ACTIVATE:
goto activate_locked;
@@ -858,7 +777,7 @@
* processes. Try to unmap it here.
*/
if (page_mapped(page) && mapping) {
- switch (try_to_unmap(page, TTU_UNMAP)) {
+ switch (try_to_unmap(page, ttu_flags)) {
case SWAP_FAIL:
goto activate_locked;
case SWAP_AGAIN:
@@ -879,7 +798,8 @@
* unless under significant pressure.
*/
if (page_is_file_cache(page) &&
- (!current_is_kswapd() || priority >= DEF_PRIORITY - 2)) {
+ (!current_is_kswapd() ||
+ sc->priority >= DEF_PRIORITY - 2)) {
/*
* Immediately reclaim when written back.
* Similar in principal to deactivate_page()
@@ -908,7 +828,7 @@
goto activate_locked;
case PAGE_SUCCESS:
if (PageWriteback(page))
- goto keep_lumpy;
+ goto keep;
if (PageDirty(page))
goto keep;
@@ -994,7 +914,6 @@
try_to_free_swap(page);
unlock_page(page);
putback_lru_page(page);
- reset_reclaim_mode(sc);
continue;
activate_locked:
@@ -1007,8 +926,6 @@
keep_locked:
unlock_page(page);
keep:
- reset_reclaim_mode(sc);
-keep_lumpy:
list_add(&page->lru, &ret_pages);
VM_BUG_ON(PageLRU(page) || PageUnevictable(page));
}
@@ -1020,7 +937,7 @@
* will encounter the same problem
*/
if (nr_dirty && nr_dirty == nr_congested && global_reclaim(sc))
- zone_set_flag(mz->zone, ZONE_CONGESTED);
+ zone_set_flag(zone, ZONE_CONGESTED);
free_hot_cold_page_list(&free_pages, 1);
@@ -1031,6 +948,33 @@
return nr_reclaimed;
}
+unsigned long reclaim_clean_pages_from_list(struct zone *zone,
+ struct list_head *page_list)
+{
+ struct scan_control sc = {
+ .gfp_mask = GFP_KERNEL,
+ .priority = DEF_PRIORITY,
+ .may_unmap = 1,
+ };
+ unsigned long ret, dummy1, dummy2;
+ struct page *page, *next;
+ LIST_HEAD(clean_pages);
+
+ list_for_each_entry_safe(page, next, page_list, lru) {
+ if (page_is_file_cache(page) && !PageDirty(page)) {
+ ClearPageActive(page);
+ list_move(&page->lru, &clean_pages);
+ }
+ }
+
+ ret = shrink_page_list(&clean_pages, zone, &sc,
+ TTU_UNMAP|TTU_IGNORE_ACCESS,
+ &dummy1, &dummy2, true);
+ list_splice(&clean_pages, page_list);
+ __mod_zone_page_state(zone, NR_ISOLATED_FILE, -ret);
+ return ret;
+}
+
/*
* Attempt to remove the specified page from its LRU. Only take this page
* if it is of the appropriate PageActive status. Pages which are being
@@ -1041,35 +985,16 @@
*
* returns 0 on success, -ve errno on failure.
*/
-int __isolate_lru_page(struct page *page, isolate_mode_t mode, int file)
+int __isolate_lru_page(struct page *page, isolate_mode_t mode)
{
- bool all_lru_mode;
int ret = -EINVAL;
/* Only take pages on the LRU. */
if (!PageLRU(page))
return ret;
- all_lru_mode = (mode & (ISOLATE_ACTIVE|ISOLATE_INACTIVE)) ==
- (ISOLATE_ACTIVE|ISOLATE_INACTIVE);
-
- /*
- * When checking the active state, we need to be sure we are
- * dealing with comparible boolean values. Take the logical not
- * of each.
- */
- if (!all_lru_mode && !PageActive(page) != !(mode & ISOLATE_ACTIVE))
- return ret;
-
- if (!all_lru_mode && !!page_is_file_cache(page) != file)
- return ret;
-
- /*
- * When this function is being called for lumpy reclaim, we
- * initially look into all LRU pages, active, inactive and
- * unevictable; only give shrink_page_list evictable pages.
- */
- if (PageUnevictable(page))
+ /* Compaction should not handle unevictable pages but CMA can do so */
+ if (PageUnevictable(page) && !(mode & ISOLATE_UNEVICTABLE))
return ret;
ret = -EBUSY;
@@ -1135,52 +1060,38 @@
* Appropriate locks must be held before calling this function.
*
* @nr_to_scan: The number of pages to look through on the list.
- * @mz: The mem_cgroup_zone to pull pages from.
+ * @lruvec: The LRU vector to pull pages from.
* @dst: The temp list to put pages on to.
* @nr_scanned: The number of pages that were scanned.
* @sc: The scan_control struct for this reclaim session
* @mode: One of the LRU isolation modes
- * @active: True [1] if isolating active pages
- * @file: True [1] if isolating file [!anon] pages
+ * @lru: LRU list id for isolating
*
* returns how many pages were moved onto *@dst.
*/
static unsigned long isolate_lru_pages(unsigned long nr_to_scan,
- struct mem_cgroup_zone *mz, struct list_head *dst,
+ struct lruvec *lruvec, struct list_head *dst,
unsigned long *nr_scanned, struct scan_control *sc,
- isolate_mode_t mode, int active, int file)
+ isolate_mode_t mode, enum lru_list lru)
{
- struct lruvec *lruvec;
struct list_head *src;
unsigned long nr_taken = 0;
- unsigned long nr_lumpy_taken = 0;
- unsigned long nr_lumpy_dirty = 0;
- unsigned long nr_lumpy_failed = 0;
unsigned long scan;
- int lru = LRU_BASE;
+ int file = is_file_lru(lru);
- lruvec = mem_cgroup_zone_lruvec(mz->zone, mz->mem_cgroup);
- if (active)
- lru += LRU_ACTIVE;
- if (file)
- lru += LRU_FILE;
src = &lruvec->lists[lru];
for (scan = 0; scan < nr_to_scan && !list_empty(src); scan++) {
struct page *page;
- unsigned long pfn;
- unsigned long end_pfn;
- unsigned long page_pfn;
- int zone_id;
page = lru_to_page(src);
prefetchw_prev_lru_page(page, src, flags);
VM_BUG_ON(!PageLRU(page));
- switch (__isolate_lru_page(page, mode, file)) {
+ switch (__isolate_lru_page(page, mode)) {
case 0:
- mem_cgroup_lru_del(page);
+ mem_cgroup_lru_del_list(page, lru);
list_move(&page->lru, dst);
nr_taken += hpage_nr_pages(page);
break;
@@ -1193,84 +1104,6 @@
default:
BUG();
}
-
- if (!sc->order || !(sc->reclaim_mode & RECLAIM_MODE_LUMPYRECLAIM))
- continue;
-
- /*
- * Attempt to take all pages in the order aligned region
- * surrounding the tag page. Only take those pages of
- * the same active state as that tag page. We may safely
- * round the target page pfn down to the requested order
- * as the mem_map is guaranteed valid out to MAX_ORDER,
- * where that page is in a different zone we will detect
- * it from its zone id and abort this block scan.
- */
- zone_id = page_zone_id(page);
- page_pfn = page_to_pfn(page);
- pfn = page_pfn & ~((1 << sc->order) - 1);
- end_pfn = pfn + (1 << sc->order);
- for (; pfn < end_pfn; pfn++) {
- struct page *cursor_page;
-
- /* The target page is in the block, ignore it. */
- if (unlikely(pfn == page_pfn))
- continue;
-
- /* Avoid holes within the zone. */
- if (unlikely(!pfn_valid_within(pfn)))
- break;
-
- cursor_page = pfn_to_page(pfn);
-
- /* Check that we have not crossed a zone boundary. */
- if (unlikely(page_zone_id(cursor_page) != zone_id))
- break;
-
- /*
- * If we don't have enough swap space, reclaiming of
- * anon page which don't already have a swap slot is
- * pointless.
- */
- if (nr_swap_pages <= 0 && PageSwapBacked(cursor_page) &&
- !PageSwapCache(cursor_page))
- break;
-
- if (__isolate_lru_page(cursor_page, mode, file) == 0) {
- unsigned int isolated_pages;
-
- mem_cgroup_lru_del(cursor_page);
- list_move(&cursor_page->lru, dst);
- isolated_pages = hpage_nr_pages(cursor_page);
- nr_taken += isolated_pages;
- nr_lumpy_taken += isolated_pages;
- if (PageDirty(cursor_page))
- nr_lumpy_dirty += isolated_pages;
- scan++;
- pfn += isolated_pages - 1;
- } else {
- /*
- * Check if the page is freed already.
- *
- * We can't use page_count() as that
- * requires compound_head and we don't
- * have a pin on the page here. If a
- * page is tail, we may or may not
- * have isolated the head, so assume
- * it's not free, it'd be tricky to
- * track the head status without a
- * page pin.
- */
- if (!PageTail(cursor_page) &&
- !atomic_read(&cursor_page->_count))
- continue;
- break;
- }
- }
-
- /* If we break out of the loop above, lumpy reclaim failed */
- if (pfn < end_pfn)
- nr_lumpy_failed++;
}
*nr_scanned = scan;
@@ -1278,7 +1111,6 @@
trace_mm_vmscan_lru_isolate(sc->order,
nr_to_scan, scan,
nr_taken,
- nr_lumpy_taken, nr_lumpy_dirty, nr_lumpy_failed,
mode, file);
return nr_taken;
}
@@ -1407,112 +1239,25 @@
list_splice(&pages_to_free, page_list);
}
-static noinline_for_stack void
-update_isolated_counts(struct mem_cgroup_zone *mz,
- struct list_head *page_list,
- unsigned long *nr_anon,
- unsigned long *nr_file)
-{
- struct zone *zone = mz->zone;
- unsigned int count[NR_LRU_LISTS] = { 0, };
- unsigned long nr_active = 0;
- struct page *page;
- int lru;
-
- /*
- * Count pages and clear active flags
- */
- list_for_each_entry(page, page_list, lru) {
- int numpages = hpage_nr_pages(page);
- lru = page_lru_base_type(page);
- if (PageActive(page)) {
- lru += LRU_ACTIVE;
- ClearPageActive(page);
- nr_active += numpages;
- }
- count[lru] += numpages;
- }
-
- preempt_disable();
- __count_vm_events(PGDEACTIVATE, nr_active);
-
- __mod_zone_page_state(zone, NR_ACTIVE_FILE,
- -count[LRU_ACTIVE_FILE]);
- __mod_zone_page_state(zone, NR_INACTIVE_FILE,
- -count[LRU_INACTIVE_FILE]);
- __mod_zone_page_state(zone, NR_ACTIVE_ANON,
- -count[LRU_ACTIVE_ANON]);
- __mod_zone_page_state(zone, NR_INACTIVE_ANON,
- -count[LRU_INACTIVE_ANON]);
-
- *nr_anon = count[LRU_ACTIVE_ANON] + count[LRU_INACTIVE_ANON];
- *nr_file = count[LRU_ACTIVE_FILE] + count[LRU_INACTIVE_FILE];
-
- __mod_zone_page_state(zone, NR_ISOLATED_ANON, *nr_anon);
- __mod_zone_page_state(zone, NR_ISOLATED_FILE, *nr_file);
- preempt_enable();
-}
-
-/*
- * Returns true if a direct reclaim should wait on pages under writeback.
- *
- * If we are direct reclaiming for contiguous pages and we do not reclaim
- * everything in the list, try again and wait for writeback IO to complete.
- * This will stall high-order allocations noticeably. Only do that when really
- * need to free the pages under high memory pressure.
- */
-static inline bool should_reclaim_stall(unsigned long nr_taken,
- unsigned long nr_freed,
- int priority,
- struct scan_control *sc)
-{
- int lumpy_stall_priority;
-
- /* kswapd should not stall on sync IO */
- if (current_is_kswapd())
- return false;
-
- /* Only stall on lumpy reclaim */
- if (sc->reclaim_mode & RECLAIM_MODE_SINGLE)
- return false;
-
- /* If we have reclaimed everything on the isolated list, no stall */
- if (nr_freed == nr_taken)
- return false;
-
- /*
- * For high-order allocations, there are two stall thresholds.
- * High-cost allocations stall immediately where as lower
- * order allocations such as stacks require the scanning
- * priority to be much higher before stalling.
- */
- if (sc->order > PAGE_ALLOC_COSTLY_ORDER)
- lumpy_stall_priority = DEF_PRIORITY;
- else
- lumpy_stall_priority = DEF_PRIORITY / 3;
-
- return priority <= lumpy_stall_priority;
-}
-
/*
* shrink_inactive_list() is a helper for shrink_zone(). It returns the number
* of reclaimed pages
*/
static noinline_for_stack unsigned long
shrink_inactive_list(unsigned long nr_to_scan, struct mem_cgroup_zone *mz,
- struct scan_control *sc, int priority, int file)
+ struct scan_control *sc, enum lru_list lru)
{
LIST_HEAD(page_list);
unsigned long nr_scanned;
unsigned long nr_reclaimed = 0;
unsigned long nr_taken;
- unsigned long nr_anon;
- unsigned long nr_file;
unsigned long nr_dirty = 0;
unsigned long nr_writeback = 0;
- isolate_mode_t isolate_mode = ISOLATE_INACTIVE;
+ isolate_mode_t isolate_mode = 0;
+ int file = is_file_lru(lru);
struct zone *zone = mz->zone;
struct zone_reclaim_stat *reclaim_stat = get_reclaim_stat(mz);
+ struct lruvec *lruvec = mem_cgroup_zone_lruvec(zone, mz->mem_cgroup);
while (unlikely(too_many_isolated(zone, file, sc))) {
congestion_wait(BLK_RW_ASYNC, HZ/10);
@@ -1522,10 +1267,6 @@
return SWAP_CLUSTER_MAX;
}
- set_reclaim_mode(priority, sc, false);
- if (sc->reclaim_mode & RECLAIM_MODE_LUMPYRECLAIM)
- isolate_mode |= ISOLATE_ACTIVE;
-
lru_add_drain();
if (!sc->may_unmap)
@@ -1535,8 +1276,12 @@
spin_lock_irq(&zone->lru_lock);
- nr_taken = isolate_lru_pages(nr_to_scan, mz, &page_list, &nr_scanned,
- sc, isolate_mode, 0, file);
+ nr_taken = isolate_lru_pages(nr_to_scan, lruvec, &page_list,
+ &nr_scanned, sc, isolate_mode, lru);
+
+ __mod_zone_page_state(zone, NR_LRU_BASE + lru, -nr_taken);
+ __mod_zone_page_state(zone, NR_ISOLATED_ANON + file, nr_taken);
+
if (global_reclaim(sc)) {
zone->pages_scanned += nr_scanned;
if (current_is_kswapd())
@@ -1551,22 +1296,12 @@
if (nr_taken == 0)
return 0;
- update_isolated_counts(mz, &page_list, &nr_anon, &nr_file);
-
- nr_reclaimed = shrink_page_list(&page_list, mz, sc, priority,
- &nr_dirty, &nr_writeback);
-
- /* Check if we should syncronously wait for writeback */
- if (should_reclaim_stall(nr_taken, nr_reclaimed, priority, sc)) {
- set_reclaim_mode(priority, sc, true);
- nr_reclaimed += shrink_page_list(&page_list, mz, sc,
- priority, &nr_dirty, &nr_writeback);
- }
+ nr_reclaimed = shrink_page_list(&page_list, zone, sc, TTU_UNMAP,
+ &nr_dirty, &nr_writeback, false);
spin_lock_irq(&zone->lru_lock);
- reclaim_stat->recent_scanned[0] += nr_anon;
- reclaim_stat->recent_scanned[1] += nr_file;
+ reclaim_stat->recent_scanned[file] += nr_taken;
if (global_reclaim(sc)) {
if (current_is_kswapd())
@@ -1579,8 +1314,7 @@
putback_inactive_pages(mz, &page_list);
- __mod_zone_page_state(zone, NR_ISOLATED_ANON, -nr_anon);
- __mod_zone_page_state(zone, NR_ISOLATED_FILE, -nr_file);
+ __mod_zone_page_state(zone, NR_ISOLATED_ANON + file, -nr_taken);
spin_unlock_irq(&zone->lru_lock);
@@ -1609,14 +1343,15 @@
* DEF_PRIORITY-6 For SWAP_CLUSTER_MAX isolated pages, throttle if any
* isolated page is PageWriteback
*/
- if (nr_writeback && nr_writeback >= (nr_taken >> (DEF_PRIORITY-priority)))
+ if (nr_writeback && nr_writeback >=
+ (nr_taken >> (DEF_PRIORITY - sc->priority)))
wait_iff_congested(zone, BLK_RW_ASYNC, HZ/10);
trace_mm_vmscan_lru_shrink_inactive(zone->zone_pgdat->node_id,
zone_idx(zone),
nr_scanned, nr_reclaimed,
- priority,
- trace_shrink_flags(file, sc->reclaim_mode));
+ sc->priority,
+ trace_shrink_flags(file));
return nr_reclaimed;
}
@@ -1679,7 +1414,7 @@
static void shrink_active_list(unsigned long nr_to_scan,
struct mem_cgroup_zone *mz,
struct scan_control *sc,
- int priority, int file)
+ enum lru_list lru)
{
unsigned long nr_taken;
unsigned long nr_scanned;
@@ -1690,13 +1425,13 @@
struct page *page;
struct zone_reclaim_stat *reclaim_stat = get_reclaim_stat(mz);
unsigned long nr_rotated = 0;
- isolate_mode_t isolate_mode = ISOLATE_ACTIVE;
+ isolate_mode_t isolate_mode = 0;
+ int file = is_file_lru(lru);
struct zone *zone = mz->zone;
+ struct lruvec *lruvec = mem_cgroup_zone_lruvec(zone, mz->mem_cgroup);
lru_add_drain();
- reset_reclaim_mode(sc);
-
if (!sc->may_unmap)
isolate_mode |= ISOLATE_UNMAPPED;
if (!sc->may_writepage)
@@ -1704,18 +1439,15 @@
spin_lock_irq(&zone->lru_lock);
- nr_taken = isolate_lru_pages(nr_to_scan, mz, &l_hold, &nr_scanned, sc,
- isolate_mode, 1, file);
+ nr_taken = isolate_lru_pages(nr_to_scan, lruvec, &l_hold,
+ &nr_scanned, sc, isolate_mode, lru);
if (global_reclaim(sc))
zone->pages_scanned += nr_scanned;
reclaim_stat->recent_scanned[file] += nr_taken;
__count_zone_vm_events(PGREFILL, zone, nr_scanned);
- if (file)
- __mod_zone_page_state(zone, NR_ACTIVE_FILE, -nr_taken);
- else
- __mod_zone_page_state(zone, NR_ACTIVE_ANON, -nr_taken);
+ __mod_zone_page_state(zone, NR_LRU_BASE + lru, -nr_taken);
__mod_zone_page_state(zone, NR_ISOLATED_ANON + file, nr_taken);
spin_unlock_irq(&zone->lru_lock);
@@ -1737,7 +1469,8 @@
}
}
- if (page_referenced(page, 0, mz->mem_cgroup, &vm_flags)) {
+ if (page_referenced(page, 0, sc->target_mem_cgroup,
+ &vm_flags)) {
nr_rotated += hpage_nr_pages(page);
/*
* Identify referenced, file-backed active pages and
@@ -1770,10 +1503,8 @@
*/
reclaim_stat->recent_rotated[file] += nr_rotated;
- move_active_pages_to_lru(zone, &l_active, &l_hold,
- LRU_ACTIVE + file * LRU_FILE);
- move_active_pages_to_lru(zone, &l_inactive, &l_hold,
- LRU_BASE + file * LRU_FILE);
+ move_active_pages_to_lru(zone, &l_active, &l_hold, lru);
+ move_active_pages_to_lru(zone, &l_inactive, &l_hold, lru - LRU_ACTIVE);
__mod_zone_page_state(zone, NR_ISOLATED_ANON + file, -nr_taken);
spin_unlock_irq(&zone->lru_lock);
@@ -1811,7 +1542,7 @@
if (!total_swap_pages)
return 0;
- if (!scanning_global_lru(mz))
+ if (!mem_cgroup_disabled())
return mem_cgroup_inactive_anon_is_low(mz->mem_cgroup,
mz->zone);
@@ -1850,7 +1581,7 @@
*/
static int inactive_file_is_low(struct mem_cgroup_zone *mz)
{
- if (!scanning_global_lru(mz))
+ if (!mem_cgroup_disabled())
return mem_cgroup_inactive_file_is_low(mz->mem_cgroup,
mz->zone);
@@ -1867,25 +1598,24 @@
static unsigned long shrink_list(enum lru_list lru, unsigned long nr_to_scan,
struct mem_cgroup_zone *mz,
- struct scan_control *sc, int priority)
+ struct scan_control *sc)
{
int file = is_file_lru(lru);
if (is_active_lru(lru)) {
if (inactive_list_is_low(mz, file))
- shrink_active_list(nr_to_scan, mz, sc, priority, file);
+ shrink_active_list(nr_to_scan, mz, sc, lru);
return 0;
}
- return shrink_inactive_list(nr_to_scan, mz, sc, priority, file);
+ return shrink_inactive_list(nr_to_scan, mz, sc, lru);
}
-static int vmscan_swappiness(struct mem_cgroup_zone *mz,
- struct scan_control *sc)
+static int vmscan_swappiness(struct scan_control *sc)
{
if (global_reclaim(sc))
return vm_swappiness;
- return mem_cgroup_swappiness(mz->mem_cgroup);
+ return mem_cgroup_swappiness(sc->target_mem_cgroup);
}
/*
@@ -1897,7 +1627,7 @@
* nr[0] = anon pages to scan; nr[1] = file pages to scan
*/
static void get_scan_count(struct mem_cgroup_zone *mz, struct scan_control *sc,
- unsigned long *nr, int priority)
+ unsigned long *nr)
{
unsigned long anon, file, free;
unsigned long anon_prio, file_prio;
@@ -1953,8 +1683,8 @@
* With swappiness at 100, anonymous and file have the same priority.
* This scanning priority is essentially the inverse of IO cost.
*/
- anon_prio = vmscan_swappiness(mz, sc);
- file_prio = 200 - vmscan_swappiness(mz, sc);
+ anon_prio = vmscan_swappiness(sc);
+ file_prio = 200 - vmscan_swappiness(sc);
/*
* OK, so we have swap space and a fair amount of page cache
@@ -1983,10 +1713,10 @@
* proportional to the fraction of recently scanned pages on
* each list that were recently referenced and in active use.
*/
- ap = (anon_prio + 1) * (reclaim_stat->recent_scanned[0] + 1);
+ ap = anon_prio * (reclaim_stat->recent_scanned[0] + 1);
ap /= reclaim_stat->recent_rotated[0] + 1;
- fp = (file_prio + 1) * (reclaim_stat->recent_scanned[1] + 1);
+ fp = file_prio * (reclaim_stat->recent_scanned[1] + 1);
fp /= reclaim_stat->recent_rotated[1] + 1;
spin_unlock_irq(&mz->zone->lru_lock);
@@ -1999,8 +1729,8 @@
unsigned long scan;
scan = zone_nr_lru_pages(mz, lru);
- if (priority || noswap) {
- scan >>= priority;
+ if (sc->priority || noswap || !vmscan_swappiness(sc)) {
+ scan >>= sc->priority;
if (!scan && force_scan)
scan = SWAP_CLUSTER_MAX;
scan = div64_u64(scan * fraction[file], denominator);
@@ -2009,12 +1739,23 @@
}
}
+/* Use reclaim/compaction for costly allocs or under memory pressure */
+static bool in_reclaim_compaction(struct scan_control *sc)
+{
+ if (COMPACTION_BUILD && sc->order &&
+ (sc->order > PAGE_ALLOC_COSTLY_ORDER ||
+ sc->priority < DEF_PRIORITY - 2))
+ return true;
+
+ return false;
+}
+
/*
- * Reclaim/compaction depends on a number of pages being freed. To avoid
- * disruption to the system, a small number of order-0 pages continue to be
- * rotated and reclaimed in the normal fashion. However, by the time we get
- * back to the allocator and call try_to_compact_zone(), we ensure that
- * there are enough free pages for it to be likely successful
+ * Reclaim/compaction is used for high-order allocation requests. It reclaims
+ * order-0 pages before compacting the zone. should_continue_reclaim() returns
+ * true if more pages should be reclaimed such that when the page allocator
+ * calls try_to_compact_zone() that it will have enough free pages to succeed.
+ * It will give up earlier than that if there is difficulty reclaiming pages.
*/
static inline bool should_continue_reclaim(struct mem_cgroup_zone *mz,
unsigned long nr_reclaimed,
@@ -2025,7 +1766,7 @@
unsigned long inactive_lru_pages;
/* If not in reclaim/compaction mode, stop */
- if (!(sc->reclaim_mode & RECLAIM_MODE_COMPACTION))
+ if (!in_reclaim_compaction(sc))
return false;
/* Consider stopping depending on scan and reclaim activity */
@@ -2076,7 +1817,7 @@
/*
* This is a basic per-zone page freer. Used by both kswapd and direct reclaim.
*/
-static void shrink_mem_cgroup_zone(int priority, struct mem_cgroup_zone *mz,
+static void shrink_mem_cgroup_zone(struct mem_cgroup_zone *mz,
struct scan_control *sc)
{
unsigned long nr[NR_LRU_LISTS];
@@ -2089,7 +1830,7 @@
restart:
nr_reclaimed = 0;
nr_scanned = sc->nr_scanned;
- get_scan_count(mz, sc, nr, priority);
+ get_scan_count(mz, sc, nr);
blk_start_plug(&plug);
while (nr[LRU_INACTIVE_ANON] || nr[LRU_ACTIVE_FILE] ||
@@ -2101,7 +1842,7 @@
nr[lru] -= nr_to_scan;
nr_reclaimed += shrink_list(lru, nr_to_scan,
- mz, sc, priority);
+ mz, sc);
}
}
/*
@@ -2112,7 +1853,8 @@
* with multiple processes reclaiming pages, the total
* freeing target can get unreasonably large.
*/
- if (nr_reclaimed >= nr_to_reclaim && priority < DEF_PRIORITY)
+ if (nr_reclaimed >= nr_to_reclaim &&
+ sc->priority < DEF_PRIORITY)
break;
}
blk_finish_plug(&plug);
@@ -2123,23 +1865,23 @@
* rebalance the anon lru active/inactive ratio.
*/
if (inactive_anon_is_low(mz))
- shrink_active_list(SWAP_CLUSTER_MAX, mz, sc, priority, 0);
+ shrink_active_list(SWAP_CLUSTER_MAX, mz,
+ sc, LRU_ACTIVE_ANON);
/* reclaim/compaction might need reclaim to continue */
if (should_continue_reclaim(mz, nr_reclaimed,
- sc->nr_scanned - nr_scanned, sc))
+ sc->nr_scanned - nr_scanned, sc))
goto restart;
throttle_vm_writeout(sc->gfp_mask);
}
-static void shrink_zone(int priority, struct zone *zone,
- struct scan_control *sc)
+static void shrink_zone(struct zone *zone, struct scan_control *sc)
{
struct mem_cgroup *root = sc->target_mem_cgroup;
struct mem_cgroup_reclaim_cookie reclaim = {
.zone = zone,
- .priority = priority,
+ .priority = sc->priority,
};
struct mem_cgroup *memcg;
@@ -2150,7 +1892,7 @@
.zone = zone,
};
- shrink_mem_cgroup_zone(priority, &mz, sc);
+ shrink_mem_cgroup_zone(&mz, sc);
/*
* Limit reclaim has historically picked one memcg and
* scanned it with decreasing priority levels until
@@ -2226,8 +1968,7 @@
* the caller that it should consider retrying the allocation instead of
* further reclaim.
*/
-static bool shrink_zones(int priority, struct zonelist *zonelist,
- struct scan_control *sc)
+static bool shrink_zones(struct zonelist *zonelist, struct scan_control *sc)
{
struct zoneref *z;
struct zone *zone;
@@ -2254,7 +1995,8 @@
if (global_reclaim(sc)) {
if (!cpuset_zone_allowed_hardwall(zone, GFP_KERNEL))
continue;
- if (zone->all_unreclaimable && priority != DEF_PRIORITY)
+ if (zone->all_unreclaimable &&
+ sc->priority != DEF_PRIORITY)
continue; /* Let kswapd poll it */
if (COMPACTION_BUILD) {
/*
@@ -2286,7 +2028,7 @@
/* need some check for avoid more shrink_zone() */
}
- shrink_zone(priority, zone, sc);
+ shrink_zone(zone, sc);
}
return aborted_reclaim;
@@ -2337,7 +2079,6 @@
struct scan_control *sc,
struct shrink_control *shrink)
{
- int priority;
unsigned long total_scanned = 0;
struct reclaim_state *reclaim_state = current->reclaim_state;
struct zoneref *z;
@@ -2350,11 +2091,9 @@
if (global_reclaim(sc))
count_vm_event(ALLOCSTALL);
- for (priority = DEF_PRIORITY; priority >= 0; priority--) {
+ do {
sc->nr_scanned = 0;
- if (!priority)
- disable_swap_token(sc->target_mem_cgroup);
- aborted_reclaim = shrink_zones(priority, zonelist, sc);
+ aborted_reclaim = shrink_zones(zonelist, sc);
/*
* Don't shrink slabs when reclaiming memory from
@@ -2396,7 +2135,7 @@
/* Take a nap, wait for some writeback to complete */
if (!sc->hibernation_mode && sc->nr_scanned &&
- priority < DEF_PRIORITY - 2) {
+ sc->priority < DEF_PRIORITY - 2) {
struct zone *preferred_zone;
first_zones_zonelist(zonelist, gfp_zone(sc->gfp_mask),
@@ -2404,7 +2143,7 @@
&preferred_zone);
wait_iff_congested(preferred_zone, BLK_RW_ASYNC, HZ/10);
}
- }
+ } while (--sc->priority >= 0);
out:
delayacct_freepages_end();
@@ -2442,6 +2181,7 @@
.may_unmap = 1,
.may_swap = 1,
.order = order,
+ .priority = DEF_PRIORITY,
.target_mem_cgroup = NULL,
.nodemask = nodemask,
};
@@ -2474,6 +2214,7 @@
.may_unmap = 1,
.may_swap = !noswap,
.order = 0,
+ .priority = 0,
.target_mem_cgroup = memcg,
};
struct mem_cgroup_zone mz = {
@@ -2484,7 +2225,7 @@
sc.gfp_mask = (gfp_mask & GFP_RECLAIM_MASK) |
(GFP_HIGHUSER_MOVABLE & ~GFP_RECLAIM_MASK);
- trace_mm_vmscan_memcg_softlimit_reclaim_begin(0,
+ trace_mm_vmscan_memcg_softlimit_reclaim_begin(sc.order,
sc.may_writepage,
sc.gfp_mask);
@@ -2495,7 +2236,7 @@
* will pick up pages from other mem cgroup's as well. We hack
* the priority and make it zero.
*/
- shrink_mem_cgroup_zone(0, &mz, &sc);
+ shrink_mem_cgroup_zone(&mz, &sc);
trace_mm_vmscan_memcg_softlimit_reclaim_end(sc.nr_reclaimed);
@@ -2516,6 +2257,7 @@
.may_swap = !noswap,
.nr_to_reclaim = SWAP_CLUSTER_MAX,
.order = 0,
+ .priority = DEF_PRIORITY,
.target_mem_cgroup = memcg,
.nodemask = NULL, /* we don't care the placement */
.gfp_mask = (gfp_mask & GFP_RECLAIM_MASK) |
@@ -2546,8 +2288,7 @@
}
#endif
-static void age_active_anon(struct zone *zone, struct scan_control *sc,
- int priority)
+static void age_active_anon(struct zone *zone, struct scan_control *sc)
{
struct mem_cgroup *memcg;
@@ -2563,7 +2304,7 @@
if (inactive_anon_is_low(&mz))
shrink_active_list(SWAP_CLUSTER_MAX, &mz,
- sc, priority, 0);
+ sc, LRU_ACTIVE_ANON);
memcg = mem_cgroup_iter(NULL, memcg, NULL);
} while (memcg);
@@ -2672,7 +2413,6 @@
{
int all_zones_ok;
unsigned long balanced;
- int priority;
int i;
int end_zone = 0; /* Inclusive. 0 = ZONE_DMA */
unsigned long total_scanned;
@@ -2696,18 +2436,15 @@
};
loop_again:
total_scanned = 0;
+ sc.priority = DEF_PRIORITY;
sc.nr_reclaimed = 0;
sc.may_writepage = !laptop_mode;
count_vm_event(PAGEOUTRUN);
- for (priority = DEF_PRIORITY; priority >= 0; priority--) {
+ do {
unsigned long lru_pages = 0;
int has_under_min_watermark_zone = 0;
- /* The swap token gets in the way of swapout... */
- if (!priority)
- disable_swap_token(NULL);
-
all_zones_ok = 1;
balanced = 0;
@@ -2721,14 +2458,15 @@
if (!populated_zone(zone))
continue;
- if (zone->all_unreclaimable && priority != DEF_PRIORITY)
+ if (zone->all_unreclaimable &&
+ sc.priority != DEF_PRIORITY)
continue;
/*
* Do some background aging of the anon list, to give
* pages a chance to be referenced before reclaiming.
*/
- age_active_anon(zone, &sc, priority);
+ age_active_anon(zone, &sc);
/*
* If the number of buffer_heads in the machine
@@ -2776,7 +2514,8 @@
if (!populated_zone(zone))
continue;
- if (zone->all_unreclaimable && priority != DEF_PRIORITY)
+ if (zone->all_unreclaimable &&
+ sc.priority != DEF_PRIORITY)
continue;
sc.nr_scanned = 0;
@@ -2820,7 +2559,7 @@
!zone_watermark_ok_safe(zone, testorder,
high_wmark_pages(zone) + balance_gap,
end_zone, 0)) {
- shrink_zone(priority, zone, &sc);
+ shrink_zone(zone, &sc);
reclaim_state->reclaimed_slab = 0;
nr_slab = shrink_slab(&shrink, sc.nr_scanned, lru_pages);
@@ -2877,7 +2616,7 @@
* OK, kswapd is getting into trouble. Take a nap, then take
* another pass across the zones.
*/
- if (total_scanned && (priority < DEF_PRIORITY - 2)) {
+ if (total_scanned && (sc.priority < DEF_PRIORITY - 2)) {
if (has_under_min_watermark_zone)
count_vm_event(KSWAPD_SKIP_CONGESTION_WAIT);
else
@@ -2892,7 +2631,7 @@
*/
if (sc.nr_reclaimed >= SWAP_CLUSTER_MAX)
break;
- }
+ } while (--sc.priority >= 0);
out:
/*
@@ -2942,7 +2681,8 @@
if (!populated_zone(zone))
continue;
- if (zone->all_unreclaimable && priority != DEF_PRIORITY)
+ if (zone->all_unreclaimable &&
+ sc.priority != DEF_PRIORITY)
continue;
/* Would compaction fail due to lack of free memory? */
@@ -3013,7 +2753,18 @@
* them before going back to sleep.
*/
set_pgdat_percpu_threshold(pgdat, calculate_normal_threshold);
- schedule();
+
+ /*
+ * Compaction records what page blocks it recently failed to
+ * isolate pages from and skips them in the future scanning.
+ * When kswapd is going to sleep, it is reasonable to assume
+ * that pages and compaction may succeed so reset the cache.
+ */
+ reset_isolation_suitable(pgdat);
+
+ if (!kthread_should_stop())
+ schedule();
+
set_pgdat_percpu_threshold(pgdat, calculate_pressure_threshold);
} else {
if (remaining)
@@ -3209,6 +2960,7 @@
.nr_to_reclaim = nr_to_reclaim,
.hibernation_mode = 1,
.order = 0,
+ .priority = DEF_PRIORITY,
};
struct shrink_control shrink = {
.gfp_mask = sc.gfp_mask,
@@ -3386,7 +3138,6 @@
const unsigned long nr_pages = 1 << order;
struct task_struct *p = current;
struct reclaim_state reclaim_state;
- int priority;
struct scan_control sc = {
.may_writepage = !!(zone_reclaim_mode & RECLAIM_WRITE),
.may_unmap = !!(zone_reclaim_mode & RECLAIM_SWAP),
@@ -3395,6 +3146,7 @@
SWAP_CLUSTER_MAX),
.gfp_mask = gfp_mask,
.order = order,
+ .priority = ZONE_RECLAIM_PRIORITY,
};
struct shrink_control shrink = {
.gfp_mask = sc.gfp_mask,
@@ -3417,11 +3169,9 @@
* Free memory by calling shrink zone with increasing
* priorities until we have enough memory freed.
*/
- priority = ZONE_RECLAIM_PRIORITY;
do {
- shrink_zone(priority, zone, &sc);
- priority--;
- } while (priority >= 0 && sc.nr_reclaimed < nr_pages);
+ shrink_zone(zone, &sc);
+ } while (sc.nr_reclaimed < nr_pages && --sc.priority >= 0);
}
nr_slab_pages0 = zone_page_state(zone, NR_SLAB_RECLAIMABLE);
diff --git a/mm/vmstat.c b/mm/vmstat.c
index 8e18d6b..959a558 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -761,10 +761,14 @@
"pgrotated",
+#ifdef CONFIG_MIGRATION
+ "pgmigrate_success",
+ "pgmigrate_fail",
+#endif
#ifdef CONFIG_COMPACTION
- "compact_blocks_moved",
- "compact_pages_moved",
- "compact_pagemigrate_failed",
+ "compact_migrate_scanned",
+ "compact_free_scanned",
+ "compact_isolated",
"compact_stall",
"compact_fail",
"compact_success",
diff --git a/sound/soc/codecs/msm8x10-wcd.c b/sound/soc/codecs/msm8x10-wcd.c
index f3c6ef8..1260e1c 100644
--- a/sound/soc/codecs/msm8x10-wcd.c
+++ b/sound/soc/codecs/msm8x10-wcd.c
@@ -1382,6 +1382,7 @@
snd_soc_update_bits(codec, micb_int_reg, 0x10, 0x10);
else if (strnstr(w->name, internal3_text, 30))
snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x2);
+ snd_soc_update_bits(codec, w->reg, 0x1, 0x0);
break;
case SND_SOC_DAPM_POST_PMU:
usleep_range(20000, 20100);
@@ -1394,6 +1395,7 @@
else if (strnstr(w->name, internal3_text, 30))
snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x0);
+ snd_soc_update_bits(codec, w->reg, 0x1, 0x1);
break;
}
return 0;
@@ -1695,9 +1697,6 @@
{"I2S TX1", NULL, "TX_I2S_CLK"},
{"I2S TX2", NULL, "TX_I2S_CLK"},
- {"DEC1 MUX", NULL, "TX CLK"},
- {"DEC2 MUX", NULL, "TX CLK"},
-
{"I2S TX1", NULL, "DEC1 MUX"},
{"I2S TX2", NULL, "DEC2 MUX"},
@@ -2136,8 +2135,6 @@
SND_SOC_DAPM_AIF_IN("I2S RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_SUPPLY("TX CLK", MSM8X10_WCD_A_CDC_DIG_CLK_CTL,
- 4, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("INT_LDO_H", SND_SOC_NOPM, 1, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("HEADPHONE"),
@@ -2336,10 +2333,11 @@
/* Reduce LINE DAC bias to 70% */
MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_RX_LINE_BIAS_PA, 0x78),
- /* Disable TX7 internal biasing path which can cause leakage */
+ /* Disable internal biasing path which can cause leakage */
MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_BIAS_CURR_CTL_2, 0x04),
MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_MICB_CFILT_1_VAL, 0x60),
- MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_MICB_1_CTL, 0x82),
+ /* Enable pulldown to reduce leakage */
+ MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_MICB_1_CTL, 0x83),
MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_TX_COM_BIAS, 0xE0),
MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_TX_1_EN, 0x32),
MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_TX_2_EN, 0x32),
@@ -2350,6 +2348,9 @@
MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_CDC_CLSG_FREQ_THRESH_B3_CTL, 0x1A),
MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_CDC_CLSG_FREQ_THRESH_B4_CTL, 0x47),
MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_CDC_CLSG_GAIN_THRESH_CTL, 0x23),
+
+ /* Always set TXD_CLK_EN bit to reduce the leakage */
+ MSM8X10_WCD_REG_VAL(MSM8X10_WCD_A_CDC_DIG_CLK_CTL, 0x10),
};
static void msm8x10_wcd_update_reg_defaults(struct snd_soc_codec *codec)
diff --git a/sound/soc/codecs/wcd9306.c b/sound/soc/codecs/wcd9306.c
index 8949e03..b31c7c9 100644
--- a/sound/soc/codecs/wcd9306.c
+++ b/sound/soc/codecs/wcd9306.c
@@ -73,6 +73,9 @@
#define TAPAN_SLIM_IRQ_OVERFLOW (1 << 0)
#define TAPAN_SLIM_IRQ_UNDERFLOW (1 << 1)
#define TAPAN_SLIM_IRQ_PORT_CLOSED (1 << 2)
+
+#define TAPAN_IRQ_MBHC_JACK_SWITCH 21
+
enum {
AIF1_PB = 0,
AIF1_CAP,
@@ -303,18 +306,18 @@
return 0;
}
- WCD9XXX_BCL_LOCK(&priv->resmgr);
+ codec = priv->codec;
+ mutex_lock(&codec->mutex);
old = spkr_drv_wrnd;
ret = param_set_int(val, kp);
if (ret) {
- WCD9XXX_BCL_UNLOCK(&priv->resmgr);
+ mutex_unlock(&codec->mutex);
return ret;
}
- codec = priv->codec;
dev_dbg(codec->dev, "%s: spkr_drv_wrnd %d -> %d\n",
__func__, old, spkr_drv_wrnd);
- if (old == 0 && spkr_drv_wrnd == 1) {
+ if ((old == -1 || old == 0) && spkr_drv_wrnd == 1) {
WCD9XXX_BG_CLK_LOCK(&priv->resmgr);
wcd9xxx_resmgr_get_bandgap(&priv->resmgr,
WCD9XXX_BANDGAP_AUDIO_MODE);
@@ -329,8 +332,8 @@
snd_soc_update_bits(codec, TAPAN_A_SPKR_DRV_EN, 0x80,
0x00);
}
+ mutex_unlock(&codec->mutex);
- WCD9XXX_BCL_UNLOCK(&priv->resmgr);
return 0;
}
@@ -1698,7 +1701,6 @@
struct tapan_priv *tapan = snd_soc_codec_get_drvdata(codec);
dev_dbg(codec->dev, "%s: %s %d\n", __func__, w->name, event);
- WCD9XXX_BCL_LOCK(&tapan->resmgr);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
tapan->spkr_pa_widget_on = true;
@@ -1709,7 +1711,6 @@
snd_soc_update_bits(codec, TAPAN_A_SPKR_DRV_EN, 0x80, 0x00);
break;
}
- WCD9XXX_BCL_UNLOCK(&tapan->resmgr);
return 0;
}
@@ -4450,6 +4451,72 @@
wcd9xxx_free_irq(codec->control_data, WCD9XXX_IRQ_SLIMBUS, tapan);
}
+
+static void tapan_enable_mux_bias_block(struct snd_soc_codec *codec)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x00);
+}
+
+static void tapan_put_cfilt_fast_mode(struct snd_soc_codec *codec,
+ struct wcd9xxx_mbhc *mbhc)
+{
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x30, 0x30);
+}
+
+static void tapan_codec_specific_cal_setup(struct snd_soc_codec *codec,
+ struct wcd9xxx_mbhc *mbhc)
+{
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x0C, 0x04);
+ snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0xE0, 0xE0);
+}
+
+static int tapan_get_jack_detect_irq(struct snd_soc_codec *codec)
+{
+ return TAPAN_IRQ_MBHC_JACK_SWITCH;
+}
+
+static struct wcd9xxx_cfilt_mode tapan_codec_switch_cfilt_mode(
+ struct wcd9xxx_mbhc *mbhc,
+ bool fast)
+{
+ struct snd_soc_codec *codec = mbhc->codec;
+ struct wcd9xxx_cfilt_mode cfilt_mode;
+
+ if (fast)
+ cfilt_mode.reg_mode_val = WCD9XXX_CFILT_EXT_PRCHG_EN;
+ else
+ cfilt_mode.reg_mode_val = WCD9XXX_CFILT_EXT_PRCHG_DSBL;
+
+ cfilt_mode.cur_mode_val =
+ snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x30;
+ return cfilt_mode;
+}
+
+static void tapan_select_cfilt(struct snd_soc_codec *codec,
+ struct wcd9xxx_mbhc *mbhc)
+{
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60, 0x00);
+}
+
+static void tapan_free_irq(struct wcd9xxx_mbhc *mbhc)
+{
+ void *cdata = mbhc->codec->control_data;
+ wcd9xxx_free_irq(cdata, WCD9306_IRQ_MBHC_JACK_SWITCH, mbhc);
+}
+
+static const struct wcd9xxx_mbhc_cb mbhc_cb = {
+ .enable_mux_bias_block = tapan_enable_mux_bias_block,
+ .cfilt_fast_mode = tapan_put_cfilt_fast_mode,
+ .codec_specific_cal = tapan_codec_specific_cal_setup,
+ .jack_detect_irq = tapan_get_jack_detect_irq,
+ .switch_cfilt_mode = tapan_codec_switch_cfilt_mode,
+ .select_cfilt = tapan_select_cfilt,
+ .free_irq = tapan_free_irq,
+};
+
int tapan_hs_detect(struct snd_soc_codec *codec,
struct wcd9xxx_mbhc_config *mbhc_cfg)
{
@@ -4468,7 +4535,6 @@
codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
tapan = snd_soc_codec_get_drvdata(codec);
mutex_lock(&codec->mutex);
- WCD9XXX_BCL_LOCK(&tapan->resmgr);
if (codec->reg_def_copy) {
pr_debug("%s: Update ASOC cache", __func__);
@@ -4477,7 +4543,6 @@
codec->reg_size, GFP_KERNEL);
if (!codec->reg_cache) {
pr_err("%s: Cache update failed!\n", __func__);
- WCD9XXX_BCL_UNLOCK(&tapan->resmgr);
mutex_unlock(&codec->mutex);
return -ENOMEM;
}
@@ -4486,7 +4551,6 @@
wcd9xxx_resmgr_post_ssr(&tapan->resmgr);
if (spkr_drv_wrnd == 1)
snd_soc_update_bits(codec, TAPAN_A_SPKR_DRV_EN, 0x80, 0x80);
- WCD9XXX_BCL_UNLOCK(&tapan->resmgr);
tapan_update_reg_defaults(codec);
tapan_update_reg_mclk_rate(wcd9xxx);
@@ -4505,8 +4569,7 @@
rco_clk_rate = TAPAN_MCLK_CLK_9P6MHZ;
ret = wcd9xxx_mbhc_init(&tapan->mbhc, &tapan->resmgr, codec, NULL,
- WCD9XXX_MBHC_VERSION_TAPAN,
- rco_clk_rate);
+ &mbhc_cb, rco_clk_rate);
if (ret)
pr_err("%s: mbhc init failed %d\n", __func__, ret);
else
@@ -4578,8 +4641,8 @@
rco_clk_rate = TAPAN_MCLK_CLK_9P6MHZ;
ret = wcd9xxx_mbhc_init(&tapan->mbhc, &tapan->resmgr, codec, NULL,
- WCD9XXX_MBHC_VERSION_TAPAN,
- rco_clk_rate);
+ &mbhc_cb, rco_clk_rate);
+
if (ret) {
pr_err("%s: mbhc init failed %d\n", __func__, ret);
return ret;
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index 49894ca..516ac4f 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -604,18 +604,17 @@
return 0;
}
- WCD9XXX_BCL_LOCK(&priv->resmgr);
+ codec = priv->codec;
+ mutex_lock(&codec->mutex);
old = spkr_drv_wrnd;
ret = param_set_int(val, kp);
if (ret) {
- WCD9XXX_BCL_UNLOCK(&priv->resmgr);
+ mutex_unlock(&codec->mutex);
return ret;
}
- WCD9XXX_BCL_UNLOCK(&priv->resmgr);
pr_debug("%s: spkr_drv_wrnd %d -> %d\n", __func__, old, spkr_drv_wrnd);
- codec = priv->codec;
- if (old == 0 && spkr_drv_wrnd == 1) {
+ if ((old == -1 || old == 0) && spkr_drv_wrnd == 1) {
WCD9XXX_BG_CLK_LOCK(&priv->resmgr);
wcd9xxx_resmgr_get_bandgap(&priv->resmgr,
WCD9XXX_BANDGAP_AUDIO_MODE);
@@ -630,6 +629,7 @@
snd_soc_update_bits(codec, TAIKO_A_SPKR_DRV_EN, 0x80,
0x00);
}
+ mutex_unlock(&codec->mutex);
return 0;
}
@@ -2420,7 +2420,6 @@
struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
pr_debug("%s: %d %s\n", __func__, event, w->name);
- WCD9XXX_BCL_LOCK(&taiko->resmgr);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
taiko->spkr_pa_widget_on = true;
@@ -2431,7 +2430,6 @@
snd_soc_update_bits(codec, TAIKO_A_SPKR_DRV_EN, 0x80, 0x00);
break;
}
- WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
return 0;
}
@@ -4639,7 +4637,7 @@
.rate_max = 192000,
.rate_min = 8000,
.channels_min = 1,
- .channels_max = 5,
+ .channels_max = 8,
},
.ops = &taiko_dai_ops,
},
@@ -6189,7 +6187,6 @@
codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
taiko = snd_soc_codec_get_drvdata(codec);
mutex_lock(&codec->mutex);
- WCD9XXX_BCL_LOCK(&taiko->resmgr);
if (codec->reg_def_copy) {
pr_debug("%s: Update ASOC cache", __func__);
@@ -6201,7 +6198,6 @@
wcd9xxx_resmgr_post_ssr(&taiko->resmgr);
if (spkr_drv_wrnd == 1)
snd_soc_update_bits(codec, TAIKO_A_SPKR_DRV_EN, 0x80, 0x80);
- WCD9XXX_BCL_UNLOCK(&taiko->resmgr);
taiko_update_reg_defaults(codec);
taiko_codec_init_reg(codec);
@@ -6223,8 +6219,7 @@
ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec,
taiko_enable_mbhc_micbias,
- WCD9XXX_MBHC_VERSION_TAIKO,
- rco_clk_rate);
+ NULL, rco_clk_rate);
if (ret) {
pr_err("%s: mbhc init failed %d\n", __func__, ret);
} else {
@@ -6404,8 +6399,7 @@
/* init and start mbhc */
ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec,
taiko_enable_mbhc_micbias,
- WCD9XXX_MBHC_VERSION_TAIKO,
- rco_clk_rate);
+ NULL, rco_clk_rate);
if (ret) {
pr_err("%s: mbhc init failed %d\n", __func__, ret);
goto err_init;
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
index 618070b..cd11703 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.c
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -94,6 +94,8 @@
#define WCD9XXX_USLEEP_RANGE_MARGIN_US 1000
+#define WCD9XXX_IRQ_MBHC_JACK_SWITCH_DEFAULT 28
+
static bool detect_use_vddio_switch = true;
struct wcd9xxx_mbhc_detect {
@@ -150,64 +152,6 @@
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, on << 2);
}
-static int wcd9xxx_enable_mux_bias_block(struct snd_soc_codec *codec,
- struct wcd9xxx_mbhc *mbhc)
-{
- switch (mbhc->mbhc_version) {
- case WCD9XXX_MBHC_VERSION_TAIKO:
- snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
- 0x80, 0x80);
- break;
- case WCD9XXX_MBHC_VERSION_TAPAN:
- snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
- 0x80, 0x00);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int wcd9xxx_put_cfilt_fast_mode(struct snd_soc_codec *codec,
- struct wcd9xxx_mbhc *mbhc)
-{
- switch (mbhc->mbhc_version) {
- case WCD9XXX_MBHC_VERSION_TAIKO:
- snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
- 0x70, 0x00);
- break;
- case WCD9XXX_MBHC_VERSION_TAPAN:
- snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
- 0x70, 0x70);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int wcd9xxx_codec_specific_cal_setup(struct snd_soc_codec *codec,
- struct wcd9xxx_mbhc *mbhc)
-{
- switch (mbhc->mbhc_version) {
- case WCD9XXX_MBHC_VERSION_TAIKO:
- snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
- 0x04, 0x04);
- break;
- case WCD9XXX_MBHC_VERSION_TAPAN:
- snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
- 0x0C, 0x04);
- snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0xE0, 0xE0);
- /* Make sure the calibration is ON */
- snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_val,
- 0x02, 0x02);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
/* called under codec_resource_lock acquisition */
static void wcd9xxx_pause_hs_polling(struct wcd9xxx_mbhc *mbhc)
{
@@ -229,20 +173,18 @@
{
struct snd_soc_codec *codec = mbhc->codec;
int mbhc_state = mbhc->mbhc_state;
- int ret;
pr_debug("%s: enter\n", __func__);
if (!mbhc->polling_active) {
pr_debug("Polling is not active, do not start polling\n");
return;
}
-
snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
- ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
- if (ret) {
- pr_err("%s: Error returned, ret: %d\n", __func__, ret);
- return;
- }
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
if (!mbhc->no_mic_headset_override &&
mbhc_state == MBHC_STATE_POTENTIAL) {
@@ -481,14 +423,18 @@
(mbhc->mbhc_data.v_brl >> 8) & 0xFF);
}
-static int wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc,
+static void wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc,
bool fast)
{
struct snd_soc_codec *codec = mbhc->codec;
+ struct wcd9xxx_cfilt_mode cfilt_mode;
u8 reg_mode_val, cur_mode_val;
- switch (mbhc->mbhc_version) {
- case WCD9XXX_MBHC_VERSION_TAIKO:
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->switch_cfilt_mode) {
+ cfilt_mode = mbhc->mbhc_cb->switch_cfilt_mode(mbhc, fast);
+ reg_mode_val = cfilt_mode.reg_mode_val;
+ cur_mode_val = cfilt_mode.cur_mode_val;
+ } else {
if (fast)
reg_mode_val = WCD9XXX_CFILT_FAST_MODE;
else
@@ -496,50 +442,21 @@
cur_mode_val =
snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x40;
-
- if (cur_mode_val != reg_mode_val) {
- if (mbhc->polling_active)
- wcd9xxx_pause_hs_polling(mbhc);
- snd_soc_update_bits(codec,
- mbhc->mbhc_bias_regs.cfilt_ctl,
- 0x40, reg_mode_val);
- if (mbhc->polling_active)
- wcd9xxx_start_hs_polling(mbhc);
- pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
- cur_mode_val, reg_mode_val);
- } else {
- pr_debug("%s: CFILT Value is already %x\n",
- __func__, cur_mode_val);
- }
- break;
- case WCD9XXX_MBHC_VERSION_TAPAN:
- if (fast)
- reg_mode_val = WCD9XXX_CFILT_EXT_PRCHG_EN;
- else
- reg_mode_val = WCD9XXX_CFILT_EXT_PRCHG_DSBL;
-
- cur_mode_val =
- snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x70;
-
- if (cur_mode_val != reg_mode_val) {
- if (mbhc->polling_active)
- wcd9xxx_pause_hs_polling(mbhc);
- snd_soc_update_bits(codec,
- mbhc->mbhc_bias_regs.cfilt_ctl,
- 0x70, reg_mode_val);
- if (mbhc->polling_active)
- wcd9xxx_start_hs_polling(mbhc);
- pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
- cur_mode_val, reg_mode_val);
- } else {
- pr_debug("%s: CFILT Value is already %x\n",
- __func__, cur_mode_val);
- }
- break;
- default:
- return -EINVAL;
}
- return 0;
+ if (cur_mode_val != reg_mode_val) {
+ if (mbhc->polling_active)
+ wcd9xxx_pause_hs_polling(mbhc);
+ snd_soc_update_bits(codec,
+ mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x40, reg_mode_val);
+ if (mbhc->polling_active)
+ wcd9xxx_start_hs_polling(mbhc);
+ pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
+ cur_mode_val, reg_mode_val);
+ } else {
+ pr_debug("%s: CFILT Value is already %x\n",
+ __func__, cur_mode_val);
+ }
}
static void wcd9xxx_jack_report(struct wcd9xxx_mbhc *mbhc,
@@ -1043,7 +960,6 @@
struct snd_soc_codec *codec = mbhc->codec;
short bias_value;
u8 cfilt_mode;
- int ret;
WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
@@ -1066,14 +982,19 @@
/* Make sure CFILT is in fast mode, save current mode */
cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
- ret = wcd9xxx_put_cfilt_fast_mode(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
+ mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
+ else
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x70, 0x00);
+
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
- ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x80);
snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x1F, 0x1C);
@@ -1094,10 +1015,6 @@
snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
return bias_value;
-
-gen_err:
- pr_err("%s: Error returned, ret: %d\n", __func__, ret);
- return ret;
}
static void wcd9xxx_shutdown_hs_removal_detect(struct wcd9xxx_mbhc *mbhc)
@@ -2927,7 +2844,6 @@
{
u8 cfilt_mode;
u16 reg0, reg1;
- int ret;
struct snd_soc_codec *codec = mbhc->codec;
pr_debug("%s: enter\n", __func__);
@@ -2944,21 +2860,30 @@
* Need to restore defaults once calculation is done.
*/
cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
- ret = wcd9xxx_put_cfilt_fast_mode(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
+ mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
+ else
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x40, 0x00);
+
/*
* Micbias, CFILT, LDOH, MBHC MUX mode settings
* to perform ADC calibration
*/
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->select_cfilt)
+ mbhc->mbhc_cb->select_cfilt(codec, mbhc);
+ else
snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60,
mbhc->mbhc_cfg->micbias << 5);
snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, 0x60, 0x60);
snd_soc_write(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x78);
- ret = wcd9xxx_codec_specific_cal_setup(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_specific_cal)
+ mbhc->mbhc_cb->codec_specific_cal(codec, mbhc);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+ 0x04, 0x04);
+
/* Pull down micbias to ground */
reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 1, 1);
@@ -2967,9 +2892,11 @@
snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4, 1 << 0);
/* Connect the MUX to micbias */
snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
- ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
/*
* Hardware that has external cap can delay mic bias ramping down up
* to 50ms.
@@ -2991,9 +2918,11 @@
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
- ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
/*
* Hardware that has external cap can delay mic bias ramping down up
* to 50ms.
@@ -3008,9 +2937,11 @@
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
- ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
/*
* Hardware that has external cap can delay mic bias ramping down up
* to 50ms.
@@ -3024,19 +2955,17 @@
snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
- ret = wcd9xxx_enable_mux_bias_block(codec, mbhc);
- if (ret)
- goto gen_err;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+ mbhc->mbhc_cb->enable_mux_bias_block(codec);
+ else
+ snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+ 0x80, 0x80);
usleep_range(100, 100);
wcd9xxx_enable_irq(codec->control_data, WCD9XXX_IRQ_MBHC_POTENTIAL);
wcd9xxx_turn_onoff_rel_detection(codec, true);
pr_debug("%s: leave\n", __func__);
- return;
-
-gen_err:
- pr_err("%s: Error returned, ret: %d\n", __func__, ret);
}
static void wcd9xxx_mbhc_setup(struct wcd9xxx_mbhc *mbhc)
@@ -3095,8 +3024,14 @@
{
int ret = 0;
void *core = mbhc->resmgr->core;
+ struct snd_soc_codec *codec = mbhc->codec;
int jack_irq;
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->jack_detect_irq)
+ jack_irq = mbhc->mbhc_cb->jack_detect_irq(codec);
+ else
+ jack_irq = WCD9XXX_IRQ_MBHC_JACK_SWITCH_DEFAULT;
+
if (mbhc->mbhc_cfg->gpio) {
ret = request_threaded_irq(mbhc->mbhc_cfg->gpio_irq, NULL,
wcd9xxx_mech_plug_detect_irq,
@@ -3118,17 +3053,6 @@
snd_soc_update_bits(mbhc->codec, WCD9XXX_A_RX_HPH_OCP_CTL,
1 << 1, 1 << 1);
- switch (mbhc->mbhc_version) {
- case WCD9XXX_MBHC_VERSION_TAIKO:
- jack_irq = WCD9320_IRQ_MBHC_JACK_SWITCH;
- break;
- case WCD9XXX_MBHC_VERSION_TAPAN:
- jack_irq = WCD9306_IRQ_MBHC_JACK_SWITCH;
- break;
- default:
- return -EINVAL;
- }
-
ret = wcd9xxx_request_irq(core, jack_irq,
wcd9xxx_mech_plug_detect_irq,
"Jack Detect",
@@ -3357,7 +3281,7 @@
int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
struct wcd9xxx_mbhc_config *mbhc_cfg)
{
- int rc;
+ int rc = 0;
struct snd_soc_codec *codec = mbhc->codec;
pr_debug("%s: enter\n", __func__);
@@ -3381,10 +3305,13 @@
wcd9xxx_get_mbhc_micbias_regs(mbhc, &mbhc->mbhc_bias_regs);
/* Put CFILT in fast mode by default */
- rc = wcd9xxx_put_cfilt_fast_mode(codec, mbhc);
- if (rc)
- pr_err("%s: Error returned, ret: %d\n", __func__, rc);
- else if (!mbhc->mbhc_cfg->read_fw_bin)
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
+ mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
+ else
+ snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+ 0x40, WCD9XXX_CFILT_FAST_MODE);
+
+ if (!mbhc->mbhc_cfg->read_fw_bin)
rc = wcd9xxx_init_and_calibrate(mbhc);
else
schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
@@ -3612,7 +3539,7 @@
* Switch CFILT to slow mode if MBHC CFILT is being
* used.
*/
- ret = wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
+ wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
break;
case WCD9XXX_EVENT_POST_CFILT_1_OFF:
case WCD9XXX_EVENT_POST_CFILT_2_OFF:
@@ -3623,7 +3550,7 @@
* Switch CFILT to fast mode if MBHC CFILT is not
* used anymore.
*/
- ret = wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
+ wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
break;
/* System resume */
case WCD9XXX_EVENT_POST_RESUME:
@@ -3656,8 +3583,7 @@
int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
struct snd_soc_codec *codec,
int (*micbias_enable_cb) (struct snd_soc_codec*, bool),
- int version,
- int rco_clk_rate)
+ const struct wcd9xxx_mbhc_cb *mbhc_cb, int rco_clk_rate)
{
int ret;
void *core;
@@ -3681,8 +3607,8 @@
mbhc->resmgr = resmgr;
mbhc->resmgr->mbhc = mbhc;
mbhc->micbias_enable_cb = micbias_enable_cb;
- mbhc->mbhc_version = version;
mbhc->rco_clk_rate = rco_clk_rate;
+ mbhc->mbhc_cb = mbhc_cb;
if (mbhc->headset_jack.jack == NULL) {
ret = snd_soc_jack_new(codec, "Headset Jack", WCD9XXX_JACK_MASK,
@@ -3805,17 +3731,11 @@
wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_REMOVAL, mbhc);
wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_MBHC_INSERTION, mbhc);
- switch (mbhc->mbhc_version) {
- case WCD9XXX_MBHC_VERSION_TAIKO:
- wcd9xxx_free_irq(cdata, WCD9320_IRQ_MBHC_JACK_SWITCH, mbhc);
- break;
- case WCD9XXX_MBHC_VERSION_TAPAN:
- wcd9xxx_free_irq(cdata, WCD9306_IRQ_MBHC_JACK_SWITCH, mbhc);
- break;
- default:
- pr_err("%s: irq free failed! Invalid MBHC version %d\n",
- __func__, mbhc->mbhc_version);
- }
+ if (mbhc->mbhc_cb && mbhc->mbhc_cb->free_irq)
+ mbhc->mbhc_cb->free_irq(mbhc);
+ else
+ wcd9xxx_free_irq(cdata, WCD9320_IRQ_MBHC_JACK_SWITCH,
+ mbhc);
wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, mbhc);
wcd9xxx_free_irq(cdata, WCD9XXX_IRQ_HPH_PA_OCPR_FAULT, mbhc);
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.h b/sound/soc/codecs/wcd9xxx-mbhc.h
index 71a62b2..02ecced 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.h
+++ b/sound/soc/codecs/wcd9xxx-mbhc.h
@@ -16,8 +16,8 @@
#define WCD9XXX_CFILT_FAST_MODE 0x00
#define WCD9XXX_CFILT_SLOW_MODE 0x40
-#define WCD9XXX_CFILT_EXT_PRCHG_EN 0x70
-#define WCD9XXX_CFILT_EXT_PRCHG_DSBL 0x40
+#define WCD9XXX_CFILT_EXT_PRCHG_EN 0x30
+#define WCD9XXX_CFILT_EXT_PRCHG_DSBL 0x00
struct mbhc_micbias_regs {
u16 cfilt_val;
@@ -61,12 +61,6 @@
s16 v_inval_ins_high;
};
-enum wcd9xxx_mbhc_version {
- WCD9XXX_MBHC_VERSION_UNKNOWN = 0,
- WCD9XXX_MBHC_VERSION_TAIKO,
- WCD9XXX_MBHC_VERSION_TAPAN,
-};
-
enum wcd9xxx_mbhc_plug_type {
PLUG_TYPE_INVALID = -1,
PLUG_TYPE_NONE,
@@ -218,6 +212,23 @@
bool (*swap_gnd_mic) (struct snd_soc_codec *);
};
+struct wcd9xxx_cfilt_mode {
+ u8 reg_mode_val;
+ u8 cur_mode_val;
+};
+
+struct wcd9xxx_mbhc_cb {
+ void (*enable_mux_bias_block) (struct snd_soc_codec *);
+ void (*cfilt_fast_mode) (struct snd_soc_codec *, struct wcd9xxx_mbhc *);
+ void (*codec_specific_cal) (struct snd_soc_codec *,
+ struct wcd9xxx_mbhc *);
+ int (*jack_detect_irq) (struct snd_soc_codec *);
+ struct wcd9xxx_cfilt_mode (*switch_cfilt_mode) (struct wcd9xxx_mbhc *,
+ bool);
+ void (*select_cfilt) (struct snd_soc_codec *, struct wcd9xxx_mbhc *);
+ void (*free_irq) (struct wcd9xxx_mbhc *);
+};
+
struct wcd9xxx_mbhc {
bool polling_active;
/* Delayed work to report long button press */
@@ -225,6 +236,7 @@
int buttons_pressed;
enum wcd9xxx_mbhc_state mbhc_state;
struct wcd9xxx_mbhc_config *mbhc_cfg;
+ const struct wcd9xxx_mbhc_cb *mbhc_cb;
struct mbhc_internal_cal_data mbhc_data;
@@ -279,8 +291,6 @@
bool micbias_enable;
int (*micbias_enable_cb) (struct snd_soc_codec*, bool);
- enum wcd9xxx_mbhc_version mbhc_version;
-
u32 rco_clk_rate;
#ifdef CONFIG_DEBUG_FS
@@ -349,7 +359,7 @@
int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
struct snd_soc_codec *codec,
int (*micbias_enable_cb) (struct snd_soc_codec*, bool),
- int version,
+ const struct wcd9xxx_mbhc_cb *mbhc_cb,
int rco_clk_rate);
void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc);
void *wcd9xxx_mbhc_cal_btn_det_mp(
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.c b/sound/soc/codecs/wcd9xxx-resmgr.c
index 84f236e..be11e53 100644
--- a/sound/soc/codecs/wcd9xxx-resmgr.c
+++ b/sound/soc/codecs/wcd9xxx-resmgr.c
@@ -209,8 +209,8 @@
int old_clk_rco_users, old_clk_mclk_users;
pr_debug("%s: enter\n", __func__);
- WCD9XXX_BCL_ASSERT_LOCKED(resmgr);
+ WCD9XXX_BG_CLK_LOCK(resmgr);
old_bg_audio_users = resmgr->bg_audio_users;
old_bg_mbhc_users = resmgr->bg_mbhc_users;
old_clk_rco_users = resmgr->clk_rco_users;
@@ -243,6 +243,7 @@
while (old_clk_rco_users--)
wcd9xxx_resmgr_get_clk_block(resmgr, WCD9XXX_CLK_RCO);
}
+ WCD9XXX_BG_CLK_UNLOCK(resmgr);
pr_debug("%s: leave\n", __func__);
}
diff --git a/sound/soc/msm/msm-dai-q6.c b/sound/soc/msm/msm-dai-q6.c
index d973c17..171db0a 100644
--- a/sound/soc/msm/msm-dai-q6.c
+++ b/sound/soc/msm/msm-dai-q6.c
@@ -1805,7 +1805,7 @@
SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
- .channels_max = 5,
+ .channels_max = 8,
.rate_min = 8000,
.rate_max = 192000,
},
diff --git a/sound/soc/msm/msm8226.c b/sound/soc/msm/msm8226.c
index 9782b2d..5dfd326 100644
--- a/sound/soc/msm/msm8226.c
+++ b/sound/soc/msm/msm8226.c
@@ -72,7 +72,7 @@
.gpio_irq = 0,
.gpio_level_insert = 0,
.detect_extn_cable = true,
- .micbias_enable_flags = 0,
+ .micbias_enable_flags = 1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET,
.insert_detect = true,
.swap_gnd_mic = NULL,
};
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index a39a18b..0a86221 100644
--- a/sound/soc/msm/msm8974.c
+++ b/sound/soc/msm/msm8974.c
@@ -711,7 +711,8 @@
static const char *const spk_function[] = {"Off", "On"};
static const char *const slim0_rx_ch_text[] = {"One", "Two"};
static const char *const slim0_tx_ch_text[] = {"One", "Two", "Three", "Four",
- "Five"};
+ "Five", "Six", "Seven",
+ "Eight"};
static char const *hdmi_rx_ch_text[] = {"Two", "Three", "Four", "Five",
"Six", "Seven", "Eight"};
static char const *rx_bit_format_text[] = {"S16_LE", "S24_LE"};
@@ -1319,7 +1320,7 @@
static const struct soc_enum msm_snd_enum[] = {
SOC_ENUM_SINGLE_EXT(2, spk_function),
SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
- SOC_ENUM_SINGLE_EXT(5, slim0_tx_ch_text),
+ SOC_ENUM_SINGLE_EXT(8, slim0_tx_ch_text),
SOC_ENUM_SINGLE_EXT(7, hdmi_rx_ch_text),
SOC_ENUM_SINGLE_EXT(2, rx_bit_format_text),
SOC_ENUM_SINGLE_EXT(3, slim0_rx_sample_rate_text),
diff --git a/sound/soc/msm/msm8x10.c b/sound/soc/msm/msm8x10.c
index a076246..4d9632c 100644
--- a/sound/soc/msm/msm8x10.c
+++ b/sound/soc/msm/msm8x10.c
@@ -34,11 +34,19 @@
#define BTSCO_RATE_8KHZ 8000
#define BTSCO_RATE_16KHZ 16000
+/* It takes about 13ms for Class-D PAs to ramp-up */
+#define EXT_CLASS_D_EN_DELAY 13000
+#define EXT_CLASS_D_DIS_DELAY 3000
+#define EXT_CLASS_D_DELAY_DELTA 2000
+
+
static int msm_btsco_rate = BTSCO_RATE_8KHZ;
static int msm_btsco_ch = 1;
static int msm_proxy_rx_ch = 2;
static struct snd_soc_jack hs_jack;
+static struct platform_device *spdev;
+static int ext_spk_amp_gpio = -1;
/*
@@ -86,15 +94,69 @@
static int msm8x10_mclk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
+static int msm_ext_spkramp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+static void msm8x10_enable_ext_spk_power_amp(u32 on);
static const struct snd_soc_dapm_widget msm8x10_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0,
msm8x10_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SPK("Lineout amp", msm_ext_spkramp_event),
SND_SOC_DAPM_MIC("Handset Mic", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
};
+static int msm8x10_ext_spk_power_amp_init(void)
+{
+ int ret = 0;
+
+ ext_spk_amp_gpio = of_get_named_gpio(spdev->dev.of_node,
+ "qcom,ext-spk-amp-gpio", 0);
+ if (ext_spk_amp_gpio >= 0) {
+ ret = gpio_request(ext_spk_amp_gpio, "ext_spk_amp_gpio");
+ if (ret) {
+ pr_err("%s: gpio_request failed for ext_spk_amp_gpio.\n",
+ __func__);
+ return -EINVAL;
+ }
+ gpio_direction_output(ext_spk_amp_gpio, 0);
+ }
+ return 0;
+}
+
+static int msm_ext_spkramp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ pr_debug("%s()\n", __func__);
+
+ if (ext_spk_amp_gpio >= 0) {
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ msm8x10_enable_ext_spk_power_amp(1);
+ else
+ msm8x10_enable_ext_spk_power_amp(0);
+ }
+ return 0;
+
+}
+
+static void msm8x10_enable_ext_spk_power_amp(u32 on)
+{
+ if (on) {
+ gpio_direction_output(ext_spk_amp_gpio, on);
+ /*time takes enable the external power amplifier*/
+ usleep_range(EXT_CLASS_D_EN_DELAY,
+ EXT_CLASS_D_EN_DELAY + EXT_CLASS_D_DELAY_DELTA);
+ } else {
+ gpio_direction_output(ext_spk_amp_gpio, on);
+ /*time takes disable the external power amplifier*/
+ usleep_range(EXT_CLASS_D_DIS_DELAY,
+ EXT_CLASS_D_DIS_DELAY + EXT_CLASS_D_DELAY_DELTA);
+ }
+
+ pr_debug("%s: %s external speaker PAs.\n", __func__,
+ on ? "Enable" : "Disable");
+}
static int msm_config_mclk(u16 port_id, struct afe_digital_clk_cfg *cfg)
{
@@ -265,10 +327,11 @@
int ret = 0;
pr_debug("%s(),dev_name%s\n", __func__, dev_name(cpu_dai->dev));
-
+ msm8x10_ext_spk_power_amp_init();
snd_soc_dapm_new_controls(dapm, msm8x10_dapm_widgets,
ARRAY_SIZE(msm8x10_dapm_widgets));
+ snd_soc_dapm_enable_pin(dapm, "Lineout amp");
snd_soc_dapm_sync(dapm);
ret = snd_soc_jack_new(codec, "Headset Jack",
@@ -686,11 +749,16 @@
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
+ ret = snd_soc_of_parse_card_name(card, "qcom,model");
+ if (ret)
+ goto err;
+
ret = snd_soc_of_parse_audio_routing(card,
"qcom,audio-routing");
if (ret)
goto err;
+ spdev = pdev;
ret = snd_soc_register_card(card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
@@ -708,6 +776,8 @@
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
+ if (gpio_is_valid(ext_spk_amp_gpio))
+ gpio_free(ext_spk_amp_gpio);
snd_soc_unregister_card(card);
mutex_destroy(&cdc_mclk_mutex);
return 0;